Blame view

net/netfilter/nfnetlink.c 13.2 KB
f9e815b37   Harald Welte   [NETFITLER]: Add ...
1
2
3
4
5
  /* Netfilter messages via netlink socket. Allows for user space
   * protocol helpers and general trouble making from userspace.
   *
   * (C) 2001 by Jay Schulist <jschlst@samba.org>,
   * (C) 2002-2005 by Harald Welte <laforge@gnumonks.org>
67ca39660   Pablo Neira Ayuso   [NETFILTER]: nfne...
6
   * (C) 2005,2007 by Pablo Neira Ayuso <pablo@netfilter.org>
f9e815b37   Harald Welte   [NETFITLER]: Add ...
7
8
9
10
11
12
13
14
15
   *
   * Initial netfilter messages via netlink development funded and
   * generally made possible by Network Robots, Inc. (www.networkrobots.com)
   *
   * Further development of this code funded by Astaro AG (http://www.astaro.com)
   *
   * This software may be used and distributed according to the terms
   * of the GNU General Public License, incorporated herein by reference.
   */
f9e815b37   Harald Welte   [NETFITLER]: Add ...
16
17
18
19
  #include <linux/module.h>
  #include <linux/types.h>
  #include <linux/socket.h>
  #include <linux/kernel.h>
f9e815b37   Harald Welte   [NETFITLER]: Add ...
20
21
22
  #include <linux/string.h>
  #include <linux/sockios.h>
  #include <linux/net.h>
f9e815b37   Harald Welte   [NETFITLER]: Add ...
23
24
  #include <linux/skbuff.h>
  #include <asm/uaccess.h>
f9e815b37   Harald Welte   [NETFITLER]: Add ...
25
26
  #include <net/sock.h>
  #include <linux/init.h>
f9e815b37   Harald Welte   [NETFITLER]: Add ...
27

573ce260b   Hong zhi guo   net-next: replace...
28
  #include <net/netlink.h>
f9e815b37   Harald Welte   [NETFITLER]: Add ...
29
30
31
  #include <linux/netfilter/nfnetlink.h>
  
  MODULE_LICENSE("GPL");
4fdb3bb72   Harald Welte   [NETLINK]: Add pr...
32
33
  MODULE_AUTHOR("Harald Welte <laforge@netfilter.org>");
  MODULE_ALIAS_NET_PF_PROTO(PF_NETLINK, NETLINK_NETFILTER);
f9e815b37   Harald Welte   [NETFITLER]: Add ...
34

9c55d3b54   Florian Westphal   nfnetlink: add nf...
35
36
37
  #define nfnl_dereference_protected(id) \
  	rcu_dereference_protected(table[(id)].subsys, \
  				  lockdep_nfnl_is_held((id)))
f9e815b37   Harald Welte   [NETFITLER]: Add ...
38
  static char __initdata nfversion[] = "0.30";
c14b78e7d   Pablo Neira Ayuso   netfilter: nfnetl...
39
40
41
42
  static struct {
  	struct mutex				mutex;
  	const struct nfnetlink_subsystem __rcu	*subsys;
  } table[NFNL_SUBSYS_COUNT];
f9e815b37   Harald Welte   [NETFITLER]: Add ...
43

03292745b   Pablo Neira Ayuso   netlink: add nlk-...
44
45
46
47
48
49
50
  static const int nfnl_group2type[NFNLGRP_MAX+1] = {
  	[NFNLGRP_CONNTRACK_NEW]		= NFNL_SUBSYS_CTNETLINK,
  	[NFNLGRP_CONNTRACK_UPDATE]	= NFNL_SUBSYS_CTNETLINK,
  	[NFNLGRP_CONNTRACK_DESTROY]	= NFNL_SUBSYS_CTNETLINK,
  	[NFNLGRP_CONNTRACK_EXP_NEW]	= NFNL_SUBSYS_CTNETLINK_EXP,
  	[NFNLGRP_CONNTRACK_EXP_UPDATE]	= NFNL_SUBSYS_CTNETLINK_EXP,
  	[NFNLGRP_CONNTRACK_EXP_DESTROY] = NFNL_SUBSYS_CTNETLINK_EXP,
97840cb67   Pablo Neira Ayuso   netfilter: nfnetl...
51
52
  	[NFNLGRP_NFTABLES]		= NFNL_SUBSYS_NFTABLES,
  	[NFNLGRP_ACCT_QUOTA]		= NFNL_SUBSYS_ACCT,
33d5a7b14   Florian Westphal   netfilter: nf_tab...
53
  	[NFNLGRP_NFTRACE]		= NFNL_SUBSYS_NFTABLES,
03292745b   Pablo Neira Ayuso   netlink: add nlk-...
54
  };
c14b78e7d   Pablo Neira Ayuso   netfilter: nfnetl...
55
  void nfnl_lock(__u8 subsys_id)
f9e815b37   Harald Welte   [NETFITLER]: Add ...
56
  {
c14b78e7d   Pablo Neira Ayuso   netfilter: nfnetl...
57
  	mutex_lock(&table[subsys_id].mutex);
f9e815b37   Harald Welte   [NETFITLER]: Add ...
58
  }
