~ [ source navigation ] ~ [ diff markup ] ~ [ identifier search ] ~

TOMOYO Linux Cross Reference
Linux/net/netfilter/nft_chain_filter.c

Version: ~ [ linux-6.11.5 ] ~ [ linux-6.10.14 ] ~ [ linux-6.9.12 ] ~ [ linux-6.8.12 ] ~ [ linux-6.7.12 ] ~ [ linux-6.6.58 ] ~ [ linux-6.5.13 ] ~ [ linux-6.4.16 ] ~ [ linux-6.3.13 ] ~ [ linux-6.2.16 ] ~ [ linux-6.1.114 ] ~ [ linux-6.0.19 ] ~ [ linux-5.19.17 ] ~ [ linux-5.18.19 ] ~ [ linux-5.17.15 ] ~ [ linux-5.16.20 ] ~ [ linux-5.15.169 ] ~ [ linux-5.14.21 ] ~ [ linux-5.13.19 ] ~ [ linux-5.12.19 ] ~ [ linux-5.11.22 ] ~ [ linux-5.10.228 ] ~ [ linux-5.9.16 ] ~ [ linux-5.8.18 ] ~ [ linux-5.7.19 ] ~ [ linux-5.6.19 ] ~ [ linux-5.5.19 ] ~ [ linux-5.4.284 ] ~ [ linux-5.3.18 ] ~ [ linux-5.2.21 ] ~ [ linux-5.1.21 ] ~ [ linux-5.0.21 ] ~ [ linux-4.20.17 ] ~ [ linux-4.19.322 ] ~ [ linux-4.18.20 ] ~ [ linux-4.17.19 ] ~ [ linux-4.16.18 ] ~ [ linux-4.15.18 ] ~ [ linux-4.14.336 ] ~ [ linux-4.13.16 ] ~ [ linux-4.12.14 ] ~ [ linux-4.11.12 ] ~ [ linux-4.10.17 ] ~ [ linux-4.9.337 ] ~ [ linux-4.4.302 ] ~ [ linux-3.10.108 ] ~ [ linux-2.6.32.71 ] ~ [ linux-2.6.0 ] ~ [ linux-2.4.37.11 ] ~ [ unix-v6-master ] ~ [ ccs-tools-1.8.9 ] ~ [ policy-sample ] ~
Architecture: ~ [ i386 ] ~ [ alpha ] ~ [ m68k ] ~ [ mips ] ~ [ ppc ] ~ [ sparc ] ~ [ sparc64 ] ~

  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
 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);
 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 
 44 static void nft_chain_filter_ipv4_init(void)
 45 {
 46         nft_register_chain_type(&nft_chain_filter_ipv4);
 47 }
 48 static void nft_chain_filter_ipv4_fini(void)
 49 {
 50         nft_unregister_chain_type(&nft_chain_filter_ipv4);
 51 }
 52 
 53 #else
 54 static inline void nft_chain_filter_ipv4_init(void) {}
 55 static inline void nft_chain_filter_ipv4_fini(void) {}
 56 #endif /* CONFIG_NF_TABLES_IPV4 */
 57 
 58 #ifdef CONFIG_NF_TABLES_ARP
 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);
 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 
 83 static void nft_chain_filter_arp_init(void)
 84 {
 85         nft_register_chain_type(&nft_chain_filter_arp);
 86 }
 87 
 88 static void nft_chain_filter_arp_fini(void)
 89 {
 90         nft_unregister_chain_type(&nft_chain_filter_arp);
 91 }
 92 #else
 93 static inline void nft_chain_filter_arp_init(void) {}
 94 static inline void nft_chain_filter_arp_fini(void) {}
 95 #endif /* CONFIG_NF_TABLES_ARP */
 96 
 97 #ifdef CONFIG_NF_TABLES_IPV6
 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);
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 
128 static void nft_chain_filter_ipv6_init(void)
129 {
130         nft_register_chain_type(&nft_chain_filter_ipv6);
131 }
132 
133 static void nft_chain_filter_ipv6_fini(void)
134 {
135         nft_unregister_chain_type(&nft_chain_filter_ipv6);
136 }
137 #else
138 static inline void nft_chain_filter_ipv6_init(void) {}
139 static inline void nft_chain_filter_ipv6_fini(void) {}
140 #endif /* CONFIG_NF_TABLES_IPV6 */
141 
142 #ifdef CONFIG_NF_TABLES_INET
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);
153                 break;
154         case NFPROTO_IPV6:
155                 nft_set_pktinfo_ipv6(&pkt);
156                 break;
157         default:
158                 break;
159         }
160 
161         return nft_do_chain(&pkt, priv);
162 }
163 
164 static 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 
177                 if (nft_set_pktinfo_ipv4_ingress(&pkt) < 0)
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 
185                 if (nft_set_pktinfo_ipv6_ingress(&pkt) < 0)
186                         return NF_DROP;
187                 break;
188         default:
189                 return NF_ACCEPT;
190         }
191 
192         return nft_do_chain(&pkt, priv);
193 }
194 
195 static const struct nft_chain_type nft_chain_filter_inet = {
196         .name           = "filter",
197         .type           = NFT_CHAIN_T_DEFAULT,
198         .family         = NFPROTO_INET,
199         .hook_mask      = (1 << NF_INET_INGRESS) |
200                           (1 << NF_INET_LOCAL_IN) |
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          = {
206                 [NF_INET_INGRESS]       = nft_do_chain_inet_ingress,
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 
215 static void nft_chain_filter_inet_init(void)
216 {
217         nft_register_chain_type(&nft_chain_filter_inet);
218 }
219 
220 static void nft_chain_filter_inet_fini(void)
221 {
222         nft_unregister_chain_type(&nft_chain_filter_inet);
223 }
224 #else
225 static inline void nft_chain_filter_inet_init(void) {}
226 static inline void nft_chain_filter_inet_fini(void) {}
227 #endif /* CONFIG_NF_TABLES_IPV6 */
228 
229 #if IS_ENABLED(CONFIG_NF_TABLES_BRIDGE)
230 static unsigned int
231 nft_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):
241                 nft_set_pktinfo_ipv4_validate(&pkt);
242                 break;
243         case htons(ETH_P_IPV6):
244                 nft_set_pktinfo_ipv6_validate(&pkt);
245                 break;
246         default:
247                 nft_set_pktinfo_unspec(&pkt);
248                 break;
249         }
250 
251         return nft_do_chain(&pkt, priv);
252 }
253 
254 static 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 
272 static void nft_chain_filter_bridge_init(void)
273 {
274         nft_register_chain_type(&nft_chain_filter_bridge);
275 }
276 
277 static void nft_chain_filter_bridge_fini(void)
278 {
279         nft_unregister_chain_type(&nft_chain_filter_bridge);
280 }
281 #else
282 static inline void nft_chain_filter_bridge_init(void) {}
283 static inline void nft_chain_filter_bridge_fini(void) {}
284 #endif /* CONFIG_NF_TABLES_BRIDGE */
285 
286 #ifdef CONFIG_NF_TABLES_NETDEV
287 static 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):
296                 nft_set_pktinfo_ipv4_validate(&pkt);
297                 break;
298         case htons(ETH_P_IPV6):
299                 nft_set_pktinfo_ipv6_validate(&pkt);
300                 break;
301         default:
302                 nft_set_pktinfo_unspec(&pkt);
303                 break;
304         }
305 
306         return nft_do_chain(&pkt, priv);
307 }
308 
309 static const struct nft_chain_type nft_chain_filter_netdev = {
310         .name           = "filter",
311         .type           = NFT_CHAIN_T_DEFAULT,
312         .family         = NFPROTO_NETDEV,
313         .hook_mask      = (1 << NF_NETDEV_INGRESS) |
314                           (1 << NF_NETDEV_EGRESS),
315         .hooks          = {
316                 [NF_NETDEV_INGRESS]     = nft_do_chain_netdev,
317                 [NF_NETDEV_EGRESS]      = nft_do_chain_netdev,
318         },
319 };
320 
321 static 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);
325         struct nft_hook *hook, *found = NULL;
326         int n = 0;
327 
328         list_for_each_entry(hook, &basechain->hook_list, list) {
329                 if (hook->ops.dev == dev)
330                         found = hook;
331 
332                 n++;
333         }
334         if (!found)
335                 return;
336 
337         if (n > 1) {
338                 if (!(ctx->chain->table->flags & NFT_TABLE_F_DORMANT))
339                         nf_unregister_net_hook(ctx->net, &found->ops);
340 
341                 list_del_rcu(&found->list);
342                 kfree_rcu(found, rcu);
343                 return;
344         }
345 
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          */
352         __nft_release_basechain(ctx);
353 }
354 
355 static 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);
359         struct nft_base_chain *basechain;
360         struct nftables_pernet *nft_net;
361         struct nft_chain *chain, *nr;
362         struct nft_table *table;
363         struct nft_ctx ctx = {
364                 .net    = dev_net(dev),
365         };
366 
367         if (event != NETDEV_UNREGISTER)
368                 return NOTIFY_DONE;
369 
370         nft_net = nft_pernet(ctx.net);
371         mutex_lock(&nft_net->commit_mutex);
372         list_for_each_entry(table, &nft_net->tables, list) {
373                 if (table->family != NFPROTO_NETDEV &&
374                     table->family != NFPROTO_INET)
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 
383                         basechain = nft_base_chain(chain);
384                         if (table->family == NFPROTO_INET &&
385                             basechain->ops.hooknum != NF_INET_INGRESS)
386                                 continue;
387 
388                         ctx.chain = chain;
389                         nft_netdev_event(event, dev, &ctx);
390                 }
391         }
392         mutex_unlock(&nft_net->commit_mutex);
393 
394         return NOTIFY_DONE;
395 }
396 
397 static struct notifier_block nf_tables_netdev_notifier = {
398         .notifier_call  = nf_tables_netdev_event,
399 };
400 
401 static 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 
413 err_register_netdevice_notifier:
414         nft_unregister_chain_type(&nft_chain_filter_netdev);
415 
416         return err;
417 }
418 
419 static 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
425 static inline int nft_chain_filter_netdev_init(void) { return 0; }
426 static inline void nft_chain_filter_netdev_fini(void) {}
427 #endif /* CONFIG_NF_TABLES_NETDEV */
428 
429 int __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 
446 void nft_chain_filter_fini(void)
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 }
455 

~ [ source navigation ] ~ [ diff markup ] ~ [ identifier search ] ~

kernel.org | git.kernel.org | LWN.net | Project Home | SVN repository | Mail admin

Linux® is a registered trademark of Linus Torvalds in the United States and other countries.
TOMOYO® is a registered trademark of NTT DATA CORPORATION.

sflogo.php