Blame view

net/netfilter/nfnetlink.c 6.01 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
25
26
  #include <linux/skbuff.h>
  #include <asm/uaccess.h>
  #include <asm/system.h>
  #include <net/sock.h>
a3c5029cf   Patrick McHardy   [NETFILTER]: nfne...
27
  #include <net/netlink.h>
f9e815b37   Harald Welte   [NETFITLER]: Add ...
28
  #include <linux/init.h>
f9e815b37   Harald Welte   [NETFITLER]: Add ...
29

f9e815b37   Harald Welte   [NETFITLER]: Add ...
30
31
32
33
  #include <linux/netlink.h>
  #include <linux/netfilter/nfnetlink.h>
  
  MODULE_LICENSE("GPL");
4fdb3bb72   Harald Welte   [NETLINK]: Add pr...
34
35
  MODULE_AUTHOR("Harald Welte <laforge@netfilter.org>");
  MODULE_ALIAS_NET_PF_PROTO(PF_NETLINK, NETLINK_NETFILTER);
f9e815b37   Harald Welte   [NETFITLER]: Add ...
36
37
  
  static char __initdata nfversion[] = "0.30";
6b75e3e8d   Eric Dumazet   netfilter: nfnetl...
38
  static const struct nfnetlink_subsystem __rcu *subsys_table[NFNL_SUBSYS_COUNT];
a3c5029cf   Patrick McHardy   [NETFILTER]: nfne...
39
  static DEFINE_MUTEX(nfnl_mutex);
f9e815b37   Harald Welte   [NETFITLER]: Add ...
40

e6a7d3c04   Pablo Neira Ayuso   netfilter: ctnetl...
41
  void nfnl_lock(void)
f9e815b37   Harald Welte   [NETFITLER]: Add ...
42
  {
a3c5029cf   Patrick McHardy   [NETFILTER]: nfne...
43
  	mutex_lock(&nfnl_mutex);
f9e815b37   Harald Welte   [NETFITLER]: Add ...
44
  }
e6a7d3c04   Pablo Neira Ayuso   netfilter: ctnetl...
45
  EXPORT_SYMBOL_GPL(nfnl_lock);
f9e815b37   Harald Welte   [NETFITLER]: Add ...
46

e6a7d3c04   Pablo Neira Ayuso   netfilter: ctnetl...
47
  void nfnl_unlock(void)
a3c5029cf   Patrick McHardy   [NETFILTER]: nfne...
48
49
  {
  	mutex_unlock(&nfnl_mutex);
f9e815b37   Harald Welte   [NETFITLER]: Add ...
50
  }
e6a7d3c04   Pablo Neira Ayuso   netfilter: ctnetl...
51
  EXPORT_SYMBOL_GPL(nfnl_unlock);
f9e815b37   Harald Welte   [NETFITLER]: Add ...
52

7c8d4cb41   Patrick McHardy   [NETFILTER]: nfne...
53
  int nfnetlink_subsys_register(const struct nfnetlink_subsystem *n)
f9e815b37   Harald Welte   [NETFITLER]: Add ...
54
  {
f9e815b37   Harald Welte   [NETFITLER]: Add ...
55
  	nfnl_lock();
0ab43f849   Harald Welte   [NETFILTER]: Core...
56
57
58
59
  	if (subsys_table[n->subsys_id]) {
  		nfnl_unlock();
  		return -EBUSY;
  	}
cf778b00e   Eric Dumazet   net: reintroduce ...
60
  	rcu_assign_pointer(subsys_table[n->subsys_id], n);
f9e815b37   Harald Welte   [NETFITLER]: Add ...
61
62
63
64
  	nfnl_unlock();
  
  	return 0;
  }
f4bc177f0   Pablo Neira Ayuso   [NETFILTER]: nfne...
65
  EXPORT_SYMBOL_GPL(nfnetlink_subsys_register);
f9e815b37   Harald Welte   [NETFITLER]: Add ...
66

7c8d4cb41   Patrick McHardy   [NETFILTER]: nfne...
67
  int nfnetlink_subsys_unregister(const struct nfnetlink_subsystem *n)
f9e815b37   Harald Welte   [NETFITLER]: Add ...
68
  {
f9e815b37   Harald Welte   [NETFITLER]: Add ...
69
70
71
  	nfnl_lock();
  	subsys_table[n->subsys_id] = NULL;
  	nfnl_unlock();
6b75e3e8d   Eric Dumazet   netfilter: nfnetl...
72
  	synchronize_rcu();
f9e815b37   Harald Welte   [NETFITLER]: Add ...
73
74
  	return 0;
  }
f4bc177f0   Pablo Neira Ayuso   [NETFILTER]: nfne...
75
  EXPORT_SYMBOL_GPL(nfnetlink_subsys_unregister);