e6a7d3c04   Pablo Neira Ayuso   netfilter: ctnetl...
59
  EXPORT_SYMBOL_GPL(nfnl_lock);
f9e815b37   Harald Welte   [NETFITLER]: Add ...
60

c14b78e7d   Pablo Neira Ayuso   netfilter: nfnetl...
61
  void nfnl_unlock(__u8 subsys_id)
a3c5029cf   Patrick McHardy   [NETFILTER]: nfne...
62
  {
c14b78e7d   Pablo Neira Ayuso   netfilter: nfnetl...
63
  	mutex_unlock(&table[subsys_id].mutex);
f9e815b37   Harald Welte   [NETFITLER]: Add ...
64
  }
e6a7d3c04   Pablo Neira Ayuso   netfilter: ctnetl...
65
  EXPORT_SYMBOL_GPL(nfnl_unlock);
f9e815b37   Harald Welte   [NETFITLER]: Add ...
66

0eb5db7ad   Patrick McHardy   netfilter: nfnetl...
67
  #ifdef CONFIG_PROVE_LOCKING
875e08294   Yaowei Bai   net/nfnetlink: lo...
68
  bool lockdep_nfnl_is_held(u8 subsys_id)
0eb5db7ad   Patrick McHardy   netfilter: nfnetl...
69
70
71
72
73
  {
  	return lockdep_is_held(&table[subsys_id].mutex);
  }
  EXPORT_SYMBOL_GPL(lockdep_nfnl_is_held);
  #endif
7c8d4cb41   Patrick McHardy   [NETFILTER]: nfne...
74
  int nfnetlink_subsys_register(const struct nfnetlink_subsystem *n)
f9e815b37   Harald Welte   [NETFITLER]: Add ...
75
  {
c14b78e7d   Pablo Neira Ayuso   netfilter: nfnetl...
76
77
78
  	nfnl_lock(n->subsys_id);
  	if (table[n->subsys_id].subsys) {
  		nfnl_unlock(n->subsys_id);
0ab43f849   Harald Welte   [NETFILTER]: Core...
79
80
  		return -EBUSY;
  	}
c14b78e7d   Pablo Neira Ayuso   netfilter: nfnetl...
81
82
  	rcu_assign_pointer(table[n->subsys_id].subsys, n);
  	nfnl_unlock(n->subsys_id);
f9e815b37   Harald Welte   [NETFITLER]: Add ...
83
84
85
  
  	return 0;
  }
f4bc177f0   Pablo Neira Ayuso   [NETFILTER]: nfne...
86
  EXPORT_SYMBOL_GPL(nfnetlink_subsys_register);
f9e815b37   Harald Welte   [NETFITLER]: Add ...
87

7c8d4cb41   Patrick McHardy   [NETFILTER]: nfne...
88
  int nfnetlink_subsys_unregister(const struct nfnetlink_subsystem *n)
f9e815b37   Harald Welte   [NETFITLER]: Add ...
89
  {
c14b78e7d   Pablo Neira Ayuso   netfilter: nfnetl...
90
91
92
  	nfnl_lock(n->subsys_id);
  	table[n->subsys_id].subsys = NULL;
  	nfnl_unlock(n->subsys_id);
6b75e3e8d   Eric Dumazet   netfilter: nfnetl...
93
  	synchronize_rcu();
f9e815b37   Harald Welte   [NETFITLER]: Add ...
94
95
  	return 0;
  }
f4bc177f0   Pablo Neira Ayuso   [NETFILTER]: nfne...
96
  EXPORT_SYMBOL_GPL(nfnetlink_subsys_unregister);
f9e815b37   Harald Welte   [NETFITLER]: Add ...
97

7c8d4cb41   Patrick McHardy   [NETFILTER]: nfne...
98
  static inline const struct nfnetlink_subsystem *nfnetlink_get_subsys(u_int16_t type)
f9e815b37   Harald Welte   [NETFITLER]: Add ...
99
100
  {
  	u_int8_t subsys_id = NFNL_SUBSYS_ID(type);
ac0f1d989   Pablo Neira Ayuso   [NETFILTER]: nfne...
101
  	if (subsys_id >= NFNL_SUBSYS_COUNT)
f9e815b37   Harald Welte   [NETFITLER]: Add ...
102
  		return NULL;
c14b78e7d   Pablo Neira Ayuso   netfilter: nfnetl...
103
  	return rcu_dereference(table[subsys_id].subsys);
f9e815b37   Harald Welte   [NETFITLER]: Add ...
104
  }
7c8d4cb41   Patrick McHardy   [NETFILTER]: nfne...
105
106
  static inline const struct nfnl_callback *
  nfnetlink_find_client(u_int16_t type, const struct nfnetlink_subsystem *ss)
f9e815b37   Harald Welte   [NETFITLER]: Add ...
107
108
  {
  	u_int8_t cb_id = NFNL_MSG_TYPE(type);
601e68e10   YOSHIFUJI Hideaki   [NETFILTER]: Fix ...
109

67ca39660   Pablo Neira Ayuso   [NETFILTER]: nfne...
110
  	if (cb_id >= ss->cb_count)
f9e815b37   Harald Welte   [NETFITLER]: Add ...
111
  		return NULL;
f9e815b37   Harald Welte   [NETFITLER]: Add ...
112
113
114
  
  	return &ss->cb[cb_id];
  }
