blob: 5586426a6169d2915e9acaffce8b31a86686772c [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{
Pablo Neira Ayuso128ad332014-05-09 17:14:24 +020099 ctx->net = sock_net(skb->sk);
100 ctx->afi = afi;
101 ctx->table = table;
102 ctx->chain = chain;
103 ctx->nla = nla;
104 ctx->portid = NETLINK_CB(skb).portid;
105 ctx->report = nlmsg_report(nlh);
106 ctx->seq = nlh->nlmsg_seq;
Pablo Neira Ayuso7c95f6d2014-04-04 01:22:45 +0200107}
108
Pablo Neira Ayusob380e5c2014-04-04 01:38:51 +0200109static struct nft_trans *nft_trans_alloc(struct nft_ctx *ctx, int msg_type,
110 u32 size)
Pablo Neira Ayuso1081d112014-04-04 01:24:07 +0200111{
112 struct nft_trans *trans;
113
114 trans = kzalloc(sizeof(struct nft_trans) + size, GFP_KERNEL);
115 if (trans == NULL)
116 return NULL;
117
Pablo Neira Ayusob380e5c2014-04-04 01:38:51 +0200118 trans->msg_type = msg_type;
Pablo Neira Ayuso1081d112014-04-04 01:24:07 +0200119 trans->ctx = *ctx;
120
121 return trans;
122}
123
124static void nft_trans_destroy(struct nft_trans *trans)
125{
126 list_del(&trans->list);
127 kfree(trans);
128}
129
Patrick McHardy96518512013-10-14 11:00:02 +0200130/*
131 * Tables
132 */
133
134static struct nft_table *nft_table_lookup(const struct nft_af_info *afi,
135 const struct nlattr *nla)
136{
137 struct nft_table *table;
138
139 list_for_each_entry(table, &afi->tables, list) {
140 if (!nla_strcmp(nla, table->name))
141 return table;
142 }
143 return NULL;
144}
145
146static struct nft_table *nf_tables_table_lookup(const struct nft_af_info *afi,
Pablo Neira Ayuso93707612013-10-10 23:21:26 +0200147 const struct nlattr *nla)
Patrick McHardy96518512013-10-14 11:00:02 +0200148{
149 struct nft_table *table;
150
151 if (nla == NULL)
152 return ERR_PTR(-EINVAL);
153
154 table = nft_table_lookup(afi, nla);
155 if (table != NULL)
156 return table;
157
Patrick McHardy96518512013-10-14 11:00:02 +0200158 return ERR_PTR(-ENOENT);
159}
160
161static inline u64 nf_tables_alloc_handle(struct nft_table *table)
162{
163 return ++table->hgenerator;
164}
165
Patrick McHardy2a37d752014-01-09 18:42:37 +0000166static const struct nf_chain_type *chain_type[AF_MAX][NFT_CHAIN_T_MAX];
Pablo Neira Ayuso93707612013-10-10 23:21:26 +0200167
Patrick McHardy2a37d752014-01-09 18:42:37 +0000168static const struct nf_chain_type *
Patrick McHardybaae3e62014-01-09 18:42:34 +0000169__nf_tables_chain_type_lookup(int family, const struct nlattr *nla)
Pablo Neira Ayuso93707612013-10-10 23:21:26 +0200170{
171 int i;
172
Patrick McHardybaae3e62014-01-09 18:42:34 +0000173 for (i = 0; i < NFT_CHAIN_T_MAX; i++) {
Pablo Neira Ayuso93707612013-10-10 23:21:26 +0200174 if (chain_type[family][i] != NULL &&
175 !nla_strcmp(nla, chain_type[family][i]->name))
Patrick McHardybaae3e62014-01-09 18:42:34 +0000176 return chain_type[family][i];
Pablo Neira Ayuso93707612013-10-10 23:21:26 +0200177 }
Patrick McHardybaae3e62014-01-09 18:42:34 +0000178 return NULL;
Pablo Neira Ayuso93707612013-10-10 23:21:26 +0200179}
180
Patrick McHardy2a37d752014-01-09 18:42:37 +0000181static const struct nf_chain_type *
Patrick McHardybaae3e62014-01-09 18:42:34 +0000182nf_tables_chain_type_lookup(const struct nft_af_info *afi,
183 const struct nlattr *nla,
184 bool autoload)
Pablo Neira Ayuso93707612013-10-10 23:21:26 +0200185{
Patrick McHardy2a37d752014-01-09 18:42:37 +0000186 const struct nf_chain_type *type;
Pablo Neira Ayuso93707612013-10-10 23:21:26 +0200187
188 type = __nf_tables_chain_type_lookup(afi->family, nla);
Patrick McHardy93b08062014-01-09 18:42:36 +0000189 if (type != NULL)
190 return type;
Pablo Neira Ayuso93707612013-10-10 23:21:26 +0200191#ifdef CONFIG_MODULES
Patrick McHardy93b08062014-01-09 18:42:36 +0000192 if (autoload) {
Pablo Neira Ayuso93707612013-10-10 23:21:26 +0200193 nfnl_unlock(NFNL_SUBSYS_NFTABLES);
Pablo Neira Ayuso2fec6bb2014-03-31 12:26:39 +0200194 request_module("nft-chain-%u-%.*s", afi->family,
195 nla_len(nla), (const char *)nla_data(nla));
Pablo Neira Ayuso93707612013-10-10 23:21:26 +0200196 nfnl_lock(NFNL_SUBSYS_NFTABLES);
197 type = __nf_tables_chain_type_lookup(afi->family, nla);
Patrick McHardy93b08062014-01-09 18:42:36 +0000198 if (type != NULL)
199 return ERR_PTR(-EAGAIN);
Pablo Neira Ayuso93707612013-10-10 23:21:26 +0200200 }
201#endif
Patrick McHardy93b08062014-01-09 18:42:36 +0000202 return ERR_PTR(-ENOENT);
Pablo Neira Ayuso93707612013-10-10 23:21:26 +0200203}
204
Patrick McHardy96518512013-10-14 11:00:02 +0200205static const struct nla_policy nft_table_policy[NFTA_TABLE_MAX + 1] = {
206 [NFTA_TABLE_NAME] = { .type = NLA_STRING },
Pablo Neira Ayuso9ddf6322013-10-10 13:26:33 +0200207 [NFTA_TABLE_FLAGS] = { .type = NLA_U32 },
Patrick McHardy96518512013-10-14 11:00:02 +0200208};
209
210static int nf_tables_fill_table_info(struct sk_buff *skb, u32 portid, u32 seq,
211 int event, u32 flags, int family,
212 const struct nft_table *table)
213{
214 struct nlmsghdr *nlh;
215 struct nfgenmsg *nfmsg;
216
217 event |= NFNL_SUBSYS_NFTABLES << 8;
218 nlh = nlmsg_put(skb, portid, seq, event, sizeof(struct nfgenmsg), flags);
219 if (nlh == NULL)
220 goto nla_put_failure;
221
222 nfmsg = nlmsg_data(nlh);
223 nfmsg->nfgen_family = family;
224 nfmsg->version = NFNETLINK_V0;
225 nfmsg->res_id = 0;
226
Pablo Neira Ayuso9ddf6322013-10-10 13:26:33 +0200227 if (nla_put_string(skb, NFTA_TABLE_NAME, table->name) ||
Tomasz Bursztykad8bcc7682013-12-12 15:00:42 +0200228 nla_put_be32(skb, NFTA_TABLE_FLAGS, htonl(table->flags)) ||
229 nla_put_be32(skb, NFTA_TABLE_USE, htonl(table->use)))
Patrick McHardy96518512013-10-14 11:00:02 +0200230 goto nla_put_failure;
231
232 return nlmsg_end(skb, nlh);
233
234nla_put_failure:
235 nlmsg_trim(skb, nlh);
236 return -1;
237}
238
Pablo Neira Ayuso35151d82014-05-05 17:12:40 +0200239static int nf_tables_table_notify(const struct nft_ctx *ctx, int event)
Patrick McHardy96518512013-10-14 11:00:02 +0200240{
241 struct sk_buff *skb;
Patrick McHardy96518512013-10-14 11:00:02 +0200242 int err;
243
Pablo Neira Ayuso128ad332014-05-09 17:14:24 +0200244 if (!ctx->report &&
245 !nfnetlink_has_listeners(ctx->net, NFNLGRP_NFTABLES))
Patrick McHardy96518512013-10-14 11:00:02 +0200246 return 0;
247
248 err = -ENOBUFS;
249 skb = nlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL);
250 if (skb == NULL)
251 goto err;
252
Pablo Neira Ayuso128ad332014-05-09 17:14:24 +0200253 err = nf_tables_fill_table_info(skb, ctx->portid, ctx->seq, event, 0,
Pablo Neira Ayuso35151d82014-05-05 17:12:40 +0200254 ctx->afi->family, ctx->table);
Patrick McHardy96518512013-10-14 11:00:02 +0200255 if (err < 0) {
256 kfree_skb(skb);
257 goto err;
258 }
259
Pablo Neira Ayuso128ad332014-05-09 17:14:24 +0200260 err = nfnetlink_send(skb, ctx->net, ctx->portid, NFNLGRP_NFTABLES,
261 ctx->report, GFP_KERNEL);
Patrick McHardy96518512013-10-14 11:00:02 +0200262err:
Pablo Neira Ayuso128ad332014-05-09 17:14:24 +0200263 if (err < 0) {
264 nfnetlink_set_err(ctx->net, ctx->portid, NFNLGRP_NFTABLES,
265 err);
266 }
Patrick McHardy96518512013-10-14 11:00:02 +0200267 return err;
268}
269
270static int nf_tables_dump_tables(struct sk_buff *skb,
271 struct netlink_callback *cb)
272{
273 const struct nfgenmsg *nfmsg = nlmsg_data(cb->nlh);
274 const struct nft_af_info *afi;
275 const struct nft_table *table;
276 unsigned int idx = 0, s_idx = cb->args[0];
Pablo Neira Ayuso99633ab2013-10-10 23:28:33 +0200277 struct net *net = sock_net(skb->sk);
Patrick McHardy96518512013-10-14 11:00:02 +0200278 int family = nfmsg->nfgen_family;
279
Pablo Neira Ayuso99633ab2013-10-10 23:28:33 +0200280 list_for_each_entry(afi, &net->nft.af_info, list) {
Patrick McHardy96518512013-10-14 11:00:02 +0200281 if (family != NFPROTO_UNSPEC && family != afi->family)
282 continue;
283
284 list_for_each_entry(table, &afi->tables, list) {
285 if (idx < s_idx)
286 goto cont;
287 if (idx > s_idx)
288 memset(&cb->args[1], 0,
289 sizeof(cb->args) - sizeof(cb->args[0]));
290 if (nf_tables_fill_table_info(skb,
291 NETLINK_CB(cb->skb).portid,
292 cb->nlh->nlmsg_seq,
293 NFT_MSG_NEWTABLE,
294 NLM_F_MULTI,
295 afi->family, table) < 0)
296 goto done;
297cont:
298 idx++;
299 }
300 }
301done:
302 cb->args[0] = idx;
303 return skb->len;
304}
305
Pablo Neira Ayuso55dd6f92014-04-03 11:53:37 +0200306/* Internal table flags */
307#define NFT_TABLE_INACTIVE (1 << 15)
308
Patrick McHardy96518512013-10-14 11:00:02 +0200309static int nf_tables_gettable(struct sock *nlsk, struct sk_buff *skb,
310 const struct nlmsghdr *nlh,
311 const struct nlattr * const nla[])
312{
313 const struct nfgenmsg *nfmsg = nlmsg_data(nlh);
314 const struct nft_af_info *afi;
315 const struct nft_table *table;
316 struct sk_buff *skb2;
Pablo Neira Ayuso99633ab2013-10-10 23:28:33 +0200317 struct net *net = sock_net(skb->sk);
Patrick McHardy96518512013-10-14 11:00:02 +0200318 int family = nfmsg->nfgen_family;
319 int err;
320
321 if (nlh->nlmsg_flags & NLM_F_DUMP) {
322 struct netlink_dump_control c = {
323 .dump = nf_tables_dump_tables,
324 };
325 return netlink_dump_start(nlsk, skb, nlh, &c);
326 }
327
Pablo Neira Ayuso99633ab2013-10-10 23:28:33 +0200328 afi = nf_tables_afinfo_lookup(net, family, false);
Patrick McHardy96518512013-10-14 11:00:02 +0200329 if (IS_ERR(afi))
330 return PTR_ERR(afi);
331
Pablo Neira Ayuso93707612013-10-10 23:21:26 +0200332 table = nf_tables_table_lookup(afi, nla[NFTA_TABLE_NAME]);
Patrick McHardy96518512013-10-14 11:00:02 +0200333 if (IS_ERR(table))
334 return PTR_ERR(table);
Pablo Neira Ayuso55dd6f92014-04-03 11:53:37 +0200335 if (table->flags & NFT_TABLE_INACTIVE)
336 return -ENOENT;
Patrick McHardy96518512013-10-14 11:00:02 +0200337
338 skb2 = alloc_skb(NLMSG_GOODSIZE, GFP_KERNEL);
339 if (!skb2)
340 return -ENOMEM;
341
342 err = nf_tables_fill_table_info(skb2, NETLINK_CB(skb).portid,
343 nlh->nlmsg_seq, NFT_MSG_NEWTABLE, 0,
344 family, table);
345 if (err < 0)
346 goto err;
347
348 return nlmsg_unicast(nlsk, skb2, NETLINK_CB(skb).portid);
349
350err:
351 kfree_skb(skb2);
352 return err;
353}
354
Patrick McHardy115a60b2014-01-03 12:16:15 +0000355static int nf_tables_table_enable(const struct nft_af_info *afi,
356 struct nft_table *table)
Pablo Neira Ayuso9ddf6322013-10-10 13:26:33 +0200357{
358 struct nft_chain *chain;
359 int err, i = 0;
360
361 list_for_each_entry(chain, &table->chains, list) {
Pablo Neira Ayusod2012972013-12-27 10:44:23 +0100362 if (!(chain->flags & NFT_BASE_CHAIN))
363 continue;
364
Patrick McHardy115a60b2014-01-03 12:16:15 +0000365 err = nf_register_hooks(nft_base_chain(chain)->ops, afi->nops);
Pablo Neira Ayuso9ddf6322013-10-10 13:26:33 +0200366 if (err < 0)
367 goto err;
368
369 i++;
370 }
371 return 0;
372err:
373 list_for_each_entry(chain, &table->chains, list) {
Pablo Neira Ayusod2012972013-12-27 10:44:23 +0100374 if (!(chain->flags & NFT_BASE_CHAIN))
375 continue;
376
Pablo Neira Ayuso9ddf6322013-10-10 13:26:33 +0200377 if (i-- <= 0)
378 break;
379
Patrick McHardy115a60b2014-01-03 12:16:15 +0000380 nf_unregister_hooks(nft_base_chain(chain)->ops, afi->nops);
Pablo Neira Ayuso9ddf6322013-10-10 13:26:33 +0200381 }
382 return err;
383}
384
Pablo Neira Ayusof75edf52014-03-30 14:04:52 +0200385static void nf_tables_table_disable(const struct nft_af_info *afi,
Patrick McHardy115a60b2014-01-03 12:16:15 +0000386 struct nft_table *table)
Pablo Neira Ayuso9ddf6322013-10-10 13:26:33 +0200387{
388 struct nft_chain *chain;
389
Pablo Neira Ayusod2012972013-12-27 10:44:23 +0100390 list_for_each_entry(chain, &table->chains, list) {
391 if (chain->flags & NFT_BASE_CHAIN)
Patrick McHardy115a60b2014-01-03 12:16:15 +0000392 nf_unregister_hooks(nft_base_chain(chain)->ops,
393 afi->nops);
Pablo Neira Ayusod2012972013-12-27 10:44:23 +0100394 }
Pablo Neira Ayuso9ddf6322013-10-10 13:26:33 +0200395}
396
Pablo Neira Ayusoe1aaca92014-03-30 14:04:53 +0200397static int nf_tables_updtable(struct nft_ctx *ctx)
Pablo Neira Ayuso9ddf6322013-10-10 13:26:33 +0200398{
Pablo Neira Ayuso55dd6f92014-04-03 11:53:37 +0200399 struct nft_trans *trans;
Pablo Neira Ayusoe1aaca92014-03-30 14:04:53 +0200400 u32 flags;
Pablo Neira Ayuso55dd6f92014-04-03 11:53:37 +0200401 int ret = 0;
Pablo Neira Ayuso9ddf6322013-10-10 13:26:33 +0200402
Pablo Neira Ayusoe1aaca92014-03-30 14:04:53 +0200403 if (!ctx->nla[NFTA_TABLE_FLAGS])
404 return 0;
Pablo Neira Ayuso9ddf6322013-10-10 13:26:33 +0200405
Pablo Neira Ayusoe1aaca92014-03-30 14:04:53 +0200406 flags = ntohl(nla_get_be32(ctx->nla[NFTA_TABLE_FLAGS]));
407 if (flags & ~NFT_TABLE_F_DORMANT)
408 return -EINVAL;
Pablo Neira Ayuso9ddf6322013-10-10 13:26:33 +0200409
Pablo Neira Ayuso55dd6f92014-04-03 11:53:37 +0200410 trans = nft_trans_alloc(ctx, NFT_MSG_NEWTABLE,
411 sizeof(struct nft_trans_table));
412 if (trans == NULL)
413 return -ENOMEM;
414
Pablo Neira Ayusoe1aaca92014-03-30 14:04:53 +0200415 if ((flags & NFT_TABLE_F_DORMANT) &&
416 !(ctx->table->flags & NFT_TABLE_F_DORMANT)) {
Pablo Neira Ayuso55dd6f92014-04-03 11:53:37 +0200417 nft_trans_table_enable(trans) = false;
Pablo Neira Ayusoe1aaca92014-03-30 14:04:53 +0200418 } else if (!(flags & NFT_TABLE_F_DORMANT) &&
419 ctx->table->flags & NFT_TABLE_F_DORMANT) {
420 ret = nf_tables_table_enable(ctx->afi, ctx->table);
Pablo Neira Ayuso55dd6f92014-04-03 11:53:37 +0200421 if (ret >= 0) {
Pablo Neira Ayusoe1aaca92014-03-30 14:04:53 +0200422 ctx->table->flags &= ~NFT_TABLE_F_DORMANT;
Pablo Neira Ayuso55dd6f92014-04-03 11:53:37 +0200423 nft_trans_table_enable(trans) = true;
Pablo Neira Ayuso9ddf6322013-10-10 13:26:33 +0200424 }
Pablo Neira Ayuso9ddf6322013-10-10 13:26:33 +0200425 }
Pablo Neira Ayusoe1aaca92014-03-30 14:04:53 +0200426 if (ret < 0)
427 goto err;
Pablo Neira Ayuso9ddf6322013-10-10 13:26:33 +0200428
Pablo Neira Ayuso55dd6f92014-04-03 11:53:37 +0200429 nft_trans_table_update(trans) = true;
430 list_add_tail(&trans->list, &ctx->net->nft.commit_list);
431 return 0;
Pablo Neira Ayuso9ddf6322013-10-10 13:26:33 +0200432err:
Pablo Neira Ayuso55dd6f92014-04-03 11:53:37 +0200433 nft_trans_destroy(trans);
Pablo Neira Ayuso9ddf6322013-10-10 13:26:33 +0200434 return ret;
435}
436
Pablo Neira Ayuso55dd6f92014-04-03 11:53:37 +0200437static int nft_trans_table_add(struct nft_ctx *ctx, int msg_type)
438{
439 struct nft_trans *trans;
440
441 trans = nft_trans_alloc(ctx, msg_type, sizeof(struct nft_trans_table));
442 if (trans == NULL)
443 return -ENOMEM;
444
445 if (msg_type == NFT_MSG_NEWTABLE)
446 ctx->table->flags |= NFT_TABLE_INACTIVE;
447
448 list_add_tail(&trans->list, &ctx->net->nft.commit_list);
449 return 0;
450}
451
Patrick McHardy96518512013-10-14 11:00:02 +0200452static int nf_tables_newtable(struct sock *nlsk, struct sk_buff *skb,
453 const struct nlmsghdr *nlh,
454 const struct nlattr * const nla[])
455{
456 const struct nfgenmsg *nfmsg = nlmsg_data(nlh);
457 const struct nlattr *name;
458 struct nft_af_info *afi;
459 struct nft_table *table;
Pablo Neira Ayuso99633ab2013-10-10 23:28:33 +0200460 struct net *net = sock_net(skb->sk);
Patrick McHardy96518512013-10-14 11:00:02 +0200461 int family = nfmsg->nfgen_family;
Patrick McHardyc5c1f972014-01-09 18:42:39 +0000462 u32 flags = 0;
Pablo Neira Ayusoe1aaca92014-03-30 14:04:53 +0200463 struct nft_ctx ctx;
Pablo Neira Ayuso55dd6f92014-04-03 11:53:37 +0200464 int err;
Patrick McHardy96518512013-10-14 11:00:02 +0200465
Pablo Neira Ayuso99633ab2013-10-10 23:28:33 +0200466 afi = nf_tables_afinfo_lookup(net, family, true);
Patrick McHardy96518512013-10-14 11:00:02 +0200467 if (IS_ERR(afi))
468 return PTR_ERR(afi);
469
470 name = nla[NFTA_TABLE_NAME];
Pablo Neira Ayuso93707612013-10-10 23:21:26 +0200471 table = nf_tables_table_lookup(afi, name);
Patrick McHardy96518512013-10-14 11:00:02 +0200472 if (IS_ERR(table)) {
473 if (PTR_ERR(table) != -ENOENT)
474 return PTR_ERR(table);
475 table = NULL;
476 }
477
478 if (table != NULL) {
Pablo Neira Ayuso55dd6f92014-04-03 11:53:37 +0200479 if (table->flags & NFT_TABLE_INACTIVE)
480 return -ENOENT;
Patrick McHardy96518512013-10-14 11:00:02 +0200481 if (nlh->nlmsg_flags & NLM_F_EXCL)
482 return -EEXIST;
483 if (nlh->nlmsg_flags & NLM_F_REPLACE)
484 return -EOPNOTSUPP;
Pablo Neira Ayusoe1aaca92014-03-30 14:04:53 +0200485
486 nft_ctx_init(&ctx, skb, nlh, afi, table, NULL, nla);
487 return nf_tables_updtable(&ctx);
Patrick McHardy96518512013-10-14 11:00:02 +0200488 }
489
Patrick McHardyc5c1f972014-01-09 18:42:39 +0000490 if (nla[NFTA_TABLE_FLAGS]) {
491 flags = ntohl(nla_get_be32(nla[NFTA_TABLE_FLAGS]));
492 if (flags & ~NFT_TABLE_F_DORMANT)
493 return -EINVAL;
494 }
495
Patrick McHardy7047f9d2014-01-09 18:42:40 +0000496 if (!try_module_get(afi->owner))
497 return -EAFNOSUPPORT;
498
Patrick McHardy96518512013-10-14 11:00:02 +0200499 table = kzalloc(sizeof(*table) + nla_len(name), GFP_KERNEL);
Patrick McHardy7047f9d2014-01-09 18:42:40 +0000500 if (table == NULL) {
501 module_put(afi->owner);
Patrick McHardy96518512013-10-14 11:00:02 +0200502 return -ENOMEM;
Patrick McHardy7047f9d2014-01-09 18:42:40 +0000503 }
Patrick McHardy96518512013-10-14 11:00:02 +0200504
505 nla_strlcpy(table->name, name, nla_len(name));
506 INIT_LIST_HEAD(&table->chains);
Patrick McHardy20a69342013-10-11 12:06:22 +0200507 INIT_LIST_HEAD(&table->sets);
Patrick McHardyc5c1f972014-01-09 18:42:39 +0000508 table->flags = flags;
Pablo Neira Ayuso9ddf6322013-10-10 13:26:33 +0200509
Pablo Neira Ayuso55dd6f92014-04-03 11:53:37 +0200510 nft_ctx_init(&ctx, skb, nlh, afi, table, NULL, nla);
511 err = nft_trans_table_add(&ctx, NFT_MSG_NEWTABLE);
512 if (err < 0) {
513 kfree(table);
514 module_put(afi->owner);
515 return err;
516 }
Patrick McHardy96518512013-10-14 11:00:02 +0200517 list_add_tail(&table->list, &afi->tables);
Patrick McHardy96518512013-10-14 11:00:02 +0200518 return 0;
519}
520
521static int nf_tables_deltable(struct sock *nlsk, struct sk_buff *skb,
522 const struct nlmsghdr *nlh,
523 const struct nlattr * const nla[])
524{
525 const struct nfgenmsg *nfmsg = nlmsg_data(nlh);
526 struct nft_af_info *afi;
527 struct nft_table *table;
Pablo Neira Ayuso99633ab2013-10-10 23:28:33 +0200528 struct net *net = sock_net(skb->sk);
Pablo Neira Ayuso55dd6f92014-04-03 11:53:37 +0200529 int family = nfmsg->nfgen_family, err;
530 struct nft_ctx ctx;
Patrick McHardy96518512013-10-14 11:00:02 +0200531
Pablo Neira Ayuso99633ab2013-10-10 23:28:33 +0200532 afi = nf_tables_afinfo_lookup(net, family, false);
Patrick McHardy96518512013-10-14 11:00:02 +0200533 if (IS_ERR(afi))
534 return PTR_ERR(afi);
535
Pablo Neira Ayuso93707612013-10-10 23:21:26 +0200536 table = nf_tables_table_lookup(afi, nla[NFTA_TABLE_NAME]);
Patrick McHardy96518512013-10-14 11:00:02 +0200537 if (IS_ERR(table))
538 return PTR_ERR(table);
Pablo Neira Ayuso55dd6f92014-04-03 11:53:37 +0200539 if (table->flags & NFT_TABLE_INACTIVE)
540 return -ENOENT;
Pablo Neira Ayuso4fefee52014-05-23 12:39:26 +0200541 if (table->use > 0)
Patrick McHardy96518512013-10-14 11:00:02 +0200542 return -EBUSY;
543
Pablo Neira Ayuso55dd6f92014-04-03 11:53:37 +0200544 nft_ctx_init(&ctx, skb, nlh, afi, table, NULL, nla);
545 err = nft_trans_table_add(&ctx, NFT_MSG_DELTABLE);
546 if (err < 0)
547 return err;
548
Patrick McHardy96518512013-10-14 11:00:02 +0200549 list_del(&table->list);
Patrick McHardy96518512013-10-14 11:00:02 +0200550 return 0;
551}
552
Pablo Neira Ayuso55dd6f92014-04-03 11:53:37 +0200553static void nf_tables_table_destroy(struct nft_ctx *ctx)
554{
Pablo Neira Ayuso4fefee52014-05-23 12:39:26 +0200555 BUG_ON(ctx->table->use > 0);
556
Pablo Neira Ayuso55dd6f92014-04-03 11:53:37 +0200557 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;
Patrick McHardy96518512013-10-14 11:00:02 +0200724 int err;
725
Pablo Neira Ayuso128ad332014-05-09 17:14:24 +0200726 if (!ctx->report &&
727 !nfnetlink_has_listeners(ctx->net, NFNLGRP_NFTABLES))
Patrick McHardy96518512013-10-14 11:00:02 +0200728 return 0;
729
730 err = -ENOBUFS;
731 skb = nlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL);
732 if (skb == NULL)
733 goto err;
734
Pablo Neira Ayuso128ad332014-05-09 17:14:24 +0200735 err = nf_tables_fill_chain_info(skb, ctx->portid, ctx->seq, event, 0,
Pablo Neira Ayuso35151d82014-05-05 17:12:40 +0200736 ctx->afi->family, ctx->table,
737 ctx->chain);
Patrick McHardy96518512013-10-14 11:00:02 +0200738 if (err < 0) {
739 kfree_skb(skb);
740 goto err;
741 }
742
Pablo Neira Ayuso128ad332014-05-09 17:14:24 +0200743 err = nfnetlink_send(skb, ctx->net, ctx->portid, NFNLGRP_NFTABLES,
744 ctx->report, GFP_KERNEL);
Patrick McHardy96518512013-10-14 11:00:02 +0200745err:
Pablo Neira Ayuso128ad332014-05-09 17:14:24 +0200746 if (err < 0) {
747 nfnetlink_set_err(ctx->net, ctx->portid, NFNLGRP_NFTABLES,
748 err);
749 }
Patrick McHardy96518512013-10-14 11:00:02 +0200750 return err;
751}
752
753static int nf_tables_dump_chains(struct sk_buff *skb,
754 struct netlink_callback *cb)
755{
756 const struct nfgenmsg *nfmsg = nlmsg_data(cb->nlh);
757 const struct nft_af_info *afi;
758 const struct nft_table *table;
759 const struct nft_chain *chain;
760 unsigned int idx = 0, s_idx = cb->args[0];
Pablo Neira Ayuso99633ab2013-10-10 23:28:33 +0200761 struct net *net = sock_net(skb->sk);
Patrick McHardy96518512013-10-14 11:00:02 +0200762 int family = nfmsg->nfgen_family;
763
Pablo Neira Ayuso99633ab2013-10-10 23:28:33 +0200764 list_for_each_entry(afi, &net->nft.af_info, list) {
Patrick McHardy96518512013-10-14 11:00:02 +0200765 if (family != NFPROTO_UNSPEC && family != afi->family)
766 continue;
767
768 list_for_each_entry(table, &afi->tables, list) {
769 list_for_each_entry(chain, &table->chains, list) {
770 if (idx < s_idx)
771 goto cont;
772 if (idx > s_idx)
773 memset(&cb->args[1], 0,
774 sizeof(cb->args) - sizeof(cb->args[0]));
775 if (nf_tables_fill_chain_info(skb, NETLINK_CB(cb->skb).portid,
776 cb->nlh->nlmsg_seq,
777 NFT_MSG_NEWCHAIN,
778 NLM_F_MULTI,
779 afi->family, table, chain) < 0)
780 goto done;
781cont:
782 idx++;
783 }
784 }
785 }
786done:
787 cb->args[0] = idx;
788 return skb->len;
789}
790
791
792static int nf_tables_getchain(struct sock *nlsk, struct sk_buff *skb,
793 const struct nlmsghdr *nlh,
794 const struct nlattr * const nla[])
795{
796 const struct nfgenmsg *nfmsg = nlmsg_data(nlh);
797 const struct nft_af_info *afi;
798 const struct nft_table *table;
799 const struct nft_chain *chain;
800 struct sk_buff *skb2;
Pablo Neira Ayuso99633ab2013-10-10 23:28:33 +0200801 struct net *net = sock_net(skb->sk);
Patrick McHardy96518512013-10-14 11:00:02 +0200802 int family = nfmsg->nfgen_family;
803 int err;
804
805 if (nlh->nlmsg_flags & NLM_F_DUMP) {
806 struct netlink_dump_control c = {
807 .dump = nf_tables_dump_chains,
808 };
809 return netlink_dump_start(nlsk, skb, nlh, &c);
810 }
811
Pablo Neira Ayuso99633ab2013-10-10 23:28:33 +0200812 afi = nf_tables_afinfo_lookup(net, family, false);
Patrick McHardy96518512013-10-14 11:00:02 +0200813 if (IS_ERR(afi))
814 return PTR_ERR(afi);
815
Pablo Neira Ayuso93707612013-10-10 23:21:26 +0200816 table = nf_tables_table_lookup(afi, nla[NFTA_CHAIN_TABLE]);
Patrick McHardy96518512013-10-14 11:00:02 +0200817 if (IS_ERR(table))
818 return PTR_ERR(table);
Pablo Neira Ayuso55dd6f92014-04-03 11:53:37 +0200819 if (table->flags & NFT_TABLE_INACTIVE)
820 return -ENOENT;
Patrick McHardy96518512013-10-14 11:00:02 +0200821
822 chain = nf_tables_chain_lookup(table, nla[NFTA_CHAIN_NAME]);
823 if (IS_ERR(chain))
824 return PTR_ERR(chain);
Pablo Neira Ayuso91c7b382014-04-09 11:58:08 +0200825 if (chain->flags & NFT_CHAIN_INACTIVE)
826 return -ENOENT;
Patrick McHardy96518512013-10-14 11:00:02 +0200827
828 skb2 = alloc_skb(NLMSG_GOODSIZE, GFP_KERNEL);
829 if (!skb2)
830 return -ENOMEM;
831
832 err = nf_tables_fill_chain_info(skb2, NETLINK_CB(skb).portid,
833 nlh->nlmsg_seq, NFT_MSG_NEWCHAIN, 0,
834 family, table, chain);
835 if (err < 0)
836 goto err;
837
838 return nlmsg_unicast(nlsk, skb2, NETLINK_CB(skb).portid);
839
840err:
841 kfree_skb(skb2);
842 return err;
843}
844
Pablo Neira Ayuso0ca743a2013-10-14 00:06:06 +0200845static const struct nla_policy nft_counter_policy[NFTA_COUNTER_MAX + 1] = {
846 [NFTA_COUNTER_PACKETS] = { .type = NLA_U64 },
847 [NFTA_COUNTER_BYTES] = { .type = NLA_U64 },
848};
849
Pablo Neira Ayusoff3cd7b2014-04-09 11:53:06 +0200850static struct nft_stats __percpu *nft_stats_alloc(const struct nlattr *attr)
Pablo Neira Ayuso0ca743a2013-10-14 00:06:06 +0200851{
852 struct nlattr *tb[NFTA_COUNTER_MAX+1];
853 struct nft_stats __percpu *newstats;
854 struct nft_stats *stats;
855 int err;
856
857 err = nla_parse_nested(tb, NFTA_COUNTER_MAX, attr, nft_counter_policy);
858 if (err < 0)
Pablo Neira Ayusoff3cd7b2014-04-09 11:53:06 +0200859 return ERR_PTR(err);
Pablo Neira Ayuso0ca743a2013-10-14 00:06:06 +0200860
861 if (!tb[NFTA_COUNTER_BYTES] || !tb[NFTA_COUNTER_PACKETS])
Pablo Neira Ayusoff3cd7b2014-04-09 11:53:06 +0200862 return ERR_PTR(-EINVAL);
Pablo Neira Ayuso0ca743a2013-10-14 00:06:06 +0200863
864 newstats = alloc_percpu(struct nft_stats);
865 if (newstats == NULL)
Pablo Neira Ayusoff3cd7b2014-04-09 11:53:06 +0200866 return ERR_PTR(-ENOMEM);
Pablo Neira Ayuso0ca743a2013-10-14 00:06:06 +0200867
868 /* Restore old counters on this cpu, no problem. Per-cpu statistics
869 * are not exposed to userspace.
870 */
871 stats = this_cpu_ptr(newstats);
872 stats->bytes = be64_to_cpu(nla_get_be64(tb[NFTA_COUNTER_BYTES]));
873 stats->pkts = be64_to_cpu(nla_get_be64(tb[NFTA_COUNTER_PACKETS]));
874
Pablo Neira Ayusoff3cd7b2014-04-09 11:53:06 +0200875 return newstats;
876}
877
878static void nft_chain_stats_replace(struct nft_base_chain *chain,
879 struct nft_stats __percpu *newstats)
880{
Pablo Neira Ayuso0ca743a2013-10-14 00:06:06 +0200881 if (chain->stats) {
Pablo Neira Ayuso0ca743a2013-10-14 00:06:06 +0200882 struct nft_stats __percpu *oldstats =
Patrick McHardy67a8fc22014-02-18 18:06:49 +0000883 nft_dereference(chain->stats);
Pablo Neira Ayuso0ca743a2013-10-14 00:06:06 +0200884
885 rcu_assign_pointer(chain->stats, newstats);
886 synchronize_rcu();
887 free_percpu(oldstats);
888 } else
889 rcu_assign_pointer(chain->stats, newstats);
Pablo Neira Ayuso0ca743a2013-10-14 00:06:06 +0200890}
Pablo Neira Ayuso0ca743a2013-10-14 00:06:06 +0200891
Pablo Neira Ayuso91c7b382014-04-09 11:58:08 +0200892static int nft_trans_chain_add(struct nft_ctx *ctx, int msg_type)
893{
894 struct nft_trans *trans;
895
896 trans = nft_trans_alloc(ctx, msg_type, sizeof(struct nft_trans_chain));
897 if (trans == NULL)
898 return -ENOMEM;
899
900 if (msg_type == NFT_MSG_NEWCHAIN)
901 ctx->chain->flags |= NFT_CHAIN_INACTIVE;
902
903 list_add_tail(&trans->list, &ctx->net->nft.commit_list);
Pablo Neira Ayuso0ca743a2013-10-14 00:06:06 +0200904 return 0;
905}
906
Pablo Neira Ayuso91c7b382014-04-09 11:58:08 +0200907static void nf_tables_chain_destroy(struct nft_chain *chain)
908{
909 BUG_ON(chain->use > 0);
910
911 if (chain->flags & NFT_BASE_CHAIN) {
912 module_put(nft_base_chain(chain)->type->owner);
913 free_percpu(nft_base_chain(chain)->stats);
914 kfree(nft_base_chain(chain));
915 } else {
916 kfree(chain);
917 }
918}
919
Patrick McHardy96518512013-10-14 11:00:02 +0200920static int nf_tables_newchain(struct sock *nlsk, struct sk_buff *skb,
921 const struct nlmsghdr *nlh,
922 const struct nlattr * const nla[])
923{
924 const struct nfgenmsg *nfmsg = nlmsg_data(nlh);
925 const struct nlattr * uninitialized_var(name);
Pablo Neira Ayuso7c95f6d2014-04-04 01:22:45 +0200926 struct nft_af_info *afi;
Patrick McHardy96518512013-10-14 11:00:02 +0200927 struct nft_table *table;
928 struct nft_chain *chain;
Pablo Neira Ayuso0ca743a2013-10-14 00:06:06 +0200929 struct nft_base_chain *basechain = NULL;
Patrick McHardy96518512013-10-14 11:00:02 +0200930 struct nlattr *ha[NFTA_HOOK_MAX + 1];
Pablo Neira Ayuso99633ab2013-10-10 23:28:33 +0200931 struct net *net = sock_net(skb->sk);
Patrick McHardy96518512013-10-14 11:00:02 +0200932 int family = nfmsg->nfgen_family;
Patrick McHardy57de2a02014-01-09 18:42:31 +0000933 u8 policy = NF_ACCEPT;
Patrick McHardy96518512013-10-14 11:00:02 +0200934 u64 handle = 0;
Patrick McHardy115a60b2014-01-03 12:16:15 +0000935 unsigned int i;
Pablo Neira Ayusoff3cd7b2014-04-09 11:53:06 +0200936 struct nft_stats __percpu *stats;
Patrick McHardy96518512013-10-14 11:00:02 +0200937 int err;
938 bool create;
Pablo Neira Ayuso91c7b382014-04-09 11:58:08 +0200939 struct nft_ctx ctx;
Patrick McHardy96518512013-10-14 11:00:02 +0200940
941 create = nlh->nlmsg_flags & NLM_F_CREATE ? true : false;
942
Pablo Neira Ayuso99633ab2013-10-10 23:28:33 +0200943 afi = nf_tables_afinfo_lookup(net, family, true);
Patrick McHardy96518512013-10-14 11:00:02 +0200944 if (IS_ERR(afi))
945 return PTR_ERR(afi);
946
Pablo Neira Ayuso93707612013-10-10 23:21:26 +0200947 table = nf_tables_table_lookup(afi, nla[NFTA_CHAIN_TABLE]);
Patrick McHardy96518512013-10-14 11:00:02 +0200948 if (IS_ERR(table))
949 return PTR_ERR(table);
950
Patrick McHardy96518512013-10-14 11:00:02 +0200951 chain = NULL;
952 name = nla[NFTA_CHAIN_NAME];
953
954 if (nla[NFTA_CHAIN_HANDLE]) {
955 handle = be64_to_cpu(nla_get_be64(nla[NFTA_CHAIN_HANDLE]));
956 chain = nf_tables_chain_lookup_byhandle(table, handle);
957 if (IS_ERR(chain))
958 return PTR_ERR(chain);
959 } else {
960 chain = nf_tables_chain_lookup(table, name);
961 if (IS_ERR(chain)) {
962 if (PTR_ERR(chain) != -ENOENT)
963 return PTR_ERR(chain);
964 chain = NULL;
965 }
966 }
967
Patrick McHardy57de2a02014-01-09 18:42:31 +0000968 if (nla[NFTA_CHAIN_POLICY]) {
969 if ((chain != NULL &&
970 !(chain->flags & NFT_BASE_CHAIN)) ||
971 nla[NFTA_CHAIN_HOOK] == NULL)
972 return -EOPNOTSUPP;
973
Pablo Neira Ayuso8f46df12014-01-10 15:11:25 +0100974 policy = ntohl(nla_get_be32(nla[NFTA_CHAIN_POLICY]));
Patrick McHardy57de2a02014-01-09 18:42:31 +0000975 switch (policy) {
976 case NF_DROP:
977 case NF_ACCEPT:
978 break;
979 default:
980 return -EINVAL;
981 }
982 }
983
Patrick McHardy96518512013-10-14 11:00:02 +0200984 if (chain != NULL) {
Pablo Neira Ayuso91c7b382014-04-09 11:58:08 +0200985 struct nft_stats *stats = NULL;
986 struct nft_trans *trans;
987
988 if (chain->flags & NFT_CHAIN_INACTIVE)
989 return -ENOENT;
Patrick McHardy96518512013-10-14 11:00:02 +0200990 if (nlh->nlmsg_flags & NLM_F_EXCL)
991 return -EEXIST;
992 if (nlh->nlmsg_flags & NLM_F_REPLACE)
993 return -EOPNOTSUPP;
994
995 if (nla[NFTA_CHAIN_HANDLE] && name &&
996 !IS_ERR(nf_tables_chain_lookup(table, nla[NFTA_CHAIN_NAME])))
997 return -EEXIST;
998
Pablo Neira Ayuso0ca743a2013-10-14 00:06:06 +0200999 if (nla[NFTA_CHAIN_COUNTERS]) {
1000 if (!(chain->flags & NFT_BASE_CHAIN))
1001 return -EOPNOTSUPP;
1002
Pablo Neira Ayusoff3cd7b2014-04-09 11:53:06 +02001003 stats = nft_stats_alloc(nla[NFTA_CHAIN_COUNTERS]);
1004 if (IS_ERR(stats))
1005 return PTR_ERR(stats);
Pablo Neira Ayuso0ca743a2013-10-14 00:06:06 +02001006 }
1007
Pablo Neira Ayuso91c7b382014-04-09 11:58:08 +02001008 nft_ctx_init(&ctx, skb, nlh, afi, table, chain, nla);
1009 trans = nft_trans_alloc(&ctx, NFT_MSG_NEWCHAIN,
1010 sizeof(struct nft_trans_chain));
1011 if (trans == NULL)
1012 return -ENOMEM;
1013
1014 nft_trans_chain_stats(trans) = stats;
1015 nft_trans_chain_update(trans) = true;
1016
Patrick McHardy4401a862014-01-09 18:42:32 +00001017 if (nla[NFTA_CHAIN_POLICY])
Pablo Neira Ayuso91c7b382014-04-09 11:58:08 +02001018 nft_trans_chain_policy(trans) = policy;
1019 else
1020 nft_trans_chain_policy(trans) = -1;
Patrick McHardy4401a862014-01-09 18:42:32 +00001021
Pablo Neira Ayuso91c7b382014-04-09 11:58:08 +02001022 if (nla[NFTA_CHAIN_HANDLE] && name) {
1023 nla_strlcpy(nft_trans_chain_name(trans), name,
1024 NFT_CHAIN_MAXNAMELEN);
1025 }
1026 list_add_tail(&trans->list, &net->nft.commit_list);
1027 return 0;
Patrick McHardy96518512013-10-14 11:00:02 +02001028 }
1029
Patrick McHardy75820672014-01-09 18:42:33 +00001030 if (table->use == UINT_MAX)
1031 return -EOVERFLOW;
1032
Patrick McHardy96518512013-10-14 11:00:02 +02001033 if (nla[NFTA_CHAIN_HOOK]) {
Patrick McHardy2a37d752014-01-09 18:42:37 +00001034 const struct nf_chain_type *type;
Patrick McHardy96518512013-10-14 11:00:02 +02001035 struct nf_hook_ops *ops;
Pablo Neira Ayuso93707612013-10-10 23:21:26 +02001036 nf_hookfn *hookfn;
Patrick McHardy115a60b2014-01-03 12:16:15 +00001037 u32 hooknum, priority;
Pablo Neira Ayuso93707612013-10-10 23:21:26 +02001038
Patrick McHardybaae3e62014-01-09 18:42:34 +00001039 type = chain_type[family][NFT_CHAIN_T_DEFAULT];
Pablo Neira Ayuso93707612013-10-10 23:21:26 +02001040 if (nla[NFTA_CHAIN_TYPE]) {
1041 type = nf_tables_chain_type_lookup(afi,
1042 nla[NFTA_CHAIN_TYPE],
1043 create);
Patrick McHardy93b08062014-01-09 18:42:36 +00001044 if (IS_ERR(type))
1045 return PTR_ERR(type);
Pablo Neira Ayuso93707612013-10-10 23:21:26 +02001046 }
Patrick McHardy96518512013-10-14 11:00:02 +02001047
1048 err = nla_parse_nested(ha, NFTA_HOOK_MAX, nla[NFTA_CHAIN_HOOK],
1049 nft_hook_policy);
1050 if (err < 0)
1051 return err;
1052 if (ha[NFTA_HOOK_HOOKNUM] == NULL ||
1053 ha[NFTA_HOOK_PRIORITY] == NULL)
1054 return -EINVAL;
Pablo Neira Ayuso93707612013-10-10 23:21:26 +02001055
1056 hooknum = ntohl(nla_get_be32(ha[NFTA_HOOK_HOOKNUM]));
1057 if (hooknum >= afi->nhooks)
Patrick McHardy96518512013-10-14 11:00:02 +02001058 return -EINVAL;
Patrick McHardy115a60b2014-01-03 12:16:15 +00001059 priority = ntohl(nla_get_be32(ha[NFTA_HOOK_PRIORITY]));
Patrick McHardy96518512013-10-14 11:00:02 +02001060
Patrick McHardybaae3e62014-01-09 18:42:34 +00001061 if (!(type->hook_mask & (1 << hooknum)))
Pablo Neira Ayuso93707612013-10-10 23:21:26 +02001062 return -EOPNOTSUPP;
Patrick McHardyfa2c1de2014-01-09 18:42:38 +00001063 if (!try_module_get(type->owner))
Patrick McHardybaae3e62014-01-09 18:42:34 +00001064 return -ENOENT;
Patrick McHardyfa2c1de2014-01-09 18:42:38 +00001065 hookfn = type->hooks[hooknum];
Pablo Neira Ayuso93707612013-10-10 23:21:26 +02001066
Patrick McHardy96518512013-10-14 11:00:02 +02001067 basechain = kzalloc(sizeof(*basechain), GFP_KERNEL);
1068 if (basechain == NULL)
1069 return -ENOMEM;
Pablo Neira Ayuso93707612013-10-10 23:21:26 +02001070
Patrick McHardy4401a862014-01-09 18:42:32 +00001071 if (nla[NFTA_CHAIN_COUNTERS]) {
Pablo Neira Ayusoff3cd7b2014-04-09 11:53:06 +02001072 stats = nft_stats_alloc(nla[NFTA_CHAIN_COUNTERS]);
1073 if (IS_ERR(stats)) {
Patrick McHardyfa2c1de2014-01-09 18:42:38 +00001074 module_put(type->owner);
Patrick McHardy4401a862014-01-09 18:42:32 +00001075 kfree(basechain);
Pablo Neira Ayusoff3cd7b2014-04-09 11:53:06 +02001076 return PTR_ERR(stats);
Patrick McHardy4401a862014-01-09 18:42:32 +00001077 }
Pablo Neira Ayusoff3cd7b2014-04-09 11:53:06 +02001078 basechain->stats = stats;
Patrick McHardy4401a862014-01-09 18:42:32 +00001079 } else {
Pablo Neira Ayusoff3cd7b2014-04-09 11:53:06 +02001080 stats = alloc_percpu(struct nft_stats);
1081 if (IS_ERR(stats)) {
Patrick McHardyfa2c1de2014-01-09 18:42:38 +00001082 module_put(type->owner);
Patrick McHardy4401a862014-01-09 18:42:32 +00001083 kfree(basechain);
Pablo Neira Ayusoff3cd7b2014-04-09 11:53:06 +02001084 return PTR_ERR(stats);
Patrick McHardy4401a862014-01-09 18:42:32 +00001085 }
Pablo Neira Ayusoff3cd7b2014-04-09 11:53:06 +02001086 rcu_assign_pointer(basechain->stats, stats);
Patrick McHardy4401a862014-01-09 18:42:32 +00001087 }
1088
Pablo Neira Ayuso93707612013-10-10 23:21:26 +02001089 basechain->type = type;
Patrick McHardy96518512013-10-14 11:00:02 +02001090 chain = &basechain->chain;
1091
Patrick McHardy115a60b2014-01-03 12:16:15 +00001092 for (i = 0; i < afi->nops; i++) {
1093 ops = &basechain->ops[i];
1094 ops->pf = family;
1095 ops->owner = afi->owner;
1096 ops->hooknum = hooknum;
1097 ops->priority = priority;
1098 ops->priv = chain;
1099 ops->hook = afi->hooks[ops->hooknum];
1100 if (hookfn)
1101 ops->hook = hookfn;
1102 if (afi->hook_ops_init)
1103 afi->hook_ops_init(ops, i);
1104 }
Patrick McHardy96518512013-10-14 11:00:02 +02001105
1106 chain->flags |= NFT_BASE_CHAIN;
Patrick McHardy57de2a02014-01-09 18:42:31 +00001107 basechain->policy = policy;
Patrick McHardy96518512013-10-14 11:00:02 +02001108 } else {
1109 chain = kzalloc(sizeof(*chain), GFP_KERNEL);
1110 if (chain == NULL)
1111 return -ENOMEM;
1112 }
1113
1114 INIT_LIST_HEAD(&chain->rules);
1115 chain->handle = nf_tables_alloc_handle(table);
Pablo Neira Ayuso0628b122013-10-14 11:05:33 +02001116 chain->net = net;
Pablo Neira Ayusob5bc89b2013-10-10 16:49:19 +02001117 chain->table = table;
Patrick McHardy96518512013-10-14 11:00:02 +02001118 nla_strlcpy(chain->name, name, NFT_CHAIN_MAXNAMELEN);
1119
Pablo Neira Ayuso9ddf6322013-10-10 13:26:33 +02001120 if (!(table->flags & NFT_TABLE_F_DORMANT) &&
1121 chain->flags & NFT_BASE_CHAIN) {
Patrick McHardy115a60b2014-01-03 12:16:15 +00001122 err = nf_register_hooks(nft_base_chain(chain)->ops, afi->nops);
Pablo Neira Ayuso91c7b382014-04-09 11:58:08 +02001123 if (err < 0)
1124 goto err1;
Pablo Neira Ayuso0ca743a2013-10-14 00:06:06 +02001125 }
Pablo Neira Ayuso91c7b382014-04-09 11:58:08 +02001126
1127 nft_ctx_init(&ctx, skb, nlh, afi, table, chain, nla);
1128 err = nft_trans_chain_add(&ctx, NFT_MSG_NEWCHAIN);
1129 if (err < 0)
1130 goto err2;
1131
Pablo Neira Ayuso4fefee52014-05-23 12:39:26 +02001132 table->use++;
Pablo Neira Ayuso9ddf6322013-10-10 13:26:33 +02001133 list_add_tail(&chain->list, &table->chains);
Patrick McHardy96518512013-10-14 11:00:02 +02001134 return 0;
Pablo Neira Ayuso91c7b382014-04-09 11:58:08 +02001135err2:
1136 if (!(table->flags & NFT_TABLE_F_DORMANT) &&
1137 chain->flags & NFT_BASE_CHAIN) {
1138 nf_unregister_hooks(nft_base_chain(chain)->ops,
1139 afi->nops);
1140 }
1141err1:
1142 nf_tables_chain_destroy(chain);
1143 return err;
Patrick McHardy96518512013-10-14 11:00:02 +02001144}
1145
1146static int nf_tables_delchain(struct sock *nlsk, struct sk_buff *skb,
1147 const struct nlmsghdr *nlh,
1148 const struct nlattr * const nla[])
1149{
1150 const struct nfgenmsg *nfmsg = nlmsg_data(nlh);
Pablo Neira Ayuso7c95f6d2014-04-04 01:22:45 +02001151 struct nft_af_info *afi;
Patrick McHardy96518512013-10-14 11:00:02 +02001152 struct nft_table *table;
1153 struct nft_chain *chain;
Pablo Neira Ayuso99633ab2013-10-10 23:28:33 +02001154 struct net *net = sock_net(skb->sk);
Patrick McHardy96518512013-10-14 11:00:02 +02001155 int family = nfmsg->nfgen_family;
Pablo Neira Ayuso91c7b382014-04-09 11:58:08 +02001156 struct nft_ctx ctx;
1157 int err;
Patrick McHardy96518512013-10-14 11:00:02 +02001158
Pablo Neira Ayuso99633ab2013-10-10 23:28:33 +02001159 afi = nf_tables_afinfo_lookup(net, family, false);
Patrick McHardy96518512013-10-14 11:00:02 +02001160 if (IS_ERR(afi))
1161 return PTR_ERR(afi);
1162
Pablo Neira Ayuso93707612013-10-10 23:21:26 +02001163 table = nf_tables_table_lookup(afi, nla[NFTA_CHAIN_TABLE]);
Patrick McHardy96518512013-10-14 11:00:02 +02001164 if (IS_ERR(table))
1165 return PTR_ERR(table);
Pablo Neira Ayuso55dd6f92014-04-03 11:53:37 +02001166 if (table->flags & NFT_TABLE_INACTIVE)
1167 return -ENOENT;
Patrick McHardy96518512013-10-14 11:00:02 +02001168
1169 chain = nf_tables_chain_lookup(table, nla[NFTA_CHAIN_NAME]);
1170 if (IS_ERR(chain))
1171 return PTR_ERR(chain);
Pablo Neira Ayuso91c7b382014-04-09 11:58:08 +02001172 if (chain->flags & NFT_CHAIN_INACTIVE)
1173 return -ENOENT;
Pablo Neira Ayuso4fefee52014-05-23 12:39:26 +02001174 if (chain->use > 0)
Patrick McHardy96518512013-10-14 11:00:02 +02001175 return -EBUSY;
1176
Pablo Neira Ayuso91c7b382014-04-09 11:58:08 +02001177 nft_ctx_init(&ctx, skb, nlh, afi, table, chain, nla);
1178 err = nft_trans_chain_add(&ctx, NFT_MSG_DELCHAIN);
1179 if (err < 0)
1180 return err;
1181
Pablo Neira Ayuso4fefee52014-05-23 12:39:26 +02001182 table->use--;
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{
1476 struct sk_buff *skb;
Patrick McHardy96518512013-10-14 11:00:02 +02001477 int err;
1478
Pablo Neira Ayuso128ad332014-05-09 17:14:24 +02001479 if (!ctx->report &&
1480 !nfnetlink_has_listeners(ctx->net, NFNLGRP_NFTABLES))
Patrick McHardy96518512013-10-14 11:00:02 +02001481 return 0;
1482
1483 err = -ENOBUFS;
1484 skb = nlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL);
1485 if (skb == NULL)
1486 goto err;
1487
Pablo Neira Ayuso128ad332014-05-09 17:14:24 +02001488 err = nf_tables_fill_rule_info(skb, ctx->portid, ctx->seq, event, 0,
Pablo Neira Ayuso35151d82014-05-05 17:12:40 +02001489 ctx->afi->family, ctx->table,
1490 ctx->chain, rule);
Patrick McHardy96518512013-10-14 11:00:02 +02001491 if (err < 0) {
1492 kfree_skb(skb);
1493 goto err;
1494 }
1495
Pablo Neira Ayuso128ad332014-05-09 17:14:24 +02001496 err = nfnetlink_send(skb, ctx->net, ctx->portid, NFNLGRP_NFTABLES,
1497 ctx->report, GFP_KERNEL);
Patrick McHardy96518512013-10-14 11:00:02 +02001498err:
Pablo Neira Ayuso128ad332014-05-09 17:14:24 +02001499 if (err < 0) {
1500 nfnetlink_set_err(ctx->net, ctx->portid, NFNLGRP_NFTABLES,
1501 err);
1502 }
Patrick McHardy96518512013-10-14 11:00:02 +02001503 return err;
1504}
1505
Pablo Neira Ayuso0628b122013-10-14 11:05:33 +02001506static inline bool
1507nft_rule_is_active(struct net *net, const struct nft_rule *rule)
1508{
1509 return (rule->genmask & (1 << net->nft.gencursor)) == 0;
1510}
1511
1512static inline int gencursor_next(struct net *net)
1513{
1514 return net->nft.gencursor+1 == 1 ? 1 : 0;
1515}
1516
1517static inline int
1518nft_rule_is_active_next(struct net *net, const struct nft_rule *rule)
1519{
1520 return (rule->genmask & (1 << gencursor_next(net))) == 0;
1521}
1522
1523static inline void
1524nft_rule_activate_next(struct net *net, struct nft_rule *rule)
1525{
1526 /* Now inactive, will be active in the future */
1527 rule->genmask = (1 << net->nft.gencursor);
1528}
1529
1530static inline void
1531nft_rule_disactivate_next(struct net *net, struct nft_rule *rule)
1532{
1533 rule->genmask = (1 << gencursor_next(net));
1534}
1535
1536static inline void nft_rule_clear(struct net *net, struct nft_rule *rule)
1537{
1538 rule->genmask = 0;
1539}
1540
Patrick McHardy96518512013-10-14 11:00:02 +02001541static int nf_tables_dump_rules(struct sk_buff *skb,
1542 struct netlink_callback *cb)
1543{
1544 const struct nfgenmsg *nfmsg = nlmsg_data(cb->nlh);
1545 const struct nft_af_info *afi;
1546 const struct nft_table *table;
1547 const struct nft_chain *chain;
1548 const struct nft_rule *rule;
1549 unsigned int idx = 0, s_idx = cb->args[0];
Pablo Neira Ayuso99633ab2013-10-10 23:28:33 +02001550 struct net *net = sock_net(skb->sk);
Patrick McHardy96518512013-10-14 11:00:02 +02001551 int family = nfmsg->nfgen_family;
Pablo Neira Ayuso0628b122013-10-14 11:05:33 +02001552 u8 genctr = ACCESS_ONCE(net->nft.genctr);
1553 u8 gencursor = ACCESS_ONCE(net->nft.gencursor);
Patrick McHardy96518512013-10-14 11:00:02 +02001554
Pablo Neira Ayuso99633ab2013-10-10 23:28:33 +02001555 list_for_each_entry(afi, &net->nft.af_info, list) {
Patrick McHardy96518512013-10-14 11:00:02 +02001556 if (family != NFPROTO_UNSPEC && family != afi->family)
1557 continue;
1558
1559 list_for_each_entry(table, &afi->tables, list) {
1560 list_for_each_entry(chain, &table->chains, list) {
1561 list_for_each_entry(rule, &chain->rules, list) {
Pablo Neira Ayuso0628b122013-10-14 11:05:33 +02001562 if (!nft_rule_is_active(net, rule))
1563 goto cont;
Patrick McHardy96518512013-10-14 11:00:02 +02001564 if (idx < s_idx)
1565 goto cont;
1566 if (idx > s_idx)
1567 memset(&cb->args[1], 0,
1568 sizeof(cb->args) - sizeof(cb->args[0]));
1569 if (nf_tables_fill_rule_info(skb, NETLINK_CB(cb->skb).portid,
1570 cb->nlh->nlmsg_seq,
1571 NFT_MSG_NEWRULE,
1572 NLM_F_MULTI | NLM_F_APPEND,
1573 afi->family, table, chain, rule) < 0)
1574 goto done;
1575cont:
1576 idx++;
1577 }
1578 }
1579 }
1580 }
1581done:
Pablo Neira Ayuso0628b122013-10-14 11:05:33 +02001582 /* Invalidate this dump, a transition to the new generation happened */
1583 if (gencursor != net->nft.gencursor || genctr != net->nft.genctr)
1584 return -EBUSY;
1585
Patrick McHardy96518512013-10-14 11:00:02 +02001586 cb->args[0] = idx;
1587 return skb->len;
1588}
1589
1590static int nf_tables_getrule(struct sock *nlsk, struct sk_buff *skb,
1591 const struct nlmsghdr *nlh,
1592 const struct nlattr * const nla[])
1593{
1594 const struct nfgenmsg *nfmsg = nlmsg_data(nlh);
1595 const struct nft_af_info *afi;
1596 const struct nft_table *table;
1597 const struct nft_chain *chain;
1598 const struct nft_rule *rule;
1599 struct sk_buff *skb2;
Pablo Neira Ayuso99633ab2013-10-10 23:28:33 +02001600 struct net *net = sock_net(skb->sk);
Patrick McHardy96518512013-10-14 11:00:02 +02001601 int family = nfmsg->nfgen_family;
1602 int err;
1603
1604 if (nlh->nlmsg_flags & NLM_F_DUMP) {
1605 struct netlink_dump_control c = {
1606 .dump = nf_tables_dump_rules,
1607 };
1608 return netlink_dump_start(nlsk, skb, nlh, &c);
1609 }
1610
Pablo Neira Ayuso99633ab2013-10-10 23:28:33 +02001611 afi = nf_tables_afinfo_lookup(net, family, false);
Patrick McHardy96518512013-10-14 11:00:02 +02001612 if (IS_ERR(afi))
1613 return PTR_ERR(afi);
1614
Pablo Neira Ayuso93707612013-10-10 23:21:26 +02001615 table = nf_tables_table_lookup(afi, nla[NFTA_RULE_TABLE]);
Patrick McHardy96518512013-10-14 11:00:02 +02001616 if (IS_ERR(table))
1617 return PTR_ERR(table);
Pablo Neira Ayuso55dd6f92014-04-03 11:53:37 +02001618 if (table->flags & NFT_TABLE_INACTIVE)
1619 return -ENOENT;
Patrick McHardy96518512013-10-14 11:00:02 +02001620
1621 chain = nf_tables_chain_lookup(table, nla[NFTA_RULE_CHAIN]);
1622 if (IS_ERR(chain))
1623 return PTR_ERR(chain);
Pablo Neira Ayuso91c7b382014-04-09 11:58:08 +02001624 if (chain->flags & NFT_CHAIN_INACTIVE)
1625 return -ENOENT;
Patrick McHardy96518512013-10-14 11:00:02 +02001626
1627 rule = nf_tables_rule_lookup(chain, nla[NFTA_RULE_HANDLE]);
1628 if (IS_ERR(rule))
1629 return PTR_ERR(rule);
1630
1631 skb2 = alloc_skb(NLMSG_GOODSIZE, GFP_KERNEL);
1632 if (!skb2)
1633 return -ENOMEM;
1634
1635 err = nf_tables_fill_rule_info(skb2, NETLINK_CB(skb).portid,
1636 nlh->nlmsg_seq, NFT_MSG_NEWRULE, 0,
1637 family, table, chain, rule);
1638 if (err < 0)
1639 goto err;
1640
1641 return nlmsg_unicast(nlsk, skb2, NETLINK_CB(skb).portid);
1642
1643err:
1644 kfree_skb(skb2);
1645 return err;
1646}
1647
Patrick McHardy62472bc2014-03-07 19:08:30 +01001648static void nf_tables_rule_destroy(const struct nft_ctx *ctx,
1649 struct nft_rule *rule)
Patrick McHardy96518512013-10-14 11:00:02 +02001650{
Patrick McHardy96518512013-10-14 11:00:02 +02001651 struct nft_expr *expr;
1652
1653 /*
1654 * Careful: some expressions might not be initialized in case this
1655 * is called on error from nf_tables_newrule().
1656 */
1657 expr = nft_expr_first(rule);
1658 while (expr->ops && expr != nft_expr_last(rule)) {
Patrick McHardy62472bc2014-03-07 19:08:30 +01001659 nf_tables_expr_destroy(ctx, expr);
Patrick McHardy96518512013-10-14 11:00:02 +02001660 expr = nft_expr_next(expr);
1661 }
1662 kfree(rule);
1663}
1664
Pablo Neira Ayusob380e5c2014-04-04 01:38:51 +02001665static struct nft_trans *nft_trans_rule_add(struct nft_ctx *ctx, int msg_type,
Pablo Neira Ayuso1081d112014-04-04 01:24:07 +02001666 struct nft_rule *rule)
1667{
1668 struct nft_trans *trans;
1669
Pablo Neira Ayusob380e5c2014-04-04 01:38:51 +02001670 trans = nft_trans_alloc(ctx, msg_type, sizeof(struct nft_trans_rule));
Pablo Neira Ayuso1081d112014-04-04 01:24:07 +02001671 if (trans == NULL)
1672 return NULL;
1673
1674 nft_trans_rule(trans) = rule;
1675 list_add_tail(&trans->list, &ctx->net->nft.commit_list);
1676
1677 return trans;
1678}
1679
Patrick McHardy96518512013-10-14 11:00:02 +02001680#define NFT_RULE_MAXEXPRS 128
1681
1682static struct nft_expr_info *info;
1683
1684static int nf_tables_newrule(struct sock *nlsk, struct sk_buff *skb,
1685 const struct nlmsghdr *nlh,
1686 const struct nlattr * const nla[])
1687{
1688 const struct nfgenmsg *nfmsg = nlmsg_data(nlh);
Pablo Neira Ayuso7c95f6d2014-04-04 01:22:45 +02001689 struct nft_af_info *afi;
Pablo Neira Ayuso99633ab2013-10-10 23:28:33 +02001690 struct net *net = sock_net(skb->sk);
Patrick McHardy96518512013-10-14 11:00:02 +02001691 struct nft_table *table;
1692 struct nft_chain *chain;
1693 struct nft_rule *rule, *old_rule = NULL;
Pablo Neira Ayuso1081d112014-04-04 01:24:07 +02001694 struct nft_trans *trans = NULL;
Patrick McHardy96518512013-10-14 11:00:02 +02001695 struct nft_expr *expr;
1696 struct nft_ctx ctx;
1697 struct nlattr *tmp;
Pablo Neira Ayuso0768b3b2014-02-19 17:27:06 +01001698 unsigned int size, i, n, ulen = 0;
Patrick McHardy96518512013-10-14 11:00:02 +02001699 int err, rem;
1700 bool create;
Eric Leblond5e948462013-10-10 13:41:44 +02001701 u64 handle, pos_handle;
Patrick McHardy96518512013-10-14 11:00:02 +02001702
1703 create = nlh->nlmsg_flags & NLM_F_CREATE ? true : false;
1704
Pablo Neira Ayuso99633ab2013-10-10 23:28:33 +02001705 afi = nf_tables_afinfo_lookup(net, nfmsg->nfgen_family, create);
Patrick McHardy96518512013-10-14 11:00:02 +02001706 if (IS_ERR(afi))
1707 return PTR_ERR(afi);
1708
Pablo Neira Ayuso93707612013-10-10 23:21:26 +02001709 table = nf_tables_table_lookup(afi, nla[NFTA_RULE_TABLE]);
Patrick McHardy96518512013-10-14 11:00:02 +02001710 if (IS_ERR(table))
1711 return PTR_ERR(table);
1712
1713 chain = nf_tables_chain_lookup(table, nla[NFTA_RULE_CHAIN]);
1714 if (IS_ERR(chain))
1715 return PTR_ERR(chain);
1716
1717 if (nla[NFTA_RULE_HANDLE]) {
1718 handle = be64_to_cpu(nla_get_be64(nla[NFTA_RULE_HANDLE]));
1719 rule = __nf_tables_rule_lookup(chain, handle);
1720 if (IS_ERR(rule))
1721 return PTR_ERR(rule);
1722
1723 if (nlh->nlmsg_flags & NLM_F_EXCL)
1724 return -EEXIST;
1725 if (nlh->nlmsg_flags & NLM_F_REPLACE)
1726 old_rule = rule;
1727 else
1728 return -EOPNOTSUPP;
1729 } else {
1730 if (!create || nlh->nlmsg_flags & NLM_F_REPLACE)
1731 return -EINVAL;
1732 handle = nf_tables_alloc_handle(table);
Pablo Neira Ayusoa0a73792014-06-10 10:53:01 +02001733
1734 if (chain->use == UINT_MAX)
1735 return -EOVERFLOW;
Patrick McHardy96518512013-10-14 11:00:02 +02001736 }
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);
Pablo Neira Ayuso5bc5c302014-06-10 10:53:00 +02001802 list_add_tail_rcu(&rule->list, &old_rule->list);
Pablo Neira Ayuso0628b122013-10-14 11:05:33 +02001803 } 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 }
Pablo Neira Ayuso4fefee52014-05-23 12:39:26 +02001823 chain->use++;
Patrick McHardy96518512013-10-14 11:00:02 +02001824 return 0;
1825
Pablo Neira Ayuso0628b122013-10-14 11:05:33 +02001826err3:
1827 list_del_rcu(&rule->list);
Pablo Neira Ayuso1081d112014-04-04 01:24:07 +02001828 if (trans) {
1829 list_del_rcu(&nft_trans_rule(trans)->list);
1830 nft_rule_clear(net, nft_trans_rule(trans));
1831 nft_trans_destroy(trans);
Pablo Neira Ayuso0628b122013-10-14 11:05:33 +02001832 }
Patrick McHardy96518512013-10-14 11:00:02 +02001833err2:
Patrick McHardy62472bc2014-03-07 19:08:30 +01001834 nf_tables_rule_destroy(&ctx, rule);
Patrick McHardy96518512013-10-14 11:00:02 +02001835err1:
1836 for (i = 0; i < n; i++) {
1837 if (info[i].ops != NULL)
Patrick McHardyef1f7df2013-10-10 11:41:20 +02001838 module_put(info[i].ops->type->owner);
Patrick McHardy96518512013-10-14 11:00:02 +02001839 }
1840 return err;
1841}
1842
Pablo Neira Ayuso0628b122013-10-14 11:05:33 +02001843static int
1844nf_tables_delrule_one(struct nft_ctx *ctx, struct nft_rule *rule)
1845{
1846 /* You cannot delete the same rule twice */
1847 if (nft_rule_is_active_next(ctx->net, rule)) {
Pablo Neira Ayusob380e5c2014-04-04 01:38:51 +02001848 if (nft_trans_rule_add(ctx, NFT_MSG_DELRULE, rule) == NULL)
Pablo Neira Ayuso0628b122013-10-14 11:05:33 +02001849 return -ENOMEM;
1850 nft_rule_disactivate_next(ctx->net, rule);
Pablo Neira Ayuso4fefee52014-05-23 12:39:26 +02001851 ctx->chain->use--;
Pablo Neira Ayuso0628b122013-10-14 11:05:33 +02001852 return 0;
1853 }
1854 return -ENOENT;
1855}
1856
Pablo Neira Ayusocf9dc092013-11-24 20:39:10 +01001857static int nf_table_delrule_by_chain(struct nft_ctx *ctx)
1858{
1859 struct nft_rule *rule;
1860 int err;
1861
1862 list_for_each_entry(rule, &ctx->chain->rules, list) {
1863 err = nf_tables_delrule_one(ctx, rule);
1864 if (err < 0)
1865 return err;
1866 }
1867 return 0;
1868}
1869
Patrick McHardy96518512013-10-14 11:00:02 +02001870static int nf_tables_delrule(struct sock *nlsk, struct sk_buff *skb,
1871 const struct nlmsghdr *nlh,
1872 const struct nlattr * const nla[])
1873{
1874 const struct nfgenmsg *nfmsg = nlmsg_data(nlh);
Pablo Neira Ayuso7c95f6d2014-04-04 01:22:45 +02001875 struct nft_af_info *afi;
Pablo Neira Ayuso99633ab2013-10-10 23:28:33 +02001876 struct net *net = sock_net(skb->sk);
Pablo Neira Ayuso7c95f6d2014-04-04 01:22:45 +02001877 struct nft_table *table;
Pablo Neira Ayusocf9dc092013-11-24 20:39:10 +01001878 struct nft_chain *chain = NULL;
1879 struct nft_rule *rule;
Pablo Neira Ayuso0628b122013-10-14 11:05:33 +02001880 int family = nfmsg->nfgen_family, err = 0;
1881 struct nft_ctx ctx;
Patrick McHardy96518512013-10-14 11:00:02 +02001882
Pablo Neira Ayuso99633ab2013-10-10 23:28:33 +02001883 afi = nf_tables_afinfo_lookup(net, family, false);
Patrick McHardy96518512013-10-14 11:00:02 +02001884 if (IS_ERR(afi))
1885 return PTR_ERR(afi);
1886
Pablo Neira Ayuso93707612013-10-10 23:21:26 +02001887 table = nf_tables_table_lookup(afi, nla[NFTA_RULE_TABLE]);
Patrick McHardy96518512013-10-14 11:00:02 +02001888 if (IS_ERR(table))
1889 return PTR_ERR(table);
Pablo Neira Ayuso55dd6f92014-04-03 11:53:37 +02001890 if (table->flags & NFT_TABLE_INACTIVE)
1891 return -ENOENT;
Patrick McHardy96518512013-10-14 11:00:02 +02001892
Pablo Neira Ayusocf9dc092013-11-24 20:39:10 +01001893 if (nla[NFTA_RULE_CHAIN]) {
1894 chain = nf_tables_chain_lookup(table, nla[NFTA_RULE_CHAIN]);
1895 if (IS_ERR(chain))
1896 return PTR_ERR(chain);
1897 }
Patrick McHardy96518512013-10-14 11:00:02 +02001898
Pablo Neira Ayuso0628b122013-10-14 11:05:33 +02001899 nft_ctx_init(&ctx, skb, nlh, afi, table, chain, nla);
1900
Pablo Neira Ayusocf9dc092013-11-24 20:39:10 +01001901 if (chain) {
1902 if (nla[NFTA_RULE_HANDLE]) {
1903 rule = nf_tables_rule_lookup(chain,
1904 nla[NFTA_RULE_HANDLE]);
1905 if (IS_ERR(rule))
1906 return PTR_ERR(rule);
Patrick McHardy96518512013-10-14 11:00:02 +02001907
Pablo Neira Ayuso0628b122013-10-14 11:05:33 +02001908 err = nf_tables_delrule_one(&ctx, rule);
Pablo Neira Ayusocf9dc092013-11-24 20:39:10 +01001909 } else {
1910 err = nf_table_delrule_by_chain(&ctx);
1911 }
1912 } else {
1913 list_for_each_entry(chain, &table->chains, list) {
1914 ctx.chain = chain;
1915 err = nf_table_delrule_by_chain(&ctx);
Pablo Neira Ayuso0628b122013-10-14 11:05:33 +02001916 if (err < 0)
1917 break;
Patrick McHardy96518512013-10-14 11:00:02 +02001918 }
1919 }
1920
Pablo Neira Ayuso0628b122013-10-14 11:05:33 +02001921 return err;
1922}
1923
Patrick McHardy20a69342013-10-11 12:06:22 +02001924/*
1925 * Sets
1926 */
1927
1928static LIST_HEAD(nf_tables_set_ops);
1929
1930int nft_register_set(struct nft_set_ops *ops)
1931{
1932 nfnl_lock(NFNL_SUBSYS_NFTABLES);
1933 list_add_tail(&ops->list, &nf_tables_set_ops);
1934 nfnl_unlock(NFNL_SUBSYS_NFTABLES);
1935 return 0;
1936}
1937EXPORT_SYMBOL_GPL(nft_register_set);
1938
1939void nft_unregister_set(struct nft_set_ops *ops)
1940{
1941 nfnl_lock(NFNL_SUBSYS_NFTABLES);
1942 list_del(&ops->list);
1943 nfnl_unlock(NFNL_SUBSYS_NFTABLES);
1944}
1945EXPORT_SYMBOL_GPL(nft_unregister_set);
1946
Patrick McHardyc50b9602014-03-28 10:19:47 +00001947/*
1948 * Select a set implementation based on the data characteristics and the
1949 * given policy. The total memory use might not be known if no size is
1950 * given, in that case the amount of memory per element is used.
1951 */
1952static const struct nft_set_ops *
1953nft_select_set_ops(const struct nlattr * const nla[],
1954 const struct nft_set_desc *desc,
1955 enum nft_set_policies policy)
Patrick McHardy20a69342013-10-11 12:06:22 +02001956{
Patrick McHardyc50b9602014-03-28 10:19:47 +00001957 const struct nft_set_ops *ops, *bops;
1958 struct nft_set_estimate est, best;
Patrick McHardy20a69342013-10-11 12:06:22 +02001959 u32 features;
1960
1961#ifdef CONFIG_MODULES
1962 if (list_empty(&nf_tables_set_ops)) {
1963 nfnl_unlock(NFNL_SUBSYS_NFTABLES);
1964 request_module("nft-set");
1965 nfnl_lock(NFNL_SUBSYS_NFTABLES);
1966 if (!list_empty(&nf_tables_set_ops))
1967 return ERR_PTR(-EAGAIN);
1968 }
1969#endif
1970 features = 0;
1971 if (nla[NFTA_SET_FLAGS] != NULL) {
1972 features = ntohl(nla_get_be32(nla[NFTA_SET_FLAGS]));
1973 features &= NFT_SET_INTERVAL | NFT_SET_MAP;
1974 }
1975
Patrick McHardyc50b9602014-03-28 10:19:47 +00001976 bops = NULL;
1977 best.size = ~0;
1978 best.class = ~0;
1979
Patrick McHardy20a69342013-10-11 12:06:22 +02001980 list_for_each_entry(ops, &nf_tables_set_ops, list) {
1981 if ((ops->features & features) != features)
1982 continue;
Patrick McHardyc50b9602014-03-28 10:19:47 +00001983 if (!ops->estimate(desc, features, &est))
1984 continue;
1985
1986 switch (policy) {
1987 case NFT_SET_POL_PERFORMANCE:
1988 if (est.class < best.class)
1989 break;
1990 if (est.class == best.class && est.size < best.size)
1991 break;
1992 continue;
1993 case NFT_SET_POL_MEMORY:
1994 if (est.size < best.size)
1995 break;
1996 if (est.size == best.size && est.class < best.class)
1997 break;
1998 continue;
1999 default:
2000 break;
2001 }
2002
Patrick McHardy20a69342013-10-11 12:06:22 +02002003 if (!try_module_get(ops->owner))
2004 continue;
Patrick McHardyc50b9602014-03-28 10:19:47 +00002005 if (bops != NULL)
2006 module_put(bops->owner);
2007
2008 bops = ops;
2009 best = est;
Patrick McHardy20a69342013-10-11 12:06:22 +02002010 }
2011
Patrick McHardyc50b9602014-03-28 10:19:47 +00002012 if (bops != NULL)
2013 return bops;
2014
Patrick McHardy20a69342013-10-11 12:06:22 +02002015 return ERR_PTR(-EOPNOTSUPP);
2016}
2017
2018static const struct nla_policy nft_set_policy[NFTA_SET_MAX + 1] = {
2019 [NFTA_SET_TABLE] = { .type = NLA_STRING },
Pablo Neira Ayusoa9bdd832014-03-24 15:10:37 +01002020 [NFTA_SET_NAME] = { .type = NLA_STRING,
2021 .len = IFNAMSIZ - 1 },
Patrick McHardy20a69342013-10-11 12:06:22 +02002022 [NFTA_SET_FLAGS] = { .type = NLA_U32 },
2023 [NFTA_SET_KEY_TYPE] = { .type = NLA_U32 },
2024 [NFTA_SET_KEY_LEN] = { .type = NLA_U32 },
2025 [NFTA_SET_DATA_TYPE] = { .type = NLA_U32 },
2026 [NFTA_SET_DATA_LEN] = { .type = NLA_U32 },
Patrick McHardyc50b9602014-03-28 10:19:47 +00002027 [NFTA_SET_POLICY] = { .type = NLA_U32 },
2028 [NFTA_SET_DESC] = { .type = NLA_NESTED },
Pablo Neira Ayuso958bee12014-04-03 11:48:44 +02002029 [NFTA_SET_ID] = { .type = NLA_U32 },
Patrick McHardyc50b9602014-03-28 10:19:47 +00002030};
2031
2032static const struct nla_policy nft_set_desc_policy[NFTA_SET_DESC_MAX + 1] = {
2033 [NFTA_SET_DESC_SIZE] = { .type = NLA_U32 },
Patrick McHardy20a69342013-10-11 12:06:22 +02002034};
2035
2036static int nft_ctx_init_from_setattr(struct nft_ctx *ctx,
2037 const struct sk_buff *skb,
2038 const struct nlmsghdr *nlh,
2039 const struct nlattr * const nla[])
2040{
Pablo Neira Ayuso99633ab2013-10-10 23:28:33 +02002041 struct net *net = sock_net(skb->sk);
Patrick McHardy20a69342013-10-11 12:06:22 +02002042 const struct nfgenmsg *nfmsg = nlmsg_data(nlh);
Pablo Neira Ayuso7c95f6d2014-04-04 01:22:45 +02002043 struct nft_af_info *afi = NULL;
2044 struct nft_table *table = NULL;
Patrick McHardy20a69342013-10-11 12:06:22 +02002045
Pablo Neira Ayusoc9c8e482013-12-26 16:49:03 +01002046 if (nfmsg->nfgen_family != NFPROTO_UNSPEC) {
2047 afi = nf_tables_afinfo_lookup(net, nfmsg->nfgen_family, false);
2048 if (IS_ERR(afi))
2049 return PTR_ERR(afi);
2050 }
Patrick McHardy20a69342013-10-11 12:06:22 +02002051
2052 if (nla[NFTA_SET_TABLE] != NULL) {
Patrick McHardyec2c9932014-02-05 15:03:35 +00002053 if (afi == NULL)
2054 return -EAFNOSUPPORT;
2055
Pablo Neira Ayuso93707612013-10-10 23:21:26 +02002056 table = nf_tables_table_lookup(afi, nla[NFTA_SET_TABLE]);
Patrick McHardy20a69342013-10-11 12:06:22 +02002057 if (IS_ERR(table))
2058 return PTR_ERR(table);
Pablo Neira Ayuso55dd6f92014-04-03 11:53:37 +02002059 if (table->flags & NFT_TABLE_INACTIVE)
2060 return -ENOENT;
Patrick McHardy20a69342013-10-11 12:06:22 +02002061 }
2062
Pablo Neira Ayuso0ca743a2013-10-14 00:06:06 +02002063 nft_ctx_init(ctx, skb, nlh, afi, table, NULL, nla);
Patrick McHardy20a69342013-10-11 12:06:22 +02002064 return 0;
2065}
2066
2067struct nft_set *nf_tables_set_lookup(const struct nft_table *table,
2068 const struct nlattr *nla)
2069{
2070 struct nft_set *set;
2071
2072 if (nla == NULL)
2073 return ERR_PTR(-EINVAL);
2074
2075 list_for_each_entry(set, &table->sets, list) {
2076 if (!nla_strcmp(nla, set->name))
2077 return set;
2078 }
2079 return ERR_PTR(-ENOENT);
2080}
2081
Pablo Neira Ayuso958bee12014-04-03 11:48:44 +02002082struct nft_set *nf_tables_set_lookup_byid(const struct net *net,
2083 const struct nlattr *nla)
2084{
2085 struct nft_trans *trans;
2086 u32 id = ntohl(nla_get_be32(nla));
2087
2088 list_for_each_entry(trans, &net->nft.commit_list, list) {
2089 if (trans->msg_type == NFT_MSG_NEWSET &&
2090 id == nft_trans_set_id(trans))
2091 return nft_trans_set(trans);
2092 }
2093 return ERR_PTR(-ENOENT);
2094}
2095
Patrick McHardy20a69342013-10-11 12:06:22 +02002096static int nf_tables_set_alloc_name(struct nft_ctx *ctx, struct nft_set *set,
2097 const char *name)
2098{
2099 const struct nft_set *i;
2100 const char *p;
2101 unsigned long *inuse;
Patrick McHardy60eb1892014-03-07 12:34:05 +01002102 unsigned int n = 0, min = 0;
Patrick McHardy20a69342013-10-11 12:06:22 +02002103
2104 p = strnchr(name, IFNAMSIZ, '%');
2105 if (p != NULL) {
2106 if (p[1] != 'd' || strchr(p + 2, '%'))
2107 return -EINVAL;
2108
2109 inuse = (unsigned long *)get_zeroed_page(GFP_KERNEL);
2110 if (inuse == NULL)
2111 return -ENOMEM;
Patrick McHardy60eb1892014-03-07 12:34:05 +01002112cont:
Patrick McHardy20a69342013-10-11 12:06:22 +02002113 list_for_each_entry(i, &ctx->table->sets, list) {
Daniel Borkmann14662912013-12-31 12:40:05 +01002114 int tmp;
2115
2116 if (!sscanf(i->name, name, &tmp))
Patrick McHardy20a69342013-10-11 12:06:22 +02002117 continue;
Patrick McHardy60eb1892014-03-07 12:34:05 +01002118 if (tmp < min || tmp >= min + BITS_PER_BYTE * PAGE_SIZE)
Patrick McHardy20a69342013-10-11 12:06:22 +02002119 continue;
Daniel Borkmann14662912013-12-31 12:40:05 +01002120
Patrick McHardy60eb1892014-03-07 12:34:05 +01002121 set_bit(tmp - min, inuse);
Patrick McHardy20a69342013-10-11 12:06:22 +02002122 }
2123
Patrick McHardy53b70282014-02-05 12:26:22 +01002124 n = find_first_zero_bit(inuse, BITS_PER_BYTE * PAGE_SIZE);
Patrick McHardy60eb1892014-03-07 12:34:05 +01002125 if (n >= BITS_PER_BYTE * PAGE_SIZE) {
2126 min += BITS_PER_BYTE * PAGE_SIZE;
2127 memset(inuse, 0, PAGE_SIZE);
2128 goto cont;
2129 }
Patrick McHardy20a69342013-10-11 12:06:22 +02002130 free_page((unsigned long)inuse);
2131 }
2132
Patrick McHardy60eb1892014-03-07 12:34:05 +01002133 snprintf(set->name, sizeof(set->name), name, min + n);
Patrick McHardy20a69342013-10-11 12:06:22 +02002134 list_for_each_entry(i, &ctx->table->sets, list) {
2135 if (!strcmp(set->name, i->name))
2136 return -ENFILE;
2137 }
2138 return 0;
2139}
2140
2141static int nf_tables_fill_set(struct sk_buff *skb, const struct nft_ctx *ctx,
2142 const struct nft_set *set, u16 event, u16 flags)
2143{
2144 struct nfgenmsg *nfmsg;
2145 struct nlmsghdr *nlh;
Patrick McHardyc50b9602014-03-28 10:19:47 +00002146 struct nlattr *desc;
Pablo Neira Ayuso128ad332014-05-09 17:14:24 +02002147 u32 portid = ctx->portid;
2148 u32 seq = ctx->seq;
Patrick McHardy20a69342013-10-11 12:06:22 +02002149
2150 event |= NFNL_SUBSYS_NFTABLES << 8;
2151 nlh = nlmsg_put(skb, portid, seq, event, sizeof(struct nfgenmsg),
2152 flags);
2153 if (nlh == NULL)
2154 goto nla_put_failure;
2155
2156 nfmsg = nlmsg_data(nlh);
2157 nfmsg->nfgen_family = ctx->afi->family;
2158 nfmsg->version = NFNETLINK_V0;
2159 nfmsg->res_id = 0;
2160
2161 if (nla_put_string(skb, NFTA_SET_TABLE, ctx->table->name))
2162 goto nla_put_failure;
2163 if (nla_put_string(skb, NFTA_SET_NAME, set->name))
2164 goto nla_put_failure;
2165 if (set->flags != 0)
2166 if (nla_put_be32(skb, NFTA_SET_FLAGS, htonl(set->flags)))
2167 goto nla_put_failure;
2168
2169 if (nla_put_be32(skb, NFTA_SET_KEY_TYPE, htonl(set->ktype)))
2170 goto nla_put_failure;
2171 if (nla_put_be32(skb, NFTA_SET_KEY_LEN, htonl(set->klen)))
2172 goto nla_put_failure;
2173 if (set->flags & NFT_SET_MAP) {
2174 if (nla_put_be32(skb, NFTA_SET_DATA_TYPE, htonl(set->dtype)))
2175 goto nla_put_failure;
2176 if (nla_put_be32(skb, NFTA_SET_DATA_LEN, htonl(set->dlen)))
2177 goto nla_put_failure;
2178 }
2179
Patrick McHardyc50b9602014-03-28 10:19:47 +00002180 desc = nla_nest_start(skb, NFTA_SET_DESC);
2181 if (desc == NULL)
2182 goto nla_put_failure;
2183 if (set->size &&
2184 nla_put_be32(skb, NFTA_SET_DESC_SIZE, htonl(set->size)))
2185 goto nla_put_failure;
2186 nla_nest_end(skb, desc);
2187
Patrick McHardy20a69342013-10-11 12:06:22 +02002188 return nlmsg_end(skb, nlh);
2189
2190nla_put_failure:
2191 nlmsg_trim(skb, nlh);
2192 return -1;
2193}
2194
2195static int nf_tables_set_notify(const struct nft_ctx *ctx,
2196 const struct nft_set *set,
Pablo Neira Ayuso31f84412014-05-29 10:29:58 +02002197 int event, gfp_t gfp_flags)
Patrick McHardy20a69342013-10-11 12:06:22 +02002198{
2199 struct sk_buff *skb;
Pablo Neira Ayuso128ad332014-05-09 17:14:24 +02002200 u32 portid = ctx->portid;
Patrick McHardy20a69342013-10-11 12:06:22 +02002201 int err;
2202
Pablo Neira Ayuso128ad332014-05-09 17:14:24 +02002203 if (!ctx->report &&
2204 !nfnetlink_has_listeners(ctx->net, NFNLGRP_NFTABLES))
Patrick McHardy20a69342013-10-11 12:06:22 +02002205 return 0;
2206
2207 err = -ENOBUFS;
Pablo Neira Ayuso31f84412014-05-29 10:29:58 +02002208 skb = nlmsg_new(NLMSG_GOODSIZE, gfp_flags);
Patrick McHardy20a69342013-10-11 12:06:22 +02002209 if (skb == NULL)
2210 goto err;
2211
2212 err = nf_tables_fill_set(skb, ctx, set, event, 0);
2213 if (err < 0) {
2214 kfree_skb(skb);
2215 goto err;
2216 }
2217
Pablo Neira Ayuso128ad332014-05-09 17:14:24 +02002218 err = nfnetlink_send(skb, ctx->net, portid, NFNLGRP_NFTABLES,
Pablo Neira Ayuso31f84412014-05-29 10:29:58 +02002219 ctx->report, gfp_flags);
Patrick McHardy20a69342013-10-11 12:06:22 +02002220err:
2221 if (err < 0)
Pablo Neira Ayuso99633ab2013-10-10 23:28:33 +02002222 nfnetlink_set_err(ctx->net, portid, NFNLGRP_NFTABLES, err);
Patrick McHardy20a69342013-10-11 12:06:22 +02002223 return err;
2224}
2225
2226static int nf_tables_dump_sets_table(struct nft_ctx *ctx, struct sk_buff *skb,
2227 struct netlink_callback *cb)
2228{
2229 const struct nft_set *set;
2230 unsigned int idx = 0, s_idx = cb->args[0];
2231
2232 if (cb->args[1])
2233 return skb->len;
2234
2235 list_for_each_entry(set, &ctx->table->sets, list) {
2236 if (idx < s_idx)
2237 goto cont;
2238 if (nf_tables_fill_set(skb, ctx, set, NFT_MSG_NEWSET,
2239 NLM_F_MULTI) < 0) {
2240 cb->args[0] = idx;
2241 goto done;
2242 }
2243cont:
2244 idx++;
2245 }
2246 cb->args[1] = 1;
2247done:
2248 return skb->len;
2249}
2250
Pablo Neira Ayusoc9c8e482013-12-26 16:49:03 +01002251static int nf_tables_dump_sets_family(struct nft_ctx *ctx, struct sk_buff *skb,
2252 struct netlink_callback *cb)
Patrick McHardy20a69342013-10-11 12:06:22 +02002253{
2254 const struct nft_set *set;
Pablo Neira Ayusoe38195b2013-12-24 18:32:35 +01002255 unsigned int idx, s_idx = cb->args[0];
Patrick McHardy20a69342013-10-11 12:06:22 +02002256 struct nft_table *table, *cur_table = (struct nft_table *)cb->args[2];
2257
2258 if (cb->args[1])
2259 return skb->len;
2260
2261 list_for_each_entry(table, &ctx->afi->tables, list) {
Pablo Neira Ayusoe38195b2013-12-24 18:32:35 +01002262 if (cur_table) {
2263 if (cur_table != table)
2264 continue;
Patrick McHardy20a69342013-10-11 12:06:22 +02002265
Pablo Neira Ayusoe38195b2013-12-24 18:32:35 +01002266 cur_table = NULL;
2267 }
Patrick McHardy20a69342013-10-11 12:06:22 +02002268 ctx->table = table;
Pablo Neira Ayusoe38195b2013-12-24 18:32:35 +01002269 idx = 0;
Patrick McHardy20a69342013-10-11 12:06:22 +02002270 list_for_each_entry(set, &ctx->table->sets, list) {
2271 if (idx < s_idx)
2272 goto cont;
2273 if (nf_tables_fill_set(skb, ctx, set, NFT_MSG_NEWSET,
2274 NLM_F_MULTI) < 0) {
2275 cb->args[0] = idx;
2276 cb->args[2] = (unsigned long) table;
2277 goto done;
2278 }
2279cont:
2280 idx++;
2281 }
2282 }
2283 cb->args[1] = 1;
2284done:
2285 return skb->len;
2286}
2287
Pablo Neira Ayusoc9c8e482013-12-26 16:49:03 +01002288static int nf_tables_dump_sets_all(struct nft_ctx *ctx, struct sk_buff *skb,
2289 struct netlink_callback *cb)
2290{
2291 const struct nft_set *set;
2292 unsigned int idx, s_idx = cb->args[0];
Pablo Neira Ayuso7c95f6d2014-04-04 01:22:45 +02002293 struct nft_af_info *afi;
Pablo Neira Ayusoc9c8e482013-12-26 16:49:03 +01002294 struct nft_table *table, *cur_table = (struct nft_table *)cb->args[2];
2295 struct net *net = sock_net(skb->sk);
2296 int cur_family = cb->args[3];
2297
2298 if (cb->args[1])
2299 return skb->len;
2300
2301 list_for_each_entry(afi, &net->nft.af_info, list) {
2302 if (cur_family) {
2303 if (afi->family != cur_family)
2304 continue;
2305
2306 cur_family = 0;
2307 }
2308
2309 list_for_each_entry(table, &afi->tables, list) {
2310 if (cur_table) {
2311 if (cur_table != table)
2312 continue;
2313
2314 cur_table = NULL;
2315 }
2316
2317 ctx->table = table;
2318 ctx->afi = afi;
2319 idx = 0;
2320 list_for_each_entry(set, &ctx->table->sets, list) {
2321 if (idx < s_idx)
2322 goto cont;
2323 if (nf_tables_fill_set(skb, ctx, set,
2324 NFT_MSG_NEWSET,
2325 NLM_F_MULTI) < 0) {
2326 cb->args[0] = idx;
2327 cb->args[2] = (unsigned long) table;
2328 cb->args[3] = afi->family;
2329 goto done;
2330 }
2331cont:
2332 idx++;
2333 }
2334 if (s_idx)
2335 s_idx = 0;
2336 }
2337 }
2338 cb->args[1] = 1;
2339done:
2340 return skb->len;
2341}
2342
Patrick McHardy20a69342013-10-11 12:06:22 +02002343static int nf_tables_dump_sets(struct sk_buff *skb, struct netlink_callback *cb)
2344{
2345 const struct nfgenmsg *nfmsg = nlmsg_data(cb->nlh);
2346 struct nlattr *nla[NFTA_SET_MAX + 1];
2347 struct nft_ctx ctx;
2348 int err, ret;
2349
2350 err = nlmsg_parse(cb->nlh, sizeof(*nfmsg), nla, NFTA_SET_MAX,
2351 nft_set_policy);
2352 if (err < 0)
2353 return err;
2354
2355 err = nft_ctx_init_from_setattr(&ctx, cb->skb, cb->nlh, (void *)nla);
2356 if (err < 0)
2357 return err;
2358
Pablo Neira Ayusoc9c8e482013-12-26 16:49:03 +01002359 if (ctx.table == NULL) {
2360 if (ctx.afi == NULL)
2361 ret = nf_tables_dump_sets_all(&ctx, skb, cb);
2362 else
2363 ret = nf_tables_dump_sets_family(&ctx, skb, cb);
2364 } else
Patrick McHardy20a69342013-10-11 12:06:22 +02002365 ret = nf_tables_dump_sets_table(&ctx, skb, cb);
2366
2367 return ret;
2368}
2369
Pablo Neira Ayuso958bee12014-04-03 11:48:44 +02002370#define NFT_SET_INACTIVE (1 << 15) /* Internal set flag */
2371
Patrick McHardy20a69342013-10-11 12:06:22 +02002372static int nf_tables_getset(struct sock *nlsk, struct sk_buff *skb,
2373 const struct nlmsghdr *nlh,
2374 const struct nlattr * const nla[])
2375{
2376 const struct nft_set *set;
2377 struct nft_ctx ctx;
2378 struct sk_buff *skb2;
Pablo Neira Ayusoc9c8e482013-12-26 16:49:03 +01002379 const struct nfgenmsg *nfmsg = nlmsg_data(nlh);
Patrick McHardy20a69342013-10-11 12:06:22 +02002380 int err;
2381
2382 /* Verify existance before starting dump */
2383 err = nft_ctx_init_from_setattr(&ctx, skb, nlh, nla);
2384 if (err < 0)
2385 return err;
2386
2387 if (nlh->nlmsg_flags & NLM_F_DUMP) {
2388 struct netlink_dump_control c = {
2389 .dump = nf_tables_dump_sets,
2390 };
2391 return netlink_dump_start(nlsk, skb, nlh, &c);
2392 }
2393
Pablo Neira Ayusoc9c8e482013-12-26 16:49:03 +01002394 /* Only accept unspec with dump */
2395 if (nfmsg->nfgen_family == NFPROTO_UNSPEC)
2396 return -EAFNOSUPPORT;
2397
Patrick McHardy20a69342013-10-11 12:06:22 +02002398 set = nf_tables_set_lookup(ctx.table, nla[NFTA_SET_NAME]);
2399 if (IS_ERR(set))
2400 return PTR_ERR(set);
Pablo Neira Ayuso958bee12014-04-03 11:48:44 +02002401 if (set->flags & NFT_SET_INACTIVE)
2402 return -ENOENT;
Patrick McHardy20a69342013-10-11 12:06:22 +02002403
2404 skb2 = alloc_skb(NLMSG_GOODSIZE, GFP_KERNEL);
2405 if (skb2 == NULL)
2406 return -ENOMEM;
2407
2408 err = nf_tables_fill_set(skb2, &ctx, set, NFT_MSG_NEWSET, 0);
2409 if (err < 0)
2410 goto err;
2411
2412 return nlmsg_unicast(nlsk, skb2, NETLINK_CB(skb).portid);
2413
2414err:
2415 kfree_skb(skb2);
2416 return err;
2417}
2418
Patrick McHardyc50b9602014-03-28 10:19:47 +00002419static int nf_tables_set_desc_parse(const struct nft_ctx *ctx,
2420 struct nft_set_desc *desc,
2421 const struct nlattr *nla)
2422{
2423 struct nlattr *da[NFTA_SET_DESC_MAX + 1];
2424 int err;
2425
2426 err = nla_parse_nested(da, NFTA_SET_DESC_MAX, nla, nft_set_desc_policy);
2427 if (err < 0)
2428 return err;
2429
2430 if (da[NFTA_SET_DESC_SIZE] != NULL)
2431 desc->size = ntohl(nla_get_be32(da[NFTA_SET_DESC_SIZE]));
2432
2433 return 0;
2434}
2435
Pablo Neira Ayuso958bee12014-04-03 11:48:44 +02002436static int nft_trans_set_add(struct nft_ctx *ctx, int msg_type,
2437 struct nft_set *set)
2438{
2439 struct nft_trans *trans;
2440
2441 trans = nft_trans_alloc(ctx, msg_type, sizeof(struct nft_trans_set));
2442 if (trans == NULL)
2443 return -ENOMEM;
2444
2445 if (msg_type == NFT_MSG_NEWSET && ctx->nla[NFTA_SET_ID] != NULL) {
2446 nft_trans_set_id(trans) =
2447 ntohl(nla_get_be32(ctx->nla[NFTA_SET_ID]));
2448 set->flags |= NFT_SET_INACTIVE;
2449 }
2450 nft_trans_set(trans) = set;
2451 list_add_tail(&trans->list, &ctx->net->nft.commit_list);
2452
2453 return 0;
2454}
2455
Patrick McHardy20a69342013-10-11 12:06:22 +02002456static int nf_tables_newset(struct sock *nlsk, struct sk_buff *skb,
2457 const struct nlmsghdr *nlh,
2458 const struct nlattr * const nla[])
2459{
2460 const struct nfgenmsg *nfmsg = nlmsg_data(nlh);
2461 const struct nft_set_ops *ops;
Pablo Neira Ayuso7c95f6d2014-04-04 01:22:45 +02002462 struct nft_af_info *afi;
Pablo Neira Ayuso99633ab2013-10-10 23:28:33 +02002463 struct net *net = sock_net(skb->sk);
Patrick McHardy20a69342013-10-11 12:06:22 +02002464 struct nft_table *table;
2465 struct nft_set *set;
2466 struct nft_ctx ctx;
2467 char name[IFNAMSIZ];
2468 unsigned int size;
2469 bool create;
Patrick McHardyc50b9602014-03-28 10:19:47 +00002470 u32 ktype, dtype, flags, policy;
2471 struct nft_set_desc desc;
Patrick McHardy20a69342013-10-11 12:06:22 +02002472 int err;
2473
2474 if (nla[NFTA_SET_TABLE] == NULL ||
2475 nla[NFTA_SET_NAME] == NULL ||
Pablo Neira Ayuso958bee12014-04-03 11:48:44 +02002476 nla[NFTA_SET_KEY_LEN] == NULL ||
2477 nla[NFTA_SET_ID] == NULL)
Patrick McHardy20a69342013-10-11 12:06:22 +02002478 return -EINVAL;
2479
Patrick McHardyc50b9602014-03-28 10:19:47 +00002480 memset(&desc, 0, sizeof(desc));
2481
Patrick McHardy20a69342013-10-11 12:06:22 +02002482 ktype = NFT_DATA_VALUE;
2483 if (nla[NFTA_SET_KEY_TYPE] != NULL) {
2484 ktype = ntohl(nla_get_be32(nla[NFTA_SET_KEY_TYPE]));
2485 if ((ktype & NFT_DATA_RESERVED_MASK) == NFT_DATA_RESERVED_MASK)
2486 return -EINVAL;
2487 }
2488
Patrick McHardyc50b9602014-03-28 10:19:47 +00002489 desc.klen = ntohl(nla_get_be32(nla[NFTA_SET_KEY_LEN]));
2490 if (desc.klen == 0 || desc.klen > FIELD_SIZEOF(struct nft_data, data))
Patrick McHardy20a69342013-10-11 12:06:22 +02002491 return -EINVAL;
2492
2493 flags = 0;
2494 if (nla[NFTA_SET_FLAGS] != NULL) {
2495 flags = ntohl(nla_get_be32(nla[NFTA_SET_FLAGS]));
2496 if (flags & ~(NFT_SET_ANONYMOUS | NFT_SET_CONSTANT |
2497 NFT_SET_INTERVAL | NFT_SET_MAP))
2498 return -EINVAL;
2499 }
2500
2501 dtype = 0;
Patrick McHardy20a69342013-10-11 12:06:22 +02002502 if (nla[NFTA_SET_DATA_TYPE] != NULL) {
2503 if (!(flags & NFT_SET_MAP))
2504 return -EINVAL;
2505
2506 dtype = ntohl(nla_get_be32(nla[NFTA_SET_DATA_TYPE]));
2507 if ((dtype & NFT_DATA_RESERVED_MASK) == NFT_DATA_RESERVED_MASK &&
2508 dtype != NFT_DATA_VERDICT)
2509 return -EINVAL;
2510
2511 if (dtype != NFT_DATA_VERDICT) {
2512 if (nla[NFTA_SET_DATA_LEN] == NULL)
2513 return -EINVAL;
Patrick McHardyc50b9602014-03-28 10:19:47 +00002514 desc.dlen = ntohl(nla_get_be32(nla[NFTA_SET_DATA_LEN]));
2515 if (desc.dlen == 0 ||
2516 desc.dlen > FIELD_SIZEOF(struct nft_data, data))
Patrick McHardy20a69342013-10-11 12:06:22 +02002517 return -EINVAL;
2518 } else
Patrick McHardyc50b9602014-03-28 10:19:47 +00002519 desc.dlen = sizeof(struct nft_data);
Patrick McHardy20a69342013-10-11 12:06:22 +02002520 } else if (flags & NFT_SET_MAP)
2521 return -EINVAL;
2522
Patrick McHardyc50b9602014-03-28 10:19:47 +00002523 policy = NFT_SET_POL_PERFORMANCE;
2524 if (nla[NFTA_SET_POLICY] != NULL)
2525 policy = ntohl(nla_get_be32(nla[NFTA_SET_POLICY]));
2526
2527 if (nla[NFTA_SET_DESC] != NULL) {
2528 err = nf_tables_set_desc_parse(&ctx, &desc, nla[NFTA_SET_DESC]);
2529 if (err < 0)
2530 return err;
2531 }
2532
Patrick McHardy20a69342013-10-11 12:06:22 +02002533 create = nlh->nlmsg_flags & NLM_F_CREATE ? true : false;
2534
Pablo Neira Ayuso99633ab2013-10-10 23:28:33 +02002535 afi = nf_tables_afinfo_lookup(net, nfmsg->nfgen_family, create);
Patrick McHardy20a69342013-10-11 12:06:22 +02002536 if (IS_ERR(afi))
2537 return PTR_ERR(afi);
2538
Pablo Neira Ayuso93707612013-10-10 23:21:26 +02002539 table = nf_tables_table_lookup(afi, nla[NFTA_SET_TABLE]);
Patrick McHardy20a69342013-10-11 12:06:22 +02002540 if (IS_ERR(table))
2541 return PTR_ERR(table);
2542
Pablo Neira Ayuso0ca743a2013-10-14 00:06:06 +02002543 nft_ctx_init(&ctx, skb, nlh, afi, table, NULL, nla);
Patrick McHardy20a69342013-10-11 12:06:22 +02002544
2545 set = nf_tables_set_lookup(table, nla[NFTA_SET_NAME]);
2546 if (IS_ERR(set)) {
2547 if (PTR_ERR(set) != -ENOENT)
2548 return PTR_ERR(set);
2549 set = NULL;
2550 }
2551
2552 if (set != NULL) {
2553 if (nlh->nlmsg_flags & NLM_F_EXCL)
2554 return -EEXIST;
2555 if (nlh->nlmsg_flags & NLM_F_REPLACE)
2556 return -EOPNOTSUPP;
2557 return 0;
2558 }
2559
2560 if (!(nlh->nlmsg_flags & NLM_F_CREATE))
2561 return -ENOENT;
2562
Patrick McHardyc50b9602014-03-28 10:19:47 +00002563 ops = nft_select_set_ops(nla, &desc, policy);
Patrick McHardy20a69342013-10-11 12:06:22 +02002564 if (IS_ERR(ops))
2565 return PTR_ERR(ops);
2566
2567 size = 0;
2568 if (ops->privsize != NULL)
2569 size = ops->privsize(nla);
2570
2571 err = -ENOMEM;
2572 set = kzalloc(sizeof(*set) + size, GFP_KERNEL);
2573 if (set == NULL)
2574 goto err1;
2575
2576 nla_strlcpy(name, nla[NFTA_SET_NAME], sizeof(set->name));
2577 err = nf_tables_set_alloc_name(&ctx, set, name);
2578 if (err < 0)
2579 goto err2;
2580
2581 INIT_LIST_HEAD(&set->bindings);
2582 set->ops = ops;
2583 set->ktype = ktype;
Patrick McHardyc50b9602014-03-28 10:19:47 +00002584 set->klen = desc.klen;
Patrick McHardy20a69342013-10-11 12:06:22 +02002585 set->dtype = dtype;
Patrick McHardyc50b9602014-03-28 10:19:47 +00002586 set->dlen = desc.dlen;
Patrick McHardy20a69342013-10-11 12:06:22 +02002587 set->flags = flags;
Patrick McHardyc50b9602014-03-28 10:19:47 +00002588 set->size = desc.size;
Patrick McHardy20a69342013-10-11 12:06:22 +02002589
Patrick McHardyc50b9602014-03-28 10:19:47 +00002590 err = ops->init(set, &desc, nla);
Patrick McHardy20a69342013-10-11 12:06:22 +02002591 if (err < 0)
2592 goto err2;
2593
Pablo Neira Ayuso958bee12014-04-03 11:48:44 +02002594 err = nft_trans_set_add(&ctx, NFT_MSG_NEWSET, set);
Patrick McHardy20a69342013-10-11 12:06:22 +02002595 if (err < 0)
2596 goto err2;
2597
2598 list_add_tail(&set->list, &table->sets);
Pablo Neira Ayuso4fefee52014-05-23 12:39:26 +02002599 table->use++;
Patrick McHardy20a69342013-10-11 12:06:22 +02002600 return 0;
2601
2602err2:
2603 kfree(set);
2604err1:
2605 module_put(ops->owner);
2606 return err;
2607}
2608
Pablo Neira Ayuso958bee12014-04-03 11:48:44 +02002609static void nft_set_destroy(struct nft_set *set)
2610{
2611 set->ops->destroy(set);
2612 module_put(set->ops->owner);
2613 kfree(set);
2614}
2615
Patrick McHardy20a69342013-10-11 12:06:22 +02002616static void nf_tables_set_destroy(const struct nft_ctx *ctx, struct nft_set *set)
2617{
2618 list_del(&set->list);
Pablo Neira Ayuso31f84412014-05-29 10:29:58 +02002619 nf_tables_set_notify(ctx, set, NFT_MSG_DELSET, GFP_ATOMIC);
Pablo Neira Ayuso958bee12014-04-03 11:48:44 +02002620 nft_set_destroy(set);
Patrick McHardy20a69342013-10-11 12:06:22 +02002621}
2622
2623static int nf_tables_delset(struct sock *nlsk, struct sk_buff *skb,
2624 const struct nlmsghdr *nlh,
2625 const struct nlattr * const nla[])
2626{
Pablo Neira Ayusoc9c8e482013-12-26 16:49:03 +01002627 const struct nfgenmsg *nfmsg = nlmsg_data(nlh);
Patrick McHardy20a69342013-10-11 12:06:22 +02002628 struct nft_set *set;
2629 struct nft_ctx ctx;
2630 int err;
2631
Patrick McHardyec2c9932014-02-05 15:03:35 +00002632 if (nfmsg->nfgen_family == NFPROTO_UNSPEC)
2633 return -EAFNOSUPPORT;
Patrick McHardy20a69342013-10-11 12:06:22 +02002634 if (nla[NFTA_SET_TABLE] == NULL)
2635 return -EINVAL;
2636
2637 err = nft_ctx_init_from_setattr(&ctx, skb, nlh, nla);
2638 if (err < 0)
2639 return err;
2640
2641 set = nf_tables_set_lookup(ctx.table, nla[NFTA_SET_NAME]);
2642 if (IS_ERR(set))
2643 return PTR_ERR(set);
Pablo Neira Ayuso958bee12014-04-03 11:48:44 +02002644 if (set->flags & NFT_SET_INACTIVE)
2645 return -ENOENT;
Patrick McHardy20a69342013-10-11 12:06:22 +02002646 if (!list_empty(&set->bindings))
2647 return -EBUSY;
2648
Pablo Neira Ayuso958bee12014-04-03 11:48:44 +02002649 err = nft_trans_set_add(&ctx, NFT_MSG_DELSET, set);
2650 if (err < 0)
2651 return err;
2652
2653 list_del(&set->list);
Pablo Neira Ayuso4fefee52014-05-23 12:39:26 +02002654 ctx.table->use--;
Patrick McHardy20a69342013-10-11 12:06:22 +02002655 return 0;
2656}
2657
2658static int nf_tables_bind_check_setelem(const struct nft_ctx *ctx,
2659 const struct nft_set *set,
2660 const struct nft_set_iter *iter,
2661 const struct nft_set_elem *elem)
2662{
2663 enum nft_registers dreg;
2664
2665 dreg = nft_type_to_reg(set->dtype);
Pablo Neira Ayuso2ee0d3c2013-12-28 00:59:38 +01002666 return nft_validate_data_load(ctx, dreg, &elem->data,
2667 set->dtype == NFT_DATA_VERDICT ?
2668 NFT_DATA_VERDICT : NFT_DATA_VALUE);
Patrick McHardy20a69342013-10-11 12:06:22 +02002669}
2670
2671int nf_tables_bind_set(const struct nft_ctx *ctx, struct nft_set *set,
2672 struct nft_set_binding *binding)
2673{
2674 struct nft_set_binding *i;
2675 struct nft_set_iter iter;
2676
2677 if (!list_empty(&set->bindings) && set->flags & NFT_SET_ANONYMOUS)
2678 return -EBUSY;
2679
2680 if (set->flags & NFT_SET_MAP) {
2681 /* If the set is already bound to the same chain all
2682 * jumps are already validated for that chain.
2683 */
2684 list_for_each_entry(i, &set->bindings, list) {
2685 if (i->chain == binding->chain)
2686 goto bind;
2687 }
2688
2689 iter.skip = 0;
2690 iter.count = 0;
2691 iter.err = 0;
2692 iter.fn = nf_tables_bind_check_setelem;
2693
2694 set->ops->walk(ctx, set, &iter);
2695 if (iter.err < 0) {
2696 /* Destroy anonymous sets if binding fails */
2697 if (set->flags & NFT_SET_ANONYMOUS)
2698 nf_tables_set_destroy(ctx, set);
2699
2700 return iter.err;
2701 }
2702 }
2703bind:
2704 binding->chain = ctx->chain;
2705 list_add_tail(&binding->list, &set->bindings);
2706 return 0;
2707}
2708
2709void nf_tables_unbind_set(const struct nft_ctx *ctx, struct nft_set *set,
2710 struct nft_set_binding *binding)
2711{
2712 list_del(&binding->list);
2713
Pablo Neira Ayuso958bee12014-04-03 11:48:44 +02002714 if (list_empty(&set->bindings) && set->flags & NFT_SET_ANONYMOUS &&
2715 !(set->flags & NFT_SET_INACTIVE))
Patrick McHardy20a69342013-10-11 12:06:22 +02002716 nf_tables_set_destroy(ctx, set);
2717}
2718
2719/*
2720 * Set elements
2721 */
2722
2723static const struct nla_policy nft_set_elem_policy[NFTA_SET_ELEM_MAX + 1] = {
2724 [NFTA_SET_ELEM_KEY] = { .type = NLA_NESTED },
2725 [NFTA_SET_ELEM_DATA] = { .type = NLA_NESTED },
2726 [NFTA_SET_ELEM_FLAGS] = { .type = NLA_U32 },
2727};
2728
2729static const struct nla_policy nft_set_elem_list_policy[NFTA_SET_ELEM_LIST_MAX + 1] = {
2730 [NFTA_SET_ELEM_LIST_TABLE] = { .type = NLA_STRING },
2731 [NFTA_SET_ELEM_LIST_SET] = { .type = NLA_STRING },
2732 [NFTA_SET_ELEM_LIST_ELEMENTS] = { .type = NLA_NESTED },
Pablo Neira Ayuso958bee12014-04-03 11:48:44 +02002733 [NFTA_SET_ELEM_LIST_SET_ID] = { .type = NLA_U32 },
Patrick McHardy20a69342013-10-11 12:06:22 +02002734};
2735
2736static int nft_ctx_init_from_elemattr(struct nft_ctx *ctx,
2737 const struct sk_buff *skb,
2738 const struct nlmsghdr *nlh,
Pablo Neira Ayuso55dd6f92014-04-03 11:53:37 +02002739 const struct nlattr * const nla[],
2740 bool trans)
Patrick McHardy20a69342013-10-11 12:06:22 +02002741{
2742 const struct nfgenmsg *nfmsg = nlmsg_data(nlh);
Pablo Neira Ayuso7c95f6d2014-04-04 01:22:45 +02002743 struct nft_af_info *afi;
2744 struct nft_table *table;
Pablo Neira Ayuso99633ab2013-10-10 23:28:33 +02002745 struct net *net = sock_net(skb->sk);
Patrick McHardy20a69342013-10-11 12:06:22 +02002746
Pablo Neira Ayuso99633ab2013-10-10 23:28:33 +02002747 afi = nf_tables_afinfo_lookup(net, nfmsg->nfgen_family, false);
Patrick McHardy20a69342013-10-11 12:06:22 +02002748 if (IS_ERR(afi))
2749 return PTR_ERR(afi);
2750
Pablo Neira Ayuso93707612013-10-10 23:21:26 +02002751 table = nf_tables_table_lookup(afi, nla[NFTA_SET_ELEM_LIST_TABLE]);
Patrick McHardy20a69342013-10-11 12:06:22 +02002752 if (IS_ERR(table))
2753 return PTR_ERR(table);
Pablo Neira Ayuso55dd6f92014-04-03 11:53:37 +02002754 if (!trans && (table->flags & NFT_TABLE_INACTIVE))
2755 return -ENOENT;
Patrick McHardy20a69342013-10-11 12:06:22 +02002756
Pablo Neira Ayuso0ca743a2013-10-14 00:06:06 +02002757 nft_ctx_init(ctx, skb, nlh, afi, table, NULL, nla);
Patrick McHardy20a69342013-10-11 12:06:22 +02002758 return 0;
2759}
2760
2761static int nf_tables_fill_setelem(struct sk_buff *skb,
2762 const struct nft_set *set,
2763 const struct nft_set_elem *elem)
2764{
2765 unsigned char *b = skb_tail_pointer(skb);
2766 struct nlattr *nest;
2767
2768 nest = nla_nest_start(skb, NFTA_LIST_ELEM);
2769 if (nest == NULL)
2770 goto nla_put_failure;
2771
2772 if (nft_data_dump(skb, NFTA_SET_ELEM_KEY, &elem->key, NFT_DATA_VALUE,
2773 set->klen) < 0)
2774 goto nla_put_failure;
2775
2776 if (set->flags & NFT_SET_MAP &&
2777 !(elem->flags & NFT_SET_ELEM_INTERVAL_END) &&
2778 nft_data_dump(skb, NFTA_SET_ELEM_DATA, &elem->data,
2779 set->dtype == NFT_DATA_VERDICT ? NFT_DATA_VERDICT : NFT_DATA_VALUE,
2780 set->dlen) < 0)
2781 goto nla_put_failure;
2782
2783 if (elem->flags != 0)
2784 if (nla_put_be32(skb, NFTA_SET_ELEM_FLAGS, htonl(elem->flags)))
2785 goto nla_put_failure;
2786
2787 nla_nest_end(skb, nest);
2788 return 0;
2789
2790nla_put_failure:
2791 nlmsg_trim(skb, b);
2792 return -EMSGSIZE;
2793}
2794
2795struct nft_set_dump_args {
2796 const struct netlink_callback *cb;
2797 struct nft_set_iter iter;
2798 struct sk_buff *skb;
2799};
2800
2801static int nf_tables_dump_setelem(const struct nft_ctx *ctx,
2802 const struct nft_set *set,
2803 const struct nft_set_iter *iter,
2804 const struct nft_set_elem *elem)
2805{
2806 struct nft_set_dump_args *args;
2807
2808 args = container_of(iter, struct nft_set_dump_args, iter);
2809 return nf_tables_fill_setelem(args->skb, set, elem);
2810}
2811
2812static int nf_tables_dump_set(struct sk_buff *skb, struct netlink_callback *cb)
2813{
2814 const struct nft_set *set;
2815 struct nft_set_dump_args args;
2816 struct nft_ctx ctx;
2817 struct nlattr *nla[NFTA_SET_ELEM_LIST_MAX + 1];
2818 struct nfgenmsg *nfmsg;
2819 struct nlmsghdr *nlh;
2820 struct nlattr *nest;
2821 u32 portid, seq;
2822 int event, err;
2823
Michal Nazarewicz720e0df2014-01-01 06:27:19 +01002824 err = nlmsg_parse(cb->nlh, sizeof(struct nfgenmsg), nla,
2825 NFTA_SET_ELEM_LIST_MAX, nft_set_elem_list_policy);
Patrick McHardy20a69342013-10-11 12:06:22 +02002826 if (err < 0)
2827 return err;
2828
Pablo Neira Ayuso55dd6f92014-04-03 11:53:37 +02002829 err = nft_ctx_init_from_elemattr(&ctx, cb->skb, cb->nlh, (void *)nla,
2830 false);
Patrick McHardy20a69342013-10-11 12:06:22 +02002831 if (err < 0)
2832 return err;
2833
2834 set = nf_tables_set_lookup(ctx.table, nla[NFTA_SET_ELEM_LIST_SET]);
2835 if (IS_ERR(set))
2836 return PTR_ERR(set);
Pablo Neira Ayuso958bee12014-04-03 11:48:44 +02002837 if (set->flags & NFT_SET_INACTIVE)
2838 return -ENOENT;
Patrick McHardy20a69342013-10-11 12:06:22 +02002839
2840 event = NFT_MSG_NEWSETELEM;
2841 event |= NFNL_SUBSYS_NFTABLES << 8;
2842 portid = NETLINK_CB(cb->skb).portid;
2843 seq = cb->nlh->nlmsg_seq;
2844
2845 nlh = nlmsg_put(skb, portid, seq, event, sizeof(struct nfgenmsg),
2846 NLM_F_MULTI);
2847 if (nlh == NULL)
2848 goto nla_put_failure;
2849
2850 nfmsg = nlmsg_data(nlh);
2851 nfmsg->nfgen_family = NFPROTO_UNSPEC;
2852 nfmsg->version = NFNETLINK_V0;
2853 nfmsg->res_id = 0;
2854
2855 if (nla_put_string(skb, NFTA_SET_ELEM_LIST_TABLE, ctx.table->name))
2856 goto nla_put_failure;
2857 if (nla_put_string(skb, NFTA_SET_ELEM_LIST_SET, set->name))
2858 goto nla_put_failure;
2859
2860 nest = nla_nest_start(skb, NFTA_SET_ELEM_LIST_ELEMENTS);
2861 if (nest == NULL)
2862 goto nla_put_failure;
2863
2864 args.cb = cb;
2865 args.skb = skb;
2866 args.iter.skip = cb->args[0];
2867 args.iter.count = 0;
2868 args.iter.err = 0;
2869 args.iter.fn = nf_tables_dump_setelem;
2870 set->ops->walk(&ctx, set, &args.iter);
2871
2872 nla_nest_end(skb, nest);
2873 nlmsg_end(skb, nlh);
2874
2875 if (args.iter.err && args.iter.err != -EMSGSIZE)
2876 return args.iter.err;
2877 if (args.iter.count == cb->args[0])
2878 return 0;
2879
2880 cb->args[0] = args.iter.count;
2881 return skb->len;
2882
2883nla_put_failure:
2884 return -ENOSPC;
2885}
2886
2887static int nf_tables_getsetelem(struct sock *nlsk, struct sk_buff *skb,
2888 const struct nlmsghdr *nlh,
2889 const struct nlattr * const nla[])
2890{
2891 const struct nft_set *set;
2892 struct nft_ctx ctx;
2893 int err;
2894
Pablo Neira Ayuso55dd6f92014-04-03 11:53:37 +02002895 err = nft_ctx_init_from_elemattr(&ctx, skb, nlh, nla, false);
Patrick McHardy20a69342013-10-11 12:06:22 +02002896 if (err < 0)
2897 return err;
2898
2899 set = nf_tables_set_lookup(ctx.table, nla[NFTA_SET_ELEM_LIST_SET]);
2900 if (IS_ERR(set))
2901 return PTR_ERR(set);
Pablo Neira Ayuso958bee12014-04-03 11:48:44 +02002902 if (set->flags & NFT_SET_INACTIVE)
2903 return -ENOENT;
Patrick McHardy20a69342013-10-11 12:06:22 +02002904
2905 if (nlh->nlmsg_flags & NLM_F_DUMP) {
2906 struct netlink_dump_control c = {
2907 .dump = nf_tables_dump_set,
2908 };
2909 return netlink_dump_start(nlsk, skb, nlh, &c);
2910 }
2911 return -EOPNOTSUPP;
2912}
2913
Arturo Borrerod60ce622014-04-01 14:06:07 +02002914static int nf_tables_fill_setelem_info(struct sk_buff *skb,
2915 const struct nft_ctx *ctx, u32 seq,
2916 u32 portid, int event, u16 flags,
2917 const struct nft_set *set,
2918 const struct nft_set_elem *elem)
2919{
2920 struct nfgenmsg *nfmsg;
2921 struct nlmsghdr *nlh;
2922 struct nlattr *nest;
2923 int err;
2924
2925 event |= NFNL_SUBSYS_NFTABLES << 8;
2926 nlh = nlmsg_put(skb, portid, seq, event, sizeof(struct nfgenmsg),
2927 flags);
2928 if (nlh == NULL)
2929 goto nla_put_failure;
2930
2931 nfmsg = nlmsg_data(nlh);
2932 nfmsg->nfgen_family = ctx->afi->family;
2933 nfmsg->version = NFNETLINK_V0;
2934 nfmsg->res_id = 0;
2935
2936 if (nla_put_string(skb, NFTA_SET_TABLE, ctx->table->name))
2937 goto nla_put_failure;
2938 if (nla_put_string(skb, NFTA_SET_NAME, set->name))
2939 goto nla_put_failure;
2940
2941 nest = nla_nest_start(skb, NFTA_SET_ELEM_LIST_ELEMENTS);
2942 if (nest == NULL)
2943 goto nla_put_failure;
2944
2945 err = nf_tables_fill_setelem(skb, set, elem);
2946 if (err < 0)
2947 goto nla_put_failure;
2948
2949 nla_nest_end(skb, nest);
2950
2951 return nlmsg_end(skb, nlh);
2952
2953nla_put_failure:
2954 nlmsg_trim(skb, nlh);
2955 return -1;
2956}
2957
2958static int nf_tables_setelem_notify(const struct nft_ctx *ctx,
2959 const struct nft_set *set,
2960 const struct nft_set_elem *elem,
2961 int event, u16 flags)
2962{
Pablo Neira Ayuso128ad332014-05-09 17:14:24 +02002963 struct net *net = ctx->net;
2964 u32 portid = ctx->portid;
Arturo Borrerod60ce622014-04-01 14:06:07 +02002965 struct sk_buff *skb;
2966 int err;
2967
Pablo Neira Ayuso128ad332014-05-09 17:14:24 +02002968 if (!ctx->report && !nfnetlink_has_listeners(net, NFNLGRP_NFTABLES))
Arturo Borrerod60ce622014-04-01 14:06:07 +02002969 return 0;
2970
2971 err = -ENOBUFS;
2972 skb = nlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL);
2973 if (skb == NULL)
2974 goto err;
2975
2976 err = nf_tables_fill_setelem_info(skb, ctx, 0, portid, event, flags,
2977 set, elem);
2978 if (err < 0) {
2979 kfree_skb(skb);
2980 goto err;
2981 }
2982
Pablo Neira Ayuso128ad332014-05-09 17:14:24 +02002983 err = nfnetlink_send(skb, net, portid, NFNLGRP_NFTABLES, ctx->report,
Arturo Borrerod60ce622014-04-01 14:06:07 +02002984 GFP_KERNEL);
2985err:
2986 if (err < 0)
2987 nfnetlink_set_err(net, portid, NFNLGRP_NFTABLES, err);
2988 return err;
2989}
2990
Pablo Neira Ayuso60319eb2014-04-04 03:36:42 +02002991static struct nft_trans *nft_trans_elem_alloc(struct nft_ctx *ctx,
2992 int msg_type,
2993 struct nft_set *set)
2994{
2995 struct nft_trans *trans;
2996
2997 trans = nft_trans_alloc(ctx, msg_type, sizeof(struct nft_trans_elem));
2998 if (trans == NULL)
2999 return NULL;
3000
3001 nft_trans_elem_set(trans) = set;
3002 return trans;
3003}
3004
3005static int nft_add_set_elem(struct nft_ctx *ctx, struct nft_set *set,
Patrick McHardy20a69342013-10-11 12:06:22 +02003006 const struct nlattr *attr)
3007{
3008 struct nlattr *nla[NFTA_SET_ELEM_MAX + 1];
3009 struct nft_data_desc d1, d2;
3010 struct nft_set_elem elem;
3011 struct nft_set_binding *binding;
3012 enum nft_registers dreg;
Pablo Neira Ayuso60319eb2014-04-04 03:36:42 +02003013 struct nft_trans *trans;
Patrick McHardy20a69342013-10-11 12:06:22 +02003014 int err;
3015
Patrick McHardyc50b9602014-03-28 10:19:47 +00003016 if (set->size && set->nelems == set->size)
3017 return -ENFILE;
3018
Patrick McHardy20a69342013-10-11 12:06:22 +02003019 err = nla_parse_nested(nla, NFTA_SET_ELEM_MAX, attr,
3020 nft_set_elem_policy);
3021 if (err < 0)
3022 return err;
3023
3024 if (nla[NFTA_SET_ELEM_KEY] == NULL)
3025 return -EINVAL;
3026
3027 elem.flags = 0;
3028 if (nla[NFTA_SET_ELEM_FLAGS] != NULL) {
3029 elem.flags = ntohl(nla_get_be32(nla[NFTA_SET_ELEM_FLAGS]));
3030 if (elem.flags & ~NFT_SET_ELEM_INTERVAL_END)
3031 return -EINVAL;
3032 }
3033
3034 if (set->flags & NFT_SET_MAP) {
3035 if (nla[NFTA_SET_ELEM_DATA] == NULL &&
3036 !(elem.flags & NFT_SET_ELEM_INTERVAL_END))
3037 return -EINVAL;
Pablo Neira Ayusobd7fc642014-02-07 12:53:07 +01003038 if (nla[NFTA_SET_ELEM_DATA] != NULL &&
3039 elem.flags & NFT_SET_ELEM_INTERVAL_END)
3040 return -EINVAL;
Patrick McHardy20a69342013-10-11 12:06:22 +02003041 } else {
3042 if (nla[NFTA_SET_ELEM_DATA] != NULL)
3043 return -EINVAL;
3044 }
3045
3046 err = nft_data_init(ctx, &elem.key, &d1, nla[NFTA_SET_ELEM_KEY]);
3047 if (err < 0)
3048 goto err1;
3049 err = -EINVAL;
3050 if (d1.type != NFT_DATA_VALUE || d1.len != set->klen)
3051 goto err2;
3052
3053 err = -EEXIST;
3054 if (set->ops->get(set, &elem) == 0)
3055 goto err2;
3056
3057 if (nla[NFTA_SET_ELEM_DATA] != NULL) {
3058 err = nft_data_init(ctx, &elem.data, &d2, nla[NFTA_SET_ELEM_DATA]);
3059 if (err < 0)
3060 goto err2;
3061
3062 err = -EINVAL;
3063 if (set->dtype != NFT_DATA_VERDICT && d2.len != set->dlen)
3064 goto err3;
3065
3066 dreg = nft_type_to_reg(set->dtype);
3067 list_for_each_entry(binding, &set->bindings, list) {
3068 struct nft_ctx bind_ctx = {
3069 .afi = ctx->afi,
3070 .table = ctx->table,
Pablo Neira Ayuso7c95f6d2014-04-04 01:22:45 +02003071 .chain = (struct nft_chain *)binding->chain,
Patrick McHardy20a69342013-10-11 12:06:22 +02003072 };
3073
3074 err = nft_validate_data_load(&bind_ctx, dreg,
3075 &elem.data, d2.type);
3076 if (err < 0)
3077 goto err3;
3078 }
3079 }
3080
Pablo Neira Ayuso60319eb2014-04-04 03:36:42 +02003081 trans = nft_trans_elem_alloc(ctx, NFT_MSG_NEWSETELEM, set);
3082 if (trans == NULL)
Patrick McHardy20a69342013-10-11 12:06:22 +02003083 goto err3;
3084
Patrick McHardy20a69342013-10-11 12:06:22 +02003085 err = set->ops->insert(set, &elem);
3086 if (err < 0)
Pablo Neira Ayuso60319eb2014-04-04 03:36:42 +02003087 goto err4;
Patrick McHardy20a69342013-10-11 12:06:22 +02003088
Pablo Neira Ayuso60319eb2014-04-04 03:36:42 +02003089 nft_trans_elem(trans) = elem;
Pablo Neira Ayuso46bbafc2014-05-22 12:36:03 +02003090 list_add_tail(&trans->list, &ctx->net->nft.commit_list);
Patrick McHardy20a69342013-10-11 12:06:22 +02003091 return 0;
3092
Pablo Neira Ayuso60319eb2014-04-04 03:36:42 +02003093err4:
3094 kfree(trans);
Patrick McHardy20a69342013-10-11 12:06:22 +02003095err3:
3096 if (nla[NFTA_SET_ELEM_DATA] != NULL)
3097 nft_data_uninit(&elem.data, d2.type);
3098err2:
3099 nft_data_uninit(&elem.key, d1.type);
3100err1:
3101 return err;
3102}
3103
3104static int nf_tables_newsetelem(struct sock *nlsk, struct sk_buff *skb,
3105 const struct nlmsghdr *nlh,
3106 const struct nlattr * const nla[])
3107{
Pablo Neira Ayuso958bee12014-04-03 11:48:44 +02003108 struct net *net = sock_net(skb->sk);
Patrick McHardy20a69342013-10-11 12:06:22 +02003109 const struct nlattr *attr;
3110 struct nft_set *set;
3111 struct nft_ctx ctx;
Pablo Neira Ayuso60319eb2014-04-04 03:36:42 +02003112 int rem, err = 0;
Patrick McHardy20a69342013-10-11 12:06:22 +02003113
Pablo Neira Ayuso55dd6f92014-04-03 11:53:37 +02003114 err = nft_ctx_init_from_elemattr(&ctx, skb, nlh, nla, true);
Patrick McHardy20a69342013-10-11 12:06:22 +02003115 if (err < 0)
3116 return err;
3117
3118 set = nf_tables_set_lookup(ctx.table, nla[NFTA_SET_ELEM_LIST_SET]);
Pablo Neira Ayuso958bee12014-04-03 11:48:44 +02003119 if (IS_ERR(set)) {
3120 if (nla[NFTA_SET_ELEM_LIST_SET_ID]) {
3121 set = nf_tables_set_lookup_byid(net,
3122 nla[NFTA_SET_ELEM_LIST_SET_ID]);
3123 }
3124 if (IS_ERR(set))
3125 return PTR_ERR(set);
3126 }
3127
Patrick McHardy20a69342013-10-11 12:06:22 +02003128 if (!list_empty(&set->bindings) && set->flags & NFT_SET_CONSTANT)
3129 return -EBUSY;
3130
3131 nla_for_each_nested(attr, nla[NFTA_SET_ELEM_LIST_ELEMENTS], rem) {
3132 err = nft_add_set_elem(&ctx, set, attr);
3133 if (err < 0)
Pablo Neira Ayuso60319eb2014-04-04 03:36:42 +02003134 break;
Pablo Neira Ayuso4fefee52014-05-23 12:39:26 +02003135
3136 set->nelems++;
Patrick McHardy20a69342013-10-11 12:06:22 +02003137 }
Pablo Neira Ayuso60319eb2014-04-04 03:36:42 +02003138 return err;
Patrick McHardy20a69342013-10-11 12:06:22 +02003139}
3140
Pablo Neira Ayuso60319eb2014-04-04 03:36:42 +02003141static int nft_del_setelem(struct nft_ctx *ctx, struct nft_set *set,
Patrick McHardy20a69342013-10-11 12:06:22 +02003142 const struct nlattr *attr)
3143{
3144 struct nlattr *nla[NFTA_SET_ELEM_MAX + 1];
3145 struct nft_data_desc desc;
3146 struct nft_set_elem elem;
Pablo Neira Ayuso60319eb2014-04-04 03:36:42 +02003147 struct nft_trans *trans;
Patrick McHardy20a69342013-10-11 12:06:22 +02003148 int err;
3149
3150 err = nla_parse_nested(nla, NFTA_SET_ELEM_MAX, attr,
3151 nft_set_elem_policy);
3152 if (err < 0)
3153 goto err1;
3154
3155 err = -EINVAL;
3156 if (nla[NFTA_SET_ELEM_KEY] == NULL)
3157 goto err1;
3158
3159 err = nft_data_init(ctx, &elem.key, &desc, nla[NFTA_SET_ELEM_KEY]);
3160 if (err < 0)
3161 goto err1;
3162
3163 err = -EINVAL;
3164 if (desc.type != NFT_DATA_VALUE || desc.len != set->klen)
3165 goto err2;
3166
3167 err = set->ops->get(set, &elem);
3168 if (err < 0)
3169 goto err2;
3170
Pablo Neira Ayuso60319eb2014-04-04 03:36:42 +02003171 trans = nft_trans_elem_alloc(ctx, NFT_MSG_DELSETELEM, set);
3172 if (trans == NULL)
3173 goto err2;
Patrick McHardy20a69342013-10-11 12:06:22 +02003174
Pablo Neira Ayuso60319eb2014-04-04 03:36:42 +02003175 nft_trans_elem(trans) = elem;
Pablo Neira Ayuso46bbafc2014-05-22 12:36:03 +02003176 list_add_tail(&trans->list, &ctx->net->nft.commit_list);
Patrick McHardy20a69342013-10-11 12:06:22 +02003177
3178 nft_data_uninit(&elem.key, NFT_DATA_VALUE);
3179 if (set->flags & NFT_SET_MAP)
3180 nft_data_uninit(&elem.data, set->dtype);
3181
3182err2:
3183 nft_data_uninit(&elem.key, desc.type);
3184err1:
3185 return err;
3186}
3187
3188static int nf_tables_delsetelem(struct sock *nlsk, struct sk_buff *skb,
3189 const struct nlmsghdr *nlh,
3190 const struct nlattr * const nla[])
3191{
3192 const struct nlattr *attr;
3193 struct nft_set *set;
3194 struct nft_ctx ctx;
Pablo Neira Ayuso60319eb2014-04-04 03:36:42 +02003195 int rem, err = 0;
Patrick McHardy20a69342013-10-11 12:06:22 +02003196
Pablo Neira Ayuso55dd6f92014-04-03 11:53:37 +02003197 err = nft_ctx_init_from_elemattr(&ctx, skb, nlh, nla, false);
Patrick McHardy20a69342013-10-11 12:06:22 +02003198 if (err < 0)
3199 return err;
3200
3201 set = nf_tables_set_lookup(ctx.table, nla[NFTA_SET_ELEM_LIST_SET]);
3202 if (IS_ERR(set))
3203 return PTR_ERR(set);
3204 if (!list_empty(&set->bindings) && set->flags & NFT_SET_CONSTANT)
3205 return -EBUSY;
3206
3207 nla_for_each_nested(attr, nla[NFTA_SET_ELEM_LIST_ELEMENTS], rem) {
3208 err = nft_del_setelem(&ctx, set, attr);
3209 if (err < 0)
Pablo Neira Ayuso60319eb2014-04-04 03:36:42 +02003210 break;
Pablo Neira Ayuso4fefee52014-05-23 12:39:26 +02003211
3212 set->nelems--;
Patrick McHardy20a69342013-10-11 12:06:22 +02003213 }
Pablo Neira Ayuso60319eb2014-04-04 03:36:42 +02003214 return err;
Patrick McHardy20a69342013-10-11 12:06:22 +02003215}
3216
Patrick McHardy96518512013-10-14 11:00:02 +02003217static const struct nfnl_callback nf_tables_cb[NFT_MSG_MAX] = {
3218 [NFT_MSG_NEWTABLE] = {
Pablo Neira Ayuso55dd6f92014-04-03 11:53:37 +02003219 .call_batch = nf_tables_newtable,
Patrick McHardy96518512013-10-14 11:00:02 +02003220 .attr_count = NFTA_TABLE_MAX,
3221 .policy = nft_table_policy,
3222 },
3223 [NFT_MSG_GETTABLE] = {
3224 .call = nf_tables_gettable,
3225 .attr_count = NFTA_TABLE_MAX,
3226 .policy = nft_table_policy,
3227 },
3228 [NFT_MSG_DELTABLE] = {
Pablo Neira Ayuso55dd6f92014-04-03 11:53:37 +02003229 .call_batch = nf_tables_deltable,
Patrick McHardy96518512013-10-14 11:00:02 +02003230 .attr_count = NFTA_TABLE_MAX,
3231 .policy = nft_table_policy,
3232 },
3233 [NFT_MSG_NEWCHAIN] = {
Pablo Neira Ayuso91c7b382014-04-09 11:58:08 +02003234 .call_batch = nf_tables_newchain,
Patrick McHardy96518512013-10-14 11:00:02 +02003235 .attr_count = NFTA_CHAIN_MAX,
3236 .policy = nft_chain_policy,
3237 },
3238 [NFT_MSG_GETCHAIN] = {
3239 .call = nf_tables_getchain,
3240 .attr_count = NFTA_CHAIN_MAX,
3241 .policy = nft_chain_policy,
3242 },
3243 [NFT_MSG_DELCHAIN] = {
Pablo Neira Ayuso91c7b382014-04-09 11:58:08 +02003244 .call_batch = nf_tables_delchain,
Patrick McHardy96518512013-10-14 11:00:02 +02003245 .attr_count = NFTA_CHAIN_MAX,
3246 .policy = nft_chain_policy,
3247 },
3248 [NFT_MSG_NEWRULE] = {
Pablo Neira Ayuso0628b122013-10-14 11:05:33 +02003249 .call_batch = nf_tables_newrule,
Patrick McHardy96518512013-10-14 11:00:02 +02003250 .attr_count = NFTA_RULE_MAX,
3251 .policy = nft_rule_policy,
3252 },
3253 [NFT_MSG_GETRULE] = {
3254 .call = nf_tables_getrule,
3255 .attr_count = NFTA_RULE_MAX,
3256 .policy = nft_rule_policy,
3257 },
3258 [NFT_MSG_DELRULE] = {
Pablo Neira Ayuso0628b122013-10-14 11:05:33 +02003259 .call_batch = nf_tables_delrule,
Patrick McHardy96518512013-10-14 11:00:02 +02003260 .attr_count = NFTA_RULE_MAX,
3261 .policy = nft_rule_policy,
3262 },
Patrick McHardy20a69342013-10-11 12:06:22 +02003263 [NFT_MSG_NEWSET] = {
Pablo Neira Ayuso958bee12014-04-03 11:48:44 +02003264 .call_batch = nf_tables_newset,
Patrick McHardy20a69342013-10-11 12:06:22 +02003265 .attr_count = NFTA_SET_MAX,
3266 .policy = nft_set_policy,
3267 },
3268 [NFT_MSG_GETSET] = {
3269 .call = nf_tables_getset,
3270 .attr_count = NFTA_SET_MAX,
3271 .policy = nft_set_policy,
3272 },
3273 [NFT_MSG_DELSET] = {
Pablo Neira Ayuso958bee12014-04-03 11:48:44 +02003274 .call_batch = nf_tables_delset,
Patrick McHardy20a69342013-10-11 12:06:22 +02003275 .attr_count = NFTA_SET_MAX,
3276 .policy = nft_set_policy,
3277 },
3278 [NFT_MSG_NEWSETELEM] = {
Pablo Neira Ayuso958bee12014-04-03 11:48:44 +02003279 .call_batch = nf_tables_newsetelem,
Patrick McHardy20a69342013-10-11 12:06:22 +02003280 .attr_count = NFTA_SET_ELEM_LIST_MAX,
3281 .policy = nft_set_elem_list_policy,
3282 },
3283 [NFT_MSG_GETSETELEM] = {
3284 .call = nf_tables_getsetelem,
3285 .attr_count = NFTA_SET_ELEM_LIST_MAX,
3286 .policy = nft_set_elem_list_policy,
3287 },
3288 [NFT_MSG_DELSETELEM] = {
Pablo Neira Ayuso958bee12014-04-03 11:48:44 +02003289 .call_batch = nf_tables_delsetelem,
Patrick McHardy20a69342013-10-11 12:06:22 +02003290 .attr_count = NFTA_SET_ELEM_LIST_MAX,
3291 .policy = nft_set_elem_list_policy,
3292 },
Patrick McHardy96518512013-10-14 11:00:02 +02003293};
3294
Pablo Neira Ayuso91c7b382014-04-09 11:58:08 +02003295static void nft_chain_commit_update(struct nft_trans *trans)
3296{
3297 struct nft_base_chain *basechain;
3298
3299 if (nft_trans_chain_name(trans)[0])
3300 strcpy(trans->ctx.chain->name, nft_trans_chain_name(trans));
3301
3302 if (!(trans->ctx.chain->flags & NFT_BASE_CHAIN))
3303 return;
3304
3305 basechain = nft_base_chain(trans->ctx.chain);
3306 nft_chain_stats_replace(basechain, nft_trans_chain_stats(trans));
3307
3308 switch (nft_trans_chain_policy(trans)) {
3309 case NF_DROP:
3310 case NF_ACCEPT:
3311 basechain->policy = nft_trans_chain_policy(trans);
3312 break;
3313 }
3314}
3315
Pablo Neira Ayusoc7c32e72014-04-10 00:31:10 +02003316/* Schedule objects for release via rcu to make sure no packets are accesing
3317 * removed rules.
3318 */
3319static void nf_tables_commit_release_rcu(struct rcu_head *rt)
3320{
3321 struct nft_trans *trans = container_of(rt, struct nft_trans, rcu_head);
3322
3323 switch (trans->msg_type) {
3324 case NFT_MSG_DELTABLE:
3325 nf_tables_table_destroy(&trans->ctx);
3326 break;
3327 case NFT_MSG_DELCHAIN:
3328 nf_tables_chain_destroy(trans->ctx.chain);
3329 break;
3330 case NFT_MSG_DELRULE:
3331 nf_tables_rule_destroy(&trans->ctx, nft_trans_rule(trans));
3332 break;
3333 case NFT_MSG_DELSET:
3334 nft_set_destroy(nft_trans_set(trans));
3335 break;
3336 }
3337 kfree(trans);
3338}
3339
Pablo Neira Ayuso37082f92014-04-03 11:56:37 +02003340static int nf_tables_commit(struct sk_buff *skb)
3341{
3342 struct net *net = sock_net(skb->sk);
3343 struct nft_trans *trans, *next;
Pablo Neira Ayuso60319eb2014-04-04 03:36:42 +02003344 struct nft_set *set;
Pablo Neira Ayuso37082f92014-04-03 11:56:37 +02003345
3346 /* Bump generation counter, invalidate any dump in progress */
3347 net->nft.genctr++;
3348
3349 /* A new generation has just started */
3350 net->nft.gencursor = gencursor_next(net);
3351
3352 /* Make sure all packets have left the previous generation before
3353 * purging old rules.
3354 */
3355 synchronize_rcu();
3356
3357 list_for_each_entry_safe(trans, next, &net->nft.commit_list, list) {
Pablo Neira Ayusob380e5c2014-04-04 01:38:51 +02003358 switch (trans->msg_type) {
Pablo Neira Ayuso55dd6f92014-04-03 11:53:37 +02003359 case NFT_MSG_NEWTABLE:
3360 if (nft_trans_table_update(trans)) {
3361 if (!nft_trans_table_enable(trans)) {
3362 nf_tables_table_disable(trans->ctx.afi,
3363 trans->ctx.table);
3364 trans->ctx.table->flags |= NFT_TABLE_F_DORMANT;
3365 }
3366 } else {
3367 trans->ctx.table->flags &= ~NFT_TABLE_INACTIVE;
3368 }
Pablo Neira Ayuso35151d82014-05-05 17:12:40 +02003369 nf_tables_table_notify(&trans->ctx, NFT_MSG_NEWTABLE);
Pablo Neira Ayuso55dd6f92014-04-03 11:53:37 +02003370 nft_trans_destroy(trans);
3371 break;
3372 case NFT_MSG_DELTABLE:
Pablo Neira Ayuso35151d82014-05-05 17:12:40 +02003373 nf_tables_table_notify(&trans->ctx, NFT_MSG_DELTABLE);
Pablo Neira Ayuso55dd6f92014-04-03 11:53:37 +02003374 break;
Pablo Neira Ayuso91c7b382014-04-09 11:58:08 +02003375 case NFT_MSG_NEWCHAIN:
3376 if (nft_trans_chain_update(trans))
3377 nft_chain_commit_update(trans);
Pablo Neira Ayuso4fefee52014-05-23 12:39:26 +02003378 else
Pablo Neira Ayuso91c7b382014-04-09 11:58:08 +02003379 trans->ctx.chain->flags &= ~NFT_CHAIN_INACTIVE;
Pablo Neira Ayuso4fefee52014-05-23 12:39:26 +02003380
Pablo Neira Ayuso35151d82014-05-05 17:12:40 +02003381 nf_tables_chain_notify(&trans->ctx, NFT_MSG_NEWCHAIN);
Pablo Neira Ayuso91c7b382014-04-09 11:58:08 +02003382 nft_trans_destroy(trans);
3383 break;
3384 case NFT_MSG_DELCHAIN:
Pablo Neira Ayuso35151d82014-05-05 17:12:40 +02003385 nf_tables_chain_notify(&trans->ctx, NFT_MSG_DELCHAIN);
Pablo Neira Ayuso91c7b382014-04-09 11:58:08 +02003386 if (!(trans->ctx.table->flags & NFT_TABLE_F_DORMANT) &&
3387 trans->ctx.chain->flags & NFT_BASE_CHAIN) {
3388 nf_unregister_hooks(nft_base_chain(trans->ctx.chain)->ops,
3389 trans->ctx.afi->nops);
3390 }
3391 break;
Pablo Neira Ayusob380e5c2014-04-04 01:38:51 +02003392 case NFT_MSG_NEWRULE:
3393 nft_rule_clear(trans->ctx.net, nft_trans_rule(trans));
Pablo Neira Ayuso35151d82014-05-05 17:12:40 +02003394 nf_tables_rule_notify(&trans->ctx,
Pablo Neira Ayuso37082f92014-04-03 11:56:37 +02003395 nft_trans_rule(trans),
Pablo Neira Ayuso35151d82014-05-05 17:12:40 +02003396 NFT_MSG_NEWRULE);
Pablo Neira Ayuso37082f92014-04-03 11:56:37 +02003397 nft_trans_destroy(trans);
Pablo Neira Ayusob380e5c2014-04-04 01:38:51 +02003398 break;
3399 case NFT_MSG_DELRULE:
3400 list_del_rcu(&nft_trans_rule(trans)->list);
Pablo Neira Ayuso35151d82014-05-05 17:12:40 +02003401 nf_tables_rule_notify(&trans->ctx,
3402 nft_trans_rule(trans),
3403 NFT_MSG_DELRULE);
Pablo Neira Ayusob380e5c2014-04-04 01:38:51 +02003404 break;
Pablo Neira Ayuso958bee12014-04-03 11:48:44 +02003405 case NFT_MSG_NEWSET:
3406 nft_trans_set(trans)->flags &= ~NFT_SET_INACTIVE;
Pablo Neira Ayuso4fefee52014-05-23 12:39:26 +02003407 /* This avoids hitting -EBUSY when deleting the table
3408 * from the transaction.
3409 */
3410 if (nft_trans_set(trans)->flags & NFT_SET_ANONYMOUS &&
3411 !list_empty(&nft_trans_set(trans)->bindings))
3412 trans->ctx.table->use--;
3413
Pablo Neira Ayuso958bee12014-04-03 11:48:44 +02003414 nf_tables_set_notify(&trans->ctx, nft_trans_set(trans),
Pablo Neira Ayuso31f84412014-05-29 10:29:58 +02003415 NFT_MSG_NEWSET, GFP_KERNEL);
Pablo Neira Ayuso958bee12014-04-03 11:48:44 +02003416 nft_trans_destroy(trans);
3417 break;
3418 case NFT_MSG_DELSET:
3419 nf_tables_set_notify(&trans->ctx, nft_trans_set(trans),
Pablo Neira Ayuso31f84412014-05-29 10:29:58 +02003420 NFT_MSG_DELSET, GFP_KERNEL);
Pablo Neira Ayuso958bee12014-04-03 11:48:44 +02003421 break;
Pablo Neira Ayuso60319eb2014-04-04 03:36:42 +02003422 case NFT_MSG_NEWSETELEM:
Pablo Neira Ayuso60319eb2014-04-04 03:36:42 +02003423 nf_tables_setelem_notify(&trans->ctx,
3424 nft_trans_elem_set(trans),
3425 &nft_trans_elem(trans),
3426 NFT_MSG_NEWSETELEM, 0);
3427 nft_trans_destroy(trans);
3428 break;
3429 case NFT_MSG_DELSETELEM:
Pablo Neira Ayuso60319eb2014-04-04 03:36:42 +02003430 nf_tables_setelem_notify(&trans->ctx,
3431 nft_trans_elem_set(trans),
3432 &nft_trans_elem(trans),
3433 NFT_MSG_DELSETELEM, 0);
3434 set = nft_trans_elem_set(trans);
3435 set->ops->get(set, &nft_trans_elem(trans));
3436 set->ops->remove(set, &nft_trans_elem(trans));
3437 nft_trans_destroy(trans);
3438 break;
Pablo Neira Ayuso37082f92014-04-03 11:56:37 +02003439 }
Pablo Neira Ayuso37082f92014-04-03 11:56:37 +02003440 }
3441
Pablo Neira Ayuso37082f92014-04-03 11:56:37 +02003442 list_for_each_entry_safe(trans, next, &net->nft.commit_list, list) {
Pablo Neira Ayusoc7c32e72014-04-10 00:31:10 +02003443 list_del(&trans->list);
3444 trans->ctx.nla = NULL;
3445 call_rcu(&trans->rcu_head, nf_tables_commit_release_rcu);
Pablo Neira Ayuso37082f92014-04-03 11:56:37 +02003446 }
3447
3448 return 0;
3449}
3450
Pablo Neira Ayusoc7c32e72014-04-10 00:31:10 +02003451/* Schedule objects for release via rcu to make sure no packets are accesing
3452 * aborted rules.
3453 */
3454static void nf_tables_abort_release_rcu(struct rcu_head *rt)
3455{
3456 struct nft_trans *trans = container_of(rt, struct nft_trans, rcu_head);
3457
3458 switch (trans->msg_type) {
3459 case NFT_MSG_NEWTABLE:
3460 nf_tables_table_destroy(&trans->ctx);
3461 break;
3462 case NFT_MSG_NEWCHAIN:
3463 nf_tables_chain_destroy(trans->ctx.chain);
3464 break;
3465 case NFT_MSG_NEWRULE:
3466 nf_tables_rule_destroy(&trans->ctx, nft_trans_rule(trans));
3467 break;
3468 case NFT_MSG_NEWSET:
3469 nft_set_destroy(nft_trans_set(trans));
3470 break;
3471 }
3472 kfree(trans);
3473}
3474
Pablo Neira Ayuso37082f92014-04-03 11:56:37 +02003475static int nf_tables_abort(struct sk_buff *skb)
3476{
3477 struct net *net = sock_net(skb->sk);
3478 struct nft_trans *trans, *next;
Pablo Neira Ayuso60319eb2014-04-04 03:36:42 +02003479 struct nft_set *set;
Pablo Neira Ayuso37082f92014-04-03 11:56:37 +02003480
3481 list_for_each_entry_safe(trans, next, &net->nft.commit_list, list) {
Pablo Neira Ayusob380e5c2014-04-04 01:38:51 +02003482 switch (trans->msg_type) {
Pablo Neira Ayuso55dd6f92014-04-03 11:53:37 +02003483 case NFT_MSG_NEWTABLE:
3484 if (nft_trans_table_update(trans)) {
3485 if (nft_trans_table_enable(trans)) {
3486 nf_tables_table_disable(trans->ctx.afi,
3487 trans->ctx.table);
3488 trans->ctx.table->flags |= NFT_TABLE_F_DORMANT;
3489 }
3490 nft_trans_destroy(trans);
3491 } else {
3492 list_del(&trans->ctx.table->list);
3493 }
3494 break;
3495 case NFT_MSG_DELTABLE:
3496 list_add_tail(&trans->ctx.table->list,
3497 &trans->ctx.afi->tables);
3498 nft_trans_destroy(trans);
3499 break;
Pablo Neira Ayuso91c7b382014-04-09 11:58:08 +02003500 case NFT_MSG_NEWCHAIN:
3501 if (nft_trans_chain_update(trans)) {
3502 if (nft_trans_chain_stats(trans))
3503 free_percpu(nft_trans_chain_stats(trans));
3504
3505 nft_trans_destroy(trans);
3506 } else {
Pablo Neira Ayuso4fefee52014-05-23 12:39:26 +02003507 trans->ctx.table->use--;
Pablo Neira Ayuso91c7b382014-04-09 11:58:08 +02003508 list_del(&trans->ctx.chain->list);
3509 if (!(trans->ctx.table->flags & NFT_TABLE_F_DORMANT) &&
3510 trans->ctx.chain->flags & NFT_BASE_CHAIN) {
3511 nf_unregister_hooks(nft_base_chain(trans->ctx.chain)->ops,
3512 trans->ctx.afi->nops);
3513 }
3514 }
3515 break;
3516 case NFT_MSG_DELCHAIN:
Pablo Neira Ayuso4fefee52014-05-23 12:39:26 +02003517 trans->ctx.table->use++;
Pablo Neira Ayuso91c7b382014-04-09 11:58:08 +02003518 list_add_tail(&trans->ctx.chain->list,
3519 &trans->ctx.table->chains);
3520 nft_trans_destroy(trans);
3521 break;
Pablo Neira Ayusob380e5c2014-04-04 01:38:51 +02003522 case NFT_MSG_NEWRULE:
Pablo Neira Ayuso4fefee52014-05-23 12:39:26 +02003523 trans->ctx.chain->use--;
Pablo Neira Ayusob380e5c2014-04-04 01:38:51 +02003524 list_del_rcu(&nft_trans_rule(trans)->list);
3525 break;
3526 case NFT_MSG_DELRULE:
Pablo Neira Ayuso4fefee52014-05-23 12:39:26 +02003527 trans->ctx.chain->use++;
Pablo Neira Ayusob380e5c2014-04-04 01:38:51 +02003528 nft_rule_clear(trans->ctx.net, nft_trans_rule(trans));
Pablo Neira Ayuso37082f92014-04-03 11:56:37 +02003529 nft_trans_destroy(trans);
Pablo Neira Ayusob380e5c2014-04-04 01:38:51 +02003530 break;
Pablo Neira Ayuso958bee12014-04-03 11:48:44 +02003531 case NFT_MSG_NEWSET:
Pablo Neira Ayuso4fefee52014-05-23 12:39:26 +02003532 trans->ctx.table->use--;
Pablo Neira Ayuso958bee12014-04-03 11:48:44 +02003533 list_del(&nft_trans_set(trans)->list);
3534 break;
3535 case NFT_MSG_DELSET:
Pablo Neira Ayuso4fefee52014-05-23 12:39:26 +02003536 trans->ctx.table->use++;
Pablo Neira Ayuso958bee12014-04-03 11:48:44 +02003537 list_add_tail(&nft_trans_set(trans)->list,
3538 &trans->ctx.table->sets);
3539 nft_trans_destroy(trans);
3540 break;
Pablo Neira Ayuso60319eb2014-04-04 03:36:42 +02003541 case NFT_MSG_NEWSETELEM:
Pablo Neira Ayuso4fefee52014-05-23 12:39:26 +02003542 nft_trans_elem_set(trans)->nelems--;
Pablo Neira Ayuso60319eb2014-04-04 03:36:42 +02003543 set = nft_trans_elem_set(trans);
3544 set->ops->get(set, &nft_trans_elem(trans));
3545 set->ops->remove(set, &nft_trans_elem(trans));
3546 nft_trans_destroy(trans);
3547 break;
3548 case NFT_MSG_DELSETELEM:
Pablo Neira Ayuso4fefee52014-05-23 12:39:26 +02003549 nft_trans_elem_set(trans)->nelems++;
Pablo Neira Ayuso60319eb2014-04-04 03:36:42 +02003550 nft_trans_destroy(trans);
3551 break;
Pablo Neira Ayuso37082f92014-04-03 11:56:37 +02003552 }
Pablo Neira Ayuso37082f92014-04-03 11:56:37 +02003553 }
3554
Pablo Neira Ayusoa1cee072014-05-23 11:09:42 +02003555 list_for_each_entry_safe_reverse(trans, next,
3556 &net->nft.commit_list, list) {
Pablo Neira Ayusoc7c32e72014-04-10 00:31:10 +02003557 list_del(&trans->list);
3558 trans->ctx.nla = NULL;
3559 call_rcu(&trans->rcu_head, nf_tables_abort_release_rcu);
Pablo Neira Ayuso37082f92014-04-03 11:56:37 +02003560 }
3561
3562 return 0;
3563}
3564
Patrick McHardy96518512013-10-14 11:00:02 +02003565static const struct nfnetlink_subsystem nf_tables_subsys = {
3566 .name = "nf_tables",
3567 .subsys_id = NFNL_SUBSYS_NFTABLES,
3568 .cb_count = NFT_MSG_MAX,
3569 .cb = nf_tables_cb,
Pablo Neira Ayuso0628b122013-10-14 11:05:33 +02003570 .commit = nf_tables_commit,
3571 .abort = nf_tables_abort,
Patrick McHardy96518512013-10-14 11:00:02 +02003572};
3573
Patrick McHardy20a69342013-10-11 12:06:22 +02003574/*
3575 * Loop detection - walk through the ruleset beginning at the destination chain
3576 * of a new jump until either the source chain is reached (loop) or all
3577 * reachable chains have been traversed.
3578 *
3579 * The loop check is performed whenever a new jump verdict is added to an
3580 * expression or verdict map or a verdict map is bound to a new chain.
3581 */
3582
3583static int nf_tables_check_loops(const struct nft_ctx *ctx,
3584 const struct nft_chain *chain);
3585
3586static int nf_tables_loop_check_setelem(const struct nft_ctx *ctx,
3587 const struct nft_set *set,
3588 const struct nft_set_iter *iter,
3589 const struct nft_set_elem *elem)
3590{
Pablo Neira Ayuso62f9c8b2014-02-07 14:45:01 +01003591 if (elem->flags & NFT_SET_ELEM_INTERVAL_END)
3592 return 0;
3593
Patrick McHardy20a69342013-10-11 12:06:22 +02003594 switch (elem->data.verdict) {
3595 case NFT_JUMP:
3596 case NFT_GOTO:
3597 return nf_tables_check_loops(ctx, elem->data.chain);
3598 default:
3599 return 0;
3600 }
3601}
3602
3603static int nf_tables_check_loops(const struct nft_ctx *ctx,
3604 const struct nft_chain *chain)
3605{
3606 const struct nft_rule *rule;
3607 const struct nft_expr *expr, *last;
Patrick McHardy20a69342013-10-11 12:06:22 +02003608 const struct nft_set *set;
3609 struct nft_set_binding *binding;
3610 struct nft_set_iter iter;
Patrick McHardy20a69342013-10-11 12:06:22 +02003611
3612 if (ctx->chain == chain)
3613 return -ELOOP;
3614
3615 list_for_each_entry(rule, &chain->rules, list) {
3616 nft_rule_for_each_expr(expr, last, rule) {
Pablo Neira Ayuso0ca743a2013-10-14 00:06:06 +02003617 const struct nft_data *data = NULL;
3618 int err;
3619
3620 if (!expr->ops->validate)
Patrick McHardy20a69342013-10-11 12:06:22 +02003621 continue;
3622
Pablo Neira Ayuso0ca743a2013-10-14 00:06:06 +02003623 err = expr->ops->validate(ctx, expr, &data);
3624 if (err < 0)
3625 return err;
3626
Patrick McHardy20a69342013-10-11 12:06:22 +02003627 if (data == NULL)
Pablo Neira Ayuso0ca743a2013-10-14 00:06:06 +02003628 continue;
Patrick McHardy20a69342013-10-11 12:06:22 +02003629
3630 switch (data->verdict) {
3631 case NFT_JUMP:
3632 case NFT_GOTO:
3633 err = nf_tables_check_loops(ctx, data->chain);
3634 if (err < 0)
3635 return err;
3636 default:
3637 break;
3638 }
3639 }
3640 }
3641
3642 list_for_each_entry(set, &ctx->table->sets, list) {
3643 if (!(set->flags & NFT_SET_MAP) ||
3644 set->dtype != NFT_DATA_VERDICT)
3645 continue;
3646
3647 list_for_each_entry(binding, &set->bindings, list) {
3648 if (binding->chain != chain)
3649 continue;
3650
3651 iter.skip = 0;
3652 iter.count = 0;
3653 iter.err = 0;
3654 iter.fn = nf_tables_loop_check_setelem;
3655
3656 set->ops->walk(ctx, set, &iter);
3657 if (iter.err < 0)
3658 return iter.err;
3659 }
3660 }
3661
3662 return 0;
3663}
3664
Patrick McHardy96518512013-10-14 11:00:02 +02003665/**
3666 * nft_validate_input_register - validate an expressions' input register
3667 *
3668 * @reg: the register number
3669 *
3670 * Validate that the input register is one of the general purpose
3671 * registers.
3672 */
3673int nft_validate_input_register(enum nft_registers reg)
3674{
3675 if (reg <= NFT_REG_VERDICT)
3676 return -EINVAL;
3677 if (reg > NFT_REG_MAX)
3678 return -ERANGE;
3679 return 0;
3680}
3681EXPORT_SYMBOL_GPL(nft_validate_input_register);
3682
3683/**
3684 * nft_validate_output_register - validate an expressions' output register
3685 *
3686 * @reg: the register number
3687 *
3688 * Validate that the output register is one of the general purpose
3689 * registers or the verdict register.
3690 */
3691int nft_validate_output_register(enum nft_registers reg)
3692{
3693 if (reg < NFT_REG_VERDICT)
3694 return -EINVAL;
3695 if (reg > NFT_REG_MAX)
3696 return -ERANGE;
3697 return 0;
3698}
3699EXPORT_SYMBOL_GPL(nft_validate_output_register);
3700
3701/**
3702 * nft_validate_data_load - validate an expressions' data load
3703 *
3704 * @ctx: context of the expression performing the load
3705 * @reg: the destination register number
3706 * @data: the data to load
3707 * @type: the data type
3708 *
3709 * Validate that a data load uses the appropriate data type for
3710 * the destination register. A value of NULL for the data means
3711 * that its runtime gathered data, which is always of type
3712 * NFT_DATA_VALUE.
3713 */
3714int nft_validate_data_load(const struct nft_ctx *ctx, enum nft_registers reg,
3715 const struct nft_data *data,
3716 enum nft_data_types type)
3717{
Patrick McHardy20a69342013-10-11 12:06:22 +02003718 int err;
3719
Patrick McHardy96518512013-10-14 11:00:02 +02003720 switch (reg) {
3721 case NFT_REG_VERDICT:
3722 if (data == NULL || type != NFT_DATA_VERDICT)
3723 return -EINVAL;
Patrick McHardy20a69342013-10-11 12:06:22 +02003724
3725 if (data->verdict == NFT_GOTO || data->verdict == NFT_JUMP) {
3726 err = nf_tables_check_loops(ctx, data->chain);
3727 if (err < 0)
3728 return err;
3729
3730 if (ctx->chain->level + 1 > data->chain->level) {
3731 if (ctx->chain->level + 1 == NFT_JUMP_STACK_SIZE)
3732 return -EMLINK;
3733 data->chain->level = ctx->chain->level + 1;
3734 }
3735 }
3736
Patrick McHardy96518512013-10-14 11:00:02 +02003737 return 0;
3738 default:
3739 if (data != NULL && type != NFT_DATA_VALUE)
3740 return -EINVAL;
3741 return 0;
3742 }
3743}
3744EXPORT_SYMBOL_GPL(nft_validate_data_load);
3745
3746static const struct nla_policy nft_verdict_policy[NFTA_VERDICT_MAX + 1] = {
3747 [NFTA_VERDICT_CODE] = { .type = NLA_U32 },
3748 [NFTA_VERDICT_CHAIN] = { .type = NLA_STRING,
3749 .len = NFT_CHAIN_MAXNAMELEN - 1 },
3750};
3751
3752static int nft_verdict_init(const struct nft_ctx *ctx, struct nft_data *data,
3753 struct nft_data_desc *desc, const struct nlattr *nla)
3754{
3755 struct nlattr *tb[NFTA_VERDICT_MAX + 1];
3756 struct nft_chain *chain;
3757 int err;
3758
3759 err = nla_parse_nested(tb, NFTA_VERDICT_MAX, nla, nft_verdict_policy);
3760 if (err < 0)
3761 return err;
3762
3763 if (!tb[NFTA_VERDICT_CODE])
3764 return -EINVAL;
3765 data->verdict = ntohl(nla_get_be32(tb[NFTA_VERDICT_CODE]));
3766
3767 switch (data->verdict) {
Patrick McHardye0abdad2014-02-18 18:06:50 +00003768 default:
3769 switch (data->verdict & NF_VERDICT_MASK) {
3770 case NF_ACCEPT:
3771 case NF_DROP:
3772 case NF_QUEUE:
3773 break;
3774 default:
3775 return -EINVAL;
3776 }
3777 /* fall through */
Patrick McHardy96518512013-10-14 11:00:02 +02003778 case NFT_CONTINUE:
3779 case NFT_BREAK:
3780 case NFT_RETURN:
3781 desc->len = sizeof(data->verdict);
3782 break;
3783 case NFT_JUMP:
3784 case NFT_GOTO:
3785 if (!tb[NFTA_VERDICT_CHAIN])
3786 return -EINVAL;
3787 chain = nf_tables_chain_lookup(ctx->table,
3788 tb[NFTA_VERDICT_CHAIN]);
3789 if (IS_ERR(chain))
3790 return PTR_ERR(chain);
3791 if (chain->flags & NFT_BASE_CHAIN)
3792 return -EOPNOTSUPP;
3793
Patrick McHardy96518512013-10-14 11:00:02 +02003794 chain->use++;
3795 data->chain = chain;
3796 desc->len = sizeof(data);
3797 break;
Patrick McHardy96518512013-10-14 11:00:02 +02003798 }
3799
3800 desc->type = NFT_DATA_VERDICT;
3801 return 0;
3802}
3803
3804static void nft_verdict_uninit(const struct nft_data *data)
3805{
3806 switch (data->verdict) {
3807 case NFT_JUMP:
3808 case NFT_GOTO:
3809 data->chain->use--;
3810 break;
3811 }
3812}
3813
3814static int nft_verdict_dump(struct sk_buff *skb, const struct nft_data *data)
3815{
3816 struct nlattr *nest;
3817
3818 nest = nla_nest_start(skb, NFTA_DATA_VERDICT);
3819 if (!nest)
3820 goto nla_put_failure;
3821
3822 if (nla_put_be32(skb, NFTA_VERDICT_CODE, htonl(data->verdict)))
3823 goto nla_put_failure;
3824
3825 switch (data->verdict) {
3826 case NFT_JUMP:
3827 case NFT_GOTO:
3828 if (nla_put_string(skb, NFTA_VERDICT_CHAIN, data->chain->name))
3829 goto nla_put_failure;
3830 }
3831 nla_nest_end(skb, nest);
3832 return 0;
3833
3834nla_put_failure:
3835 return -1;
3836}
3837
3838static int nft_value_init(const struct nft_ctx *ctx, struct nft_data *data,
3839 struct nft_data_desc *desc, const struct nlattr *nla)
3840{
3841 unsigned int len;
3842
3843 len = nla_len(nla);
3844 if (len == 0)
3845 return -EINVAL;
3846 if (len > sizeof(data->data))
3847 return -EOVERFLOW;
3848
3849 nla_memcpy(data->data, nla, sizeof(data->data));
3850 desc->type = NFT_DATA_VALUE;
3851 desc->len = len;
3852 return 0;
3853}
3854
3855static int nft_value_dump(struct sk_buff *skb, const struct nft_data *data,
3856 unsigned int len)
3857{
3858 return nla_put(skb, NFTA_DATA_VALUE, len, data->data);
3859}
3860
3861static const struct nla_policy nft_data_policy[NFTA_DATA_MAX + 1] = {
3862 [NFTA_DATA_VALUE] = { .type = NLA_BINARY,
3863 .len = FIELD_SIZEOF(struct nft_data, data) },
3864 [NFTA_DATA_VERDICT] = { .type = NLA_NESTED },
3865};
3866
3867/**
3868 * nft_data_init - parse nf_tables data netlink attributes
3869 *
3870 * @ctx: context of the expression using the data
3871 * @data: destination struct nft_data
3872 * @desc: data description
3873 * @nla: netlink attribute containing data
3874 *
3875 * Parse the netlink data attributes and initialize a struct nft_data.
3876 * The type and length of data are returned in the data description.
3877 *
3878 * The caller can indicate that it only wants to accept data of type
3879 * NFT_DATA_VALUE by passing NULL for the ctx argument.
3880 */
3881int nft_data_init(const struct nft_ctx *ctx, struct nft_data *data,
3882 struct nft_data_desc *desc, const struct nlattr *nla)
3883{
3884 struct nlattr *tb[NFTA_DATA_MAX + 1];
3885 int err;
3886
3887 err = nla_parse_nested(tb, NFTA_DATA_MAX, nla, nft_data_policy);
3888 if (err < 0)
3889 return err;
3890
3891 if (tb[NFTA_DATA_VALUE])
3892 return nft_value_init(ctx, data, desc, tb[NFTA_DATA_VALUE]);
3893 if (tb[NFTA_DATA_VERDICT] && ctx != NULL)
3894 return nft_verdict_init(ctx, data, desc, tb[NFTA_DATA_VERDICT]);
3895 return -EINVAL;
3896}
3897EXPORT_SYMBOL_GPL(nft_data_init);
3898
3899/**
3900 * nft_data_uninit - release a nft_data item
3901 *
3902 * @data: struct nft_data to release
3903 * @type: type of data
3904 *
3905 * Release a nft_data item. NFT_DATA_VALUE types can be silently discarded,
3906 * all others need to be released by calling this function.
3907 */
3908void nft_data_uninit(const struct nft_data *data, enum nft_data_types type)
3909{
3910 switch (type) {
3911 case NFT_DATA_VALUE:
3912 return;
3913 case NFT_DATA_VERDICT:
3914 return nft_verdict_uninit(data);
3915 default:
3916 WARN_ON(1);
3917 }
3918}
3919EXPORT_SYMBOL_GPL(nft_data_uninit);
3920
3921int nft_data_dump(struct sk_buff *skb, int attr, const struct nft_data *data,
3922 enum nft_data_types type, unsigned int len)
3923{
3924 struct nlattr *nest;
3925 int err;
3926
3927 nest = nla_nest_start(skb, attr);
3928 if (nest == NULL)
3929 return -1;
3930
3931 switch (type) {
3932 case NFT_DATA_VALUE:
3933 err = nft_value_dump(skb, data, len);
3934 break;
3935 case NFT_DATA_VERDICT:
3936 err = nft_verdict_dump(skb, data);
3937 break;
3938 default:
3939 err = -EINVAL;
3940 WARN_ON(1);
3941 }
3942
3943 nla_nest_end(skb, nest);
3944 return err;
3945}
3946EXPORT_SYMBOL_GPL(nft_data_dump);
3947
Pablo Neira Ayuso99633ab2013-10-10 23:28:33 +02003948static int nf_tables_init_net(struct net *net)
3949{
3950 INIT_LIST_HEAD(&net->nft.af_info);
Pablo Neira Ayuso0628b122013-10-14 11:05:33 +02003951 INIT_LIST_HEAD(&net->nft.commit_list);
Pablo Neira Ayuso99633ab2013-10-10 23:28:33 +02003952 return 0;
3953}
3954
3955static struct pernet_operations nf_tables_net_ops = {
3956 .init = nf_tables_init_net,
3957};
3958
Patrick McHardy96518512013-10-14 11:00:02 +02003959static int __init nf_tables_module_init(void)
3960{
3961 int err;
3962
3963 info = kmalloc(sizeof(struct nft_expr_info) * NFT_RULE_MAXEXPRS,
3964 GFP_KERNEL);
3965 if (info == NULL) {
3966 err = -ENOMEM;
3967 goto err1;
3968 }
3969
3970 err = nf_tables_core_module_init();
3971 if (err < 0)
3972 goto err2;
3973
3974 err = nfnetlink_subsys_register(&nf_tables_subsys);
3975 if (err < 0)
3976 goto err3;
3977
3978 pr_info("nf_tables: (c) 2007-2009 Patrick McHardy <kaber@trash.net>\n");
Pablo Neira Ayuso99633ab2013-10-10 23:28:33 +02003979 return register_pernet_subsys(&nf_tables_net_ops);
Patrick McHardy96518512013-10-14 11:00:02 +02003980err3:
3981 nf_tables_core_module_exit();
3982err2:
3983 kfree(info);
3984err1:
3985 return err;
3986}
3987
3988static void __exit nf_tables_module_exit(void)
3989{
Pablo Neira Ayuso99633ab2013-10-10 23:28:33 +02003990 unregister_pernet_subsys(&nf_tables_net_ops);
Patrick McHardy96518512013-10-14 11:00:02 +02003991 nfnetlink_subsys_unregister(&nf_tables_subsys);
3992 nf_tables_core_module_exit();
3993 kfree(info);
3994}
3995
3996module_init(nf_tables_module_init);
3997module_exit(nf_tables_module_exit);
3998
3999MODULE_LICENSE("GPL");
4000MODULE_AUTHOR("Patrick McHardy <kaber@trash.net>");
4001MODULE_ALIAS_NFNL_SUBSYS(NFNL_SUBSYS_NFTABLES);