--- /dev/null
+From 4e645b47c4f000a503b9c90163ad905786b9bc1d Mon Sep 17 00:00:00 2001
+From: Florian Westphal <fw@strlen.de>
+Date: Fri, 1 Dec 2017 00:21:02 +0100
+Subject: [PATCH 02/11] netfilter: core: make nf_unregister_net_hooks simple
+ wrapper again
+
+This reverts commit d3ad2c17b4047
+("netfilter: core: batch nf_unregister_net_hooks synchronize_net calls").
+
+Nothing wrong with it. However, followup patch will delay freeing of hooks
+with call_rcu, so all synchronize_net() calls become obsolete and there
+is no need anymore for this batching.
+
+This revert causes a temporary performance degradation when destroying
+network namespace, but its resolved with the upcoming call_rcu conversion.
+
+Signed-off-by: Florian Westphal <fw@strlen.de>
+Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
+---
+ net/netfilter/core.c | 59 +++-------------------------------------------------
+ 1 file changed, 3 insertions(+), 56 deletions(-)
+
+--- a/net/netfilter/core.c
++++ b/net/netfilter/core.c
+@@ -395,63 +395,10 @@ EXPORT_SYMBOL(nf_register_net_hooks);
+ void nf_unregister_net_hooks(struct net *net, const struct nf_hook_ops *reg,
+ unsigned int hookcount)
+ {
+- struct nf_hook_entries *to_free[16], *p;
+- struct nf_hook_entries __rcu **pp;
+- unsigned int i, j, n;
++ unsigned int i;
+
+- mutex_lock(&nf_hook_mutex);
+- for (i = 0; i < hookcount; i++) {
+- pp = nf_hook_entry_head(net, ®[i]);
+- if (!pp)
+- continue;
+-
+- p = nf_entry_dereference(*pp);
+- if (WARN_ON_ONCE(!p))
+- continue;
+- __nf_unregister_net_hook(p, ®[i]);
+- }
+- mutex_unlock(&nf_hook_mutex);
+-
+- do {
+- n = min_t(unsigned int, hookcount, ARRAY_SIZE(to_free));
+-
+- mutex_lock(&nf_hook_mutex);
+-
+- for (i = 0, j = 0; i < hookcount && j < n; i++) {
+- pp = nf_hook_entry_head(net, ®[i]);
+- if (!pp)
+- continue;
+-
+- p = nf_entry_dereference(*pp);
+- if (!p)
+- continue;
+-
+- to_free[j] = __nf_hook_entries_try_shrink(pp);
+- if (to_free[j])
+- ++j;
+- }
+-
+- mutex_unlock(&nf_hook_mutex);
+-
+- if (j) {
+- unsigned int nfq;
+-
+- synchronize_net();
+-
+- /* need 2nd synchronize_net() if nfqueue is used, skb
+- * can get reinjected right before nf_queue_hook_drop()
+- */
+- nfq = nf_queue_nf_hook_drop(net);
+- if (nfq)
+- synchronize_net();
+-
+- for (i = 0; i < j; i++)
+- kvfree(to_free[i]);
+- }
+-
+- reg += n;
+- hookcount -= n;
+- } while (hookcount > 0);
++ for (i = 0; i < hookcount; i++)
++ nf_unregister_net_hook(net, ®[i]);
+ }
+ EXPORT_SYMBOL(nf_unregister_net_hooks);
+
--- /dev/null
+From 26888dfd7e7454686b8d3ea9ba5045d5f236e4d7 Mon Sep 17 00:00:00 2001
+From: Florian Westphal <fw@strlen.de>
+Date: Fri, 1 Dec 2017 00:21:03 +0100
+Subject: [PATCH 03/11] netfilter: core: remove synchronize_net call if nfqueue
+ is used
+
+since commit 960632ece6949b ("netfilter: convert hook list to an array")
+nfqueue no longer stores a pointer to the hook that caused the packet
+to be queued. Therefore no extra synchronize_net() call is needed after
+dropping the packets enqueued by the old rule blob.
+
+Signed-off-by: Florian Westphal <fw@strlen.de>
+Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
+---
+ include/net/netfilter/nf_queue.h | 2 +-
+ net/netfilter/core.c | 6 +-----
+ net/netfilter/nf_internals.h | 2 +-
+ net/netfilter/nf_queue.c | 7 ++-----
+ net/netfilter/nfnetlink_queue.c | 9 ++-------
+ 5 files changed, 7 insertions(+), 19 deletions(-)
+
+--- a/include/net/netfilter/nf_queue.h
++++ b/include/net/netfilter/nf_queue.h
+@@ -25,7 +25,7 @@ struct nf_queue_entry {
+ struct nf_queue_handler {
+ int (*outfn)(struct nf_queue_entry *entry,
+ unsigned int queuenum);
+- unsigned int (*nf_hook_drop)(struct net *net);
++ void (*nf_hook_drop)(struct net *net);
+ };
+
+ void nf_register_queue_handler(struct net *net, const struct nf_queue_handler *qh);
+--- a/net/netfilter/core.c
++++ b/net/netfilter/core.c
+@@ -341,7 +341,6 @@ void nf_unregister_net_hook(struct net *
+ {
+ struct nf_hook_entries __rcu **pp;
+ struct nf_hook_entries *p;
+- unsigned int nfq;
+
+ pp = nf_hook_entry_head(net, reg);
+ if (!pp)
+@@ -364,10 +363,7 @@ void nf_unregister_net_hook(struct net *
+
+ synchronize_net();
+
+- /* other cpu might still process nfqueue verdict that used reg */
+- nfq = nf_queue_nf_hook_drop(net);
+- if (nfq)
+- synchronize_net();
++ nf_queue_nf_hook_drop(net);
+ kvfree(p);
+ }
+ EXPORT_SYMBOL(nf_unregister_net_hook);
+--- a/net/netfilter/nf_internals.h
++++ b/net/netfilter/nf_internals.h
+@@ -10,7 +10,7 @@
+ int nf_queue(struct sk_buff *skb, struct nf_hook_state *state,
+ const struct nf_hook_entries *entries, unsigned int index,
+ unsigned int verdict);
+-unsigned int nf_queue_nf_hook_drop(struct net *net);
++void nf_queue_nf_hook_drop(struct net *net);
+
+ /* nf_log.c */
+ int __init netfilter_log_init(void);
+--- a/net/netfilter/nf_queue.c
++++ b/net/netfilter/nf_queue.c
+@@ -96,18 +96,15 @@ void nf_queue_entry_get_refs(struct nf_q
+ }
+ EXPORT_SYMBOL_GPL(nf_queue_entry_get_refs);
+
+-unsigned int nf_queue_nf_hook_drop(struct net *net)
++void nf_queue_nf_hook_drop(struct net *net)
+ {
+ const struct nf_queue_handler *qh;
+- unsigned int count = 0;
+
+ rcu_read_lock();
+ qh = rcu_dereference(net->nf.queue_handler);
+ if (qh)
+- count = qh->nf_hook_drop(net);
++ qh->nf_hook_drop(net);
+ rcu_read_unlock();
+-
+- return count;
+ }
+ EXPORT_SYMBOL_GPL(nf_queue_nf_hook_drop);
+
+--- a/net/netfilter/nfnetlink_queue.c
++++ b/net/netfilter/nfnetlink_queue.c
+@@ -941,23 +941,18 @@ static struct notifier_block nfqnl_dev_n
+ .notifier_call = nfqnl_rcv_dev_event,
+ };
+
+-static unsigned int nfqnl_nf_hook_drop(struct net *net)
++static void nfqnl_nf_hook_drop(struct net *net)
+ {
+ struct nfnl_queue_net *q = nfnl_queue_pernet(net);
+- unsigned int instances = 0;
+ int i;
+
+ for (i = 0; i < INSTANCE_BUCKETS; i++) {
+ struct nfqnl_instance *inst;
+ struct hlist_head *head = &q->instance_table[i];
+
+- hlist_for_each_entry_rcu(inst, head, hlist) {
++ hlist_for_each_entry_rcu(inst, head, hlist)
+ nfqnl_flush(inst, NULL, 0);
+- instances++;
+- }
+ }
+-
+- return instances;
+ }
+
+ static int
--- /dev/null
+From 8c873e2199700c2de7dbd5eedb9d90d5f109462b Mon Sep 17 00:00:00 2001
+From: Florian Westphal <fw@strlen.de>
+Date: Fri, 1 Dec 2017 00:21:04 +0100
+Subject: [PATCH 04/11] netfilter: core: free hooks with call_rcu
+
+Giuseppe Scrivano says:
+ "SELinux, if enabled, registers for each new network namespace 6
+ netfilter hooks."
+
+Cost for this is high. With synchronize_net() removed:
+ "The net benefit on an SMP machine with two cores is that creating a
+ new network namespace takes -40% of the original time."
+
+This patch replaces synchronize_net+kvfree with call_rcu().
+We store rcu_head at the tail of a structure that has no fixed layout,
+i.e. we cannot use offsetof() to compute the start of the original
+allocation. Thus store this information right after the rcu head.
+
+We could simplify this by just placing the rcu_head at the start
+of struct nf_hook_entries. However, this structure is used in
+packet processing hotpath, so only place what is needed for that
+at the beginning of the struct.
+
+Reported-by: Giuseppe Scrivano <gscrivan@redhat.com>
+Signed-off-by: Florian Westphal <fw@strlen.de>
+Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
+---
+ include/linux/netfilter.h | 19 +++++++++++++++----
+ net/netfilter/core.c | 34 ++++++++++++++++++++++++++++------
+ 2 files changed, 43 insertions(+), 10 deletions(-)
+
+--- a/include/linux/netfilter.h
++++ b/include/linux/netfilter.h
+@@ -77,17 +77,28 @@ struct nf_hook_entry {
+ void *priv;
+ };
+
++struct nf_hook_entries_rcu_head {
++ struct rcu_head head;
++ void *allocation;
++};
++
+ struct nf_hook_entries {
+ u16 num_hook_entries;
+ /* padding */
+ struct nf_hook_entry hooks[];
+
+- /* trailer: pointers to original orig_ops of each hook.
+- *
+- * This is not part of struct nf_hook_entry since its only
+- * needed in slow path (hook register/unregister).
++ /* trailer: pointers to original orig_ops of each hook,
++ * followed by rcu_head and scratch space used for freeing
++ * the structure via call_rcu.
+ *
++ * This is not part of struct nf_hook_entry since its only
++ * needed in slow path (hook register/unregister):
+ * const struct nf_hook_ops *orig_ops[]
++ *
++ * For the same reason, we store this at end -- its
++ * only needed when a hook is deleted, not during
++ * packet path processing:
++ * struct nf_hook_entries_rcu_head head
+ */
+ };
+
+--- a/net/netfilter/core.c
++++ b/net/netfilter/core.c
+@@ -74,7 +74,8 @@ static struct nf_hook_entries *allocate_
+ struct nf_hook_entries *e;
+ size_t alloc = sizeof(*e) +
+ sizeof(struct nf_hook_entry) * num +
+- sizeof(struct nf_hook_ops *) * num;
++ sizeof(struct nf_hook_ops *) * num +
++ sizeof(struct nf_hook_entries_rcu_head);
+
+ if (num == 0)
+ return NULL;
+@@ -85,6 +86,30 @@ static struct nf_hook_entries *allocate_
+ return e;
+ }
+
++static void __nf_hook_entries_free(struct rcu_head *h)
++{
++ struct nf_hook_entries_rcu_head *head;
++
++ head = container_of(h, struct nf_hook_entries_rcu_head, head);
++ kvfree(head->allocation);
++}
++
++static void nf_hook_entries_free(struct nf_hook_entries *e)
++{
++ struct nf_hook_entries_rcu_head *head;
++ struct nf_hook_ops **ops;
++ unsigned int num;
++
++ if (!e)
++ return;
++
++ num = e->num_hook_entries;
++ ops = nf_hook_entries_get_hook_ops(e);
++ head = (void *)&ops[num];
++ head->allocation = e;
++ call_rcu(&head->head, __nf_hook_entries_free);
++}
++
+ static unsigned int accept_all(void *priv,
+ struct sk_buff *skb,
+ const struct nf_hook_state *state)
+@@ -291,9 +316,8 @@ int nf_register_net_hook(struct net *net
+ #ifdef HAVE_JUMP_LABEL
+ static_key_slow_inc(&nf_hooks_needed[reg->pf][reg->hooknum]);
+ #endif
+- synchronize_net();
+ BUG_ON(p == new_hooks);
+- kvfree(p);
++ nf_hook_entries_free(p);
+ return 0;
+ }
+ EXPORT_SYMBOL(nf_register_net_hook);
+@@ -361,10 +385,8 @@ void nf_unregister_net_hook(struct net *
+ if (!p)
+ return;
+
+- synchronize_net();
+-
+ nf_queue_nf_hook_drop(net);
+- kvfree(p);
++ nf_hook_entries_free(p);
+ }
+ EXPORT_SYMBOL(nf_unregister_net_hook);
+
--- /dev/null
+From b0f38338aef2dae5ade3c16acf713737e3b15a73 Mon Sep 17 00:00:00 2001
+From: Florian Westphal <fw@strlen.de>
+Date: Sun, 3 Dec 2017 00:58:47 +0100
+Subject: [PATCH 05/11] netfilter: reduce size of hook entry point locations
+
+struct net contains:
+
+struct nf_hook_entries __rcu *hooks[NFPROTO_NUMPROTO][NF_MAX_HOOKS];
+
+which store the hook entry point locations for the various protocol
+families and the hooks.
+
+Using array results in compact c code when doing accesses, i.e.
+ x = rcu_dereference(net->nf.hooks[pf][hook]);
+
+but its also wasting a lot of memory, as most families are
+not used.
+
+So split the array into those families that are used, which
+are only 5 (instead of 13). In most cases, the 'pf' argument is
+constant, i.e. gcc removes switch statement.
+
+struct net before:
+ /* size: 5184, cachelines: 81, members: 46 */
+after:
+ /* size: 4672, cachelines: 73, members: 46 */
+
+Signed-off-by: Florian Westphal <fw@strlen.de>
+Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
+---
+ include/linux/netfilter.h | 24 ++++++++++++++++++++++--
+ include/net/netns/netfilter.h | 6 +++++-
+ net/bridge/br_netfilter_hooks.c | 2 +-
+ net/netfilter/core.c | 38 ++++++++++++++++++++++++++++++--------
+ net/netfilter/nf_queue.c | 21 +++++++++++++++++++--
+ 5 files changed, 77 insertions(+), 14 deletions(-)
+
+--- a/include/linux/netfilter.h
++++ b/include/linux/netfilter.h
+@@ -195,7 +195,7 @@ static inline int nf_hook(u_int8_t pf, u
+ struct net_device *indev, struct net_device *outdev,
+ int (*okfn)(struct net *, struct sock *, struct sk_buff *))
+ {
+- struct nf_hook_entries *hook_head;
++ struct nf_hook_entries *hook_head = NULL;
+ int ret = 1;
+
+ #ifdef HAVE_JUMP_LABEL
+@@ -206,7 +206,27 @@ static inline int nf_hook(u_int8_t pf, u
+ #endif
+
+ rcu_read_lock();
+- hook_head = rcu_dereference(net->nf.hooks[pf][hook]);
++ switch (pf) {
++ case NFPROTO_IPV4:
++ hook_head = rcu_dereference(net->nf.hooks_ipv4[hook]);
++ break;
++ case NFPROTO_IPV6:
++ hook_head = rcu_dereference(net->nf.hooks_ipv6[hook]);
++ break;
++ case NFPROTO_ARP:
++ hook_head = rcu_dereference(net->nf.hooks_arp[hook]);
++ break;
++ case NFPROTO_BRIDGE:
++ hook_head = rcu_dereference(net->nf.hooks_bridge[hook]);
++ break;
++ case NFPROTO_DECNET:
++ hook_head = rcu_dereference(net->nf.hooks_decnet[hook]);
++ break;
++ default:
++ WARN_ON_ONCE(1);
++ break;
++ }
++
+ if (hook_head) {
+ struct nf_hook_state state;
+
+--- a/include/net/netns/netfilter.h
++++ b/include/net/netns/netfilter.h
+@@ -17,7 +17,11 @@ struct netns_nf {
+ #ifdef CONFIG_SYSCTL
+ struct ctl_table_header *nf_log_dir_header;
+ #endif
+- struct nf_hook_entries __rcu *hooks[NFPROTO_NUMPROTO][NF_MAX_HOOKS];
++ struct nf_hook_entries __rcu *hooks_ipv4[NF_MAX_HOOKS];
++ struct nf_hook_entries __rcu *hooks_ipv6[NF_MAX_HOOKS];
++ struct nf_hook_entries __rcu *hooks_arp[NF_MAX_HOOKS];
++ struct nf_hook_entries __rcu *hooks_bridge[NF_MAX_HOOKS];
++ struct nf_hook_entries __rcu *hooks_decnet[NF_MAX_HOOKS];
+ #if IS_ENABLED(CONFIG_NF_DEFRAG_IPV4)
+ bool defrag_ipv4;
+ #endif
+--- a/net/bridge/br_netfilter_hooks.c
++++ b/net/bridge/br_netfilter_hooks.c
+@@ -991,7 +991,7 @@ int br_nf_hook_thresh(unsigned int hook,
+ unsigned int i;
+ int ret;
+
+- e = rcu_dereference(net->nf.hooks[NFPROTO_BRIDGE][hook]);
++ e = rcu_dereference(net->nf.hooks_bridge[hook]);
+ if (!e)
+ return okfn(net, sk, skb);
+
+--- a/net/netfilter/core.c
++++ b/net/netfilter/core.c
+@@ -264,8 +264,23 @@ out_assign:
+
+ static struct nf_hook_entries __rcu **nf_hook_entry_head(struct net *net, const struct nf_hook_ops *reg)
+ {
+- if (reg->pf != NFPROTO_NETDEV)
+- return net->nf.hooks[reg->pf]+reg->hooknum;
++ switch (reg->pf) {
++ case NFPROTO_NETDEV:
++ break;
++ case NFPROTO_ARP:
++ return net->nf.hooks_arp + reg->hooknum;
++ case NFPROTO_BRIDGE:
++ return net->nf.hooks_bridge + reg->hooknum;
++ case NFPROTO_IPV4:
++ return net->nf.hooks_ipv4 + reg->hooknum;
++ case NFPROTO_IPV6:
++ return net->nf.hooks_ipv6 + reg->hooknum;
++ case NFPROTO_DECNET:
++ return net->nf.hooks_decnet + reg->hooknum;
++ default:
++ WARN_ON_ONCE(1);
++ return NULL;
++ }
+
+ #ifdef CONFIG_NETFILTER_INGRESS
+ if (reg->hooknum == NF_NETDEV_INGRESS) {
+@@ -534,14 +549,21 @@ void (*nf_nat_decode_session_hook)(struc
+ EXPORT_SYMBOL(nf_nat_decode_session_hook);
+ #endif
+
+-static int __net_init netfilter_net_init(struct net *net)
++static void __net_init __netfilter_net_init(struct nf_hook_entries *e[NF_MAX_HOOKS])
+ {
+- int i, h;
++ int h;
+
+- for (i = 0; i < ARRAY_SIZE(net->nf.hooks); i++) {
+- for (h = 0; h < NF_MAX_HOOKS; h++)
+- RCU_INIT_POINTER(net->nf.hooks[i][h], NULL);
+- }
++ for (h = 0; h < NF_MAX_HOOKS; h++)
++ RCU_INIT_POINTER(e[h], NULL);
++}
++
++static int __net_init netfilter_net_init(struct net *net)
++{
++ __netfilter_net_init(net->nf.hooks_ipv4);
++ __netfilter_net_init(net->nf.hooks_ipv6);
++ __netfilter_net_init(net->nf.hooks_arp);
++ __netfilter_net_init(net->nf.hooks_bridge);
++ __netfilter_net_init(net->nf.hooks_decnet);
+
+ #ifdef CONFIG_PROC_FS
+ net->nf.proc_netfilter = proc_net_mkdir(net, "netfilter",
+--- a/net/netfilter/nf_queue.c
++++ b/net/netfilter/nf_queue.c
+@@ -201,6 +201,23 @@ repeat:
+ return NF_ACCEPT;
+ }
+
++static struct nf_hook_entries *nf_hook_entries_head(const struct net *net, u8 pf, u8 hooknum)
++{
++ switch (pf) {
++ case NFPROTO_BRIDGE:
++ return rcu_dereference(net->nf.hooks_bridge[hooknum]);
++ case NFPROTO_IPV4:
++ return rcu_dereference(net->nf.hooks_ipv4[hooknum]);
++ case NFPROTO_IPV6:
++ return rcu_dereference(net->nf.hooks_ipv6[hooknum]);
++ default:
++ WARN_ON_ONCE(1);
++ return NULL;
++ }
++
++ return NULL;
++}
++
+ /* Caller must hold rcu read-side lock */
+ void nf_reinject(struct nf_queue_entry *entry, unsigned int verdict)
+ {
+@@ -216,12 +233,12 @@ void nf_reinject(struct nf_queue_entry *
+ net = entry->state.net;
+ pf = entry->state.pf;
+
+- hooks = rcu_dereference(net->nf.hooks[pf][entry->state.hook]);
++ hooks = nf_hook_entries_head(net, pf, entry->state.hook);
+
+ nf_queue_entry_release_refs(entry);
+
+ i = entry->hook_index;
+- if (WARN_ON_ONCE(i >= hooks->num_hook_entries)) {
++ if (WARN_ON_ONCE(!hooks || i >= hooks->num_hook_entries)) {
+ kfree_skb(skb);
+ kfree(entry);
+ return;
--- /dev/null
+From ef57170bbfdd6958281011332b1fd237712f69f0 Mon Sep 17 00:00:00 2001
+From: Florian Westphal <fw@strlen.de>
+Date: Thu, 7 Dec 2017 16:28:24 +0100
+Subject: [PATCH 06/11] netfilter: reduce hook array sizes to what is needed
+
+Not all families share the same hook count, adjust sizes to what is
+needed.
+
+struct net before:
+/* size: 6592, cachelines: 103, members: 46 */
+after:
+/* size: 5952, cachelines: 93, members: 46 */
+
+Signed-off-by: Florian Westphal <fw@strlen.de>
+Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
+---
+ include/net/netns/netfilter.h | 10 +++++-----
+ net/netfilter/core.c | 24 +++++++++++++++++-------
+ 2 files changed, 22 insertions(+), 12 deletions(-)
+
+--- a/include/net/netns/netfilter.h
++++ b/include/net/netns/netfilter.h
+@@ -17,11 +17,11 @@ struct netns_nf {
+ #ifdef CONFIG_SYSCTL
+ struct ctl_table_header *nf_log_dir_header;
+ #endif
+- struct nf_hook_entries __rcu *hooks_ipv4[NF_MAX_HOOKS];
+- struct nf_hook_entries __rcu *hooks_ipv6[NF_MAX_HOOKS];
+- struct nf_hook_entries __rcu *hooks_arp[NF_MAX_HOOKS];
+- struct nf_hook_entries __rcu *hooks_bridge[NF_MAX_HOOKS];
+- struct nf_hook_entries __rcu *hooks_decnet[NF_MAX_HOOKS];
++ struct nf_hook_entries __rcu *hooks_ipv4[NF_INET_NUMHOOKS];
++ struct nf_hook_entries __rcu *hooks_ipv6[NF_INET_NUMHOOKS];
++ struct nf_hook_entries __rcu *hooks_arp[NF_ARP_NUMHOOKS];
++ struct nf_hook_entries __rcu *hooks_bridge[NF_INET_NUMHOOKS];
++ struct nf_hook_entries __rcu *hooks_decnet[NF_DN_NUMHOOKS];
+ #if IS_ENABLED(CONFIG_NF_DEFRAG_IPV4)
+ bool defrag_ipv4;
+ #endif
+--- a/net/netfilter/core.c
++++ b/net/netfilter/core.c
+@@ -268,14 +268,24 @@ static struct nf_hook_entries __rcu **nf
+ case NFPROTO_NETDEV:
+ break;
+ case NFPROTO_ARP:
++ if (WARN_ON_ONCE(ARRAY_SIZE(net->nf.hooks_arp) <= reg->hooknum))
++ return NULL;
+ return net->nf.hooks_arp + reg->hooknum;
+ case NFPROTO_BRIDGE:
++ if (WARN_ON_ONCE(ARRAY_SIZE(net->nf.hooks_bridge) <= reg->hooknum))
++ return NULL;
+ return net->nf.hooks_bridge + reg->hooknum;
+ case NFPROTO_IPV4:
++ if (WARN_ON_ONCE(ARRAY_SIZE(net->nf.hooks_ipv4) <= reg->hooknum))
++ return NULL;
+ return net->nf.hooks_ipv4 + reg->hooknum;
+ case NFPROTO_IPV6:
++ if (WARN_ON_ONCE(ARRAY_SIZE(net->nf.hooks_ipv6) <= reg->hooknum))
++ return NULL;
+ return net->nf.hooks_ipv6 + reg->hooknum;
+ case NFPROTO_DECNET:
++ if (WARN_ON_ONCE(ARRAY_SIZE(net->nf.hooks_decnet) <= reg->hooknum))
++ return NULL;
+ return net->nf.hooks_decnet + reg->hooknum;
+ default:
+ WARN_ON_ONCE(1);
+@@ -549,21 +559,21 @@ void (*nf_nat_decode_session_hook)(struc
+ EXPORT_SYMBOL(nf_nat_decode_session_hook);
+ #endif
+
+-static void __net_init __netfilter_net_init(struct nf_hook_entries *e[NF_MAX_HOOKS])
++static void __net_init __netfilter_net_init(struct nf_hook_entries **e, int max)
+ {
+ int h;
+
+- for (h = 0; h < NF_MAX_HOOKS; h++)
++ for (h = 0; h < max; h++)
+ RCU_INIT_POINTER(e[h], NULL);
+ }
+
+ static int __net_init netfilter_net_init(struct net *net)
+ {
+- __netfilter_net_init(net->nf.hooks_ipv4);
+- __netfilter_net_init(net->nf.hooks_ipv6);
+- __netfilter_net_init(net->nf.hooks_arp);
+- __netfilter_net_init(net->nf.hooks_bridge);
+- __netfilter_net_init(net->nf.hooks_decnet);
++ __netfilter_net_init(net->nf.hooks_ipv4, ARRAY_SIZE(net->nf.hooks_ipv4));
++ __netfilter_net_init(net->nf.hooks_ipv6, ARRAY_SIZE(net->nf.hooks_ipv6));
++ __netfilter_net_init(net->nf.hooks_arp, ARRAY_SIZE(net->nf.hooks_arp));
++ __netfilter_net_init(net->nf.hooks_bridge, ARRAY_SIZE(net->nf.hooks_bridge));
++ __netfilter_net_init(net->nf.hooks_decnet, ARRAY_SIZE(net->nf.hooks_decnet));
+
+ #ifdef CONFIG_PROC_FS
+ net->nf.proc_netfilter = proc_net_mkdir(net, "netfilter",
--- /dev/null
+From bb4badf3a3dc81190f7c1c1fa063cdefb18df45f Mon Sep 17 00:00:00 2001
+From: Florian Westphal <fw@strlen.de>
+Date: Thu, 7 Dec 2017 16:28:25 +0100
+Subject: [PATCH 07/11] netfilter: don't allocate space for decnet hooks unless
+ needed
+
+no need to define hook points if the family isn't supported.
+
+Signed-off-by: Florian Westphal <fw@strlen.de>
+Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
+---
+ include/linux/netfilter.h | 2 ++
+ include/net/netns/netfilter.h | 2 ++
+ net/netfilter/core.c | 4 ++++
+ 3 files changed, 8 insertions(+)
+
+--- a/include/linux/netfilter.h
++++ b/include/linux/netfilter.h
+@@ -219,9 +219,11 @@ static inline int nf_hook(u_int8_t pf, u
+ case NFPROTO_BRIDGE:
+ hook_head = rcu_dereference(net->nf.hooks_bridge[hook]);
+ break;
++#if IS_ENABLED(CONFIG_DECNET)
+ case NFPROTO_DECNET:
+ hook_head = rcu_dereference(net->nf.hooks_decnet[hook]);
+ break;
++#endif
+ default:
+ WARN_ON_ONCE(1);
+ break;
+--- a/include/net/netns/netfilter.h
++++ b/include/net/netns/netfilter.h
+@@ -21,7 +21,9 @@ struct netns_nf {
+ struct nf_hook_entries __rcu *hooks_ipv6[NF_INET_NUMHOOKS];
+ struct nf_hook_entries __rcu *hooks_arp[NF_ARP_NUMHOOKS];
+ struct nf_hook_entries __rcu *hooks_bridge[NF_INET_NUMHOOKS];
++#if IS_ENABLED(CONFIG_DECNET)
+ struct nf_hook_entries __rcu *hooks_decnet[NF_DN_NUMHOOKS];
++#endif
+ #if IS_ENABLED(CONFIG_NF_DEFRAG_IPV4)
+ bool defrag_ipv4;
+ #endif
+--- a/net/netfilter/core.c
++++ b/net/netfilter/core.c
+@@ -283,10 +283,12 @@ static struct nf_hook_entries __rcu **nf
+ if (WARN_ON_ONCE(ARRAY_SIZE(net->nf.hooks_ipv6) <= reg->hooknum))
+ return NULL;
+ return net->nf.hooks_ipv6 + reg->hooknum;
++#if IS_ENABLED(CONFIG_DECNET)
+ case NFPROTO_DECNET:
+ if (WARN_ON_ONCE(ARRAY_SIZE(net->nf.hooks_decnet) <= reg->hooknum))
+ return NULL;
+ return net->nf.hooks_decnet + reg->hooknum;
++#endif
+ default:
+ WARN_ON_ONCE(1);
+ return NULL;
+@@ -573,7 +575,9 @@ static int __net_init netfilter_net_init
+ __netfilter_net_init(net->nf.hooks_ipv6, ARRAY_SIZE(net->nf.hooks_ipv6));
+ __netfilter_net_init(net->nf.hooks_arp, ARRAY_SIZE(net->nf.hooks_arp));
+ __netfilter_net_init(net->nf.hooks_bridge, ARRAY_SIZE(net->nf.hooks_bridge));
++#if IS_ENABLED(CONFIG_DECNET)
+ __netfilter_net_init(net->nf.hooks_decnet, ARRAY_SIZE(net->nf.hooks_decnet));
++#endif
+
+ #ifdef CONFIG_PROC_FS
+ net->nf.proc_netfilter = proc_net_mkdir(net, "netfilter",
--- /dev/null
+From 2a95183a5e0375df756efb2ca37602d71e8455f9 Mon Sep 17 00:00:00 2001
+From: Florian Westphal <fw@strlen.de>
+Date: Thu, 7 Dec 2017 16:28:26 +0100
+Subject: [PATCH 08/11] netfilter: don't allocate space for arp/bridge hooks
+ unless needed
+
+no need to define hook points if the family isn't supported.
+Because we need these hooks for either nftables, arp/ebtables
+or the 'call-iptables' hack we have in the bridge layer add two
+new dependencies, NETFILTER_FAMILY_{ARP,BRIDGE}, and have the
+users select them.
+
+Signed-off-by: Florian Westphal <fw@strlen.de>
+Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
+---
+ include/linux/netfilter.h | 4 ++++
+ include/net/netns/netfilter.h | 4 ++++
+ net/Kconfig | 1 +
+ net/bridge/netfilter/Kconfig | 2 ++
+ net/ipv4/netfilter/Kconfig | 2 ++
+ net/netfilter/Kconfig | 6 ++++++
+ net/netfilter/core.c | 8 ++++++++
+ net/netfilter/nf_queue.c | 2 ++
+ 8 files changed, 29 insertions(+)
+
+--- a/include/linux/netfilter.h
++++ b/include/linux/netfilter.h
+@@ -214,10 +214,14 @@ static inline int nf_hook(u_int8_t pf, u
+ hook_head = rcu_dereference(net->nf.hooks_ipv6[hook]);
+ break;
+ case NFPROTO_ARP:
++#ifdef CONFIG_NETFILTER_FAMILY_ARP
+ hook_head = rcu_dereference(net->nf.hooks_arp[hook]);
++#endif
+ break;
+ case NFPROTO_BRIDGE:
++#ifdef CONFIG_NETFILTER_FAMILY_BRIDGE
+ hook_head = rcu_dereference(net->nf.hooks_bridge[hook]);
++#endif
+ break;
+ #if IS_ENABLED(CONFIG_DECNET)
+ case NFPROTO_DECNET:
+--- a/include/net/netns/netfilter.h
++++ b/include/net/netns/netfilter.h
+@@ -19,8 +19,12 @@ struct netns_nf {
+ #endif
+ struct nf_hook_entries __rcu *hooks_ipv4[NF_INET_NUMHOOKS];
+ struct nf_hook_entries __rcu *hooks_ipv6[NF_INET_NUMHOOKS];
++#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_DECNET)
+ struct nf_hook_entries __rcu *hooks_decnet[NF_DN_NUMHOOKS];
+ #endif
+--- a/net/Kconfig
++++ b/net/Kconfig
+@@ -182,6 +182,7 @@ config BRIDGE_NETFILTER
+ depends on BRIDGE
+ depends on NETFILTER && INET
+ depends on NETFILTER_ADVANCED
++ select NETFILTER_FAMILY_BRIDGE
+ default m
+ ---help---
+ Enabling this option will let arptables resp. iptables see bridged
+--- a/net/bridge/netfilter/Kconfig
++++ b/net/bridge/netfilter/Kconfig
+@@ -4,6 +4,7 @@
+ #
+ menuconfig NF_TABLES_BRIDGE
+ depends on BRIDGE && NETFILTER && NF_TABLES
++ select NETFILTER_FAMILY_BRIDGE
+ tristate "Ethernet Bridge nf_tables support"
+
+ if NF_TABLES_BRIDGE
+@@ -29,6 +30,7 @@ endif # NF_TABLES_BRIDGE
+ menuconfig BRIDGE_NF_EBTABLES
+ tristate "Ethernet Bridge tables (ebtables) support"
+ depends on BRIDGE && NETFILTER && NETFILTER_XTABLES
++ select NETFILTER_FAMILY_BRIDGE
+ help
+ ebtables is a general, extensible frame/packet identification
+ framework. Say 'Y' or 'M' here if you want to do Ethernet
+--- a/net/ipv4/netfilter/Kconfig
++++ b/net/ipv4/netfilter/Kconfig
+@@ -72,6 +72,7 @@ endif # NF_TABLES_IPV4
+
+ config NF_TABLES_ARP
+ tristate "ARP nf_tables support"
++ select NETFILTER_FAMILY_ARP
+ help
+ This option enables the ARP support for nf_tables.
+
+@@ -392,6 +393,7 @@ endif # IP_NF_IPTABLES
+ config IP_NF_ARPTABLES
+ tristate "ARP tables support"
+ select NETFILTER_XTABLES
++ select NETFILTER_FAMILY_ARP
+ depends on NETFILTER_ADVANCED
+ help
+ arptables is a general, extensible packet identification framework.
+--- a/net/netfilter/Kconfig
++++ b/net/netfilter/Kconfig
+@@ -12,6 +12,12 @@ config NETFILTER_INGRESS
+ config NETFILTER_NETLINK
+ tristate
+
++config NETFILTER_FAMILY_BRIDGE
++ bool
++
++config NETFILTER_FAMILY_ARP
++ bool
++
+ config NETFILTER_NETLINK_ACCT
+ tristate "Netfilter NFACCT over NFNETLINK interface"
+ depends on NETFILTER_ADVANCED
+--- a/net/netfilter/core.c
++++ b/net/netfilter/core.c
+@@ -267,14 +267,18 @@ static struct nf_hook_entries __rcu **nf
+ switch (reg->pf) {
+ case NFPROTO_NETDEV:
+ break;
++#ifdef CONFIG_NETFILTER_FAMILY_ARP
+ case NFPROTO_ARP:
+ if (WARN_ON_ONCE(ARRAY_SIZE(net->nf.hooks_arp) <= reg->hooknum))
+ return NULL;
+ return net->nf.hooks_arp + reg->hooknum;
++#endif
++#ifdef CONFIG_NETFILTER_FAMILY_BRIDGE
+ case NFPROTO_BRIDGE:
+ if (WARN_ON_ONCE(ARRAY_SIZE(net->nf.hooks_bridge) <= reg->hooknum))
+ return NULL;
+ return net->nf.hooks_bridge + reg->hooknum;
++#endif
+ case NFPROTO_IPV4:
+ if (WARN_ON_ONCE(ARRAY_SIZE(net->nf.hooks_ipv4) <= reg->hooknum))
+ return NULL;
+@@ -573,8 +577,12 @@ static int __net_init netfilter_net_init
+ {
+ __netfilter_net_init(net->nf.hooks_ipv4, ARRAY_SIZE(net->nf.hooks_ipv4));
+ __netfilter_net_init(net->nf.hooks_ipv6, ARRAY_SIZE(net->nf.hooks_ipv6));
++#ifdef CONFIG_NETFILTER_FAMILY_ARP
+ __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));
++#endif
+ #if IS_ENABLED(CONFIG_DECNET)
+ __netfilter_net_init(net->nf.hooks_decnet, ARRAY_SIZE(net->nf.hooks_decnet));
+ #endif
+--- a/net/netfilter/nf_queue.c
++++ b/net/netfilter/nf_queue.c
+@@ -204,8 +204,10 @@ repeat:
+ static struct nf_hook_entries *nf_hook_entries_head(const struct net *net, u8 pf, u8 hooknum)
+ {
+ switch (pf) {
++#ifdef CONFIG_NETFILTER_FAMILY_BRIDGE
+ case NFPROTO_BRIDGE:
+ return rcu_dereference(net->nf.hooks_bridge[hooknum]);
++#endif
+ case NFPROTO_IPV4:
+ return rcu_dereference(net->nf.hooks_ipv4[hooknum]);
+ case NFPROTO_IPV6:
--- /dev/null
+From 62a0fe46e2aaba1812d3cbcae014a41539f9eb09 Mon Sep 17 00:00:00 2001
+From: Pablo Neira Ayuso <pablo@netfilter.org>
+Date: Sat, 9 Dec 2017 15:23:51 +0100
+Subject: [PATCH 09/11] netfilter: core: pass hook number, family and device to
+ nf_find_hook_list()
+
+Instead of passing struct nf_hook_ops, this is needed by follow up
+patches to handle NFPROTO_INET from the core.
+
+Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
+---
+ net/netfilter/core.c | 36 +++++++++++++++++++-----------------
+ 1 file changed, 19 insertions(+), 17 deletions(-)
+
+--- a/net/netfilter/core.c
++++ b/net/netfilter/core.c
+@@ -262,36 +262,38 @@ out_assign:
+ return old;
+ }
+
+-static struct nf_hook_entries __rcu **nf_hook_entry_head(struct net *net, const struct nf_hook_ops *reg)
++static struct nf_hook_entries __rcu **
++nf_hook_entry_head(struct net *net, int pf, unsigned int hooknum,
++ struct net_device *dev)
+ {
+- switch (reg->pf) {
++ switch (pf) {
+ case NFPROTO_NETDEV:
+ break;
+ #ifdef CONFIG_NETFILTER_FAMILY_ARP
+ case NFPROTO_ARP:
+- if (WARN_ON_ONCE(ARRAY_SIZE(net->nf.hooks_arp) <= reg->hooknum))
++ if (WARN_ON_ONCE(ARRAY_SIZE(net->nf.hooks_arp) <= hooknum))
+ return NULL;
+- return net->nf.hooks_arp + reg->hooknum;
++ return net->nf.hooks_arp + hooknum;
+ #endif
+ #ifdef CONFIG_NETFILTER_FAMILY_BRIDGE
+ case NFPROTO_BRIDGE:
+- if (WARN_ON_ONCE(ARRAY_SIZE(net->nf.hooks_bridge) <= reg->hooknum))
++ if (WARN_ON_ONCE(ARRAY_SIZE(net->nf.hooks_bridge) <= hooknum))
+ return NULL;
+- return net->nf.hooks_bridge + reg->hooknum;
++ return net->nf.hooks_bridge + hooknum;
+ #endif
+ case NFPROTO_IPV4:
+- if (WARN_ON_ONCE(ARRAY_SIZE(net->nf.hooks_ipv4) <= reg->hooknum))
++ if (WARN_ON_ONCE(ARRAY_SIZE(net->nf.hooks_ipv4) <= hooknum))
+ return NULL;
+- return net->nf.hooks_ipv4 + reg->hooknum;
++ return net->nf.hooks_ipv4 + hooknum;
+ case NFPROTO_IPV6:
+- if (WARN_ON_ONCE(ARRAY_SIZE(net->nf.hooks_ipv6) <= reg->hooknum))
++ if (WARN_ON_ONCE(ARRAY_SIZE(net->nf.hooks_ipv6) <= hooknum))
+ return NULL;
+- return net->nf.hooks_ipv6 + reg->hooknum;
++ return net->nf.hooks_ipv6 + hooknum;
+ #if IS_ENABLED(CONFIG_DECNET)
+ case NFPROTO_DECNET:
+- if (WARN_ON_ONCE(ARRAY_SIZE(net->nf.hooks_decnet) <= reg->hooknum))
++ if (WARN_ON_ONCE(ARRAY_SIZE(net->nf.hooks_decnet) <= hooknum))
+ return NULL;
+- return net->nf.hooks_decnet + reg->hooknum;
++ return net->nf.hooks_decnet + hooknum;
+ #endif
+ default:
+ WARN_ON_ONCE(1);
+@@ -299,9 +301,9 @@ static struct nf_hook_entries __rcu **nf
+ }
+
+ #ifdef CONFIG_NETFILTER_INGRESS
+- if (reg->hooknum == NF_NETDEV_INGRESS) {
+- if (reg->dev && dev_net(reg->dev) == net)
+- return ®->dev->nf_hooks_ingress;
++ if (hooknum == NF_NETDEV_INGRESS) {
++ if (dev && dev_net(dev) == net)
++ return &dev->nf_hooks_ingress;
+ }
+ #endif
+ WARN_ON_ONCE(1);
+@@ -323,7 +325,7 @@ int nf_register_net_hook(struct net *net
+ return -EINVAL;
+ }
+
+- pp = nf_hook_entry_head(net, reg);
++ pp = nf_hook_entry_head(net, reg->pf, reg->hooknum, reg->dev);
+ if (!pp)
+ return -EINVAL;
+
+@@ -397,7 +399,7 @@ void nf_unregister_net_hook(struct net *
+ struct nf_hook_entries __rcu **pp;
+ struct nf_hook_entries *p;
+
+- pp = nf_hook_entry_head(net, reg);
++ pp = nf_hook_entry_head(net, reg->pf, reg->hooknum, reg->dev);
+ if (!pp)
+ return;
+
--- /dev/null
+From 3d3cdc38e8c265a9f9d3825e823e772872bca1b8 Mon Sep 17 00:00:00 2001
+From: Pablo Neira Ayuso <pablo@netfilter.org>
+Date: Sat, 9 Dec 2017 15:19:14 +0100
+Subject: [PATCH 01/11] netfilter: core: add nf_remove_net_hook
+
+Just a cleanup, __nf_unregister_net_hook() is used by a follow up patch
+when handling NFPROTO_INET as a real family from the core.
+
+Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
+---
+ net/netfilter/core.c | 8 ++++----
+ 1 file changed, 4 insertions(+), 4 deletions(-)
+
+--- a/net/netfilter/core.c
++++ b/net/netfilter/core.c
+@@ -356,7 +356,7 @@ int nf_register_net_hook(struct net *net
+ EXPORT_SYMBOL(nf_register_net_hook);
+
+ /*
+- * __nf_unregister_net_hook - remove a hook from blob
++ * nf_remove_net_hook - remove a hook from blob
+ *
+ * @oldp: current address of hook blob
+ * @unreg: hook to unregister
+@@ -364,8 +364,8 @@ EXPORT_SYMBOL(nf_register_net_hook);
+ * This cannot fail, hook unregistration must always succeed.
+ * Therefore replace the to-be-removed hook with a dummy hook.
+ */
+-static void __nf_unregister_net_hook(struct nf_hook_entries *old,
+- const struct nf_hook_ops *unreg)
++static void nf_remove_net_hook(struct nf_hook_entries *old,
++ const struct nf_hook_ops *unreg)
+ {
+ struct nf_hook_ops **orig_ops;
+ bool found = false;
+@@ -411,7 +411,7 @@ void nf_unregister_net_hook(struct net *
+ return;
+ }
+
+- __nf_unregister_net_hook(p, reg);
++ nf_remove_net_hook(p, reg);
+
+ p = __nf_hook_entries_try_shrink(pp);
+ mutex_unlock(&nf_hook_mutex);
--- /dev/null
+From 30259408118f550f5969fda19c0d67020d21eda8 Mon Sep 17 00:00:00 2001
+From: Pablo Neira Ayuso <pablo@netfilter.org>
+Date: Sat, 9 Dec 2017 15:26:37 +0100
+Subject: [PATCH 10/11] netfilter: core: pass family as parameter to
+ nf_remove_net_hook()
+
+So static_key_slow_dec applies to the family behind NFPROTO_INET.
+
+Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
+---
+ net/netfilter/core.c | 10 +++++-----
+ 1 file changed, 5 insertions(+), 5 deletions(-)
+
+--- a/net/netfilter/core.c
++++ b/net/netfilter/core.c
+@@ -365,7 +365,7 @@ EXPORT_SYMBOL(nf_register_net_hook);
+ * Therefore replace the to-be-removed hook with a dummy hook.
+ */
+ static void nf_remove_net_hook(struct nf_hook_entries *old,
+- const struct nf_hook_ops *unreg)
++ const struct nf_hook_ops *unreg, int pf)
+ {
+ struct nf_hook_ops **orig_ops;
+ bool found = false;
+@@ -383,14 +383,14 @@ static void nf_remove_net_hook(struct nf
+
+ if (found) {
+ #ifdef CONFIG_NETFILTER_INGRESS
+- if (unreg->pf == NFPROTO_NETDEV && unreg->hooknum == NF_NETDEV_INGRESS)
++ if (pf == NFPROTO_NETDEV && unreg->hooknum == NF_NETDEV_INGRESS)
+ net_dec_ingress_queue();
+ #endif
+ #ifdef HAVE_JUMP_LABEL
+- static_key_slow_dec(&nf_hooks_needed[unreg->pf][unreg->hooknum]);
++ static_key_slow_dec(&nf_hooks_needed[pf][unreg->hooknum]);
+ #endif
+ } else {
+- WARN_ONCE(1, "hook not found, pf %d num %d", unreg->pf, unreg->hooknum);
++ WARN_ONCE(1, "hook not found, pf %d num %d", pf, unreg->hooknum);
+ }
+ }
+
+@@ -411,7 +411,7 @@ void nf_unregister_net_hook(struct net *
+ return;
+ }
+
+- nf_remove_net_hook(p, reg);
++ nf_remove_net_hook(p, reg, reg->pf);
+
+ p = __nf_hook_entries_try_shrink(pp);
+ mutex_unlock(&nf_hook_mutex);
--- /dev/null
+From cb7ccd835ebb333669e400f99c650e4f3abf11c0 Mon Sep 17 00:00:00 2001
+From: Pablo Neira Ayuso <pablo@netfilter.org>
+Date: Sat, 9 Dec 2017 15:30:26 +0100
+Subject: [PATCH 11/11] netfilter: core: support for NFPROTO_INET hook
+ registration
+
+Expand NFPROTO_INET in two hook registrations, one for NFPROTO_IPV4 and
+another for NFPROTO_IPV6. Hence, we handle NFPROTO_INET from the core.
+
+Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
+---
+ net/netfilter/core.c | 53 +++++++++++++++++++++++++++++++++++++++++++---------
+ 1 file changed, 44 insertions(+), 9 deletions(-)
+
+--- a/net/netfilter/core.c
++++ b/net/netfilter/core.c
+@@ -310,12 +310,13 @@ nf_hook_entry_head(struct net *net, int
+ return NULL;
+ }
+
+-int nf_register_net_hook(struct net *net, const struct nf_hook_ops *reg)
++static int __nf_register_net_hook(struct net *net, int pf,
++ const struct nf_hook_ops *reg)
+ {
+ struct nf_hook_entries *p, *new_hooks;
+ struct nf_hook_entries __rcu **pp;
+
+- if (reg->pf == NFPROTO_NETDEV) {
++ if (pf == NFPROTO_NETDEV) {
+ #ifndef CONFIG_NETFILTER_INGRESS
+ if (reg->hooknum == NF_NETDEV_INGRESS)
+ return -EOPNOTSUPP;
+@@ -325,7 +326,7 @@ int nf_register_net_hook(struct net *net
+ return -EINVAL;
+ }
+
+- pp = nf_hook_entry_head(net, reg->pf, reg->hooknum, reg->dev);
++ pp = nf_hook_entry_head(net, pf, reg->hooknum, reg->dev);
+ if (!pp)
+ return -EINVAL;
+
+@@ -343,17 +344,16 @@ int nf_register_net_hook(struct net *net
+
+ hooks_validate(new_hooks);
+ #ifdef CONFIG_NETFILTER_INGRESS
+- if (reg->pf == NFPROTO_NETDEV && reg->hooknum == NF_NETDEV_INGRESS)
++ if (pf == NFPROTO_NETDEV && reg->hooknum == NF_NETDEV_INGRESS)
+ net_inc_ingress_queue();
+ #endif
+ #ifdef HAVE_JUMP_LABEL
+- static_key_slow_inc(&nf_hooks_needed[reg->pf][reg->hooknum]);
++ static_key_slow_inc(&nf_hooks_needed[pf][reg->hooknum]);
+ #endif
+ BUG_ON(p == new_hooks);
+ nf_hook_entries_free(p);
+ return 0;
+ }
+-EXPORT_SYMBOL(nf_register_net_hook);
+
+ /*
+ * nf_remove_net_hook - remove a hook from blob
+@@ -394,12 +394,13 @@ static void nf_remove_net_hook(struct nf
+ }
+ }
+
+-void nf_unregister_net_hook(struct net *net, const struct nf_hook_ops *reg)
++void __nf_unregister_net_hook(struct net *net, int pf,
++ const struct nf_hook_ops *reg)
+ {
+ struct nf_hook_entries __rcu **pp;
+ struct nf_hook_entries *p;
+
+- pp = nf_hook_entry_head(net, reg->pf, reg->hooknum, reg->dev);
++ pp = nf_hook_entry_head(net, pf, reg->hooknum, reg->dev);
+ if (!pp)
+ return;
+
+@@ -411,7 +412,7 @@ void nf_unregister_net_hook(struct net *
+ return;
+ }
+
+- nf_remove_net_hook(p, reg, reg->pf);
++ nf_remove_net_hook(p, reg, pf);
+
+ p = __nf_hook_entries_try_shrink(pp);
+ mutex_unlock(&nf_hook_mutex);
+@@ -421,8 +422,42 @@ void nf_unregister_net_hook(struct net *
+ nf_queue_nf_hook_drop(net);
+ nf_hook_entries_free(p);
+ }
++
++void nf_unregister_net_hook(struct net *net, const struct nf_hook_ops *reg)
++{
++ if (reg->pf == NFPROTO_INET) {
++ __nf_unregister_net_hook(net, NFPROTO_IPV4, reg);
++ __nf_unregister_net_hook(net, NFPROTO_IPV6, reg);
++ } else {
++ __nf_unregister_net_hook(net, reg->pf, reg);
++ }
++}
+ EXPORT_SYMBOL(nf_unregister_net_hook);
+
++int nf_register_net_hook(struct net *net, const struct nf_hook_ops *reg)
++{
++ int err;
++
++ if (reg->pf == NFPROTO_INET) {
++ err = __nf_register_net_hook(net, NFPROTO_IPV4, reg);
++ if (err < 0)
++ return err;
++
++ err = __nf_register_net_hook(net, NFPROTO_IPV6, reg);
++ if (err < 0) {
++ __nf_unregister_net_hook(net, NFPROTO_IPV4, reg);
++ return err;
++ }
++ } else {
++ err = __nf_register_net_hook(net, reg->pf, reg);
++ if (err < 0)
++ return err;
++ }
++
++ return 0;
++}
++EXPORT_SYMBOL(nf_register_net_hook);
++
+ int nf_register_net_hooks(struct net *net, const struct nf_hook_ops *reg,
+ unsigned int n)
+ {
.priority = NF_IP6_PRI_NAT_SRC,
--- a/net/netfilter/core.c
+++ b/net/netfilter/core.c
-@@ -135,6 +135,12 @@ nf_hook_entries_grow(const struct nf_hoo
+@@ -160,6 +160,12 @@ nf_hook_entries_grow(const struct nf_hoo
++i;
continue;
}
--- a/include/linux/netfilter.h
+++ b/include/linux/netfilter.h
-@@ -274,8 +274,6 @@ struct nf_queue_entry;
+@@ -311,8 +311,6 @@ struct nf_queue_entry;
struct nf_afinfo {
unsigned short family;
__sum16 (*checksum_partial)(struct sk_buff *skb,
unsigned int hook,
unsigned int dataoff,
-@@ -296,20 +294,9 @@ static inline const struct nf_afinfo *nf
+@@ -333,20 +331,9 @@ static inline const struct nf_afinfo *nf
return rcu_dereference(nf_afinfo[family]);
}
--- a/include/linux/netfilter.h
+++ b/include/linux/netfilter.h
-@@ -274,11 +274,6 @@ struct nf_queue_entry;
+@@ -311,11 +311,6 @@ struct nf_queue_entry;
struct nf_afinfo {
unsigned short family;
int (*route)(struct net *net, struct dst_entry **dst,
struct flowi *fl, bool strict);
void (*saveroute)(const struct sk_buff *skb,
-@@ -298,22 +293,9 @@ __sum16 nf_checksum(struct sk_buff *skb,
+@@ -335,22 +330,9 @@ __sum16 nf_checksum(struct sk_buff *skb,
unsigned int dataoff, u_int8_t protocol,
unsigned short family);
--- a/include/linux/netfilter.h
+++ b/include/linux/netfilter.h
-@@ -276,8 +276,6 @@ struct nf_afinfo {
+@@ -313,8 +313,6 @@ struct nf_afinfo {
unsigned short family;
int (*route)(struct net *net, struct dst_entry **dst,
struct flowi *fl, bool strict);
#include <linux/netfilter_bridge.h>
#include <linux/seq_file.h>
#include <linux/rcupdate.h>
-@@ -111,6 +113,35 @@ unsigned int nf_queue_nf_hook_drop(struc
+@@ -108,6 +110,35 @@ void nf_queue_nf_hook_drop(struct net *n
}
EXPORT_SYMBOL_GPL(nf_queue_nf_hook_drop);
static int __nf_queue(struct sk_buff *skb, const struct nf_hook_state *state,
const struct nf_hook_entries *entries,
unsigned int index, unsigned int queuenum)
-@@ -147,7 +178,16 @@ static int __nf_queue(struct sk_buff *sk
+@@ -144,7 +175,16 @@ static int __nf_queue(struct sk_buff *sk
nf_queue_entry_get_refs(entry);
skb_dst_force(skb);
--- a/include/linux/netfilter.h
+++ b/include/linux/netfilter.h
-@@ -274,8 +274,6 @@ struct nf_queue_entry;
+@@ -311,8 +311,6 @@ struct nf_queue_entry;
struct nf_afinfo {
unsigned short family;
int (*reroute)(struct net *net, struct sk_buff *skb,
const struct nf_queue_entry *entry);
int route_key_size;
-@@ -294,6 +292,8 @@ __sum16 nf_checksum(struct sk_buff *skb,
+@@ -331,6 +329,8 @@ __sum16 nf_checksum(struct sk_buff *skb,
__sum16 nf_checksum_partial(struct sk_buff *skb, unsigned int hook,
unsigned int dataoff, unsigned int len,
u_int8_t protocol, unsigned short family);
--- a/include/linux/netfilter.h
+++ b/include/linux/netfilter.h
-@@ -274,8 +274,6 @@ struct nf_queue_entry;
+@@ -311,8 +311,6 @@ struct nf_queue_entry;
struct nf_afinfo {
unsigned short family;
int route_key_size;
};
-@@ -294,6 +292,7 @@ __sum16 nf_checksum_partial(struct sk_bu
+@@ -331,6 +329,7 @@ __sum16 nf_checksum_partial(struct sk_bu
u_int8_t protocol, unsigned short family);
int nf_route(struct net *net, struct dst_entry **dst, struct flowi *fl,
bool strict, unsigned short family);
--- a/net/netfilter/nf_queue.c
+++ b/net/netfilter/nf_queue.c
-@@ -250,7 +250,6 @@ void nf_reinject(struct nf_queue_entry *
+@@ -266,7 +266,6 @@ void nf_reinject(struct nf_queue_entry *
const struct nf_hook_entry *hook_entry;
const struct nf_hook_entries *hooks;
struct sk_buff *skb = entry->skb;
const struct net *net;
unsigned int i;
int err;
-@@ -277,8 +276,7 @@ void nf_reinject(struct nf_queue_entry *
+@@ -293,8 +292,7 @@ void nf_reinject(struct nf_queue_entry *
verdict = nf_hook_entry_hookfn(hook_entry, skb, &entry->state);
if (verdict == NF_ACCEPT) {
--- a/include/linux/netfilter.h
+++ b/include/linux/netfilter.h
-@@ -274,7 +274,6 @@ struct nf_queue_entry;
+@@ -311,7 +311,6 @@ struct nf_queue_entry;
struct nf_afinfo {
unsigned short family;
#include <net/protocol.h>
#include <net/netfilter/nf_queue.h>
#include <net/dst.h>
-@@ -148,9 +150,9 @@ static int __nf_queue(struct sk_buff *sk
+@@ -145,9 +147,9 @@ static int __nf_queue(struct sk_buff *sk
{
int status = -ENOENT;
struct nf_queue_entry *entry = NULL;
/* QUEUE == DROP if no one is waiting, to be safe. */
qh = rcu_dereference(net->nf.queue_handler);
-@@ -159,11 +161,19 @@ static int __nf_queue(struct sk_buff *sk
+@@ -156,11 +158,19 @@ static int __nf_queue(struct sk_buff *sk
goto err;
}
if (!entry) {
status = -ENOMEM;
goto err;
-@@ -173,7 +183,7 @@ static int __nf_queue(struct sk_buff *sk
+@@ -170,7 +180,7 @@ static int __nf_queue(struct sk_buff *sk
.skb = skb,
.state = *state,
.hook_index = index,
--- a/include/linux/netfilter.h
+++ b/include/linux/netfilter.h
-@@ -272,16 +272,6 @@ int skb_make_writable(struct sk_buff *sk
+@@ -309,16 +309,6 @@ int skb_make_writable(struct sk_buff *sk
struct flowi;
struct nf_queue_entry;
__sum16 nf_checksum(struct sk_buff *skb, unsigned int hook,
unsigned int dataoff, u_int8_t protocol,
unsigned short family);
-@@ -293,9 +283,6 @@ int nf_route(struct net *net, struct dst
+@@ -330,9 +320,6 @@ int nf_route(struct net *net, struct dst
bool strict, unsigned short family);
int nf_reroute(struct sk_buff *skb, struct nf_queue_entry *entry);
#endif /* _FLOW_OFFLOAD_H */
--- a/net/netfilter/Kconfig
+++ b/net/netfilter/Kconfig
-@@ -661,6 +661,13 @@ endif # NF_TABLES_NETDEV
+@@ -667,6 +667,13 @@ endif # NF_TABLES_NETDEV
endif # NF_TABLES
--- a/net/ipv4/netfilter/Kconfig
+++ b/net/ipv4/netfilter/Kconfig
-@@ -77,6 +77,14 @@ config NF_TABLES_ARP
+@@ -78,6 +78,14 @@ config NF_TABLES_ARP
endif # NF_TABLES
.family = NFPROTO_IPV6,
--- a/net/netfilter/Kconfig
+++ b/net/netfilter/Kconfig
-@@ -661,6 +661,14 @@ endif # NF_TABLES_NETDEV
+@@ -667,6 +667,14 @@ endif # NF_TABLES_NETDEV
endif # NF_TABLES
NFT_LIMIT_PKT_BYTES
--- a/net/netfilter/Kconfig
+++ b/net/netfilter/Kconfig
-@@ -509,6 +509,13 @@ config NFT_CT
+@@ -515,6 +515,13 @@ config NFT_CT
This option adds the "ct" expression that you can use to match
connection tracking information such as the flow state.
--- a/net/ipv4/netfilter/Kconfig
+++ b/net/ipv4/netfilter/Kconfig
-@@ -78,8 +78,9 @@ config NF_TABLES_ARP
+@@ -79,8 +79,9 @@ config NF_TABLES_ARP
endif # NF_TABLES
config NF_FLOW_TABLE_IPV4
--- a/net/netfilter/Kconfig
+++ b/net/netfilter/Kconfig
-@@ -669,8 +669,9 @@ endif # NF_TABLES_NETDEV
+@@ -675,8 +675,9 @@ endif # NF_TABLES_NETDEV
endif # NF_TABLES
config NF_FLOW_TABLE_INET
help
This option adds the flow table mixed IPv4/IPv6 support.
-@@ -678,6 +679,7 @@ config NF_FLOW_TABLE_INET
+@@ -684,6 +685,7 @@ config NF_FLOW_TABLE_INET
config NF_FLOW_TABLE
tristate "Netfilter flow table module"
static struct pernet_operations nfnl_log_net_ops = {
--- a/net/netfilter/nfnetlink_queue.c
+++ b/net/netfilter/nfnetlink_queue.c
-@@ -1515,10 +1515,15 @@ static int __net_init nfnl_queue_net_ini
+@@ -1510,10 +1510,15 @@ static int __net_init nfnl_queue_net_ini
static void __net_exit nfnl_queue_net_exit(struct net *net)
{
--- a/net/ipv4/netfilter/Kconfig
+++ b/net/ipv4/netfilter/Kconfig
-@@ -79,8 +79,7 @@ endif # NF_TABLES
+@@ -80,8 +80,7 @@ endif # NF_TABLES
config NF_FLOW_TABLE_IPV4
tristate "Netfilter flow table IPv4 module"
--- a/net/netfilter/Kconfig
+++ b/net/netfilter/Kconfig
-@@ -670,8 +670,8 @@ endif # NF_TABLES
+@@ -676,8 +676,8 @@ endif # NF_TABLES
config NF_FLOW_TABLE_INET
tristate "Netfilter flow table mixed IPv4/IPv6 module"
help
This option adds the flow table mixed IPv4/IPv6 support.
-@@ -679,7 +679,9 @@ config NF_FLOW_TABLE_INET
+@@ -685,7 +685,9 @@ config NF_FLOW_TABLE_INET
config NF_FLOW_TABLE
tristate "Netfilter flow table module"
--- a/net/netfilter/Kconfig
+++ b/net/netfilter/Kconfig
-@@ -670,8 +670,7 @@ endif # NF_TABLES
+@@ -676,8 +676,7 @@ endif # NF_TABLES
config NF_FLOW_TABLE_INET
tristate "Netfilter flow table mixed IPv4/IPv6 module"
--- a/net/netfilter/Kconfig
+++ b/net/netfilter/Kconfig
-@@ -223,7 +223,6 @@ config NF_CONNTRACK_FTP
+@@ -229,7 +229,6 @@ config NF_CONNTRACK_FTP
config NF_CONNTRACK_H323
tristate "H.323 protocol support"
depends on NETFILTER_ADVANCED
help
H.323 is a VoIP signalling protocol from ITU-T. As one of the most
-@@ -1046,7 +1045,6 @@ config NETFILTER_XT_TARGET_SECMARK
+@@ -1052,7 +1051,6 @@ config NETFILTER_XT_TARGET_SECMARK
config NETFILTER_XT_TARGET_TCPMSS
tristate '"TCPMSS" target support'
- tristate
+ tristate "Netfilter NFNETLINK interface"
- config NETFILTER_NETLINK_ACCT
- tristate "Netfilter NFACCT over NFNETLINK interface"
+ config NETFILTER_FAMILY_BRIDGE
+ bool
--- a/net/wireless/Kconfig
+++ b/net/wireless/Kconfig
@@ -1,5 +1,5 @@
--- a/net/ipv4/netfilter/Kconfig
+++ b/net/ipv4/netfilter/Kconfig
-@@ -75,8 +75,6 @@ config NF_TABLES_ARP
+@@ -76,8 +76,6 @@ config NF_TABLES_ARP
help
This option enables the ARP support for nf_tables.
config NF_FLOW_TABLE_IPV4
tristate "Netfilter flow table IPv4 module"
depends on NF_FLOW_TABLE
-@@ -85,6 +83,8 @@ config NF_FLOW_TABLE_IPV4
+@@ -86,6 +84,8 @@ config NF_FLOW_TABLE_IPV4
To compile it as a module, choose M here.
depends on !NF_CONNTRACK || NF_CONNTRACK
--- a/net/netfilter/Kconfig
+++ b/net/netfilter/Kconfig
-@@ -665,8 +665,6 @@ config NFT_FIB_NETDEV
+@@ -671,8 +671,6 @@ config NFT_FIB_NETDEV
endif # NF_TABLES_NETDEV
config NF_FLOW_TABLE_INET
tristate "Netfilter flow table mixed IPv4/IPv6 module"
depends on NF_FLOW_TABLE
-@@ -675,11 +673,12 @@ config NF_FLOW_TABLE_INET
+@@ -681,11 +679,12 @@ config NF_FLOW_TABLE_INET
To compile it as a module, choose M here.
help
This option adds the flow table core infrastructure.
-@@ -968,6 +967,15 @@ config NETFILTER_XT_TARGET_NOTRACK
+@@ -974,6 +973,15 @@ config NETFILTER_XT_TARGET_NOTRACK
depends on NETFILTER_ADVANCED
select NETFILTER_XT_TARGET_CT
#define NFTA_FLOWTABLE_MAX (__NFTA_FLOWTABLE_MAX - 1)
--- a/net/netfilter/Kconfig
+++ b/net/netfilter/Kconfig
-@@ -686,6 +686,15 @@ config NF_FLOW_TABLE
+@@ -692,6 +692,15 @@ config NF_FLOW_TABLE
To compile it as a module, choose M here.