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");
401