GNU Linux-libre 5.19-rc6-gnu
[releases.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/module.h>
9 #include <linux/kallsyms.h>
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
25 static 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
37 static 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
54 struct nfnl_dump_hook_data {
55         char devname[IFNAMSIZ];
56         unsigned long headv;
57         u8 hook;
58 };
59
60 static 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
93         ret = nla_put_string(nlskb, NFNLA_CHAIN_TABLE, chain->table->name);
94         if (ret)
95                 goto cancel_nest;
96
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);
102         if (ret)
103                 goto cancel_nest;
104
105         nla_nest_end(nlskb, nest2);
106         nla_nest_end(nlskb, nest);
107         return ret;
108
109 cancel_nest:
110         nla_nest_cancel(nlskb, nest);
111         return -EMSGSIZE;
112 }
113
114 static int nfnl_hook_dump_one(struct sk_buff *nlskb,
115                               const struct nfnl_dump_hook_data *ctx,
116                               const struct nf_hook_ops *ops,
117                               int family, unsigned int seq)
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;
123         u32 hooknum;
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,
129                            NLM_F_MULTI, family, NFNETLINK_V0, 0);
130         if (!nlh)
131                 goto nla_put_failure;
132
133 #ifdef CONFIG_KALLSYMS
134         ret = snprintf(sym, sizeof(sym), "%ps", ops->hook);
135         if (ret >= sizeof(sym)) {
136                 ret = -EINVAL;
137                 goto nla_put_failure;
138         }
139
140         module_name = strstr(sym, " [");
141         if (module_name) {
142                 char *end;
143
144                 *module_name = '\0';
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
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));
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;
180 nla_put_failure:
181         nlmsg_trim(nlskb, nlh);
182         return ret;
183 }
184
185 static const struct nf_hook_entries *
186 nfnl_hook_entries_head(u8 pf, unsigned int hook, struct net *net, const char *dev)
187 {
188         const struct nf_hook_entries *hook_head = NULL;
189 #if defined(CONFIG_NETFILTER_INGRESS) || defined(CONFIG_NETFILTER_EGRESS)
190         struct net_device *netdev;
191 #endif
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:
200                 if (hook >= ARRAY_SIZE(net->nf.hooks_ipv6))
201                         return ERR_PTR(-EINVAL);
202                 hook_head = rcu_dereference(net->nf.hooks_ipv6[hook]);
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;
218 #if IS_ENABLED(CONFIG_DECNET)
219         case NFPROTO_DECNET:
220                 if (hook >= ARRAY_SIZE(net->nf.hooks_decnet))
221                         return ERR_PTR(-EINVAL);
222                 hook_head = rcu_dereference(net->nf.hooks_decnet[hook]);
223                 break;
224 #endif
225 #if defined(CONFIG_NETFILTER_INGRESS) || defined(CONFIG_NETFILTER_EGRESS)
226         case NFPROTO_NETDEV:
227                 if (hook >= NF_NETDEV_NUMHOOKS)
228                         return ERR_PTR(-EOPNOTSUPP);
229
230                 if (!dev)
231                         return ERR_PTR(-ENODEV);
232
233                 netdev = dev_get_by_name_rcu(net, dev);
234                 if (!netdev)
235                         return ERR_PTR(-ENODEV);
236
237 #ifdef CONFIG_NETFILTER_INGRESS
238                 if (hook == NF_NETDEV_INGRESS)
239                         return rcu_dereference(netdev->nf_hooks_ingress);
240 #endif
241 #ifdef CONFIG_NETFILTER_EGRESS
242                 if (hook == NF_NETDEV_EGRESS)
243                         return rcu_dereference(netdev->nf_hooks_egress);
244 #endif
245                 fallthrough;
246 #endif
247         default:
248                 return ERR_PTR(-EPROTONOSUPPORT);
249         }
250
251         return hook_head;
252 }
253
254 static int nfnl_hook_dump(struct sk_buff *nlskb,
255                           struct netlink_callback *cb)
256 {
257         struct nfgenmsg *nfmsg = nlmsg_data(cb->nlh);
258         struct nfnl_dump_hook_data *ctx = cb->data;
259         int err, family = nfmsg->nfgen_family;
260         struct net *net = sock_net(nlskb->sk);
261         struct nf_hook_ops * const *ops;
262         const struct nf_hook_entries *e;
263         unsigned int i = cb->args[0];
264
265         rcu_read_lock();
266
267         e = nfnl_hook_entries_head(family, ctx->hook, net, ctx->devname);
268         if (!e)
269                 goto done;
270
271         if (IS_ERR(e)) {
272                 cb->seq++;
273                 goto done;
274         }
275
276         if ((unsigned long)e != ctx->headv || i >= e->num_hook_entries)
277                 cb->seq++;
278
279         ops = nf_hook_entries_get_hook_ops(e);
280
281         for (; i < e->num_hook_entries; i++) {
282                 err = nfnl_hook_dump_one(nlskb, ctx, ops[i], family,
283                                          cb->nlh->nlmsg_seq);
284                 if (err)
285                         break;
286         }
287
288 done:
289         nl_dump_check_consistent(cb, nlmsg_hdr(nlskb));
290         rcu_read_unlock();
291         cb->args[0] = i;
292         return nlskb->len;
293 }
294
295 static int nfnl_hook_dump_start(struct netlink_callback *cb)
296 {
297         const struct nfgenmsg *nfmsg = nlmsg_data(cb->nlh);
298         const struct nlattr * const *nla = cb->data;
299         struct nfnl_dump_hook_data *ctx = NULL;
300         struct net *net = sock_net(cb->skb->sk);
301         u8 family = nfmsg->nfgen_family;
302         char name[IFNAMSIZ] = "";
303         const void *head;
304         u32 hooknum;
305
306         hooknum = ntohl(nla_get_be32(nla[NFNLA_HOOK_HOOKNUM]));
307         if (hooknum > 255)
308                 return -EINVAL;
309
310         if (family == NFPROTO_NETDEV) {
311                 if (!nla[NFNLA_HOOK_DEV])
312                         return -EINVAL;
313
314                 nla_strscpy(name, nla[NFNLA_HOOK_DEV], sizeof(name));
315         }
316
317         rcu_read_lock();
318         /* Not dereferenced; for consistency check only */
319         head = nfnl_hook_entries_head(family, hooknum, net, name);
320         rcu_read_unlock();
321
322         if (head && IS_ERR(head))
323                 return PTR_ERR(head);
324
325         ctx = kzalloc(sizeof(*ctx), GFP_KERNEL);
326         if (!ctx)
327                 return -ENOMEM;
328
329         strscpy(ctx->devname, name, sizeof(ctx->devname));
330         ctx->headv = (unsigned long)head;
331         ctx->hook = hooknum;
332
333         cb->seq = 1;
334         cb->data = ctx;
335
336         return 0;
337 }
338
339 static int nfnl_hook_dump_stop(struct netlink_callback *cb)
340 {
341         kfree(cb->data);
342         return 0;
343 }
344
345 static int nfnl_hook_get(struct sk_buff *skb,
346                          const struct nfnl_info *info,
347                          const struct nlattr * const nla[])
348 {
349         if (!nla[NFNLA_HOOK_HOOKNUM])
350                 return -EINVAL;
351
352         if (info->nlh->nlmsg_flags & NLM_F_DUMP) {
353                 struct netlink_dump_control c = {
354                         .start = nfnl_hook_dump_start,
355                         .done = nfnl_hook_dump_stop,
356                         .dump = nfnl_hook_dump,
357                         .module = THIS_MODULE,
358                         .data = (void *)nla,
359                 };
360
361                 return nf_netlink_dump_start_rcu(info->sk, skb, info->nlh, &c);
362         }
363
364         return -EOPNOTSUPP;
365 }
366
367 static const struct nfnl_callback nfnl_hook_cb[NFNL_MSG_HOOK_MAX] = {
368         [NFNL_MSG_HOOK_GET] = {
369                 .call           = nfnl_hook_get,
370                 .type           = NFNL_CB_RCU,
371                 .attr_count     = NFNLA_HOOK_MAX,
372                 .policy         = nfnl_hook_nla_policy
373         },
374 };
375
376 static const struct nfnetlink_subsystem nfhook_subsys = {
377         .name                           = "nfhook",
378         .subsys_id                      = NFNL_SUBSYS_HOOK,
379         .cb_count                       = NFNL_MSG_HOOK_MAX,
380         .cb                             = nfnl_hook_cb,
381 };
382
383 MODULE_ALIAS_NFNL_SUBSYS(NFNL_SUBSYS_HOOK);
384
385 static int __init nfnetlink_hook_init(void)
386 {
387         return nfnetlink_subsys_register(&nfhook_subsys);
388 }
389
390 static void __exit nfnetlink_hook_exit(void)
391 {
392         nfnetlink_subsys_unregister(&nfhook_subsys);
393 }
394
395 module_init(nfnetlink_hook_init);
396 module_exit(nfnetlink_hook_exit);
397
398 MODULE_LICENSE("GPL");
399 MODULE_AUTHOR("Florian Westphal <fw@strlen.de>");
400 MODULE_DESCRIPTION("nfnetlink_hook: list registered netfilter hooks");