xref: /openbmc/linux/net/netfilter/nfnetlink.c (revision a1a64a15)
1f9e815b3SHarald Welte /* Netfilter messages via netlink socket. Allows for user space
2f9e815b3SHarald Welte  * protocol helpers and general trouble making from userspace.
3f9e815b3SHarald Welte  *
4f9e815b3SHarald Welte  * (C) 2001 by Jay Schulist <jschlst@samba.org>,
5f9e815b3SHarald Welte  * (C) 2002-2005 by Harald Welte <laforge@gnumonks.org>
68c4d4e8bSPablo Neira Ayuso  * (C) 2005-2017 by Pablo Neira Ayuso <pablo@netfilter.org>
7f9e815b3SHarald Welte  *
8f9e815b3SHarald Welte  * Initial netfilter messages via netlink development funded and
9f9e815b3SHarald Welte  * generally made possible by Network Robots, Inc. (www.networkrobots.com)
10f9e815b3SHarald Welte  *
11f9e815b3SHarald Welte  * Further development of this code funded by Astaro AG (http://www.astaro.com)
12f9e815b3SHarald Welte  *
13f9e815b3SHarald Welte  * This software may be used and distributed according to the terms
14f9e815b3SHarald Welte  * of the GNU General Public License, incorporated herein by reference.
15f9e815b3SHarald Welte  */
16f9e815b3SHarald Welte 
17f9e815b3SHarald Welte #include <linux/module.h>
18f9e815b3SHarald Welte #include <linux/types.h>
19f9e815b3SHarald Welte #include <linux/socket.h>
20f9e815b3SHarald Welte #include <linux/kernel.h>
21f9e815b3SHarald Welte #include <linux/string.h>
22f9e815b3SHarald Welte #include <linux/sockios.h>
23f9e815b3SHarald Welte #include <linux/net.h>
24f9e815b3SHarald Welte #include <linux/skbuff.h>
257c0f6ba6SLinus Torvalds #include <linux/uaccess.h>
26f9e815b3SHarald Welte #include <net/sock.h>
27f9e815b3SHarald Welte #include <linux/init.h>
288a3d4c36SFlorian Westphal #include <linux/sched/signal.h>
29f9e815b3SHarald Welte 
30573ce260SHong zhi guo #include <net/netlink.h>
311be05ea7SFlorian Westphal #include <net/netns/generic.h>
32fdf64911SFlorian Westphal #include <linux/netfilter.h>
33f9e815b3SHarald Welte #include <linux/netfilter/nfnetlink.h>
34f9e815b3SHarald Welte 
35f9e815b3SHarald Welte MODULE_LICENSE("GPL");
364fdb3bb7SHarald Welte MODULE_AUTHOR("Harald Welte <laforge@netfilter.org>");
374fdb3bb7SHarald Welte MODULE_ALIAS_NET_PF_PROTO(PF_NETLINK, NETLINK_NETFILTER);
384cacc395SRob Gill MODULE_DESCRIPTION("Netfilter messages via netlink socket");
39f9e815b3SHarald Welte 
409c55d3b5SFlorian Westphal #define nfnl_dereference_protected(id) \
419c55d3b5SFlorian Westphal 	rcu_dereference_protected(table[(id)].subsys, \
429c55d3b5SFlorian Westphal 				  lockdep_nfnl_is_held((id)))
439c55d3b5SFlorian Westphal 
447b7744e2SKees Cook #define NFNL_MAX_ATTR_COUNT	32
457b7744e2SKees Cook 
461be05ea7SFlorian Westphal static unsigned int nfnetlink_pernet_id __read_mostly;
471be05ea7SFlorian Westphal 
480b2f3212SFlorian Westphal #ifdef CONFIG_NF_CONNTRACK_EVENTS
490b2f3212SFlorian Westphal static DEFINE_SPINLOCK(nfnl_grp_active_lock);
500b2f3212SFlorian Westphal #endif
510b2f3212SFlorian Westphal 
521be05ea7SFlorian Westphal struct nfnl_net {
531be05ea7SFlorian Westphal 	struct sock *nfnl;
541be05ea7SFlorian Westphal };
551be05ea7SFlorian Westphal 
56c14b78e7SPablo Neira Ayuso static struct {
57c14b78e7SPablo Neira Ayuso 	struct mutex				mutex;
58c14b78e7SPablo Neira Ayuso 	const struct nfnetlink_subsystem __rcu	*subsys;
59c14b78e7SPablo Neira Ayuso } table[NFNL_SUBSYS_COUNT];
60f9e815b3SHarald Welte 
61ab6c41eeSFlorian Westphal static struct lock_class_key nfnl_lockdep_keys[NFNL_SUBSYS_COUNT];
62ab6c41eeSFlorian Westphal 
63ab6c41eeSFlorian Westphal static const char *const nfnl_lockdep_names[NFNL_SUBSYS_COUNT] = {
64ab6c41eeSFlorian Westphal 	[NFNL_SUBSYS_NONE] = "nfnl_subsys_none",
65ab6c41eeSFlorian Westphal 	[NFNL_SUBSYS_CTNETLINK] = "nfnl_subsys_ctnetlink",
66ab6c41eeSFlorian Westphal 	[NFNL_SUBSYS_CTNETLINK_EXP] = "nfnl_subsys_ctnetlink_exp",
67ab6c41eeSFlorian Westphal 	[NFNL_SUBSYS_QUEUE] = "nfnl_subsys_queue",
68ab6c41eeSFlorian Westphal 	[NFNL_SUBSYS_ULOG] = "nfnl_subsys_ulog",
69ab6c41eeSFlorian Westphal 	[NFNL_SUBSYS_OSF] = "nfnl_subsys_osf",
70ab6c41eeSFlorian Westphal 	[NFNL_SUBSYS_IPSET] = "nfnl_subsys_ipset",
71ab6c41eeSFlorian Westphal 	[NFNL_SUBSYS_ACCT] = "nfnl_subsys_acct",
72ab6c41eeSFlorian Westphal 	[NFNL_SUBSYS_CTNETLINK_TIMEOUT] = "nfnl_subsys_cttimeout",
73ab6c41eeSFlorian Westphal 	[NFNL_SUBSYS_CTHELPER] = "nfnl_subsys_cthelper",
74ab6c41eeSFlorian Westphal 	[NFNL_SUBSYS_NFTABLES] = "nfnl_subsys_nftables",
75ab6c41eeSFlorian Westphal 	[NFNL_SUBSYS_NFT_COMPAT] = "nfnl_subsys_nftcompat",
76e2cf17d3SFlorian Westphal 	[NFNL_SUBSYS_HOOK] = "nfnl_subsys_hook",
77ab6c41eeSFlorian Westphal };
78ab6c41eeSFlorian Westphal 
7903292745SPablo Neira Ayuso static const int nfnl_group2type[NFNLGRP_MAX+1] = {
8003292745SPablo Neira Ayuso 	[NFNLGRP_CONNTRACK_NEW]		= NFNL_SUBSYS_CTNETLINK,
8103292745SPablo Neira Ayuso 	[NFNLGRP_CONNTRACK_UPDATE]	= NFNL_SUBSYS_CTNETLINK,
8203292745SPablo Neira Ayuso 	[NFNLGRP_CONNTRACK_DESTROY]	= NFNL_SUBSYS_CTNETLINK,
8303292745SPablo Neira Ayuso 	[NFNLGRP_CONNTRACK_EXP_NEW]	= NFNL_SUBSYS_CTNETLINK_EXP,
8403292745SPablo Neira Ayuso 	[NFNLGRP_CONNTRACK_EXP_UPDATE]	= NFNL_SUBSYS_CTNETLINK_EXP,
8503292745SPablo Neira Ayuso 	[NFNLGRP_CONNTRACK_EXP_DESTROY] = NFNL_SUBSYS_CTNETLINK_EXP,
8697840cb6SPablo Neira Ayuso 	[NFNLGRP_NFTABLES]		= NFNL_SUBSYS_NFTABLES,
8797840cb6SPablo Neira Ayuso 	[NFNLGRP_ACCT_QUOTA]		= NFNL_SUBSYS_ACCT,
8833d5a7b1SFlorian Westphal 	[NFNLGRP_NFTRACE]		= NFNL_SUBSYS_NFTABLES,
8903292745SPablo Neira Ayuso };
9003292745SPablo Neira Ayuso 
nfnl_pernet(struct net * net)911be05ea7SFlorian Westphal static struct nfnl_net *nfnl_pernet(struct net *net)
921be05ea7SFlorian Westphal {
931be05ea7SFlorian Westphal 	return net_generic(net, nfnetlink_pernet_id);
941be05ea7SFlorian Westphal }
951be05ea7SFlorian Westphal 
nfnl_lock(__u8 subsys_id)96c14b78e7SPablo Neira Ayuso void nfnl_lock(__u8 subsys_id)
97f9e815b3SHarald Welte {
98c14b78e7SPablo Neira Ayuso 	mutex_lock(&table[subsys_id].mutex);
99f9e815b3SHarald Welte }
100e6a7d3c0SPablo Neira Ayuso EXPORT_SYMBOL_GPL(nfnl_lock);
101f9e815b3SHarald Welte 
nfnl_unlock(__u8 subsys_id)102c14b78e7SPablo Neira Ayuso void nfnl_unlock(__u8 subsys_id)
103a3c5029cSPatrick McHardy {
104c14b78e7SPablo Neira Ayuso 	mutex_unlock(&table[subsys_id].mutex);
105a3c5029cSPatrick McHardy }
106e6a7d3c0SPablo Neira Ayuso EXPORT_SYMBOL_GPL(nfnl_unlock);
107a3c5029cSPatrick McHardy 
1080eb5db7aSPatrick McHardy #ifdef CONFIG_PROVE_LOCKING
lockdep_nfnl_is_held(u8 subsys_id)109875e0829SYaowei Bai bool lockdep_nfnl_is_held(u8 subsys_id)
1100eb5db7aSPatrick McHardy {
1110eb5db7aSPatrick McHardy 	return lockdep_is_held(&table[subsys_id].mutex);
1120eb5db7aSPatrick McHardy }
1130eb5db7aSPatrick McHardy EXPORT_SYMBOL_GPL(lockdep_nfnl_is_held);
1140eb5db7aSPatrick McHardy #endif
1150eb5db7aSPatrick McHardy 
nfnetlink_subsys_register(const struct nfnetlink_subsystem * n)1167c8d4cb4SPatrick McHardy int nfnetlink_subsys_register(const struct nfnetlink_subsystem *n)
117f9e815b3SHarald Welte {
1187b7744e2SKees Cook 	u8 cb_id;
1197b7744e2SKees Cook 
1207b7744e2SKees Cook 	/* Sanity-check attr_count size to avoid stack buffer overflow. */
1217b7744e2SKees Cook 	for (cb_id = 0; cb_id < n->cb_count; cb_id++)
1227b7744e2SKees Cook 		if (WARN_ON(n->cb[cb_id].attr_count > NFNL_MAX_ATTR_COUNT))
1237b7744e2SKees Cook 			return -EINVAL;
1247b7744e2SKees Cook 
125c14b78e7SPablo Neira Ayuso 	nfnl_lock(n->subsys_id);
126c14b78e7SPablo Neira Ayuso 	if (table[n->subsys_id].subsys) {
127c14b78e7SPablo Neira Ayuso 		nfnl_unlock(n->subsys_id);
1280ab43f84SHarald Welte 		return -EBUSY;
1290ab43f84SHarald Welte 	}
130c14b78e7SPablo Neira Ayuso 	rcu_assign_pointer(table[n->subsys_id].subsys, n);
131c14b78e7SPablo Neira Ayuso 	nfnl_unlock(n->subsys_id);
132f9e815b3SHarald Welte 
133f9e815b3SHarald Welte 	return 0;
134f9e815b3SHarald Welte }
135f4bc177fSPablo Neira Ayuso EXPORT_SYMBOL_GPL(nfnetlink_subsys_register);
136f9e815b3SHarald Welte 
nfnetlink_subsys_unregister(const struct nfnetlink_subsystem * n)1377c8d4cb4SPatrick McHardy int nfnetlink_subsys_unregister(const struct nfnetlink_subsystem *n)
138f9e815b3SHarald Welte {
139c14b78e7SPablo Neira Ayuso 	nfnl_lock(n->subsys_id);
140c14b78e7SPablo Neira Ayuso 	table[n->subsys_id].subsys = NULL;
141c14b78e7SPablo Neira Ayuso 	nfnl_unlock(n->subsys_id);
1426b75e3e8SEric Dumazet 	synchronize_rcu();
143f9e815b3SHarald Welte 	return 0;
144f9e815b3SHarald Welte }
145f4bc177fSPablo Neira Ayuso EXPORT_SYMBOL_GPL(nfnetlink_subsys_unregister);
146f9e815b3SHarald Welte 
nfnetlink_get_subsys(u16 type)147b745d035SPablo Neira Ayuso static inline const struct nfnetlink_subsystem *nfnetlink_get_subsys(u16 type)
148f9e815b3SHarald Welte {
149b745d035SPablo Neira Ayuso 	u8 subsys_id = NFNL_SUBSYS_ID(type);
150f9e815b3SHarald Welte 
151ac0f1d98SPablo Neira Ayuso 	if (subsys_id >= NFNL_SUBSYS_COUNT)
152f9e815b3SHarald Welte 		return NULL;
153f9e815b3SHarald Welte 
154c14b78e7SPablo Neira Ayuso 	return rcu_dereference(table[subsys_id].subsys);
155f9e815b3SHarald Welte }
156f9e815b3SHarald Welte 
1577c8d4cb4SPatrick McHardy static inline const struct nfnl_callback *
nfnetlink_find_client(u16 type,const struct nfnetlink_subsystem * ss)158b745d035SPablo Neira Ayuso nfnetlink_find_client(u16 type, const struct nfnetlink_subsystem *ss)
159f9e815b3SHarald Welte {
160b745d035SPablo Neira Ayuso 	u8 cb_id = NFNL_MSG_TYPE(type);
161f9e815b3SHarald Welte 
16267ca3966SPablo Neira Ayuso 	if (cb_id >= ss->cb_count)
163f9e815b3SHarald Welte 		return NULL;
164f9e815b3SHarald Welte 
165f9e815b3SHarald Welte 	return &ss->cb[cb_id];
166f9e815b3SHarald Welte }
167f9e815b3SHarald Welte 
nfnetlink_has_listeners(struct net * net,unsigned int group)168cd8c20b6SAlexey Dobriyan int nfnetlink_has_listeners(struct net *net, unsigned int group)
169a2427692SPatrick McHardy {
1701be05ea7SFlorian Westphal 	struct nfnl_net *nfnlnet = nfnl_pernet(net);
1711be05ea7SFlorian Westphal 
1721be05ea7SFlorian Westphal 	return netlink_has_listeners(nfnlnet->nfnl, group);
173a2427692SPatrick McHardy }
174a2427692SPatrick McHardy EXPORT_SYMBOL_GPL(nfnetlink_has_listeners);
175a2427692SPatrick McHardy 
nfnetlink_send(struct sk_buff * skb,struct net * net,u32 portid,unsigned int group,int echo,gfp_t flags)176ec464e5dSPatrick McHardy int nfnetlink_send(struct sk_buff *skb, struct net *net, u32 portid,
17795c96174SEric Dumazet 		   unsigned int group, int echo, gfp_t flags)
178f9e815b3SHarald Welte {
1791be05ea7SFlorian Westphal 	struct nfnl_net *nfnlnet = nfnl_pernet(net);
1801be05ea7SFlorian Westphal 
1811be05ea7SFlorian Westphal 	return nlmsg_notify(nfnlnet->nfnl, skb, portid, group, echo, flags);
182f9e815b3SHarald Welte }
183f4bc177fSPablo Neira Ayuso EXPORT_SYMBOL_GPL(nfnetlink_send);
184f9e815b3SHarald Welte 
nfnetlink_set_err(struct net * net,u32 portid,u32 group,int error)185ec464e5dSPatrick McHardy int nfnetlink_set_err(struct net *net, u32 portid, u32 group, int error)
186dd5b6ce6SPablo Neira Ayuso {
1871be05ea7SFlorian Westphal 	struct nfnl_net *nfnlnet = nfnl_pernet(net);
1881be05ea7SFlorian Westphal 
1891be05ea7SFlorian Westphal 	return netlink_set_err(nfnlnet->nfnl, portid, group, error);
190dd5b6ce6SPablo Neira Ayuso }
191dd5b6ce6SPablo Neira Ayuso EXPORT_SYMBOL_GPL(nfnetlink_set_err);
192dd5b6ce6SPablo Neira Ayuso 
nfnetlink_unicast(struct sk_buff * skb,struct net * net,u32 portid)193ee921183SPablo Neira Ayuso int nfnetlink_unicast(struct sk_buff *skb, struct net *net, u32 portid)
194f9e815b3SHarald Welte {
1951be05ea7SFlorian Westphal 	struct nfnl_net *nfnlnet = nfnl_pernet(net);
196ee921183SPablo Neira Ayuso 	int err;
197ee921183SPablo Neira Ayuso 
1981be05ea7SFlorian Westphal 	err = nlmsg_unicast(nfnlnet->nfnl, skb, portid);
199ee921183SPablo Neira Ayuso 	if (err == -EAGAIN)
200ee921183SPablo Neira Ayuso 		err = -ENOBUFS;
201ee921183SPablo Neira Ayuso 
202ee921183SPablo Neira Ayuso 	return err;
203f9e815b3SHarald Welte }
204f4bc177fSPablo Neira Ayuso EXPORT_SYMBOL_GPL(nfnetlink_unicast);
205f9e815b3SHarald Welte 
nfnetlink_broadcast(struct net * net,struct sk_buff * skb,__u32 portid,__u32 group,gfp_t allocation)206237c609fSFlorian Westphal void nfnetlink_broadcast(struct net *net, struct sk_buff *skb, __u32 portid,
207237c609fSFlorian Westphal 			 __u32 group, gfp_t allocation)
208237c609fSFlorian Westphal {
2091be05ea7SFlorian Westphal 	struct nfnl_net *nfnlnet = nfnl_pernet(net);
2101be05ea7SFlorian Westphal 
2111be05ea7SFlorian Westphal 	netlink_broadcast(nfnlnet->nfnl, skb, portid, group, allocation);
212237c609fSFlorian Westphal }
213237c609fSFlorian Westphal EXPORT_SYMBOL_GPL(nfnetlink_broadcast);
214237c609fSFlorian Westphal 
215f9e815b3SHarald Welte /* Process one complete nfnetlink message. */
nfnetlink_rcv_msg(struct sk_buff * skb,struct nlmsghdr * nlh,struct netlink_ext_ack * extack)2162d4bc933SJohannes Berg static int nfnetlink_rcv_msg(struct sk_buff *skb, struct nlmsghdr *nlh,
2172d4bc933SJohannes Berg 			     struct netlink_ext_ack *extack)
218f9e815b3SHarald Welte {
219cd8c20b6SAlexey Dobriyan 	struct net *net = sock_net(skb->sk);
2207c8d4cb4SPatrick McHardy 	const struct nfnl_callback *nc;
2217c8d4cb4SPatrick McHardy 	const struct nfnetlink_subsystem *ss;
2221d00a4ebSThomas Graf 	int type, err;
223f9e815b3SHarald Welte 
224f9e815b3SHarald Welte 	/* All the messages must at least contain nfgenmsg */
225573ce260SHong zhi guo 	if (nlmsg_len(nlh) < sizeof(struct nfgenmsg))
226f9e815b3SHarald Welte 		return 0;
227f9e815b3SHarald Welte 
228f9e815b3SHarald Welte 	type = nlh->nlmsg_type;
229e6a7d3c0SPablo Neira Ayuso replay:
2306b75e3e8SEric Dumazet 	rcu_read_lock();
2311be05ea7SFlorian Westphal 
232f9e815b3SHarald Welte 	ss = nfnetlink_get_subsys(type);
2330ab43f84SHarald Welte 	if (!ss) {
23495a5afcaSJohannes Berg #ifdef CONFIG_MODULES
2356b75e3e8SEric Dumazet 		rcu_read_unlock();
23637d2e7a2SHarald Welte 		request_module("nfnetlink-subsys-%d", NFNL_SUBSYS_ID(type));
2376b75e3e8SEric Dumazet 		rcu_read_lock();
2380ab43f84SHarald Welte 		ss = nfnetlink_get_subsys(type);
239f9e815b3SHarald Welte 		if (!ss)
2400ab43f84SHarald Welte #endif
2416b75e3e8SEric Dumazet 		{
2426b75e3e8SEric Dumazet 			rcu_read_unlock();
2431d00a4ebSThomas Graf 			return -EINVAL;
2440ab43f84SHarald Welte 		}
2456b75e3e8SEric Dumazet 	}
246f9e815b3SHarald Welte 
247f9e815b3SHarald Welte 	nc = nfnetlink_find_client(type, ss);
2486b75e3e8SEric Dumazet 	if (!nc) {
2496b75e3e8SEric Dumazet 		rcu_read_unlock();
2501d00a4ebSThomas Graf 		return -EINVAL;
2516b75e3e8SEric Dumazet 	}
252f9e815b3SHarald Welte 
253f9e815b3SHarald Welte 	{
254573ce260SHong zhi guo 		int min_len = nlmsg_total_size(sizeof(struct nfgenmsg));
2551be05ea7SFlorian Westphal 		struct nfnl_net *nfnlnet = nfnl_pernet(net);
256b745d035SPablo Neira Ayuso 		u8 cb_id = NFNL_MSG_TYPE(nlh->nlmsg_type);
2577b7744e2SKees Cook 		struct nlattr *cda[NFNL_MAX_ATTR_COUNT + 1];
258f49c857fSPablo Neira Ayuso 		struct nlattr *attr = (void *)nlh + min_len;
259f49c857fSPablo Neira Ayuso 		int attrlen = nlh->nlmsg_len - min_len;
260c14b78e7SPablo Neira Ayuso 		__u8 subsys_id = NFNL_SUBSYS_ID(type);
261a6555365SPablo Neira Ayuso 		struct nfnl_info info = {
262a6555365SPablo Neira Ayuso 			.net	= net,
263a6555365SPablo Neira Ayuso 			.sk	= nfnlnet->nfnl,
264a6555365SPablo Neira Ayuso 			.nlh	= nlh,
265ef4b65e5SPablo Neira Ayuso 			.nfmsg	= nlmsg_data(nlh),
266a6555365SPablo Neira Ayuso 			.extack	= extack,
267a6555365SPablo Neira Ayuso 		};
268f9e815b3SHarald Welte 
2697b7744e2SKees Cook 		/* Sanity-check NFNL_MAX_ATTR_COUNT */
2707b7744e2SKees Cook 		if (ss->cb[cb_id].attr_count > NFNL_MAX_ATTR_COUNT) {
2717b7744e2SKees Cook 			rcu_read_unlock();
2727b7744e2SKees Cook 			return -ENOMEM;
2737b7744e2SKees Cook 		}
2747b7744e2SKees Cook 
2758cb08174SJohannes Berg 		err = nla_parse_deprecated(cda, ss->cb[cb_id].attr_count,
2768cb08174SJohannes Berg 					   attr, attrlen,
277fe52145fSJohannes Berg 					   ss->cb[cb_id].policy, extack);
2784009e188STomasz Bursztyka 		if (err < 0) {
2794009e188STomasz Bursztyka 			rcu_read_unlock();
280f9e815b3SHarald Welte 			return err;
2814009e188STomasz Bursztyka 		}
282e3730578SPatrick McHardy 
28350f2db9eSPablo Neira Ayuso 		if (!nc->call) {
2846b75e3e8SEric Dumazet 			rcu_read_unlock();
28550f2db9eSPablo Neira Ayuso 			return -EINVAL;
28650f2db9eSPablo Neira Ayuso 		}
28750f2db9eSPablo Neira Ayuso 
28850f2db9eSPablo Neira Ayuso 		switch (nc->type) {
28950f2db9eSPablo Neira Ayuso 		case NFNL_CB_RCU:
29050f2db9eSPablo Neira Ayuso 			err = nc->call(skb, &info, (const struct nlattr **)cda);
29150f2db9eSPablo Neira Ayuso 			rcu_read_unlock();
29250f2db9eSPablo Neira Ayuso 			break;
29350f2db9eSPablo Neira Ayuso 		case NFNL_CB_MUTEX:
2946b75e3e8SEric Dumazet 			rcu_read_unlock();
295c14b78e7SPablo Neira Ayuso 			nfnl_lock(subsys_id);
2969c55d3b5SFlorian Westphal 			if (nfnl_dereference_protected(subsys_id) != ss ||
297a6555365SPablo Neira Ayuso 			    nfnetlink_find_client(type, ss) != nc) {
29803832a32SZiyang Xuan 				nfnl_unlock(subsys_id);
2996b75e3e8SEric Dumazet 				err = -EAGAIN;
30050f2db9eSPablo Neira Ayuso 				break;
301a6555365SPablo Neira Ayuso 			}
30250f2db9eSPablo Neira Ayuso 			err = nc->call(skb, &info, (const struct nlattr **)cda);
303c14b78e7SPablo Neira Ayuso 			nfnl_unlock(subsys_id);
30450f2db9eSPablo Neira Ayuso 			break;
30550f2db9eSPablo Neira Ayuso 		default:
3067072a355SEric Dumazet 			rcu_read_unlock();
30750f2db9eSPablo Neira Ayuso 			err = -EINVAL;
30850f2db9eSPablo Neira Ayuso 			break;
3096b75e3e8SEric Dumazet 		}
310e6a7d3c0SPablo Neira Ayuso 		if (err == -EAGAIN)
311e6a7d3c0SPablo Neira Ayuso 			goto replay;
312e6a7d3c0SPablo Neira Ayuso 		return err;
313f9e815b3SHarald Welte 	}
314f9e815b3SHarald Welte }
315f9e815b3SHarald Welte 
316cbb8125eSPablo Neira Ayuso struct nfnl_err {
317cbb8125eSPablo Neira Ayuso 	struct list_head	head;
318cbb8125eSPablo Neira Ayuso 	struct nlmsghdr		*nlh;
319cbb8125eSPablo Neira Ayuso 	int			err;
32004ba724bSPablo Neira Ayuso 	struct netlink_ext_ack	extack;
321cbb8125eSPablo Neira Ayuso };
322cbb8125eSPablo Neira Ayuso 
nfnl_err_add(struct list_head * list,struct nlmsghdr * nlh,int err,const struct netlink_ext_ack * extack)32304ba724bSPablo Neira Ayuso static int nfnl_err_add(struct list_head *list, struct nlmsghdr *nlh, int err,
32404ba724bSPablo Neira Ayuso 			const struct netlink_ext_ack *extack)
325cbb8125eSPablo Neira Ayuso {
326cbb8125eSPablo Neira Ayuso 	struct nfnl_err *nfnl_err;
327cbb8125eSPablo Neira Ayuso 
328cbb8125eSPablo Neira Ayuso 	nfnl_err = kmalloc(sizeof(struct nfnl_err), GFP_KERNEL);
329cbb8125eSPablo Neira Ayuso 	if (nfnl_err == NULL)
330cbb8125eSPablo Neira Ayuso 		return -ENOMEM;
331cbb8125eSPablo Neira Ayuso 
332cbb8125eSPablo Neira Ayuso 	nfnl_err->nlh = nlh;
333cbb8125eSPablo Neira Ayuso 	nfnl_err->err = err;
33404ba724bSPablo Neira Ayuso 	nfnl_err->extack = *extack;
335cbb8125eSPablo Neira Ayuso 	list_add_tail(&nfnl_err->head, list);
336cbb8125eSPablo Neira Ayuso 
337cbb8125eSPablo Neira Ayuso 	return 0;
338cbb8125eSPablo Neira Ayuso }
339cbb8125eSPablo Neira Ayuso 
nfnl_err_del(struct nfnl_err * nfnl_err)340cbb8125eSPablo Neira Ayuso static void nfnl_err_del(struct nfnl_err *nfnl_err)
341cbb8125eSPablo Neira Ayuso {
342cbb8125eSPablo Neira Ayuso 	list_del(&nfnl_err->head);
343cbb8125eSPablo Neira Ayuso 	kfree(nfnl_err);
344cbb8125eSPablo Neira Ayuso }
345cbb8125eSPablo Neira Ayuso 
nfnl_err_reset(struct list_head * err_list)346cbb8125eSPablo Neira Ayuso static void nfnl_err_reset(struct list_head *err_list)
347cbb8125eSPablo Neira Ayuso {
348cbb8125eSPablo Neira Ayuso 	struct nfnl_err *nfnl_err, *next;
349cbb8125eSPablo Neira Ayuso 
350cbb8125eSPablo Neira Ayuso 	list_for_each_entry_safe(nfnl_err, next, err_list, head)
351cbb8125eSPablo Neira Ayuso 		nfnl_err_del(nfnl_err);
352cbb8125eSPablo Neira Ayuso }
353cbb8125eSPablo Neira Ayuso 
nfnl_err_deliver(struct list_head * err_list,struct sk_buff * skb)354cbb8125eSPablo Neira Ayuso static void nfnl_err_deliver(struct list_head *err_list, struct sk_buff *skb)
355cbb8125eSPablo Neira Ayuso {
356cbb8125eSPablo Neira Ayuso 	struct nfnl_err *nfnl_err, *next;
357cbb8125eSPablo Neira Ayuso 
358cbb8125eSPablo Neira Ayuso 	list_for_each_entry_safe(nfnl_err, next, err_list, head) {
35904ba724bSPablo Neira Ayuso 		netlink_ack(skb, nfnl_err->nlh, nfnl_err->err,
36004ba724bSPablo Neira Ayuso 			    &nfnl_err->extack);
361cbb8125eSPablo Neira Ayuso 		nfnl_err_del(nfnl_err);
362cbb8125eSPablo Neira Ayuso 	}
363cbb8125eSPablo Neira Ayuso }
364cbb8125eSPablo Neira Ayuso 
3656742b9e3SPablo Neira Ayuso enum {
3666742b9e3SPablo Neira Ayuso 	NFNL_BATCH_FAILURE	= (1 << 0),
3676742b9e3SPablo Neira Ayuso 	NFNL_BATCH_DONE		= (1 << 1),
3686742b9e3SPablo Neira Ayuso 	NFNL_BATCH_REPLAY	= (1 << 2),
3696742b9e3SPablo Neira Ayuso };
3706742b9e3SPablo Neira Ayuso 
nfnetlink_rcv_batch(struct sk_buff * skb,struct nlmsghdr * nlh,u16 subsys_id,u32 genid)3710628b123SPablo Neira Ayuso static void nfnetlink_rcv_batch(struct sk_buff *skb, struct nlmsghdr *nlh,
3728c4d4e8bSPablo Neira Ayuso 				u16 subsys_id, u32 genid)
3730628b123SPablo Neira Ayuso {
3740f816232SDuan Jiong 	struct sk_buff *oskb = skb;
3750628b123SPablo Neira Ayuso 	struct net *net = sock_net(skb->sk);
3760628b123SPablo Neira Ayuso 	const struct nfnetlink_subsystem *ss;
3770628b123SPablo Neira Ayuso 	const struct nfnl_callback *nc;
37804ba724bSPablo Neira Ayuso 	struct netlink_ext_ack extack;
3794eba8b78SLiping Zhang 	LIST_HEAD(err_list);
3806742b9e3SPablo Neira Ayuso 	u32 status;
3810628b123SPablo Neira Ayuso 	int err;
3820628b123SPablo Neira Ayuso 
3830628b123SPablo Neira Ayuso 	if (subsys_id >= NFNL_SUBSYS_COUNT)
3842d4bc933SJohannes Berg 		return netlink_ack(skb, nlh, -EINVAL, NULL);
3850628b123SPablo Neira Ayuso replay:
3866742b9e3SPablo Neira Ayuso 	status = 0;
387c0391b6aSPablo Neira Ayuso replay_abort:
3880f816232SDuan Jiong 	skb = netlink_skb_clone(oskb, GFP_KERNEL);
3890f816232SDuan Jiong 	if (!skb)
3902d4bc933SJohannes Berg 		return netlink_ack(oskb, nlh, -ENOMEM, NULL);
3910628b123SPablo Neira Ayuso 
3920628b123SPablo Neira Ayuso 	nfnl_lock(subsys_id);
3939c55d3b5SFlorian Westphal 	ss = nfnl_dereference_protected(subsys_id);
3940628b123SPablo Neira Ayuso 	if (!ss) {
3950628b123SPablo Neira Ayuso #ifdef CONFIG_MODULES
3960628b123SPablo Neira Ayuso 		nfnl_unlock(subsys_id);
3970628b123SPablo Neira Ayuso 		request_module("nfnetlink-subsys-%d", subsys_id);
3980628b123SPablo Neira Ayuso 		nfnl_lock(subsys_id);
3999c55d3b5SFlorian Westphal 		ss = nfnl_dereference_protected(subsys_id);
4000628b123SPablo Neira Ayuso 		if (!ss)
4010628b123SPablo Neira Ayuso #endif
4020628b123SPablo Neira Ayuso 		{
4030628b123SPablo Neira Ayuso 			nfnl_unlock(subsys_id);
4042d4bc933SJohannes Berg 			netlink_ack(oskb, nlh, -EOPNOTSUPP, NULL);
4050f816232SDuan Jiong 			return kfree_skb(skb);
4060628b123SPablo Neira Ayuso 		}
4070628b123SPablo Neira Ayuso 	}
4080628b123SPablo Neira Ayuso 
409ca2f18beSFlorian Westphal 	if (!ss->valid_genid || !ss->commit || !ss->abort) {
4100628b123SPablo Neira Ayuso 		nfnl_unlock(subsys_id);
4112d4bc933SJohannes Berg 		netlink_ack(oskb, nlh, -EOPNOTSUPP, NULL);
412ecd15dd7SDenys Fedoryshchenko 		return kfree_skb(skb);
4130628b123SPablo Neira Ayuso 	}
4140628b123SPablo Neira Ayuso 
415be2ab5b4SFlorian Westphal 	if (!try_module_get(ss->owner)) {
416be2ab5b4SFlorian Westphal 		nfnl_unlock(subsys_id);
417be2ab5b4SFlorian Westphal 		netlink_ack(oskb, nlh, -EOPNOTSUPP, NULL);
418be2ab5b4SFlorian Westphal 		return kfree_skb(skb);
419be2ab5b4SFlorian Westphal 	}
420be2ab5b4SFlorian Westphal 
421ca2f18beSFlorian Westphal 	if (!ss->valid_genid(net, genid)) {
422be2ab5b4SFlorian Westphal 		module_put(ss->owner);
4238c4d4e8bSPablo Neira Ayuso 		nfnl_unlock(subsys_id);
4242d4bc933SJohannes Berg 		netlink_ack(oskb, nlh, -ERESTART, NULL);
4258c4d4e8bSPablo Neira Ayuso 		return kfree_skb(skb);
4268c4d4e8bSPablo Neira Ayuso 	}
4278c4d4e8bSPablo Neira Ayuso 
428f102d66bSFlorian Westphal 	nfnl_unlock(subsys_id);
429f102d66bSFlorian Westphal 
4300628b123SPablo Neira Ayuso 	while (skb->len >= nlmsg_total_size(0)) {
4310628b123SPablo Neira Ayuso 		int msglen, type;
4320628b123SPablo Neira Ayuso 
4338a3d4c36SFlorian Westphal 		if (fatal_signal_pending(current)) {
4348a3d4c36SFlorian Westphal 			nfnl_err_reset(&err_list);
4358a3d4c36SFlorian Westphal 			err = -EINTR;
4368a3d4c36SFlorian Westphal 			status = NFNL_BATCH_FAILURE;
4378a3d4c36SFlorian Westphal 			goto done;
4388a3d4c36SFlorian Westphal 		}
4398a3d4c36SFlorian Westphal 
44004ba724bSPablo Neira Ayuso 		memset(&extack, 0, sizeof(extack));
4410628b123SPablo Neira Ayuso 		nlh = nlmsg_hdr(skb);
4420628b123SPablo Neira Ayuso 		err = 0;
4430628b123SPablo Neira Ayuso 
444c58d6c93SPhil Turnbull 		if (nlh->nlmsg_len < NLMSG_HDRLEN ||
445c58d6c93SPhil Turnbull 		    skb->len < nlh->nlmsg_len ||
446c58d6c93SPhil Turnbull 		    nlmsg_len(nlh) < sizeof(struct nfgenmsg)) {
447c58d6c93SPhil Turnbull 			nfnl_err_reset(&err_list);
448c58d6c93SPhil Turnbull 			status |= NFNL_BATCH_FAILURE;
449c58d6c93SPhil Turnbull 			goto done;
4500628b123SPablo Neira Ayuso 		}
4510628b123SPablo Neira Ayuso 
4520628b123SPablo Neira Ayuso 		/* Only requests are handled by the kernel */
4530628b123SPablo Neira Ayuso 		if (!(nlh->nlmsg_flags & NLM_F_REQUEST)) {
4540628b123SPablo Neira Ayuso 			err = -EINVAL;
4550628b123SPablo Neira Ayuso 			goto ack;
4560628b123SPablo Neira Ayuso 		}
4570628b123SPablo Neira Ayuso 
4580628b123SPablo Neira Ayuso 		type = nlh->nlmsg_type;
4590628b123SPablo Neira Ayuso 		if (type == NFNL_MSG_BATCH_BEGIN) {
4600628b123SPablo Neira Ayuso 			/* Malformed: Batch begin twice */
461cbb8125eSPablo Neira Ayuso 			nfnl_err_reset(&err_list);
4626742b9e3SPablo Neira Ayuso 			status |= NFNL_BATCH_FAILURE;
4630628b123SPablo Neira Ayuso 			goto done;
4640628b123SPablo Neira Ayuso 		} else if (type == NFNL_MSG_BATCH_END) {
4656742b9e3SPablo Neira Ayuso 			status |= NFNL_BATCH_DONE;
4660628b123SPablo Neira Ayuso 			goto done;
4670628b123SPablo Neira Ayuso 		} else if (type < NLMSG_MIN_TYPE) {
4680628b123SPablo Neira Ayuso 			err = -EINVAL;
4690628b123SPablo Neira Ayuso 			goto ack;
4700628b123SPablo Neira Ayuso 		}
4710628b123SPablo Neira Ayuso 
4720628b123SPablo Neira Ayuso 		/* We only accept a batch with messages for the same
4730628b123SPablo Neira Ayuso 		 * subsystem.
4740628b123SPablo Neira Ayuso 		 */
4750628b123SPablo Neira Ayuso 		if (NFNL_SUBSYS_ID(type) != subsys_id) {
4760628b123SPablo Neira Ayuso 			err = -EINVAL;
4770628b123SPablo Neira Ayuso 			goto ack;
4780628b123SPablo Neira Ayuso 		}
4790628b123SPablo Neira Ayuso 
4800628b123SPablo Neira Ayuso 		nc = nfnetlink_find_client(type, ss);
4810628b123SPablo Neira Ayuso 		if (!nc) {
4820628b123SPablo Neira Ayuso 			err = -EINVAL;
4830628b123SPablo Neira Ayuso 			goto ack;
4840628b123SPablo Neira Ayuso 		}
4850628b123SPablo Neira Ayuso 
48650f2db9eSPablo Neira Ayuso 		if (nc->type != NFNL_CB_BATCH) {
48750f2db9eSPablo Neira Ayuso 			err = -EINVAL;
48850f2db9eSPablo Neira Ayuso 			goto ack;
48950f2db9eSPablo Neira Ayuso 		}
49050f2db9eSPablo Neira Ayuso 
4910628b123SPablo Neira Ayuso 		{
4920628b123SPablo Neira Ayuso 			int min_len = nlmsg_total_size(sizeof(struct nfgenmsg));
4937dab8ee3SPablo Neira Ayuso 			struct nfnl_net *nfnlnet = nfnl_pernet(net);
4947b7744e2SKees Cook 			struct nlattr *cda[NFNL_MAX_ATTR_COUNT + 1];
4950628b123SPablo Neira Ayuso 			struct nlattr *attr = (void *)nlh + min_len;
49650f2db9eSPablo Neira Ayuso 			u8 cb_id = NFNL_MSG_TYPE(nlh->nlmsg_type);
4970628b123SPablo Neira Ayuso 			int attrlen = nlh->nlmsg_len - min_len;
4987dab8ee3SPablo Neira Ayuso 			struct nfnl_info info = {
4997dab8ee3SPablo Neira Ayuso 				.net	= net,
5007dab8ee3SPablo Neira Ayuso 				.sk	= nfnlnet->nfnl,
5017dab8ee3SPablo Neira Ayuso 				.nlh	= nlh,
502ef4b65e5SPablo Neira Ayuso 				.nfmsg	= nlmsg_data(nlh),
5037dab8ee3SPablo Neira Ayuso 				.extack	= &extack,
5047dab8ee3SPablo Neira Ayuso 			};
5050628b123SPablo Neira Ayuso 
5067b7744e2SKees Cook 			/* Sanity-check NFTA_MAX_ATTR */
5077b7744e2SKees Cook 			if (ss->cb[cb_id].attr_count > NFNL_MAX_ATTR_COUNT) {
5087b7744e2SKees Cook 				err = -ENOMEM;
5097b7744e2SKees Cook 				goto ack;
5107b7744e2SKees Cook 			}
5117b7744e2SKees Cook 
5128cb08174SJohannes Berg 			err = nla_parse_deprecated(cda,
5138cb08174SJohannes Berg 						   ss->cb[cb_id].attr_count,
5148cb08174SJohannes Berg 						   attr, attrlen,
5158cb08174SJohannes Berg 						   ss->cb[cb_id].policy, NULL);
5160628b123SPablo Neira Ayuso 			if (err < 0)
5170628b123SPablo Neira Ayuso 				goto ack;
5180628b123SPablo Neira Ayuso 
51950f2db9eSPablo Neira Ayuso 			err = nc->call(skb, &info, (const struct nlattr **)cda);
5200628b123SPablo Neira Ayuso 
5210628b123SPablo Neira Ayuso 			/* The lock was released to autoload some module, we
5220628b123SPablo Neira Ayuso 			 * have to abort and start from scratch using the
5230628b123SPablo Neira Ayuso 			 * original skb.
5240628b123SPablo Neira Ayuso 			 */
5250628b123SPablo Neira Ayuso 			if (err == -EAGAIN) {
5266742b9e3SPablo Neira Ayuso 				status |= NFNL_BATCH_REPLAY;
52771ad00c5SFlorian Westphal 				goto done;
5280628b123SPablo Neira Ayuso 			}
5290628b123SPablo Neira Ayuso 		}
5300628b123SPablo Neira Ayuso ack:
5310628b123SPablo Neira Ayuso 		if (nlh->nlmsg_flags & NLM_F_ACK || err) {
532cbb8125eSPablo Neira Ayuso 			/* Errors are delivered once the full batch has been
533cbb8125eSPablo Neira Ayuso 			 * processed, this avoids that the same error is
534cbb8125eSPablo Neira Ayuso 			 * reported several times when replaying the batch.
535cbb8125eSPablo Neira Ayuso 			 */
536*a1a64a15SPablo Neira Ayuso 			if (err == -ENOMEM ||
537*a1a64a15SPablo Neira Ayuso 			    nfnl_err_add(&err_list, nlh, err, &extack) < 0) {
538cbb8125eSPablo Neira Ayuso 				/* We failed to enqueue an error, reset the
539cbb8125eSPablo Neira Ayuso 				 * list of errors and send OOM to userspace
540cbb8125eSPablo Neira Ayuso 				 * pointing to the batch header.
541cbb8125eSPablo Neira Ayuso 				 */
542cbb8125eSPablo Neira Ayuso 				nfnl_err_reset(&err_list);
5432d4bc933SJohannes Berg 				netlink_ack(oskb, nlmsg_hdr(oskb), -ENOMEM,
5442d4bc933SJohannes Berg 					    NULL);
5456742b9e3SPablo Neira Ayuso 				status |= NFNL_BATCH_FAILURE;
546cbb8125eSPablo Neira Ayuso 				goto done;
547cbb8125eSPablo Neira Ayuso 			}
5480628b123SPablo Neira Ayuso 			/* We don't stop processing the batch on errors, thus,
5490628b123SPablo Neira Ayuso 			 * userspace gets all the errors that the batch
5500628b123SPablo Neira Ayuso 			 * triggers.
5510628b123SPablo Neira Ayuso 			 */
5520628b123SPablo Neira Ayuso 			if (err)
5536742b9e3SPablo Neira Ayuso 				status |= NFNL_BATCH_FAILURE;
5540628b123SPablo Neira Ayuso 		}
55571ad00c5SFlorian Westphal 
5560628b123SPablo Neira Ayuso 		msglen = NLMSG_ALIGN(nlh->nlmsg_len);
5570628b123SPablo Neira Ayuso 		if (msglen > skb->len)
5580628b123SPablo Neira Ayuso 			msglen = skb->len;
5590628b123SPablo Neira Ayuso 		skb_pull(skb, msglen);
5600628b123SPablo Neira Ayuso 	}
5610628b123SPablo Neira Ayuso done:
5626742b9e3SPablo Neira Ayuso 	if (status & NFNL_BATCH_REPLAY) {
563c0391b6aSPablo Neira Ayuso 		ss->abort(net, oskb, NFNL_ABORT_AUTOLOAD);
5646742b9e3SPablo Neira Ayuso 		nfnl_err_reset(&err_list);
5656742b9e3SPablo Neira Ayuso 		kfree_skb(skb);
566be2ab5b4SFlorian Westphal 		module_put(ss->owner);
5676742b9e3SPablo Neira Ayuso 		goto replay;
5686742b9e3SPablo Neira Ayuso 	} else if (status == NFNL_BATCH_DONE) {
56900308791SFlorian Westphal 		err = ss->commit(net, oskb);
57000308791SFlorian Westphal 		if (err == -EAGAIN) {
57100308791SFlorian Westphal 			status |= NFNL_BATCH_REPLAY;
57200308791SFlorian Westphal 			goto done;
57300308791SFlorian Westphal 		} else if (err) {
574c0391b6aSPablo Neira Ayuso 			ss->abort(net, oskb, NFNL_ABORT_NONE);
57500308791SFlorian Westphal 			netlink_ack(oskb, nlmsg_hdr(oskb), err, NULL);
57600308791SFlorian Westphal 		}
5776742b9e3SPablo Neira Ayuso 	} else {
578c0391b6aSPablo Neira Ayuso 		enum nfnl_abort_action abort_action;
579c0391b6aSPablo Neira Ayuso 
580c0391b6aSPablo Neira Ayuso 		if (status & NFNL_BATCH_FAILURE)
581c0391b6aSPablo Neira Ayuso 			abort_action = NFNL_ABORT_NONE;
582c0391b6aSPablo Neira Ayuso 		else
583c0391b6aSPablo Neira Ayuso 			abort_action = NFNL_ABORT_VALIDATE;
584c0391b6aSPablo Neira Ayuso 
585c0391b6aSPablo Neira Ayuso 		err = ss->abort(net, oskb, abort_action);
586c0391b6aSPablo Neira Ayuso 		if (err == -EAGAIN) {
587c0391b6aSPablo Neira Ayuso 			nfnl_err_reset(&err_list);
588c0391b6aSPablo Neira Ayuso 			kfree_skb(skb);
589c0391b6aSPablo Neira Ayuso 			module_put(ss->owner);
590c0391b6aSPablo Neira Ayuso 			status |= NFNL_BATCH_FAILURE;
591c0391b6aSPablo Neira Ayuso 			goto replay_abort;
592c0391b6aSPablo Neira Ayuso 		}
5936742b9e3SPablo Neira Ayuso 	}
5940628b123SPablo Neira Ayuso 
595cbb8125eSPablo Neira Ayuso 	nfnl_err_deliver(&err_list, oskb);
5960f816232SDuan Jiong 	kfree_skb(skb);
597be2ab5b4SFlorian Westphal 	module_put(ss->owner);
5980628b123SPablo Neira Ayuso }
5990628b123SPablo Neira Ayuso 
6008c4d4e8bSPablo Neira Ayuso static const struct nla_policy nfnl_batch_policy[NFNL_BATCH_MAX + 1] = {
6018c4d4e8bSPablo Neira Ayuso 	[NFNL_BATCH_GENID]	= { .type = NLA_U32 },
6028c4d4e8bSPablo Neira Ayuso };
6038c4d4e8bSPablo Neira Ayuso 
nfnetlink_rcv_skb_batch(struct sk_buff * skb,struct nlmsghdr * nlh)60448656835SPablo Neira Ayuso static void nfnetlink_rcv_skb_batch(struct sk_buff *skb, struct nlmsghdr *nlh)
605f9e815b3SHarald Welte {
6068c4d4e8bSPablo Neira Ayuso 	int min_len = nlmsg_total_size(sizeof(struct nfgenmsg));
6078c4d4e8bSPablo Neira Ayuso 	struct nlattr *attr = (void *)nlh + min_len;
6088c4d4e8bSPablo Neira Ayuso 	struct nlattr *cda[NFNL_BATCH_MAX + 1];
6098c4d4e8bSPablo Neira Ayuso 	int attrlen = nlh->nlmsg_len - min_len;
61048656835SPablo Neira Ayuso 	struct nfgenmsg *nfgenmsg;
6118c4d4e8bSPablo Neira Ayuso 	int msglen, err;
6128c4d4e8bSPablo Neira Ayuso 	u32 gen_id = 0;
613b745d035SPablo Neira Ayuso 	u16 res_id;
6140628b123SPablo Neira Ayuso 
6150628b123SPablo Neira Ayuso 	msglen = NLMSG_ALIGN(nlh->nlmsg_len);
6160628b123SPablo Neira Ayuso 	if (msglen > skb->len)
6170628b123SPablo Neira Ayuso 		msglen = skb->len;
6180628b123SPablo Neira Ayuso 
619f55ce7b0SMateusz Jurczyk 	if (skb->len < NLMSG_HDRLEN + sizeof(struct nfgenmsg))
6200628b123SPablo Neira Ayuso 		return;
6210628b123SPablo Neira Ayuso 
6228cb08174SJohannes Berg 	err = nla_parse_deprecated(cda, NFNL_BATCH_MAX, attr, attrlen,
6238cb08174SJohannes Berg 				   nfnl_batch_policy, NULL);
6248c4d4e8bSPablo Neira Ayuso 	if (err < 0) {
6252d4bc933SJohannes Berg 		netlink_ack(skb, nlh, err, NULL);
6268c4d4e8bSPablo Neira Ayuso 		return;
6278c4d4e8bSPablo Neira Ayuso 	}
6288c4d4e8bSPablo Neira Ayuso 	if (cda[NFNL_BATCH_GENID])
6298c4d4e8bSPablo Neira Ayuso 		gen_id = ntohl(nla_get_be32(cda[NFNL_BATCH_GENID]));
6308c4d4e8bSPablo Neira Ayuso 
6310628b123SPablo Neira Ayuso 	nfgenmsg = nlmsg_data(nlh);
6320628b123SPablo Neira Ayuso 	skb_pull(skb, msglen);
633a9de9777SPablo Neira Ayuso 	/* Work around old nft using host byte order */
634ec6f2ff0SFlorian Westphal 	if (nfgenmsg->res_id == (__force __be16)NFNL_SUBSYS_NFTABLES)
635a9de9777SPablo Neira Ayuso 		res_id = NFNL_SUBSYS_NFTABLES;
636a9de9777SPablo Neira Ayuso 	else
637a9de9777SPablo Neira Ayuso 		res_id = ntohs(nfgenmsg->res_id);
63848656835SPablo Neira Ayuso 
6398c4d4e8bSPablo Neira Ayuso 	nfnetlink_rcv_batch(skb, nlh, res_id, gen_id);
640f9e815b3SHarald Welte }
64148656835SPablo Neira Ayuso 
nfnetlink_rcv(struct sk_buff * skb)64248656835SPablo Neira Ayuso static void nfnetlink_rcv(struct sk_buff *skb)
64348656835SPablo Neira Ayuso {
64448656835SPablo Neira Ayuso 	struct nlmsghdr *nlh = nlmsg_hdr(skb);
64548656835SPablo Neira Ayuso 
646f55ce7b0SMateusz Jurczyk 	if (skb->len < NLMSG_HDRLEN ||
647f55ce7b0SMateusz Jurczyk 	    nlh->nlmsg_len < NLMSG_HDRLEN ||
64848656835SPablo Neira Ayuso 	    skb->len < nlh->nlmsg_len)
64948656835SPablo Neira Ayuso 		return;
65048656835SPablo Neira Ayuso 
65148656835SPablo Neira Ayuso 	if (!netlink_net_capable(skb, CAP_NET_ADMIN)) {
6522d4bc933SJohannes Berg 		netlink_ack(skb, nlh, -EPERM, NULL);
65348656835SPablo Neira Ayuso 		return;
65448656835SPablo Neira Ayuso 	}
65548656835SPablo Neira Ayuso 
65648656835SPablo Neira Ayuso 	if (nlh->nlmsg_type == NFNL_MSG_BATCH_BEGIN)
65748656835SPablo Neira Ayuso 		nfnetlink_rcv_skb_batch(skb, nlh);
65848656835SPablo Neira Ayuso 	else
659d4ef3835SArushi Singhal 		netlink_rcv_skb(skb, nfnetlink_rcv_msg);
6600628b123SPablo Neira Ayuso }
661f9e815b3SHarald Welte 
nfnetlink_bind_event(struct net * net,unsigned int group)6620b2f3212SFlorian Westphal static void nfnetlink_bind_event(struct net *net, unsigned int group)
6630b2f3212SFlorian Westphal {
6640b2f3212SFlorian Westphal #ifdef CONFIG_NF_CONNTRACK_EVENTS
6650b2f3212SFlorian Westphal 	int type, group_bit;
6660b2f3212SFlorian Westphal 	u8 v;
6670b2f3212SFlorian Westphal 
6680b2f3212SFlorian Westphal 	/* All NFNLGRP_CONNTRACK_* group bits fit into u8.
6690b2f3212SFlorian Westphal 	 * The other groups are not relevant and can be ignored.
6700b2f3212SFlorian Westphal 	 */
6710b2f3212SFlorian Westphal 	if (group >= 8)
6720b2f3212SFlorian Westphal 		return;
6730b2f3212SFlorian Westphal 
6740b2f3212SFlorian Westphal 	type = nfnl_group2type[group];
6750b2f3212SFlorian Westphal 
6760b2f3212SFlorian Westphal 	switch (type) {
6770b2f3212SFlorian Westphal 	case NFNL_SUBSYS_CTNETLINK:
6780b2f3212SFlorian Westphal 		break;
6790b2f3212SFlorian Westphal 	case NFNL_SUBSYS_CTNETLINK_EXP:
6800b2f3212SFlorian Westphal 		break;
6810b2f3212SFlorian Westphal 	default:
6820b2f3212SFlorian Westphal 		return;
6830b2f3212SFlorian Westphal 	}
6840b2f3212SFlorian Westphal 
6850b2f3212SFlorian Westphal 	group_bit = (1 << group);
6860b2f3212SFlorian Westphal 
6870b2f3212SFlorian Westphal 	spin_lock(&nfnl_grp_active_lock);
688fdf64911SFlorian Westphal 	v = READ_ONCE(nf_ctnetlink_has_listener);
6890b2f3212SFlorian Westphal 	if ((v & group_bit) == 0) {
6900b2f3212SFlorian Westphal 		v |= group_bit;
6910b2f3212SFlorian Westphal 
6920b2f3212SFlorian Westphal 		/* read concurrently without nfnl_grp_active_lock held. */
693fdf64911SFlorian Westphal 		WRITE_ONCE(nf_ctnetlink_has_listener, v);
6940b2f3212SFlorian Westphal 	}
6950b2f3212SFlorian Westphal 
6960b2f3212SFlorian Westphal 	spin_unlock(&nfnl_grp_active_lock);
6970b2f3212SFlorian Westphal #endif
6980b2f3212SFlorian Westphal }
6990b2f3212SFlorian Westphal 
nfnetlink_bind(struct net * net,int group)700023e2cfaSJohannes Berg static int nfnetlink_bind(struct net *net, int group)
70103292745SPablo Neira Ayuso {
70203292745SPablo Neira Ayuso 	const struct nfnetlink_subsystem *ss;
70397840cb6SPablo Neira Ayuso 	int type;
70497840cb6SPablo Neira Ayuso 
70597840cb6SPablo Neira Ayuso 	if (group <= NFNLGRP_NONE || group > NFNLGRP_MAX)
70662924af2SPablo Neira Ayuso 		return 0;
70797840cb6SPablo Neira Ayuso 
70897840cb6SPablo Neira Ayuso 	type = nfnl_group2type[group];
70903292745SPablo Neira Ayuso 
71003292745SPablo Neira Ayuso 	rcu_read_lock();
711dbc3617fSFlorian Westphal 	ss = nfnetlink_get_subsys(type << 8);
71203292745SPablo Neira Ayuso 	rcu_read_unlock();
713bfe4bc71SRichard Guy Briggs 	if (!ss)
7141b0890cdSFlorian Westphal 		request_module_nowait("nfnetlink-subsys-%d", type);
7152794cdb0SFlorian Westphal 
7160b2f3212SFlorian Westphal 	nfnetlink_bind_event(net, group);
7172794cdb0SFlorian Westphal 	return 0;
7182794cdb0SFlorian Westphal }
7192794cdb0SFlorian Westphal 
nfnetlink_unbind(struct net * net,int group)7202794cdb0SFlorian Westphal static void nfnetlink_unbind(struct net *net, int group)
7212794cdb0SFlorian Westphal {
7222794cdb0SFlorian Westphal #ifdef CONFIG_NF_CONNTRACK_EVENTS
7230b2f3212SFlorian Westphal 	int type, group_bit;
7240b2f3212SFlorian Westphal 
725ffd219efSFlorian Westphal 	if (group <= NFNLGRP_NONE || group > NFNLGRP_MAX)
726ffd219efSFlorian Westphal 		return;
7272794cdb0SFlorian Westphal 
7280b2f3212SFlorian Westphal 	type = nfnl_group2type[group];
7290b2f3212SFlorian Westphal 
7300b2f3212SFlorian Westphal 	switch (type) {
7310b2f3212SFlorian Westphal 	case NFNL_SUBSYS_CTNETLINK:
7320b2f3212SFlorian Westphal 		break;
7330b2f3212SFlorian Westphal 	case NFNL_SUBSYS_CTNETLINK_EXP:
7340b2f3212SFlorian Westphal 		break;
7350b2f3212SFlorian Westphal 	default:
7360b2f3212SFlorian Westphal 		return;
7372794cdb0SFlorian Westphal 	}
7380b2f3212SFlorian Westphal 
7390b2f3212SFlorian Westphal 	/* ctnetlink_has_listener is u8 */
7400b2f3212SFlorian Westphal 	if (group >= 8)
7410b2f3212SFlorian Westphal 		return;
7420b2f3212SFlorian Westphal 
7430b2f3212SFlorian Westphal 	group_bit = (1 << group);
7440b2f3212SFlorian Westphal 
7450b2f3212SFlorian Westphal 	spin_lock(&nfnl_grp_active_lock);
7460b2f3212SFlorian Westphal 	if (!nfnetlink_has_listeners(net, group)) {
747fdf64911SFlorian Westphal 		u8 v = READ_ONCE(nf_ctnetlink_has_listener);
7480b2f3212SFlorian Westphal 
7490b2f3212SFlorian Westphal 		v &= ~group_bit;
7500b2f3212SFlorian Westphal 
7510b2f3212SFlorian Westphal 		/* read concurrently without nfnl_grp_active_lock held. */
752fdf64911SFlorian Westphal 		WRITE_ONCE(nf_ctnetlink_has_listener, v);
7530b2f3212SFlorian Westphal 	}
7540b2f3212SFlorian Westphal 	spin_unlock(&nfnl_grp_active_lock);
7552794cdb0SFlorian Westphal #endif
7562794cdb0SFlorian Westphal }
75703292745SPablo Neira Ayuso 
nfnetlink_net_init(struct net * net)758cd8c20b6SAlexey Dobriyan static int __net_init nfnetlink_net_init(struct net *net)
759f9e815b3SHarald Welte {
7601be05ea7SFlorian Westphal 	struct nfnl_net *nfnlnet = nfnl_pernet(net);
761a31f2d17SPablo Neira Ayuso 	struct netlink_kernel_cfg cfg = {
762a31f2d17SPablo Neira Ayuso 		.groups	= NFNLGRP_MAX,
763a31f2d17SPablo Neira Ayuso 		.input	= nfnetlink_rcv,
76403292745SPablo Neira Ayuso 		.bind	= nfnetlink_bind,
7652794cdb0SFlorian Westphal 		.unbind	= nfnetlink_unbind,
766a31f2d17SPablo Neira Ayuso 	};
767cd8c20b6SAlexey Dobriyan 
7681be05ea7SFlorian Westphal 	nfnlnet->nfnl = netlink_kernel_create(net, NETLINK_NETFILTER, &cfg);
7691be05ea7SFlorian Westphal 	if (!nfnlnet->nfnl)
770cd8c20b6SAlexey Dobriyan 		return -ENOMEM;
771cd8c20b6SAlexey Dobriyan 	return 0;
772f9e815b3SHarald Welte }
773f9e815b3SHarald Welte 
nfnetlink_net_exit_batch(struct list_head * net_exit_list)774cd8c20b6SAlexey Dobriyan static void __net_exit nfnetlink_net_exit_batch(struct list_head *net_exit_list)
775cd8c20b6SAlexey Dobriyan {
7761be05ea7SFlorian Westphal 	struct nfnl_net *nfnlnet;
777cd8c20b6SAlexey Dobriyan 	struct net *net;
778cd8c20b6SAlexey Dobriyan 
7791be05ea7SFlorian Westphal 	list_for_each_entry(net, net_exit_list, exit_list) {
7801be05ea7SFlorian Westphal 		nfnlnet = nfnl_pernet(net);
7811be05ea7SFlorian Westphal 
7821be05ea7SFlorian Westphal 		netlink_kernel_release(nfnlnet->nfnl);
7831be05ea7SFlorian Westphal 	}
784cd8c20b6SAlexey Dobriyan }
785cd8c20b6SAlexey Dobriyan 
786cd8c20b6SAlexey Dobriyan static struct pernet_operations nfnetlink_net_ops = {
787cd8c20b6SAlexey Dobriyan 	.init		= nfnetlink_net_init,
788cd8c20b6SAlexey Dobriyan 	.exit_batch	= nfnetlink_net_exit_batch,
7891be05ea7SFlorian Westphal 	.id		= &nfnetlink_pernet_id,
7901be05ea7SFlorian Westphal 	.size		= sizeof(struct nfnl_net),
791cd8c20b6SAlexey Dobriyan };
792cd8c20b6SAlexey Dobriyan 
nfnetlink_init(void)793395dde20SAdrian Bunk static int __init nfnetlink_init(void)
794f9e815b3SHarald Welte {
795c14b78e7SPablo Neira Ayuso 	int i;
796c14b78e7SPablo Neira Ayuso 
79797840cb6SPablo Neira Ayuso 	for (i = NFNLGRP_NONE + 1; i <= NFNLGRP_MAX; i++)
79897840cb6SPablo Neira Ayuso 		BUG_ON(nfnl_group2type[i] == NFNL_SUBSYS_NONE);
79997840cb6SPablo Neira Ayuso 
800c14b78e7SPablo Neira Ayuso 	for (i=0; i<NFNL_SUBSYS_COUNT; i++)
801ab6c41eeSFlorian Westphal 		__mutex_init(&table[i].mutex, nfnl_lockdep_names[i], &nfnl_lockdep_keys[i]);
802c14b78e7SPablo Neira Ayuso 
803cd8c20b6SAlexey Dobriyan 	return register_pernet_subsys(&nfnetlink_net_ops);
804f9e815b3SHarald Welte }
805f9e815b3SHarald Welte 
nfnetlink_exit(void)806cd8c20b6SAlexey Dobriyan static void __exit nfnetlink_exit(void)
807cd8c20b6SAlexey Dobriyan {
808cd8c20b6SAlexey Dobriyan 	unregister_pernet_subsys(&nfnetlink_net_ops);
809f9e815b3SHarald Welte }
810f9e815b3SHarald Welte module_init(nfnetlink_init);
811f9e815b3SHarald Welte module_exit(nfnetlink_exit);
812