cd8c20b65   Alexey Dobriyan   netfilter: nfnetl...
115
  int nfnetlink_has_listeners(struct net *net, unsigned int group)
a24276924   Patrick McHardy   [NETFILTER]: ctne...
116
  {
cd8c20b65   Alexey Dobriyan   netfilter: nfnetl...
117
  	return netlink_has_listeners(net->nfnl, group);
a24276924   Patrick McHardy   [NETFILTER]: ctne...
118
119
  }
  EXPORT_SYMBOL_GPL(nfnetlink_has_listeners);
ec464e5dc   Patrick McHardy   netfilter: rename...
120
  int nfnetlink_send(struct sk_buff *skb, struct net *net, u32 portid,
95c961747   Eric Dumazet   net: cleanup unsi...
121
  		   unsigned int group, int echo, gfp_t flags)
f9e815b37   Harald Welte   [NETFITLER]: Add ...
122
  {
ec464e5dc   Patrick McHardy   netfilter: rename...
123
  	return nlmsg_notify(net->nfnl, skb, portid, group, echo, flags);
f9e815b37   Harald Welte   [NETFITLER]: Add ...
124
  }
f4bc177f0   Pablo Neira Ayuso   [NETFILTER]: nfne...
125
  EXPORT_SYMBOL_GPL(nfnetlink_send);
f9e815b37   Harald Welte   [NETFITLER]: Add ...
126

ec464e5dc   Patrick McHardy   netfilter: rename...
127
  int nfnetlink_set_err(struct net *net, u32 portid, u32 group, int error)
dd5b6ce6f   Pablo Neira Ayuso   nefilter: nfnetli...
128
  {
ec464e5dc   Patrick McHardy   netfilter: rename...
129
  	return netlink_set_err(net->nfnl, portid, group, error);
dd5b6ce6f   Pablo Neira Ayuso   nefilter: nfnetli...
130
131
  }
  EXPORT_SYMBOL_GPL(nfnetlink_set_err);
ec464e5dc   Patrick McHardy   netfilter: rename...
132
133
  int nfnetlink_unicast(struct sk_buff *skb, struct net *net, u32 portid,
  		      int flags)
f9e815b37   Harald Welte   [NETFITLER]: Add ...
134
  {
ec464e5dc   Patrick McHardy   netfilter: rename...
135
  	return netlink_unicast(net->nfnl, skb, portid, flags);
f9e815b37   Harald Welte   [NETFITLER]: Add ...
136
  }
f4bc177f0   Pablo Neira Ayuso   [NETFILTER]: nfne...
137
  EXPORT_SYMBOL_GPL(nfnetlink_unicast);
f9e815b37   Harald Welte   [NETFITLER]: Add ...
138
139
  
  /* Process one complete nfnetlink message. */
1d00a4eb4   Thomas Graf   [NETLINK]: Remove...
140
  static int nfnetlink_rcv_msg(struct sk_buff *skb, struct nlmsghdr *nlh)
