| diff --git a/include/net/netfilter/nf_flow_table.h b/include/net/netfilter/nf_flow_table.h |
| index 8a84de3..1a23c03 100644 |
| --- a/include/net/netfilter/nf_flow_table.h |
| +++ b/include/net/netfilter/nf_flow_table.h |
| @@ -276,6 +276,7 @@ int nf_flow_table_init(struct nf_flowtable *flow_table); |
| void nf_flow_table_free(struct nf_flowtable *flow_table); |
| |
| void flow_offload_teardown(struct flow_offload *flow); |
| +void flow_offload_teardown_by_tuple(struct flow_offload_tuple *tuple); |
| |
| int nf_flow_table_iterate(struct nf_flowtable *flow_table, |
| void (*iter)(struct flow_offload *flow, void *data), |
| diff --git a/include/uapi/linux/netfilter/nfnetlink.h b/include/uapi/linux/netfilter/nfnetlink.h |
| index 5bc960f..603d9c0 100644 |
| --- a/include/uapi/linux/netfilter/nfnetlink.h |
| +++ b/include/uapi/linux/netfilter/nfnetlink.h |
| @@ -60,7 +60,8 @@ struct nfgenmsg { |
| #define NFNL_SUBSYS_CTHELPER 9 |
| #define NFNL_SUBSYS_NFTABLES 10 |
| #define NFNL_SUBSYS_NFT_COMPAT 11 |
| -#define NFNL_SUBSYS_COUNT 12 |
| +#define NFNL_SUBSYS_FLOWTABLE 12 |
| +#define NFNL_SUBSYS_COUNT 13 |
| |
| /* Reserved control nfnetlink messages */ |
| #define NFNL_MSG_BATCH_BEGIN NLMSG_MIN_TYPE |
| diff --git a/net/netfilter/Kconfig b/net/netfilter/Kconfig |
| index 5d690ab..8ec87aa 100644 |
| --- a/net/netfilter/Kconfig |
| +++ b/net/netfilter/Kconfig |
| @@ -708,6 +708,15 @@ config NF_FLOW_TABLE |
| |
| To compile it as a module, choose M here. |
| |
| +config NF_FLOW_TABLE_NETLINK |
| + tristate "Netfilter flow table netlink module" |
| + depends on NETFILTER_INGRESS |
| + depends on NF_CONNTRACK |
| + help |
| + This option adds the flow table core infrastructure. |
| + |
| + To compile it as a module, choose M here. |
| + |
| config NETFILTER_XTABLES |
| tristate "Netfilter Xtables support (required for ip_tables)" |
| default m if NETFILTER_ADVANCED=n |
| diff --git a/net/netfilter/Makefile b/net/netfilter/Makefile |
| index d93a121..fa6ffb1 100644 |
| --- a/net/netfilter/Makefile |
| +++ b/net/netfilter/Makefile |
| @@ -124,6 +124,7 @@ nf_flow_table-objs := nf_flow_table_core.o nf_flow_table_ip.o \ |
| nf_flow_table_offload.o |
| |
| obj-$(CONFIG_NF_FLOW_TABLE_INET) += nf_flow_table_inet.o |
| +obj-$(CONFIG_NF_FLOW_TABLE_NETLINK) += nf_flow_table_netlink.o |
| |
| # generic X tables |
| obj-$(CONFIG_NETFILTER_XTABLES) += x_tables.o xt_tcpudp.o |
| diff --git a/net/netfilter/nf_flow_table_core.c b/net/netfilter/nf_flow_table_core.c |
| index 1036558..a0f52f6 100644 |
| --- a/net/netfilter/nf_flow_table_core.c |
| +++ b/net/netfilter/nf_flow_table_core.c |
| @@ -373,6 +373,29 @@ void flow_offload_teardown(struct flow_offload *flow) |
| } |
| EXPORT_SYMBOL_GPL(flow_offload_teardown); |
| |
| +void flow_offload_teardown_by_tuple(struct flow_offload_tuple *tuple) |
| +{ |
| + struct net_device *netdev; |
| + struct nf_flowtable *flowtable; |
| + struct flow_offload_tuple_rhash *tuplehash; |
| + struct flow_offload *flow; |
| + int dir; |
| + |
| + list_for_each_entry(flowtable, &flowtables, list) { |
| + for_each_netdev(&init_net, netdev) { |
| + tuple->iifidx = netdev->ifindex; |
| + tuplehash = flow_offload_lookup(flowtable, tuple); |
| + if (!tuplehash) |
| + continue; |
| + |
| + dir = tuplehash->tuple.dir; |
| + flow = container_of(tuplehash, struct flow_offload, tuplehash[dir]); |
| + flow_offload_teardown(flow); |
| + } |
| + }; |
| +} |
| +EXPORT_SYMBOL_GPL(flow_offload_teardown_by_tuple); |
| + |
| struct flow_offload_tuple_rhash * |
| flow_offload_lookup(struct nf_flowtable *flow_table, |
| struct flow_offload_tuple *tuple) |
| diff --git a/net/netfilter/nf_flow_table_netlink.c b/net/netfilter/nf_flow_table_netlink.c |
| new file mode 100644 |
| index 0000000..f05f29e |
| --- /dev/null |
| +++ b/net/netfilter/nf_flow_table_netlink.c |
| @@ -0,0 +1,239 @@ |
| +#include <linux/types.h> |
| +#include <linux/kernel.h> |
| +#include <linux/init.h> |
| +#include <linux/module.h> |
| +#include <linux/netfilter.h> |
| +#include <linux/netlink.h> |
| +#include <net/netlink.h> |
| +#include <net/ip.h> |
| +#include <linux/netfilter/nfnetlink.h> |
| +#include <net/netfilter/nf_flow_table.h> |
| + |
| +enum ft_netlink_msg_types { |
| + FT_MSG_DEL, |
| + FT_MSG_ADD, |
| + FT_MSG_FLUSH, |
| + FT_MSG_MAX |
| +}; |
| + |
| +enum ftattr_type { |
| + FTA_UNSPEC, |
| + FTA_TUPLE, |
| + __FTA_MAX |
| +}; |
| +#define FTA_MAX (__FTA_MAX - 1) |
| + |
| +enum ftattr_tuple { |
| + FTA_TUPLE_UNSPEC, |
| + FTA_TUPLE_IP, |
| + FTA_TUPLE_PROTO, |
| + FTA_TUPLE_ZONE, |
| + __FTA_TUPLE_MAX |
| +}; |
| +#define FTA_TUPLE_MAX (__FTA_TUPLE_MAX - 1) |
| + |
| +enum ftattr_ip { |
| + FTA_IP_UNSPEC, |
| + FTA_IP_V4_SRC, |
| + FTA_IP_V4_DST, |
| + __FTA_IP_MAX |
| +}; |
| +#define FTA_IP_MAX (__FTA_IP_MAX - 1) |
| + |
| +enum ftattr_l4proto { |
| + FTA_PROTO_UNSPEC, |
| + FTA_PROTO_NUM, |
| + FTA_PROTO_SPORT, |
| + FTA_PROTO_DPORT, |
| + __FTA_PROTO_MAX |
| +}; |
| +#define FTA_PROTO_MAX (__FTA_PROTO_MAX - 1) |
| + |
| +static const struct nla_policy tuple_nla_policy[FTA_TUPLE_MAX + 1] = { |
| + [FTA_TUPLE_IP] = { .type = NLA_NESTED }, |
| + [FTA_TUPLE_PROTO] = { .type = NLA_NESTED }, |
| + [FTA_TUPLE_ZONE] = { .type = NLA_U16 }, |
| +}; |
| + |
| +static const struct nla_policy ip_nla_policy[FTA_IP_MAX + 1] = { |
| + [FTA_IP_V4_SRC] = { .type = NLA_U32 }, |
| + [FTA_IP_V4_DST] = { .type = NLA_U32 }, |
| +}; |
| + |
| +static const struct nla_policy l4proto_nla_policy[FTA_PROTO_MAX + 1] = { |
| + [FTA_PROTO_NUM] = { .type = NLA_U8 }, |
| + [FTA_PROTO_SPORT] = {.type = NLA_U16}, |
| + [FTA_PROTO_DPORT] = {.type = NLA_U16}, |
| +}; |
| + |
| +static inline int ftnetlink_parse_tuple_ip(struct nlattr *attr, |
| + struct flow_offload_tuple *tuple) |
| +{ |
| + struct nlattr *tb[FTA_IP_MAX+1]; |
| + int err; |
| + |
| + err = nla_parse_nested_deprecated(tb, FTA_IP_MAX, attr, ip_nla_policy, NULL); |
| + |
| + if (err < 0) |
| + return err; |
| + |
| + switch (tuple->l3proto) { |
| + case NFPROTO_IPV4: |
| + if (!tb[FTA_IP_V4_SRC] || !tb[FTA_IP_V4_DST]) |
| + return -EINVAL; |
| + |
| + tuple->src_v4.s_addr = nla_get_in_addr(tb[FTA_IP_V4_SRC]); |
| + tuple->dst_v4.s_addr = nla_get_in_addr(tb[FTA_IP_V4_DST]); |
| + } |
| + |
| + return err; |
| +} |
| + |
| +static inline int ftnetlink_parse_tuple_proto(struct nlattr *attr, |
| + struct flow_offload_tuple *tuple) |
| +{ |
| + struct nlattr *tb[FTA_PROTO_MAX+1]; |
| + int err; |
| + |
| + err = nla_parse_nested_deprecated(tb, FTA_PROTO_MAX, attr, l4proto_nla_policy, NULL); |
| + |
| + if(err < 0) |
| + return err; |
| + |
| + if (!tb[FTA_PROTO_NUM] || !tb[FTA_PROTO_SPORT] || !tb[FTA_PROTO_DPORT]) |
| + return -EINVAL; |
| + |
| + tuple->l4proto = nla_get_u8(tb[FTA_PROTO_NUM]); |
| + tuple->src_port = nla_get_u16(tb[FTA_PROTO_SPORT]); |
| + tuple->dst_port = nla_get_u16(tb[FTA_PROTO_DPORT]); |
| + |
| + return err; |
| +} |
| + |
| +static int ftnetlink_parse_tuple(const struct nlattr * const cda[], |
| + struct flow_offload_tuple *tuple, |
| + int attrtype, int l3proto) |
| +{ |
| + struct nlattr *tb[FTA_TUPLE_MAX+1]; |
| + int err; |
| + |
| + memset(tuple, 0, sizeof(*tuple)); |
| + |
| + err = nla_parse_nested_deprecated(tb, FTA_TUPLE_MAX, cda[attrtype], tuple_nla_policy, NULL); |
| + if (err < 0) |
| + return err; |
| + |
| + if (!tb[FTA_TUPLE_IP]) |
| + return -EINVAL; |
| + |
| + /* parse IP */ |
| + tuple->l3proto = l3proto; |
| + err = ftnetlink_parse_tuple_ip(tb[FTA_TUPLE_IP], tuple); |
| + if (err < 0) |
| + return err; |
| + |
| + /* parse proto */ |
| + if (!tb[FTA_TUPLE_PROTO]) |
| + return -EINVAL; |
| + err = ftnetlink_parse_tuple_proto(tb[FTA_TUPLE_PROTO], tuple); |
| + |
| + if (err >= 0) |
| + printk("tuple info:sip=%pI4,dip=%pI4 proto=%d " |
| + "sport=%d dport=%d\n", |
| + &tuple->src_v4, &tuple->dst_v4, tuple->l4proto, |
| + ntohs(tuple->src_port), ntohs(tuple->dst_port)); |
| + |
| + return err; |
| +} |
| + |
| +static int ftnetlink_del_nf_flow(struct net *net, struct sock *ftnl, struct sk_buff *skb, |
| + const struct nlmsghdr *nlh, |
| + const struct nlattr * const cda[], |
| + struct netlink_ext_ack *extack) |
| +{ |
| + struct net_device *dev = skb->dev; |
| + struct flow_offload_tuple tuple; |
| + int err = -1; |
| + struct nfgenmsg *nfmsg = nlmsg_data(nlh); |
| + u_int8_t u3 = nfmsg->nfgen_family; |
| + |
| + /* parse tuple */ |
| + if(!cda[FTA_TUPLE]) |
| + return -EINVAL; |
| + |
| + err = ftnetlink_parse_tuple(cda, &tuple, FTA_TUPLE, u3); |
| + if (err < 0) |
| + return err; |
| + |
| + /* teardown the flow */ |
| + flow_offload_teardown_by_tuple(&tuple); |
| + |
| + return 0; |
| +} |
| + |
| +static int ftnetlink_add_nf_flow(struct net *net, struct sock *ftnl, struct sk_buff *skb, |
| + const struct nlmsghdr *nlh, |
| + const struct nlattr * const cda[], |
| + struct netlink_ext_ack *extack) |
| +{ |
| + return 0; |
| +} |
| + |
| +static int ftnetlink_flush_table(struct net *net, struct sock *ftnl, struct sk_buff *skb, |
| + const struct nlmsghdr *nlh, |
| + const struct nlattr * const cda[], |
| + struct netlink_ext_ack *extack) |
| +{ |
| + struct net_device *dev = skb->dev; |
| + |
| + nf_flow_table_cleanup(dev); |
| + |
| + return 0; |
| +} |
| + |
| +static const struct nla_policy ft_nla_policy[FTA_MAX + 1] = { |
| + [FTA_TUPLE] = { .type = NLA_NESTED }, |
| +}; |
| + |
| +static const struct nfnl_callback flow_table_cb[FT_MSG_MAX] = { |
| + [FT_MSG_DEL] = { |
| + .call = ftnetlink_del_nf_flow, |
| + .attr_count = FTA_MAX, |
| + .policy = ft_nla_policy |
| + }, |
| + [FT_MSG_ADD] = { |
| + .call = ftnetlink_add_nf_flow, |
| + .attr_count = FTA_MAX, |
| + .policy = ft_nla_policy |
| + }, |
| + [FT_MSG_FLUSH] = { |
| + .call = ftnetlink_flush_table, |
| + .attr_count = FTA_MAX, |
| + .policy = ft_nla_policy |
| + }, |
| +}; |
| + |
| +static const struct nfnetlink_subsystem ftnl_subsys = { |
| + .name = "flowtable", |
| + .subsys_id = NFNL_SUBSYS_FLOWTABLE, |
| + .cb_count = FT_MSG_MAX, |
| + .cb = flow_table_cb, |
| +}; |
| + |
| +static int __init ftnetlink_init(void) |
| +{ |
| + int ret; |
| + |
| + ret = nfnetlink_subsys_register(&ftnl_subsys); |
| + |
| + return ret; |
| +} |
| + |
| +static void ftnetlink_exit(void) |
| +{ |
| + nfnetlink_subsys_unregister(&ftnl_subsys); |
| +} |
| + |
| +MODULE_LICENSE("GPL"); |
| +module_init(ftnetlink_init); |
| +module_exit(ftnetlink_exit); |