1
2
3
4
5
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
24static 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
36static 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
53struct nfnl_dump_hook_data {
54 char devname[IFNAMSIZ];
55 unsigned long headv;
56 u8 hook;
57};
58
59static 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
108cancel_nest:
109 nla_nest_cancel(nlskb, nest);
110 return -EMSGSIZE;
111}
112
113static 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;
179nla_put_failure:
180 nlmsg_trim(nlskb, nlh);
181 return ret;
182}
183
184static const struct nf_hook_entries *
185nfnl_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
245static 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
279done:
280 nl_dump_check_consistent(cb, nlmsg_hdr(nlskb));
281 rcu_read_unlock();
282 cb->args[0] = i;
283 return nlskb->len;
284}
285
286static 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
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
330static int nfnl_hook_dump_stop(struct netlink_callback *cb)
331{
332 kfree(cb->data);
333 return 0;
334}
335
336static 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
358static 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
367static 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
374MODULE_ALIAS_NFNL_SUBSYS(NFNL_SUBSYS_HOOK);
375
376static int __init nfnetlink_hook_init(void)
377{
378 return nfnetlink_subsys_register(&nfhook_subsys);
379}
380
381static void __exit nfnetlink_hook_exit(void)
382{
383 nfnetlink_subsys_unregister(&nfhook_subsys);
384}
385
386module_init(nfnetlink_hook_init);
387module_exit(nfnetlink_hook_exit);
388
389MODULE_LICENSE("GPL");
390MODULE_AUTHOR("Florian Westphal <fw@strlen.de>");
391MODULE_DESCRIPTION("nfnetlink_hook: list registered netfilter hooks");
392