f9e815b37   Harald Welte   [NETFITLER]: Add ...
141
  {
cd8c20b65   Alexey Dobriyan   netfilter: nfnetl...
142
  	struct net *net = sock_net(skb->sk);
7c8d4cb41   Patrick McHardy   [NETFILTER]: nfne...
143
144
  	const struct nfnl_callback *nc;
  	const struct nfnetlink_subsystem *ss;
1d00a4eb4   Thomas Graf   [NETLINK]: Remove...
145
  	int type, err;
f9e815b37   Harald Welte   [NETFITLER]: Add ...
146

f9e815b37   Harald Welte   [NETFITLER]: Add ...
147
  	/* All the messages must at least contain nfgenmsg */
573ce260b   Hong zhi guo   net-next: replace...
148
  	if (nlmsg_len(nlh) < sizeof(struct nfgenmsg))
f9e815b37   Harald Welte   [NETFITLER]: Add ...
149
  		return 0;
f9e815b37   Harald Welte   [NETFITLER]: Add ...
150
151
  
  	type = nlh->nlmsg_type;
e6a7d3c04   Pablo Neira Ayuso   netfilter: ctnetl...
152
  replay:
6b75e3e8d   Eric Dumazet   netfilter: nfnetl...
153
  	rcu_read_lock();
f9e815b37   Harald Welte   [NETFITLER]: Add ...
154
  	ss = nfnetlink_get_subsys(type);
0ab43f849   Harald Welte   [NETFILTER]: Core...
155
  	if (!ss) {
95a5afca4   Johannes Berg   net: Remove CONFI...
156
  #ifdef CONFIG_MODULES
6b75e3e8d   Eric Dumazet   netfilter: nfnetl...
157
  		rcu_read_unlock();
37d2e7a20   Harald Welte   [NETFILTER] nfnet...
158
  		request_module("nfnetlink-subsys-%d", NFNL_SUBSYS_ID(type));
6b75e3e8d   Eric Dumazet   netfilter: nfnetl...
159
  		rcu_read_lock();
37d2e7a20   Harald Welte   [NETFILTER] nfnet...
160
  		ss = nfnetlink_get_subsys(type);
0ab43f849   Harald Welte   [NETFILTER]: Core...
161
162
  		if (!ss)
  #endif
6b75e3e8d   Eric Dumazet   netfilter: nfnetl...
163
164
  		{
  			rcu_read_unlock();
1d00a4eb4   Thomas Graf   [NETLINK]: Remove...
165
  			return -EINVAL;
6b75e3e8d   Eric Dumazet   netfilter: nfnetl...
166
  		}
0ab43f849   Harald Welte   [NETFILTER]: Core...
167
  	}
f9e815b37   Harald Welte   [NETFITLER]: Add ...
168
169
  
  	nc = nfnetlink_find_client(type, ss);
6b75e3e8d   Eric Dumazet   netfilter: nfnetl...
170
171
  	if (!nc) {
  		rcu_read_unlock();
1d00a4eb4   Thomas Graf   [NETLINK]: Remove...
172
  		return -EINVAL;
6b75e3e8d   Eric Dumazet   netfilter: nfnetl...
173
  	}
f9e815b37   Harald Welte   [NETFITLER]: Add ...
174

f9e815b37   Harald Welte   [NETFITLER]: Add ...
175
  	{
573ce260b   Hong zhi guo   net-next: replace...
176
  		int min_len = nlmsg_total_size(sizeof(struct nfgenmsg));
e37305782   Patrick McHardy   [NETFILTER]: nfne...
177
  		u_int8_t cb_id = NFNL_MSG_TYPE(nlh->nlmsg_type);
f49c857ff   Pablo Neira Ayuso   netfilter: nfnetl...
178
179
180
  		struct nlattr *cda[ss->cb[cb_id].attr_count + 1];
  		struct nlattr *attr = (void *)nlh + min_len;
  		int attrlen = nlh->nlmsg_len - min_len;
c14b78e7d   Pablo Neira Ayuso   netfilter: nfnetl...
181
  		__u8 subsys_id = NFNL_SUBSYS_ID(type);
f49c857ff   Pablo Neira Ayuso   netfilter: nfnetl...
182
183
184
  
  		err = nla_parse(cda, ss->cb[cb_id].attr_count,
  				attr, attrlen, ss->cb[cb_id].policy);
4009e1885   Tomasz Bursztyka   netfilter: nfnetl...
185
186
  		if (err < 0) {
  			rcu_read_unlock();
f49c857ff   Pablo Neira Ayuso   netfilter: nfnetl...
187
  			return err;
4009e1885   Tomasz Bursztyka   netfilter: nfnetl...
188
  		}
601e68e10   YOSHIFUJI Hideaki   [NETFILTER]: Fix ...
189

6b75e3e8d   Eric Dumazet   netfilter: nfnetl...
190
  		if (nc->call_rcu) {
7b8002a15   Pablo Neira Ayuso   netfilter: nfnetl...
191
  			err = nc->call_rcu(net, net->nfnl, skb, nlh,
6b75e3e8d   Eric Dumazet   netfilter: nfnetl...
192
193
194
195
  					   (const struct nlattr **)cda);
  			rcu_read_unlock();
  		} else {
  			rcu_read_unlock();
c14b78e7d   Pablo Neira Ayuso   netfilter: nfnetl...
196
  			nfnl_lock(subsys_id);
9c55d3b54   Florian Westphal   nfnetlink: add nf...
197
  			if (nfnl_dereference_protected(subsys_id) != ss ||
6b75e3e8d   Eric Dumazet   netfilter: nfnetl...
198
199
  			    nfnetlink_find_client(type, ss) != nc)
  				err = -EAGAIN;
59560a38a   Tomasz Bursztyka   netfilter: nfnetl...
200
  			else if (nc->call)
7b8002a15   Pablo Neira Ayuso   netfilter: nfnetl...
201
202
  				err = nc->call(net, net->nfnl, skb, nlh,
  					       (const struct nlattr **)cda);
59560a38a   Tomasz Bursztyka   netfilter: nfnetl...
203
204
  			else
  				err = -EINVAL;
c14b78e7d   Pablo Neira Ayuso   netfilter: nfnetl...
205
  			nfnl_unlock(subsys_id);
6b75e3e8d   Eric Dumazet   netfilter: nfnetl...
206
  		}
e6a7d3c04   Pablo Neira Ayuso   netfilter: ctnetl...
207
208
209
  		if (err == -EAGAIN)
  			goto replay;
  		return err;
f9e815b37   Harald Welte   [NETFITLER]: Add ...
210
  	}
f9e815b37   Harald Welte   [NETFITLER]: Add ...
211
  }
