GNU Linux-libre 6.1.90-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 defined(CONFIG_NETFILTER_INGRESS) || defined(CONFIG_NETFILTER_EGRESS)
219         case NFPROTO_NETDEV:
220                 if (hook >= NF_NETDEV_NUMHOOKS)
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
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;
239 #endif
240         default:
241                 return ERR_PTR(-EPROTONOSUPPORT);
242         }
243
244         return hook_head;
245 }
246
247 static 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++) {
275                 err = nfnl_hook_dump_one(nlskb, ctx, ops[i], family,
276                                          cb->nlh->nlmsg_seq);
277                 if (err)
278                         break;
279         }
280
281 done:
282         nl_dump_check_consistent(cb, nlmsg_hdr(nlskb));
283         rcu_read_unlock();
284         cb->args[0] = i;
285         return nlskb->len;
286 }
287
288 static 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
332 static int nfnl_hook_dump_stop(struct netlink_callback *cb)
333 {
334         kfree(cb->data);
335         return 0;
336 }
337
338 static 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
360 static 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
369 static 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
376 MODULE_ALIAS_NFNL_SUBSYS(NFNL_SUBSYS_HOOK);
377
378 static int __init nfnetlink_hook_init(void)
379 {
380         return nfnetlink_subsys_register(&nfhook_subsys);
381 }
382
383 static void __exit nfnetlink_hook_exit(void)
384 {
385         nfnetlink_subsys_unregister(&nfhook_subsys);
386 }
387
388 module_init(nfnetlink_hook_init);
389 module_exit(nfnetlink_hook_exit);
390
391 MODULE_LICENSE("GPL");
392 MODULE_AUTHOR("Florian Westphal <fw@strlen.de>");
393 MODULE_DESCRIPTION("nfnetlink_hook: list registered netfilter hooks");