Linux 6.10-rc6
[linux-2.6-block.git] / net / netfilter / nft_chain_filter.c
CommitLineData
02c7b25e
PNA
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
14static 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);
f06ad944 21 nft_set_pktinfo_ipv4(&pkt);
02c7b25e
PNA
22
23 return nft_do_chain(&pkt, priv);
24}
25
26static 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
44static void nft_chain_filter_ipv4_init(void)
45{
46 nft_register_chain_type(&nft_chain_filter_ipv4);
47}
48static void nft_chain_filter_ipv4_fini(void)
49{
50 nft_unregister_chain_type(&nft_chain_filter_ipv4);
51}
52
53#else
54static inline void nft_chain_filter_ipv4_init(void) {}
55static inline void nft_chain_filter_ipv4_fini(void) {}
56#endif /* CONFIG_NF_TABLES_IPV4 */
57
58#ifdef CONFIG_NF_TABLES_ARP
59static 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);
f06ad944 65 nft_set_pktinfo_unspec(&pkt);
02c7b25e
PNA
66
67 return nft_do_chain(&pkt, priv);
68}
69
70static 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
83static void nft_chain_filter_arp_init(void)
84{
85 nft_register_chain_type(&nft_chain_filter_arp);
86}
87
88static void nft_chain_filter_arp_fini(void)
89{
90 nft_unregister_chain_type(&nft_chain_filter_arp);
91}
92#else
93static inline void nft_chain_filter_arp_init(void) {}
94static inline void nft_chain_filter_arp_fini(void) {}
95#endif /* CONFIG_NF_TABLES_ARP */
96
97#ifdef CONFIG_NF_TABLES_IPV6
98static 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);
f06ad944 105 nft_set_pktinfo_ipv6(&pkt);
02c7b25e
PNA
106
107 return nft_do_chain(&pkt, priv);
108}
109
110static 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
128static void nft_chain_filter_ipv6_init(void)
129{
130 nft_register_chain_type(&nft_chain_filter_ipv6);
131}
132
133static void nft_chain_filter_ipv6_fini(void)
134{
135 nft_unregister_chain_type(&nft_chain_filter_ipv6);
136}
137#else
138static inline void nft_chain_filter_ipv6_init(void) {}
139static inline void nft_chain_filter_ipv6_fini(void) {}
140#endif /* CONFIG_NF_TABLES_IPV6 */
141
142#ifdef CONFIG_NF_TABLES_INET
143static 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:
f06ad944 152 nft_set_pktinfo_ipv4(&pkt);
02c7b25e
PNA
153 break;
154 case NFPROTO_IPV6:
f06ad944 155 nft_set_pktinfo_ipv6(&pkt);
02c7b25e
PNA
156 break;
157 default:
158 break;
159 }
160
161 return nft_do_chain(&pkt, priv);
162}
163
d3519cb8
PNA
164static unsigned int nft_do_chain_inet_ingress(void *priv, struct sk_buff *skb,
165 const struct nf_hook_state *state)
166{
167 struct nf_hook_state ingress_state = *state;
168 struct nft_pktinfo pkt;
169
170 switch (skb->protocol) {
171 case htons(ETH_P_IP):
172 /* Original hook is NFPROTO_NETDEV and NF_NETDEV_INGRESS. */
173 ingress_state.pf = NFPROTO_IPV4;
174 ingress_state.hook = NF_INET_INGRESS;
175 nft_set_pktinfo(&pkt, skb, &ingress_state);
176
f06ad944 177 if (nft_set_pktinfo_ipv4_ingress(&pkt) < 0)
d3519cb8
PNA
178 return NF_DROP;
179 break;
180 case htons(ETH_P_IPV6):
181 ingress_state.pf = NFPROTO_IPV6;
182 ingress_state.hook = NF_INET_INGRESS;
183 nft_set_pktinfo(&pkt, skb, &ingress_state);
184
f06ad944 185 if (nft_set_pktinfo_ipv6_ingress(&pkt) < 0)
d3519cb8
PNA
186 return NF_DROP;
187 break;
188 default:
189 return NF_ACCEPT;
190 }
191
192 return nft_do_chain(&pkt, priv);
193}
194
02c7b25e
PNA
195static const struct nft_chain_type nft_chain_filter_inet = {
196 .name = "filter",
197 .type = NFT_CHAIN_T_DEFAULT,
198 .family = NFPROTO_INET,
d3519cb8
PNA
199 .hook_mask = (1 << NF_INET_INGRESS) |
200 (1 << NF_INET_LOCAL_IN) |
02c7b25e
PNA
201 (1 << NF_INET_LOCAL_OUT) |
202 (1 << NF_INET_FORWARD) |
203 (1 << NF_INET_PRE_ROUTING) |
204 (1 << NF_INET_POST_ROUTING),
205 .hooks = {
d3519cb8 206 [NF_INET_INGRESS] = nft_do_chain_inet_ingress,
02c7b25e
PNA
207 [NF_INET_LOCAL_IN] = nft_do_chain_inet,
208 [NF_INET_LOCAL_OUT] = nft_do_chain_inet,
209 [NF_INET_FORWARD] = nft_do_chain_inet,
210 [NF_INET_PRE_ROUTING] = nft_do_chain_inet,
211 [NF_INET_POST_ROUTING] = nft_do_chain_inet,
212 },
213};
214
215static void nft_chain_filter_inet_init(void)
216{
217 nft_register_chain_type(&nft_chain_filter_inet);
218}
219
220static void nft_chain_filter_inet_fini(void)
221{
222 nft_unregister_chain_type(&nft_chain_filter_inet);
223}
224#else
225static inline void nft_chain_filter_inet_init(void) {}
226static inline void nft_chain_filter_inet_fini(void) {}
227#endif /* CONFIG_NF_TABLES_IPV6 */
228
dfee0e99 229#if IS_ENABLED(CONFIG_NF_TABLES_BRIDGE)
02c7b25e
PNA
230static unsigned int
231nft_do_chain_bridge(void *priv,
232 struct sk_buff *skb,
233 const struct nf_hook_state *state)
234{
235 struct nft_pktinfo pkt;
236
237 nft_set_pktinfo(&pkt, skb, state);
238
239 switch (eth_hdr(skb)->h_proto) {
240 case htons(ETH_P_IP):
f06ad944 241 nft_set_pktinfo_ipv4_validate(&pkt);
02c7b25e
PNA
242 break;
243 case htons(ETH_P_IPV6):
f06ad944 244 nft_set_pktinfo_ipv6_validate(&pkt);
02c7b25e
PNA
245 break;
246 default:
f06ad944 247 nft_set_pktinfo_unspec(&pkt);
02c7b25e
PNA
248 break;
249 }
250
251 return nft_do_chain(&pkt, priv);
252}
253
254static const struct nft_chain_type nft_chain_filter_bridge = {
255 .name = "filter",
256 .type = NFT_CHAIN_T_DEFAULT,
257 .family = NFPROTO_BRIDGE,
258 .hook_mask = (1 << NF_BR_PRE_ROUTING) |
259 (1 << NF_BR_LOCAL_IN) |
260 (1 << NF_BR_FORWARD) |
261 (1 << NF_BR_LOCAL_OUT) |
262 (1 << NF_BR_POST_ROUTING),
263 .hooks = {
264 [NF_BR_PRE_ROUTING] = nft_do_chain_bridge,
265 [NF_BR_LOCAL_IN] = nft_do_chain_bridge,
266 [NF_BR_FORWARD] = nft_do_chain_bridge,
267 [NF_BR_LOCAL_OUT] = nft_do_chain_bridge,
268 [NF_BR_POST_ROUTING] = nft_do_chain_bridge,
269 },
270};
271
272static void nft_chain_filter_bridge_init(void)
273{
274 nft_register_chain_type(&nft_chain_filter_bridge);
275}
276
277static void nft_chain_filter_bridge_fini(void)
278{
279 nft_unregister_chain_type(&nft_chain_filter_bridge);
280}
281#else
282static inline void nft_chain_filter_bridge_init(void) {}
283static inline void nft_chain_filter_bridge_fini(void) {}
284#endif /* CONFIG_NF_TABLES_BRIDGE */
285
286#ifdef CONFIG_NF_TABLES_NETDEV
287static unsigned int nft_do_chain_netdev(void *priv, struct sk_buff *skb,
288 const struct nf_hook_state *state)
289{
290 struct nft_pktinfo pkt;
291
292 nft_set_pktinfo(&pkt, skb, state);
293
294 switch (skb->protocol) {
295 case htons(ETH_P_IP):
f06ad944 296 nft_set_pktinfo_ipv4_validate(&pkt);
02c7b25e
PNA
297 break;
298 case htons(ETH_P_IPV6):
f06ad944 299 nft_set_pktinfo_ipv6_validate(&pkt);
02c7b25e
PNA
300 break;
301 default:
f06ad944 302 nft_set_pktinfo_unspec(&pkt);
02c7b25e
PNA
303 break;
304 }
305
306 return nft_do_chain(&pkt, priv);
307}
308
309static const struct nft_chain_type nft_chain_filter_netdev = {
310 .name = "filter",
311 .type = NFT_CHAIN_T_DEFAULT,
312 .family = NFPROTO_NETDEV,
42df6e1d
LW
313 .hook_mask = (1 << NF_NETDEV_INGRESS) |
314 (1 << NF_NETDEV_EGRESS),
02c7b25e
PNA
315 .hooks = {
316 [NF_NETDEV_INGRESS] = nft_do_chain_netdev,
42df6e1d 317 [NF_NETDEV_EGRESS] = nft_do_chain_netdev,
02c7b25e
PNA
318 },
319};
320
321static void nft_netdev_event(unsigned long event, struct net_device *dev,
322 struct nft_ctx *ctx)
323{
324 struct nft_base_chain *basechain = nft_base_chain(ctx->chain);
d54725cd
PNA
325 struct nft_hook *hook, *found = NULL;
326 int n = 0;
02c7b25e 327
d54725cd
PNA
328 list_for_each_entry(hook, &basechain->hook_list, list) {
329 if (hook->ops.dev == dev)
330 found = hook;
331
332 n++;
02c7b25e 333 }
d54725cd
PNA
334 if (!found)
335 return;
336
337 if (n > 1) {
8e30abc9
PNA
338 if (!(ctx->chain->table->flags & NFT_TABLE_F_DORMANT))
339 nf_unregister_net_hook(ctx->net, &found->ops);
340
d54725cd
PNA
341 list_del_rcu(&found->list);
342 kfree_rcu(found, rcu);
343 return;
344 }
345
dc1c9fd4
FW
346 /* UNREGISTER events are also happening on netns exit.
347 *
348 * Although nf_tables core releases all tables/chains, only this event
349 * handler provides guarantee that hook->ops.dev is still accessible,
350 * so we cannot skip exiting net namespaces.
351 */
d54725cd 352 __nft_release_basechain(ctx);
02c7b25e
PNA
353}
354
355static int nf_tables_netdev_event(struct notifier_block *this,
356 unsigned long event, void *ptr)
357{
358 struct net_device *dev = netdev_notifier_info_to_dev(ptr);
01acb2e8 359 struct nft_base_chain *basechain;
0854db2a 360 struct nftables_pernet *nft_net;
02c7b25e 361 struct nft_chain *chain, *nr;
01acb2e8 362 struct nft_table *table;
02c7b25e
PNA
363 struct nft_ctx ctx = {
364 .net = dev_net(dev),
365 };
366
6e20eef4 367 if (event != NETDEV_UNREGISTER)
02c7b25e
PNA
368 return NOTIFY_DONE;
369
d59d2f82 370 nft_net = nft_pernet(ctx.net);
0854db2a
FW
371 mutex_lock(&nft_net->commit_mutex);
372 list_for_each_entry(table, &nft_net->tables, list) {
01acb2e8
PNA
373 if (table->family != NFPROTO_NETDEV &&
374 table->family != NFPROTO_INET)
02c7b25e
PNA
375 continue;
376
377 ctx.family = table->family;
378 ctx.table = table;
379 list_for_each_entry_safe(chain, nr, &table->chains, list) {
380 if (!nft_is_base_chain(chain))
381 continue;
382
01acb2e8
PNA
383 basechain = nft_base_chain(chain);
384 if (table->family == NFPROTO_INET &&
385 basechain->ops.hooknum != NF_INET_INGRESS)
386 continue;
387
02c7b25e
PNA
388 ctx.chain = chain;
389 nft_netdev_event(event, dev, &ctx);
390 }
391 }
0854db2a 392 mutex_unlock(&nft_net->commit_mutex);
02c7b25e
PNA
393
394 return NOTIFY_DONE;
395}
396
397static struct notifier_block nf_tables_netdev_notifier = {
398 .notifier_call = nf_tables_netdev_event,
399};
400
401static int nft_chain_filter_netdev_init(void)
402{
403 int err;
404
405 nft_register_chain_type(&nft_chain_filter_netdev);
406
407 err = register_netdevice_notifier(&nf_tables_netdev_notifier);
408 if (err)
409 goto err_register_netdevice_notifier;
410
411 return 0;
412
413err_register_netdevice_notifier:
414 nft_unregister_chain_type(&nft_chain_filter_netdev);
415
416 return err;
417}
418
419static void nft_chain_filter_netdev_fini(void)
420{
421 nft_unregister_chain_type(&nft_chain_filter_netdev);
422 unregister_netdevice_notifier(&nf_tables_netdev_notifier);
423}
424#else
425static inline int nft_chain_filter_netdev_init(void) { return 0; }
426static inline void nft_chain_filter_netdev_fini(void) {}
427#endif /* CONFIG_NF_TABLES_NETDEV */
428
429int __init nft_chain_filter_init(void)
430{
431 int err;
432
433 err = nft_chain_filter_netdev_init();
434 if (err < 0)
435 return err;
436
437 nft_chain_filter_ipv4_init();
438 nft_chain_filter_ipv6_init();
439 nft_chain_filter_arp_init();
440 nft_chain_filter_inet_init();
441 nft_chain_filter_bridge_init();
442
443 return 0;
444}
445
d209df3e 446void nft_chain_filter_fini(void)
02c7b25e
PNA
447{
448 nft_chain_filter_bridge_fini();
449 nft_chain_filter_inet_fini();
450 nft_chain_filter_arp_fini();
451 nft_chain_filter_ipv6_fini();
452 nft_chain_filter_ipv4_fini();
453 nft_chain_filter_netdev_fini();
454}