netfilter: bridge: move br_netfilter out of the core
authorPablo Neira Ayuso <pablo@netfilter.org>
Thu, 18 Sep 2014 09:29:03 +0000 (11:29 +0200)
committerPablo Neira Ayuso <pablo@netfilter.org>
Fri, 26 Sep 2014 16:42:31 +0000 (18:42 +0200)
Jesper reported that br_netfilter always registers the hooks since
this is part of the bridge core. This harms performance for people that
don't need this.

This patch modularizes br_netfilter so it can be rmmod'ed, thus,
the hooks can be unregistered. I think the bridge netfilter should have
been a separated module since the beginning, Patrick agreed on that.

Note that this is breaking compatibility for users that expect that
bridge netfilter is going to be available after explicitly 'modprobe
bridge' or via automatic load through brctl.

However, the damage can be easily undone by modprobing br_netfilter.
The bridge core also spots a message to provide a clue to people that
didn't notice that this has been deprecated.

On top of that, the plan is that nftables will not rely on this software
layer, but integrate the connection tracking into the bridge layer to
enable stateful filtering and NAT, which is was bridge netfilter users
seem to require.

This patch still keeps the fake_dst_ops in the bridge core, since this
is required by when the bridge port is initialized. So we can safely
modprobe/rmmod br_netfilter anytime.

Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
Acked-by: Florian Westphal <fw@strlen.de>
16 files changed:
include/linux/netfilter_bridge.h
include/linux/skbuff.h
include/net/neighbour.h
include/net/netfilter/ipv4/nf_reject.h
include/net/netfilter/ipv6/nf_reject.h
net/Kconfig
net/bridge/Makefile
net/bridge/br.c
net/bridge/br_device.c
net/bridge/br_forward.c
net/bridge/br_input.c
net/bridge/br_netfilter.c
net/bridge/br_netlink.c
net/bridge/br_nf_core.c [new file with mode: 0644]
net/bridge/br_private.h
net/bridge/br_sysfs_br.c

index fe996d59de64268021945988089e0bac32a6e7c0..c755e4971fa3cc89e3000b669e6f3ba557f555b8 100644 (file)
@@ -15,7 +15,7 @@ enum nf_br_hook_priorities {
        NF_BR_PRI_LAST = INT_MAX,
 };
 
-#ifdef CONFIG_BRIDGE_NETFILTER
+#if IS_ENABLED(CONFIG_BRIDGE_NETFILTER)
 
 #define BRNF_PKT_TYPE                  0x01
 #define BRNF_BRIDGED_DNAT              0x02
index 07c9fdd0c1266a72f3c1a4536586b5109cbe4f27..c4ff43f845737feac0995a3ca0e042c6ff49ecc5 100644 (file)
@@ -156,7 +156,7 @@ struct nf_conntrack {
 };
 #endif
 
