Translate basic iptables rule blob to the IMR, then ask IMR to translate to ebpf.
IMR is shared between nft and bpfilter translators. iptables_gen_append() is the only relevant function here, as it demonstrates simple 'source/destination matches x' test. Signed-off-by: Florian Westphal <f...@strlen.de> --- net/bpfilter/Makefile | 2 +- net/bpfilter/bpfilter_gen.h | 15 +++++++++ net/bpfilter/bpfilter_mod.h | 16 +--------- net/bpfilter/iptables.c | 76 +++++++++++++++++++++++++++++++++++++++++++++ net/bpfilter/iptables.h | 4 +++ net/bpfilter/sockopt.c | 73 +++++++++++++++++++++++++++++++++---------- 6 files changed, 154 insertions(+), 32 deletions(-) create mode 100644 net/bpfilter/bpfilter_gen.h create mode 100644 net/bpfilter/iptables.c create mode 100644 net/bpfilter/iptables.h diff --git a/net/bpfilter/Makefile b/net/bpfilter/Makefile index a4064986dc2f..21a8afb60b7c 100644 --- a/net/bpfilter/Makefile +++ b/net/bpfilter/Makefile @@ -5,7 +5,7 @@ hostprogs-y := nftjit.ko bpfilter.ko always := $(hostprogs-y) -bpfilter.ko-objs := bpfilter.o tgts.o targets.o tables.o init.o ctor.o sockopt.o gen.o +bpfilter.ko-objs := bpfilter.o tgts.o targets.o tables.o init.o ctor.o sockopt.o gen.o iptables.o imr.o NFT_LIBS = -lnftnl nftjit.ko-objs := tgts.o targets.o tables.o init.o ctor.o gen.o nftables.o imr.o diff --git a/net/bpfilter/bpfilter_gen.h b/net/bpfilter/bpfilter_gen.h new file mode 100644 index 000000000000..71c6e8a73e24 --- /dev/null +++ b/net/bpfilter/bpfilter_gen.h @@ -0,0 +1,15 @@ +struct bpfilter_gen_ctx { + struct bpf_insn *img; + u32 len_cur; + u32 len_max; + u32 default_verdict; + int fd; + int ifindex; + bool offloaded; +}; + +int bpfilter_gen_init(struct bpfilter_gen_ctx *ctx); +int bpfilter_gen_prologue(struct bpfilter_gen_ctx *ctx); +int bpfilter_gen_epilogue(struct bpfilter_gen_ctx *ctx); +int bpfilter_gen_commit(struct bpfilter_gen_ctx *ctx); +void bpfilter_gen_destroy(struct bpfilter_gen_ctx *ctx); diff --git a/net/bpfilter/bpfilter_mod.h b/net/bpfilter/bpfilter_mod.h index b4209985efff..dc3a90df1788 100644 --- a/net/bpfilter/bpfilter_mod.h +++ b/net/bpfilter/bpfilter_mod.h @@ -4,6 +4,7 @@ #include "include/uapi/linux/bpfilter.h" #include <linux/list.h> +#include "bpfilter_gen.h" struct bpfilter_table { struct hlist_node hash; @@ -71,26 +72,11 @@ struct bpfilter_target { u8 rev; }; -struct bpfilter_gen_ctx { - struct bpf_insn *img; - u32 len_cur; - u32 len_max; - u32 default_verdict; - int fd; - int ifindex; - bool offloaded; -}; - union bpf_attr; int sys_bpf(int cmd, union bpf_attr *attr, unsigned int size); -int bpfilter_gen_init(struct bpfilter_gen_ctx *ctx); -int bpfilter_gen_prologue(struct bpfilter_gen_ctx *ctx); -int bpfilter_gen_epilogue(struct bpfilter_gen_ctx *ctx); int bpfilter_gen_append(struct bpfilter_gen_ctx *ctx, struct bpfilter_ipt_ip *ent, int verdict); -int bpfilter_gen_commit(struct bpfilter_gen_ctx *ctx); -void bpfilter_gen_destroy(struct bpfilter_gen_ctx *ctx); struct bpfilter_target *bpfilter_target_get_by_name(const char *name); void bpfilter_target_put(struct bpfilter_target *tgt); diff --git a/net/bpfilter/iptables.c b/net/bpfilter/iptables.c new file mode 100644 index 000000000000..055cfa8fbf21 --- /dev/null +++ b/net/bpfilter/iptables.c @@ -0,0 +1,76 @@ +#include <string.h> +#include <stdint.h> + +typedef uint16_t __sum16; /* hack */ +#include <linux/ip.h> + +#include "bpfilter_mod.h" +#include "iptables.h" +#include "imr.h" + +static int check_entry(const struct bpfilter_ipt_ip *ent) +{ +#define M_FF "\xff\xff\xff\xff" + static const __u8 mask1[IFNAMSIZ] = M_FF M_FF M_FF M_FF; + static const __u8 mask0[IFNAMSIZ] = { }; + int ones = strlen(ent->in_iface); ones += ones > 0; +#undef M_FF + if (strlen(ent->out_iface) > 0) + return -ENOTSUPP; + if (memcmp(ent->in_iface_mask, mask1, ones) || + memcmp(&ent->in_iface_mask[ones], mask0, sizeof(mask0) - ones)) + return -ENOTSUPP; + if ((ent->src_mask != 0 && ent->src_mask != 0xffffffff) || + (ent->dst_mask != 0 && ent->dst_mask != 0xffffffff)) + return -ENOTSUPP; + + return 0; +} + +int iptables_gen_append(struct imr_state *state, + struct bpfilter_ipt_ip *ent, int verdict) +{ + struct imr_object *left, *right, *relop; + int ret; + + ret = check_entry(ent); + if (ret < 0) + return ret; + if (ent->src_mask == 0 && ent->dst_mask == 0) + return 0; + + imr_state_rule_begin(state); + + if (ent->src_mask) { + left = imr_object_alloc_payload(IMR_PAYLOAD_BASE_NH, + offsetof(struct iphdr, saddr), + sizeof(uint32_t)); + right = imr_object_alloc_imm32(ent->src); + + relop = imr_object_alloc_relational(IMR_RELOP_EQ, left, right); + imr_state_add_obj(state, relop); + } + + if (ent->dst_mask) { + left = imr_object_alloc_payload(IMR_PAYLOAD_BASE_NH, + offsetof(struct iphdr, daddr), + sizeof(uint32_t)); + right = imr_object_alloc_imm32(ent->dst); + + relop = imr_object_alloc_relational(IMR_RELOP_EQ, left, right); + imr_state_add_obj(state, relop); + } + + switch (verdict) { + case -1: + verdict = IMR_VERDICT_DROP; + break; + default: + verdict = IMR_VERDICT_PASS; + break; + } + + imr_state_add_obj(state, imr_object_alloc_verdict(verdict)); + + return imr_state_rule_end(state); +} diff --git a/net/bpfilter/iptables.h b/net/bpfilter/iptables.h new file mode 100644 index 000000000000..8d1299f10e55 --- /dev/null +++ b/net/bpfilter/iptables.h @@ -0,0 +1,4 @@ +#include "imr.h" + +int iptables_gen_append(struct imr_state *state, + struct bpfilter_ipt_ip *ent, int verdict); diff --git a/net/bpfilter/sockopt.c b/net/bpfilter/sockopt.c index 26ad12a11736..c601fa0839a1 100644 --- a/net/bpfilter/sockopt.c +++ b/net/bpfilter/sockopt.c @@ -5,8 +5,13 @@ #include <stdlib.h> #include <sys/socket.h> +#include <net/if.h> + +#include <linux/if_ether.h> +#include <arpa/inet.h> #include "bpfilter_mod.h" +#include "iptables.h" /* TODO: Get all of this in here properly done in encoding/decoding layer. */ static int fetch_name(void *addr, int len, char *name, int name_len) @@ -144,22 +149,53 @@ int bpfilter_get_entries(void *cmd, int len) return err; } +/* We run via XDP so skip non ip ethernet frames */ +static int iptables_accept_non_ipv4(struct imr_state *state) +{ + struct imr_object *eth_p_ip, *lltype, *relop; + int ret; + + imr_state_rule_begin(state); + lltype = imr_object_alloc_payload(IMR_PAYLOAD_BASE_LL, + offsetof(struct ethhdr, h_proto), + sizeof(uint16_t)); + if (!lltype) + return -ENOMEM; + + eth_p_ip = imr_object_alloc_imm32(htons(ETH_P_IP)); + if (!eth_p_ip) { + imr_object_free(lltype); + return -ENOMEM; + } + + relop = imr_object_alloc_relational(IMR_RELOP_NE, lltype, eth_p_ip); + if (!relop) { + imr_object_free(eth_p_ip); + imr_object_free(lltype); + return -ENOMEM; + } + + ret = imr_state_add_obj(state, relop); + if (ret == 0) { + ret = imr_state_add_obj(state, imr_object_alloc_verdict(IMR_VERDICT_PASS)); + if (ret == 0) + return imr_state_rule_end(state); + } + + return ret; +} + static int do_set_replace(struct bpfilter_ipt_replace *req, void *base, struct bpfilter_table *tbl) { unsigned int total_size = req->size; struct bpfilter_table_info *info; struct bpfilter_ipt_entry *ent; - struct bpfilter_gen_ctx ctx; + struct imr_state *imr_state; unsigned int off, sents = 0, ents = 0; + int last_iface = 0; int ret; - ret = bpfilter_gen_init(&ctx); - if (ret < 0) - return ret; - ret = bpfilter_gen_prologue(&ctx); - if (ret < 0) - return ret; info = bpfilter_ipv4_table_alloc(tbl, total_size); if (!info) return -ENOMEM; @@ -167,15 +203,25 @@ static int do_set_replace(struct bpfilter_ipt_replace *req, void *base, free(info); return -EFAULT; } + imr_state = imr_state_alloc(); base = &info->entries[0]; + iptables_accept_non_ipv4(imr_state); for (off = 0; off < total_size; off += ent->next_offset) { struct bpfilter_standard_target *tgt; + int ifindex; ent = base + off; + ifindex = if_nametoindex(ent->ip.in_iface); + if (!ifindex) + continue; + if (last_iface && last_iface != ifindex) + return -ENOTSUPP; + else + last_iface = ifindex; ents++; sents += ent->next_offset; tgt = (void *) ent + ent->target_offset; target_u2k(tgt); - ret = bpfilter_gen_append(&ctx, &ent->ip, tgt->verdict); + ret = iptables_gen_append(imr_state, &ent->ip, tgt->verdict); if (ret < 0) goto err; } @@ -183,16 +229,11 @@ static int do_set_replace(struct bpfilter_ipt_replace *req, void *base, info->size = sents; memcpy(info->hook_entry, req->hook_entry, sizeof(info->hook_entry)); memcpy(info->underflow, req->underflow, sizeof(info->hook_entry)); - ret = bpfilter_gen_epilogue(&ctx); - if (ret < 0) - goto err; - ret = bpfilter_gen_commit(&ctx); - if (ret < 0) - goto err; + ret = imr_do_bpf(imr_state); + imr_state_free(imr_state); free(tbl->info); tbl->info = info; - bpfilter_gen_destroy(&ctx); - dprintf(debug_fd, "offloaded %u\n", ctx.offloaded); + return ret; err: free(info); -- 2.16.1