summaryrefslogtreecommitdiff
path: root/net/bridge
diff options
context:
space:
mode:
Diffstat (limited to 'net/bridge')
-rw-r--r--net/bridge/br_device.c24
-rw-r--r--net/bridge/br_if.c3
-rw-r--r--net/bridge/br_ioctl.c8
-rw-r--r--net/bridge/br_netfilter.c63
-rw-r--r--net/bridge/br_private.h6
-rw-r--r--net/bridge/br_stp.c25
-rw-r--r--net/bridge/br_sysfs_br.c26
7 files changed, 104 insertions, 51 deletions
diff --git a/net/bridge/br_device.c b/net/bridge/br_device.c
index d9449df7cad5..4f52c3d50ebe 100644
--- a/net/bridge/br_device.c
+++ b/net/bridge/br_device.c
@@ -68,10 +68,17 @@ static int br_dev_stop(struct net_device *dev)
static int br_change_mtu(struct net_device *dev, int new_mtu)
{
- if (new_mtu < 68 || new_mtu > br_min_mtu(netdev_priv(dev)))
+ struct net_bridge *br = netdev_priv(dev);
+ if (new_mtu < 68 || new_mtu > br_min_mtu(br))
return -EINVAL;
dev->mtu = new_mtu;
+
+#ifdef CONFIG_BRIDGE_NETFILTER
+ /* remember the MTU in the rtable for PMTU */
+ br->fake_rtable.u.dst.metrics[RTAX_MTU - 1] = new_mtu;
+#endif
+
return 0;
}
@@ -141,11 +148,16 @@ static int br_set_tx_csum(struct net_device *dev, u32 data)
}
static struct ethtool_ops br_ethtool_ops = {
- .get_drvinfo = br_getinfo,
- .get_link = ethtool_op_get_link,
- .set_sg = br_set_sg,
- .set_tx_csum = br_set_tx_csum,
- .set_tso = br_set_tso,
+ .get_drvinfo = br_getinfo,
+ .get_link = ethtool_op_get_link,
+ .get_tx_csum = ethtool_op_get_tx_csum,
+ .set_tx_csum = br_set_tx_csum,
+ .get_sg = ethtool_op_get_sg,
+ .set_sg = br_set_sg,
+ .get_tso = ethtool_op_get_tso,
+ .set_tso = br_set_tso,
+ .get_ufo = ethtool_op_get_ufo,
+ .get_flags = ethtool_op_get_flags,
};
void br_dev_setup(struct net_device *dev)
diff --git a/net/bridge/br_if.c b/net/bridge/br_if.c
index a072ea5ca6f5..63c18aacde8c 100644
--- a/net/bridge/br_if.c
+++ b/net/bridge/br_if.c
@@ -202,6 +202,9 @@ static struct net_device *new_bridge_dev(const char *name)
br->topology_change = 0;
br->topology_change_detected = 0;
br->ageing_time = 300 * HZ;
+
+ br_netfilter_rtable_init(br);
+
INIT_LIST_HEAD(&br->age_list);
br_stp_timer_init(br);
diff --git a/net/bridge/br_ioctl.c b/net/bridge/br_ioctl.c
index eeee218eed80..5bbf07362172 100644
--- a/net/bridge/br_ioctl.c
+++ b/net/bridge/br_ioctl.c
@@ -188,15 +188,21 @@ static int old_dev_ioctl(struct net_device *dev, struct ifreq *rq, int cmd)
return 0;
case BRCTL_SET_BRIDGE_HELLO_TIME:
+ {
+ unsigned long t = clock_t_to_jiffies(args[1]);
if (!capable(CAP_NET_ADMIN))
return -EPERM;
+ if (t < HZ)
+ return -EINVAL;
+
spin_lock_bh(&br->lock);
- br->bridge_hello_time = clock_t_to_jiffies(args[1]);
+ br->bridge_hello_time = t;
if (br_is_root_bridge(br))
br->hello_time = br->bridge_hello_time;
spin_unlock_bh(&br->lock);
return 0;
+ }
case BRCTL_SET_BRIDGE_MAX_AGE:
if (!capable(CAP_NET_ADMIN))
diff --git a/net/bridge/br_netfilter.c b/net/bridge/br_netfilter.c
index bb90cd7bace3..6a9a6cd74b1e 100644
--- a/net/bridge/br_netfilter.c
+++ b/net/bridge/br_netfilter.c
@@ -101,33 +101,30 @@ static inline __be16 pppoe_proto(const struct sk_buff *skb)
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.
- *
+/*
+ * 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 struct net_device __fake_net_device = {
- .hard_header_len = ETH_HLEN,
-#ifdef CONFIG_NET_NS
- .nd_net = &init_net,
-#endif
-};
+ * require us to fill additional fields.
+ */
+void br_netfilter_rtable_init(struct net_bridge *br)
+{
+ struct rtable *rt = &br->fake_rtable;
-static struct rtable __fake_rtable = {
- .u = {
- .dst = {
- .__refcnt = ATOMIC_INIT(1),
- .dev = &__fake_net_device,
- .path = &__fake_rtable.u.dst,
- .metrics = {[RTAX_MTU - 1] = 1500},
- .flags = DST_NOXFRM,
- }
- },
- .rt_flags = 0,
-};
+ atomic_set(&rt->u.dst.__refcnt, 1);
+ rt->u.dst.dev = br->dev;
+ rt->u.dst.path = &rt->u.dst;
+ rt->u.dst.metrics[RTAX_MTU - 1] = 1500;
+ rt->u.dst.flags = DST_NOXFRM;
+}
+
+static inline struct rtable *bridge_parent_rtable(const struct net_device *dev)
+{
+ struct net_bridge_port *port = rcu_dereference(dev->br_port);
+
+ return port ? &port->br->fake_rtable : NULL;
+}
static inline struct net_device *bridge_parent(const struct net_device *dev)
{
@@ -226,8 +223,12 @@ static int br_nf_pre_routing_finish_ipv6(struct sk_buff *skb)
}
nf_bridge->mask ^= BRNF_NF_BRIDGE_PREROUTING;
- skb->rtable = &__fake_rtable;
- dst_hold(&__fake_rtable.u.dst);
+ skb->rtable = bridge_parent_rtable(nf_bridge->physindev);
+ if (!skb->rtable) {
+ kfree_skb(skb);
+ return 0;
+ }
+ dst_hold(&skb->rtable->u.dst);
skb->dev = nf_bridge->physindev;
nf_bridge_push_encap_header(skb);
@@ -391,8 +392,12 @@ bridged_dnat:
skb->pkt_type = PACKET_HOST;
}
} else {
- skb->rtable = &__fake_rtable;
- dst_hold(&__fake_rtable.u.dst);
+ skb->rtable = bridge_parent_rtable(nf_bridge->physindev);
+ if (!skb->rtable) {
+ kfree_skb(skb);
+ return 0;
+ }
+ dst_hold(&skb->rtable->u.dst);
}
skb->dev = nf_bridge->physindev;
@@ -611,8 +616,8 @@ static unsigned int br_nf_local_in(unsigned int hook, struct sk_buff *skb,
const struct net_device *out,
int (*okfn)(struct sk_buff *))
{
- if (skb->rtable == &__fake_rtable) {
- dst_release(&__fake_rtable.u.dst);
+ if (skb->rtable && skb->rtable == bridge_parent_rtable(in)) {
+ dst_release(&skb->rtable->u.dst);
skb->rtable = NULL;
}
diff --git a/net/bridge/br_private.h b/net/bridge/br_private.h
index 815ed38925b2..c3dc18ddc043 100644
--- a/net/bridge/br_private.h
+++ b/net/bridge/br_private.h
@@ -15,6 +15,7 @@
#include <linux/netdevice.h>
#include <linux/if_bridge.h>
+#include <net/route.h>
#define BR_HASH_BITS 8
#define BR_HASH_SIZE (1 << BR_HASH_BITS)
@@ -92,6 +93,9 @@ struct net_bridge
struct hlist_head hash[BR_HASH_SIZE];
struct list_head age_list;
unsigned long feature_mask;
+#ifdef CONFIG_BRIDGE_NETFILTER
+ struct rtable fake_rtable;
+#endif
unsigned long flags;
#define BR_SET_MAC_ADDR 0x00000001
@@ -197,9 +201,11 @@ extern int br_ioctl_deviceless_stub(struct net *net, unsigned int cmd, void __us
#ifdef CONFIG_BRIDGE_NETFILTER
extern int br_netfilter_init(void);
extern void br_netfilter_fini(void);
+extern void br_netfilter_rtable_init(struct net_bridge *);
#else
#define br_netfilter_init() (0)
#define br_netfilter_fini() do { } while(0)
+#define br_netfilter_rtable_init(x)
#endif
/* br_stp.c */
diff --git a/net/bridge/br_stp.c b/net/bridge/br_stp.c
index 921bbe5cb94a..6e63ec3f1fcf 100644
--- a/net/bridge/br_stp.c
+++ b/net/bridge/br_stp.c
@@ -368,14 +368,25 @@ static void br_make_blocking(struct net_bridge_port *p)
/* called under bridge lock */
static void br_make_forwarding(struct net_bridge_port *p)
{
- if (p->state == BR_STATE_BLOCKING) {
- if (p->br->stp_enabled == BR_KERNEL_STP)
- p->state = BR_STATE_LISTENING;
- else
- p->state = BR_STATE_LEARNING;
+ struct net_bridge *br = p->br;
- br_log_state(p);
- mod_timer(&p->forward_delay_timer, jiffies + p->br->forward_delay); }
+ if (p->state != BR_STATE_BLOCKING)
+ return;
+
+ if (br->forward_delay == 0) {
+ p->state = BR_STATE_FORWARDING;
+ br_topology_change_detection(br);
+ del_timer(&p->forward_delay_timer);
+ }
+ else if (p->br->stp_enabled == BR_KERNEL_STP)
+ p->state = BR_STATE_LISTENING;
+ else
+ p->state = BR_STATE_LEARNING;
+
+ br_log_state(p);
+
+ if (br->forward_delay != 0)
+ mod_timer(&p->forward_delay_timer, jiffies + br->forward_delay);
}
/* called under bridge lock */
diff --git a/net/bridge/br_sysfs_br.c b/net/bridge/br_sysfs_br.c
index 27d6a511c8c1..158dee8b4965 100644
--- a/net/bridge/br_sysfs_br.c
+++ b/net/bridge/br_sysfs_br.c
@@ -29,11 +29,12 @@
*/
static ssize_t store_bridge_parm(struct device *d,
const char *buf, size_t len,
- void (*set)(struct net_bridge *, unsigned long))
+ int (*set)(struct net_bridge *, unsigned long))
{
struct net_bridge *br = to_bridge(d);
char *endp;
unsigned long val;
+ int err;
if (!capable(CAP_NET_ADMIN))
return -EPERM;
@@ -43,9 +44,9 @@ static ssize_t store_bridge_parm(struct device *d,
return -EINVAL;
spin_lock_bh(&br->lock);
- (*set)(br, val);
+ err = (*set)(br, val);
spin_unlock_bh(&br->lock);
- return len;
+ return err ? err : len;
}
@@ -56,12 +57,13 @@ static ssize_t show_forward_delay(struct device *d,
return sprintf(buf, "%lu\n", jiffies_to_clock_t(br->forward_delay));
}
-static void set_forward_delay(struct net_bridge *br, unsigned long val)
+static int set_forward_delay(struct net_bridge *br, unsigned long val)
{
unsigned long delay = clock_t_to_jiffies(val);
br->forward_delay = delay;
if (br_is_root_bridge(br))
br->bridge_forward_delay = delay;
+ return 0;
}
static ssize_t store_forward_delay(struct device *d,
@@ -80,12 +82,17 @@ static ssize_t show_hello_time(struct device *d, struct device_attribute *attr,
jiffies_to_clock_t(to_bridge(d)->hello_time));
}
-static void set_hello_time(struct net_bridge *br, unsigned long val)
+static int set_hello_time(struct net_bridge *br, unsigned long val)
{
unsigned long t = clock_t_to_jiffies(val);
+
+ if (t < HZ)
+ return -EINVAL;
+
br->hello_time = t;
if (br_is_root_bridge(br))
br->bridge_hello_time = t;
+ return 0;
}
static ssize_t store_hello_time(struct device *d,
@@ -104,12 +111,13 @@ static ssize_t show_max_age(struct device *d, struct device_attribute *attr,
jiffies_to_clock_t(to_bridge(d)->max_age));
}
-static void set_max_age(struct net_bridge *br, unsigned long val)
+static int set_max_age(struct net_bridge *br, unsigned long val)
{
unsigned long t = clock_t_to_jiffies(val);
br->max_age = t;
if (br_is_root_bridge(br))
br->bridge_max_age = t;
+ return 0;
}
static ssize_t store_max_age(struct device *d, struct device_attribute *attr,
@@ -126,9 +134,10 @@ static ssize_t show_ageing_time(struct device *d,
return sprintf(buf, "%lu\n", jiffies_to_clock_t(br->ageing_time));
}
-static void set_ageing_time(struct net_bridge *br, unsigned long val)
+static int set_ageing_time(struct net_bridge *br, unsigned long val)
{
br->ageing_time = clock_t_to_jiffies(val);
+ return 0;
}
static ssize_t store_ageing_time(struct device *d,
@@ -180,9 +189,10 @@ static ssize_t show_priority(struct device *d, struct device_attribute *attr,
(br->bridge_id.prio[0] << 8) | br->bridge_id.prio[1]);
}
-static void set_priority(struct net_bridge *br, unsigned long val)
+static int set_priority(struct net_bridge *br, unsigned long val)
{
br_stp_set_bridge_priority(br, (u16) val);
+ return 0;
}
static ssize_t store_priority(struct device *d, struct device_attribute *attr,