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/bpf.h>
9 #include <linux/module.h>
10 #include <linux/kallsyms.h>
11 #include <linux/kernel.h>
12 #include <linux/types.h>
13 #include <linux/skbuff.h>
14 #include <linux/errno.h>
15 #include <linux/netlink.h>
16 #include <linux/slab.h>
17
18 #include <linux/netfilter.h>
19
20 #include <linux/netfilter/nfnetlink.h>
21 #include <linux/netfilter/nfnetlink_hook.h>
22
23 #include <net/netfilter/nf_tables.h>
24 #include <net/sock.h>
25
26 static const struct nla_policy nfnl_hook_nla_policy[NFNLA_HOOK_MAX + 1] = {
27 [NFNLA_HOOK_HOOKNUM] = { .type = NLA_U32 },
28 [NFNLA_HOOK_PRIORITY] = { .type = NLA_U32 },
29 [NFNLA_HOOK_DEV] = { .type = NLA_STRING,
30 .len = IFNAMSIZ - 1 },
31 [NFNLA_HOOK_FUNCTION_NAME] = { .type = NLA_NUL_STRING,
32 .len = KSYM_NAME_LEN, },
33 [NFNLA_HOOK_MODULE_NAME] = { .type = NLA_NUL_STRING,
34 .len = MODULE_NAME_LEN, },
35 [NFNLA_HOOK_CHAIN_INFO] = { .type = NLA_NESTED, },
36 };
37
nf_netlink_dump_start_rcu(struct sock * nlsk,struct sk_buff * skb,const struct nlmsghdr * nlh,struct netlink_dump_control * c)38 static int nf_netlink_dump_start_rcu(struct sock *nlsk, struct sk_buff *skb,
39 const struct nlmsghdr *nlh,
40 struct netlink_dump_control *c)
41 {
42 int err;
43
44 if (!try_module_get(THIS_MODULE))
45 return -EINVAL;
46
47 rcu_read_unlock();
48 err = netlink_dump_start(nlsk, skb, nlh, c);
49 rcu_read_lock();
50 module_put(THIS_MODULE);
51
52 return err;
53 }
54
55 struct nfnl_dump_hook_data {
56 char devname[IFNAMSIZ];
57 unsigned long headv;
58 u8 hook;
59 };
60
nfnl_start_info_type(struct sk_buff * nlskb,enum nfnl_hook_chaintype t)61 static struct nlattr *nfnl_start_info_type(struct sk_buff *nlskb, enum nfnl_hook_chaintype t)
62 {
63 struct nlattr *nest = nla_nest_start(nlskb, NFNLA_HOOK_CHAIN_INFO);
64 int ret;
65
66 if (!nest)
67 return NULL;
68
69 ret = nla_put_be32(nlskb, NFNLA_HOOK_INFO_TYPE, htonl(t));
70 if (ret == 0)
71 return nest;
72
73 nla_nest_cancel(nlskb, nest);
74 return NULL;
75 }
76
nfnl_hook_put_bpf_prog_info(struct sk_buff * nlskb,const struct nfnl_dump_hook_data * ctx,unsigned int seq,const struct bpf_prog * prog)77 static int nfnl_hook_put_bpf_prog_info(struct sk_buff *nlskb,
78 const struct nfnl_dump_hook_data *ctx,
79 unsigned int seq,
80 const struct bpf_prog *prog)
81 {
82 struct nlattr *nest, *nest2;
83 int ret;
84
85 if (!IS_ENABLED(CONFIG_NETFILTER_BPF_LINK))
86 return 0;
87
88 if (WARN_ON_ONCE(!prog))
89 return 0;
90
91 nest = nfnl_start_info_type(nlskb, NFNL_HOOK_TYPE_BPF);
92 if (!nest)
93 return -EMSGSIZE;
94
95 nest2 = nla_nest_start(nlskb, NFNLA_HOOK_INFO_DESC);
96 if (!nest2)
97 goto cancel_nest;
98
99 ret = nla_put_be32(nlskb, NFNLA_HOOK_BPF_ID, htonl(prog->aux->id));
100 if (ret)
101 goto cancel_nest;
102
103 nla_nest_end(nlskb, nest2);
104 nla_nest_end(nlskb, nest);
105 return 0;
106
107 cancel_nest:
108 nla_nest_cancel(nlskb, nest);
109 return -EMSGSIZE;
110 }
111
nfnl_hook_put_nft_info_desc(struct sk_buff * nlskb,const char * tname,const char * name,u8 family)112 static int nfnl_hook_put_nft_info_desc(struct sk_buff *nlskb, const char *tname,
113 const char *name, u8 family)
114 {
115 struct nlattr *nest;
116
117 nest = nla_nest_start(nlskb, NFNLA_HOOK_INFO_DESC);
118 if (!nest ||
119 nla_put_string(nlskb, NFNLA_CHAIN_TABLE, tname) ||
120 nla_put_string(nlskb, NFNLA_CHAIN_NAME, name) ||
121 nla_put_u8(nlskb, NFNLA_CHAIN_FAMILY, family)) {
122 nla_nest_cancel(nlskb, nest);
123 return -EMSGSIZE;
124 }
125 nla_nest_end(nlskb, nest);
126 return 0;
127 }
128
nfnl_hook_put_nft_chain_info(struct sk_buff * nlskb,const struct nfnl_dump_hook_data * ctx,unsigned int seq,struct nft_chain * chain)129 static int nfnl_hook_put_nft_chain_info(struct sk_buff *nlskb,
130 const struct nfnl_dump_hook_data *ctx,
131 unsigned int seq,
132 struct nft_chain *chain)
133 {
134 struct net *net = sock_net(nlskb->sk);
135 struct nlattr *nest;
136 int ret = 0;
137
138 if (WARN_ON_ONCE(!chain))
139 return 0;
140
141 if (!nft_is_active(net, chain))
142 return 0;
143
144 nest = nfnl_start_info_type(nlskb, NFNL_HOOK_TYPE_NFTABLES);
145 if (!nest)
146 return -EMSGSIZE;
147
148 ret = nfnl_hook_put_nft_info_desc(nlskb, chain->table->name,
149 chain->name, chain->table->family);
150 if (ret) {
151 nla_nest_cancel(nlskb, nest);
152 return ret;
153 }
154
155 nla_nest_end(nlskb, nest);
156 return 0;
157 }
158
nfnl_hook_put_nft_ft_info(struct sk_buff * nlskb,const struct nfnl_dump_hook_data * ctx,unsigned int seq,struct nf_flowtable * nf_ft)159 static int nfnl_hook_put_nft_ft_info(struct sk_buff *nlskb,
160 const struct nfnl_dump_hook_data *ctx,
161 unsigned int seq,
162 struct nf_flowtable *nf_ft)
163 {
164 struct nft_flowtable *ft =
165 container_of(nf_ft, struct nft_flowtable, data);
166 struct net *net = sock_net(nlskb->sk);
167 struct nlattr *nest;
168 int ret = 0;
169
170 if (WARN_ON_ONCE(!nf_ft))
171 return 0;
172
173 if (!nft_is_active(net, ft))
174 return 0;
175
176 nest = nfnl_start_info_type(nlskb, NFNL_HOOK_TYPE_NFT_FLOWTABLE);
177 if (!nest)
178 return -EMSGSIZE;
179
180 ret = nfnl_hook_put_nft_info_desc(nlskb, ft->table->name,
181 ft->name, ft->table->family);
182 if (ret) {
183 nla_nest_cancel(nlskb, nest);
184 return ret;
185 }
186
187 nla_nest_end(nlskb, nest);
188 return 0;
189 }
190
nfnl_hook_dump_one(struct sk_buff * nlskb,const struct nfnl_dump_hook_data * ctx,const struct nf_hook_ops * ops,int family,unsigned int seq)191 static int nfnl_hook_dump_one(struct sk_buff *nlskb,
192 const struct nfnl_dump_hook_data *ctx,
193 const struct nf_hook_ops *ops,
194 int family, unsigned int seq)
195 {
196 u16 event = nfnl_msg_type(NFNL_SUBSYS_HOOK, NFNL_MSG_HOOK_GET);
197 unsigned int portid = NETLINK_CB(nlskb).portid;
198 struct nlmsghdr *nlh;
199 int ret = -EMSGSIZE;
200 u32 hooknum;
201 #ifdef CONFIG_KALLSYMS
202 char sym[KSYM_SYMBOL_LEN];
203 char *module_name;
204 #endif
205 nlh = nfnl_msg_put(nlskb, portid, seq, event,
206 NLM_F_MULTI, family, NFNETLINK_V0, 0);
207 if (!nlh)
208 goto nla_put_failure;
209
210 #ifdef CONFIG_KALLSYMS
211 ret = snprintf(sym, sizeof(sym), "%ps", ops->hook);
212 if (ret >= sizeof(sym)) {
213 ret = -EINVAL;
214 goto nla_put_failure;
215 }
216
217 module_name = strstr(sym, " [");
218 if (module_name) {
219 char *end;
220
221 *module_name = '\0';
222 module_name += 2;
223 end = strchr(module_name, ']');
224 if (end) {
225 *end = 0;
226
227 ret = nla_put_string(nlskb, NFNLA_HOOK_MODULE_NAME, module_name);
228 if (ret)
229 goto nla_put_failure;
230 }
231 }
232
233 ret = nla_put_string(nlskb, NFNLA_HOOK_FUNCTION_NAME, sym);
234 if (ret)
235 goto nla_put_failure;
236 #endif
237
238 if (ops->pf == NFPROTO_INET && ops->hooknum == NF_INET_INGRESS)
239 hooknum = NF_NETDEV_INGRESS;
240 else
241 hooknum = ops->hooknum;
242
243 ret = nla_put_be32(nlskb, NFNLA_HOOK_HOOKNUM, htonl(hooknum));
244 if (ret)
245 goto nla_put_failure;
246
247 ret = nla_put_be32(nlskb, NFNLA_HOOK_PRIORITY, htonl(ops->priority));
248 if (ret)
249 goto nla_put_failure;
250
251 switch (ops->hook_ops_type) {
252 case NF_HOOK_OP_NF_TABLES:
253 ret = nfnl_hook_put_nft_chain_info(nlskb, ctx, seq, ops->priv);
254 break;
255 case NF_HOOK_OP_BPF:
256 ret = nfnl_hook_put_bpf_prog_info(nlskb, ctx, seq, ops->priv);
257 break;
258 case NF_HOOK_OP_NFT_FT:
259 ret = nfnl_hook_put_nft_ft_info(nlskb, ctx, seq, ops->priv);
260 break;
261 case NF_HOOK_OP_UNDEFINED:
262 break;
263 default:
264 WARN_ON_ONCE(1);
265 break;
266 }
267
268 if (ret)
269 goto nla_put_failure;
270
271 nlmsg_end(nlskb, nlh);
272 return 0;
273 nla_put_failure:
274 nlmsg_trim(nlskb, nlh);
275 return ret;
276 }
277
278 static const struct nf_hook_entries *
nfnl_hook_entries_head(u8 pf,unsigned int hook,struct net * net,const char * dev)279 nfnl_hook_entries_head(u8 pf, unsigned int hook, struct net *net, const char *dev)
280 {
281 const struct nf_hook_entries *hook_head = NULL;
282 #if defined(CONFIG_NETFILTER_INGRESS) || defined(CONFIG_NETFILTER_EGRESS)
283 struct net_device *netdev;
284 #endif
285
286 switch (pf) {
287 case NFPROTO_IPV4:
288 if (hook >= ARRAY_SIZE(net->nf.hooks_ipv4))
289 return ERR_PTR(-EINVAL);
290 hook_head = rcu_dereference(net->nf.hooks_ipv4[hook]);
291 break;
292 case NFPROTO_IPV6:
293 if (hook >= ARRAY_SIZE(net->nf.hooks_ipv6))
294 return ERR_PTR(-EINVAL);
295 hook_head = rcu_dereference(net->nf.hooks_ipv6[hook]);
296 break;
297 case NFPROTO_ARP:
298 #ifdef CONFIG_NETFILTER_FAMILY_ARP
299 if (hook >= ARRAY_SIZE(net->nf.hooks_arp))
300 return ERR_PTR(-EINVAL);
301 hook_head = rcu_dereference(net->nf.hooks_arp[hook]);
302 #endif
303 break;
304 case NFPROTO_BRIDGE:
305 #ifdef CONFIG_NETFILTER_FAMILY_BRIDGE
306 if (hook >= ARRAY_SIZE(net->nf.hooks_bridge))
307 return ERR_PTR(-EINVAL);
308 hook_head = rcu_dereference(net->nf.hooks_bridge[hook]);
309 #endif
310 break;
311 #if defined(CONFIG_NETFILTER_INGRESS) || defined(CONFIG_NETFILTER_EGRESS)
312 case NFPROTO_NETDEV:
313 if (hook >= NF_NETDEV_NUMHOOKS)
314 return ERR_PTR(-EOPNOTSUPP);
315
316 if (!dev)
317 return ERR_PTR(-ENODEV);
318
319 netdev = dev_get_by_name_rcu(net, dev);
320 if (!netdev)
321 return ERR_PTR(-ENODEV);
322
323 #ifdef CONFIG_NETFILTER_INGRESS
324 if (hook == NF_NETDEV_INGRESS)
325 return rcu_dereference(netdev->nf_hooks_ingress);
326 #endif
327 #ifdef CONFIG_NETFILTER_EGRESS
328 if (hook == NF_NETDEV_EGRESS)
329 return rcu_dereference(netdev->nf_hooks_egress);
330 #endif
331 fallthrough;
332 #endif
333 default:
334 return ERR_PTR(-EPROTONOSUPPORT);
335 }
336
337 return hook_head;
338 }
339
nfnl_hook_dump(struct sk_buff * nlskb,struct netlink_callback * cb)340 static int nfnl_hook_dump(struct sk_buff *nlskb,
341 struct netlink_callback *cb)
342 {
343 struct nfgenmsg *nfmsg = nlmsg_data(cb->nlh);
344 struct nfnl_dump_hook_data *ctx = cb->data;
345 int err, family = nfmsg->nfgen_family;
346 struct net *net = sock_net(nlskb->sk);
347 struct nf_hook_ops * const *ops;
348 const struct nf_hook_entries *e;
349 unsigned int i = cb->args[0];
350
351 rcu_read_lock();
352
353 e = nfnl_hook_entries_head(family, ctx->hook, net, ctx->devname);
354 if (!e)
355 goto done;
356
357 if (IS_ERR(e)) {
358 cb->seq++;
359 goto done;
360 }
361
362 if ((unsigned long)e != ctx->headv || i >= e->num_hook_entries)
363 cb->seq++;
364
365 ops = nf_hook_entries_get_hook_ops(e);
366
367 for (; i < e->num_hook_entries; i++) {
368 err = nfnl_hook_dump_one(nlskb, ctx, ops[i], family,
369 cb->nlh->nlmsg_seq);
370 if (err)
371 break;
372 }
373
374 done:
375 nl_dump_check_consistent(cb, nlmsg_hdr(nlskb));
376 rcu_read_unlock();
377 cb->args[0] = i;
378 return nlskb->len;
379 }
380
nfnl_hook_dump_start(struct netlink_callback * cb)381 static int nfnl_hook_dump_start(struct netlink_callback *cb)
382 {
383 const struct nfgenmsg *nfmsg = nlmsg_data(cb->nlh);
384 const struct nlattr * const *nla = cb->data;
385 struct nfnl_dump_hook_data *ctx = NULL;
386 struct net *net = sock_net(cb->skb->sk);
387 u8 family = nfmsg->nfgen_family;
388 char name[IFNAMSIZ] = "";
389 const void *head;
390 u32 hooknum;
391
392 hooknum = ntohl(nla_get_be32(nla[NFNLA_HOOK_HOOKNUM]));
393 if (hooknum > 255)
394 return -EINVAL;
395
396 if (family == NFPROTO_NETDEV) {
397 if (!nla[NFNLA_HOOK_DEV])
398 return -EINVAL;
399
400 nla_strscpy(name, nla[NFNLA_HOOK_DEV], sizeof(name));
401 }
402
403 rcu_read_lock();
404 /* Not dereferenced; for consistency check only */
405 head = nfnl_hook_entries_head(family, hooknum, net, name);
406 rcu_read_unlock();
407
408 if (head && IS_ERR(head))
409 return PTR_ERR(head);
410
411 ctx = kzalloc(sizeof(*ctx), GFP_KERNEL);
412 if (!ctx)
413 return -ENOMEM;
414
415 strscpy(ctx->devname, name, sizeof(ctx->devname));
416 ctx->headv = (unsigned long)head;
417 ctx->hook = hooknum;
418
419 cb->seq = 1;
420 cb->data = ctx;
421
422 return 0;
423 }
424
nfnl_hook_dump_stop(struct netlink_callback * cb)425 static int nfnl_hook_dump_stop(struct netlink_callback *cb)
426 {
427 kfree(cb->data);
428 return 0;
429 }
430
nfnl_hook_get(struct sk_buff * skb,const struct nfnl_info * info,const struct nlattr * const nla[])431 static int nfnl_hook_get(struct sk_buff *skb,
432 const struct nfnl_info *info,
433 const struct nlattr * const nla[])
434 {
435 if (!nla[NFNLA_HOOK_HOOKNUM])
436 return -EINVAL;
437
438 if (info->nlh->nlmsg_flags & NLM_F_DUMP) {
439 struct netlink_dump_control c = {
440 .start = nfnl_hook_dump_start,
441 .done = nfnl_hook_dump_stop,
442 .dump = nfnl_hook_dump,
443 .module = THIS_MODULE,
444 .data = (void *)nla,
445 };
446
447 return nf_netlink_dump_start_rcu(info->sk, skb, info->nlh, &c);
448 }
449
450 return -EOPNOTSUPP;
451 }
452
453 static const struct nfnl_callback nfnl_hook_cb[NFNL_MSG_HOOK_MAX] = {
454 [NFNL_MSG_HOOK_GET] = {
455 .call = nfnl_hook_get,
456 .type = NFNL_CB_RCU,
457 .attr_count = NFNLA_HOOK_MAX,
458 .policy = nfnl_hook_nla_policy
459 },
460 };
461
462 static const struct nfnetlink_subsystem nfhook_subsys = {
463 .name = "nfhook",
464 .subsys_id = NFNL_SUBSYS_HOOK,
465 .cb_count = NFNL_MSG_HOOK_MAX,
466 .cb = nfnl_hook_cb,
467 };
468
469 MODULE_ALIAS_NFNL_SUBSYS(NFNL_SUBSYS_HOOK);
470
nfnetlink_hook_init(void)471 static int __init nfnetlink_hook_init(void)
472 {
473 return nfnetlink_subsys_register(&nfhook_subsys);
474 }
475
nfnetlink_hook_exit(void)476 static void __exit nfnetlink_hook_exit(void)
477 {
478 nfnetlink_subsys_unregister(&nfhook_subsys);
479 }
480
481 module_init(nfnetlink_hook_init);
482 module_exit(nfnetlink_hook_exit);
483
484 MODULE_LICENSE("GPL");
485 MODULE_AUTHOR("Florian Westphal <fw@strlen.de>");
486 MODULE_DESCRIPTION("nfnetlink_hook: list registered netfilter hooks");
487