Linux 6.9-rc1
[linux-2.6-microblaze.git] / net / netfilter / nfnetlink_hook.c
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
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
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
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
112 static int nfnl_hook_put_nft_chain_info(struct sk_buff *nlskb,
113                                         const struct nfnl_dump_hook_data *ctx,
114                                         unsigned int seq,
115                                         struct nft_chain *chain)
116 {
117         struct net *net = sock_net(nlskb->sk);
118         struct nlattr *nest, *nest2;
119         int ret = 0;
120
121         if (WARN_ON_ONCE(!chain))
122                 return 0;
123
124         if (!nft_is_active(net, chain))
125                 return 0;
126
127         nest = nfnl_start_info_type(nlskb, NFNL_HOOK_TYPE_NFTABLES);
128         if (!nest)
129                 return -EMSGSIZE;
130
131         nest2 = nla_nest_start(nlskb, NFNLA_HOOK_INFO_DESC);
132         if (!nest2)
133                 goto cancel_nest;
134
135         ret = nla_put_string(nlskb, NFNLA_CHAIN_TABLE, chain->table->name);
136         if (ret)
137                 goto cancel_nest;
138
139         ret = nla_put_string(nlskb, NFNLA_CHAIN_NAME, chain->name);
140         if (ret)
141                 goto cancel_nest;
142
143         ret = nla_put_u8(nlskb, NFNLA_CHAIN_FAMILY, chain->table->family);
144         if (ret)
145                 goto cancel_nest;
146
147         nla_nest_end(nlskb, nest2);
148         nla_nest_end(nlskb, nest);
149         return ret;
150
151 cancel_nest:
152         nla_nest_cancel(nlskb, nest);
153         return -EMSGSIZE;
154 }
155
156 static int nfnl_hook_dump_one(struct sk_buff *nlskb,
157                               const struct nfnl_dump_hook_data *ctx,
158                               const struct nf_hook_ops *ops,
159                               int family, unsigned int seq)
160 {
161         u16 event = nfnl_msg_type(NFNL_SUBSYS_HOOK, NFNL_MSG_HOOK_GET);
162         unsigned int portid = NETLINK_CB(nlskb).portid;
163         struct nlmsghdr *nlh;
164         int ret = -EMSGSIZE;
165         u32 hooknum;
166 #ifdef CONFIG_KALLSYMS
167         char sym[KSYM_SYMBOL_LEN];
168         char *module_name;
169 #endif
170         nlh = nfnl_msg_put(nlskb, portid, seq, event,
171                            NLM_F_MULTI, family, NFNETLINK_V0, 0);
172         if (!nlh)
173                 goto nla_put_failure;
174
175 #ifdef CONFIG_KALLSYMS
176         ret = snprintf(sym, sizeof(sym), "%ps", ops->hook);
177         if (ret >= sizeof(sym)) {
178                 ret = -EINVAL;
179                 goto nla_put_failure;
180         }
181
182         module_name = strstr(sym, " [");
183         if (module_name) {
184                 char *end;
185
186                 *module_name = '\0';
187                 module_name += 2;
188                 end = strchr(module_name, ']');
189                 if (end) {
190                         *end = 0;
191
192                         ret = nla_put_string(nlskb, NFNLA_HOOK_MODULE_NAME, module_name);
193                         if (ret)
194                                 goto nla_put_failure;
195                 }
196         }
197
198         ret = nla_put_string(nlskb, NFNLA_HOOK_FUNCTION_NAME, sym);
199         if (ret)
200                 goto nla_put_failure;
201 #endif
202
203         if (ops->pf == NFPROTO_INET && ops->hooknum == NF_INET_INGRESS)
204                 hooknum = NF_NETDEV_INGRESS;
205         else
206                 hooknum = ops->hooknum;
207
208         ret = nla_put_be32(nlskb, NFNLA_HOOK_HOOKNUM, htonl(hooknum));
209         if (ret)
210                 goto nla_put_failure;
211
212         ret = nla_put_be32(nlskb, NFNLA_HOOK_PRIORITY, htonl(ops->priority));
213         if (ret)
214                 goto nla_put_failure;
215
216         switch (ops->hook_ops_type) {
217         case NF_HOOK_OP_NF_TABLES:
218                 ret = nfnl_hook_put_nft_chain_info(nlskb, ctx, seq, ops->priv);
219                 break;
220         case NF_HOOK_OP_BPF:
221                 ret = nfnl_hook_put_bpf_prog_info(nlskb, ctx, seq, ops->priv);
222                 break;
223         case NF_HOOK_OP_UNDEFINED:
224                 break;
225         default:
226                 WARN_ON_ONCE(1);
227                 break;
228         }
229
230         if (ret)
231                 goto nla_put_failure;
232
233         nlmsg_end(nlskb, nlh);
234         return 0;
235 nla_put_failure:
236         nlmsg_trim(nlskb, nlh);
237         return ret;
238 }
239
240 static const struct nf_hook_entries *
241 nfnl_hook_entries_head(u8 pf, unsigned int hook, struct net *net, const char *dev)
242 {
243         const struct nf_hook_entries *hook_head = NULL;
244 #if defined(CONFIG_NETFILTER_INGRESS) || defined(CONFIG_NETFILTER_EGRESS)
245         struct net_device *netdev;
246 #endif
247
248         switch (pf) {
249         case NFPROTO_IPV4:
250                 if (hook >= ARRAY_SIZE(net->nf.hooks_ipv4))
251                         return ERR_PTR(-EINVAL);
252                 hook_head = rcu_dereference(net->nf.hooks_ipv4[hook]);
253                 break;
254         case NFPROTO_IPV6:
255                 if (hook >= ARRAY_SIZE(net->nf.hooks_ipv6))
256                         return ERR_PTR(-EINVAL);
257                 hook_head = rcu_dereference(net->nf.hooks_ipv6[hook]);
258                 break;
259         case NFPROTO_ARP:
260 #ifdef CONFIG_NETFILTER_FAMILY_ARP
261                 if (hook >= ARRAY_SIZE(net->nf.hooks_arp))
262                         return ERR_PTR(-EINVAL);
263                 hook_head = rcu_dereference(net->nf.hooks_arp[hook]);
264 #endif
265                 break;
266         case NFPROTO_BRIDGE:
267 #ifdef CONFIG_NETFILTER_FAMILY_BRIDGE
268                 if (hook >= ARRAY_SIZE(net->nf.hooks_bridge))
269                         return ERR_PTR(-EINVAL);
270                 hook_head = rcu_dereference(net->nf.hooks_bridge[hook]);
271 #endif
272                 break;
273 #if defined(CONFIG_NETFILTER_INGRESS) || defined(CONFIG_NETFILTER_EGRESS)
274         case NFPROTO_NETDEV:
275                 if (hook >= NF_NETDEV_NUMHOOKS)
276                         return ERR_PTR(-EOPNOTSUPP);
277
278                 if (!dev)
279                         return ERR_PTR(-ENODEV);
280
281                 netdev = dev_get_by_name_rcu(net, dev);
282                 if (!netdev)
283                         return ERR_PTR(-ENODEV);
284
285 #ifdef CONFIG_NETFILTER_INGRESS
286                 if (hook == NF_NETDEV_INGRESS)
287                         return rcu_dereference(netdev->nf_hooks_ingress);
288 #endif
289 #ifdef CONFIG_NETFILTER_EGRESS
290                 if (hook == NF_NETDEV_EGRESS)
291                         return rcu_dereference(netdev->nf_hooks_egress);
292 #endif
293                 fallthrough;
294 #endif
295         default:
296                 return ERR_PTR(-EPROTONOSUPPORT);
297         }
298
299         return hook_head;
300 }
301
302 static int nfnl_hook_dump(struct sk_buff *nlskb,
303                           struct netlink_callback *cb)
304 {
305         struct nfgenmsg *nfmsg = nlmsg_data(cb->nlh);
306         struct nfnl_dump_hook_data *ctx = cb->data;
307         int err, family = nfmsg->nfgen_family;
308         struct net *net = sock_net(nlskb->sk);
309         struct nf_hook_ops * const *ops;
310         const struct nf_hook_entries *e;
311         unsigned int i = cb->args[0];
312
313         rcu_read_lock();
314
315         e = nfnl_hook_entries_head(family, ctx->hook, net, ctx->devname);
316         if (!e)
317                 goto done;
318
319         if (IS_ERR(e)) {
320                 cb->seq++;
321                 goto done;
322         }
323
324         if ((unsigned long)e != ctx->headv || i >= e->num_hook_entries)
325                 cb->seq++;
326
327         ops = nf_hook_entries_get_hook_ops(e);
328
329         for (; i < e->num_hook_entries; i++) {
330                 err = nfnl_hook_dump_one(nlskb, ctx, ops[i], family,
331                                          cb->nlh->nlmsg_seq);
332                 if (err)
333                         break;
334         }
335
336 done:
337         nl_dump_check_consistent(cb, nlmsg_hdr(nlskb));
338         rcu_read_unlock();
339         cb->args[0] = i;
340         return nlskb->len;
341 }
342
343 static int nfnl_hook_dump_start(struct netlink_callback *cb)
344 {
345         const struct nfgenmsg *nfmsg = nlmsg_data(cb->nlh);
346         const struct nlattr * const *nla = cb->data;
347         struct nfnl_dump_hook_data *ctx = NULL;
348         struct net *net = sock_net(cb->skb->sk);
349         u8 family = nfmsg->nfgen_family;
350         char name[IFNAMSIZ] = "";
351         const void *head;
352         u32 hooknum;
353
354         hooknum = ntohl(nla_get_be32(nla[NFNLA_HOOK_HOOKNUM]));
355         if (hooknum > 255)
356                 return -EINVAL;
357
358         if (family == NFPROTO_NETDEV) {
359                 if (!nla[NFNLA_HOOK_DEV])
360                         return -EINVAL;
361
362                 nla_strscpy(name, nla[NFNLA_HOOK_DEV], sizeof(name));
363         }
364
365         rcu_read_lock();
366         /* Not dereferenced; for consistency check only */
367         head = nfnl_hook_entries_head(family, hooknum, net, name);
368         rcu_read_unlock();
369
370         if (head && IS_ERR(head))
371                 return PTR_ERR(head);
372
373         ctx = kzalloc(sizeof(*ctx), GFP_KERNEL);
374         if (!ctx)
375                 return -ENOMEM;
376
377         strscpy(ctx->devname, name, sizeof(ctx->devname));
378         ctx->headv = (unsigned long)head;
379         ctx->hook = hooknum;
380
381         cb->seq = 1;
382         cb->data = ctx;
383
384         return 0;
385 }
386
387 static int nfnl_hook_dump_stop(struct netlink_callback *cb)
388 {
389         kfree(cb->data);
390         return 0;
391 }
392
393 static int nfnl_hook_get(struct sk_buff *skb,
394                          const struct nfnl_info *info,
395                          const struct nlattr * const nla[])
396 {
397         if (!nla[NFNLA_HOOK_HOOKNUM])
398                 return -EINVAL;
399
400         if (info->nlh->nlmsg_flags & NLM_F_DUMP) {
401                 struct netlink_dump_control c = {
402                         .start = nfnl_hook_dump_start,
403                         .done = nfnl_hook_dump_stop,
404                         .dump = nfnl_hook_dump,
405                         .module = THIS_MODULE,
406                         .data = (void *)nla,
407                 };
408
409                 return nf_netlink_dump_start_rcu(info->sk, skb, info->nlh, &c);
410         }
411
412         return -EOPNOTSUPP;
413 }
414
415 static const struct nfnl_callback nfnl_hook_cb[NFNL_MSG_HOOK_MAX] = {
416         [NFNL_MSG_HOOK_GET] = {
417                 .call           = nfnl_hook_get,
418                 .type           = NFNL_CB_RCU,
419                 .attr_count     = NFNLA_HOOK_MAX,
420                 .policy         = nfnl_hook_nla_policy
421         },
422 };
423
424 static const struct nfnetlink_subsystem nfhook_subsys = {
425         .name                           = "nfhook",
426         .subsys_id                      = NFNL_SUBSYS_HOOK,
427         .cb_count                       = NFNL_MSG_HOOK_MAX,
428         .cb                             = nfnl_hook_cb,
429 };
430
431 MODULE_ALIAS_NFNL_SUBSYS(NFNL_SUBSYS_HOOK);
432
433 static int __init nfnetlink_hook_init(void)
434 {
435         return nfnetlink_subsys_register(&nfhook_subsys);
436 }
437
438 static void __exit nfnetlink_hook_exit(void)
439 {
440         nfnetlink_subsys_unregister(&nfhook_subsys);
441 }
442
443 module_init(nfnetlink_hook_init);
444 module_exit(nfnetlink_hook_exit);
445
446 MODULE_LICENSE("GPL");
447 MODULE_AUTHOR("Florian Westphal <fw@strlen.de>");
448 MODULE_DESCRIPTION("nfnetlink_hook: list registered netfilter hooks");