Skip to content

Commit

Permalink
netfilter: bridge: move br_netfilter out of the core
Browse files Browse the repository at this point in the history
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 <[email protected]>
Acked-by: Florian Westphal <[email protected]>
  • Loading branch information
ummakynes committed Sep 26, 2014
1 parent 7276ca3 commit 34666d4
Show file tree
Hide file tree
Showing 16 changed files with 151 additions and 104 deletions.
2 changes: 1 addition & 1 deletion include/linux/netfilter_bridge.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
12 changes: 6 additions & 6 deletions include/linux/skbuff.h
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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

Expand Down Expand Up @@ -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))
Expand All @@ -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
Expand All @@ -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
Expand All @@ -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);
Expand Down
2 changes: 1 addition & 1 deletion include/net/neighbour.h
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
2 changes: 1 addition & 1 deletion include/net/netfilter/ipv4/nf_reject.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
2 changes: 1 addition & 1 deletion include/net/netfilter/ipv6/nf_reject.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
7 changes: 4 additions & 3 deletions net/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
5 changes: 3 additions & 2 deletions net/bridge/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
14 changes: 8 additions & 6 deletions net/bridge/br.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand All @@ -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:
Expand All @@ -196,20 +201,17 @@ static int __init br_init(void)
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();
}

Expand Down
4 changes: 2 additions & 2 deletions net/bridge/br_device.c
Original file line number Diff line number Diff line change
Expand Up @@ -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();
Expand Down Expand Up @@ -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
Expand Down
2 changes: 2 additions & 0 deletions net/bridge/br_forward.c
Original file line number Diff line number Diff line change
Expand Up @@ -49,13 +49,15 @@ 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)
{
return NF_HOOK(NFPROTO_BRIDGE, NF_BR_POST_ROUTING, skb, NULL, skb->dev,
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)
{
Expand Down
1 change: 1 addition & 0 deletions net/bridge/br_input.c
Original file line number Diff line number Diff line change
Expand Up @@ -140,6 +140,7 @@ int br_handle_frame_finish(struct sk_buff *skb)
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)
Expand Down
88 changes: 16 additions & 72 deletions net/bridge/br_netfilter.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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 <[email protected]>");
MODULE_AUTHOR("Bart De Schuymer <[email protected]>");
MODULE_DESCRIPTION("Linux ethernet netfilter firewall bridge");
2 changes: 1 addition & 1 deletion net/bridge/br_netlink.c
Original file line number Diff line number Diff line change
Expand Up @@ -602,7 +602,7 @@ int __init br_netlink_init(void)
return err;
}

void __exit br_netlink_fini(void)
void br_netlink_fini(void)
{
br_mdb_uninit();
rtnl_af_unregister(&br_af_ops);
Expand Down
Loading

0 comments on commit 34666d4

Please sign in to comment.