• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 #include <linux/init.h>
2 #include <linux/kernel.h>
3 #include <linux/netdevice.h>
4 #include <net/net_namespace.h>
5 #include <net/netfilter/nf_tables.h>
6 #include <linux/netfilter_ipv4.h>
7 #include <linux/netfilter_ipv6.h>
8 #include <linux/netfilter_bridge.h>
9 #include <linux/netfilter_arp.h>
10 #include <net/netfilter/nf_tables_ipv4.h>
11 #include <net/netfilter/nf_tables_ipv6.h>
12 
13 #ifdef CONFIG_NF_TABLES_IPV4
nft_do_chain_ipv4(void * priv,struct sk_buff * skb,const struct nf_hook_state * state)14 static unsigned int nft_do_chain_ipv4(void *priv,
15 				      struct sk_buff *skb,
16 				      const struct nf_hook_state *state)
17 {
18 	struct nft_pktinfo pkt;
19 
20 	nft_set_pktinfo(&pkt, skb, state);
21 	nft_set_pktinfo_ipv4(&pkt, skb);
22 
23 	return nft_do_chain(&pkt, priv);
24 }
25 
26 static const struct nft_chain_type nft_chain_filter_ipv4 = {
27 	.name		= "filter",
28 	.type		= NFT_CHAIN_T_DEFAULT,
29 	.family		= NFPROTO_IPV4,
30 	.hook_mask	= (1 << NF_INET_LOCAL_IN) |
31 			  (1 << NF_INET_LOCAL_OUT) |
32 			  (1 << NF_INET_FORWARD) |
33 			  (1 << NF_INET_PRE_ROUTING) |
34 			  (1 << NF_INET_POST_ROUTING),
35 	.hooks		= {
36 		[NF_INET_LOCAL_IN]	= nft_do_chain_ipv4,
37 		[NF_INET_LOCAL_OUT]	= nft_do_chain_ipv4,
38 		[NF_INET_FORWARD]	= nft_do_chain_ipv4,
39 		[NF_INET_PRE_ROUTING]	= nft_do_chain_ipv4,
40 		[NF_INET_POST_ROUTING]	= nft_do_chain_ipv4,
41 	},
42 };
43 
nft_chain_filter_ipv4_init(void)44 static void nft_chain_filter_ipv4_init(void)
45 {
46 	nft_register_chain_type(&nft_chain_filter_ipv4);
47 }
nft_chain_filter_ipv4_fini(void)48 static void nft_chain_filter_ipv4_fini(void)
49 {
50 	nft_unregister_chain_type(&nft_chain_filter_ipv4);
51 }
52 
53 #else
nft_chain_filter_ipv4_init(void)54 static inline void nft_chain_filter_ipv4_init(void) {}
nft_chain_filter_ipv4_fini(void)55 static inline void nft_chain_filter_ipv4_fini(void) {}
56 #endif /* CONFIG_NF_TABLES_IPV4 */
57 
58 #ifdef CONFIG_NF_TABLES_ARP
nft_do_chain_arp(void * priv,struct sk_buff * skb,const struct nf_hook_state * state)59 static unsigned int nft_do_chain_arp(void *priv, struct sk_buff *skb,
60 				     const struct nf_hook_state *state)
61 {
62 	struct nft_pktinfo pkt;
63 
64 	nft_set_pktinfo(&pkt, skb, state);
65 	nft_set_pktinfo_unspec(&pkt, skb);
66 
67 	return nft_do_chain(&pkt, priv);
68 }
69 
70 static const struct nft_chain_type nft_chain_filter_arp = {
71 	.name		= "filter",
72 	.type		= NFT_CHAIN_T_DEFAULT,
73 	.family		= NFPROTO_ARP,
74 	.owner		= THIS_MODULE,
75 	.hook_mask	= (1 << NF_ARP_IN) |
76 			  (1 << NF_ARP_OUT),
77 	.hooks		= {
78 		[NF_ARP_IN]		= nft_do_chain_arp,
79 		[NF_ARP_OUT]		= nft_do_chain_arp,
80 	},
81 };
82 
nft_chain_filter_arp_init(void)83 static void nft_chain_filter_arp_init(void)
84 {
85 	nft_register_chain_type(&nft_chain_filter_arp);
86 }
87 
nft_chain_filter_arp_fini(void)88 static void nft_chain_filter_arp_fini(void)
89 {
90 	nft_unregister_chain_type(&nft_chain_filter_arp);
91 }
92 #else
nft_chain_filter_arp_init(void)93 static inline void nft_chain_filter_arp_init(void) {}
nft_chain_filter_arp_fini(void)94 static inline void nft_chain_filter_arp_fini(void) {}
95 #endif /* CONFIG_NF_TABLES_ARP */
96 
97 #ifdef CONFIG_NF_TABLES_IPV6
nft_do_chain_ipv6(void * priv,struct sk_buff * skb,const struct nf_hook_state * state)98 static unsigned int nft_do_chain_ipv6(void *priv,
99 				      struct sk_buff *skb,
100 				      const struct nf_hook_state *state)
101 {
102 	struct nft_pktinfo pkt;
103 
104 	nft_set_pktinfo(&pkt, skb, state);
105 	nft_set_pktinfo_ipv6(&pkt, skb);
106 
107 	return nft_do_chain(&pkt, priv);
108 }
109 
110 static const struct nft_chain_type nft_chain_filter_ipv6 = {
111 	.name		= "filter",
112 	.type		= NFT_CHAIN_T_DEFAULT,
113 	.family		= NFPROTO_IPV6,
114 	.hook_mask	= (1 << NF_INET_LOCAL_IN) |
115 			  (1 << NF_INET_LOCAL_OUT) |
116 			  (1 << NF_INET_FORWARD) |
117 			  (1 << NF_INET_PRE_ROUTING) |
118 			  (1 << NF_INET_POST_ROUTING),
119 	.hooks		= {
120 		[NF_INET_LOCAL_IN]	= nft_do_chain_ipv6,
121 		[NF_INET_LOCAL_OUT]	= nft_do_chain_ipv6,
122 		[NF_INET_FORWARD]	= nft_do_chain_ipv6,
123 		[NF_INET_PRE_ROUTING]	= nft_do_chain_ipv6,
124 		[NF_INET_POST_ROUTING]	= nft_do_chain_ipv6,
125 	},
126 };
127 
nft_chain_filter_ipv6_init(void)128 static void nft_chain_filter_ipv6_init(void)
129 {
130 	nft_register_chain_type(&nft_chain_filter_ipv6);
131 }
132 
nft_chain_filter_ipv6_fini(void)133 static void nft_chain_filter_ipv6_fini(void)
134 {
135 	nft_unregister_chain_type(&nft_chain_filter_ipv6);
136 }
137 #else
nft_chain_filter_ipv6_init(void)138 static inline void nft_chain_filter_ipv6_init(void) {}
nft_chain_filter_ipv6_fini(void)139 static inline void nft_chain_filter_ipv6_fini(void) {}
140 #endif /* CONFIG_NF_TABLES_IPV6 */
141 
142 #ifdef CONFIG_NF_TABLES_INET
nft_do_chain_inet(void * priv,struct sk_buff * skb,const struct nf_hook_state * state)143 static unsigned int nft_do_chain_inet(void *priv, struct sk_buff *skb,
144 				      const struct nf_hook_state *state)
145 {
146 	struct nft_pktinfo pkt;
147 
148 	nft_set_pktinfo(&pkt, skb, state);
149 
150 	switch (state->pf) {
151 	case NFPROTO_IPV4:
152 		nft_set_pktinfo_ipv4(&pkt, skb);
153 		break;
154 	case NFPROTO_IPV6:
155 		nft_set_pktinfo_ipv6(&pkt, skb);
156 		break;
157 	default:
158 		break;
159 	}
160 
161 	return nft_do_chain(&pkt, priv);
162 }
163 
164 static const struct nft_chain_type nft_chain_filter_inet = {
165 	.name		= "filter",
166 	.type		= NFT_CHAIN_T_DEFAULT,
167 	.family		= NFPROTO_INET,
168 	.hook_mask	= (1 << NF_INET_LOCAL_IN) |
169 			  (1 << NF_INET_LOCAL_OUT) |
170 			  (1 << NF_INET_FORWARD) |
171 			  (1 << NF_INET_PRE_ROUTING) |
172 			  (1 << NF_INET_POST_ROUTING),
173 	.hooks		= {
174 		[NF_INET_LOCAL_IN]	= nft_do_chain_inet,
175 		[NF_INET_LOCAL_OUT]	= nft_do_chain_inet,
176 		[NF_INET_FORWARD]	= nft_do_chain_inet,
177 		[NF_INET_PRE_ROUTING]	= nft_do_chain_inet,
178 		[NF_INET_POST_ROUTING]	= nft_do_chain_inet,
179         },
180 };
181 
nft_chain_filter_inet_init(void)182 static void nft_chain_filter_inet_init(void)
183 {
184 	nft_register_chain_type(&nft_chain_filter_inet);
185 }
186 
nft_chain_filter_inet_fini(void)187 static void nft_chain_filter_inet_fini(void)
188 {
189 	nft_unregister_chain_type(&nft_chain_filter_inet);
190 }
191 #else
nft_chain_filter_inet_init(void)192 static inline void nft_chain_filter_inet_init(void) {}
nft_chain_filter_inet_fini(void)193 static inline void nft_chain_filter_inet_fini(void) {}
194 #endif /* CONFIG_NF_TABLES_IPV6 */
195 
196 #if IS_ENABLED(CONFIG_NF_TABLES_BRIDGE)
197 static unsigned int
nft_do_chain_bridge(void * priv,struct sk_buff * skb,const struct nf_hook_state * state)198 nft_do_chain_bridge(void *priv,
199 		    struct sk_buff *skb,
200 		    const struct nf_hook_state *state)
201 {
202 	struct nft_pktinfo pkt;
203 
204 	nft_set_pktinfo(&pkt, skb, state);
205 
206 	switch (eth_hdr(skb)->h_proto) {
207 	case htons(ETH_P_IP):
208 		nft_set_pktinfo_ipv4_validate(&pkt, skb);
209 		break;
210 	case htons(ETH_P_IPV6):
211 		nft_set_pktinfo_ipv6_validate(&pkt, skb);
212 		break;
213 	default:
214 		nft_set_pktinfo_unspec(&pkt, skb);
215 		break;
216 	}
217 
218 	return nft_do_chain(&pkt, priv);
219 }
220 
221 static const struct nft_chain_type nft_chain_filter_bridge = {
222 	.name		= "filter",
223 	.type		= NFT_CHAIN_T_DEFAULT,
224 	.family		= NFPROTO_BRIDGE,
225 	.hook_mask	= (1 << NF_BR_PRE_ROUTING) |
226 			  (1 << NF_BR_LOCAL_IN) |
227 			  (1 << NF_BR_FORWARD) |
228 			  (1 << NF_BR_LOCAL_OUT) |
229 			  (1 << NF_BR_POST_ROUTING),
230 	.hooks		= {
231 		[NF_BR_PRE_ROUTING]	= nft_do_chain_bridge,
232 		[NF_BR_LOCAL_IN]	= nft_do_chain_bridge,
233 		[NF_BR_FORWARD]		= nft_do_chain_bridge,
234 		[NF_BR_LOCAL_OUT]	= nft_do_chain_bridge,
235 		[NF_BR_POST_ROUTING]	= nft_do_chain_bridge,
236 	},
237 };
238 
nft_chain_filter_bridge_init(void)239 static void nft_chain_filter_bridge_init(void)
240 {
241 	nft_register_chain_type(&nft_chain_filter_bridge);
242 }
243 
nft_chain_filter_bridge_fini(void)244 static void nft_chain_filter_bridge_fini(void)
245 {
246 	nft_unregister_chain_type(&nft_chain_filter_bridge);
247 }
248 #else
nft_chain_filter_bridge_init(void)249 static inline void nft_chain_filter_bridge_init(void) {}
nft_chain_filter_bridge_fini(void)250 static inline void nft_chain_filter_bridge_fini(void) {}
251 #endif /* CONFIG_NF_TABLES_BRIDGE */
252 
253 #ifdef CONFIG_NF_TABLES_NETDEV
nft_do_chain_netdev(void * priv,struct sk_buff * skb,const struct nf_hook_state * state)254 static unsigned int nft_do_chain_netdev(void *priv, struct sk_buff *skb,
255 					const struct nf_hook_state *state)
256 {
257 	struct nft_pktinfo pkt;
258 
259 	nft_set_pktinfo(&pkt, skb, state);
260 
261 	switch (skb->protocol) {
262 	case htons(ETH_P_IP):
263 		nft_set_pktinfo_ipv4_validate(&pkt, skb);
264 		break;
265 	case htons(ETH_P_IPV6):
266 		nft_set_pktinfo_ipv6_validate(&pkt, skb);
267 		break;
268 	default:
269 		nft_set_pktinfo_unspec(&pkt, skb);
270 		break;
271 	}
272 
273 	return nft_do_chain(&pkt, priv);
274 }
275 
276 static const struct nft_chain_type nft_chain_filter_netdev = {
277 	.name		= "filter",
278 	.type		= NFT_CHAIN_T_DEFAULT,
279 	.family		= NFPROTO_NETDEV,
280 	.hook_mask	= (1 << NF_NETDEV_INGRESS),
281 	.hooks		= {
282 		[NF_NETDEV_INGRESS]	= nft_do_chain_netdev,
283 	},
284 };
285 
nft_netdev_event(unsigned long event,struct net_device * dev,struct nft_ctx * ctx)286 static void nft_netdev_event(unsigned long event, struct net_device *dev,
287 			     struct nft_ctx *ctx)
288 {
289 	struct nft_base_chain *basechain = nft_base_chain(ctx->chain);
290 
291 	switch (event) {
292 	case NETDEV_UNREGISTER:
293 		if (strcmp(basechain->dev_name, dev->name) != 0)
294 			return;
295 
296 		/* UNREGISTER events are also happpening on netns exit.
297 		 *
298 		 * Altough nf_tables core releases all tables/chains, only
299 		 * this event handler provides guarantee that
300 		 * basechain.ops->dev is still accessible, so we cannot
301 		 * skip exiting net namespaces.
302 		 */
303 		__nft_release_basechain(ctx);
304 		break;
305 	case NETDEV_CHANGENAME:
306 		if (dev->ifindex != basechain->ops.dev->ifindex)
307 			return;
308 
309 		strncpy(basechain->dev_name, dev->name, IFNAMSIZ);
310 		break;
311 	}
312 }
313 
nf_tables_netdev_event(struct notifier_block * this,unsigned long event,void * ptr)314 static int nf_tables_netdev_event(struct notifier_block *this,
315 				  unsigned long event, void *ptr)
316 {
317 	struct net_device *dev = netdev_notifier_info_to_dev(ptr);
318 	struct nft_table *table;
319 	struct nft_chain *chain, *nr;
320 	struct nft_ctx ctx = {
321 		.net	= dev_net(dev),
322 	};
323 
324 	if (event != NETDEV_UNREGISTER &&
325 	    event != NETDEV_CHANGENAME)
326 		return NOTIFY_DONE;
327 
328 	mutex_lock(&ctx.net->nft.commit_mutex);
329 	list_for_each_entry(table, &ctx.net->nft.tables, list) {
330 		if (table->family != NFPROTO_NETDEV)
331 			continue;
332 
333 		ctx.family = table->family;
334 		ctx.table = table;
335 		list_for_each_entry_safe(chain, nr, &table->chains, list) {
336 			if (!nft_is_base_chain(chain))
337 				continue;
338 
339 			ctx.chain = chain;
340 			nft_netdev_event(event, dev, &ctx);
341 		}
342 	}
343 	mutex_unlock(&ctx.net->nft.commit_mutex);
344 
345 	return NOTIFY_DONE;
346 }
347 
348 static struct notifier_block nf_tables_netdev_notifier = {
349 	.notifier_call	= nf_tables_netdev_event,
350 };
351 
nft_chain_filter_netdev_init(void)352 static int nft_chain_filter_netdev_init(void)
353 {
354 	int err;
355 
356 	nft_register_chain_type(&nft_chain_filter_netdev);
357 
358 	err = register_netdevice_notifier(&nf_tables_netdev_notifier);
359 	if (err)
360 		goto err_register_netdevice_notifier;
361 
362 	return 0;
363 
364 err_register_netdevice_notifier:
365 	nft_unregister_chain_type(&nft_chain_filter_netdev);
366 
367 	return err;
368 }
369 
nft_chain_filter_netdev_fini(void)370 static void nft_chain_filter_netdev_fini(void)
371 {
372 	nft_unregister_chain_type(&nft_chain_filter_netdev);
373 	unregister_netdevice_notifier(&nf_tables_netdev_notifier);
374 }
375 #else
nft_chain_filter_netdev_init(void)376 static inline int nft_chain_filter_netdev_init(void) { return 0; }
nft_chain_filter_netdev_fini(void)377 static inline void nft_chain_filter_netdev_fini(void) {}
378 #endif /* CONFIG_NF_TABLES_NETDEV */
379 
nft_chain_filter_init(void)380 int __init nft_chain_filter_init(void)
381 {
382 	int err;
383 
384 	err = nft_chain_filter_netdev_init();
385 	if (err < 0)
386 		return err;
387 
388 	nft_chain_filter_ipv4_init();
389 	nft_chain_filter_ipv6_init();
390 	nft_chain_filter_arp_init();
391 	nft_chain_filter_inet_init();
392 	nft_chain_filter_bridge_init();
393 
394 	return 0;
395 }
396 
nft_chain_filter_fini(void)397 void nft_chain_filter_fini(void)
398 {
399 	nft_chain_filter_bridge_fini();
400 	nft_chain_filter_inet_fini();
401 	nft_chain_filter_arp_fini();
402 	nft_chain_filter_ipv6_fini();
403 	nft_chain_filter_ipv4_fini();
404 	nft_chain_filter_netdev_fini();
405 }
406