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 
nf_netlink_dump_start_rcu(struct sock * nlsk,struct sk_buff * skb,const struct nlmsghdr * nlh,struct netlink_dump_control * c)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 
nfnl_hook_put_nft_chain_info(struct sk_buff * nlskb,const struct nfnl_dump_hook_data * ctx,unsigned int seq,const struct nf_hook_ops * ops)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 
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)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 *
nfnl_hook_entries_head(u8 pf,unsigned int hook,struct net * net,const char * dev)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 
nfnl_hook_dump(struct sk_buff * nlskb,struct netlink_callback * cb)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 
nfnl_hook_dump_start(struct netlink_callback * cb)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 
nfnl_hook_dump_stop(struct netlink_callback * cb)330 static int nfnl_hook_dump_stop(struct netlink_callback *cb)
331 {
332 	kfree(cb->data);
333 	return 0;
334 }
335 
nfnl_hook_get(struct sk_buff * skb,const struct nfnl_info * info,const struct nlattr * const nla[])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 
nfnetlink_hook_init(void)376 static int __init nfnetlink_hook_init(void)
377 {
378 	return nfnetlink_subsys_register(&nfhook_subsys);
379 }
380 
nfnetlink_hook_exit(void)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");
392