diff options
| -rw-r--r-- | include/net/netfilter/nf_flow_table.h | 23 | ||||
| -rw-r--r-- | include/net/netfilter/nf_tables.h | 48 | ||||
| -rw-r--r-- | include/uapi/linux/netfilter/nf_tables.h | 53 | ||||
| -rw-r--r-- | net/netfilter/nf_tables_api.c | 747 |
4 files changed, 870 insertions, 1 deletions
diff --git a/include/net/netfilter/nf_flow_table.h b/include/net/netfilter/nf_flow_table.h new file mode 100644 index 000000000000..3a0779589281 --- /dev/null +++ b/include/net/netfilter/nf_flow_table.h | |||
| @@ -0,0 +1,23 @@ | |||
| 1 | #ifndef _NF_FLOW_TABLE_H | ||
| 2 | #define _NF_FLOW_TABLE_H | ||
| 3 | |||
| 4 | #include <linux/rhashtable.h> | ||
| 5 | |||
| 6 | struct nf_flowtable; | ||
| 7 | |||
| 8 | struct nf_flowtable_type { | ||
| 9 | struct list_head list; | ||
| 10 | int family; | ||
| 11 | void (*gc)(struct work_struct *work); | ||
| 12 | const struct rhashtable_params *params; | ||
| 13 | nf_hookfn *hook; | ||
| 14 | struct module *owner; | ||
| 15 | }; | ||
| 16 | |||
| 17 | struct nf_flowtable { | ||
| 18 | struct rhashtable rhashtable; | ||
| 19 | const struct nf_flowtable_type *type; | ||
| 20 | struct delayed_work gc_work; | ||
| 21 | }; | ||
| 22 | |||
| 23 | #endif /* _FLOW_OFFLOAD_H */ | ||
diff --git a/include/net/netfilter/nf_tables.h b/include/net/netfilter/nf_tables.h index e3ec02fd0f67..dd238950df81 100644 --- a/include/net/netfilter/nf_tables.h +++ b/include/net/netfilter/nf_tables.h | |||
| @@ -9,6 +9,7 @@ | |||
| 9 | #include <linux/netfilter/x_tables.h> | 9 | #include <linux/netfilter/x_tables.h> |
| 10 | #include <linux/netfilter/nf_tables.h> | 10 | #include <linux/netfilter/nf_tables.h> |
| 11 | #include <linux/u64_stats_sync.h> | 11 | #include <linux/u64_stats_sync.h> |
| 12 | #include <net/netfilter/nf_flow_table.h> | ||
| 12 | #include <net/netlink.h> | 13 | #include <net/netlink.h> |
| 13 | 14 | ||
| 14 | #define NFT_JUMP_STACK_SIZE 16 | 15 | #define NFT_JUMP_STACK_SIZE 16 |
| @@ -943,6 +944,7 @@ unsigned int nft_do_chain(struct nft_pktinfo *pkt, void *priv); | |||
| 943 | * @chains: chains in the table | 944 | * @chains: chains in the table |
| 944 | * @sets: sets in the table | 945 | * @sets: sets in the table |
| 945 | * @objects: stateful objects in the table | 946 | * @objects: stateful objects in the table |
| 947 | * @flowtables: flow tables in the table | ||
| 946 | * @hgenerator: handle generator state | 948 | * @hgenerator: handle generator state |
| 947 | * @use: number of chain references to this table | 949 | * @use: number of chain references to this table |
| 948 | * @flags: table flag (see enum nft_table_flags) | 950 | * @flags: table flag (see enum nft_table_flags) |
| @@ -954,6 +956,7 @@ struct nft_table { | |||
| 954 | struct list_head chains; | 956 | struct list_head chains; |
| 955 | struct list_head sets; | 957 | struct list_head sets; |
| 956 | struct list_head objects; | 958 | struct list_head objects; |
| 959 | struct list_head flowtables; | ||
| 957 | u64 hgenerator; | 960 | u64 hgenerator; |
| 958 | u32 use; | 961 | u32 use; |
| 959 | u16 flags:14, | 962 | u16 flags:14, |
| @@ -1085,6 +1088,44 @@ int nft_register_obj(struct nft_object_type *obj_type); | |||
| 1085 | void nft_unregister_obj(struct nft_object_type *obj_type); | 1088 | void nft_unregister_obj(struct nft_object_type *obj_type); |
| 1086 | 1089 | ||
| 1087 | /** | 1090 | /** |
| 1091 | * struct nft_flowtable - nf_tables flow table | ||
| 1092 | * | ||
| 1093 | * @list: flow table list node in table list | ||
| 1094 | * @table: the table the flow table is contained in | ||
| 1095 | * @name: name of this flow table | ||
| 1096 | * @hooknum: hook number | ||
| 1097 | * @priority: hook priority | ||
| 1098 | * @ops_len: number of hooks in array | ||
| 1099 | * @genmask: generation mask | ||
| 1100 | * @use: number of references to this flow table | ||
| 1101 | * @data: rhashtable and garbage collector | ||
| 1102 | * @ops: array of hooks | ||
| 1103 | */ | ||
| 1104 | struct nft_flowtable { | ||
| 1105 | struct list_head list; | ||
| 1106 | struct nft_table *table; | ||
| 1107 | char *name; | ||
| 1108 | int hooknum; | ||
| 1109 | int priority; | ||
| 1110 | int ops_len; | ||
| 1111 | u32 genmask:2, | ||
| 1112 | use:30; | ||
| 1113 | /* runtime data below here */ | ||
| 1114 | struct nf_hook_ops *ops ____cacheline_aligned; | ||
| 1115 | struct nf_flowtable data; | ||
| 1116 | }; | ||
| 1117 | |||
| 1118 | struct nft_flowtable *nf_tables_flowtable_lookup(const struct nft_table *table, | ||
| 1119 | const struct nlattr *nla, | ||
| 1120 | u8 genmask); | ||
| 1121 | void nft_flow_table_iterate(struct net *net, | ||
| 1122 | void (*iter)(struct nf_flowtable *flowtable, void *data), | ||
| 1123 | void *data); | ||
| 1124 | |||
| 1125 | void nft_register_flowtable_type(struct nf_flowtable_type *type); | ||
| 1126 | void nft_unregister_flowtable_type(struct nf_flowtable_type *type); | ||
| 1127 | |||
| 1128 | /** | ||
| 1088 | * struct nft_traceinfo - nft tracing information and state | 1129 | * struct nft_traceinfo - nft tracing information and state |
| 1089 | * | 1130 | * |
| 1090 | * @pkt: pktinfo currently processed | 1131 | * @pkt: pktinfo currently processed |
| @@ -1317,4 +1358,11 @@ struct nft_trans_obj { | |||
| 1317 | #define nft_trans_obj(trans) \ | 1358 | #define nft_trans_obj(trans) \ |
| 1318 | (((struct nft_trans_obj *)trans->data)->obj) | 1359 | (((struct nft_trans_obj *)trans->data)->obj) |
| 1319 | 1360 | ||
| 1361 | struct nft_trans_flowtable { | ||
| 1362 | struct nft_flowtable *flowtable; | ||
| 1363 | }; | ||
| 1364 | |||
| 1365 | #define nft_trans_flowtable(trans) \ | ||
| 1366 | (((struct nft_trans_flowtable *)trans->data)->flowtable) | ||
| 1367 | |||
| 1320 | #endif /* _NET_NF_TABLES_H */ | 1368 | #endif /* _NET_NF_TABLES_H */ |
diff --git a/include/uapi/linux/netfilter/nf_tables.h b/include/uapi/linux/netfilter/nf_tables.h index 2efbf9744c2a..591b53bce070 100644 --- a/include/uapi/linux/netfilter/nf_tables.h +++ b/include/uapi/linux/netfilter/nf_tables.h | |||
| @@ -92,6 +92,9 @@ enum nft_verdicts { | |||
| 92 | * @NFT_MSG_GETOBJ: get a stateful object (enum nft_obj_attributes) | 92 | * @NFT_MSG_GETOBJ: get a stateful object (enum nft_obj_attributes) |
| 93 | * @NFT_MSG_DELOBJ: delete a stateful object (enum nft_obj_attributes) | 93 | * @NFT_MSG_DELOBJ: delete a stateful object (enum nft_obj_attributes) |
| 94 | * @NFT_MSG_GETOBJ_RESET: get and reset a stateful object (enum nft_obj_attributes) | 94 | * @NFT_MSG_GETOBJ_RESET: get and reset a stateful object (enum nft_obj_attributes) |
| 95 | * @NFT_MSG_NEWFLOWTABLE: add new flow table (enum nft_flowtable_attributes) | ||
| 96 | * @NFT_MSG_GETFLOWTABLE: get flow table (enum nft_flowtable_attributes) | ||
| 97 | * @NFT_MSG_DELFLOWTABLE: delete flow table (enum nft_flowtable_attributes) | ||
| 95 | */ | 98 | */ |
| 96 | enum nf_tables_msg_types { | 99 | enum nf_tables_msg_types { |
| 97 | NFT_MSG_NEWTABLE, | 100 | NFT_MSG_NEWTABLE, |
| @@ -116,6 +119,9 @@ enum nf_tables_msg_types { | |||
| 116 | NFT_MSG_GETOBJ, | 119 | NFT_MSG_GETOBJ, |
| 117 | NFT_MSG_DELOBJ, | 120 | NFT_MSG_DELOBJ, |
| 118 | NFT_MSG_GETOBJ_RESET, | 121 | NFT_MSG_GETOBJ_RESET, |
| 122 | NFT_MSG_NEWFLOWTABLE, | ||
| 123 | NFT_MSG_GETFLOWTABLE, | ||
| 124 | NFT_MSG_DELFLOWTABLE, | ||
| 119 | NFT_MSG_MAX, | 125 | NFT_MSG_MAX, |
| 120 | }; | 126 | }; |
| 121 | 127 | ||
| @@ -1310,6 +1316,53 @@ enum nft_object_attributes { | |||
| 1310 | #define NFTA_OBJ_MAX (__NFTA_OBJ_MAX - 1) | 1316 | #define NFTA_OBJ_MAX (__NFTA_OBJ_MAX - 1) |
| 1311 | 1317 | ||
| 1312 | /** | 1318 | /** |
| 1319 | * enum nft_flowtable_attributes - nf_tables flow table netlink attributes | ||
| 1320 | * | ||
| 1321 | * @NFTA_FLOWTABLE_TABLE: name of the table containing the expression (NLA_STRING) | ||
| 1322 | * @NFTA_FLOWTABLE_NAME: name of this flow table (NLA_STRING) | ||
| 1323 | * @NFTA_FLOWTABLE_HOOK: netfilter hook configuration(NLA_U32) | ||
| 1324 | * @NFTA_FLOWTABLE_USE: number of references to this flow table (NLA_U32) | ||
| 1325 | */ | ||
| 1326 | enum nft_flowtable_attributes { | ||
| 1327 | NFTA_FLOWTABLE_UNSPEC, | ||
| 1328 | NFTA_FLOWTABLE_TABLE, | ||
| 1329 | NFTA_FLOWTABLE_NAME, | ||
| 1330 | NFTA_FLOWTABLE_HOOK, | ||
| 1331 | NFTA_FLOWTABLE_USE, | ||
| 1332 | __NFTA_FLOWTABLE_MAX | ||
| 1333 | }; | ||
| 1334 | #define NFTA_FLOWTABLE_MAX (__NFTA_FLOWTABLE_MAX - 1) | ||
| 1335 | |||
| 1336 | /** | ||
| 1337 | * enum nft_flowtable_hook_attributes - nf_tables flow table hook netlink attributes | ||
| 1338 | * | ||
| 1339 | * @NFTA_FLOWTABLE_HOOK_NUM: netfilter hook number (NLA_U32) | ||
| 1340 | * @NFTA_FLOWTABLE_HOOK_PRIORITY: netfilter hook priority (NLA_U32) | ||
| 1341 | * @NFTA_FLOWTABLE_HOOK_DEVS: input devices this flow table is bound to (NLA_NESTED) | ||
| 1342 | */ | ||
| 1343 | enum nft_flowtable_hook_attributes { | ||
| 1344 | NFTA_FLOWTABLE_HOOK_UNSPEC, | ||
| 1345 | NFTA_FLOWTABLE_HOOK_NUM, | ||
| 1346 | NFTA_FLOWTABLE_HOOK_PRIORITY, | ||
| 1347 | NFTA_FLOWTABLE_HOOK_DEVS, | ||
| 1348 | __NFTA_FLOWTABLE_HOOK_MAX | ||
| 1349 | }; | ||
| 1350 | #define NFTA_FLOWTABLE_HOOK_MAX (__NFTA_FLOWTABLE_HOOK_MAX - 1) | ||
| 1351 | |||
| 1352 | /** | ||
| 1353 | * enum nft_device_attributes - nf_tables device netlink attributes | ||
| 1354 | * | ||
| 1355 | * @NFTA_DEVICE_NAME: name of this device (NLA_STRING) | ||
| 1356 | */ | ||
| 1357 | enum nft_devices_attributes { | ||
| 1358 | NFTA_DEVICE_UNSPEC, | ||
| 1359 | NFTA_DEVICE_NAME, | ||
| 1360 | __NFTA_DEVICE_MAX | ||
| 1361 | }; | ||
| 1362 | #define NFTA_DEVICE_MAX (__NFTA_DEVICE_MAX - 1) | ||
| 1363 | |||
| 1364 | |||
| 1365 | /** | ||
| 1313 | * enum nft_trace_attributes - nf_tables trace netlink attributes | 1366 | * enum nft_trace_attributes - nf_tables trace netlink attributes |
| 1314 | * | 1367 | * |
| 1315 | * @NFTA_TRACE_TABLE: name of the table (NLA_STRING) | 1368 | * @NFTA_TRACE_TABLE: name of the table (NLA_STRING) |
diff --git a/net/netfilter/nf_tables_api.c b/net/netfilter/nf_tables_api.c index fa564dac66a2..db0933256ec9 100644 --- a/net/netfilter/nf_tables_api.c +++ b/net/netfilter/nf_tables_api.c | |||
| @@ -17,6 +17,7 @@ | |||
| 17 | #include <linux/netfilter.h> | 17 | #include <linux/netfilter.h> |
| 18 | #include <linux/netfilter/nfnetlink.h> | 18 | #include <linux/netfilter/nfnetlink.h> |
| 19 | #include <linux/netfilter/nf_tables.h> | 19 | #include <linux/netfilter/nf_tables.h> |
| 20 | #include <net/netfilter/nf_flow_table.h> | ||
| 20 | #include <net/netfilter/nf_tables_core.h> | 21 | #include <net/netfilter/nf_tables_core.h> |
| 21 | #include <net/netfilter/nf_tables.h> | 22 | #include <net/netfilter/nf_tables.h> |
| 22 | #include <net/net_namespace.h> | 23 | #include <net/net_namespace.h> |
| @@ -24,6 +25,7 @@ | |||
| 24 | 25 | ||
| 25 | static LIST_HEAD(nf_tables_expressions); | 26 | static LIST_HEAD(nf_tables_expressions); |
| 26 | static LIST_HEAD(nf_tables_objects); | 27 | static LIST_HEAD(nf_tables_objects); |
| 28 | static LIST_HEAD(nf_tables_flowtables); | ||
| 27 | 29 | ||
| 28 | /** | 30 | /** |
| 29 | * nft_register_afinfo - register nf_tables address family info | 31 | * nft_register_afinfo - register nf_tables address family info |
| @@ -345,6 +347,40 @@ static int nft_delobj(struct nft_ctx *ctx, struct nft_object *obj) | |||
| 345 | return err; | 347 | return err; |
| 346 | } | 348 | } |
| 347 | 349 | ||
| 350 | static int nft_trans_flowtable_add(struct nft_ctx *ctx, int msg_type, | ||
| 351 | struct nft_flowtable *flowtable) | ||
| 352 | { | ||
| 353 | struct nft_trans *trans; | ||
| 354 | |||
| 355 | trans = nft_trans_alloc(ctx, msg_type, | ||
| 356 | sizeof(struct nft_trans_flowtable)); | ||
| 357 | if (trans == NULL) | ||
| 358 | return -ENOMEM; | ||
| 359 | |||
| 360 | if (msg_type == NFT_MSG_NEWFLOWTABLE) | ||
| 361 | nft_activate_next(ctx->net, flowtable); | ||
| 362 | |||
| 363 | nft_trans_flowtable(trans) = flowtable; | ||
| 364 | list_add_tail(&trans->list, &ctx->net->nft.commit_list); | ||
| 365 | |||
| 366 | return 0; | ||
| 367 | } | ||
| 368 | |||
| 369 | static int nft_delflowtable(struct nft_ctx *ctx, | ||
| 370 | struct nft_flowtable *flowtable) | ||
| 371 | { | ||
| 372 | int err; | ||
| 373 | |||
| 374 | err = nft_trans_flowtable_add(ctx, NFT_MSG_DELFLOWTABLE, flowtable); | ||
| 375 | if (err < 0) | ||
| 376 | return err; | ||
| 377 | |||
| 378 | nft_deactivate_next(ctx->net, flowtable); | ||
| 379 | ctx->table->use--; | ||
| 380 | |||
| 381 | return err; | ||
| 382 | } | ||
| 383 | |||
| 348 | /* | 384 | /* |
| 349 | * Tables | 385 | * Tables |
| 350 | */ | 386 | */ |
| @@ -728,6 +764,7 @@ static int nf_tables_newtable(struct net *net, struct sock *nlsk, | |||
| 728 | INIT_LIST_HEAD(&table->chains); | 764 | INIT_LIST_HEAD(&table->chains); |
| 729 | INIT_LIST_HEAD(&table->sets); | 765 | INIT_LIST_HEAD(&table->sets); |
| 730 | INIT_LIST_HEAD(&table->objects); | 766 | INIT_LIST_HEAD(&table->objects); |
| 767 | INIT_LIST_HEAD(&table->flowtables); | ||
| 731 | table->flags = flags; | 768 | table->flags = flags; |
| 732 | 769 | ||
| 733 | nft_ctx_init(&ctx, net, skb, nlh, afi, table, NULL, nla); | 770 | nft_ctx_init(&ctx, net, skb, nlh, afi, table, NULL, nla); |
| @@ -749,10 +786,11 @@ err1: | |||
| 749 | 786 | ||
| 750 | static int nft_flush_table(struct nft_ctx *ctx) | 787 | static int nft_flush_table(struct nft_ctx *ctx) |
| 751 | { | 788 | { |
| 752 | int err; | 789 | struct nft_flowtable *flowtable, *nft; |
| 753 | struct nft_chain *chain, *nc; | 790 | struct nft_chain *chain, *nc; |
| 754 | struct nft_object *obj, *ne; | 791 | struct nft_object *obj, *ne; |
| 755 | struct nft_set *set, *ns; | 792 | struct nft_set *set, *ns; |
| 793 | int err; | ||
| 756 | 794 | ||
| 757 | list_for_each_entry(chain, &ctx->table->chains, list) { | 795 | list_for_each_entry(chain, &ctx->table->chains, list) { |
| 758 | if (!nft_is_active_next(ctx->net, chain)) | 796 | if (!nft_is_active_next(ctx->net, chain)) |
| @@ -778,6 +816,12 @@ static int nft_flush_table(struct nft_ctx *ctx) | |||
| 778 | goto out; | 816 | goto out; |
| 779 | } | 817 | } |
| 780 | 818 | ||
| 819 | list_for_each_entry_safe(flowtable, nft, &ctx->table->flowtables, list) { | ||
| 820 | err = nft_delflowtable(ctx, flowtable); | ||
| 821 | if (err < 0) | ||
| 822 | goto out; | ||
| 823 | } | ||
| 824 | |||
| 781 | list_for_each_entry_safe(obj, ne, &ctx->table->objects, list) { | 825 | list_for_each_entry_safe(obj, ne, &ctx->table->objects, list) { |
| 782 | err = nft_delobj(ctx, obj); | 826 | err = nft_delobj(ctx, obj); |
| 783 | if (err < 0) | 827 | if (err < 0) |
| @@ -4839,6 +4883,605 @@ static void nf_tables_obj_notify(const struct nft_ctx *ctx, | |||
| 4839 | ctx->afi->family, ctx->report, GFP_KERNEL); | 4883 | ctx->afi->family, ctx->report, GFP_KERNEL); |
| 4840 | } | 4884 | } |
| 4841 | 4885 | ||
| 4886 | /* | ||
| 4887 | * Flow tables | ||
| 4888 | */ | ||
| 4889 | void nft_register_flowtable_type(struct nf_flowtable_type *type) | ||
| 4890 | { | ||
| 4891 | nfnl_lock(NFNL_SUBSYS_NFTABLES); | ||
| 4892 | list_add_tail_rcu(&type->list, &nf_tables_flowtables); | ||
| 4893 | nfnl_unlock(NFNL_SUBSYS_NFTABLES); | ||
| 4894 | } | ||
| 4895 | EXPORT_SYMBOL_GPL(nft_register_flowtable_type); | ||
| 4896 | |||
| 4897 | void nft_unregister_flowtable_type(struct nf_flowtable_type *type) | ||
| 4898 | { | ||
| 4899 | nfnl_lock(NFNL_SUBSYS_NFTABLES); | ||
| 4900 | list_del_rcu(&type->list); | ||
| 4901 | nfnl_unlock(NFNL_SUBSYS_NFTABLES); | ||
| 4902 | } | ||
| 4903 | EXPORT_SYMBOL_GPL(nft_unregister_flowtable_type); | ||
| 4904 | |||
| 4905 | static const struct nla_policy nft_flowtable_policy[NFTA_FLOWTABLE_MAX + 1] = { | ||
| 4906 | [NFTA_FLOWTABLE_TABLE] = { .type = NLA_STRING, | ||
| 4907 | .len = NFT_NAME_MAXLEN - 1 }, | ||
| 4908 | [NFTA_FLOWTABLE_NAME] = { .type = NLA_STRING, | ||
| 4909 | .len = NFT_NAME_MAXLEN - 1 }, | ||
| 4910 | [NFTA_FLOWTABLE_HOOK] = { .type = NLA_NESTED }, | ||
| 4911 | }; | ||
| 4912 | |||
| 4913 | struct nft_flowtable *nf_tables_flowtable_lookup(const struct nft_table *table, | ||
| 4914 | const struct nlattr *nla, | ||
| 4915 | u8 genmask) | ||
| 4916 | { | ||
| 4917 | struct nft_flowtable *flowtable; | ||
| 4918 | |||
| 4919 | list_for_each_entry(flowtable, &table->flowtables, list) { | ||
| 4920 | if (!nla_strcmp(nla, flowtable->name) && | ||
| 4921 | nft_active_genmask(flowtable, genmask)) | ||
| 4922 | return flowtable; | ||
| 4923 | } | ||
| 4924 | return ERR_PTR(-ENOENT); | ||
| 4925 | } | ||
| 4926 | EXPORT_SYMBOL_GPL(nf_tables_flowtable_lookup); | ||
| 4927 | |||
| 4928 | #define NFT_FLOWTABLE_DEVICE_MAX 8 | ||
| 4929 | |||
| 4930 | static int nf_tables_parse_devices(const struct nft_ctx *ctx, | ||
| 4931 | const struct nlattr *attr, | ||
| 4932 | struct net_device *dev_array[], int *len) | ||
| 4933 | { | ||
| 4934 | const struct nlattr *tmp; | ||
| 4935 | struct net_device *dev; | ||
| 4936 | char ifname[IFNAMSIZ]; | ||
| 4937 | int rem, n = 0, err; | ||
| 4938 | |||
| 4939 | nla_for_each_nested(tmp, attr, rem) { | ||
| 4940 | if (nla_type(tmp) != NFTA_DEVICE_NAME) { | ||
| 4941 | err = -EINVAL; | ||
| 4942 | goto err1; | ||
| 4943 | } | ||
| 4944 | |||
| 4945 | nla_strlcpy(ifname, tmp, IFNAMSIZ); | ||
| 4946 | dev = dev_get_by_name(ctx->net, ifname); | ||
| 4947 | if (!dev) { | ||
| 4948 | err = -ENOENT; | ||
| 4949 | goto err1; | ||
| 4950 | } | ||
| 4951 | |||
| 4952 | dev_array[n++] = dev; | ||
| 4953 | if (n == NFT_FLOWTABLE_DEVICE_MAX) { | ||
| 4954 | err = -EFBIG; | ||
| 4955 | goto err1; | ||
| 4956 | } | ||
| 4957 | } | ||
| 4958 | if (!len) | ||
| 4959 | return -EINVAL; | ||
| 4960 | |||
| 4961 | err = 0; | ||
| 4962 | err1: | ||
| 4963 | *len = n; | ||
| 4964 | return err; | ||
| 4965 | } | ||
| 4966 | |||
| 4967 | static const struct nla_policy nft_flowtable_hook_policy[NFTA_FLOWTABLE_HOOK_MAX + 1] = { | ||
| 4968 | [NFTA_FLOWTABLE_HOOK_NUM] = { .type = NLA_U32 }, | ||
| 4969 | [NFTA_FLOWTABLE_HOOK_PRIORITY] = { .type = NLA_U32 }, | ||
| 4970 | [NFTA_FLOWTABLE_HOOK_DEVS] = { .type = NLA_NESTED }, | ||
| 4971 | }; | ||
| 4972 | |||
| 4973 | static int nf_tables_flowtable_parse_hook(const struct nft_ctx *ctx, | ||
| 4974 | const struct nlattr *attr, | ||
| 4975 | struct nft_flowtable *flowtable) | ||
| 4976 | { | ||
| 4977 | struct net_device *dev_array[NFT_FLOWTABLE_DEVICE_MAX]; | ||
| 4978 | struct nlattr *tb[NFTA_FLOWTABLE_HOOK_MAX + 1]; | ||
| 4979 | struct nf_hook_ops *ops; | ||
| 4980 | int hooknum, priority; | ||
| 4981 | int err, n = 0, i; | ||
| 4982 | |||
| 4983 | err = nla_parse_nested(tb, NFTA_FLOWTABLE_HOOK_MAX, attr, | ||
| 4984 | nft_flowtable_hook_policy, NULL); | ||
| 4985 | if (err < 0) | ||
| 4986 | return err; | ||
| 4987 | |||
| 4988 | if (!tb[NFTA_FLOWTABLE_HOOK_NUM] || | ||
| 4989 | !tb[NFTA_FLOWTABLE_HOOK_PRIORITY] || | ||
| 4990 | !tb[NFTA_FLOWTABLE_HOOK_DEVS]) | ||
| 4991 | return -EINVAL; | ||
| 4992 | |||
| 4993 | hooknum = ntohl(nla_get_be32(tb[NFTA_FLOWTABLE_HOOK_NUM])); | ||
| 4994 | if (hooknum >= ctx->afi->nhooks) | ||
| 4995 | return -EINVAL; | ||
| 4996 | |||
| 4997 | priority = ntohl(nla_get_be32(tb[NFTA_FLOWTABLE_HOOK_PRIORITY])); | ||
| 4998 | |||
| 4999 | err = nf_tables_parse_devices(ctx, tb[NFTA_FLOWTABLE_HOOK_DEVS], | ||
| 5000 | dev_array, &n); | ||
| 5001 | if (err < 0) | ||
| 5002 | goto err1; | ||
| 5003 | |||
| 5004 | ops = kzalloc(sizeof(struct nf_hook_ops) * n, GFP_KERNEL); | ||
| 5005 | if (!ops) { | ||
| 5006 | err = -ENOMEM; | ||
| 5007 | goto err1; | ||
| 5008 | } | ||
| 5009 | |||
| 5010 | flowtable->ops = ops; | ||
| 5011 | flowtable->ops_len = n; | ||
| 5012 | |||
| 5013 | for (i = 0; i < n; i++) { | ||
| 5014 | flowtable->ops[i].pf = NFPROTO_NETDEV; | ||
| 5015 | flowtable->ops[i].hooknum = hooknum; | ||
| 5016 | flowtable->ops[i].priority = priority; | ||
| 5017 | flowtable->ops[i].priv = &flowtable->data.rhashtable; | ||
| 5018 | flowtable->ops[i].hook = flowtable->data.type->hook; | ||
| 5019 | flowtable->ops[i].dev = dev_array[i]; | ||
| 5020 | } | ||
| 5021 | |||
| 5022 | err = 0; | ||
| 5023 | err1: | ||
| 5024 | for (i = 0; i < n; i++) | ||
| 5025 | dev_put(dev_array[i]); | ||
| 5026 | |||
| 5027 | return err; | ||
| 5028 | } | ||
| 5029 | |||
| 5030 | static const struct nf_flowtable_type * | ||
| 5031 | __nft_flowtable_type_get(const struct nft_af_info *afi) | ||
| 5032 | { | ||
| 5033 | const struct nf_flowtable_type *type; | ||
| 5034 | |||
| 5035 | list_for_each_entry(type, &nf_tables_flowtables, list) { | ||
| 5036 | if (afi->family == type->family) | ||
| 5037 | return type; | ||
| 5038 | } | ||
| 5039 | return NULL; | ||
| 5040 | } | ||
| 5041 | |||
| 5042 | static const struct nf_flowtable_type * | ||
| 5043 | nft_flowtable_type_get(const struct nft_af_info *afi) | ||
| 5044 | { | ||
| 5045 | const struct nf_flowtable_type *type; | ||
| 5046 | |||
| 5047 | type = __nft_flowtable_type_get(afi); | ||
| 5048 | if (type != NULL && try_module_get(type->owner)) | ||
| 5049 | return type; | ||
| 5050 | |||
| 5051 | #ifdef CONFIG_MODULES | ||
| 5052 | if (type == NULL) { | ||
| 5053 | nfnl_unlock(NFNL_SUBSYS_NFTABLES); | ||
| 5054 | request_module("nf-flowtable-%u", afi->family); | ||
| 5055 | nfnl_lock(NFNL_SUBSYS_NFTABLES); | ||
| 5056 | if (__nft_flowtable_type_get(afi)) | ||
| 5057 | return ERR_PTR(-EAGAIN); | ||
| 5058 | } | ||
| 5059 | #endif | ||
| 5060 | return ERR_PTR(-ENOENT); | ||
| 5061 | } | ||
| 5062 | |||
| 5063 | void nft_flow_table_iterate(struct net *net, | ||
| 5064 | void (*iter)(struct nf_flowtable *flowtable, void *data), | ||
| 5065 | void *data) | ||
| 5066 | { | ||
| 5067 | struct nft_flowtable *flowtable; | ||
| 5068 | const struct nft_af_info *afi; | ||
| 5069 | const struct nft_table *table; | ||
| 5070 | |||
| 5071 | rcu_read_lock(); | ||
| 5072 | list_for_each_entry_rcu(afi, &net->nft.af_info, list) { | ||
| 5073 | list_for_each_entry_rcu(table, &afi->tables, list) { | ||
| 5074 | list_for_each_entry_rcu(flowtable, &table->flowtables, list) { | ||
| 5075 | iter(&flowtable->data, data); | ||
| 5076 | } | ||
| 5077 | } | ||
| 5078 | } | ||
| 5079 | rcu_read_unlock(); | ||
| 5080 | } | ||
| 5081 | EXPORT_SYMBOL_GPL(nft_flow_table_iterate); | ||
| 5082 | |||
| 5083 | static void nft_unregister_flowtable_net_hooks(struct net *net, | ||
| 5084 | struct nft_flowtable *flowtable) | ||
| 5085 | { | ||
| 5086 | int i; | ||
| 5087 | |||
| 5088 | for (i = 0; i < flowtable->ops_len; i++) { | ||
| 5089 | if (!flowtable->ops[i].dev) | ||
| 5090 | continue; | ||
| 5091 | |||
| 5092 | nf_unregister_net_hook(net, &flowtable->ops[i]); | ||
| 5093 | } | ||
| 5094 | } | ||
| 5095 | |||
| 5096 | static int nf_tables_newflowtable(struct net *net, struct sock *nlsk, | ||
| 5097 | struct sk_buff *skb, | ||
| 5098 | const struct nlmsghdr *nlh, | ||
| 5099 | const struct nlattr * const nla[], | ||
| 5100 | struct netlink_ext_ack *extack) | ||
| 5101 | { | ||
| 5102 | const struct nfgenmsg *nfmsg = nlmsg_data(nlh); | ||
| 5103 | const struct nf_flowtable_type *type; | ||
| 5104 | u8 genmask = nft_genmask_next(net); | ||
| 5105 | int family = nfmsg->nfgen_family; | ||
| 5106 | struct nft_flowtable *flowtable; | ||
| 5107 | struct nft_af_info *afi; | ||
| 5108 | struct nft_table *table; | ||
| 5109 | struct nft_ctx ctx; | ||
| 5110 | int err, i, k; | ||
| 5111 | |||
| 5112 | if (!nla[NFTA_FLOWTABLE_TABLE] || | ||
| 5113 | !nla[NFTA_FLOWTABLE_NAME] || | ||
| 5114 | !nla[NFTA_FLOWTABLE_HOOK]) | ||
| 5115 | return -EINVAL; | ||
| 5116 | |||
| 5117 | afi = nf_tables_afinfo_lookup(net, family, true); | ||
| 5118 | if (IS_ERR(afi)) | ||
| 5119 | return PTR_ERR(afi); | ||
| 5120 | |||
| 5121 | table = nf_tables_table_lookup(afi, nla[NFTA_FLOWTABLE_TABLE], genmask); | ||
| 5122 | if (IS_ERR(table)) | ||
| 5123 | return PTR_ERR(table); | ||
| 5124 | |||
| 5125 | flowtable = nf_tables_flowtable_lookup(table, nla[NFTA_FLOWTABLE_NAME], | ||
| 5126 | genmask); | ||
| 5127 | if (IS_ERR(flowtable)) { | ||
| 5128 | err = PTR_ERR(flowtable); | ||
| 5129 | if (err != -ENOENT) | ||
| 5130 | return err; | ||
| 5131 | } else { | ||
| 5132 | if (nlh->nlmsg_flags & NLM_F_EXCL) | ||
| 5133 | return -EEXIST; | ||
| 5134 | |||
| 5135 | return 0; | ||
| 5136 | } | ||
| 5137 | |||
| 5138 | nft_ctx_init(&ctx, net, skb, nlh, afi, table, NULL, nla); | ||
| 5139 | |||
| 5140 | flowtable = kzalloc(sizeof(*flowtable), GFP_KERNEL); | ||
| 5141 | if (!flowtable) | ||
| 5142 | return -ENOMEM; | ||
| 5143 | |||
| 5144 | flowtable->table = table; | ||
| 5145 | flowtable->name = nla_strdup(nla[NFTA_FLOWTABLE_NAME], GFP_KERNEL); | ||
| 5146 | if (!flowtable->name) { | ||
| 5147 | err = -ENOMEM; | ||
| 5148 | goto err1; | ||
| 5149 | } | ||
| 5150 | |||
| 5151 | type = nft_flowtable_type_get(afi); | ||
| 5152 | if (IS_ERR(type)) { | ||
| 5153 | err = PTR_ERR(type); | ||
| 5154 | goto err2; | ||
| 5155 | } | ||
| 5156 | |||
| 5157 | flowtable->data.type = type; | ||
| 5158 | err = rhashtable_init(&flowtable->data.rhashtable, type->params); | ||
| 5159 | if (err < 0) | ||
| 5160 | goto err3; | ||
| 5161 | |||
| 5162 | err = nf_tables_flowtable_parse_hook(&ctx, nla[NFTA_FLOWTABLE_HOOK], | ||
| 5163 | flowtable); | ||
| 5164 | if (err < 0) | ||
| 5165 | goto err3; | ||
| 5166 | |||
| 5167 | for (i = 0; i < flowtable->ops_len; i++) { | ||
| 5168 | err = nf_register_net_hook(net, &flowtable->ops[i]); | ||
| 5169 | if (err < 0) | ||
| 5170 | goto err4; | ||
| 5171 | } | ||
| 5172 | |||
| 5173 | err = nft_trans_flowtable_add(&ctx, NFT_MSG_NEWFLOWTABLE, flowtable); | ||
| 5174 | if (err < 0) | ||
| 5175 | goto err5; | ||
| 5176 | |||
| 5177 | INIT_DEFERRABLE_WORK(&flowtable->data.gc_work, type->gc); | ||
| 5178 | queue_delayed_work(system_power_efficient_wq, | ||
| 5179 | &flowtable->data.gc_work, HZ); | ||
| 5180 | |||
| 5181 | list_add_tail_rcu(&flowtable->list, &table->flowtables); | ||
| 5182 | table->use++; | ||
| 5183 | |||
| 5184 | return 0; | ||
| 5185 | err5: | ||
| 5186 | i = flowtable->ops_len; | ||
| 5187 | err4: | ||
| 5188 | for (k = i - 1; k >= 0; k--) | ||
| 5189 | nf_unregister_net_hook(net, &flowtable->ops[i]); | ||
| 5190 | |||
| 5191 | kfree(flowtable->ops); | ||
| 5192 | err3: | ||
| 5193 | module_put(type->owner); | ||
| 5194 | err2: | ||
| 5195 | kfree(flowtable->name); | ||
| 5196 | err1: | ||
| 5197 | kfree(flowtable); | ||
| 5198 | return err; | ||
| 5199 | } | ||
| 5200 | |||
| 5201 | static int nf_tables_delflowtable(struct net *net, struct sock *nlsk, | ||
| 5202 | struct sk_buff *skb, | ||
| 5203 | const struct nlmsghdr *nlh, | ||
| 5204 | const struct nlattr * const nla[], | ||
| 5205 | struct netlink_ext_ack *extack) | ||
| 5206 | { | ||
| 5207 | const struct nfgenmsg *nfmsg = nlmsg_data(nlh); | ||
| 5208 | u8 genmask = nft_genmask_next(net); | ||
| 5209 | int family = nfmsg->nfgen_family; | ||
| 5210 | struct nft_flowtable *flowtable; | ||
| 5211 | struct nft_af_info *afi; | ||
| 5212 | struct nft_table *table; | ||
| 5213 | struct nft_ctx ctx; | ||
| 5214 | |||
| 5215 | afi = nf_tables_afinfo_lookup(net, family, true); | ||
| 5216 | if (IS_ERR(afi)) | ||
| 5217 | return PTR_ERR(afi); | ||
| 5218 | |||
| 5219 | table = nf_tables_table_lookup(afi, nla[NFTA_FLOWTABLE_TABLE], genmask); | ||
| 5220 | if (IS_ERR(table)) | ||
| 5221 | return PTR_ERR(table); | ||
| 5222 | |||
| 5223 | flowtable = nf_tables_flowtable_lookup(table, nla[NFTA_FLOWTABLE_NAME], | ||
| 5224 | genmask); | ||
| 5225 | if (IS_ERR(flowtable)) | ||
| 5226 | return PTR_ERR(flowtable); | ||
| 5227 | if (flowtable->use > 0) | ||
| 5228 | return -EBUSY; | ||
| 5229 | |||
| 5230 | nft_ctx_init(&ctx, net, skb, nlh, afi, table, NULL, nla); | ||
| 5231 | |||
| 5232 | return nft_delflowtable(&ctx, flowtable); | ||
| 5233 | } | ||
| 5234 | |||
| 5235 | static int nf_tables_fill_flowtable_info(struct sk_buff *skb, struct net *net, | ||
| 5236 | u32 portid, u32 seq, int event, | ||
| 5237 | u32 flags, int family, | ||
| 5238 | struct nft_flowtable *flowtable) | ||
| 5239 | { | ||
| 5240 | struct nlattr *nest, *nest_devs; | ||
| 5241 | struct nfgenmsg *nfmsg; | ||
| 5242 | struct nlmsghdr *nlh; | ||
| 5243 | int i; | ||
| 5244 | |||
| 5245 | event = nfnl_msg_type(NFNL_SUBSYS_NFTABLES, event); | ||
| 5246 | nlh = nlmsg_put(skb, portid, seq, event, sizeof(struct nfgenmsg), flags); | ||
| 5247 | if (nlh == NULL) | ||
| 5248 | goto nla_put_failure; | ||
| 5249 | |||
| 5250 | nfmsg = nlmsg_data(nlh); | ||
| 5251 | nfmsg->nfgen_family = family; | ||
| 5252 | nfmsg->version = NFNETLINK_V0; | ||
| 5253 | nfmsg->res_id = htons(net->nft.base_seq & 0xffff); | ||
| 5254 | |||
| 5255 | if (nla_put_string(skb, NFTA_FLOWTABLE_TABLE, flowtable->table->name) || | ||
| 5256 | nla_put_string(skb, NFTA_FLOWTABLE_NAME, flowtable->name) || | ||
| 5257 | nla_put_be32(skb, NFTA_FLOWTABLE_USE, htonl(flowtable->use))) | ||
| 5258 | goto nla_put_failure; | ||
| 5259 | |||
| 5260 | nest = nla_nest_start(skb, NFTA_FLOWTABLE_HOOK); | ||
| 5261 | if (nla_put_be32(skb, NFTA_FLOWTABLE_HOOK_NUM, htonl(flowtable->hooknum)) || | ||
| 5262 | nla_put_be32(skb, NFTA_FLOWTABLE_HOOK_PRIORITY, htonl(flowtable->priority))) | ||
| 5263 | goto nla_put_failure; | ||
| 5264 | |||
| 5265 | nest_devs = nla_nest_start(skb, NFTA_FLOWTABLE_HOOK_DEVS); | ||
| 5266 | if (!nest_devs) | ||
| 5267 | goto nla_put_failure; | ||
| 5268 | |||
| 5269 | for (i = 0; i < flowtable->ops_len; i++) { | ||
| 5270 | if (flowtable->ops[i].dev && | ||
| 5271 | nla_put_string(skb, NFTA_DEVICE_NAME, | ||
| 5272 | flowtable->ops[i].dev->name)) | ||
| 5273 | goto nla_put_failure; | ||
| 5274 | } | ||
| 5275 | nla_nest_end(skb, nest_devs); | ||
| 5276 | nla_nest_end(skb, nest); | ||
| 5277 | |||
| 5278 | nlmsg_end(skb, nlh); | ||
| 5279 | return 0; | ||
| 5280 | |||
| 5281 | nla_put_failure: | ||
| 5282 | nlmsg_trim(skb, nlh); | ||
| 5283 | return -1; | ||
| 5284 | } | ||
| 5285 | |||
| 5286 | struct nft_flowtable_filter { | ||
| 5287 | char *table; | ||
| 5288 | }; | ||
| 5289 | |||
| 5290 | static int nf_tables_dump_flowtable(struct sk_buff *skb, | ||
| 5291 | struct netlink_callback *cb) | ||
| 5292 | { | ||
| 5293 | const struct nfgenmsg *nfmsg = nlmsg_data(cb->nlh); | ||
| 5294 | struct nft_flowtable_filter *filter = cb->data; | ||
| 5295 | unsigned int idx = 0, s_idx = cb->args[0]; | ||
| 5296 | struct net *net = sock_net(skb->sk); | ||
| 5297 | int family = nfmsg->nfgen_family; | ||
| 5298 | struct nft_flowtable *flowtable; | ||
| 5299 | const struct nft_af_info *afi; | ||
| 5300 | const struct nft_table *table; | ||
| 5301 | |||
| 5302 | rcu_read_lock(); | ||
| 5303 | cb->seq = net->nft.base_seq; | ||
| 5304 | |||
| 5305 | list_for_each_entry_rcu(afi, &net->nft.af_info, list) { | ||
| 5306 | if (family != NFPROTO_UNSPEC && family != afi->family) | ||
| 5307 | continue; | ||
| 5308 | |||
| 5309 | list_for_each_entry_rcu(table, &afi->tables, list) { | ||
| 5310 | list_for_each_entry_rcu(flowtable, &table->flowtables, list) { | ||
| 5311 | if (!nft_is_active(net, flowtable)) | ||
| 5312 | goto cont; | ||
| 5313 | if (idx < s_idx) | ||
| 5314 | goto cont; | ||
| 5315 | if (idx > s_idx) | ||
| 5316 | memset(&cb->args[1], 0, | ||
| 5317 | sizeof(cb->args) - sizeof(cb->args[0])); | ||
| 5318 | if (filter && filter->table[0] && | ||
| 5319 | strcmp(filter->table, table->name)) | ||
| 5320 | goto cont; | ||
| 5321 | |||
| 5322 | if (nf_tables_fill_flowtable_info(skb, net, NETLINK_CB(cb->skb).portid, | ||
| 5323 | cb->nlh->nlmsg_seq, | ||
| 5324 | NFT_MSG_NEWFLOWTABLE, | ||
| 5325 | NLM_F_MULTI | NLM_F_APPEND, | ||
| 5326 | afi->family, flowtable) < 0) | ||
| 5327 | goto done; | ||
| 5328 | |||
| 5329 | nl_dump_check_consistent(cb, nlmsg_hdr(skb)); | ||
| 5330 | cont: | ||
| 5331 | idx++; | ||
| 5332 | } | ||
| 5333 | } | ||
| 5334 | } | ||
| 5335 | done: | ||
| 5336 | rcu_read_unlock(); | ||
| 5337 | |||
| 5338 | cb->args[0] = idx; | ||
| 5339 | return skb->len; | ||
| 5340 | } | ||
| 5341 | |||
| 5342 | static int nf_tables_dump_flowtable_done(struct netlink_callback *cb) | ||
| 5343 | { | ||
| 5344 | struct nft_flowtable_filter *filter = cb->data; | ||
| 5345 | |||
| 5346 | if (!filter) | ||
| 5347 | return 0; | ||
| 5348 | |||
| 5349 | kfree(filter->table); | ||
| 5350 | kfree(filter); | ||
| 5351 | |||
| 5352 | return 0; | ||
| 5353 | } | ||
| 5354 | |||
| 5355 | static struct nft_flowtable_filter * | ||
| 5356 | nft_flowtable_filter_alloc(const struct nlattr * const nla[]) | ||
| 5357 | { | ||
| 5358 | struct nft_flowtable_filter *filter; | ||
| 5359 | |||
| 5360 | filter = kzalloc(sizeof(*filter), GFP_KERNEL); | ||
| 5361 | if (!filter) | ||
| 5362 | return ERR_PTR(-ENOMEM); | ||
| 5363 | |||
| 5364 | if (nla[NFTA_FLOWTABLE_TABLE]) { | ||
| 5365 | filter->table = nla_strdup(nla[NFTA_FLOWTABLE_TABLE], | ||
| 5366 | GFP_KERNEL); | ||
| 5367 | if (!filter->table) { | ||
| 5368 | kfree(filter); | ||
| 5369 | return ERR_PTR(-ENOMEM); | ||
| 5370 | } | ||
| 5371 | } | ||
| 5372 | return filter; | ||
| 5373 | } | ||
| 5374 | |||
| 5375 | static int nf_tables_getflowtable(struct net *net, struct sock *nlsk, | ||
| 5376 | struct sk_buff *skb, | ||
| 5377 | const struct nlmsghdr *nlh, | ||
| 5378 | const struct nlattr * const nla[], | ||
| 5379 | struct netlink_ext_ack *extack) | ||
| 5380 | { | ||
| 5381 | const struct nfgenmsg *nfmsg = nlmsg_data(nlh); | ||
| 5382 | u8 genmask = nft_genmask_cur(net); | ||
| 5383 | int family = nfmsg->nfgen_family; | ||
| 5384 | struct nft_flowtable *flowtable; | ||
| 5385 | const struct nft_af_info *afi; | ||
| 5386 | const struct nft_table *table; | ||
| 5387 | struct sk_buff *skb2; | ||
| 5388 | int err; | ||
| 5389 | |||
| 5390 | if (nlh->nlmsg_flags & NLM_F_DUMP) { | ||
| 5391 | struct netlink_dump_control c = { | ||
| 5392 | .dump = nf_tables_dump_flowtable, | ||
| 5393 | .done = nf_tables_dump_flowtable_done, | ||
| 5394 | }; | ||
| 5395 | |||
| 5396 | if (nla[NFTA_FLOWTABLE_TABLE]) { | ||
| 5397 | struct nft_flowtable_filter *filter; | ||
| 5398 | |||
| 5399 | filter = nft_flowtable_filter_alloc(nla); | ||
| 5400 | if (IS_ERR(filter)) | ||
| 5401 | return -ENOMEM; | ||
| 5402 | |||
| 5403 | c.data = filter; | ||
| 5404 | } | ||
| 5405 | return netlink_dump_start(nlsk, skb, nlh, &c); | ||
| 5406 | } | ||
| 5407 | |||
| 5408 | if (!nla[NFTA_FLOWTABLE_NAME]) | ||
| 5409 | return -EINVAL; | ||
| 5410 | |||
| 5411 | afi = nf_tables_afinfo_lookup(net, family, false); | ||
| 5412 | if (IS_ERR(afi)) | ||
| 5413 | return PTR_ERR(afi); | ||
| 5414 | |||
| 5415 | table = nf_tables_table_lookup(afi, nla[NFTA_FLOWTABLE_TABLE], genmask); | ||
| 5416 | if (IS_ERR(table)) | ||
| 5417 | return PTR_ERR(table); | ||
| 5418 | |||
| 5419 | flowtable = nf_tables_flowtable_lookup(table, nla[NFTA_FLOWTABLE_NAME], | ||
| 5420 | genmask); | ||
| 5421 | if (IS_ERR(table)) | ||
| 5422 | return PTR_ERR(flowtable); | ||
| 5423 | |||
| 5424 | skb2 = alloc_skb(NLMSG_GOODSIZE, GFP_KERNEL); | ||
| 5425 | if (!skb2) | ||
| 5426 | return -ENOMEM; | ||
| 5427 | |||
| 5428 | err = nf_tables_fill_flowtable_info(skb2, net, NETLINK_CB(skb).portid, | ||
| 5429 | nlh->nlmsg_seq, | ||
| 5430 | NFT_MSG_NEWFLOWTABLE, 0, family, | ||
| 5431 | flowtable); | ||
| 5432 | if (err < 0) | ||
| 5433 | goto err; | ||
| 5434 | |||
| 5435 | return nlmsg_unicast(nlsk, skb2, NETLINK_CB(skb).portid); | ||
| 5436 | err: | ||
| 5437 | kfree_skb(skb2); | ||
| 5438 | return err; | ||
| 5439 | } | ||
| 5440 | |||
| 5441 | static void nf_tables_flowtable_notify(struct nft_ctx *ctx, | ||
| 5442 | struct nft_flowtable *flowtable, | ||
| 5443 | int event) | ||
| 5444 | { | ||
| 5445 | struct sk_buff *skb; | ||
| 5446 | int err; | ||
| 5447 | |||
| 5448 | if (ctx->report && | ||
| 5449 | !nfnetlink_has_listeners(ctx->net, NFNLGRP_NFTABLES)) | ||
| 5450 | return; | ||
| 5451 | |||
| 5452 | skb = nlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL); | ||
| 5453 | if (skb == NULL) | ||
| 5454 | goto err; | ||
| 5455 | |||
| 5456 | err = nf_tables_fill_flowtable_info(skb, ctx->net, ctx->portid, | ||
| 5457 | ctx->seq, event, 0, | ||
| 5458 | ctx->afi->family, flowtable); | ||
| 5459 | if (err < 0) { | ||
| 5460 | kfree_skb(skb); | ||
| 5461 | goto err; | ||
| 5462 | } | ||
| 5463 | |||
| 5464 | nfnetlink_send(skb, ctx->net, ctx->portid, NFNLGRP_NFTABLES, | ||
| 5465 | ctx->report, GFP_KERNEL); | ||
| 5466 | return; | ||
| 5467 | err: | ||
| 5468 | nfnetlink_set_err(ctx->net, ctx->portid, NFNLGRP_NFTABLES, -ENOBUFS); | ||
| 5469 | } | ||
| 5470 | |||
| 5471 | static void nft_flowtable_destroy(void *ptr, void *arg) | ||
| 5472 | { | ||
| 5473 | kfree(ptr); | ||
| 5474 | } | ||
| 5475 | |||
| 5476 | static void nf_tables_flowtable_destroy(struct nft_flowtable *flowtable) | ||
| 5477 | { | ||
| 5478 | cancel_delayed_work_sync(&flowtable->data.gc_work); | ||
| 5479 | kfree(flowtable->name); | ||
| 5480 | rhashtable_free_and_destroy(&flowtable->data.rhashtable, | ||
| 5481 | nft_flowtable_destroy, NULL); | ||
| 5482 | module_put(flowtable->data.type->owner); | ||
| 5483 | } | ||
| 5484 | |||
| 4842 | static int nf_tables_fill_gen_info(struct sk_buff *skb, struct net *net, | 5485 | static int nf_tables_fill_gen_info(struct sk_buff *skb, struct net *net, |
| 4843 | u32 portid, u32 seq) | 5486 | u32 portid, u32 seq) |
| 4844 | { | 5487 | { |
| @@ -4869,6 +5512,49 @@ nla_put_failure: | |||
| 4869 | return -EMSGSIZE; | 5512 | return -EMSGSIZE; |
| 4870 | } | 5513 | } |
| 4871 | 5514 | ||
| 5515 | static void nft_flowtable_event(unsigned long event, struct net_device *dev, | ||
| 5516 | struct nft_flowtable *flowtable) | ||
| 5517 | { | ||
| 5518 | int i; | ||
| 5519 | |||
| 5520 | for (i = 0; i < flowtable->ops_len; i++) { | ||
| 5521 | if (flowtable->ops[i].dev != dev) | ||
| 5522 | continue; | ||
| 5523 | |||
| 5524 | nf_unregister_net_hook(dev_net(dev), &flowtable->ops[i]); | ||
| 5525 | flowtable->ops[i].dev = NULL; | ||
| 5526 | break; | ||
| 5527 | } | ||
| 5528 | } | ||
| 5529 | |||
| 5530 | static int nf_tables_flowtable_event(struct notifier_block *this, | ||
| 5531 | unsigned long event, void *ptr) | ||
| 5532 | { | ||
| 5533 | struct net_device *dev = netdev_notifier_info_to_dev(ptr); | ||
| 5534 | struct nft_flowtable *flowtable; | ||
| 5535 | struct nft_table *table; | ||
| 5536 | struct nft_af_info *afi; | ||
| 5537 | |||
| 5538 | if (event != NETDEV_UNREGISTER) | ||
| 5539 | return 0; | ||
| 5540 | |||
| 5541 | nfnl_lock(NFNL_SUBSYS_NFTABLES); | ||
| 5542 | list_for_each_entry(afi, &dev_net(dev)->nft.af_info, list) { | ||
| 5543 | list_for_each_entry(table, &afi->tables, list) { | ||
| 5544 | list_for_each_entry(flowtable, &table->flowtables, list) { | ||
| 5545 | nft_flowtable_event(event, dev, flowtable); | ||
| 5546 | } | ||
| 5547 | } | ||
| 5548 | } | ||
| 5549 | nfnl_unlock(NFNL_SUBSYS_NFTABLES); | ||
| 5550 | |||
| 5551 | return NOTIFY_DONE; | ||
| 5552 | } | ||
| 5553 | |||
| 5554 | static struct notifier_block nf_tables_flowtable_notifier = { | ||
| 5555 | .notifier_call = nf_tables_flowtable_event, | ||
| 5556 | }; | ||
| 5557 | |||
| 4872 | static void nf_tables_gen_notify(struct net *net, struct sk_buff *skb, | 5558 | static void nf_tables_gen_notify(struct net *net, struct sk_buff *skb, |
| 4873 | int event) | 5559 | int event) |
| 4874 | { | 5560 | { |
| @@ -5021,6 +5707,21 @@ static const struct nfnl_callback nf_tables_cb[NFT_MSG_MAX] = { | |||
| 5021 | .attr_count = NFTA_OBJ_MAX, | 5707 | .attr_count = NFTA_OBJ_MAX, |
| 5022 | .policy = nft_obj_policy, | 5708 | .policy = nft_obj_policy, |
| 5023 | }, | 5709 | }, |
| 5710 | [NFT_MSG_NEWFLOWTABLE] = { | ||
| 5711 | .call_batch = nf_tables_newflowtable, | ||
| 5712 | .attr_count = NFTA_FLOWTABLE_MAX, | ||
| 5713 | .policy = nft_flowtable_policy, | ||
| 5714 | }, | ||
| 5715 | [NFT_MSG_GETFLOWTABLE] = { | ||
| 5716 | .call = nf_tables_getflowtable, | ||
| 5717 | .attr_count = NFTA_FLOWTABLE_MAX, | ||
| 5718 | .policy = nft_flowtable_policy, | ||
| 5719 | }, | ||
| 5720 | [NFT_MSG_DELFLOWTABLE] = { | ||
| 5721 | .call_batch = nf_tables_delflowtable, | ||
| 5722 | .attr_count = NFTA_FLOWTABLE_MAX, | ||
| 5723 | .policy = nft_flowtable_policy, | ||
| 5724 | }, | ||
| 5024 | }; | 5725 | }; |
| 5025 | 5726 | ||
| 5026 | static void nft_chain_commit_update(struct nft_trans *trans) | 5727 | static void nft_chain_commit_update(struct nft_trans *trans) |
| @@ -5066,6 +5767,9 @@ static void nf_tables_commit_release(struct nft_trans *trans) | |||
| 5066 | case NFT_MSG_DELOBJ: | 5767 | case NFT_MSG_DELOBJ: |
| 5067 | nft_obj_destroy(nft_trans_obj(trans)); | 5768 | nft_obj_destroy(nft_trans_obj(trans)); |
| 5068 | break; | 5769 | break; |
| 5770 | case NFT_MSG_DELFLOWTABLE: | ||
| 5771 | nf_tables_flowtable_destroy(nft_trans_flowtable(trans)); | ||
| 5772 | break; | ||
| 5069 | } | 5773 | } |
| 5070 | kfree(trans); | 5774 | kfree(trans); |
| 5071 | } | 5775 | } |
| @@ -5183,6 +5887,21 @@ static int nf_tables_commit(struct net *net, struct sk_buff *skb) | |||
| 5183 | nf_tables_obj_notify(&trans->ctx, nft_trans_obj(trans), | 5887 | nf_tables_obj_notify(&trans->ctx, nft_trans_obj(trans), |
| 5184 | NFT_MSG_DELOBJ); | 5888 | NFT_MSG_DELOBJ); |
| 5185 | break; | 5889 | break; |
| 5890 | case NFT_MSG_NEWFLOWTABLE: | ||
| 5891 | nft_clear(net, nft_trans_flowtable(trans)); | ||
| 5892 | nf_tables_flowtable_notify(&trans->ctx, | ||
| 5893 | nft_trans_flowtable(trans), | ||
| 5894 | NFT_MSG_NEWFLOWTABLE); | ||
| 5895 | nft_trans_destroy(trans); | ||
| 5896 | break; | ||
| 5897 | case NFT_MSG_DELFLOWTABLE: | ||
| 5898 | list_del_rcu(&nft_trans_flowtable(trans)->list); | ||
| 5899 | nf_tables_flowtable_notify(&trans->ctx, | ||
| 5900 | nft_trans_flowtable(trans), | ||
| 5901 | NFT_MSG_DELFLOWTABLE); | ||
| 5902 | nft_unregister_flowtable_net_hooks(net, | ||
| 5903 | nft_trans_flowtable(trans)); | ||
| 5904 | break; | ||
| 5186 | } | 5905 | } |
| 5187 | } | 5906 | } |
| 5188 | 5907 | ||
| @@ -5220,6 +5939,9 @@ static void nf_tables_abort_release(struct nft_trans *trans) | |||
| 5220 | case NFT_MSG_NEWOBJ: | 5939 | case NFT_MSG_NEWOBJ: |
| 5221 | nft_obj_destroy(nft_trans_obj(trans)); | 5940 | nft_obj_destroy(nft_trans_obj(trans)); |
| 5222 | break; | 5941 | break; |
| 5942 | case NFT_MSG_NEWFLOWTABLE: | ||
| 5943 | nf_tables_flowtable_destroy(nft_trans_flowtable(trans)); | ||
| 5944 | break; | ||
| 5223 | } | 5945 | } |
| 5224 | kfree(trans); | 5946 | kfree(trans); |
| 5225 | } | 5947 | } |
| @@ -5309,6 +6031,17 @@ static int nf_tables_abort(struct net *net, struct sk_buff *skb) | |||
| 5309 | nft_clear(trans->ctx.net, nft_trans_obj(trans)); | 6031 | nft_clear(trans->ctx.net, nft_trans_obj(trans)); |
| 5310 | nft_trans_destroy(trans); | 6032 | nft_trans_destroy(trans); |
| 5311 | break; | 6033 | break; |
| 6034 | case NFT_MSG_NEWFLOWTABLE: | ||
| 6035 | trans->ctx.table->use--; | ||
| 6036 | list_del_rcu(&nft_trans_flowtable(trans)->list); | ||
| 6037 | nft_unregister_flowtable_net_hooks(net, | ||
| 6038 | nft_trans_flowtable(trans)); | ||
| 6039 | break; | ||
| 6040 | case NFT_MSG_DELFLOWTABLE: | ||
| 6041 | trans->ctx.table->use++; | ||
| 6042 | nft_clear(trans->ctx.net, nft_trans_flowtable(trans)); | ||
| 6043 | nft_trans_destroy(trans); | ||
| 6044 | break; | ||
| 5312 | } | 6045 | } |
| 5313 | } | 6046 | } |
| 5314 | 6047 | ||
| @@ -5865,6 +6598,7 @@ EXPORT_SYMBOL_GPL(__nft_release_basechain); | |||
| 5865 | /* Called by nft_unregister_afinfo() from __net_exit path, nfnl_lock is held. */ | 6598 | /* Called by nft_unregister_afinfo() from __net_exit path, nfnl_lock is held. */ |
| 5866 | static void __nft_release_afinfo(struct net *net, struct nft_af_info *afi) | 6599 | static void __nft_release_afinfo(struct net *net, struct nft_af_info *afi) |
| 5867 | { | 6600 | { |
| 6601 | struct nft_flowtable *flowtable, *nf; | ||
| 5868 | struct nft_table *table, *nt; | 6602 | struct nft_table *table, *nt; |
| 5869 | struct nft_chain *chain, *nc; | 6603 | struct nft_chain *chain, *nc; |
| 5870 | struct nft_object *obj, *ne; | 6604 | struct nft_object *obj, *ne; |
| @@ -5878,6 +6612,9 @@ static void __nft_release_afinfo(struct net *net, struct nft_af_info *afi) | |||
| 5878 | list_for_each_entry_safe(table, nt, &afi->tables, list) { | 6612 | list_for_each_entry_safe(table, nt, &afi->tables, list) { |
| 5879 | list_for_each_entry(chain, &table->chains, list) | 6613 | list_for_each_entry(chain, &table->chains, list) |
| 5880 | nf_tables_unregister_hook(net, table, chain); | 6614 | nf_tables_unregister_hook(net, table, chain); |
| 6615 | list_for_each_entry(flowtable, &table->flowtables, list) | ||
| 6616 | nf_unregister_net_hooks(net, flowtable->ops, | ||
| 6617 | flowtable->ops_len); | ||
| 5881 | /* No packets are walking on these chains anymore. */ | 6618 | /* No packets are walking on these chains anymore. */ |
| 5882 | ctx.table = table; | 6619 | ctx.table = table; |
| 5883 | list_for_each_entry(chain, &table->chains, list) { | 6620 | list_for_each_entry(chain, &table->chains, list) { |
| @@ -5888,6 +6625,11 @@ static void __nft_release_afinfo(struct net *net, struct nft_af_info *afi) | |||
| 5888 | nf_tables_rule_destroy(&ctx, rule); | 6625 | nf_tables_rule_destroy(&ctx, rule); |
| 5889 | } | 6626 | } |
| 5890 | } | 6627 | } |
| 6628 | list_for_each_entry_safe(flowtable, nf, &table->flowtables, list) { | ||
| 6629 | list_del(&flowtable->list); | ||
| 6630 | table->use--; | ||
| 6631 | nf_tables_flowtable_destroy(flowtable); | ||
| 6632 | } | ||
| 5891 | list_for_each_entry_safe(set, ns, &table->sets, list) { | 6633 | list_for_each_entry_safe(set, ns, &table->sets, list) { |
| 5892 | list_del(&set->list); | 6634 | list_del(&set->list); |
| 5893 | table->use--; | 6635 | table->use--; |
| @@ -5932,6 +6674,8 @@ static int __init nf_tables_module_init(void) | |||
| 5932 | if (err < 0) | 6674 | if (err < 0) |
| 5933 | goto err3; | 6675 | goto err3; |
| 5934 | 6676 | ||
| 6677 | register_netdevice_notifier(&nf_tables_flowtable_notifier); | ||
| 6678 | |||
| 5935 | pr_info("nf_tables: (c) 2007-2009 Patrick McHardy <kaber@trash.net>\n"); | 6679 | pr_info("nf_tables: (c) 2007-2009 Patrick McHardy <kaber@trash.net>\n"); |
| 5936 | return register_pernet_subsys(&nf_tables_net_ops); | 6680 | return register_pernet_subsys(&nf_tables_net_ops); |
| 5937 | err3: | 6681 | err3: |
| @@ -5946,6 +6690,7 @@ static void __exit nf_tables_module_exit(void) | |||
| 5946 | { | 6690 | { |
| 5947 | unregister_pernet_subsys(&nf_tables_net_ops); | 6691 | unregister_pernet_subsys(&nf_tables_net_ops); |
| 5948 | nfnetlink_subsys_unregister(&nf_tables_subsys); | 6692 | nfnetlink_subsys_unregister(&nf_tables_subsys); |
| 6693 | unregister_netdevice_notifier(&nf_tables_flowtable_notifier); | ||
| 5949 | rcu_barrier(); | 6694 | rcu_barrier(); |
| 5950 | nf_tables_core_module_exit(); | 6695 | nf_tables_core_module_exit(); |
| 5951 | kfree(info); | 6696 | kfree(info); |
