[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Message-Id: <20180304194044.26751-4-fw@strlen.de>
Date: Sun, 4 Mar 2018 20:40:44 +0100
From: Florian Westphal <fw@...len.de>
To: <netdev@...r.kernel.org>
Cc: daniel@...earbox.net, ast@...nel.org, pablo@...filter.org,
Florian Westphal <fw@...len.de>
Subject: [RFC,POC 3/3] bpfilter: switch bpfilter to iptables->IMR translation
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 <fw@...len.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
Powered by blists - more mailing lists