blob: 86d055a51f0bc2709b4c286ddfb18bc175f966be [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 Ayuso99633ab2013-10-10 23:28:33 +020038 list_add_tail(&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);
54 list_del(&afi->list);
55 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{
99 ctx->net = sock_net(skb->sk);
100 ctx->skb = skb;
101 ctx->nlh = nlh;
102 ctx->afi = afi;
103 ctx->table = table;
104 ctx->chain = chain;
105 ctx->nla = nla;
106}
107
Pablo Neira Ayusob380e5c2014-04-04 01:38:51 +0200108static struct nft_trans *nft_trans_alloc(struct nft_ctx *ctx, int msg_type,
109 u32 size)
Pablo Neira Ayuso1081d112014-04-04 01:24:07 +0200110{
111 struct nft_trans *trans;
112
113 trans = kzalloc(sizeof(struct nft_trans) + size, GFP_KERNEL);
114 if (trans == NULL)
115 return NULL;
116
Pablo Neira Ayusob380e5c2014-04-04 01:38:51 +0200117 trans->msg_type = msg_type;
Pablo Neira Ayuso1081d112014-04-04 01:24:07 +0200118 trans->ctx = *ctx;
119
120 return trans;
121}
122
123static void nft_trans_destroy(struct nft_trans *trans)
124{
125 list_del(&trans->list);
126 kfree(trans);
127}
128
Patrick McHardy96518512013-10-14 11:00:02 +0200129/*
130 * Tables
131 */
132
133static struct nft_table *nft_table_lookup(const struct nft_af_info *afi,
134 const struct nlattr *nla)
135{
136 struct nft_table *table;
137
138 list_for_each_entry(table, &afi->tables, list) {
139 if (!nla_strcmp(nla, table->name))
140 return table;
141 }
142 return NULL;
143}
144
145static struct nft_table *nf_tables_table_lookup(const struct nft_af_info *afi,
Pablo Neira Ayuso93707612013-10-10 23:21:26 +0200146 const struct nlattr *nla)
Patrick McHardy96518512013-10-14 11:00:02 +0200147{
148 struct nft_table *table;
149
150 if (nla == NULL)
151 return ERR_PTR(-EINVAL);
152
153 table = nft_table_lookup(afi, nla);
154 if (table != NULL)
155 return table;
156
Patrick McHardy96518512013-10-14 11:00:02 +0200157 return ERR_PTR(-ENOENT);
158}
159
160static inline u64 nf_tables_alloc_handle(struct nft_table *table)
161{
162 return ++table->hgenerator;
163}
164
Patrick McHardy2a37d752014-01-09 18:42:37 +0000165static const struct nf_chain_type *chain_type[AF_MAX][NFT_CHAIN_T_MAX];
Pablo Neira Ayuso93707612013-10-10 23:21:26 +0200166
Patrick McHardy2a37d752014-01-09 18:42:37 +0000167static const struct nf_chain_type *
Patrick McHardybaae3e62014-01-09 18:42:34 +0000168__nf_tables_chain_type_lookup(int family, const struct nlattr *nla)
Pablo Neira Ayuso93707612013-10-10 23:21:26 +0200169{
170 int i;
171
Patrick McHardybaae3e62014-01-09 18:42:34 +0000172 for (i = 0; i < NFT_CHAIN_T_MAX; i++) {
Pablo Neira Ayuso93707612013-10-10 23:21:26 +0200173 if (chain_type[family][i] != NULL &&
174 !nla_strcmp(nla, chain_type[family][i]->name))
Patrick McHardybaae3e62014-01-09 18:42:34 +0000175 return chain_type[family][i];
Pablo Neira Ayuso93707612013-10-10 23:21:26 +0200176 }
Patrick McHardybaae3e62014-01-09 18:42:34 +0000177 return NULL;
Pablo Neira Ayuso93707612013-10-10 23:21:26 +0200178}
179
Patrick McHardy2a37d752014-01-09 18:42:37 +0000180static const struct nf_chain_type *
Patrick McHardybaae3e62014-01-09 18:42:34 +0000181nf_tables_chain_type_lookup(const struct nft_af_info *afi,
182 const struct nlattr *nla,
183 bool autoload)
Pablo Neira Ayuso93707612013-10-10 23:21:26 +0200184{
Patrick McHardy2a37d752014-01-09 18:42:37 +0000185 const struct nf_chain_type *type;
Pablo Neira Ayuso93707612013-10-10 23:21:26 +0200186
187 type = __nf_tables_chain_type_lookup(afi->family, nla);
Patrick McHardy93b08062014-01-09 18:42:36 +0000188 if (type != NULL)
189 return type;
Pablo Neira Ayuso93707612013-10-10 23:21:26 +0200190#ifdef CONFIG_MODULES
Patrick McHardy93b08062014-01-09 18:42:36 +0000191 if (autoload) {
Pablo Neira Ayuso93707612013-10-10 23:21:26 +0200192 nfnl_unlock(NFNL_SUBSYS_NFTABLES);
193 request_module("nft-chain-%u-%*.s", afi->family,
194 nla_len(nla)-1, (const char *)nla_data(nla));
195 nfnl_lock(NFNL_SUBSYS_NFTABLES);
196 type = __nf_tables_chain_type_lookup(afi->family, nla);
Patrick McHardy93b08062014-01-09 18:42:36 +0000197 if (type != NULL)
198 return ERR_PTR(-EAGAIN);
Pablo Neira Ayuso93707612013-10-10 23:21:26 +0200199 }
200#endif
Patrick McHardy93b08062014-01-09 18:42:36 +0000201 return ERR_PTR(-ENOENT);
Pablo Neira Ayuso93707612013-10-10 23:21:26 +0200202}
203
Patrick McHardy96518512013-10-14 11:00:02 +0200204static const struct nla_policy nft_table_policy[NFTA_TABLE_MAX + 1] = {
205 [NFTA_TABLE_NAME] = { .type = NLA_STRING },
Pablo Neira Ayuso9ddf6322013-10-10 13:26:33 +0200206 [NFTA_TABLE_FLAGS] = { .type = NLA_U32 },
Patrick McHardy96518512013-10-14 11:00:02 +0200207};
208
209static int nf_tables_fill_table_info(struct sk_buff *skb, u32 portid, u32 seq,
210 int event, u32 flags, int family,
211 const struct nft_table *table)
212{
213 struct nlmsghdr *nlh;
214 struct nfgenmsg *nfmsg;
215
216 event |= NFNL_SUBSYS_NFTABLES << 8;
217 nlh = nlmsg_put(skb, portid, seq, event, sizeof(struct nfgenmsg), flags);
218 if (nlh == NULL)
219 goto nla_put_failure;
220
221 nfmsg = nlmsg_data(nlh);
222 nfmsg->nfgen_family = family;
223 nfmsg->version = NFNETLINK_V0;
224 nfmsg->res_id = 0;
225
Pablo Neira Ayuso9ddf6322013-10-10 13:26:33 +0200226 if (nla_put_string(skb, NFTA_TABLE_NAME, table->name) ||
Tomasz Bursztykad8bcc7682013-12-12 15:00:42 +0200227 nla_put_be32(skb, NFTA_TABLE_FLAGS, htonl(table->flags)) ||
228 nla_put_be32(skb, NFTA_TABLE_USE, htonl(table->use)))
Patrick McHardy96518512013-10-14 11:00:02 +0200229 goto nla_put_failure;
230
231 return nlmsg_end(skb, nlh);
232
233nla_put_failure:
234 nlmsg_trim(skb, nlh);
235 return -1;
236}
237
Pablo Neira Ayuso35151d82014-05-05 17:12:40 +0200238static int nf_tables_table_notify(const struct nft_ctx *ctx, int event)
Patrick McHardy96518512013-10-14 11:00:02 +0200239{
240 struct sk_buff *skb;
Pablo Neira Ayuso35151d82014-05-05 17:12:40 +0200241 u32 portid = NETLINK_CB(ctx->skb).portid;
242 u32 seq = ctx->nlh->nlmsg_seq;
243 struct net *net = sock_net(ctx->skb->sk);
Patrick McHardy96518512013-10-14 11:00:02 +0200244 bool report;
245 int err;
246
Pablo Neira Ayuso35151d82014-05-05 17:12:40 +0200247 report = nlmsg_report(ctx->nlh);
Patrick McHardy96518512013-10-14 11:00:02 +0200248 if (!report && !nfnetlink_has_listeners(net, NFNLGRP_NFTABLES))
249 return 0;
250
251 err = -ENOBUFS;
252 skb = nlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL);
253 if (skb == NULL)
254 goto err;
255
256 err = nf_tables_fill_table_info(skb, portid, seq, event, 0,
Pablo Neira Ayuso35151d82014-05-05 17:12:40 +0200257 ctx->afi->family, ctx->table);
Patrick McHardy96518512013-10-14 11:00:02 +0200258 if (err < 0) {
259 kfree_skb(skb);
260 goto err;
261 }
262
263 err = nfnetlink_send(skb, net, portid, NFNLGRP_NFTABLES, report,
264 GFP_KERNEL);
265err:
266 if (err < 0)
267 nfnetlink_set_err(net, portid, NFNLGRP_NFTABLES, err);
268 return err;
269}
270
271static int nf_tables_dump_tables(struct sk_buff *skb,
272 struct netlink_callback *cb)
273{
274 const struct nfgenmsg *nfmsg = nlmsg_data(cb->nlh);
275 const struct nft_af_info *afi;
276 const struct nft_table *table;
277 unsigned int idx = 0, s_idx = cb->args[0];
Pablo Neira Ayuso99633ab2013-10-10 23:28:33 +0200278 struct net *net = sock_net(skb->sk);
Patrick McHardy96518512013-10-14 11:00:02 +0200279 int family = nfmsg->nfgen_family;
280
Pablo Neira Ayuso99633ab2013-10-10 23:28:33 +0200281 list_for_each_entry(afi, &net->nft.af_info, list) {
Patrick McHardy96518512013-10-14 11:00:02 +0200282 if (family != NFPROTO_UNSPEC && family != afi->family)
283 continue;
284
285 list_for_each_entry(table, &afi->tables, list) {
286 if (idx < s_idx)
287 goto cont;
288 if (idx > s_idx)
289 memset(&cb->args[1], 0,
290 sizeof(cb->args) - sizeof(cb->args[0]));
291 if (nf_tables_fill_table_info(skb,
292 NETLINK_CB(cb->skb).portid,
293 cb->nlh->nlmsg_seq,
294 NFT_MSG_NEWTABLE,
295 NLM_F_MULTI,
296 afi->family, table) < 0)
297 goto done;
298cont:
299 idx++;
300 }
301 }
302done:
303 cb->args[0] = idx;
304 return skb->len;
305}
306
Pablo Neira Ayuso55dd6f92014-04-03 11:53:37 +0200307/* Internal table flags */
308#define NFT_TABLE_INACTIVE (1 << 15)
309
Patrick McHardy96518512013-10-14 11:00:02 +0200310static int nf_tables_gettable(struct sock *nlsk, struct sk_buff *skb,
311 const struct nlmsghdr *nlh,
312 const struct nlattr * const nla[])
313{
314 const struct nfgenmsg *nfmsg = nlmsg_data(nlh);
315 const struct nft_af_info *afi;
316 const struct nft_table *table;
317 struct sk_buff *skb2;
Pablo Neira Ayuso99633ab2013-10-10 23:28:33 +0200318 struct net *net = sock_net(skb->sk);
Patrick McHardy96518512013-10-14 11:00:02 +0200319 int family = nfmsg->nfgen_family;
320 int err;
321
322 if (nlh->nlmsg_flags & NLM_F_DUMP) {
323 struct netlink_dump_control c = {
324 .dump = nf_tables_dump_tables,
325 };
326 return netlink_dump_start(nlsk, skb, nlh, &c);
327 }
328
Pablo Neira Ayuso99633ab2013-10-10 23:28:33 +0200329 afi = nf_tables_afinfo_lookup(net, family, false);
Patrick McHardy96518512013-10-14 11:00:02 +0200330 if (IS_ERR(afi))
331 return PTR_ERR(afi);
332
Pablo Neira Ayuso93707612013-10-10 23:21:26 +0200333 table = nf_tables_table_lookup(afi, nla[NFTA_TABLE_NAME]);
Patrick McHardy96518512013-10-14 11:00:02 +0200334 if (IS_ERR(table))
335 return PTR_ERR(table);
Pablo Neira Ayuso55dd6f92014-04-03 11:53:37 +0200336 if (table->flags & NFT_TABLE_INACTIVE)
337 return -ENOENT;
Patrick McHardy96518512013-10-14 11:00:02 +0200338
339 skb2 = alloc_skb(NLMSG_GOODSIZE, GFP_KERNEL);
340 if (!skb2)
341 return -ENOMEM;
342
343 err = nf_tables_fill_table_info(skb2, NETLINK_CB(skb).portid,
344 nlh->nlmsg_seq, NFT_MSG_NEWTABLE, 0,
345 family, table);
346 if (err < 0)
347 goto err;
348
349 return nlmsg_unicast(nlsk, skb2, NETLINK_CB(skb).portid);
350
351err:
352 kfree_skb(skb2);
353 return err;
354}
355
Patrick McHardy115a60b2014-01-03 12:16:15 +0000356static int nf_tables_table_enable(const struct nft_af_info *afi,
357 struct nft_table *table)
Pablo Neira Ayuso9ddf6322013-10-10 13:26:33 +0200358{
359 struct nft_chain *chain;
360 int err, i = 0;
361
362 list_for_each_entry(chain, &table->chains, list) {
Pablo Neira Ayusod2012972013-12-27 10:44:23 +0100363 if (!(chain->flags & NFT_BASE_CHAIN))
364 continue;
365
Patrick McHardy115a60b2014-01-03 12:16:15 +0000366 err = nf_register_hooks(nft_base_chain(chain)->ops, afi->nops);
Pablo Neira Ayuso9ddf6322013-10-10 13:26:33 +0200367 if (err < 0)
368 goto err;
369
370 i++;
371 }
372 return 0;
373err:
374 list_for_each_entry(chain, &table->chains, list) {
Pablo Neira Ayusod2012972013-12-27 10:44:23 +0100375 if (!(chain->flags & NFT_BASE_CHAIN))
376 continue;
377
Pablo Neira Ayuso9ddf6322013-10-10 13:26:33 +0200378 if (i-- <= 0)
379 break;
380
Patrick McHardy115a60b2014-01-03 12:16:15 +0000381 nf_unregister_hooks(nft_base_chain(chain)->ops, afi->nops);
Pablo Neira Ayuso9ddf6322013-10-10 13:26:33 +0200382 }
383 return err;
384}
385
Pablo Neira Ayusof75edf52014-03-30 14:04:52 +0200386static void nf_tables_table_disable(const struct nft_af_info *afi,
Patrick McHardy115a60b2014-01-03 12:16:15 +0000387 struct nft_table *table)
Pablo Neira Ayuso9ddf6322013-10-10 13:26:33 +0200388{
389 struct nft_chain *chain;
390
Pablo Neira Ayusod2012972013-12-27 10:44:23 +0100391 list_for_each_entry(chain, &table->chains, list) {
392 if (chain->flags & NFT_BASE_CHAIN)
Patrick McHardy115a60b2014-01-03 12:16:15 +0000393 nf_unregister_hooks(nft_base_chain(chain)->ops,
394 afi->nops);
Pablo Neira Ayusod2012972013-12-27 10:44:23 +0100395 }
Pablo Neira Ayuso9ddf6322013-10-10 13:26:33 +0200396}
397
Pablo Neira Ayusoe1aaca92014-03-30 14:04:53 +0200398static int nf_tables_updtable(struct nft_ctx *ctx)
Pablo Neira Ayuso9ddf6322013-10-10 13:26:33 +0200399{
Pablo Neira Ayuso55dd6f92014-04-03 11:53:37 +0200400 struct nft_trans *trans;
Pablo Neira Ayusoe1aaca92014-03-30 14:04:53 +0200401 u32 flags;
Pablo Neira Ayuso55dd6f92014-04-03 11:53:37 +0200402 int ret = 0;
Pablo Neira Ayuso9ddf6322013-10-10 13:26:33 +0200403
Pablo Neira Ayusoe1aaca92014-03-30 14:04:53 +0200404 if (!ctx->nla[NFTA_TABLE_FLAGS])
405 return 0;
Pablo Neira Ayuso9ddf6322013-10-10 13:26:33 +0200406
Pablo Neira Ayusoe1aaca92014-03-30 14:04:53 +0200407 flags = ntohl(nla_get_be32(ctx->nla[NFTA_TABLE_FLAGS]));
408 if (flags & ~NFT_TABLE_F_DORMANT)
409 return -EINVAL;
Pablo Neira Ayuso9ddf6322013-10-10 13:26:33 +0200410
Pablo Neira Ayuso55dd6f92014-04-03 11:53:37 +0200411 trans = nft_trans_alloc(ctx, NFT_MSG_NEWTABLE,
412 sizeof(struct nft_trans_table));
413 if (trans == NULL)
414 return -ENOMEM;
415
Pablo Neira Ayusoe1aaca92014-03-30 14:04:53 +0200416 if ((flags & NFT_TABLE_F_DORMANT) &&
417 !(ctx->table->flags & NFT_TABLE_F_DORMANT)) {
Pablo Neira Ayuso55dd6f92014-04-03 11:53:37 +0200418 nft_trans_table_enable(trans) = false;
Pablo Neira Ayusoe1aaca92014-03-30 14:04:53 +0200419 } else if (!(flags & NFT_TABLE_F_DORMANT) &&
420 ctx->table->flags & NFT_TABLE_F_DORMANT) {
421 ret = nf_tables_table_enable(ctx->afi, ctx->table);
Pablo Neira Ayuso55dd6f92014-04-03 11:53:37 +0200422 if (ret >= 0) {
Pablo Neira Ayusoe1aaca92014-03-30 14:04:53 +0200423 ctx->table->flags &= ~NFT_TABLE_F_DORMANT;
Pablo Neira Ayuso55dd6f92014-04-03 11:53:37 +0200424 nft_trans_table_enable(trans) = true;
425 }
Pablo Neira Ayuso9ddf6322013-10-10 13:26:33 +0200426 }
Pablo Neira Ayusoe1aaca92014-03-30 14:04:53 +0200427 if (ret < 0)
428 goto err;
Pablo Neira Ayuso9ddf6322013-10-10 13:26:33 +0200429
Pablo Neira Ayuso55dd6f92014-04-03 11:53:37 +0200430 nft_trans_table_update(trans) = true;
431 list_add_tail(&trans->list, &ctx->net->nft.commit_list);
432 return 0;
Pablo Neira Ayuso9ddf6322013-10-10 13:26:33 +0200433err:
Pablo Neira Ayuso55dd6f92014-04-03 11:53:37 +0200434 nft_trans_destroy(trans);
Pablo Neira Ayuso9ddf6322013-10-10 13:26:33 +0200435 return ret;
436}
437
Pablo Neira Ayuso55dd6f92014-04-03 11:53:37 +0200438static int nft_trans_table_add(struct nft_ctx *ctx, int msg_type)
439{
440 struct nft_trans *trans;
441
442 trans = nft_trans_alloc(ctx, msg_type, sizeof(struct nft_trans_table));
443 if (trans == NULL)
444 return -ENOMEM;
445
446 if (msg_type == NFT_MSG_NEWTABLE)
447 ctx->table->flags |= NFT_TABLE_INACTIVE;
448
449 list_add_tail(&trans->list, &ctx->net->nft.commit_list);
450 return 0;
451}
452
Patrick McHardy96518512013-10-14 11:00:02 +0200453static int nf_tables_newtable(struct sock *nlsk, struct sk_buff *skb,
454 const struct nlmsghdr *nlh,
455 const struct nlattr * const nla[])
456{
457 const struct nfgenmsg *nfmsg = nlmsg_data(nlh);
458 const struct nlattr *name;
459 struct nft_af_info *afi;
460 struct nft_table *table;
Pablo Neira Ayuso99633ab2013-10-10 23:28:33 +0200461 struct net *net = sock_net(skb->sk);
Patrick McHardy96518512013-10-14 11:00:02 +0200462 int family = nfmsg->nfgen_family;
Patrick McHardyc5c1f972014-01-09 18:42:39 +0000463 u32 flags = 0;
Pablo Neira Ayusoe1aaca92014-03-30 14:04:53 +0200464 struct nft_ctx ctx;
Pablo Neira Ayuso55dd6f92014-04-03 11:53:37 +0200465 int err;
Patrick McHardy96518512013-10-14 11:00:02 +0200466
Pablo Neira Ayuso99633ab2013-10-10 23:28:33 +0200467 afi = nf_tables_afinfo_lookup(net, family, true);
Patrick McHardy96518512013-10-14 11:00:02 +0200468 if (IS_ERR(afi))
469 return PTR_ERR(afi);
470
471 name = nla[NFTA_TABLE_NAME];
Pablo Neira Ayuso93707612013-10-10 23:21:26 +0200472 table = nf_tables_table_lookup(afi, name);
Patrick McHardy96518512013-10-14 11:00:02 +0200473 if (IS_ERR(table)) {
474 if (PTR_ERR(table) != -ENOENT)
475 return PTR_ERR(table);
476 table = NULL;
477 }
478
479 if (table != NULL) {
Pablo Neira Ayuso55dd6f92014-04-03 11:53:37 +0200480 if (table->flags & NFT_TABLE_INACTIVE)
481 return -ENOENT;
Patrick McHardy96518512013-10-14 11:00:02 +0200482 if (nlh->nlmsg_flags & NLM_F_EXCL)
483 return -EEXIST;
484 if (nlh->nlmsg_flags & NLM_F_REPLACE)
485 return -EOPNOTSUPP;
Pablo Neira Ayusoe1aaca92014-03-30 14:04:53 +0200486
487 nft_ctx_init(&ctx, skb, nlh, afi, table, NULL, nla);
488 return nf_tables_updtable(&ctx);
Patrick McHardy96518512013-10-14 11:00:02 +0200489 }
490
Patrick McHardyc5c1f972014-01-09 18:42:39 +0000491 if (nla[NFTA_TABLE_FLAGS]) {
492 flags = ntohl(nla_get_be32(nla[NFTA_TABLE_FLAGS]));
493 if (flags & ~NFT_TABLE_F_DORMANT)
494 return -EINVAL;
495 }
496
Patrick McHardy7047f9d2014-01-09 18:42:40 +0000497 if (!try_module_get(afi->owner))
498 return -EAFNOSUPPORT;
499
Patrick McHardy96518512013-10-14 11:00:02 +0200500 table = kzalloc(sizeof(*table) + nla_len(name), GFP_KERNEL);
Patrick McHardy7047f9d2014-01-09 18:42:40 +0000501 if (table == NULL) {
502 module_put(afi->owner);
Patrick McHardy96518512013-10-14 11:00:02 +0200503 return -ENOMEM;
Patrick McHardy7047f9d2014-01-09 18:42:40 +0000504 }
Patrick McHardy96518512013-10-14 11:00:02 +0200505
506 nla_strlcpy(table->name, name, nla_len(name));
507 INIT_LIST_HEAD(&table->chains);
Patrick McHardy20a69342013-10-11 12:06:22 +0200508 INIT_LIST_HEAD(&table->sets);
Patrick McHardyc5c1f972014-01-09 18:42:39 +0000509 table->flags = flags;
Pablo Neira Ayuso9ddf6322013-10-10 13:26:33 +0200510
Pablo Neira Ayuso55dd6f92014-04-03 11:53:37 +0200511 nft_ctx_init(&ctx, skb, nlh, afi, table, NULL, nla);
512 err = nft_trans_table_add(&ctx, NFT_MSG_NEWTABLE);
513 if (err < 0) {
514 kfree(table);
515 module_put(afi->owner);
516 return err;
517 }
Patrick McHardy96518512013-10-14 11:00:02 +0200518 list_add_tail(&table->list, &afi->tables);
Patrick McHardy96518512013-10-14 11:00:02 +0200519 return 0;
520}
521
522static int nf_tables_deltable(struct sock *nlsk, struct sk_buff *skb,
523 const struct nlmsghdr *nlh,
524 const struct nlattr * const nla[])
525{
526 const struct nfgenmsg *nfmsg = nlmsg_data(nlh);
527 struct nft_af_info *afi;
528 struct nft_table *table;
Pablo Neira Ayuso99633ab2013-10-10 23:28:33 +0200529 struct net *net = sock_net(skb->sk);
Pablo Neira Ayuso55dd6f92014-04-03 11:53:37 +0200530 int family = nfmsg->nfgen_family, err;
531 struct nft_ctx ctx;
Patrick McHardy96518512013-10-14 11:00:02 +0200532
Pablo Neira Ayuso99633ab2013-10-10 23:28:33 +0200533 afi = nf_tables_afinfo_lookup(net, family, false);
Patrick McHardy96518512013-10-14 11:00:02 +0200534 if (IS_ERR(afi))
535 return PTR_ERR(afi);
536
Pablo Neira Ayuso93707612013-10-10 23:21:26 +0200537 table = nf_tables_table_lookup(afi, nla[NFTA_TABLE_NAME]);
Patrick McHardy96518512013-10-14 11:00:02 +0200538 if (IS_ERR(table))
539 return PTR_ERR(table);
Pablo Neira Ayuso55dd6f92014-04-03 11:53:37 +0200540 if (table->flags & NFT_TABLE_INACTIVE)
541 return -ENOENT;
Patrick McHardy96518512013-10-14 11:00:02 +0200542
Patrick McHardy44a6f0d2014-01-09 18:42:41 +0000543 if (!list_empty(&table->chains) || !list_empty(&table->sets))
Patrick McHardy96518512013-10-14 11:00:02 +0200544 return -EBUSY;
545
Pablo Neira Ayuso55dd6f92014-04-03 11:53:37 +0200546 nft_ctx_init(&ctx, skb, nlh, afi, table, NULL, nla);
547 err = nft_trans_table_add(&ctx, NFT_MSG_DELTABLE);
548 if (err < 0)
549 return err;
550
Patrick McHardy96518512013-10-14 11:00:02 +0200551 list_del(&table->list);
Patrick McHardy96518512013-10-14 11:00:02 +0200552 return 0;
553}
554
Pablo Neira Ayuso55dd6f92014-04-03 11:53:37 +0200555static void nf_tables_table_destroy(struct nft_ctx *ctx)
556{
557 kfree(ctx->table);
558 module_put(ctx->afi->owner);
559}
560
Patrick McHardy2a37d752014-01-09 18:42:37 +0000561int nft_register_chain_type(const struct nf_chain_type *ctype)
Patrick McHardy96518512013-10-14 11:00:02 +0200562{
Pablo Neira Ayuso93707612013-10-10 23:21:26 +0200563 int err = 0;
Patrick McHardy96518512013-10-14 11:00:02 +0200564
565 nfnl_lock(NFNL_SUBSYS_NFTABLES);
Pablo Neira Ayuso93707612013-10-10 23:21:26 +0200566 if (chain_type[ctype->family][ctype->type] != NULL) {
567 err = -EBUSY;
568 goto out;
Patrick McHardy96518512013-10-14 11:00:02 +0200569 }
Pablo Neira Ayuso93707612013-10-10 23:21:26 +0200570 chain_type[ctype->family][ctype->type] = ctype;
571out:
Patrick McHardy96518512013-10-14 11:00:02 +0200572 nfnl_unlock(NFNL_SUBSYS_NFTABLES);
573 return err;
574}
Pablo Neira Ayuso93707612013-10-10 23:21:26 +0200575EXPORT_SYMBOL_GPL(nft_register_chain_type);
Patrick McHardy96518512013-10-14 11:00:02 +0200576
Patrick McHardy2a37d752014-01-09 18:42:37 +0000577void nft_unregister_chain_type(const struct nf_chain_type *ctype)
Patrick McHardy96518512013-10-14 11:00:02 +0200578{
Patrick McHardy96518512013-10-14 11:00:02 +0200579 nfnl_lock(NFNL_SUBSYS_NFTABLES);
Pablo Neira Ayuso93707612013-10-10 23:21:26 +0200580 chain_type[ctype->family][ctype->type] = NULL;
Patrick McHardy96518512013-10-14 11:00:02 +0200581 nfnl_unlock(NFNL_SUBSYS_NFTABLES);
582}
Pablo Neira Ayuso93707612013-10-10 23:21:26 +0200583EXPORT_SYMBOL_GPL(nft_unregister_chain_type);
Patrick McHardy96518512013-10-14 11:00:02 +0200584
585/*
586 * Chains
587 */
588
589static struct nft_chain *
590nf_tables_chain_lookup_byhandle(const struct nft_table *table, u64 handle)
591{
592 struct nft_chain *chain;
593
594 list_for_each_entry(chain, &table->chains, list) {
595 if (chain->handle == handle)
596 return chain;
597 }
598
599 return ERR_PTR(-ENOENT);
600}
601
602static struct nft_chain *nf_tables_chain_lookup(const struct nft_table *table,
603 const struct nlattr *nla)
604{
605 struct nft_chain *chain;
606
607 if (nla == NULL)
608 return ERR_PTR(-EINVAL);
609
610 list_for_each_entry(chain, &table->chains, list) {
611 if (!nla_strcmp(nla, chain->name))
612 return chain;
613 }
614
615 return ERR_PTR(-ENOENT);
616}
617
618static const struct nla_policy nft_chain_policy[NFTA_CHAIN_MAX + 1] = {
619 [NFTA_CHAIN_TABLE] = { .type = NLA_STRING },
620 [NFTA_CHAIN_HANDLE] = { .type = NLA_U64 },
621 [NFTA_CHAIN_NAME] = { .type = NLA_STRING,
622 .len = NFT_CHAIN_MAXNAMELEN - 1 },
623 [NFTA_CHAIN_HOOK] = { .type = NLA_NESTED },
Pablo Neira Ayuso0ca743a2013-10-14 00:06:06 +0200624 [NFTA_CHAIN_POLICY] = { .type = NLA_U32 },
Pablo Neira4c1f7812014-03-31 17:43:47 +0200625 [NFTA_CHAIN_TYPE] = { .type = NLA_STRING },
Pablo Neira Ayuso0ca743a2013-10-14 00:06:06 +0200626 [NFTA_CHAIN_COUNTERS] = { .type = NLA_NESTED },
Patrick McHardy96518512013-10-14 11:00:02 +0200627};
628
629static const struct nla_policy nft_hook_policy[NFTA_HOOK_MAX + 1] = {
630 [NFTA_HOOK_HOOKNUM] = { .type = NLA_U32 },
631 [NFTA_HOOK_PRIORITY] = { .type = NLA_U32 },
632};
633
Pablo Neira Ayuso0ca743a2013-10-14 00:06:06 +0200634static int nft_dump_stats(struct sk_buff *skb, struct nft_stats __percpu *stats)
635{
636 struct nft_stats *cpu_stats, total;
637 struct nlattr *nest;
638 int cpu;
639
640 memset(&total, 0, sizeof(total));
641 for_each_possible_cpu(cpu) {
642 cpu_stats = per_cpu_ptr(stats, cpu);
643 total.pkts += cpu_stats->pkts;
644 total.bytes += cpu_stats->bytes;
645 }
646 nest = nla_nest_start(skb, NFTA_CHAIN_COUNTERS);
647 if (nest == NULL)
648 goto nla_put_failure;
649
650 if (nla_put_be64(skb, NFTA_COUNTER_PACKETS, cpu_to_be64(total.pkts)) ||
651 nla_put_be64(skb, NFTA_COUNTER_BYTES, cpu_to_be64(total.bytes)))
652 goto nla_put_failure;
653
654 nla_nest_end(skb, nest);
655 return 0;
656
657nla_put_failure:
658 return -ENOSPC;
659}
660
Patrick McHardy96518512013-10-14 11:00:02 +0200661static int nf_tables_fill_chain_info(struct sk_buff *skb, u32 portid, u32 seq,
662 int event, u32 flags, int family,
663 const struct nft_table *table,
664 const struct nft_chain *chain)
665{
666 struct nlmsghdr *nlh;
667 struct nfgenmsg *nfmsg;
668
669 event |= NFNL_SUBSYS_NFTABLES << 8;
670 nlh = nlmsg_put(skb, portid, seq, event, sizeof(struct nfgenmsg), flags);
671 if (nlh == NULL)
672 goto nla_put_failure;
673
674 nfmsg = nlmsg_data(nlh);
675 nfmsg->nfgen_family = family;
676 nfmsg->version = NFNETLINK_V0;
677 nfmsg->res_id = 0;
678
679 if (nla_put_string(skb, NFTA_CHAIN_TABLE, table->name))
680 goto nla_put_failure;
681 if (nla_put_be64(skb, NFTA_CHAIN_HANDLE, cpu_to_be64(chain->handle)))
682 goto nla_put_failure;
683 if (nla_put_string(skb, NFTA_CHAIN_NAME, chain->name))
684 goto nla_put_failure;
685
686 if (chain->flags & NFT_BASE_CHAIN) {
Pablo Neira Ayuso0ca743a2013-10-14 00:06:06 +0200687 const struct nft_base_chain *basechain = nft_base_chain(chain);
Patrick McHardy115a60b2014-01-03 12:16:15 +0000688 const struct nf_hook_ops *ops = &basechain->ops[0];
Pablo Neira Ayuso0ca743a2013-10-14 00:06:06 +0200689 struct nlattr *nest;
690
691 nest = nla_nest_start(skb, NFTA_CHAIN_HOOK);
Patrick McHardy96518512013-10-14 11:00:02 +0200692 if (nest == NULL)
693 goto nla_put_failure;
694 if (nla_put_be32(skb, NFTA_HOOK_HOOKNUM, htonl(ops->hooknum)))
695 goto nla_put_failure;
696 if (nla_put_be32(skb, NFTA_HOOK_PRIORITY, htonl(ops->priority)))
697 goto nla_put_failure;
698 nla_nest_end(skb, nest);
Pablo Neira Ayuso93707612013-10-10 23:21:26 +0200699
Pablo Neira Ayuso0ca743a2013-10-14 00:06:06 +0200700 if (nla_put_be32(skb, NFTA_CHAIN_POLICY,
701 htonl(basechain->policy)))
702 goto nla_put_failure;
703
Patrick McHardybaae3e62014-01-09 18:42:34 +0000704 if (nla_put_string(skb, NFTA_CHAIN_TYPE, basechain->type->name))
705 goto nla_put_failure;
Pablo Neira Ayuso0ca743a2013-10-14 00:06:06 +0200706
707 if (nft_dump_stats(skb, nft_base_chain(chain)->stats))
708 goto nla_put_failure;
Patrick McHardy96518512013-10-14 11:00:02 +0200709 }
710
Pablo Neira Ayuso0ca743a2013-10-14 00:06:06 +0200711 if (nla_put_be32(skb, NFTA_CHAIN_USE, htonl(chain->use)))
712 goto nla_put_failure;
713
Patrick McHardy96518512013-10-14 11:00:02 +0200714 return nlmsg_end(skb, nlh);
715
716nla_put_failure:
717 nlmsg_trim(skb, nlh);
718 return -1;
719}
720
Pablo Neira Ayuso35151d82014-05-05 17:12:40 +0200721static int nf_tables_chain_notify(const struct nft_ctx *ctx, int event)
Patrick McHardy96518512013-10-14 11:00:02 +0200722{
723 struct sk_buff *skb;
Pablo Neira Ayuso35151d82014-05-05 17:12:40 +0200724 u32 portid = NETLINK_CB(ctx->skb).portid;
725 struct net *net = sock_net(ctx->skb->sk);
726 u32 seq = ctx->nlh->nlmsg_seq;
Patrick McHardy96518512013-10-14 11:00:02 +0200727 bool report;
728 int err;
729
Pablo Neira Ayuso35151d82014-05-05 17:12:40 +0200730 report = nlmsg_report(ctx->nlh);
Patrick McHardy96518512013-10-14 11:00:02 +0200731 if (!report && !nfnetlink_has_listeners(net, NFNLGRP_NFTABLES))
732 return 0;
733
734 err = -ENOBUFS;
735 skb = nlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL);
736 if (skb == NULL)
737 goto err;
738
Pablo Neira Ayuso35151d82014-05-05 17:12:40 +0200739 err = nf_tables_fill_chain_info(skb, portid, seq, event, 0,
740 ctx->afi->family, ctx->table,
741 ctx->chain);
Patrick McHardy96518512013-10-14 11:00:02 +0200742 if (err < 0) {
743 kfree_skb(skb);
744 goto err;
745 }
746
747 err = nfnetlink_send(skb, net, portid, NFNLGRP_NFTABLES, report,
748 GFP_KERNEL);
749err:
750 if (err < 0)
751 nfnetlink_set_err(net, portid, NFNLGRP_NFTABLES, err);
752 return err;
753}
754
755static int nf_tables_dump_chains(struct sk_buff *skb,
756 struct netlink_callback *cb)
757{
758 const struct nfgenmsg *nfmsg = nlmsg_data(cb->nlh);
759 const struct nft_af_info *afi;
760 const struct nft_table *table;
761 const struct nft_chain *chain;
762 unsigned int idx = 0, s_idx = cb->args[0];
Pablo Neira Ayuso99633ab2013-10-10 23:28:33 +0200763 struct net *net = sock_net(skb->sk);
Patrick McHardy96518512013-10-14 11:00:02 +0200764 int family = nfmsg->nfgen_family;
765
Pablo Neira Ayuso99633ab2013-10-10 23:28:33 +0200766 list_for_each_entry(afi, &net->nft.af_info, list) {
Patrick McHardy96518512013-10-14 11:00:02 +0200767 if (family != NFPROTO_UNSPEC && family != afi->family)
768 continue;
769
770 list_for_each_entry(table, &afi->tables, list) {
771 list_for_each_entry(chain, &table->chains, list) {
772 if (idx < s_idx)
773 goto cont;
774 if (idx > s_idx)
775 memset(&cb->args[1], 0,
776 sizeof(cb->args) - sizeof(cb->args[0]));
777 if (nf_tables_fill_chain_info(skb, NETLINK_CB(cb->skb).portid,
778 cb->nlh->nlmsg_seq,
779 NFT_MSG_NEWCHAIN,
780 NLM_F_MULTI,
781 afi->family, table, chain) < 0)
782 goto done;
783cont:
784 idx++;
785 }
786 }
787 }
788done:
789 cb->args[0] = idx;
790 return skb->len;
791}
792
793
794static int nf_tables_getchain(struct sock *nlsk, struct sk_buff *skb,
795 const struct nlmsghdr *nlh,
796 const struct nlattr * const nla[])
797{
798 const struct nfgenmsg *nfmsg = nlmsg_data(nlh);
799 const struct nft_af_info *afi;
800 const struct nft_table *table;
801 const struct nft_chain *chain;
802 struct sk_buff *skb2;
Pablo Neira Ayuso99633ab2013-10-10 23:28:33 +0200803 struct net *net = sock_net(skb->sk);
Patrick McHardy96518512013-10-14 11:00:02 +0200804 int family = nfmsg->nfgen_family;
805 int err;
806
807 if (nlh->nlmsg_flags & NLM_F_DUMP) {
808 struct netlink_dump_control c = {
809 .dump = nf_tables_dump_chains,
810 };
811 return netlink_dump_start(nlsk, skb, nlh, &c);
812 }
813
Pablo Neira Ayuso99633ab2013-10-10 23:28:33 +0200814 afi = nf_tables_afinfo_lookup(net, family, false);
Patrick McHardy96518512013-10-14 11:00:02 +0200815 if (IS_ERR(afi))
816 return PTR_ERR(afi);
817
Pablo Neira Ayuso93707612013-10-10 23:21:26 +0200818 table = nf_tables_table_lookup(afi, nla[NFTA_CHAIN_TABLE]);
Patrick McHardy96518512013-10-14 11:00:02 +0200819 if (IS_ERR(table))
820 return PTR_ERR(table);
Pablo Neira Ayuso55dd6f92014-04-03 11:53:37 +0200821 if (table->flags & NFT_TABLE_INACTIVE)
822 return -ENOENT;
Patrick McHardy96518512013-10-14 11:00:02 +0200823
824 chain = nf_tables_chain_lookup(table, nla[NFTA_CHAIN_NAME]);
825 if (IS_ERR(chain))
826 return PTR_ERR(chain);
Pablo Neira Ayuso91c7b382014-04-09 11:58:08 +0200827 if (chain->flags & NFT_CHAIN_INACTIVE)
828 return -ENOENT;
Patrick McHardy96518512013-10-14 11:00:02 +0200829
830 skb2 = alloc_skb(NLMSG_GOODSIZE, GFP_KERNEL);
831 if (!skb2)
832 return -ENOMEM;
833
834 err = nf_tables_fill_chain_info(skb2, NETLINK_CB(skb).portid,
835 nlh->nlmsg_seq, NFT_MSG_NEWCHAIN, 0,
836 family, table, chain);
837 if (err < 0)
838 goto err;
839
840 return nlmsg_unicast(nlsk, skb2, NETLINK_CB(skb).portid);
841
842err:
843 kfree_skb(skb2);
844 return err;
845}
846
Pablo Neira Ayuso0ca743a2013-10-14 00:06:06 +0200847static const struct nla_policy nft_counter_policy[NFTA_COUNTER_MAX + 1] = {
848 [NFTA_COUNTER_PACKETS] = { .type = NLA_U64 },
849 [NFTA_COUNTER_BYTES] = { .type = NLA_U64 },
850};
851
Pablo Neira Ayusoff3cd7b2014-04-09 11:53:06 +0200852static struct nft_stats __percpu *nft_stats_alloc(const struct nlattr *attr)
Pablo Neira Ayuso0ca743a2013-10-14 00:06:06 +0200853{
854 struct nlattr *tb[NFTA_COUNTER_MAX+1];
855 struct nft_stats __percpu *newstats;
856 struct nft_stats *stats;
857 int err;
858
859 err = nla_parse_nested(tb, NFTA_COUNTER_MAX, attr, nft_counter_policy);
860 if (err < 0)
Pablo Neira Ayusoff3cd7b2014-04-09 11:53:06 +0200861 return ERR_PTR(err);
Pablo Neira Ayuso0ca743a2013-10-14 00:06:06 +0200862
863 if (!tb[NFTA_COUNTER_BYTES] || !tb[NFTA_COUNTER_PACKETS])
Pablo Neira Ayusoff3cd7b2014-04-09 11:53:06 +0200864 return ERR_PTR(-EINVAL);
Pablo Neira Ayuso0ca743a2013-10-14 00:06:06 +0200865
866 newstats = alloc_percpu(struct nft_stats);
867 if (newstats == NULL)
Pablo Neira Ayusoff3cd7b2014-04-09 11:53:06 +0200868 return ERR_PTR(-ENOMEM);
Pablo Neira Ayuso0ca743a2013-10-14 00:06:06 +0200869
870 /* Restore old counters on this cpu, no problem. Per-cpu statistics
871 * are not exposed to userspace.
872 */
873 stats = this_cpu_ptr(newstats);
874 stats->bytes = be64_to_cpu(nla_get_be64(tb[NFTA_COUNTER_BYTES]));
875 stats->pkts = be64_to_cpu(nla_get_be64(tb[NFTA_COUNTER_PACKETS]));
876
Pablo Neira Ayusoff3cd7b2014-04-09 11:53:06 +0200877 return newstats;
878}
879
880static void nft_chain_stats_replace(struct nft_base_chain *chain,
881 struct nft_stats __percpu *newstats)
882{
Pablo Neira Ayuso0ca743a2013-10-14 00:06:06 +0200883 if (chain->stats) {
Pablo Neira Ayuso0ca743a2013-10-14 00:06:06 +0200884 struct nft_stats __percpu *oldstats =
Patrick McHardy67a8fc22014-02-18 18:06:49 +0000885 nft_dereference(chain->stats);
Pablo Neira Ayuso0ca743a2013-10-14 00:06:06 +0200886
887 rcu_assign_pointer(chain->stats, newstats);
888 synchronize_rcu();
889 free_percpu(oldstats);
890 } else
891 rcu_assign_pointer(chain->stats, newstats);
Pablo Neira Ayuso0ca743a2013-10-14 00:06:06 +0200892}
893
Pablo Neira Ayuso91c7b382014-04-09 11:58:08 +0200894static int nft_trans_chain_add(struct nft_ctx *ctx, int msg_type)
895{
896 struct nft_trans *trans;
897
898 trans = nft_trans_alloc(ctx, msg_type, sizeof(struct nft_trans_chain));
899 if (trans == NULL)
900 return -ENOMEM;
901
902 if (msg_type == NFT_MSG_NEWCHAIN)
903 ctx->chain->flags |= NFT_CHAIN_INACTIVE;
904
905 list_add_tail(&trans->list, &ctx->net->nft.commit_list);
906 return 0;
907}
908
909static void nf_tables_chain_destroy(struct nft_chain *chain)
910{
911 BUG_ON(chain->use > 0);
912
913 if (chain->flags & NFT_BASE_CHAIN) {
914 module_put(nft_base_chain(chain)->type->owner);
915 free_percpu(nft_base_chain(chain)->stats);
916 kfree(nft_base_chain(chain));
917 } else {
918 kfree(chain);
919 }
920}
921
Patrick McHardy96518512013-10-14 11:00:02 +0200922static int nf_tables_newchain(struct sock *nlsk, struct sk_buff *skb,
923 const struct nlmsghdr *nlh,
924 const struct nlattr * const nla[])
925{
926 const struct nfgenmsg *nfmsg = nlmsg_data(nlh);
927 const struct nlattr * uninitialized_var(name);
Pablo Neira Ayuso7c95f6d2014-04-04 01:22:45 +0200928 struct nft_af_info *afi;
Patrick McHardy96518512013-10-14 11:00:02 +0200929 struct nft_table *table;
930 struct nft_chain *chain;
Pablo Neira Ayuso0ca743a2013-10-14 00:06:06 +0200931 struct nft_base_chain *basechain = NULL;
Patrick McHardy96518512013-10-14 11:00:02 +0200932 struct nlattr *ha[NFTA_HOOK_MAX + 1];
Pablo Neira Ayuso99633ab2013-10-10 23:28:33 +0200933 struct net *net = sock_net(skb->sk);
Patrick McHardy96518512013-10-14 11:00:02 +0200934 int family = nfmsg->nfgen_family;
Patrick McHardy57de2a02014-01-09 18:42:31 +0000935 u8 policy = NF_ACCEPT;
Patrick McHardy96518512013-10-14 11:00:02 +0200936 u64 handle = 0;
Patrick McHardy115a60b2014-01-03 12:16:15 +0000937 unsigned int i;
Pablo Neira Ayusoff3cd7b2014-04-09 11:53:06 +0200938 struct nft_stats __percpu *stats;
Patrick McHardy96518512013-10-14 11:00:02 +0200939 int err;
940 bool create;
Pablo Neira Ayuso91c7b382014-04-09 11:58:08 +0200941 struct nft_ctx ctx;
Patrick McHardy96518512013-10-14 11:00:02 +0200942
943 create = nlh->nlmsg_flags & NLM_F_CREATE ? true : false;
944
Pablo Neira Ayuso99633ab2013-10-10 23:28:33 +0200945 afi = nf_tables_afinfo_lookup(net, family, true);
Patrick McHardy96518512013-10-14 11:00:02 +0200946 if (IS_ERR(afi))
947 return PTR_ERR(afi);
948
Pablo Neira Ayuso93707612013-10-10 23:21:26 +0200949 table = nf_tables_table_lookup(afi, nla[NFTA_CHAIN_TABLE]);
Patrick McHardy96518512013-10-14 11:00:02 +0200950 if (IS_ERR(table))
951 return PTR_ERR(table);
952
Patrick McHardy96518512013-10-14 11:00:02 +0200953 chain = NULL;
954 name = nla[NFTA_CHAIN_NAME];
955
956 if (nla[NFTA_CHAIN_HANDLE]) {
957 handle = be64_to_cpu(nla_get_be64(nla[NFTA_CHAIN_HANDLE]));
958 chain = nf_tables_chain_lookup_byhandle(table, handle);
959 if (IS_ERR(chain))
960 return PTR_ERR(chain);
961 } else {
962 chain = nf_tables_chain_lookup(table, name);
963 if (IS_ERR(chain)) {
964 if (PTR_ERR(chain) != -ENOENT)
965 return PTR_ERR(chain);
966 chain = NULL;
967 }
968 }
969
Patrick McHardy57de2a02014-01-09 18:42:31 +0000970 if (nla[NFTA_CHAIN_POLICY]) {
971 if ((chain != NULL &&
972 !(chain->flags & NFT_BASE_CHAIN)) ||
973 nla[NFTA_CHAIN_HOOK] == NULL)
974 return -EOPNOTSUPP;
975
Pablo Neira Ayuso8f46df12014-01-10 15:11:25 +0100976 policy = ntohl(nla_get_be32(nla[NFTA_CHAIN_POLICY]));
Patrick McHardy57de2a02014-01-09 18:42:31 +0000977 switch (policy) {
978 case NF_DROP:
979 case NF_ACCEPT:
980 break;
981 default:
982 return -EINVAL;
983 }
984 }
985
Patrick McHardy96518512013-10-14 11:00:02 +0200986 if (chain != NULL) {
Pablo Neira Ayuso91c7b382014-04-09 11:58:08 +0200987 struct nft_stats *stats = NULL;
988 struct nft_trans *trans;
989
990 if (chain->flags & NFT_CHAIN_INACTIVE)
991 return -ENOENT;
Patrick McHardy96518512013-10-14 11:00:02 +0200992 if (nlh->nlmsg_flags & NLM_F_EXCL)
993 return -EEXIST;
994 if (nlh->nlmsg_flags & NLM_F_REPLACE)
995 return -EOPNOTSUPP;
996
997 if (nla[NFTA_CHAIN_HANDLE] && name &&
998 !IS_ERR(nf_tables_chain_lookup(table, nla[NFTA_CHAIN_NAME])))
999 return -EEXIST;
1000
Pablo Neira Ayuso0ca743a2013-10-14 00:06:06 +02001001 if (nla[NFTA_CHAIN_COUNTERS]) {
1002 if (!(chain->flags & NFT_BASE_CHAIN))
1003 return -EOPNOTSUPP;
1004
Pablo Neira Ayusoff3cd7b2014-04-09 11:53:06 +02001005 stats = nft_stats_alloc(nla[NFTA_CHAIN_COUNTERS]);
1006 if (IS_ERR(stats))
1007 return PTR_ERR(stats);
Pablo Neira Ayuso0ca743a2013-10-14 00:06:06 +02001008 }
1009
Pablo Neira Ayuso91c7b382014-04-09 11:58:08 +02001010 nft_ctx_init(&ctx, skb, nlh, afi, table, chain, nla);
1011 trans = nft_trans_alloc(&ctx, NFT_MSG_NEWCHAIN,
1012 sizeof(struct nft_trans_chain));
1013 if (trans == NULL)
1014 return -ENOMEM;
1015
1016 nft_trans_chain_stats(trans) = stats;
1017 nft_trans_chain_update(trans) = true;
1018
Patrick McHardy4401a862014-01-09 18:42:32 +00001019 if (nla[NFTA_CHAIN_POLICY])
Pablo Neira Ayuso91c7b382014-04-09 11:58:08 +02001020 nft_trans_chain_policy(trans) = policy;
1021 else
1022 nft_trans_chain_policy(trans) = -1;
Patrick McHardy4401a862014-01-09 18:42:32 +00001023
Pablo Neira Ayuso91c7b382014-04-09 11:58:08 +02001024 if (nla[NFTA_CHAIN_HANDLE] && name) {
1025 nla_strlcpy(nft_trans_chain_name(trans), name,
1026 NFT_CHAIN_MAXNAMELEN);
1027 }
1028 list_add_tail(&trans->list, &net->nft.commit_list);
1029 return 0;
Patrick McHardy96518512013-10-14 11:00:02 +02001030 }
1031
Patrick McHardy75820672014-01-09 18:42:33 +00001032 if (table->use == UINT_MAX)
1033 return -EOVERFLOW;
1034
Patrick McHardy96518512013-10-14 11:00:02 +02001035 if (nla[NFTA_CHAIN_HOOK]) {
Patrick McHardy2a37d752014-01-09 18:42:37 +00001036 const struct nf_chain_type *type;
Patrick McHardy96518512013-10-14 11:00:02 +02001037 struct nf_hook_ops *ops;
Pablo Neira Ayuso93707612013-10-10 23:21:26 +02001038 nf_hookfn *hookfn;
Patrick McHardy115a60b2014-01-03 12:16:15 +00001039 u32 hooknum, priority;
Pablo Neira Ayuso93707612013-10-10 23:21:26 +02001040
Patrick McHardybaae3e62014-01-09 18:42:34 +00001041 type = chain_type[family][NFT_CHAIN_T_DEFAULT];
Pablo Neira Ayuso93707612013-10-10 23:21:26 +02001042 if (nla[NFTA_CHAIN_TYPE]) {
1043 type = nf_tables_chain_type_lookup(afi,
1044 nla[NFTA_CHAIN_TYPE],
1045 create);
Patrick McHardy93b08062014-01-09 18:42:36 +00001046 if (IS_ERR(type))
1047 return PTR_ERR(type);
Pablo Neira Ayuso93707612013-10-10 23:21:26 +02001048 }
Patrick McHardy96518512013-10-14 11:00:02 +02001049
1050 err = nla_parse_nested(ha, NFTA_HOOK_MAX, nla[NFTA_CHAIN_HOOK],
1051 nft_hook_policy);
1052 if (err < 0)
1053 return err;
1054 if (ha[NFTA_HOOK_HOOKNUM] == NULL ||
1055 ha[NFTA_HOOK_PRIORITY] == NULL)
1056 return -EINVAL;
Pablo Neira Ayuso93707612013-10-10 23:21:26 +02001057
1058 hooknum = ntohl(nla_get_be32(ha[NFTA_HOOK_HOOKNUM]));
1059 if (hooknum >= afi->nhooks)
Patrick McHardy96518512013-10-14 11:00:02 +02001060 return -EINVAL;
Patrick McHardy115a60b2014-01-03 12:16:15 +00001061 priority = ntohl(nla_get_be32(ha[NFTA_HOOK_PRIORITY]));
Patrick McHardy96518512013-10-14 11:00:02 +02001062
Patrick McHardybaae3e62014-01-09 18:42:34 +00001063 if (!(type->hook_mask & (1 << hooknum)))
Pablo Neira Ayuso93707612013-10-10 23:21:26 +02001064 return -EOPNOTSUPP;
Patrick McHardyfa2c1de2014-01-09 18:42:38 +00001065 if (!try_module_get(type->owner))
Patrick McHardybaae3e62014-01-09 18:42:34 +00001066 return -ENOENT;
Patrick McHardyfa2c1de2014-01-09 18:42:38 +00001067 hookfn = type->hooks[hooknum];
Pablo Neira Ayuso93707612013-10-10 23:21:26 +02001068
Patrick McHardy96518512013-10-14 11:00:02 +02001069 basechain = kzalloc(sizeof(*basechain), GFP_KERNEL);
1070 if (basechain == NULL)
1071 return -ENOMEM;
Pablo Neira Ayuso93707612013-10-10 23:21:26 +02001072
Patrick McHardy4401a862014-01-09 18:42:32 +00001073 if (nla[NFTA_CHAIN_COUNTERS]) {
Pablo Neira Ayusoff3cd7b2014-04-09 11:53:06 +02001074 stats = nft_stats_alloc(nla[NFTA_CHAIN_COUNTERS]);
1075 if (IS_ERR(stats)) {
Patrick McHardyfa2c1de2014-01-09 18:42:38 +00001076 module_put(type->owner);
Patrick McHardy4401a862014-01-09 18:42:32 +00001077 kfree(basechain);
Pablo Neira Ayusoff3cd7b2014-04-09 11:53:06 +02001078 return PTR_ERR(stats);
Patrick McHardy4401a862014-01-09 18:42:32 +00001079 }
Pablo Neira Ayusoff3cd7b2014-04-09 11:53:06 +02001080 basechain->stats = stats;
Patrick McHardy4401a862014-01-09 18:42:32 +00001081 } else {
Pablo Neira Ayusoff3cd7b2014-04-09 11:53:06 +02001082 stats = alloc_percpu(struct nft_stats);
1083 if (IS_ERR(stats)) {
Patrick McHardyfa2c1de2014-01-09 18:42:38 +00001084 module_put(type->owner);
Patrick McHardy4401a862014-01-09 18:42:32 +00001085 kfree(basechain);
Pablo Neira Ayusoff3cd7b2014-04-09 11:53:06 +02001086 return PTR_ERR(stats);
Patrick McHardy4401a862014-01-09 18:42:32 +00001087 }
Pablo Neira Ayusoff3cd7b2014-04-09 11:53:06 +02001088 rcu_assign_pointer(basechain->stats, stats);
Patrick McHardy4401a862014-01-09 18:42:32 +00001089 }
1090
Pablo Neira Ayuso93707612013-10-10 23:21:26 +02001091 basechain->type = type;
Patrick McHardy96518512013-10-14 11:00:02 +02001092 chain = &basechain->chain;
1093
Patrick McHardy115a60b2014-01-03 12:16:15 +00001094 for (i = 0; i < afi->nops; i++) {
1095 ops = &basechain->ops[i];
1096 ops->pf = family;
1097 ops->owner = afi->owner;
1098 ops->hooknum = hooknum;
1099 ops->priority = priority;
1100 ops->priv = chain;
1101 ops->hook = afi->hooks[ops->hooknum];
1102 if (hookfn)
1103 ops->hook = hookfn;
1104 if (afi->hook_ops_init)
1105 afi->hook_ops_init(ops, i);
1106 }
Patrick McHardy96518512013-10-14 11:00:02 +02001107
1108 chain->flags |= NFT_BASE_CHAIN;
Patrick McHardy57de2a02014-01-09 18:42:31 +00001109 basechain->policy = policy;
Patrick McHardy96518512013-10-14 11:00:02 +02001110 } else {
1111 chain = kzalloc(sizeof(*chain), GFP_KERNEL);
1112 if (chain == NULL)
1113 return -ENOMEM;
1114 }
1115
1116 INIT_LIST_HEAD(&chain->rules);
1117 chain->handle = nf_tables_alloc_handle(table);
Pablo Neira Ayuso0628b122013-10-14 11:05:33 +02001118 chain->net = net;
Pablo Neira Ayusob5bc89b2013-10-10 16:49:19 +02001119 chain->table = table;
Patrick McHardy96518512013-10-14 11:00:02 +02001120 nla_strlcpy(chain->name, name, NFT_CHAIN_MAXNAMELEN);
1121
Pablo Neira Ayuso9ddf6322013-10-10 13:26:33 +02001122 if (!(table->flags & NFT_TABLE_F_DORMANT) &&
1123 chain->flags & NFT_BASE_CHAIN) {
Patrick McHardy115a60b2014-01-03 12:16:15 +00001124 err = nf_register_hooks(nft_base_chain(chain)->ops, afi->nops);
Pablo Neira Ayuso91c7b382014-04-09 11:58:08 +02001125 if (err < 0)
1126 goto err1;
Pablo Neira Ayuso0ca743a2013-10-14 00:06:06 +02001127 }
Pablo Neira Ayuso91c7b382014-04-09 11:58:08 +02001128
1129 nft_ctx_init(&ctx, skb, nlh, afi, table, chain, nla);
1130 err = nft_trans_chain_add(&ctx, NFT_MSG_NEWCHAIN);
1131 if (err < 0)
1132 goto err2;
1133
Pablo Neira Ayuso9ddf6322013-10-10 13:26:33 +02001134 list_add_tail(&chain->list, &table->chains);
Patrick McHardy96518512013-10-14 11:00:02 +02001135 return 0;
Pablo Neira Ayuso91c7b382014-04-09 11:58:08 +02001136err2:
1137 if (!(table->flags & NFT_TABLE_F_DORMANT) &&
1138 chain->flags & NFT_BASE_CHAIN) {
1139 nf_unregister_hooks(nft_base_chain(chain)->ops,
1140 afi->nops);
1141 }
1142err1:
1143 nf_tables_chain_destroy(chain);
1144 return err;
Patrick McHardy96518512013-10-14 11:00:02 +02001145}
1146
1147static int nf_tables_delchain(struct sock *nlsk, struct sk_buff *skb,
1148 const struct nlmsghdr *nlh,
1149 const struct nlattr * const nla[])
1150{
1151 const struct nfgenmsg *nfmsg = nlmsg_data(nlh);
Pablo Neira Ayuso7c95f6d2014-04-04 01:22:45 +02001152 struct nft_af_info *afi;
Patrick McHardy96518512013-10-14 11:00:02 +02001153 struct nft_table *table;
1154 struct nft_chain *chain;
Pablo Neira Ayuso99633ab2013-10-10 23:28:33 +02001155 struct net *net = sock_net(skb->sk);
Patrick McHardy96518512013-10-14 11:00:02 +02001156 int family = nfmsg->nfgen_family;
Pablo Neira Ayuso91c7b382014-04-09 11:58:08 +02001157 struct nft_ctx ctx;
1158 int err;
Patrick McHardy96518512013-10-14 11:00:02 +02001159
Pablo Neira Ayuso99633ab2013-10-10 23:28:33 +02001160 afi = nf_tables_afinfo_lookup(net, family, false);
Patrick McHardy96518512013-10-14 11:00:02 +02001161 if (IS_ERR(afi))
1162 return PTR_ERR(afi);
1163
Pablo Neira Ayuso93707612013-10-10 23:21:26 +02001164 table = nf_tables_table_lookup(afi, nla[NFTA_CHAIN_TABLE]);
Patrick McHardy96518512013-10-14 11:00:02 +02001165 if (IS_ERR(table))
1166 return PTR_ERR(table);
Pablo Neira Ayuso55dd6f92014-04-03 11:53:37 +02001167 if (table->flags & NFT_TABLE_INACTIVE)
1168 return -ENOENT;
Patrick McHardy96518512013-10-14 11:00:02 +02001169
1170 chain = nf_tables_chain_lookup(table, nla[NFTA_CHAIN_NAME]);
1171 if (IS_ERR(chain))
1172 return PTR_ERR(chain);
Pablo Neira Ayuso91c7b382014-04-09 11:58:08 +02001173 if (chain->flags & NFT_CHAIN_INACTIVE)
1174 return -ENOENT;
Patrick McHardy3dd72792014-01-25 08:04:07 +00001175 if (!list_empty(&chain->rules) || chain->use > 0)
Patrick McHardy96518512013-10-14 11:00:02 +02001176 return -EBUSY;
1177
Pablo Neira Ayuso91c7b382014-04-09 11:58:08 +02001178 nft_ctx_init(&ctx, skb, nlh, afi, table, chain, nla);
1179 err = nft_trans_chain_add(&ctx, NFT_MSG_DELCHAIN);
1180 if (err < 0)
1181 return err;
1182
Patrick McHardy96518512013-10-14 11:00:02 +02001183 list_del(&chain->list);
Patrick McHardy96518512013-10-14 11:00:02 +02001184 return 0;
1185}
1186
Patrick McHardy96518512013-10-14 11:00:02 +02001187/*
1188 * Expressions
1189 */
1190
1191/**
Patrick McHardyef1f7df2013-10-10 11:41:20 +02001192 * nft_register_expr - register nf_tables expr type
1193 * @ops: expr type
Patrick McHardy96518512013-10-14 11:00:02 +02001194 *
Patrick McHardyef1f7df2013-10-10 11:41:20 +02001195 * Registers the expr type for use with nf_tables. Returns zero on
Patrick McHardy96518512013-10-14 11:00:02 +02001196 * success or a negative errno code otherwise.
1197 */
Patrick McHardyef1f7df2013-10-10 11:41:20 +02001198int nft_register_expr(struct nft_expr_type *type)
Patrick McHardy96518512013-10-14 11:00:02 +02001199{
1200 nfnl_lock(NFNL_SUBSYS_NFTABLES);
Tomasz Bursztyka758dbce2014-04-14 15:41:26 +03001201 if (type->family == NFPROTO_UNSPEC)
1202 list_add_tail(&type->list, &nf_tables_expressions);
1203 else
1204 list_add(&type->list, &nf_tables_expressions);
Patrick McHardy96518512013-10-14 11:00:02 +02001205 nfnl_unlock(NFNL_SUBSYS_NFTABLES);
1206 return 0;
1207}
1208EXPORT_SYMBOL_GPL(nft_register_expr);
1209
1210/**
Patrick McHardyef1f7df2013-10-10 11:41:20 +02001211 * nft_unregister_expr - unregister nf_tables expr type
1212 * @ops: expr type
Patrick McHardy96518512013-10-14 11:00:02 +02001213 *
Patrick McHardyef1f7df2013-10-10 11:41:20 +02001214 * Unregisters the expr typefor use with nf_tables.
Patrick McHardy96518512013-10-14 11:00:02 +02001215 */
Patrick McHardyef1f7df2013-10-10 11:41:20 +02001216void nft_unregister_expr(struct nft_expr_type *type)
Patrick McHardy96518512013-10-14 11:00:02 +02001217{
1218 nfnl_lock(NFNL_SUBSYS_NFTABLES);
Patrick McHardyef1f7df2013-10-10 11:41:20 +02001219 list_del(&type->list);
Patrick McHardy96518512013-10-14 11:00:02 +02001220 nfnl_unlock(NFNL_SUBSYS_NFTABLES);
1221}
1222EXPORT_SYMBOL_GPL(nft_unregister_expr);
1223
Patrick McHardy64d46802014-02-05 15:03:37 +00001224static const struct nft_expr_type *__nft_expr_type_get(u8 family,
1225 struct nlattr *nla)
Patrick McHardy96518512013-10-14 11:00:02 +02001226{
Patrick McHardyef1f7df2013-10-10 11:41:20 +02001227 const struct nft_expr_type *type;
Patrick McHardy96518512013-10-14 11:00:02 +02001228
Patrick McHardyef1f7df2013-10-10 11:41:20 +02001229 list_for_each_entry(type, &nf_tables_expressions, list) {
Patrick McHardy64d46802014-02-05 15:03:37 +00001230 if (!nla_strcmp(nla, type->name) &&
1231 (!type->family || type->family == family))
Patrick McHardyef1f7df2013-10-10 11:41:20 +02001232 return type;
Patrick McHardy96518512013-10-14 11:00:02 +02001233 }
1234 return NULL;
1235}
1236
Patrick McHardy64d46802014-02-05 15:03:37 +00001237static const struct nft_expr_type *nft_expr_type_get(u8 family,
1238 struct nlattr *nla)
Patrick McHardy96518512013-10-14 11:00:02 +02001239{
Patrick McHardyef1f7df2013-10-10 11:41:20 +02001240 const struct nft_expr_type *type;
Patrick McHardy96518512013-10-14 11:00:02 +02001241
1242 if (nla == NULL)
1243 return ERR_PTR(-EINVAL);
1244
Patrick McHardy64d46802014-02-05 15:03:37 +00001245 type = __nft_expr_type_get(family, nla);
Patrick McHardyef1f7df2013-10-10 11:41:20 +02001246 if (type != NULL && try_module_get(type->owner))
1247 return type;
Patrick McHardy96518512013-10-14 11:00:02 +02001248
1249#ifdef CONFIG_MODULES
Patrick McHardyef1f7df2013-10-10 11:41:20 +02001250 if (type == NULL) {
Patrick McHardy96518512013-10-14 11:00:02 +02001251 nfnl_unlock(NFNL_SUBSYS_NFTABLES);
Patrick McHardy64d46802014-02-05 15:03:37 +00001252 request_module("nft-expr-%u-%.*s", family,
1253 nla_len(nla), (char *)nla_data(nla));
1254 nfnl_lock(NFNL_SUBSYS_NFTABLES);
1255 if (__nft_expr_type_get(family, nla))
1256 return ERR_PTR(-EAGAIN);
1257
1258 nfnl_unlock(NFNL_SUBSYS_NFTABLES);
Patrick McHardy96518512013-10-14 11:00:02 +02001259 request_module("nft-expr-%.*s",
1260 nla_len(nla), (char *)nla_data(nla));
1261 nfnl_lock(NFNL_SUBSYS_NFTABLES);
Patrick McHardy64d46802014-02-05 15:03:37 +00001262 if (__nft_expr_type_get(family, nla))
Patrick McHardy96518512013-10-14 11:00:02 +02001263 return ERR_PTR(-EAGAIN);
1264 }
1265#endif
1266 return ERR_PTR(-ENOENT);
1267}
1268
1269static const struct nla_policy nft_expr_policy[NFTA_EXPR_MAX + 1] = {
1270 [NFTA_EXPR_NAME] = { .type = NLA_STRING },
1271 [NFTA_EXPR_DATA] = { .type = NLA_NESTED },
1272};
1273
1274static int nf_tables_fill_expr_info(struct sk_buff *skb,
1275 const struct nft_expr *expr)
1276{
Patrick McHardyef1f7df2013-10-10 11:41:20 +02001277 if (nla_put_string(skb, NFTA_EXPR_NAME, expr->ops->type->name))
Patrick McHardy96518512013-10-14 11:00:02 +02001278 goto nla_put_failure;
1279
1280 if (expr->ops->dump) {
1281 struct nlattr *data = nla_nest_start(skb, NFTA_EXPR_DATA);
1282 if (data == NULL)
1283 goto nla_put_failure;
1284 if (expr->ops->dump(skb, expr) < 0)
1285 goto nla_put_failure;
1286 nla_nest_end(skb, data);
1287 }
1288
1289 return skb->len;
1290
1291nla_put_failure:
1292 return -1;
1293};
1294
1295struct nft_expr_info {
1296 const struct nft_expr_ops *ops;
Patrick McHardyef1f7df2013-10-10 11:41:20 +02001297 struct nlattr *tb[NFT_EXPR_MAXATTR + 1];
Patrick McHardy96518512013-10-14 11:00:02 +02001298};
1299
Pablo Neira Ayuso0ca743a2013-10-14 00:06:06 +02001300static int nf_tables_expr_parse(const struct nft_ctx *ctx,
1301 const struct nlattr *nla,
Patrick McHardy96518512013-10-14 11:00:02 +02001302 struct nft_expr_info *info)
1303{
Patrick McHardyef1f7df2013-10-10 11:41:20 +02001304 const struct nft_expr_type *type;
Patrick McHardy96518512013-10-14 11:00:02 +02001305 const struct nft_expr_ops *ops;
Patrick McHardyef1f7df2013-10-10 11:41:20 +02001306 struct nlattr *tb[NFTA_EXPR_MAX + 1];
Patrick McHardy96518512013-10-14 11:00:02 +02001307 int err;
1308
Patrick McHardyef1f7df2013-10-10 11:41:20 +02001309 err = nla_parse_nested(tb, NFTA_EXPR_MAX, nla, nft_expr_policy);
Patrick McHardy96518512013-10-14 11:00:02 +02001310 if (err < 0)
1311 return err;
1312
Patrick McHardy64d46802014-02-05 15:03:37 +00001313 type = nft_expr_type_get(ctx->afi->family, tb[NFTA_EXPR_NAME]);
Patrick McHardyef1f7df2013-10-10 11:41:20 +02001314 if (IS_ERR(type))
1315 return PTR_ERR(type);
1316
1317 if (tb[NFTA_EXPR_DATA]) {
1318 err = nla_parse_nested(info->tb, type->maxattr,
1319 tb[NFTA_EXPR_DATA], type->policy);
1320 if (err < 0)
1321 goto err1;
1322 } else
1323 memset(info->tb, 0, sizeof(info->tb[0]) * (type->maxattr + 1));
1324
1325 if (type->select_ops != NULL) {
Pablo Neira Ayuso0ca743a2013-10-14 00:06:06 +02001326 ops = type->select_ops(ctx,
1327 (const struct nlattr * const *)info->tb);
Patrick McHardyef1f7df2013-10-10 11:41:20 +02001328 if (IS_ERR(ops)) {
1329 err = PTR_ERR(ops);
1330 goto err1;
1331 }
1332 } else
1333 ops = type->ops;
1334
Patrick McHardy96518512013-10-14 11:00:02 +02001335 info->ops = ops;
1336 return 0;
Patrick McHardyef1f7df2013-10-10 11:41:20 +02001337
1338err1:
1339 module_put(type->owner);
1340 return err;
Patrick McHardy96518512013-10-14 11:00:02 +02001341}
1342
1343static int nf_tables_newexpr(const struct nft_ctx *ctx,
Patrick McHardyef1f7df2013-10-10 11:41:20 +02001344 const struct nft_expr_info *info,
Patrick McHardy96518512013-10-14 11:00:02 +02001345 struct nft_expr *expr)
1346{
1347 const struct nft_expr_ops *ops = info->ops;
1348 int err;
1349
1350 expr->ops = ops;
1351 if (ops->init) {
Patrick McHardyef1f7df2013-10-10 11:41:20 +02001352 err = ops->init(ctx, expr, (const struct nlattr **)info->tb);
Patrick McHardy96518512013-10-14 11:00:02 +02001353 if (err < 0)
1354 goto err1;
1355 }
1356
Patrick McHardy96518512013-10-14 11:00:02 +02001357 return 0;
1358
1359err1:
1360 expr->ops = NULL;
1361 return err;
1362}
1363
Patrick McHardy62472bc2014-03-07 19:08:30 +01001364static void nf_tables_expr_destroy(const struct nft_ctx *ctx,
1365 struct nft_expr *expr)
Patrick McHardy96518512013-10-14 11:00:02 +02001366{
1367 if (expr->ops->destroy)
Patrick McHardy62472bc2014-03-07 19:08:30 +01001368 expr->ops->destroy(ctx, expr);
Patrick McHardyef1f7df2013-10-10 11:41:20 +02001369 module_put(expr->ops->type->owner);
Patrick McHardy96518512013-10-14 11:00:02 +02001370}
1371
1372/*
1373 * Rules
1374 */
1375
1376static struct nft_rule *__nf_tables_rule_lookup(const struct nft_chain *chain,
1377 u64 handle)
1378{
1379 struct nft_rule *rule;
1380
1381 // FIXME: this sucks
1382 list_for_each_entry(rule, &chain->rules, list) {
1383 if (handle == rule->handle)
1384 return rule;
1385 }
1386
1387 return ERR_PTR(-ENOENT);
1388}
1389
1390static struct nft_rule *nf_tables_rule_lookup(const struct nft_chain *chain,
1391 const struct nlattr *nla)
1392{
1393 if (nla == NULL)
1394 return ERR_PTR(-EINVAL);
1395
1396 return __nf_tables_rule_lookup(chain, be64_to_cpu(nla_get_be64(nla)));
1397}
1398
1399static const struct nla_policy nft_rule_policy[NFTA_RULE_MAX + 1] = {
1400 [NFTA_RULE_TABLE] = { .type = NLA_STRING },
1401 [NFTA_RULE_CHAIN] = { .type = NLA_STRING,
1402 .len = NFT_CHAIN_MAXNAMELEN - 1 },
1403 [NFTA_RULE_HANDLE] = { .type = NLA_U64 },
1404 [NFTA_RULE_EXPRESSIONS] = { .type = NLA_NESTED },
Pablo Neira Ayuso0ca743a2013-10-14 00:06:06 +02001405 [NFTA_RULE_COMPAT] = { .type = NLA_NESTED },
Eric Leblond5e948462013-10-10 13:41:44 +02001406 [NFTA_RULE_POSITION] = { .type = NLA_U64 },
Pablo Neira Ayuso0768b3b2014-02-19 17:27:06 +01001407 [NFTA_RULE_USERDATA] = { .type = NLA_BINARY,
1408 .len = NFT_USERDATA_MAXLEN },
Patrick McHardy96518512013-10-14 11:00:02 +02001409};
1410
1411static int nf_tables_fill_rule_info(struct sk_buff *skb, u32 portid, u32 seq,
1412 int event, u32 flags, int family,
1413 const struct nft_table *table,
1414 const struct nft_chain *chain,
1415 const struct nft_rule *rule)
1416{
1417 struct nlmsghdr *nlh;
1418 struct nfgenmsg *nfmsg;
1419 const struct nft_expr *expr, *next;
1420 struct nlattr *list;
Eric Leblond5e948462013-10-10 13:41:44 +02001421 const struct nft_rule *prule;
1422 int type = event | NFNL_SUBSYS_NFTABLES << 8;
Patrick McHardy96518512013-10-14 11:00:02 +02001423
Eric Leblond5e948462013-10-10 13:41:44 +02001424 nlh = nlmsg_put(skb, portid, seq, type, sizeof(struct nfgenmsg),
Patrick McHardy96518512013-10-14 11:00:02 +02001425 flags);
1426 if (nlh == NULL)
1427 goto nla_put_failure;
1428
1429 nfmsg = nlmsg_data(nlh);
1430 nfmsg->nfgen_family = family;
1431 nfmsg->version = NFNETLINK_V0;
1432 nfmsg->res_id = 0;
1433
1434 if (nla_put_string(skb, NFTA_RULE_TABLE, table->name))
1435 goto nla_put_failure;
1436 if (nla_put_string(skb, NFTA_RULE_CHAIN, chain->name))
1437 goto nla_put_failure;
1438 if (nla_put_be64(skb, NFTA_RULE_HANDLE, cpu_to_be64(rule->handle)))
1439 goto nla_put_failure;
1440
Eric Leblond5e948462013-10-10 13:41:44 +02001441 if ((event != NFT_MSG_DELRULE) && (rule->list.prev != &chain->rules)) {
1442 prule = list_entry(rule->list.prev, struct nft_rule, list);
1443 if (nla_put_be64(skb, NFTA_RULE_POSITION,
1444 cpu_to_be64(prule->handle)))
1445 goto nla_put_failure;
1446 }
1447
Patrick McHardy96518512013-10-14 11:00:02 +02001448 list = nla_nest_start(skb, NFTA_RULE_EXPRESSIONS);
1449 if (list == NULL)
1450 goto nla_put_failure;
1451 nft_rule_for_each_expr(expr, next, rule) {
1452 struct nlattr *elem = nla_nest_start(skb, NFTA_LIST_ELEM);
1453 if (elem == NULL)
1454 goto nla_put_failure;
1455 if (nf_tables_fill_expr_info(skb, expr) < 0)
1456 goto nla_put_failure;
1457 nla_nest_end(skb, elem);
1458 }
1459 nla_nest_end(skb, list);
1460
Pablo Neira Ayuso0768b3b2014-02-19 17:27:06 +01001461 if (rule->ulen &&
1462 nla_put(skb, NFTA_RULE_USERDATA, rule->ulen, nft_userdata(rule)))
1463 goto nla_put_failure;
1464
Patrick McHardy96518512013-10-14 11:00:02 +02001465 return nlmsg_end(skb, nlh);
1466
1467nla_put_failure:
1468 nlmsg_trim(skb, nlh);
1469 return -1;
1470}
1471
Pablo Neira Ayuso35151d82014-05-05 17:12:40 +02001472static int nf_tables_rule_notify(const struct nft_ctx *ctx,
Patrick McHardy96518512013-10-14 11:00:02 +02001473 const struct nft_rule *rule,
Pablo Neira Ayuso35151d82014-05-05 17:12:40 +02001474 int event)
Patrick McHardy96518512013-10-14 11:00:02 +02001475{
Pablo Neira Ayuso35151d82014-05-05 17:12:40 +02001476 const struct sk_buff *oskb = ctx->skb;
Patrick McHardy96518512013-10-14 11:00:02 +02001477 struct sk_buff *skb;
1478 u32 portid = NETLINK_CB(oskb).portid;
Pablo Neira Ayuso35151d82014-05-05 17:12:40 +02001479 struct net *net = sock_net(oskb->sk);
1480 u32 seq = ctx->nlh->nlmsg_seq;
Patrick McHardy96518512013-10-14 11:00:02 +02001481 bool report;
1482 int err;
1483
Pablo Neira Ayuso35151d82014-05-05 17:12:40 +02001484 report = nlmsg_report(ctx->nlh);
Patrick McHardy96518512013-10-14 11:00:02 +02001485 if (!report && !nfnetlink_has_listeners(net, NFNLGRP_NFTABLES))
1486 return 0;
1487
1488 err = -ENOBUFS;
1489 skb = nlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL);
1490 if (skb == NULL)
1491 goto err;
1492
Pablo Neira Ayuso35151d82014-05-05 17:12:40 +02001493 err = nf_tables_fill_rule_info(skb, portid, seq, event, 0,
1494 ctx->afi->family, ctx->table,
1495 ctx->chain, rule);
Patrick McHardy96518512013-10-14 11:00:02 +02001496 if (err < 0) {
1497 kfree_skb(skb);
1498 goto err;
1499 }
1500
1501 err = nfnetlink_send(skb, net, portid, NFNLGRP_NFTABLES, report,
1502 GFP_KERNEL);
1503err:
1504 if (err < 0)
1505 nfnetlink_set_err(net, portid, NFNLGRP_NFTABLES, err);
1506 return err;
1507}
1508
Pablo Neira Ayuso0628b122013-10-14 11:05:33 +02001509static inline bool
1510nft_rule_is_active(struct net *net, const struct nft_rule *rule)
1511{
1512 return (rule->genmask & (1 << net->nft.gencursor)) == 0;
1513}
1514
1515static inline int gencursor_next(struct net *net)
1516{
1517 return net->nft.gencursor+1 == 1 ? 1 : 0;
1518}
1519
1520static inline int
1521nft_rule_is_active_next(struct net *net, const struct nft_rule *rule)
1522{
1523 return (rule->genmask & (1 << gencursor_next(net))) == 0;
1524}
1525
1526static inline void
1527nft_rule_activate_next(struct net *net, struct nft_rule *rule)
1528{
1529 /* Now inactive, will be active in the future */
1530 rule->genmask = (1 << net->nft.gencursor);
1531}
1532
1533static inline void
1534nft_rule_disactivate_next(struct net *net, struct nft_rule *rule)
1535{
1536 rule->genmask = (1 << gencursor_next(net));
1537}
1538
1539static inline void nft_rule_clear(struct net *net, struct nft_rule *rule)
1540{
1541 rule->genmask = 0;
1542}
1543
Patrick McHardy96518512013-10-14 11:00:02 +02001544static int nf_tables_dump_rules(struct sk_buff *skb,
1545 struct netlink_callback *cb)
1546{
1547 const struct nfgenmsg *nfmsg = nlmsg_data(cb->nlh);
1548 const struct nft_af_info *afi;
1549 const struct nft_table *table;
1550 const struct nft_chain *chain;
1551 const struct nft_rule *rule;
1552 unsigned int idx = 0, s_idx = cb->args[0];
Pablo Neira Ayuso99633ab2013-10-10 23:28:33 +02001553 struct net *net = sock_net(skb->sk);
Patrick McHardy96518512013-10-14 11:00:02 +02001554 int family = nfmsg->nfgen_family;
Pablo Neira Ayuso0628b122013-10-14 11:05:33 +02001555 u8 genctr = ACCESS_ONCE(net->nft.genctr);
1556 u8 gencursor = ACCESS_ONCE(net->nft.gencursor);
Patrick McHardy96518512013-10-14 11:00:02 +02001557
Pablo Neira Ayuso99633ab2013-10-10 23:28:33 +02001558 list_for_each_entry(afi, &net->nft.af_info, list) {
Patrick McHardy96518512013-10-14 11:00:02 +02001559 if (family != NFPROTO_UNSPEC && family != afi->family)
1560 continue;
1561
1562 list_for_each_entry(table, &afi->tables, list) {
1563 list_for_each_entry(chain, &table->chains, list) {
1564 list_for_each_entry(rule, &chain->rules, list) {
Pablo Neira Ayuso0628b122013-10-14 11:05:33 +02001565 if (!nft_rule_is_active(net, rule))
1566 goto cont;
Patrick McHardy96518512013-10-14 11:00:02 +02001567 if (idx < s_idx)
1568 goto cont;
1569 if (idx > s_idx)
1570 memset(&cb->args[1], 0,
1571 sizeof(cb->args) - sizeof(cb->args[0]));
1572 if (nf_tables_fill_rule_info(skb, NETLINK_CB(cb->skb).portid,
1573 cb->nlh->nlmsg_seq,
1574 NFT_MSG_NEWRULE,
1575 NLM_F_MULTI | NLM_F_APPEND,
1576 afi->family, table, chain, rule) < 0)
1577 goto done;
1578cont:
1579 idx++;
1580 }
1581 }
1582 }
1583 }
1584done:
Pablo Neira Ayuso0628b122013-10-14 11:05:33 +02001585 /* Invalidate this dump, a transition to the new generation happened */
1586 if (gencursor != net->nft.gencursor || genctr != net->nft.genctr)
1587 return -EBUSY;
1588
Patrick McHardy96518512013-10-14 11:00:02 +02001589 cb->args[0] = idx;
1590 return skb->len;
1591}
1592
1593static int nf_tables_getrule(struct sock *nlsk, struct sk_buff *skb,
1594 const struct nlmsghdr *nlh,
1595 const struct nlattr * const nla[])
1596{
1597 const struct nfgenmsg *nfmsg = nlmsg_data(nlh);
1598 const struct nft_af_info *afi;
1599 const struct nft_table *table;
1600 const struct nft_chain *chain;
1601 const struct nft_rule *rule;
1602 struct sk_buff *skb2;
Pablo Neira Ayuso99633ab2013-10-10 23:28:33 +02001603 struct net *net = sock_net(skb->sk);
Patrick McHardy96518512013-10-14 11:00:02 +02001604 int family = nfmsg->nfgen_family;
1605 int err;
1606
1607 if (nlh->nlmsg_flags & NLM_F_DUMP) {
1608 struct netlink_dump_control c = {
1609 .dump = nf_tables_dump_rules,
1610 };
1611 return netlink_dump_start(nlsk, skb, nlh, &c);
1612 }
1613
Pablo Neira Ayuso99633ab2013-10-10 23:28:33 +02001614 afi = nf_tables_afinfo_lookup(net, family, false);
Patrick McHardy96518512013-10-14 11:00:02 +02001615 if (IS_ERR(afi))
1616 return PTR_ERR(afi);
1617
Pablo Neira Ayuso93707612013-10-10 23:21:26 +02001618 table = nf_tables_table_lookup(afi, nla[NFTA_RULE_TABLE]);
Patrick McHardy96518512013-10-14 11:00:02 +02001619 if (IS_ERR(table))
1620 return PTR_ERR(table);
Pablo Neira Ayuso55dd6f92014-04-03 11:53:37 +02001621 if (table->flags & NFT_TABLE_INACTIVE)
1622 return -ENOENT;
Patrick McHardy96518512013-10-14 11:00:02 +02001623
1624 chain = nf_tables_chain_lookup(table, nla[NFTA_RULE_CHAIN]);
1625 if (IS_ERR(chain))
1626 return PTR_ERR(chain);
Pablo Neira Ayuso91c7b382014-04-09 11:58:08 +02001627 if (chain->flags & NFT_CHAIN_INACTIVE)
1628 return -ENOENT;
Patrick McHardy96518512013-10-14 11:00:02 +02001629
1630 rule = nf_tables_rule_lookup(chain, nla[NFTA_RULE_HANDLE]);
1631 if (IS_ERR(rule))
1632 return PTR_ERR(rule);
1633
1634 skb2 = alloc_skb(NLMSG_GOODSIZE, GFP_KERNEL);
1635 if (!skb2)
1636 return -ENOMEM;
1637
1638 err = nf_tables_fill_rule_info(skb2, NETLINK_CB(skb).portid,
1639 nlh->nlmsg_seq, NFT_MSG_NEWRULE, 0,
1640 family, table, chain, rule);
1641 if (err < 0)
1642 goto err;
1643
1644 return nlmsg_unicast(nlsk, skb2, NETLINK_CB(skb).portid);
1645
1646err:
1647 kfree_skb(skb2);
1648 return err;
1649}
1650
Patrick McHardy62472bc2014-03-07 19:08:30 +01001651static void nf_tables_rule_destroy(const struct nft_ctx *ctx,
1652 struct nft_rule *rule)
Patrick McHardy96518512013-10-14 11:00:02 +02001653{
Patrick McHardy96518512013-10-14 11:00:02 +02001654 struct nft_expr *expr;
1655
1656 /*
1657 * Careful: some expressions might not be initialized in case this
1658 * is called on error from nf_tables_newrule().
1659 */
1660 expr = nft_expr_first(rule);
1661 while (expr->ops && expr != nft_expr_last(rule)) {
Patrick McHardy62472bc2014-03-07 19:08:30 +01001662 nf_tables_expr_destroy(ctx, expr);
Patrick McHardy96518512013-10-14 11:00:02 +02001663 expr = nft_expr_next(expr);
1664 }
1665 kfree(rule);
1666}
1667
Pablo Neira Ayusob380e5c2014-04-04 01:38:51 +02001668static struct nft_trans *nft_trans_rule_add(struct nft_ctx *ctx, int msg_type,
Pablo Neira Ayuso1081d112014-04-04 01:24:07 +02001669 struct nft_rule *rule)
1670{
1671 struct nft_trans *trans;
1672
Pablo Neira Ayusob380e5c2014-04-04 01:38:51 +02001673 trans = nft_trans_alloc(ctx, msg_type, sizeof(struct nft_trans_rule));
Pablo Neira Ayuso1081d112014-04-04 01:24:07 +02001674 if (trans == NULL)
1675 return NULL;
1676
1677 nft_trans_rule(trans) = rule;
1678 list_add_tail(&trans->list, &ctx->net->nft.commit_list);
1679
1680 return trans;
1681}
1682
Patrick McHardy96518512013-10-14 11:00:02 +02001683#define NFT_RULE_MAXEXPRS 128
1684
1685static struct nft_expr_info *info;
1686
1687static int nf_tables_newrule(struct sock *nlsk, struct sk_buff *skb,
1688 const struct nlmsghdr *nlh,
1689 const struct nlattr * const nla[])
1690{
1691 const struct nfgenmsg *nfmsg = nlmsg_data(nlh);
Pablo Neira Ayuso7c95f6d2014-04-04 01:22:45 +02001692 struct nft_af_info *afi;
Pablo Neira Ayuso99633ab2013-10-10 23:28:33 +02001693 struct net *net = sock_net(skb->sk);
Patrick McHardy96518512013-10-14 11:00:02 +02001694 struct nft_table *table;
1695 struct nft_chain *chain;
1696 struct nft_rule *rule, *old_rule = NULL;
Pablo Neira Ayuso1081d112014-04-04 01:24:07 +02001697 struct nft_trans *trans = NULL;
Patrick McHardy96518512013-10-14 11:00:02 +02001698 struct nft_expr *expr;
1699 struct nft_ctx ctx;
1700 struct nlattr *tmp;
Pablo Neira Ayuso0768b3b2014-02-19 17:27:06 +01001701 unsigned int size, i, n, ulen = 0;
Patrick McHardy96518512013-10-14 11:00:02 +02001702 int err, rem;
1703 bool create;
Eric Leblond5e948462013-10-10 13:41:44 +02001704 u64 handle, pos_handle;
Patrick McHardy96518512013-10-14 11:00:02 +02001705
1706 create = nlh->nlmsg_flags & NLM_F_CREATE ? true : false;
1707
Pablo Neira Ayuso99633ab2013-10-10 23:28:33 +02001708 afi = nf_tables_afinfo_lookup(net, nfmsg->nfgen_family, create);
Patrick McHardy96518512013-10-14 11:00:02 +02001709 if (IS_ERR(afi))
1710 return PTR_ERR(afi);
1711
Pablo Neira Ayuso93707612013-10-10 23:21:26 +02001712 table = nf_tables_table_lookup(afi, nla[NFTA_RULE_TABLE]);
Patrick McHardy96518512013-10-14 11:00:02 +02001713 if (IS_ERR(table))
1714 return PTR_ERR(table);
1715
1716 chain = nf_tables_chain_lookup(table, nla[NFTA_RULE_CHAIN]);
1717 if (IS_ERR(chain))
1718 return PTR_ERR(chain);
1719
1720 if (nla[NFTA_RULE_HANDLE]) {
1721 handle = be64_to_cpu(nla_get_be64(nla[NFTA_RULE_HANDLE]));
1722 rule = __nf_tables_rule_lookup(chain, handle);
1723 if (IS_ERR(rule))
1724 return PTR_ERR(rule);
1725
1726 if (nlh->nlmsg_flags & NLM_F_EXCL)
1727 return -EEXIST;
1728 if (nlh->nlmsg_flags & NLM_F_REPLACE)
1729 old_rule = rule;
1730 else
1731 return -EOPNOTSUPP;
1732 } else {
1733 if (!create || nlh->nlmsg_flags & NLM_F_REPLACE)
1734 return -EINVAL;
1735 handle = nf_tables_alloc_handle(table);
1736 }
1737
Eric Leblond5e948462013-10-10 13:41:44 +02001738 if (nla[NFTA_RULE_POSITION]) {
1739 if (!(nlh->nlmsg_flags & NLM_F_CREATE))
1740 return -EOPNOTSUPP;
1741
1742 pos_handle = be64_to_cpu(nla_get_be64(nla[NFTA_RULE_POSITION]));
1743 old_rule = __nf_tables_rule_lookup(chain, pos_handle);
1744 if (IS_ERR(old_rule))
1745 return PTR_ERR(old_rule);
1746 }
1747
Pablo Neira Ayuso0ca743a2013-10-14 00:06:06 +02001748 nft_ctx_init(&ctx, skb, nlh, afi, table, chain, nla);
1749
Patrick McHardy96518512013-10-14 11:00:02 +02001750 n = 0;
1751 size = 0;
1752 if (nla[NFTA_RULE_EXPRESSIONS]) {
1753 nla_for_each_nested(tmp, nla[NFTA_RULE_EXPRESSIONS], rem) {
1754 err = -EINVAL;
1755 if (nla_type(tmp) != NFTA_LIST_ELEM)
1756 goto err1;
1757 if (n == NFT_RULE_MAXEXPRS)
1758 goto err1;
Pablo Neira Ayuso0ca743a2013-10-14 00:06:06 +02001759 err = nf_tables_expr_parse(&ctx, tmp, &info[n]);
Patrick McHardy96518512013-10-14 11:00:02 +02001760 if (err < 0)
1761 goto err1;
1762 size += info[n].ops->size;
1763 n++;
1764 }
1765 }
1766
Pablo Neira Ayuso0768b3b2014-02-19 17:27:06 +01001767 if (nla[NFTA_RULE_USERDATA])
1768 ulen = nla_len(nla[NFTA_RULE_USERDATA]);
1769
Patrick McHardy96518512013-10-14 11:00:02 +02001770 err = -ENOMEM;
Pablo Neira Ayuso0768b3b2014-02-19 17:27:06 +01001771 rule = kzalloc(sizeof(*rule) + size + ulen, GFP_KERNEL);
Patrick McHardy96518512013-10-14 11:00:02 +02001772 if (rule == NULL)
1773 goto err1;
1774
Pablo Neira Ayuso0628b122013-10-14 11:05:33 +02001775 nft_rule_activate_next(net, rule);
1776
Patrick McHardy96518512013-10-14 11:00:02 +02001777 rule->handle = handle;
1778 rule->dlen = size;
Pablo Neira Ayuso0768b3b2014-02-19 17:27:06 +01001779 rule->ulen = ulen;
1780
1781 if (ulen)
1782 nla_memcpy(nft_userdata(rule), nla[NFTA_RULE_USERDATA], ulen);
Patrick McHardy96518512013-10-14 11:00:02 +02001783
Patrick McHardy96518512013-10-14 11:00:02 +02001784 expr = nft_expr_first(rule);
1785 for (i = 0; i < n; i++) {
1786 err = nf_tables_newexpr(&ctx, &info[i], expr);
1787 if (err < 0)
1788 goto err2;
Patrick McHardyef1f7df2013-10-10 11:41:20 +02001789 info[i].ops = NULL;
Patrick McHardy96518512013-10-14 11:00:02 +02001790 expr = nft_expr_next(expr);
1791 }
1792
Patrick McHardy96518512013-10-14 11:00:02 +02001793 if (nlh->nlmsg_flags & NLM_F_REPLACE) {
Pablo Neira Ayuso0628b122013-10-14 11:05:33 +02001794 if (nft_rule_is_active_next(net, old_rule)) {
Pablo Neira Ayusob380e5c2014-04-04 01:38:51 +02001795 trans = nft_trans_rule_add(&ctx, NFT_MSG_NEWRULE,
1796 old_rule);
Pablo Neira Ayuso1081d112014-04-04 01:24:07 +02001797 if (trans == NULL) {
Pablo Neira Ayuso0628b122013-10-14 11:05:33 +02001798 err = -ENOMEM;
1799 goto err2;
1800 }
1801 nft_rule_disactivate_next(net, old_rule);
1802 list_add_tail(&rule->list, &old_rule->list);
1803 } else {
1804 err = -ENOENT;
1805 goto err2;
1806 }
Patrick McHardy96518512013-10-14 11:00:02 +02001807 } else if (nlh->nlmsg_flags & NLM_F_APPEND)
Eric Leblond5e948462013-10-10 13:41:44 +02001808 if (old_rule)
1809 list_add_rcu(&rule->list, &old_rule->list);
1810 else
1811 list_add_tail_rcu(&rule->list, &chain->rules);
1812 else {
1813 if (old_rule)
1814 list_add_tail_rcu(&rule->list, &old_rule->list);
1815 else
1816 list_add_rcu(&rule->list, &chain->rules);
1817 }
Patrick McHardy96518512013-10-14 11:00:02 +02001818
Pablo Neira Ayusob380e5c2014-04-04 01:38:51 +02001819 if (nft_trans_rule_add(&ctx, NFT_MSG_NEWRULE, rule) == NULL) {
Pablo Neira Ayuso0628b122013-10-14 11:05:33 +02001820 err = -ENOMEM;
1821 goto err3;
1822 }
Patrick McHardy96518512013-10-14 11:00:02 +02001823 return 0;
1824
Pablo Neira Ayuso0628b122013-10-14 11:05:33 +02001825err3:
1826 list_del_rcu(&rule->list);
Pablo Neira Ayuso1081d112014-04-04 01:24:07 +02001827 if (trans) {
1828 list_del_rcu(&nft_trans_rule(trans)->list);
1829 nft_rule_clear(net, nft_trans_rule(trans));
1830 nft_trans_destroy(trans);
Pablo Neira Ayuso0628b122013-10-14 11:05:33 +02001831 }
Patrick McHardy96518512013-10-14 11:00:02 +02001832err2:
Patrick McHardy62472bc2014-03-07 19:08:30 +01001833 nf_tables_rule_destroy(&ctx, rule);
Patrick McHardy96518512013-10-14 11:00:02 +02001834err1:
1835 for (i = 0; i < n; i++) {
1836 if (info[i].ops != NULL)
Patrick McHardyef1f7df2013-10-10 11:41:20 +02001837 module_put(info[i].ops->type->owner);
Patrick McHardy96518512013-10-14 11:00:02 +02001838 }
1839 return err;
1840}
1841
Pablo Neira Ayuso0628b122013-10-14 11:05:33 +02001842static int
1843nf_tables_delrule_one(struct nft_ctx *ctx, struct nft_rule *rule)
1844{
1845 /* You cannot delete the same rule twice */
1846 if (nft_rule_is_active_next(ctx->net, rule)) {
Pablo Neira Ayusob380e5c2014-04-04 01:38:51 +02001847 if (nft_trans_rule_add(ctx, NFT_MSG_DELRULE, rule) == NULL)
Pablo Neira Ayuso0628b122013-10-14 11:05:33 +02001848 return -ENOMEM;
1849 nft_rule_disactivate_next(ctx->net, rule);
1850 return 0;
1851 }
1852 return -ENOENT;
1853}
1854
Pablo Neira Ayusocf9dc092013-11-24 20:39:10 +01001855static int nf_table_delrule_by_chain(struct nft_ctx *ctx)
1856{
1857 struct nft_rule *rule;
1858 int err;
1859
1860 list_for_each_entry(rule, &ctx->chain->rules, list) {
1861 err = nf_tables_delrule_one(ctx, rule);
1862 if (err < 0)
1863 return err;
1864 }
1865 return 0;
1866}
1867
Patrick McHardy96518512013-10-14 11:00:02 +02001868static int nf_tables_delrule(struct sock *nlsk, struct sk_buff *skb,
1869 const struct nlmsghdr *nlh,
1870 const struct nlattr * const nla[])
1871{
1872 const struct nfgenmsg *nfmsg = nlmsg_data(nlh);
Pablo Neira Ayuso7c95f6d2014-04-04 01:22:45 +02001873 struct nft_af_info *afi;
Pablo Neira Ayuso99633ab2013-10-10 23:28:33 +02001874 struct net *net = sock_net(skb->sk);
Pablo Neira Ayuso7c95f6d2014-04-04 01:22:45 +02001875 struct nft_table *table;
Pablo Neira Ayusocf9dc092013-11-24 20:39:10 +01001876 struct nft_chain *chain = NULL;
1877 struct nft_rule *rule;
Pablo Neira Ayuso0628b122013-10-14 11:05:33 +02001878 int family = nfmsg->nfgen_family, err = 0;
1879 struct nft_ctx ctx;
Patrick McHardy96518512013-10-14 11:00:02 +02001880
Pablo Neira Ayuso99633ab2013-10-10 23:28:33 +02001881 afi = nf_tables_afinfo_lookup(net, family, false);
Patrick McHardy96518512013-10-14 11:00:02 +02001882 if (IS_ERR(afi))
1883 return PTR_ERR(afi);
1884
Pablo Neira Ayuso93707612013-10-10 23:21:26 +02001885 table = nf_tables_table_lookup(afi, nla[NFTA_RULE_TABLE]);
Patrick McHardy96518512013-10-14 11:00:02 +02001886 if (IS_ERR(table))
1887 return PTR_ERR(table);
Pablo Neira Ayuso55dd6f92014-04-03 11:53:37 +02001888 if (table->flags & NFT_TABLE_INACTIVE)
1889 return -ENOENT;
Patrick McHardy96518512013-10-14 11:00:02 +02001890
Pablo Neira Ayusocf9dc092013-11-24 20:39:10 +01001891 if (nla[NFTA_RULE_CHAIN]) {
1892 chain = nf_tables_chain_lookup(table, nla[NFTA_RULE_CHAIN]);
1893 if (IS_ERR(chain))
1894 return PTR_ERR(chain);
1895 }
Patrick McHardy96518512013-10-14 11:00:02 +02001896
Pablo Neira Ayuso0628b122013-10-14 11:05:33 +02001897 nft_ctx_init(&ctx, skb, nlh, afi, table, chain, nla);
1898
Pablo Neira Ayusocf9dc092013-11-24 20:39:10 +01001899 if (chain) {
1900 if (nla[NFTA_RULE_HANDLE]) {
1901 rule = nf_tables_rule_lookup(chain,
1902 nla[NFTA_RULE_HANDLE]);
1903 if (IS_ERR(rule))
1904 return PTR_ERR(rule);
Patrick McHardy96518512013-10-14 11:00:02 +02001905
Pablo Neira Ayuso0628b122013-10-14 11:05:33 +02001906 err = nf_tables_delrule_one(&ctx, rule);
Pablo Neira Ayusocf9dc092013-11-24 20:39:10 +01001907 } else {
1908 err = nf_table_delrule_by_chain(&ctx);
1909 }
1910 } else {
1911 list_for_each_entry(chain, &table->chains, list) {
1912 ctx.chain = chain;
1913 err = nf_table_delrule_by_chain(&ctx);
Pablo Neira Ayuso0628b122013-10-14 11:05:33 +02001914 if (err < 0)
1915 break;
Patrick McHardy96518512013-10-14 11:00:02 +02001916 }
1917 }
1918
Pablo Neira Ayuso0628b122013-10-14 11:05:33 +02001919 return err;
1920}
1921
Patrick McHardy20a69342013-10-11 12:06:22 +02001922/*
1923 * Sets
1924 */
1925
1926static LIST_HEAD(nf_tables_set_ops);
1927
1928int nft_register_set(struct nft_set_ops *ops)
1929{
1930 nfnl_lock(NFNL_SUBSYS_NFTABLES);
1931 list_add_tail(&ops->list, &nf_tables_set_ops);
1932 nfnl_unlock(NFNL_SUBSYS_NFTABLES);
1933 return 0;
1934}
1935EXPORT_SYMBOL_GPL(nft_register_set);
1936
1937void nft_unregister_set(struct nft_set_ops *ops)
1938{
1939 nfnl_lock(NFNL_SUBSYS_NFTABLES);
1940 list_del(&ops->list);
1941 nfnl_unlock(NFNL_SUBSYS_NFTABLES);
1942}
1943EXPORT_SYMBOL_GPL(nft_unregister_set);
1944
Patrick McHardyc50b9602014-03-28 10:19:47 +00001945/*
1946 * Select a set implementation based on the data characteristics and the
1947 * given policy. The total memory use might not be known if no size is
1948 * given, in that case the amount of memory per element is used.
1949 */
1950static const struct nft_set_ops *
1951nft_select_set_ops(const struct nlattr * const nla[],
1952 const struct nft_set_desc *desc,
1953 enum nft_set_policies policy)
Patrick McHardy20a69342013-10-11 12:06:22 +02001954{
Patrick McHardyc50b9602014-03-28 10:19:47 +00001955 const struct nft_set_ops *ops, *bops;
1956 struct nft_set_estimate est, best;
Patrick McHardy20a69342013-10-11 12:06:22 +02001957 u32 features;
1958
1959#ifdef CONFIG_MODULES
1960 if (list_empty(&nf_tables_set_ops)) {
1961 nfnl_unlock(NFNL_SUBSYS_NFTABLES);
1962 request_module("nft-set");
1963 nfnl_lock(NFNL_SUBSYS_NFTABLES);
1964 if (!list_empty(&nf_tables_set_ops))
1965 return ERR_PTR(-EAGAIN);
1966 }
1967#endif
1968 features = 0;
1969 if (nla[NFTA_SET_FLAGS] != NULL) {
1970 features = ntohl(nla_get_be32(nla[NFTA_SET_FLAGS]));
1971 features &= NFT_SET_INTERVAL | NFT_SET_MAP;
1972 }
1973
Patrick McHardyc50b9602014-03-28 10:19:47 +00001974 bops = NULL;
1975 best.size = ~0;
1976 best.class = ~0;
1977
Patrick McHardy20a69342013-10-11 12:06:22 +02001978 list_for_each_entry(ops, &nf_tables_set_ops, list) {
1979 if ((ops->features & features) != features)
1980 continue;
Patrick McHardyc50b9602014-03-28 10:19:47 +00001981 if (!ops->estimate(desc, features, &est))
1982 continue;
1983
1984 switch (policy) {
1985 case NFT_SET_POL_PERFORMANCE:
1986 if (est.class < best.class)
1987 break;
1988 if (est.class == best.class && est.size < best.size)
1989 break;
1990 continue;
1991 case NFT_SET_POL_MEMORY:
1992 if (est.size < best.size)
1993 break;
1994 if (est.size == best.size && est.class < best.class)
1995 break;
1996 continue;
1997 default:
1998 break;
1999 }
2000
Patrick McHardy20a69342013-10-11 12:06:22 +02002001 if (!try_module_get(ops->owner))
2002 continue;
Patrick McHardyc50b9602014-03-28 10:19:47 +00002003 if (bops != NULL)
2004 module_put(bops->owner);
2005
2006 bops = ops;
2007 best = est;
Patrick McHardy20a69342013-10-11 12:06:22 +02002008 }
2009
Patrick McHardyc50b9602014-03-28 10:19:47 +00002010 if (bops != NULL)
2011 return bops;
2012
Patrick McHardy20a69342013-10-11 12:06:22 +02002013 return ERR_PTR(-EOPNOTSUPP);
2014}
2015
2016static const struct nla_policy nft_set_policy[NFTA_SET_MAX + 1] = {
2017 [NFTA_SET_TABLE] = { .type = NLA_STRING },
2018 [NFTA_SET_NAME] = { .type = NLA_STRING },
2019 [NFTA_SET_FLAGS] = { .type = NLA_U32 },
2020 [NFTA_SET_KEY_TYPE] = { .type = NLA_U32 },
2021 [NFTA_SET_KEY_LEN] = { .type = NLA_U32 },
2022 [NFTA_SET_DATA_TYPE] = { .type = NLA_U32 },
2023 [NFTA_SET_DATA_LEN] = { .type = NLA_U32 },
Patrick McHardyc50b9602014-03-28 10:19:47 +00002024 [NFTA_SET_POLICY] = { .type = NLA_U32 },
2025 [NFTA_SET_DESC] = { .type = NLA_NESTED },
Pablo Neira Ayuso958bee12014-04-03 11:48:44 +02002026 [NFTA_SET_ID] = { .type = NLA_U32 },
Patrick McHardyc50b9602014-03-28 10:19:47 +00002027};
2028
2029static const struct nla_policy nft_set_desc_policy[NFTA_SET_DESC_MAX + 1] = {
2030 [NFTA_SET_DESC_SIZE] = { .type = NLA_U32 },
Patrick McHardy20a69342013-10-11 12:06:22 +02002031};
2032
2033static int nft_ctx_init_from_setattr(struct nft_ctx *ctx,
2034 const struct sk_buff *skb,
2035 const struct nlmsghdr *nlh,
2036 const struct nlattr * const nla[])
2037{
Pablo Neira Ayuso99633ab2013-10-10 23:28:33 +02002038 struct net *net = sock_net(skb->sk);
Patrick McHardy20a69342013-10-11 12:06:22 +02002039 const struct nfgenmsg *nfmsg = nlmsg_data(nlh);
Pablo Neira Ayuso7c95f6d2014-04-04 01:22:45 +02002040 struct nft_af_info *afi = NULL;
2041 struct nft_table *table = NULL;
Patrick McHardy20a69342013-10-11 12:06:22 +02002042
Pablo Neira Ayusoc9c8e482013-12-26 16:49:03 +01002043 if (nfmsg->nfgen_family != NFPROTO_UNSPEC) {
2044 afi = nf_tables_afinfo_lookup(net, nfmsg->nfgen_family, false);
2045 if (IS_ERR(afi))
2046 return PTR_ERR(afi);
2047 }
Patrick McHardy20a69342013-10-11 12:06:22 +02002048
2049 if (nla[NFTA_SET_TABLE] != NULL) {
Patrick McHardyec2c9932014-02-05 15:03:35 +00002050 if (afi == NULL)
2051 return -EAFNOSUPPORT;
2052
Pablo Neira Ayuso93707612013-10-10 23:21:26 +02002053 table = nf_tables_table_lookup(afi, nla[NFTA_SET_TABLE]);
Patrick McHardy20a69342013-10-11 12:06:22 +02002054 if (IS_ERR(table))
2055 return PTR_ERR(table);
Pablo Neira Ayuso55dd6f92014-04-03 11:53:37 +02002056 if (table->flags & NFT_TABLE_INACTIVE)
2057 return -ENOENT;
Patrick McHardy20a69342013-10-11 12:06:22 +02002058 }
2059
Pablo Neira Ayuso0ca743a2013-10-14 00:06:06 +02002060 nft_ctx_init(ctx, skb, nlh, afi, table, NULL, nla);
Patrick McHardy20a69342013-10-11 12:06:22 +02002061 return 0;
2062}
2063
2064struct nft_set *nf_tables_set_lookup(const struct nft_table *table,
2065 const struct nlattr *nla)
2066{
2067 struct nft_set *set;
2068
2069 if (nla == NULL)
2070 return ERR_PTR(-EINVAL);
2071
2072 list_for_each_entry(set, &table->sets, list) {
2073 if (!nla_strcmp(nla, set->name))
2074 return set;
2075 }
2076 return ERR_PTR(-ENOENT);
2077}
2078
Pablo Neira Ayuso958bee12014-04-03 11:48:44 +02002079struct nft_set *nf_tables_set_lookup_byid(const struct net *net,
2080 const struct nlattr *nla)
2081{
2082 struct nft_trans *trans;
2083 u32 id = ntohl(nla_get_be32(nla));
2084
2085 list_for_each_entry(trans, &net->nft.commit_list, list) {
2086 if (trans->msg_type == NFT_MSG_NEWSET &&
2087 id == nft_trans_set_id(trans))
2088 return nft_trans_set(trans);
2089 }
2090 return ERR_PTR(-ENOENT);
2091}
2092
Patrick McHardy20a69342013-10-11 12:06:22 +02002093static int nf_tables_set_alloc_name(struct nft_ctx *ctx, struct nft_set *set,
2094 const char *name)
2095{
2096 const struct nft_set *i;
2097 const char *p;
2098 unsigned long *inuse;
Patrick McHardy60eb1892014-03-07 12:34:05 +01002099 unsigned int n = 0, min = 0;
Patrick McHardy20a69342013-10-11 12:06:22 +02002100
2101 p = strnchr(name, IFNAMSIZ, '%');
2102 if (p != NULL) {
2103 if (p[1] != 'd' || strchr(p + 2, '%'))
2104 return -EINVAL;
2105
2106 inuse = (unsigned long *)get_zeroed_page(GFP_KERNEL);
2107 if (inuse == NULL)
2108 return -ENOMEM;
Patrick McHardy60eb1892014-03-07 12:34:05 +01002109cont:
Patrick McHardy20a69342013-10-11 12:06:22 +02002110 list_for_each_entry(i, &ctx->table->sets, list) {
Daniel Borkmann14662912013-12-31 12:40:05 +01002111 int tmp;
2112
2113 if (!sscanf(i->name, name, &tmp))
Patrick McHardy20a69342013-10-11 12:06:22 +02002114 continue;
Patrick McHardy60eb1892014-03-07 12:34:05 +01002115 if (tmp < min || tmp >= min + BITS_PER_BYTE * PAGE_SIZE)
Patrick McHardy20a69342013-10-11 12:06:22 +02002116 continue;
Daniel Borkmann14662912013-12-31 12:40:05 +01002117
Patrick McHardy60eb1892014-03-07 12:34:05 +01002118 set_bit(tmp - min, inuse);
Patrick McHardy20a69342013-10-11 12:06:22 +02002119 }
2120
Patrick McHardy53b70282014-02-05 12:26:22 +01002121 n = find_first_zero_bit(inuse, BITS_PER_BYTE * PAGE_SIZE);
Patrick McHardy60eb1892014-03-07 12:34:05 +01002122 if (n >= BITS_PER_BYTE * PAGE_SIZE) {
2123 min += BITS_PER_BYTE * PAGE_SIZE;
2124 memset(inuse, 0, PAGE_SIZE);
2125 goto cont;
2126 }
Patrick McHardy20a69342013-10-11 12:06:22 +02002127 free_page((unsigned long)inuse);
2128 }
2129
Patrick McHardy60eb1892014-03-07 12:34:05 +01002130 snprintf(set->name, sizeof(set->name), name, min + n);
Patrick McHardy20a69342013-10-11 12:06:22 +02002131 list_for_each_entry(i, &ctx->table->sets, list) {
2132 if (!strcmp(set->name, i->name))
2133 return -ENFILE;
2134 }
2135 return 0;
2136}
2137
2138static int nf_tables_fill_set(struct sk_buff *skb, const struct nft_ctx *ctx,
2139 const struct nft_set *set, u16 event, u16 flags)
2140{
2141 struct nfgenmsg *nfmsg;
2142 struct nlmsghdr *nlh;
Patrick McHardyc50b9602014-03-28 10:19:47 +00002143 struct nlattr *desc;
Patrick McHardy20a69342013-10-11 12:06:22 +02002144 u32 portid = NETLINK_CB(ctx->skb).portid;
2145 u32 seq = ctx->nlh->nlmsg_seq;
2146
2147 event |= NFNL_SUBSYS_NFTABLES << 8;
2148 nlh = nlmsg_put(skb, portid, seq, event, sizeof(struct nfgenmsg),
2149 flags);
2150 if (nlh == NULL)
2151 goto nla_put_failure;
2152
2153 nfmsg = nlmsg_data(nlh);
2154 nfmsg->nfgen_family = ctx->afi->family;
2155 nfmsg->version = NFNETLINK_V0;
2156 nfmsg->res_id = 0;
2157
2158 if (nla_put_string(skb, NFTA_SET_TABLE, ctx->table->name))
2159 goto nla_put_failure;
2160 if (nla_put_string(skb, NFTA_SET_NAME, set->name))
2161 goto nla_put_failure;
2162 if (set->flags != 0)
2163 if (nla_put_be32(skb, NFTA_SET_FLAGS, htonl(set->flags)))
2164 goto nla_put_failure;
2165
2166 if (nla_put_be32(skb, NFTA_SET_KEY_TYPE, htonl(set->ktype)))
2167 goto nla_put_failure;
2168 if (nla_put_be32(skb, NFTA_SET_KEY_LEN, htonl(set->klen)))
2169 goto nla_put_failure;
2170 if (set->flags & NFT_SET_MAP) {
2171 if (nla_put_be32(skb, NFTA_SET_DATA_TYPE, htonl(set->dtype)))
2172 goto nla_put_failure;
2173 if (nla_put_be32(skb, NFTA_SET_DATA_LEN, htonl(set->dlen)))
2174 goto nla_put_failure;
2175 }
2176
Patrick McHardyc50b9602014-03-28 10:19:47 +00002177 desc = nla_nest_start(skb, NFTA_SET_DESC);
2178 if (desc == NULL)
2179 goto nla_put_failure;
2180 if (set->size &&
2181 nla_put_be32(skb, NFTA_SET_DESC_SIZE, htonl(set->size)))
2182 goto nla_put_failure;
2183 nla_nest_end(skb, desc);
2184
Patrick McHardy20a69342013-10-11 12:06:22 +02002185 return nlmsg_end(skb, nlh);
2186
2187nla_put_failure:
2188 nlmsg_trim(skb, nlh);
2189 return -1;
2190}
2191
2192static int nf_tables_set_notify(const struct nft_ctx *ctx,
2193 const struct nft_set *set,
2194 int event)
2195{
2196 struct sk_buff *skb;
2197 u32 portid = NETLINK_CB(ctx->skb).portid;
Patrick McHardy20a69342013-10-11 12:06:22 +02002198 bool report;
2199 int err;
2200
2201 report = nlmsg_report(ctx->nlh);
Pablo Neira Ayuso99633ab2013-10-10 23:28:33 +02002202 if (!report && !nfnetlink_has_listeners(ctx->net, NFNLGRP_NFTABLES))
Patrick McHardy20a69342013-10-11 12:06:22 +02002203 return 0;
2204
2205 err = -ENOBUFS;
2206 skb = nlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL);
2207 if (skb == NULL)
2208 goto err;
2209
2210 err = nf_tables_fill_set(skb, ctx, set, event, 0);
2211 if (err < 0) {
2212 kfree_skb(skb);
2213 goto err;
2214 }
2215
Pablo Neira Ayuso99633ab2013-10-10 23:28:33 +02002216 err = nfnetlink_send(skb, ctx->net, portid, NFNLGRP_NFTABLES, report,
Patrick McHardy20a69342013-10-11 12:06:22 +02002217 GFP_KERNEL);
2218err:
2219 if (err < 0)
Pablo Neira Ayuso99633ab2013-10-10 23:28:33 +02002220 nfnetlink_set_err(ctx->net, portid, NFNLGRP_NFTABLES, err);
Patrick McHardy20a69342013-10-11 12:06:22 +02002221 return err;
2222}
2223
2224static int nf_tables_dump_sets_table(struct nft_ctx *ctx, struct sk_buff *skb,
2225 struct netlink_callback *cb)
2226{
2227 const struct nft_set *set;
2228 unsigned int idx = 0, s_idx = cb->args[0];
2229
2230 if (cb->args[1])
2231 return skb->len;
2232
2233 list_for_each_entry(set, &ctx->table->sets, list) {
2234 if (idx < s_idx)
2235 goto cont;
2236 if (nf_tables_fill_set(skb, ctx, set, NFT_MSG_NEWSET,
2237 NLM_F_MULTI) < 0) {
2238 cb->args[0] = idx;
2239 goto done;
2240 }
2241cont:
2242 idx++;
2243 }
2244 cb->args[1] = 1;
2245done:
2246 return skb->len;
2247}
2248
Pablo Neira Ayusoc9c8e482013-12-26 16:49:03 +01002249static int nf_tables_dump_sets_family(struct nft_ctx *ctx, struct sk_buff *skb,
2250 struct netlink_callback *cb)
Patrick McHardy20a69342013-10-11 12:06:22 +02002251{
2252 const struct nft_set *set;
Pablo Neira Ayusoe38195b2013-12-24 18:32:35 +01002253 unsigned int idx, s_idx = cb->args[0];
Patrick McHardy20a69342013-10-11 12:06:22 +02002254 struct nft_table *table, *cur_table = (struct nft_table *)cb->args[2];
2255
2256 if (cb->args[1])
2257 return skb->len;
2258
2259 list_for_each_entry(table, &ctx->afi->tables, list) {
Pablo Neira Ayusoe38195b2013-12-24 18:32:35 +01002260 if (cur_table) {
2261 if (cur_table != table)
2262 continue;
Patrick McHardy20a69342013-10-11 12:06:22 +02002263
Pablo Neira Ayusoe38195b2013-12-24 18:32:35 +01002264 cur_table = NULL;
2265 }
Patrick McHardy20a69342013-10-11 12:06:22 +02002266 ctx->table = table;
Pablo Neira Ayusoe38195b2013-12-24 18:32:35 +01002267 idx = 0;
Patrick McHardy20a69342013-10-11 12:06:22 +02002268 list_for_each_entry(set, &ctx->table->sets, list) {
2269 if (idx < s_idx)
2270 goto cont;
2271 if (nf_tables_fill_set(skb, ctx, set, NFT_MSG_NEWSET,
2272 NLM_F_MULTI) < 0) {
2273 cb->args[0] = idx;
2274 cb->args[2] = (unsigned long) table;
2275 goto done;
2276 }
2277cont:
2278 idx++;
2279 }
2280 }
2281 cb->args[1] = 1;
2282done:
2283 return skb->len;
2284}
2285
Pablo Neira Ayusoc9c8e482013-12-26 16:49:03 +01002286static int nf_tables_dump_sets_all(struct nft_ctx *ctx, struct sk_buff *skb,
2287 struct netlink_callback *cb)
2288{
2289 const struct nft_set *set;
2290 unsigned int idx, s_idx = cb->args[0];
Pablo Neira Ayuso7c95f6d2014-04-04 01:22:45 +02002291 struct nft_af_info *afi;
Pablo Neira Ayusoc9c8e482013-12-26 16:49:03 +01002292 struct nft_table *table, *cur_table = (struct nft_table *)cb->args[2];
2293 struct net *net = sock_net(skb->sk);
2294 int cur_family = cb->args[3];
2295
2296 if (cb->args[1])
2297 return skb->len;
2298
2299 list_for_each_entry(afi, &net->nft.af_info, list) {
2300 if (cur_family) {
2301 if (afi->family != cur_family)
2302 continue;
2303
2304 cur_family = 0;
2305 }
2306
2307 list_for_each_entry(table, &afi->tables, list) {
2308 if (cur_table) {
2309 if (cur_table != table)
2310 continue;
2311
2312 cur_table = NULL;
2313 }
2314
2315 ctx->table = table;
2316 ctx->afi = afi;
2317 idx = 0;
2318 list_for_each_entry(set, &ctx->table->sets, list) {
2319 if (idx < s_idx)
2320 goto cont;
2321 if (nf_tables_fill_set(skb, ctx, set,
2322 NFT_MSG_NEWSET,
2323 NLM_F_MULTI) < 0) {
2324 cb->args[0] = idx;
2325 cb->args[2] = (unsigned long) table;
2326 cb->args[3] = afi->family;
2327 goto done;
2328 }
2329cont:
2330 idx++;
2331 }
2332 if (s_idx)
2333 s_idx = 0;
2334 }
2335 }
2336 cb->args[1] = 1;
2337done:
2338 return skb->len;
2339}
2340
Patrick McHardy20a69342013-10-11 12:06:22 +02002341static int nf_tables_dump_sets(struct sk_buff *skb, struct netlink_callback *cb)
2342{
2343 const struct nfgenmsg *nfmsg = nlmsg_data(cb->nlh);
2344 struct nlattr *nla[NFTA_SET_MAX + 1];
2345 struct nft_ctx ctx;
2346 int err, ret;
2347
2348 err = nlmsg_parse(cb->nlh, sizeof(*nfmsg), nla, NFTA_SET_MAX,
2349 nft_set_policy);
2350 if (err < 0)
2351 return err;
2352
2353 err = nft_ctx_init_from_setattr(&ctx, cb->skb, cb->nlh, (void *)nla);
2354 if (err < 0)
2355 return err;
2356
Pablo Neira Ayusoc9c8e482013-12-26 16:49:03 +01002357 if (ctx.table == NULL) {
2358 if (ctx.afi == NULL)
2359 ret = nf_tables_dump_sets_all(&ctx, skb, cb);
2360 else
2361 ret = nf_tables_dump_sets_family(&ctx, skb, cb);
2362 } else
Patrick McHardy20a69342013-10-11 12:06:22 +02002363 ret = nf_tables_dump_sets_table(&ctx, skb, cb);
2364
2365 return ret;
2366}
2367
Pablo Neira Ayuso958bee12014-04-03 11:48:44 +02002368#define NFT_SET_INACTIVE (1 << 15) /* Internal set flag */
2369
Patrick McHardy20a69342013-10-11 12:06:22 +02002370static int nf_tables_getset(struct sock *nlsk, struct sk_buff *skb,
2371 const struct nlmsghdr *nlh,
2372 const struct nlattr * const nla[])
2373{
2374 const struct nft_set *set;
2375 struct nft_ctx ctx;
2376 struct sk_buff *skb2;
Pablo Neira Ayusoc9c8e482013-12-26 16:49:03 +01002377 const struct nfgenmsg *nfmsg = nlmsg_data(nlh);
Patrick McHardy20a69342013-10-11 12:06:22 +02002378 int err;
2379
2380 /* Verify existance before starting dump */
2381 err = nft_ctx_init_from_setattr(&ctx, skb, nlh, nla);
2382 if (err < 0)
2383 return err;
2384
2385 if (nlh->nlmsg_flags & NLM_F_DUMP) {
2386 struct netlink_dump_control c = {
2387 .dump = nf_tables_dump_sets,
2388 };
2389 return netlink_dump_start(nlsk, skb, nlh, &c);
2390 }
2391
Pablo Neira Ayusoc9c8e482013-12-26 16:49:03 +01002392 /* Only accept unspec with dump */
2393 if (nfmsg->nfgen_family == NFPROTO_UNSPEC)
2394 return -EAFNOSUPPORT;
2395
Patrick McHardy20a69342013-10-11 12:06:22 +02002396 set = nf_tables_set_lookup(ctx.table, nla[NFTA_SET_NAME]);
2397 if (IS_ERR(set))
2398 return PTR_ERR(set);
Pablo Neira Ayuso958bee12014-04-03 11:48:44 +02002399 if (set->flags & NFT_SET_INACTIVE)
2400 return -ENOENT;
Patrick McHardy20a69342013-10-11 12:06:22 +02002401
2402 skb2 = alloc_skb(NLMSG_GOODSIZE, GFP_KERNEL);
2403 if (skb2 == NULL)
2404 return -ENOMEM;
2405
2406 err = nf_tables_fill_set(skb2, &ctx, set, NFT_MSG_NEWSET, 0);
2407 if (err < 0)
2408 goto err;
2409
2410 return nlmsg_unicast(nlsk, skb2, NETLINK_CB(skb).portid);
2411
2412err:
2413 kfree_skb(skb2);
2414 return err;
2415}
2416
Patrick McHardyc50b9602014-03-28 10:19:47 +00002417static int nf_tables_set_desc_parse(const struct nft_ctx *ctx,
2418 struct nft_set_desc *desc,
2419 const struct nlattr *nla)
2420{
2421 struct nlattr *da[NFTA_SET_DESC_MAX + 1];
2422 int err;
2423
2424 err = nla_parse_nested(da, NFTA_SET_DESC_MAX, nla, nft_set_desc_policy);
2425 if (err < 0)
2426 return err;
2427
2428 if (da[NFTA_SET_DESC_SIZE] != NULL)
2429 desc->size = ntohl(nla_get_be32(da[NFTA_SET_DESC_SIZE]));
2430
2431 return 0;
2432}
2433
Pablo Neira Ayuso958bee12014-04-03 11:48:44 +02002434static int nft_trans_set_add(struct nft_ctx *ctx, int msg_type,
2435 struct nft_set *set)
2436{
2437 struct nft_trans *trans;
2438
2439 trans = nft_trans_alloc(ctx, msg_type, sizeof(struct nft_trans_set));
2440 if (trans == NULL)
2441 return -ENOMEM;
2442
2443 if (msg_type == NFT_MSG_NEWSET && ctx->nla[NFTA_SET_ID] != NULL) {
2444 nft_trans_set_id(trans) =
2445 ntohl(nla_get_be32(ctx->nla[NFTA_SET_ID]));
2446 set->flags |= NFT_SET_INACTIVE;
2447 }
2448 nft_trans_set(trans) = set;
2449 list_add_tail(&trans->list, &ctx->net->nft.commit_list);
2450
2451 return 0;
2452}
2453
Patrick McHardy20a69342013-10-11 12:06:22 +02002454static int nf_tables_newset(struct sock *nlsk, struct sk_buff *skb,
2455 const struct nlmsghdr *nlh,
2456 const struct nlattr * const nla[])
2457{
2458 const struct nfgenmsg *nfmsg = nlmsg_data(nlh);
2459 const struct nft_set_ops *ops;
Pablo Neira Ayuso7c95f6d2014-04-04 01:22:45 +02002460 struct nft_af_info *afi;
Pablo Neira Ayuso99633ab2013-10-10 23:28:33 +02002461 struct net *net = sock_net(skb->sk);
Patrick McHardy20a69342013-10-11 12:06:22 +02002462 struct nft_table *table;
2463 struct nft_set *set;
2464 struct nft_ctx ctx;
2465 char name[IFNAMSIZ];
2466 unsigned int size;
2467 bool create;
Patrick McHardyc50b9602014-03-28 10:19:47 +00002468 u32 ktype, dtype, flags, policy;
2469 struct nft_set_desc desc;
Patrick McHardy20a69342013-10-11 12:06:22 +02002470 int err;
2471
2472 if (nla[NFTA_SET_TABLE] == NULL ||
2473 nla[NFTA_SET_NAME] == NULL ||
Pablo Neira Ayuso958bee12014-04-03 11:48:44 +02002474 nla[NFTA_SET_KEY_LEN] == NULL ||
2475 nla[NFTA_SET_ID] == NULL)
Patrick McHardy20a69342013-10-11 12:06:22 +02002476 return -EINVAL;
2477
Patrick McHardyc50b9602014-03-28 10:19:47 +00002478 memset(&desc, 0, sizeof(desc));
2479
Patrick McHardy20a69342013-10-11 12:06:22 +02002480 ktype = NFT_DATA_VALUE;
2481 if (nla[NFTA_SET_KEY_TYPE] != NULL) {
2482 ktype = ntohl(nla_get_be32(nla[NFTA_SET_KEY_TYPE]));
2483 if ((ktype & NFT_DATA_RESERVED_MASK) == NFT_DATA_RESERVED_MASK)
2484 return -EINVAL;
2485 }
2486
Patrick McHardyc50b9602014-03-28 10:19:47 +00002487 desc.klen = ntohl(nla_get_be32(nla[NFTA_SET_KEY_LEN]));
2488 if (desc.klen == 0 || desc.klen > FIELD_SIZEOF(struct nft_data, data))
Patrick McHardy20a69342013-10-11 12:06:22 +02002489 return -EINVAL;
2490
2491 flags = 0;
2492 if (nla[NFTA_SET_FLAGS] != NULL) {
2493 flags = ntohl(nla_get_be32(nla[NFTA_SET_FLAGS]));
2494 if (flags & ~(NFT_SET_ANONYMOUS | NFT_SET_CONSTANT |
2495 NFT_SET_INTERVAL | NFT_SET_MAP))
2496 return -EINVAL;
2497 }
2498
2499 dtype = 0;
Patrick McHardy20a69342013-10-11 12:06:22 +02002500 if (nla[NFTA_SET_DATA_TYPE] != NULL) {
2501 if (!(flags & NFT_SET_MAP))
2502 return -EINVAL;
2503
2504 dtype = ntohl(nla_get_be32(nla[NFTA_SET_DATA_TYPE]));
2505 if ((dtype & NFT_DATA_RESERVED_MASK) == NFT_DATA_RESERVED_MASK &&
2506 dtype != NFT_DATA_VERDICT)
2507 return -EINVAL;
2508
2509 if (dtype != NFT_DATA_VERDICT) {
2510 if (nla[NFTA_SET_DATA_LEN] == NULL)
2511 return -EINVAL;
Patrick McHardyc50b9602014-03-28 10:19:47 +00002512 desc.dlen = ntohl(nla_get_be32(nla[NFTA_SET_DATA_LEN]));
2513 if (desc.dlen == 0 ||
2514 desc.dlen > FIELD_SIZEOF(struct nft_data, data))
Patrick McHardy20a69342013-10-11 12:06:22 +02002515 return -EINVAL;
2516 } else
Patrick McHardyc50b9602014-03-28 10:19:47 +00002517 desc.dlen = sizeof(struct nft_data);
Patrick McHardy20a69342013-10-11 12:06:22 +02002518 } else if (flags & NFT_SET_MAP)
2519 return -EINVAL;
2520
Patrick McHardyc50b9602014-03-28 10:19:47 +00002521 policy = NFT_SET_POL_PERFORMANCE;
2522 if (nla[NFTA_SET_POLICY] != NULL)
2523 policy = ntohl(nla_get_be32(nla[NFTA_SET_POLICY]));
2524
2525 if (nla[NFTA_SET_DESC] != NULL) {
2526 err = nf_tables_set_desc_parse(&ctx, &desc, nla[NFTA_SET_DESC]);
2527 if (err < 0)
2528 return err;
2529 }
2530
Patrick McHardy20a69342013-10-11 12:06:22 +02002531 create = nlh->nlmsg_flags & NLM_F_CREATE ? true : false;
2532
Pablo Neira Ayuso99633ab2013-10-10 23:28:33 +02002533 afi = nf_tables_afinfo_lookup(net, nfmsg->nfgen_family, create);
Patrick McHardy20a69342013-10-11 12:06:22 +02002534 if (IS_ERR(afi))
2535 return PTR_ERR(afi);
2536
Pablo Neira Ayuso93707612013-10-10 23:21:26 +02002537 table = nf_tables_table_lookup(afi, nla[NFTA_SET_TABLE]);
Patrick McHardy20a69342013-10-11 12:06:22 +02002538 if (IS_ERR(table))
2539 return PTR_ERR(table);
2540
Pablo Neira Ayuso0ca743a2013-10-14 00:06:06 +02002541 nft_ctx_init(&ctx, skb, nlh, afi, table, NULL, nla);
Patrick McHardy20a69342013-10-11 12:06:22 +02002542
2543 set = nf_tables_set_lookup(table, nla[NFTA_SET_NAME]);
2544 if (IS_ERR(set)) {
2545 if (PTR_ERR(set) != -ENOENT)
2546 return PTR_ERR(set);
2547 set = NULL;
2548 }
2549
2550 if (set != NULL) {
2551 if (nlh->nlmsg_flags & NLM_F_EXCL)
2552 return -EEXIST;
2553 if (nlh->nlmsg_flags & NLM_F_REPLACE)
2554 return -EOPNOTSUPP;
2555 return 0;
2556 }
2557
2558 if (!(nlh->nlmsg_flags & NLM_F_CREATE))
2559 return -ENOENT;
2560
Patrick McHardyc50b9602014-03-28 10:19:47 +00002561 ops = nft_select_set_ops(nla, &desc, policy);
Patrick McHardy20a69342013-10-11 12:06:22 +02002562 if (IS_ERR(ops))
2563 return PTR_ERR(ops);
2564
2565 size = 0;
2566 if (ops->privsize != NULL)
2567 size = ops->privsize(nla);
2568
2569 err = -ENOMEM;
2570 set = kzalloc(sizeof(*set) + size, GFP_KERNEL);
2571 if (set == NULL)
2572 goto err1;
2573
2574 nla_strlcpy(name, nla[NFTA_SET_NAME], sizeof(set->name));
2575 err = nf_tables_set_alloc_name(&ctx, set, name);
2576 if (err < 0)
2577 goto err2;
2578
2579 INIT_LIST_HEAD(&set->bindings);
2580 set->ops = ops;
2581 set->ktype = ktype;
Patrick McHardyc50b9602014-03-28 10:19:47 +00002582 set->klen = desc.klen;
Patrick McHardy20a69342013-10-11 12:06:22 +02002583 set->dtype = dtype;
Patrick McHardyc50b9602014-03-28 10:19:47 +00002584 set->dlen = desc.dlen;
Patrick McHardy20a69342013-10-11 12:06:22 +02002585 set->flags = flags;
Patrick McHardyc50b9602014-03-28 10:19:47 +00002586 set->size = desc.size;
Patrick McHardy20a69342013-10-11 12:06:22 +02002587
Patrick McHardyc50b9602014-03-28 10:19:47 +00002588 err = ops->init(set, &desc, nla);
Patrick McHardy20a69342013-10-11 12:06:22 +02002589 if (err < 0)
2590 goto err2;
2591
Pablo Neira Ayuso958bee12014-04-03 11:48:44 +02002592 err = nft_trans_set_add(&ctx, NFT_MSG_NEWSET, set);
2593 if (err < 0)
2594 goto err2;
2595
Patrick McHardy20a69342013-10-11 12:06:22 +02002596 list_add_tail(&set->list, &table->sets);
Patrick McHardy20a69342013-10-11 12:06:22 +02002597 return 0;
2598
2599err2:
2600 kfree(set);
2601err1:
2602 module_put(ops->owner);
2603 return err;
2604}
2605
Pablo Neira Ayuso958bee12014-04-03 11:48:44 +02002606static void nft_set_destroy(struct nft_set *set)
2607{
2608 set->ops->destroy(set);
2609 module_put(set->ops->owner);
2610 kfree(set);
2611}
2612
Patrick McHardy20a69342013-10-11 12:06:22 +02002613static void nf_tables_set_destroy(const struct nft_ctx *ctx, struct nft_set *set)
2614{
2615 list_del(&set->list);
Patrick McHardyab9da5c12014-03-07 19:08:31 +01002616 nf_tables_set_notify(ctx, set, NFT_MSG_DELSET);
Pablo Neira Ayuso958bee12014-04-03 11:48:44 +02002617 nft_set_destroy(set);
Patrick McHardy20a69342013-10-11 12:06:22 +02002618}
2619
2620static int nf_tables_delset(struct sock *nlsk, struct sk_buff *skb,
2621 const struct nlmsghdr *nlh,
2622 const struct nlattr * const nla[])
2623{
Pablo Neira Ayusoc9c8e482013-12-26 16:49:03 +01002624 const struct nfgenmsg *nfmsg = nlmsg_data(nlh);
Patrick McHardy20a69342013-10-11 12:06:22 +02002625 struct nft_set *set;
2626 struct nft_ctx ctx;
2627 int err;
2628
Patrick McHardyec2c9932014-02-05 15:03:35 +00002629 if (nfmsg->nfgen_family == NFPROTO_UNSPEC)
2630 return -EAFNOSUPPORT;
Patrick McHardy20a69342013-10-11 12:06:22 +02002631 if (nla[NFTA_SET_TABLE] == NULL)
2632 return -EINVAL;
2633
2634 err = nft_ctx_init_from_setattr(&ctx, skb, nlh, nla);
2635 if (err < 0)
2636 return err;
2637
2638 set = nf_tables_set_lookup(ctx.table, nla[NFTA_SET_NAME]);
2639 if (IS_ERR(set))
2640 return PTR_ERR(set);
Pablo Neira Ayuso958bee12014-04-03 11:48:44 +02002641 if (set->flags & NFT_SET_INACTIVE)
2642 return -ENOENT;
Patrick McHardy20a69342013-10-11 12:06:22 +02002643 if (!list_empty(&set->bindings))
2644 return -EBUSY;
2645
Pablo Neira Ayuso958bee12014-04-03 11:48:44 +02002646 err = nft_trans_set_add(&ctx, NFT_MSG_DELSET, set);
2647 if (err < 0)
2648 return err;
2649
2650 list_del(&set->list);
Patrick McHardy20a69342013-10-11 12:06:22 +02002651 return 0;
2652}
2653
2654static int nf_tables_bind_check_setelem(const struct nft_ctx *ctx,
2655 const struct nft_set *set,
2656 const struct nft_set_iter *iter,
2657 const struct nft_set_elem *elem)
2658{
2659 enum nft_registers dreg;
2660
2661 dreg = nft_type_to_reg(set->dtype);
Pablo Neira Ayuso2ee0d3c2013-12-28 00:59:38 +01002662 return nft_validate_data_load(ctx, dreg, &elem->data,
2663 set->dtype == NFT_DATA_VERDICT ?
2664 NFT_DATA_VERDICT : NFT_DATA_VALUE);
Patrick McHardy20a69342013-10-11 12:06:22 +02002665}
2666
2667int nf_tables_bind_set(const struct nft_ctx *ctx, struct nft_set *set,
2668 struct nft_set_binding *binding)
2669{
2670 struct nft_set_binding *i;
2671 struct nft_set_iter iter;
2672
2673 if (!list_empty(&set->bindings) && set->flags & NFT_SET_ANONYMOUS)
2674 return -EBUSY;
2675
2676 if (set->flags & NFT_SET_MAP) {
2677 /* If the set is already bound to the same chain all
2678 * jumps are already validated for that chain.
2679 */
2680 list_for_each_entry(i, &set->bindings, list) {
2681 if (i->chain == binding->chain)
2682 goto bind;
2683 }
2684
2685 iter.skip = 0;
2686 iter.count = 0;
2687 iter.err = 0;
2688 iter.fn = nf_tables_bind_check_setelem;
2689
2690 set->ops->walk(ctx, set, &iter);
2691 if (iter.err < 0) {
2692 /* Destroy anonymous sets if binding fails */
2693 if (set->flags & NFT_SET_ANONYMOUS)
2694 nf_tables_set_destroy(ctx, set);
2695
2696 return iter.err;
2697 }
2698 }
2699bind:
2700 binding->chain = ctx->chain;
2701 list_add_tail(&binding->list, &set->bindings);
2702 return 0;
2703}
2704
2705void nf_tables_unbind_set(const struct nft_ctx *ctx, struct nft_set *set,
2706 struct nft_set_binding *binding)
2707{
2708 list_del(&binding->list);
2709
Pablo Neira Ayuso958bee12014-04-03 11:48:44 +02002710 if (list_empty(&set->bindings) && set->flags & NFT_SET_ANONYMOUS &&
2711 !(set->flags & NFT_SET_INACTIVE))
Patrick McHardy20a69342013-10-11 12:06:22 +02002712 nf_tables_set_destroy(ctx, set);
2713}
2714
2715/*
2716 * Set elements
2717 */
2718
2719static const struct nla_policy nft_set_elem_policy[NFTA_SET_ELEM_MAX + 1] = {
2720 [NFTA_SET_ELEM_KEY] = { .type = NLA_NESTED },
2721 [NFTA_SET_ELEM_DATA] = { .type = NLA_NESTED },
2722 [NFTA_SET_ELEM_FLAGS] = { .type = NLA_U32 },
2723};
2724
2725static const struct nla_policy nft_set_elem_list_policy[NFTA_SET_ELEM_LIST_MAX + 1] = {
2726 [NFTA_SET_ELEM_LIST_TABLE] = { .type = NLA_STRING },
2727 [NFTA_SET_ELEM_LIST_SET] = { .type = NLA_STRING },
2728 [NFTA_SET_ELEM_LIST_ELEMENTS] = { .type = NLA_NESTED },
Pablo Neira Ayuso958bee12014-04-03 11:48:44 +02002729 [NFTA_SET_ELEM_LIST_SET_ID] = { .type = NLA_U32 },
Patrick McHardy20a69342013-10-11 12:06:22 +02002730};
2731
2732static int nft_ctx_init_from_elemattr(struct nft_ctx *ctx,
2733 const struct sk_buff *skb,
2734 const struct nlmsghdr *nlh,
Pablo Neira Ayuso55dd6f92014-04-03 11:53:37 +02002735 const struct nlattr * const nla[],
2736 bool trans)
Patrick McHardy20a69342013-10-11 12:06:22 +02002737{
2738 const struct nfgenmsg *nfmsg = nlmsg_data(nlh);
Pablo Neira Ayuso7c95f6d2014-04-04 01:22:45 +02002739 struct nft_af_info *afi;
2740 struct nft_table *table;
Pablo Neira Ayuso99633ab2013-10-10 23:28:33 +02002741 struct net *net = sock_net(skb->sk);
Patrick McHardy20a69342013-10-11 12:06:22 +02002742
Pablo Neira Ayuso99633ab2013-10-10 23:28:33 +02002743 afi = nf_tables_afinfo_lookup(net, nfmsg->nfgen_family, false);
Patrick McHardy20a69342013-10-11 12:06:22 +02002744 if (IS_ERR(afi))
2745 return PTR_ERR(afi);
2746
Pablo Neira Ayuso93707612013-10-10 23:21:26 +02002747 table = nf_tables_table_lookup(afi, nla[NFTA_SET_ELEM_LIST_TABLE]);
Patrick McHardy20a69342013-10-11 12:06:22 +02002748 if (IS_ERR(table))
2749 return PTR_ERR(table);
Pablo Neira Ayuso55dd6f92014-04-03 11:53:37 +02002750 if (!trans && (table->flags & NFT_TABLE_INACTIVE))
2751 return -ENOENT;
Patrick McHardy20a69342013-10-11 12:06:22 +02002752
Pablo Neira Ayuso0ca743a2013-10-14 00:06:06 +02002753 nft_ctx_init(ctx, skb, nlh, afi, table, NULL, nla);
Patrick McHardy20a69342013-10-11 12:06:22 +02002754 return 0;
2755}
2756
2757static int nf_tables_fill_setelem(struct sk_buff *skb,
2758 const struct nft_set *set,
2759 const struct nft_set_elem *elem)
2760{
2761 unsigned char *b = skb_tail_pointer(skb);
2762 struct nlattr *nest;
2763
2764 nest = nla_nest_start(skb, NFTA_LIST_ELEM);
2765 if (nest == NULL)
2766 goto nla_put_failure;
2767
2768 if (nft_data_dump(skb, NFTA_SET_ELEM_KEY, &elem->key, NFT_DATA_VALUE,
2769 set->klen) < 0)
2770 goto nla_put_failure;
2771
2772 if (set->flags & NFT_SET_MAP &&
2773 !(elem->flags & NFT_SET_ELEM_INTERVAL_END) &&
2774 nft_data_dump(skb, NFTA_SET_ELEM_DATA, &elem->data,
2775 set->dtype == NFT_DATA_VERDICT ? NFT_DATA_VERDICT : NFT_DATA_VALUE,
2776 set->dlen) < 0)
2777 goto nla_put_failure;
2778
2779 if (elem->flags != 0)
2780 if (nla_put_be32(skb, NFTA_SET_ELEM_FLAGS, htonl(elem->flags)))
2781 goto nla_put_failure;
2782
2783 nla_nest_end(skb, nest);
2784 return 0;
2785
2786nla_put_failure:
2787 nlmsg_trim(skb, b);
2788 return -EMSGSIZE;
2789}
2790
2791struct nft_set_dump_args {
2792 const struct netlink_callback *cb;
2793 struct nft_set_iter iter;
2794 struct sk_buff *skb;
2795};
2796
2797static int nf_tables_dump_setelem(const struct nft_ctx *ctx,
2798 const struct nft_set *set,
2799 const struct nft_set_iter *iter,
2800 const struct nft_set_elem *elem)
2801{
2802 struct nft_set_dump_args *args;
2803
2804 args = container_of(iter, struct nft_set_dump_args, iter);
2805 return nf_tables_fill_setelem(args->skb, set, elem);
2806}
2807
2808static int nf_tables_dump_set(struct sk_buff *skb, struct netlink_callback *cb)
2809{
2810 const struct nft_set *set;
2811 struct nft_set_dump_args args;
2812 struct nft_ctx ctx;
2813 struct nlattr *nla[NFTA_SET_ELEM_LIST_MAX + 1];
2814 struct nfgenmsg *nfmsg;
2815 struct nlmsghdr *nlh;
2816 struct nlattr *nest;
2817 u32 portid, seq;
2818 int event, err;
2819
Michal Nazarewicz720e0df2014-01-01 06:27:19 +01002820 err = nlmsg_parse(cb->nlh, sizeof(struct nfgenmsg), nla,
2821 NFTA_SET_ELEM_LIST_MAX, nft_set_elem_list_policy);
Patrick McHardy20a69342013-10-11 12:06:22 +02002822 if (err < 0)
2823 return err;
2824
Pablo Neira Ayuso55dd6f92014-04-03 11:53:37 +02002825 err = nft_ctx_init_from_elemattr(&ctx, cb->skb, cb->nlh, (void *)nla,
2826 false);
Patrick McHardy20a69342013-10-11 12:06:22 +02002827 if (err < 0)
2828 return err;
2829
2830 set = nf_tables_set_lookup(ctx.table, nla[NFTA_SET_ELEM_LIST_SET]);
2831 if (IS_ERR(set))
2832 return PTR_ERR(set);
Pablo Neira Ayuso958bee12014-04-03 11:48:44 +02002833 if (set->flags & NFT_SET_INACTIVE)
2834 return -ENOENT;
Patrick McHardy20a69342013-10-11 12:06:22 +02002835
2836 event = NFT_MSG_NEWSETELEM;
2837 event |= NFNL_SUBSYS_NFTABLES << 8;
2838 portid = NETLINK_CB(cb->skb).portid;
2839 seq = cb->nlh->nlmsg_seq;
2840
2841 nlh = nlmsg_put(skb, portid, seq, event, sizeof(struct nfgenmsg),
2842 NLM_F_MULTI);
2843 if (nlh == NULL)
2844 goto nla_put_failure;
2845
2846 nfmsg = nlmsg_data(nlh);
2847 nfmsg->nfgen_family = NFPROTO_UNSPEC;
2848 nfmsg->version = NFNETLINK_V0;
2849 nfmsg->res_id = 0;
2850
2851 if (nla_put_string(skb, NFTA_SET_ELEM_LIST_TABLE, ctx.table->name))
2852 goto nla_put_failure;
2853 if (nla_put_string(skb, NFTA_SET_ELEM_LIST_SET, set->name))
2854 goto nla_put_failure;
2855
2856 nest = nla_nest_start(skb, NFTA_SET_ELEM_LIST_ELEMENTS);
2857 if (nest == NULL)
2858 goto nla_put_failure;
2859
2860 args.cb = cb;
2861 args.skb = skb;
2862 args.iter.skip = cb->args[0];
2863 args.iter.count = 0;
2864 args.iter.err = 0;
2865 args.iter.fn = nf_tables_dump_setelem;
2866 set->ops->walk(&ctx, set, &args.iter);
2867
2868 nla_nest_end(skb, nest);
2869 nlmsg_end(skb, nlh);
2870
2871 if (args.iter.err && args.iter.err != -EMSGSIZE)
2872 return args.iter.err;
2873 if (args.iter.count == cb->args[0])
2874 return 0;
2875
2876 cb->args[0] = args.iter.count;
2877 return skb->len;
2878
2879nla_put_failure:
2880 return -ENOSPC;
2881}
2882
2883static int nf_tables_getsetelem(struct sock *nlsk, struct sk_buff *skb,
2884 const struct nlmsghdr *nlh,
2885 const struct nlattr * const nla[])
2886{
2887 const struct nft_set *set;
2888 struct nft_ctx ctx;
2889 int err;
2890
Pablo Neira Ayuso55dd6f92014-04-03 11:53:37 +02002891 err = nft_ctx_init_from_elemattr(&ctx, skb, nlh, nla, false);
Patrick McHardy20a69342013-10-11 12:06:22 +02002892 if (err < 0)
2893 return err;
2894
2895 set = nf_tables_set_lookup(ctx.table, nla[NFTA_SET_ELEM_LIST_SET]);
2896 if (IS_ERR(set))
2897 return PTR_ERR(set);
Pablo Neira Ayuso958bee12014-04-03 11:48:44 +02002898 if (set->flags & NFT_SET_INACTIVE)
2899 return -ENOENT;
Patrick McHardy20a69342013-10-11 12:06:22 +02002900
2901 if (nlh->nlmsg_flags & NLM_F_DUMP) {
2902 struct netlink_dump_control c = {
2903 .dump = nf_tables_dump_set,
2904 };
2905 return netlink_dump_start(nlsk, skb, nlh, &c);
2906 }
2907 return -EOPNOTSUPP;
2908}
2909
Arturo Borrerod60ce622014-04-01 14:06:07 +02002910static int nf_tables_fill_setelem_info(struct sk_buff *skb,
2911 const struct nft_ctx *ctx, u32 seq,
2912 u32 portid, int event, u16 flags,
2913 const struct nft_set *set,
2914 const struct nft_set_elem *elem)
2915{
2916 struct nfgenmsg *nfmsg;
2917 struct nlmsghdr *nlh;
2918 struct nlattr *nest;
2919 int err;
2920
2921 event |= NFNL_SUBSYS_NFTABLES << 8;
2922 nlh = nlmsg_put(skb, portid, seq, event, sizeof(struct nfgenmsg),
2923 flags);
2924 if (nlh == NULL)
2925 goto nla_put_failure;
2926
2927 nfmsg = nlmsg_data(nlh);
2928 nfmsg->nfgen_family = ctx->afi->family;
2929 nfmsg->version = NFNETLINK_V0;
2930 nfmsg->res_id = 0;
2931
2932 if (nla_put_string(skb, NFTA_SET_TABLE, ctx->table->name))
2933 goto nla_put_failure;
2934 if (nla_put_string(skb, NFTA_SET_NAME, set->name))
2935 goto nla_put_failure;
2936
2937 nest = nla_nest_start(skb, NFTA_SET_ELEM_LIST_ELEMENTS);
2938 if (nest == NULL)
2939 goto nla_put_failure;
2940
2941 err = nf_tables_fill_setelem(skb, set, elem);
2942 if (err < 0)
2943 goto nla_put_failure;
2944
2945 nla_nest_end(skb, nest);
2946
2947 return nlmsg_end(skb, nlh);
2948
2949nla_put_failure:
2950 nlmsg_trim(skb, nlh);
2951 return -1;
2952}
2953
2954static int nf_tables_setelem_notify(const struct nft_ctx *ctx,
2955 const struct nft_set *set,
2956 const struct nft_set_elem *elem,
2957 int event, u16 flags)
2958{
2959 const struct sk_buff *oskb = ctx->skb;
2960 struct net *net = sock_net(oskb->sk);
2961 u32 portid = NETLINK_CB(oskb).portid;
2962 bool report = nlmsg_report(ctx->nlh);
2963 struct sk_buff *skb;
2964 int err;
2965
2966 if (!report && !nfnetlink_has_listeners(net, NFNLGRP_NFTABLES))
2967 return 0;
2968
2969 err = -ENOBUFS;
2970 skb = nlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL);
2971 if (skb == NULL)
2972 goto err;
2973
2974 err = nf_tables_fill_setelem_info(skb, ctx, 0, portid, event, flags,
2975 set, elem);
2976 if (err < 0) {
2977 kfree_skb(skb);
2978 goto err;
2979 }
2980
2981 err = nfnetlink_send(skb, net, portid, NFNLGRP_NFTABLES, report,
2982 GFP_KERNEL);
2983err:
2984 if (err < 0)
2985 nfnetlink_set_err(net, portid, NFNLGRP_NFTABLES, err);
2986 return err;
2987}
2988
Pablo Neira Ayuso60319eb2014-04-04 03:36:42 +02002989static struct nft_trans *nft_trans_elem_alloc(struct nft_ctx *ctx,
2990 int msg_type,
2991 struct nft_set *set)
2992{
2993 struct nft_trans *trans;
2994
2995 trans = nft_trans_alloc(ctx, msg_type, sizeof(struct nft_trans_elem));
2996 if (trans == NULL)
2997 return NULL;
2998
2999 nft_trans_elem_set(trans) = set;
3000 return trans;
3001}
3002
3003static int nft_add_set_elem(struct nft_ctx *ctx, struct nft_set *set,
Patrick McHardy20a69342013-10-11 12:06:22 +02003004 const struct nlattr *attr)
3005{
3006 struct nlattr *nla[NFTA_SET_ELEM_MAX + 1];
3007 struct nft_data_desc d1, d2;
3008 struct nft_set_elem elem;
3009 struct nft_set_binding *binding;
3010 enum nft_registers dreg;
Pablo Neira Ayuso60319eb2014-04-04 03:36:42 +02003011 struct nft_trans *trans;
Patrick McHardy20a69342013-10-11 12:06:22 +02003012 int err;
3013
Patrick McHardyc50b9602014-03-28 10:19:47 +00003014 if (set->size && set->nelems == set->size)
3015 return -ENFILE;
3016
Patrick McHardy20a69342013-10-11 12:06:22 +02003017 err = nla_parse_nested(nla, NFTA_SET_ELEM_MAX, attr,
3018 nft_set_elem_policy);
3019 if (err < 0)
3020 return err;
3021
3022 if (nla[NFTA_SET_ELEM_KEY] == NULL)
3023 return -EINVAL;
3024
3025 elem.flags = 0;
3026 if (nla[NFTA_SET_ELEM_FLAGS] != NULL) {
3027 elem.flags = ntohl(nla_get_be32(nla[NFTA_SET_ELEM_FLAGS]));
3028 if (elem.flags & ~NFT_SET_ELEM_INTERVAL_END)
3029 return -EINVAL;
3030 }
3031
3032 if (set->flags & NFT_SET_MAP) {
3033 if (nla[NFTA_SET_ELEM_DATA] == NULL &&
3034 !(elem.flags & NFT_SET_ELEM_INTERVAL_END))
3035 return -EINVAL;
Pablo Neira Ayusobd7fc642014-02-07 12:53:07 +01003036 if (nla[NFTA_SET_ELEM_DATA] != NULL &&
3037 elem.flags & NFT_SET_ELEM_INTERVAL_END)
3038 return -EINVAL;
Patrick McHardy20a69342013-10-11 12:06:22 +02003039 } else {
3040 if (nla[NFTA_SET_ELEM_DATA] != NULL)
3041 return -EINVAL;
3042 }
3043
3044 err = nft_data_init(ctx, &elem.key, &d1, nla[NFTA_SET_ELEM_KEY]);
3045 if (err < 0)
3046 goto err1;
3047 err = -EINVAL;
3048 if (d1.type != NFT_DATA_VALUE || d1.len != set->klen)
3049 goto err2;
3050
3051 err = -EEXIST;
3052 if (set->ops->get(set, &elem) == 0)
3053 goto err2;
3054
3055 if (nla[NFTA_SET_ELEM_DATA] != NULL) {
3056 err = nft_data_init(ctx, &elem.data, &d2, nla[NFTA_SET_ELEM_DATA]);
3057 if (err < 0)
3058 goto err2;
3059
3060 err = -EINVAL;
3061 if (set->dtype != NFT_DATA_VERDICT && d2.len != set->dlen)
3062 goto err3;
3063
3064 dreg = nft_type_to_reg(set->dtype);
3065 list_for_each_entry(binding, &set->bindings, list) {
3066 struct nft_ctx bind_ctx = {
3067 .afi = ctx->afi,
3068 .table = ctx->table,
Pablo Neira Ayuso7c95f6d2014-04-04 01:22:45 +02003069 .chain = (struct nft_chain *)binding->chain,
Patrick McHardy20a69342013-10-11 12:06:22 +02003070 };
3071
3072 err = nft_validate_data_load(&bind_ctx, dreg,
3073 &elem.data, d2.type);
3074 if (err < 0)
3075 goto err3;
3076 }
3077 }
3078
Pablo Neira Ayuso60319eb2014-04-04 03:36:42 +02003079 trans = nft_trans_elem_alloc(ctx, NFT_MSG_NEWSETELEM, set);
3080 if (trans == NULL)
3081 goto err3;
3082
Patrick McHardy20a69342013-10-11 12:06:22 +02003083 err = set->ops->insert(set, &elem);
3084 if (err < 0)
Pablo Neira Ayuso60319eb2014-04-04 03:36:42 +02003085 goto err4;
Patrick McHardy20a69342013-10-11 12:06:22 +02003086
Pablo Neira Ayuso60319eb2014-04-04 03:36:42 +02003087 nft_trans_elem(trans) = elem;
3088 list_add(&trans->list, &ctx->net->nft.commit_list);
Patrick McHardy20a69342013-10-11 12:06:22 +02003089 return 0;
3090
Pablo Neira Ayuso60319eb2014-04-04 03:36:42 +02003091err4:
3092 kfree(trans);
Patrick McHardy20a69342013-10-11 12:06:22 +02003093err3:
3094 if (nla[NFTA_SET_ELEM_DATA] != NULL)
3095 nft_data_uninit(&elem.data, d2.type);
3096err2:
3097 nft_data_uninit(&elem.key, d1.type);
3098err1:
3099 return err;
3100}
3101
3102static int nf_tables_newsetelem(struct sock *nlsk, struct sk_buff *skb,
3103 const struct nlmsghdr *nlh,
3104 const struct nlattr * const nla[])
3105{
Pablo Neira Ayuso958bee12014-04-03 11:48:44 +02003106 struct net *net = sock_net(skb->sk);
Patrick McHardy20a69342013-10-11 12:06:22 +02003107 const struct nlattr *attr;
3108 struct nft_set *set;
3109 struct nft_ctx ctx;
Pablo Neira Ayuso60319eb2014-04-04 03:36:42 +02003110 int rem, err = 0;
Patrick McHardy20a69342013-10-11 12:06:22 +02003111
Pablo Neira Ayuso55dd6f92014-04-03 11:53:37 +02003112 err = nft_ctx_init_from_elemattr(&ctx, skb, nlh, nla, true);
Patrick McHardy20a69342013-10-11 12:06:22 +02003113 if (err < 0)
3114 return err;
3115
3116 set = nf_tables_set_lookup(ctx.table, nla[NFTA_SET_ELEM_LIST_SET]);
Pablo Neira Ayuso958bee12014-04-03 11:48:44 +02003117 if (IS_ERR(set)) {
3118 if (nla[NFTA_SET_ELEM_LIST_SET_ID]) {
3119 set = nf_tables_set_lookup_byid(net,
3120 nla[NFTA_SET_ELEM_LIST_SET_ID]);
3121 }
3122 if (IS_ERR(set))
3123 return PTR_ERR(set);
3124 }
3125
Patrick McHardy20a69342013-10-11 12:06:22 +02003126 if (!list_empty(&set->bindings) && set->flags & NFT_SET_CONSTANT)
3127 return -EBUSY;
3128
3129 nla_for_each_nested(attr, nla[NFTA_SET_ELEM_LIST_ELEMENTS], rem) {
3130 err = nft_add_set_elem(&ctx, set, attr);
3131 if (err < 0)
Pablo Neira Ayuso60319eb2014-04-04 03:36:42 +02003132 break;
Patrick McHardy20a69342013-10-11 12:06:22 +02003133 }
Pablo Neira Ayuso60319eb2014-04-04 03:36:42 +02003134 return err;
Patrick McHardy20a69342013-10-11 12:06:22 +02003135}
3136
Pablo Neira Ayuso60319eb2014-04-04 03:36:42 +02003137static int nft_del_setelem(struct nft_ctx *ctx, struct nft_set *set,
Patrick McHardy20a69342013-10-11 12:06:22 +02003138 const struct nlattr *attr)
3139{
3140 struct nlattr *nla[NFTA_SET_ELEM_MAX + 1];
3141 struct nft_data_desc desc;
3142 struct nft_set_elem elem;
Pablo Neira Ayuso60319eb2014-04-04 03:36:42 +02003143 struct nft_trans *trans;
Patrick McHardy20a69342013-10-11 12:06:22 +02003144 int err;
3145
3146 err = nla_parse_nested(nla, NFTA_SET_ELEM_MAX, attr,
3147 nft_set_elem_policy);
3148 if (err < 0)
3149 goto err1;
3150
3151 err = -EINVAL;
3152 if (nla[NFTA_SET_ELEM_KEY] == NULL)
3153 goto err1;
3154
3155 err = nft_data_init(ctx, &elem.key, &desc, nla[NFTA_SET_ELEM_KEY]);
3156 if (err < 0)
3157 goto err1;
3158
3159 err = -EINVAL;
3160 if (desc.type != NFT_DATA_VALUE || desc.len != set->klen)
3161 goto err2;
3162
3163 err = set->ops->get(set, &elem);
3164 if (err < 0)
3165 goto err2;
3166
Pablo Neira Ayuso60319eb2014-04-04 03:36:42 +02003167 trans = nft_trans_elem_alloc(ctx, NFT_MSG_DELSETELEM, set);
3168 if (trans == NULL)
3169 goto err2;
Patrick McHardy20a69342013-10-11 12:06:22 +02003170
Pablo Neira Ayuso60319eb2014-04-04 03:36:42 +02003171 nft_trans_elem(trans) = elem;
3172 list_add(&trans->list, &ctx->net->nft.commit_list);
Arturo Borrerod60ce622014-04-01 14:06:07 +02003173
Patrick McHardy20a69342013-10-11 12:06:22 +02003174 nft_data_uninit(&elem.key, NFT_DATA_VALUE);
3175 if (set->flags & NFT_SET_MAP)
3176 nft_data_uninit(&elem.data, set->dtype);
3177
3178err2:
3179 nft_data_uninit(&elem.key, desc.type);
3180err1:
3181 return err;
3182}
3183
3184static int nf_tables_delsetelem(struct sock *nlsk, struct sk_buff *skb,
3185 const struct nlmsghdr *nlh,
3186 const struct nlattr * const nla[])
3187{
3188 const struct nlattr *attr;
3189 struct nft_set *set;
3190 struct nft_ctx ctx;
Pablo Neira Ayuso60319eb2014-04-04 03:36:42 +02003191 int rem, err = 0;
Patrick McHardy20a69342013-10-11 12:06:22 +02003192
Pablo Neira Ayuso55dd6f92014-04-03 11:53:37 +02003193 err = nft_ctx_init_from_elemattr(&ctx, skb, nlh, nla, false);
Patrick McHardy20a69342013-10-11 12:06:22 +02003194 if (err < 0)
3195 return err;
3196
3197 set = nf_tables_set_lookup(ctx.table, nla[NFTA_SET_ELEM_LIST_SET]);
3198 if (IS_ERR(set))
3199 return PTR_ERR(set);
3200 if (!list_empty(&set->bindings) && set->flags & NFT_SET_CONSTANT)
3201 return -EBUSY;
3202
3203 nla_for_each_nested(attr, nla[NFTA_SET_ELEM_LIST_ELEMENTS], rem) {
3204 err = nft_del_setelem(&ctx, set, attr);
3205 if (err < 0)
Pablo Neira Ayuso60319eb2014-04-04 03:36:42 +02003206 break;
Patrick McHardy20a69342013-10-11 12:06:22 +02003207 }
Pablo Neira Ayuso60319eb2014-04-04 03:36:42 +02003208 return err;
Patrick McHardy20a69342013-10-11 12:06:22 +02003209}
3210
Patrick McHardy96518512013-10-14 11:00:02 +02003211static const struct nfnl_callback nf_tables_cb[NFT_MSG_MAX] = {
3212 [NFT_MSG_NEWTABLE] = {
Pablo Neira Ayuso55dd6f92014-04-03 11:53:37 +02003213 .call_batch = nf_tables_newtable,
Patrick McHardy96518512013-10-14 11:00:02 +02003214 .attr_count = NFTA_TABLE_MAX,
3215 .policy = nft_table_policy,
3216 },
3217 [NFT_MSG_GETTABLE] = {
3218 .call = nf_tables_gettable,
3219 .attr_count = NFTA_TABLE_MAX,
3220 .policy = nft_table_policy,
3221 },
3222 [NFT_MSG_DELTABLE] = {
Pablo Neira Ayuso55dd6f92014-04-03 11:53:37 +02003223 .call_batch = nf_tables_deltable,
Patrick McHardy96518512013-10-14 11:00:02 +02003224 .attr_count = NFTA_TABLE_MAX,
3225 .policy = nft_table_policy,
3226 },
3227 [NFT_MSG_NEWCHAIN] = {
Pablo Neira Ayuso91c7b382014-04-09 11:58:08 +02003228 .call_batch = nf_tables_newchain,
Patrick McHardy96518512013-10-14 11:00:02 +02003229 .attr_count = NFTA_CHAIN_MAX,
3230 .policy = nft_chain_policy,
3231 },
3232 [NFT_MSG_GETCHAIN] = {
3233 .call = nf_tables_getchain,
3234 .attr_count = NFTA_CHAIN_MAX,
3235 .policy = nft_chain_policy,
3236 },
3237 [NFT_MSG_DELCHAIN] = {
Pablo Neira Ayuso91c7b382014-04-09 11:58:08 +02003238 .call_batch = nf_tables_delchain,
Patrick McHardy96518512013-10-14 11:00:02 +02003239 .attr_count = NFTA_CHAIN_MAX,
3240 .policy = nft_chain_policy,
3241 },
3242 [NFT_MSG_NEWRULE] = {
Pablo Neira Ayuso0628b122013-10-14 11:05:33 +02003243 .call_batch = nf_tables_newrule,
Patrick McHardy96518512013-10-14 11:00:02 +02003244 .attr_count = NFTA_RULE_MAX,
3245 .policy = nft_rule_policy,
3246 },
3247 [NFT_MSG_GETRULE] = {
3248 .call = nf_tables_getrule,
3249 .attr_count = NFTA_RULE_MAX,
3250 .policy = nft_rule_policy,
3251 },
3252 [NFT_MSG_DELRULE] = {
Pablo Neira Ayuso0628b122013-10-14 11:05:33 +02003253 .call_batch = nf_tables_delrule,
Patrick McHardy96518512013-10-14 11:00:02 +02003254 .attr_count = NFTA_RULE_MAX,
3255 .policy = nft_rule_policy,
3256 },
Patrick McHardy20a69342013-10-11 12:06:22 +02003257 [NFT_MSG_NEWSET] = {
Pablo Neira Ayuso958bee12014-04-03 11:48:44 +02003258 .call_batch = nf_tables_newset,
Patrick McHardy20a69342013-10-11 12:06:22 +02003259 .attr_count = NFTA_SET_MAX,
3260 .policy = nft_set_policy,
3261 },
3262 [NFT_MSG_GETSET] = {
3263 .call = nf_tables_getset,
3264 .attr_count = NFTA_SET_MAX,
3265 .policy = nft_set_policy,
3266 },
3267 [NFT_MSG_DELSET] = {
Pablo Neira Ayuso958bee12014-04-03 11:48:44 +02003268 .call_batch = nf_tables_delset,
Patrick McHardy20a69342013-10-11 12:06:22 +02003269 .attr_count = NFTA_SET_MAX,
3270 .policy = nft_set_policy,
3271 },
3272 [NFT_MSG_NEWSETELEM] = {
Pablo Neira Ayuso958bee12014-04-03 11:48:44 +02003273 .call_batch = nf_tables_newsetelem,
Patrick McHardy20a69342013-10-11 12:06:22 +02003274 .attr_count = NFTA_SET_ELEM_LIST_MAX,
3275 .policy = nft_set_elem_list_policy,
3276 },
3277 [NFT_MSG_GETSETELEM] = {
3278 .call = nf_tables_getsetelem,
3279 .attr_count = NFTA_SET_ELEM_LIST_MAX,
3280 .policy = nft_set_elem_list_policy,
3281 },
3282 [NFT_MSG_DELSETELEM] = {
Pablo Neira Ayuso958bee12014-04-03 11:48:44 +02003283 .call_batch = nf_tables_delsetelem,
Patrick McHardy20a69342013-10-11 12:06:22 +02003284 .attr_count = NFTA_SET_ELEM_LIST_MAX,
3285 .policy = nft_set_elem_list_policy,
3286 },
Patrick McHardy96518512013-10-14 11:00:02 +02003287};
3288
Pablo Neira Ayuso91c7b382014-04-09 11:58:08 +02003289static void nft_chain_commit_update(struct nft_trans *trans)
3290{
3291 struct nft_base_chain *basechain;
3292
3293 if (nft_trans_chain_name(trans)[0])
3294 strcpy(trans->ctx.chain->name, nft_trans_chain_name(trans));
3295
3296 if (!(trans->ctx.chain->flags & NFT_BASE_CHAIN))
3297 return;
3298
3299 basechain = nft_base_chain(trans->ctx.chain);
3300 nft_chain_stats_replace(basechain, nft_trans_chain_stats(trans));
3301
3302 switch (nft_trans_chain_policy(trans)) {
3303 case NF_DROP:
3304 case NF_ACCEPT:
3305 basechain->policy = nft_trans_chain_policy(trans);
3306 break;
3307 }
3308}
3309
Pablo Neira Ayuso37082f92014-04-03 11:56:37 +02003310static int nf_tables_commit(struct sk_buff *skb)
3311{
3312 struct net *net = sock_net(skb->sk);
3313 struct nft_trans *trans, *next;
Pablo Neira Ayuso60319eb2014-04-04 03:36:42 +02003314 struct nft_set *set;
Pablo Neira Ayuso37082f92014-04-03 11:56:37 +02003315
3316 /* Bump generation counter, invalidate any dump in progress */
3317 net->nft.genctr++;
3318
3319 /* A new generation has just started */
3320 net->nft.gencursor = gencursor_next(net);
3321
3322 /* Make sure all packets have left the previous generation before
3323 * purging old rules.
3324 */
3325 synchronize_rcu();
3326
3327 list_for_each_entry_safe(trans, next, &net->nft.commit_list, list) {
Pablo Neira Ayusob380e5c2014-04-04 01:38:51 +02003328 switch (trans->msg_type) {
Pablo Neira Ayuso55dd6f92014-04-03 11:53:37 +02003329 case NFT_MSG_NEWTABLE:
3330 if (nft_trans_table_update(trans)) {
3331 if (!nft_trans_table_enable(trans)) {
3332 nf_tables_table_disable(trans->ctx.afi,
3333 trans->ctx.table);
3334 trans->ctx.table->flags |= NFT_TABLE_F_DORMANT;
3335 }
3336 } else {
3337 trans->ctx.table->flags &= ~NFT_TABLE_INACTIVE;
3338 }
Pablo Neira Ayuso35151d82014-05-05 17:12:40 +02003339 nf_tables_table_notify(&trans->ctx, NFT_MSG_NEWTABLE);
Pablo Neira Ayuso55dd6f92014-04-03 11:53:37 +02003340 nft_trans_destroy(trans);
3341 break;
3342 case NFT_MSG_DELTABLE:
Pablo Neira Ayuso35151d82014-05-05 17:12:40 +02003343 nf_tables_table_notify(&trans->ctx, NFT_MSG_DELTABLE);
Pablo Neira Ayuso55dd6f92014-04-03 11:53:37 +02003344 break;
Pablo Neira Ayuso91c7b382014-04-09 11:58:08 +02003345 case NFT_MSG_NEWCHAIN:
3346 if (nft_trans_chain_update(trans))
3347 nft_chain_commit_update(trans);
3348 else {
3349 trans->ctx.chain->flags &= ~NFT_CHAIN_INACTIVE;
3350 trans->ctx.table->use++;
3351 }
Pablo Neira Ayuso35151d82014-05-05 17:12:40 +02003352 nf_tables_chain_notify(&trans->ctx, NFT_MSG_NEWCHAIN);
Pablo Neira Ayuso91c7b382014-04-09 11:58:08 +02003353 nft_trans_destroy(trans);
3354 break;
3355 case NFT_MSG_DELCHAIN:
3356 trans->ctx.table->use--;
Pablo Neira Ayuso35151d82014-05-05 17:12:40 +02003357 nf_tables_chain_notify(&trans->ctx, NFT_MSG_DELCHAIN);
Pablo Neira Ayuso91c7b382014-04-09 11:58:08 +02003358 if (!(trans->ctx.table->flags & NFT_TABLE_F_DORMANT) &&
3359 trans->ctx.chain->flags & NFT_BASE_CHAIN) {
3360 nf_unregister_hooks(nft_base_chain(trans->ctx.chain)->ops,
3361 trans->ctx.afi->nops);
3362 }
3363 break;
Pablo Neira Ayusob380e5c2014-04-04 01:38:51 +02003364 case NFT_MSG_NEWRULE:
3365 nft_rule_clear(trans->ctx.net, nft_trans_rule(trans));
Pablo Neira Ayuso35151d82014-05-05 17:12:40 +02003366 nf_tables_rule_notify(&trans->ctx,
Pablo Neira Ayuso37082f92014-04-03 11:56:37 +02003367 nft_trans_rule(trans),
Pablo Neira Ayuso35151d82014-05-05 17:12:40 +02003368 NFT_MSG_NEWRULE);
Pablo Neira Ayuso37082f92014-04-03 11:56:37 +02003369 nft_trans_destroy(trans);
Pablo Neira Ayusob380e5c2014-04-04 01:38:51 +02003370 break;
3371 case NFT_MSG_DELRULE:
3372 list_del_rcu(&nft_trans_rule(trans)->list);
Pablo Neira Ayuso35151d82014-05-05 17:12:40 +02003373 nf_tables_rule_notify(&trans->ctx,
3374 nft_trans_rule(trans),
3375 NFT_MSG_DELRULE);
Pablo Neira Ayusob380e5c2014-04-04 01:38:51 +02003376 break;
Pablo Neira Ayuso958bee12014-04-03 11:48:44 +02003377 case NFT_MSG_NEWSET:
3378 nft_trans_set(trans)->flags &= ~NFT_SET_INACTIVE;
3379 nf_tables_set_notify(&trans->ctx, nft_trans_set(trans),
3380 NFT_MSG_NEWSET);
3381 nft_trans_destroy(trans);
3382 break;
3383 case NFT_MSG_DELSET:
3384 nf_tables_set_notify(&trans->ctx, nft_trans_set(trans),
3385 NFT_MSG_DELSET);
3386 break;
Pablo Neira Ayuso60319eb2014-04-04 03:36:42 +02003387 case NFT_MSG_NEWSETELEM:
3388 nft_trans_elem_set(trans)->nelems++;
3389 nf_tables_setelem_notify(&trans->ctx,
3390 nft_trans_elem_set(trans),
3391 &nft_trans_elem(trans),
3392 NFT_MSG_NEWSETELEM, 0);
3393 nft_trans_destroy(trans);
3394 break;
3395 case NFT_MSG_DELSETELEM:
3396 nft_trans_elem_set(trans)->nelems--;
3397 nf_tables_setelem_notify(&trans->ctx,
3398 nft_trans_elem_set(trans),
3399 &nft_trans_elem(trans),
3400 NFT_MSG_DELSETELEM, 0);
3401 set = nft_trans_elem_set(trans);
3402 set->ops->get(set, &nft_trans_elem(trans));
3403 set->ops->remove(set, &nft_trans_elem(trans));
3404 nft_trans_destroy(trans);
3405 break;
Pablo Neira Ayuso37082f92014-04-03 11:56:37 +02003406 }
Pablo Neira Ayuso37082f92014-04-03 11:56:37 +02003407 }
3408
3409 /* Make sure we don't see any packet traversing old rules */
3410 synchronize_rcu();
3411
3412 /* Now we can safely release unused old rules */
3413 list_for_each_entry_safe(trans, next, &net->nft.commit_list, list) {
Pablo Neira Ayusob380e5c2014-04-04 01:38:51 +02003414 switch (trans->msg_type) {
Pablo Neira Ayuso55dd6f92014-04-03 11:53:37 +02003415 case NFT_MSG_DELTABLE:
3416 nf_tables_table_destroy(&trans->ctx);
3417 break;
Pablo Neira Ayuso91c7b382014-04-09 11:58:08 +02003418 case NFT_MSG_DELCHAIN:
3419 nf_tables_chain_destroy(trans->ctx.chain);
3420 break;
Pablo Neira Ayusob380e5c2014-04-04 01:38:51 +02003421 case NFT_MSG_DELRULE:
3422 nf_tables_rule_destroy(&trans->ctx,
3423 nft_trans_rule(trans));
Pablo Neira Ayuso958bee12014-04-03 11:48:44 +02003424 break;
3425 case NFT_MSG_DELSET:
3426 nft_set_destroy(nft_trans_set(trans));
Pablo Neira Ayusob380e5c2014-04-04 01:38:51 +02003427 break;
3428 }
Pablo Neira Ayuso958bee12014-04-03 11:48:44 +02003429 nft_trans_destroy(trans);
Pablo Neira Ayuso37082f92014-04-03 11:56:37 +02003430 }
3431
3432 return 0;
3433}
3434
3435static int nf_tables_abort(struct sk_buff *skb)
3436{
3437 struct net *net = sock_net(skb->sk);
3438 struct nft_trans *trans, *next;
Pablo Neira Ayuso60319eb2014-04-04 03:36:42 +02003439 struct nft_set *set;
Pablo Neira Ayuso37082f92014-04-03 11:56:37 +02003440
3441 list_for_each_entry_safe(trans, next, &net->nft.commit_list, list) {
Pablo Neira Ayusob380e5c2014-04-04 01:38:51 +02003442 switch (trans->msg_type) {
Pablo Neira Ayuso55dd6f92014-04-03 11:53:37 +02003443 case NFT_MSG_NEWTABLE:
3444 if (nft_trans_table_update(trans)) {
3445 if (nft_trans_table_enable(trans)) {
3446 nf_tables_table_disable(trans->ctx.afi,
3447 trans->ctx.table);
3448 trans->ctx.table->flags |= NFT_TABLE_F_DORMANT;
3449 }
3450 nft_trans_destroy(trans);
3451 } else {
3452 list_del(&trans->ctx.table->list);
3453 }
3454 break;
3455 case NFT_MSG_DELTABLE:
3456 list_add_tail(&trans->ctx.table->list,
3457 &trans->ctx.afi->tables);
3458 nft_trans_destroy(trans);
3459 break;
Pablo Neira Ayuso91c7b382014-04-09 11:58:08 +02003460 case NFT_MSG_NEWCHAIN:
3461 if (nft_trans_chain_update(trans)) {
3462 if (nft_trans_chain_stats(trans))
3463 free_percpu(nft_trans_chain_stats(trans));
3464
3465 nft_trans_destroy(trans);
3466 } else {
3467 list_del(&trans->ctx.chain->list);
3468 if (!(trans->ctx.table->flags & NFT_TABLE_F_DORMANT) &&
3469 trans->ctx.chain->flags & NFT_BASE_CHAIN) {
3470 nf_unregister_hooks(nft_base_chain(trans->ctx.chain)->ops,
3471 trans->ctx.afi->nops);
3472 }
3473 }
3474 break;
3475 case NFT_MSG_DELCHAIN:
3476 list_add_tail(&trans->ctx.chain->list,
3477 &trans->ctx.table->chains);
3478 nft_trans_destroy(trans);
3479 break;
Pablo Neira Ayusob380e5c2014-04-04 01:38:51 +02003480 case NFT_MSG_NEWRULE:
3481 list_del_rcu(&nft_trans_rule(trans)->list);
3482 break;
3483 case NFT_MSG_DELRULE:
3484 nft_rule_clear(trans->ctx.net, nft_trans_rule(trans));
Pablo Neira Ayuso37082f92014-04-03 11:56:37 +02003485 nft_trans_destroy(trans);
Pablo Neira Ayusob380e5c2014-04-04 01:38:51 +02003486 break;
Pablo Neira Ayuso958bee12014-04-03 11:48:44 +02003487 case NFT_MSG_NEWSET:
3488 list_del(&nft_trans_set(trans)->list);
3489 break;
3490 case NFT_MSG_DELSET:
3491 list_add_tail(&nft_trans_set(trans)->list,
3492 &trans->ctx.table->sets);
3493 nft_trans_destroy(trans);
3494 break;
Pablo Neira Ayuso60319eb2014-04-04 03:36:42 +02003495 case NFT_MSG_NEWSETELEM:
3496 set = nft_trans_elem_set(trans);
3497 set->ops->get(set, &nft_trans_elem(trans));
3498 set->ops->remove(set, &nft_trans_elem(trans));
3499 nft_trans_destroy(trans);
3500 break;
3501 case NFT_MSG_DELSETELEM:
3502 nft_trans_destroy(trans);
3503 break;
Pablo Neira Ayuso37082f92014-04-03 11:56:37 +02003504 }
Pablo Neira Ayuso37082f92014-04-03 11:56:37 +02003505 }
3506
3507 /* Make sure we don't see any packet accessing aborted rules */
3508 synchronize_rcu();
3509
3510 list_for_each_entry_safe(trans, next, &net->nft.commit_list, list) {
Pablo Neira Ayusob380e5c2014-04-04 01:38:51 +02003511 switch (trans->msg_type) {
Pablo Neira Ayuso55dd6f92014-04-03 11:53:37 +02003512 case NFT_MSG_NEWTABLE:
3513 nf_tables_table_destroy(&trans->ctx);
3514 break;
Pablo Neira Ayuso91c7b382014-04-09 11:58:08 +02003515 case NFT_MSG_NEWCHAIN:
3516 nf_tables_chain_destroy(trans->ctx.chain);
3517 break;
Pablo Neira Ayusob380e5c2014-04-04 01:38:51 +02003518 case NFT_MSG_NEWRULE:
3519 nf_tables_rule_destroy(&trans->ctx,
3520 nft_trans_rule(trans));
Pablo Neira Ayuso958bee12014-04-03 11:48:44 +02003521 break;
3522 case NFT_MSG_NEWSET:
3523 nft_set_destroy(nft_trans_set(trans));
Pablo Neira Ayusob380e5c2014-04-04 01:38:51 +02003524 break;
3525 }
Pablo Neira Ayuso958bee12014-04-03 11:48:44 +02003526 nft_trans_destroy(trans);
Pablo Neira Ayuso37082f92014-04-03 11:56:37 +02003527 }
3528
3529 return 0;
3530}
3531
Patrick McHardy96518512013-10-14 11:00:02 +02003532static const struct nfnetlink_subsystem nf_tables_subsys = {
3533 .name = "nf_tables",
3534 .subsys_id = NFNL_SUBSYS_NFTABLES,
3535 .cb_count = NFT_MSG_MAX,
3536 .cb = nf_tables_cb,
Pablo Neira Ayuso0628b122013-10-14 11:05:33 +02003537 .commit = nf_tables_commit,
3538 .abort = nf_tables_abort,
Patrick McHardy96518512013-10-14 11:00:02 +02003539};
3540
Patrick McHardy20a69342013-10-11 12:06:22 +02003541/*
3542 * Loop detection - walk through the ruleset beginning at the destination chain
3543 * of a new jump until either the source chain is reached (loop) or all
3544 * reachable chains have been traversed.
3545 *
3546 * The loop check is performed whenever a new jump verdict is added to an
3547 * expression or verdict map or a verdict map is bound to a new chain.
3548 */
3549
3550static int nf_tables_check_loops(const struct nft_ctx *ctx,
3551 const struct nft_chain *chain);
3552
3553static int nf_tables_loop_check_setelem(const struct nft_ctx *ctx,
3554 const struct nft_set *set,
3555 const struct nft_set_iter *iter,
3556 const struct nft_set_elem *elem)
3557{
Pablo Neira Ayuso62f9c8b2014-02-07 14:45:01 +01003558 if (elem->flags & NFT_SET_ELEM_INTERVAL_END)
3559 return 0;
3560
Patrick McHardy20a69342013-10-11 12:06:22 +02003561 switch (elem->data.verdict) {
3562 case NFT_JUMP:
3563 case NFT_GOTO:
3564 return nf_tables_check_loops(ctx, elem->data.chain);
3565 default:
3566 return 0;
3567 }
3568}
3569
3570static int nf_tables_check_loops(const struct nft_ctx *ctx,
3571 const struct nft_chain *chain)
3572{
3573 const struct nft_rule *rule;
3574 const struct nft_expr *expr, *last;
Patrick McHardy20a69342013-10-11 12:06:22 +02003575 const struct nft_set *set;
3576 struct nft_set_binding *binding;
3577 struct nft_set_iter iter;
Patrick McHardy20a69342013-10-11 12:06:22 +02003578
3579 if (ctx->chain == chain)
3580 return -ELOOP;
3581
3582 list_for_each_entry(rule, &chain->rules, list) {
3583 nft_rule_for_each_expr(expr, last, rule) {
Pablo Neira Ayuso0ca743a2013-10-14 00:06:06 +02003584 const struct nft_data *data = NULL;
3585 int err;
3586
3587 if (!expr->ops->validate)
Patrick McHardy20a69342013-10-11 12:06:22 +02003588 continue;
3589
Pablo Neira Ayuso0ca743a2013-10-14 00:06:06 +02003590 err = expr->ops->validate(ctx, expr, &data);
3591 if (err < 0)
3592 return err;
3593
Patrick McHardy20a69342013-10-11 12:06:22 +02003594 if (data == NULL)
Pablo Neira Ayuso0ca743a2013-10-14 00:06:06 +02003595 continue;
Patrick McHardy20a69342013-10-11 12:06:22 +02003596
3597 switch (data->verdict) {
3598 case NFT_JUMP:
3599 case NFT_GOTO:
3600 err = nf_tables_check_loops(ctx, data->chain);
3601 if (err < 0)
3602 return err;
3603 default:
3604 break;
3605 }
3606 }
3607 }
3608
3609 list_for_each_entry(set, &ctx->table->sets, list) {
3610 if (!(set->flags & NFT_SET_MAP) ||
3611 set->dtype != NFT_DATA_VERDICT)
3612 continue;
3613
3614 list_for_each_entry(binding, &set->bindings, list) {
3615 if (binding->chain != chain)
3616 continue;
3617
3618 iter.skip = 0;
3619 iter.count = 0;
3620 iter.err = 0;
3621 iter.fn = nf_tables_loop_check_setelem;
3622
3623 set->ops->walk(ctx, set, &iter);
3624 if (iter.err < 0)
3625 return iter.err;
3626 }
3627 }
3628
3629 return 0;
3630}
3631
Patrick McHardy96518512013-10-14 11:00:02 +02003632/**
3633 * nft_validate_input_register - validate an expressions' input register
3634 *
3635 * @reg: the register number
3636 *
3637 * Validate that the input register is one of the general purpose
3638 * registers.
3639 */
3640int nft_validate_input_register(enum nft_registers reg)
3641{
3642 if (reg <= NFT_REG_VERDICT)
3643 return -EINVAL;
3644 if (reg > NFT_REG_MAX)
3645 return -ERANGE;
3646 return 0;
3647}
3648EXPORT_SYMBOL_GPL(nft_validate_input_register);
3649
3650/**
3651 * nft_validate_output_register - validate an expressions' output register
3652 *
3653 * @reg: the register number
3654 *
3655 * Validate that the output register is one of the general purpose
3656 * registers or the verdict register.
3657 */
3658int nft_validate_output_register(enum nft_registers reg)
3659{
3660 if (reg < NFT_REG_VERDICT)
3661 return -EINVAL;
3662 if (reg > NFT_REG_MAX)
3663 return -ERANGE;
3664 return 0;
3665}
3666EXPORT_SYMBOL_GPL(nft_validate_output_register);
3667
3668/**
3669 * nft_validate_data_load - validate an expressions' data load
3670 *
3671 * @ctx: context of the expression performing the load
3672 * @reg: the destination register number
3673 * @data: the data to load
3674 * @type: the data type
3675 *
3676 * Validate that a data load uses the appropriate data type for
3677 * the destination register. A value of NULL for the data means
3678 * that its runtime gathered data, which is always of type
3679 * NFT_DATA_VALUE.
3680 */
3681int nft_validate_data_load(const struct nft_ctx *ctx, enum nft_registers reg,
3682 const struct nft_data *data,
3683 enum nft_data_types type)
3684{
Patrick McHardy20a69342013-10-11 12:06:22 +02003685 int err;
3686
Patrick McHardy96518512013-10-14 11:00:02 +02003687 switch (reg) {
3688 case NFT_REG_VERDICT:
3689 if (data == NULL || type != NFT_DATA_VERDICT)
3690 return -EINVAL;
Patrick McHardy20a69342013-10-11 12:06:22 +02003691
3692 if (data->verdict == NFT_GOTO || data->verdict == NFT_JUMP) {
3693 err = nf_tables_check_loops(ctx, data->chain);
3694 if (err < 0)
3695 return err;
3696
3697 if (ctx->chain->level + 1 > data->chain->level) {
3698 if (ctx->chain->level + 1 == NFT_JUMP_STACK_SIZE)
3699 return -EMLINK;
3700 data->chain->level = ctx->chain->level + 1;
3701 }
3702 }
3703
Patrick McHardy96518512013-10-14 11:00:02 +02003704 return 0;
3705 default:
3706 if (data != NULL && type != NFT_DATA_VALUE)
3707 return -EINVAL;
3708 return 0;
3709 }
3710}
3711EXPORT_SYMBOL_GPL(nft_validate_data_load);
3712
3713static const struct nla_policy nft_verdict_policy[NFTA_VERDICT_MAX + 1] = {
3714 [NFTA_VERDICT_CODE] = { .type = NLA_U32 },
3715 [NFTA_VERDICT_CHAIN] = { .type = NLA_STRING,
3716 .len = NFT_CHAIN_MAXNAMELEN - 1 },
3717};
3718
3719static int nft_verdict_init(const struct nft_ctx *ctx, struct nft_data *data,
3720 struct nft_data_desc *desc, const struct nlattr *nla)
3721{
3722 struct nlattr *tb[NFTA_VERDICT_MAX + 1];
3723 struct nft_chain *chain;
3724 int err;
3725
3726 err = nla_parse_nested(tb, NFTA_VERDICT_MAX, nla, nft_verdict_policy);
3727 if (err < 0)
3728 return err;
3729
3730 if (!tb[NFTA_VERDICT_CODE])
3731 return -EINVAL;
3732 data->verdict = ntohl(nla_get_be32(tb[NFTA_VERDICT_CODE]));
3733
3734 switch (data->verdict) {
Patrick McHardye0abdad2014-02-18 18:06:50 +00003735 default:
3736 switch (data->verdict & NF_VERDICT_MASK) {
3737 case NF_ACCEPT:
3738 case NF_DROP:
3739 case NF_QUEUE:
3740 break;
3741 default:
3742 return -EINVAL;
3743 }
3744 /* fall through */
Patrick McHardy96518512013-10-14 11:00:02 +02003745 case NFT_CONTINUE:
3746 case NFT_BREAK:
3747 case NFT_RETURN:
3748 desc->len = sizeof(data->verdict);
3749 break;
3750 case NFT_JUMP:
3751 case NFT_GOTO:
3752 if (!tb[NFTA_VERDICT_CHAIN])
3753 return -EINVAL;
3754 chain = nf_tables_chain_lookup(ctx->table,
3755 tb[NFTA_VERDICT_CHAIN]);
3756 if (IS_ERR(chain))
3757 return PTR_ERR(chain);
3758 if (chain->flags & NFT_BASE_CHAIN)
3759 return -EOPNOTSUPP;
3760
Patrick McHardy96518512013-10-14 11:00:02 +02003761 chain->use++;
3762 data->chain = chain;
3763 desc->len = sizeof(data);
3764 break;
Patrick McHardy96518512013-10-14 11:00:02 +02003765 }
3766
3767 desc->type = NFT_DATA_VERDICT;
3768 return 0;
3769}
3770
3771static void nft_verdict_uninit(const struct nft_data *data)
3772{
3773 switch (data->verdict) {
3774 case NFT_JUMP:
3775 case NFT_GOTO:
3776 data->chain->use--;
3777 break;
3778 }
3779}
3780
3781static int nft_verdict_dump(struct sk_buff *skb, const struct nft_data *data)
3782{
3783 struct nlattr *nest;
3784
3785 nest = nla_nest_start(skb, NFTA_DATA_VERDICT);
3786 if (!nest)
3787 goto nla_put_failure;
3788
3789 if (nla_put_be32(skb, NFTA_VERDICT_CODE, htonl(data->verdict)))
3790 goto nla_put_failure;
3791
3792 switch (data->verdict) {
3793 case NFT_JUMP:
3794 case NFT_GOTO:
3795 if (nla_put_string(skb, NFTA_VERDICT_CHAIN, data->chain->name))
3796 goto nla_put_failure;
3797 }
3798 nla_nest_end(skb, nest);
3799 return 0;
3800
3801nla_put_failure:
3802 return -1;
3803}
3804
3805static int nft_value_init(const struct nft_ctx *ctx, struct nft_data *data,
3806 struct nft_data_desc *desc, const struct nlattr *nla)
3807{
3808 unsigned int len;
3809
3810 len = nla_len(nla);
3811 if (len == 0)
3812 return -EINVAL;
3813 if (len > sizeof(data->data))
3814 return -EOVERFLOW;
3815
3816 nla_memcpy(data->data, nla, sizeof(data->data));
3817 desc->type = NFT_DATA_VALUE;
3818 desc->len = len;
3819 return 0;
3820}
3821
3822static int nft_value_dump(struct sk_buff *skb, const struct nft_data *data,
3823 unsigned int len)
3824{
3825 return nla_put(skb, NFTA_DATA_VALUE, len, data->data);
3826}
3827
3828static const struct nla_policy nft_data_policy[NFTA_DATA_MAX + 1] = {
3829 [NFTA_DATA_VALUE] = { .type = NLA_BINARY,
3830 .len = FIELD_SIZEOF(struct nft_data, data) },
3831 [NFTA_DATA_VERDICT] = { .type = NLA_NESTED },
3832};
3833
3834/**
3835 * nft_data_init - parse nf_tables data netlink attributes
3836 *
3837 * @ctx: context of the expression using the data
3838 * @data: destination struct nft_data
3839 * @desc: data description
3840 * @nla: netlink attribute containing data
3841 *
3842 * Parse the netlink data attributes and initialize a struct nft_data.
3843 * The type and length of data are returned in the data description.
3844 *
3845 * The caller can indicate that it only wants to accept data of type
3846 * NFT_DATA_VALUE by passing NULL for the ctx argument.
3847 */
3848int nft_data_init(const struct nft_ctx *ctx, struct nft_data *data,
3849 struct nft_data_desc *desc, const struct nlattr *nla)
3850{
3851 struct nlattr *tb[NFTA_DATA_MAX + 1];
3852 int err;
3853
3854 err = nla_parse_nested(tb, NFTA_DATA_MAX, nla, nft_data_policy);
3855 if (err < 0)
3856 return err;
3857
3858 if (tb[NFTA_DATA_VALUE])
3859 return nft_value_init(ctx, data, desc, tb[NFTA_DATA_VALUE]);
3860 if (tb[NFTA_DATA_VERDICT] && ctx != NULL)
3861 return nft_verdict_init(ctx, data, desc, tb[NFTA_DATA_VERDICT]);
3862 return -EINVAL;
3863}
3864EXPORT_SYMBOL_GPL(nft_data_init);
3865
3866/**
3867 * nft_data_uninit - release a nft_data item
3868 *
3869 * @data: struct nft_data to release
3870 * @type: type of data
3871 *
3872 * Release a nft_data item. NFT_DATA_VALUE types can be silently discarded,
3873 * all others need to be released by calling this function.
3874 */
3875void nft_data_uninit(const struct nft_data *data, enum nft_data_types type)
3876{
3877 switch (type) {
3878 case NFT_DATA_VALUE:
3879 return;
3880 case NFT_DATA_VERDICT:
3881 return nft_verdict_uninit(data);
3882 default:
3883 WARN_ON(1);
3884 }
3885}
3886EXPORT_SYMBOL_GPL(nft_data_uninit);
3887
3888int nft_data_dump(struct sk_buff *skb, int attr, const struct nft_data *data,
3889 enum nft_data_types type, unsigned int len)
3890{
3891 struct nlattr *nest;
3892 int err;
3893
3894 nest = nla_nest_start(skb, attr);
3895 if (nest == NULL)
3896 return -1;
3897
3898 switch (type) {
3899 case NFT_DATA_VALUE:
3900 err = nft_value_dump(skb, data, len);
3901 break;
3902 case NFT_DATA_VERDICT:
3903 err = nft_verdict_dump(skb, data);
3904 break;
3905 default:
3906 err = -EINVAL;
3907 WARN_ON(1);
3908 }
3909
3910 nla_nest_end(skb, nest);
3911 return err;
3912}
3913EXPORT_SYMBOL_GPL(nft_data_dump);
3914
Pablo Neira Ayuso99633ab2013-10-10 23:28:33 +02003915static int nf_tables_init_net(struct net *net)
3916{
3917 INIT_LIST_HEAD(&net->nft.af_info);
Pablo Neira Ayuso0628b122013-10-14 11:05:33 +02003918 INIT_LIST_HEAD(&net->nft.commit_list);
Pablo Neira Ayuso99633ab2013-10-10 23:28:33 +02003919 return 0;
3920}
3921
3922static struct pernet_operations nf_tables_net_ops = {
3923 .init = nf_tables_init_net,
3924};
3925
Patrick McHardy96518512013-10-14 11:00:02 +02003926static int __init nf_tables_module_init(void)
3927{
3928 int err;
3929
3930 info = kmalloc(sizeof(struct nft_expr_info) * NFT_RULE_MAXEXPRS,
3931 GFP_KERNEL);
3932 if (info == NULL) {
3933 err = -ENOMEM;
3934 goto err1;
3935 }
3936
3937 err = nf_tables_core_module_init();
3938 if (err < 0)
3939 goto err2;
3940
3941 err = nfnetlink_subsys_register(&nf_tables_subsys);
3942 if (err < 0)
3943 goto err3;
3944
3945 pr_info("nf_tables: (c) 2007-2009 Patrick McHardy <kaber@trash.net>\n");
Pablo Neira Ayuso99633ab2013-10-10 23:28:33 +02003946 return register_pernet_subsys(&nf_tables_net_ops);
Patrick McHardy96518512013-10-14 11:00:02 +02003947err3:
3948 nf_tables_core_module_exit();
3949err2:
3950 kfree(info);
3951err1:
3952 return err;
3953}
3954
3955static void __exit nf_tables_module_exit(void)
3956{
Pablo Neira Ayuso99633ab2013-10-10 23:28:33 +02003957 unregister_pernet_subsys(&nf_tables_net_ops);
Patrick McHardy96518512013-10-14 11:00:02 +02003958 nfnetlink_subsys_unregister(&nf_tables_subsys);
3959 nf_tables_core_module_exit();
3960 kfree(info);
3961}
3962
3963module_init(nf_tables_module_init);
3964module_exit(nf_tables_module_exit);
3965
3966MODULE_LICENSE("GPL");
3967MODULE_AUTHOR("Patrick McHardy <kaber@trash.net>");
3968MODULE_ALIAS_NFNL_SUBSYS(NFNL_SUBSYS_NFTABLES);