cbb8125eb   Pablo Neira Ayuso   netfilter: nfnetl...
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
  struct nfnl_err {
  	struct list_head	head;
  	struct nlmsghdr		*nlh;
  	int			err;
  };
  
  static int nfnl_err_add(struct list_head *list, struct nlmsghdr *nlh, int err)
  {
  	struct nfnl_err *nfnl_err;
  
  	nfnl_err = kmalloc(sizeof(struct nfnl_err), GFP_KERNEL);
  	if (nfnl_err == NULL)
  		return -ENOMEM;
  
  	nfnl_err->nlh = nlh;
  	nfnl_err->err = err;
  	list_add_tail(&nfnl_err->head, list);
  
  	return 0;
  }
  
  static void nfnl_err_del(struct nfnl_err *nfnl_err)
  {
  	list_del(&nfnl_err->head);
  	kfree(nfnl_err);
  }
  
  static void nfnl_err_reset(struct list_head *err_list)
  {
  	struct nfnl_err *nfnl_err, *next;
  
  	list_for_each_entry_safe(nfnl_err, next, err_list, head)
  		nfnl_err_del(nfnl_err);
  }
  
  static void nfnl_err_deliver(struct list_head *err_list, struct sk_buff *skb)
  {
  	struct nfnl_err *nfnl_err, *next;
  
  	list_for_each_entry_safe(nfnl_err, next, err_list, head) {
  		netlink_ack(skb, nfnl_err->nlh, nfnl_err->err);
  		nfnl_err_del(nfnl_err);
  	}
  }
6742b9e31   Pablo Neira Ayuso   netfilter: nfnetl...
256
257
258
259
260
  enum {
  	NFNL_BATCH_FAILURE	= (1 << 0),
  	NFNL_BATCH_DONE		= (1 << 1),
  	NFNL_BATCH_REPLAY	= (1 << 2),
  };
