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