summaryrefslogtreecommitdiffstats
path: root/net/bridge/br.c
diff options
context:
space:
mode:
authorRalf Baechle <ralf@linux-mips.org>1998-05-07 02:55:41 +0000
committerRalf Baechle <ralf@linux-mips.org>1998-05-07 02:55:41 +0000
commitdcec8a13bf565e47942a1751a9cec21bec5648fe (patch)
tree548b69625b18cc2e88c3e68d0923be546c9ebb03 /net/bridge/br.c
parent2e0f55e79c49509b7ff70ff1a10e1e9e90a3dfd4 (diff)
o Merge with Linux 2.1.99.
o Fix ancient bug in the ELF loader making ldd crash. o Fix ancient bug in the keyboard code for SGI, SNI and Jazz.
Diffstat (limited to 'net/bridge/br.c')
-rw-r--r--net/bridge/br.c1145
1 files changed, 758 insertions, 387 deletions
diff --git a/net/bridge/br.c b/net/bridge/br.c
index 2961ff3c6..014453f8c 100644
--- a/net/bridge/br.c
+++ b/net/bridge/br.c
@@ -13,8 +13,31 @@
* as published by the Free Software Foundation; either version
* 2 of the License, or (at your option) any later version.
*
- * Fixes:
- * Yury Shevchuk : Bridge with non bridging ports
+ * Fixes:
+ * Yury Shevchuk : Bridge with non bridging ports
+ * Jean-Rene Peulve: jr.peulve@aix.pacwan.net Jan/Feb 98
+ * support Linux 2.0
+ * Handle Receive config bpdu
+ * kick mark_bh to send Spanning Tree pdus
+ * bridgeId comparison using htonl()
+ * make STP interoperable with other vendors
+ * wrong test in root_selection()
+ * add more STP debug info
+ * some performance improvments
+ * do not clear bridgeId.mac while setting priority
+ * do not reset port priority when starting bridge
+ * make port priority from user value and port number
+ * maintains user port state out of device state
+ * broacast/multicast storm limitation
+ * forwarding statistics
+ * stop br_tick when bridge is turn off
+ * add local MACs in avl_tree to forward up stack
+ * fake receive on right port for IP/ARP
+ * ages tree even if packet does not cross bridge
+ * add BRCMD_DISPLAY_FDB (ioctl for now)
+ *
+ * Alan Cox: Merged Jean-Rene's stuff, reformatted stuff a bit
+ * so blame me first if its broken ;)
*
* Todo:
* Don't bring up devices automatically. Start ports disabled
@@ -42,11 +65,17 @@
#include <linux/string.h>
#include <linux/skbuff.h>
#include <linux/if_arp.h>
+#include <linux/ip.h>
+#include <linux/version.h>
#include <linux/init.h>
#include <asm/uaccess.h>
#include <asm/system.h>
#include <net/br.h>
+#ifndef min
+#define min(a, b) (((a) <= (b)) ? (a) : (b))
+#endif
+
static void transmit_config(int port_no);
static int root_bridge(void);
static int supersedes_port_info(int port_no, Config_bpdu *config);
@@ -80,7 +109,7 @@ static void br_init_port(int port_no);
static void enable_port(int port_no);
static void disable_port(int port_no);
static void set_bridge_priority(bridge_id_t *new_bridge_id);
-static void set_port_priority(int port_no, unsigned short new_port_id);
+static void set_port_priority(int port_no);
static void set_path_cost(int port_no, unsigned short path_cost);
static void start_hello_timer(void);
static void stop_hello_timer(void);
@@ -104,11 +133,12 @@ static int br_device_event(struct notifier_block *dnot, unsigned long event, voi
static void br_tick(unsigned long arg);
static int br_forward(struct sk_buff *skb, int port); /* 3.7 */
static int br_port_cost(struct device *dev); /* 4.10.2 */
-static void br_bpdu(struct sk_buff *skb); /* consumes skb */
+static void br_bpdu(struct sk_buff *skb, int port); /* consumes skb */
static int br_cmp(unsigned int *a, unsigned int *b);
static int send_tcn_bpdu(int port_no, Tcn_bpdu *bpdu);
static int send_config_bpdu(int port_no, Config_bpdu *config_bpdu);
static int find_port(struct device *dev);
+static void br_add_local_mac(unsigned char *mac);
static int br_flood(struct sk_buff *skb, int port);
static int br_drop(struct sk_buff *skb);
static int br_learn(struct sk_buff *skb, int port); /* 3.8 */
@@ -116,8 +146,21 @@ static int br_learn(struct sk_buff *skb, int port); /* 3.8 */
static unsigned char bridge_ula[ETH_ALEN] = { 0x01, 0x80, 0xc2, 0x00, 0x00, 0x00 };
static Bridge_data bridge_info; /* (4.5.3) */
Port_data port_info[All_ports]; /* (4.5.5) */
-Config_bpdu config_bpdu[All_ports];
-Tcn_bpdu tcn_bpdu[All_ports];
+
+/* JRP: fdb cache 1/port save kmalloc/kfree on every frame */
+struct fdb *newfdb[All_ports];
+int allocated_fdb_cnt = 0;
+
+/* broacast/multicast storm limitation */
+int max_mcast_per_period = MAX_MCAST_PER_PERIOD;
+int mcast_hold_time = MCAST_HOLD_TIME;
+
+/* JRP: next two bpdu are copied to skbuff so we need only 1 of each */
+static Config_bpdu config_bpdu;
+static Tcn_bpdu tcn_bpdu;
+static unsigned char port_priority[All_ports];
+static unsigned char user_port_state[All_ports];
+
static Timer hello_timer; /* (4.5.4.1) */
static Timer tcn_timer; /* (4.5.4.2) */
static Timer topology_change_timer; /* (4.5.4.3) */
@@ -129,6 +172,7 @@ static Timer hold_timer[All_ports]; /* (4.5.6.3) */
unsigned int fdb_aging_time = FDB_TIMEOUT;
struct br_stat br_stats;
+#define br_stats_cnt br_stats.packet_cnts
static struct timer_list tl; /* for 1 second timer... */
@@ -154,23 +198,28 @@ static struct notifier_block br_dev_notifier={
#define BR_PROTOCOL_HASH(x) (x % BR_MAX_PROTOCOLS)
/* Checks if that protocol type is to be bridged */
+
int br_protocol_ok(unsigned short protocol)
{
unsigned x;
/* See if protocol statistics are to be kept */
if (br_stats.flags & BR_PROT_STATS)
- { for(x=0;x<BR_MAX_PROT_STATS &&
- br_stats.prot_id[x]!=protocol && br_stats.prot_id[x];x++) ;
- if (x<BR_MAX_PROT_STATS)
- { br_stats.prot_id[x]=protocol;br_stats.prot_counter[x]++;
- }
+ {
+ for(x=0;x<BR_MAX_PROT_STATS && br_stats.prot_id[x]!=protocol && br_stats.prot_id[x];x++);
+ if (x<BR_MAX_PROT_STATS)
+ {
+ br_stats.prot_id[x]=protocol;br_stats.prot_counter[x]++;
+ }
}
- for (x=BR_PROTOCOL_HASH(protocol); br_stats.protocols[x]!=0;) {
- if (br_stats.protocols[x]==protocol) return !br_stats.policy;
+ for (x=BR_PROTOCOL_HASH(protocol); br_stats.protocols[x]!=0;)
+ {
+ if (br_stats.protocols[x]==protocol)
+ return !br_stats.policy;
x++;
- if (x==BR_MAX_PROTOCOLS) x=0;
+ if (x==BR_MAX_PROTOCOLS)
+ x=0;
}
return br_stats.policy;
}
@@ -209,7 +258,7 @@ static int br_set_policy(int policy)
/*
* this section of code was graciously borrowed from the IEEE 802.1d
* specification section 4.9.1 starting on pg 69. It has been
- * modified somewhat to fit within out framework and structure. It
+ * modified somewhat to fit within our framework and structure. It
* implements the spanning tree algorithm that is the heart of the
* 802.1d bridging protocol.
*/
@@ -219,42 +268,44 @@ static void transmit_config(int port_no) /* (4.6.1) */
if (hold_timer[port_no].active) { /* (4.6.1.3.1) */
port_info[port_no].config_pending = TRUE; /* (4.6.1.3.1) */
} else { /* (4.6.1.3.2) */
- config_bpdu[port_no].type = BPDU_TYPE_CONFIG;
- config_bpdu[port_no].root_id = bridge_info.designated_root;
+ config_bpdu.type = BPDU_TYPE_CONFIG;
+ config_bpdu.root_id = bridge_info.designated_root;
/* (4.6.1.3.2(1)) */
- config_bpdu[port_no].root_path_cost = bridge_info.root_path_cost;
+ config_bpdu.root_path_cost = bridge_info.root_path_cost;
/* (4.6.1.3.2(2)) */
- config_bpdu[port_no].bridge_id = bridge_info.bridge_id;
+ config_bpdu.bridge_id = bridge_info.bridge_id;
/* (4.6.1.3.2(3)) */
- config_bpdu[port_no].port_id = port_info[port_no].port_id;
+ config_bpdu.port_id = port_info[port_no].port_id;
/*
* (4.6.1.3.2(4))
*/
if (root_bridge()) {
- config_bpdu[port_no].message_age = Zero; /* (4.6.1.3.2(5)) */
+ config_bpdu.message_age = Zero; /* (4.6.1.3.2(5)) */
} else {
- config_bpdu[port_no].message_age
+ config_bpdu.message_age
= message_age_timer[bridge_info.root_port].value
+ Message_age_increment; /* (4.6.1.3.2(6)) */
}
- config_bpdu[port_no].max_age = bridge_info.max_age; /* (4.6.1.3.2(7)) */
- config_bpdu[port_no].hello_time = bridge_info.hello_time;
- config_bpdu[port_no].forward_delay = bridge_info.forward_delay;
- config_bpdu[port_no].flags = 0;
- config_bpdu[port_no].flags |=
- port_info[port_no].top_change_ack ? TOPOLOGY_CHANGE_ACK : 0;
- /* (4.6.1.3.2(8)) */
+ config_bpdu.max_age = bridge_info.max_age;/* (4.6.1.3.2(7)) */
+ config_bpdu.hello_time = bridge_info.hello_time;
+ config_bpdu.forward_delay = bridge_info.forward_delay;
+ config_bpdu.top_change_ack =
+ port_info[port_no].top_change_ack;
+ /* (4.6.1.3.2(8)) */
port_info[port_no].top_change_ack = 0;
- /* (4.6.1.3.2(8)) */
- config_bpdu[port_no].flags |=
- bridge_info.top_change ? TOPOLOGY_CHANGE : 0;
- /* (4.6.1.3.2(9)) */
- send_config_bpdu(port_no, &config_bpdu[port_no]);
+ config_bpdu.top_change =
+ bridge_info.top_change; /* (4.6.1.3.2(9)) */
+
+ send_config_bpdu(port_no, &config_bpdu);
port_info[port_no].config_pending = FALSE; /* (4.6.1.3.2(10)) */
start_hold_timer(port_no); /* (4.6.1.3.2(11)) */
}
+/* JRP: we want the frame to be xmitted even if no other traffic.
+ * net_bh() will do a dev_transmit() that kicks all devices
+ */
+ mark_bh(NET_BH);
}
static int root_bridge(void)
@@ -314,8 +365,7 @@ static void record_config_timeout_values(Config_bpdu *config) /* (4.6.3) */
bridge_info.max_age = config->max_age; /* (4.6.3.3) */
bridge_info.hello_time = config->hello_time;
bridge_info.forward_delay = config->forward_delay;
- if (config->flags & TOPOLOGY_CHANGE)
- bridge_info.top_change = 1;
+ bridge_info.top_change = config->top_change;
}
static void config_bpdu_generation(void)
@@ -353,8 +403,8 @@ static void transmit_tcn(void)
int port_no;
port_no = bridge_info.root_port;
- tcn_bpdu[port_no].type = BPDU_TYPE_TOPO_CHANGE;
- send_tcn_bpdu(port_no, &tcn_bpdu[bridge_info.root_port]); /* (4.6.6.3) */
+ tcn_bpdu.type = BPDU_TYPE_TOPO_CHANGE;
+ send_tcn_bpdu(port_no, &tcn_bpdu); /* (4.6.6.3) */
}
static void configuration_update(void) /* (4.6.7) */
@@ -420,7 +470,7 @@ static void root_selection(void)
) /* (4.6.8.3.1(4)) */
||
((port_info[port_no].designated_port
- = port_info[root_port].designated_port
+/* JRP: was missing an "=" ! */ == port_info[root_port].designated_port
)
&&
(port_info[port_no].port_id
@@ -433,6 +483,10 @@ static void root_selection(void)
bridge_info.root_port = root_port; /* (4.6.8.3.1) */
if (root_port == No_port) { /* (4.6.8.3.2) */
+#ifdef DEBUG_STP
+ if (br_stats.flags & BR_DEBUG)
+ printk(KERN_DEBUG "root_selection: becomes root\n");
+#endif
bridge_info.designated_root = bridge_info.bridge_id;
/* (4.6.8.3.2(1)) */
bridge_info.root_path_cost = Zero;/* (4.6.8.3.2(2)) */
@@ -450,6 +504,8 @@ static void designated_port_selection(void)
int port_no;
for (port_no = One; port_no <= No_of_ports; port_no++) { /* (4.6.9.3) */
+ if(port_info[port_no].state == Disabled)
+ continue;
if (designated_port(port_no) /* (4.6.9.3.1) */
||
(
@@ -498,19 +554,32 @@ static void become_designated_port(int port_no)
static void port_state_selection(void)
{ /* (4.6.11) */
int port_no;
+ char *state_str;
for (port_no = One; port_no <= No_of_ports; port_no++) {
+
+ if(port_info[port_no].state == Disabled)
+ continue;
if (port_no == bridge_info.root_port) { /* (4.6.11.3.1) */
- port_info[port_no].config_pending = FALSE; /* (4.6.11.3~1(1)) */
+ state_str = "root";
+ port_info[port_no].config_pending = FALSE; /* (4.6.11.3.1(1)) */
port_info[port_no].top_change_ack = 0;
make_forwarding(port_no); /* (4.6.11.3.1(2)) */
} else if (designated_port(port_no)) { /* (4.6.11.3.2) */
+ state_str = "designated";
stop_message_age_timer(port_no); /* (4.6.11.3.2(1)) */
make_forwarding(port_no); /* (4.6.11.3.2(2)) */
} else { /* (4.6.11.3.3) */
+ state_str = "blocking";
port_info[port_no].config_pending = FALSE; /* (4.6.11.3.3(1)) */
port_info[port_no].top_change_ack = 0;
make_blocking(port_no); /* (4.6.11.3.3(2)) */
}
+#ifdef DEBUG_STP
+ if (br_stats.flags & BR_DEBUG)
+ printk(KERN_DEBUG "port_state_selection: becomes %s port %d\n",
+ state_str, port_no);
+#endif
+
}
}
@@ -525,6 +594,11 @@ static void make_forwarding(int port_no)
static void topology_change_detection(void)
{ /* (4.6.14) */
+#ifdef DEBUG_STP
+ if ((br_stats.flags & BR_DEBUG)
+ && (bridge_info.top_change_detected == 0))
+ printk(KERN_DEBUG "topology_change_detected\n");
+#endif
if (root_bridge()) { /* (4.6.14.3.1) */
bridge_info.top_change = 1;
start_topology_change_timer(); /* (4.6.14.3.1(2)) */
@@ -532,12 +606,16 @@ static void topology_change_detection(void)
transmit_tcn(); /* (4.6.14.3.2(1)) */
start_tcn_timer(); /* (4.6.14.3.2(2)) */
}
- bridge_info.top_change = 1;
+ bridge_info.top_change_detected = 1; /* (4.6.14.3.3) */
}
static void topology_change_acknowledged(void)
{ /* (4.6.15) */
- bridge_info.top_change_detected = 0;
+#ifdef DEBUG_STP
+ if (br_stats.flags & BR_DEBUG)
+ printk(KERN_DEBUG "topology_change_acked\n");
+#endif
+ bridge_info.top_change_detected = 0; /* (4.6.15.3.1) */
stop_tcn_timer(); /* (4.6.15.3.2) */
}
@@ -574,10 +652,16 @@ static void set_port_state(int port_no, int state)
static void received_config_bpdu(int port_no, Config_bpdu *config) /* (4.7.1) */
{
- int root;
+ int root;
root = root_bridge();
if (port_info[port_no].state != Disabled) {
+
+#ifdef DEBUG_STP
+ if (br_stats.flags & BR_DEBUG)
+ printk(KERN_DEBUG "received_config_bpdu: port %d\n",
+ port_no);
+#endif
if (supersedes_port_info(port_no, config)) { /* (4.7.1.1) *//* (4.
* 6.2.2) */
record_config_information(port_no, config); /* (4.7.1.1.1) */
@@ -588,7 +672,7 @@ static void received_config_bpdu(int port_no, Config_bpdu *config) /* (4.7.1)
/* (4.6.11.2.1) */
if ((!root_bridge()) && root) { /* (4.7.1.1.4) */
stop_hello_timer();
- if (bridge_info.top_change_detected) { /* (4.7.1.1.5~ */
+ if (bridge_info.top_change_detected) { /* (4.7.1.1.5 */
stop_topology_change_timer();
transmit_tcn(); /* (4.6.6.1) */
start_tcn_timer();
@@ -598,7 +682,7 @@ static void received_config_bpdu(int port_no, Config_bpdu *config) /* (4.7.1)
record_config_timeout_values(config); /* (4.7.1.1.6) */
/* (4.6.3.2) */
config_bpdu_generation(); /* (4.6.4.2.1) */
- if (config->flags & TOPOLOGY_CHANGE_ACK) { /* (4.7.1.1.7) */
+ if (config->top_change_ack) { /* (4.7.1.1.7) */
topology_change_acknowledged(); /* (4.6.15.2) */
}
}
@@ -612,6 +696,11 @@ static void received_config_bpdu(int port_no, Config_bpdu *config) /* (4.7.1)
static void received_tcn_bpdu(int port_no, Tcn_bpdu *tcn) /* (4.7.2) */
{
if (port_info[port_no].state != Disabled) {
+#ifdef DEBUG_STP
+ if (br_stats.flags & BR_DEBUG)
+ printk(KERN_DEBUG "received_tcn_bpdu: port %d\n",
+ port_no);
+#endif
if (designated_port(port_no)) {
topology_change_detection(); /* (4.7.2.1) */
/* (4.6.14.2.1) */
@@ -628,9 +717,14 @@ static void hello_timer_expiry(void)
static void message_age_timer_expiry(int port_no) /* (4.7.4) */
{
- int root;
+ int root;
root = root_bridge();
+#ifdef DEBUG_STP
+ if (br_stats.flags & BR_DEBUG)
+ printk(KERN_DEBUG "message_age_timer_expiry: port %d\n",
+ port_no);
+#endif
become_designated_port(port_no); /* (4.7.4.1) */
/* (4.6.10.2.1) */
configuration_update(); /* (4.7.4.2) */
@@ -653,12 +747,17 @@ static void message_age_timer_expiry(int port_no) /* (4.7.4) */
static void forward_delay_timer_expiry(int port_no) /* (4.7.5) */
{
- if (port_info[port_no].state == Listening) { /* (4.7.5.1) */
+ if (port_info[port_no].state == Listening)
+ { /* (4.7.5.1) */
set_port_state(port_no, Learning); /* (4.7.5.1.1) */
start_forward_delay_timer(port_no); /* (4.7.5.1.2) */
- } else if (port_info[port_no].state == Learning) { /* (4.7.5.2) */
+ }
+ else if (port_info[port_no].state == Learning)
+ {
+ /* (4.7.5.2) */
set_port_state(port_no, Forwarding); /* (4.7.5.2.1) */
- if (designated_for_some_port()) { /* (4.7.5.2.2) */
+ if (designated_for_some_port())
+ { /* (4.7.5.2.2) */
topology_change_detection(); /* (4.6.14.2.2) */
}
@@ -667,13 +766,15 @@ static void forward_delay_timer_expiry(int port_no) /* (4.7.5) */
static int designated_for_some_port(void)
{
- int port_no;
-
+ int port_no;
- for (port_no = One; port_no <= No_of_ports; port_no++) {
+ for (port_no = One; port_no <= No_of_ports; port_no++)
+ {
+ if(port_info[port_no].state == Disabled)
+ continue;
if ((br_cmp(port_info[port_no].designated_bridge.BRIDGE_ID,
- bridge_info.bridge_id.BRIDGE_ID) == 0)
- ) {
+ bridge_info.bridge_id.BRIDGE_ID) == 0))
+ {
return (TRUE);
}
}
@@ -688,26 +789,38 @@ static void tcn_timer_expiry(void)
static void topology_change_timer_expiry(void)
{ /* (4.7.7) */
- bridge_info.top_change_detected = 0;
+ bridge_info.top_change_detected = 0; /* (4.7.7.1) */
bridge_info.top_change = 0;
/* (4.7.7.2) */
}
static void hold_timer_expiry(int port_no) /* (4.7.8) */
{
- if (port_info[port_no].config_pending) {
+ if (port_info[port_no].config_pending)
+ {
transmit_config(port_no); /* (4.7.8.1) */
} /* (4.6.1.2.3) */
}
__initfunc(void br_init(void))
{ /* (4.8.1) */
- int port_no;
+ int port_no;
+
+ printk(KERN_INFO "Ethernet Bridge 005 for NET3.037 (Linux 2.1)\n");
+
+ /*
+ * Form initial topology change time.
+ * The topology change timer is only used if this is the root bridge.
+ */
+
+ bridge_info.topology_change_time = BRIDGE_MAX_AGE + BRIDGE_FORWARD_DELAY; /* (4.5.3.13) */
- printk(KERN_INFO "Ethernet Bridge 003 for NET3.037 (Linux 2.1)\n");
bridge_info.designated_root = bridge_info.bridge_id; /* (4.8.1.1) */
bridge_info.root_path_cost = Zero;
bridge_info.root_port = No_port;
+#ifdef DEBUG_STP
+ printk(KERN_INFO "br_init: becomes root\n");
+#endif
bridge_info.bridge_max_age = BRIDGE_MAX_AGE;
bridge_info.bridge_hello_time = BRIDGE_HELLO_TIME;
@@ -722,17 +835,22 @@ __initfunc(void br_init(void))
bridge_info.top_change = 0;
stop_tcn_timer();
stop_topology_change_timer();
+ memset(newfdb, 0, sizeof(newfdb));
for (port_no = One; port_no <= No_of_ports; port_no++) { /* (4.8.1.4) */
+ /* initial state = Enable */
+ user_port_state[port_no] = ~Disabled;
+ port_priority[port_no] = 128;
br_init_port(port_no);
disable_port(port_no);
}
+#if 0 /* JRP: We are not UP ! Wait for the start command */
port_state_selection(); /* (4.8.1.5) */
config_bpdu_generation(); /* (4.8.1.6) */
-
/* initialize system timer */
tl.expires = jiffies+HZ; /* 1 second */
tl.function = br_tick;
add_timer(&tl);
+#endif
register_netdevice_notifier(&br_dev_notifier);
br_stats.flags = 0; /*BR_UP | BR_DEBUG*/; /* enable bridge */
@@ -741,8 +859,14 @@ __initfunc(void br_init(void))
/*start_hello_timer();*/
}
+static inline unsigned short make_port_id(int port_no)
+{
+ return (port_priority[port_no] << 8) | port_no;
+}
+
static void br_init_port(int port_no)
{
+ port_info[port_no].port_id = make_port_id(port_no);
become_designated_port(port_no); /* (4.8.1.4.1) */
set_port_state(port_no, Blocking); /* (4.8.1.4.2) */
port_info[port_no].top_change_ack = 0;
@@ -787,10 +911,12 @@ static void set_bridge_priority(bridge_id_t *new_bridge_id)
/* (4.8.4) */
{
- int root;
- int port_no;
+ int root;
+ int port_no;
root = root_bridge();
for (port_no = One; port_no <= No_of_ports; port_no++) { /* (4.8.4.2) */
+ if(port_info[port_no].state == Disabled)
+ continue;
if (designated_port(port_no)) {
port_info[port_no].designated_bridge = *new_bridge_id;
}
@@ -810,9 +936,10 @@ static void set_bridge_priority(bridge_id_t *new_bridge_id)
}
}
-static void set_port_priority(int port_no, unsigned short new_port_id)
+static void set_port_priority(int port_no)
/* (4.8.5) */
-{
+{int new_port_id = make_port_id(port_no);
+
if (designated_port(port_no)) { /* (4.8.5.2) */
port_info[port_no].designated_port = new_port_id;
}
@@ -825,7 +952,8 @@ static void set_port_priority(int port_no, unsigned short new_port_id)
< port_info[port_no].designated_port
)
- ) {
+ )
+ {
become_designated_port(port_no); /* (4.8.5.4.1) */
port_state_selection(); /* (4.8.5.4.2) */
}
@@ -841,27 +969,33 @@ static void set_path_cost(int port_no, unsigned short path_cost)
static void br_tick(unsigned long arg)
{
- int port_no;
+ int port_no;
+
+ if(!(br_stats.flags & BR_UP))
+ return; /* JRP: we have been shot down */
- if (hello_timer_expired()) {
+ if (hello_timer_expired())
hello_timer_expiry();
- }
- if (tcn_timer_expired()) {
+
+ if (tcn_timer_expired())
tcn_timer_expiry();
- }
- if (topology_change_timer_expired()) {
+
+ if (topology_change_timer_expired())
topology_change_timer_expiry();
- }
- for (port_no = One; port_no <= No_of_ports; port_no++) {
- if (forward_delay_timer_expired(port_no)) {
+
+ for (port_no = One; port_no <= No_of_ports; port_no++)
+ {
+ if(port_info[port_no].state == Disabled)
+ continue;
+
+ if (forward_delay_timer_expired(port_no))
forward_delay_timer_expiry(port_no);
- }
- if (message_age_timer_expired(port_no)) {
+
+ if (message_age_timer_expired(port_no))
message_age_timer_expiry(port_no);
- }
- if (hold_timer_expired(port_no)) {
+
+ if (hold_timer_expired(port_no))
hold_timer_expiry(port_no);
- }
}
/* call me again sometime... */
tl.expires = jiffies+HZ; /* 1 second */
@@ -882,7 +1016,8 @@ static void stop_hello_timer(void)
static int hello_timer_expired(void)
{
- if (hello_timer.active && (++hello_timer.value >= bridge_info.hello_time)) {
+ if (hello_timer.active && (++hello_timer.value >= bridge_info.hello_time))
+ {
hello_timer.active = FALSE;
return (TRUE);
}
@@ -902,8 +1037,8 @@ static void stop_tcn_timer(void)
static int tcn_timer_expired(void)
{
- if (tcn_timer.active && (++tcn_timer.value >=
- bridge_info.bridge_hello_time)) {
+ if (tcn_timer.active && (++tcn_timer.value >= bridge_info.bridge_hello_time))
+ {
tcn_timer.active = FALSE;
return (TRUE);
}
@@ -925,9 +1060,8 @@ static void stop_topology_change_timer(void)
static int topology_change_timer_expired(void)
{
if (topology_change_timer.active
- && (++topology_change_timer.value
- >= bridge_info.topology_change_time
- )) {
+ && (++topology_change_timer.value >= bridge_info.topology_change_time ))
+ {
topology_change_timer.active = FALSE;
return (TRUE);
}
@@ -947,8 +1081,8 @@ static void stop_message_age_timer(int port_no)
static int message_age_timer_expired(int port_no)
{
- if (message_age_timer[port_no].active &&
- (++message_age_timer[port_no].value >= bridge_info.max_age)) {
+ if (message_age_timer[port_no].active && (++message_age_timer[port_no].value >= bridge_info.max_age))
+ {
message_age_timer[port_no].active = FALSE;
return (TRUE);
}
@@ -968,12 +1102,12 @@ static void stop_forward_delay_timer(int port_no)
static int forward_delay_timer_expired(int port_no)
{
- if (forward_delay_timer[port_no].active &&
- (++forward_delay_timer[port_no].value >= bridge_info.forward_delay)) {
- forward_delay_timer[port_no].active = FALSE;
- return (TRUE);
- }
- return (FALSE);
+ if (forward_delay_timer[port_no].active && (++forward_delay_timer[port_no].value >= bridge_info.forward_delay))
+ {
+ forward_delay_timer[port_no].active = FALSE;
+ return (TRUE);
+ }
+ return (FALSE);
}
static void start_hold_timer(int port_no)
@@ -990,7 +1124,8 @@ static void stop_hold_timer(int port_no)
static int hold_timer_expired(int port_no)
{
if (hold_timer[port_no].active &&
- (++hold_timer[port_no].value >= bridge_info.hold_time)) {
+ (++hold_timer[port_no].value >= bridge_info.hold_time))
+ {
hold_timer[port_no].active = FALSE;
return (TRUE);
}
@@ -998,113 +1133,112 @@ static int hold_timer_expired(int port_no)
}
-static int send_config_bpdu(int port_no, Config_bpdu *config_bpdu)
+static struct sk_buff *alloc_bridge_skb(int port_no, int pdu_size, char *pdu_name)
{
struct sk_buff *skb;
struct device *dev = port_info[port_no].dev;
- int size;
struct ethhdr *eth;
-
- if (port_info[port_no].state == Disabled) {
- printk(KERN_DEBUG "send_config_bpdu: port %i not valid\n",port_no);
- return(-1);
+ int size = dev->hard_header_len + BRIDGE_LLC1_HS + pdu_size;
+ unsigned char *llc_buffer;
+ int pad_size = 60 - size;
+
+ size = 60; /* minimum Ethernet frame - CRC */
+
+ if (port_info[port_no].state == Disabled)
+ {
+ printk(KERN_DEBUG "send_%s_bpdu: port %i not valid\n", pdu_name, port_no);
+ return NULL;
}
+
+ skb = alloc_skb(size, GFP_ATOMIC);
+ if (skb == NULL)
+ {
+ printk(KERN_DEBUG "send_%s_bpdu: no skb available\n", pdu_name);
+ return NULL;
+ }
+ skb->dev = dev;
+ skb->mac.raw = skb->h.raw = skb_put(skb,size);
+ memset(skb->h.raw + 60 - pad_size, 0xa5, pad_size);
+ eth = skb->mac.ethernet;
+ memcpy(eth->h_dest, bridge_ula, ETH_ALEN);
+ memcpy(eth->h_source, dev->dev_addr, ETH_ALEN);
+
+ if (br_stats.flags & BR_DEBUG)
+ printk("send_%s_bpdu: port %i src %02x:%02x:%02x:%02x:%02x:%02x\n",
+ pdu_name,
+ port_no,
+ eth->h_source[0],
+ eth->h_source[1],
+ eth->h_source[2],
+ eth->h_source[3],
+ eth->h_source[4],
+ eth->h_source[5]);
+#if 0
+ /* 8038 is used in older DEC spanning tree protocol which uses a
+ * different pdu layout as well
+ */
+ eth->h_proto = htons(0x8038);
+#endif
+ eth->h_proto = htons(pdu_size + BRIDGE_LLC1_HS);
+
+ skb->h.raw += skb->dev->hard_header_len;
+ llc_buffer = skb->h.raw;
+ *llc_buffer++ = BRIDGE_LLC1_DSAP;
+ *llc_buffer++ = BRIDGE_LLC1_SSAP;
+ *llc_buffer++ = BRIDGE_LLC1_CTRL;
+ /* set h.raw to where the bpdu starts */
+ skb->h.raw += BRIDGE_LLC1_HS;
+
+ /* mark that we've been here... */
+ skb->pkt_bridged = IS_BRIDGED;
+ return skb;
+}
+
+static int send_config_bpdu(int port_no, Config_bpdu *config_bpdu)
+{
+ struct sk_buff *skb;
- if (br_stats.flags & BR_DEBUG)
- printk("send_config_bpdu: ");
/*
- * create and send the message
+ * Create and send the message
*/
- size = dev->hard_header_len + sizeof(Config_bpdu);
- skb = alloc_skb(size, GFP_ATOMIC);
- if (skb == NULL)
- {
- printk(KERN_DEBUG "send_config_bpdu: no skb available\n");
+
+ skb = alloc_bridge_skb(port_no, BRIDGE_BPDU_8021_CONFIG_SIZE,
+ "config");
+ if (skb == NULL)
return(-1);
- }
- skb->dev = dev;
- skb->mac.raw = skb->h.raw = skb_put(skb, size);
- eth = skb->mac.ethernet;
- memcpy(eth->h_dest, bridge_ula, ETH_ALEN);
- memcpy(eth->h_source, dev->dev_addr, ETH_ALEN);
- if (br_stats.flags & BR_DEBUG)
- printk("port %i src %02x:%02x:%02x:%02x:%02x:%02x\
- dest %02x:%02x:%02x:%02x:%02x:%02x\n",
- port_no,
- eth->h_source[0],
- eth->h_source[1],
- eth->h_source[2],
- eth->h_source[3],
- eth->h_source[4],
- eth->h_source[5],
- eth->h_dest[0],
- eth->h_dest[1],
- eth->h_dest[2],
- eth->h_dest[3],
- eth->h_dest[4],
- eth->h_dest[5]);
- eth->h_proto = htons(0x8038);
- skb->h.raw += skb->dev->hard_header_len;
- memcpy(skb->h.raw, config_bpdu, sizeof(Config_bpdu));
+ /* copy fields before "flags" */
+ memcpy(skb->h.raw, config_bpdu, BRIDGE_BPDU_8021_CONFIG_FLAG_OFFSET);
- /* won't get bridged again... */
- skb->pkt_bridged = IS_BRIDGED;
- skb->dev=dev;
- dev_queue_xmit(skb);
- return(0);
-}
+ /* build the "flags" field */
+ *(skb->h.raw+BRIDGE_BPDU_8021_CONFIG_FLAG_OFFSET) = 0;
+ if (config_bpdu->top_change_ack)
+ *(skb->h.raw+BRIDGE_BPDU_8021_CONFIG_FLAG_OFFSET) |= 0x80;
+ if (config_bpdu->top_change)
+ *(skb->h.raw+BRIDGE_BPDU_8021_CONFIG_FLAG_OFFSET) |= 0x01;
+
+ config_bpdu_hton(config_bpdu);
+ /* copy the rest */
+ memcpy(skb->h.raw+BRIDGE_BPDU_8021_CONFIG_FLAG_OFFSET+1,
+ (char*)&(config_bpdu->root_id),
+ BRIDGE_BPDU_8021_CONFIG_SIZE-1-BRIDGE_BPDU_8021_CONFIG_FLAG_OFFSET);
+ dev_queue_xmit(skb);
+ return(0);
+}
+
static int send_tcn_bpdu(int port_no, Tcn_bpdu *bpdu)
{
struct sk_buff *skb;
- struct device *dev = port_info[port_no].dev;
- int size;
- struct ethhdr *eth;
-
- if (port_info[port_no].state == Disabled) {
- printk(KERN_DEBUG "send_tcn_bpdu: port %i not valid\n",port_no);
- return(-1);
- }
- if (br_stats.flags & BR_DEBUG)
- printk("send_tcn_bpdu: ");
- size = sizeof(Tcn_bpdu) + dev->hard_header_len;
- skb = alloc_skb(size, GFP_ATOMIC);
- if (skb == NULL) {
- printk(KERN_DEBUG "send_tcn_bpdu: no skb available\n");
- return(-1);
- }
- skb->dev = dev;
- skb->mac.raw = skb->h.raw = skb_put(skb,size);
- eth = skb->mac.ethernet;
- memcpy(eth->h_dest, bridge_ula, ETH_ALEN);
- memcpy(eth->h_source, dev->dev_addr, ETH_ALEN);
- if (br_stats.flags & BR_DEBUG)
- printk("port %i src %02x:%02x:%02x:%02x:%02x:%02x\
- dest %02x:%02x:%02x:%02x:%02x:%02x\n",
- port_no,
- eth->h_source[0],
- eth->h_source[1],
- eth->h_source[2],
- eth->h_source[3],
- eth->h_source[4],
- eth->h_source[5],
- eth->h_dest[0],
- eth->h_dest[1],
- eth->h_dest[2],
- eth->h_dest[3],
- eth->h_dest[4],
- eth->h_dest[5]);
- eth->h_proto = htons(0x8038);
-
- skb->h.raw += skb->dev->hard_header_len;
- memcpy(skb->h.raw, bpdu, sizeof(Tcn_bpdu));
-
- /* mark that we've been here... */
- skb->pkt_bridged = IS_BRIDGED;
- skb->dev=dev;
- dev_queue_xmit(skb);
- return(0);
+
+ skb = alloc_bridge_skb(port_no, sizeof(Tcn_bpdu), "tcn");
+ if (skb == NULL)
+ return(-1);
+
+ memcpy(skb->h.raw, bpdu, sizeof(Tcn_bpdu));
+
+ dev_queue_xmit(skb);
+ return(0);
}
static int br_device_event(struct notifier_block *unused, unsigned long event, void *ptr)
@@ -1116,52 +1250,59 @@ static int br_device_event(struct notifier_block *unused, unsigned long event, v
if (dev->flags & IFF_LOOPBACK)
return(NOTIFY_DONE);
- switch (event) {
- case NETDEV_DOWN:
- if (br_stats.flags & BR_DEBUG)
- printk("br_device_event: NETDEV_DOWN...\n");
- /* find our device and mark it down */
- for (i = One; i <= No_of_ports; i++) {
- if (port_info[i].dev == dev) {
- disable_port(i);
- return NOTIFY_DONE;
- break;
+ switch (event)
+ {
+ case NETDEV_DOWN:
+ if (br_stats.flags & BR_DEBUG)
+ printk(KERN_DEBUG "br_device_event: NETDEV_DOWN...\n");
+ /* find our device and mark it down */
+ for (i = One; i <= No_of_ports; i++)
+ {
+ if (port_info[i].dev == dev)
+ {
+ disable_port(i);
+ return NOTIFY_DONE;
+ break;
+ }
}
- }
- break;
- case NETDEV_UP:
- if (br_stats.flags & BR_DEBUG)
- printk("br_device_event: NETDEV_UP...\n");
- /* Only handle ethernet ports */
- if(dev->type!=ARPHRD_ETHER && dev->type!=ARPHRD_LOOPBACK)
- return NOTIFY_DONE;
- /* look up an unused device and enable it */
- for (i = One; i <= No_of_ports; i++) {
- if ((port_info[i].dev == (struct device *)0) ||
- (port_info[i].dev == dev)) {
- port_info[i].dev = dev;
- enable_port(i);
- set_path_cost(i, br_port_cost(dev));
- set_port_priority(i, 128);
- port_info[i].port_id = i;
- /* set bridge addr from 1st device addr */
- if ((bridge_info.bridge_id.BRIDGE_ID[0] == 0) &&
- (bridge_info.bridge_id.BRIDGE_ID[1] == 0)) {
- memcpy(bridge_info.bridge_id.BRIDGE_ID_ULA, dev->dev_addr, 6);
- bridge_info.bridge_id.BRIDGE_PRIORITY = port_info[i].port_id;
- set_bridge_priority(&bridge_info.bridge_id);
- }
- make_forwarding(i);
+ break;
+ case NETDEV_UP:
+ if (br_stats.flags & BR_DEBUG)
+ printk(KERN_DEBUG "br_device_event: NETDEV_UP...\n");
+ /* Only handle ethernet ports */
+ if(dev->type!=ARPHRD_ETHER && dev->type!=ARPHRD_LOOPBACK)
return NOTIFY_DONE;
- break;
+ /* look up an unused device and enable it */
+ for (i = One; i <= No_of_ports; i++)
+ {
+ if (port_info[i].dev == NULL || port_info[i].dev == dev)
+ {
+ port_info[i].dev = dev;
+ port_info[i].port_id = i;
+ /* set bridge addr from 1st device addr */
+ if (((htonl(bridge_info.bridge_id.BRIDGE_ID[0])&0xffff) == 0) &&
+ (bridge_info.bridge_id.BRIDGE_ID[1] == 0))
+ {
+ memcpy(bridge_info.bridge_id.BRIDGE_ID_ULA, dev->dev_addr, 6);
+ if(bridge_info.bridge_id.BRIDGE_PRIORITY == 0)
+ bridge_info.bridge_id.BRIDGE_PRIORITY = htons(32768);
+ set_bridge_priority(&bridge_info.bridge_id);
+ }
+ br_add_local_mac(dev->dev_addr);
+ if((br_stats.flags & BR_UP) &&
+ (user_port_state[i] != Disabled))
+ {
+ /* don't start if user said so */
+ enable_port(i);
+ set_path_cost(i, br_port_cost(dev));
+ set_port_priority(i);
+ make_forwarding(i);
+ }
+ return NOTIFY_DONE;
+ break;
+ }
}
- }
- break;
-#if 0
- default:
- printk("br_device_event: unknown event [%x]\n",
- (unsigned int)event);
-#endif
+ break;
}
return NOTIFY_DONE;
}
@@ -1175,10 +1316,9 @@ static int br_device_event(struct notifier_block *unused, unsigned long event, v
int br_receive_frame(struct sk_buff *skb) /* 3.5 */
{
int port;
+ Port_data *p;
struct ethhdr *eth;
- if (br_stats.flags & BR_DEBUG)
- printk("br_receive_frame: ");
/* sanity */
if (!skb) {
printk(KERN_CRIT "br_receive_frame: no skb!\n");
@@ -1189,88 +1329,79 @@ int br_receive_frame(struct sk_buff *skb) /* 3.5 */
/* check for loopback */
if (skb->dev->flags & IFF_LOOPBACK)
- return(0);
+ return 0 ;
port = find_port(skb->dev);
+ if(!port)
+ return 0;
+
skb->h.raw = skb->mac.raw;
eth = skb->mac.ethernet;
- if (br_stats.flags & BR_DEBUG)
- printk("port %i src %02x:%02x:%02x:%02x:%02x:%02x\
- dest %02x:%02x:%02x:%02x:%02x:%02x\n",
- port,
- eth->h_source[0],
- eth->h_source[1],
- eth->h_source[2],
- eth->h_source[3],
- eth->h_source[4],
- eth->h_source[5],
- eth->h_dest[0],
- eth->h_dest[1],
- eth->h_dest[2],
- eth->h_dest[3],
- eth->h_dest[4],
- eth->h_dest[5]);
+ p = &port_info[port];
+
+ if(p->state == Disabled)
+ {
+ /* We are here if BR_UP even if this port is Disabled.
+ * Send everything up
+ */
+ skb->pkt_type = PACKET_HOST;
+ ++br_stats_cnt.port_disable_up_stack;
+ return(0); /* pass frame up our stack (this will */
+ /* happen in net_bh() in dev.c) */
+ }
+
+ /* Here only if not disable.
+ * Remark: only frames going up will show up in NIT (tcpdump)
+ */
- if (!port) {
- if(br_stats.flags&BR_DEBUG)
- printk("\nbr_receive_frame: no port!\n");
- return(0);
+ /* JRP: even if port is Blocking we need to process the Spanning Tree
+ * frames to keep the port in that state
+ */
+ if (memcmp(eth->h_dest, bridge_ula, ETH_ALEN) == 0)
+ {
+ ++br_stats_cnt.rcv_bpdu;
+ br_bpdu(skb, port); /* br_bpdu consumes skb */
+ return(1);
}
-
- switch (port_info[port].state)
+ switch (p->state)
{
case Learning:
- (void) br_learn(skb, port); /* 3.8 */
+ if(br_learn(skb, port))
+ { /* 3.8 */
+ ++br_stats_cnt.drop_multicast;
+ return br_drop(skb);
+ }
/* fall through */
case Listening:
- /* process BPDUs */
- if (memcmp(eth->h_dest, bridge_ula, 6) == 0) {
- br_bpdu(skb);
- return(1); /* br_bpdu consumes skb */
- }
/* fall through */
case Blocking:
- /* fall through */
- case Disabled:
- /* should drop frames, but for now, we let
- * them get passed up to the next higher layer
+ ++br_stats_cnt.notForwarding;
return(br_drop(skb));
- */
- return(0); /* pass frame up stack */
+ /*
+ case Disabled: is now handled before this switch !
+ Keep the break to allow GCC to use a jmp table.
+ */
break;
case Forwarding:
- (void) br_learn(skb, port); /* 3.8 */
- /* process BPDUs */
- if (memcmp(eth->h_dest, bridge_ula,
- ETH_ALEN) == 0)
- {
- /*printk("frame bpdu processor for me!!!\n");*/
- br_bpdu(skb);
- return(1); /* br_bpdu consumes skb */
- }
- /* is frame for me? */
- if (memcmp(eth->h_dest,
- port_info[port].dev->dev_addr,
- ETH_ALEN) == 0)
- {
- /* Packet is for us */
- skb->pkt_type = PACKET_HOST;
- return(0); /* pass frame up our stack (this will */
- /* happen in net_bh() in dev.c) */
+ if(br_learn(skb, port)) { /* 3.8 */
+ ++br_stats_cnt.drop_multicast;
+ return br_drop(skb);
}
/* Now this frame came from one of bridged
- ports, and it appears to be not for me;
- this means we should attempt to forward it.
- But actually this frame can still be for me
- [as well] if it is destined to one of our
- multicast groups. br_forward() will not
- consume the frame if this is the case */
+ ports this means we should attempt to forward it.
+ JRP: local addresses are now in the AVL tree,
+ br_forward will pass frames up if it matches
+ one of our local MACs or if it is a multicast
+ group address.
+ br_forward() will not consume the frame if this
+ is the case */
return(br_forward(skb, port));
default:
printk(KERN_DEBUG "br_receive_frame: port [%i] unknown state [%i]\n",
- port, port_info[port].state);
- return(0); /* pass frame up stack? */
+ port, p->state);
+ ++br_stats_cnt.unknown_state;
+ return(br_drop(skb)); /* discard frame */
}
}
@@ -1304,15 +1435,17 @@ int br_tx_frame(struct sk_buff *skb) /* 3.5 */
/* if bridging is not enabled on the port we are going to send
to, we have nothing to do with this frame, hands off */
- if (! find_port(skb->dev))
+ if (((port=find_port(skb->dev))==0)||(port_info[port].state==Disabled)) {
+ ++br_stats_cnt.port_disable;
return(0);
-
+ }
+ ++br_stats_cnt.port_not_disable;
skb->mac.raw = skb->h.raw = skb->data;
eth = skb->mac.ethernet;
- port = 0; /* an impossible port */
+ port = 0; /* an impossible port (locally generated) */
if (br_stats.flags & BR_DEBUG)
- printk("br_tx_fr : port %i src %02x:%02x:%02x:%02x:%02x:%02x\
- dest %02x:%02x:%02x:%02x:%02x:%02x\n",
+ printk("br_tx_fr : port %i src %02x:%02x:%02x:%02x:%02x:%02x"
+ " dest %02x:%02x:%02x:%02x:%02x:%02x\n",
port,
eth->h_source[0],
eth->h_source[1],
@@ -1329,55 +1462,135 @@ int br_tx_frame(struct sk_buff *skb) /* 3.5 */
return(br_forward(skb, port));
}
+static void br_add_local_mac(unsigned char *mac)
+{
+ struct fdb *f;
+ f = (struct fdb *)kmalloc(sizeof(struct fdb), GFP_ATOMIC);
+ if (!f)
+ {
+ printk(KERN_CRIT "br_add_local_mac: unable to malloc fdb\n");
+ return;
+ }
+ f->port = 0; /* dest port == 0 =>local */
+ memcpy(f->ula, mac, 6);
+ f->timer = 0; /* will not aged anyway */
+ f->flags = 0; /* not valid => br_forward special route */
+ /*
+ * add entity to AVL tree. If entity already
+ * exists in the tree, update the fields with
+ * what we have here.
+ */
+ if (br_avl_insert(f) != NULL)
+ {
+ /* Already in */
+ kfree(f);
+ }
+}
+
+/* Avoid broadcast loop by limiting the number of broacast frames per
+ * period. The idea is to limit this per source
+ * returns: 0 if limit is not reached
+ * 1 if frame should be dropped
+ */
+
+static inline int mcast_quench(struct fdb *f)
+{
+ if(f->mcast_count++ == 0) /* first time */
+ f->mcast_timer = jiffies;
+ else {
+ if(f->mcast_count > max_mcast_per_period) {
+ if(jiffies > (f->mcast_timer + mcast_hold_time))
+ f->mcast_count = 0;
+ else return 1;
+ }
+ }
+ return 0;
+}
+
/*
* this routine returns 0 when it learns (or updates) from the
- * frame, and -1 if the frame is simply discarded due to port
- * state or lack of resources...
+ * frame, and 1 if we must dropped the frame.
*/
static int br_learn(struct sk_buff *skb, int port) /* 3.8 */
{
- struct fdb *f;
+ struct fdb *f, *oldfdb;
+ Port_data *p = &port_info[port];
+ struct ethhdr *eth = skb->mac.ethernet;
- switch (port_info[port].state) {
- case Listening:
- case Blocking:
- case Disabled:
- default:
- return(-1);
- /* break; */
- case Learning:
- case Forwarding:
- /* don't keep group addresses in the tree */
- if (skb->mac.ethernet->h_source[0] & 0x01)
- return(-1);
-
- f = (struct fdb *)kmalloc(sizeof(struct fdb),
- GFP_ATOMIC);
+ /* JRP: no reason to check port state again. We are called by
+ * br_receive_frame() only when in Learning or Forwarding
+ * Remark: code not realigned yet to keep diffs smaller
+ */
- if (!f) {
- printk(KERN_DEBUG "br_learn: unable to malloc fdb\n");
- return(-1);
- }
- f->port = port; /* source port */
- memcpy(f->ula, skb->mac.ethernet->h_source, 6);
- f->timer = CURRENT_TIME;
- f->flags = FDB_ENT_VALID;
- /*
- * add entity to AVL tree. If entity already
- * exists in the tree, update the fields with
- * what we have here.
- */
- if (br_avl_insert(f) == 0) { /* update */
- kfree(f);
- return(0);
- }
- /* add to head of port chain */
- f->fdb_next = port_info[port].fdb;
- port_info[port].fdb = f;
- return(0);
- /* break */
+ /* don't keep group addresses in the tree */
+ if (eth->h_source[0] & 0x01)
+ return 0;
+
+ if((f= newfdb[port]) == NULL)
+ {
+ newfdb[port] = f = (struct fdb *)kmalloc(sizeof(struct fdb), GFP_ATOMIC);
+ if (!f)
+ {
+ printk(KERN_DEBUG "br_learn: unable to malloc fdb\n");
+ return(-1); /* this drop the frame */
+ }
+ }
+ f->port = port; /* source port */
+ memcpy(f->ula, eth->h_source, 6);
+ f->timer = CURRENT_TIME;
+ f->flags = FDB_ENT_VALID;
+ /*
+ * add entity to AVL tree. If entity already
+ * exists in the tree, update the fields with
+ * what we have here.
+ */
+ if ((oldfdb = br_avl_insert(f)))
+ {
+ /* update if !NULL */
+ if((eth->h_dest[0] & 0x01) && /* multicast */ mcast_quench(oldfdb))
+ return 1;
+ return 0;
}
+ newfdb[port] = NULL; /* force kmalloc next time */
+ f->mcast_count = 0;
+ /* add to head of port chain */
+ f->fdb_next = p->fdb;
+ p->fdb = f;
+ allocated_fdb_cnt++;
+ return 0;
+}
+
+/* JRP: always called under br_receive_frame(). No need for Q protection. */
+
+void requeue_fdb(struct fdb *node, int new_port)
+{
+ Port_data *p = &port_info[node->port];
+
+ /* dequeue */
+ if(p->fdb == node)
+ p->fdb = node->fdb_next;
+ else
+ {
+ struct fdb *prev;
+
+ for(prev = p->fdb; prev; prev = prev->fdb_next)
+ if (prev->fdb_next == node)
+ break;
+
+ if(prev != NULL)
+ prev->fdb_next = node->fdb_next;
+ else
+ {
+ /* Forget about this update. */
+ printk(KERN_ERR "br:requeue_fdb\n");
+ return;
+ }
+ }
+ /* enqueue */
+ node->port = new_port;
+ node->fdb_next = port_info[new_port].fdb;
+ port_info[new_port].fdb = node;
}
/*
@@ -1429,26 +1642,43 @@ static int br_forward(struct sk_buff *skb, int port) /* 3.7 */
* This probably should be dropped since the flood will
* have sent it anyway.
*/
- if (port == 0) /* locally generated */
+ if (port == 0)
+ {
+ /* Locally generated */
+ ++br_stats_cnt.local_multicast;
return(br_dev_drop(skb));
+ }
+ ++br_stats_cnt.forwarded_multicast;
return(0);
- } else {
- /* locate port to forward to */
+ }
+ else
+ {
+ /* unicast frame, locate port to forward to */
f = br_avl_find_addr(skb->mac.ethernet->h_dest);
/*
* Send flood and drop.
*/
- if (!f || !(f->flags & FDB_ENT_VALID)) {
- /* not found; flood all ports */
+ if (!f || !(f->flags & FDB_ENT_VALID))
+ {
+ if(f && (f->port == 0))
+ {
+ skb->pkt_type = PACKET_HOST;
+ ++br_stats_cnt.forwarded_unicast_up_stack;
+ return(0);
+ }
+ /* not found or too old; flood all ports */
+ ++br_stats_cnt.flood_unicast;
br_flood(skb, port);
return(br_dev_drop(skb));
}
/*
* Sending
*/
- if (f->port!=port && port_info[f->port].state == Forwarding) {
- /* has entry expired? */
- if (f->timer + fdb_aging_time < CURRENT_TIME) {
+ if (f->port!=port && port_info[f->port].state == Forwarding)
+ {
+ /* Has entry expired? */
+ if (f->timer + fdb_aging_time < CURRENT_TIME)
+ {
/* timer expired, invalidate entry */
f->flags &= ~FDB_ENT_VALID;
if (br_stats.flags & BR_DEBUG)
@@ -1456,9 +1686,11 @@ static int br_forward(struct sk_buff *skb, int port) /* 3.7 */
/*
* Send flood and drop original
*/
+ ++br_stats_cnt.aged_flood_unicast;
br_flood(skb, port);
return(br_dev_drop(skb));
}
+ ++br_stats_cnt.forwarded_unicast;
/* mark that's we've been here... */
skb->pkt_bridged = IS_BRIDGED;
@@ -1477,7 +1709,25 @@ static int br_forward(struct sk_buff *skb, int port) /* 3.7 */
skb->priority = 1;
dev_queue_xmit(skb);
return(1); /* skb has been consumed */
- } else {
+ }
+ else
+ {
+ /* JRP: Needs to aged entry as well, if topology changes
+ * the entry would not age. Got this while swapping
+ * two cables !
+ *
+ * Has entry expired?
+ */
+
+ if (f->timer + fdb_aging_time < CURRENT_TIME)
+ {
+ /* timer expired, invalidate entry */
+ f->flags &= ~FDB_ENT_VALID;
+ if (br_stats.flags & BR_DEBUG)
+ printk("fdb entry expired...\n");
+ ++br_stats_cnt.drop_same_port_aged;
+ }
+ else ++br_stats_cnt.drop_same_port;
/*
* Arrived on the right port, we discard
*/
@@ -1499,7 +1749,7 @@ static int br_flood(struct sk_buff *skb, int port)
for (i = One; i <= No_of_ports; i++)
{
- if (i == port)
+ if (i == port) /* don't send back where we got it */
continue;
if (port_info[i].state == Forwarding)
{
@@ -1515,8 +1765,12 @@ static int br_flood(struct sk_buff *skb, int port)
/* printk("Flood to port %d\n",i);*/
nskb->h.raw = nskb->data + ETH_HLEN;
+#if LINUX_VERSION_CODE >= 0x20100
nskb->priority = 1;
dev_queue_xmit(nskb);
+#else
+ dev_queue_xmit(nskb,nskb->dev,1);
+#endif
}
}
return(0);
@@ -1527,12 +1781,16 @@ static int find_port(struct device *dev)
int i;
for (i = One; i <= No_of_ports; i++)
- if ((port_info[i].dev == dev) &&
- (port_info[i].state != Disabled))
+ if (port_info[i].dev == dev)
return(i);
return(0);
}
+/*
+ * FIXME: This needs to come from the device structs, eg for
+ * 10,100,1Gbit ethernet.
+ */
+
static int br_port_cost(struct device *dev) /* 4.10.2 */
{
if (strncmp(dev->name, "eth", 3) == 0) /* ethernet */
@@ -1546,43 +1804,103 @@ static int br_port_cost(struct device *dev) /* 4.10.2 */
* this routine always consumes the skb
*/
-static void br_bpdu(struct sk_buff *skb) /* consumes skb */
+static void br_bpdu(struct sk_buff *skb, int port) /* consumes skb */
{
- Tcn_bpdu *bpdu;
- int port;
-
- port = find_port(skb->dev);
- if (port == 0) { /* unknown port */
- br_drop(skb);
- return;
- }
-
- bpdu = (Tcn_bpdu *) (skb->data + ETH_HLEN);
- switch (bpdu->type) {
- case BPDU_TYPE_CONFIG:
- received_config_bpdu(port, (Config_bpdu *)bpdu);
- break;
- case BPDU_TYPE_TOPO_CHANGE:
- received_tcn_bpdu(port, bpdu);
- break;
- default:
- printk(KERN_DEBUG "br_bpdu: received unknown bpdu, type = %i\n",
- bpdu->type);
+ char *bufp = skb->data + ETH_HLEN;
+ Tcn_bpdu *bpdu = (Tcn_bpdu *) (bufp + BRIDGE_LLC1_HS);
+ Config_bpdu rcv_bpdu;
+
+ if((*bufp++ == BRIDGE_LLC1_DSAP) && (*bufp++ == BRIDGE_LLC1_SSAP) &&
+ (*bufp++ == BRIDGE_LLC1_CTRL) &&
+ (bpdu->protocol_id == BRIDGE_BPDU_8021_PROTOCOL_ID) &&
+ (bpdu->protocol_version_id == BRIDGE_BPDU_8021_PROTOCOL_VERSION_ID))
+ {
+
+ switch (bpdu->type)
+ {
+ case BPDU_TYPE_CONFIG:
+ /* realign for portability to RISC */
+ memcpy((char*)&rcv_bpdu, bufp,
+ BRIDGE_BPDU_8021_CONFIG_FLAG_OFFSET);
+ bufp+= BRIDGE_BPDU_8021_CONFIG_FLAG_OFFSET;
+ rcv_bpdu.top_change_ack =
+ (*bufp & TOPOLOGY_CHANGE_ACK) != 0;
+ rcv_bpdu.top_change =
+ (*bufp & TOPOLOGY_CHANGE) != 0;
+ bufp++;
+ memcpy((char*)&rcv_bpdu.root_id, bufp,
+ BRIDGE_BPDU_8021_CONFIG_SIZE-1
+ -BRIDGE_BPDU_8021_CONFIG_FLAG_OFFSET);
+ config_bpdu_ntoh(&rcv_bpdu);
+ received_config_bpdu(port, &rcv_bpdu);
+ break;
+
+ case BPDU_TYPE_TOPO_CHANGE:
+ received_tcn_bpdu(port, bpdu);
+ break;
+ default:
+ printk(KERN_DEBUG "br_bpdu: received unknown bpdu, type = %i\n", bpdu->type);
/* break; */
+ }
}
br_drop(skb);
}
+struct fdb_info *get_fdb_info(int user_buf_size, int *copied,int *notcopied)
+{
+ int fdb_size, i, built = 0;
+ struct fdb_info *fdbi, *fdbis;
+
+ *copied = user_buf_size - sizeof(struct fdb_info_hdr);
+ *copied /= sizeof(struct fdb_info);
+ *copied = min(*copied, allocated_fdb_cnt);
+ *notcopied = allocated_fdb_cnt - *copied;
+ if(*copied == 0)
+ return NULL;
+ fdb_size = *copied * sizeof(struct fdb_info);
+ fdbis = kmalloc(fdb_size, GFP_KERNEL);
+ if(fdbis == NULL)
+ return NULL;
+ fdbi = fdbis;
+
+ for(i=One; i<=No_of_ports;i++)
+ {
+ struct fdb *fdb;
+
+ cli();
+ fdb = port_info[i].fdb;
+ while(fdb)
+ {
+ memcpy(fdbi->ula, fdb->ula, ETH_ALEN);
+ fdbi->port = fdb->port;
+ fdbi->flags = fdb->flags;
+ fdbi->timer = fdb->timer;
+ fdbi++;
+ if(++built == *copied)
+ {
+ sti();
+ return fdbis;
+ }
+ fdb = fdb->fdb_next;
+ }
+ sti();
+ }
+ printk(KERN_DEBUG "get_fdb_info: built=%d\n", built);
+ return fdbis;
+}
+
int br_ioctl(unsigned int cmd, void *arg)
{
- int err;
+ int err, i;
struct br_cf bcf;
+ bridge_id_t new_id;
switch(cmd)
{
case SIOCGIFBR: /* get bridging control blocks */
memcpy(&br_stats.bridge_data, &bridge_info, sizeof(Bridge_data));
memcpy(&br_stats.port_data, &port_info, sizeof(Port_data)*No_of_ports);
+
err = copy_to_user(arg, &br_stats, sizeof(struct br_stat));
if (err)
{
@@ -1590,17 +1908,33 @@ int br_ioctl(unsigned int cmd, void *arg)
}
return err;
case SIOCSIFBR:
- if (!suser())
- return -EPERM;
err = copy_from_user(&bcf, arg, sizeof(struct br_cf));
if (err)
return -EFAULT;
- switch (bcf.cmd) {
+ if (bcf.cmd != BRCMD_DISPLAY_FDB && !suser())
+ return -EPERM;
+ switch (bcf.cmd)
+ {
case BRCMD_BRIDGE_ENABLE:
if (br_stats.flags & BR_UP)
return(-EALREADY);
printk(KERN_DEBUG "br: enabling bridging function\n");
br_stats.flags |= BR_UP; /* enable bridge */
+ for(i=One;i<=No_of_ports; i++)
+ {
+ /* don't start if user said so */
+ if((user_port_state[i] != Disabled)
+ && port_info[i].dev)
+ {
+ enable_port(i);
+ }
+ }
+ port_state_selection(); /* (4.8.1.5) */
+ config_bpdu_generation(); /* (4.8.1.6) */
+ /* initialize system timer */
+ tl.expires = jiffies+HZ; /* 1 second */
+ tl.function = br_tick;
+ add_timer(&tl);
start_hello_timer();
break;
case BRCMD_BRIDGE_DISABLE:
@@ -1609,35 +1943,41 @@ int br_ioctl(unsigned int cmd, void *arg)
printk(KERN_DEBUG "br: disabling bridging function\n");
br_stats.flags &= ~BR_UP; /* disable bridge */
stop_hello_timer();
-#if 0
for (i = One; i <= No_of_ports; i++)
if (port_info[i].state != Disabled)
disable_port(i);
-#endif
break;
case BRCMD_PORT_ENABLE:
if (port_info[bcf.arg1].dev == 0)
return(-EINVAL);
- if (port_info[bcf.arg1].state != Disabled)
+ if (user_port_state[bcf.arg1] != Disabled)
return(-EALREADY);
printk(KERN_DEBUG "br: enabling port %i\n",bcf.arg1);
- enable_port(bcf.arg1);
+ user_port_state[bcf.arg1] = ~Disabled;
+ if(br_stats.flags & BR_UP)
+ enable_port(bcf.arg1);
break;
case BRCMD_PORT_DISABLE:
if (port_info[bcf.arg1].dev == 0)
return(-EINVAL);
- if (port_info[bcf.arg1].state == Disabled)
+ if (user_port_state[bcf.arg1] == Disabled)
return(-EALREADY);
printk(KERN_DEBUG "br: disabling port %i\n",bcf.arg1);
- disable_port(bcf.arg1);
+ user_port_state[bcf.arg1] = Disabled;
+ if(br_stats.flags & BR_UP)
+ disable_port(bcf.arg1);
break;
case BRCMD_SET_BRIDGE_PRIORITY:
- set_bridge_priority((bridge_id_t *)&bcf.arg1);
+ new_id = bridge_info.bridge_id;
+ new_id.BRIDGE_PRIORITY = htons(bcf.arg1);
+ set_bridge_priority(&new_id);
break;
case BRCMD_SET_PORT_PRIORITY:
- if (port_info[bcf.arg1].dev == 0)
+ if((port_info[bcf.arg1].dev == 0)
+ || (bcf.arg2 & ~0xff))
return(-EINVAL);
- set_port_priority(bcf.arg1, bcf.arg2);
+ port_priority[bcf.arg1] = bcf.arg2;
+ set_port_priority(bcf.arg1);
break;
case BRCMD_SET_PATH_COST:
if (port_info[bcf.arg1].dev == 0)
@@ -1664,6 +2004,36 @@ int br_ioctl(unsigned int cmd, void *arg)
memset(&br_stats.prot_id,0,sizeof(br_stats.prot_id));
memset(&br_stats.prot_counter,0,sizeof(br_stats.prot_counter));
break;
+ case BRCMD_DISPLAY_FDB:
+ {
+ struct fdb_info_hdr *user_buf = (void*) bcf.arg1;
+ struct fdb_info *u_fdbs, *fdbis;
+ int copied, notcopied;
+ u32 j = CURRENT_TIME;
+
+ if(bcf.arg2<sizeof(struct fdb_info_hdr))
+ return -EINVAL;
+ put_user(j, &user_buf->cmd_time);
+ if(allocated_fdb_cnt == 0)
+ {
+ put_user(0, &user_buf->copied);
+ put_user(0, &user_buf->not_copied);
+ return 0;
+ }
+ fdbis = get_fdb_info(bcf.arg2, &copied, &notcopied);
+ put_user(copied, &user_buf->copied);
+ put_user(notcopied, &user_buf->not_copied);
+ if(!fdbis)
+ return -ENOMEM;
+ u_fdbs = (struct fdb_info *) (user_buf+1);
+ err = copy_to_user(u_fdbs, fdbis, copied*sizeof(struct fdb_info));
+ kfree(fdbis);
+ if (err)
+ {
+ err = -EFAULT;
+ }
+ return err;
+ }
default:
return -EINVAL;
}
@@ -1680,12 +2050,13 @@ static int br_cmp(unsigned int *a, unsigned int *b)
int i;
for (i=0; i<2; i++)
{
- if (a[i] == b[i])
- continue;
- if (a[i] < b[i])
- return(1);
- if (a[i] > b[i])
+ /* JRP: compares prty then MAC address in memory byte order
+ * OK optimizer does htonl() only once per long !
+ */
+ if (htonl(a[i]) < htonl(b[i]))
return(-1);
+ if (htonl(a[i]) > htonl(b[i]))
+ return(1);
}
return(0);
}