bridge: netfilter: unroll NF_HOOK helper in bridge input path
authorFlorian Westphal <fw@strlen.de>
Thu, 11 Apr 2019 14:36:41 +0000 (16:36 +0200)
committerPablo Neira Ayuso <pablo@netfilter.org>
Thu, 11 Apr 2019 23:47:39 +0000 (01:47 +0200)
Replace NF_HOOK() based invocation of the netfilter hooks with a private
copy of nf_hook_slow().

This copy has one difference: it can return the rx handler value expected
by the stack, i.e. RX_HANDLER_CONSUMED or RX_HANDLER_PASS.

This is needed by the next patch to invoke the ebtables
"broute" table via the standard netfilter hooks rather than the custom
"br_should_route_hook" indirection that is used now.

When the skb is to be "brouted", we must return RX_HANDLER_PASS from the
bridge rx input handler, but there is no way to indicate this via
NF_HOOK(), unless perhaps by some hack such as exposing bridge_cb in the
netfilter core or a percpu flag.

  text    data     bss     dec   filename
  3369      56       0    3425   net/bridge/br_input.o.before
  3458      40       0    3498   net/bridge/br_input.o.after

This allows removal of the "br_should_route_hook" in the next patch.

Signed-off-by: Florian Westphal <fw@strlen.de>
Acked-by: David S. Miller <davem@davemloft.net>
Acked-by: Nikolay Aleksandrov <nikolay@cumulusnetworks.com>
Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
include/net/netfilter/nf_queue.h
net/bridge/br_input.c
net/netfilter/core.c
net/netfilter/nf_internals.h
net/netfilter/nf_queue.c

index a50a69f5334c8647d129ac1edce909edfc442691..7239105d9d2e27bac3d0714710cee815d082d051 100644 (file)
@@ -119,4 +119,7 @@ nfqueue_hash(const struct sk_buff *skb, u16 queue, u16 queues_total, u8 family,
        return queue;
 }
 
+int nf_queue(struct sk_buff *skb, struct nf_hook_state *state,
+            const struct nf_hook_entries *entries, unsigned int index,
+            unsigned int verdict);
 #endif /* _NF_QUEUE_H */
index e2f93e5c72dac2118cbb0f8e1d1872cebbbc365f..4ac34fb5f94323988e52ddeac9cec7201bb157a2 100644 (file)
@@ -16,6 +16,7 @@
 #include <linux/netdevice.h>
 #include <linux/etherdevice.h>
 #include <linux/netfilter_bridge.h>
+#include <net/netfilter/nf_queue.h>
 #include <linux/neighbour.h>
 #include <net/arp.h>
 #include <linux/export.h>
@@ -206,6 +207,55 @@ static int br_handle_local_finish(struct net *net, struct sock *sk, struct sk_bu
        return 0;
 }
 
+static int nf_hook_bridge_pre(struct sk_buff *skb, struct sk_buff **pskb)
+{
+#ifdef CONFIG_NETFILTER_FAMILY_BRIDGE
+       struct nf_hook_entries *e = NULL;
+       struct nf_hook_state state;
+       unsigned int verdict, i;
+       struct net *net;
+       int ret;
+
+       net = dev_net(skb->dev);
+#ifdef HAVE_JUMP_LABEL
+       if (!static_key_false(&nf_hooks_needed[NFPROTO_BRIDGE][NF_BR_PRE_ROUTING]))
+               goto frame_finish;
+#endif
+
+       e = rcu_dereference(net->nf.hooks_bridge[NF_BR_PRE_ROUTING]);
+       if (!e)
+               goto frame_finish;
+
+       nf_hook_state_init(&state, NF_BR_PRE_ROUTING,
+                          NFPROTO_BRIDGE, skb->dev, NULL, NULL,
+                          net, br_handle_frame_finish);
+
+       for (i = 0; i < e->num_hook_entries; i++) {
+               verdict = nf_hook_entry_hookfn(&e->hooks[i], skb, &state);
+               switch (verdict & NF_VERDICT_MASK) {
+               case NF_ACCEPT:
+                       break;
+               case NF_DROP:
+                       kfree_skb(skb);
+                       return RX_HANDLER_CONSUMED;
+               case NF_QUEUE:
+                       ret = nf_queue(skb, &state, e, i, verdict);
+                       if (ret == 1)
+                               continue;
+                       return RX_HANDLER_CONSUMED;
+               default: /* STOLEN */
+                       return RX_HANDLER_CONSUMED;
+               }
+       }
+frame_finish:
+       net = dev_net(skb->dev);
+       br_handle_frame_finish(net, NULL, skb);
+#else
+       br_handle_frame_finish(dev_net(skb->dev), NULL, skb);
+#endif
+       return RX_HANDLER_CONSUMED;
+}
+
 /*
  * Return NULL if skb is handled
  * note: already called with rcu_read_lock
@@ -304,10 +354,7 @@ forward:
                if (ether_addr_equal(p->br->dev->dev_addr, dest))
                        skb->pkt_type = PACKET_HOST;
 
-               NF_HOOK(NFPROTO_BRIDGE, NF_BR_PRE_ROUTING,
-                       dev_net(skb->dev), NULL, skb, skb->dev, NULL,
-                       br_handle_frame_finish);
-               break;
+               return nf_hook_bridge_pre(skb, pskb);
        default:
 drop:
                kfree_skb(skb);
index 93aaec3a54ecdf701597457d2eddd6039275add0..71f06900473e9181b2d58b1daa9d42352c7382e6 100644 (file)
@@ -23,6 +23,7 @@
 #include <linux/mm.h>
 #include <linux/rcupdate.h>
 #include <net/net_namespace.h>
+#include <net/netfilter/nf_queue.h>
 #include <net/sock.h>
 
 #include "nf_internals.h"
index e15779fd58e3a5c8425c0f8ff5296f2bfdf338c8..d6c43902ebd766c1653befa5929659e5c4658606 100644 (file)
@@ -7,9 +7,6 @@
 #include <linux/netdevice.h>
 
 /* nf_queue.c */
-int nf_queue(struct sk_buff *skb, struct nf_hook_state *state,
-            const struct nf_hook_entries *entries, unsigned int index,
-            unsigned int verdict);
 void nf_queue_nf_hook_drop(struct net *net);
 
 /* nf_log.c */
index a36a77bae1d6d2c79459dc72826c066a442f60c3..9dc1d6e04946f59ea34377a14834bd711c4cd092 100644 (file)
@@ -240,6 +240,7 @@ int nf_queue(struct sk_buff *skb, struct nf_hook_state *state,
 
        return 0;
 }
+EXPORT_SYMBOL_GPL(nf_queue);
 
 static unsigned int nf_iterate(struct sk_buff *skb,
                               struct nf_hook_state *state,