f9e815b37   Harald Welte   [NETFITLER]: Add ...
76

7c8d4cb41   Patrick McHardy   [NETFILTER]: nfne...
77
  static inline const struct nfnetlink_subsystem *nfnetlink_get_subsys(u_int16_t type)
f9e815b37   Harald Welte   [NETFITLER]: Add ...
78
79
  {
  	u_int8_t subsys_id = NFNL_SUBSYS_ID(type);
ac0f1d989   Pablo Neira Ayuso   [NETFILTER]: nfne...
80
  	if (subsys_id >= NFNL_SUBSYS_COUNT)
f9e815b37   Harald Welte   [NETFITLER]: Add ...
81
  		return NULL;
6b75e3e8d   Eric Dumazet   netfilter: nfnetl...
82
  	return rcu_dereference(subsys_table[subsys_id]);
f9e815b37   Harald Welte   [NETFITLER]: Add ...
83
  }
7c8d4cb41   Patrick McHardy   [NETFILTER]: nfne...
84
85
  static inline const struct nfnl_callback *
  nfnetlink_find_client(u_int16_t type, const struct nfnetlink_subsystem *ss)
f9e815b37   Harald Welte   [NETFITLER]: Add ...
86
87
  {
  	u_int8_t cb_id = NFNL_MSG_TYPE(type);
601e68e10   YOSHIFUJI Hideaki   [NETFILTER]: Fix ...
88

67ca39660   Pablo Neira Ayuso   [NETFILTER]: nfne...
89
  	if (cb_id >= ss->cb_count)
f9e815b37   Harald Welte   [NETFITLER]: Add ...
90
  		return NULL;
f9e815b37   Harald Welte   [NETFITLER]: Add ...
91
92
93
  
  	return &ss->cb[cb_id];
  }
cd8c20b65   Alexey Dobriyan   netfilter: nfnetl...
94
  int nfnetlink_has_listeners(struct net *net, unsigned int group)
a24276924   Patrick McHardy   [NETFILTER]: ctne...
95
  {
cd8c20b65   Alexey Dobriyan   netfilter: nfnetl...
96
  	return netlink_has_listeners(net->nfnl, group);
a24276924   Patrick McHardy   [NETFILTER]: ctne...
97
98
  }
  EXPORT_SYMBOL_GPL(nfnetlink_has_listeners);
cd8c20b65   Alexey Dobriyan   netfilter: nfnetl...
99
  int nfnetlink_send(struct sk_buff *skb, struct net *net, u32 pid,
e34d5c1a4   Pablo Neira Ayuso   netfilter: conntr...
100
  		   unsigned group, int echo, gfp_t flags)
f9e815b37   Harald Welte   [NETFITLER]: Add ...
101
  {
cd8c20b65   Alexey Dobriyan   netfilter: nfnetl...
102
  	return nlmsg_notify(net->nfnl, skb, pid, group, echo, flags);
f9e815b37   Harald Welte   [NETFITLER]: Add ...
103
  }
f4bc177f0   Pablo Neira Ayuso   [NETFILTER]: nfne...
104
  EXPORT_SYMBOL_GPL(nfnetlink_send);
f9e815b37   Harald Welte   [NETFITLER]: Add ...
105

37b7ef720   Pablo Neira Ayuso   netfilter: ctnetl...
106
  int nfnetlink_set_err(struct net *net, u32 pid, u32 group, int error)
dd5b6ce6f   Pablo Neira Ayuso   nefilter: nfnetli...
107
  {
37b7ef720   Pablo Neira Ayuso   netfilter: ctnetl...
108
  	return netlink_set_err(net->nfnl, pid, group, error);
dd5b6ce6f   Pablo Neira Ayuso   nefilter: nfnetli...
109
110
  }
  EXPORT_SYMBOL_GPL(nfnetlink_set_err);
cd8c20b65   Alexey Dobriyan   netfilter: nfnetl...
111
  int nfnetlink_unicast(struct sk_buff *skb, struct net *net, u_int32_t pid, int flags)
f9e815b37   Harald Welte   [NETFITLER]: Add ...
112
  {
cd8c20b65   Alexey Dobriyan   netfilter: nfnetl...
113
  	return netlink_unicast(net->nfnl, skb, pid, flags);
f9e815b37   Harald Welte   [NETFITLER]: Add ...
114
  }
f4bc177f0   Pablo Neira Ayuso   [NETFILTER]: nfne...
115
  EXPORT_SYMBOL_GPL(nfnetlink_unicast);
f9e815b37   Harald Welte   [NETFITLER]: Add ...
116
117
  
  /* Process one complete nfnetlink message. */