0628b123c   Pablo Neira Ayuso   netfilter: nfnetl...
261
262
263
  static void nfnetlink_rcv_batch(struct sk_buff *skb, struct nlmsghdr *nlh,
  				u_int16_t subsys_id)
  {
0f8162326   Duan Jiong   netfilter: nfnetl...
264
  	struct sk_buff *oskb = skb;
0628b123c   Pablo Neira Ayuso   netfilter: nfnetl...
265
266
267
  	struct net *net = sock_net(skb->sk);
  	const struct nfnetlink_subsystem *ss;
  	const struct nfnl_callback *nc;
cbb8125eb   Pablo Neira Ayuso   netfilter: nfnetl...
268
  	static LIST_HEAD(err_list);
6742b9e31   Pablo Neira Ayuso   netfilter: nfnetl...
269
  	u32 status;
0628b123c   Pablo Neira Ayuso   netfilter: nfnetl...
270
271
272
273
274
  	int err;
  
  	if (subsys_id >= NFNL_SUBSYS_COUNT)
  		return netlink_ack(skb, nlh, -EINVAL);
  replay:
6742b9e31   Pablo Neira Ayuso   netfilter: nfnetl...
275
  	status = 0;
0f8162326   Duan Jiong   netfilter: nfnetl...
276
277
  	skb = netlink_skb_clone(oskb, GFP_KERNEL);
  	if (!skb)
0628b123c   Pablo Neira Ayuso   netfilter: nfnetl...
278
  		return netlink_ack(oskb, nlh, -ENOMEM);
0628b123c   Pablo Neira Ayuso   netfilter: nfnetl...
279
  	nfnl_lock(subsys_id);
9c55d3b54   Florian Westphal   nfnetlink: add nf...
280
  	ss = nfnl_dereference_protected(subsys_id);
0628b123c   Pablo Neira Ayuso   netfilter: nfnetl...
281
282
283
284
285
  	if (!ss) {
  #ifdef CONFIG_MODULES
  		nfnl_unlock(subsys_id);
  		request_module("nfnetlink-subsys-%d", subsys_id);
  		nfnl_lock(subsys_id);
9c55d3b54   Florian Westphal   nfnetlink: add nf...
286
  		ss = nfnl_dereference_protected(subsys_id);
0628b123c   Pablo Neira Ayuso   netfilter: nfnetl...
287
288
289
290
  		if (!ss)
  #endif
  		{
  			nfnl_unlock(subsys_id);
7c7bdf359   Pablo Neira Ayuso   netfilter: nfnetl...
291
  			netlink_ack(oskb, nlh, -EOPNOTSUPP);
0f8162326   Duan Jiong   netfilter: nfnetl...
292
  			return kfree_skb(skb);
0628b123c   Pablo Neira Ayuso   netfilter: nfnetl...
293
294
295
296
297
  		}
  	}
  
  	if (!ss->commit || !ss->abort) {
  		nfnl_unlock(subsys_id);
7c7bdf359   Pablo Neira Ayuso   netfilter: nfnetl...
298
  		netlink_ack(oskb, nlh, -EOPNOTSUPP);
ecd15dd7e   Denys Fedoryshchenko   netfilter: nfnetl...
299
  		return kfree_skb(skb);
0628b123c   Pablo Neira Ayuso   netfilter: nfnetl...
300
301
302
303
304
305
306
  	}
  
  	while (skb->len >= nlmsg_total_size(0)) {
  		int msglen, type;
  
  		nlh = nlmsg_hdr(skb);
  		err = 0;
c58d6c936   Phil Turnbull   netfilter: nfnetl...
307
308
309
310
311
312
  		if (nlh->nlmsg_len < NLMSG_HDRLEN ||
  		    skb->len < nlh->nlmsg_len ||
  		    nlmsg_len(nlh) < sizeof(struct nfgenmsg)) {
  			nfnl_err_reset(&err_list);
  			status |= NFNL_BATCH_FAILURE;
  			goto done;
0628b123c   Pablo Neira Ayuso   netfilter: nfnetl...
313
314
315
316
317
318
319
320
321
322
323
  		}
  
  		/* Only requests are handled by the kernel */
  		if (!(nlh->nlmsg_flags & NLM_F_REQUEST)) {
  			err = -EINVAL;
  			goto ack;
  		}
  
  		type = nlh->nlmsg_type;
  		if (type == NFNL_MSG_BATCH_BEGIN) {
  			/* Malformed: Batch begin twice */
cbb8125eb   Pablo Neira Ayuso   netfilter: nfnetl...
324
  			nfnl_err_reset(&err_list);
6742b9e31   Pablo Neira Ayuso   netfilter: nfnetl...
325
  			status |= NFNL_BATCH_FAILURE;
0628b123c   Pablo Neira Ayuso   netfilter: nfnetl...
326
327
  			goto done;
  		} else if (type == NFNL_MSG_BATCH_END) {
6742b9e31   Pablo Neira Ayuso   netfilter: nfnetl...
328
  			status |= NFNL_BATCH_DONE;
0628b123c   Pablo Neira Ayuso   netfilter: nfnetl...
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
  			goto done;
  		} else if (type < NLMSG_MIN_TYPE) {
  			err = -EINVAL;
  			goto ack;
  		}
  
  		/* We only accept a batch with messages for the same
  		 * subsystem.
  		 */
  		if (NFNL_SUBSYS_ID(type) != subsys_id) {
  			err = -EINVAL;
  			goto ack;
  		}
  
  		nc = nfnetlink_find_client(type, ss);
  		if (!nc) {
  			err = -EINVAL;
  			goto ack;
  		}
  
  		{
  			int min_len = nlmsg_total_size(sizeof(struct nfgenmsg));
  			u_int8_t cb_id = NFNL_MSG_TYPE(nlh->nlmsg_type);
  			struct nlattr *cda[ss->cb[cb_id].attr_count + 1];
  			struct nlattr *attr = (void *)nlh + min_len;
  			int attrlen = nlh->nlmsg_len - min_len;
  
  			err = nla_parse(cda, ss->cb[cb_id].attr_count,
  					attr, attrlen, ss->cb[cb_id].policy);
  			if (err < 0)
  				goto ack;
  
  			if (nc->call_batch) {
633c9a840   Pablo Neira Ayuso   netfilter: nfnetl...
362
  				err = nc->call_batch(net, net->nfnl, skb, nlh,
0628b123c   Pablo Neira Ayuso   netfilter: nfnetl...
363
364
365
366
367
368
369
370
  						     (const struct nlattr **)cda);
  			}
  
  			/* The lock was released to autoload some module, we
  			 * have to abort and start from scratch using the
  			 * original skb.
  			 */
  			if (err == -EAGAIN) {
6742b9e31   Pablo Neira Ayuso   netfilter: nfnetl...
371
372
  				status |= NFNL_BATCH_REPLAY;
  				goto next;
0628b123c   Pablo Neira Ayuso   netfilter: nfnetl...
373
374
375
376
  			}
  		}
  ack:
  		if (nlh->nlmsg_flags & NLM_F_ACK || err) {
cbb8125eb   Pablo Neira Ayuso   netfilter: nfnetl...
377
378
379
380
381
382
383
384
385
386
  			/* Errors are delivered once the full batch has been
  			 * processed, this avoids that the same error is
  			 * reported several times when replaying the batch.
  			 */
  			if (nfnl_err_add(&err_list, nlh, err) < 0) {
  				/* We failed to enqueue an error, reset the
  				 * list of errors and send OOM to userspace
  				 * pointing to the batch header.
  				 */
  				nfnl_err_reset(&err_list);
7c7bdf359   Pablo Neira Ayuso   netfilter: nfnetl...
387
  				netlink_ack(oskb, nlmsg_hdr(oskb), -ENOMEM);
6742b9e31   Pablo Neira Ayuso   netfilter: nfnetl...
388
  				status |= NFNL_BATCH_FAILURE;
cbb8125eb   Pablo Neira Ayuso   netfilter: nfnetl...
389
390
  				goto done;
  			}
0628b123c   Pablo Neira Ayuso   netfilter: nfnetl...
391
392
393
394
  			/* We don't stop processing the batch on errors, thus,
  			 * userspace gets all the errors that the batch
  			 * triggers.
  			 */
0628b123c   Pablo Neira Ayuso   netfilter: nfnetl...
395
  			if (err)
6742b9e31   Pablo Neira Ayuso   netfilter: nfnetl...
396
  				status |= NFNL_BATCH_FAILURE;
0628b123c   Pablo Neira Ayuso   netfilter: nfnetl...
397
  		}
6742b9e31   Pablo Neira Ayuso   netfilter: nfnetl...
398
  next:
0628b123c   Pablo Neira Ayuso   netfilter: nfnetl...
399
400
401
402
403
404
  		msglen = NLMSG_ALIGN(nlh->nlmsg_len);
  		if (msglen > skb->len)
  			msglen = skb->len;
  		skb_pull(skb, msglen);
  	}
  done:
6742b9e31   Pablo Neira Ayuso   netfilter: nfnetl...
405
  	if (status & NFNL_BATCH_REPLAY) {
5913beaf0   Pablo Neira Ayuso   netfilter: nfnetl...
406
  		ss->abort(net, oskb);
6742b9e31   Pablo Neira Ayuso   netfilter: nfnetl...
407
408
409
410
411
  		nfnl_err_reset(&err_list);
  		nfnl_unlock(subsys_id);
  		kfree_skb(skb);
  		goto replay;
  	} else if (status == NFNL_BATCH_DONE) {
5913beaf0   Pablo Neira Ayuso   netfilter: nfnetl...
412
  		ss->commit(net, oskb);
6742b9e31   Pablo Neira Ayuso   netfilter: nfnetl...
413
  	} else {
5913beaf0   Pablo Neira Ayuso   netfilter: nfnetl...
414
  		ss->abort(net, oskb);
6742b9e31   Pablo Neira Ayuso   netfilter: nfnetl...
415
  	}
0628b123c   Pablo Neira Ayuso   netfilter: nfnetl...
416

cbb8125eb   Pablo Neira Ayuso   netfilter: nfnetl...
417
  	nfnl_err_deliver(&err_list, oskb);
0628b123c   Pablo Neira Ayuso   netfilter: nfnetl...
418
  	nfnl_unlock(subsys_id);
0f8162326   Duan Jiong   netfilter: nfnetl...
419
  	kfree_skb(skb);
0628b123c   Pablo Neira Ayuso   netfilter: nfnetl...
420
  }
