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