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

netfilter: bridge: namespace bridge netfilter sysctls

Currently, the /proc/sys/net/bridge folder is only created in the initial
network namespace. This patch ensures that the /proc/sys/net/bridge folder
is available in each network namespace if the module is loaded and
disappears from all network namespaces when the module is unloaded.

In doing so the patch makes the sysctls:

bridge-nf-call-arptables
bridge-nf-call-ip6tables
bridge-nf-call-iptables
bridge-nf-filter-pppoe-tagged
bridge-nf-filter-vlan-tagged
bridge-nf-pass-vlan-input-dev

apply per network namespace. This unblocks some use-cases where users would
like to e.g. not do bridge filtering for bridges in a specific network
namespace while doing so for bridges located in another network namespace.

The netfilter rules are afaict already per network namespace so it should
be safe for users to specify whether bridge devices inside a network
namespace are supposed to go through iptables et al. or not. Also, this can
already be done per-bridge by setting an option for each individual bridge
via Netlink. It should also be possible to do this for all bridges in a
network namespace via sysctls.

Cc: Tyler Hicks <tyhicks@canonical.com>
Signed-off-by: Christian Brauner <christian.brauner@ubuntu.com>
Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>

authored by

Christian Brauner and committed by
Pablo Neira Ayuso
22567590 ff6d090d