-#ifdef CONFIG_BRIDGE_NETFILTER
+#if IS_ENABLED(CONFIG_BRIDGE_NETFILTER)
 struct nf_bridge_info {
        atomic_t                use;
        unsigned int            mask;
@@ -560,7 +560,7 @@ struct sk_buff {
 #if defined(CONFIG_NF_CONNTRACK) || defined(CONFIG_NF_CONNTRACK_MODULE)
        struct nf_conntrack     *nfct;
 #endif
-#ifdef CONFIG_BRIDGE_NETFILTER
+#if IS_ENABLED(CONFIG_BRIDGE_NETFILTER)
        struct nf_bridge_info   *nf_bridge;
 #endif
 
@@ -2977,7 +2977,7 @@ static inline void nf_conntrack_get(struct nf_conntrack *nfct)
                atomic_inc(&nfct->use);
 }
 #endif
-#ifdef CONFIG_BRIDGE_NETFILTER
+#if IS_ENABLED(CONFIG_BRIDGE_NETFILTER)
 static inline void nf_bridge_put(struct nf_bridge_info *nf_bridge)
 {
        if (nf_bridge && atomic_dec_and_test(&nf_bridge->use))
@@ -2995,7 +2995,7 @@ static inline void nf_reset(struct sk_buff *skb)
        nf_conntrack_put(skb->nfct);
        skb->nfct = NULL;
 #endif
-#ifdef CONFIG_BRIDGE_NETFILTER
+#if IS_ENABLED(CONFIG_BRIDGE_NETFILTER)
        nf_bridge_put(skb->nf_bridge);
        skb->nf_bridge = NULL;
 #endif
@@ -3016,7 +3016,7 @@ static inline void __nf_copy(struct sk_buff *dst, const struct sk_buff *src)
        nf_conntrack_get(src->nfct);
        dst->nfctinfo = src->nfctinfo;
 #endif
-#ifdef CONFIG_BRIDGE_NETFILTER
+#if IS_ENABLED(CONFIG_BRIDGE_NETFILTER)
        dst->nf_bridge  = src->nf_bridge;
        nf_bridge_get(src->nf_bridge);
 #endif
@@ -3030,7 +3030,7 @@ static inline void nf_copy(struct sk_buff *dst, const struct sk_buff *src)
 #if defined(CONFIG_NF_CONNTRACK) || defined(CONFIG_NF_CONNTRACK_MODULE)
        nf_conntrack_put(dst->nfct);
 #endif
-#ifdef CONFIG_BRIDGE_NETFILTER
+#if IS_ENABLED(CONFIG_BRIDGE_NETFILTER)
        nf_bridge_put(dst->nf_bridge);
 #endif
        __nf_copy(dst, src);
index 47f425464f847fd827719ac5da99cf2749824a14..f60558d0254ca1a482ab4eb2924ecbaf8d6c169c 100644 (file)
@@ -373,7 +373,7 @@ static inline int neigh_event_send(struct neighbour *neigh, struct sk_buff *skb)
        return 0;
 }
 
-#ifdef CONFIG_BRIDGE_NETFILTER
+#if IS_ENABLED(CONFIG_BRIDGE_NETFILTER)
 static inline int neigh_hh_bridge(struct hh_cache *hh, struct sk_buff *skb)
 {
        unsigned int seq, hh_alen;
index 931fbf812171afa37c53f258213a77759cd43edb..f713b5a31d622b7e29542d40bbe6a099b8a96720 100644 (file)
@@ -98,7 +98,7 @@ static void nf_send_reset(struct sk_buff *oldskb, int hook)
 
        nf_ct_attach(nskb, oldskb);
 
-#ifdef CONFIG_BRIDGE_NETFILTER
+#if IS_ENABLED(CONFIG_BRIDGE_NETFILTER)
        /* If we use ip_local_out for bridged traffic, the MAC source on
         * the RST will be ours, instead of the destination's.  This confuses
         * some routers/firewalls, and they drop the packet.  So we need to
index 710d17ed70b4c0ad6f2d28db779e12d976d10387..7a10cfcd8e33fd73e47e14877f31b1d5baf65b1f 100644 (file)
@@ -147,7 +147,7 @@ static void nf_send_reset6(struct net *net, struct sk_buff *oldskb, int hook)
 
        nf_ct_attach(nskb, oldskb);
 
-#ifdef CONFIG_BRIDGE_NETFILTER
+#if IS_ENABLED(CONFIG_BRIDGE_NETFILTER)
        /* If we use ip6_local_out for bridged traffic, the MAC source on
         * the RST will be ours, instead of the destination's.  This confuses
         * some routers/firewalls, and they drop the packet.  So we need to
index 4051fdfa43677ccf7142244c97e2e383515e77e8..dc5d700d05e70cc25aee44e511fc0a9890d113bd 100644 (file)
@@ -176,10 +176,11 @@ config NETFILTER_ADVANCED
          If unsure, say Y.
 
 config BRIDGE_NETFILTER
-       bool "Bridged IP/ARP packets filtering"
-       depends on BRIDGE && NETFILTER && INET
+       tristate "Bridged IP/ARP packets filtering"
+       depends on (BRIDGE || BRIDGE=n)
+       depends on NETFILTER && INET
        depends on NETFILTER_ADVANCED
-       default y
+       default m
        ---help---
          Enabling this option will let arptables resp. iptables see bridged
          ARP resp. IP traffic. If you want a bridging firewall, you probably
index 8590b942bffa62a11d23c4e7a0666c9564d310c1..5e3eac5dc8b9006a6dc8d146b017380debc41bc2 100644 (file)
@@ -6,11 +6,12 @@ obj-$(CONFIG_BRIDGE) += bridge.o
 
 bridge-y       := br.o br_device.o br_fdb.o br_forward.o br_if.o br_input.o \
                        br_ioctl.o br_stp.o br_stp_bpdu.o \
-                       br_stp_if.o br_stp_timer.o br_netlink.o
+                       br_stp_if.o br_stp_timer.o br_netlink.o \
+                       br_nf_core.o
 
 bridge-$(CONFIG_SYSFS) += br_sysfs_if.o br_sysfs_br.o
 
-bridge-$(CONFIG_BRIDGE_NETFILTER) += br_netfilter.o
+obj-$(CONFIG_BRIDGE_NETFILTER) += br_netfilter.o
 
 bridge-$(CONFIG_BRIDGE_IGMP_SNOOPING) += br_multicast.o br_mdb.o
 
index 1a755a1e54101d924e88ea240a82c154dcb7bbe5..44425aff7cba15f93c659fd0502a7b650d050b5e 100644 (file)
@@ -161,7 +161,7 @@ static int __init br_init(void)
        if (err)
                goto err_out1;
 
-       err = br_netfilter_init();
+       err = br_nf_core_init();
        if (err)
                goto err_out2;
 
@@ -179,11 +179,16 @@ static int __init br_init(void)
        br_fdb_test_addr_hook = br_fdb_test_addr;
 #endif
 
+       pr_info("bridge: automatic filtering via arp/ip/ip6tables has been "
+               "deprecated. Update your scripts to load br_netfilter if you "
+               "need this.\n");
+
        return 0;
+
 err_out4:
        unregister_netdevice_notifier(&br_device_notifier);
 err_out3:
-       br_netfilter_fini();
+       br_nf_core_fini();
 err_out2:
        unregister_pernet_subsys(&br_net_ops);
 err_out1:
@@ -196,20 +201,17 @@ err_out:
 static void __exit br_deinit(void)
 {
        stp_proto_unregister(&br_stp_proto);
-
        br_netlink_fini();
        unregister_netdevice_notifier(&br_device_notifier);
        brioctl_set(NULL);
-
        unregister_pernet_subsys(&br_net_ops);
 
        rcu_barrier(); /* Wait for completion of call_rcu()'s */
 
-       br_netfilter_fini();
+       br_nf_core_fini();
 #if IS_ENABLED(CONFIG_ATM_LANE)
        br_fdb_test_addr_hook = NULL;
 #endif
-
        br_fdb_fini();
 }
 
index 568cccd39a3d8a25716ef13e14b2f4e4850c1db5..659cac15c0dfd7a76176ffbfb8e88a0b1181e04a 100644 (file)
@@ -36,7 +36,7 @@ netdev_tx_t br_dev_xmit(struct sk_buff *skb, struct net_device *dev)
        u16 vid = 0;
 
        rcu_read_lock();
-#ifdef CONFIG_BRIDGE_NETFILTER
+#if IS_ENABLED(CONFIG_BRIDGE_NETFILTER)
        if (skb->nf_bridge && (skb->nf_bridge->mask & BRNF_BRIDGED_DNAT)) {
                br_nf_pre_routing_finish_bridge_slow(skb);
                rcu_read_unlock();
@@ -167,7 +167,7 @@ static int br_change_mtu(struct net_device *dev, int new_mtu)
 
        dev->mtu = new_mtu;
 
-#ifdef CONFIG_BRIDGE_NETFILTER
+#if IS_ENABLED(CONFIG_BRIDGE_NETFILTER)
        /* remember the MTU in the rtable for PMTU */
        dst_metric_set(&br->fake_rtable.dst, RTAX_MTU, new_mtu);
 #endif
index 056b67b0e2778fdce7bd80a2bf4ede2d552c0e1d..992ec49a96aa7e289bd3c74bca9a606762c63614 100644 (file)
@@ -49,6 +49,7 @@ int br_dev_queue_push_xmit(struct sk_buff *skb)
 
        return 0;
 }
+EXPORT_SYMBOL_GPL(br_dev_queue_push_xmit);
 
 int br_forward_finish(struct sk_buff *skb)
 {
@@ -56,6 +57,7 @@ int br_forward_finish(struct sk_buff *skb)
                       br_dev_queue_push_xmit);
 
 }
