| /* |
| * teamd_bpf_chef.c - Cooking BPF functions for teamd |
| * Copyright (C) 2012-2015 Jiri Pirko <jiri@resnulli.us> |
| * |
| * This library is free software; you can redistribute it and/or |
| * modify it under the terms of the GNU Lesser General Public |
| * License as published by the Free Software Foundation; either |
| * version 2.1 of the License, or (at your option) any later version. |
| * |
| * This library is distributed in the hope that it will be useful, |
| * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
| * Lesser General Public License for more details. |
| * |
| * You should have received a copy of the GNU Lesser General Public |
| * License along with this library; if not, write to the Free Software |
| * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA |
| */ |
| |
| #include <stdlib.h> |
| #include <errno.h> |
| #include <stdbool.h> |
| #include <linux/filter.h> |
| |
| #include "teamd_bpf_chef.h" |
| |
| /* protocol offsets */ |
| #define ETH_TYPE_OFFSET 12 |
| #define VLAN_TAG_OFFSET 14 |
| #define IPV4_FLAGS_OFFSET 20 |
| #define IPV4_PROTO_OFFSET 23 |
| #define IPV4_FRAG_BITS 0x1fff |
| #define IPV6_NEXTHEADER_OFFSET 20 |
| |
| |
| /* protocol codes */ |
| #define PROTOID_IPV4 0x800 |
| #define PROTOID_IPV6 0x86dd |
| #define PROTOID_VLAN 0x8100 |
| #define PROTOID_TCP 0x6 |
| #define PROTOID_UDP 0x11 |
| #define PROTOID_SCTP 0x84 |
| |
| /* jump stack flags */ |
| #define FIX_JT 0x1 |
| #define FIX_JF 0x2 |
| #define FIX_K 0x4 |
| |
| |
| #define VLAN_HEADER_SIZE 4 |
| static int vlan_hdr_shift(unsigned int offset) |
| { |
| return offset + VLAN_HEADER_SIZE; |
| } |
| |
| static int __add_inst(struct sock_fprog *fprog, struct sock_filter *inst) |
| { |
| ssize_t newsize; |
| struct sock_filter *newfilter; |
| |
| newsize = sizeof(struct sock_filter) * (fprog->len + 1); |
| newfilter = realloc(fprog->filter, newsize); |
| if (!newfilter) |
| return -ENOMEM; |
| fprog->filter = newfilter; |
| fprog->filter[fprog->len] = *inst; |
| fprog->len += 1; |
| return 0; |
| } |
| |
| #define add_inst(fprog, __inst) \ |
| { \ |
| struct sock_filter inst = __inst; \ |
| err = __add_inst(fprog, &inst); \ |
| if (err) \ |
| goto err_add_inst; \ |
| } |
| |
| #define bpf_load_byte(pos) \ |
| add_inst(fprog, BPF_STMT(BPF_LD + BPF_B + BPF_ABS, pos)) |
| |
| #define bpf_load_half(pos) \ |
| add_inst(fprog, BPF_STMT(BPF_LD + BPF_H + BPF_ABS, pos)) |
| |
| #define bpf_load_word(pos) \ |
| add_inst(fprog, BPF_STMT(BPF_LD + BPF_W + BPF_ABS, pos)) |
| |
| #define bpf_and_word(w) \ |
| add_inst(fprog, BPF_STMT(BPF_AND + BPF_ALU, w)) |
| |
| #define bpf_push_a() \ |
| add_inst(fprog, BPF_STMT(BPF_ST, 0)) |
| |
| #define bpf_push_x() \ |
| add_inst(fprog, BPF_STMT(BPF_STX, 0)) |
| |
| #define bpf_pop_x() \ |
| add_inst(fprog, BPF_STMT(BPF_LDX + BPF_W + BPF_MEM, 0)) |
| |
| #define bpf_calc_hash() \ |
| add_inst(fprog, BPF_STMT(BPF_LD + BPF_B + BPF_ABS, \ |
| SKF_AD_OFF + SKF_AD_ALU_XOR_X)) |
| |
| #define bpf_move_to_x() \ |
| add_inst(fprog, BPF_STMT(BPF_MISC + BPF_TAX, 0)); |
| |
| #define bpf_move_to_a() \ |
| add_inst(fprog, BPF_STMT(BPF_MISC + BPF_TXA, 0)); |
| |
| #define bpf_return_a() \ |
| add_inst(fprog, BPF_STMT(BPF_RET + BPF_A, 0)); |
| |
| #define bpf_cmp(jt, jf, k, flags) \ |
| do { \ |
| add_inst(fprog, BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, \ |
| k, jt, jf)); \ |
| push_addr(fprog, flags); \ |
| } while (0) |
| |
| #define bpf_jump(k) \ |
| do { \ |
| add_inst(fprog, BPF_JUMP(BPF_JMP + BPF_JA, k, 0, 0)); \ |
| push_addr(fprog, FIX_K); \ |
| } while (0) |
| |
| |
| #define bpf_and(jt, jf, k, flags) \ |
| do { \ |
| add_inst(fprog, BPF_JUMP(BPF_JMP + BPF_JSET + BPF_K, \ |
| k, jt, jf)); \ |
| push_addr(fprog, flags); \ |
| } while (0) |
| |
| |
| #define bpf_vlan_tag_present() \ |
| add_inst(fprog, BPF_STMT(BPF_LD + BPF_B + BPF_ABS, \ |
| SKF_AD_OFF + SKF_AD_VLAN_TAG_PRESENT)) |
| |
| #define bpf_vlan_tag_id() \ |
| add_inst(fprog, BPF_STMT(BPF_LD + BPF_B + BPF_ABS, \ |
| SKF_AD_OFF + SKF_AD_VLAN_TAG)) |
| |
| #define bpf_ipv4_len_to_x(pos) \ |
| add_inst(fprog, BPF_STMT(BPF_LDX + BPF_B + BPF_MSH, pos)) |
| |
| #define bpf_l4v4_port_to_a(pos) \ |
| add_inst(fprog, BPF_STMT(BPF_LD + BPF_H + BPF_IND, pos)) |
| |
| #define bpf_hash_return() \ |
| do { \ |
| bpf_move_to_a(); \ |
| bpf_return_a(); \ |
| } while(0) |
| |
| |
| enum bpf_labels { |
| LABEL_VLAN_BRANCH, |
| LABEL_VLAN_TAG_SKIP, |
| LABEL_NOVLAN_IPV6, |
| LABEL_NOVLAN_L4v4_OUT, |
| LABEL_NOVLAN_TRY_UDP4, |
| LABEL_NOVLAN_L4v4_HASH, |
| LABEL_NOVLAN_TRY_STCP4, |
| LABEL_NOVLAN_IPV6_CONTINUE, |
| LABEL_NOVLAN_TRY_UDP6, |
| LABEL_NOVLAN_L4v6_OUT, |
| LABEL_NOVLAN_L4v6_HASH, |
| LABEL_NOVLAN_TRY_STCP6, |
| LABEL_VLAN_IPV6, |
| LABEL_VLAN_L4v4_OUT, |
| LABEL_VLAN_TRY_UDP4, |
| LABEL_VLAN_L4v4_HASH, |
| LABEL_VLAN_TRY_STCP4, |
| LABEL_VLAN_IPV6_CONTINUE, |
| LABEL_VLAN_TRY_UDP6, |
| LABEL_VLAN_L4v6_OUT, |
| LABEL_VLAN_L4v6_HASH, |
| LABEL_VLAN_TRY_STCP6, |
| }; |
| |
| /* stack */ |
| struct stack_entry { |
| struct stack_entry *next; |
| unsigned int addr; |
| union { |
| enum bpf_labels label; |
| int flags; |
| }; |
| }; |
| |
| static struct stack_entry *stack_labels; |
| static struct stack_entry *stack_addrs; |
| |
| enum hashing_flags { |
| HASH_ETH, |
| HASH_VLAN, |
| HASH_VLAN_IPV4, |
| HASH_VLAN_IPV6, |
| HASH_VLAN_UDP4, |
| HASH_VLAN_TCP4, |
| HASH_VLAN_SCTP4, |
| HASH_VLAN_UDP6, |
| HASH_VLAN_TCP6, |
| HASH_VLAN_SCTP6, |
| HASH_NOVLAN_IPV4, |
| HASH_NOVLAN_IPV6, |
| HASH_NOVLAN_UDP4, |
| HASH_NOVLAN_TCP4, |
| HASH_NOVLAN_SCTP4, |
| HASH_NOVLAN_UDP6, |
| HASH_NOVLAN_TCP6, |
| HASH_NOVLAN_SCTP6, |
| }; |
| |
| struct hash_flags { |
| unsigned int required; |
| unsigned int built; |
| }; |
| |
| static struct hash_flags hflags; |
| |
| static void hash_flags_init(struct hash_flags *flags) |
| { |
| flags->required = 0; |
| flags->built = 0; |
| } |
| |
| static int hash_test_and_set_flag(struct hash_flags *flags, |
| enum hashing_flags hflag) |
| { |
| int flag = 1 << hflag; |
| int ret; |
| |
| ret = 0; |
| if (flags->required & flag) { |
| flags->built |= flag; |
| ret = 1; |
| } |
| |
| return ret; |
| } |
| |
| static int hash_is_complete(struct hash_flags *flags) |
| { |
| return flags->built == flags->required; |
| } |
| |
| static int hash_set_enable(struct hash_flags *flags, enum hashing_flags hflag) |
| { |
| flags->required |= (1 << hflag); |
| return 0; |
| } |
| |
| static int hash_is_enabled(struct hash_flags *flags, enum hashing_flags hflag) |
| { |
| return (flags->required & (1 << hflag)); |
| } |
| |
| static int hash_is_l4v4_enabled(struct hash_flags *flags) |
| { |
| if (hash_is_enabled(flags, HASH_NOVLAN_TCP4) || |
| hash_is_enabled(flags, HASH_NOVLAN_UDP4) || |
| hash_is_enabled(flags, HASH_NOVLAN_SCTP4) || |
| hash_is_enabled(flags, HASH_VLAN_TCP4) || |
| hash_is_enabled(flags, HASH_VLAN_UDP4) || |
| hash_is_enabled(flags, HASH_VLAN_SCTP4)) |
| return 1; |
| |
| return 0; |
| } |
| |
| static int hash_is_l4v6_enabled(struct hash_flags *flags) |
| { |
| if (hash_is_enabled(flags, HASH_NOVLAN_TCP6) || |
| hash_is_enabled(flags, HASH_NOVLAN_UDP6) || |
| hash_is_enabled(flags, HASH_NOVLAN_SCTP6) || |
| hash_is_enabled(flags, HASH_VLAN_TCP6) || |
| hash_is_enabled(flags, HASH_VLAN_UDP6) || |
| hash_is_enabled(flags, HASH_VLAN_SCTP6)) |
| return 1; |
| |
| return 0; |
| } |
| |
| static int hash_is_novlan_l3l4_enabled(struct hash_flags *flags) |
| { |
| |
| if (hash_is_enabled(flags, HASH_NOVLAN_IPV4) || |
| hash_is_enabled(flags, HASH_NOVLAN_IPV6) || |
| hash_is_enabled(flags, HASH_NOVLAN_TCP4) || |
| hash_is_enabled(flags, HASH_NOVLAN_UDP4) || |
| hash_is_enabled(flags, HASH_NOVLAN_SCTP4) || |
| hash_is_enabled(flags, HASH_NOVLAN_TCP6) || |
| hash_is_enabled(flags, HASH_NOVLAN_UDP6) || |
| hash_is_enabled(flags, HASH_NOVLAN_SCTP6)) |
| return 1; |
| |
| return 0; |
| } |
| |
| static void stack_init(void) |
| { |
| stack_labels = NULL; |
| stack_addrs = NULL; |
| } |
| |
| static void __stack_release(struct stack_entry *root) |
| { |
| struct stack_entry *p; |
| struct stack_entry *pn; |
| |
| p = root; |
| while (p) { |
| pn = p->next; |
| free(p); |
| p = pn; |
| } |
| } |
| |
| static void stack_release(void) |
| { |
| __stack_release(stack_labels); |
| __stack_release(stack_addrs); |
| stack_init(); |
| } |
| |
| static int push_addr(struct sock_fprog *fprog, int flags) |
| { |
| struct stack_entry *pa; |
| |
| pa = malloc(sizeof(struct stack_entry)); |
| if (!pa) |
| return -ENOMEM; |
| |
| pa->addr = fprog->len - 1; |
| pa->flags = flags; |
| |
| pa->next = stack_addrs; |
| stack_addrs = pa; |
| |
| return 0; |
| } |
| |
| static struct stack_entry *__find_label(enum bpf_labels label) |
| { |
| struct stack_entry *p; |
| |
| p = stack_labels; |
| while (p) { |
| if (p->label == label) |
| return p; |
| |
| p = p->next; |
| } |
| |
| return NULL; |
| } |
| |
| static int push_label(struct sock_fprog *fprog, enum bpf_labels label) |
| { |
| struct stack_entry *pl; |
| |
| if (__find_label(label)) |
| return -EEXIST; |
| |
| pl = malloc(sizeof(struct stack_entry)); |
| if (!pl) |
| return -ENOMEM; |
| |
| pl->addr = fprog->len; |
| pl->label = label; |
| |
| pl->next = stack_labels; |
| stack_labels = pl; |
| |
| return 0; |
| } |
| |
| static int stack_resolve_offsets(struct sock_fprog *fprog) |
| { |
| struct stack_entry *paddr; |
| struct stack_entry *naddr; |
| struct stack_entry *plabel; |
| struct stack_entry *nlabel; |
| struct sock_filter *sf; |
| int offset; |
| |
| paddr = stack_addrs; |
| while (paddr) { |
| sf = fprog->filter + paddr->addr; |
| |
| if (paddr->flags & ~(FIX_K|FIX_JT|FIX_JF)) |
| return -EINVAL; |
| |
| if (paddr->flags & FIX_K) { |
| plabel = __find_label(sf->k); |
| if (!plabel) |
| return -ENOENT; |
| |
| offset = plabel->addr - paddr->addr - 1; |
| if (offset < 0 || offset > 255) |
| return -EINVAL; |
| sf->k = offset; |
| } |
| |
| if (paddr->flags & FIX_JT) { |
| plabel = __find_label(sf->jt); |
| if (!plabel) |
| return -ENOENT; |
| |
| offset = plabel->addr - paddr->addr - 1; |
| if (offset < 0 || offset > 255) |
| return -EINVAL; |
| sf->jt = offset; |
| } |
| |
| if (paddr->flags & FIX_JF) { |
| plabel = __find_label(sf->jf); |
| if (!plabel) |
| return -ENOENT; |
| |
| offset = plabel->addr - paddr->addr - 1; |
| if (offset < 0 || offset > 255) |
| return -EINVAL; |
| sf->jf = offset; |
| } |
| |
| naddr = paddr->next; |
| free(paddr); |
| paddr = naddr; |
| } |
| |
| plabel = stack_labels; |
| while (plabel) { |
| nlabel = plabel->next; |
| free(plabel); |
| plabel = nlabel; |
| } |
| |
| stack_init(); |
| return 0; |
| } |
| |
| static int bpf_eth_hash(struct sock_fprog *fprog) |
| { |
| int err; |
| |
| /* hash dest mac addr */ |
| bpf_load_word(2); |
| bpf_calc_hash(); |
| bpf_move_to_x(); |
| bpf_load_half(0); |
| bpf_calc_hash(); |
| bpf_move_to_x(); |
| |
| /* hash source mac addr */ |
| bpf_load_word(8); |
| bpf_calc_hash(); |
| bpf_move_to_x(); |
| bpf_load_half(6); |
| bpf_calc_hash(); |
| bpf_move_to_x(); |
| return 0; |
| |
| err_add_inst: |
| return err; |
| } |
| |
| static int bpf_vlan_hash(struct sock_fprog *fprog) |
| { |
| int err; |
| |
| bpf_vlan_tag_id(); |
| bpf_calc_hash(); |
| bpf_move_to_x(); |
| |
| err_add_inst: |
| return err; |
| } |
| |
| static int bpf_vlan_hash_header(struct sock_fprog *fprog) |
| { |
| int err; |
| |
| bpf_load_half(VLAN_TAG_OFFSET); |
| bpf_and_word(0x0fff); |
| bpf_calc_hash(); |
| bpf_move_to_x(); |
| |
| err_add_inst: |
| return err; |
| } |
| |
| static int __bpf_ipv4_hash(struct sock_fprog *fprog, bool vlan) |
| { |
| int vlan_shift = vlan ? vlan_hdr_shift(0) : 0; |
| int err; |
| |
| bpf_load_word(26 + vlan_shift); |
| bpf_calc_hash(); |
| bpf_move_to_x(); |
| bpf_load_word(30 + vlan_shift); |
| bpf_calc_hash(); |
| bpf_move_to_x(); |
| return 0; |
| |
| err_add_inst: |
| return err; |
| } |
| |
| static int bpf_vlan_ipv4_hash(struct sock_fprog *fprog) |
| { |
| return __bpf_ipv4_hash(fprog, true); |
| } |
| |
| static int bpf_novlan_ipv4_hash(struct sock_fprog *fprog) |
| { |
| return __bpf_ipv4_hash(fprog, false); |
| } |
| |
| static int __bpf_ipv6_hash(struct sock_fprog *fprog, bool vlan) |
| { |
| int vlan_shift = vlan ? vlan_hdr_shift(0) : 0; |
| int err; |
| |
| bpf_load_word(22 + vlan_shift); |
| bpf_calc_hash(); |
| bpf_move_to_x(); |
| bpf_load_word(26 + vlan_shift); |
| bpf_calc_hash(); |
| bpf_move_to_x(); |
| bpf_load_word(30 + vlan_shift); |
| bpf_calc_hash(); |
| bpf_move_to_x(); |
| bpf_load_word(34 + vlan_shift); |
| bpf_calc_hash(); |
| bpf_move_to_x(); |
| bpf_load_word(38 + vlan_shift); |
| bpf_calc_hash(); |
| bpf_move_to_x(); |
| bpf_load_word(42 + vlan_shift); |
| bpf_calc_hash(); |
| bpf_move_to_x(); |
| bpf_load_word(46 + vlan_shift); |
| bpf_calc_hash(); |
| bpf_move_to_x(); |
| bpf_load_word(50 + vlan_shift); |
| bpf_calc_hash(); |
| bpf_move_to_x(); |
| return 0; |
| |
| err_add_inst: |
| return err; |
| } |
| |
| static int bpf_vlan_ipv6_hash(struct sock_fprog *fprog) |
| { |
| return __bpf_ipv6_hash(fprog, true); |
| } |
| |
| static int bpf_novlan_ipv6_hash(struct sock_fprog *fprog) |
| { |
| return __bpf_ipv6_hash(fprog, false); |
| } |
| |
| static int __bpf_l4v4_hash(struct sock_fprog *fprog, bool vlan) |
| { |
| int vlan_shift = vlan ? vlan_hdr_shift(0) : 0; |
| int err; |
| |
| bpf_push_x(); |
| bpf_ipv4_len_to_x(14 + vlan_shift); |
| /* source port offset */ |
| bpf_l4v4_port_to_a(14 + vlan_shift); |
| bpf_pop_x(); |
| bpf_calc_hash(); |
| bpf_push_a(); |
| bpf_ipv4_len_to_x(14 + vlan_shift); |
| /* dest port offset */ |
| bpf_l4v4_port_to_a(16 + vlan_shift); |
| bpf_pop_x(); |
| bpf_calc_hash(); |
| bpf_move_to_x(); |
| return 0; |
| |
| err_add_inst: |
| return err; |
| } |
| |
| static int bpf_vlan_l4v4_hash(struct sock_fprog *fprog) |
| { |
| return __bpf_l4v4_hash(fprog, true); |
| } |
| |
| static int bpf_novlan_l4v4_hash(struct sock_fprog *fprog) |
| { |
| return __bpf_l4v4_hash(fprog, false); |
| } |
| |
| static int __bpf_l4v6_hash(struct sock_fprog *fprog, bool vlan) |
| { |
| int vlan_shift = vlan ? vlan_hdr_shift(0) : 0; |
| int err; |
| |
| bpf_load_half(54 + vlan_shift); |
| bpf_calc_hash(); |
| bpf_load_half(56 + vlan_shift); |
| bpf_calc_hash(); |
| bpf_move_to_x(); |
| return 0; |
| |
| err_add_inst: |
| return err; |
| } |
| |
| static int bpf_vlan_l4v6_hash(struct sock_fprog *fprog) |
| { |
| return __bpf_l4v6_hash(fprog, true); |
| } |
| |
| static int bpf_novlan_l4v6_hash(struct sock_fprog *fprog) |
| { |
| return __bpf_l4v6_hash(fprog, false); |
| } |
| |
| /* bpf_create_code: |
| * This function creates the entire bpf hashing code and follows |
| * this scheme: |
| * |
| * start |
| * V |
| * handle ethernet |
| * V |
| * check vlan tag presense |
| * |yes | no |
| * handle | |
| * vlan | |
| * V V |
| * +----- ipv4/ipv6? +-- ipv4/ipv6?--+ |
| * |ipv4 |ipv6 | ipv4 |ipv6 |
| * +--frag?-+ | +-frag?-+ | |
| * |yes |no | |yes |no | |
| * return V V return V V |
| * handle handle handle handle |
| * ipv4 ipv6 ipv4 ipv6 |
| * | | | | |
| * proto? proto? proto? proto? |
| * |
| * for each branch above: |
| * |tcp |udp |sctp |none |
| * | | | | |
| * handle | |
| * return <------+ |
| */ |
| static int bpf_create_code(struct sock_fprog *fprog, struct hash_flags *flags) |
| { |
| int err; |
| |
| /* generate the ethernet hashing code */ |
| if (hash_test_and_set_flag(flags, HASH_ETH)) |
| bpf_eth_hash(fprog); |
| |
| if (hash_is_complete(flags)) { |
| bpf_hash_return(); |
| /* there is no need to keep going, all done */ |
| return 0; |
| } |
| |
| bpf_load_half(ETH_TYPE_OFFSET); |
| bpf_cmp(LABEL_VLAN_BRANCH, 0, PROTOID_VLAN, FIX_JT); |
| |
| /* no vlan branch, but might be offloaded */ |
| if (hash_test_and_set_flag(flags, HASH_VLAN)) { |
| bpf_vlan_tag_present(); |
| bpf_cmp(LABEL_VLAN_TAG_SKIP, 0, 0, FIX_JT); |
| bpf_vlan_hash(fprog); |
| push_label(fprog, LABEL_VLAN_TAG_SKIP); |
| } |
| |
| if (!hash_is_novlan_l3l4_enabled(flags)) |
| bpf_hash_return(); |
| |
| /* vlan hashing overwrote this */ |
| if (hash_is_enabled(flags, HASH_VLAN)) |
| bpf_load_half(ETH_TYPE_OFFSET); |
| |
| /* no vlan branch */ |
| bpf_cmp(0, LABEL_NOVLAN_IPV6, PROTOID_IPV4, FIX_JF); |
| |
| /* no vlan ipv4 branch */ |
| if (hash_test_and_set_flag(flags, HASH_NOVLAN_IPV4)) |
| bpf_novlan_ipv4_hash(fprog); |
| |
| if (!hash_is_l4v4_enabled(flags)) |
| bpf_hash_return(); |
| |
| /* no vlan ipv4 L4 */ |
| /* ignore IP frags */ |
| bpf_load_half(IPV4_FLAGS_OFFSET); |
| bpf_and(LABEL_NOVLAN_L4v4_OUT, 0, IPV4_FRAG_BITS, FIX_JT); |
| |
| /* L4 protocol check */ |
| bpf_load_byte(IPV4_PROTO_OFFSET); |
| bpf_cmp(0, LABEL_NOVLAN_TRY_UDP4, PROTOID_TCP, FIX_JF); |
| |
| if (hash_test_and_set_flag(flags, HASH_NOVLAN_TCP4)) |
| bpf_jump(LABEL_NOVLAN_L4v4_HASH); |
| else |
| bpf_jump(LABEL_NOVLAN_L4v4_OUT); |
| |
| /* no vlan try udp4 */ |
| push_label(fprog, LABEL_NOVLAN_TRY_UDP4); |
| bpf_cmp(0, LABEL_NOVLAN_TRY_STCP4, PROTOID_UDP, FIX_JF); |
| if (hash_test_and_set_flag(flags, HASH_NOVLAN_UDP4)) |
| bpf_jump(LABEL_NOVLAN_L4v4_HASH); |
| else |
| bpf_jump(LABEL_NOVLAN_L4v4_OUT); |
| |
| /* no vlan try sctp4 */ |
| push_label(fprog, LABEL_NOVLAN_TRY_STCP4); |
| bpf_cmp(0, LABEL_NOVLAN_L4v4_OUT, PROTOID_SCTP, FIX_JF); |
| if (!hash_test_and_set_flag(flags, HASH_NOVLAN_SCTP4)) |
| bpf_jump(LABEL_NOVLAN_L4v4_OUT); |
| |
| /* no vlan L4v4 hashing */ |
| push_label(fprog, LABEL_NOVLAN_L4v4_HASH); |
| bpf_novlan_l4v4_hash(fprog); |
| |
| /* no vlan L4v4 out: */ |
| push_label(fprog, LABEL_NOVLAN_L4v4_OUT); |
| bpf_hash_return(); |
| |
| /* no vlan ipv6 branch */ |
| push_label(fprog, LABEL_NOVLAN_IPV6); |
| bpf_cmp(LABEL_NOVLAN_IPV6_CONTINUE, 0, PROTOID_IPV6, FIX_JT); |
| bpf_hash_return(); |
| |
| /* no vlan ipv6 continue */ |
| push_label(fprog, LABEL_NOVLAN_IPV6_CONTINUE); |
| if (hash_test_and_set_flag(flags, HASH_NOVLAN_IPV6)) |
| bpf_novlan_ipv6_hash(fprog); |
| |
| if (!hash_is_l4v6_enabled(flags)) |
| bpf_hash_return(); |
| |
| /* no vlan ipv6 l4 branch */ |
| /* L4 protocol check (Next Header) */ |
| bpf_load_byte(IPV6_NEXTHEADER_OFFSET); |
| bpf_cmp(0, LABEL_NOVLAN_TRY_UDP6, PROTOID_TCP, FIX_JF); |
| |
| if (hash_test_and_set_flag(flags, HASH_NOVLAN_TCP6)) |
| bpf_jump(LABEL_NOVLAN_L4v6_HASH); |
| else |
| bpf_jump(LABEL_NOVLAN_L4v6_OUT); |
| |
| /* no vlan check udp6 */ |
| push_label(fprog, LABEL_NOVLAN_TRY_UDP6); |
| bpf_cmp(0, LABEL_NOVLAN_TRY_STCP6, PROTOID_UDP, FIX_JF); |
| if (hash_test_and_set_flag(flags, HASH_NOVLAN_UDP6)) |
| bpf_jump(LABEL_NOVLAN_L4v6_HASH); |
| else |
| bpf_jump(LABEL_NOVLAN_L4v6_OUT); |
| |
| /* no vlan check sctp6 */ |
| push_label(fprog, LABEL_NOVLAN_TRY_STCP6); |
| bpf_cmp(0, LABEL_NOVLAN_L4v6_OUT, PROTOID_SCTP, FIX_JF); |
| if (!hash_test_and_set_flag(flags, HASH_NOVLAN_SCTP4)) |
| bpf_jump(LABEL_NOVLAN_L4v6_OUT); |
| |
| /* no vlan l4v6 hashing */ |
| push_label(fprog, LABEL_NOVLAN_L4v6_HASH); |
| bpf_novlan_l4v6_hash(fprog); |
| |
| /* no vlan l4v6 out */ |
| push_label(fprog, LABEL_NOVLAN_L4v6_OUT); |
| bpf_hash_return(); |
| |
| /* vlan branch */ |
| push_label(fprog, LABEL_VLAN_BRANCH); |
| if (hash_test_and_set_flag(flags, HASH_VLAN)) |
| bpf_vlan_hash_header(fprog); |
| |
| if (hash_is_complete(flags)) { |
| bpf_hash_return(); |
| return 0; |
| } |
| |
| bpf_load_half(vlan_hdr_shift(ETH_TYPE_OFFSET)); |
| bpf_cmp(0, LABEL_VLAN_IPV6, PROTOID_IPV4, FIX_JF); |
| /* vlan ipv4 branch */ |
| if (hash_test_and_set_flag(flags, HASH_VLAN_IPV4)) |
| bpf_vlan_ipv4_hash(fprog); |
| |
| if (!hash_is_l4v4_enabled(flags)) |
| bpf_hash_return(); |
| |
| /* vlan ipv4 L4 */ |
| /* ignore IP frags */ |
| bpf_load_half(vlan_hdr_shift(IPV4_FLAGS_OFFSET)); |
| bpf_and(LABEL_VLAN_L4v4_OUT, 0, IPV4_FRAG_BITS, FIX_JT); |
| |
| /* L4 protocol check */ |
| bpf_load_byte(vlan_hdr_shift(IPV4_PROTO_OFFSET)); |
| bpf_cmp(0, LABEL_VLAN_TRY_UDP4, PROTOID_TCP, FIX_JF); |
| |
| if (hash_test_and_set_flag(flags, HASH_VLAN_TCP4)) |
| bpf_jump(LABEL_VLAN_L4v4_HASH); |
| else |
| bpf_jump(LABEL_VLAN_L4v4_OUT); |
| |
| /* vlan try udp4 */ |
| push_label(fprog, LABEL_VLAN_TRY_UDP4); |
| bpf_cmp(0, LABEL_VLAN_TRY_STCP4, PROTOID_UDP, FIX_JF); |
| if (hash_test_and_set_flag(flags, HASH_VLAN_UDP4)) |
| bpf_jump(LABEL_VLAN_L4v4_HASH); |
| else |
| bpf_jump(LABEL_VLAN_L4v4_OUT); |
| |
| /* vlan try sctp4 */ |
| push_label(fprog, LABEL_VLAN_TRY_STCP4); |
| bpf_cmp(0, LABEL_VLAN_L4v4_OUT, PROTOID_SCTP, FIX_JF); |
| if (!hash_test_and_set_flag(flags, HASH_VLAN_SCTP4)) |
| bpf_jump(LABEL_VLAN_L4v4_OUT); |
| |
| /* vlan L4v4 hashing */ |
| push_label(fprog, LABEL_VLAN_L4v4_HASH); |
| bpf_vlan_l4v4_hash(fprog); |
| |
| /* vlan L4v4 out: */ |
| push_label(fprog, LABEL_VLAN_L4v4_OUT); |
| bpf_hash_return(); |
| |
| /* vlan ipv6 branch */ |
| push_label(fprog, LABEL_VLAN_IPV6); |
| bpf_cmp(LABEL_VLAN_IPV6_CONTINUE, 0, PROTOID_IPV6, FIX_JT); |
| bpf_hash_return(); |
| |
| /* vlan ipv6 continue */ |
| push_label(fprog, LABEL_VLAN_IPV6_CONTINUE); |
| if (hash_test_and_set_flag(flags, HASH_VLAN_IPV6)) |
| bpf_vlan_ipv6_hash(fprog); |
| |
| if (!hash_is_l4v6_enabled(flags)) |
| bpf_hash_return(); |
| |
| /* vlan ipv6 l4 branch */ |
| /* L4 protocol check (Next Header) */ |
| bpf_load_byte(vlan_hdr_shift(IPV6_NEXTHEADER_OFFSET)); |
| bpf_cmp(0, LABEL_VLAN_TRY_UDP6, PROTOID_TCP, FIX_JF); |
| |
| if (hash_test_and_set_flag(flags, HASH_VLAN_TCP6)) |
| bpf_jump(LABEL_VLAN_L4v6_HASH); |
| else |
| bpf_jump(LABEL_VLAN_L4v6_OUT); |
| |
| /* vlan check udp6 */ |
| push_label(fprog, LABEL_VLAN_TRY_UDP6); |
| bpf_cmp(0, LABEL_VLAN_TRY_STCP6, PROTOID_UDP, FIX_JF); |
| if (hash_test_and_set_flag(flags, HASH_VLAN_UDP6)) |
| bpf_jump(LABEL_VLAN_L4v6_HASH); |
| else |
| bpf_jump(LABEL_VLAN_L4v6_OUT); |
| |
| /* vlan check sctp6 */ |
| push_label(fprog, LABEL_VLAN_TRY_STCP6); |
| bpf_cmp(0, LABEL_VLAN_L4v6_OUT, PROTOID_SCTP, FIX_JF); |
| if (!hash_test_and_set_flag(flags, HASH_VLAN_SCTP4)) |
| bpf_jump(LABEL_VLAN_L4v6_OUT); |
| |
| /* vlan l4v6 hashing */ |
| push_label(fprog, LABEL_VLAN_L4v6_HASH); |
| bpf_vlan_l4v6_hash(fprog); |
| |
| /* vlan l4v6 out */ |
| push_label(fprog, LABEL_VLAN_L4v6_OUT); |
| bpf_hash_return(); |
| return 0; |
| |
| err_add_inst: |
| return err; |
| } |
| |
| int teamd_bpf_desc_add_frag(struct sock_fprog *fprog, |
| const struct teamd_bpf_desc_frag *frag) |
| { |
| switch (frag->hproto) { |
| case PROTO_ETH: |
| hash_set_enable(&hflags, HASH_ETH); |
| break; |
| |
| case PROTO_VLAN: |
| hash_set_enable(&hflags, HASH_VLAN); |
| break; |
| |
| case PROTO_IP: |
| case PROTO_L3: |
| hash_set_enable(&hflags, HASH_VLAN_IPV4); |
| hash_set_enable(&hflags, HASH_VLAN_IPV6); |
| hash_set_enable(&hflags, HASH_NOVLAN_IPV4); |
| hash_set_enable(&hflags, HASH_NOVLAN_IPV6); |
| break; |
| |
| case PROTO_IPV4: |
| hash_set_enable(&hflags, HASH_VLAN_IPV4); |
| hash_set_enable(&hflags, HASH_NOVLAN_IPV4); |
| break; |
| |
| case PROTO_IPV6: |
| hash_set_enable(&hflags, HASH_VLAN_IPV6); |
| hash_set_enable(&hflags, HASH_NOVLAN_IPV6); |
| break; |
| |
| case PROTO_L4: |
| hash_set_enable(&hflags, HASH_VLAN_TCP4); |
| hash_set_enable(&hflags, HASH_VLAN_TCP6); |
| hash_set_enable(&hflags, HASH_VLAN_UDP4); |
| hash_set_enable(&hflags, HASH_VLAN_UDP6); |
| hash_set_enable(&hflags, HASH_VLAN_SCTP4); |
| hash_set_enable(&hflags, HASH_VLAN_SCTP6); |
| hash_set_enable(&hflags, HASH_NOVLAN_TCP4); |
| hash_set_enable(&hflags, HASH_NOVLAN_TCP6); |
| hash_set_enable(&hflags, HASH_NOVLAN_UDP4); |
| hash_set_enable(&hflags, HASH_NOVLAN_UDP6); |
| hash_set_enable(&hflags, HASH_NOVLAN_SCTP4); |
| hash_set_enable(&hflags, HASH_NOVLAN_SCTP6); |
| break; |
| |
| case PROTO_TCP: |
| hash_set_enable(&hflags, HASH_VLAN_TCP4); |
| hash_set_enable(&hflags, HASH_VLAN_TCP6); |
| hash_set_enable(&hflags, HASH_NOVLAN_TCP4); |
| hash_set_enable(&hflags, HASH_NOVLAN_TCP6); |
| break; |
| |
| case PROTO_UDP: |
| hash_set_enable(&hflags, HASH_VLAN_UDP4); |
| hash_set_enable(&hflags, HASH_VLAN_UDP6); |
| hash_set_enable(&hflags, HASH_NOVLAN_UDP4); |
| hash_set_enable(&hflags, HASH_NOVLAN_UDP6); |
| break; |
| |
| case PROTO_SCTP: |
| hash_set_enable(&hflags, HASH_VLAN_SCTP4); |
| hash_set_enable(&hflags, HASH_VLAN_SCTP6); |
| hash_set_enable(&hflags, HASH_NOVLAN_SCTP4); |
| hash_set_enable(&hflags, HASH_NOVLAN_SCTP6); |
| break; |
| |
| default: |
| return -EINVAL; |
| } |
| |
| return 0; |
| } |
| |
| static void __compile_init(struct sock_fprog *fprog) |
| { |
| fprog->len = 0; |
| fprog->filter = NULL; |
| stack_init(); |
| hash_flags_init(&hflags); |
| } |
| |
| void teamd_bpf_desc_compile_start(struct sock_fprog *fprog) |
| { |
| __compile_init(fprog); |
| } |
| |
| void teamd_bpf_desc_compile_release(struct sock_fprog *fprog) |
| { |
| free(fprog->filter); |
| __compile_init(fprog); |
| stack_release(); |
| hash_flags_init(&hflags); |
| } |
| |
| int teamd_bpf_desc_compile_finish(struct sock_fprog *fprog) |
| { |
| return 0; |
| } |
| |
| int teamd_bpf_desc_compile(struct sock_fprog *fprog) |
| { |
| int err; |
| |
| err = bpf_create_code(fprog, &hflags); |
| if (err) |
| return err; |
| |
| err = stack_resolve_offsets(fprog); |
| return err; |
| } |