Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
awilliam
GitHub Repository: awilliam/linux-vfio
Path: blob/master/net/netfilter/nfnetlink.c
15109 views
1
/* Netfilter messages via netlink socket. Allows for user space
2
* protocol helpers and general trouble making from userspace.
3
*
4
* (C) 2001 by Jay Schulist <[email protected]>,
5
* (C) 2002-2005 by Harald Welte <[email protected]>
6
* (C) 2005,2007 by Pablo Neira Ayuso <[email protected]>
7
*
8
* Initial netfilter messages via netlink development funded and
9
* generally made possible by Network Robots, Inc. (www.networkrobots.com)
10
*
11
* Further development of this code funded by Astaro AG (http://www.astaro.com)
12
*
13
* This software may be used and distributed according to the terms
14
* of the GNU General Public License, incorporated herein by reference.
15
*/
16
17
#include <linux/module.h>
18
#include <linux/types.h>
19
#include <linux/socket.h>
20
#include <linux/kernel.h>
21
#include <linux/string.h>
22
#include <linux/sockios.h>
23
#include <linux/net.h>
24
#include <linux/skbuff.h>
25
#include <asm/uaccess.h>
26
#include <asm/system.h>
27
#include <net/sock.h>
28
#include <net/netlink.h>
29
#include <linux/init.h>
30
31
#include <linux/netlink.h>
32
#include <linux/netfilter/nfnetlink.h>
33
34
MODULE_LICENSE("GPL");
35
MODULE_AUTHOR("Harald Welte <[email protected]>");
36
MODULE_ALIAS_NET_PF_PROTO(PF_NETLINK, NETLINK_NETFILTER);
37
38
static char __initdata nfversion[] = "0.30";
39
40
static const struct nfnetlink_subsystem *subsys_table[NFNL_SUBSYS_COUNT];
41
static DEFINE_MUTEX(nfnl_mutex);
42
43
void nfnl_lock(void)
44
{
45
mutex_lock(&nfnl_mutex);
46
}
47
EXPORT_SYMBOL_GPL(nfnl_lock);
48
49
void nfnl_unlock(void)
50
{
51
mutex_unlock(&nfnl_mutex);
52
}
53
EXPORT_SYMBOL_GPL(nfnl_unlock);
54
55
int nfnetlink_subsys_register(const struct nfnetlink_subsystem *n)
56
{
57
nfnl_lock();
58
if (subsys_table[n->subsys_id]) {
59
nfnl_unlock();
60
return -EBUSY;
61
}
62
subsys_table[n->subsys_id] = n;
63
nfnl_unlock();
64
65
return 0;
66
}
67
EXPORT_SYMBOL_GPL(nfnetlink_subsys_register);
68
69
int nfnetlink_subsys_unregister(const struct nfnetlink_subsystem *n)
70
{
71
nfnl_lock();
72
subsys_table[n->subsys_id] = NULL;
73
nfnl_unlock();
74
75
return 0;
76
}
77
EXPORT_SYMBOL_GPL(nfnetlink_subsys_unregister);
78
79
static inline const struct nfnetlink_subsystem *nfnetlink_get_subsys(u_int16_t type)
80
{
81
u_int8_t subsys_id = NFNL_SUBSYS_ID(type);
82
83
if (subsys_id >= NFNL_SUBSYS_COUNT)
84
return NULL;
85
86
return subsys_table[subsys_id];
87
}
88
89
static inline const struct nfnl_callback *
90
nfnetlink_find_client(u_int16_t type, const struct nfnetlink_subsystem *ss)
91
{
92
u_int8_t cb_id = NFNL_MSG_TYPE(type);
93
94
if (cb_id >= ss->cb_count)
95
return NULL;
96
97
return &ss->cb[cb_id];
98
}
99
100
int nfnetlink_has_listeners(struct net *net, unsigned int group)
101
{
102
return netlink_has_listeners(net->nfnl, group);
103
}
104
EXPORT_SYMBOL_GPL(nfnetlink_has_listeners);
105
106
int nfnetlink_send(struct sk_buff *skb, struct net *net, u32 pid,
107
unsigned group, int echo, gfp_t flags)
108
{
109
return nlmsg_notify(net->nfnl, skb, pid, group, echo, flags);
110
}
111
EXPORT_SYMBOL_GPL(nfnetlink_send);
112
113
int nfnetlink_set_err(struct net *net, u32 pid, u32 group, int error)
114
{
115
return netlink_set_err(net->nfnl, pid, group, error);
116
}
117
EXPORT_SYMBOL_GPL(nfnetlink_set_err);
118
119
int nfnetlink_unicast(struct sk_buff *skb, struct net *net, u_int32_t pid, int flags)
120
{
121
return netlink_unicast(net->nfnl, skb, pid, flags);
122
}
123
EXPORT_SYMBOL_GPL(nfnetlink_unicast);
124
125
/* Process one complete nfnetlink message. */
126
static int nfnetlink_rcv_msg(struct sk_buff *skb, struct nlmsghdr *nlh)
127
{
128
struct net *net = sock_net(skb->sk);
129
const struct nfnl_callback *nc;
130
const struct nfnetlink_subsystem *ss;
131
int type, err;
132
133
if (security_netlink_recv(skb, CAP_NET_ADMIN))
134
return -EPERM;
135
136
/* All the messages must at least contain nfgenmsg */
137
if (nlh->nlmsg_len < NLMSG_LENGTH(sizeof(struct nfgenmsg)))
138
return 0;
139
140
type = nlh->nlmsg_type;
141
replay:
142
ss = nfnetlink_get_subsys(type);
143
if (!ss) {
144
#ifdef CONFIG_MODULES
145
nfnl_unlock();
146
request_module("nfnetlink-subsys-%d", NFNL_SUBSYS_ID(type));
147
nfnl_lock();
148
ss = nfnetlink_get_subsys(type);
149
if (!ss)
150
#endif
151
return -EINVAL;
152
}
153
154
nc = nfnetlink_find_client(type, ss);
155
if (!nc)
156
return -EINVAL;
157
158
{
159
int min_len = NLMSG_SPACE(sizeof(struct nfgenmsg));
160
u_int8_t cb_id = NFNL_MSG_TYPE(nlh->nlmsg_type);
161
struct nlattr *cda[ss->cb[cb_id].attr_count + 1];
162
struct nlattr *attr = (void *)nlh + min_len;
163
int attrlen = nlh->nlmsg_len - min_len;
164
165
err = nla_parse(cda, ss->cb[cb_id].attr_count,
166
attr, attrlen, ss->cb[cb_id].policy);
167
if (err < 0)
168
return err;
169
170
err = nc->call(net->nfnl, skb, nlh, (const struct nlattr **)cda);
171
if (err == -EAGAIN)
172
goto replay;
173
return err;
174
}
175
}
176
177
static void nfnetlink_rcv(struct sk_buff *skb)
178
{
179
nfnl_lock();
180
netlink_rcv_skb(skb, &nfnetlink_rcv_msg);
181
nfnl_unlock();
182
}
183
184
static int __net_init nfnetlink_net_init(struct net *net)
185
{
186
struct sock *nfnl;
187
188
nfnl = netlink_kernel_create(net, NETLINK_NETFILTER, NFNLGRP_MAX,
189
nfnetlink_rcv, NULL, THIS_MODULE);
190
if (!nfnl)
191
return -ENOMEM;
192
net->nfnl_stash = nfnl;
193
rcu_assign_pointer(net->nfnl, nfnl);
194
return 0;
195
}
196
197
static void __net_exit nfnetlink_net_exit_batch(struct list_head *net_exit_list)
198
{
199
struct net *net;
200
201
list_for_each_entry(net, net_exit_list, exit_list)
202
rcu_assign_pointer(net->nfnl, NULL);
203
synchronize_net();
204
list_for_each_entry(net, net_exit_list, exit_list)
205
netlink_kernel_release(net->nfnl_stash);
206
}
207
208
static struct pernet_operations nfnetlink_net_ops = {
209
.init = nfnetlink_net_init,
210
.exit_batch = nfnetlink_net_exit_batch,
211
};
212
213
static int __init nfnetlink_init(void)
214
{
215
pr_info("Netfilter messages via NETLINK v%s.\n", nfversion);
216
return register_pernet_subsys(&nfnetlink_net_ops);
217
}
218
219
static void __exit nfnetlink_exit(void)
220
{
221
pr_info("Removing netfilter NETLINK layer.\n");
222
unregister_pernet_subsys(&nfnetlink_net_ops);
223
}
224
module_init(nfnetlink_init);
225
module_exit(nfnetlink_exit);
226
227