12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079 |
- From: Pablo Neira Ayuso <pablo@netfilter.org>
- Date: Sun, 7 Jan 2018 01:04:07 +0100
- Subject: [PATCH] netfilter: nf_tables: add flow table netlink frontend
- This patch introduces a netlink control plane to create, delete and dump
- flow tables. Flow tables are identified by name, this name is used from
- rules to refer to an specific flow table. Flow tables use the rhashtable
- class and a generic garbage collector to remove expired entries.
- This also adds the infrastructure to add different flow table types, so
- we can add one for each layer 3 protocol family.
- Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
- ---
- create mode 100644 include/net/netfilter/nf_flow_table.h
- --- /dev/null
- +++ b/include/net/netfilter/nf_flow_table.h
- @@ -0,0 +1,23 @@
- +#ifndef _NF_FLOW_TABLE_H
- +#define _NF_FLOW_TABLE_H
- +
- +#include <linux/rhashtable.h>
- +
- +struct nf_flowtable;
- +
- +struct nf_flowtable_type {
- + struct list_head list;
- + int family;
- + void (*gc)(struct work_struct *work);
- + const struct rhashtable_params *params;
- + nf_hookfn *hook;
- + struct module *owner;
- +};
- +
- +struct nf_flowtable {
- + struct rhashtable rhashtable;
- + const struct nf_flowtable_type *type;
- + struct delayed_work gc_work;
- +};
- +
- +#endif /* _FLOW_OFFLOAD_H */
- --- a/include/net/netfilter/nf_tables.h
- +++ b/include/net/netfilter/nf_tables.h
- @@ -9,6 +9,7 @@
- #include <linux/netfilter/x_tables.h>
- #include <linux/netfilter/nf_tables.h>
- #include <linux/u64_stats_sync.h>
- +#include <net/netfilter/nf_flow_table.h>
- #include <net/netlink.h>
-
- #define NFT_JUMP_STACK_SIZE 16
- @@ -941,6 +942,7 @@ unsigned int nft_do_chain(struct nft_pkt
- * @chains: chains in the table
- * @sets: sets in the table
- * @objects: stateful objects in the table
- + * @flowtables: flow tables in the table
- * @hgenerator: handle generator state
- * @use: number of chain references to this table
- * @flags: table flag (see enum nft_table_flags)
- @@ -952,6 +954,7 @@ struct nft_table {
- struct list_head chains;
- struct list_head sets;
- struct list_head objects;
- + struct list_head flowtables;
- u64 hgenerator;
- u32 use;
- u16 flags:14,
- @@ -1083,6 +1086,44 @@ int nft_register_obj(struct nft_object_t
- void nft_unregister_obj(struct nft_object_type *obj_type);
-
- /**
- + * struct nft_flowtable - nf_tables flow table
- + *
- + * @list: flow table list node in table list
- + * @table: the table the flow table is contained in
- + * @name: name of this flow table
- + * @hooknum: hook number
- + * @priority: hook priority
- + * @ops_len: number of hooks in array
- + * @genmask: generation mask
- + * @use: number of references to this flow table
- + * @data: rhashtable and garbage collector
- + * @ops: array of hooks
- + */
- +struct nft_flowtable {
- + struct list_head list;
- + struct nft_table *table;
- + char *name;
- + int hooknum;
- + int priority;
- + int ops_len;
- + u32 genmask:2,
- + use:30;
- + /* runtime data below here */
- + struct nf_hook_ops *ops ____cacheline_aligned;
- + struct nf_flowtable data;
- +};
- +
- +struct nft_flowtable *nf_tables_flowtable_lookup(const struct nft_table *table,
- + const struct nlattr *nla,
- + u8 genmask);
- +void nft_flow_table_iterate(struct net *net,
- + void (*iter)(struct nf_flowtable *flowtable, void *data),
- + void *data);
- +
- +void nft_register_flowtable_type(struct nf_flowtable_type *type);
- +void nft_unregister_flowtable_type(struct nf_flowtable_type *type);
- +
- +/**
- * struct nft_traceinfo - nft tracing information and state
- *
- * @pkt: pktinfo currently processed
- @@ -1318,4 +1359,11 @@ struct nft_trans_obj {
- #define nft_trans_obj(trans) \
- (((struct nft_trans_obj *)trans->data)->obj)
-
- +struct nft_trans_flowtable {
- + struct nft_flowtable *flowtable;
- +};
- +
- +#define nft_trans_flowtable(trans) \
- + (((struct nft_trans_flowtable *)trans->data)->flowtable)
- +
- #endif /* _NET_NF_TABLES_H */
- --- a/include/uapi/linux/netfilter/nf_tables.h
- +++ b/include/uapi/linux/netfilter/nf_tables.h
- @@ -92,6 +92,9 @@ enum nft_verdicts {
- * @NFT_MSG_GETOBJ: get a stateful object (enum nft_obj_attributes)
- * @NFT_MSG_DELOBJ: delete a stateful object (enum nft_obj_attributes)
- * @NFT_MSG_GETOBJ_RESET: get and reset a stateful object (enum nft_obj_attributes)
- + * @NFT_MSG_NEWFLOWTABLE: add new flow table (enum nft_flowtable_attributes)
- + * @NFT_MSG_GETFLOWTABLE: get flow table (enum nft_flowtable_attributes)
- + * @NFT_MSG_DELFLOWTABLE: delete flow table (enum nft_flowtable_attributes)
- */
- enum nf_tables_msg_types {
- NFT_MSG_NEWTABLE,
- @@ -116,6 +119,9 @@ enum nf_tables_msg_types {
- NFT_MSG_GETOBJ,
- NFT_MSG_DELOBJ,
- NFT_MSG_GETOBJ_RESET,
- + NFT_MSG_NEWFLOWTABLE,
- + NFT_MSG_GETFLOWTABLE,
- + NFT_MSG_DELFLOWTABLE,
- NFT_MSG_MAX,
- };
-
- @@ -1310,6 +1316,53 @@ enum nft_object_attributes {
- #define NFTA_OBJ_MAX (__NFTA_OBJ_MAX - 1)
-
- /**
- + * enum nft_flowtable_attributes - nf_tables flow table netlink attributes
- + *
- + * @NFTA_FLOWTABLE_TABLE: name of the table containing the expression (NLA_STRING)
- + * @NFTA_FLOWTABLE_NAME: name of this flow table (NLA_STRING)
- + * @NFTA_FLOWTABLE_HOOK: netfilter hook configuration(NLA_U32)
- + * @NFTA_FLOWTABLE_USE: number of references to this flow table (NLA_U32)
- + */
- +enum nft_flowtable_attributes {
- + NFTA_FLOWTABLE_UNSPEC,
- + NFTA_FLOWTABLE_TABLE,
- + NFTA_FLOWTABLE_NAME,
- + NFTA_FLOWTABLE_HOOK,
- + NFTA_FLOWTABLE_USE,
- + __NFTA_FLOWTABLE_MAX
- +};
- +#define NFTA_FLOWTABLE_MAX (__NFTA_FLOWTABLE_MAX - 1)
- +
- +/**
- + * enum nft_flowtable_hook_attributes - nf_tables flow table hook netlink attributes
- + *
- + * @NFTA_FLOWTABLE_HOOK_NUM: netfilter hook number (NLA_U32)
- + * @NFTA_FLOWTABLE_HOOK_PRIORITY: netfilter hook priority (NLA_U32)
- + * @NFTA_FLOWTABLE_HOOK_DEVS: input devices this flow table is bound to (NLA_NESTED)
- + */
- +enum nft_flowtable_hook_attributes {
- + NFTA_FLOWTABLE_HOOK_UNSPEC,
- + NFTA_FLOWTABLE_HOOK_NUM,
- + NFTA_FLOWTABLE_HOOK_PRIORITY,
- + NFTA_FLOWTABLE_HOOK_DEVS,
- + __NFTA_FLOWTABLE_HOOK_MAX
- +};
- +#define NFTA_FLOWTABLE_HOOK_MAX (__NFTA_FLOWTABLE_HOOK_MAX - 1)
- +
- +/**
- + * enum nft_device_attributes - nf_tables device netlink attributes
- + *
- + * @NFTA_DEVICE_NAME: name of this device (NLA_STRING)
- + */
- +enum nft_devices_attributes {
- + NFTA_DEVICE_UNSPEC,
- + NFTA_DEVICE_NAME,
- + __NFTA_DEVICE_MAX
- +};
- +#define NFTA_DEVICE_MAX (__NFTA_DEVICE_MAX - 1)
- +
- +
- +/**
- * enum nft_trace_attributes - nf_tables trace netlink attributes
- *
- * @NFTA_TRACE_TABLE: name of the table (NLA_STRING)
- --- a/net/netfilter/nf_tables_api.c
- +++ b/net/netfilter/nf_tables_api.c
- @@ -17,6 +17,7 @@
- #include <linux/netfilter.h>
- #include <linux/netfilter/nfnetlink.h>
- #include <linux/netfilter/nf_tables.h>
- +#include <net/netfilter/nf_flow_table.h>
- #include <net/netfilter/nf_tables_core.h>
- #include <net/netfilter/nf_tables.h>
- #include <net/net_namespace.h>
- @@ -24,6 +25,7 @@
-
- static LIST_HEAD(nf_tables_expressions);
- static LIST_HEAD(nf_tables_objects);
- +static LIST_HEAD(nf_tables_flowtables);
-
- /**
- * nft_register_afinfo - register nf_tables address family info
- @@ -389,6 +391,40 @@ static int nft_delobj(struct nft_ctx *ct
- return err;
- }
-
- +static int nft_trans_flowtable_add(struct nft_ctx *ctx, int msg_type,
- + struct nft_flowtable *flowtable)
- +{
- + struct nft_trans *trans;
- +
- + trans = nft_trans_alloc(ctx, msg_type,
- + sizeof(struct nft_trans_flowtable));
- + if (trans == NULL)
- + return -ENOMEM;
- +
- + if (msg_type == NFT_MSG_NEWFLOWTABLE)
- + nft_activate_next(ctx->net, flowtable);
- +
- + nft_trans_flowtable(trans) = flowtable;
- + list_add_tail(&trans->list, &ctx->net->nft.commit_list);
- +
- + return 0;
- +}
- +
- +static int nft_delflowtable(struct nft_ctx *ctx,
- + struct nft_flowtable *flowtable)
- +{
- + int err;
- +
- + err = nft_trans_flowtable_add(ctx, NFT_MSG_DELFLOWTABLE, flowtable);
- + if (err < 0)
- + return err;
- +
- + nft_deactivate_next(ctx->net, flowtable);
- + ctx->table->use--;
- +
- + return err;
- +}
- +
- /*
- * Tables
- */
- @@ -772,6 +808,7 @@ static int nf_tables_newtable(struct net
- INIT_LIST_HEAD(&table->chains);
- INIT_LIST_HEAD(&table->sets);
- INIT_LIST_HEAD(&table->objects);
- + INIT_LIST_HEAD(&table->flowtables);
- table->flags = flags;
-
- nft_ctx_init(&ctx, net, skb, nlh, afi, table, NULL, nla);
- @@ -793,10 +830,11 @@ err1:
-
- static int nft_flush_table(struct nft_ctx *ctx)
- {
- - int err;
- + struct nft_flowtable *flowtable, *nft;
- struct nft_chain *chain, *nc;
- struct nft_object *obj, *ne;
- struct nft_set *set, *ns;
- + int err;
-
- list_for_each_entry(chain, &ctx->table->chains, list) {
- if (!nft_is_active_next(ctx->net, chain))
- @@ -822,6 +860,12 @@ static int nft_flush_table(struct nft_ct
- goto out;
- }
-
- + list_for_each_entry_safe(flowtable, nft, &ctx->table->flowtables, list) {
- + err = nft_delflowtable(ctx, flowtable);
- + if (err < 0)
- + goto out;
- + }
- +
- list_for_each_entry_safe(obj, ne, &ctx->table->objects, list) {
- err = nft_delobj(ctx, obj);
- if (err < 0)
- @@ -4863,6 +4907,605 @@ static void nf_tables_obj_notify(const s
- ctx->afi->family, ctx->report, GFP_KERNEL);
- }
-
- +/*
- + * Flow tables
- + */
- +void nft_register_flowtable_type(struct nf_flowtable_type *type)
- +{
- + nfnl_lock(NFNL_SUBSYS_NFTABLES);
- + list_add_tail_rcu(&type->list, &nf_tables_flowtables);
- + nfnl_unlock(NFNL_SUBSYS_NFTABLES);
- +}
- +EXPORT_SYMBOL_GPL(nft_register_flowtable_type);
- +
- +void nft_unregister_flowtable_type(struct nf_flowtable_type *type)
- +{
- + nfnl_lock(NFNL_SUBSYS_NFTABLES);
- + list_del_rcu(&type->list);
- + nfnl_unlock(NFNL_SUBSYS_NFTABLES);
- +}
- +EXPORT_SYMBOL_GPL(nft_unregister_flowtable_type);
- +
- +static const struct nla_policy nft_flowtable_policy[NFTA_FLOWTABLE_MAX + 1] = {
- + [NFTA_FLOWTABLE_TABLE] = { .type = NLA_STRING,
- + .len = NFT_NAME_MAXLEN - 1 },
- + [NFTA_FLOWTABLE_NAME] = { .type = NLA_STRING,
- + .len = NFT_NAME_MAXLEN - 1 },
- + [NFTA_FLOWTABLE_HOOK] = { .type = NLA_NESTED },
- +};
- +
- +struct nft_flowtable *nf_tables_flowtable_lookup(const struct nft_table *table,
- + const struct nlattr *nla,
- + u8 genmask)
- +{
- + struct nft_flowtable *flowtable;
- +
- + list_for_each_entry(flowtable, &table->flowtables, list) {
- + if (!nla_strcmp(nla, flowtable->name) &&
- + nft_active_genmask(flowtable, genmask))
- + return flowtable;
- + }
- + return ERR_PTR(-ENOENT);
- +}
- +EXPORT_SYMBOL_GPL(nf_tables_flowtable_lookup);
- +
- +#define NFT_FLOWTABLE_DEVICE_MAX 8
- +
- +static int nf_tables_parse_devices(const struct nft_ctx *ctx,
- + const struct nlattr *attr,
- + struct net_device *dev_array[], int *len)
- +{
- + const struct nlattr *tmp;
- + struct net_device *dev;
- + char ifname[IFNAMSIZ];
- + int rem, n = 0, err;
- +
- + nla_for_each_nested(tmp, attr, rem) {
- + if (nla_type(tmp) != NFTA_DEVICE_NAME) {
- + err = -EINVAL;
- + goto err1;
- + }
- +
- + nla_strlcpy(ifname, tmp, IFNAMSIZ);
- + dev = dev_get_by_name(ctx->net, ifname);
- + if (!dev) {
- + err = -ENOENT;
- + goto err1;
- + }
- +
- + dev_array[n++] = dev;
- + if (n == NFT_FLOWTABLE_DEVICE_MAX) {
- + err = -EFBIG;
- + goto err1;
- + }
- + }
- + if (!len)
- + return -EINVAL;
- +
- + err = 0;
- +err1:
- + *len = n;
- + return err;
- +}
- +
- +static const struct nla_policy nft_flowtable_hook_policy[NFTA_FLOWTABLE_HOOK_MAX + 1] = {
- + [NFTA_FLOWTABLE_HOOK_NUM] = { .type = NLA_U32 },
- + [NFTA_FLOWTABLE_HOOK_PRIORITY] = { .type = NLA_U32 },
- + [NFTA_FLOWTABLE_HOOK_DEVS] = { .type = NLA_NESTED },
- +};
- +
- +static int nf_tables_flowtable_parse_hook(const struct nft_ctx *ctx,
- + const struct nlattr *attr,
- + struct nft_flowtable *flowtable)
- +{
- + struct net_device *dev_array[NFT_FLOWTABLE_DEVICE_MAX];
- + struct nlattr *tb[NFTA_FLOWTABLE_HOOK_MAX + 1];
- + struct nf_hook_ops *ops;
- + int hooknum, priority;
- + int err, n = 0, i;
- +
- + err = nla_parse_nested(tb, NFTA_FLOWTABLE_HOOK_MAX, attr,
- + nft_flowtable_hook_policy, NULL);
- + if (err < 0)
- + return err;
- +
- + if (!tb[NFTA_FLOWTABLE_HOOK_NUM] ||
- + !tb[NFTA_FLOWTABLE_HOOK_PRIORITY] ||
- + !tb[NFTA_FLOWTABLE_HOOK_DEVS])
- + return -EINVAL;
- +
- + hooknum = ntohl(nla_get_be32(tb[NFTA_FLOWTABLE_HOOK_NUM]));
- + if (hooknum >= ctx->afi->nhooks)
- + return -EINVAL;
- +
- + priority = ntohl(nla_get_be32(tb[NFTA_FLOWTABLE_HOOK_PRIORITY]));
- +
- + err = nf_tables_parse_devices(ctx, tb[NFTA_FLOWTABLE_HOOK_DEVS],
- + dev_array, &n);
- + if (err < 0)
- + goto err1;
- +
- + ops = kzalloc(sizeof(struct nf_hook_ops) * n, GFP_KERNEL);
- + if (!ops) {
- + err = -ENOMEM;
- + goto err1;
- + }
- +
- + flowtable->ops = ops;
- + flowtable->ops_len = n;
- +
- + for (i = 0; i < n; i++) {
- + flowtable->ops[i].pf = NFPROTO_NETDEV;
- + flowtable->ops[i].hooknum = hooknum;
- + flowtable->ops[i].priority = priority;
- + flowtable->ops[i].priv = &flowtable->data.rhashtable;
- + flowtable->ops[i].hook = flowtable->data.type->hook;
- + flowtable->ops[i].dev = dev_array[i];
- + }
- +
- + err = 0;
- +err1:
- + for (i = 0; i < n; i++)
- + dev_put(dev_array[i]);
- +
- + return err;
- +}
- +
- +static const struct nf_flowtable_type *
- +__nft_flowtable_type_get(const struct nft_af_info *afi)
- +{
- + const struct nf_flowtable_type *type;
- +
- + list_for_each_entry(type, &nf_tables_flowtables, list) {
- + if (afi->family == type->family)
- + return type;
- + }
- + return NULL;
- +}
- +
- +static const struct nf_flowtable_type *
- +nft_flowtable_type_get(const struct nft_af_info *afi)
- +{
- + const struct nf_flowtable_type *type;
- +
- + type = __nft_flowtable_type_get(afi);
- + if (type != NULL && try_module_get(type->owner))
- + return type;
- +
- +#ifdef CONFIG_MODULES
- + if (type == NULL) {
- + nfnl_unlock(NFNL_SUBSYS_NFTABLES);
- + request_module("nf-flowtable-%u", afi->family);
- + nfnl_lock(NFNL_SUBSYS_NFTABLES);
- + if (__nft_flowtable_type_get(afi))
- + return ERR_PTR(-EAGAIN);
- + }
- +#endif
- + return ERR_PTR(-ENOENT);
- +}
- +
- +void nft_flow_table_iterate(struct net *net,
- + void (*iter)(struct nf_flowtable *flowtable, void *data),
- + void *data)
- +{
- + struct nft_flowtable *flowtable;
- + const struct nft_af_info *afi;
- + const struct nft_table *table;
- +
- + rcu_read_lock();
- + list_for_each_entry_rcu(afi, &net->nft.af_info, list) {
- + list_for_each_entry_rcu(table, &afi->tables, list) {
- + list_for_each_entry_rcu(flowtable, &table->flowtables, list) {
- + iter(&flowtable->data, data);
- + }
- + }
- + }
- + rcu_read_unlock();
- +}
- +EXPORT_SYMBOL_GPL(nft_flow_table_iterate);
- +
- +static void nft_unregister_flowtable_net_hooks(struct net *net,
- + struct nft_flowtable *flowtable)
- +{
- + int i;
- +
- + for (i = 0; i < flowtable->ops_len; i++) {
- + if (!flowtable->ops[i].dev)
- + continue;
- +
- + nf_unregister_net_hook(net, &flowtable->ops[i]);
- + }
- +}
- +
- +static int nf_tables_newflowtable(struct net *net, struct sock *nlsk,
- + struct sk_buff *skb,
- + const struct nlmsghdr *nlh,
- + const struct nlattr * const nla[],
- + struct netlink_ext_ack *extack)
- +{
- + const struct nfgenmsg *nfmsg = nlmsg_data(nlh);
- + const struct nf_flowtable_type *type;
- + u8 genmask = nft_genmask_next(net);
- + int family = nfmsg->nfgen_family;
- + struct nft_flowtable *flowtable;
- + struct nft_af_info *afi;
- + struct nft_table *table;
- + struct nft_ctx ctx;
- + int err, i, k;
- +
- + if (!nla[NFTA_FLOWTABLE_TABLE] ||
- + !nla[NFTA_FLOWTABLE_NAME] ||
- + !nla[NFTA_FLOWTABLE_HOOK])
- + return -EINVAL;
- +
- + afi = nf_tables_afinfo_lookup(net, family, true);
- + if (IS_ERR(afi))
- + return PTR_ERR(afi);
- +
- + table = nf_tables_table_lookup(afi, nla[NFTA_FLOWTABLE_TABLE], genmask);
- + if (IS_ERR(table))
- + return PTR_ERR(table);
- +
- + flowtable = nf_tables_flowtable_lookup(table, nla[NFTA_FLOWTABLE_NAME],
- + genmask);
- + if (IS_ERR(flowtable)) {
- + err = PTR_ERR(flowtable);
- + if (err != -ENOENT)
- + return err;
- + } else {
- + if (nlh->nlmsg_flags & NLM_F_EXCL)
- + return -EEXIST;
- +
- + return 0;
- + }
- +
- + nft_ctx_init(&ctx, net, skb, nlh, afi, table, NULL, nla);
- +
- + flowtable = kzalloc(sizeof(*flowtable), GFP_KERNEL);
- + if (!flowtable)
- + return -ENOMEM;
- +
- + flowtable->table = table;
- + flowtable->name = nla_strdup(nla[NFTA_FLOWTABLE_NAME], GFP_KERNEL);
- + if (!flowtable->name) {
- + err = -ENOMEM;
- + goto err1;
- + }
- +
- + type = nft_flowtable_type_get(afi);
- + if (IS_ERR(type)) {
- + err = PTR_ERR(type);
- + goto err2;
- + }
- +
- + flowtable->data.type = type;
- + err = rhashtable_init(&flowtable->data.rhashtable, type->params);
- + if (err < 0)
- + goto err3;
- +
- + err = nf_tables_flowtable_parse_hook(&ctx, nla[NFTA_FLOWTABLE_HOOK],
- + flowtable);
- + if (err < 0)
- + goto err3;
- +
- + for (i = 0; i < flowtable->ops_len; i++) {
- + err = nf_register_net_hook(net, &flowtable->ops[i]);
- + if (err < 0)
- + goto err4;
- + }
- +
- + err = nft_trans_flowtable_add(&ctx, NFT_MSG_NEWFLOWTABLE, flowtable);
- + if (err < 0)
- + goto err5;
- +
- + INIT_DEFERRABLE_WORK(&flowtable->data.gc_work, type->gc);
- + queue_delayed_work(system_power_efficient_wq,
- + &flowtable->data.gc_work, HZ);
- +
- + list_add_tail_rcu(&flowtable->list, &table->flowtables);
- + table->use++;
- +
- + return 0;
- +err5:
- + i = flowtable->ops_len;
- +err4:
- + for (k = i - 1; k >= 0; k--)
- + nf_unregister_net_hook(net, &flowtable->ops[i]);
- +
- + kfree(flowtable->ops);
- +err3:
- + module_put(type->owner);
- +err2:
- + kfree(flowtable->name);
- +err1:
- + kfree(flowtable);
- + return err;
- +}
- +
- +static int nf_tables_delflowtable(struct net *net, struct sock *nlsk,
- + struct sk_buff *skb,
- + const struct nlmsghdr *nlh,
- + const struct nlattr * const nla[],
- + struct netlink_ext_ack *extack)
- +{
- + const struct nfgenmsg *nfmsg = nlmsg_data(nlh);
- + u8 genmask = nft_genmask_next(net);
- + int family = nfmsg->nfgen_family;
- + struct nft_flowtable *flowtable;
- + struct nft_af_info *afi;
- + struct nft_table *table;
- + struct nft_ctx ctx;
- +
- + afi = nf_tables_afinfo_lookup(net, family, true);
- + if (IS_ERR(afi))
- + return PTR_ERR(afi);
- +
- + table = nf_tables_table_lookup(afi, nla[NFTA_FLOWTABLE_TABLE], genmask);
- + if (IS_ERR(table))
- + return PTR_ERR(table);
- +
- + flowtable = nf_tables_flowtable_lookup(table, nla[NFTA_FLOWTABLE_NAME],
- + genmask);
- + if (IS_ERR(flowtable))
- + return PTR_ERR(flowtable);
- + if (flowtable->use > 0)
- + return -EBUSY;
- +
- + nft_ctx_init(&ctx, net, skb, nlh, afi, table, NULL, nla);
- +
- + return nft_delflowtable(&ctx, flowtable);
- +}
- +
- +static int nf_tables_fill_flowtable_info(struct sk_buff *skb, struct net *net,
- + u32 portid, u32 seq, int event,
- + u32 flags, int family,
- + struct nft_flowtable *flowtable)
- +{
- + struct nlattr *nest, *nest_devs;
- + struct nfgenmsg *nfmsg;
- + struct nlmsghdr *nlh;
- + int i;
- +
- + event = nfnl_msg_type(NFNL_SUBSYS_NFTABLES, event);
- + nlh = nlmsg_put(skb, portid, seq, event, sizeof(struct nfgenmsg), flags);
- + if (nlh == NULL)
- + goto nla_put_failure;
- +
- + nfmsg = nlmsg_data(nlh);
- + nfmsg->nfgen_family = family;
- + nfmsg->version = NFNETLINK_V0;
- + nfmsg->res_id = htons(net->nft.base_seq & 0xffff);
- +
- + if (nla_put_string(skb, NFTA_FLOWTABLE_TABLE, flowtable->table->name) ||
- + nla_put_string(skb, NFTA_FLOWTABLE_NAME, flowtable->name) ||
- + nla_put_be32(skb, NFTA_FLOWTABLE_USE, htonl(flowtable->use)))
- + goto nla_put_failure;
- +
- + nest = nla_nest_start(skb, NFTA_FLOWTABLE_HOOK);
- + if (nla_put_be32(skb, NFTA_FLOWTABLE_HOOK_NUM, htonl(flowtable->hooknum)) ||
- + nla_put_be32(skb, NFTA_FLOWTABLE_HOOK_PRIORITY, htonl(flowtable->priority)))
- + goto nla_put_failure;
- +
- + nest_devs = nla_nest_start(skb, NFTA_FLOWTABLE_HOOK_DEVS);
- + if (!nest_devs)
- + goto nla_put_failure;
- +
- + for (i = 0; i < flowtable->ops_len; i++) {
- + if (flowtable->ops[i].dev &&
- + nla_put_string(skb, NFTA_DEVICE_NAME,
- + flowtable->ops[i].dev->name))
- + goto nla_put_failure;
- + }
- + nla_nest_end(skb, nest_devs);
- + nla_nest_end(skb, nest);
- +
- + nlmsg_end(skb, nlh);
- + return 0;
- +
- +nla_put_failure:
- + nlmsg_trim(skb, nlh);
- + return -1;
- +}
- +
- +struct nft_flowtable_filter {
- + char *table;
- +};
- +
- +static int nf_tables_dump_flowtable(struct sk_buff *skb,
- + struct netlink_callback *cb)
- +{
- + const struct nfgenmsg *nfmsg = nlmsg_data(cb->nlh);
- + struct nft_flowtable_filter *filter = cb->data;
- + unsigned int idx = 0, s_idx = cb->args[0];
- + struct net *net = sock_net(skb->sk);
- + int family = nfmsg->nfgen_family;
- + struct nft_flowtable *flowtable;
- + const struct nft_af_info *afi;
- + const struct nft_table *table;
- +
- + rcu_read_lock();
- + cb->seq = net->nft.base_seq;
- +
- + list_for_each_entry_rcu(afi, &net->nft.af_info, list) {
- + if (family != NFPROTO_UNSPEC && family != afi->family)
- + continue;
- +
- + list_for_each_entry_rcu(table, &afi->tables, list) {
- + list_for_each_entry_rcu(flowtable, &table->flowtables, list) {
- + if (!nft_is_active(net, flowtable))
- + goto cont;
- + if (idx < s_idx)
- + goto cont;
- + if (idx > s_idx)
- + memset(&cb->args[1], 0,
- + sizeof(cb->args) - sizeof(cb->args[0]));
- + if (filter && filter->table[0] &&
- + strcmp(filter->table, table->name))
- + goto cont;
- +
- + if (nf_tables_fill_flowtable_info(skb, net, NETLINK_CB(cb->skb).portid,
- + cb->nlh->nlmsg_seq,
- + NFT_MSG_NEWFLOWTABLE,
- + NLM_F_MULTI | NLM_F_APPEND,
- + afi->family, flowtable) < 0)
- + goto done;
- +
- + nl_dump_check_consistent(cb, nlmsg_hdr(skb));
- +cont:
- + idx++;
- + }
- + }
- + }
- +done:
- + rcu_read_unlock();
- +
- + cb->args[0] = idx;
- + return skb->len;
- +}
- +
- +static int nf_tables_dump_flowtable_done(struct netlink_callback *cb)
- +{
- + struct nft_flowtable_filter *filter = cb->data;
- +
- + if (!filter)
- + return 0;
- +
- + kfree(filter->table);
- + kfree(filter);
- +
- + return 0;
- +}
- +
- +static struct nft_flowtable_filter *
- +nft_flowtable_filter_alloc(const struct nlattr * const nla[])
- +{
- + struct nft_flowtable_filter *filter;
- +
- + filter = kzalloc(sizeof(*filter), GFP_KERNEL);
- + if (!filter)
- + return ERR_PTR(-ENOMEM);
- +
- + if (nla[NFTA_FLOWTABLE_TABLE]) {
- + filter->table = nla_strdup(nla[NFTA_FLOWTABLE_TABLE],
- + GFP_KERNEL);
- + if (!filter->table) {
- + kfree(filter);
- + return ERR_PTR(-ENOMEM);
- + }
- + }
- + return filter;
- +}
- +
- +static int nf_tables_getflowtable(struct net *net, struct sock *nlsk,
- + struct sk_buff *skb,
- + const struct nlmsghdr *nlh,
- + const struct nlattr * const nla[],
- + struct netlink_ext_ack *extack)
- +{
- + const struct nfgenmsg *nfmsg = nlmsg_data(nlh);
- + u8 genmask = nft_genmask_cur(net);
- + int family = nfmsg->nfgen_family;
- + struct nft_flowtable *flowtable;
- + const struct nft_af_info *afi;
- + const struct nft_table *table;
- + struct sk_buff *skb2;
- + int err;
- +
- + if (nlh->nlmsg_flags & NLM_F_DUMP) {
- + struct netlink_dump_control c = {
- + .dump = nf_tables_dump_flowtable,
- + .done = nf_tables_dump_flowtable_done,
- + };
- +
- + if (nla[NFTA_FLOWTABLE_TABLE]) {
- + struct nft_flowtable_filter *filter;
- +
- + filter = nft_flowtable_filter_alloc(nla);
- + if (IS_ERR(filter))
- + return -ENOMEM;
- +
- + c.data = filter;
- + }
- + return netlink_dump_start(nlsk, skb, nlh, &c);
- + }
- +
- + if (!nla[NFTA_FLOWTABLE_NAME])
- + return -EINVAL;
- +
- + afi = nf_tables_afinfo_lookup(net, family, false);
- + if (IS_ERR(afi))
- + return PTR_ERR(afi);
- +
- + table = nf_tables_table_lookup(afi, nla[NFTA_FLOWTABLE_TABLE], genmask);
- + if (IS_ERR(table))
- + return PTR_ERR(table);
- +
- + flowtable = nf_tables_flowtable_lookup(table, nla[NFTA_FLOWTABLE_NAME],
- + genmask);
- + if (IS_ERR(table))
- + return PTR_ERR(flowtable);
- +
- + skb2 = alloc_skb(NLMSG_GOODSIZE, GFP_KERNEL);
- + if (!skb2)
- + return -ENOMEM;
- +
- + err = nf_tables_fill_flowtable_info(skb2, net, NETLINK_CB(skb).portid,
- + nlh->nlmsg_seq,
- + NFT_MSG_NEWFLOWTABLE, 0, family,
- + flowtable);
- + if (err < 0)
- + goto err;
- +
- + return nlmsg_unicast(nlsk, skb2, NETLINK_CB(skb).portid);
- +err:
- + kfree_skb(skb2);
- + return err;
- +}
- +
- +static void nf_tables_flowtable_notify(struct nft_ctx *ctx,
- + struct nft_flowtable *flowtable,
- + int event)
- +{
- + struct sk_buff *skb;
- + int err;
- +
- + if (ctx->report &&
- + !nfnetlink_has_listeners(ctx->net, NFNLGRP_NFTABLES))
- + return;
- +
- + skb = nlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL);
- + if (skb == NULL)
- + goto err;
- +
- + err = nf_tables_fill_flowtable_info(skb, ctx->net, ctx->portid,
- + ctx->seq, event, 0,
- + ctx->afi->family, flowtable);
- + if (err < 0) {
- + kfree_skb(skb);
- + goto err;
- + }
- +
- + nfnetlink_send(skb, ctx->net, ctx->portid, NFNLGRP_NFTABLES,
- + ctx->report, GFP_KERNEL);
- + return;
- +err:
- + nfnetlink_set_err(ctx->net, ctx->portid, NFNLGRP_NFTABLES, -ENOBUFS);
- +}
- +
- +static void nft_flowtable_destroy(void *ptr, void *arg)
- +{
- + kfree(ptr);
- +}
- +
- +static void nf_tables_flowtable_destroy(struct nft_flowtable *flowtable)
- +{
- + cancel_delayed_work_sync(&flowtable->data.gc_work);
- + kfree(flowtable->name);
- + rhashtable_free_and_destroy(&flowtable->data.rhashtable,
- + nft_flowtable_destroy, NULL);
- + module_put(flowtable->data.type->owner);
- +}
- +
- static int nf_tables_fill_gen_info(struct sk_buff *skb, struct net *net,
- u32 portid, u32 seq)
- {
- @@ -4893,6 +5536,49 @@ nla_put_failure:
- return -EMSGSIZE;
- }
-
- +static void nft_flowtable_event(unsigned long event, struct net_device *dev,
- + struct nft_flowtable *flowtable)
- +{
- + int i;
- +
- + for (i = 0; i < flowtable->ops_len; i++) {
- + if (flowtable->ops[i].dev != dev)
- + continue;
- +
- + nf_unregister_net_hook(dev_net(dev), &flowtable->ops[i]);
- + flowtable->ops[i].dev = NULL;
- + break;
- + }
- +}
- +
- +static int nf_tables_flowtable_event(struct notifier_block *this,
- + unsigned long event, void *ptr)
- +{
- + struct net_device *dev = netdev_notifier_info_to_dev(ptr);
- + struct nft_flowtable *flowtable;
- + struct nft_table *table;
- + struct nft_af_info *afi;
- +
- + if (event != NETDEV_UNREGISTER)
- + return 0;
- +
- + nfnl_lock(NFNL_SUBSYS_NFTABLES);
- + list_for_each_entry(afi, &dev_net(dev)->nft.af_info, list) {
- + list_for_each_entry(table, &afi->tables, list) {
- + list_for_each_entry(flowtable, &table->flowtables, list) {
- + nft_flowtable_event(event, dev, flowtable);
- + }
- + }
- + }
- + nfnl_unlock(NFNL_SUBSYS_NFTABLES);
- +
- + return NOTIFY_DONE;
- +}
- +
- +static struct notifier_block nf_tables_flowtable_notifier = {
- + .notifier_call = nf_tables_flowtable_event,
- +};
- +
- static void nf_tables_gen_notify(struct net *net, struct sk_buff *skb,
- int event)
- {
- @@ -5045,6 +5731,21 @@ static const struct nfnl_callback nf_tab
- .attr_count = NFTA_OBJ_MAX,
- .policy = nft_obj_policy,
- },
- + [NFT_MSG_NEWFLOWTABLE] = {
- + .call_batch = nf_tables_newflowtable,
- + .attr_count = NFTA_FLOWTABLE_MAX,
- + .policy = nft_flowtable_policy,
- + },
- + [NFT_MSG_GETFLOWTABLE] = {
- + .call = nf_tables_getflowtable,
- + .attr_count = NFTA_FLOWTABLE_MAX,
- + .policy = nft_flowtable_policy,
- + },
- + [NFT_MSG_DELFLOWTABLE] = {
- + .call_batch = nf_tables_delflowtable,
- + .attr_count = NFTA_FLOWTABLE_MAX,
- + .policy = nft_flowtable_policy,
- + },
- };
-
- static void nft_chain_commit_update(struct nft_trans *trans)
- @@ -5093,6 +5794,9 @@ static void nf_tables_commit_release(str
- case NFT_MSG_DELOBJ:
- nft_obj_destroy(nft_trans_obj(trans));
- break;
- + case NFT_MSG_DELFLOWTABLE:
- + nf_tables_flowtable_destroy(nft_trans_flowtable(trans));
- + break;
- }
- kfree(trans);
- }
- @@ -5212,6 +5916,21 @@ static int nf_tables_commit(struct net *
- nf_tables_obj_notify(&trans->ctx, nft_trans_obj(trans),
- NFT_MSG_DELOBJ);
- break;
- + case NFT_MSG_NEWFLOWTABLE:
- + nft_clear(net, nft_trans_flowtable(trans));
- + nf_tables_flowtable_notify(&trans->ctx,
- + nft_trans_flowtable(trans),
- + NFT_MSG_NEWFLOWTABLE);
- + nft_trans_destroy(trans);
- + break;
- + case NFT_MSG_DELFLOWTABLE:
- + list_del_rcu(&nft_trans_flowtable(trans)->list);
- + nf_tables_flowtable_notify(&trans->ctx,
- + nft_trans_flowtable(trans),
- + NFT_MSG_DELFLOWTABLE);
- + nft_unregister_flowtable_net_hooks(net,
- + nft_trans_flowtable(trans));
- + break;
- }
- }
-
- @@ -5249,6 +5968,9 @@ static void nf_tables_abort_release(stru
- case NFT_MSG_NEWOBJ:
- nft_obj_destroy(nft_trans_obj(trans));
- break;
- + case NFT_MSG_NEWFLOWTABLE:
- + nf_tables_flowtable_destroy(nft_trans_flowtable(trans));
- + break;
- }
- kfree(trans);
- }
- @@ -5340,6 +6062,17 @@ static int nf_tables_abort(struct net *n
- nft_clear(trans->ctx.net, nft_trans_obj(trans));
- nft_trans_destroy(trans);
- break;
- + case NFT_MSG_NEWFLOWTABLE:
- + trans->ctx.table->use--;
- + list_del_rcu(&nft_trans_flowtable(trans)->list);
- + nft_unregister_flowtable_net_hooks(net,
- + nft_trans_flowtable(trans));
- + break;
- + case NFT_MSG_DELFLOWTABLE:
- + trans->ctx.table->use++;
- + nft_clear(trans->ctx.net, nft_trans_flowtable(trans));
- + nft_trans_destroy(trans);
- + break;
- }
- }
-
- @@ -5890,6 +6623,7 @@ EXPORT_SYMBOL_GPL(__nft_release_basechai
- /* Called by nft_unregister_afinfo() from __net_exit path, nfnl_lock is held. */
- static void __nft_release_afinfo(struct net *net, struct nft_af_info *afi)
- {
- + struct nft_flowtable *flowtable, *nf;
- struct nft_table *table, *nt;
- struct nft_chain *chain, *nc;
- struct nft_object *obj, *ne;
- @@ -5903,6 +6637,9 @@ static void __nft_release_afinfo(struct
- list_for_each_entry_safe(table, nt, &afi->tables, list) {
- list_for_each_entry(chain, &table->chains, list)
- nf_tables_unregister_hook(net, table, chain);
- + list_for_each_entry(flowtable, &table->flowtables, list)
- + nf_unregister_net_hooks(net, flowtable->ops,
- + flowtable->ops_len);
- /* No packets are walking on these chains anymore. */
- ctx.table = table;
- list_for_each_entry(chain, &table->chains, list) {
- @@ -5913,6 +6650,11 @@ static void __nft_release_afinfo(struct
- nf_tables_rule_release(&ctx, rule);
- }
- }
- + list_for_each_entry_safe(flowtable, nf, &table->flowtables, list) {
- + list_del(&flowtable->list);
- + table->use--;
- + nf_tables_flowtable_destroy(flowtable);
- + }
- list_for_each_entry_safe(set, ns, &table->sets, list) {
- list_del(&set->list);
- table->use--;
- @@ -5956,6 +6698,8 @@ static int __init nf_tables_module_init(
- if (err < 0)
- goto err3;
-
- + register_netdevice_notifier(&nf_tables_flowtable_notifier);
- +
- pr_info("nf_tables: (c) 2007-2009 Patrick McHardy <kaber@trash.net>\n");
- return register_pernet_subsys(&nf_tables_net_ops);
- err3:
- @@ -5970,6 +6714,7 @@ static void __exit nf_tables_module_exit
- {
- unregister_pernet_subsys(&nf_tables_net_ops);
- nfnetlink_subsys_unregister(&nf_tables_subsys);
- + unregister_netdevice_notifier(&nf_tables_flowtable_notifier);
- rcu_barrier();
- nf_tables_core_module_exit();
- kfree(info);
|