blob: 90e3496511789a91661059d62f7e16df74cff55c [file] [log] [blame]
Patrick McHardy96518512013-10-14 11:00:02 +02001/*
Patrick McHardy20a69342013-10-11 12:06:22 +02002 * Copyright (c) 2007-2009 Patrick McHardy <kaber@trash.net>
Patrick McHardy96518512013-10-14 11:00:02 +02003 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License version 2 as
6 * published by the Free Software Foundation.
7 *
8 * Development of this code funded by Astaro AG (http://www.astaro.com/)
9 */
10
11#include <linux/module.h>
12#include <linux/init.h>
13#include <linux/list.h>
14#include <linux/skbuff.h>
15#include <linux/netlink.h>
16#include <linux/netfilter.h>
17#include <linux/netfilter/nfnetlink.h>
18#include <linux/netfilter/nf_tables.h>
19#include <net/netfilter/nf_tables_core.h>
20#include <net/netfilter/nf_tables.h>
Pablo Neira Ayuso99633ab2013-10-10 23:28:33 +020021#include <net/net_namespace.h>
Patrick McHardy96518512013-10-14 11:00:02 +020022#include <net/sock.h>
23
Patrick McHardy96518512013-10-14 11:00:02 +020024static LIST_HEAD(nf_tables_expressions);
25
26/**
27 * nft_register_afinfo - register nf_tables address family info
28 *
29 * @afi: address family info to register
30 *
31 * Register the address family for use with nf_tables. Returns zero on
32 * success or a negative errno code otherwise.
33 */
Pablo Neira Ayuso99633ab2013-10-10 23:28:33 +020034int nft_register_afinfo(struct net *net, struct nft_af_info *afi)
Patrick McHardy96518512013-10-14 11:00:02 +020035{
36 INIT_LIST_HEAD(&afi->tables);
37 nfnl_lock(NFNL_SUBSYS_NFTABLES);
Pablo Neira Ayusoe688a7f2014-07-01 11:49:18 +020038 list_add_tail_rcu(&afi->list, &net->nft.af_info);
Patrick McHardy96518512013-10-14 11:00:02 +020039 nfnl_unlock(NFNL_SUBSYS_NFTABLES);
40 return 0;
41}
42EXPORT_SYMBOL_GPL(nft_register_afinfo);
43
44/**
45 * nft_unregister_afinfo - unregister nf_tables address family info
46 *
47 * @afi: address family info to unregister
48 *
49 * Unregister the address family for use with nf_tables.
50 */
51void nft_unregister_afinfo(struct nft_af_info *afi)
52{
53 nfnl_lock(NFNL_SUBSYS_NFTABLES);
Pablo Neira Ayusoe688a7f2014-07-01 11:49:18 +020054 list_del_rcu(&afi->list);
Patrick McHardy96518512013-10-14 11:00:02 +020055 nfnl_unlock(NFNL_SUBSYS_NFTABLES);
56}
57EXPORT_SYMBOL_GPL(nft_unregister_afinfo);
58
Pablo Neira Ayuso99633ab2013-10-10 23:28:33 +020059static struct nft_af_info *nft_afinfo_lookup(struct net *net, int family)
Patrick McHardy96518512013-10-14 11:00:02 +020060{
61 struct nft_af_info *afi;
62
Pablo Neira Ayuso99633ab2013-10-10 23:28:33 +020063 list_for_each_entry(afi, &net->nft.af_info, list) {
Patrick McHardy96518512013-10-14 11:00:02 +020064 if (afi->family == family)
65 return afi;
66 }
67 return NULL;
68}
69
Pablo Neira Ayuso99633ab2013-10-10 23:28:33 +020070static struct nft_af_info *
71nf_tables_afinfo_lookup(struct net *net, int family, bool autoload)
Patrick McHardy96518512013-10-14 11:00:02 +020072{
73 struct nft_af_info *afi;
74
Pablo Neira Ayuso99633ab2013-10-10 23:28:33 +020075 afi = nft_afinfo_lookup(net, family);
Patrick McHardy96518512013-10-14 11:00:02 +020076 if (afi != NULL)
77 return afi;
78#ifdef CONFIG_MODULES
79 if (autoload) {
80 nfnl_unlock(NFNL_SUBSYS_NFTABLES);
81 request_module("nft-afinfo-%u", family);
82 nfnl_lock(NFNL_SUBSYS_NFTABLES);
Pablo Neira Ayuso99633ab2013-10-10 23:28:33 +020083 afi = nft_afinfo_lookup(net, family);
Patrick McHardy96518512013-10-14 11:00:02 +020084 if (afi != NULL)
85 return ERR_PTR(-EAGAIN);
86 }
87#endif
88 return ERR_PTR(-EAFNOSUPPORT);
89}
90
Pablo Neira Ayuso7c95f6d2014-04-04 01:22:45 +020091static void nft_ctx_init(struct nft_ctx *ctx,
92 const struct sk_buff *skb,
93 const struct nlmsghdr *nlh,
94 struct nft_af_info *afi,
95 struct nft_table *table,
96 struct nft_chain *chain,
97 const struct nlattr * const *nla)
98{
Pablo Neira Ayuso128ad332014-05-09 17:14:24 +020099 ctx->net = sock_net(skb->sk);
100 ctx->afi = afi;
101 ctx->table = table;
102 ctx->chain = chain;
103 ctx->nla = nla;
104 ctx->portid = NETLINK_CB(skb).portid;
105 ctx->report = nlmsg_report(nlh);
106 ctx->seq = nlh->nlmsg_seq;
Pablo Neira Ayuso7c95f6d2014-04-04 01:22:45 +0200107}
108
Pablo Neira Ayusob380e5c2014-04-04 01:38:51 +0200109static struct nft_trans *nft_trans_alloc(struct nft_ctx *ctx, int msg_type,
110 u32 size)
Pablo Neira Ayuso1081d112014-04-04 01:24:07 +0200111{
112 struct nft_trans *trans;
113
114 trans = kzalloc(sizeof(struct nft_trans) + size, GFP_KERNEL);
115 if (trans == NULL)
116 return NULL;
117
Pablo Neira Ayusob380e5c2014-04-04 01:38:51 +0200118 trans->msg_type = msg_type;
Pablo Neira Ayuso1081d112014-04-04 01:24:07 +0200119 trans->ctx = *ctx;
120
121 return trans;
122}
123
124static void nft_trans_destroy(struct nft_trans *trans)
125{
126 list_del(&trans->list);
127 kfree(trans);
128}
129
Arturo Borreroc5598792014-09-02 16:42:23 +0200130static void nf_tables_unregister_hooks(const struct nft_table *table,
131 const struct nft_chain *chain,
132 unsigned int hook_nops)
133{
134 if (!(table->flags & NFT_TABLE_F_DORMANT) &&
135 chain->flags & NFT_BASE_CHAIN)
136 nf_unregister_hooks(nft_base_chain(chain)->ops, hook_nops);
137}
138
Patrick McHardy96518512013-10-14 11:00:02 +0200139/*
140 * Tables
141 */
142
143static struct nft_table *nft_table_lookup(const struct nft_af_info *afi,
144 const struct nlattr *nla)
145{
146 struct nft_table *table;
147
148 list_for_each_entry(table, &afi->tables, list) {
149 if (!nla_strcmp(nla, table->name))
150 return table;
151 }
152 return NULL;
153}
154
155static struct nft_table *nf_tables_table_lookup(const struct nft_af_info *afi,
Pablo Neira Ayuso93707612013-10-10 23:21:26 +0200156 const struct nlattr *nla)
Patrick McHardy96518512013-10-14 11:00:02 +0200157{
158 struct nft_table *table;
159
160 if (nla == NULL)
161 return ERR_PTR(-EINVAL);
162
163 table = nft_table_lookup(afi, nla);
164 if (table != NULL)
165 return table;
166
Patrick McHardy96518512013-10-14 11:00:02 +0200167 return ERR_PTR(-ENOENT);
168}
169
170static inline u64 nf_tables_alloc_handle(struct nft_table *table)
171{
172 return ++table->hgenerator;
173}
174
Patrick McHardy2a37d752014-01-09 18:42:37 +0000175static const struct nf_chain_type *chain_type[AF_MAX][NFT_CHAIN_T_MAX];
Pablo Neira Ayuso93707612013-10-10 23:21:26 +0200176
Patrick McHardy2a37d752014-01-09 18:42:37 +0000177static const struct nf_chain_type *
Patrick McHardybaae3e62014-01-09 18:42:34 +0000178__nf_tables_chain_type_lookup(int family, const struct nlattr *nla)
Pablo Neira Ayuso93707612013-10-10 23:21:26 +0200179{
180 int i;
181
Patrick McHardybaae3e62014-01-09 18:42:34 +0000182 for (i = 0; i < NFT_CHAIN_T_MAX; i++) {
Pablo Neira Ayuso93707612013-10-10 23:21:26 +0200183 if (chain_type[family][i] != NULL &&
184 !nla_strcmp(nla, chain_type[family][i]->name))
Patrick McHardybaae3e62014-01-09 18:42:34 +0000185 return chain_type[family][i];
Pablo Neira Ayuso93707612013-10-10 23:21:26 +0200186 }
Patrick McHardybaae3e62014-01-09 18:42:34 +0000187 return NULL;
Pablo Neira Ayuso93707612013-10-10 23:21:26 +0200188}
189
Patrick McHardy2a37d752014-01-09 18:42:37 +0000190static const struct nf_chain_type *
Patrick McHardybaae3e62014-01-09 18:42:34 +0000191nf_tables_chain_type_lookup(const struct nft_af_info *afi,
192 const struct nlattr *nla,
193 bool autoload)
Pablo Neira Ayuso93707612013-10-10 23:21:26 +0200194{
Patrick McHardy2a37d752014-01-09 18:42:37 +0000195 const struct nf_chain_type *type;
Pablo Neira Ayuso93707612013-10-10 23:21:26 +0200196
197 type = __nf_tables_chain_type_lookup(afi->family, nla);
Patrick McHardy93b08062014-01-09 18:42:36 +0000198 if (type != NULL)
199 return type;
Pablo Neira Ayuso93707612013-10-10 23:21:26 +0200200#ifdef CONFIG_MODULES
Patrick McHardy93b08062014-01-09 18:42:36 +0000201 if (autoload) {
Pablo Neira Ayuso93707612013-10-10 23:21:26 +0200202 nfnl_unlock(NFNL_SUBSYS_NFTABLES);
Pablo Neira Ayuso2fec6bb2014-03-31 12:26:39 +0200203 request_module("nft-chain-%u-%.*s", afi->family,
204 nla_len(nla), (const char *)nla_data(nla));
Pablo Neira Ayuso93707612013-10-10 23:21:26 +0200205 nfnl_lock(NFNL_SUBSYS_NFTABLES);
206 type = __nf_tables_chain_type_lookup(afi->family, nla);
Patrick McHardy93b08062014-01-09 18:42:36 +0000207 if (type != NULL)
208 return ERR_PTR(-EAGAIN);
Pablo Neira Ayuso93707612013-10-10 23:21:26 +0200209 }
210#endif
Patrick McHardy93b08062014-01-09 18:42:36 +0000211 return ERR_PTR(-ENOENT);
Pablo Neira Ayuso93707612013-10-10 23:21:26 +0200212}
213
Patrick McHardy96518512013-10-14 11:00:02 +0200214static const struct nla_policy nft_table_policy[NFTA_TABLE_MAX + 1] = {
215 [NFTA_TABLE_NAME] = { .type = NLA_STRING },
Pablo Neira Ayuso9ddf6322013-10-10 13:26:33 +0200216 [NFTA_TABLE_FLAGS] = { .type = NLA_U32 },
Patrick McHardy96518512013-10-14 11:00:02 +0200217};
218
219static int nf_tables_fill_table_info(struct sk_buff *skb, u32 portid, u32 seq,
220 int event, u32 flags, int family,
221 const struct nft_table *table)
222{
223 struct nlmsghdr *nlh;
224 struct nfgenmsg *nfmsg;
225
226 event |= NFNL_SUBSYS_NFTABLES << 8;
227 nlh = nlmsg_put(skb, portid, seq, event, sizeof(struct nfgenmsg), flags);
228 if (nlh == NULL)
229 goto nla_put_failure;
230
231 nfmsg = nlmsg_data(nlh);
232 nfmsg->nfgen_family = family;
233 nfmsg->version = NFNETLINK_V0;
234 nfmsg->res_id = 0;
235
Pablo Neira Ayuso9ddf6322013-10-10 13:26:33 +0200236 if (nla_put_string(skb, NFTA_TABLE_NAME, table->name) ||
Tomasz Bursztykad8bcc7682013-12-12 15:00:42 +0200237 nla_put_be32(skb, NFTA_TABLE_FLAGS, htonl(table->flags)) ||
238 nla_put_be32(skb, NFTA_TABLE_USE, htonl(table->use)))
Patrick McHardy96518512013-10-14 11:00:02 +0200239 goto nla_put_failure;
240
241 return nlmsg_end(skb, nlh);
242
243nla_put_failure:
244 nlmsg_trim(skb, nlh);
245 return -1;
246}
247
Pablo Neira Ayuso35151d82014-05-05 17:12:40 +0200248static int nf_tables_table_notify(const struct nft_ctx *ctx, int event)
Patrick McHardy96518512013-10-14 11:00:02 +0200249{
250 struct sk_buff *skb;
Patrick McHardy96518512013-10-14 11:00:02 +0200251 int err;
252
Pablo Neira Ayuso128ad332014-05-09 17:14:24 +0200253 if (!ctx->report &&
254 !nfnetlink_has_listeners(ctx->net, NFNLGRP_NFTABLES))
Patrick McHardy96518512013-10-14 11:00:02 +0200255 return 0;
256
257 err = -ENOBUFS;
258 skb = nlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL);
259 if (skb == NULL)
260 goto err;
261
Pablo Neira Ayuso128ad332014-05-09 17:14:24 +0200262 err = nf_tables_fill_table_info(skb, ctx->portid, ctx->seq, event, 0,
Pablo Neira Ayuso35151d82014-05-05 17:12:40 +0200263 ctx->afi->family, ctx->table);
Patrick McHardy96518512013-10-14 11:00:02 +0200264 if (err < 0) {
265 kfree_skb(skb);
266 goto err;
267 }
268
Pablo Neira Ayuso128ad332014-05-09 17:14:24 +0200269 err = nfnetlink_send(skb, ctx->net, ctx->portid, NFNLGRP_NFTABLES,
270 ctx->report, GFP_KERNEL);
Patrick McHardy96518512013-10-14 11:00:02 +0200271err:
Pablo Neira Ayuso128ad332014-05-09 17:14:24 +0200272 if (err < 0) {
273 nfnetlink_set_err(ctx->net, ctx->portid, NFNLGRP_NFTABLES,
274 err);
275 }
Patrick McHardy96518512013-10-14 11:00:02 +0200276 return err;
277}
278
279static int nf_tables_dump_tables(struct sk_buff *skb,
280 struct netlink_callback *cb)
281{
282 const struct nfgenmsg *nfmsg = nlmsg_data(cb->nlh);
283 const struct nft_af_info *afi;
284 const struct nft_table *table;
285 unsigned int idx = 0, s_idx = cb->args[0];
Pablo Neira Ayuso99633ab2013-10-10 23:28:33 +0200286 struct net *net = sock_net(skb->sk);
Patrick McHardy96518512013-10-14 11:00:02 +0200287 int family = nfmsg->nfgen_family;
288
Pablo Neira Ayusoe688a7f2014-07-01 11:49:18 +0200289 rcu_read_lock();
Pablo Neira Ayuso38e029f2014-07-01 12:23:12 +0200290 cb->seq = net->nft.base_seq;
291
Pablo Neira Ayusoe688a7f2014-07-01 11:49:18 +0200292 list_for_each_entry_rcu(afi, &net->nft.af_info, list) {
Patrick McHardy96518512013-10-14 11:00:02 +0200293 if (family != NFPROTO_UNSPEC && family != afi->family)
294 continue;
295
Pablo Neira Ayusoe688a7f2014-07-01 11:49:18 +0200296 list_for_each_entry_rcu(table, &afi->tables, list) {
Patrick McHardy96518512013-10-14 11:00:02 +0200297 if (idx < s_idx)
298 goto cont;
299 if (idx > s_idx)
300 memset(&cb->args[1], 0,
301 sizeof(cb->args) - sizeof(cb->args[0]));
302 if (nf_tables_fill_table_info(skb,
303 NETLINK_CB(cb->skb).portid,
304 cb->nlh->nlmsg_seq,
305 NFT_MSG_NEWTABLE,
306 NLM_F_MULTI,
307 afi->family, table) < 0)
308 goto done;
Pablo Neira Ayuso38e029f2014-07-01 12:23:12 +0200309
310 nl_dump_check_consistent(cb, nlmsg_hdr(skb));
Patrick McHardy96518512013-10-14 11:00:02 +0200311cont:
312 idx++;
313 }
314 }
315done:
Pablo Neira Ayusoe688a7f2014-07-01 11:49:18 +0200316 rcu_read_unlock();
Patrick McHardy96518512013-10-14 11:00:02 +0200317 cb->args[0] = idx;
318 return skb->len;
319}
320
Pablo Neira Ayuso55dd6f92014-04-03 11:53:37 +0200321/* Internal table flags */
322#define NFT_TABLE_INACTIVE (1 << 15)
323
Patrick McHardy96518512013-10-14 11:00:02 +0200324static int nf_tables_gettable(struct sock *nlsk, struct sk_buff *skb,
325 const struct nlmsghdr *nlh,
326 const struct nlattr * const nla[])
327{
328 const struct nfgenmsg *nfmsg = nlmsg_data(nlh);
329 const struct nft_af_info *afi;
330 const struct nft_table *table;
331 struct sk_buff *skb2;
Pablo Neira Ayuso99633ab2013-10-10 23:28:33 +0200332 struct net *net = sock_net(skb->sk);
Patrick McHardy96518512013-10-14 11:00:02 +0200333 int family = nfmsg->nfgen_family;
334 int err;
335
336 if (nlh->nlmsg_flags & NLM_F_DUMP) {
337 struct netlink_dump_control c = {
338 .dump = nf_tables_dump_tables,
339 };
340 return netlink_dump_start(nlsk, skb, nlh, &c);
341 }
342
Pablo Neira Ayuso99633ab2013-10-10 23:28:33 +0200343 afi = nf_tables_afinfo_lookup(net, family, false);
Patrick McHardy96518512013-10-14 11:00:02 +0200344 if (IS_ERR(afi))
345 return PTR_ERR(afi);
346
Pablo Neira Ayuso93707612013-10-10 23:21:26 +0200347 table = nf_tables_table_lookup(afi, nla[NFTA_TABLE_NAME]);
Patrick McHardy96518512013-10-14 11:00:02 +0200348 if (IS_ERR(table))
349 return PTR_ERR(table);
Pablo Neira Ayuso55dd6f92014-04-03 11:53:37 +0200350 if (table->flags & NFT_TABLE_INACTIVE)
351 return -ENOENT;
Patrick McHardy96518512013-10-14 11:00:02 +0200352
353 skb2 = alloc_skb(NLMSG_GOODSIZE, GFP_KERNEL);
354 if (!skb2)
355 return -ENOMEM;
356
357 err = nf_tables_fill_table_info(skb2, NETLINK_CB(skb).portid,
358 nlh->nlmsg_seq, NFT_MSG_NEWTABLE, 0,
359 family, table);
360 if (err < 0)
361 goto err;
362
363 return nlmsg_unicast(nlsk, skb2, NETLINK_CB(skb).portid);
364
365err:
366 kfree_skb(skb2);
367 return err;
368}
369
Patrick McHardy115a60b2014-01-03 12:16:15 +0000370static int nf_tables_table_enable(const struct nft_af_info *afi,
371 struct nft_table *table)
Pablo Neira Ayuso9ddf6322013-10-10 13:26:33 +0200372{
373 struct nft_chain *chain;
374 int err, i = 0;
375
376 list_for_each_entry(chain, &table->chains, list) {
Pablo Neira Ayusod2012972013-12-27 10:44:23 +0100377 if (!(chain->flags & NFT_BASE_CHAIN))
378 continue;
379
Patrick McHardy115a60b2014-01-03 12:16:15 +0000380 err = nf_register_hooks(nft_base_chain(chain)->ops, afi->nops);
Pablo Neira Ayuso9ddf6322013-10-10 13:26:33 +0200381 if (err < 0)
382 goto err;
383
384 i++;
385 }
386 return 0;
387err:
388 list_for_each_entry(chain, &table->chains, list) {
Pablo Neira Ayusod2012972013-12-27 10:44:23 +0100389 if (!(chain->flags & NFT_BASE_CHAIN))
390 continue;
391
Pablo Neira Ayuso9ddf6322013-10-10 13:26:33 +0200392 if (i-- <= 0)
393 break;
394
Patrick McHardy115a60b2014-01-03 12:16:15 +0000395 nf_unregister_hooks(nft_base_chain(chain)->ops, afi->nops);
Pablo Neira Ayuso9ddf6322013-10-10 13:26:33 +0200396 }
397 return err;
398}
399
Pablo Neira Ayusof75edf52014-03-30 14:04:52 +0200400static void nf_tables_table_disable(const struct nft_af_info *afi,
Patrick McHardy115a60b2014-01-03 12:16:15 +0000401 struct nft_table *table)
Pablo Neira Ayuso9ddf6322013-10-10 13:26:33 +0200402{
403 struct nft_chain *chain;
404
Pablo Neira Ayusod2012972013-12-27 10:44:23 +0100405 list_for_each_entry(chain, &table->chains, list) {
406 if (chain->flags & NFT_BASE_CHAIN)
Patrick McHardy115a60b2014-01-03 12:16:15 +0000407 nf_unregister_hooks(nft_base_chain(chain)->ops,
408 afi->nops);
Pablo Neira Ayusod2012972013-12-27 10:44:23 +0100409 }
Pablo Neira Ayuso9ddf6322013-10-10 13:26:33 +0200410}
411
Pablo Neira Ayusoe1aaca92014-03-30 14:04:53 +0200412static int nf_tables_updtable(struct nft_ctx *ctx)
Pablo Neira Ayuso9ddf6322013-10-10 13:26:33 +0200413{
Pablo Neira Ayuso55dd6f92014-04-03 11:53:37 +0200414 struct nft_trans *trans;
Pablo Neira Ayusoe1aaca92014-03-30 14:04:53 +0200415 u32 flags;
Pablo Neira Ayuso55dd6f92014-04-03 11:53:37 +0200416 int ret = 0;
Pablo Neira Ayuso9ddf6322013-10-10 13:26:33 +0200417
Pablo Neira Ayusoe1aaca92014-03-30 14:04:53 +0200418 if (!ctx->nla[NFTA_TABLE_FLAGS])
419 return 0;
Pablo Neira Ayuso9ddf6322013-10-10 13:26:33 +0200420
Pablo Neira Ayusoe1aaca92014-03-30 14:04:53 +0200421 flags = ntohl(nla_get_be32(ctx->nla[NFTA_TABLE_FLAGS]));
422 if (flags & ~NFT_TABLE_F_DORMANT)
423 return -EINVAL;
Pablo Neira Ayuso9ddf6322013-10-10 13:26:33 +0200424
Pablo Neira Ayuso63283dd2014-06-27 18:51:39 +0200425 if (flags == ctx->table->flags)
426 return 0;
427
Pablo Neira Ayuso55dd6f92014-04-03 11:53:37 +0200428 trans = nft_trans_alloc(ctx, NFT_MSG_NEWTABLE,
429 sizeof(struct nft_trans_table));
430 if (trans == NULL)
431 return -ENOMEM;
432
Pablo Neira Ayusoe1aaca92014-03-30 14:04:53 +0200433 if ((flags & NFT_TABLE_F_DORMANT) &&
434 !(ctx->table->flags & NFT_TABLE_F_DORMANT)) {
Pablo Neira Ayuso55dd6f92014-04-03 11:53:37 +0200435 nft_trans_table_enable(trans) = false;
Pablo Neira Ayusoe1aaca92014-03-30 14:04:53 +0200436 } else if (!(flags & NFT_TABLE_F_DORMANT) &&
437 ctx->table->flags & NFT_TABLE_F_DORMANT) {
438 ret = nf_tables_table_enable(ctx->afi, ctx->table);
Pablo Neira Ayuso55dd6f92014-04-03 11:53:37 +0200439 if (ret >= 0) {
Pablo Neira Ayusoe1aaca92014-03-30 14:04:53 +0200440 ctx->table->flags &= ~NFT_TABLE_F_DORMANT;
Pablo Neira Ayuso55dd6f92014-04-03 11:53:37 +0200441 nft_trans_table_enable(trans) = true;
Pablo Neira Ayuso9ddf6322013-10-10 13:26:33 +0200442 }
Pablo Neira Ayuso9ddf6322013-10-10 13:26:33 +0200443 }
Pablo Neira Ayusoe1aaca92014-03-30 14:04:53 +0200444 if (ret < 0)
445 goto err;
Pablo Neira Ayuso9ddf6322013-10-10 13:26:33 +0200446
Pablo Neira Ayuso55dd6f92014-04-03 11:53:37 +0200447 nft_trans_table_update(trans) = true;
448 list_add_tail(&trans->list, &ctx->net->nft.commit_list);
449 return 0;
Pablo Neira Ayuso9ddf6322013-10-10 13:26:33 +0200450err:
Pablo Neira Ayuso55dd6f92014-04-03 11:53:37 +0200451 nft_trans_destroy(trans);
Pablo Neira Ayuso9ddf6322013-10-10 13:26:33 +0200452 return ret;
453}
454
Pablo Neira Ayuso55dd6f92014-04-03 11:53:37 +0200455static int nft_trans_table_add(struct nft_ctx *ctx, int msg_type)
456{
457 struct nft_trans *trans;
458
459 trans = nft_trans_alloc(ctx, msg_type, sizeof(struct nft_trans_table));
460 if (trans == NULL)
461 return -ENOMEM;
462
463 if (msg_type == NFT_MSG_NEWTABLE)
464 ctx->table->flags |= NFT_TABLE_INACTIVE;
465
466 list_add_tail(&trans->list, &ctx->net->nft.commit_list);
467 return 0;
468}
469
Patrick McHardy96518512013-10-14 11:00:02 +0200470static int nf_tables_newtable(struct sock *nlsk, struct sk_buff *skb,
471 const struct nlmsghdr *nlh,
472 const struct nlattr * const nla[])
473{
474 const struct nfgenmsg *nfmsg = nlmsg_data(nlh);
475 const struct nlattr *name;
476 struct nft_af_info *afi;
477 struct nft_table *table;
Pablo Neira Ayuso99633ab2013-10-10 23:28:33 +0200478 struct net *net = sock_net(skb->sk);
Patrick McHardy96518512013-10-14 11:00:02 +0200479 int family = nfmsg->nfgen_family;
Patrick McHardyc5c1f972014-01-09 18:42:39 +0000480 u32 flags = 0;
Pablo Neira Ayusoe1aaca92014-03-30 14:04:53 +0200481 struct nft_ctx ctx;
Pablo Neira Ayuso55dd6f92014-04-03 11:53:37 +0200482 int err;
Patrick McHardy96518512013-10-14 11:00:02 +0200483
Pablo Neira Ayuso99633ab2013-10-10 23:28:33 +0200484 afi = nf_tables_afinfo_lookup(net, family, true);
Patrick McHardy96518512013-10-14 11:00:02 +0200485 if (IS_ERR(afi))
486 return PTR_ERR(afi);
487
488 name = nla[NFTA_TABLE_NAME];
Pablo Neira Ayuso93707612013-10-10 23:21:26 +0200489 table = nf_tables_table_lookup(afi, name);
Patrick McHardy96518512013-10-14 11:00:02 +0200490 if (IS_ERR(table)) {
491 if (PTR_ERR(table) != -ENOENT)
492 return PTR_ERR(table);
493 table = NULL;
494 }
495
496 if (table != NULL) {
Pablo Neira Ayuso55dd6f92014-04-03 11:53:37 +0200497 if (table->flags & NFT_TABLE_INACTIVE)
498 return -ENOENT;
Patrick McHardy96518512013-10-14 11:00:02 +0200499 if (nlh->nlmsg_flags & NLM_F_EXCL)
500 return -EEXIST;
501 if (nlh->nlmsg_flags & NLM_F_REPLACE)
502 return -EOPNOTSUPP;
Pablo Neira Ayusoe1aaca92014-03-30 14:04:53 +0200503
504 nft_ctx_init(&ctx, skb, nlh, afi, table, NULL, nla);
505 return nf_tables_updtable(&ctx);
Patrick McHardy96518512013-10-14 11:00:02 +0200506 }
507
Patrick McHardyc5c1f972014-01-09 18:42:39 +0000508 if (nla[NFTA_TABLE_FLAGS]) {
509 flags = ntohl(nla_get_be32(nla[NFTA_TABLE_FLAGS]));
510 if (flags & ~NFT_TABLE_F_DORMANT)
511 return -EINVAL;
512 }
513
Patrick McHardy7047f9d2014-01-09 18:42:40 +0000514 if (!try_module_get(afi->owner))
515 return -EAFNOSUPPORT;
516
Patrick McHardy96518512013-10-14 11:00:02 +0200517 table = kzalloc(sizeof(*table) + nla_len(name), GFP_KERNEL);
Patrick McHardy7047f9d2014-01-09 18:42:40 +0000518 if (table == NULL) {
519 module_put(afi->owner);
Patrick McHardy96518512013-10-14 11:00:02 +0200520 return -ENOMEM;
Patrick McHardy7047f9d2014-01-09 18:42:40 +0000521 }
Patrick McHardy96518512013-10-14 11:00:02 +0200522
523 nla_strlcpy(table->name, name, nla_len(name));
524 INIT_LIST_HEAD(&table->chains);
Patrick McHardy20a69342013-10-11 12:06:22 +0200525 INIT_LIST_HEAD(&table->sets);
Patrick McHardyc5c1f972014-01-09 18:42:39 +0000526 table->flags = flags;
Pablo Neira Ayuso9ddf6322013-10-10 13:26:33 +0200527
Pablo Neira Ayuso55dd6f92014-04-03 11:53:37 +0200528 nft_ctx_init(&ctx, skb, nlh, afi, table, NULL, nla);
529 err = nft_trans_table_add(&ctx, NFT_MSG_NEWTABLE);
530 if (err < 0) {
531 kfree(table);
532 module_put(afi->owner);
533 return err;
534 }
Pablo Neira Ayusoe688a7f2014-07-01 11:49:18 +0200535 list_add_tail_rcu(&table->list, &afi->tables);
Patrick McHardy96518512013-10-14 11:00:02 +0200536 return 0;
537}
538
539static int nf_tables_deltable(struct sock *nlsk, struct sk_buff *skb,
540 const struct nlmsghdr *nlh,
541 const struct nlattr * const nla[])
542{
543 const struct nfgenmsg *nfmsg = nlmsg_data(nlh);
544 struct nft_af_info *afi;
545 struct nft_table *table;
Pablo Neira Ayuso99633ab2013-10-10 23:28:33 +0200546 struct net *net = sock_net(skb->sk);
Pablo Neira Ayuso55dd6f92014-04-03 11:53:37 +0200547 int family = nfmsg->nfgen_family, err;
548 struct nft_ctx ctx;
Patrick McHardy96518512013-10-14 11:00:02 +0200549
Pablo Neira Ayuso99633ab2013-10-10 23:28:33 +0200550 afi = nf_tables_afinfo_lookup(net, family, false);
Patrick McHardy96518512013-10-14 11:00:02 +0200551 if (IS_ERR(afi))
552 return PTR_ERR(afi);
553
Pablo Neira Ayuso93707612013-10-10 23:21:26 +0200554 table = nf_tables_table_lookup(afi, nla[NFTA_TABLE_NAME]);
Patrick McHardy96518512013-10-14 11:00:02 +0200555 if (IS_ERR(table))
556 return PTR_ERR(table);
Pablo Neira Ayuso55dd6f92014-04-03 11:53:37 +0200557 if (table->flags & NFT_TABLE_INACTIVE)
558 return -ENOENT;
Pablo Neira Ayuso4fefee52014-05-23 12:39:26 +0200559 if (table->use > 0)
Patrick McHardy96518512013-10-14 11:00:02 +0200560 return -EBUSY;
561
Pablo Neira Ayuso55dd6f92014-04-03 11:53:37 +0200562 nft_ctx_init(&ctx, skb, nlh, afi, table, NULL, nla);
563 err = nft_trans_table_add(&ctx, NFT_MSG_DELTABLE);
564 if (err < 0)
565 return err;
566
Pablo Neira Ayusoe688a7f2014-07-01 11:49:18 +0200567 list_del_rcu(&table->list);
Patrick McHardy96518512013-10-14 11:00:02 +0200568 return 0;
569}
570
Pablo Neira Ayuso55dd6f92014-04-03 11:53:37 +0200571static void nf_tables_table_destroy(struct nft_ctx *ctx)
572{
Pablo Neira Ayuso4fefee52014-05-23 12:39:26 +0200573 BUG_ON(ctx->table->use > 0);
574
Pablo Neira Ayuso55dd6f92014-04-03 11:53:37 +0200575 kfree(ctx->table);
576 module_put(ctx->afi->owner);
577}
578
Patrick McHardy2a37d752014-01-09 18:42:37 +0000579int nft_register_chain_type(const struct nf_chain_type *ctype)
Patrick McHardy96518512013-10-14 11:00:02 +0200580{
Pablo Neira Ayuso93707612013-10-10 23:21:26 +0200581 int err = 0;
Patrick McHardy96518512013-10-14 11:00:02 +0200582
583 nfnl_lock(NFNL_SUBSYS_NFTABLES);
Pablo Neira Ayuso93707612013-10-10 23:21:26 +0200584 if (chain_type[ctype->family][ctype->type] != NULL) {
585 err = -EBUSY;
586 goto out;
Patrick McHardy96518512013-10-14 11:00:02 +0200587 }
Pablo Neira Ayuso93707612013-10-10 23:21:26 +0200588 chain_type[ctype->family][ctype->type] = ctype;
589out:
Patrick McHardy96518512013-10-14 11:00:02 +0200590 nfnl_unlock(NFNL_SUBSYS_NFTABLES);
591 return err;
592}
Pablo Neira Ayuso93707612013-10-10 23:21:26 +0200593EXPORT_SYMBOL_GPL(nft_register_chain_type);
Patrick McHardy96518512013-10-14 11:00:02 +0200594
Patrick McHardy2a37d752014-01-09 18:42:37 +0000595void nft_unregister_chain_type(const struct nf_chain_type *ctype)
Patrick McHardy96518512013-10-14 11:00:02 +0200596{
Patrick McHardy96518512013-10-14 11:00:02 +0200597 nfnl_lock(NFNL_SUBSYS_NFTABLES);
Pablo Neira Ayuso93707612013-10-10 23:21:26 +0200598 chain_type[ctype->family][ctype->type] = NULL;
Patrick McHardy96518512013-10-14 11:00:02 +0200599 nfnl_unlock(NFNL_SUBSYS_NFTABLES);
600}
Pablo Neira Ayuso93707612013-10-10 23:21:26 +0200601EXPORT_SYMBOL_GPL(nft_unregister_chain_type);
Patrick McHardy96518512013-10-14 11:00:02 +0200602
603/*
604 * Chains
605 */
606
607static struct nft_chain *
608nf_tables_chain_lookup_byhandle(const struct nft_table *table, u64 handle)
609{
610 struct nft_chain *chain;
611
612 list_for_each_entry(chain, &table->chains, list) {
613 if (chain->handle == handle)
614 return chain;
615 }
616
617 return ERR_PTR(-ENOENT);
618}
619
620static struct nft_chain *nf_tables_chain_lookup(const struct nft_table *table,
621 const struct nlattr *nla)
622{
623 struct nft_chain *chain;
624
625 if (nla == NULL)
626 return ERR_PTR(-EINVAL);
627
628 list_for_each_entry(chain, &table->chains, list) {
629 if (!nla_strcmp(nla, chain->name))
630 return chain;
631 }
632
633 return ERR_PTR(-ENOENT);
634}
635
636static const struct nla_policy nft_chain_policy[NFTA_CHAIN_MAX + 1] = {
637 [NFTA_CHAIN_TABLE] = { .type = NLA_STRING },
638 [NFTA_CHAIN_HANDLE] = { .type = NLA_U64 },
639 [NFTA_CHAIN_NAME] = { .type = NLA_STRING,
640 .len = NFT_CHAIN_MAXNAMELEN - 1 },
641 [NFTA_CHAIN_HOOK] = { .type = NLA_NESTED },
Pablo Neira Ayuso0ca743a2013-10-14 00:06:06 +0200642 [NFTA_CHAIN_POLICY] = { .type = NLA_U32 },
Pablo Neira4c1f7812014-03-31 17:43:47 +0200643 [NFTA_CHAIN_TYPE] = { .type = NLA_STRING },
Pablo Neira Ayuso0ca743a2013-10-14 00:06:06 +0200644 [NFTA_CHAIN_COUNTERS] = { .type = NLA_NESTED },
Patrick McHardy96518512013-10-14 11:00:02 +0200645};
646
647static const struct nla_policy nft_hook_policy[NFTA_HOOK_MAX + 1] = {
648 [NFTA_HOOK_HOOKNUM] = { .type = NLA_U32 },
649 [NFTA_HOOK_PRIORITY] = { .type = NLA_U32 },
650};
651
Pablo Neira Ayuso0ca743a2013-10-14 00:06:06 +0200652static int nft_dump_stats(struct sk_buff *skb, struct nft_stats __percpu *stats)
653{
654 struct nft_stats *cpu_stats, total;
655 struct nlattr *nest;
Eric Dumazetce355e22014-07-09 15:14:06 +0200656 unsigned int seq;
657 u64 pkts, bytes;
Pablo Neira Ayuso0ca743a2013-10-14 00:06:06 +0200658 int cpu;
659
660 memset(&total, 0, sizeof(total));
661 for_each_possible_cpu(cpu) {
662 cpu_stats = per_cpu_ptr(stats, cpu);
Eric Dumazetce355e22014-07-09 15:14:06 +0200663 do {
664 seq = u64_stats_fetch_begin_irq(&cpu_stats->syncp);
665 pkts = cpu_stats->pkts;
666 bytes = cpu_stats->bytes;
667 } while (u64_stats_fetch_retry_irq(&cpu_stats->syncp, seq));
668 total.pkts += pkts;
669 total.bytes += bytes;
Pablo Neira Ayuso0ca743a2013-10-14 00:06:06 +0200670 }
671 nest = nla_nest_start(skb, NFTA_CHAIN_COUNTERS);
672 if (nest == NULL)
673 goto nla_put_failure;
674
675 if (nla_put_be64(skb, NFTA_COUNTER_PACKETS, cpu_to_be64(total.pkts)) ||
676 nla_put_be64(skb, NFTA_COUNTER_BYTES, cpu_to_be64(total.bytes)))
677 goto nla_put_failure;
678
679 nla_nest_end(skb, nest);
680 return 0;
681
682nla_put_failure:
683 return -ENOSPC;
684}
685
Patrick McHardy96518512013-10-14 11:00:02 +0200686static int nf_tables_fill_chain_info(struct sk_buff *skb, u32 portid, u32 seq,
687 int event, u32 flags, int family,
688 const struct nft_table *table,
689 const struct nft_chain *chain)
690{
691 struct nlmsghdr *nlh;
692 struct nfgenmsg *nfmsg;
693
694 event |= NFNL_SUBSYS_NFTABLES << 8;
695 nlh = nlmsg_put(skb, portid, seq, event, sizeof(struct nfgenmsg), flags);
696 if (nlh == NULL)
697 goto nla_put_failure;
698
699 nfmsg = nlmsg_data(nlh);
700 nfmsg->nfgen_family = family;
701 nfmsg->version = NFNETLINK_V0;
702 nfmsg->res_id = 0;
703
704 if (nla_put_string(skb, NFTA_CHAIN_TABLE, table->name))
705 goto nla_put_failure;
706 if (nla_put_be64(skb, NFTA_CHAIN_HANDLE, cpu_to_be64(chain->handle)))
707 goto nla_put_failure;
708 if (nla_put_string(skb, NFTA_CHAIN_NAME, chain->name))
709 goto nla_put_failure;
710
711 if (chain->flags & NFT_BASE_CHAIN) {
Pablo Neira Ayuso0ca743a2013-10-14 00:06:06 +0200712 const struct nft_base_chain *basechain = nft_base_chain(chain);
Patrick McHardy115a60b2014-01-03 12:16:15 +0000713 const struct nf_hook_ops *ops = &basechain->ops[0];
Pablo Neira Ayuso0ca743a2013-10-14 00:06:06 +0200714 struct nlattr *nest;
715
716 nest = nla_nest_start(skb, NFTA_CHAIN_HOOK);
Patrick McHardy96518512013-10-14 11:00:02 +0200717 if (nest == NULL)
718 goto nla_put_failure;
719 if (nla_put_be32(skb, NFTA_HOOK_HOOKNUM, htonl(ops->hooknum)))
720 goto nla_put_failure;
721 if (nla_put_be32(skb, NFTA_HOOK_PRIORITY, htonl(ops->priority)))
722 goto nla_put_failure;
723 nla_nest_end(skb, nest);
Pablo Neira Ayuso93707612013-10-10 23:21:26 +0200724
Pablo Neira Ayuso0ca743a2013-10-14 00:06:06 +0200725 if (nla_put_be32(skb, NFTA_CHAIN_POLICY,
726 htonl(basechain->policy)))
727 goto nla_put_failure;
728
Patrick McHardybaae3e62014-01-09 18:42:34 +0000729 if (nla_put_string(skb, NFTA_CHAIN_TYPE, basechain->type->name))
730 goto nla_put_failure;
Pablo Neira Ayuso0ca743a2013-10-14 00:06:06 +0200731
732 if (nft_dump_stats(skb, nft_base_chain(chain)->stats))
733 goto nla_put_failure;
Patrick McHardy96518512013-10-14 11:00:02 +0200734 }
735
Pablo Neira Ayuso0ca743a2013-10-14 00:06:06 +0200736 if (nla_put_be32(skb, NFTA_CHAIN_USE, htonl(chain->use)))
737 goto nla_put_failure;
738
Patrick McHardy96518512013-10-14 11:00:02 +0200739 return nlmsg_end(skb, nlh);
740
741nla_put_failure:
742 nlmsg_trim(skb, nlh);
743 return -1;
744}
745
Pablo Neira Ayuso35151d82014-05-05 17:12:40 +0200746static int nf_tables_chain_notify(const struct nft_ctx *ctx, int event)
Patrick McHardy96518512013-10-14 11:00:02 +0200747{
748 struct sk_buff *skb;
Patrick McHardy96518512013-10-14 11:00:02 +0200749 int err;
750
Pablo Neira Ayuso128ad332014-05-09 17:14:24 +0200751 if (!ctx->report &&
752 !nfnetlink_has_listeners(ctx->net, NFNLGRP_NFTABLES))
Patrick McHardy96518512013-10-14 11:00:02 +0200753 return 0;
754
755 err = -ENOBUFS;
756 skb = nlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL);
757 if (skb == NULL)
758 goto err;
759
Pablo Neira Ayuso128ad332014-05-09 17:14:24 +0200760 err = nf_tables_fill_chain_info(skb, ctx->portid, ctx->seq, event, 0,
Pablo Neira Ayuso35151d82014-05-05 17:12:40 +0200761 ctx->afi->family, ctx->table,
762 ctx->chain);
Patrick McHardy96518512013-10-14 11:00:02 +0200763 if (err < 0) {
764 kfree_skb(skb);
765 goto err;
766 }
767
Pablo Neira Ayuso128ad332014-05-09 17:14:24 +0200768 err = nfnetlink_send(skb, ctx->net, ctx->portid, NFNLGRP_NFTABLES,
769 ctx->report, GFP_KERNEL);
Patrick McHardy96518512013-10-14 11:00:02 +0200770err:
Pablo Neira Ayuso128ad332014-05-09 17:14:24 +0200771 if (err < 0) {
772 nfnetlink_set_err(ctx->net, ctx->portid, NFNLGRP_NFTABLES,
773 err);
774 }
Patrick McHardy96518512013-10-14 11:00:02 +0200775 return err;
776}
777
778static int nf_tables_dump_chains(struct sk_buff *skb,
779 struct netlink_callback *cb)
780{
781 const struct nfgenmsg *nfmsg = nlmsg_data(cb->nlh);
782 const struct nft_af_info *afi;
783 const struct nft_table *table;
784 const struct nft_chain *chain;
785 unsigned int idx = 0, s_idx = cb->args[0];
Pablo Neira Ayuso99633ab2013-10-10 23:28:33 +0200786 struct net *net = sock_net(skb->sk);
Patrick McHardy96518512013-10-14 11:00:02 +0200787 int family = nfmsg->nfgen_family;
788
Pablo Neira Ayusoe688a7f2014-07-01 11:49:18 +0200789 rcu_read_lock();
Pablo Neira Ayuso38e029f2014-07-01 12:23:12 +0200790 cb->seq = net->nft.base_seq;
791
Pablo Neira Ayusoe688a7f2014-07-01 11:49:18 +0200792 list_for_each_entry_rcu(afi, &net->nft.af_info, list) {
Patrick McHardy96518512013-10-14 11:00:02 +0200793 if (family != NFPROTO_UNSPEC && family != afi->family)
794 continue;
795
Pablo Neira Ayusoe688a7f2014-07-01 11:49:18 +0200796 list_for_each_entry_rcu(table, &afi->tables, list) {
797 list_for_each_entry_rcu(chain, &table->chains, list) {
Patrick McHardy96518512013-10-14 11:00:02 +0200798 if (idx < s_idx)
799 goto cont;
800 if (idx > s_idx)
801 memset(&cb->args[1], 0,
802 sizeof(cb->args) - sizeof(cb->args[0]));
803 if (nf_tables_fill_chain_info(skb, NETLINK_CB(cb->skb).portid,
804 cb->nlh->nlmsg_seq,
805 NFT_MSG_NEWCHAIN,
806 NLM_F_MULTI,
807 afi->family, table, chain) < 0)
808 goto done;
Pablo Neira Ayuso38e029f2014-07-01 12:23:12 +0200809
810 nl_dump_check_consistent(cb, nlmsg_hdr(skb));
Patrick McHardy96518512013-10-14 11:00:02 +0200811cont:
812 idx++;
813 }
814 }
815 }
816done:
Pablo Neira Ayusoe688a7f2014-07-01 11:49:18 +0200817 rcu_read_unlock();
Patrick McHardy96518512013-10-14 11:00:02 +0200818 cb->args[0] = idx;
819 return skb->len;
820}
821
Patrick McHardy96518512013-10-14 11:00:02 +0200822static int nf_tables_getchain(struct sock *nlsk, struct sk_buff *skb,
823 const struct nlmsghdr *nlh,
824 const struct nlattr * const nla[])
825{
826 const struct nfgenmsg *nfmsg = nlmsg_data(nlh);
827 const struct nft_af_info *afi;
828 const struct nft_table *table;
829 const struct nft_chain *chain;
830 struct sk_buff *skb2;
Pablo Neira Ayuso99633ab2013-10-10 23:28:33 +0200831 struct net *net = sock_net(skb->sk);
Patrick McHardy96518512013-10-14 11:00:02 +0200832 int family = nfmsg->nfgen_family;
833 int err;
834
835 if (nlh->nlmsg_flags & NLM_F_DUMP) {
836 struct netlink_dump_control c = {
837 .dump = nf_tables_dump_chains,
838 };
839 return netlink_dump_start(nlsk, skb, nlh, &c);
840 }
841
Pablo Neira Ayuso99633ab2013-10-10 23:28:33 +0200842 afi = nf_tables_afinfo_lookup(net, family, false);
Patrick McHardy96518512013-10-14 11:00:02 +0200843 if (IS_ERR(afi))
844 return PTR_ERR(afi);
845
Pablo Neira Ayuso93707612013-10-10 23:21:26 +0200846 table = nf_tables_table_lookup(afi, nla[NFTA_CHAIN_TABLE]);
Patrick McHardy96518512013-10-14 11:00:02 +0200847 if (IS_ERR(table))
848 return PTR_ERR(table);
Pablo Neira Ayuso55dd6f92014-04-03 11:53:37 +0200849 if (table->flags & NFT_TABLE_INACTIVE)
850 return -ENOENT;
Patrick McHardy96518512013-10-14 11:00:02 +0200851
852 chain = nf_tables_chain_lookup(table, nla[NFTA_CHAIN_NAME]);
853 if (IS_ERR(chain))
854 return PTR_ERR(chain);
Pablo Neira Ayuso91c7b382014-04-09 11:58:08 +0200855 if (chain->flags & NFT_CHAIN_INACTIVE)
856 return -ENOENT;
Patrick McHardy96518512013-10-14 11:00:02 +0200857
858 skb2 = alloc_skb(NLMSG_GOODSIZE, GFP_KERNEL);
859 if (!skb2)
860 return -ENOMEM;
861
862 err = nf_tables_fill_chain_info(skb2, NETLINK_CB(skb).portid,
863 nlh->nlmsg_seq, NFT_MSG_NEWCHAIN, 0,
864 family, table, chain);
865 if (err < 0)
866 goto err;
867
868 return nlmsg_unicast(nlsk, skb2, NETLINK_CB(skb).portid);
869
870err:
871 kfree_skb(skb2);
872 return err;
873}
874
Pablo Neira Ayuso0ca743a2013-10-14 00:06:06 +0200875static const struct nla_policy nft_counter_policy[NFTA_COUNTER_MAX + 1] = {
876 [NFTA_COUNTER_PACKETS] = { .type = NLA_U64 },
877 [NFTA_COUNTER_BYTES] = { .type = NLA_U64 },
878};
879
Pablo Neira Ayusoff3cd7b2014-04-09 11:53:06 +0200880static struct nft_stats __percpu *nft_stats_alloc(const struct nlattr *attr)
Pablo Neira Ayuso0ca743a2013-10-14 00:06:06 +0200881{
882 struct nlattr *tb[NFTA_COUNTER_MAX+1];
883 struct nft_stats __percpu *newstats;
884 struct nft_stats *stats;
885 int err;
886
887 err = nla_parse_nested(tb, NFTA_COUNTER_MAX, attr, nft_counter_policy);
888 if (err < 0)
Pablo Neira Ayusoff3cd7b2014-04-09 11:53:06 +0200889 return ERR_PTR(err);
Pablo Neira Ayuso0ca743a2013-10-14 00:06:06 +0200890
891 if (!tb[NFTA_COUNTER_BYTES] || !tb[NFTA_COUNTER_PACKETS])
Pablo Neira Ayusoff3cd7b2014-04-09 11:53:06 +0200892 return ERR_PTR(-EINVAL);
Pablo Neira Ayuso0ca743a2013-10-14 00:06:06 +0200893
Eric Dumazetce355e22014-07-09 15:14:06 +0200894 newstats = netdev_alloc_pcpu_stats(struct nft_stats);
Pablo Neira Ayuso0ca743a2013-10-14 00:06:06 +0200895 if (newstats == NULL)
Pablo Neira Ayusoff3cd7b2014-04-09 11:53:06 +0200896 return ERR_PTR(-ENOMEM);
Pablo Neira Ayuso0ca743a2013-10-14 00:06:06 +0200897
898 /* Restore old counters on this cpu, no problem. Per-cpu statistics
899 * are not exposed to userspace.
900 */
901 stats = this_cpu_ptr(newstats);
902 stats->bytes = be64_to_cpu(nla_get_be64(tb[NFTA_COUNTER_BYTES]));
903 stats->pkts = be64_to_cpu(nla_get_be64(tb[NFTA_COUNTER_PACKETS]));
904
Pablo Neira Ayusoff3cd7b2014-04-09 11:53:06 +0200905 return newstats;
906}
907
908static void nft_chain_stats_replace(struct nft_base_chain *chain,
909 struct nft_stats __percpu *newstats)
910{
Pablo Neira Ayusob88825d2014-08-05 17:25:59 +0200911 if (newstats == NULL)
912 return;
913
Pablo Neira Ayuso0ca743a2013-10-14 00:06:06 +0200914 if (chain->stats) {
Pablo Neira Ayuso0ca743a2013-10-14 00:06:06 +0200915 struct nft_stats __percpu *oldstats =
Patrick McHardy67a8fc22014-02-18 18:06:49 +0000916 nft_dereference(chain->stats);
Pablo Neira Ayuso0ca743a2013-10-14 00:06:06 +0200917
918 rcu_assign_pointer(chain->stats, newstats);
919 synchronize_rcu();
920 free_percpu(oldstats);
921 } else
922 rcu_assign_pointer(chain->stats, newstats);
Pablo Neira Ayuso0ca743a2013-10-14 00:06:06 +0200923}
Pablo Neira Ayuso0ca743a2013-10-14 00:06:06 +0200924
Pablo Neira Ayuso91c7b382014-04-09 11:58:08 +0200925static int nft_trans_chain_add(struct nft_ctx *ctx, int msg_type)
926{
927 struct nft_trans *trans;
928
929 trans = nft_trans_alloc(ctx, msg_type, sizeof(struct nft_trans_chain));
930 if (trans == NULL)
931 return -ENOMEM;
932
933 if (msg_type == NFT_MSG_NEWCHAIN)
934 ctx->chain->flags |= NFT_CHAIN_INACTIVE;
935
936 list_add_tail(&trans->list, &ctx->net->nft.commit_list);
Pablo Neira Ayuso0ca743a2013-10-14 00:06:06 +0200937 return 0;
938}
939
Pablo Neira Ayuso91c7b382014-04-09 11:58:08 +0200940static void nf_tables_chain_destroy(struct nft_chain *chain)
941{
942 BUG_ON(chain->use > 0);
943
944 if (chain->flags & NFT_BASE_CHAIN) {
945 module_put(nft_base_chain(chain)->type->owner);
946 free_percpu(nft_base_chain(chain)->stats);
947 kfree(nft_base_chain(chain));
948 } else {
949 kfree(chain);
950 }
951}
952
Patrick McHardy96518512013-10-14 11:00:02 +0200953static int nf_tables_newchain(struct sock *nlsk, struct sk_buff *skb,
954 const struct nlmsghdr *nlh,
955 const struct nlattr * const nla[])
956{
957 const struct nfgenmsg *nfmsg = nlmsg_data(nlh);
958 const struct nlattr * uninitialized_var(name);
Pablo Neira Ayuso7c95f6d2014-04-04 01:22:45 +0200959 struct nft_af_info *afi;
Patrick McHardy96518512013-10-14 11:00:02 +0200960 struct nft_table *table;
961 struct nft_chain *chain;
Pablo Neira Ayuso0ca743a2013-10-14 00:06:06 +0200962 struct nft_base_chain *basechain = NULL;
Patrick McHardy96518512013-10-14 11:00:02 +0200963 struct nlattr *ha[NFTA_HOOK_MAX + 1];
Pablo Neira Ayuso99633ab2013-10-10 23:28:33 +0200964 struct net *net = sock_net(skb->sk);
Patrick McHardy96518512013-10-14 11:00:02 +0200965 int family = nfmsg->nfgen_family;
Patrick McHardy57de2a02014-01-09 18:42:31 +0000966 u8 policy = NF_ACCEPT;
Patrick McHardy96518512013-10-14 11:00:02 +0200967 u64 handle = 0;
Patrick McHardy115a60b2014-01-03 12:16:15 +0000968 unsigned int i;
Pablo Neira Ayusoff3cd7b2014-04-09 11:53:06 +0200969 struct nft_stats __percpu *stats;
Patrick McHardy96518512013-10-14 11:00:02 +0200970 int err;
971 bool create;
Pablo Neira Ayuso91c7b382014-04-09 11:58:08 +0200972 struct nft_ctx ctx;
Patrick McHardy96518512013-10-14 11:00:02 +0200973
974 create = nlh->nlmsg_flags & NLM_F_CREATE ? true : false;
975
Pablo Neira Ayuso99633ab2013-10-10 23:28:33 +0200976 afi = nf_tables_afinfo_lookup(net, family, true);
Patrick McHardy96518512013-10-14 11:00:02 +0200977 if (IS_ERR(afi))
978 return PTR_ERR(afi);
979
Pablo Neira Ayuso93707612013-10-10 23:21:26 +0200980 table = nf_tables_table_lookup(afi, nla[NFTA_CHAIN_TABLE]);
Patrick McHardy96518512013-10-14 11:00:02 +0200981 if (IS_ERR(table))
982 return PTR_ERR(table);
983
Patrick McHardy96518512013-10-14 11:00:02 +0200984 chain = NULL;
985 name = nla[NFTA_CHAIN_NAME];
986
987 if (nla[NFTA_CHAIN_HANDLE]) {
988 handle = be64_to_cpu(nla_get_be64(nla[NFTA_CHAIN_HANDLE]));
989 chain = nf_tables_chain_lookup_byhandle(table, handle);
990 if (IS_ERR(chain))
991 return PTR_ERR(chain);
992 } else {
993 chain = nf_tables_chain_lookup(table, name);
994 if (IS_ERR(chain)) {
995 if (PTR_ERR(chain) != -ENOENT)
996 return PTR_ERR(chain);
997 chain = NULL;
998 }
999 }
1000
Patrick McHardy57de2a02014-01-09 18:42:31 +00001001 if (nla[NFTA_CHAIN_POLICY]) {
1002 if ((chain != NULL &&
1003 !(chain->flags & NFT_BASE_CHAIN)) ||
1004 nla[NFTA_CHAIN_HOOK] == NULL)
1005 return -EOPNOTSUPP;
1006
Pablo Neira Ayuso8f46df12014-01-10 15:11:25 +01001007 policy = ntohl(nla_get_be32(nla[NFTA_CHAIN_POLICY]));
Patrick McHardy57de2a02014-01-09 18:42:31 +00001008 switch (policy) {
1009 case NF_DROP:
1010 case NF_ACCEPT:
1011 break;
1012 default:
1013 return -EINVAL;
1014 }
1015 }
1016
Patrick McHardy96518512013-10-14 11:00:02 +02001017 if (chain != NULL) {
Pablo Neira Ayuso91c7b382014-04-09 11:58:08 +02001018 struct nft_stats *stats = NULL;
1019 struct nft_trans *trans;
1020
1021 if (chain->flags & NFT_CHAIN_INACTIVE)
1022 return -ENOENT;
Patrick McHardy96518512013-10-14 11:00:02 +02001023 if (nlh->nlmsg_flags & NLM_F_EXCL)
1024 return -EEXIST;
1025 if (nlh->nlmsg_flags & NLM_F_REPLACE)
1026 return -EOPNOTSUPP;
1027
1028 if (nla[NFTA_CHAIN_HANDLE] && name &&
1029 !IS_ERR(nf_tables_chain_lookup(table, nla[NFTA_CHAIN_NAME])))
1030 return -EEXIST;
1031
Pablo Neira Ayuso0ca743a2013-10-14 00:06:06 +02001032 if (nla[NFTA_CHAIN_COUNTERS]) {
1033 if (!(chain->flags & NFT_BASE_CHAIN))
1034 return -EOPNOTSUPP;
1035
Pablo Neira Ayusoff3cd7b2014-04-09 11:53:06 +02001036 stats = nft_stats_alloc(nla[NFTA_CHAIN_COUNTERS]);
1037 if (IS_ERR(stats))
1038 return PTR_ERR(stats);
Pablo Neira Ayuso0ca743a2013-10-14 00:06:06 +02001039 }
1040
Pablo Neira Ayuso91c7b382014-04-09 11:58:08 +02001041 nft_ctx_init(&ctx, skb, nlh, afi, table, chain, nla);
1042 trans = nft_trans_alloc(&ctx, NFT_MSG_NEWCHAIN,
1043 sizeof(struct nft_trans_chain));
1044 if (trans == NULL)
1045 return -ENOMEM;
1046
1047 nft_trans_chain_stats(trans) = stats;
1048 nft_trans_chain_update(trans) = true;
1049
Patrick McHardy4401a862014-01-09 18:42:32 +00001050 if (nla[NFTA_CHAIN_POLICY])
Pablo Neira Ayuso91c7b382014-04-09 11:58:08 +02001051 nft_trans_chain_policy(trans) = policy;
1052 else
1053 nft_trans_chain_policy(trans) = -1;
Patrick McHardy4401a862014-01-09 18:42:32 +00001054
Pablo Neira Ayuso91c7b382014-04-09 11:58:08 +02001055 if (nla[NFTA_CHAIN_HANDLE] && name) {
1056 nla_strlcpy(nft_trans_chain_name(trans), name,
1057 NFT_CHAIN_MAXNAMELEN);
1058 }
1059 list_add_tail(&trans->list, &net->nft.commit_list);
1060 return 0;
Patrick McHardy96518512013-10-14 11:00:02 +02001061 }
1062
Patrick McHardy75820672014-01-09 18:42:33 +00001063 if (table->use == UINT_MAX)
1064 return -EOVERFLOW;
1065
Patrick McHardy96518512013-10-14 11:00:02 +02001066 if (nla[NFTA_CHAIN_HOOK]) {
Patrick McHardy2a37d752014-01-09 18:42:37 +00001067 const struct nf_chain_type *type;
Patrick McHardy96518512013-10-14 11:00:02 +02001068 struct nf_hook_ops *ops;
Pablo Neira Ayuso93707612013-10-10 23:21:26 +02001069 nf_hookfn *hookfn;
Patrick McHardy115a60b2014-01-03 12:16:15 +00001070 u32 hooknum, priority;
Pablo Neira Ayuso93707612013-10-10 23:21:26 +02001071
Patrick McHardybaae3e62014-01-09 18:42:34 +00001072 type = chain_type[family][NFT_CHAIN_T_DEFAULT];
Pablo Neira Ayuso93707612013-10-10 23:21:26 +02001073 if (nla[NFTA_CHAIN_TYPE]) {
1074 type = nf_tables_chain_type_lookup(afi,
1075 nla[NFTA_CHAIN_TYPE],
1076 create);
Patrick McHardy93b08062014-01-09 18:42:36 +00001077 if (IS_ERR(type))
1078 return PTR_ERR(type);
Pablo Neira Ayuso93707612013-10-10 23:21:26 +02001079 }
Patrick McHardy96518512013-10-14 11:00:02 +02001080
1081 err = nla_parse_nested(ha, NFTA_HOOK_MAX, nla[NFTA_CHAIN_HOOK],
1082 nft_hook_policy);
1083 if (err < 0)
1084 return err;
1085 if (ha[NFTA_HOOK_HOOKNUM] == NULL ||
1086 ha[NFTA_HOOK_PRIORITY] == NULL)
1087 return -EINVAL;
Pablo Neira Ayuso93707612013-10-10 23:21:26 +02001088
1089 hooknum = ntohl(nla_get_be32(ha[NFTA_HOOK_HOOKNUM]));
1090 if (hooknum >= afi->nhooks)
Patrick McHardy96518512013-10-14 11:00:02 +02001091 return -EINVAL;
Patrick McHardy115a60b2014-01-03 12:16:15 +00001092 priority = ntohl(nla_get_be32(ha[NFTA_HOOK_PRIORITY]));
Patrick McHardy96518512013-10-14 11:00:02 +02001093
Patrick McHardybaae3e62014-01-09 18:42:34 +00001094 if (!(type->hook_mask & (1 << hooknum)))
Pablo Neira Ayuso93707612013-10-10 23:21:26 +02001095 return -EOPNOTSUPP;
Patrick McHardyfa2c1de2014-01-09 18:42:38 +00001096 if (!try_module_get(type->owner))
Patrick McHardybaae3e62014-01-09 18:42:34 +00001097 return -ENOENT;
Patrick McHardyfa2c1de2014-01-09 18:42:38 +00001098 hookfn = type->hooks[hooknum];
Pablo Neira Ayuso93707612013-10-10 23:21:26 +02001099
Patrick McHardy96518512013-10-14 11:00:02 +02001100 basechain = kzalloc(sizeof(*basechain), GFP_KERNEL);
1101 if (basechain == NULL)
1102 return -ENOMEM;
Pablo Neira Ayuso93707612013-10-10 23:21:26 +02001103
Patrick McHardy4401a862014-01-09 18:42:32 +00001104 if (nla[NFTA_CHAIN_COUNTERS]) {
Pablo Neira Ayusoff3cd7b2014-04-09 11:53:06 +02001105 stats = nft_stats_alloc(nla[NFTA_CHAIN_COUNTERS]);
1106 if (IS_ERR(stats)) {
Patrick McHardyfa2c1de2014-01-09 18:42:38 +00001107 module_put(type->owner);
Patrick McHardy4401a862014-01-09 18:42:32 +00001108 kfree(basechain);
Pablo Neira Ayusoff3cd7b2014-04-09 11:53:06 +02001109 return PTR_ERR(stats);
Patrick McHardy4401a862014-01-09 18:42:32 +00001110 }
Pablo Neira Ayusoff3cd7b2014-04-09 11:53:06 +02001111 basechain->stats = stats;
Patrick McHardy4401a862014-01-09 18:42:32 +00001112 } else {
Eric Dumazetce355e22014-07-09 15:14:06 +02001113 stats = netdev_alloc_pcpu_stats(struct nft_stats);
Pablo Neira Ayusoff3cd7b2014-04-09 11:53:06 +02001114 if (IS_ERR(stats)) {
Patrick McHardyfa2c1de2014-01-09 18:42:38 +00001115 module_put(type->owner);
Patrick McHardy4401a862014-01-09 18:42:32 +00001116 kfree(basechain);
Pablo Neira Ayusoff3cd7b2014-04-09 11:53:06 +02001117 return PTR_ERR(stats);
Patrick McHardy4401a862014-01-09 18:42:32 +00001118 }
Pablo Neira Ayusoff3cd7b2014-04-09 11:53:06 +02001119 rcu_assign_pointer(basechain->stats, stats);
Patrick McHardy4401a862014-01-09 18:42:32 +00001120 }
1121
Pablo Neira Ayuso93707612013-10-10 23:21:26 +02001122 basechain->type = type;
Patrick McHardy96518512013-10-14 11:00:02 +02001123 chain = &basechain->chain;
1124
Patrick McHardy115a60b2014-01-03 12:16:15 +00001125 for (i = 0; i < afi->nops; i++) {
1126 ops = &basechain->ops[i];
1127 ops->pf = family;
1128 ops->owner = afi->owner;
1129 ops->hooknum = hooknum;
1130 ops->priority = priority;
1131 ops->priv = chain;
1132 ops->hook = afi->hooks[ops->hooknum];
1133 if (hookfn)
1134 ops->hook = hookfn;
1135 if (afi->hook_ops_init)
1136 afi->hook_ops_init(ops, i);
1137 }
Patrick McHardy96518512013-10-14 11:00:02 +02001138
1139 chain->flags |= NFT_BASE_CHAIN;
Patrick McHardy57de2a02014-01-09 18:42:31 +00001140 basechain->policy = policy;
Patrick McHardy96518512013-10-14 11:00:02 +02001141 } else {
1142 chain = kzalloc(sizeof(*chain), GFP_KERNEL);
1143 if (chain == NULL)
1144 return -ENOMEM;
1145 }
1146
1147 INIT_LIST_HEAD(&chain->rules);
1148 chain->handle = nf_tables_alloc_handle(table);
Pablo Neira Ayuso0628b122013-10-14 11:05:33 +02001149 chain->net = net;
Pablo Neira Ayusob5bc89b2013-10-10 16:49:19 +02001150 chain->table = table;
Patrick McHardy96518512013-10-14 11:00:02 +02001151 nla_strlcpy(chain->name, name, NFT_CHAIN_MAXNAMELEN);
1152
Pablo Neira Ayuso9ddf6322013-10-10 13:26:33 +02001153 if (!(table->flags & NFT_TABLE_F_DORMANT) &&
1154 chain->flags & NFT_BASE_CHAIN) {
Patrick McHardy115a60b2014-01-03 12:16:15 +00001155 err = nf_register_hooks(nft_base_chain(chain)->ops, afi->nops);
Pablo Neira Ayuso91c7b382014-04-09 11:58:08 +02001156 if (err < 0)
1157 goto err1;
Pablo Neira Ayuso0ca743a2013-10-14 00:06:06 +02001158 }
Pablo Neira Ayuso91c7b382014-04-09 11:58:08 +02001159
1160 nft_ctx_init(&ctx, skb, nlh, afi, table, chain, nla);
1161 err = nft_trans_chain_add(&ctx, NFT_MSG_NEWCHAIN);
1162 if (err < 0)
1163 goto err2;
1164
Pablo Neira Ayuso4fefee52014-05-23 12:39:26 +02001165 table->use++;
Pablo Neira Ayusoe688a7f2014-07-01 11:49:18 +02001166 list_add_tail_rcu(&chain->list, &table->chains);
Patrick McHardy96518512013-10-14 11:00:02 +02001167 return 0;
Pablo Neira Ayuso91c7b382014-04-09 11:58:08 +02001168err2:
Arturo Borreroc5598792014-09-02 16:42:23 +02001169 nf_tables_unregister_hooks(table, chain, afi->nops);
Pablo Neira Ayuso91c7b382014-04-09 11:58:08 +02001170err1:
1171 nf_tables_chain_destroy(chain);
1172 return err;
Patrick McHardy96518512013-10-14 11:00:02 +02001173}
1174
1175static int nf_tables_delchain(struct sock *nlsk, struct sk_buff *skb,
1176 const struct nlmsghdr *nlh,
1177 const struct nlattr * const nla[])
1178{
1179 const struct nfgenmsg *nfmsg = nlmsg_data(nlh);
Pablo Neira Ayuso7c95f6d2014-04-04 01:22:45 +02001180 struct nft_af_info *afi;
Patrick McHardy96518512013-10-14 11:00:02 +02001181 struct nft_table *table;
1182 struct nft_chain *chain;
Pablo Neira Ayuso99633ab2013-10-10 23:28:33 +02001183 struct net *net = sock_net(skb->sk);
Patrick McHardy96518512013-10-14 11:00:02 +02001184 int family = nfmsg->nfgen_family;
Pablo Neira Ayuso91c7b382014-04-09 11:58:08 +02001185 struct nft_ctx ctx;
1186 int err;
Patrick McHardy96518512013-10-14 11:00:02 +02001187
Pablo Neira Ayuso99633ab2013-10-10 23:28:33 +02001188 afi = nf_tables_afinfo_lookup(net, family, false);
Patrick McHardy96518512013-10-14 11:00:02 +02001189 if (IS_ERR(afi))
1190 return PTR_ERR(afi);
1191
Pablo Neira Ayuso93707612013-10-10 23:21:26 +02001192 table = nf_tables_table_lookup(afi, nla[NFTA_CHAIN_TABLE]);
Patrick McHardy96518512013-10-14 11:00:02 +02001193 if (IS_ERR(table))
1194 return PTR_ERR(table);
Pablo Neira Ayuso55dd6f92014-04-03 11:53:37 +02001195 if (table->flags & NFT_TABLE_INACTIVE)
1196 return -ENOENT;
Patrick McHardy96518512013-10-14 11:00:02 +02001197
1198 chain = nf_tables_chain_lookup(table, nla[NFTA_CHAIN_NAME]);
1199 if (IS_ERR(chain))
1200 return PTR_ERR(chain);
Pablo Neira Ayuso91c7b382014-04-09 11:58:08 +02001201 if (chain->flags & NFT_CHAIN_INACTIVE)
1202 return -ENOENT;
Pablo Neira Ayuso4fefee52014-05-23 12:39:26 +02001203 if (chain->use > 0)
Patrick McHardy96518512013-10-14 11:00:02 +02001204 return -EBUSY;
1205
Pablo Neira Ayuso91c7b382014-04-09 11:58:08 +02001206 nft_ctx_init(&ctx, skb, nlh, afi, table, chain, nla);
1207 err = nft_trans_chain_add(&ctx, NFT_MSG_DELCHAIN);
1208 if (err < 0)
1209 return err;
1210
Pablo Neira Ayuso4fefee52014-05-23 12:39:26 +02001211 table->use--;
Pablo Neira Ayusoe688a7f2014-07-01 11:49:18 +02001212 list_del_rcu(&chain->list);
Patrick McHardy96518512013-10-14 11:00:02 +02001213 return 0;
1214}
1215
Patrick McHardy96518512013-10-14 11:00:02 +02001216/*
1217 * Expressions
1218 */
1219
1220/**
Patrick McHardyef1f7df2013-10-10 11:41:20 +02001221 * nft_register_expr - register nf_tables expr type
1222 * @ops: expr type
Patrick McHardy96518512013-10-14 11:00:02 +02001223 *
Patrick McHardyef1f7df2013-10-10 11:41:20 +02001224 * Registers the expr type for use with nf_tables. Returns zero on
Patrick McHardy96518512013-10-14 11:00:02 +02001225 * success or a negative errno code otherwise.
1226 */
Patrick McHardyef1f7df2013-10-10 11:41:20 +02001227int nft_register_expr(struct nft_expr_type *type)
Patrick McHardy96518512013-10-14 11:00:02 +02001228{
1229 nfnl_lock(NFNL_SUBSYS_NFTABLES);
Tomasz Bursztyka758dbce2014-04-14 15:41:26 +03001230 if (type->family == NFPROTO_UNSPEC)
Pablo Neira Ayusoe688a7f2014-07-01 11:49:18 +02001231 list_add_tail_rcu(&type->list, &nf_tables_expressions);
Tomasz Bursztyka758dbce2014-04-14 15:41:26 +03001232 else
Pablo Neira Ayusoe688a7f2014-07-01 11:49:18 +02001233 list_add_rcu(&type->list, &nf_tables_expressions);
Patrick McHardy96518512013-10-14 11:00:02 +02001234 nfnl_unlock(NFNL_SUBSYS_NFTABLES);
1235 return 0;
1236}
1237EXPORT_SYMBOL_GPL(nft_register_expr);
1238
1239/**
Patrick McHardyef1f7df2013-10-10 11:41:20 +02001240 * nft_unregister_expr - unregister nf_tables expr type
1241 * @ops: expr type
Patrick McHardy96518512013-10-14 11:00:02 +02001242 *
Patrick McHardyef1f7df2013-10-10 11:41:20 +02001243 * Unregisters the expr typefor use with nf_tables.
Patrick McHardy96518512013-10-14 11:00:02 +02001244 */
Patrick McHardyef1f7df2013-10-10 11:41:20 +02001245void nft_unregister_expr(struct nft_expr_type *type)
Patrick McHardy96518512013-10-14 11:00:02 +02001246{
1247 nfnl_lock(NFNL_SUBSYS_NFTABLES);
Pablo Neira Ayusoe688a7f2014-07-01 11:49:18 +02001248 list_del_rcu(&type->list);
Patrick McHardy96518512013-10-14 11:00:02 +02001249 nfnl_unlock(NFNL_SUBSYS_NFTABLES);
1250}
1251EXPORT_SYMBOL_GPL(nft_unregister_expr);
1252
Patrick McHardy64d46802014-02-05 15:03:37 +00001253static const struct nft_expr_type *__nft_expr_type_get(u8 family,
1254 struct nlattr *nla)
Patrick McHardy96518512013-10-14 11:00:02 +02001255{
Patrick McHardyef1f7df2013-10-10 11:41:20 +02001256 const struct nft_expr_type *type;
Patrick McHardy96518512013-10-14 11:00:02 +02001257
Patrick McHardyef1f7df2013-10-10 11:41:20 +02001258 list_for_each_entry(type, &nf_tables_expressions, list) {
Patrick McHardy64d46802014-02-05 15:03:37 +00001259 if (!nla_strcmp(nla, type->name) &&
1260 (!type->family || type->family == family))
Patrick McHardyef1f7df2013-10-10 11:41:20 +02001261 return type;
Patrick McHardy96518512013-10-14 11:00:02 +02001262 }
1263 return NULL;
1264}
1265
Patrick McHardy64d46802014-02-05 15:03:37 +00001266static const struct nft_expr_type *nft_expr_type_get(u8 family,
1267 struct nlattr *nla)
Patrick McHardy96518512013-10-14 11:00:02 +02001268{
Patrick McHardyef1f7df2013-10-10 11:41:20 +02001269 const struct nft_expr_type *type;
Patrick McHardy96518512013-10-14 11:00:02 +02001270
1271 if (nla == NULL)
1272 return ERR_PTR(-EINVAL);
1273
Patrick McHardy64d46802014-02-05 15:03:37 +00001274 type = __nft_expr_type_get(family, nla);
Patrick McHardyef1f7df2013-10-10 11:41:20 +02001275 if (type != NULL && try_module_get(type->owner))
1276 return type;
Patrick McHardy96518512013-10-14 11:00:02 +02001277
1278#ifdef CONFIG_MODULES
Patrick McHardyef1f7df2013-10-10 11:41:20 +02001279 if (type == NULL) {
Patrick McHardy96518512013-10-14 11:00:02 +02001280 nfnl_unlock(NFNL_SUBSYS_NFTABLES);
Patrick McHardy64d46802014-02-05 15:03:37 +00001281 request_module("nft-expr-%u-%.*s", family,
1282 nla_len(nla), (char *)nla_data(nla));
1283 nfnl_lock(NFNL_SUBSYS_NFTABLES);
1284 if (__nft_expr_type_get(family, nla))
1285 return ERR_PTR(-EAGAIN);
1286
1287 nfnl_unlock(NFNL_SUBSYS_NFTABLES);
Patrick McHardy96518512013-10-14 11:00:02 +02001288 request_module("nft-expr-%.*s",
1289 nla_len(nla), (char *)nla_data(nla));
1290 nfnl_lock(NFNL_SUBSYS_NFTABLES);
Patrick McHardy64d46802014-02-05 15:03:37 +00001291 if (__nft_expr_type_get(family, nla))
Patrick McHardy96518512013-10-14 11:00:02 +02001292 return ERR_PTR(-EAGAIN);
1293 }
1294#endif
1295 return ERR_PTR(-ENOENT);
1296}
1297
1298static const struct nla_policy nft_expr_policy[NFTA_EXPR_MAX + 1] = {
1299 [NFTA_EXPR_NAME] = { .type = NLA_STRING },
1300 [NFTA_EXPR_DATA] = { .type = NLA_NESTED },
1301};
1302
1303static int nf_tables_fill_expr_info(struct sk_buff *skb,
1304 const struct nft_expr *expr)
1305{
Patrick McHardyef1f7df2013-10-10 11:41:20 +02001306 if (nla_put_string(skb, NFTA_EXPR_NAME, expr->ops->type->name))
Patrick McHardy96518512013-10-14 11:00:02 +02001307 goto nla_put_failure;
1308
1309 if (expr->ops->dump) {
1310 struct nlattr *data = nla_nest_start(skb, NFTA_EXPR_DATA);
1311 if (data == NULL)
1312 goto nla_put_failure;
1313 if (expr->ops->dump(skb, expr) < 0)
1314 goto nla_put_failure;
1315 nla_nest_end(skb, data);
1316 }
1317
1318 return skb->len;
1319
1320nla_put_failure:
1321 return -1;
1322};
1323
1324struct nft_expr_info {
1325 const struct nft_expr_ops *ops;
Patrick McHardyef1f7df2013-10-10 11:41:20 +02001326 struct nlattr *tb[NFT_EXPR_MAXATTR + 1];
Patrick McHardy96518512013-10-14 11:00:02 +02001327};
1328
Pablo Neira Ayuso0ca743a2013-10-14 00:06:06 +02001329static int nf_tables_expr_parse(const struct nft_ctx *ctx,
1330 const struct nlattr *nla,
Patrick McHardy96518512013-10-14 11:00:02 +02001331 struct nft_expr_info *info)
1332{
Patrick McHardyef1f7df2013-10-10 11:41:20 +02001333 const struct nft_expr_type *type;
Patrick McHardy96518512013-10-14 11:00:02 +02001334 const struct nft_expr_ops *ops;
Patrick McHardyef1f7df2013-10-10 11:41:20 +02001335 struct nlattr *tb[NFTA_EXPR_MAX + 1];
Patrick McHardy96518512013-10-14 11:00:02 +02001336 int err;
1337
Patrick McHardyef1f7df2013-10-10 11:41:20 +02001338 err = nla_parse_nested(tb, NFTA_EXPR_MAX, nla, nft_expr_policy);
Patrick McHardy96518512013-10-14 11:00:02 +02001339 if (err < 0)
1340 return err;
1341
Patrick McHardy64d46802014-02-05 15:03:37 +00001342 type = nft_expr_type_get(ctx->afi->family, tb[NFTA_EXPR_NAME]);
Patrick McHardyef1f7df2013-10-10 11:41:20 +02001343 if (IS_ERR(type))
1344 return PTR_ERR(type);
1345
1346 if (tb[NFTA_EXPR_DATA]) {
1347 err = nla_parse_nested(info->tb, type->maxattr,
1348 tb[NFTA_EXPR_DATA], type->policy);
1349 if (err < 0)
1350 goto err1;
1351 } else
1352 memset(info->tb, 0, sizeof(info->tb[0]) * (type->maxattr + 1));
1353
1354 if (type->select_ops != NULL) {
Pablo Neira Ayuso0ca743a2013-10-14 00:06:06 +02001355 ops = type->select_ops(ctx,
1356 (const struct nlattr * const *)info->tb);
Patrick McHardyef1f7df2013-10-10 11:41:20 +02001357 if (IS_ERR(ops)) {
1358 err = PTR_ERR(ops);
1359 goto err1;
1360 }
1361 } else
1362 ops = type->ops;
1363
Patrick McHardy96518512013-10-14 11:00:02 +02001364 info->ops = ops;
1365 return 0;
Patrick McHardyef1f7df2013-10-10 11:41:20 +02001366
1367err1:
1368 module_put(type->owner);
1369 return err;
Patrick McHardy96518512013-10-14 11:00:02 +02001370}
1371
1372static int nf_tables_newexpr(const struct nft_ctx *ctx,
Patrick McHardyef1f7df2013-10-10 11:41:20 +02001373 const struct nft_expr_info *info,
Patrick McHardy96518512013-10-14 11:00:02 +02001374 struct nft_expr *expr)
1375{
1376 const struct nft_expr_ops *ops = info->ops;
1377 int err;
1378
1379 expr->ops = ops;
1380 if (ops->init) {
Patrick McHardyef1f7df2013-10-10 11:41:20 +02001381 err = ops->init(ctx, expr, (const struct nlattr **)info->tb);
Patrick McHardy96518512013-10-14 11:00:02 +02001382 if (err < 0)
1383 goto err1;
1384 }
1385
Patrick McHardy96518512013-10-14 11:00:02 +02001386 return 0;
1387
1388err1:
1389 expr->ops = NULL;
1390 return err;
1391}
1392
Patrick McHardy62472bc2014-03-07 19:08:30 +01001393static void nf_tables_expr_destroy(const struct nft_ctx *ctx,
1394 struct nft_expr *expr)
Patrick McHardy96518512013-10-14 11:00:02 +02001395{
1396 if (expr->ops->destroy)
Patrick McHardy62472bc2014-03-07 19:08:30 +01001397 expr->ops->destroy(ctx, expr);
Patrick McHardyef1f7df2013-10-10 11:41:20 +02001398 module_put(expr->ops->type->owner);
Patrick McHardy96518512013-10-14 11:00:02 +02001399}
1400
1401/*
1402 * Rules
1403 */
1404
1405static struct nft_rule *__nf_tables_rule_lookup(const struct nft_chain *chain,
1406 u64 handle)
1407{
1408 struct nft_rule *rule;
1409
1410 // FIXME: this sucks
1411 list_for_each_entry(rule, &chain->rules, list) {
1412 if (handle == rule->handle)
1413 return rule;
1414 }
1415
1416 return ERR_PTR(-ENOENT);
1417}
1418
1419static struct nft_rule *nf_tables_rule_lookup(const struct nft_chain *chain,
1420 const struct nlattr *nla)
1421{
1422 if (nla == NULL)
1423 return ERR_PTR(-EINVAL);
1424
1425 return __nf_tables_rule_lookup(chain, be64_to_cpu(nla_get_be64(nla)));
1426}
1427
1428static const struct nla_policy nft_rule_policy[NFTA_RULE_MAX + 1] = {
1429 [NFTA_RULE_TABLE] = { .type = NLA_STRING },
1430 [NFTA_RULE_CHAIN] = { .type = NLA_STRING,
1431 .len = NFT_CHAIN_MAXNAMELEN - 1 },
1432 [NFTA_RULE_HANDLE] = { .type = NLA_U64 },
1433 [NFTA_RULE_EXPRESSIONS] = { .type = NLA_NESTED },
Pablo Neira Ayuso0ca743a2013-10-14 00:06:06 +02001434 [NFTA_RULE_COMPAT] = { .type = NLA_NESTED },
Eric Leblond5e948462013-10-10 13:41:44 +02001435 [NFTA_RULE_POSITION] = { .type = NLA_U64 },
Pablo Neira Ayuso0768b3b2014-02-19 17:27:06 +01001436 [NFTA_RULE_USERDATA] = { .type = NLA_BINARY,
1437 .len = NFT_USERDATA_MAXLEN },
Patrick McHardy96518512013-10-14 11:00:02 +02001438};
1439
1440static int nf_tables_fill_rule_info(struct sk_buff *skb, u32 portid, u32 seq,
1441 int event, u32 flags, int family,
1442 const struct nft_table *table,
1443 const struct nft_chain *chain,
1444 const struct nft_rule *rule)
1445{
1446 struct nlmsghdr *nlh;
1447 struct nfgenmsg *nfmsg;
1448 const struct nft_expr *expr, *next;
1449 struct nlattr *list;
Eric Leblond5e948462013-10-10 13:41:44 +02001450 const struct nft_rule *prule;
1451 int type = event | NFNL_SUBSYS_NFTABLES << 8;
Patrick McHardy96518512013-10-14 11:00:02 +02001452
Eric Leblond5e948462013-10-10 13:41:44 +02001453 nlh = nlmsg_put(skb, portid, seq, type, sizeof(struct nfgenmsg),
Patrick McHardy96518512013-10-14 11:00:02 +02001454 flags);
1455 if (nlh == NULL)
1456 goto nla_put_failure;
1457
1458 nfmsg = nlmsg_data(nlh);
1459 nfmsg->nfgen_family = family;
1460 nfmsg->version = NFNETLINK_V0;
1461 nfmsg->res_id = 0;
1462
1463 if (nla_put_string(skb, NFTA_RULE_TABLE, table->name))
1464 goto nla_put_failure;
1465 if (nla_put_string(skb, NFTA_RULE_CHAIN, chain->name))
1466 goto nla_put_failure;
1467 if (nla_put_be64(skb, NFTA_RULE_HANDLE, cpu_to_be64(rule->handle)))
1468 goto nla_put_failure;
1469
Eric Leblond5e948462013-10-10 13:41:44 +02001470 if ((event != NFT_MSG_DELRULE) && (rule->list.prev != &chain->rules)) {
1471 prule = list_entry(rule->list.prev, struct nft_rule, list);
1472 if (nla_put_be64(skb, NFTA_RULE_POSITION,
1473 cpu_to_be64(prule->handle)))
1474 goto nla_put_failure;
1475 }
1476
Patrick McHardy96518512013-10-14 11:00:02 +02001477 list = nla_nest_start(skb, NFTA_RULE_EXPRESSIONS);
1478 if (list == NULL)
1479 goto nla_put_failure;
1480 nft_rule_for_each_expr(expr, next, rule) {
1481 struct nlattr *elem = nla_nest_start(skb, NFTA_LIST_ELEM);
1482 if (elem == NULL)
1483 goto nla_put_failure;
1484 if (nf_tables_fill_expr_info(skb, expr) < 0)
1485 goto nla_put_failure;
1486 nla_nest_end(skb, elem);
1487 }
1488 nla_nest_end(skb, list);
1489
Pablo Neira Ayuso0768b3b2014-02-19 17:27:06 +01001490 if (rule->ulen &&
1491 nla_put(skb, NFTA_RULE_USERDATA, rule->ulen, nft_userdata(rule)))
1492 goto nla_put_failure;
1493
Patrick McHardy96518512013-10-14 11:00:02 +02001494 return nlmsg_end(skb, nlh);
1495
1496nla_put_failure:
1497 nlmsg_trim(skb, nlh);
1498 return -1;
1499}
1500
Pablo Neira Ayuso35151d82014-05-05 17:12:40 +02001501static int nf_tables_rule_notify(const struct nft_ctx *ctx,
Patrick McHardy96518512013-10-14 11:00:02 +02001502 const struct nft_rule *rule,
Pablo Neira Ayuso35151d82014-05-05 17:12:40 +02001503 int event)
Patrick McHardy96518512013-10-14 11:00:02 +02001504{
1505 struct sk_buff *skb;
Patrick McHardy96518512013-10-14 11:00:02 +02001506 int err;
1507
Pablo Neira Ayuso128ad332014-05-09 17:14:24 +02001508 if (!ctx->report &&
1509 !nfnetlink_has_listeners(ctx->net, NFNLGRP_NFTABLES))
Patrick McHardy96518512013-10-14 11:00:02 +02001510 return 0;
1511
1512 err = -ENOBUFS;
1513 skb = nlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL);
1514 if (skb == NULL)
1515 goto err;
1516
Pablo Neira Ayuso128ad332014-05-09 17:14:24 +02001517 err = nf_tables_fill_rule_info(skb, ctx->portid, ctx->seq, event, 0,
Pablo Neira Ayuso35151d82014-05-05 17:12:40 +02001518 ctx->afi->family, ctx->table,
1519 ctx->chain, rule);
Patrick McHardy96518512013-10-14 11:00:02 +02001520 if (err < 0) {
1521 kfree_skb(skb);
1522 goto err;
1523 }
1524
Pablo Neira Ayuso128ad332014-05-09 17:14:24 +02001525 err = nfnetlink_send(skb, ctx->net, ctx->portid, NFNLGRP_NFTABLES,
1526 ctx->report, GFP_KERNEL);
Patrick McHardy96518512013-10-14 11:00:02 +02001527err:
Pablo Neira Ayuso128ad332014-05-09 17:14:24 +02001528 if (err < 0) {
1529 nfnetlink_set_err(ctx->net, ctx->portid, NFNLGRP_NFTABLES,
1530 err);
1531 }
Patrick McHardy96518512013-10-14 11:00:02 +02001532 return err;
1533}
1534
Pablo Neira Ayuso0628b122013-10-14 11:05:33 +02001535static inline bool
1536nft_rule_is_active(struct net *net, const struct nft_rule *rule)
1537{
1538 return (rule->genmask & (1 << net->nft.gencursor)) == 0;
1539}
1540
1541static inline int gencursor_next(struct net *net)
1542{
1543 return net->nft.gencursor+1 == 1 ? 1 : 0;
1544}
1545
1546static inline int
1547nft_rule_is_active_next(struct net *net, const struct nft_rule *rule)
1548{
1549 return (rule->genmask & (1 << gencursor_next(net))) == 0;
1550}
1551
1552static inline void
1553nft_rule_activate_next(struct net *net, struct nft_rule *rule)
1554{
1555 /* Now inactive, will be active in the future */
1556 rule->genmask = (1 << net->nft.gencursor);
1557}
1558
1559static inline void
1560nft_rule_disactivate_next(struct net *net, struct nft_rule *rule)
1561{
1562 rule->genmask = (1 << gencursor_next(net));
1563}
1564
1565static inline void nft_rule_clear(struct net *net, struct nft_rule *rule)
1566{
1567 rule->genmask = 0;
1568}
1569
Patrick McHardy96518512013-10-14 11:00:02 +02001570static int nf_tables_dump_rules(struct sk_buff *skb,
1571 struct netlink_callback *cb)
1572{
1573 const struct nfgenmsg *nfmsg = nlmsg_data(cb->nlh);
1574 const struct nft_af_info *afi;
1575 const struct nft_table *table;
1576 const struct nft_chain *chain;
1577 const struct nft_rule *rule;
1578 unsigned int idx = 0, s_idx = cb->args[0];
Pablo Neira Ayuso99633ab2013-10-10 23:28:33 +02001579 struct net *net = sock_net(skb->sk);
Patrick McHardy96518512013-10-14 11:00:02 +02001580 int family = nfmsg->nfgen_family;
1581
Pablo Neira Ayusoe688a7f2014-07-01 11:49:18 +02001582 rcu_read_lock();
Pablo Neira Ayuso38e029f2014-07-01 12:23:12 +02001583 cb->seq = net->nft.base_seq;
1584
Pablo Neira Ayusoe688a7f2014-07-01 11:49:18 +02001585 list_for_each_entry_rcu(afi, &net->nft.af_info, list) {
Patrick McHardy96518512013-10-14 11:00:02 +02001586 if (family != NFPROTO_UNSPEC && family != afi->family)
1587 continue;
1588
Pablo Neira Ayusoe688a7f2014-07-01 11:49:18 +02001589 list_for_each_entry_rcu(table, &afi->tables, list) {
1590 list_for_each_entry_rcu(chain, &table->chains, list) {
1591 list_for_each_entry_rcu(rule, &chain->rules, list) {
Pablo Neira Ayuso0628b122013-10-14 11:05:33 +02001592 if (!nft_rule_is_active(net, rule))
1593 goto cont;
Patrick McHardy96518512013-10-14 11:00:02 +02001594 if (idx < s_idx)
1595 goto cont;
1596 if (idx > s_idx)
1597 memset(&cb->args[1], 0,
1598 sizeof(cb->args) - sizeof(cb->args[0]));
1599 if (nf_tables_fill_rule_info(skb, NETLINK_CB(cb->skb).portid,
1600 cb->nlh->nlmsg_seq,
1601 NFT_MSG_NEWRULE,
1602 NLM_F_MULTI | NLM_F_APPEND,
1603 afi->family, table, chain, rule) < 0)
1604 goto done;
Pablo Neira Ayuso38e029f2014-07-01 12:23:12 +02001605
1606 nl_dump_check_consistent(cb, nlmsg_hdr(skb));
Patrick McHardy96518512013-10-14 11:00:02 +02001607cont:
1608 idx++;
1609 }
1610 }
1611 }
1612 }
1613done:
Pablo Neira Ayusoe688a7f2014-07-01 11:49:18 +02001614 rcu_read_unlock();
1615
Patrick McHardy96518512013-10-14 11:00:02 +02001616 cb->args[0] = idx;
1617 return skb->len;
1618}
1619
1620static int nf_tables_getrule(struct sock *nlsk, struct sk_buff *skb,
1621 const struct nlmsghdr *nlh,
1622 const struct nlattr * const nla[])
1623{
1624 const struct nfgenmsg *nfmsg = nlmsg_data(nlh);
1625 const struct nft_af_info *afi;
1626 const struct nft_table *table;
1627 const struct nft_chain *chain;
1628 const struct nft_rule *rule;
1629 struct sk_buff *skb2;
Pablo Neira Ayuso99633ab2013-10-10 23:28:33 +02001630 struct net *net = sock_net(skb->sk);
Patrick McHardy96518512013-10-14 11:00:02 +02001631 int family = nfmsg->nfgen_family;
1632 int err;
1633
1634 if (nlh->nlmsg_flags & NLM_F_DUMP) {
1635 struct netlink_dump_control c = {
1636 .dump = nf_tables_dump_rules,
1637 };
1638 return netlink_dump_start(nlsk, skb, nlh, &c);
1639 }
1640
Pablo Neira Ayuso99633ab2013-10-10 23:28:33 +02001641 afi = nf_tables_afinfo_lookup(net, family, false);
Patrick McHardy96518512013-10-14 11:00:02 +02001642 if (IS_ERR(afi))
1643 return PTR_ERR(afi);
1644
Pablo Neira Ayuso93707612013-10-10 23:21:26 +02001645 table = nf_tables_table_lookup(afi, nla[NFTA_RULE_TABLE]);
Patrick McHardy96518512013-10-14 11:00:02 +02001646 if (IS_ERR(table))
1647 return PTR_ERR(table);
Pablo Neira Ayuso55dd6f92014-04-03 11:53:37 +02001648 if (table->flags & NFT_TABLE_INACTIVE)
1649 return -ENOENT;
Patrick McHardy96518512013-10-14 11:00:02 +02001650
1651 chain = nf_tables_chain_lookup(table, nla[NFTA_RULE_CHAIN]);
1652 if (IS_ERR(chain))
1653 return PTR_ERR(chain);
Pablo Neira Ayuso91c7b382014-04-09 11:58:08 +02001654 if (chain->flags & NFT_CHAIN_INACTIVE)
1655 return -ENOENT;
Patrick McHardy96518512013-10-14 11:00:02 +02001656
1657 rule = nf_tables_rule_lookup(chain, nla[NFTA_RULE_HANDLE]);
1658 if (IS_ERR(rule))
1659 return PTR_ERR(rule);
1660
1661 skb2 = alloc_skb(NLMSG_GOODSIZE, GFP_KERNEL);
1662 if (!skb2)
1663 return -ENOMEM;
1664
1665 err = nf_tables_fill_rule_info(skb2, NETLINK_CB(skb).portid,
1666 nlh->nlmsg_seq, NFT_MSG_NEWRULE, 0,
1667 family, table, chain, rule);
1668 if (err < 0)
1669 goto err;
1670
1671 return nlmsg_unicast(nlsk, skb2, NETLINK_CB(skb).portid);
1672
1673err:
1674 kfree_skb(skb2);
1675 return err;
1676}
1677
Patrick McHardy62472bc2014-03-07 19:08:30 +01001678static void nf_tables_rule_destroy(const struct nft_ctx *ctx,
1679 struct nft_rule *rule)
Patrick McHardy96518512013-10-14 11:00:02 +02001680{
Patrick McHardy96518512013-10-14 11:00:02 +02001681 struct nft_expr *expr;
1682
1683 /*
1684 * Careful: some expressions might not be initialized in case this
1685 * is called on error from nf_tables_newrule().
1686 */
1687 expr = nft_expr_first(rule);
1688 while (expr->ops && expr != nft_expr_last(rule)) {
Patrick McHardy62472bc2014-03-07 19:08:30 +01001689 nf_tables_expr_destroy(ctx, expr);
Patrick McHardy96518512013-10-14 11:00:02 +02001690 expr = nft_expr_next(expr);
1691 }
1692 kfree(rule);
1693}
1694
Pablo Neira Ayusob380e5c2014-04-04 01:38:51 +02001695static struct nft_trans *nft_trans_rule_add(struct nft_ctx *ctx, int msg_type,
Pablo Neira Ayuso1081d112014-04-04 01:24:07 +02001696 struct nft_rule *rule)
1697{
1698 struct nft_trans *trans;
1699
Pablo Neira Ayusob380e5c2014-04-04 01:38:51 +02001700 trans = nft_trans_alloc(ctx, msg_type, sizeof(struct nft_trans_rule));
Pablo Neira Ayuso1081d112014-04-04 01:24:07 +02001701 if (trans == NULL)
1702 return NULL;
1703
1704 nft_trans_rule(trans) = rule;
1705 list_add_tail(&trans->list, &ctx->net->nft.commit_list);
1706
1707 return trans;
1708}
1709
Patrick McHardy96518512013-10-14 11:00:02 +02001710#define NFT_RULE_MAXEXPRS 128
1711
1712static struct nft_expr_info *info;
1713
1714static int nf_tables_newrule(struct sock *nlsk, struct sk_buff *skb,
1715 const struct nlmsghdr *nlh,
1716 const struct nlattr * const nla[])
1717{
1718 const struct nfgenmsg *nfmsg = nlmsg_data(nlh);
Pablo Neira Ayuso7c95f6d2014-04-04 01:22:45 +02001719 struct nft_af_info *afi;
Pablo Neira Ayuso99633ab2013-10-10 23:28:33 +02001720 struct net *net = sock_net(skb->sk);
Patrick McHardy96518512013-10-14 11:00:02 +02001721 struct nft_table *table;
1722 struct nft_chain *chain;
1723 struct nft_rule *rule, *old_rule = NULL;
Pablo Neira Ayuso1081d112014-04-04 01:24:07 +02001724 struct nft_trans *trans = NULL;
Patrick McHardy96518512013-10-14 11:00:02 +02001725 struct nft_expr *expr;
1726 struct nft_ctx ctx;
1727 struct nlattr *tmp;
Pablo Neira Ayuso0768b3b2014-02-19 17:27:06 +01001728 unsigned int size, i, n, ulen = 0;
Patrick McHardy96518512013-10-14 11:00:02 +02001729 int err, rem;
1730 bool create;
Eric Leblond5e948462013-10-10 13:41:44 +02001731 u64 handle, pos_handle;
Patrick McHardy96518512013-10-14 11:00:02 +02001732
1733 create = nlh->nlmsg_flags & NLM_F_CREATE ? true : false;
1734
Pablo Neira Ayuso99633ab2013-10-10 23:28:33 +02001735 afi = nf_tables_afinfo_lookup(net, nfmsg->nfgen_family, create);
Patrick McHardy96518512013-10-14 11:00:02 +02001736 if (IS_ERR(afi))
1737 return PTR_ERR(afi);
1738
Pablo Neira Ayuso93707612013-10-10 23:21:26 +02001739 table = nf_tables_table_lookup(afi, nla[NFTA_RULE_TABLE]);
Patrick McHardy96518512013-10-14 11:00:02 +02001740 if (IS_ERR(table))
1741 return PTR_ERR(table);
1742
1743 chain = nf_tables_chain_lookup(table, nla[NFTA_RULE_CHAIN]);
1744 if (IS_ERR(chain))
1745 return PTR_ERR(chain);
1746
1747 if (nla[NFTA_RULE_HANDLE]) {
1748 handle = be64_to_cpu(nla_get_be64(nla[NFTA_RULE_HANDLE]));
1749 rule = __nf_tables_rule_lookup(chain, handle);
1750 if (IS_ERR(rule))
1751 return PTR_ERR(rule);
1752
1753 if (nlh->nlmsg_flags & NLM_F_EXCL)
1754 return -EEXIST;
1755 if (nlh->nlmsg_flags & NLM_F_REPLACE)
1756 old_rule = rule;
1757 else
1758 return -EOPNOTSUPP;
1759 } else {
1760 if (!create || nlh->nlmsg_flags & NLM_F_REPLACE)
1761 return -EINVAL;
1762 handle = nf_tables_alloc_handle(table);
Pablo Neira Ayusoa0a73792014-06-10 10:53:01 +02001763
1764 if (chain->use == UINT_MAX)
1765 return -EOVERFLOW;
Patrick McHardy96518512013-10-14 11:00:02 +02001766 }
1767
Eric Leblond5e948462013-10-10 13:41:44 +02001768 if (nla[NFTA_RULE_POSITION]) {
1769 if (!(nlh->nlmsg_flags & NLM_F_CREATE))
1770 return -EOPNOTSUPP;
1771
1772 pos_handle = be64_to_cpu(nla_get_be64(nla[NFTA_RULE_POSITION]));
1773 old_rule = __nf_tables_rule_lookup(chain, pos_handle);
1774 if (IS_ERR(old_rule))
1775 return PTR_ERR(old_rule);
1776 }
1777
Pablo Neira Ayuso0ca743a2013-10-14 00:06:06 +02001778 nft_ctx_init(&ctx, skb, nlh, afi, table, chain, nla);
1779
Patrick McHardy96518512013-10-14 11:00:02 +02001780 n = 0;
1781 size = 0;
1782 if (nla[NFTA_RULE_EXPRESSIONS]) {
1783 nla_for_each_nested(tmp, nla[NFTA_RULE_EXPRESSIONS], rem) {
1784 err = -EINVAL;
1785 if (nla_type(tmp) != NFTA_LIST_ELEM)
1786 goto err1;
1787 if (n == NFT_RULE_MAXEXPRS)
1788 goto err1;
Pablo Neira Ayuso0ca743a2013-10-14 00:06:06 +02001789 err = nf_tables_expr_parse(&ctx, tmp, &info[n]);
Patrick McHardy96518512013-10-14 11:00:02 +02001790 if (err < 0)
1791 goto err1;
1792 size += info[n].ops->size;
1793 n++;
1794 }
1795 }
1796
Pablo Neira Ayuso0768b3b2014-02-19 17:27:06 +01001797 if (nla[NFTA_RULE_USERDATA])
1798 ulen = nla_len(nla[NFTA_RULE_USERDATA]);
1799
Patrick McHardy96518512013-10-14 11:00:02 +02001800 err = -ENOMEM;
Pablo Neira Ayuso0768b3b2014-02-19 17:27:06 +01001801 rule = kzalloc(sizeof(*rule) + size + ulen, GFP_KERNEL);
Patrick McHardy96518512013-10-14 11:00:02 +02001802 if (rule == NULL)
1803 goto err1;
1804
Pablo Neira Ayuso0628b122013-10-14 11:05:33 +02001805 nft_rule_activate_next(net, rule);
1806
Patrick McHardy96518512013-10-14 11:00:02 +02001807 rule->handle = handle;
1808 rule->dlen = size;
Pablo Neira Ayuso0768b3b2014-02-19 17:27:06 +01001809 rule->ulen = ulen;
1810
1811 if (ulen)
1812 nla_memcpy(nft_userdata(rule), nla[NFTA_RULE_USERDATA], ulen);
Patrick McHardy96518512013-10-14 11:00:02 +02001813
Patrick McHardy96518512013-10-14 11:00:02 +02001814 expr = nft_expr_first(rule);
1815 for (i = 0; i < n; i++) {
1816 err = nf_tables_newexpr(&ctx, &info[i], expr);
1817 if (err < 0)
1818 goto err2;
Patrick McHardyef1f7df2013-10-10 11:41:20 +02001819 info[i].ops = NULL;
Patrick McHardy96518512013-10-14 11:00:02 +02001820 expr = nft_expr_next(expr);
1821 }
1822
Patrick McHardy96518512013-10-14 11:00:02 +02001823 if (nlh->nlmsg_flags & NLM_F_REPLACE) {
Pablo Neira Ayuso0628b122013-10-14 11:05:33 +02001824 if (nft_rule_is_active_next(net, old_rule)) {
Pablo Neira Ayusoac904ac2014-06-10 10:53:03 +02001825 trans = nft_trans_rule_add(&ctx, NFT_MSG_DELRULE,
Pablo Neira Ayusob380e5c2014-04-04 01:38:51 +02001826 old_rule);
Pablo Neira Ayuso1081d112014-04-04 01:24:07 +02001827 if (trans == NULL) {
Pablo Neira Ayuso0628b122013-10-14 11:05:33 +02001828 err = -ENOMEM;
1829 goto err2;
1830 }
1831 nft_rule_disactivate_next(net, old_rule);
Pablo Neira Ayusoac34b862014-06-10 10:53:02 +02001832 chain->use--;
Pablo Neira Ayuso5bc5c302014-06-10 10:53:00 +02001833 list_add_tail_rcu(&rule->list, &old_rule->list);
Pablo Neira Ayuso0628b122013-10-14 11:05:33 +02001834 } else {
1835 err = -ENOENT;
1836 goto err2;
1837 }
Patrick McHardy96518512013-10-14 11:00:02 +02001838 } else if (nlh->nlmsg_flags & NLM_F_APPEND)
Eric Leblond5e948462013-10-10 13:41:44 +02001839 if (old_rule)
1840 list_add_rcu(&rule->list, &old_rule->list);
1841 else
1842 list_add_tail_rcu(&rule->list, &chain->rules);
1843 else {
1844 if (old_rule)
1845 list_add_tail_rcu(&rule->list, &old_rule->list);
1846 else
1847 list_add_rcu(&rule->list, &chain->rules);
1848 }
Patrick McHardy96518512013-10-14 11:00:02 +02001849
Pablo Neira Ayusob380e5c2014-04-04 01:38:51 +02001850 if (nft_trans_rule_add(&ctx, NFT_MSG_NEWRULE, rule) == NULL) {
Pablo Neira Ayuso0628b122013-10-14 11:05:33 +02001851 err = -ENOMEM;
1852 goto err3;
1853 }
Pablo Neira Ayuso4fefee52014-05-23 12:39:26 +02001854 chain->use++;
Patrick McHardy96518512013-10-14 11:00:02 +02001855 return 0;
1856
Pablo Neira Ayuso0628b122013-10-14 11:05:33 +02001857err3:
1858 list_del_rcu(&rule->list);
Pablo Neira Ayuso1081d112014-04-04 01:24:07 +02001859 if (trans) {
1860 list_del_rcu(&nft_trans_rule(trans)->list);
1861 nft_rule_clear(net, nft_trans_rule(trans));
1862 nft_trans_destroy(trans);
Pablo Neira Ayusoac34b862014-06-10 10:53:02 +02001863 chain->use++;
Pablo Neira Ayuso0628b122013-10-14 11:05:33 +02001864 }
Patrick McHardy96518512013-10-14 11:00:02 +02001865err2:
Patrick McHardy62472bc2014-03-07 19:08:30 +01001866 nf_tables_rule_destroy(&ctx, rule);
Patrick McHardy96518512013-10-14 11:00:02 +02001867err1:
1868 for (i = 0; i < n; i++) {
1869 if (info[i].ops != NULL)
Patrick McHardyef1f7df2013-10-10 11:41:20 +02001870 module_put(info[i].ops->type->owner);
Patrick McHardy96518512013-10-14 11:00:02 +02001871 }
1872 return err;
1873}
1874
Pablo Neira Ayuso0628b122013-10-14 11:05:33 +02001875static int
Arturo Borrero5e266fe2014-09-02 16:42:21 +02001876nf_tables_delrule_deactivate(struct nft_ctx *ctx, struct nft_rule *rule)
Pablo Neira Ayuso0628b122013-10-14 11:05:33 +02001877{
1878 /* You cannot delete the same rule twice */
1879 if (nft_rule_is_active_next(ctx->net, rule)) {
Pablo Neira Ayuso0628b122013-10-14 11:05:33 +02001880 nft_rule_disactivate_next(ctx->net, rule);
Pablo Neira Ayuso4fefee52014-05-23 12:39:26 +02001881 ctx->chain->use--;
Pablo Neira Ayuso0628b122013-10-14 11:05:33 +02001882 return 0;
1883 }
1884 return -ENOENT;
1885}
1886
Arturo Borrero5e266fe2014-09-02 16:42:21 +02001887static int nft_delrule(struct nft_ctx *ctx, struct nft_rule *rule)
1888{
1889 struct nft_trans *trans;
1890 int err;
1891
1892 trans = nft_trans_rule_add(ctx, NFT_MSG_DELRULE, rule);
1893 if (trans == NULL)
1894 return -ENOMEM;
1895
1896 err = nf_tables_delrule_deactivate(ctx, rule);
1897 if (err < 0) {
1898 nft_trans_destroy(trans);
1899 return err;
1900 }
1901
1902 return 0;
1903}
1904
Pablo Neira Ayusocf9dc092013-11-24 20:39:10 +01001905static int nf_table_delrule_by_chain(struct nft_ctx *ctx)
1906{
1907 struct nft_rule *rule;
1908 int err;
1909
1910 list_for_each_entry(rule, &ctx->chain->rules, list) {
Arturo Borrero5e266fe2014-09-02 16:42:21 +02001911 err = nft_delrule(ctx, rule);
Pablo Neira Ayusocf9dc092013-11-24 20:39:10 +01001912 if (err < 0)
1913 return err;
1914 }
1915 return 0;
1916}
1917
Patrick McHardy96518512013-10-14 11:00:02 +02001918static int nf_tables_delrule(struct sock *nlsk, struct sk_buff *skb,
1919 const struct nlmsghdr *nlh,
1920 const struct nlattr * const nla[])
1921{
1922 const struct nfgenmsg *nfmsg = nlmsg_data(nlh);
Pablo Neira Ayuso7c95f6d2014-04-04 01:22:45 +02001923 struct nft_af_info *afi;
Pablo Neira Ayuso99633ab2013-10-10 23:28:33 +02001924 struct net *net = sock_net(skb->sk);
Pablo Neira Ayuso7c95f6d2014-04-04 01:22:45 +02001925 struct nft_table *table;
Pablo Neira Ayusocf9dc092013-11-24 20:39:10 +01001926 struct nft_chain *chain = NULL;
1927 struct nft_rule *rule;
Pablo Neira Ayuso0628b122013-10-14 11:05:33 +02001928 int family = nfmsg->nfgen_family, err = 0;
1929 struct nft_ctx ctx;
Patrick McHardy96518512013-10-14 11:00:02 +02001930
Pablo Neira Ayuso99633ab2013-10-10 23:28:33 +02001931 afi = nf_tables_afinfo_lookup(net, family, false);
Patrick McHardy96518512013-10-14 11:00:02 +02001932 if (IS_ERR(afi))
1933 return PTR_ERR(afi);
1934
Pablo Neira Ayuso93707612013-10-10 23:21:26 +02001935 table = nf_tables_table_lookup(afi, nla[NFTA_RULE_TABLE]);
Patrick McHardy96518512013-10-14 11:00:02 +02001936 if (IS_ERR(table))
1937 return PTR_ERR(table);
Pablo Neira Ayuso55dd6f92014-04-03 11:53:37 +02001938 if (table->flags & NFT_TABLE_INACTIVE)
1939 return -ENOENT;
Patrick McHardy96518512013-10-14 11:00:02 +02001940
Pablo Neira Ayusocf9dc092013-11-24 20:39:10 +01001941 if (nla[NFTA_RULE_CHAIN]) {
1942 chain = nf_tables_chain_lookup(table, nla[NFTA_RULE_CHAIN]);
1943 if (IS_ERR(chain))
1944 return PTR_ERR(chain);
1945 }
Patrick McHardy96518512013-10-14 11:00:02 +02001946
Pablo Neira Ayuso0628b122013-10-14 11:05:33 +02001947 nft_ctx_init(&ctx, skb, nlh, afi, table, chain, nla);
1948
Pablo Neira Ayusocf9dc092013-11-24 20:39:10 +01001949 if (chain) {
1950 if (nla[NFTA_RULE_HANDLE]) {
1951 rule = nf_tables_rule_lookup(chain,
1952 nla[NFTA_RULE_HANDLE]);
1953 if (IS_ERR(rule))
1954 return PTR_ERR(rule);
Patrick McHardy96518512013-10-14 11:00:02 +02001955
Arturo Borrero5e266fe2014-09-02 16:42:21 +02001956 err = nft_delrule(&ctx, rule);
Pablo Neira Ayusocf9dc092013-11-24 20:39:10 +01001957 } else {
1958 err = nf_table_delrule_by_chain(&ctx);
1959 }
1960 } else {
1961 list_for_each_entry(chain, &table->chains, list) {
1962 ctx.chain = chain;
1963 err = nf_table_delrule_by_chain(&ctx);
Pablo Neira Ayuso0628b122013-10-14 11:05:33 +02001964 if (err < 0)
1965 break;
Patrick McHardy96518512013-10-14 11:00:02 +02001966 }
1967 }
1968
Pablo Neira Ayuso0628b122013-10-14 11:05:33 +02001969 return err;
1970}
1971
Patrick McHardy20a69342013-10-11 12:06:22 +02001972/*
1973 * Sets
1974 */
1975
1976static LIST_HEAD(nf_tables_set_ops);
1977
1978int nft_register_set(struct nft_set_ops *ops)
1979{
1980 nfnl_lock(NFNL_SUBSYS_NFTABLES);
Pablo Neira Ayusoe688a7f2014-07-01 11:49:18 +02001981 list_add_tail_rcu(&ops->list, &nf_tables_set_ops);
Patrick McHardy20a69342013-10-11 12:06:22 +02001982 nfnl_unlock(NFNL_SUBSYS_NFTABLES);
1983 return 0;
1984}
1985EXPORT_SYMBOL_GPL(nft_register_set);
1986
1987void nft_unregister_set(struct nft_set_ops *ops)
1988{
1989 nfnl_lock(NFNL_SUBSYS_NFTABLES);
Pablo Neira Ayusoe688a7f2014-07-01 11:49:18 +02001990 list_del_rcu(&ops->list);
Patrick McHardy20a69342013-10-11 12:06:22 +02001991 nfnl_unlock(NFNL_SUBSYS_NFTABLES);
1992}
1993EXPORT_SYMBOL_GPL(nft_unregister_set);
1994
Patrick McHardyc50b9602014-03-28 10:19:47 +00001995/*
1996 * Select a set implementation based on the data characteristics and the
1997 * given policy. The total memory use might not be known if no size is
1998 * given, in that case the amount of memory per element is used.
1999 */
2000static const struct nft_set_ops *
2001nft_select_set_ops(const struct nlattr * const nla[],
2002 const struct nft_set_desc *desc,
2003 enum nft_set_policies policy)
Patrick McHardy20a69342013-10-11 12:06:22 +02002004{
Patrick McHardyc50b9602014-03-28 10:19:47 +00002005 const struct nft_set_ops *ops, *bops;
2006 struct nft_set_estimate est, best;
Patrick McHardy20a69342013-10-11 12:06:22 +02002007 u32 features;
2008
2009#ifdef CONFIG_MODULES
2010 if (list_empty(&nf_tables_set_ops)) {
2011 nfnl_unlock(NFNL_SUBSYS_NFTABLES);
2012 request_module("nft-set");
2013 nfnl_lock(NFNL_SUBSYS_NFTABLES);
2014 if (!list_empty(&nf_tables_set_ops))
2015 return ERR_PTR(-EAGAIN);
2016 }
2017#endif
2018 features = 0;
2019 if (nla[NFTA_SET_FLAGS] != NULL) {
2020 features = ntohl(nla_get_be32(nla[NFTA_SET_FLAGS]));
2021 features &= NFT_SET_INTERVAL | NFT_SET_MAP;
2022 }
2023
Patrick McHardyc50b9602014-03-28 10:19:47 +00002024 bops = NULL;
2025 best.size = ~0;
2026 best.class = ~0;
2027
Patrick McHardy20a69342013-10-11 12:06:22 +02002028 list_for_each_entry(ops, &nf_tables_set_ops, list) {
2029 if ((ops->features & features) != features)
2030 continue;
Patrick McHardyc50b9602014-03-28 10:19:47 +00002031 if (!ops->estimate(desc, features, &est))
2032 continue;
2033
2034 switch (policy) {
2035 case NFT_SET_POL_PERFORMANCE:
2036 if (est.class < best.class)
2037 break;
2038 if (est.class == best.class && est.size < best.size)
2039 break;
2040 continue;
2041 case NFT_SET_POL_MEMORY:
2042 if (est.size < best.size)
2043 break;
2044 if (est.size == best.size && est.class < best.class)
2045 break;
2046 continue;
2047 default:
2048 break;
2049 }
2050
Patrick McHardy20a69342013-10-11 12:06:22 +02002051 if (!try_module_get(ops->owner))
2052 continue;
Patrick McHardyc50b9602014-03-28 10:19:47 +00002053 if (bops != NULL)
2054 module_put(bops->owner);
2055
2056 bops = ops;
2057 best = est;
Patrick McHardy20a69342013-10-11 12:06:22 +02002058 }
2059
Patrick McHardyc50b9602014-03-28 10:19:47 +00002060 if (bops != NULL)
2061 return bops;
2062
Patrick McHardy20a69342013-10-11 12:06:22 +02002063 return ERR_PTR(-EOPNOTSUPP);
2064}
2065
2066static const struct nla_policy nft_set_policy[NFTA_SET_MAX + 1] = {
2067 [NFTA_SET_TABLE] = { .type = NLA_STRING },
Pablo Neira Ayusoa9bdd832014-03-24 15:10:37 +01002068 [NFTA_SET_NAME] = { .type = NLA_STRING,
2069 .len = IFNAMSIZ - 1 },
Patrick McHardy20a69342013-10-11 12:06:22 +02002070 [NFTA_SET_FLAGS] = { .type = NLA_U32 },
2071 [NFTA_SET_KEY_TYPE] = { .type = NLA_U32 },
2072 [NFTA_SET_KEY_LEN] = { .type = NLA_U32 },
2073 [NFTA_SET_DATA_TYPE] = { .type = NLA_U32 },
2074 [NFTA_SET_DATA_LEN] = { .type = NLA_U32 },
Patrick McHardyc50b9602014-03-28 10:19:47 +00002075 [NFTA_SET_POLICY] = { .type = NLA_U32 },
2076 [NFTA_SET_DESC] = { .type = NLA_NESTED },
Pablo Neira Ayuso958bee12014-04-03 11:48:44 +02002077 [NFTA_SET_ID] = { .type = NLA_U32 },
Patrick McHardyc50b9602014-03-28 10:19:47 +00002078};
2079
2080static const struct nla_policy nft_set_desc_policy[NFTA_SET_DESC_MAX + 1] = {
2081 [NFTA_SET_DESC_SIZE] = { .type = NLA_U32 },
Patrick McHardy20a69342013-10-11 12:06:22 +02002082};
2083
2084static int nft_ctx_init_from_setattr(struct nft_ctx *ctx,
2085 const struct sk_buff *skb,
2086 const struct nlmsghdr *nlh,
2087 const struct nlattr * const nla[])
2088{
Pablo Neira Ayuso99633ab2013-10-10 23:28:33 +02002089 struct net *net = sock_net(skb->sk);
Patrick McHardy20a69342013-10-11 12:06:22 +02002090 const struct nfgenmsg *nfmsg = nlmsg_data(nlh);
Pablo Neira Ayuso7c95f6d2014-04-04 01:22:45 +02002091 struct nft_af_info *afi = NULL;
2092 struct nft_table *table = NULL;
Patrick McHardy20a69342013-10-11 12:06:22 +02002093
Pablo Neira Ayusoc9c8e482013-12-26 16:49:03 +01002094 if (nfmsg->nfgen_family != NFPROTO_UNSPEC) {
2095 afi = nf_tables_afinfo_lookup(net, nfmsg->nfgen_family, false);
2096 if (IS_ERR(afi))
2097 return PTR_ERR(afi);
2098 }
Patrick McHardy20a69342013-10-11 12:06:22 +02002099
2100 if (nla[NFTA_SET_TABLE] != NULL) {
Patrick McHardyec2c9932014-02-05 15:03:35 +00002101 if (afi == NULL)
2102 return -EAFNOSUPPORT;
2103
Pablo Neira Ayuso93707612013-10-10 23:21:26 +02002104 table = nf_tables_table_lookup(afi, nla[NFTA_SET_TABLE]);
Patrick McHardy20a69342013-10-11 12:06:22 +02002105 if (IS_ERR(table))
2106 return PTR_ERR(table);
Pablo Neira Ayuso55dd6f92014-04-03 11:53:37 +02002107 if (table->flags & NFT_TABLE_INACTIVE)
2108 return -ENOENT;
Patrick McHardy20a69342013-10-11 12:06:22 +02002109 }
2110
Pablo Neira Ayuso0ca743a2013-10-14 00:06:06 +02002111 nft_ctx_init(ctx, skb, nlh, afi, table, NULL, nla);
Patrick McHardy20a69342013-10-11 12:06:22 +02002112 return 0;
2113}
2114
2115struct nft_set *nf_tables_set_lookup(const struct nft_table *table,
2116 const struct nlattr *nla)
2117{
2118 struct nft_set *set;
2119
2120 if (nla == NULL)
2121 return ERR_PTR(-EINVAL);
2122
2123 list_for_each_entry(set, &table->sets, list) {
2124 if (!nla_strcmp(nla, set->name))
2125 return set;
2126 }
2127 return ERR_PTR(-ENOENT);
2128}
2129
Pablo Neira Ayuso958bee12014-04-03 11:48:44 +02002130struct nft_set *nf_tables_set_lookup_byid(const struct net *net,
2131 const struct nlattr *nla)
2132{
2133 struct nft_trans *trans;
2134 u32 id = ntohl(nla_get_be32(nla));
2135
2136 list_for_each_entry(trans, &net->nft.commit_list, list) {
2137 if (trans->msg_type == NFT_MSG_NEWSET &&
2138 id == nft_trans_set_id(trans))
2139 return nft_trans_set(trans);
2140 }
2141 return ERR_PTR(-ENOENT);
2142}
2143
Patrick McHardy20a69342013-10-11 12:06:22 +02002144static int nf_tables_set_alloc_name(struct nft_ctx *ctx, struct nft_set *set,
2145 const char *name)
2146{
2147 const struct nft_set *i;
2148 const char *p;
2149 unsigned long *inuse;
Patrick McHardy60eb1892014-03-07 12:34:05 +01002150 unsigned int n = 0, min = 0;
Patrick McHardy20a69342013-10-11 12:06:22 +02002151
2152 p = strnchr(name, IFNAMSIZ, '%');
2153 if (p != NULL) {
2154 if (p[1] != 'd' || strchr(p + 2, '%'))
2155 return -EINVAL;
2156
2157 inuse = (unsigned long *)get_zeroed_page(GFP_KERNEL);
2158 if (inuse == NULL)
2159 return -ENOMEM;
Patrick McHardy60eb1892014-03-07 12:34:05 +01002160cont:
Patrick McHardy20a69342013-10-11 12:06:22 +02002161 list_for_each_entry(i, &ctx->table->sets, list) {
Daniel Borkmann14662912013-12-31 12:40:05 +01002162 int tmp;
2163
2164 if (!sscanf(i->name, name, &tmp))
Patrick McHardy20a69342013-10-11 12:06:22 +02002165 continue;
Patrick McHardy60eb1892014-03-07 12:34:05 +01002166 if (tmp < min || tmp >= min + BITS_PER_BYTE * PAGE_SIZE)
Patrick McHardy20a69342013-10-11 12:06:22 +02002167 continue;
Daniel Borkmann14662912013-12-31 12:40:05 +01002168
Patrick McHardy60eb1892014-03-07 12:34:05 +01002169 set_bit(tmp - min, inuse);
Patrick McHardy20a69342013-10-11 12:06:22 +02002170 }
2171
Patrick McHardy53b70282014-02-05 12:26:22 +01002172 n = find_first_zero_bit(inuse, BITS_PER_BYTE * PAGE_SIZE);
Patrick McHardy60eb1892014-03-07 12:34:05 +01002173 if (n >= BITS_PER_BYTE * PAGE_SIZE) {
2174 min += BITS_PER_BYTE * PAGE_SIZE;
2175 memset(inuse, 0, PAGE_SIZE);
2176 goto cont;
2177 }
Patrick McHardy20a69342013-10-11 12:06:22 +02002178 free_page((unsigned long)inuse);
2179 }
2180
Patrick McHardy60eb1892014-03-07 12:34:05 +01002181 snprintf(set->name, sizeof(set->name), name, min + n);
Patrick McHardy20a69342013-10-11 12:06:22 +02002182 list_for_each_entry(i, &ctx->table->sets, list) {
2183 if (!strcmp(set->name, i->name))
2184 return -ENFILE;
2185 }
2186 return 0;
2187}
2188
2189static int nf_tables_fill_set(struct sk_buff *skb, const struct nft_ctx *ctx,
2190 const struct nft_set *set, u16 event, u16 flags)
2191{
2192 struct nfgenmsg *nfmsg;
2193 struct nlmsghdr *nlh;
Patrick McHardyc50b9602014-03-28 10:19:47 +00002194 struct nlattr *desc;
Pablo Neira Ayuso128ad332014-05-09 17:14:24 +02002195 u32 portid = ctx->portid;
2196 u32 seq = ctx->seq;
Patrick McHardy20a69342013-10-11 12:06:22 +02002197
2198 event |= NFNL_SUBSYS_NFTABLES << 8;
2199 nlh = nlmsg_put(skb, portid, seq, event, sizeof(struct nfgenmsg),
2200 flags);
2201 if (nlh == NULL)
2202 goto nla_put_failure;
2203
2204 nfmsg = nlmsg_data(nlh);
2205 nfmsg->nfgen_family = ctx->afi->family;
2206 nfmsg->version = NFNETLINK_V0;
2207 nfmsg->res_id = 0;
2208
2209 if (nla_put_string(skb, NFTA_SET_TABLE, ctx->table->name))
2210 goto nla_put_failure;
2211 if (nla_put_string(skb, NFTA_SET_NAME, set->name))
2212 goto nla_put_failure;
2213 if (set->flags != 0)
2214 if (nla_put_be32(skb, NFTA_SET_FLAGS, htonl(set->flags)))
2215 goto nla_put_failure;
2216
2217 if (nla_put_be32(skb, NFTA_SET_KEY_TYPE, htonl(set->ktype)))
2218 goto nla_put_failure;
2219 if (nla_put_be32(skb, NFTA_SET_KEY_LEN, htonl(set->klen)))
2220 goto nla_put_failure;
2221 if (set->flags & NFT_SET_MAP) {
2222 if (nla_put_be32(skb, NFTA_SET_DATA_TYPE, htonl(set->dtype)))
2223 goto nla_put_failure;
2224 if (nla_put_be32(skb, NFTA_SET_DATA_LEN, htonl(set->dlen)))
2225 goto nla_put_failure;
2226 }
2227
Patrick McHardyc50b9602014-03-28 10:19:47 +00002228 desc = nla_nest_start(skb, NFTA_SET_DESC);
2229 if (desc == NULL)
2230 goto nla_put_failure;
2231 if (set->size &&
2232 nla_put_be32(skb, NFTA_SET_DESC_SIZE, htonl(set->size)))
2233 goto nla_put_failure;
2234 nla_nest_end(skb, desc);
2235
Patrick McHardy20a69342013-10-11 12:06:22 +02002236 return nlmsg_end(skb, nlh);
2237
2238nla_put_failure:
2239 nlmsg_trim(skb, nlh);
2240 return -1;
2241}
2242
2243static int nf_tables_set_notify(const struct nft_ctx *ctx,
2244 const struct nft_set *set,
Pablo Neira Ayuso31f84412014-05-29 10:29:58 +02002245 int event, gfp_t gfp_flags)
Patrick McHardy20a69342013-10-11 12:06:22 +02002246{
2247 struct sk_buff *skb;
Pablo Neira Ayuso128ad332014-05-09 17:14:24 +02002248 u32 portid = ctx->portid;
Patrick McHardy20a69342013-10-11 12:06:22 +02002249 int err;
2250
Pablo Neira Ayuso128ad332014-05-09 17:14:24 +02002251 if (!ctx->report &&
2252 !nfnetlink_has_listeners(ctx->net, NFNLGRP_NFTABLES))
Patrick McHardy20a69342013-10-11 12:06:22 +02002253 return 0;
2254
2255 err = -ENOBUFS;
Pablo Neira Ayuso31f84412014-05-29 10:29:58 +02002256 skb = nlmsg_new(NLMSG_GOODSIZE, gfp_flags);
Patrick McHardy20a69342013-10-11 12:06:22 +02002257 if (skb == NULL)
2258 goto err;
2259
2260 err = nf_tables_fill_set(skb, ctx, set, event, 0);
2261 if (err < 0) {
2262 kfree_skb(skb);
2263 goto err;
2264 }
2265
Pablo Neira Ayuso128ad332014-05-09 17:14:24 +02002266 err = nfnetlink_send(skb, ctx->net, portid, NFNLGRP_NFTABLES,
Pablo Neira Ayuso31f84412014-05-29 10:29:58 +02002267 ctx->report, gfp_flags);
Patrick McHardy20a69342013-10-11 12:06:22 +02002268err:
2269 if (err < 0)
Pablo Neira Ayuso99633ab2013-10-10 23:28:33 +02002270 nfnetlink_set_err(ctx->net, portid, NFNLGRP_NFTABLES, err);
Patrick McHardy20a69342013-10-11 12:06:22 +02002271 return err;
2272}
2273
Pablo Neira Ayuso5b96af72014-07-16 17:35:18 +02002274static int nf_tables_dump_sets(struct sk_buff *skb, struct netlink_callback *cb)
Pablo Neira Ayusoc9c8e482013-12-26 16:49:03 +01002275{
2276 const struct nft_set *set;
2277 unsigned int idx, s_idx = cb->args[0];
Pablo Neira Ayuso7c95f6d2014-04-04 01:22:45 +02002278 struct nft_af_info *afi;
Pablo Neira Ayusoc9c8e482013-12-26 16:49:03 +01002279 struct nft_table *table, *cur_table = (struct nft_table *)cb->args[2];
2280 struct net *net = sock_net(skb->sk);
2281 int cur_family = cb->args[3];
Pablo Neira Ayuso5b96af72014-07-16 17:35:18 +02002282 struct nft_ctx *ctx = cb->data, ctx_set;
Pablo Neira Ayusoc9c8e482013-12-26 16:49:03 +01002283
2284 if (cb->args[1])
2285 return skb->len;
2286
Pablo Neira Ayusoe688a7f2014-07-01 11:49:18 +02002287 rcu_read_lock();
Pablo Neira Ayuso38e029f2014-07-01 12:23:12 +02002288 cb->seq = net->nft.base_seq;
2289
Pablo Neira Ayusoe688a7f2014-07-01 11:49:18 +02002290 list_for_each_entry_rcu(afi, &net->nft.af_info, list) {
Pablo Neira Ayuso5b96af72014-07-16 17:35:18 +02002291 if (ctx->afi && ctx->afi != afi)
2292 continue;
2293
Pablo Neira Ayusoc9c8e482013-12-26 16:49:03 +01002294 if (cur_family) {
2295 if (afi->family != cur_family)
2296 continue;
2297
2298 cur_family = 0;
2299 }
Pablo Neira Ayusoe688a7f2014-07-01 11:49:18 +02002300 list_for_each_entry_rcu(table, &afi->tables, list) {
Pablo Neira Ayuso5b96af72014-07-16 17:35:18 +02002301 if (ctx->table && ctx->table != table)
2302 continue;
2303
Pablo Neira Ayusoc9c8e482013-12-26 16:49:03 +01002304 if (cur_table) {
2305 if (cur_table != table)
2306 continue;
2307
2308 cur_table = NULL;
2309 }
Pablo Neira Ayusoc9c8e482013-12-26 16:49:03 +01002310 idx = 0;
Pablo Neira Ayuso5b96af72014-07-16 17:35:18 +02002311 list_for_each_entry_rcu(set, &table->sets, list) {
Pablo Neira Ayusoc9c8e482013-12-26 16:49:03 +01002312 if (idx < s_idx)
2313 goto cont;
Pablo Neira Ayuso5b96af72014-07-16 17:35:18 +02002314
2315 ctx_set = *ctx;
2316 ctx_set.table = table;
2317 ctx_set.afi = afi;
2318 if (nf_tables_fill_set(skb, &ctx_set, set,
Pablo Neira Ayusoc9c8e482013-12-26 16:49:03 +01002319 NFT_MSG_NEWSET,
2320 NLM_F_MULTI) < 0) {
2321 cb->args[0] = idx;
2322 cb->args[2] = (unsigned long) table;
2323 cb->args[3] = afi->family;
2324 goto done;
2325 }
Pablo Neira Ayuso38e029f2014-07-01 12:23:12 +02002326 nl_dump_check_consistent(cb, nlmsg_hdr(skb));
Pablo Neira Ayusoc9c8e482013-12-26 16:49:03 +01002327cont:
2328 idx++;
2329 }
2330 if (s_idx)
2331 s_idx = 0;
2332 }
2333 }
2334 cb->args[1] = 1;
2335done:
Pablo Neira Ayusoe688a7f2014-07-01 11:49:18 +02002336 rcu_read_unlock();
Pablo Neira Ayusoc9c8e482013-12-26 16:49:03 +01002337 return skb->len;
2338}
2339
Pablo Neira Ayuso5b96af72014-07-16 17:35:18 +02002340static int nf_tables_dump_sets_done(struct netlink_callback *cb)
Patrick McHardy20a69342013-10-11 12:06:22 +02002341{
Pablo Neira Ayuso5b96af72014-07-16 17:35:18 +02002342 kfree(cb->data);
2343 return 0;
Patrick McHardy20a69342013-10-11 12:06:22 +02002344}
2345
Pablo Neira Ayuso958bee12014-04-03 11:48:44 +02002346#define NFT_SET_INACTIVE (1 << 15) /* Internal set flag */
2347
Patrick McHardy20a69342013-10-11 12:06:22 +02002348static int nf_tables_getset(struct sock *nlsk, struct sk_buff *skb,
2349 const struct nlmsghdr *nlh,
2350 const struct nlattr * const nla[])
2351{
2352 const struct nft_set *set;
2353 struct nft_ctx ctx;
2354 struct sk_buff *skb2;
Pablo Neira Ayusoc9c8e482013-12-26 16:49:03 +01002355 const struct nfgenmsg *nfmsg = nlmsg_data(nlh);
Patrick McHardy20a69342013-10-11 12:06:22 +02002356 int err;
2357
2358 /* Verify existance before starting dump */
2359 err = nft_ctx_init_from_setattr(&ctx, skb, nlh, nla);
2360 if (err < 0)
2361 return err;
2362
2363 if (nlh->nlmsg_flags & NLM_F_DUMP) {
2364 struct netlink_dump_control c = {
2365 .dump = nf_tables_dump_sets,
Pablo Neira Ayuso5b96af72014-07-16 17:35:18 +02002366 .done = nf_tables_dump_sets_done,
Patrick McHardy20a69342013-10-11 12:06:22 +02002367 };
Pablo Neira Ayuso5b96af72014-07-16 17:35:18 +02002368 struct nft_ctx *ctx_dump;
2369
2370 ctx_dump = kmalloc(sizeof(*ctx_dump), GFP_KERNEL);
2371 if (ctx_dump == NULL)
2372 return -ENOMEM;
2373
2374 *ctx_dump = ctx;
2375 c.data = ctx_dump;
2376
Patrick McHardy20a69342013-10-11 12:06:22 +02002377 return netlink_dump_start(nlsk, skb, nlh, &c);
2378 }
2379
Pablo Neira Ayusoc9c8e482013-12-26 16:49:03 +01002380 /* Only accept unspec with dump */
2381 if (nfmsg->nfgen_family == NFPROTO_UNSPEC)
2382 return -EAFNOSUPPORT;
2383
Patrick McHardy20a69342013-10-11 12:06:22 +02002384 set = nf_tables_set_lookup(ctx.table, nla[NFTA_SET_NAME]);
2385 if (IS_ERR(set))
2386 return PTR_ERR(set);
Pablo Neira Ayuso958bee12014-04-03 11:48:44 +02002387 if (set->flags & NFT_SET_INACTIVE)
2388 return -ENOENT;
Patrick McHardy20a69342013-10-11 12:06:22 +02002389
2390 skb2 = alloc_skb(NLMSG_GOODSIZE, GFP_KERNEL);
2391 if (skb2 == NULL)
2392 return -ENOMEM;
2393
2394 err = nf_tables_fill_set(skb2, &ctx, set, NFT_MSG_NEWSET, 0);
2395 if (err < 0)
2396 goto err;
2397
2398 return nlmsg_unicast(nlsk, skb2, NETLINK_CB(skb).portid);
2399
2400err:
2401 kfree_skb(skb2);
2402 return err;
2403}
2404
Patrick McHardyc50b9602014-03-28 10:19:47 +00002405static int nf_tables_set_desc_parse(const struct nft_ctx *ctx,
2406 struct nft_set_desc *desc,
2407 const struct nlattr *nla)
2408{
2409 struct nlattr *da[NFTA_SET_DESC_MAX + 1];
2410 int err;
2411
2412 err = nla_parse_nested(da, NFTA_SET_DESC_MAX, nla, nft_set_desc_policy);
2413 if (err < 0)
2414 return err;
2415
2416 if (da[NFTA_SET_DESC_SIZE] != NULL)
2417 desc->size = ntohl(nla_get_be32(da[NFTA_SET_DESC_SIZE]));
2418
2419 return 0;
2420}
2421
Pablo Neira Ayuso958bee12014-04-03 11:48:44 +02002422static int nft_trans_set_add(struct nft_ctx *ctx, int msg_type,
2423 struct nft_set *set)
2424{
2425 struct nft_trans *trans;
2426
2427 trans = nft_trans_alloc(ctx, msg_type, sizeof(struct nft_trans_set));
2428 if (trans == NULL)
2429 return -ENOMEM;
2430
2431 if (msg_type == NFT_MSG_NEWSET && ctx->nla[NFTA_SET_ID] != NULL) {
2432 nft_trans_set_id(trans) =
2433 ntohl(nla_get_be32(ctx->nla[NFTA_SET_ID]));
2434 set->flags |= NFT_SET_INACTIVE;
2435 }
2436 nft_trans_set(trans) = set;
2437 list_add_tail(&trans->list, &ctx->net->nft.commit_list);
2438
2439 return 0;
2440}
2441
Patrick McHardy20a69342013-10-11 12:06:22 +02002442static int nf_tables_newset(struct sock *nlsk, struct sk_buff *skb,
2443 const struct nlmsghdr *nlh,
2444 const struct nlattr * const nla[])
2445{
2446 const struct nfgenmsg *nfmsg = nlmsg_data(nlh);
2447 const struct nft_set_ops *ops;
Pablo Neira Ayuso7c95f6d2014-04-04 01:22:45 +02002448 struct nft_af_info *afi;
Pablo Neira Ayuso99633ab2013-10-10 23:28:33 +02002449 struct net *net = sock_net(skb->sk);
Patrick McHardy20a69342013-10-11 12:06:22 +02002450 struct nft_table *table;
2451 struct nft_set *set;
2452 struct nft_ctx ctx;
2453 char name[IFNAMSIZ];
2454 unsigned int size;
2455 bool create;
Patrick McHardyc50b9602014-03-28 10:19:47 +00002456 u32 ktype, dtype, flags, policy;
2457 struct nft_set_desc desc;
Patrick McHardy20a69342013-10-11 12:06:22 +02002458 int err;
2459
2460 if (nla[NFTA_SET_TABLE] == NULL ||
2461 nla[NFTA_SET_NAME] == NULL ||
Pablo Neira Ayuso958bee12014-04-03 11:48:44 +02002462 nla[NFTA_SET_KEY_LEN] == NULL ||
2463 nla[NFTA_SET_ID] == NULL)
Patrick McHardy20a69342013-10-11 12:06:22 +02002464 return -EINVAL;
2465
Patrick McHardyc50b9602014-03-28 10:19:47 +00002466 memset(&desc, 0, sizeof(desc));
2467
Patrick McHardy20a69342013-10-11 12:06:22 +02002468 ktype = NFT_DATA_VALUE;
2469 if (nla[NFTA_SET_KEY_TYPE] != NULL) {
2470 ktype = ntohl(nla_get_be32(nla[NFTA_SET_KEY_TYPE]));
2471 if ((ktype & NFT_DATA_RESERVED_MASK) == NFT_DATA_RESERVED_MASK)
2472 return -EINVAL;
2473 }
2474
Patrick McHardyc50b9602014-03-28 10:19:47 +00002475 desc.klen = ntohl(nla_get_be32(nla[NFTA_SET_KEY_LEN]));
2476 if (desc.klen == 0 || desc.klen > FIELD_SIZEOF(struct nft_data, data))
Patrick McHardy20a69342013-10-11 12:06:22 +02002477 return -EINVAL;
2478
2479 flags = 0;
2480 if (nla[NFTA_SET_FLAGS] != NULL) {
2481 flags = ntohl(nla_get_be32(nla[NFTA_SET_FLAGS]));
2482 if (flags & ~(NFT_SET_ANONYMOUS | NFT_SET_CONSTANT |
2483 NFT_SET_INTERVAL | NFT_SET_MAP))
2484 return -EINVAL;
2485 }
2486
2487 dtype = 0;
Patrick McHardy20a69342013-10-11 12:06:22 +02002488 if (nla[NFTA_SET_DATA_TYPE] != NULL) {
2489 if (!(flags & NFT_SET_MAP))
2490 return -EINVAL;
2491
2492 dtype = ntohl(nla_get_be32(nla[NFTA_SET_DATA_TYPE]));
2493 if ((dtype & NFT_DATA_RESERVED_MASK) == NFT_DATA_RESERVED_MASK &&
2494 dtype != NFT_DATA_VERDICT)
2495 return -EINVAL;
2496
2497 if (dtype != NFT_DATA_VERDICT) {
2498 if (nla[NFTA_SET_DATA_LEN] == NULL)
2499 return -EINVAL;
Patrick McHardyc50b9602014-03-28 10:19:47 +00002500 desc.dlen = ntohl(nla_get_be32(nla[NFTA_SET_DATA_LEN]));
2501 if (desc.dlen == 0 ||
2502 desc.dlen > FIELD_SIZEOF(struct nft_data, data))
Patrick McHardy20a69342013-10-11 12:06:22 +02002503 return -EINVAL;
2504 } else
Patrick McHardyc50b9602014-03-28 10:19:47 +00002505 desc.dlen = sizeof(struct nft_data);
Patrick McHardy20a69342013-10-11 12:06:22 +02002506 } else if (flags & NFT_SET_MAP)
2507 return -EINVAL;
2508
Patrick McHardyc50b9602014-03-28 10:19:47 +00002509 policy = NFT_SET_POL_PERFORMANCE;
2510 if (nla[NFTA_SET_POLICY] != NULL)
2511 policy = ntohl(nla_get_be32(nla[NFTA_SET_POLICY]));
2512
2513 if (nla[NFTA_SET_DESC] != NULL) {
2514 err = nf_tables_set_desc_parse(&ctx, &desc, nla[NFTA_SET_DESC]);
2515 if (err < 0)
2516 return err;
2517 }
2518
Patrick McHardy20a69342013-10-11 12:06:22 +02002519 create = nlh->nlmsg_flags & NLM_F_CREATE ? true : false;
2520
Pablo Neira Ayuso99633ab2013-10-10 23:28:33 +02002521 afi = nf_tables_afinfo_lookup(net, nfmsg->nfgen_family, create);
Patrick McHardy20a69342013-10-11 12:06:22 +02002522 if (IS_ERR(afi))
2523 return PTR_ERR(afi);
2524
Pablo Neira Ayuso93707612013-10-10 23:21:26 +02002525 table = nf_tables_table_lookup(afi, nla[NFTA_SET_TABLE]);
Patrick McHardy20a69342013-10-11 12:06:22 +02002526 if (IS_ERR(table))
2527 return PTR_ERR(table);
2528
Pablo Neira Ayuso0ca743a2013-10-14 00:06:06 +02002529 nft_ctx_init(&ctx, skb, nlh, afi, table, NULL, nla);
Patrick McHardy20a69342013-10-11 12:06:22 +02002530
2531 set = nf_tables_set_lookup(table, nla[NFTA_SET_NAME]);
2532 if (IS_ERR(set)) {
2533 if (PTR_ERR(set) != -ENOENT)
2534 return PTR_ERR(set);
2535 set = NULL;
2536 }
2537
2538 if (set != NULL) {
2539 if (nlh->nlmsg_flags & NLM_F_EXCL)
2540 return -EEXIST;
2541 if (nlh->nlmsg_flags & NLM_F_REPLACE)
2542 return -EOPNOTSUPP;
2543 return 0;
2544 }
2545
2546 if (!(nlh->nlmsg_flags & NLM_F_CREATE))
2547 return -ENOENT;
2548
Patrick McHardyc50b9602014-03-28 10:19:47 +00002549 ops = nft_select_set_ops(nla, &desc, policy);
Patrick McHardy20a69342013-10-11 12:06:22 +02002550 if (IS_ERR(ops))
2551 return PTR_ERR(ops);
2552
2553 size = 0;
2554 if (ops->privsize != NULL)
2555 size = ops->privsize(nla);
2556
2557 err = -ENOMEM;
2558 set = kzalloc(sizeof(*set) + size, GFP_KERNEL);
2559 if (set == NULL)
2560 goto err1;
2561
2562 nla_strlcpy(name, nla[NFTA_SET_NAME], sizeof(set->name));
2563 err = nf_tables_set_alloc_name(&ctx, set, name);
2564 if (err < 0)
2565 goto err2;
2566
2567 INIT_LIST_HEAD(&set->bindings);
2568 set->ops = ops;
2569 set->ktype = ktype;
Patrick McHardyc50b9602014-03-28 10:19:47 +00002570 set->klen = desc.klen;
Patrick McHardy20a69342013-10-11 12:06:22 +02002571 set->dtype = dtype;
Patrick McHardyc50b9602014-03-28 10:19:47 +00002572 set->dlen = desc.dlen;
Patrick McHardy20a69342013-10-11 12:06:22 +02002573 set->flags = flags;
Patrick McHardyc50b9602014-03-28 10:19:47 +00002574 set->size = desc.size;
Patrick McHardy20a69342013-10-11 12:06:22 +02002575
Patrick McHardyc50b9602014-03-28 10:19:47 +00002576 err = ops->init(set, &desc, nla);
Patrick McHardy20a69342013-10-11 12:06:22 +02002577 if (err < 0)
2578 goto err2;
2579
Pablo Neira Ayuso958bee12014-04-03 11:48:44 +02002580 err = nft_trans_set_add(&ctx, NFT_MSG_NEWSET, set);
Patrick McHardy20a69342013-10-11 12:06:22 +02002581 if (err < 0)
2582 goto err2;
2583
Pablo Neira Ayusoe688a7f2014-07-01 11:49:18 +02002584 list_add_tail_rcu(&set->list, &table->sets);
Pablo Neira Ayuso4fefee52014-05-23 12:39:26 +02002585 table->use++;
Patrick McHardy20a69342013-10-11 12:06:22 +02002586 return 0;
2587
2588err2:
2589 kfree(set);
2590err1:
2591 module_put(ops->owner);
2592 return err;
2593}
2594
Pablo Neira Ayuso958bee12014-04-03 11:48:44 +02002595static void nft_set_destroy(struct nft_set *set)
2596{
2597 set->ops->destroy(set);
2598 module_put(set->ops->owner);
2599 kfree(set);
2600}
2601
Patrick McHardy20a69342013-10-11 12:06:22 +02002602static void nf_tables_set_destroy(const struct nft_ctx *ctx, struct nft_set *set)
2603{
Pablo Neira Ayusoe688a7f2014-07-01 11:49:18 +02002604 list_del_rcu(&set->list);
Pablo Neira Ayuso31f84412014-05-29 10:29:58 +02002605 nf_tables_set_notify(ctx, set, NFT_MSG_DELSET, GFP_ATOMIC);
Pablo Neira Ayuso958bee12014-04-03 11:48:44 +02002606 nft_set_destroy(set);
Patrick McHardy20a69342013-10-11 12:06:22 +02002607}
2608
2609static int nf_tables_delset(struct sock *nlsk, struct sk_buff *skb,
2610 const struct nlmsghdr *nlh,
2611 const struct nlattr * const nla[])
2612{
Pablo Neira Ayusoc9c8e482013-12-26 16:49:03 +01002613 const struct nfgenmsg *nfmsg = nlmsg_data(nlh);
Patrick McHardy20a69342013-10-11 12:06:22 +02002614 struct nft_set *set;
2615 struct nft_ctx ctx;
2616 int err;
2617
Patrick McHardyec2c9932014-02-05 15:03:35 +00002618 if (nfmsg->nfgen_family == NFPROTO_UNSPEC)
2619 return -EAFNOSUPPORT;
Patrick McHardy20a69342013-10-11 12:06:22 +02002620 if (nla[NFTA_SET_TABLE] == NULL)
2621 return -EINVAL;
2622
2623 err = nft_ctx_init_from_setattr(&ctx, skb, nlh, nla);
2624 if (err < 0)
2625 return err;
2626
2627 set = nf_tables_set_lookup(ctx.table, nla[NFTA_SET_NAME]);
2628 if (IS_ERR(set))
2629 return PTR_ERR(set);
Pablo Neira Ayuso958bee12014-04-03 11:48:44 +02002630 if (set->flags & NFT_SET_INACTIVE)
2631 return -ENOENT;
Patrick McHardy20a69342013-10-11 12:06:22 +02002632 if (!list_empty(&set->bindings))
2633 return -EBUSY;
2634
Pablo Neira Ayuso958bee12014-04-03 11:48:44 +02002635 err = nft_trans_set_add(&ctx, NFT_MSG_DELSET, set);
2636 if (err < 0)
2637 return err;
2638
Pablo Neira Ayusoe688a7f2014-07-01 11:49:18 +02002639 list_del_rcu(&set->list);
Pablo Neira Ayuso4fefee52014-05-23 12:39:26 +02002640 ctx.table->use--;
Patrick McHardy20a69342013-10-11 12:06:22 +02002641 return 0;
2642}
2643
2644static int nf_tables_bind_check_setelem(const struct nft_ctx *ctx,
2645 const struct nft_set *set,
2646 const struct nft_set_iter *iter,
2647 const struct nft_set_elem *elem)
2648{
2649 enum nft_registers dreg;
2650
2651 dreg = nft_type_to_reg(set->dtype);
Pablo Neira Ayuso2ee0d3c2013-12-28 00:59:38 +01002652 return nft_validate_data_load(ctx, dreg, &elem->data,
2653 set->dtype == NFT_DATA_VERDICT ?
2654 NFT_DATA_VERDICT : NFT_DATA_VALUE);
Patrick McHardy20a69342013-10-11 12:06:22 +02002655}
2656
2657int nf_tables_bind_set(const struct nft_ctx *ctx, struct nft_set *set,
2658 struct nft_set_binding *binding)
2659{
2660 struct nft_set_binding *i;
2661 struct nft_set_iter iter;
2662
2663 if (!list_empty(&set->bindings) && set->flags & NFT_SET_ANONYMOUS)
2664 return -EBUSY;
2665
2666 if (set->flags & NFT_SET_MAP) {
2667 /* If the set is already bound to the same chain all
2668 * jumps are already validated for that chain.
2669 */
2670 list_for_each_entry(i, &set->bindings, list) {
2671 if (i->chain == binding->chain)
2672 goto bind;
2673 }
2674
2675 iter.skip = 0;
2676 iter.count = 0;
2677 iter.err = 0;
2678 iter.fn = nf_tables_bind_check_setelem;
2679
2680 set->ops->walk(ctx, set, &iter);
2681 if (iter.err < 0) {
2682 /* Destroy anonymous sets if binding fails */
2683 if (set->flags & NFT_SET_ANONYMOUS)
2684 nf_tables_set_destroy(ctx, set);
2685
2686 return iter.err;
2687 }
2688 }
2689bind:
2690 binding->chain = ctx->chain;
Pablo Neira Ayusoe688a7f2014-07-01 11:49:18 +02002691 list_add_tail_rcu(&binding->list, &set->bindings);
Patrick McHardy20a69342013-10-11 12:06:22 +02002692 return 0;
2693}
2694
2695void nf_tables_unbind_set(const struct nft_ctx *ctx, struct nft_set *set,
2696 struct nft_set_binding *binding)
2697{
Pablo Neira Ayusoe688a7f2014-07-01 11:49:18 +02002698 list_del_rcu(&binding->list);
Patrick McHardy20a69342013-10-11 12:06:22 +02002699
Pablo Neira Ayuso958bee12014-04-03 11:48:44 +02002700 if (list_empty(&set->bindings) && set->flags & NFT_SET_ANONYMOUS &&
2701 !(set->flags & NFT_SET_INACTIVE))
Patrick McHardy20a69342013-10-11 12:06:22 +02002702 nf_tables_set_destroy(ctx, set);
2703}
2704
2705/*
2706 * Set elements
2707 */
2708
2709static const struct nla_policy nft_set_elem_policy[NFTA_SET_ELEM_MAX + 1] = {
2710 [NFTA_SET_ELEM_KEY] = { .type = NLA_NESTED },
2711 [NFTA_SET_ELEM_DATA] = { .type = NLA_NESTED },
2712 [NFTA_SET_ELEM_FLAGS] = { .type = NLA_U32 },
2713};
2714
2715static const struct nla_policy nft_set_elem_list_policy[NFTA_SET_ELEM_LIST_MAX + 1] = {
2716 [NFTA_SET_ELEM_LIST_TABLE] = { .type = NLA_STRING },
2717 [NFTA_SET_ELEM_LIST_SET] = { .type = NLA_STRING },
2718 [NFTA_SET_ELEM_LIST_ELEMENTS] = { .type = NLA_NESTED },
Pablo Neira Ayuso958bee12014-04-03 11:48:44 +02002719 [NFTA_SET_ELEM_LIST_SET_ID] = { .type = NLA_U32 },
Patrick McHardy20a69342013-10-11 12:06:22 +02002720};
2721
2722static int nft_ctx_init_from_elemattr(struct nft_ctx *ctx,
2723 const struct sk_buff *skb,
2724 const struct nlmsghdr *nlh,
Pablo Neira Ayuso55dd6f92014-04-03 11:53:37 +02002725 const struct nlattr * const nla[],
2726 bool trans)
Patrick McHardy20a69342013-10-11 12:06:22 +02002727{
2728 const struct nfgenmsg *nfmsg = nlmsg_data(nlh);
Pablo Neira Ayuso7c95f6d2014-04-04 01:22:45 +02002729 struct nft_af_info *afi;
2730 struct nft_table *table;
Pablo Neira Ayuso99633ab2013-10-10 23:28:33 +02002731 struct net *net = sock_net(skb->sk);
Patrick McHardy20a69342013-10-11 12:06:22 +02002732
Pablo Neira Ayuso99633ab2013-10-10 23:28:33 +02002733 afi = nf_tables_afinfo_lookup(net, nfmsg->nfgen_family, false);
Patrick McHardy20a69342013-10-11 12:06:22 +02002734 if (IS_ERR(afi))
2735 return PTR_ERR(afi);
2736
Pablo Neira Ayuso93707612013-10-10 23:21:26 +02002737 table = nf_tables_table_lookup(afi, nla[NFTA_SET_ELEM_LIST_TABLE]);
Patrick McHardy20a69342013-10-11 12:06:22 +02002738 if (IS_ERR(table))
2739 return PTR_ERR(table);
Pablo Neira Ayuso55dd6f92014-04-03 11:53:37 +02002740 if (!trans && (table->flags & NFT_TABLE_INACTIVE))
2741 return -ENOENT;
Patrick McHardy20a69342013-10-11 12:06:22 +02002742
Pablo Neira Ayuso0ca743a2013-10-14 00:06:06 +02002743 nft_ctx_init(ctx, skb, nlh, afi, table, NULL, nla);
Patrick McHardy20a69342013-10-11 12:06:22 +02002744 return 0;
2745}
2746
2747static int nf_tables_fill_setelem(struct sk_buff *skb,
2748 const struct nft_set *set,
2749 const struct nft_set_elem *elem)
2750{
2751 unsigned char *b = skb_tail_pointer(skb);
2752 struct nlattr *nest;
2753
2754 nest = nla_nest_start(skb, NFTA_LIST_ELEM);
2755 if (nest == NULL)
2756 goto nla_put_failure;
2757
2758 if (nft_data_dump(skb, NFTA_SET_ELEM_KEY, &elem->key, NFT_DATA_VALUE,
2759 set->klen) < 0)
2760 goto nla_put_failure;
2761
2762 if (set->flags & NFT_SET_MAP &&
2763 !(elem->flags & NFT_SET_ELEM_INTERVAL_END) &&
2764 nft_data_dump(skb, NFTA_SET_ELEM_DATA, &elem->data,
2765 set->dtype == NFT_DATA_VERDICT ? NFT_DATA_VERDICT : NFT_DATA_VALUE,
2766 set->dlen) < 0)
2767 goto nla_put_failure;
2768
2769 if (elem->flags != 0)
2770 if (nla_put_be32(skb, NFTA_SET_ELEM_FLAGS, htonl(elem->flags)))
2771 goto nla_put_failure;
2772
2773 nla_nest_end(skb, nest);
2774 return 0;
2775
2776nla_put_failure:
2777 nlmsg_trim(skb, b);
2778 return -EMSGSIZE;
2779}
2780
2781struct nft_set_dump_args {
2782 const struct netlink_callback *cb;
2783 struct nft_set_iter iter;
2784 struct sk_buff *skb;
2785};
2786
2787static int nf_tables_dump_setelem(const struct nft_ctx *ctx,
2788 const struct nft_set *set,
2789 const struct nft_set_iter *iter,
2790 const struct nft_set_elem *elem)
2791{
2792 struct nft_set_dump_args *args;
2793
2794 args = container_of(iter, struct nft_set_dump_args, iter);
2795 return nf_tables_fill_setelem(args->skb, set, elem);
2796}
2797
2798static int nf_tables_dump_set(struct sk_buff *skb, struct netlink_callback *cb)
2799{
2800 const struct nft_set *set;
2801 struct nft_set_dump_args args;
2802 struct nft_ctx ctx;
2803 struct nlattr *nla[NFTA_SET_ELEM_LIST_MAX + 1];
2804 struct nfgenmsg *nfmsg;
2805 struct nlmsghdr *nlh;
2806 struct nlattr *nest;
2807 u32 portid, seq;
2808 int event, err;
2809
Michal Nazarewicz720e0df2014-01-01 06:27:19 +01002810 err = nlmsg_parse(cb->nlh, sizeof(struct nfgenmsg), nla,
2811 NFTA_SET_ELEM_LIST_MAX, nft_set_elem_list_policy);
Patrick McHardy20a69342013-10-11 12:06:22 +02002812 if (err < 0)
2813 return err;
2814
Pablo Neira Ayuso55dd6f92014-04-03 11:53:37 +02002815 err = nft_ctx_init_from_elemattr(&ctx, cb->skb, cb->nlh, (void *)nla,
2816 false);
Patrick McHardy20a69342013-10-11 12:06:22 +02002817 if (err < 0)
2818 return err;
2819
2820 set = nf_tables_set_lookup(ctx.table, nla[NFTA_SET_ELEM_LIST_SET]);
2821 if (IS_ERR(set))
2822 return PTR_ERR(set);
Pablo Neira Ayuso958bee12014-04-03 11:48:44 +02002823 if (set->flags & NFT_SET_INACTIVE)
2824 return -ENOENT;
Patrick McHardy20a69342013-10-11 12:06:22 +02002825
2826 event = NFT_MSG_NEWSETELEM;
2827 event |= NFNL_SUBSYS_NFTABLES << 8;
2828 portid = NETLINK_CB(cb->skb).portid;
2829 seq = cb->nlh->nlmsg_seq;
2830
2831 nlh = nlmsg_put(skb, portid, seq, event, sizeof(struct nfgenmsg),
2832 NLM_F_MULTI);
2833 if (nlh == NULL)
2834 goto nla_put_failure;
2835
2836 nfmsg = nlmsg_data(nlh);
Pablo Neira Ayuso6403d962014-06-11 19:05:28 +02002837 nfmsg->nfgen_family = ctx.afi->family;
Patrick McHardy20a69342013-10-11 12:06:22 +02002838 nfmsg->version = NFNETLINK_V0;
2839 nfmsg->res_id = 0;
2840
2841 if (nla_put_string(skb, NFTA_SET_ELEM_LIST_TABLE, ctx.table->name))
2842 goto nla_put_failure;
2843 if (nla_put_string(skb, NFTA_SET_ELEM_LIST_SET, set->name))
2844 goto nla_put_failure;
2845
2846 nest = nla_nest_start(skb, NFTA_SET_ELEM_LIST_ELEMENTS);
2847 if (nest == NULL)
2848 goto nla_put_failure;
2849
2850 args.cb = cb;
2851 args.skb = skb;
2852 args.iter.skip = cb->args[0];
2853 args.iter.count = 0;
2854 args.iter.err = 0;
2855 args.iter.fn = nf_tables_dump_setelem;
2856 set->ops->walk(&ctx, set, &args.iter);
2857
2858 nla_nest_end(skb, nest);
2859 nlmsg_end(skb, nlh);
2860
2861 if (args.iter.err && args.iter.err != -EMSGSIZE)
2862 return args.iter.err;
2863 if (args.iter.count == cb->args[0])
2864 return 0;
2865
2866 cb->args[0] = args.iter.count;
2867 return skb->len;
2868
2869nla_put_failure:
2870 return -ENOSPC;
2871}
2872
2873static int nf_tables_getsetelem(struct sock *nlsk, struct sk_buff *skb,
2874 const struct nlmsghdr *nlh,
2875 const struct nlattr * const nla[])
2876{
2877 const struct nft_set *set;
2878 struct nft_ctx ctx;
2879 int err;
2880
Pablo Neira Ayuso55dd6f92014-04-03 11:53:37 +02002881 err = nft_ctx_init_from_elemattr(&ctx, skb, nlh, nla, false);
Patrick McHardy20a69342013-10-11 12:06:22 +02002882 if (err < 0)
2883 return err;
2884
2885 set = nf_tables_set_lookup(ctx.table, nla[NFTA_SET_ELEM_LIST_SET]);
2886 if (IS_ERR(set))
2887 return PTR_ERR(set);
Pablo Neira Ayuso958bee12014-04-03 11:48:44 +02002888 if (set->flags & NFT_SET_INACTIVE)
2889 return -ENOENT;
Patrick McHardy20a69342013-10-11 12:06:22 +02002890
2891 if (nlh->nlmsg_flags & NLM_F_DUMP) {
2892 struct netlink_dump_control c = {
2893 .dump = nf_tables_dump_set,
2894 };
2895 return netlink_dump_start(nlsk, skb, nlh, &c);
2896 }
2897 return -EOPNOTSUPP;
2898}
2899
Arturo Borrerod60ce622014-04-01 14:06:07 +02002900static int nf_tables_fill_setelem_info(struct sk_buff *skb,
2901 const struct nft_ctx *ctx, u32 seq,
2902 u32 portid, int event, u16 flags,
2903 const struct nft_set *set,
2904 const struct nft_set_elem *elem)
2905{
2906 struct nfgenmsg *nfmsg;
2907 struct nlmsghdr *nlh;
2908 struct nlattr *nest;
2909 int err;
2910
2911 event |= NFNL_SUBSYS_NFTABLES << 8;
2912 nlh = nlmsg_put(skb, portid, seq, event, sizeof(struct nfgenmsg),
2913 flags);
2914 if (nlh == NULL)
2915 goto nla_put_failure;
2916
2917 nfmsg = nlmsg_data(nlh);
2918 nfmsg->nfgen_family = ctx->afi->family;
2919 nfmsg->version = NFNETLINK_V0;
2920 nfmsg->res_id = 0;
2921
2922 if (nla_put_string(skb, NFTA_SET_TABLE, ctx->table->name))
2923 goto nla_put_failure;
2924 if (nla_put_string(skb, NFTA_SET_NAME, set->name))
2925 goto nla_put_failure;
2926
2927 nest = nla_nest_start(skb, NFTA_SET_ELEM_LIST_ELEMENTS);
2928 if (nest == NULL)
2929 goto nla_put_failure;
2930
2931 err = nf_tables_fill_setelem(skb, set, elem);
2932 if (err < 0)
2933 goto nla_put_failure;
2934
2935 nla_nest_end(skb, nest);
2936
2937 return nlmsg_end(skb, nlh);
2938
2939nla_put_failure:
2940 nlmsg_trim(skb, nlh);
2941 return -1;
2942}
2943
2944static int nf_tables_setelem_notify(const struct nft_ctx *ctx,
2945 const struct nft_set *set,
2946 const struct nft_set_elem *elem,
2947 int event, u16 flags)
2948{
Pablo Neira Ayuso128ad332014-05-09 17:14:24 +02002949 struct net *net = ctx->net;
2950 u32 portid = ctx->portid;
Arturo Borrerod60ce622014-04-01 14:06:07 +02002951 struct sk_buff *skb;
2952 int err;
2953
Pablo Neira Ayuso128ad332014-05-09 17:14:24 +02002954 if (!ctx->report && !nfnetlink_has_listeners(net, NFNLGRP_NFTABLES))
Arturo Borrerod60ce622014-04-01 14:06:07 +02002955 return 0;
2956
2957 err = -ENOBUFS;
2958 skb = nlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL);
2959 if (skb == NULL)
2960 goto err;
2961
2962 err = nf_tables_fill_setelem_info(skb, ctx, 0, portid, event, flags,
2963 set, elem);
2964 if (err < 0) {
2965 kfree_skb(skb);
2966 goto err;
2967 }
2968
Pablo Neira Ayuso128ad332014-05-09 17:14:24 +02002969 err = nfnetlink_send(skb, net, portid, NFNLGRP_NFTABLES, ctx->report,
Arturo Borrerod60ce622014-04-01 14:06:07 +02002970 GFP_KERNEL);
2971err:
2972 if (err < 0)
2973 nfnetlink_set_err(net, portid, NFNLGRP_NFTABLES, err);
2974 return err;
2975}
2976
Pablo Neira Ayuso60319eb2014-04-04 03:36:42 +02002977static struct nft_trans *nft_trans_elem_alloc(struct nft_ctx *ctx,
2978 int msg_type,
2979 struct nft_set *set)
2980{
2981 struct nft_trans *trans;
2982
2983 trans = nft_trans_alloc(ctx, msg_type, sizeof(struct nft_trans_elem));
2984 if (trans == NULL)
2985 return NULL;
2986
2987 nft_trans_elem_set(trans) = set;
2988 return trans;
2989}
2990
2991static int nft_add_set_elem(struct nft_ctx *ctx, struct nft_set *set,
Patrick McHardy20a69342013-10-11 12:06:22 +02002992 const struct nlattr *attr)
2993{
2994 struct nlattr *nla[NFTA_SET_ELEM_MAX + 1];
2995 struct nft_data_desc d1, d2;
2996 struct nft_set_elem elem;
2997 struct nft_set_binding *binding;
2998 enum nft_registers dreg;
Pablo Neira Ayuso60319eb2014-04-04 03:36:42 +02002999 struct nft_trans *trans;
Patrick McHardy20a69342013-10-11 12:06:22 +02003000 int err;
3001
Patrick McHardyc50b9602014-03-28 10:19:47 +00003002 if (set->size && set->nelems == set->size)
3003 return -ENFILE;
3004
Patrick McHardy20a69342013-10-11 12:06:22 +02003005 err = nla_parse_nested(nla, NFTA_SET_ELEM_MAX, attr,
3006 nft_set_elem_policy);
3007 if (err < 0)
3008 return err;
3009
3010 if (nla[NFTA_SET_ELEM_KEY] == NULL)
3011 return -EINVAL;
3012
3013 elem.flags = 0;
3014 if (nla[NFTA_SET_ELEM_FLAGS] != NULL) {
3015 elem.flags = ntohl(nla_get_be32(nla[NFTA_SET_ELEM_FLAGS]));
3016 if (elem.flags & ~NFT_SET_ELEM_INTERVAL_END)
3017 return -EINVAL;
3018 }
3019
3020 if (set->flags & NFT_SET_MAP) {
3021 if (nla[NFTA_SET_ELEM_DATA] == NULL &&
3022 !(elem.flags & NFT_SET_ELEM_INTERVAL_END))
3023 return -EINVAL;
Pablo Neira Ayusobd7fc642014-02-07 12:53:07 +01003024 if (nla[NFTA_SET_ELEM_DATA] != NULL &&
3025 elem.flags & NFT_SET_ELEM_INTERVAL_END)
3026 return -EINVAL;
Patrick McHardy20a69342013-10-11 12:06:22 +02003027 } else {
3028 if (nla[NFTA_SET_ELEM_DATA] != NULL)
3029 return -EINVAL;
3030 }
3031
3032 err = nft_data_init(ctx, &elem.key, &d1, nla[NFTA_SET_ELEM_KEY]);
3033 if (err < 0)
3034 goto err1;
3035 err = -EINVAL;
3036 if (d1.type != NFT_DATA_VALUE || d1.len != set->klen)
3037 goto err2;
3038
3039 err = -EEXIST;
3040 if (set->ops->get(set, &elem) == 0)
3041 goto err2;
3042
3043 if (nla[NFTA_SET_ELEM_DATA] != NULL) {
3044 err = nft_data_init(ctx, &elem.data, &d2, nla[NFTA_SET_ELEM_DATA]);
3045 if (err < 0)
3046 goto err2;
3047
3048 err = -EINVAL;
3049 if (set->dtype != NFT_DATA_VERDICT && d2.len != set->dlen)
3050 goto err3;
3051
3052 dreg = nft_type_to_reg(set->dtype);
3053 list_for_each_entry(binding, &set->bindings, list) {
3054 struct nft_ctx bind_ctx = {
3055 .afi = ctx->afi,
3056 .table = ctx->table,
Pablo Neira Ayuso7c95f6d2014-04-04 01:22:45 +02003057 .chain = (struct nft_chain *)binding->chain,
Patrick McHardy20a69342013-10-11 12:06:22 +02003058 };
3059
3060 err = nft_validate_data_load(&bind_ctx, dreg,
3061 &elem.data, d2.type);
3062 if (err < 0)
3063 goto err3;
3064 }
3065 }
3066
Pablo Neira Ayuso60319eb2014-04-04 03:36:42 +02003067 trans = nft_trans_elem_alloc(ctx, NFT_MSG_NEWSETELEM, set);
3068 if (trans == NULL)
Patrick McHardy20a69342013-10-11 12:06:22 +02003069 goto err3;
3070
Patrick McHardy20a69342013-10-11 12:06:22 +02003071 err = set->ops->insert(set, &elem);
3072 if (err < 0)
Pablo Neira Ayuso60319eb2014-04-04 03:36:42 +02003073 goto err4;
Patrick McHardy20a69342013-10-11 12:06:22 +02003074
Pablo Neira Ayuso60319eb2014-04-04 03:36:42 +02003075 nft_trans_elem(trans) = elem;
Pablo Neira Ayuso46bbafc2014-05-22 12:36:03 +02003076 list_add_tail(&trans->list, &ctx->net->nft.commit_list);
Patrick McHardy20a69342013-10-11 12:06:22 +02003077 return 0;
3078
Pablo Neira Ayuso60319eb2014-04-04 03:36:42 +02003079err4:
3080 kfree(trans);
Patrick McHardy20a69342013-10-11 12:06:22 +02003081err3:
3082 if (nla[NFTA_SET_ELEM_DATA] != NULL)
3083 nft_data_uninit(&elem.data, d2.type);
3084err2:
3085 nft_data_uninit(&elem.key, d1.type);
3086err1:
3087 return err;
3088}
3089
3090static int nf_tables_newsetelem(struct sock *nlsk, struct sk_buff *skb,
3091 const struct nlmsghdr *nlh,
3092 const struct nlattr * const nla[])
3093{
Pablo Neira Ayuso958bee12014-04-03 11:48:44 +02003094 struct net *net = sock_net(skb->sk);
Patrick McHardy20a69342013-10-11 12:06:22 +02003095 const struct nlattr *attr;
3096 struct nft_set *set;
3097 struct nft_ctx ctx;
Pablo Neira Ayuso60319eb2014-04-04 03:36:42 +02003098 int rem, err = 0;
Patrick McHardy20a69342013-10-11 12:06:22 +02003099
Pablo Neira Ayuso7d5570c2014-07-25 13:15:36 +02003100 if (nla[NFTA_SET_ELEM_LIST_ELEMENTS] == NULL)
3101 return -EINVAL;
3102
Pablo Neira Ayuso55dd6f92014-04-03 11:53:37 +02003103 err = nft_ctx_init_from_elemattr(&ctx, skb, nlh, nla, true);
Patrick McHardy20a69342013-10-11 12:06:22 +02003104 if (err < 0)
3105 return err;
3106
3107 set = nf_tables_set_lookup(ctx.table, nla[NFTA_SET_ELEM_LIST_SET]);
Pablo Neira Ayuso958bee12014-04-03 11:48:44 +02003108 if (IS_ERR(set)) {
3109 if (nla[NFTA_SET_ELEM_LIST_SET_ID]) {
3110 set = nf_tables_set_lookup_byid(net,
3111 nla[NFTA_SET_ELEM_LIST_SET_ID]);
3112 }
3113 if (IS_ERR(set))
3114 return PTR_ERR(set);
3115 }
3116
Patrick McHardy20a69342013-10-11 12:06:22 +02003117 if (!list_empty(&set->bindings) && set->flags & NFT_SET_CONSTANT)
3118 return -EBUSY;
3119
3120 nla_for_each_nested(attr, nla[NFTA_SET_ELEM_LIST_ELEMENTS], rem) {
3121 err = nft_add_set_elem(&ctx, set, attr);
3122 if (err < 0)
Pablo Neira Ayuso60319eb2014-04-04 03:36:42 +02003123 break;
Pablo Neira Ayuso4fefee52014-05-23 12:39:26 +02003124
3125 set->nelems++;
Patrick McHardy20a69342013-10-11 12:06:22 +02003126 }
Pablo Neira Ayuso60319eb2014-04-04 03:36:42 +02003127 return err;
Patrick McHardy20a69342013-10-11 12:06:22 +02003128}
3129
Pablo Neira Ayuso60319eb2014-04-04 03:36:42 +02003130static int nft_del_setelem(struct nft_ctx *ctx, struct nft_set *set,
Patrick McHardy20a69342013-10-11 12:06:22 +02003131 const struct nlattr *attr)
3132{
3133 struct nlattr *nla[NFTA_SET_ELEM_MAX + 1];
3134 struct nft_data_desc desc;
3135 struct nft_set_elem elem;
Pablo Neira Ayuso60319eb2014-04-04 03:36:42 +02003136 struct nft_trans *trans;
Patrick McHardy20a69342013-10-11 12:06:22 +02003137 int err;
3138
3139 err = nla_parse_nested(nla, NFTA_SET_ELEM_MAX, attr,
3140 nft_set_elem_policy);
3141 if (err < 0)
3142 goto err1;
3143
3144 err = -EINVAL;
3145 if (nla[NFTA_SET_ELEM_KEY] == NULL)
3146 goto err1;
3147
3148 err = nft_data_init(ctx, &elem.key, &desc, nla[NFTA_SET_ELEM_KEY]);
3149 if (err < 0)
3150 goto err1;
3151
3152 err = -EINVAL;
3153 if (desc.type != NFT_DATA_VALUE || desc.len != set->klen)
3154 goto err2;
3155
3156 err = set->ops->get(set, &elem);
3157 if (err < 0)
3158 goto err2;
3159
Pablo Neira Ayuso60319eb2014-04-04 03:36:42 +02003160 trans = nft_trans_elem_alloc(ctx, NFT_MSG_DELSETELEM, set);
Julia Lawall609ccf02014-08-07 14:49:08 +02003161 if (trans == NULL) {
3162 err = -ENOMEM;
Pablo Neira Ayuso60319eb2014-04-04 03:36:42 +02003163 goto err2;
Julia Lawall609ccf02014-08-07 14:49:08 +02003164 }
Patrick McHardy20a69342013-10-11 12:06:22 +02003165
Pablo Neira Ayuso60319eb2014-04-04 03:36:42 +02003166 nft_trans_elem(trans) = elem;
Pablo Neira Ayuso46bbafc2014-05-22 12:36:03 +02003167 list_add_tail(&trans->list, &ctx->net->nft.commit_list);
Thomas Graf0dc13622014-08-01 17:25:38 +02003168 return 0;
Patrick McHardy20a69342013-10-11 12:06:22 +02003169err2:
3170 nft_data_uninit(&elem.key, desc.type);
3171err1:
3172 return err;
3173}
3174
3175static int nf_tables_delsetelem(struct sock *nlsk, struct sk_buff *skb,
3176 const struct nlmsghdr *nlh,
3177 const struct nlattr * const nla[])
3178{
3179 const struct nlattr *attr;
3180 struct nft_set *set;
3181 struct nft_ctx ctx;
Pablo Neira Ayuso60319eb2014-04-04 03:36:42 +02003182 int rem, err = 0;
Patrick McHardy20a69342013-10-11 12:06:22 +02003183
Pablo Neira Ayuso7d5570c2014-07-25 13:15:36 +02003184 if (nla[NFTA_SET_ELEM_LIST_ELEMENTS] == NULL)
3185 return -EINVAL;
3186
Pablo Neira Ayuso55dd6f92014-04-03 11:53:37 +02003187 err = nft_ctx_init_from_elemattr(&ctx, skb, nlh, nla, false);
Patrick McHardy20a69342013-10-11 12:06:22 +02003188 if (err < 0)
3189 return err;
3190
3191 set = nf_tables_set_lookup(ctx.table, nla[NFTA_SET_ELEM_LIST_SET]);
3192 if (IS_ERR(set))
3193 return PTR_ERR(set);
3194 if (!list_empty(&set->bindings) && set->flags & NFT_SET_CONSTANT)
3195 return -EBUSY;
3196
3197 nla_for_each_nested(attr, nla[NFTA_SET_ELEM_LIST_ELEMENTS], rem) {
3198 err = nft_del_setelem(&ctx, set, attr);
3199 if (err < 0)
Pablo Neira Ayuso60319eb2014-04-04 03:36:42 +02003200 break;
Pablo Neira Ayuso4fefee52014-05-23 12:39:26 +02003201
3202 set->nelems--;
Patrick McHardy20a69342013-10-11 12:06:22 +02003203 }
Pablo Neira Ayuso60319eb2014-04-04 03:36:42 +02003204 return err;
Patrick McHardy20a69342013-10-11 12:06:22 +02003205}
3206
Patrick McHardy96518512013-10-14 11:00:02 +02003207static const struct nfnl_callback nf_tables_cb[NFT_MSG_MAX] = {
3208 [NFT_MSG_NEWTABLE] = {
Pablo Neira Ayuso55dd6f92014-04-03 11:53:37 +02003209 .call_batch = nf_tables_newtable,
Patrick McHardy96518512013-10-14 11:00:02 +02003210 .attr_count = NFTA_TABLE_MAX,
3211 .policy = nft_table_policy,
3212 },
3213 [NFT_MSG_GETTABLE] = {
3214 .call = nf_tables_gettable,
3215 .attr_count = NFTA_TABLE_MAX,
3216 .policy = nft_table_policy,
3217 },
3218 [NFT_MSG_DELTABLE] = {
Pablo Neira Ayuso55dd6f92014-04-03 11:53:37 +02003219 .call_batch = nf_tables_deltable,
Patrick McHardy96518512013-10-14 11:00:02 +02003220 .attr_count = NFTA_TABLE_MAX,
3221 .policy = nft_table_policy,
3222 },
3223 [NFT_MSG_NEWCHAIN] = {
Pablo Neira Ayuso91c7b382014-04-09 11:58:08 +02003224 .call_batch = nf_tables_newchain,
Patrick McHardy96518512013-10-14 11:00:02 +02003225 .attr_count = NFTA_CHAIN_MAX,
3226 .policy = nft_chain_policy,
3227 },
3228 [NFT_MSG_GETCHAIN] = {
3229 .call = nf_tables_getchain,
3230 .attr_count = NFTA_CHAIN_MAX,
3231 .policy = nft_chain_policy,
3232 },
3233 [NFT_MSG_DELCHAIN] = {
Pablo Neira Ayuso91c7b382014-04-09 11:58:08 +02003234 .call_batch = nf_tables_delchain,
Patrick McHardy96518512013-10-14 11:00:02 +02003235 .attr_count = NFTA_CHAIN_MAX,
3236 .policy = nft_chain_policy,
3237 },
3238 [NFT_MSG_NEWRULE] = {
Pablo Neira Ayuso0628b122013-10-14 11:05:33 +02003239 .call_batch = nf_tables_newrule,
Patrick McHardy96518512013-10-14 11:00:02 +02003240 .attr_count = NFTA_RULE_MAX,
3241 .policy = nft_rule_policy,
3242 },
3243 [NFT_MSG_GETRULE] = {
3244 .call = nf_tables_getrule,
3245 .attr_count = NFTA_RULE_MAX,
3246 .policy = nft_rule_policy,
3247 },
3248 [NFT_MSG_DELRULE] = {
Pablo Neira Ayuso0628b122013-10-14 11:05:33 +02003249 .call_batch = nf_tables_delrule,
Patrick McHardy96518512013-10-14 11:00:02 +02003250 .attr_count = NFTA_RULE_MAX,
3251 .policy = nft_rule_policy,
3252 },
Patrick McHardy20a69342013-10-11 12:06:22 +02003253 [NFT_MSG_NEWSET] = {
Pablo Neira Ayuso958bee12014-04-03 11:48:44 +02003254 .call_batch = nf_tables_newset,
Patrick McHardy20a69342013-10-11 12:06:22 +02003255 .attr_count = NFTA_SET_MAX,
3256 .policy = nft_set_policy,
3257 },
3258 [NFT_MSG_GETSET] = {
3259 .call = nf_tables_getset,
3260 .attr_count = NFTA_SET_MAX,
3261 .policy = nft_set_policy,
3262 },
3263 [NFT_MSG_DELSET] = {
Pablo Neira Ayuso958bee12014-04-03 11:48:44 +02003264 .call_batch = nf_tables_delset,
Patrick McHardy20a69342013-10-11 12:06:22 +02003265 .attr_count = NFTA_SET_MAX,
3266 .policy = nft_set_policy,
3267 },
3268 [NFT_MSG_NEWSETELEM] = {
Pablo Neira Ayuso958bee12014-04-03 11:48:44 +02003269 .call_batch = nf_tables_newsetelem,
Patrick McHardy20a69342013-10-11 12:06:22 +02003270 .attr_count = NFTA_SET_ELEM_LIST_MAX,
3271 .policy = nft_set_elem_list_policy,
3272 },
3273 [NFT_MSG_GETSETELEM] = {
3274 .call = nf_tables_getsetelem,
3275 .attr_count = NFTA_SET_ELEM_LIST_MAX,
3276 .policy = nft_set_elem_list_policy,
3277 },
3278 [NFT_MSG_DELSETELEM] = {
Pablo Neira Ayuso958bee12014-04-03 11:48:44 +02003279 .call_batch = nf_tables_delsetelem,
Patrick McHardy20a69342013-10-11 12:06:22 +02003280 .attr_count = NFTA_SET_ELEM_LIST_MAX,
3281 .policy = nft_set_elem_list_policy,
3282 },
Patrick McHardy96518512013-10-14 11:00:02 +02003283};
3284
Pablo Neira Ayuso91c7b382014-04-09 11:58:08 +02003285static void nft_chain_commit_update(struct nft_trans *trans)
3286{
3287 struct nft_base_chain *basechain;
3288
3289 if (nft_trans_chain_name(trans)[0])
3290 strcpy(trans->ctx.chain->name, nft_trans_chain_name(trans));
3291
3292 if (!(trans->ctx.chain->flags & NFT_BASE_CHAIN))
3293 return;
3294
3295 basechain = nft_base_chain(trans->ctx.chain);
3296 nft_chain_stats_replace(basechain, nft_trans_chain_stats(trans));
3297
3298 switch (nft_trans_chain_policy(trans)) {
3299 case NF_DROP:
3300 case NF_ACCEPT:
3301 basechain->policy = nft_trans_chain_policy(trans);
3302 break;
3303 }
3304}
3305
Pablo Neira Ayusoc7c32e72014-04-10 00:31:10 +02003306/* Schedule objects for release via rcu to make sure no packets are accesing
3307 * removed rules.
3308 */
3309static void nf_tables_commit_release_rcu(struct rcu_head *rt)
3310{
3311 struct nft_trans *trans = container_of(rt, struct nft_trans, rcu_head);
3312
3313 switch (trans->msg_type) {
3314 case NFT_MSG_DELTABLE:
3315 nf_tables_table_destroy(&trans->ctx);
3316 break;
3317 case NFT_MSG_DELCHAIN:
3318 nf_tables_chain_destroy(trans->ctx.chain);
3319 break;
3320 case NFT_MSG_DELRULE:
3321 nf_tables_rule_destroy(&trans->ctx, nft_trans_rule(trans));
3322 break;
3323 case NFT_MSG_DELSET:
3324 nft_set_destroy(nft_trans_set(trans));
3325 break;
3326 }
3327 kfree(trans);
3328}
3329
Pablo Neira Ayuso37082f92014-04-03 11:56:37 +02003330static int nf_tables_commit(struct sk_buff *skb)
3331{
3332 struct net *net = sock_net(skb->sk);
3333 struct nft_trans *trans, *next;
Pablo Neira Ayusoa3716e72014-08-01 19:32:41 +02003334 struct nft_trans_elem *te;
Pablo Neira Ayuso37082f92014-04-03 11:56:37 +02003335
3336 /* Bump generation counter, invalidate any dump in progress */
Pablo Neira Ayuso38e029f2014-07-01 12:23:12 +02003337 while (++net->nft.base_seq == 0);
Pablo Neira Ayuso37082f92014-04-03 11:56:37 +02003338
3339 /* A new generation has just started */
3340 net->nft.gencursor = gencursor_next(net);
3341
3342 /* Make sure all packets have left the previous generation before
3343 * purging old rules.
3344 */
3345 synchronize_rcu();
3346
3347 list_for_each_entry_safe(trans, next, &net->nft.commit_list, list) {
Pablo Neira Ayusob380e5c2014-04-04 01:38:51 +02003348 switch (trans->msg_type) {
Pablo Neira Ayuso55dd6f92014-04-03 11:53:37 +02003349 case NFT_MSG_NEWTABLE:
3350 if (nft_trans_table_update(trans)) {
3351 if (!nft_trans_table_enable(trans)) {
3352 nf_tables_table_disable(trans->ctx.afi,
3353 trans->ctx.table);
3354 trans->ctx.table->flags |= NFT_TABLE_F_DORMANT;
3355 }
3356 } else {
3357 trans->ctx.table->flags &= ~NFT_TABLE_INACTIVE;
3358 }
Pablo Neira Ayuso35151d82014-05-05 17:12:40 +02003359 nf_tables_table_notify(&trans->ctx, NFT_MSG_NEWTABLE);
Pablo Neira Ayuso55dd6f92014-04-03 11:53:37 +02003360 nft_trans_destroy(trans);
3361 break;
3362 case NFT_MSG_DELTABLE:
Pablo Neira Ayuso35151d82014-05-05 17:12:40 +02003363 nf_tables_table_notify(&trans->ctx, NFT_MSG_DELTABLE);
Pablo Neira Ayuso55dd6f92014-04-03 11:53:37 +02003364 break;
Pablo Neira Ayuso91c7b382014-04-09 11:58:08 +02003365 case NFT_MSG_NEWCHAIN:
3366 if (nft_trans_chain_update(trans))
3367 nft_chain_commit_update(trans);
Pablo Neira Ayuso4fefee52014-05-23 12:39:26 +02003368 else
Pablo Neira Ayuso91c7b382014-04-09 11:58:08 +02003369 trans->ctx.chain->flags &= ~NFT_CHAIN_INACTIVE;
Pablo Neira Ayuso4fefee52014-05-23 12:39:26 +02003370
Pablo Neira Ayuso35151d82014-05-05 17:12:40 +02003371 nf_tables_chain_notify(&trans->ctx, NFT_MSG_NEWCHAIN);
Pablo Neira Ayuso91c7b382014-04-09 11:58:08 +02003372 nft_trans_destroy(trans);
3373 break;
3374 case NFT_MSG_DELCHAIN:
Pablo Neira Ayuso35151d82014-05-05 17:12:40 +02003375 nf_tables_chain_notify(&trans->ctx, NFT_MSG_DELCHAIN);
Arturo Borreroc5598792014-09-02 16:42:23 +02003376 nf_tables_unregister_hooks(trans->ctx.table,
3377 trans->ctx.chain,
3378 trans->ctx.afi->nops);
Pablo Neira Ayuso91c7b382014-04-09 11:58:08 +02003379 break;
Pablo Neira Ayusob380e5c2014-04-04 01:38:51 +02003380 case NFT_MSG_NEWRULE:
3381 nft_rule_clear(trans->ctx.net, nft_trans_rule(trans));
Pablo Neira Ayuso35151d82014-05-05 17:12:40 +02003382 nf_tables_rule_notify(&trans->ctx,
Pablo Neira Ayuso37082f92014-04-03 11:56:37 +02003383 nft_trans_rule(trans),
Pablo Neira Ayuso35151d82014-05-05 17:12:40 +02003384 NFT_MSG_NEWRULE);
Pablo Neira Ayuso37082f92014-04-03 11:56:37 +02003385 nft_trans_destroy(trans);
Pablo Neira Ayusob380e5c2014-04-04 01:38:51 +02003386 break;
3387 case NFT_MSG_DELRULE:
3388 list_del_rcu(&nft_trans_rule(trans)->list);
Pablo Neira Ayuso35151d82014-05-05 17:12:40 +02003389 nf_tables_rule_notify(&trans->ctx,
3390 nft_trans_rule(trans),
3391 NFT_MSG_DELRULE);
Pablo Neira Ayusob380e5c2014-04-04 01:38:51 +02003392 break;
Pablo Neira Ayuso958bee12014-04-03 11:48:44 +02003393 case NFT_MSG_NEWSET:
3394 nft_trans_set(trans)->flags &= ~NFT_SET_INACTIVE;
Pablo Neira Ayuso4fefee52014-05-23 12:39:26 +02003395 /* This avoids hitting -EBUSY when deleting the table
3396 * from the transaction.
3397 */
3398 if (nft_trans_set(trans)->flags & NFT_SET_ANONYMOUS &&
3399 !list_empty(&nft_trans_set(trans)->bindings))
3400 trans->ctx.table->use--;
3401
Pablo Neira Ayuso958bee12014-04-03 11:48:44 +02003402 nf_tables_set_notify(&trans->ctx, nft_trans_set(trans),
Pablo Neira Ayuso31f84412014-05-29 10:29:58 +02003403 NFT_MSG_NEWSET, GFP_KERNEL);
Pablo Neira Ayuso958bee12014-04-03 11:48:44 +02003404 nft_trans_destroy(trans);
3405 break;
3406 case NFT_MSG_DELSET:
3407 nf_tables_set_notify(&trans->ctx, nft_trans_set(trans),
Pablo Neira Ayuso31f84412014-05-29 10:29:58 +02003408 NFT_MSG_DELSET, GFP_KERNEL);
Pablo Neira Ayuso958bee12014-04-03 11:48:44 +02003409 break;
Pablo Neira Ayuso60319eb2014-04-04 03:36:42 +02003410 case NFT_MSG_NEWSETELEM:
Pablo Neira Ayuso60319eb2014-04-04 03:36:42 +02003411 nf_tables_setelem_notify(&trans->ctx,
3412 nft_trans_elem_set(trans),
3413 &nft_trans_elem(trans),
3414 NFT_MSG_NEWSETELEM, 0);
3415 nft_trans_destroy(trans);
3416 break;
3417 case NFT_MSG_DELSETELEM:
Pablo Neira Ayusoa3716e72014-08-01 19:32:41 +02003418 te = (struct nft_trans_elem *)trans->data;
3419 nf_tables_setelem_notify(&trans->ctx, te->set,
3420 &te->elem,
Pablo Neira Ayuso60319eb2014-04-04 03:36:42 +02003421 NFT_MSG_DELSETELEM, 0);
Pablo Neira Ayusoa3716e72014-08-01 19:32:41 +02003422 te->set->ops->get(te->set, &te->elem);
3423 te->set->ops->remove(te->set, &te->elem);
3424 nft_data_uninit(&te->elem.key, NFT_DATA_VALUE);
3425 if (te->elem.flags & NFT_SET_MAP) {
3426 nft_data_uninit(&te->elem.data,
3427 te->set->dtype);
3428 }
Pablo Neira Ayuso60319eb2014-04-04 03:36:42 +02003429 nft_trans_destroy(trans);
3430 break;
Pablo Neira Ayuso37082f92014-04-03 11:56:37 +02003431 }
Pablo Neira Ayuso37082f92014-04-03 11:56:37 +02003432 }
3433
Pablo Neira Ayuso37082f92014-04-03 11:56:37 +02003434 list_for_each_entry_safe(trans, next, &net->nft.commit_list, list) {
Pablo Neira Ayusoc7c32e72014-04-10 00:31:10 +02003435 list_del(&trans->list);
3436 trans->ctx.nla = NULL;
3437 call_rcu(&trans->rcu_head, nf_tables_commit_release_rcu);
Pablo Neira Ayuso37082f92014-04-03 11:56:37 +02003438 }
3439
3440 return 0;
3441}
3442
Pablo Neira Ayusoc7c32e72014-04-10 00:31:10 +02003443/* Schedule objects for release via rcu to make sure no packets are accesing
3444 * aborted rules.
3445 */
3446static void nf_tables_abort_release_rcu(struct rcu_head *rt)
3447{
3448 struct nft_trans *trans = container_of(rt, struct nft_trans, rcu_head);
3449
3450 switch (trans->msg_type) {
3451 case NFT_MSG_NEWTABLE:
3452 nf_tables_table_destroy(&trans->ctx);
3453 break;
3454 case NFT_MSG_NEWCHAIN:
3455 nf_tables_chain_destroy(trans->ctx.chain);
3456 break;
3457 case NFT_MSG_NEWRULE:
3458 nf_tables_rule_destroy(&trans->ctx, nft_trans_rule(trans));
3459 break;
3460 case NFT_MSG_NEWSET:
3461 nft_set_destroy(nft_trans_set(trans));
3462 break;
3463 }
3464 kfree(trans);
3465}
3466
Pablo Neira Ayuso37082f92014-04-03 11:56:37 +02003467static int nf_tables_abort(struct sk_buff *skb)
3468{
3469 struct net *net = sock_net(skb->sk);
3470 struct nft_trans *trans, *next;
Pablo Neira Ayuso60319eb2014-04-04 03:36:42 +02003471 struct nft_set *set;
Pablo Neira Ayuso37082f92014-04-03 11:56:37 +02003472
3473 list_for_each_entry_safe(trans, next, &net->nft.commit_list, list) {
Pablo Neira Ayusob380e5c2014-04-04 01:38:51 +02003474 switch (trans->msg_type) {
Pablo Neira Ayuso55dd6f92014-04-03 11:53:37 +02003475 case NFT_MSG_NEWTABLE:
3476 if (nft_trans_table_update(trans)) {
3477 if (nft_trans_table_enable(trans)) {
3478 nf_tables_table_disable(trans->ctx.afi,
3479 trans->ctx.table);
3480 trans->ctx.table->flags |= NFT_TABLE_F_DORMANT;
3481 }
3482 nft_trans_destroy(trans);
3483 } else {
Pablo Neira Ayusoe688a7f2014-07-01 11:49:18 +02003484 list_del_rcu(&trans->ctx.table->list);
Pablo Neira Ayuso55dd6f92014-04-03 11:53:37 +02003485 }
3486 break;
3487 case NFT_MSG_DELTABLE:
Pablo Neira Ayusoe688a7f2014-07-01 11:49:18 +02003488 list_add_tail_rcu(&trans->ctx.table->list,
3489 &trans->ctx.afi->tables);
Pablo Neira Ayuso55dd6f92014-04-03 11:53:37 +02003490 nft_trans_destroy(trans);
3491 break;
Pablo Neira Ayuso91c7b382014-04-09 11:58:08 +02003492 case NFT_MSG_NEWCHAIN:
3493 if (nft_trans_chain_update(trans)) {
3494 if (nft_trans_chain_stats(trans))
3495 free_percpu(nft_trans_chain_stats(trans));
3496
3497 nft_trans_destroy(trans);
3498 } else {
Pablo Neira Ayuso4fefee52014-05-23 12:39:26 +02003499 trans->ctx.table->use--;
Pablo Neira Ayusoe688a7f2014-07-01 11:49:18 +02003500 list_del_rcu(&trans->ctx.chain->list);
Arturo Borreroc5598792014-09-02 16:42:23 +02003501 nf_tables_unregister_hooks(trans->ctx.table,
3502 trans->ctx.chain,
3503 trans->ctx.afi->nops);
Pablo Neira Ayuso91c7b382014-04-09 11:58:08 +02003504 }
3505 break;
3506 case NFT_MSG_DELCHAIN:
Pablo Neira Ayuso4fefee52014-05-23 12:39:26 +02003507 trans->ctx.table->use++;
Pablo Neira Ayusoe688a7f2014-07-01 11:49:18 +02003508 list_add_tail_rcu(&trans->ctx.chain->list,
3509 &trans->ctx.table->chains);
Pablo Neira Ayuso91c7b382014-04-09 11:58:08 +02003510 nft_trans_destroy(trans);
3511 break;
Pablo Neira Ayusob380e5c2014-04-04 01:38:51 +02003512 case NFT_MSG_NEWRULE:
Pablo Neira Ayuso4fefee52014-05-23 12:39:26 +02003513 trans->ctx.chain->use--;
Pablo Neira Ayusob380e5c2014-04-04 01:38:51 +02003514 list_del_rcu(&nft_trans_rule(trans)->list);
3515 break;
3516 case NFT_MSG_DELRULE:
Pablo Neira Ayuso4fefee52014-05-23 12:39:26 +02003517 trans->ctx.chain->use++;
Pablo Neira Ayusob380e5c2014-04-04 01:38:51 +02003518 nft_rule_clear(trans->ctx.net, nft_trans_rule(trans));
Pablo Neira Ayuso37082f92014-04-03 11:56:37 +02003519 nft_trans_destroy(trans);
Pablo Neira Ayusob380e5c2014-04-04 01:38:51 +02003520 break;
Pablo Neira Ayuso958bee12014-04-03 11:48:44 +02003521 case NFT_MSG_NEWSET:
Pablo Neira Ayuso4fefee52014-05-23 12:39:26 +02003522 trans->ctx.table->use--;
Pablo Neira Ayusoe688a7f2014-07-01 11:49:18 +02003523 list_del_rcu(&nft_trans_set(trans)->list);
Pablo Neira Ayuso958bee12014-04-03 11:48:44 +02003524 break;
3525 case NFT_MSG_DELSET:
Pablo Neira Ayuso4fefee52014-05-23 12:39:26 +02003526 trans->ctx.table->use++;
Pablo Neira Ayusoe688a7f2014-07-01 11:49:18 +02003527 list_add_tail_rcu(&nft_trans_set(trans)->list,
3528 &trans->ctx.table->sets);
Pablo Neira Ayuso958bee12014-04-03 11:48:44 +02003529 nft_trans_destroy(trans);
3530 break;
Pablo Neira Ayuso60319eb2014-04-04 03:36:42 +02003531 case NFT_MSG_NEWSETELEM:
Pablo Neira Ayuso4fefee52014-05-23 12:39:26 +02003532 nft_trans_elem_set(trans)->nelems--;
Pablo Neira Ayuso60319eb2014-04-04 03:36:42 +02003533 set = nft_trans_elem_set(trans);
3534 set->ops->get(set, &nft_trans_elem(trans));
3535 set->ops->remove(set, &nft_trans_elem(trans));
3536 nft_trans_destroy(trans);
3537 break;
3538 case NFT_MSG_DELSETELEM:
Pablo Neira Ayuso4fefee52014-05-23 12:39:26 +02003539 nft_trans_elem_set(trans)->nelems++;
Pablo Neira Ayuso60319eb2014-04-04 03:36:42 +02003540 nft_trans_destroy(trans);
3541 break;
Pablo Neira Ayuso37082f92014-04-03 11:56:37 +02003542 }
Pablo Neira Ayuso37082f92014-04-03 11:56:37 +02003543 }
3544
Pablo Neira Ayusoa1cee072014-05-23 11:09:42 +02003545 list_for_each_entry_safe_reverse(trans, next,
3546 &net->nft.commit_list, list) {
Pablo Neira Ayusoc7c32e72014-04-10 00:31:10 +02003547 list_del(&trans->list);
3548 trans->ctx.nla = NULL;
3549 call_rcu(&trans->rcu_head, nf_tables_abort_release_rcu);
Pablo Neira Ayuso37082f92014-04-03 11:56:37 +02003550 }
3551
3552 return 0;
3553}
3554
Patrick McHardy96518512013-10-14 11:00:02 +02003555static const struct nfnetlink_subsystem nf_tables_subsys = {
3556 .name = "nf_tables",
3557 .subsys_id = NFNL_SUBSYS_NFTABLES,
3558 .cb_count = NFT_MSG_MAX,
3559 .cb = nf_tables_cb,
Pablo Neira Ayuso0628b122013-10-14 11:05:33 +02003560 .commit = nf_tables_commit,
3561 .abort = nf_tables_abort,
Patrick McHardy96518512013-10-14 11:00:02 +02003562};
3563
Patrick McHardy20a69342013-10-11 12:06:22 +02003564/*
3565 * Loop detection - walk through the ruleset beginning at the destination chain
3566 * of a new jump until either the source chain is reached (loop) or all
3567 * reachable chains have been traversed.
3568 *
3569 * The loop check is performed whenever a new jump verdict is added to an
3570 * expression or verdict map or a verdict map is bound to a new chain.
3571 */
3572
3573static int nf_tables_check_loops(const struct nft_ctx *ctx,
3574 const struct nft_chain *chain);
3575
3576static int nf_tables_loop_check_setelem(const struct nft_ctx *ctx,
3577 const struct nft_set *set,
3578 const struct nft_set_iter *iter,
3579 const struct nft_set_elem *elem)
3580{
Pablo Neira Ayuso62f9c8b2014-02-07 14:45:01 +01003581 if (elem->flags & NFT_SET_ELEM_INTERVAL_END)
3582 return 0;
3583
Patrick McHardy20a69342013-10-11 12:06:22 +02003584 switch (elem->data.verdict) {
3585 case NFT_JUMP:
3586 case NFT_GOTO:
3587 return nf_tables_check_loops(ctx, elem->data.chain);
3588 default:
3589 return 0;
3590 }
3591}
3592
3593static int nf_tables_check_loops(const struct nft_ctx *ctx,
3594 const struct nft_chain *chain)
3595{
3596 const struct nft_rule *rule;
3597 const struct nft_expr *expr, *last;
Patrick McHardy20a69342013-10-11 12:06:22 +02003598 const struct nft_set *set;
3599 struct nft_set_binding *binding;
3600 struct nft_set_iter iter;
Patrick McHardy20a69342013-10-11 12:06:22 +02003601
3602 if (ctx->chain == chain)
3603 return -ELOOP;
3604
3605 list_for_each_entry(rule, &chain->rules, list) {
3606 nft_rule_for_each_expr(expr, last, rule) {
Pablo Neira Ayuso0ca743a2013-10-14 00:06:06 +02003607 const struct nft_data *data = NULL;
3608 int err;
3609
3610 if (!expr->ops->validate)
Patrick McHardy20a69342013-10-11 12:06:22 +02003611 continue;
3612
Pablo Neira Ayuso0ca743a2013-10-14 00:06:06 +02003613 err = expr->ops->validate(ctx, expr, &data);
3614 if (err < 0)
3615 return err;
3616
Patrick McHardy20a69342013-10-11 12:06:22 +02003617 if (data == NULL)
Pablo Neira Ayuso0ca743a2013-10-14 00:06:06 +02003618 continue;
Patrick McHardy20a69342013-10-11 12:06:22 +02003619
3620 switch (data->verdict) {
3621 case NFT_JUMP:
3622 case NFT_GOTO:
3623 err = nf_tables_check_loops(ctx, data->chain);
3624 if (err < 0)
3625 return err;
3626 default:
3627 break;
3628 }
3629 }
3630 }
3631
3632 list_for_each_entry(set, &ctx->table->sets, list) {
3633 if (!(set->flags & NFT_SET_MAP) ||
3634 set->dtype != NFT_DATA_VERDICT)
3635 continue;
3636
3637 list_for_each_entry(binding, &set->bindings, list) {
3638 if (binding->chain != chain)
3639 continue;
3640
3641 iter.skip = 0;
3642 iter.count = 0;
3643 iter.err = 0;
3644 iter.fn = nf_tables_loop_check_setelem;
3645
3646 set->ops->walk(ctx, set, &iter);
3647 if (iter.err < 0)
3648 return iter.err;
3649 }
3650 }
3651
3652 return 0;
3653}
3654
Patrick McHardy96518512013-10-14 11:00:02 +02003655/**
3656 * nft_validate_input_register - validate an expressions' input register
3657 *
3658 * @reg: the register number
3659 *
3660 * Validate that the input register is one of the general purpose
3661 * registers.
3662 */
3663int nft_validate_input_register(enum nft_registers reg)
3664{
3665 if (reg <= NFT_REG_VERDICT)
3666 return -EINVAL;
3667 if (reg > NFT_REG_MAX)
3668 return -ERANGE;
3669 return 0;
3670}
3671EXPORT_SYMBOL_GPL(nft_validate_input_register);
3672
3673/**
3674 * nft_validate_output_register - validate an expressions' output register
3675 *
3676 * @reg: the register number
3677 *
3678 * Validate that the output register is one of the general purpose
3679 * registers or the verdict register.
3680 */
3681int nft_validate_output_register(enum nft_registers reg)
3682{
3683 if (reg < NFT_REG_VERDICT)
3684 return -EINVAL;
3685 if (reg > NFT_REG_MAX)
3686 return -ERANGE;
3687 return 0;
3688}
3689EXPORT_SYMBOL_GPL(nft_validate_output_register);
3690
3691/**
3692 * nft_validate_data_load - validate an expressions' data load
3693 *
3694 * @ctx: context of the expression performing the load
3695 * @reg: the destination register number
3696 * @data: the data to load
3697 * @type: the data type
3698 *
3699 * Validate that a data load uses the appropriate data type for
3700 * the destination register. A value of NULL for the data means
3701 * that its runtime gathered data, which is always of type
3702 * NFT_DATA_VALUE.
3703 */
3704int nft_validate_data_load(const struct nft_ctx *ctx, enum nft_registers reg,
3705 const struct nft_data *data,
3706 enum nft_data_types type)
3707{
Patrick McHardy20a69342013-10-11 12:06:22 +02003708 int err;
3709
Patrick McHardy96518512013-10-14 11:00:02 +02003710 switch (reg) {
3711 case NFT_REG_VERDICT:
3712 if (data == NULL || type != NFT_DATA_VERDICT)
3713 return -EINVAL;
Patrick McHardy20a69342013-10-11 12:06:22 +02003714
3715 if (data->verdict == NFT_GOTO || data->verdict == NFT_JUMP) {
3716 err = nf_tables_check_loops(ctx, data->chain);
3717 if (err < 0)
3718 return err;
3719
3720 if (ctx->chain->level + 1 > data->chain->level) {
3721 if (ctx->chain->level + 1 == NFT_JUMP_STACK_SIZE)
3722 return -EMLINK;
3723 data->chain->level = ctx->chain->level + 1;
3724 }
3725 }
3726
Patrick McHardy96518512013-10-14 11:00:02 +02003727 return 0;
3728 default:
3729 if (data != NULL && type != NFT_DATA_VALUE)
3730 return -EINVAL;
3731 return 0;
3732 }
3733}
3734EXPORT_SYMBOL_GPL(nft_validate_data_load);
3735
3736static const struct nla_policy nft_verdict_policy[NFTA_VERDICT_MAX + 1] = {
3737 [NFTA_VERDICT_CODE] = { .type = NLA_U32 },
3738 [NFTA_VERDICT_CHAIN] = { .type = NLA_STRING,
3739 .len = NFT_CHAIN_MAXNAMELEN - 1 },
3740};
3741
3742static int nft_verdict_init(const struct nft_ctx *ctx, struct nft_data *data,
3743 struct nft_data_desc *desc, const struct nlattr *nla)
3744{
3745 struct nlattr *tb[NFTA_VERDICT_MAX + 1];
3746 struct nft_chain *chain;
3747 int err;
3748
3749 err = nla_parse_nested(tb, NFTA_VERDICT_MAX, nla, nft_verdict_policy);
3750 if (err < 0)
3751 return err;
3752
3753 if (!tb[NFTA_VERDICT_CODE])
3754 return -EINVAL;
3755 data->verdict = ntohl(nla_get_be32(tb[NFTA_VERDICT_CODE]));
3756
3757 switch (data->verdict) {
Patrick McHardye0abdad2014-02-18 18:06:50 +00003758 default:
3759 switch (data->verdict & NF_VERDICT_MASK) {
3760 case NF_ACCEPT:
3761 case NF_DROP:
3762 case NF_QUEUE:
3763 break;
3764 default:
3765 return -EINVAL;
3766 }
3767 /* fall through */
Patrick McHardy96518512013-10-14 11:00:02 +02003768 case NFT_CONTINUE:
3769 case NFT_BREAK:
3770 case NFT_RETURN:
3771 desc->len = sizeof(data->verdict);
3772 break;
3773 case NFT_JUMP:
3774 case NFT_GOTO:
3775 if (!tb[NFTA_VERDICT_CHAIN])
3776 return -EINVAL;
3777 chain = nf_tables_chain_lookup(ctx->table,
3778 tb[NFTA_VERDICT_CHAIN]);
3779 if (IS_ERR(chain))
3780 return PTR_ERR(chain);
3781 if (chain->flags & NFT_BASE_CHAIN)
3782 return -EOPNOTSUPP;
3783
Patrick McHardy96518512013-10-14 11:00:02 +02003784 chain->use++;
3785 data->chain = chain;
3786 desc->len = sizeof(data);
3787 break;
Patrick McHardy96518512013-10-14 11:00:02 +02003788 }
3789
3790 desc->type = NFT_DATA_VERDICT;
3791 return 0;
3792}
3793
3794static void nft_verdict_uninit(const struct nft_data *data)
3795{
3796 switch (data->verdict) {
3797 case NFT_JUMP:
3798 case NFT_GOTO:
3799 data->chain->use--;
3800 break;
3801 }
3802}
3803
3804static int nft_verdict_dump(struct sk_buff *skb, const struct nft_data *data)
3805{
3806 struct nlattr *nest;
3807
3808 nest = nla_nest_start(skb, NFTA_DATA_VERDICT);
3809 if (!nest)
3810 goto nla_put_failure;
3811
3812 if (nla_put_be32(skb, NFTA_VERDICT_CODE, htonl(data->verdict)))
3813 goto nla_put_failure;
3814
3815 switch (data->verdict) {
3816 case NFT_JUMP:
3817 case NFT_GOTO:
3818 if (nla_put_string(skb, NFTA_VERDICT_CHAIN, data->chain->name))
3819 goto nla_put_failure;
3820 }
3821 nla_nest_end(skb, nest);
3822 return 0;
3823
3824nla_put_failure:
3825 return -1;
3826}
3827
3828static int nft_value_init(const struct nft_ctx *ctx, struct nft_data *data,
3829 struct nft_data_desc *desc, const struct nlattr *nla)
3830{
3831 unsigned int len;
3832
3833 len = nla_len(nla);
3834 if (len == 0)
3835 return -EINVAL;
3836 if (len > sizeof(data->data))
3837 return -EOVERFLOW;
3838
3839 nla_memcpy(data->data, nla, sizeof(data->data));
3840 desc->type = NFT_DATA_VALUE;
3841 desc->len = len;
3842 return 0;
3843}
3844
3845static int nft_value_dump(struct sk_buff *skb, const struct nft_data *data,
3846 unsigned int len)
3847{
3848 return nla_put(skb, NFTA_DATA_VALUE, len, data->data);
3849}
3850
3851static const struct nla_policy nft_data_policy[NFTA_DATA_MAX + 1] = {
3852 [NFTA_DATA_VALUE] = { .type = NLA_BINARY,
3853 .len = FIELD_SIZEOF(struct nft_data, data) },
3854 [NFTA_DATA_VERDICT] = { .type = NLA_NESTED },
3855};
3856
3857/**
3858 * nft_data_init - parse nf_tables data netlink attributes
3859 *
3860 * @ctx: context of the expression using the data
3861 * @data: destination struct nft_data
3862 * @desc: data description
3863 * @nla: netlink attribute containing data
3864 *
3865 * Parse the netlink data attributes and initialize a struct nft_data.
3866 * The type and length of data are returned in the data description.
3867 *
3868 * The caller can indicate that it only wants to accept data of type
3869 * NFT_DATA_VALUE by passing NULL for the ctx argument.
3870 */
3871int nft_data_init(const struct nft_ctx *ctx, struct nft_data *data,
3872 struct nft_data_desc *desc, const struct nlattr *nla)
3873{
3874 struct nlattr *tb[NFTA_DATA_MAX + 1];
3875 int err;
3876
3877 err = nla_parse_nested(tb, NFTA_DATA_MAX, nla, nft_data_policy);
3878 if (err < 0)
3879 return err;
3880
3881 if (tb[NFTA_DATA_VALUE])
3882 return nft_value_init(ctx, data, desc, tb[NFTA_DATA_VALUE]);
3883 if (tb[NFTA_DATA_VERDICT] && ctx != NULL)
3884 return nft_verdict_init(ctx, data, desc, tb[NFTA_DATA_VERDICT]);
3885 return -EINVAL;
3886}
3887EXPORT_SYMBOL_GPL(nft_data_init);
3888
3889/**
3890 * nft_data_uninit - release a nft_data item
3891 *
3892 * @data: struct nft_data to release
3893 * @type: type of data
3894 *
3895 * Release a nft_data item. NFT_DATA_VALUE types can be silently discarded,
3896 * all others need to be released by calling this function.
3897 */
3898void nft_data_uninit(const struct nft_data *data, enum nft_data_types type)
3899{
3900 switch (type) {
3901 case NFT_DATA_VALUE:
3902 return;
3903 case NFT_DATA_VERDICT:
3904 return nft_verdict_uninit(data);
3905 default:
3906 WARN_ON(1);
3907 }
3908}
3909EXPORT_SYMBOL_GPL(nft_data_uninit);
3910
3911int nft_data_dump(struct sk_buff *skb, int attr, const struct nft_data *data,
3912 enum nft_data_types type, unsigned int len)
3913{
3914 struct nlattr *nest;
3915 int err;
3916
3917 nest = nla_nest_start(skb, attr);
3918 if (nest == NULL)
3919 return -1;
3920
3921 switch (type) {
3922 case NFT_DATA_VALUE:
3923 err = nft_value_dump(skb, data, len);
3924 break;
3925 case NFT_DATA_VERDICT:
3926 err = nft_verdict_dump(skb, data);
3927 break;
3928 default:
3929 err = -EINVAL;
3930 WARN_ON(1);
3931 }
3932
3933 nla_nest_end(skb, nest);
3934 return err;
3935}
3936EXPORT_SYMBOL_GPL(nft_data_dump);
3937
Pablo Neira Ayuso99633ab2013-10-10 23:28:33 +02003938static int nf_tables_init_net(struct net *net)
3939{
3940 INIT_LIST_HEAD(&net->nft.af_info);
Pablo Neira Ayuso0628b122013-10-14 11:05:33 +02003941 INIT_LIST_HEAD(&net->nft.commit_list);
Pablo Neira Ayuso38e029f2014-07-01 12:23:12 +02003942 net->nft.base_seq = 1;
Pablo Neira Ayuso99633ab2013-10-10 23:28:33 +02003943 return 0;
3944}
3945
3946static struct pernet_operations nf_tables_net_ops = {
3947 .init = nf_tables_init_net,
3948};
3949
Patrick McHardy96518512013-10-14 11:00:02 +02003950static int __init nf_tables_module_init(void)
3951{
3952 int err;
3953
3954 info = kmalloc(sizeof(struct nft_expr_info) * NFT_RULE_MAXEXPRS,
3955 GFP_KERNEL);
3956 if (info == NULL) {
3957 err = -ENOMEM;
3958 goto err1;
3959 }
3960
3961 err = nf_tables_core_module_init();
3962 if (err < 0)
3963 goto err2;
3964
3965 err = nfnetlink_subsys_register(&nf_tables_subsys);
3966 if (err < 0)
3967 goto err3;
3968
3969 pr_info("nf_tables: (c) 2007-2009 Patrick McHardy <kaber@trash.net>\n");
Pablo Neira Ayuso99633ab2013-10-10 23:28:33 +02003970 return register_pernet_subsys(&nf_tables_net_ops);
Patrick McHardy96518512013-10-14 11:00:02 +02003971err3:
3972 nf_tables_core_module_exit();
3973err2:
3974 kfree(info);
3975err1:
3976 return err;
3977}
3978
3979static void __exit nf_tables_module_exit(void)
3980{
Pablo Neira Ayuso99633ab2013-10-10 23:28:33 +02003981 unregister_pernet_subsys(&nf_tables_net_ops);
Patrick McHardy96518512013-10-14 11:00:02 +02003982 nfnetlink_subsys_unregister(&nf_tables_subsys);
3983 nf_tables_core_module_exit();
3984 kfree(info);
3985}
3986
3987module_init(nf_tables_module_init);
3988module_exit(nf_tables_module_exit);
3989
3990MODULE_LICENSE("GPL");
3991MODULE_AUTHOR("Patrick McHardy <kaber@trash.net>");
3992MODULE_ALIAS_NFNL_SUBSYS(NFNL_SUBSYS_NFTABLES);