321-v4.16-netfilter-nf_tables-add-flow-table-netlink-frontend.patch 29 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079
  1. From: Pablo Neira Ayuso <pablo@netfilter.org>
  2. Date: Sun, 7 Jan 2018 01:04:07 +0100
  3. Subject: [PATCH] netfilter: nf_tables: add flow table netlink frontend
  4. This patch introduces a netlink control plane to create, delete and dump
  5. flow tables. Flow tables are identified by name, this name is used from
  6. rules to refer to an specific flow table. Flow tables use the rhashtable
  7. class and a generic garbage collector to remove expired entries.
  8. This also adds the infrastructure to add different flow table types, so
  9. we can add one for each layer 3 protocol family.
  10. Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
  11. ---
  12. create mode 100644 include/net/netfilter/nf_flow_table.h
  13. --- /dev/null
  14. +++ b/include/net/netfilter/nf_flow_table.h
  15. @@ -0,0 +1,23 @@
  16. +#ifndef _NF_FLOW_TABLE_H
  17. +#define _NF_FLOW_TABLE_H
  18. +
  19. +#include <linux/rhashtable.h>
  20. +
  21. +struct nf_flowtable;
  22. +
  23. +struct nf_flowtable_type {
  24. + struct list_head list;
  25. + int family;
  26. + void (*gc)(struct work_struct *work);
  27. + const struct rhashtable_params *params;
  28. + nf_hookfn *hook;
  29. + struct module *owner;
  30. +};
  31. +
  32. +struct nf_flowtable {
  33. + struct rhashtable rhashtable;
  34. + const struct nf_flowtable_type *type;
  35. + struct delayed_work gc_work;
  36. +};
  37. +
  38. +#endif /* _FLOW_OFFLOAD_H */
  39. --- a/include/net/netfilter/nf_tables.h
  40. +++ b/include/net/netfilter/nf_tables.h
  41. @@ -9,6 +9,7 @@
  42. #include <linux/netfilter/x_tables.h>
  43. #include <linux/netfilter/nf_tables.h>
  44. #include <linux/u64_stats_sync.h>
  45. +#include <net/netfilter/nf_flow_table.h>
  46. #include <net/netlink.h>
  47. #define NFT_JUMP_STACK_SIZE 16
  48. @@ -941,6 +942,7 @@ unsigned int nft_do_chain(struct nft_pkt
  49. * @chains: chains in the table
  50. * @sets: sets in the table
  51. * @objects: stateful objects in the table
  52. + * @flowtables: flow tables in the table
  53. * @hgenerator: handle generator state
  54. * @use: number of chain references to this table
  55. * @flags: table flag (see enum nft_table_flags)
  56. @@ -952,6 +954,7 @@ struct nft_table {
  57. struct list_head chains;
  58. struct list_head sets;
  59. struct list_head objects;
  60. + struct list_head flowtables;
  61. u64 hgenerator;
  62. u32 use;
  63. u16 flags:14,
  64. @@ -1083,6 +1086,44 @@ int nft_register_obj(struct nft_object_t
  65. void nft_unregister_obj(struct nft_object_type *obj_type);
  66. /**
  67. + * struct nft_flowtable - nf_tables flow table
  68. + *
  69. + * @list: flow table list node in table list
  70. + * @table: the table the flow table is contained in
  71. + * @name: name of this flow table
  72. + * @hooknum: hook number
  73. + * @priority: hook priority
  74. + * @ops_len: number of hooks in array
  75. + * @genmask: generation mask
  76. + * @use: number of references to this flow table
  77. + * @data: rhashtable and garbage collector
  78. + * @ops: array of hooks
  79. + */
  80. +struct nft_flowtable {
  81. + struct list_head list;
  82. + struct nft_table *table;
  83. + char *name;
  84. + int hooknum;
  85. + int priority;
  86. + int ops_len;
  87. + u32 genmask:2,
  88. + use:30;
  89. + /* runtime data below here */
  90. + struct nf_hook_ops *ops ____cacheline_aligned;
  91. + struct nf_flowtable data;
  92. +};
  93. +
  94. +struct nft_flowtable *nf_tables_flowtable_lookup(const struct nft_table *table,
  95. + const struct nlattr *nla,
  96. + u8 genmask);
  97. +void nft_flow_table_iterate(struct net *net,
  98. + void (*iter)(struct nf_flowtable *flowtable, void *data),
  99. + void *data);
  100. +
  101. +void nft_register_flowtable_type(struct nf_flowtable_type *type);
  102. +void nft_unregister_flowtable_type(struct nf_flowtable_type *type);
  103. +
  104. +/**
  105. * struct nft_traceinfo - nft tracing information and state
  106. *
  107. * @pkt: pktinfo currently processed
  108. @@ -1318,4 +1359,11 @@ struct nft_trans_obj {
  109. #define nft_trans_obj(trans) \
  110. (((struct nft_trans_obj *)trans->data)->obj)
  111. +struct nft_trans_flowtable {
  112. + struct nft_flowtable *flowtable;
  113. +};
  114. +
  115. +#define nft_trans_flowtable(trans) \
  116. + (((struct nft_trans_flowtable *)trans->data)->flowtable)
  117. +
  118. #endif /* _NET_NF_TABLES_H */
  119. --- a/include/uapi/linux/netfilter/nf_tables.h
  120. +++ b/include/uapi/linux/netfilter/nf_tables.h
  121. @@ -92,6 +92,9 @@ enum nft_verdicts {
  122. * @NFT_MSG_GETOBJ: get a stateful object (enum nft_obj_attributes)
  123. * @NFT_MSG_DELOBJ: delete a stateful object (enum nft_obj_attributes)
  124. * @NFT_MSG_GETOBJ_RESET: get and reset a stateful object (enum nft_obj_attributes)
  125. + * @NFT_MSG_NEWFLOWTABLE: add new flow table (enum nft_flowtable_attributes)
  126. + * @NFT_MSG_GETFLOWTABLE: get flow table (enum nft_flowtable_attributes)
  127. + * @NFT_MSG_DELFLOWTABLE: delete flow table (enum nft_flowtable_attributes)
  128. */
  129. enum nf_tables_msg_types {
  130. NFT_MSG_NEWTABLE,
  131. @@ -116,6 +119,9 @@ enum nf_tables_msg_types {
  132. NFT_MSG_GETOBJ,
  133. NFT_MSG_DELOBJ,
  134. NFT_MSG_GETOBJ_RESET,
  135. + NFT_MSG_NEWFLOWTABLE,
  136. + NFT_MSG_GETFLOWTABLE,
  137. + NFT_MSG_DELFLOWTABLE,
  138. NFT_MSG_MAX,
  139. };
  140. @@ -1310,6 +1316,53 @@ enum nft_object_attributes {
  141. #define NFTA_OBJ_MAX (__NFTA_OBJ_MAX - 1)
  142. /**
  143. + * enum nft_flowtable_attributes - nf_tables flow table netlink attributes
  144. + *
  145. + * @NFTA_FLOWTABLE_TABLE: name of the table containing the expression (NLA_STRING)
  146. + * @NFTA_FLOWTABLE_NAME: name of this flow table (NLA_STRING)
  147. + * @NFTA_FLOWTABLE_HOOK: netfilter hook configuration(NLA_U32)
  148. + * @NFTA_FLOWTABLE_USE: number of references to this flow table (NLA_U32)
  149. + */
  150. +enum nft_flowtable_attributes {
  151. + NFTA_FLOWTABLE_UNSPEC,
  152. + NFTA_FLOWTABLE_TABLE,
  153. + NFTA_FLOWTABLE_NAME,
  154. + NFTA_FLOWTABLE_HOOK,
  155. + NFTA_FLOWTABLE_USE,
  156. + __NFTA_FLOWTABLE_MAX
  157. +};
  158. +#define NFTA_FLOWTABLE_MAX (__NFTA_FLOWTABLE_MAX - 1)
  159. +
  160. +/**
  161. + * enum nft_flowtable_hook_attributes - nf_tables flow table hook netlink attributes
  162. + *
  163. + * @NFTA_FLOWTABLE_HOOK_NUM: netfilter hook number (NLA_U32)
  164. + * @NFTA_FLOWTABLE_HOOK_PRIORITY: netfilter hook priority (NLA_U32)
  165. + * @NFTA_FLOWTABLE_HOOK_DEVS: input devices this flow table is bound to (NLA_NESTED)
  166. + */
  167. +enum nft_flowtable_hook_attributes {
  168. + NFTA_FLOWTABLE_HOOK_UNSPEC,
  169. + NFTA_FLOWTABLE_HOOK_NUM,
  170. + NFTA_FLOWTABLE_HOOK_PRIORITY,
  171. + NFTA_FLOWTABLE_HOOK_DEVS,
  172. + __NFTA_FLOWTABLE_HOOK_MAX
  173. +};
  174. +#define NFTA_FLOWTABLE_HOOK_MAX (__NFTA_FLOWTABLE_HOOK_MAX - 1)
  175. +
  176. +/**
  177. + * enum nft_device_attributes - nf_tables device netlink attributes
  178. + *
  179. + * @NFTA_DEVICE_NAME: name of this device (NLA_STRING)
  180. + */
  181. +enum nft_devices_attributes {
  182. + NFTA_DEVICE_UNSPEC,
  183. + NFTA_DEVICE_NAME,
  184. + __NFTA_DEVICE_MAX
  185. +};
  186. +#define NFTA_DEVICE_MAX (__NFTA_DEVICE_MAX - 1)
  187. +
  188. +
  189. +/**
  190. * enum nft_trace_attributes - nf_tables trace netlink attributes
  191. *
  192. * @NFTA_TRACE_TABLE: name of the table (NLA_STRING)
  193. --- a/net/netfilter/nf_tables_api.c
  194. +++ b/net/netfilter/nf_tables_api.c
  195. @@ -17,6 +17,7 @@
  196. #include <linux/netfilter.h>
  197. #include <linux/netfilter/nfnetlink.h>
  198. #include <linux/netfilter/nf_tables.h>
  199. +#include <net/netfilter/nf_flow_table.h>
  200. #include <net/netfilter/nf_tables_core.h>
  201. #include <net/netfilter/nf_tables.h>
  202. #include <net/net_namespace.h>
  203. @@ -24,6 +25,7 @@
  204. static LIST_HEAD(nf_tables_expressions);
  205. static LIST_HEAD(nf_tables_objects);
  206. +static LIST_HEAD(nf_tables_flowtables);
  207. /**
  208. * nft_register_afinfo - register nf_tables address family info
  209. @@ -389,6 +391,40 @@ static int nft_delobj(struct nft_ctx *ct
  210. return err;
  211. }
  212. +static int nft_trans_flowtable_add(struct nft_ctx *ctx, int msg_type,
  213. + struct nft_flowtable *flowtable)
  214. +{
  215. + struct nft_trans *trans;
  216. +
  217. + trans = nft_trans_alloc(ctx, msg_type,
  218. + sizeof(struct nft_trans_flowtable));
  219. + if (trans == NULL)
  220. + return -ENOMEM;
  221. +
  222. + if (msg_type == NFT_MSG_NEWFLOWTABLE)
  223. + nft_activate_next(ctx->net, flowtable);
  224. +
  225. + nft_trans_flowtable(trans) = flowtable;
  226. + list_add_tail(&trans->list, &ctx->net->nft.commit_list);
  227. +
  228. + return 0;
  229. +}
  230. +
  231. +static int nft_delflowtable(struct nft_ctx *ctx,
  232. + struct nft_flowtable *flowtable)
  233. +{
  234. + int err;
  235. +
  236. + err = nft_trans_flowtable_add(ctx, NFT_MSG_DELFLOWTABLE, flowtable);
  237. + if (err < 0)
  238. + return err;
  239. +
  240. + nft_deactivate_next(ctx->net, flowtable);
  241. + ctx->table->use--;
  242. +
  243. + return err;
  244. +}
  245. +
  246. /*
  247. * Tables
  248. */
  249. @@ -772,6 +808,7 @@ static int nf_tables_newtable(struct net
  250. INIT_LIST_HEAD(&table->chains);
  251. INIT_LIST_HEAD(&table->sets);
  252. INIT_LIST_HEAD(&table->objects);
  253. + INIT_LIST_HEAD(&table->flowtables);
  254. table->flags = flags;
  255. nft_ctx_init(&ctx, net, skb, nlh, afi, table, NULL, nla);
  256. @@ -793,10 +830,11 @@ err1:
  257. static int nft_flush_table(struct nft_ctx *ctx)
  258. {
  259. - int err;
  260. + struct nft_flowtable *flowtable, *nft;
  261. struct nft_chain *chain, *nc;
  262. struct nft_object *obj, *ne;
  263. struct nft_set *set, *ns;
  264. + int err;
  265. list_for_each_entry(chain, &ctx->table->chains, list) {
  266. if (!nft_is_active_next(ctx->net, chain))
  267. @@ -822,6 +860,12 @@ static int nft_flush_table(struct nft_ct
  268. goto out;
  269. }
  270. + list_for_each_entry_safe(flowtable, nft, &ctx->table->flowtables, list) {
  271. + err = nft_delflowtable(ctx, flowtable);
  272. + if (err < 0)
  273. + goto out;
  274. + }
  275. +
  276. list_for_each_entry_safe(obj, ne, &ctx->table->objects, list) {
  277. err = nft_delobj(ctx, obj);
  278. if (err < 0)
  279. @@ -4863,6 +4907,605 @@ static void nf_tables_obj_notify(const s
  280. ctx->afi->family, ctx->report, GFP_KERNEL);
  281. }
  282. +/*
  283. + * Flow tables
  284. + */
  285. +void nft_register_flowtable_type(struct nf_flowtable_type *type)
  286. +{
  287. + nfnl_lock(NFNL_SUBSYS_NFTABLES);
  288. + list_add_tail_rcu(&type->list, &nf_tables_flowtables);
  289. + nfnl_unlock(NFNL_SUBSYS_NFTABLES);
  290. +}
  291. +EXPORT_SYMBOL_GPL(nft_register_flowtable_type);
  292. +
  293. +void nft_unregister_flowtable_type(struct nf_flowtable_type *type)
  294. +{
  295. + nfnl_lock(NFNL_SUBSYS_NFTABLES);
  296. + list_del_rcu(&type->list);
  297. + nfnl_unlock(NFNL_SUBSYS_NFTABLES);
  298. +}
  299. +EXPORT_SYMBOL_GPL(nft_unregister_flowtable_type);
  300. +
  301. +static const struct nla_policy nft_flowtable_policy[NFTA_FLOWTABLE_MAX + 1] = {
  302. + [NFTA_FLOWTABLE_TABLE] = { .type = NLA_STRING,
  303. + .len = NFT_NAME_MAXLEN - 1 },
  304. + [NFTA_FLOWTABLE_NAME] = { .type = NLA_STRING,
  305. + .len = NFT_NAME_MAXLEN - 1 },
  306. + [NFTA_FLOWTABLE_HOOK] = { .type = NLA_NESTED },
  307. +};
  308. +
  309. +struct nft_flowtable *nf_tables_flowtable_lookup(const struct nft_table *table,
  310. + const struct nlattr *nla,
  311. + u8 genmask)
  312. +{
  313. + struct nft_flowtable *flowtable;
  314. +
  315. + list_for_each_entry(flowtable, &table->flowtables, list) {
  316. + if (!nla_strcmp(nla, flowtable->name) &&
  317. + nft_active_genmask(flowtable, genmask))
  318. + return flowtable;
  319. + }
  320. + return ERR_PTR(-ENOENT);
  321. +}
  322. +EXPORT_SYMBOL_GPL(nf_tables_flowtable_lookup);
  323. +
  324. +#define NFT_FLOWTABLE_DEVICE_MAX 8
  325. +
  326. +static int nf_tables_parse_devices(const struct nft_ctx *ctx,
  327. + const struct nlattr *attr,
  328. + struct net_device *dev_array[], int *len)
  329. +{
  330. + const struct nlattr *tmp;
  331. + struct net_device *dev;
  332. + char ifname[IFNAMSIZ];
  333. + int rem, n = 0, err;
  334. +
  335. + nla_for_each_nested(tmp, attr, rem) {
  336. + if (nla_type(tmp) != NFTA_DEVICE_NAME) {
  337. + err = -EINVAL;
  338. + goto err1;
  339. + }
  340. +
  341. + nla_strlcpy(ifname, tmp, IFNAMSIZ);
  342. + dev = dev_get_by_name(ctx->net, ifname);
  343. + if (!dev) {
  344. + err = -ENOENT;
  345. + goto err1;
  346. + }
  347. +
  348. + dev_array[n++] = dev;
  349. + if (n == NFT_FLOWTABLE_DEVICE_MAX) {
  350. + err = -EFBIG;
  351. + goto err1;
  352. + }
  353. + }
  354. + if (!len)
  355. + return -EINVAL;
  356. +
  357. + err = 0;
  358. +err1:
  359. + *len = n;
  360. + return err;
  361. +}
  362. +
  363. +static const struct nla_policy nft_flowtable_hook_policy[NFTA_FLOWTABLE_HOOK_MAX + 1] = {
  364. + [NFTA_FLOWTABLE_HOOK_NUM] = { .type = NLA_U32 },
  365. + [NFTA_FLOWTABLE_HOOK_PRIORITY] = { .type = NLA_U32 },
  366. + [NFTA_FLOWTABLE_HOOK_DEVS] = { .type = NLA_NESTED },
  367. +};
  368. +
  369. +static int nf_tables_flowtable_parse_hook(const struct nft_ctx *ctx,
  370. + const struct nlattr *attr,
  371. + struct nft_flowtable *flowtable)
  372. +{
  373. + struct net_device *dev_array[NFT_FLOWTABLE_DEVICE_MAX];
  374. + struct nlattr *tb[NFTA_FLOWTABLE_HOOK_MAX + 1];
  375. + struct nf_hook_ops *ops;
  376. + int hooknum, priority;
  377. + int err, n = 0, i;
  378. +
  379. + err = nla_parse_nested(tb, NFTA_FLOWTABLE_HOOK_MAX, attr,
  380. + nft_flowtable_hook_policy, NULL);
  381. + if (err < 0)
  382. + return err;
  383. +
  384. + if (!tb[NFTA_FLOWTABLE_HOOK_NUM] ||
  385. + !tb[NFTA_FLOWTABLE_HOOK_PRIORITY] ||
  386. + !tb[NFTA_FLOWTABLE_HOOK_DEVS])
  387. + return -EINVAL;
  388. +
  389. + hooknum = ntohl(nla_get_be32(tb[NFTA_FLOWTABLE_HOOK_NUM]));
  390. + if (hooknum >= ctx->afi->nhooks)
  391. + return -EINVAL;
  392. +
  393. + priority = ntohl(nla_get_be32(tb[NFTA_FLOWTABLE_HOOK_PRIORITY]));
  394. +
  395. + err = nf_tables_parse_devices(ctx, tb[NFTA_FLOWTABLE_HOOK_DEVS],
  396. + dev_array, &n);
  397. + if (err < 0)
  398. + goto err1;
  399. +
  400. + ops = kzalloc(sizeof(struct nf_hook_ops) * n, GFP_KERNEL);
  401. + if (!ops) {
  402. + err = -ENOMEM;
  403. + goto err1;
  404. + }
  405. +
  406. + flowtable->ops = ops;
  407. + flowtable->ops_len = n;
  408. +
  409. + for (i = 0; i < n; i++) {
  410. + flowtable->ops[i].pf = NFPROTO_NETDEV;
  411. + flowtable->ops[i].hooknum = hooknum;
  412. + flowtable->ops[i].priority = priority;
  413. + flowtable->ops[i].priv = &flowtable->data.rhashtable;
  414. + flowtable->ops[i].hook = flowtable->data.type->hook;
  415. + flowtable->ops[i].dev = dev_array[i];
  416. + }
  417. +
  418. + err = 0;
  419. +err1:
  420. + for (i = 0; i < n; i++)
  421. + dev_put(dev_array[i]);
  422. +
  423. + return err;
  424. +}
  425. +
  426. +static const struct nf_flowtable_type *
  427. +__nft_flowtable_type_get(const struct nft_af_info *afi)
  428. +{
  429. + const struct nf_flowtable_type *type;
  430. +
  431. + list_for_each_entry(type, &nf_tables_flowtables, list) {
  432. + if (afi->family == type->family)
  433. + return type;
  434. + }
  435. + return NULL;
  436. +}
  437. +
  438. +static const struct nf_flowtable_type *
  439. +nft_flowtable_type_get(const struct nft_af_info *afi)
  440. +{
  441. + const struct nf_flowtable_type *type;
  442. +
  443. + type = __nft_flowtable_type_get(afi);
  444. + if (type != NULL && try_module_get(type->owner))
  445. + return type;
  446. +
  447. +#ifdef CONFIG_MODULES
  448. + if (type == NULL) {
  449. + nfnl_unlock(NFNL_SUBSYS_NFTABLES);
  450. + request_module("nf-flowtable-%u", afi->family);
  451. + nfnl_lock(NFNL_SUBSYS_NFTABLES);
  452. + if (__nft_flowtable_type_get(afi))
  453. + return ERR_PTR(-EAGAIN);
  454. + }
  455. +#endif
  456. + return ERR_PTR(-ENOENT);
  457. +}
  458. +
  459. +void nft_flow_table_iterate(struct net *net,
  460. + void (*iter)(struct nf_flowtable *flowtable, void *data),
  461. + void *data)
  462. +{
  463. + struct nft_flowtable *flowtable;
  464. + const struct nft_af_info *afi;
  465. + const struct nft_table *table;
  466. +
  467. + rcu_read_lock();
  468. + list_for_each_entry_rcu(afi, &net->nft.af_info, list) {
  469. + list_for_each_entry_rcu(table, &afi->tables, list) {
  470. + list_for_each_entry_rcu(flowtable, &table->flowtables, list) {
  471. + iter(&flowtable->data, data);
  472. + }
  473. + }
  474. + }
  475. + rcu_read_unlock();
  476. +}
  477. +EXPORT_SYMBOL_GPL(nft_flow_table_iterate);
  478. +
  479. +static void nft_unregister_flowtable_net_hooks(struct net *net,
  480. + struct nft_flowtable *flowtable)
  481. +{
  482. + int i;
  483. +
  484. + for (i = 0; i < flowtable->ops_len; i++) {
  485. + if (!flowtable->ops[i].dev)
  486. + continue;
  487. +
  488. + nf_unregister_net_hook(net, &flowtable->ops[i]);
  489. + }
  490. +}
  491. +
  492. +static int nf_tables_newflowtable(struct net *net, struct sock *nlsk,
  493. + struct sk_buff *skb,
  494. + const struct nlmsghdr *nlh,
  495. + const struct nlattr * const nla[],
  496. + struct netlink_ext_ack *extack)
  497. +{
  498. + const struct nfgenmsg *nfmsg = nlmsg_data(nlh);
  499. + const struct nf_flowtable_type *type;
  500. + u8 genmask = nft_genmask_next(net);
  501. + int family = nfmsg->nfgen_family;
  502. + struct nft_flowtable *flowtable;
  503. + struct nft_af_info *afi;
  504. + struct nft_table *table;
  505. + struct nft_ctx ctx;
  506. + int err, i, k;
  507. +
  508. + if (!nla[NFTA_FLOWTABLE_TABLE] ||
  509. + !nla[NFTA_FLOWTABLE_NAME] ||
  510. + !nla[NFTA_FLOWTABLE_HOOK])
  511. + return -EINVAL;
  512. +
  513. + afi = nf_tables_afinfo_lookup(net, family, true);
  514. + if (IS_ERR(afi))
  515. + return PTR_ERR(afi);
  516. +
  517. + table = nf_tables_table_lookup(afi, nla[NFTA_FLOWTABLE_TABLE], genmask);
  518. + if (IS_ERR(table))
  519. + return PTR_ERR(table);
  520. +
  521. + flowtable = nf_tables_flowtable_lookup(table, nla[NFTA_FLOWTABLE_NAME],
  522. + genmask);
  523. + if (IS_ERR(flowtable)) {
  524. + err = PTR_ERR(flowtable);
  525. + if (err != -ENOENT)
  526. + return err;
  527. + } else {
  528. + if (nlh->nlmsg_flags & NLM_F_EXCL)
  529. + return -EEXIST;
  530. +
  531. + return 0;
  532. + }
  533. +
  534. + nft_ctx_init(&ctx, net, skb, nlh, afi, table, NULL, nla);
  535. +
  536. + flowtable = kzalloc(sizeof(*flowtable), GFP_KERNEL);
  537. + if (!flowtable)
  538. + return -ENOMEM;
  539. +
  540. + flowtable->table = table;
  541. + flowtable->name = nla_strdup(nla[NFTA_FLOWTABLE_NAME], GFP_KERNEL);
  542. + if (!flowtable->name) {
  543. + err = -ENOMEM;
  544. + goto err1;
  545. + }
  546. +
  547. + type = nft_flowtable_type_get(afi);
  548. + if (IS_ERR(type)) {
  549. + err = PTR_ERR(type);
  550. + goto err2;
  551. + }
  552. +
  553. + flowtable->data.type = type;
  554. + err = rhashtable_init(&flowtable->data.rhashtable, type->params);
  555. + if (err < 0)
  556. + goto err3;
  557. +
  558. + err = nf_tables_flowtable_parse_hook(&ctx, nla[NFTA_FLOWTABLE_HOOK],
  559. + flowtable);
  560. + if (err < 0)
  561. + goto err3;
  562. +
  563. + for (i = 0; i < flowtable->ops_len; i++) {
  564. + err = nf_register_net_hook(net, &flowtable->ops[i]);
  565. + if (err < 0)
  566. + goto err4;
  567. + }
  568. +
  569. + err = nft_trans_flowtable_add(&ctx, NFT_MSG_NEWFLOWTABLE, flowtable);
  570. + if (err < 0)
  571. + goto err5;
  572. +
  573. + INIT_DEFERRABLE_WORK(&flowtable->data.gc_work, type->gc);
  574. + queue_delayed_work(system_power_efficient_wq,
  575. + &flowtable->data.gc_work, HZ);
  576. +
  577. + list_add_tail_rcu(&flowtable->list, &table->flowtables);
  578. + table->use++;
  579. +
  580. + return 0;
  581. +err5:
  582. + i = flowtable->ops_len;
  583. +err4:
  584. + for (k = i - 1; k >= 0; k--)
  585. + nf_unregister_net_hook(net, &flowtable->ops[i]);
  586. +
  587. + kfree(flowtable->ops);
  588. +err3:
  589. + module_put(type->owner);
  590. +err2:
  591. + kfree(flowtable->name);
  592. +err1:
  593. + kfree(flowtable);
  594. + return err;
  595. +}
  596. +
  597. +static int nf_tables_delflowtable(struct net *net, struct sock *nlsk,
  598. + struct sk_buff *skb,
  599. + const struct nlmsghdr *nlh,
  600. + const struct nlattr * const nla[],
  601. + struct netlink_ext_ack *extack)
  602. +{
  603. + const struct nfgenmsg *nfmsg = nlmsg_data(nlh);
  604. + u8 genmask = nft_genmask_next(net);
  605. + int family = nfmsg->nfgen_family;
  606. + struct nft_flowtable *flowtable;
  607. + struct nft_af_info *afi;
  608. + struct nft_table *table;
  609. + struct nft_ctx ctx;
  610. +
  611. + afi = nf_tables_afinfo_lookup(net, family, true);
  612. + if (IS_ERR(afi))
  613. + return PTR_ERR(afi);
  614. +
  615. + table = nf_tables_table_lookup(afi, nla[NFTA_FLOWTABLE_TABLE], genmask);
  616. + if (IS_ERR(table))
  617. + return PTR_ERR(table);
  618. +
  619. + flowtable = nf_tables_flowtable_lookup(table, nla[NFTA_FLOWTABLE_NAME],
  620. + genmask);
  621. + if (IS_ERR(flowtable))
  622. + return PTR_ERR(flowtable);
  623. + if (flowtable->use > 0)
  624. + return -EBUSY;
  625. +
  626. + nft_ctx_init(&ctx, net, skb, nlh, afi, table, NULL, nla);
  627. +
  628. + return nft_delflowtable(&ctx, flowtable);
  629. +}
  630. +
  631. +static int nf_tables_fill_flowtable_info(struct sk_buff *skb, struct net *net,
  632. + u32 portid, u32 seq, int event,
  633. + u32 flags, int family,
  634. + struct nft_flowtable *flowtable)
  635. +{
  636. + struct nlattr *nest, *nest_devs;
  637. + struct nfgenmsg *nfmsg;
  638. + struct nlmsghdr *nlh;
  639. + int i;
  640. +
  641. + event = nfnl_msg_type(NFNL_SUBSYS_NFTABLES, event);
  642. + nlh = nlmsg_put(skb, portid, seq, event, sizeof(struct nfgenmsg), flags);
  643. + if (nlh == NULL)
  644. + goto nla_put_failure;
  645. +
  646. + nfmsg = nlmsg_data(nlh);
  647. + nfmsg->nfgen_family = family;
  648. + nfmsg->version = NFNETLINK_V0;
  649. + nfmsg->res_id = htons(net->nft.base_seq & 0xffff);
  650. +
  651. + if (nla_put_string(skb, NFTA_FLOWTABLE_TABLE, flowtable->table->name) ||
  652. + nla_put_string(skb, NFTA_FLOWTABLE_NAME, flowtable->name) ||
  653. + nla_put_be32(skb, NFTA_FLOWTABLE_USE, htonl(flowtable->use)))
  654. + goto nla_put_failure;
  655. +
  656. + nest = nla_nest_start(skb, NFTA_FLOWTABLE_HOOK);
  657. + if (nla_put_be32(skb, NFTA_FLOWTABLE_HOOK_NUM, htonl(flowtable->hooknum)) ||
  658. + nla_put_be32(skb, NFTA_FLOWTABLE_HOOK_PRIORITY, htonl(flowtable->priority)))
  659. + goto nla_put_failure;
  660. +
  661. + nest_devs = nla_nest_start(skb, NFTA_FLOWTABLE_HOOK_DEVS);
  662. + if (!nest_devs)
  663. + goto nla_put_failure;
  664. +
  665. + for (i = 0; i < flowtable->ops_len; i++) {
  666. + if (flowtable->ops[i].dev &&
  667. + nla_put_string(skb, NFTA_DEVICE_NAME,
  668. + flowtable->ops[i].dev->name))
  669. + goto nla_put_failure;
  670. + }
  671. + nla_nest_end(skb, nest_devs);
  672. + nla_nest_end(skb, nest);
  673. +
  674. + nlmsg_end(skb, nlh);
  675. + return 0;
  676. +
  677. +nla_put_failure:
  678. + nlmsg_trim(skb, nlh);
  679. + return -1;
  680. +}
  681. +
  682. +struct nft_flowtable_filter {
  683. + char *table;
  684. +};
  685. +
  686. +static int nf_tables_dump_flowtable(struct sk_buff *skb,
  687. + struct netlink_callback *cb)
  688. +{
  689. + const struct nfgenmsg *nfmsg = nlmsg_data(cb->nlh);
  690. + struct nft_flowtable_filter *filter = cb->data;
  691. + unsigned int idx = 0, s_idx = cb->args[0];
  692. + struct net *net = sock_net(skb->sk);
  693. + int family = nfmsg->nfgen_family;
  694. + struct nft_flowtable *flowtable;
  695. + const struct nft_af_info *afi;
  696. + const struct nft_table *table;
  697. +
  698. + rcu_read_lock();
  699. + cb->seq = net->nft.base_seq;
  700. +
  701. + list_for_each_entry_rcu(afi, &net->nft.af_info, list) {
  702. + if (family != NFPROTO_UNSPEC && family != afi->family)
  703. + continue;
  704. +
  705. + list_for_each_entry_rcu(table, &afi->tables, list) {
  706. + list_for_each_entry_rcu(flowtable, &table->flowtables, list) {
  707. + if (!nft_is_active(net, flowtable))
  708. + goto cont;
  709. + if (idx < s_idx)
  710. + goto cont;
  711. + if (idx > s_idx)
  712. + memset(&cb->args[1], 0,
  713. + sizeof(cb->args) - sizeof(cb->args[0]));
  714. + if (filter && filter->table[0] &&
  715. + strcmp(filter->table, table->name))
  716. + goto cont;
  717. +
  718. + if (nf_tables_fill_flowtable_info(skb, net, NETLINK_CB(cb->skb).portid,
  719. + cb->nlh->nlmsg_seq,
  720. + NFT_MSG_NEWFLOWTABLE,
  721. + NLM_F_MULTI | NLM_F_APPEND,
  722. + afi->family, flowtable) < 0)
  723. + goto done;
  724. +
  725. + nl_dump_check_consistent(cb, nlmsg_hdr(skb));
  726. +cont:
  727. + idx++;
  728. + }
  729. + }
  730. + }
  731. +done:
  732. + rcu_read_unlock();
  733. +
  734. + cb->args[0] = idx;
  735. + return skb->len;
  736. +}
  737. +
  738. +static int nf_tables_dump_flowtable_done(struct netlink_callback *cb)
  739. +{
  740. + struct nft_flowtable_filter *filter = cb->data;
  741. +
  742. + if (!filter)
  743. + return 0;
  744. +
  745. + kfree(filter->table);
  746. + kfree(filter);
  747. +
  748. + return 0;
  749. +}
  750. +
  751. +static struct nft_flowtable_filter *
  752. +nft_flowtable_filter_alloc(const struct nlattr * const nla[])
  753. +{
  754. + struct nft_flowtable_filter *filter;
  755. +
  756. + filter = kzalloc(sizeof(*filter), GFP_KERNEL);
  757. + if (!filter)
  758. + return ERR_PTR(-ENOMEM);
  759. +
  760. + if (nla[NFTA_FLOWTABLE_TABLE]) {
  761. + filter->table = nla_strdup(nla[NFTA_FLOWTABLE_TABLE],
  762. + GFP_KERNEL);
  763. + if (!filter->table) {
  764. + kfree(filter);
  765. + return ERR_PTR(-ENOMEM);
  766. + }
  767. + }
  768. + return filter;
  769. +}
  770. +
  771. +static int nf_tables_getflowtable(struct net *net, struct sock *nlsk,
  772. + struct sk_buff *skb,
  773. + const struct nlmsghdr *nlh,
  774. + const struct nlattr * const nla[],
  775. + struct netlink_ext_ack *extack)
  776. +{
  777. + const struct nfgenmsg *nfmsg = nlmsg_data(nlh);
  778. + u8 genmask = nft_genmask_cur(net);
  779. + int family = nfmsg->nfgen_family;
  780. + struct nft_flowtable *flowtable;
  781. + const struct nft_af_info *afi;
  782. + const struct nft_table *table;
  783. + struct sk_buff *skb2;
  784. + int err;
  785. +
  786. + if (nlh->nlmsg_flags & NLM_F_DUMP) {
  787. + struct netlink_dump_control c = {
  788. + .dump = nf_tables_dump_flowtable,
  789. + .done = nf_tables_dump_flowtable_done,
  790. + };
  791. +
  792. + if (nla[NFTA_FLOWTABLE_TABLE]) {
  793. + struct nft_flowtable_filter *filter;
  794. +
  795. + filter = nft_flowtable_filter_alloc(nla);
  796. + if (IS_ERR(filter))
  797. + return -ENOMEM;
  798. +
  799. + c.data = filter;
  800. + }
  801. + return netlink_dump_start(nlsk, skb, nlh, &c);
  802. + }
  803. +
  804. + if (!nla[NFTA_FLOWTABLE_NAME])
  805. + return -EINVAL;
  806. +
  807. + afi = nf_tables_afinfo_lookup(net, family, false);
  808. + if (IS_ERR(afi))
  809. + return PTR_ERR(afi);
  810. +
  811. + table = nf_tables_table_lookup(afi, nla[NFTA_FLOWTABLE_TABLE], genmask);
  812. + if (IS_ERR(table))
  813. + return PTR_ERR(table);
  814. +
  815. + flowtable = nf_tables_flowtable_lookup(table, nla[NFTA_FLOWTABLE_NAME],
  816. + genmask);
  817. + if (IS_ERR(table))
  818. + return PTR_ERR(flowtable);
  819. +
  820. + skb2 = alloc_skb(NLMSG_GOODSIZE, GFP_KERNEL);
  821. + if (!skb2)
  822. + return -ENOMEM;
  823. +
  824. + err = nf_tables_fill_flowtable_info(skb2, net, NETLINK_CB(skb).portid,
  825. + nlh->nlmsg_seq,
  826. + NFT_MSG_NEWFLOWTABLE, 0, family,
  827. + flowtable);
  828. + if (err < 0)
  829. + goto err;
  830. +
  831. + return nlmsg_unicast(nlsk, skb2, NETLINK_CB(skb).portid);
  832. +err:
  833. + kfree_skb(skb2);
  834. + return err;
  835. +}
  836. +
  837. +static void nf_tables_flowtable_notify(struct nft_ctx *ctx,
  838. + struct nft_flowtable *flowtable,
  839. + int event)
  840. +{
  841. + struct sk_buff *skb;
  842. + int err;
  843. +
  844. + if (ctx->report &&
  845. + !nfnetlink_has_listeners(ctx->net, NFNLGRP_NFTABLES))
  846. + return;
  847. +
  848. + skb = nlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL);
  849. + if (skb == NULL)
  850. + goto err;
  851. +
  852. + err = nf_tables_fill_flowtable_info(skb, ctx->net, ctx->portid,
  853. + ctx->seq, event, 0,
  854. + ctx->afi->family, flowtable);
  855. + if (err < 0) {
  856. + kfree_skb(skb);
  857. + goto err;
  858. + }
  859. +
  860. + nfnetlink_send(skb, ctx->net, ctx->portid, NFNLGRP_NFTABLES,
  861. + ctx->report, GFP_KERNEL);
  862. + return;
  863. +err:
  864. + nfnetlink_set_err(ctx->net, ctx->portid, NFNLGRP_NFTABLES, -ENOBUFS);
  865. +}
  866. +
  867. +static void nft_flowtable_destroy(void *ptr, void *arg)
  868. +{
  869. + kfree(ptr);
  870. +}
  871. +
  872. +static void nf_tables_flowtable_destroy(struct nft_flowtable *flowtable)
  873. +{
  874. + cancel_delayed_work_sync(&flowtable->data.gc_work);
  875. + kfree(flowtable->name);
  876. + rhashtable_free_and_destroy(&flowtable->data.rhashtable,
  877. + nft_flowtable_destroy, NULL);
  878. + module_put(flowtable->data.type->owner);
  879. +}
  880. +
  881. static int nf_tables_fill_gen_info(struct sk_buff *skb, struct net *net,
  882. u32 portid, u32 seq)
  883. {
  884. @@ -4893,6 +5536,49 @@ nla_put_failure:
  885. return -EMSGSIZE;
  886. }
  887. +static void nft_flowtable_event(unsigned long event, struct net_device *dev,
  888. + struct nft_flowtable *flowtable)
  889. +{
  890. + int i;
  891. +
  892. + for (i = 0; i < flowtable->ops_len; i++) {
  893. + if (flowtable->ops[i].dev != dev)
  894. + continue;
  895. +
  896. + nf_unregister_net_hook(dev_net(dev), &flowtable->ops[i]);
  897. + flowtable->ops[i].dev = NULL;
  898. + break;
  899. + }
  900. +}
  901. +
  902. +static int nf_tables_flowtable_event(struct notifier_block *this,
  903. + unsigned long event, void *ptr)
  904. +{
  905. + struct net_device *dev = netdev_notifier_info_to_dev(ptr);
  906. + struct nft_flowtable *flowtable;
  907. + struct nft_table *table;
  908. + struct nft_af_info *afi;
  909. +
  910. + if (event != NETDEV_UNREGISTER)
  911. + return 0;
  912. +
  913. + nfnl_lock(NFNL_SUBSYS_NFTABLES);
  914. + list_for_each_entry(afi, &dev_net(dev)->nft.af_info, list) {
  915. + list_for_each_entry(table, &afi->tables, list) {
  916. + list_for_each_entry(flowtable, &table->flowtables, list) {
  917. + nft_flowtable_event(event, dev, flowtable);
  918. + }
  919. + }
  920. + }
  921. + nfnl_unlock(NFNL_SUBSYS_NFTABLES);
  922. +
  923. + return NOTIFY_DONE;
  924. +}
  925. +
  926. +static struct notifier_block nf_tables_flowtable_notifier = {
  927. + .notifier_call = nf_tables_flowtable_event,
  928. +};
  929. +
  930. static void nf_tables_gen_notify(struct net *net, struct sk_buff *skb,
  931. int event)
  932. {
  933. @@ -5045,6 +5731,21 @@ static const struct nfnl_callback nf_tab
  934. .attr_count = NFTA_OBJ_MAX,
  935. .policy = nft_obj_policy,
  936. },
  937. + [NFT_MSG_NEWFLOWTABLE] = {
  938. + .call_batch = nf_tables_newflowtable,
  939. + .attr_count = NFTA_FLOWTABLE_MAX,
  940. + .policy = nft_flowtable_policy,
  941. + },
  942. + [NFT_MSG_GETFLOWTABLE] = {
  943. + .call = nf_tables_getflowtable,
  944. + .attr_count = NFTA_FLOWTABLE_MAX,
  945. + .policy = nft_flowtable_policy,
  946. + },
  947. + [NFT_MSG_DELFLOWTABLE] = {
  948. + .call_batch = nf_tables_delflowtable,
  949. + .attr_count = NFTA_FLOWTABLE_MAX,
  950. + .policy = nft_flowtable_policy,
  951. + },
  952. };
  953. static void nft_chain_commit_update(struct nft_trans *trans)
  954. @@ -5093,6 +5794,9 @@ static void nf_tables_commit_release(str
  955. case NFT_MSG_DELOBJ:
  956. nft_obj_destroy(nft_trans_obj(trans));
  957. break;
  958. + case NFT_MSG_DELFLOWTABLE:
  959. + nf_tables_flowtable_destroy(nft_trans_flowtable(trans));
  960. + break;
  961. }
  962. kfree(trans);
  963. }
  964. @@ -5212,6 +5916,21 @@ static int nf_tables_commit(struct net *
  965. nf_tables_obj_notify(&trans->ctx, nft_trans_obj(trans),
  966. NFT_MSG_DELOBJ);
  967. break;
  968. + case NFT_MSG_NEWFLOWTABLE:
  969. + nft_clear(net, nft_trans_flowtable(trans));
  970. + nf_tables_flowtable_notify(&trans->ctx,
  971. + nft_trans_flowtable(trans),
  972. + NFT_MSG_NEWFLOWTABLE);
  973. + nft_trans_destroy(trans);
  974. + break;
  975. + case NFT_MSG_DELFLOWTABLE:
  976. + list_del_rcu(&nft_trans_flowtable(trans)->list);
  977. + nf_tables_flowtable_notify(&trans->ctx,
  978. + nft_trans_flowtable(trans),
  979. + NFT_MSG_DELFLOWTABLE);
  980. + nft_unregister_flowtable_net_hooks(net,
  981. + nft_trans_flowtable(trans));
  982. + break;
  983. }
  984. }
  985. @@ -5249,6 +5968,9 @@ static void nf_tables_abort_release(stru
  986. case NFT_MSG_NEWOBJ:
  987. nft_obj_destroy(nft_trans_obj(trans));
  988. break;
  989. + case NFT_MSG_NEWFLOWTABLE:
  990. + nf_tables_flowtable_destroy(nft_trans_flowtable(trans));
  991. + break;
  992. }
  993. kfree(trans);
  994. }
  995. @@ -5340,6 +6062,17 @@ static int nf_tables_abort(struct net *n
  996. nft_clear(trans->ctx.net, nft_trans_obj(trans));
  997. nft_trans_destroy(trans);
  998. break;
  999. + case NFT_MSG_NEWFLOWTABLE:
  1000. + trans->ctx.table->use--;
  1001. + list_del_rcu(&nft_trans_flowtable(trans)->list);
  1002. + nft_unregister_flowtable_net_hooks(net,
  1003. + nft_trans_flowtable(trans));
  1004. + break;
  1005. + case NFT_MSG_DELFLOWTABLE:
  1006. + trans->ctx.table->use++;
  1007. + nft_clear(trans->ctx.net, nft_trans_flowtable(trans));
  1008. + nft_trans_destroy(trans);
  1009. + break;
  1010. }
  1011. }
  1012. @@ -5890,6 +6623,7 @@ EXPORT_SYMBOL_GPL(__nft_release_basechai
  1013. /* Called by nft_unregister_afinfo() from __net_exit path, nfnl_lock is held. */
  1014. static void __nft_release_afinfo(struct net *net, struct nft_af_info *afi)
  1015. {
  1016. + struct nft_flowtable *flowtable, *nf;
  1017. struct nft_table *table, *nt;
  1018. struct nft_chain *chain, *nc;
  1019. struct nft_object *obj, *ne;
  1020. @@ -5903,6 +6637,9 @@ static void __nft_release_afinfo(struct
  1021. list_for_each_entry_safe(table, nt, &afi->tables, list) {
  1022. list_for_each_entry(chain, &table->chains, list)
  1023. nf_tables_unregister_hook(net, table, chain);
  1024. + list_for_each_entry(flowtable, &table->flowtables, list)
  1025. + nf_unregister_net_hooks(net, flowtable->ops,
  1026. + flowtable->ops_len);
  1027. /* No packets are walking on these chains anymore. */
  1028. ctx.table = table;
  1029. list_for_each_entry(chain, &table->chains, list) {
  1030. @@ -5913,6 +6650,11 @@ static void __nft_release_afinfo(struct
  1031. nf_tables_rule_release(&ctx, rule);
  1032. }
  1033. }
  1034. + list_for_each_entry_safe(flowtable, nf, &table->flowtables, list) {
  1035. + list_del(&flowtable->list);
  1036. + table->use--;
  1037. + nf_tables_flowtable_destroy(flowtable);
  1038. + }
  1039. list_for_each_entry_safe(set, ns, &table->sets, list) {
  1040. list_del(&set->list);
  1041. table->use--;
  1042. @@ -5956,6 +6698,8 @@ static int __init nf_tables_module_init(
  1043. if (err < 0)
  1044. goto err3;
  1045. + register_netdevice_notifier(&nf_tables_flowtable_notifier);
  1046. +
  1047. pr_info("nf_tables: (c) 2007-2009 Patrick McHardy <kaber@trash.net>\n");
  1048. return register_pernet_subsys(&nf_tables_net_ops);
  1049. err3:
  1050. @@ -5970,6 +6714,7 @@ static void __exit nf_tables_module_exit
  1051. {
  1052. unregister_pernet_subsys(&nf_tables_net_ops);
  1053. nfnetlink_subsys_unregister(&nf_tables_subsys);
  1054. + unregister_netdevice_notifier(&nf_tables_flowtable_notifier);
  1055. rcu_barrier();
  1056. nf_tables_core_module_exit();
  1057. kfree(info);