+EXPORT_SYMBOL_GPL(br_forward_finish);
 
 static void __br_deliver(const struct net_bridge_port *to, struct sk_buff *skb)
 {
index 366c43649079d9bdef66063af2ab848965ca8197..6fd5522df696ce744558a4db82803c34394eed6f 100644 (file)
@@ -140,6 +140,7 @@ drop:
        kfree_skb(skb);
        goto out;
 }
+EXPORT_SYMBOL_GPL(br_handle_frame_finish);
 
 /* note: already called with rcu_read_lock */
 static int br_handle_local_finish(struct sk_buff *skb)
index 61929a7cd815e7e8c2a9fff0c8a2d534022778bc..97e43937aaca2b090bf883428ea3cfdce94103c2 100644 (file)
@@ -111,66 +111,6 @@ static inline __be16 pppoe_proto(const struct sk_buff *skb)
         pppoe_proto(skb) == htons(PPP_IPV6) && \
         brnf_filter_pppoe_tagged)
 
-static void fake_update_pmtu(struct dst_entry *dst, struct sock *sk,
-                            struct sk_buff *skb, u32 mtu)
-{
-}
-
-static void fake_redirect(struct dst_entry *dst, struct sock *sk,
-                         struct sk_buff *skb)
-{
-}
-
-static u32 *fake_cow_metrics(struct dst_entry *dst, unsigned long old)
-{
-       return NULL;
-}
-
-static struct neighbour *fake_neigh_lookup(const struct dst_entry *dst,
-                                          struct sk_buff *skb,
-                                          const void *daddr)
-{
-       return NULL;
-}
-
-static unsigned int fake_mtu(const struct dst_entry *dst)
-{
-       return dst->dev->mtu;
-}
-
-static struct dst_ops fake_dst_ops = {
-       .family =               AF_INET,
-       .protocol =             cpu_to_be16(ETH_P_IP),
-       .update_pmtu =          fake_update_pmtu,
-       .redirect =             fake_redirect,
-       .cow_metrics =          fake_cow_metrics,
-       .neigh_lookup =         fake_neigh_lookup,
-       .mtu =                  fake_mtu,
-};
-
-/*
- * Initialize bogus route table used to keep netfilter happy.
- * Currently, we fill in the PMTU entry because netfilter
- * refragmentation needs it, and the rt_flags entry because
- * ipt_REJECT needs it.  Future netfilter modules might
- * require us to fill additional fields.
- */
-static const u32 br_dst_default_metrics[RTAX_MAX] = {
-       [RTAX_MTU - 1] = 1500,
-};
-
-void br_netfilter_rtable_init(struct net_bridge *br)
-{
-       struct rtable *rt = &br->fake_rtable;
-
-       atomic_set(&rt->dst.__refcnt, 1);
-       rt->dst.dev = br->dev;
-       rt->dst.path = &rt->dst;
-       dst_init_metrics(&rt->dst, br_dst_default_metrics, true);
-       rt->dst.flags   = DST_NOXFRM | DST_FAKE_RTABLE;
-       rt->dst.ops = &fake_dst_ops;
-}
-
 static inline struct rtable *bridge_parent_rtable(const struct net_device *dev)
 {
        struct net_bridge_port *port;
@@ -1031,38 +971,42 @@ static struct ctl_table brnf_table[] = {
 };
 #endif
 
-int __init br_netfilter_init(void)
+static int __init br_netfilter_init(void)
 {
        int ret;
 
-       ret = dst_entries_init(&fake_dst_ops);
+       ret = nf_register_hooks(br_nf_ops, ARRAY_SIZE(br_nf_ops));
        if (ret < 0)
                return ret;
 
-       ret = nf_register_hooks(br_nf_ops, ARRAY_SIZE(br_nf_ops));
-       if (ret < 0) {
-               dst_entries_destroy(&fake_dst_ops);
-               return ret;
-       }
 #ifdef CONFIG_SYSCTL
        brnf_sysctl_header = register_net_sysctl(&init_net, "net/bridge", brnf_table);
        if (brnf_sysctl_header == NULL) {
                printk(KERN_WARNING
                       "br_netfilter: can't register to sysctl.\n");
-               nf_unregister_hooks(br_nf_ops, ARRAY_SIZE(br_nf_ops));
-               dst_entries_destroy(&fake_dst_ops);
-               return -ENOMEM;
+               ret = -ENOMEM;
+               goto err1;
        }
 #endif
        printk(KERN_NOTICE "Bridge firewalling registered\n");
        return 0;
+err1:
+       nf_unregister_hooks(br_nf_ops, ARRAY_SIZE(br_nf_ops));
+       return ret;
 }
 
-void br_netfilter_fini(void)
+static void __exit br_netfilter_fini(void)
 {
        nf_unregister_hooks(br_nf_ops, ARRAY_SIZE(br_nf_ops));
 #ifdef CONFIG_SYSCTL
        unregister_net_sysctl_table(brnf_sysctl_header);
 #endif
-       dst_entries_destroy(&fake_dst_ops);
 }
+
+module_init(br_netfilter_init);
+module_exit(br_netfilter_fini);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Lennert Buytenhek <buytenh@gnu.org>");
+MODULE_AUTHOR("Bart De Schuymer <bdschuym@pandora.be>");
+MODULE_DESCRIPTION("Linux ethernet netfilter firewall bridge");
index 90a91e137acc3112004a4108abb68371919fa2be..0fa66b83685f44b824ec726c2cfae7aca4d11475 100644 (file)
@@ -602,7 +602,7 @@ out_af:
        return err;
 }
 