1d00a4eb4   Thomas Graf   [NETLINK]: Remove...
118
  static int nfnetlink_rcv_msg(struct sk_buff *skb, struct nlmsghdr *nlh)
f9e815b37   Harald Welte   [NETFITLER]: Add ...
119
  {
cd8c20b65   Alexey Dobriyan   netfilter: nfnetl...
120
  	struct net *net = sock_net(skb->sk);
7c8d4cb41   Patrick McHardy   [NETFILTER]: nfne...
121
122
  	const struct nfnl_callback *nc;
  	const struct nfnetlink_subsystem *ss;
1d00a4eb4   Thomas Graf   [NETLINK]: Remove...
123
  	int type, err;
f9e815b37   Harald Welte   [NETFITLER]: Add ...
124

fd7784615   Eric Paris   security: remove ...
125
  	if (!capable(CAP_NET_ADMIN))
1d00a4eb4   Thomas Graf   [NETLINK]: Remove...
126
  		return -EPERM;
37d2e7a20   Harald Welte   [NETFILTER] nfnet...
127

f9e815b37   Harald Welte   [NETFITLER]: Add ...
128
  	/* All the messages must at least contain nfgenmsg */
f49c857ff   Pablo Neira Ayuso   netfilter: nfnetl...
129
  	if (nlh->nlmsg_len < NLMSG_LENGTH(sizeof(struct nfgenmsg)))
f9e815b37   Harald Welte   [NETFITLER]: Add ...
130
  		return 0;
f9e815b37   Harald Welte   [NETFITLER]: Add ...
131
132
  
  	type = nlh->nlmsg_type;
e6a7d3c04   Pablo Neira Ayuso   netfilter: ctnetl...
133
  replay:
6b75e3e8d   Eric Dumazet   netfilter: nfnetl...
134
  	rcu_read_lock();
f9e815b37   Harald Welte   [NETFITLER]: Add ...
135
  	ss = nfnetlink_get_subsys(type);
0ab43f849   Harald Welte   [NETFILTER]: Core...
136
  	if (!ss) {
95a5afca4   Johannes Berg   net: Remove CONFI...
137
  #ifdef CONFIG_MODULES
6b75e3e8d   Eric Dumazet   netfilter: nfnetl...
138
  		rcu_read_unlock();
37d2e7a20   Harald Welte   [NETFILTER] nfnet...
139
  		request_module("nfnetlink-subsys-%d", NFNL_SUBSYS_ID(type));
6b75e3e8d   Eric Dumazet   netfilter: nfnetl...
140
  		rcu_read_lock();
37d2e7a20   Harald Welte   [NETFILTER] nfnet...
141
  		ss = nfnetlink_get_subsys(type);
0ab43f849   Harald Welte   [NETFILTER]: Core...
142
143
  		if (!ss)
  #endif
6b75e3e8d   Eric Dumazet   netfilter: nfnetl...
144
145
  		{
  			rcu_read_unlock();
1d00a4eb4   Thomas Graf   [NETLINK]: Remove...
146
  			return -EINVAL;
6b75e3e8d   Eric Dumazet   netfilter: nfnetl...
147
  		}
0ab43f849   Harald Welte   [NETFILTER]: Core...
148
  	}
f9e815b37   Harald Welte   [NETFITLER]: Add ...
149
150
  
  	nc = nfnetlink_find_client(type, ss);
6b75e3e8d   Eric Dumazet   netfilter: nfnetl...
151
152
  	if (!nc) {
  		rcu_read_unlock();
1d00a4eb4   Thomas Graf   [NETLINK]: Remove...
153
  		return -EINVAL;
6b75e3e8d   Eric Dumazet   netfilter: nfnetl...
154
  	}
f9e815b37   Harald Welte   [NETFITLER]: Add ...
155

f9e815b37   Harald Welte   [NETFITLER]: Add ...
156
  	{
e37305782   Patrick McHardy   [NETFILTER]: nfne...
157
158
  		int min_len = NLMSG_SPACE(sizeof(struct nfgenmsg));
  		u_int8_t cb_id = NFNL_MSG_TYPE(nlh->nlmsg_type);
f49c857ff   Pablo Neira Ayuso   netfilter: nfnetl...
159
160
161
162
163
164
165
166
  		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)
  			return err;
601e68e10   YOSHIFUJI Hideaki   [NETFILTER]: Fix ...
167

6b75e3e8d   Eric Dumazet   netfilter: nfnetl...
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
  		if (nc->call_rcu) {
  			err = nc->call_rcu(net->nfnl, skb, nlh,
  					   (const struct nlattr **)cda);
  			rcu_read_unlock();
  		} else {
  			rcu_read_unlock();
  			nfnl_lock();
  			if (rcu_dereference_protected(
  					subsys_table[NFNL_SUBSYS_ID(type)],
  					lockdep_is_held(&nfnl_mutex)) != ss ||
  			    nfnetlink_find_client(type, ss) != nc)
  				err = -EAGAIN;
  			else
  				err = nc->call(net->nfnl, skb, nlh,
  						   (const struct nlattr **)cda);
  			nfnl_unlock();
  		}
e6a7d3c04   Pablo Neira Ayuso   netfilter: ctnetl...
185
186
187
  		if (err == -EAGAIN)
  			goto replay;
  		return err;
f9e815b37   Harald Welte   [NETFITLER]: Add ...
188
  	}
