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
Linux® is a registered trademark of Linus Torvalds in the United States and other countries.
TOMOYO® is a registered trademark of NTT DATA CORPORATION.