FILE
nft_chain_filter.c
File preview with syntax highlighting.
linux-6.19.14/net/netfilter/nft_chain_filter.c
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); 21 nft_set_pktinfo_ipv4(&pkt); 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); 65 nft_set_pktinfo_unspec(&pkt); 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); 105 nft_set_pktinfo_ipv6(&pkt); 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: 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 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 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 195static 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 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 229#if IS_ENABLED(CONFIG_NF_TABLES_BRIDGE) 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): 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 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): 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 309static 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 321static int nft_netdev_event(unsigned long event, struct net_device *dev, 322 struct nft_base_chain *basechain, bool changename) 323{ 324 struct nft_table *table = basechain->chain.table; 325 struct nf_hook_ops *ops; 326 struct nft_hook *hook; 327 bool match; 328 329 list_for_each_entry(hook, &basechain->hook_list, list) { 330 ops = nft_hook_find_ops(hook, dev); 331 match = !strncmp(hook->ifname, dev->name, hook->ifnamelen); 332 333 switch (event) { 334 case NETDEV_UNREGISTER: 335 /* NOP if not found or new name still matching */ 336 if (!ops || (changename && match)) 337 continue; 338 339 if (!(table->flags & NFT_TABLE_F_DORMANT)) 340 nf_unregister_net_hook(dev_net(dev), ops); 341 342 list_del_rcu(&ops->list); 343 kfree_rcu(ops, rcu); 344 break; 345 case NETDEV_REGISTER: 346 /* NOP if not matching or already registered */ 347 if (!match || ops) 348 continue; 349 350 ops = kmemdup(&basechain->ops, 351 sizeof(struct nf_hook_ops), 352 GFP_KERNEL_ACCOUNT); 353 if (!ops) 354 return 1; 355 356 ops->dev = dev; 357 358 if (!(table->flags & NFT_TABLE_F_DORMANT) && 359 nf_register_net_hook(dev_net(dev), ops)) { 360 kfree(ops); 361 return 1; 362 } 363 list_add_tail_rcu(&ops->list, &hook->ops_list); 364 break; 365 } 366 break; 367 } 368 return 0; 369} 370 371static int __nf_tables_netdev_event(unsigned long event, 372 struct net_device *dev, 373 bool changename) 374{ 375 struct nft_base_chain *basechain; 376 struct nftables_pernet *nft_net; 377 struct nft_chain *chain; 378 struct nft_table *table; 379 380 nft_net = nft_pernet(dev_net(dev)); 381 list_for_each_entry(table, &nft_net->tables, list) { 382 if (table->family != NFPROTO_NETDEV && 383 table->family != NFPROTO_INET) 384 continue; 385 386 list_for_each_entry(chain, &table->chains, list) { 387 if (!nft_is_base_chain(chain)) 388 continue; 389 390 basechain = nft_base_chain(chain); 391 if (table->family == NFPROTO_INET && 392 basechain->ops.hooknum != NF_INET_INGRESS) 393 continue; 394 395 if (nft_netdev_event(event, dev, basechain, changename)) 396 return 1; 397 } 398 } 399 return 0; 400} 401 402static int nf_tables_netdev_event(struct notifier_block *this, 403 unsigned long event, void *ptr) 404{ 405 struct net_device *dev = netdev_notifier_info_to_dev(ptr); 406 struct nftables_pernet *nft_net; 407 int ret = NOTIFY_DONE; 408 409 if (event != NETDEV_REGISTER && 410 event != NETDEV_UNREGISTER && 411 event != NETDEV_CHANGENAME) 412 return NOTIFY_DONE; 413 414 nft_net = nft_pernet(dev_net(dev)); 415 mutex_lock(&nft_net->commit_mutex); 416 417 if (event == NETDEV_CHANGENAME) { 418 if (__nf_tables_netdev_event(NETDEV_REGISTER, dev, true)) { 419 ret = NOTIFY_BAD; 420 goto out_unlock; 421 } 422 __nf_tables_netdev_event(NETDEV_UNREGISTER, dev, true); 423 } else if (__nf_tables_netdev_event(event, dev, false)) { 424 ret = NOTIFY_BAD; 425 } 426out_unlock: 427 mutex_unlock(&nft_net->commit_mutex); 428 return ret; 429} 430 431static struct notifier_block nf_tables_netdev_notifier = { 432 .notifier_call = nf_tables_netdev_event, 433}; 434 435static int nft_chain_filter_netdev_init(void) 436{ 437 int err; 438 439 nft_register_chain_type(&nft_chain_filter_netdev); 440 441 err = register_netdevice_notifier(&nf_tables_netdev_notifier); 442 if (err) 443 goto err_register_netdevice_notifier; 444 445 return 0; 446 447err_register_netdevice_notifier: 448 nft_unregister_chain_type(&nft_chain_filter_netdev); 449 450 return err; 451} 452 453static void nft_chain_filter_netdev_fini(void) 454{ 455 nft_unregister_chain_type(&nft_chain_filter_netdev); 456 unregister_netdevice_notifier(&nf_tables_netdev_notifier); 457} 458#else 459static inline int nft_chain_filter_netdev_init(void) { return 0; } 460static inline void nft_chain_filter_netdev_fini(void) {} 461#endif /* CONFIG_NF_TABLES_NETDEV */ 462 463int __init nft_chain_filter_init(void) 464{ 465 int err; 466 467 err = nft_chain_filter_netdev_init(); 468 if (err < 0) 469 return err; 470 471 nft_chain_filter_ipv4_init(); 472 nft_chain_filter_ipv6_init(); 473 nft_chain_filter_arp_init(); 474 nft_chain_filter_inet_init(); 475 nft_chain_filter_bridge_init(); 476 477 return 0; 478} 479 480void nft_chain_filter_fini(void) 481{ 482 nft_chain_filter_bridge_fini(); 483 nft_chain_filter_inet_fini(); 484 nft_chain_filter_arp_fini(); 485 nft_chain_filter_ipv6_fini(); 486 nft_chain_filter_ipv4_fini(); 487 nft_chain_filter_netdev_fini(); 488}
~/
Read-only file preview.