x86/mm/pae: Make pmd_t similar to pte_t
[linux-2.6-block.git] / net / netfilter / nfnetlink_hook.c
CommitLineData
e2cf17d3
FW
1// SPDX-License-Identifier: GPL-2.0-or-later
2/*
3 * Copyright (c) 2021 Red Hat GmbH
4 *
5 * Author: Florian Westphal <fw@strlen.de>
6 */
7
8#include <linux/module.h>
b6459415 9#include <linux/kallsyms.h>
e2cf17d3
FW
10#include <linux/kernel.h>
11#include <linux/types.h>
12#include <linux/skbuff.h>
13#include <linux/errno.h>
14#include <linux/netlink.h>
15#include <linux/slab.h>
16
17#include <linux/netfilter.h>
18
19#include <linux/netfilter/nfnetlink.h>
20#include <linux/netfilter/nfnetlink_hook.h>
21
22#include <net/netfilter/nf_tables.h>
23#include <net/sock.h>
24
25static const struct nla_policy nfnl_hook_nla_policy[NFNLA_HOOK_MAX + 1] = {
26 [NFNLA_HOOK_HOOKNUM] = { .type = NLA_U32 },
27 [NFNLA_HOOK_PRIORITY] = { .type = NLA_U32 },
28 [NFNLA_HOOK_DEV] = { .type = NLA_STRING,
29 .len = IFNAMSIZ - 1 },
30 [NFNLA_HOOK_FUNCTION_NAME] = { .type = NLA_NUL_STRING,
31 .len = KSYM_NAME_LEN, },
32 [NFNLA_HOOK_MODULE_NAME] = { .type = NLA_NUL_STRING,
33 .len = MODULE_NAME_LEN, },
34 [NFNLA_HOOK_CHAIN_INFO] = { .type = NLA_NESTED, },
35};
36
37static int nf_netlink_dump_start_rcu(struct sock *nlsk, struct sk_buff *skb,
38 const struct nlmsghdr *nlh,
39 struct netlink_dump_control *c)
40{
41 int err;
42
43 if (!try_module_get(THIS_MODULE))
44 return -EINVAL;
45
46 rcu_read_unlock();
47 err = netlink_dump_start(nlsk, skb, nlh, c);
48 rcu_read_lock();
49 module_put(THIS_MODULE);
50
51 return err;
52}
53
54struct nfnl_dump_hook_data {
55 char devname[IFNAMSIZ];
56 unsigned long headv;
57 u8 hook;
58};
59
60static int nfnl_hook_put_nft_chain_info(struct sk_buff *nlskb,
61 const struct nfnl_dump_hook_data *ctx,
62 unsigned int seq,
63 const struct nf_hook_ops *ops)
64{
65 struct net *net = sock_net(nlskb->sk);
66 struct nlattr *nest, *nest2;
67 struct nft_chain *chain;
68 int ret = 0;
69
70 if (ops->hook_ops_type != NF_HOOK_OP_NF_TABLES)
71 return 0;
72
73 chain = ops->priv;
74 if (WARN_ON_ONCE(!chain))
75 return 0;
76
77 if (!nft_is_active(net, chain))
78 return 0;
79
80 nest = nla_nest_start(nlskb, NFNLA_HOOK_CHAIN_INFO);
81 if (!nest)
82 return -EMSGSIZE;
83
84 ret = nla_put_be32(nlskb, NFNLA_HOOK_INFO_TYPE,
85 htonl(NFNL_HOOK_TYPE_NFTABLES));
86 if (ret)
87 goto cancel_nest;
88
89 nest2 = nla_nest_start(nlskb, NFNLA_HOOK_INFO_DESC);
90 if (!nest2)
91 goto cancel_nest;
92
a6e57c4a 93 ret = nla_put_string(nlskb, NFNLA_CHAIN_TABLE, chain->table->name);
e2cf17d3
FW
94 if (ret)
95 goto cancel_nest;
96
a6e57c4a
PNA
97 ret = nla_put_string(nlskb, NFNLA_CHAIN_NAME, chain->name);
98 if (ret)
99 goto cancel_nest;
100
101 ret = nla_put_u8(nlskb, NFNLA_CHAIN_FAMILY, chain->table->family);
e2cf17d3
FW
102 if (ret)
103 goto cancel_nest;
104
105 nla_nest_end(nlskb, nest2);
106 nla_nest_end(nlskb, nest);
107 return ret;
108
109cancel_nest:
110 nla_nest_cancel(nlskb, nest);
111 return -EMSGSIZE;
112}
113
114static int nfnl_hook_dump_one(struct sk_buff *nlskb,
115 const struct nfnl_dump_hook_data *ctx,
116 const struct nf_hook_ops *ops,
69311e7c 117 int family, unsigned int seq)
e2cf17d3
FW
118{
119 u16 event = nfnl_msg_type(NFNL_SUBSYS_HOOK, NFNL_MSG_HOOK_GET);
120 unsigned int portid = NETLINK_CB(nlskb).portid;
121 struct nlmsghdr *nlh;
122 int ret = -EMSGSIZE;
269fc695 123 u32 hooknum;
e2cf17d3
FW
124#ifdef CONFIG_KALLSYMS
125 char sym[KSYM_SYMBOL_LEN];
126 char *module_name;
127#endif
128 nlh = nfnl_msg_put(nlskb, portid, seq, event,
69311e7c 129 NLM_F_MULTI, family, NFNETLINK_V0, 0);
e2cf17d3
FW
130 if (!nlh)
131 goto nla_put_failure;
132
133#ifdef CONFIG_KALLSYMS
134 ret = snprintf(sym, sizeof(sym), "%ps", ops->hook);
24610ed8
DC
135 if (ret >= sizeof(sym)) {
136 ret = -EINVAL;
e2cf17d3 137 goto nla_put_failure;
24610ed8 138 }
e2cf17d3
FW
139
140 module_name = strstr(sym, " [");
141 if (module_name) {
142 char *end;
143
61e0c2bc 144 *module_name = '\0';
e2cf17d3
FW
145 module_name += 2;
146 end = strchr(module_name, ']');
147 if (end) {
148 *end = 0;
149
150 ret = nla_put_string(nlskb, NFNLA_HOOK_MODULE_NAME, module_name);
151 if (ret)
152 goto nla_put_failure;
153 }
154 }
155
156 ret = nla_put_string(nlskb, NFNLA_HOOK_FUNCTION_NAME, sym);
157 if (ret)
158 goto nla_put_failure;
159#endif
160
269fc695
PNA
161 if (ops->pf == NFPROTO_INET && ops->hooknum == NF_INET_INGRESS)
162 hooknum = NF_NETDEV_INGRESS;
163 else
164 hooknum = ops->hooknum;
165
166 ret = nla_put_be32(nlskb, NFNLA_HOOK_HOOKNUM, htonl(hooknum));
e2cf17d3
FW
167 if (ret)
168 goto nla_put_failure;
169
170 ret = nla_put_be32(nlskb, NFNLA_HOOK_PRIORITY, htonl(ops->priority));
171 if (ret)
172 goto nla_put_failure;
173
174 ret = nfnl_hook_put_nft_chain_info(nlskb, ctx, seq, ops);
175 if (ret)
176 goto nla_put_failure;
177
178 nlmsg_end(nlskb, nlh);
179 return 0;
180nla_put_failure:
181 nlmsg_trim(nlskb, nlh);
182 return ret;
183}
184
185static const struct nf_hook_entries *
186nfnl_hook_entries_head(u8 pf, unsigned int hook, struct net *net, const char *dev)
187{
188 const struct nf_hook_entries *hook_head = NULL;
42df6e1d 189#if defined(CONFIG_NETFILTER_INGRESS) || defined(CONFIG_NETFILTER_EGRESS)
e2cf17d3 190 struct net_device *netdev;
217e26bd 191#endif
e2cf17d3
FW
192
193 switch (pf) {
194 case NFPROTO_IPV4:
195 if (hook >= ARRAY_SIZE(net->nf.hooks_ipv4))
196 return ERR_PTR(-EINVAL);
197 hook_head = rcu_dereference(net->nf.hooks_ipv4[hook]);
198 break;
199 case NFPROTO_IPV6:
e2cf17d3
FW
200 if (hook >= ARRAY_SIZE(net->nf.hooks_ipv6))
201 return ERR_PTR(-EINVAL);
5302560b 202 hook_head = rcu_dereference(net->nf.hooks_ipv6[hook]);
e2cf17d3
FW
203 break;
204 case NFPROTO_ARP:
205#ifdef CONFIG_NETFILTER_FAMILY_ARP
206 if (hook >= ARRAY_SIZE(net->nf.hooks_arp))
207 return ERR_PTR(-EINVAL);
208 hook_head = rcu_dereference(net->nf.hooks_arp[hook]);
209#endif
210 break;
211 case NFPROTO_BRIDGE:
212#ifdef CONFIG_NETFILTER_FAMILY_BRIDGE
213 if (hook >= ARRAY_SIZE(net->nf.hooks_bridge))
214 return ERR_PTR(-EINVAL);
215 hook_head = rcu_dereference(net->nf.hooks_bridge[hook]);
216#endif
217 break;
42df6e1d 218#if defined(CONFIG_NETFILTER_INGRESS) || defined(CONFIG_NETFILTER_EGRESS)
e2cf17d3 219 case NFPROTO_NETDEV:
42df6e1d 220 if (hook >= NF_NETDEV_NUMHOOKS)
e2cf17d3
FW
221 return ERR_PTR(-EOPNOTSUPP);
222
223 if (!dev)
224 return ERR_PTR(-ENODEV);
225
226 netdev = dev_get_by_name_rcu(net, dev);
227 if (!netdev)
228 return ERR_PTR(-ENODEV);
229
42df6e1d
LW
230#ifdef CONFIG_NETFILTER_INGRESS
231 if (hook == NF_NETDEV_INGRESS)
232 return rcu_dereference(netdev->nf_hooks_ingress);
233#endif
234#ifdef CONFIG_NETFILTER_EGRESS
235 if (hook == NF_NETDEV_EGRESS)
236 return rcu_dereference(netdev->nf_hooks_egress);
237#endif
238 fallthrough;
e2cf17d3
FW
239#endif
240 default:
241 return ERR_PTR(-EPROTONOSUPPORT);
242 }
243
244 return hook_head;
245}
246
247static int nfnl_hook_dump(struct sk_buff *nlskb,
248 struct netlink_callback *cb)
249{
250 struct nfgenmsg *nfmsg = nlmsg_data(cb->nlh);
251 struct nfnl_dump_hook_data *ctx = cb->data;
252 int err, family = nfmsg->nfgen_family;
253 struct net *net = sock_net(nlskb->sk);
254 struct nf_hook_ops * const *ops;
255 const struct nf_hook_entries *e;
256 unsigned int i = cb->args[0];
257
258 rcu_read_lock();
259
260 e = nfnl_hook_entries_head(family, ctx->hook, net, ctx->devname);
261 if (!e)
262 goto done;
263
264 if (IS_ERR(e)) {
265 cb->seq++;
266 goto done;
267 }
268
269 if ((unsigned long)e != ctx->headv || i >= e->num_hook_entries)
270 cb->seq++;
271
272 ops = nf_hook_entries_get_hook_ops(e);
273
274 for (; i < e->num_hook_entries; i++) {
69311e7c 275 err = nfnl_hook_dump_one(nlskb, ctx, ops[i], family,
3d9bbaf6 276 cb->nlh->nlmsg_seq);
e2cf17d3
FW
277 if (err)
278 break;
279 }
280
281done:
282 nl_dump_check_consistent(cb, nlmsg_hdr(nlskb));
283 rcu_read_unlock();
284 cb->args[0] = i;
285 return nlskb->len;
286}
287
288static int nfnl_hook_dump_start(struct netlink_callback *cb)
289{
290 const struct nfgenmsg *nfmsg = nlmsg_data(cb->nlh);
291 const struct nlattr * const *nla = cb->data;
292 struct nfnl_dump_hook_data *ctx = NULL;
293 struct net *net = sock_net(cb->skb->sk);
294 u8 family = nfmsg->nfgen_family;
295 char name[IFNAMSIZ] = "";
296 const void *head;
297 u32 hooknum;
298
299 hooknum = ntohl(nla_get_be32(nla[NFNLA_HOOK_HOOKNUM]));
300 if (hooknum > 255)
301 return -EINVAL;
302
303 if (family == NFPROTO_NETDEV) {
304 if (!nla[NFNLA_HOOK_DEV])
305 return -EINVAL;
306
307 nla_strscpy(name, nla[NFNLA_HOOK_DEV], sizeof(name));
308 }
309
310 rcu_read_lock();
311 /* Not dereferenced; for consistency check only */
312 head = nfnl_hook_entries_head(family, hooknum, net, name);
313 rcu_read_unlock();
314
315 if (head && IS_ERR(head))
316 return PTR_ERR(head);
317
318 ctx = kzalloc(sizeof(*ctx), GFP_KERNEL);
319 if (!ctx)
320 return -ENOMEM;
321
322 strscpy(ctx->devname, name, sizeof(ctx->devname));
323 ctx->headv = (unsigned long)head;
324 ctx->hook = hooknum;
325
326 cb->seq = 1;
327 cb->data = ctx;
328
329 return 0;
330}
331
332static int nfnl_hook_dump_stop(struct netlink_callback *cb)
333{
334 kfree(cb->data);
335 return 0;
336}
337
338static int nfnl_hook_get(struct sk_buff *skb,
339 const struct nfnl_info *info,
340 const struct nlattr * const nla[])
341{
342 if (!nla[NFNLA_HOOK_HOOKNUM])
343 return -EINVAL;
344
345 if (info->nlh->nlmsg_flags & NLM_F_DUMP) {
346 struct netlink_dump_control c = {
347 .start = nfnl_hook_dump_start,
348 .done = nfnl_hook_dump_stop,
349 .dump = nfnl_hook_dump,
350 .module = THIS_MODULE,
351 .data = (void *)nla,
352 };
353
354 return nf_netlink_dump_start_rcu(info->sk, skb, info->nlh, &c);
355 }
356
357 return -EOPNOTSUPP;
358}
359
360static const struct nfnl_callback nfnl_hook_cb[NFNL_MSG_HOOK_MAX] = {
361 [NFNL_MSG_HOOK_GET] = {
362 .call = nfnl_hook_get,
363 .type = NFNL_CB_RCU,
364 .attr_count = NFNLA_HOOK_MAX,
365 .policy = nfnl_hook_nla_policy
366 },
367};
368
369static const struct nfnetlink_subsystem nfhook_subsys = {
370 .name = "nfhook",
371 .subsys_id = NFNL_SUBSYS_HOOK,
372 .cb_count = NFNL_MSG_HOOK_MAX,
373 .cb = nfnl_hook_cb,
374};
375
376MODULE_ALIAS_NFNL_SUBSYS(NFNL_SUBSYS_HOOK);
377
378static int __init nfnetlink_hook_init(void)
379{
380 return nfnetlink_subsys_register(&nfhook_subsys);
381}
382
383static void __exit nfnetlink_hook_exit(void)
384{
385 nfnetlink_subsys_unregister(&nfhook_subsys);
386}
387
388module_init(nfnetlink_hook_init);
389module_exit(nfnetlink_hook_exit);
390
391MODULE_LICENSE("GPL");
392MODULE_AUTHOR("Florian Westphal <fw@strlen.de>");
393MODULE_DESCRIPTION("nfnetlink_hook: list registered netfilter hooks");