diff --git a/include/linux/netfilter.h b/include/linux/netfilter.h index 445494c502ba..49dc95d21f01 100644 --- a/include/linux/netfilter.h +++ b/include/linux/netfilter.h @@ -243,7 +243,7 @@ static inline int nf_hook(u_int8_t pf, unsigned int hook, struct net *net, break; case NFPROTO_BRIDGE: #ifdef CONFIG_NETFILTER_FAMILY_BRIDGE - hook_head = rcu_dereference(net->nf.hooks_bridge[hook]); + hook_head = rcu_dereference(get_nf_hooks_bridge(net)[hook]); #endif break; default: diff --git a/include/net/net_namespace.h b/include/net/net_namespace.h index 8c3587d5c308..6641c4543d18 100644 --- a/include/net/net_namespace.h +++ b/include/net/net_namespace.h @@ -188,6 +188,36 @@ struct net { #endif } __randomize_layout; +/* + * To work around a KMI issue, hooks_bridge[] could not be + * added to struct netns_nf. Since the only use of netns_nf + * is embedded in struct net, struct ext_net is added to + * contain struct net plus the new field. Users of the new + * field must use get_nf_hooks_bridge() to access the field. + */ +struct ext_net { + struct net net; +#ifdef CONFIG_NETFILTER_FAMILY_BRIDGE + struct nf_hook_entries __rcu *hooks_bridge[NF_INET_NUMHOOKS]; +#endif + ANDROID_VENDOR_DATA(1); +}; + +#ifdef CONFIG_NETFILTER_FAMILY_BRIDGE +extern struct net init_net; +extern struct nf_hook_entries **init_nf_hooks_bridgep; + +static inline struct nf_hook_entries __rcu **get_nf_hooks_bridge(const struct net *net) +{ + struct ext_net *ext_net; + + if (net == &init_net) + return init_nf_hooks_bridgep; + ext_net = container_of(net, struct ext_net, net); + return ext_net->hooks_bridge; +} +#endif + #include /* Init's network namespace */ diff --git a/include/net/netns/netfilter.h b/include/net/netns/netfilter.h index 3b7eb0cb1201..56c72117b5b3 100644 --- a/include/net/netns/netfilter.h +++ b/include/net/netns/netfilter.h @@ -22,9 +22,6 @@ struct netns_nf { #ifdef CONFIG_NETFILTER_FAMILY_ARP struct nf_hook_entries __rcu *hooks_arp[NF_ARP_NUMHOOKS]; #endif -#ifdef CONFIG_NETFILTER_FAMILY_BRIDGE - struct nf_hook_entries __rcu *hooks_bridge[NF_INET_NUMHOOKS]; -#endif #if IS_ENABLED(CONFIG_NF_DEFRAG_IPV4) unsigned int defrag_ipv4_users; #endif diff --git a/init/Kconfig.gki b/init/Kconfig.gki index 081b1cdc9c7e..1a17a3d6e27b 100644 --- a/init/Kconfig.gki +++ b/init/Kconfig.gki @@ -202,6 +202,7 @@ config GKI_HIDDEN_NET_CONFIGS select PAGE_POOL select NET_PTP_CLASSIFY select NET_DEVLINK + select NETFILTER_FAMILY_BRIDGE help Dummy config option used to enable the networking hidden config, required by various SoC platforms. diff --git a/net/bridge/br_input.c b/net/bridge/br_input.c index 6bb272894c96..0da15e1f3f72 100644 --- a/net/bridge/br_input.c +++ b/net/bridge/br_input.c @@ -243,7 +243,7 @@ static int nf_hook_bridge_pre(struct sk_buff *skb, struct sk_buff **pskb) goto frame_finish; #endif - e = rcu_dereference(net->nf.hooks_bridge[NF_BR_PRE_ROUTING]); + e = rcu_dereference(get_nf_hooks_bridge(net)[NF_BR_PRE_ROUTING]); if (!e) goto frame_finish; diff --git a/net/bridge/br_netfilter_hooks.c b/net/bridge/br_netfilter_hooks.c index 01d690d9fe5f..c7f0aedf2244 100644 --- a/net/bridge/br_netfilter_hooks.c +++ b/net/bridge/br_netfilter_hooks.c @@ -1016,7 +1016,7 @@ int br_nf_hook_thresh(unsigned int hook, struct net *net, unsigned int i; int ret; - e = rcu_dereference(net->nf.hooks_bridge[hook]); + e = rcu_dereference(get_nf_hooks_bridge(net)[hook]); if (!e) return okfn(net, sk, skb); diff --git a/net/core/net_namespace.c b/net/core/net_namespace.c index 4c1707d0eb9b..1d0110152862 100644 --- a/net/core/net_namespace.c +++ b/net/core/net_namespace.c @@ -1093,9 +1093,13 @@ void __init net_ns_init(void) struct net_generic *ng; #ifdef CONFIG_NET_NS - net_cachep = kmem_cache_create("net_namespace", sizeof(struct net), - SMP_CACHE_BYTES, - SLAB_PANIC|SLAB_ACCOUNT, NULL); + /* Allocate size for struct ext_net instead of struct net + * to fix a KMI issue when CONFIG_NETFILTER_FAMILY_BRIDGE + * is enabled + */ + net_cachep = kmem_cache_create("net_namespace", sizeof(struct ext_net), + SMP_CACHE_BYTES, + SLAB_PANIC | SLAB_ACCOUNT, NULL); /* Create workqueue for cleanup */ netns_wq = create_singlethread_workqueue("netns"); diff --git a/net/netfilter/core.c b/net/netfilter/core.c index 55a7f72d547c..6c7a44f84b93 100644 --- a/net/netfilter/core.c +++ b/net/netfilter/core.c @@ -39,6 +39,12 @@ struct static_key nf_hooks_needed[NFPROTO_NUMPROTO][NF_MAX_HOOKS]; EXPORT_SYMBOL(nf_hooks_needed); #endif +#ifdef CONFIG_NETFILTER_FAMILY_BRIDGE +struct nf_hook_entries __rcu *init_nf_hooks_bridge[NF_INET_NUMHOOKS]; +struct nf_hook_entries __rcu **init_nf_hooks_bridgep = &init_nf_hooks_bridge[0]; +EXPORT_SYMBOL_GPL(init_nf_hooks_bridgep); +#endif + static DEFINE_MUTEX(nf_hook_mutex); /* max hooks per family/hooknum */ @@ -278,9 +284,9 @@ nf_hook_entry_head(struct net *net, int pf, unsigned int hooknum, #endif #ifdef CONFIG_NETFILTER_FAMILY_BRIDGE case NFPROTO_BRIDGE: - if (WARN_ON_ONCE(ARRAY_SIZE(net->nf.hooks_bridge) <= hooknum)) + if (WARN_ON_ONCE(hooknum >= NF_INET_NUMHOOKS)) return NULL; - return net->nf.hooks_bridge + hooknum; + return get_nf_hooks_bridge(net) + hooknum; #endif #ifdef CONFIG_NETFILTER_INGRESS case NFPROTO_INET: @@ -747,7 +753,7 @@ static int __net_init netfilter_net_init(struct net *net) __netfilter_net_init(net->nf.hooks_arp, ARRAY_SIZE(net->nf.hooks_arp)); #endif #ifdef CONFIG_NETFILTER_FAMILY_BRIDGE - __netfilter_net_init(net->nf.hooks_bridge, ARRAY_SIZE(net->nf.hooks_bridge)); + __netfilter_net_init(get_nf_hooks_bridge(net), NF_INET_NUMHOOKS); #endif #ifdef CONFIG_PROC_FS net->nf.proc_netfilter = proc_net_mkdir(net, "netfilter", diff --git a/net/netfilter/nf_queue.c b/net/netfilter/nf_queue.c index 63d1516816b1..566f7794bf58 100644 --- a/net/netfilter/nf_queue.c +++ b/net/netfilter/nf_queue.c @@ -281,7 +281,7 @@ static struct nf_hook_entries *nf_hook_entries_head(const struct net *net, u8 pf switch (pf) { #ifdef CONFIG_NETFILTER_FAMILY_BRIDGE case NFPROTO_BRIDGE: - return rcu_dereference(net->nf.hooks_bridge[hooknum]); + return rcu_dereference(get_nf_hooks_bridge(net)[hooknum]); #endif case NFPROTO_IPV4: return rcu_dereference(net->nf.hooks_ipv4[hooknum]); diff --git a/net/netfilter/nfnetlink_hook.c b/net/netfilter/nfnetlink_hook.c index 8120aadf6a0f..3ca3c3a3ba01 100644 --- a/net/netfilter/nfnetlink_hook.c +++ b/net/netfilter/nfnetlink_hook.c @@ -210,9 +210,9 @@ nfnl_hook_entries_head(u8 pf, unsigned int hook, struct net *net, const char *de break; case NFPROTO_BRIDGE: #ifdef CONFIG_NETFILTER_FAMILY_BRIDGE - if (hook >= ARRAY_SIZE(net->nf.hooks_bridge)) + if (hook >= NF_INET_NUMHOOKS) return ERR_PTR(-EINVAL); - hook_head = rcu_dereference(net->nf.hooks_bridge[hook]); + hook_head = rcu_dereference(get_nf_hooks_bridge(net)[hook]); #endif break; #if defined(CONFIG_NETFILTER_INGRESS) || defined(CONFIG_NETFILTER_EGRESS)