cd40b7d39   Denis V. Lunev   [NET]: make netli...
421
  static void nfnetlink_rcv(struct sk_buff *skb)
f9e815b37   Harald Welte   [NETFITLER]: Add ...
422
  {
0628b123c   Pablo Neira Ayuso   netfilter: nfnetl...
423
  	struct nlmsghdr *nlh = nlmsg_hdr(skb);
a9de9777d   Pablo Neira Ayuso   netfilter: nfnetl...
424
  	u_int16_t res_id;
0628b123c   Pablo Neira Ayuso   netfilter: nfnetl...
425
  	int msglen;
0628b123c   Pablo Neira Ayuso   netfilter: nfnetl...
426
427
428
  	if (nlh->nlmsg_len < NLMSG_HDRLEN ||
  	    skb->len < nlh->nlmsg_len)
  		return;
90f62cf30   Eric W. Biederman   net: Use netlink_...
429
  	if (!netlink_net_capable(skb, CAP_NET_ADMIN)) {
cdbe7c2d6   Jiri Benc   nfnetlink: do not...
430
431
432
  		netlink_ack(skb, nlh, -EPERM);
  		return;
  	}
0628b123c   Pablo Neira Ayuso   netfilter: nfnetl...
433
434
435
436
437
438
439
440
441
442
443
444
445
  	if (nlh->nlmsg_type == NFNL_MSG_BATCH_BEGIN) {
  		struct nfgenmsg *nfgenmsg;
  
  		msglen = NLMSG_ALIGN(nlh->nlmsg_len);
  		if (msglen > skb->len)
  			msglen = skb->len;
  
  		if (nlh->nlmsg_len < NLMSG_HDRLEN ||
  		    skb->len < NLMSG_HDRLEN + sizeof(struct nfgenmsg))
  			return;
  
  		nfgenmsg = nlmsg_data(nlh);
  		skb_pull(skb, msglen);
a9de9777d   Pablo Neira Ayuso   netfilter: nfnetl...
446
447
448
449
450
451
  		/* Work around old nft using host byte order */
  		if (nfgenmsg->res_id == NFNL_SUBSYS_NFTABLES)
  			res_id = NFNL_SUBSYS_NFTABLES;
  		else
  			res_id = ntohs(nfgenmsg->res_id);
  		nfnetlink_rcv_batch(skb, nlh, res_id);
0628b123c   Pablo Neira Ayuso   netfilter: nfnetl...
452
453
454
  	} else {
  		netlink_rcv_skb(skb, &nfnetlink_rcv_msg);
  	}
f9e815b37   Harald Welte   [NETFITLER]: Add ...
455
  }
03292745b   Pablo Neira Ayuso   netlink: add nlk-...
456
  #ifdef CONFIG_MODULES
023e2cfa3   Johannes Berg   netlink/genetlink...
457
  static int nfnetlink_bind(struct net *net, int group)
03292745b   Pablo Neira Ayuso   netlink: add nlk-...
458
459
  {
  	const struct nfnetlink_subsystem *ss;
97840cb67   Pablo Neira Ayuso   netfilter: nfnetl...
460
461
462
  	int type;
  
  	if (group <= NFNLGRP_NONE || group > NFNLGRP_MAX)
62924af24   Pablo Neira Ayuso   netfilter: nfnetl...
463
  		return 0;
97840cb67   Pablo Neira Ayuso   netfilter: nfnetl...
464
465
  
  	type = nfnl_group2type[group];
03292745b   Pablo Neira Ayuso   netlink: add nlk-...
466
467
  
  	rcu_read_lock();
dbc3617f4   Florian Westphal   netfilter: nfnetl...
468
  	ss = nfnetlink_get_subsys(type << 8);
03292745b   Pablo Neira Ayuso   netlink: add nlk-...
469
  	rcu_read_unlock();
bfe4bc71c   Richard Guy Briggs   netlink: simplify...
470
471
  	if (!ss)
  		request_module("nfnetlink-subsys-%d", type);
4f5209005   Richard Guy Briggs   netlink: have net...
472
  	return 0;
03292745b   Pablo Neira Ayuso   netlink: add nlk-...
473
474
  }
  #endif
