nft_chain_filter.c (10843B)
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 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 if (event != NETDEV_UNREGISTER) 329 return; 330 331 list_for_each_entry(hook, &basechain->hook_list, list) { 332 if (hook->ops.dev == dev) 333 found = hook; 334 335 n++; 336 } 337 if (!found) 338 return; 339 340 if (n > 1) { 341 nf_unregister_net_hook(ctx->net, &found->ops); 342 list_del_rcu(&found->list); 343 kfree_rcu(found, rcu); 344 return; 345 } 346 347 __nft_release_basechain(ctx); 348} 349 350static int nf_tables_netdev_event(struct notifier_block *this, 351 unsigned long event, void *ptr) 352{ 353 struct net_device *dev = netdev_notifier_info_to_dev(ptr); 354 struct nftables_pernet *nft_net; 355 struct nft_table *table; 356 struct nft_chain *chain, *nr; 357 struct nft_ctx ctx = { 358 .net = dev_net(dev), 359 }; 360 361 if (event != NETDEV_UNREGISTER && 362 event != NETDEV_CHANGENAME) 363 return NOTIFY_DONE; 364 365 if (!check_net(ctx.net)) 366 return NOTIFY_DONE; 367 368 nft_net = nft_pernet(ctx.net); 369 mutex_lock(&nft_net->commit_mutex); 370 list_for_each_entry(table, &nft_net->tables, list) { 371 if (table->family != NFPROTO_NETDEV) 372 continue; 373 374 ctx.family = table->family; 375 ctx.table = table; 376 list_for_each_entry_safe(chain, nr, &table->chains, list) { 377 if (!nft_is_base_chain(chain)) 378 continue; 379 380 ctx.chain = chain; 381 nft_netdev_event(event, dev, &ctx); 382 } 383 } 384 mutex_unlock(&nft_net->commit_mutex); 385 386 return NOTIFY_DONE; 387} 388 389static struct notifier_block nf_tables_netdev_notifier = { 390 .notifier_call = nf_tables_netdev_event, 391}; 392 393static int nft_chain_filter_netdev_init(void) 394{ 395 int err; 396 397 nft_register_chain_type(&nft_chain_filter_netdev); 398 399 err = register_netdevice_notifier(&nf_tables_netdev_notifier); 400 if (err) 401 goto err_register_netdevice_notifier; 402 403 return 0; 404 405err_register_netdevice_notifier: 406 nft_unregister_chain_type(&nft_chain_filter_netdev); 407 408 return err; 409} 410 411static void nft_chain_filter_netdev_fini(void) 412{ 413 nft_unregister_chain_type(&nft_chain_filter_netdev); 414 unregister_netdevice_notifier(&nf_tables_netdev_notifier); 415} 416#else 417static inline int nft_chain_filter_netdev_init(void) { return 0; } 418static inline void nft_chain_filter_netdev_fini(void) {} 419#endif /* CONFIG_NF_TABLES_NETDEV */ 420 421int __init nft_chain_filter_init(void) 422{ 423 int err; 424 425 err = nft_chain_filter_netdev_init(); 426 if (err < 0) 427 return err; 428 429 nft_chain_filter_ipv4_init(); 430 nft_chain_filter_ipv6_init(); 431 nft_chain_filter_arp_init(); 432 nft_chain_filter_inet_init(); 433 nft_chain_filter_bridge_init(); 434 435 return 0; 436} 437 438void nft_chain_filter_fini(void) 439{ 440 nft_chain_filter_bridge_fini(); 441 nft_chain_filter_inet_fini(); 442 nft_chain_filter_arp_fini(); 443 nft_chain_filter_ipv6_fini(); 444 nft_chain_filter_ipv4_fini(); 445 nft_chain_filter_netdev_fini(); 446}