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

netfilter: bridge: register hooks only when bridge interface is added

This moves bridge hooks to a register-when-needed scheme.

We use a device notifier to register the 'call-iptables' netfilter hooks
only once a bridge gets added.

This means that if the initial namespace uses a bridge, newly created
network namespaces no longer get the PRE_ROUTING ipt_sabotage hook.

It will registered in that network namespace once a bridge is created
within that namespace.

A few modules still use global hooks:

- conntrack
- bridge PF_BRIDGE hooks
- IPVS
- CLUSTER match (deprecated)
- SYNPROXY

As long as these modules are not loaded/used, a new network namespace has
empty hook list and NF_HOOK() will boil down to single list_empty test even
if initial namespace does stateless packet filtering.

Signed-off-by: Florian Westphal <fw@strlen.de>
Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>

authored by

Florian Westphal and committed by
Pablo Neira Ayuso
5f6c253e b9e69e12

+65 -3
+65 -3
net/bridge/br_netfilter_hooks.c
··· 37 37 #include <net/addrconf.h> 38 38 #include <net/route.h> 39 39 #include <net/netfilter/br_netfilter.h> 40 + #include <net/netns/generic.h> 40 41 41 42 #include <asm/uaccess.h> 42 43 #include "br_private.h" 43 44 #ifdef CONFIG_SYSCTL 44 45 #include <linux/sysctl.h> 45 46 #endif 47 + 48 + static int brnf_net_id __read_mostly; 49 + 50 + struct brnf_net { 51 + bool enabled; 52 + }; 46 53 47 54 #ifdef CONFIG_SYSCTL 48 55 static struct ctl_table_header *brnf_sysctl_header; ··· 945 938 }, 946 939 }; 947 940 941 + static int brnf_device_event(struct notifier_block *unused, unsigned long event, 942 + void *ptr) 943 + { 944 + struct net_device *dev = netdev_notifier_info_to_dev(ptr); 945 + struct brnf_net *brnet; 946 + struct net *net; 947 + int ret; 948 + 949 + if (event != NETDEV_REGISTER || !(dev->priv_flags & IFF_EBRIDGE)) 950 + return NOTIFY_DONE; 951 + 952 + ASSERT_RTNL(); 953 + 954 + net = dev_net(dev); 955 + brnet = net_generic(net, brnf_net_id); 956 + if (brnet->enabled) 957 + return NOTIFY_OK; 958 + 959 + ret = nf_register_net_hooks(net, br_nf_ops, ARRAY_SIZE(br_nf_ops)); 960 + if (ret) 961 + return NOTIFY_BAD; 962 + 963 + brnet->enabled = true; 964 + return NOTIFY_OK; 965 + } 966 + 967 + static void __net_exit brnf_exit_net(struct net *net) 968 + { 969 + struct brnf_net *brnet = net_generic(net, brnf_net_id); 970 + 971 + if (!brnet->enabled) 972 + return; 973 + 974 + nf_unregister_net_hooks(net, br_nf_ops, ARRAY_SIZE(br_nf_ops)); 975 + brnet->enabled = false; 976 + } 977 + 978 + static struct pernet_operations brnf_net_ops __read_mostly = { 979 + .exit = brnf_exit_net, 980 + .id = &brnf_net_id, 981 + .size = sizeof(struct brnf_net), 982 + }; 983 + 984 + static struct notifier_block brnf_notifier __read_mostly = { 985 + .notifier_call = brnf_device_event, 986 + }; 987 + 948 988 #ifdef CONFIG_SYSCTL 949 989 static 950 990 int brnf_sysctl_call_tables(struct ctl_table *ctl, int write, ··· 1057 1003 { 1058 1004 int ret; 1059 1005 1060 - ret = nf_register_hooks(br_nf_ops, ARRAY_SIZE(br_nf_ops)); 1006 + ret = register_pernet_subsys(&brnf_net_ops); 1061 1007 if (ret < 0) 1062 1008 return ret; 1009 + 1010 + ret = register_netdevice_notifier(&brnf_notifier); 1011 + if (ret < 0) { 1012 + unregister_pernet_subsys(&brnf_net_ops); 1013 + return ret; 1014 + } 1063 1015 1064 1016 #ifdef CONFIG_SYSCTL 1065 1017 brnf_sysctl_header = register_net_sysctl(&init_net, "net/bridge", brnf_table); 1066 1018 if (brnf_sysctl_header == NULL) { 1067 1019 printk(KERN_WARNING 1068 1020 "br_netfilter: can't register to sysctl.\n"); 1069 - nf_unregister_hooks(br_nf_ops, ARRAY_SIZE(br_nf_ops)); 1021 + unregister_netdevice_notifier(&brnf_notifier); 1022 + unregister_pernet_subsys(&brnf_net_ops); 1070 1023 return -ENOMEM; 1071 1024 } 1072 1025 #endif ··· 1085 1024 static void __exit br_netfilter_fini(void) 1086 1025 { 1087 1026 RCU_INIT_POINTER(nf_br_ops, NULL); 1088 - nf_unregister_hooks(br_nf_ops, ARRAY_SIZE(br_nf_ops)); 1027 + unregister_netdevice_notifier(&brnf_notifier); 1028 + unregister_pernet_subsys(&brnf_net_ops); 1089 1029 #ifdef CONFIG_SYSCTL 1090 1030 unregister_net_sysctl_table(brnf_sysctl_header); 1091 1031 #endif