-void __exit br_netlink_fini(void)
+void br_netlink_fini(void)
 {
        br_mdb_uninit();
        rtnl_af_unregister(&br_af_ops);
diff --git a/net/bridge/br_nf_core.c b/net/bridge/br_nf_core.c
new file mode 100644 (file)
index 0000000..387cb3b
--- /dev/null
@@ -0,0 +1,96 @@
+/*
+ *     Handle firewalling core
+ *     Linux ethernet bridge
+ *
+ *     Authors:
+ *     Lennert Buytenhek               <buytenh@gnu.org>
+ *     Bart De Schuymer                <bdschuym@pandora.be>
+ *
+ *     This program is free software; you can redistribute it and/or
+ *     modify it under the terms of the GNU General Public License
+ *     as published by the Free Software Foundation; either version
+ *     2 of the License, or (at your option) any later version.
+ *
+ *     Lennert dedicates this file to Kerstin Wurdinger.
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/in_route.h>
+#include <linux/inetdevice.h>
+#include <net/route.h>
+
+#include "br_private.h"
+#ifdef CONFIG_SYSCTL
+#include <linux/sysctl.h>
+#endif
+
+static void fake_update_pmtu(struct dst_entry *dst, struct sock *sk,
+                            struct sk_buff *skb, u32 mtu)
+{
+}
+
+static void fake_redirect(struct dst_entry *dst, struct sock *sk,
+                         struct sk_buff *skb)
+{
+}
+
+static u32 *fake_cow_metrics(struct dst_entry *dst, unsigned long old)
+{
+       return NULL;
+}
+
+static struct neighbour *fake_neigh_lookup(const struct dst_entry *dst,
+                                          struct sk_buff *skb,
+                                          const void *daddr)
+{
+       return NULL;
+}
+
+static unsigned int fake_mtu(const struct dst_entry *dst)
+{
+       return dst->dev->mtu;
+}
+
+static struct dst_ops fake_dst_ops = {
+       .family         = AF_INET,
+       .protocol       = cpu_to_be16(ETH_P_IP),
+       .update_pmtu    = fake_update_pmtu,
+       .redirect       = fake_redirect,
+       .cow_metrics    = fake_cow_metrics,
+       .neigh_lookup   = fake_neigh_lookup,
+       .mtu            = fake_mtu,
+};
+
+/*
+ * Initialize bogus route table used to keep netfilter happy.
+ * Currently, we fill in the PMTU entry because netfilter
+ * refragmentation needs it, and the rt_flags entry because
+ * ipt_REJECT needs it.  Future netfilter modules might
+ * require us to fill additional fields.
+ */
+static const u32 br_dst_default_metrics[RTAX_MAX] = {
+       [RTAX_MTU - 1] = 1500,
+};
+
+void br_netfilter_rtable_init(struct net_bridge *br)
+{
+       struct rtable *rt = &br->fake_rtable;
+
+       atomic_set(&rt->dst.__refcnt, 1);
+       rt->dst.dev = br->dev;
+       rt->dst.path = &rt->dst;
+       dst_init_metrics(&rt->dst, br_dst_default_metrics, true);
+       rt->dst.flags   = DST_NOXFRM | DST_FAKE_RTABLE;
+       rt->dst.ops = &fake_dst_ops;
+}
+
+int __init br_nf_core_init(void)
+{
+       return dst_entries_init(&fake_dst_ops);
+}
+
+void br_nf_core_fini(void)
+{
+       dst_entries_destroy(&fake_dst_ops);
+}
index 62a7fa2e356921d27729d365627d490dbf8a6126..d304d752c091181836e6fd7e040b1b9d3179d5b4 100644 (file)
@@ -221,7 +221,7 @@ struct net_bridge
        struct pcpu_sw_netstats         __percpu *stats;
        spinlock_t                      hash_lock;
        struct hlist_head               hash[BR_HASH_SIZE];
-#ifdef CONFIG_BRIDGE_NETFILTER
+#if IS_ENABLED(CONFIG_BRIDGE_NETFILTER)
        struct rtable                   fake_rtable;
        bool                            nf_call_iptables;
        bool                            nf_call_ip6tables;
@@ -751,13 +751,13 @@ static inline int br_vlan_enabled(struct net_bridge *br)
 #endif
 
 /* br_netfilter.c */
-#ifdef CONFIG_BRIDGE_NETFILTER
-int br_netfilter_init(void);
-void br_netfilter_fini(void);
+#if IS_ENABLED(CONFIG_BRIDGE_NETFILTER)
+int br_nf_core_init(void);
+void br_nf_core_fini(void);
 void br_netfilter_rtable_init(struct net_bridge *);
 #else
-#define br_netfilter_init()    (0)
-#define br_netfilter_fini()    do { } while (0)
+static inline int br_nf_core_init(void) { return 0; }
+static inline void br_nf_core_fini(void) {}
 #define br_netfilter_rtable_init(x)
 #endif
 
index c9e2572b15f400f9183606922a87f024ffe6570f..cb431c6016ee18935ddc20cb818391a98646c49e 100644 (file)
@@ -629,7 +629,7 @@ static ssize_t multicast_startup_query_interval_store(
 }
 static DEVICE_ATTR_RW(multicast_startup_query_interval);
 #endif
-#ifdef CONFIG_BRIDGE_NETFILTER
+#if IS_ENABLED(CONFIG_BRIDGE_NETFILTER)
 static ssize_t nf_call_iptables_show(
        struct device *d, struct device_attribute *attr, char *buf)
 {
@@ -763,7 +763,7 @@ static struct attribute *bridge_attrs[] = {
        &dev_attr_multicast_query_response_interval.attr,
        &dev_attr_multicast_startup_query_interval.attr,
 #endif
-#ifdef CONFIG_BRIDGE_NETFILTER
+#if IS_ENABLED(CONFIG_BRIDGE_NETFILTER)
        &dev_attr_nf_call_iptables.attr,
        &dev_attr_nf_call_ip6tables.attr,
        &dev_attr_nf_call_arptables.attr,