f9e815b37   Harald Welte   [NETFITLER]: Add ...
189
  }
cd40b7d39   Denis V. Lunev   [NET]: make netli...
190
  static void nfnetlink_rcv(struct sk_buff *skb)
f9e815b37   Harald Welte   [NETFITLER]: Add ...
191
  {
cd40b7d39   Denis V. Lunev   [NET]: make netli...
192
  	netlink_rcv_skb(skb, &nfnetlink_rcv_msg);
f9e815b37   Harald Welte   [NETFITLER]: Add ...
193
  }
cd8c20b65   Alexey Dobriyan   netfilter: nfnetl...
194
  static int __net_init nfnetlink_net_init(struct net *net)
f9e815b37   Harald Welte   [NETFITLER]: Add ...
195
  {
cd8c20b65   Alexey Dobriyan   netfilter: nfnetl...
196
197
198
199
200
201
202
  	struct sock *nfnl;
  
  	nfnl = netlink_kernel_create(net, NETLINK_NETFILTER, NFNLGRP_MAX,
  				     nfnetlink_rcv, NULL, THIS_MODULE);
  	if (!nfnl)
  		return -ENOMEM;
  	net->nfnl_stash = nfnl;
cf778b00e   Eric Dumazet   net: reintroduce ...
203
  	rcu_assign_pointer(net->nfnl, nfnl);
cd8c20b65   Alexey Dobriyan   netfilter: nfnetl...
204
  	return 0;
f9e815b37   Harald Welte   [NETFITLER]: Add ...
205
  }
cd8c20b65   Alexey Dobriyan   netfilter: nfnetl...
206
  static void __net_exit nfnetlink_net_exit_batch(struct list_head *net_exit_list)
f9e815b37   Harald Welte   [NETFITLER]: Add ...
207
  {
cd8c20b65   Alexey Dobriyan   netfilter: nfnetl...
208
  	struct net *net;
f9e815b37   Harald Welte   [NETFITLER]: Add ...
209

cd8c20b65   Alexey Dobriyan   netfilter: nfnetl...
210
  	list_for_each_entry(net, net_exit_list, exit_list)
a9b3cd7f3   Stephen Hemminger   rcu: convert uses...
211
  		RCU_INIT_POINTER(net->nfnl, NULL);
cd8c20b65   Alexey Dobriyan   netfilter: nfnetl...
212
213
214
215
  	synchronize_net();
  	list_for_each_entry(net, net_exit_list, exit_list)
  		netlink_kernel_release(net->nfnl_stash);
  }
f9e815b37   Harald Welte   [NETFITLER]: Add ...
216

cd8c20b65   Alexey Dobriyan   netfilter: nfnetl...
217
218
219
220
221
222
223
  static struct pernet_operations nfnetlink_net_ops = {
  	.init		= nfnetlink_net_init,
  	.exit_batch	= nfnetlink_net_exit_batch,
  };
  
  static int __init nfnetlink_init(void)
  {
654d0fbdc   Stephen Hemminger   netfilter: cleanu...
224
225
  	pr_info("Netfilter messages via NETLINK v%s.
  ", nfversion);
cd8c20b65   Alexey Dobriyan   netfilter: nfnetl...
226
  	return register_pernet_subsys(&nfnetlink_net_ops);
f9e815b37   Harald Welte   [NETFITLER]: Add ...
227
  }
cd8c20b65   Alexey Dobriyan   netfilter: nfnetl...
228
229
  static void __exit nfnetlink_exit(void)
  {
654d0fbdc   Stephen Hemminger   netfilter: cleanu...
230
231
  	pr_info("Removing netfilter NETLINK layer.
  ");
cd8c20b65   Alexey Dobriyan   netfilter: nfnetl...
232
233
  	unregister_pernet_subsys(&nfnetlink_net_ops);
  }
f9e815b37   Harald Welte   [NETFITLER]: Add ...
234
235
  module_init(nfnetlink_init);
  module_exit(nfnetlink_exit);