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

Reply via email to