From: "David S. Miller" <davem@xxxxxxxxxxxxx> Signed-off-by: David S. Miller <davem@xxxxxxxxxxxxx> Signed-off-by: Alexei Starovoitov <ast@xxxxxxxxxx> --- include/linux/bpfilter.h | 13 +++ include/uapi/linux/bpfilter.h | 200 ++++++++++++++++++++++++++++++++++++++++++ net/Kconfig | 2 + net/Makefile | 1 + net/bpfilter/Kconfig | 7 ++ net/bpfilter/Makefile | 9 ++ net/bpfilter/bpfilter.c | 89 +++++++++++++++++++ net/bpfilter/bpfilter_mod.h | 96 ++++++++++++++++++++ net/bpfilter/ctor.c | 80 +++++++++++++++++ net/bpfilter/init.c | 33 +++++++ net/bpfilter/sockopt.c | 153 ++++++++++++++++++++++++++++++++ net/bpfilter/tables.c | 70 +++++++++++++++ net/bpfilter/targets.c | 51 +++++++++++ net/bpfilter/tgts.c | 25 ++++++ net/ipv4/Makefile | 2 + net/ipv4/bpfilter/Makefile | 2 + net/ipv4/bpfilter/sockopt.c | 49 +++++++++++ net/ipv4/ip_sockglue.c | 17 ++++ 18 files changed, 899 insertions(+) create mode 100644 include/linux/bpfilter.h create mode 100644 include/uapi/linux/bpfilter.h create mode 100644 net/bpfilter/Kconfig create mode 100644 net/bpfilter/Makefile create mode 100644 net/bpfilter/bpfilter.c create mode 100644 net/bpfilter/bpfilter_mod.h create mode 100644 net/bpfilter/ctor.c create mode 100644 net/bpfilter/init.c create mode 100644 net/bpfilter/sockopt.c create mode 100644 net/bpfilter/tables.c create mode 100644 net/bpfilter/targets.c create mode 100644 net/bpfilter/tgts.c create mode 100644 net/ipv4/bpfilter/Makefile create mode 100644 net/ipv4/bpfilter/sockopt.c diff --git a/include/linux/bpfilter.h b/include/linux/bpfilter.h new file mode 100644 index 0000000..26adad1 --- /dev/null +++ b/include/linux/bpfilter.h @@ -0,0 +1,13 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef _LINUX_BPFILTER_H +#define _LINUX_BPFILTER_H + +#include <uapi/linux/bpfilter.h> + +struct sock; +int bpfilter_ip_set_sockopt(struct sock *sk, int optname, char *optval, + unsigned int optlen); +int bpfilter_ip_get_sockopt(struct sock *sk, int optname, char *optval, + int *optlen); +#endif + diff --git a/include/uapi/linux/bpfilter.h b/include/uapi/linux/bpfilter.h new file mode 100644 index 0000000..38d54e9 --- /dev/null +++ b/include/uapi/linux/bpfilter.h @@ -0,0 +1,200 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef _UAPI_LINUX_BPFILTER_H +#define _UAPI_LINUX_BPFILTER_H + +#include <linux/if.h> + +enum { + BPFILTER_IPT_SO_SET_REPLACE = 64, + BPFILTER_IPT_SO_SET_ADD_COUNTERS = 65, + BPFILTER_IPT_SET_MAX, +}; + +enum { + BPFILTER_IPT_SO_GET_INFO = 64, + BPFILTER_IPT_SO_GET_ENTRIES = 65, + BPFILTER_IPT_SO_GET_REVISION_MATCH = 66, + BPFILTER_IPT_SO_GET_REVISION_TARGET = 67, + BPFILTER_IPT_GET_MAX, +}; + +enum { + BPFILTER_XT_TABLE_MAXNAMELEN = 32, +}; + +enum { + BPFILTER_NF_DROP = 0, + BPFILTER_NF_ACCEPT = 1, + BPFILTER_NF_STOLEN = 2, + BPFILTER_NF_QUEUE = 3, + BPFILTER_NF_REPEAT = 4, + BPFILTER_NF_STOP = 5, + BPFILTER_NF_MAX_VERDICT = BPFILTER_NF_STOP, +}; + +enum { + BPFILTER_INET_HOOK_PRE_ROUTING = 0, + BPFILTER_INET_HOOK_LOCAL_IN = 1, + BPFILTER_INET_HOOK_FORWARD = 2, + BPFILTER_INET_HOOK_LOCAL_OUT = 3, + BPFILTER_INET_HOOK_POST_ROUTING = 4, + BPFILTER_INET_HOOK_MAX, +}; + +enum { + BPFILTER_PROTO_UNSPEC = 0, + BPFILTER_PROTO_INET = 1, + BPFILTER_PROTO_IPV4 = 2, + BPFILTER_PROTO_ARP = 3, + BPFILTER_PROTO_NETDEV = 5, + BPFILTER_PROTO_BRIDGE = 7, + BPFILTER_PROTO_IPV6 = 10, + BPFILTER_PROTO_DECNET = 12, + BPFILTER_PROTO_NUMPROTO, +}; + +#ifndef INT_MAX +#define INT_MAX ((int)(~0U>>1)) +#endif +#ifndef INT_MIN +#define INT_MIN (-INT_MAX - 1) +#endif + +enum { + BPFILTER_IP_PRI_FIRST = INT_MIN, + BPFILTER_IP_PRI_CONNTRACK_DEFRAG = -400, + BPFILTER_IP_PRI_RAW = -300, + BPFILTER_IP_PRI_SELINUX_FIRST = -225, + BPFILTER_IP_PRI_CONNTRACK = -200, + BPFILTER_IP_PRI_MANGLE = -150, + BPFILTER_IP_PRI_NAT_DST = -100, + BPFILTER_IP_PRI_FILTER = 0, + BPFILTER_IP_PRI_SECURITY = 50, + BPFILTER_IP_PRI_NAT_SRC = 100, + BPFILTER_IP_PRI_SELINUX_LAST = 225, + BPFILTER_IP_PRI_CONNTRACK_HELPER = 300, + BPFILTER_IP_PRI_CONNTRACK_CONFIRM = INT_MAX, + BPFILTER_IP_PRI_LAST = INT_MAX, +}; + +#define BPFILTER_FUNCTION_MAXNAMELEN 30 +#define BPFILTER_EXTENSION_MAXNAMELEN 29 +#define BPFILTER_TABLE_MAXNAMELEN 32 + +struct bpfilter_match; +struct bpfilter_entry_match { + union { + struct { + __u16 match_size; + char name[BPFILTER_EXTENSION_MAXNAMELEN]; + __u8 revision; + } user; + struct { + __u16 match_size; + struct bpfilter_match *match; + } kernel; + __u16 match_size; + } u; + unsigned char data[0]; +}; + +struct bpfilter_target; +struct bpfilter_entry_target { + union { + struct { + __u16 target_size; + char name[BPFILTER_EXTENSION_MAXNAMELEN]; + __u8 revision; + } user; + struct { + __u16 target_size; + struct bpfilter_target *target; + } kernel; + __u16 target_size; + } u; + unsigned char data[0]; +}; + +struct bpfilter_standard_target { + struct bpfilter_entry_target target; + int verdict; +}; + +struct bpfilter_error_target { + struct bpfilter_entry_target target; + char error_name[BPFILTER_FUNCTION_MAXNAMELEN]; +}; + +#define __ALIGN_KERNEL(x, a) __ALIGN_KERNEL_MASK(x, (typeof(x))(a) - 1) +#define __ALIGN_KERNEL_MASK(x, mask) (((x) + (mask)) & ~(mask)) + +#define BPFILTER_ALIGN(__X) \ + __ALIGN_KERNEL(__X, __alignof__(__u64)) + +#define BPFILTER_TARGET_INIT(__name, __size) \ +{ \ + .target.u.user = { \ + .target_size = BPFILTER_ALIGN(__size), \ + .name = (__name), \ + }, \ +} +#define BPFILTER_STANDARD_TARGET "" +#define BPFILTER_ERROR_TARGET "ERROR" + +struct bpfilter_xt_counters { + __u64 packet_cnt; + __u64 byte_cnt; +}; + +struct bpfilter_ipt_ip { + __u32 src; + __u32 dst; + __u32 src_mask; + __u32 dst_mask; + char in_iface[IFNAMSIZ]; + char out_iface[IFNAMSIZ]; + __u8 in_iface_mask[IFNAMSIZ]; + __u8 out_iface_mask[IFNAMSIZ]; + __u16 protocol; + __u8 flags; + __u8 inv_flags; +}; + +struct bpfilter_ipt_entry { + struct bpfilter_ipt_ip ip; + __u32 bfcache; + __u16 target_offset; + __u16 next_offset; + __u32 camefrom; + struct bpfilter_xt_counters cntrs; + __u8 elems[0]; +}; + +struct bpfilter_ipt_get_info { + char name[BPFILTER_XT_TABLE_MAXNAMELEN]; + __u32 valid_hooks; + __u32 hook_entry[BPFILTER_INET_HOOK_MAX]; + __u32 underflow[BPFILTER_INET_HOOK_MAX]; + __u32 num_entries; + __u32 size; +}; + +struct bpfilter_ipt_get_entries { + char name[BPFILTER_XT_TABLE_MAXNAMELEN]; + __u32 size; + struct bpfilter_ipt_entry entries[0]; +}; + +struct bpfilter_ipt_replace { + char name[BPFILTER_XT_TABLE_MAXNAMELEN]; + __u32 valid_hooks; + __u32 num_entries; + __u32 size; + __u32 hook_entry[BPFILTER_INET_HOOK_MAX]; + __u32 underflow[BPFILTER_INET_HOOK_MAX]; + __u32 num_counters; + struct bpfilter_xt_counters *cntrs; + struct bpfilter_ipt_entry entries[0]; +}; + +#endif /* _UAPI_LINUX_BPFILTER_H */ diff --git a/net/Kconfig b/net/Kconfig index 37ec8e6..ec96506 100644 --- a/net/Kconfig +++ b/net/Kconfig @@ -201,6 +201,8 @@ source "net/bridge/netfilter/Kconfig" endif +source "net/bpfilter/Kconfig" + source "net/dccp/Kconfig" source "net/sctp/Kconfig" source "net/rds/Kconfig" diff --git a/net/Makefile b/net/Makefile index 14fede5..c388b3d 100644 --- a/net/Makefile +++ b/net/Makefile @@ -20,6 +20,7 @@ obj-$(CONFIG_TLS) += tls/ obj-$(CONFIG_XFRM) += xfrm/ obj-$(CONFIG_UNIX) += unix/ obj-$(CONFIG_NET) += ipv6/ +obj-$(CONFIG_BPFILTER) += bpfilter/ obj-$(CONFIG_PACKET) += packet/ obj-$(CONFIG_NET_KEY) += key/ obj-$(CONFIG_BRIDGE) += bridge/ diff --git a/net/bpfilter/Kconfig b/net/bpfilter/Kconfig new file mode 100644 index 0000000..d29b5cb --- /dev/null +++ b/net/bpfilter/Kconfig @@ -0,0 +1,7 @@ +menuconfig BPFILTER + bool "BPF Filter Configuration" + depends on NET && BPF + +if BPFILTER + +endif diff --git a/net/bpfilter/Makefile b/net/bpfilter/Makefile new file mode 100644 index 0000000..5e05505 --- /dev/null +++ b/net/bpfilter/Makefile @@ -0,0 +1,9 @@ +# SPDX-License-Identifier: GPL-2.0 +# +# Makefile for the Linux BPFILTER layer. +# + +hostprogs-y := bpfilter.ko +always := $(hostprogs-y) +bpfilter.ko-objs := bpfilter.o tgts.o targets.o tables.o init.o ctor.o sockopt.o +HOSTCFLAGS += -I. -Itools/include/ diff --git a/net/bpfilter/bpfilter.c b/net/bpfilter/bpfilter.c new file mode 100644 index 0000000..445ae65 --- /dev/null +++ b/net/bpfilter/bpfilter.c @@ -0,0 +1,89 @@ +// SPDX-License-Identifier: GPL-2.0 +#define _GNU_SOURCE +#include <sys/uio.h> +#include <errno.h> +#include <stdio.h> +#include <sys/socket.h> +#include <fcntl.h> +#include <unistd.h> +#include "include/uapi/linux/bpf.h" +#include <asm/unistd.h> +#include "bpfilter_mod.h" + +extern long int syscall (long int __sysno, ...); + +static inline int sys_bpf(enum bpf_cmd cmd, union bpf_attr *attr, + unsigned int size) +{ + return syscall(321, cmd, attr, size); +} + +int pid; +int debug_fd; + +int copy_from_user(void *dst, void *addr, int len) +{ + struct iovec local; + struct iovec remote; + + local.iov_base = dst; + local.iov_len = len; + remote.iov_base = addr; + remote.iov_len = len; + return process_vm_readv(pid, &local, 1, &remote, 1, 0) != len; +} + +int copy_to_user(void *addr, const void *src, int len) +{ + struct iovec local; + struct iovec remote; + + local.iov_base = (void *) src; + local.iov_len = len; + remote.iov_base = addr; + remote.iov_len = len; + return process_vm_writev(pid, &local, 1, &remote, 1, 0) != len; +} + +static int handle_cmd(struct bpfilter_get_cmd *cmd) +{ + pid = cmd->pid; + switch (cmd->cmd) { + case BPFILTER_IPT_SO_GET_INFO: + return bpfilter_get_info((void *) (long) cmd->addr, cmd->len); + case BPFILTER_IPT_SO_GET_ENTRIES: + return bpfilter_get_entries((void *) (long) cmd->addr, cmd->len); + default: + break; + } + return -ENOPROTOOPT; +} + +static void loop(void) +{ + bpfilter_tables_init(); + bpfilter_ipv4_init(); + + while (1) { + union bpf_attr get_cmd = {}; + union bpf_attr reply = {}; + struct bpfilter_get_cmd *cmd; + + sys_bpf(BPFILTER_GET_CMD, &get_cmd, sizeof(get_cmd)); + cmd = &get_cmd.bpfilter_get_cmd; + + dprintf(debug_fd, "pid %d cmd %d addr %llx len %d\n", + cmd->pid, cmd->cmd, cmd->addr, cmd->len); + + reply.bpfilter_reply.status = handle_cmd(cmd); + sys_bpf(BPFILTER_REPLY, &reply, sizeof(reply)); + } +} + +int main(void) +{ + debug_fd = open("/tmp/aa", 00000002 | 00000100); + loop(); + close(debug_fd); + return 0; +} diff --git a/net/bpfilter/bpfilter_mod.h b/net/bpfilter/bpfilter_mod.h new file mode 100644 index 0000000..f0de41b --- /dev/null +++ b/net/bpfilter/bpfilter_mod.h @@ -0,0 +1,96 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef _LINUX_BPFILTER_INTERNAL_H +#define _LINUX_BPFILTER_INTERNAL_H + +#include "include/uapi/linux/bpfilter.h" +#include <linux/list.h> + +struct bpfilter_table { + struct hlist_node hash; + u32 valid_hooks; + struct bpfilter_table_info *info; + int hold; + u8 family; + int priority; + const char name[BPFILTER_XT_TABLE_MAXNAMELEN]; +}; + +struct bpfilter_table_info { + unsigned int size; + u32 num_entries; + unsigned int initial_entries; + unsigned int hook_entry[BPFILTER_INET_HOOK_MAX]; + unsigned int underflow[BPFILTER_INET_HOOK_MAX]; + unsigned int stacksize; + void ***jumpstack; + unsigned char entries[0] __aligned(8); +}; + +struct bpfilter_table *bpfilter_table_get_by_name(const char *name, int name_len); +void bpfilter_table_put(struct bpfilter_table *tbl); +int bpfilter_table_add(struct bpfilter_table *tbl); + +struct bpfilter_ipt_standard { + struct bpfilter_ipt_entry entry; + struct bpfilter_standard_target target; +}; + +struct bpfilter_ipt_error { + struct bpfilter_ipt_entry entry; + struct bpfilter_error_target target; +}; + +#define BPFILTER_IPT_ENTRY_INIT(__sz) \ +{ \ + .target_offset = sizeof(struct bpfilter_ipt_entry), \ + .next_offset = (__sz), \ +} + +#define BPFILTER_IPT_STANDARD_INIT(__verdict) \ +{ \ + .entry = BPFILTER_IPT_ENTRY_INIT(sizeof(struct bpfilter_ipt_standard)), \ + .target = BPFILTER_TARGET_INIT(BPFILTER_STANDARD_TARGET, \ + sizeof(struct bpfilter_standard_target)),\ + .target.verdict = -(__verdict) - 1, \ +} + +#define BPFILTER_IPT_ERROR_INIT \ +{ \ + .entry = BPFILTER_IPT_ENTRY_INIT(sizeof(struct bpfilter_ipt_error)), \ + .target = BPFILTER_TARGET_INIT(BPFILTER_ERROR_TARGET, \ + sizeof(struct bpfilter_error_target)), \ + .target.error_name = "ERROR", \ +} + +struct bpfilter_target { + struct list_head all_target_list; + const char name[BPFILTER_EXTENSION_MAXNAMELEN]; + unsigned int size; + int hold; + u16 family; + u8 rev; +}; + +struct bpfilter_target *bpfilter_target_get_by_name(const char *name); +void bpfilter_target_put(struct bpfilter_target *tgt); +int bpfilter_target_add(struct bpfilter_target *tgt); + +struct bpfilter_table_info *bpfilter_ipv4_table_ctor(struct bpfilter_table *tbl); +int bpfilter_ipv4_register_targets(void); +void bpfilter_tables_init(void); +int bpfilter_get_info(void *addr, int len); +int bpfilter_get_entries(void *cmd, int len); +int bpfilter_ipv4_init(void); + +int copy_from_user(void *dst, void *addr, int len); +int copy_to_user(void *addr, const void *src, int len); +#define put_user(x, ptr) \ +({ \ + __typeof__(*(ptr)) __x = (x); \ + copy_to_user(ptr, &__x, sizeof(*(ptr))); \ +}) +extern int pid; +extern int debug_fd; +#define ENOTSUPP 524 + +#endif diff --git a/net/bpfilter/ctor.c b/net/bpfilter/ctor.c new file mode 100644 index 0000000..efb7fee --- /dev/null +++ b/net/bpfilter/ctor.c @@ -0,0 +1,80 @@ +// SPDX-License-Identifier: GPL-2.0 +#include <sys/socket.h> +#include <linux/bitops.h> +#include <stdlib.h> +#include <stdio.h> +#include "bpfilter_mod.h" + +unsigned int __sw_hweight32(unsigned int w) +{ + w -= (w >> 1) & 0x55555555; + w = (w & 0x33333333) + ((w >> 2) & 0x33333333); + w = (w + (w >> 4)) & 0x0f0f0f0f; + return (w * 0x01010101) >> 24; +} + +struct bpfilter_table_info *bpfilter_ipv4_table_ctor(struct bpfilter_table *tbl) +{ + unsigned int num_hooks = hweight32(tbl->valid_hooks); + struct bpfilter_ipt_standard *tgts; + struct bpfilter_table_info *info; + struct bpfilter_ipt_error *term; + unsigned int mask, offset, h, i; + unsigned int size, alloc_size; + + size = sizeof(struct bpfilter_ipt_standard) * num_hooks; + size += sizeof(struct bpfilter_ipt_error); + + alloc_size = size + sizeof(struct bpfilter_table_info); + + info = malloc(alloc_size); + if (!info) + return NULL; + + info->num_entries = num_hooks + 1; + info->size = size; + + tgts = (struct bpfilter_ipt_standard *) (info + 1); + term = (struct bpfilter_ipt_error *) (tgts + num_hooks); + + mask = tbl->valid_hooks; + offset = 0; + h = 0; + i = 0; + dprintf(debug_fd, "mask %x num_hooks %d\n", mask, num_hooks); + while (mask) { + struct bpfilter_ipt_standard *t; + + if (!(mask & 1)) + goto next; + + info->hook_entry[h] = offset; + info->underflow[h] = offset; + t = &tgts[i++]; + *t = (struct bpfilter_ipt_standard) + BPFILTER_IPT_STANDARD_INIT(BPFILTER_NF_ACCEPT); + t->target.target.u.kernel.target = + bpfilter_target_get_by_name(t->target.target.u.user.name); + dprintf(debug_fd, "user.name %s\n", t->target.target.u.user.name); + if (!t->target.target.u.kernel.target) + goto out_fail; + + offset += sizeof(struct bpfilter_ipt_standard); + next: + mask >>= 1; + h++; + } + *term = (struct bpfilter_ipt_error) BPFILTER_IPT_ERROR_INIT; + term->target.target.u.kernel.target = + bpfilter_target_get_by_name(term->target.target.u.user.name); + dprintf(debug_fd, "user.name %s\n", term->target.target.u.user.name); + if (!term->target.target.u.kernel.target) + goto out_fail; + + dprintf(debug_fd, "info %p\n", info); + return info; + +out_fail: + free(info); + return NULL; +} diff --git a/net/bpfilter/init.c b/net/bpfilter/init.c new file mode 100644 index 0000000..699f3f6 --- /dev/null +++ b/net/bpfilter/init.c @@ -0,0 +1,33 @@ +// SPDX-License-Identifier: GPL-2.0 +#include <sys/socket.h> +#include <errno.h> +#include "bpfilter_mod.h" + +static struct bpfilter_table filter_table_ipv4 = { + .name = "filter", + .valid_hooks = ((1<<BPFILTER_INET_HOOK_LOCAL_IN) | + (1<<BPFILTER_INET_HOOK_FORWARD) | + (1<<BPFILTER_INET_HOOK_LOCAL_OUT)), + .family = BPFILTER_PROTO_IPV4, + .priority = BPFILTER_IP_PRI_FILTER, +}; + +int bpfilter_ipv4_init(void) +{ + struct bpfilter_table *t = &filter_table_ipv4; + struct bpfilter_table_info *info; + int err; + + err = bpfilter_ipv4_register_targets(); + if (err) + return err; + + info = bpfilter_ipv4_table_ctor(t); + if (!info) + return -ENOMEM; + + t->info = info; + + return bpfilter_table_add(&filter_table_ipv4); +} + diff --git a/net/bpfilter/sockopt.c b/net/bpfilter/sockopt.c new file mode 100644 index 0000000..43687da --- /dev/null +++ b/net/bpfilter/sockopt.c @@ -0,0 +1,153 @@ +// SPDX-License-Identifier: GPL-2.0 +#include <sys/socket.h> +#include <errno.h> +#include <string.h> +#include <stdio.h> +#include "bpfilter_mod.h" + +static int fetch_name(void *addr, int len, char *name, int name_len) +{ + if (copy_from_user(name, addr, name_len)) + return -EFAULT; + + name[BPFILTER_XT_TABLE_MAXNAMELEN-1] = '\0'; + return 0; +} + +int bpfilter_get_info(void *addr, int len) +{ + char name[BPFILTER_XT_TABLE_MAXNAMELEN]; + struct bpfilter_ipt_get_info resp; + struct bpfilter_table_info *info; + struct bpfilter_table *tbl; + int err; + + if (len != sizeof(struct bpfilter_ipt_get_info)) + return -EINVAL; + + err = fetch_name(addr, len, name, sizeof(name)); + if (err) + return err; + + tbl = bpfilter_table_get_by_name(name, strlen(name)); + if (!tbl) + return -ENOENT; + + info = tbl->info; + if (!info) { + err = -ENOENT; + goto out_put; + } + + memset(&resp, 0, sizeof(resp)); + memcpy(resp.name, name, sizeof(resp.name)); + resp.valid_hooks = tbl->valid_hooks; + memcpy(&resp.hook_entry, info->hook_entry, sizeof(resp.hook_entry)); + memcpy(&resp.underflow, info->underflow, sizeof(resp.underflow)); + resp.num_entries = info->num_entries; + resp.size = info->size; + + err = 0; + if (copy_to_user(addr, &resp, len)) + err = -EFAULT; +out_put: + bpfilter_table_put(tbl); + return err; +} + +static int copy_target(struct bpfilter_standard_target *ut, + struct bpfilter_standard_target *kt) +{ + struct bpfilter_target *tgt; + int sz; + + + if (put_user(kt->target.u.target_size, + &ut->target.u.target_size)) + return -EFAULT; + + tgt = kt->target.u.kernel.target; + if (copy_to_user(ut->target.u.user.name, tgt->name, strlen(tgt->name))) + return -EFAULT; + + if (put_user(tgt->rev, &ut->target.u.user.revision)) + return -EFAULT; + + sz = tgt->size; + if (copy_to_user(ut->target.data, kt->target.data, sz)) + return -EFAULT; + + return 0; +} + +static int do_get_entries(void *up, + struct bpfilter_table *tbl, + struct bpfilter_table_info *info) +{ + unsigned int total_size = info->size; + const struct bpfilter_ipt_entry *ent; + unsigned int off; + void *base; + + base = info->entries; + + for (off = 0; off < total_size; off += ent->next_offset) { + struct bpfilter_xt_counters *cntrs; + struct bpfilter_standard_target *tgt; + + ent = base + off; + if (copy_to_user(up + off, ent, sizeof(*ent))) + return -EFAULT; + + /* XXX Just clear counters for now. XXX */ + cntrs = up + off + offsetof(struct bpfilter_ipt_entry, cntrs); + if (put_user(0, &cntrs->packet_cnt) || + put_user(0, &cntrs->byte_cnt)) + return -EINVAL; + + tgt = (void *) ent + ent->target_offset; + dprintf(debug_fd, "target.verdict %d\n", tgt->verdict); + if (copy_target(up + off + ent->target_offset, tgt)) + return -EFAULT; + } + return 0; +} + +int bpfilter_get_entries(void *cmd, int len) +{ + struct bpfilter_ipt_get_entries *uptr = cmd; + struct bpfilter_ipt_get_entries req; + struct bpfilter_table_info *info; + struct bpfilter_table *tbl; + int err; + + if (len < sizeof(struct bpfilter_ipt_get_entries)) + return -EINVAL; + + if (copy_from_user(&req, cmd, sizeof(req))) + return -EFAULT; + + tbl = bpfilter_table_get_by_name(req.name, strlen(req.name)); + if (!tbl) + return -ENOENT; + + info = tbl->info; + if (!info) { + err = -ENOENT; + goto out_put; + } + + if (info->size != req.size) { + err = -EINVAL; + goto out_put; + } + + err = do_get_entries(uptr->entries, tbl, info); + dprintf(debug_fd, "do_get_entries %d req.size %d\n", err, req.size); + +out_put: + bpfilter_table_put(tbl); + + return err; +} + diff --git a/net/bpfilter/tables.c b/net/bpfilter/tables.c new file mode 100644 index 0000000..9a96599 --- /dev/null +++ b/net/bpfilter/tables.c @@ -0,0 +1,70 @@ +// SPDX-License-Identifier: GPL-2.0 +#include <sys/socket.h> +#include <errno.h> +#include <string.h> +#include <linux/hashtable.h> +#include "bpfilter_mod.h" + +static unsigned int full_name_hash(const void *salt, const char *name, unsigned int len) +{ + unsigned int hash = 0; + int i; + + for (i = 0; i < len; i++) + hash ^= *(name + i); + return hash; +} + +DEFINE_HASHTABLE(bpfilter_tables, 4); +//DEFINE_MUTEX(bpfilter_table_mutex); + +struct bpfilter_table *bpfilter_table_get_by_name(const char *name, int name_len) +{ + unsigned int hval = full_name_hash(NULL, name, name_len); + struct bpfilter_table *tbl; + +// mutex_lock(&bpfilter_table_mutex); + hash_for_each_possible(bpfilter_tables, tbl, hash, hval) { + if (!strcmp(name, tbl->name)) { + tbl->hold++; + goto out; + } + } + tbl = NULL; +out: +// mutex_unlock(&bpfilter_table_mutex); + return tbl; +} + +void bpfilter_table_put(struct bpfilter_table *tbl) +{ +// mutex_lock(&bpfilter_table_mutex); + tbl->hold--; +// mutex_unlock(&bpfilter_table_mutex); +} + +int bpfilter_table_add(struct bpfilter_table *tbl) +{ + unsigned int hval = full_name_hash(NULL, tbl->name, strlen(tbl->name)); + struct bpfilter_table *srch; + +// mutex_lock(&bpfilter_table_mutex); + hash_for_each_possible(bpfilter_tables, srch, hash, hval) { + if (!strcmp(srch->name, tbl->name)) + goto exists; + } + hash_add(bpfilter_tables, &tbl->hash, hval); +// mutex_unlock(&bpfilter_table_mutex); + + return 0; + +exists: +// mutex_unlock(&bpfilter_table_mutex); + return -EEXIST; +} + +void bpfilter_tables_init(void) +{ + hash_init(bpfilter_tables); +} + diff --git a/net/bpfilter/targets.c b/net/bpfilter/targets.c new file mode 100644 index 0000000..4086ac8 --- /dev/null +++ b/net/bpfilter/targets.c @@ -0,0 +1,51 @@ +// SPDX-License-Identifier: GPL-2.0 +#include <sys/socket.h> +#include <errno.h> +#include <string.h> +#include "bpfilter_mod.h" + +//DEFINE_MUTEX(bpfilter_target_mutex); +static LIST_HEAD(bpfilter_targets); + +struct bpfilter_target *bpfilter_target_get_by_name(const char *name) +{ + struct bpfilter_target *tgt; + +// mutex_lock(&bpfilter_target_mutex); + list_for_each_entry(tgt, &bpfilter_targets, all_target_list) { + if (!strcmp(tgt->name, name)) { + tgt->hold++; + goto out; + } + } + tgt = NULL; +out: +// mutex_unlock(&bpfilter_target_mutex); + return tgt; +} + +void bpfilter_target_put(struct bpfilter_target *tgt) +{ +// mutex_lock(&bpfilter_target_mutex); + tgt->hold--; +// mutex_unlock(&bpfilter_target_mutex); +} + +int bpfilter_target_add(struct bpfilter_target *tgt) +{ + struct bpfilter_target *srch; + +// mutex_lock(&bpfilter_target_mutex); + list_for_each_entry(srch, &bpfilter_targets, all_target_list) { + if (!strcmp(srch->name, tgt->name)) + goto exists; + } + list_add_tail(&tgt->all_target_list, &bpfilter_targets); +// mutex_unlock(&bpfilter_target_mutex); + return 0; + +exists: +// mutex_unlock(&bpfilter_target_mutex); + return -EEXIST; +} + diff --git a/net/bpfilter/tgts.c b/net/bpfilter/tgts.c new file mode 100644 index 0000000..eac5e8a --- /dev/null +++ b/net/bpfilter/tgts.c @@ -0,0 +1,25 @@ +// SPDX-License-Identifier: GPL-2.0 +#include <sys/socket.h> +#include "bpfilter_mod.h" + +struct bpfilter_target std_tgt = { + .name = BPFILTER_STANDARD_TARGET, + .family = BPFILTER_PROTO_IPV4, + .size = sizeof(int), +}; + +struct bpfilter_target err_tgt = { + .name = BPFILTER_ERROR_TARGET, + .family = BPFILTER_PROTO_IPV4, + .size = BPFILTER_FUNCTION_MAXNAMELEN, +}; + +int bpfilter_ipv4_register_targets(void) +{ + int err = bpfilter_target_add(&std_tgt); + + if (err) + return err; + return bpfilter_target_add(&err_tgt); +} + diff --git a/net/ipv4/Makefile b/net/ipv4/Makefile index 47a0a66..ed5f53b 100644 --- a/net/ipv4/Makefile +++ b/net/ipv4/Makefile @@ -15,6 +15,8 @@ obj-y := route.o inetpeer.o protocol.o \ fib_frontend.o fib_semantics.o fib_trie.o fib_notifier.o \ inet_fragment.o ping.o ip_tunnel_core.o gre_offload.o +obj-$(CONFIG_BPFILTER) += bpfilter/ + obj-$(CONFIG_NET_IP_TUNNEL) += ip_tunnel.o obj-$(CONFIG_SYSCTL) += sysctl_net_ipv4.o obj-$(CONFIG_PROC_FS) += proc.o diff --git a/net/ipv4/bpfilter/Makefile b/net/ipv4/bpfilter/Makefile new file mode 100644 index 0000000..ce262d7 --- /dev/null +++ b/net/ipv4/bpfilter/Makefile @@ -0,0 +1,2 @@ +obj-$(CONFIG_BPFILTER) += sockopt.o + diff --git a/net/ipv4/bpfilter/sockopt.c b/net/ipv4/bpfilter/sockopt.c new file mode 100644 index 0000000..26e544f --- /dev/null +++ b/net/ipv4/bpfilter/sockopt.c @@ -0,0 +1,49 @@ +#include <linux/uaccess.h> +#include <linux/bpfilter.h> +#include <uapi/linux/bpf.h> +#include <linux/wait.h> +#include <linux/kmod.h> +struct sock; + +extern struct wait_queue_head bpfilter_get_cmd_wq; +extern struct wait_queue_head bpfilter_reply_wq; +extern bool bpfilter_get_cmd_ready; +extern bool bpfilter_reply_ready; +extern struct bpfilter_get_cmd bpfilter_get_cmd_mbox; +extern struct bpfilter_reply bpfilter_reply_mbox; + +bool loaded = false; + +int bpfilter_ip_set_sockopt(struct sock *sk, int optname, char __user *optval, + unsigned int optlen) +{ + int err; + + if (!loaded) { + err = request_module("bpfilter"); + printk("request_module %d\n", err); +// if (err) +// return err; + loaded = true; + } + bpfilter_get_cmd_mbox.pid = current->pid; + bpfilter_get_cmd_mbox.cmd = optname; + bpfilter_get_cmd_mbox.addr = (long) optval; + bpfilter_get_cmd_mbox.len = optlen; + bpfilter_get_cmd_ready = true; + wake_up(&bpfilter_get_cmd_wq); + wait_event_killable(bpfilter_reply_wq, bpfilter_reply_ready); + bpfilter_reply_ready = false; + return bpfilter_reply_mbox.status; +} + +int bpfilter_ip_get_sockopt(struct sock *sk, int optname, char __user *optval, + int __user *optlen) +{ + int len; + + if (get_user(len, optlen)) + return -EFAULT; + + return bpfilter_ip_set_sockopt(sk, optname, optval, len); +} diff --git a/net/ipv4/ip_sockglue.c b/net/ipv4/ip_sockglue.c index 6cc70fa..439c1b9 100644 --- a/net/ipv4/ip_sockglue.c +++ b/net/ipv4/ip_sockglue.c @@ -47,6 +47,8 @@ #include <linux/errqueue.h> #include <linux/uaccess.h> +#include <linux/bpfilter.h> + /* * SOL_IP control messages. */ @@ -1250,6 +1252,11 @@ int ip_setsockopt(struct sock *sk, int level, return -ENOPROTOOPT; err = do_ip_setsockopt(sk, level, optname, optval, optlen); +#ifdef CONFIG_BPFILTER + if (optname >= BPFILTER_IPT_SO_SET_REPLACE && + optname < BPFILTER_IPT_SET_MAX) + err = bpfilter_ip_set_sockopt(sk, optname, optval, optlen); +#endif #ifdef CONFIG_NETFILTER /* we need to exclude all possible ENOPROTOOPTs except default case */ if (err == -ENOPROTOOPT && optname != IP_HDRINCL && @@ -1564,6 +1571,11 @@ int ip_getsockopt(struct sock *sk, int level, int err; err = do_ip_getsockopt(sk, level, optname, optval, optlen, 0); +#ifdef CONFIG_BPFILTER + if (optname >= BPFILTER_IPT_SO_GET_INFO && + optname < BPFILTER_IPT_GET_MAX) + err = bpfilter_ip_get_sockopt(sk, optname, optval, optlen); +#endif #ifdef CONFIG_NETFILTER /* we need to exclude all possible ENOPROTOOPTs except default case */ if (err == -ENOPROTOOPT && optname != IP_PKTOPTIONS && @@ -1599,6 +1611,11 @@ int compat_ip_getsockopt(struct sock *sk, int level, int optname, err = do_ip_getsockopt(sk, level, optname, optval, optlen, MSG_CMSG_COMPAT); +#ifdef CONFIG_BPFILTER + if (optname >= BPFILTER_IPT_SO_GET_INFO && + optname < BPFILTER_IPT_GET_MAX) + err = bpfilter_ip_get_sockopt(sk, optname, optval, optlen); +#endif #ifdef CONFIG_NETFILTER /* we need to exclude all possible ENOPROTOOPTs except default case */ if (err == -ENOPROTOOPT && optname != IP_PKTOPTIONS && -- 2.9.5 -- To unsubscribe from this list: send the line "unsubscribe netfilter-devel" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html