Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

netfilter: add inet ingress support

This patch adds the NF_INET_INGRESS pseudohook for the NFPROTO_INET
family. This is a mapping this new hook to the existing NFPROTO_NETDEV
and NF_NETDEV_INGRESS hook. The hook does not guarantee that packets are
inet only, users must filter out non-ip traffic explicitly.

This infrastructure makes it easier to support this new hook in nf_tables.

Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>

+82 -20
+1
include/uapi/linux/netfilter.h
··· 45 45 NF_INET_FORWARD, 46 46 NF_INET_LOCAL_OUT, 47 47 NF_INET_POST_ROUTING, 48 + NF_INET_INGRESS, 48 49 NF_INET_NUMHOOKS 49 50 }; 50 51
+81 -20
net/netfilter/core.c
··· 282 282 return NULL; 283 283 return net->nf.hooks_bridge + hooknum; 284 284 #endif 285 + #ifdef CONFIG_NETFILTER_INGRESS 286 + case NFPROTO_INET: 287 + if (WARN_ON_ONCE(hooknum != NF_INET_INGRESS)) 288 + return NULL; 289 + if (!dev || dev_net(dev) != net) { 290 + WARN_ON_ONCE(1); 291 + return NULL; 292 + } 293 + return &dev->nf_hooks_ingress; 294 + #endif 285 295 case NFPROTO_IPV4: 286 296 if (WARN_ON_ONCE(ARRAY_SIZE(net->nf.hooks_ipv4) <= hooknum)) 287 297 return NULL; ··· 321 311 return NULL; 322 312 } 323 313 314 + static int nf_ingress_check(struct net *net, const struct nf_hook_ops *reg, 315 + int hooknum) 316 + { 317 + #ifndef CONFIG_NETFILTER_INGRESS 318 + if (reg->hooknum == hooknum) 319 + return -EOPNOTSUPP; 320 + #endif 321 + if (reg->hooknum != hooknum || 322 + !reg->dev || dev_net(reg->dev) != net) 323 + return -EINVAL; 324 + 325 + return 0; 326 + } 327 + 324 328 static inline bool nf_ingress_hook(const struct nf_hook_ops *reg, int pf) 325 329 { 326 - return pf == NFPROTO_NETDEV && reg->hooknum == NF_NETDEV_INGRESS; 330 + if ((pf == NFPROTO_NETDEV && reg->hooknum == NF_NETDEV_INGRESS) || 331 + (pf == NFPROTO_INET && reg->hooknum == NF_INET_INGRESS)) 332 + return true; 333 + 334 + return false; 327 335 } 328 336 329 337 static void nf_static_key_inc(const struct nf_hook_ops *reg, int pf) 330 338 { 331 339 #ifdef CONFIG_JUMP_LABEL 332 - static_key_slow_inc(&nf_hooks_needed[pf][reg->hooknum]); 340 + int hooknum; 341 + 342 + if (pf == NFPROTO_INET && reg->hooknum == NF_INET_INGRESS) { 343 + pf = NFPROTO_NETDEV; 344 + hooknum = NF_NETDEV_INGRESS; 345 + } else { 346 + hooknum = reg->hooknum; 347 + } 348 + static_key_slow_inc(&nf_hooks_needed[pf][hooknum]); 333 349 #endif 334 350 } 335 351 336 352 static void nf_static_key_dec(const struct nf_hook_ops *reg, int pf) 337 353 { 338 354 #ifdef CONFIG_JUMP_LABEL 339 - static_key_slow_dec(&nf_hooks_needed[pf][reg->hooknum]); 355 + int hooknum; 356 + 357 + if (pf == NFPROTO_INET && reg->hooknum == NF_INET_INGRESS) { 358 + pf = NFPROTO_NETDEV; 359 + hooknum = NF_NETDEV_INGRESS; 360 + } else { 361 + hooknum = reg->hooknum; 362 + } 363 + static_key_slow_dec(&nf_hooks_needed[pf][hooknum]); 340 364 #endif 341 365 } 342 366 ··· 379 335 { 380 336 struct nf_hook_entries *p, *new_hooks; 381 337 struct nf_hook_entries __rcu **pp; 338 + int err; 382 339 383 - if (pf == NFPROTO_NETDEV) { 384 - #ifndef CONFIG_NETFILTER_INGRESS 385 - if (reg->hooknum == NF_NETDEV_INGRESS) 386 - return -EOPNOTSUPP; 387 - #endif 388 - if (reg->hooknum != NF_NETDEV_INGRESS || 389 - !reg->dev || dev_net(reg->dev) != net) 390 - return -EINVAL; 340 + switch (pf) { 341 + case NFPROTO_NETDEV: 342 + err = nf_ingress_check(net, reg, NF_NETDEV_INGRESS); 343 + if (err < 0) 344 + return err; 345 + break; 346 + case NFPROTO_INET: 347 + if (reg->hooknum != NF_INET_INGRESS) 348 + break; 349 + 350 + err = nf_ingress_check(net, reg, NF_INET_INGRESS); 351 + if (err < 0) 352 + return err; 353 + break; 391 354 } 392 355 393 356 pp = nf_hook_entry_head(net, pf, reg->hooknum, reg->dev); ··· 492 441 void nf_unregister_net_hook(struct net *net, const struct nf_hook_ops *reg) 493 442 { 494 443 if (reg->pf == NFPROTO_INET) { 495 - __nf_unregister_net_hook(net, NFPROTO_IPV4, reg); 496 - __nf_unregister_net_hook(net, NFPROTO_IPV6, reg); 444 + if (reg->hooknum == NF_INET_INGRESS) { 445 + __nf_unregister_net_hook(net, NFPROTO_INET, reg); 446 + } else { 447 + __nf_unregister_net_hook(net, NFPROTO_IPV4, reg); 448 + __nf_unregister_net_hook(net, NFPROTO_IPV6, reg); 449 + } 497 450 } else { 498 451 __nf_unregister_net_hook(net, reg->pf, reg); 499 452 } ··· 522 467 int err; 523 468 524 469 if (reg->pf == NFPROTO_INET) { 525 - err = __nf_register_net_hook(net, NFPROTO_IPV4, reg); 526 - if (err < 0) 527 - return err; 470 + if (reg->hooknum == NF_INET_INGRESS) { 471 + err = __nf_register_net_hook(net, NFPROTO_INET, reg); 472 + if (err < 0) 473 + return err; 474 + } else { 475 + err = __nf_register_net_hook(net, NFPROTO_IPV4, reg); 476 + if (err < 0) 477 + return err; 528 478 529 - err = __nf_register_net_hook(net, NFPROTO_IPV6, reg); 530 - if (err < 0) { 531 - __nf_unregister_net_hook(net, NFPROTO_IPV4, reg); 532 - return err; 479 + err = __nf_register_net_hook(net, NFPROTO_IPV6, reg); 480 + if (err < 0) { 481 + __nf_unregister_net_hook(net, NFPROTO_IPV4, reg); 482 + return err; 483 + } 533 484 } 534 485 } else { 535 486 err = __nf_register_net_hook(net, reg->pf, reg);