diff options
author | Ralf Baechle <ralf@linux-mips.org> | 1998-05-07 02:55:41 +0000 |
---|---|---|
committer | Ralf Baechle <ralf@linux-mips.org> | 1998-05-07 02:55:41 +0000 |
commit | dcec8a13bf565e47942a1751a9cec21bec5648fe (patch) | |
tree | 548b69625b18cc2e88c3e68d0923be546c9ebb03 /net/bridge | |
parent | 2e0f55e79c49509b7ff70ff1a10e1e9e90a3dfd4 (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')
-rw-r--r-- | net/bridge/Makefile | 2 | ||||
-rw-r--r-- | net/bridge/br.c | 1145 | ||||
-rw-r--r-- | net/bridge/br_tree.c | 71 |
3 files changed, 807 insertions, 411 deletions
diff --git a/net/bridge/Makefile b/net/bridge/Makefile index bc432f316..bcccefb75 100644 --- a/net/bridge/Makefile +++ b/net/bridge/Makefile @@ -1,5 +1,5 @@ # -# Makefile for the Linux TCP/IP (INET) layer. +# Makefile for the Linux Bridge layer. # # Note! Dependencies are done automagically by 'make dep', which also # removes any old dependencies. DON'T put your own dependencies here 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, ¬copied); + 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); } diff --git a/net/bridge/br_tree.c b/net/bridge/br_tree.c index 8234249c5..709bafb2b 100644 --- a/net/bridge/br_tree.c +++ b/net/bridge/br_tree.c @@ -1,6 +1,7 @@ /* - * this code is derived from the avl functions in mmap.c + * This code is derived from the avl functions in mmap.c */ + #include <linux/kernel.h> #include <linux/errno.h> #include <linux/string.h> @@ -16,6 +17,10 @@ * Written by Bruno Haible <haible@ma2s2.mathematik.uni-karlsruhe.de>. * Taken from mmap.c, extensively modified by John Hayes * <hayes@netplumbing.com> + * 98-02 Modified by Jean-Rene Peulve jr.peulve@aix.pacwan.net + * update port number when topology change + * return oldfdb when updating, for broadcast storm checking + * call addr_cmp once per node */ static struct fdb fdb_head; @@ -50,8 +55,7 @@ static int addr_cmp(unsigned char *a1, unsigned char *a2); * foreach node in tree->fdb_avl_right: node->fdb_avl_key >= tree->fdb_avl_key. */ -static int -fdb_init(void) +static int fdb_init(void) { fdb_head.fdb_avl_height = 0; fdb_head.fdb_avl_left = (struct fdb *)0; @@ -109,7 +113,6 @@ struct fdb *br_avl_find_addr(unsigned char addr[6]) } -#if (0) /* * Rebalance a tree. * After inserting or deleting a node of a tree we have a sequence of subtrees @@ -196,10 +199,14 @@ static void br_avl_rebalance (struct fdb *** nodeplaces_ptr, int count) printk_avl(&fdb_head); #endif /* DEBUG_AVL */ } -#endif /* (0) */ -/* Insert a node into a tree. */ -int br_avl_insert (struct fdb * new_node) +/* Insert a node into a tree. + * Performance improvement: + * call addr_cmp() only once per node and use result in a switch. + * Return old node address if we knew that MAC address already + * Return NULL if we insert the new node + */ +struct fdb *br_avl_insert (struct fdb * new_node) { struct fdb ** nodeplace = fhpp; struct fdb ** stack[avl_maxheight]; @@ -214,15 +221,38 @@ int br_avl_insert (struct fdb * new_node) if (node == avl_br_empty) break; *stack_ptr++ = nodeplace; stack_count++; - if (addr_cmp(new_node->ula, node->ula) == 0) { /* update */ + switch(addr_cmp(new_node->ula, node->ula)) { + case 0: /* update */ + if (node->port == new_node->port) { node->flags = new_node->flags; node->timer = new_node->timer; - return(0); - } - if (addr_cmp(new_node->ula, node->ula) < 0) { - nodeplace = &node->fdb_avl_left; - } else { - nodeplace = &node->fdb_avl_right; + } else if (!(node->flags & FDB_ENT_VALID) && + node->port) { + /* update fdb but never for local interfaces */ +#if (DEBUG_AVL) + printk("node 0x%x:port changed old=%d new=%d\n", + (unsigned int)node, node->port,new_node->port); +#endif + /* JRP: update port as well if the topology change ! + * Don't do this while entry is still valid otherwise + * a broadcast that we flooded and is reentered by another + * port would mess up the good port number. + * The fdb list per port needs to be updated as well. + */ + requeue_fdb(node, new_node->port); + node->flags = new_node->flags; + node->timer = new_node->timer; +#if (DEBUG_AVL) + printk_avl(&fdb_head); +#endif /* DEBUG_AVL */ + } + return node; /* pass old fdb to caller */ + + case 1: /* new_node->ula > node->ula */ + nodeplace = &node->fdb_avl_right; + break; + default: /* -1 => new_node->ula < node->ula */ + nodeplace = &node->fdb_avl_left; } } #if (DEBUG_AVL) @@ -239,17 +269,14 @@ int br_avl_insert (struct fdb * new_node) new_node->fdb_avl_right = avl_br_empty; new_node->fdb_avl_height = 1; *nodeplace = new_node; -#if (0) br_avl_rebalance(stack_ptr,stack_count); -#endif /* (0) */ #ifdef DEBUG_AVL printk_avl(&fdb_head); #endif /* DEBUG_AVL */ - return(1); + return NULL; /* this is a new node */ } -#if (0) /* Removes a node out of a tree. */ static int br_avl_remove (struct fdb * node_to_delete) { @@ -302,7 +329,6 @@ static int br_avl_remove (struct fdb * node_to_delete) br_avl_rebalance(stack_ptr,stack_count); return(0); } -#endif /* (0) */ #ifdef DEBUG_AVL @@ -311,13 +337,14 @@ static void printk_avl (struct fdb * tree) { if (tree != avl_br_empty) { printk("("); - printk("%02x:%02x:%02x:%02x:%02x:%02x", + printk("%02x:%02x:%02x:%02x:%02x:%02x(%d)", tree->ula[0], tree->ula[1], tree->ula[2], tree->ula[3], tree->ula[4], - tree->ula[5]); + tree->ula[5], + tree->port); if (tree->fdb_avl_left != avl_br_empty) { printk_avl(tree->fdb_avl_left); printk("<"); @@ -330,7 +357,6 @@ static void printk_avl (struct fdb * tree) } } -#if (0) static char *avl_check_point = "somewhere"; /* check a tree's consistency and balancing */ @@ -387,7 +413,6 @@ static void avl_checkorder (struct fdb * tree) avl_checkright(tree->fdb_avl_right,tree->fdb_avl_key); } -#endif /* (0) */ #endif /* DEBUG_AVL */ static int addr_cmp(unsigned char a1[], unsigned char a2[]) |