blob: 2482c10facefb32cefaf19ab5a4ebd5dde7db12a [file] [log] [blame]
/*
* 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;
}