cd8c20b65   Alexey Dobriyan   netfilter: nfnetl...
475
  static int __net_init nfnetlink_net_init(struct net *net)
f9e815b37   Harald Welte   [NETFITLER]: Add ...
476
  {
cd8c20b65   Alexey Dobriyan   netfilter: nfnetl...
477
  	struct sock *nfnl;
a31f2d17b   Pablo Neira Ayuso   netlink: add netl...
478
479
480
  	struct netlink_kernel_cfg cfg = {
  		.groups	= NFNLGRP_MAX,
  		.input	= nfnetlink_rcv,
03292745b   Pablo Neira Ayuso   netlink: add nlk-...
481
482
483
  #ifdef CONFIG_MODULES
  		.bind	= nfnetlink_bind,
  #endif
a31f2d17b   Pablo Neira Ayuso   netlink: add netl...
484
  	};
cd8c20b65   Alexey Dobriyan   netfilter: nfnetl...
485

9f00d9776   Pablo Neira Ayuso   netlink: hide str...
486
  	nfnl = netlink_kernel_create(net, NETLINK_NETFILTER, &cfg);
cd8c20b65   Alexey Dobriyan   netfilter: nfnetl...
487
488
489
  	if (!nfnl)
  		return -ENOMEM;
  	net->nfnl_stash = nfnl;
cf778b00e   Eric Dumazet   net: reintroduce ...
490
  	rcu_assign_pointer(net->nfnl, nfnl);
cd8c20b65   Alexey Dobriyan   netfilter: nfnetl...
491
  	return 0;
f9e815b37   Harald Welte   [NETFITLER]: Add ...
492
  }
cd8c20b65   Alexey Dobriyan   netfilter: nfnetl...
493
  static void __net_exit nfnetlink_net_exit_batch(struct list_head *net_exit_list)
f9e815b37   Harald Welte   [NETFITLER]: Add ...
494
  {
cd8c20b65   Alexey Dobriyan   netfilter: nfnetl...
495
  	struct net *net;
f9e815b37   Harald Welte   [NETFITLER]: Add ...
496

cd8c20b65   Alexey Dobriyan   netfilter: nfnetl...
497
  	list_for_each_entry(net, net_exit_list, exit_list)
a9b3cd7f3   Stephen Hemminger   rcu: convert uses...
498
  		RCU_INIT_POINTER(net->nfnl, NULL);
cd8c20b65   Alexey Dobriyan   netfilter: nfnetl...
499
500
501
502
  	synchronize_net();
  	list_for_each_entry(net, net_exit_list, exit_list)
  		netlink_kernel_release(net->nfnl_stash);
  }
f9e815b37   Harald Welte   [NETFITLER]: Add ...
503

cd8c20b65   Alexey Dobriyan   netfilter: nfnetl...
504
505
506
507
508
509
510
  static struct pernet_operations nfnetlink_net_ops = {
  	.init		= nfnetlink_net_init,
  	.exit_batch	= nfnetlink_net_exit_batch,
  };
  
  static int __init nfnetlink_init(void)
  {
c14b78e7d   Pablo Neira Ayuso   netfilter: nfnetl...
511
  	int i;
97840cb67   Pablo Neira Ayuso   netfilter: nfnetl...
512
513
  	for (i = NFNLGRP_NONE + 1; i <= NFNLGRP_MAX; i++)
  		BUG_ON(nfnl_group2type[i] == NFNL_SUBSYS_NONE);
c14b78e7d   Pablo Neira Ayuso   netfilter: nfnetl...
514
515
  	for (i=0; i<NFNL_SUBSYS_COUNT; i++)
  		mutex_init(&table[i].mutex);
654d0fbdc   Stephen Hemminger   netfilter: cleanu...
516
517
  	pr_info("Netfilter messages via NETLINK v%s.
  ", nfversion);
cd8c20b65   Alexey Dobriyan   netfilter: nfnetl...
518
  	return register_pernet_subsys(&nfnetlink_net_ops);
f9e815b37   Harald Welte   [NETFITLER]: Add ...
519
  }
cd8c20b65   Alexey Dobriyan   netfilter: nfnetl...
520
521
  static void __exit nfnetlink_exit(void)
  {
654d0fbdc   Stephen Hemminger   netfilter: cleanu...
522
523
  	pr_info("Removing netfilter NETLINK layer.
  ");
cd8c20b65   Alexey Dobriyan   netfilter: nfnetl...
524
525
  	unregister_pernet_subsys(&nfnetlink_net_ops);
  }
f9e815b37   Harald Welte   [NETFITLER]: Add ...
526
527
  module_init(nfnetlink_init);
  module_exit(nfnetlink_exit);