summaryrefslogtreecommitdiffstats
path: root/net/bridge
diff options
context:
space:
mode:
Diffstat (limited to 'net/bridge')
-rw-r--r--net/bridge/br.c12
-rw-r--r--net/bridge/br_device.c22
-rw-r--r--net/bridge/br_fdb.c46
-rw-r--r--net/bridge/br_forward.c2
-rw-r--r--net/bridge/br_if.c38
-rw-r--r--net/bridge/br_input.c51
-rw-r--r--net/bridge/br_ioctl.c8
-rw-r--r--net/bridge/br_netfilter.c144
-rw-r--r--net/bridge/br_netlink.c24
-rw-r--r--net/bridge/br_notify.c34
-rw-r--r--net/bridge/br_private.h26
-rw-r--r--net/bridge/br_stp.c10
-rw-r--r--net/bridge/br_stp_bpdu.c19
-rw-r--r--net/bridge/br_stp_if.c68
-rw-r--r--net/bridge/br_sysfs_br.c20
-rw-r--r--net/bridge/br_sysfs_if.c8
-rw-r--r--net/bridge/netfilter/ebt_arp.c48
-rw-r--r--net/bridge/netfilter/ebt_log.c12
-rw-r--r--net/bridge/netfilter/ebt_ulog.c12
-rw-r--r--net/bridge/netfilter/ebtables.c9
20 files changed, 405 insertions, 208 deletions
diff --git a/net/bridge/br.c b/net/bridge/br.c
index 2994387999a..848b8fa8bed 100644
--- a/net/bridge/br.c
+++ b/net/bridge/br.c
@@ -37,7 +37,9 @@ static int __init br_init(void)
return -EADDRINUSE;
}
- br_fdb_init();
+ err = br_fdb_init();
+ if (err)
+ goto err_out1;
err = br_netfilter_init();
if (err)
@@ -47,7 +49,10 @@ static int __init br_init(void)
if (err)
goto err_out2;
- br_netlink_init();
+ err = br_netlink_init();
+ if (err)
+ goto err_out3;
+
brioctl_set(br_ioctl_deviceless_stub);
br_handle_frame_hook = br_handle_frame;
@@ -55,7 +60,8 @@ static int __init br_init(void)
br_fdb_put_hook = br_fdb_put;
return 0;
-
+err_out3:
+ unregister_netdevice_notifier(&br_device_notifier);
err_out2:
br_netfilter_fini();
err_out1:
diff --git a/net/bridge/br_device.c b/net/bridge/br_device.c
index 905a39c33a1..5e1892d8d87 100644
--- a/net/bridge/br_device.c
+++ b/net/bridge/br_device.c
@@ -37,7 +37,7 @@ int br_dev_xmit(struct sk_buff *skb, struct net_device *dev)
br->statistics.tx_packets++;
br->statistics.tx_bytes += skb->len;
- skb->mac.raw = skb->data;
+ skb_reset_mac_header(skb);
skb_pull(skb, ETH_HLEN);
if (dest[0] & 1)
@@ -83,27 +83,21 @@ static int br_change_mtu(struct net_device *dev, int new_mtu)
return 0;
}
-/* Allow setting mac address of pseudo-bridge to be same as
- * any of the bound interfaces
- */
+/* Allow setting mac address to any valid ethernet address. */
static int br_set_mac_address(struct net_device *dev, void *p)
{
struct net_bridge *br = netdev_priv(dev);
struct sockaddr *addr = p;
- struct net_bridge_port *port;
- int err = -EADDRNOTAVAIL;
+
+ if (!is_valid_ether_addr(addr->sa_data))
+ return -EINVAL;
spin_lock_bh(&br->lock);
- list_for_each_entry(port, &br->port_list, list) {
- if (!compare_ether_addr(port->dev->dev_addr, addr->sa_data)) {
- br_stp_change_bridge_id(br, addr->sa_data);
- err = 0;
- break;
- }
- }
+ memcpy(dev->dev_addr, addr->sa_data, ETH_ALEN);
+ br_stp_change_bridge_id(br, addr->sa_data);
spin_unlock_bh(&br->lock);
- return err;
+ return 0;
}
static void br_getinfo(struct net_device *dev, struct ethtool_drvinfo *info)
diff --git a/net/bridge/br_fdb.c b/net/bridge/br_fdb.c
index def2e403f93..91b017016d5 100644
--- a/net/bridge/br_fdb.c
+++ b/net/bridge/br_fdb.c
@@ -20,19 +20,28 @@
#include <linux/netdevice.h>
#include <linux/etherdevice.h>
#include <linux/jhash.h>
+#include <linux/random.h>
#include <asm/atomic.h>
+#include <asm/unaligned.h>
#include "br_private.h"
static struct kmem_cache *br_fdb_cache __read_mostly;
static int fdb_insert(struct net_bridge *br, struct net_bridge_port *source,
const unsigned char *addr);
-void __init br_fdb_init(void)
+static u32 fdb_salt __read_mostly;
+
+int __init br_fdb_init(void)
{
br_fdb_cache = kmem_cache_create("bridge_fdb_cache",
sizeof(struct net_bridge_fdb_entry),
0,
SLAB_HWCACHE_ALIGN, NULL, NULL);
+ if (!br_fdb_cache)
+ return -ENOMEM;
+
+ get_random_bytes(&fdb_salt, sizeof(fdb_salt));
+ return 0;
}
void __exit br_fdb_fini(void)
@@ -44,24 +53,26 @@ void __exit br_fdb_fini(void)
/* if topology_changing then use forward_delay (default 15 sec)
* otherwise keep longer (default 5 minutes)
*/
-static __inline__ unsigned long hold_time(const struct net_bridge *br)
+static inline unsigned long hold_time(const struct net_bridge *br)
{
return br->topology_change ? br->forward_delay : br->ageing_time;
}
-static __inline__ int has_expired(const struct net_bridge *br,
+static inline int has_expired(const struct net_bridge *br,
const struct net_bridge_fdb_entry *fdb)
{
return !fdb->is_static
&& time_before_eq(fdb->ageing_timer + hold_time(br), jiffies);
}
-static __inline__ int br_mac_hash(const unsigned char *mac)
+static inline int br_mac_hash(const unsigned char *mac)
{
- return jhash(mac, ETH_ALEN, 0) & (BR_HASH_SIZE - 1);
+ /* use 1 byte of OUI cnd 3 bytes of NIC */
+ u32 key = get_unaligned((u32 *)(mac + 2));
+ return jhash_1word(key, fdb_salt) & (BR_HASH_SIZE - 1);
}
-static __inline__ void fdb_delete(struct net_bridge_fdb_entry *f)
+static inline void fdb_delete(struct net_bridge_fdb_entry *f)
{
hlist_del_rcu(&f->hlist);
br_fdb_put(f);
@@ -128,7 +139,26 @@ void br_fdb_cleanup(unsigned long _data)
mod_timer(&br->gc_timer, jiffies + HZ/10);
}
+/* Completely flush all dynamic entries in forwarding database.*/
+void br_fdb_flush(struct net_bridge *br)
+{
+ int i;
+ spin_lock_bh(&br->hash_lock);
+ for (i = 0; i < BR_HASH_SIZE; i++) {
+ struct net_bridge_fdb_entry *f;
+ struct hlist_node *h, *n;
+ hlist_for_each_entry_safe(f, h, n, &br->hash[i], hlist) {
+ if (!f->is_static)
+ fdb_delete(f);
+ }
+ }
+ spin_unlock_bh(&br->hash_lock);
+}
+
+/* Flush all entries refering to a specific port.
+ * if do_all is set also flush static entries
+ */
void br_fdb_delete_by_port(struct net_bridge *br,
const struct net_bridge_port *p,
int do_all)
@@ -197,8 +227,8 @@ struct net_bridge_fdb_entry *br_fdb_get(struct net_bridge *br,
rcu_read_lock();
fdb = __br_fdb_get(br, addr);
- if (fdb)
- atomic_inc(&fdb->use_count);
+ if (fdb && !atomic_inc_not_zero(&fdb->use_count))
+ fdb = NULL;
rcu_read_unlock();
return fdb;
}
diff --git a/net/bridge/br_forward.c b/net/bridge/br_forward.c
index 3e45c1a1aa9..ada7f495445 100644
--- a/net/bridge/br_forward.c
+++ b/net/bridge/br_forward.c
@@ -71,7 +71,7 @@ static void __br_forward(const struct net_bridge_port *to, struct sk_buff *skb)
indev = skb->dev;
skb->dev = to->dev;
- skb->ip_summed = CHECKSUM_NONE;
+ skb_forward_csum(skb);
NF_HOOK(PF_BRIDGE, NF_BR_FORWARD, skb, indev, skb->dev,
br_forward_finish);
diff --git a/net/bridge/br_if.c b/net/bridge/br_if.c
index aff6a779c9c..690573bbf01 100644
--- a/net/bridge/br_if.c
+++ b/net/bridge/br_if.c
@@ -77,26 +77,15 @@ static int port_cost(struct net_device *dev)
* Called from work queue to allow for calling functions that
* might sleep (such as speed check), and to debounce.
*/
-static void port_carrier_check(struct work_struct *work)
+void br_port_carrier_check(struct net_bridge_port *p)
{
- struct net_bridge_port *p;
- struct net_device *dev;
- struct net_bridge *br;
-
- dev = container_of(work, struct net_bridge_port,
- carrier_check.work)->dev;
- work_release(work);
-
- rtnl_lock();
- p = dev->br_port;
- if (!p)
- goto done;
- br = p->br;
+ struct net_device *dev = p->dev;
+ struct net_bridge *br = p->br;
if (netif_carrier_ok(dev))
p->path_cost = port_cost(dev);
- if (br->dev->flags & IFF_UP) {
+ if (netif_running(br->dev)) {
spin_lock_bh(&br->lock);
if (netif_carrier_ok(dev)) {
if (p->state == BR_STATE_DISABLED)
@@ -107,9 +96,6 @@ static void port_carrier_check(struct work_struct *work)
}
spin_unlock_bh(&br->lock);
}
-done:
- dev_put(dev);
- rtnl_unlock();
}
static void release_nbp(struct kobject *kobj)
@@ -162,13 +148,12 @@ static void del_nbp(struct net_bridge_port *p)
dev_set_promiscuity(dev, -1);
- if (cancel_delayed_work(&p->carrier_check))
- dev_put(dev);
-
spin_lock_bh(&br->lock);
br_stp_disable_port(p);
spin_unlock_bh(&br->lock);
+ br_ifinfo_notify(RTM_DELLINK, p);
+
br_fdb_delete_by_port(br, p, 1);
list_del_rcu(&p->list);
@@ -220,7 +205,7 @@ static struct net_device *new_bridge_dev(const char *name)
memcpy(br->group_addr, br_group_address, ETH_ALEN);
br->feature_mask = dev->features;
- br->stp_enabled = 0;
+ br->stp_enabled = BR_NO_STP;
br->designated_root = br->bridge_id;
br->root_path_cost = 0;
br->root_port = 0;
@@ -282,7 +267,6 @@ static struct net_bridge_port *new_nbp(struct net_bridge *br,
p->port_no = index;
br_init_port(p);
p->state = BR_STATE_DISABLED;
- INIT_DELAYED_WORK_NAR(&p->carrier_check, port_carrier_check);
br_stp_port_timer_init(p);
kobject_init(&p->kobj);
@@ -446,12 +430,16 @@ int br_add_if(struct net_bridge *br, struct net_device *dev)
spin_lock_bh(&br->lock);
br_stp_recalculate_bridge_id(br);
br_features_recompute(br);
- if (schedule_delayed_work(&p->carrier_check, BR_PORT_DEBOUNCE))
- dev_hold(dev);
+ if ((dev->flags & IFF_UP) && netif_carrier_ok(dev) &&
+ (br->dev->flags & IFF_UP))
+ br_stp_enable_port(p);
spin_unlock_bh(&br->lock);
+ br_ifinfo_notify(RTM_NEWLINK, p);
+
dev_set_mtu(br->dev, br_min_mtu(br));
+
kobject_uevent(&p->kobj, KOBJ_ADD);
return 0;
diff --git a/net/bridge/br_input.c b/net/bridge/br_input.c
index 35b94f9a1ac..420bbb9955e 100644
--- a/net/bridge/br_input.c
+++ b/net/bridge/br_input.c
@@ -112,46 +112,59 @@ static int br_handle_local_finish(struct sk_buff *skb)
*/
static inline int is_link_local(const unsigned char *dest)
{
- return memcmp(dest, br_group_address, 5) == 0 && (dest[5] & 0xf0) == 0;
+ const u16 *a = (const u16 *) dest;
+ static const u16 *const b = (const u16 *const ) br_group_address;
+ static const u16 m = __constant_cpu_to_be16(0xfff0);
+
+ return ((a[0] ^ b[0]) | (a[1] ^ b[1]) | ((a[2] ^ b[2]) & m)) == 0;
}
/*
* Called via br_handle_frame_hook.
- * Return 0 if *pskb should be processed furthur
- * 1 if *pskb is handled
+ * Return NULL if skb is handled
* note: already called with rcu_read_lock (preempt_disabled)
*/
-int br_handle_frame(struct net_bridge_port *p, struct sk_buff **pskb)
+struct sk_buff *br_handle_frame(struct net_bridge_port *p, struct sk_buff *skb)
{
- struct sk_buff *skb = *pskb;
const unsigned char *dest = eth_hdr(skb)->h_dest;
if (!is_valid_ether_addr(eth_hdr(skb)->h_source))
- goto err;
+ goto drop;
if (unlikely(is_link_local(dest))) {
- skb->pkt_type = PACKET_HOST;
- return NF_HOOK(PF_BRIDGE, NF_BR_LOCAL_IN, skb, skb->dev,
- NULL, br_handle_local_finish) != 0;
+ /* Pause frames shouldn't be passed up by driver anyway */
+ if (skb->protocol == htons(ETH_P_PAUSE))
+ goto drop;
+
+ /* Process STP BPDU's through normal netif_receive_skb() path */
+ if (p->br->stp_enabled != BR_NO_STP) {
+ if (NF_HOOK(PF_BRIDGE, NF_BR_LOCAL_IN, skb, skb->dev,
+ NULL, br_handle_local_finish))
+ return NULL;
+ else
+ return skb;
+ }
}
- if (p->state == BR_STATE_FORWARDING || p->state == BR_STATE_LEARNING) {
+ switch (p->state) {
+ case BR_STATE_FORWARDING:
+
if (br_should_route_hook) {
- if (br_should_route_hook(pskb))
- return 0;
- skb = *pskb;
+ if (br_should_route_hook(&skb))
+ return skb;
dest = eth_hdr(skb)->h_dest;
}
-
+ /* fall through */
+ case BR_STATE_LEARNING:
if (!compare_ether_addr(p->br->dev->dev_addr, dest))
skb->pkt_type = PACKET_HOST;
NF_HOOK(PF_BRIDGE, NF_BR_PRE_ROUTING, skb, skb->dev, NULL,
br_handle_frame_finish);
- return 1;
+ break;
+ default:
+drop:
+ kfree_skb(skb);
}
-
-err:
- kfree_skb(skb);
- return 1;
+ return NULL;
}
diff --git a/net/bridge/br_ioctl.c b/net/bridge/br_ioctl.c
index 3ab153d3c50..eda0fbfc923 100644
--- a/net/bridge/br_ioctl.c
+++ b/net/bridge/br_ioctl.c
@@ -137,7 +137,8 @@ static int old_dev_ioctl(struct net_device *dev, struct ifreq *rq, int cmd)
b.topology_change = br->topology_change;
b.topology_change_detected = br->topology_change_detected;
b.root_port = br->root_port;
- b.stp_enabled = br->stp_enabled;
+
+ b.stp_enabled = (br->stp_enabled != BR_NO_STP);
b.ageing_time = jiffies_to_clock_t(br->ageing_time);
b.hello_timer_value = br_timer_value(&br->hello_timer);
b.tcn_timer_value = br_timer_value(&br->tcn_timer);
@@ -251,7 +252,7 @@ static int old_dev_ioctl(struct net_device *dev, struct ifreq *rq, int cmd)
if (!capable(CAP_NET_ADMIN))
return -EPERM;
- br->stp_enabled = args[1]?1:0;
+ br_stp_set_enabled(br, args[1]);
return 0;
case BRCTL_SET_BRIDGE_PRIORITY:
@@ -291,12 +292,11 @@ static int old_dev_ioctl(struct net_device *dev, struct ifreq *rq, int cmd)
if (!capable(CAP_NET_ADMIN))
return -EPERM;
- spin_lock_bh(&br->lock);
if ((p = br_get_port(br, args[1])) == NULL)
ret = -EINVAL;
else
br_stp_set_path_cost(p, args[2]);
- spin_unlock_bh(&br->lock);
+
return ret;
}
diff --git a/net/bridge/br_netfilter.c b/net/bridge/br_netfilter.c
index 7712d76f06b..9b2986b182b 100644
--- a/net/bridge/br_netfilter.c
+++ b/net/bridge/br_netfilter.c
@@ -29,6 +29,8 @@
#include <linux/if_arp.h>
#include <linux/if_ether.h>
#include <linux/if_vlan.h>
+#include <linux/if_pppox.h>
+#include <linux/ppp_defs.h>
#include <linux/netfilter_bridge.h>
#include <linux/netfilter_ipv4.h>
#include <linux/netfilter_ipv6.h>
@@ -48,8 +50,8 @@
#define skb_origaddr(skb) (((struct bridge_skb_cb *) \
(skb->nf_bridge->data))->daddr.ipv4)
-#define store_orig_dstaddr(skb) (skb_origaddr(skb) = (skb)->nh.iph->daddr)
-#define dnat_took_place(skb) (skb_origaddr(skb) != (skb)->nh.iph->daddr)
+#define store_orig_dstaddr(skb) (skb_origaddr(skb) = ip_hdr(skb)->daddr)
+#define dnat_took_place(skb) (skb_origaddr(skb) != ip_hdr(skb)->daddr)
#ifdef CONFIG_SYSCTL
static struct ctl_table_header *brnf_sysctl_header;
@@ -57,11 +59,13 @@ static int brnf_call_iptables __read_mostly = 1;
static int brnf_call_ip6tables __read_mostly = 1;
static int brnf_call_arptables __read_mostly = 1;
static int brnf_filter_vlan_tagged __read_mostly = 1;
+static int brnf_filter_pppoe_tagged __read_mostly = 1;
#else
#define brnf_filter_vlan_tagged 1
+#define brnf_filter_pppoe_tagged 1
#endif
-static __be16 inline vlan_proto(const struct sk_buff *skb)
+static inline __be16 vlan_proto(const struct sk_buff *skb)
{
return vlan_eth_hdr(skb)->h_vlan_encapsulated_proto;
}
@@ -81,6 +85,22 @@ static __be16 inline vlan_proto(const struct sk_buff *skb)
vlan_proto(skb) == htons(ETH_P_ARP) && \
brnf_filter_vlan_tagged)
+static inline __be16 pppoe_proto(const struct sk_buff *skb)
+{
+ return *((__be16 *)(skb_mac_header(skb) + ETH_HLEN +
+ sizeof(struct pppoe_hdr)));
+}
+
+#define IS_PPPOE_IP(skb) \
+ (skb->protocol == htons(ETH_P_PPP_SES) && \
+ pppoe_proto(skb) == htons(PPP_IP) && \
+ brnf_filter_pppoe_tagged)
+
+#define IS_PPPOE_IPV6(skb) \
+ (skb->protocol == htons(ETH_P_PPP_SES) && \
+ pppoe_proto(skb) == htons(PPP_IPV6) && \
+ brnf_filter_pppoe_tagged)
+
/* We need these fake structures to make netfilter happy --
* lots of places assume that skb->dst != NULL, which isn't
* all that unreasonable.
@@ -128,8 +148,11 @@ static inline void nf_bridge_save_header(struct sk_buff *skb)
if (skb->protocol == htons(ETH_P_8021Q))
header_size += VLAN_HLEN;
+ else if (skb->protocol == htons(ETH_P_PPP_SES))
+ header_size += PPPOE_SES_HLEN;
- memcpy(skb->nf_bridge->data, skb->data - header_size, header_size);
+ skb_copy_from_linear_data_offset(skb, -header_size,
+ skb->nf_bridge->data, header_size);
}
/*
@@ -143,15 +166,20 @@ int nf_bridge_copy_header(struct sk_buff *skb)
if (skb->protocol == htons(ETH_P_8021Q))
header_size += VLAN_HLEN;
+ else if (skb->protocol == htons(ETH_P_PPP_SES))
+ header_size += PPPOE_SES_HLEN;
err = skb_cow(skb, header_size);
if (err)
return err;
- memcpy(skb->data - header_size, skb->nf_bridge->data, header_size);
+ skb_copy_to_linear_data_offset(skb, -header_size,
+ skb->nf_bridge->data, header_size);
if (skb->protocol == htons(ETH_P_8021Q))
__skb_push(skb, VLAN_HLEN);
+ else if (skb->protocol == htons(ETH_P_PPP_SES))
+ __skb_push(skb, PPPOE_SES_HLEN);
return 0;
}
@@ -174,7 +202,10 @@ static int br_nf_pre_routing_finish_ipv6(struct sk_buff *skb)
skb->dev = nf_bridge->physindev;
if (skb->protocol == htons(ETH_P_8021Q)) {
skb_push(skb, VLAN_HLEN);
- skb->nh.raw -= VLAN_HLEN;
+ skb->network_header -= VLAN_HLEN;
+ } else if (skb->protocol == htons(ETH_P_PPP_SES)) {
+ skb_push(skb, PPPOE_SES_HLEN);
+ skb->network_header -= PPPOE_SES_HLEN;
}
NF_HOOK_THRESH(PF_BRIDGE, NF_BR_PRE_ROUTING, skb, skb->dev, NULL,
br_handle_frame_finish, 1);
@@ -255,7 +286,10 @@ static int br_nf_pre_routing_finish_bridge(struct sk_buff *skb)
else {
if (skb->protocol == htons(ETH_P_8021Q)) {
skb_pull(skb, VLAN_HLEN);
- skb->nh.raw += VLAN_HLEN;
+ skb->network_header += VLAN_HLEN;
+ } else if (skb->protocol == htons(ETH_P_PPP_SES)) {
+ skb_pull(skb, PPPOE_SES_HLEN);
+ skb->network_header += PPPOE_SES_HLEN;
}
skb->dst->output(skb);
}
@@ -265,7 +299,7 @@ static int br_nf_pre_routing_finish_bridge(struct sk_buff *skb)
static int br_nf_pre_routing_finish(struct sk_buff *skb)
{
struct net_device *dev = skb->dev;
- struct iphdr *iph = skb->nh.iph;
+ struct iphdr *iph = ip_hdr(skb);
struct nf_bridge_info *nf_bridge = skb->nf_bridge;
int err;
@@ -325,7 +359,11 @@ bridged_dnat:
if (skb->protocol ==
htons(ETH_P_8021Q)) {
skb_push(skb, VLAN_HLEN);
- skb->nh.raw -= VLAN_HLEN;
+ skb->network_header -= VLAN_HLEN;
+ } else if(skb->protocol ==
+ htons(ETH_P_PPP_SES)) {
+ skb_push(skb, PPPOE_SES_HLEN);
+ skb->network_header -= PPPOE_SES_HLEN;
}
NF_HOOK_THRESH(PF_BRIDGE, NF_BR_PRE_ROUTING,
skb, skb->dev, NULL,
@@ -344,7 +382,10 @@ bridged_dnat:
skb->dev = nf_bridge->physindev;
if (skb->protocol == htons(ETH_P_8021Q)) {
skb_push(skb, VLAN_HLEN);
- skb->nh.raw -= VLAN_HLEN;
+ skb->network_header -= VLAN_HLEN;
+ } else if (skb->protocol == htons(ETH_P_PPP_SES)) {
+ skb_push(skb, PPPOE_SES_HLEN);
+ skb->network_header -= PPPOE_SES_HLEN;
}
NF_HOOK_THRESH(PF_BRIDGE, NF_BR_PRE_ROUTING, skb, skb->dev, NULL,
br_handle_frame_finish, 1);
@@ -372,9 +413,10 @@ static struct net_device *setup_pre_routing(struct sk_buff *skb)
/* We only check the length. A bridge shouldn't do any hop-by-hop stuff anyway */
static int check_hbh_len(struct sk_buff *skb)
{
- unsigned char *raw = (u8 *) (skb->nh.ipv6h + 1);
+ unsigned char *raw = (u8 *)(ipv6_hdr(skb) + 1);
u32 pkt_len;
- int off = raw - skb->nh.raw;
+ const unsigned char *nh = skb_network_header(skb);
+ int off = raw - nh;
int len = (raw[1] + 1) << 3;
if ((raw + len) - skb->data > skb_headlen(skb))
@@ -384,9 +426,9 @@ static int check_hbh_len(struct sk_buff *skb)
len -= 2;
while (len > 0) {
- int optlen = skb->nh.raw[off + 1] + 2;
+ int optlen = nh[off + 1] + 2;
- switch (skb->nh.raw[off]) {
+ switch (nh[off]) {
case IPV6_TLV_PAD0:
optlen = 1;
break;
@@ -395,17 +437,18 @@ static int check_hbh_len(struct sk_buff *skb)
break;
case IPV6_TLV_JUMBO:
- if (skb->nh.raw[off + 1] != 4 || (off & 3) != 2)
+ if (nh[off + 1] != 4 || (off & 3) != 2)
goto bad;
- pkt_len = ntohl(*(__be32 *) (skb->nh.raw + off + 2));
+ pkt_len = ntohl(*(__be32 *) (nh + off + 2));
if (pkt_len <= IPV6_MAXPLEN ||
- skb->nh.ipv6h->payload_len)
+ ipv6_hdr(skb)->payload_len)
goto bad;
if (pkt_len > skb->len - sizeof(struct ipv6hdr))
goto bad;
if (pskb_trim_rcsum(skb,
pkt_len + sizeof(struct ipv6hdr)))
goto bad;
+ nh = skb_network_header(skb);
break;
default:
if (optlen > len)
@@ -439,7 +482,7 @@ static unsigned int br_nf_pre_routing_ipv6(unsigned int hook,
if (!pskb_may_pull(skb, sizeof(struct ipv6hdr)))
goto inhdr_error;
- hdr = skb->nh.ipv6h;
+ hdr = ipv6_hdr(skb);
if (hdr->version != 6)
goto inhdr_error;
@@ -485,7 +528,8 @@ static unsigned int br_nf_pre_routing(unsigned int hook, struct sk_buff **pskb,
__u32 len;
struct sk_buff *skb = *pskb;
- if (skb->protocol == htons(ETH_P_IPV6) || IS_VLAN_IPV6(skb)) {
+ if (skb->protocol == htons(ETH_P_IPV6) || IS_VLAN_IPV6(skb) ||
+ IS_PPPOE_IPV6(skb)) {
#ifdef CONFIG_SYSCTL
if (!brnf_call_ip6tables)
return NF_ACCEPT;
@@ -495,7 +539,10 @@ static unsigned int br_nf_pre_routing(unsigned int hook, struct sk_buff **pskb,
if (skb->protocol == htons(ETH_P_8021Q)) {
skb_pull_rcsum(skb, VLAN_HLEN);
- skb->nh.raw += VLAN_HLEN;
+ skb->network_header += VLAN_HLEN;
+ } else if (skb->protocol == htons(ETH_P_PPP_SES)) {
+ skb_pull_rcsum(skb, PPPOE_SES_HLEN);
+ skb->network_header += PPPOE_SES_HLEN;
}
return br_nf_pre_routing_ipv6(hook, skb, in, out, okfn);
}
@@ -504,7 +551,8 @@ static unsigned int br_nf_pre_routing(unsigned int hook, struct sk_buff **pskb,
return NF_ACCEPT;
#endif
- if (skb->protocol != htons(ETH_P_IP) && !IS_VLAN_IP(skb))
+ if (skb->protocol != htons(ETH_P_IP) && !IS_VLAN_IP(skb) &&
+ !IS_PPPOE_IP(skb))
return NF_ACCEPT;
if ((skb = skb_share_check(*pskb, GFP_ATOMIC)) == NULL)
@@ -512,20 +560,23 @@ static unsigned int br_nf_pre_routing(unsigned int hook, struct sk_buff **pskb,
if (skb->protocol == htons(ETH_P_8021Q)) {
skb_pull_rcsum(skb, VLAN_HLEN);
- skb->nh.raw += VLAN_HLEN;
+ skb->network_header += VLAN_HLEN;
+ } else if (skb->protocol == htons(ETH_P_PPP_SES)) {
+ skb_pull_rcsum(skb, PPPOE_SES_HLEN);
+ skb->network_header += PPPOE_SES_HLEN;
}
if (!pskb_may_pull(skb, sizeof(struct iphdr)))
goto inhdr_error;
- iph = skb->nh.iph;
+ iph = ip_hdr(skb);
if (iph->ihl < 5 || iph->version != 4)
goto inhdr_error;
if (!pskb_may_pull(skb, 4 * iph->ihl))
goto inhdr_error;
- iph = skb->nh.iph;
+ iph = ip_hdr(skb);
if (ip_fast_csum((__u8 *) iph, iph->ihl) != 0)
goto inhdr_error;
@@ -593,7 +644,10 @@ static int br_nf_forward_finish(struct sk_buff *skb)
}
if (skb->protocol == htons(ETH_P_8021Q)) {
skb_push(skb, VLAN_HLEN);
- skb->nh.raw -= VLAN_HLEN;
+ skb->network_header -= VLAN_HLEN;
+ } else if (skb->protocol == htons(ETH_P_PPP_SES)) {
+ skb_push(skb, PPPOE_SES_HLEN);
+ skb->network_header -= PPPOE_SES_HLEN;
}
NF_HOOK_THRESH(PF_BRIDGE, NF_BR_FORWARD, skb, in,
skb->dev, br_forward_finish, 1);
@@ -622,14 +676,18 @@ static unsigned int br_nf_forward_ip(unsigned int hook, struct sk_buff **pskb,
if (!parent)
return NF_DROP;
- if (skb->protocol == htons(ETH_P_IP) || IS_VLAN_IP(skb))
+ if (skb->protocol == htons(ETH_P_IP) || IS_VLAN_IP(skb) ||
+ IS_PPPOE_IP(skb))
pf = PF_INET;
else
pf = PF_INET6;
if (skb->protocol == htons(ETH_P_8021Q)) {
skb_pull(*pskb, VLAN_HLEN);
- (*pskb)->nh.raw += VLAN_HLEN;
+ (*pskb)->network_header += VLAN_HLEN;
+ } else if (skb->protocol == htons(ETH_P_PPP_SES)) {
+ skb_pull(*pskb, PPPOE_SES_HLEN);
+ (*pskb)->network_header += PPPOE_SES_HLEN;
}
nf_bridge = skb->nf_bridge;
@@ -665,13 +723,13 @@ static unsigned int br_nf_forward_arp(unsigned int hook, struct sk_buff **pskb,
if (!IS_VLAN_ARP(skb))
return NF_ACCEPT;
skb_pull(*pskb, VLAN_HLEN);
- (*pskb)->nh.raw += VLAN_HLEN;
+ (*pskb)->network_header += VLAN_HLEN;
}
- if (skb->nh.arph->ar_pln != 4) {
+ if (arp_hdr(skb)->ar_pln != 4) {
if (IS_VLAN_ARP(skb)) {
skb_push(*pskb, VLAN_HLEN);
- (*pskb)->nh.raw -= VLAN_HLEN;
+ (*pskb)->network_header -= VLAN_HLEN;
}
return NF_ACCEPT;
}
@@ -721,7 +779,10 @@ static unsigned int br_nf_local_out(unsigned int hook, struct sk_buff **pskb,
}
if (skb->protocol == htons(ETH_P_8021Q)) {
skb_push(skb, VLAN_HLEN);
- skb->nh.raw -= VLAN_HLEN;
+ skb->network_header -= VLAN_HLEN;
+ } else if (skb->protocol == htons(ETH_P_PPP_SES)) {
+ skb_push(skb, PPPOE_SES_HLEN);
+ skb->network_header -= PPPOE_SES_HLEN;
}
NF_HOOK(PF_BRIDGE, NF_BR_FORWARD, skb, realindev, skb->dev,
@@ -753,7 +814,8 @@ static unsigned int br_nf_post_routing(unsigned int hook, struct sk_buff **pskb,
#ifdef CONFIG_NETFILTER_DEBUG
/* Be very paranoid. This probably won't happen anymore, but let's
* keep the check just to be sure... */
- if (skb->mac.raw < skb->head || skb->mac.raw + ETH_HLEN > skb->data) {
+ if (skb_mac_header(skb) < skb->head ||
+ skb_mac_header(skb) + ETH_HLEN > skb->data) {
printk(KERN_CRIT "br_netfilter: Argh!! br_nf_post_routing: "
"bad mac.raw pointer.\n");
goto print_error;
@@ -766,7 +828,8 @@ static unsigned int br_nf_post_routing(unsigned int hook, struct sk_buff **pskb,
if (!realoutdev)
return NF_DROP;
- if (skb->protocol == htons(ETH_P_IP) || IS_VLAN_IP(skb))
+ if (skb->protocol == htons(ETH_P_IP) || IS_VLAN_IP(skb) ||
+ IS_PPPOE_IP(skb))
pf = PF_INET;
else
pf = PF_INET6;
@@ -787,7 +850,10 @@ static unsigned int br_nf_post_routing(unsigned int hook, struct sk_buff **pskb,
if (skb->protocol == htons(ETH_P_8021Q)) {
skb_pull(skb, VLAN_HLEN);
- skb->nh.raw += VLAN_HLEN;
+ skb->network_header += VLAN_HLEN;
+ } else if (skb->protocol == htons(ETH_P_PPP_SES)) {
+ skb_pull(skb, PPPOE_SES_HLEN);
+ skb->network_header += PPPOE_SES_HLEN;
}
nf_bridge_save_header(skb);
@@ -808,7 +874,7 @@ print_error:
if (realoutdev)
printk("[%s]", realoutdev->name);
}
- printk(" head:%p, raw:%p, data:%p\n", skb->head, skb->mac.raw,
+ printk(" head:%p, raw:%p, data:%p\n", skb->head, skb_mac_header(skb),
skb->data);
dump_stack();
return NF_ACCEPT;
@@ -925,6 +991,14 @@ static ctl_table brnf_table[] = {
.mode = 0644,
.proc_handler = &brnf_sysctl_call_tables,
},
+ {
+ .ctl_name = NET_BRIDGE_NF_FILTER_PPPOE_TAGGED,
+ .procname = "bridge-nf-filter-pppoe-tagged",
+ .data = &brnf_filter_pppoe_tagged,
+ .maxlen = sizeof(int),
+ .mode = 0644,
+ .proc_handler = &brnf_sysctl_call_tables,
+ },
{ .ctl_name = 0 }
};
diff --git a/net/bridge/br_netlink.c b/net/bridge/br_netlink.c
index 7d68b24b565..35facc0c11c 100644
--- a/net/bridge/br_netlink.c
+++ b/net/bridge/br_netlink.c
@@ -11,8 +11,7 @@
*/
#include <linux/kernel.h>
-#include <linux/rtnetlink.h>
-#include <net/netlink.h>
+#include <net/rtnetlink.h>
#include "br_private.h"
static inline size_t br_nlmsg_size(void)
@@ -110,7 +109,6 @@ static int br_dump_ifinfo(struct sk_buff *skb, struct netlink_callback *cb)
struct net_device *dev;
int idx;
- read_lock(&dev_base_lock);
for (dev = dev_base, idx = 0; dev; dev = dev->next) {
/* not a bridge port */
if (dev->br_port == NULL || idx < cb->args[0])
@@ -123,7 +121,6 @@ static int br_dump_ifinfo(struct sk_buff *skb, struct netlink_callback *cb)
skip:
++idx;
}
- read_unlock(&dev_base_lock);
cb->args[0] = idx;
@@ -166,7 +163,7 @@ static int br_rtm_setlink(struct sk_buff *skb, struct nlmsghdr *nlh, void *arg)
return -EINVAL;
/* if kernel STP is running, don't allow changes */
- if (p->br->stp_enabled)
+ if (p->br->stp_enabled == BR_KERNEL_STP)
return -EBUSY;
if (!netif_running(dev) ||
@@ -179,18 +176,19 @@ static int br_rtm_setlink(struct sk_buff *skb, struct nlmsghdr *nlh, void *arg)
}
-static struct rtnetlink_link bridge_rtnetlink_table[RTM_NR_MSGTYPES] = {
- [RTM_GETLINK - RTM_BASE] = { .dumpit = br_dump_ifinfo, },
- [RTM_SETLINK - RTM_BASE] = { .doit = br_rtm_setlink, },
-};
-
-void __init br_netlink_init(void)
+int __init br_netlink_init(void)
{
- rtnetlink_links[PF_BRIDGE] = bridge_rtnetlink_table;
+ if (__rtnl_register(PF_BRIDGE, RTM_GETLINK, NULL, br_dump_ifinfo))
+ return -ENOBUFS;
+
+ /* Only the first call to __rtnl_register can fail */
+ __rtnl_register(PF_BRIDGE, RTM_SETLINK, br_rtm_setlink, NULL);
+
+ return 0;
}
void __exit br_netlink_fini(void)
{
- rtnetlink_links[PF_BRIDGE] = NULL;
+ rtnl_unregister_all(PF_BRIDGE);
}
diff --git a/net/bridge/br_notify.c b/net/bridge/br_notify.c
index 3311c4e3082..c8451d3a070 100644
--- a/net/bridge/br_notify.c
+++ b/net/bridge/br_notify.c
@@ -42,51 +42,53 @@ static int br_device_event(struct notifier_block *unused, unsigned long event, v
br = p->br;
- spin_lock_bh(&br->lock);
switch (event) {
case NETDEV_CHANGEMTU:
dev_set_mtu(br->dev, br_min_mtu(br));
break;
case NETDEV_CHANGEADDR:
+ spin_lock_bh(&br->lock);
br_fdb_changeaddr(p, dev->dev_addr);
- br_ifinfo_notify(RTM_NEWLINK, p);
br_stp_recalculate_bridge_id(br);
+ spin_unlock_bh(&br->lock);
break;
case NETDEV_CHANGE:
- if (br->dev->flags & IFF_UP)
- if (schedule_delayed_work(&p->carrier_check,
- BR_PORT_DEBOUNCE))
- dev_hold(dev);
+ br_port_carrier_check(p);
break;
case NETDEV_FEAT_CHANGE:
- if (br->dev->flags & IFF_UP)
+ spin_lock_bh(&br->lock);
+ if (netif_running(br->dev))
br_features_recompute(br);
-
- /* could do recursive feature change notification
- * but who would care??
- */
+ spin_unlock_bh(&br->lock);
break;
case NETDEV_DOWN:
+ spin_lock_bh(&br->lock);
if (br->dev->flags & IFF_UP)
br_stp_disable_port(p);
+ spin_unlock_bh(&br->lock);
break;
case NETDEV_UP:
- if (netif_carrier_ok(dev) && (br->dev->flags & IFF_UP))
+ if (netif_carrier_ok(dev) && (br->dev->flags & IFF_UP)) {
+ spin_lock_bh(&br->lock);
br_stp_enable_port(p);
+ spin_unlock_bh(&br->lock);
+ }
break;
case NETDEV_UNREGISTER:
- spin_unlock_bh(&br->lock);
br_del_if(br, dev);
- goto done;
+ break;
}
- spin_unlock_bh(&br->lock);
- done:
+ /* Events that may cause spanning tree to refresh */
+ if (event == NETDEV_CHANGEADDR || event == NETDEV_UP ||
+ event == NETDEV_CHANGE || event == NETDEV_DOWN)
+ br_ifinfo_notify(RTM_NEWLINK, p);
+
return NOTIFY_DONE;
}
diff --git a/net/bridge/br_private.h b/net/bridge/br_private.h
index 6a0540e0591..21bf3a9a03f 100644
--- a/net/bridge/br_private.h
+++ b/net/bridge/br_private.h
@@ -16,7 +16,6 @@
#define _BR_PRIVATE_H
#include <linux/netdevice.h>
-#include <linux/miscdevice.h>
#include <linux/if_bridge.h>
#define BR_HASH_BITS 8
@@ -27,9 +26,10 @@
#define BR_PORT_BITS 10
#define BR_MAX_PORTS (1<<BR_PORT_BITS)
-#define BR_PORT_DEBOUNCE (HZ/10)
+#define BR_VERSION "2.3"
-#define BR_VERSION "2.2"
+/* Path to usermode spanning tree program */
+#define BR_STP_PROG "/sbin/bridge-stp"
typedef struct bridge_id bridge_id;
typedef struct mac_addr mac_addr;
@@ -82,7 +82,6 @@ struct net_bridge_port
struct timer_list hold_timer;
struct timer_list message_age_timer;
struct kobject kobj;
- struct delayed_work carrier_check;
struct rcu_head rcu;
};
@@ -111,7 +110,13 @@ struct net_bridge
u8 group_addr[ETH_ALEN];
u16 root_port;
- unsigned char stp_enabled;
+
+ enum {
+ BR_NO_STP, /* no spanning tree */
+ BR_KERNEL_STP, /* old STP in kernel */
+ BR_USER_STP, /* new RSTP in userspace */
+ } stp_enabled;
+
unsigned char topology_change;
unsigned char topology_change_detected;
@@ -131,14 +136,14 @@ static inline int br_is_root_bridge(const struct net_bridge *br)
return !memcmp(&br->bridge_id, &br->designated_root, 8);
}
-
/* br_device.c */
extern void br_dev_setup(struct net_device *dev);
extern int br_dev_xmit(struct sk_buff *skb, struct net_device *dev);
/* br_fdb.c */
-extern void br_fdb_init(void);
+extern int br_fdb_init(void);
extern void br_fdb_fini(void);
+extern void br_fdb_flush(struct net_bridge *br);
extern void br_fdb_changeaddr(struct net_bridge_port *p,
const unsigned char *newaddr);
extern void br_fdb_cleanup(unsigned long arg);
@@ -173,6 +178,7 @@ extern void br_flood_forward(struct net_bridge *br,
int clone);
/* br_if.c */
+extern void br_port_carrier_check(struct net_bridge_port *p);
extern int br_add_bridge(const char *name);
extern int br_del_bridge(const char *name);
extern void br_cleanup_bridges(void);
@@ -185,7 +191,8 @@ extern void br_features_recompute(struct net_bridge *br);
/* br_input.c */
extern int br_handle_frame_finish(struct sk_buff *skb);
-extern int br_handle_frame(struct net_bridge_port *p, struct sk_buff **pskb);
+extern struct sk_buff *br_handle_frame(struct net_bridge_port *p,
+ struct sk_buff *skb);
/* br_ioctl.c */
extern int br_dev_ioctl(struct net_device *dev, struct ifreq *rq, int cmd);
@@ -210,6 +217,7 @@ extern void br_become_designated_port(struct net_bridge_port *p);
/* br_stp_if.c */
extern void br_stp_enable_bridge(struct net_bridge *br);
extern void br_stp_disable_bridge(struct net_bridge *br);
+extern void br_stp_set_enabled(struct net_bridge *br, unsigned long val);
extern void br_stp_enable_port(struct net_bridge_port *p);
extern void br_stp_disable_port(struct net_bridge_port *p);
extern void br_stp_recalculate_bridge_id(struct net_bridge *br);
@@ -238,7 +246,7 @@ extern void (*br_fdb_put_hook)(struct net_bridge_fdb_entry *ent);
/* br_netlink.c */
-extern void br_netlink_init(void);
+extern int br_netlink_init(void);
extern void br_netlink_fini(void);
extern void br_ifinfo_notify(int event, struct net_bridge_port *port);
diff --git a/net/bridge/br_stp.c b/net/bridge/br_stp.c
index f9ff4d57b0d..ebb0861e9bd 100644
--- a/net/bridge/br_stp.c
+++ b/net/bridge/br_stp.c
@@ -370,11 +370,11 @@ static void br_make_blocking(struct net_bridge_port *p)
static void br_make_forwarding(struct net_bridge_port *p)
{
if (p->state == BR_STATE_BLOCKING) {
- if (p->br->stp_enabled) {
+ if (p->br->stp_enabled == BR_KERNEL_STP)
p->state = BR_STATE_LISTENING;
- } else {
+ else
p->state = BR_STATE_LEARNING;
- }
+
br_log_state(p);
mod_timer(&p->forward_delay_timer, jiffies + p->br->forward_delay); }
}
@@ -384,6 +384,10 @@ void br_port_state_selection(struct net_bridge *br)
{
struct net_bridge_port *p;
+ /* Don't change port states if userspace is handling STP */
+ if (br->stp_enabled == BR_USER_STP)
+ return;
+
list_for_each_entry(p, &br->port_list, list) {
if (p->state != BR_STATE_DISABLED) {
if (p->port_no == br->root_port) {
diff --git a/net/bridge/br_stp_bpdu.c b/net/bridge/br_stp_bpdu.c
index b9fb0dc4ab1..60112bce669 100644
--- a/net/bridge/br_stp_bpdu.c
+++ b/net/bridge/br_stp_bpdu.c
@@ -33,9 +33,6 @@ static void br_send_bpdu(struct net_bridge_port *p,
{
struct sk_buff *skb;
- if (!p->br->stp_enabled)
- return;
-
skb = dev_alloc_skb(length+LLC_RESERVE);
if (!skb)
return;
@@ -75,6 +72,9 @@ void br_send_config_bpdu(struct net_bridge_port *p, struct br_config_bpdu *bpdu)
{
unsigned char buf[35];
+ if (p->br->stp_enabled != BR_KERNEL_STP)
+ return;
+
buf[0] = 0;
buf[1] = 0;
buf[2] = 0;
@@ -117,6 +117,9 @@ void br_send_tcn_bpdu(struct net_bridge_port *p)
{
unsigned char buf[4];
+ if (p->br->stp_enabled != BR_KERNEL_STP)
+ return;
+
buf[0] = 0;
buf[1] = 0;
buf[2] = 0;
@@ -157,9 +160,13 @@ int br_stp_rcv(struct sk_buff *skb, struct net_device *dev,
br = p->br;
spin_lock(&br->lock);
- if (p->state == BR_STATE_DISABLED
- || !br->stp_enabled
- || !(br->dev->flags & IFF_UP))
+ if (br->stp_enabled != BR_KERNEL_STP)
+ goto out;
+
+ if (!(br->dev->flags & IFF_UP))
+ goto out;
+
+ if (p->state == BR_STATE_DISABLED)
goto out;
if (compare_ether_addr(dest, br->group_addr) != 0)
diff --git a/net/bridge/br_stp_if.c b/net/bridge/br_stp_if.c
index 58d13f2bd12..3e246b37020 100644
--- a/net/bridge/br_stp_if.c
+++ b/net/bridge/br_stp_if.c
@@ -87,7 +87,6 @@ void br_stp_disable_bridge(struct net_bridge *br)
void br_stp_enable_port(struct net_bridge_port *p)
{
br_init_port(p);
- br_ifinfo_notify(RTM_NEWLINK, p);
br_port_state_selection(p->br);
}
@@ -101,8 +100,6 @@ void br_stp_disable_port(struct net_bridge_port *p)
printk(KERN_INFO "%s: port %i(%s) entering %s state\n",
br->dev->name, p->port_no, p->dev->name, "disabled");
- br_ifinfo_notify(RTM_DELLINK, p);
-
wasroot = br_is_root_bridge(br);
br_become_designated_port(p);
p->state = BR_STATE_DISABLED;
@@ -123,10 +120,68 @@ void br_stp_disable_port(struct net_bridge_port *p)
br_become_root_bridge(br);
}
+static void br_stp_start(struct net_bridge *br)
+{
+ int r;
+ char *argv[] = { BR_STP_PROG, br->dev->name, "start", NULL };
+ char *envp[] = { NULL };
+
+ r = call_usermodehelper(BR_STP_PROG, argv, envp, 1);
+ if (r == 0) {
+ br->stp_enabled = BR_USER_STP;
+ printk(KERN_INFO "%s: userspace STP started\n", br->dev->name);
+ } else {
+ br->stp_enabled = BR_KERNEL_STP;
+ printk(KERN_INFO "%s: starting userspace STP failed, "
+ "staring kernel STP\n", br->dev->name);
+
+ /* To start timers on any ports left in blocking */
+ spin_lock_bh(&br->lock);
+ br_port_state_selection(br);
+ spin_unlock_bh(&br->lock);
+ }
+}
+
+static void br_stp_stop(struct net_bridge *br)
+{
+ int r;
+ char *argv[] = { BR_STP_PROG, br->dev->name, "stop", NULL };
+ char *envp[] = { NULL };
+
+ if (br->stp_enabled == BR_USER_STP) {
+ r = call_usermodehelper(BR_STP_PROG, argv, envp, 1);
+ printk(KERN_INFO "%s: userspace STP stopped, return code %d\n",
+ br->dev->name, r);
+
+
+ /* To start timers on any ports left in blocking */
+ spin_lock_bh(&br->lock);
+ br_port_state_selection(br);
+ spin_unlock_bh(&br->lock);
+ }
+
+ br->stp_enabled = BR_NO_STP;
+}
+
+void br_stp_set_enabled(struct net_bridge *br, unsigned long val)
+{
+ ASSERT_RTNL();
+
+ if (val) {
+ if (br->stp_enabled == BR_NO_STP)
+ br_stp_start(br);
+ } else {
+ if (br->stp_enabled != BR_NO_STP)
+ br_stp_stop(br);
+ }
+}
+
/* called under bridge lock */
void br_stp_change_bridge_id(struct net_bridge *br, const unsigned char *addr)
{
- unsigned char oldaddr[6];
+ /* should be aligned on 2 bytes for compare_ether_addr() */
+ unsigned short oldaddr_aligned[ETH_ALEN >> 1];
+ unsigned char *oldaddr = (unsigned char *)oldaddr_aligned;
struct net_bridge_port *p;
int wasroot;
@@ -151,11 +206,14 @@ void br_stp_change_bridge_id(struct net_bridge *br, const unsigned char *addr)
br_become_root_bridge(br);
}
-static const unsigned char br_mac_zero[6];
+/* should be aligned on 2 bytes for compare_ether_addr() */
+static const unsigned short br_mac_zero_aligned[ETH_ALEN >> 1];
/* called under bridge lock */
void br_stp_recalculate_bridge_id(struct net_bridge *br)
{
+ const unsigned char *br_mac_zero =
+ (const unsigned char *)br_mac_zero_aligned;
const unsigned char *addr = br_mac_zero;
struct net_bridge_port *p;
diff --git a/net/bridge/br_sysfs_br.c b/net/bridge/br_sysfs_br.c
index 01a22ad0cc7..33c6c4a7c68 100644
--- a/net/bridge/br_sysfs_br.c
+++ b/net/bridge/br_sysfs_br.c
@@ -149,7 +149,11 @@ static ssize_t show_stp_state(struct device *d,
static void set_stp_state(struct net_bridge *br, unsigned long val)
{
- br->stp_enabled = val;
+ rtnl_lock();
+ spin_unlock_bh(&br->lock);
+ br_stp_set_enabled(br, val);
+ spin_lock_bh(&br->lock);
+ rtnl_unlock();
}
static ssize_t store_stp_state(struct device *d,
@@ -309,6 +313,19 @@ static ssize_t store_group_addr(struct device *d,
static DEVICE_ATTR(group_addr, S_IRUGO | S_IWUSR,
show_group_addr, store_group_addr);
+static ssize_t store_flush(struct device *d,
+ struct device_attribute *attr,
+ const char *buf, size_t len)
+{
+ struct net_bridge *br = to_bridge(d);
+
+ if (!capable(CAP_NET_ADMIN))
+ return -EPERM;
+
+ br_fdb_flush(br);
+ return len;
+}
+static DEVICE_ATTR(flush, S_IWUSR, NULL, store_flush);
static struct attribute *bridge_attrs[] = {
&dev_attr_forward_delay.attr,
@@ -328,6 +345,7 @@ static struct attribute *bridge_attrs[] = {
&dev_attr_topology_change_timer.attr,
&dev_attr_gc_timer.attr,
&dev_attr_group_addr.attr,
+ &dev_attr_flush.attr,
NULL
};
diff --git a/net/bridge/br_sysfs_if.c b/net/bridge/br_sysfs_if.c
index 0bc2aef8f9f..2da22927d8d 100644
--- a/net/bridge/br_sysfs_if.c
+++ b/net/bridge/br_sysfs_if.c
@@ -137,6 +137,13 @@ static ssize_t show_hold_timer(struct net_bridge_port *p,
}
static BRPORT_ATTR(hold_timer, S_IRUGO, show_hold_timer, NULL);
+static ssize_t store_flush(struct net_bridge_port *p, unsigned long v)
+{
+ br_fdb_delete_by_port(p->br, p, 0); // Don't delete local entry
+ return 0;
+}
+static BRPORT_ATTR(flush, S_IWUSR, NULL, store_flush);
+
static struct brport_attribute *brport_attrs[] = {
&brport_attr_path_cost,
&brport_attr_priority,
@@ -152,6 +159,7 @@ static struct brport_attribute *brport_attrs[] = {
&brport_attr_message_age_timer,
&brport_attr_forward_delay_timer,
&brport_attr_hold_timer,
+ &brport_attr_flush,
NULL
};
diff --git a/net/bridge/netfilter/ebt_arp.c b/net/bridge/netfilter/ebt_arp.c
index 9c599800a90..1a46952a56d 100644
--- a/net/bridge/netfilter/ebt_arp.c
+++ b/net/bridge/netfilter/ebt_arp.c
@@ -35,40 +35,36 @@ static int ebt_filter_arp(const struct sk_buff *skb, const struct net_device *in
return EBT_NOMATCH;
if (info->bitmask & (EBT_ARP_SRC_IP | EBT_ARP_DST_IP)) {
- __be32 _addr, *ap;
+ __be32 saddr, daddr, *sap, *dap;
- /* IPv4 addresses are always 4 bytes */
- if (ah->ar_pln != sizeof(__be32))
+ if (ah->ar_pln != sizeof(__be32) || ah->ar_pro != htons(ETH_P_IP))
+ return EBT_NOMATCH;
+ sap = skb_header_pointer(skb, sizeof(struct arphdr) +
+ ah->ar_hln, sizeof(saddr),
+ &saddr);
+ if (sap == NULL)
+ return EBT_NOMATCH;
+ dap = skb_header_pointer(skb, sizeof(struct arphdr) +
+ 2*ah->ar_hln+sizeof(saddr),
+ sizeof(daddr), &daddr);
+ if (dap == NULL)
+ return EBT_NOMATCH;
+ if (info->bitmask & EBT_ARP_SRC_IP &&
+ FWINV(info->saddr != (*sap & info->smsk), EBT_ARP_SRC_IP))
+ return EBT_NOMATCH;
+ if (info->bitmask & EBT_ARP_DST_IP &&
+ FWINV(info->daddr != (*dap & info->dmsk), EBT_ARP_DST_IP))
+ return EBT_NOMATCH;
+ if (info->bitmask & EBT_ARP_GRAT &&
+ FWINV(*dap != *sap, EBT_ARP_GRAT))
return EBT_NOMATCH;
- if (info->bitmask & EBT_ARP_SRC_IP) {
- ap = skb_header_pointer(skb, sizeof(struct arphdr) +
- ah->ar_hln, sizeof(_addr),
- &_addr);
- if (ap == NULL)
- return EBT_NOMATCH;
- if (FWINV(info->saddr != (*ap & info->smsk),
- EBT_ARP_SRC_IP))
- return EBT_NOMATCH;
- }
-
- if (info->bitmask & EBT_ARP_DST_IP) {
- ap = skb_header_pointer(skb, sizeof(struct arphdr) +
- 2*ah->ar_hln+sizeof(__be32),
- sizeof(_addr), &_addr);
- if (ap == NULL)
- return EBT_NOMATCH;
- if (FWINV(info->daddr != (*ap & info->dmsk),
- EBT_ARP_DST_IP))
- return EBT_NOMATCH;
- }
}
if (info->bitmask & (EBT_ARP_SRC_MAC | EBT_ARP_DST_MAC)) {
unsigned char _mac[ETH_ALEN], *mp;
uint8_t verdict, i;
- /* MAC addresses are 6 bytes */
- if (ah->ar_hln != ETH_ALEN)
+ if (ah->ar_hln != ETH_ALEN || ah->ar_hrd != htons(ARPHRD_ETHER))
return EBT_NOMATCH;
if (info->bitmask & EBT_ARP_SRC_MAC) {
mp = skb_header_pointer(skb, sizeof(struct arphdr),
diff --git a/net/bridge/netfilter/ebt_log.c b/net/bridge/netfilter/ebt_log.c
index 45712aec6a0..031bfa4a51f 100644
--- a/net/bridge/netfilter/ebt_log.c
+++ b/net/bridge/netfilter/ebt_log.c
@@ -196,14 +196,10 @@ static int __init ebt_log_init(void)
ret = ebt_register_watcher(&log);
if (ret < 0)
return ret;
- if (nf_log_register(PF_BRIDGE, &ebt_log_logger) < 0) {
- printk(KERN_WARNING "ebt_log: not logging via system console "
- "since somebody else already registered for PF_INET\n");
- /* we cannot make module load fail here, since otherwise
- * ebtables userspace would abort */
- }
-
- return 0;
+ ret = nf_log_register(PF_BRIDGE, &ebt_log_logger);
+ if (ret < 0 && ret != -EEXIST)
+ ebt_unregister_watcher(&log);
+ return ret;
}
static void __exit ebt_log_fini(void)
diff --git a/net/bridge/netfilter/ebt_ulog.c b/net/bridge/netfilter/ebt_ulog.c
index 8e15cc47f6c..9411db62591 100644
--- a/net/bridge/netfilter/ebt_ulog.c
+++ b/net/bridge/netfilter/ebt_ulog.c
@@ -130,6 +130,7 @@ static void ebt_ulog_packet(unsigned int hooknr, const struct sk_buff *skb,
unsigned int group = uloginfo->nlgroup;
ebt_ulog_buff_t *ub = &ulog_buffers[group];
spinlock_t *lock = &ub->lock;
+ ktime_t kt;
if ((uloginfo->cprange == 0) ||
(uloginfo->cprange > skb->len + ETH_HLEN))
@@ -164,9 +165,10 @@ static void ebt_ulog_packet(unsigned int hooknr, const struct sk_buff *skb,
/* Fill in the ulog data */
pm->version = EBT_ULOG_VERSION;
- do_gettimeofday(&pm->stamp);
+ kt = ktime_get_real();
+ pm->stamp = ktime_to_timeval(kt);
if (ub->qlen == 1)
- skb_set_timestamp(ub->skb, &pm->stamp);
+ ub->skb->tstamp = kt;
pm->data_len = copy_len;
pm->mark = skb->mark;
pm->hook = hooknr;
@@ -295,14 +297,12 @@ static int __init ebt_ulog_init(void)
/* initialize ulog_buffers */
for (i = 0; i < EBT_ULOG_MAXNLGROUPS; i++) {
- init_timer(&ulog_buffers[i].timer);
- ulog_buffers[i].timer.function = ulog_timer;
- ulog_buffers[i].timer.data = i;
+ setup_timer(&ulog_buffers[i].timer, ulog_timer, i);
spin_lock_init(&ulog_buffers[i].lock);
}
ebtulognl = netlink_kernel_create(NETLINK_NFLOG, EBT_ULOG_MAXNLGROUPS,
- NULL, THIS_MODULE);
+ NULL, NULL, THIS_MODULE);
if (!ebtulognl)
ret = -ENOMEM;
else if ((ret = ebt_register_watcher(&ulog)))
diff --git a/net/bridge/netfilter/ebtables.c b/net/bridge/netfilter/ebtables.c
index 34c49799f9b..ac9984f98e5 100644
--- a/net/bridge/netfilter/ebtables.c
+++ b/net/bridge/netfilter/ebtables.c
@@ -833,8 +833,7 @@ static int translate_table(char *name, struct ebt_table_info *newinfo)
/* this will get free'd in do_replace()/ebt_register_table()
if an error occurs */
newinfo->chainstack =
- vmalloc((highest_possible_processor_id()+1)
- * sizeof(*(newinfo->chainstack)));
+ vmalloc(nr_cpu_ids * sizeof(*(newinfo->chainstack)));
if (!newinfo->chainstack)
return -ENOMEM;
for_each_possible_cpu(i) {
@@ -947,8 +946,7 @@ static int do_replace(void __user *user, unsigned int len)
if (tmp.num_counters >= INT_MAX / sizeof(struct ebt_counter))
return -ENOMEM;
- countersize = COUNTER_OFFSET(tmp.nentries) *
- (highest_possible_processor_id()+1);
+ countersize = COUNTER_OFFSET(tmp.nentries) * nr_cpu_ids;
newinfo = vmalloc(sizeof(*newinfo) + countersize);
if (!newinfo)
return -ENOMEM;
@@ -1168,8 +1166,7 @@ int ebt_register_table(struct ebt_table *table)
return -EINVAL;
}
- countersize = COUNTER_OFFSET(repl->nentries) *
- (highest_possible_processor_id()+1);
+ countersize = COUNTER_OFFSET(repl->nentries) * nr_cpu_ids;
newinfo = vmalloc(sizeof(*newinfo) + countersize);
ret = -ENOMEM;
if (!newinfo)