+72 -45
+72 -45
net/bridge/br_netfilter_hooks.c
··· 49 49 50 50 static unsigned int brnf_net_id __read_mostly; 51 51 52 - #ifdef CONFIG_SYSCTL 53 - static struct ctl_table_header *brnf_sysctl_header; 54 - #endif 55 - 56 52 struct brnf_net { 57 53 bool enabled; 54 + 55 + #ifdef CONFIG_SYSCTL 56 + struct ctl_table_header *ctl_hdr; 57 + #endif 58 58 59 59 /* default value is 1 */ 60 60 int call_iptables; ··· 980 980 return NOTIFY_OK; 981 981 } 982 982 983 - static void __net_exit brnf_exit_net(struct net *net) 984 - { 985 - struct brnf_net *brnet = net_generic(net, brnf_net_id); 986 - 987 - if (!brnet->enabled) 988 - return; 989 - 990 - nf_unregister_net_hooks(net, br_nf_ops, ARRAY_SIZE(br_nf_ops)); 991 - brnet->enabled = false; 992 - } 993 - 994 - static struct pernet_operations brnf_net_ops __read_mostly = { 995 - .exit = brnf_exit_net, 996 - .id = &brnf_net_id, 997 - .size = sizeof(struct brnf_net), 998 - }; 999 - 1000 983 static struct notifier_block brnf_notifier __read_mostly = { 1001 984 .notifier_call = brnf_device_event, 1002 985 }; ··· 1085 1102 brnf->pass_vlan_indev = 0; 1086 1103 } 1087 1104 1105 + static int br_netfilter_sysctl_init_net(struct net *net) 1106 + { 1107 + struct ctl_table *table = brnf_table; 1108 + struct brnf_net *brnet; 1109 + 1110 + if (!net_eq(net, &init_net)) { 1111 + table = kmemdup(table, sizeof(brnf_table), GFP_KERNEL); 1112 + if (!table) 1113 + return -ENOMEM; 1114 + } 1115 + 1116 + brnet = net_generic(net, brnf_net_id); 1117 + table[0].data = &brnet->call_arptables; 1118 + table[1].data = &brnet->call_iptables; 1119 + table[2].data = &brnet->call_ip6tables; 1120 + table[3].data = &brnet->filter_vlan_tagged; 1121 + table[4].data = &brnet->filter_pppoe_tagged; 1122 + table[5].data = &brnet->pass_vlan_indev; 1123 + 1124 + br_netfilter_sysctl_default(brnet); 1125 + 1126 + brnet->ctl_hdr = register_net_sysctl(net, "net/bridge", table); 1127 + if (!brnet->ctl_hdr) { 1128 + if (!net_eq(net, &init_net)) 1129 + kfree(table); 1130 + 1131 + return -ENOMEM; 1132 + } 1133 + 1134 + return 0; 1135 + } 1136 + 1137 + static void br_netfilter_sysctl_exit_net(struct net *net, 1138 + struct brnf_net *brnet) 1139 + { 1140 + unregister_net_sysctl_table(brnet->ctl_hdr); 1141 + if (!net_eq(net, &init_net)) 1142 + kfree(brnet->ctl_hdr->ctl_table_arg); 1143 + } 1144 + 1145 + static int __net_init brnf_init_net(struct net *net) 1146 + { 1147 + return br_netfilter_sysctl_init_net(net); 1148 + } 1088 1149 #endif 1150 + 1151 + static void __net_exit brnf_exit_net(struct net *net) 1152 + { 1153 + struct brnf_net *brnet; 1154 + 1155 + brnet = net_generic(net, brnf_net_id); 1156 + if (brnet->enabled) { 1157 + nf_unregister_net_hooks(net, br_nf_ops, ARRAY_SIZE(br_nf_ops)); 1158 + brnet->enabled = false; 1159 + } 1160 + 1161 + #ifdef CONFIG_SYSCTL 1162 + br_netfilter_sysctl_exit_net(net, brnet); 1163 + #endif 1164 + } 1165 + 1166 + static struct pernet_operations brnf_net_ops __read_mostly = { 1167 + #ifdef CONFIG_SYSCTL 1168 + .init = brnf_init_net, 1169 + #endif 1170 + .exit = brnf_exit_net, 1171 + .id = &brnf_net_id, 1172 + .size = sizeof(struct brnf_net), 1173 + }; 1089 1174 1090 1175 static int __init br_netfilter_init(void) 1091 1176 { 1092 1177 int ret; 1093 - struct brnf_net *brnet; 1094 1178 1095 1179 ret = register_pernet_subsys(&brnf_net_ops); 1096 1180 if (ret < 0) ··· 1169 1119 return ret; 1170 1120 } 1171 1121 1172 - #ifdef CONFIG_SYSCTL 1173 - brnet = net_generic(&init_net, brnf_net_id); 1174 - brnf_table[0].data = &brnet->call_arptables; 1175 - brnf_table[1].data = &brnet->call_iptables; 1176 - brnf_table[2].data = &brnet->call_ip6tables; 1177 - brnf_table[3].data = &brnet->filter_vlan_tagged; 1178 - brnf_table[4].data = &brnet->filter_pppoe_tagged; 1179 - brnf_table[5].data = &brnet->pass_vlan_indev; 1180 - 1181 - br_netfilter_sysctl_default(brnet); 1182 - 1183 - brnf_sysctl_header = register_net_sysctl(&init_net, "net/bridge", brnf_table); 1184 - if (brnf_sysctl_header == NULL) { 1185 - printk(KERN_WARNING 1186 - "br_netfilter: can't register to sysctl.\n"); 1187 - unregister_netdevice_notifier(&brnf_notifier); 1188 - unregister_pernet_subsys(&brnf_net_ops); 1189 - return -ENOMEM; 1190 - } 1191 - #endif 1192 1122 RCU_INIT_POINTER(nf_br_ops, &br_ops); 1193 1123 printk(KERN_NOTICE "Bridge firewalling registered\n"); 1194 1124 return 0; ··· 1179 1149 RCU_INIT_POINTER(nf_br_ops, NULL); 1180 1150 unregister_netdevice_notifier(&brnf_notifier); 1181 1151 unregister_pernet_subsys(&brnf_net_ops); 1182 - #ifdef CONFIG_SYSCTL 1183 - unregister_net_sysctl_table(brnf_sysctl_header); 1184 - #endif 1185 1152 } 1186 1153 1187 1154 module_init(br_netfilter_init);