diff -urN linux-2.2.20/CREDITS linux-2.2.20br/CREDITS --- linux-2.2.20/CREDITS Fri Nov 2 17:39:05 2001 +++ linux-2.2.20br/CREDITS Mon Dec 10 16:24:28 2001 @@ -346,6 +346,13 @@ S: Orlando, Florida S: USA +N: Lennert Buytenhek +E: buytenh@gnu.org +D: Rewrite of the ethernet bridging code +S: Ravenhorst 58B +S: 2317 AK Leiden +S: The Netherlands + N: Michael Callahan E: callahan@maths.ox.ac.uk D: PPP for Linux diff -urN linux-2.2.20/Documentation/Configure.help linux-2.2.20br/Documentation/Configure.help --- linux-2.2.20/Documentation/Configure.help Fri Nov 2 17:39:05 2001 +++ linux-2.2.20br/Documentation/Configure.help Mon Dec 10 16:24:28 2001 @@ -4099,24 +4099,26 @@ This is a Logical Link Layer protocol used for X.25 connections over Ethernet, using ordinary Ethernet cards. -Bridging (EXPERIMENTAL) +802.1d Ethernet Bridging CONFIG_BRIDGE If you say Y here, then your Linux box will be able to act as an Ethernet bridge, which means that the different Ethernet segments it is connected to will appear as one Ethernet to the participants. Several such bridges can work together to create even larger - networks of Ethernets using the IEEE802.1 spanning tree algorithm. + networks of Ethernets using the IEEE 802.1d spanning tree protocol. As this is a standard, Linux bridges will interwork properly with other third party bridge products. - In order to use this, you'll need the bridge configuration tools - available from http://lrp.plain.co.nz/tarballs/bridgex-0.30.tar.gz + In order to use the ethernet bridge, you'll need the bridge configuration + tools available from http://www.math.leidenuniv.nl/~buytenh/bridge. Please + read the documentation for more information. + Note that if your box acts as a bridge, it probably contains several - Ethernet devices, but the kernel is not able to recognize more than - one ISA ethernet card at boot time without help; for details read - the Ethernet-HOWTO, available via FTP (user: anonymous) - in ftp://metalab.unc.edu/pub/Linux/docs/HOWTO. The Bridging code is - still in test. If unsure, say N. + Ethernet devices, but the kernel is not able to recognize more than one + at boot time without help; for details read the Ethernet-HOWTO, available + via FTP (user: anonymous) in ftp://metalab.unc.edu/pub/Linux/docs/HOWTO. + + If unsure, say N. Frame Diverter (EXPERIMENTAL) CONFIG_NET_DIVERT diff -urN linux-2.2.20/Documentation/networking/bridge.txt linux-2.2.20br/Documentation/networking/bridge.txt --- linux-2.2.20/Documentation/networking/bridge.txt Thu Jan 1 01:00:00 1970 +++ linux-2.2.20br/Documentation/networking/bridge.txt Mon Dec 10 16:24:28 2001 @@ -0,0 +1,11 @@ +In order to use the ethernet bridging functionality you'll need the +userspace tools available at http://www.math.leidenuniv.nl/~buytenh/bridge. +The tarball available there contains extensive documentation, but if you +still have questions, don't hesitate to post to the mailing list (more info +at http://www.math.leidenuniv.nl/mailman/listinfo/bridge). You can also mail +me at buytenh@gnu.org. + + + +Lennert Buytenhek + diff -urN linux-2.2.20/MAINTAINERS linux-2.2.20br/MAINTAINERS --- linux-2.2.20/MAINTAINERS Fri Nov 2 17:39:05 2001 +++ linux-2.2.20br/MAINTAINERS Mon Dec 10 16:24:28 2001 @@ -370,6 +370,13 @@ L: linux-net@vger.kernel.org S: Maintained +ETHERNET BRIDGE +P: Lennert Buytenhek +M: buytenh@gnu.org +L: bridge@math.leidenuniv.nl +W: http://www.math.leidenuniv.nl/~buytenh/bridge +S: Maintained + ETHERTEAM 16I DRIVER P: Mika Kuoppala M: miku@iki.fi diff -urN linux-2.2.20/include/linux/if_bridge.h linux-2.2.20br/include/linux/if_bridge.h --- linux-2.2.20/include/linux/if_bridge.h Thu Jan 1 01:00:00 1970 +++ linux-2.2.20br/include/linux/if_bridge.h Mon Dec 10 16:24:28 2001 @@ -0,0 +1,110 @@ +/* + * Linux ethernet bridge + * + * Authors: + * Lennert Buytenhek + * + * $Id$ + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ + +#ifndef _LINUX_IF_BRIDGE_H +#define _LINUX_IF_BRIDGE_H + +#include + +#define BRCTL_VERSION 1 + +#define BRCTL_GET_VERSION 0 +#define BRCTL_GET_BRIDGES 1 +#define BRCTL_ADD_BRIDGE 2 +#define BRCTL_DEL_BRIDGE 3 +#define BRCTL_ADD_IF 4 +#define BRCTL_DEL_IF 5 +#define BRCTL_GET_BRIDGE_INFO 6 +#define BRCTL_GET_PORT_LIST 7 +#define BRCTL_SET_BRIDGE_FORWARD_DELAY 8 +#define BRCTL_SET_BRIDGE_HELLO_TIME 9 +#define BRCTL_SET_BRIDGE_MAX_AGE 10 +#define BRCTL_SET_AGEING_TIME 11 +#define BRCTL_SET_GC_INTERVAL 12 +#define BRCTL_GET_PORT_INFO 13 +#define BRCTL_SET_BRIDGE_STP_STATE 14 +#define BRCTL_SET_BRIDGE_PRIORITY 15 +#define BRCTL_SET_PORT_PRIORITY 16 +#define BRCTL_SET_PATH_COST 17 +#define BRCTL_GET_FDB_ENTRIES 18 + +#define BR_STATE_DISABLED 0 +#define BR_STATE_LISTENING 1 +#define BR_STATE_LEARNING 2 +#define BR_STATE_FORWARDING 3 +#define BR_STATE_BLOCKING 4 + +struct __bridge_info +{ + __u64 designated_root; + __u64 bridge_id; + __u32 root_path_cost; + __u32 max_age; + __u32 hello_time; + __u32 forward_delay; + __u32 bridge_max_age; + __u32 bridge_hello_time; + __u32 bridge_forward_delay; + __u8 topology_change; + __u8 topology_change_detected; + __u8 root_port; + __u8 stp_enabled; + __u32 ageing_time; + __u32 gc_interval; + __u32 hello_timer_value; + __u32 tcn_timer_value; + __u32 topology_change_timer_value; + __u32 gc_timer_value; +}; + +struct __port_info +{ + __u64 designated_root; + __u64 designated_bridge; + __u16 port_id; + __u16 designated_port; + __u32 path_cost; + __u32 designated_cost; + __u8 state; + __u8 top_change_ack; + __u8 config_pending; + __u8 unused0; + __u32 message_age_timer_value; + __u32 forward_delay_timer_value; + __u32 hold_timer_value; +}; + +struct __fdb_entry +{ + __u8 mac_addr[6]; + __u8 port_no; + __u8 is_local; + __u32 ageing_timer_value; + __u32 unused; +}; + +#ifdef __KERNEL__ + +#include + +struct net_bridge; +struct net_bridge_port; + +extern int br_init(void); +extern int (*br_ioctl_hook)(unsigned long arg); +extern void (*br_handle_frame_hook)(struct sk_buff *skb); + +#endif + +#endif diff -urN linux-2.2.20/include/linux/netdevice.h linux-2.2.20br/include/linux/netdevice.h --- linux-2.2.20/include/linux/netdevice.h Fri Nov 2 17:39:09 2001 +++ linux-2.2.20br/include/linux/netdevice.h Mon Dec 10 16:24:28 2001 @@ -270,9 +270,6 @@ struct Qdisc *qdisc_list; unsigned long tx_queue_len; /* Max frames per queue allowed */ - /* Bridge stuff */ - int bridge_port_id; - /* Pointers to interface service routines. */ int (*open)(struct device *dev); int (*stop)(struct device *dev); @@ -310,6 +307,9 @@ int (*neigh_setup)(struct device *dev, struct neigh_parms *); int (*accept_fastpath)(struct device *, struct dst_entry*); + /* bridge stuff */ + struct net_bridge_port *br_port; + #ifdef CONFIG_NET_FASTROUTE /* Really, this semaphore may be necessary and for not fastroute code; f.e. SMP?? diff -urN linux-2.2.20/include/linux/skbuff.h linux-2.2.20br/include/linux/skbuff.h --- linux-2.2.20/include/linux/skbuff.h Sun Mar 25 18:31:03 2001 +++ linux-2.2.20br/include/linux/skbuff.h Mon Dec 10 16:24:28 2001 @@ -83,7 +83,6 @@ unsigned char is_clone, /* We are a clone */ cloned, /* head may be cloned (check refcnt to be sure). */ pkt_type, /* Packet class */ - pkt_bridged, /* Tracker for bridging */ ip_summed; /* Driver fed us an IP checksum */ __u32 priority; /* Packet queueing priority */ atomic_t users; /* User count - see datagram.c,tcp.c */ diff -urN linux-2.2.20/include/net/br.h linux-2.2.20br/include/net/br.h --- linux-2.2.20/include/net/br.h Sun Mar 25 18:31:08 2001 +++ linux-2.2.20br/include/net/br.h Thu Jan 1 01:00:00 1970 @@ -1,331 +0,0 @@ -/* - * Constants and structure definitions for the bridging code - */ - -#if !defined(One) -#define Zero 0 -#define One 1 -#endif /* !defined(One) */ - -#if !defined(TRUE) -#define FALSE 0 -#define TRUE 1 -#endif /* !defined(TRUE) */ - -/** port states. **/ -#define Disabled 0 /* (4.4 5) */ -#define Listening 1 /* (4.4.2) */ -#define Learning 2 /* (4.4.3) */ -#define Forwarding 3 /* (4 4 4) */ -#define Blocking 4 /* (4.4.1) */ - - -/* MAG Yich! Easiest way of giving a configurable number of ports - * If you want more than 32, change BR_MAX_PORTS and recompile brcfg! - */ -#define BR_MAX_PORTS (32) -#if CONFIG_BRIDGE_NUM_PORTS > BR_MAX_PORTS -#undef CONFIG_BRIDGE_NUM_PORTS -#define CONFIG_BRIDGE_NUM_PORTS BR_MAX_PORTS -#endif -#define No_of_ports CONFIG_BRIDGE_NUM_PORTS -/* arbitrary choice, to allow the code below to compile */ - -#define All_ports (No_of_ports + 1) - -/* - * We time out our entries in the FDB after this many seconds. - */ -#define FDB_TIMEOUT 20 /* JRP: 20s as NSC bridge code, was 300 for Linux */ - -/* - * the following defines are the initial values used when the - * bridge is booted. These may be overridden when this bridge is - * not the root bridge. These are the recommended default values - * from the 802.1d specification. - */ -#define BRIDGE_MAX_AGE 20 -#define BRIDGE_HELLO_TIME 2 -#define BRIDGE_FORWARD_DELAY 15 -#define HOLD_TIME 1 - -/* broacast/multicast storm limitation. This per source. */ -#define MAX_MCAST_PER_PERIOD 32 -#define MCAST_HOLD_TIME (10*HZ/100) - -#define Default_path_cost 10 - -/* - * minimum increment possible to avoid underestimating age, allows for BPDU - * transmission time - */ -#define Message_age_increment 1 - -#define No_port 0 -/* - * reserved value for Bridge's root port parameter indicating no root port, - * used when Bridge is the root - also used to indicate the source when - * a frame is being generated by a higher layer protocol on this host - */ - -/** Configuration BPDU Parameters (4.5.1) **/ - -typedef struct { - union { - struct { - unsigned short priority; - unsigned char ula[6]; - } p_u; - unsigned int id[2]; - } bi; -} bridge_id_t; - -#define BRIDGE_PRIORITY bi.p_u.priority -#define BRIDGE_ID_ULA bi.p_u.ula -#define BRIDGE_ID bi.id - -/* JRP: on the network the flags field is between "type" and "root_id" - * this is unfortunated! To make the code portable to a RISC machine - * the pdus are now massaged a little bit for processing - */ -#define TOPOLOGY_CHANGE 0x01 -#define TOPOLOGY_CHANGE_ACK 0x80 -#define BRIDGE_BPDU_8021_CONFIG_SIZE 35 /* real size */ -#define BRIDGE_BPDU_8021_CONFIG_FLAG_OFFSET 4 -#define BRIDGE_BPDU_8021_PROTOCOL_ID 0 -#define BRIDGE_BPDU_8021_PROTOCOL_VERSION_ID 0 -#define BRIDGE_LLC1_HS 3 -#define BRIDGE_LLC1_DSAP 0x42 -#define BRIDGE_LLC1_SSAP 0x42 -#define BRIDGE_LLC1_CTRL 0x03 - -typedef struct { - unsigned short protocol_id; - unsigned char protocol_version_id; - unsigned char type; - bridge_id_t root_id; /* (4.5.1.1) */ - unsigned int root_path_cost; /* (4.5.1.2) */ - bridge_id_t bridge_id; /* (4.5.1.3) */ - unsigned short port_id; /* (4.5.1.4) */ - unsigned short message_age; /* (4.5.1.5) */ - unsigned short max_age; /* (4.5.1.6) */ - unsigned short hello_time; /* (4.5.1.7) */ - unsigned short forward_delay; /* (4.5.1.8) */ - unsigned char top_change_ack; - unsigned char top_change; -} Config_bpdu; - -#ifdef __LITTLE_ENDIAN -#define config_bpdu_hton(config_bpdu) \ - (config_bpdu)->root_path_cost = htonl((config_bpdu)->root_path_cost); \ - (config_bpdu)->port_id = htons((config_bpdu)->port_id); \ - (config_bpdu)->message_age = htons((config_bpdu)->message_age); \ - (config_bpdu)->max_age = htons((config_bpdu)->max_age); \ - (config_bpdu)->hello_time = htons((config_bpdu)->hello_time); \ - (config_bpdu)->forward_delay = htons((config_bpdu)->forward_delay); -#else -#define config_bpdu_hton(config_bpdu) -#endif -#define config_bpdu_ntoh config_bpdu_hton - - -/** Topology Change Notification BPDU Parameters (4.5.2) **/ - -typedef struct { - unsigned short protocol_id; - unsigned char protocol_version_id; - unsigned char type; -} Tcn_bpdu; - -#define BPDU_TYPE_CONFIG 0 -#define BPDU_TYPE_TOPO_CHANGE 128 - -/** Bridge Parameters (4.5.3) **/ -typedef struct { - bridge_id_t designated_root; /* (4.5.3.1) */ - unsigned int root_path_cost; /* (4.5.3.2) */ - unsigned int root_port; /* (4.5.3.3) */ - unsigned short max_age; /* (4.5.3.4) */ - unsigned short hello_time; /* (4.5.3.5) */ - unsigned short forward_delay; /* (4.5.3.6) */ - bridge_id_t bridge_id; /* (4.5.3.7) */ - unsigned short bridge_max_age; /* (4.5.3.8) */ - unsigned short bridge_hello_time; /* (4.5.3.9) */ - unsigned short bridge_forward_delay; /* (4.5.3.10) */ - unsigned int top_change_detected; /* (4.5.3.11) */ - unsigned int top_change; /* (4.5.3.12) */ - unsigned short topology_change_time; /* (4.5.3.13) */ - unsigned short hold_time; /* (4.5.3.14) */ - unsigned int instance; -} Bridge_data; - -/** Port Parameters (4.5.5) **/ -typedef struct { - unsigned short port_id; /* (4.5.5.1) */ - unsigned int state; /* (4.5.5.2) */ - unsigned int path_cost; /* (4.5.5.3) */ - bridge_id_t designated_root; /* (4.5.5.4) */ - unsigned int designated_cost; /* (4.5.5.5) */ - bridge_id_t designated_bridge; /* (4.5.5.6) */ - unsigned short designated_port; /* (4.5.5.7) */ - unsigned int top_change_ack; /* (4.5.5.8) */ - unsigned int config_pending; /* (4.5.5.9) */ - bridge_id_t ifmac; - unsigned int admin_state; - char ifname[IFNAMSIZ]; /* Make life easier for brcfg */ - struct device *dev; - struct fdb *fdb; /* head of per port fdb chain */ -} Port_data; - - - -/** types to support timers for this pseudo-implementation. **/ -typedef struct { - unsigned int active; /* timer in use. */ - unsigned int value; /* current value of timer, - * counting up. */ -} Timer; - -struct fdb { - unsigned char ula[6]; - unsigned char pad[2]; - unsigned short port; - unsigned int timer; - unsigned short flags; -#define FDB_ENT_VALID 0x01 - unsigned short mcast_count; - unsigned int mcast_timer; /* oldest xxxxxcast */ - -/* AVL tree of all addresses, sorted by address */ - short fdb_avl_height; - struct fdb *fdb_avl_left; - struct fdb *fdb_avl_right; -/* linked list of addresses for each port */ - struct fdb *fdb_next; -}; - -/* data returned on BRCMD_DISPLAY_FDB */ -struct fdb_info { - unsigned char ula[6]; - unsigned char port; - unsigned char flags; - unsigned int timer; -}; -struct fdb_info_hdr { - int copied; /* nb of entries copied to user */ - int not_copied; /* when user buffer is too small */ - int cmd_time; -}; - -#define IS_BRIDGED 0x2e - - -#define BR_MAX_PROTOCOLS 32 -#define BR_MAX_PROT_STATS BR_MAX_PROTOCOLS - -/* policy values for policy field */ -#define BR_ACCEPT 1 -#define BR_REJECT 0 - -/* JRP: extra statistics for debug */ -typedef struct { - /* br_receive_frame counters */ - int port_disable_up_stack; - int rcv_bpdu; - int notForwarding; - int forwarding_up_stack; - int unknown_state; - - /* br_tx_frame counters */ - int port_disable; - int port_not_disable; - - /* br_forward counters */ - int local_multicast; - int forwarded_multicast; /* up stack as well */ - int flood_unicast; - int aged_flood_unicast; - int forwarded_unicast; - int forwarded_unicast_up_stack; - int forwarded_ip_up_stack; - int forwarded_ip_up_stack_lie; /* received on alternate device */ - int arp_for_local_mac; - int drop_same_port; - int drop_same_port_aged; - int drop_multicast; -} br_stats_counter; - -struct br_stat { - unsigned int flags; - Bridge_data bridge_data; - unsigned int policy; - unsigned int exempt_protocols; - unsigned short protocols[BR_MAX_PROTOCOLS]; - unsigned short prot_id[BR_MAX_PROT_STATS]; /* Protocol encountered */ - unsigned int prot_counter[BR_MAX_PROT_STATS]; /* How many packets ? */ - br_stats_counter packet_cnts; - unsigned int num_ports; - Port_data port_data[BR_MAX_PORTS + 1]; -}; - -/* defined flags for br_stat.flags */ -#define BR_UP 0x0001 /* bridging enabled */ -#define BR_DEBUG 0x0002 /* debugging enabled */ -#define BR_PROT_STATS 0x0004 /* protocol statistics enabled */ -#define BR_STP_DISABLED 0x0008 /* Spanning tree protocol disabled */ - -struct br_cf { - unsigned int cmd; - unsigned int arg1; - unsigned int arg2; -}; - -/* defined cmds */ -#define BRCMD_BRIDGE_ENABLE 1 -#define BRCMD_BRIDGE_DISABLE 2 -#define BRCMD_PORT_ENABLE 3 /* arg1 = port */ -#define BRCMD_PORT_DISABLE 4 /* arg1 = port */ -#define BRCMD_SET_BRIDGE_PRIORITY 5 /* arg1 = priority */ -#define BRCMD_SET_PORT_PRIORITY 6 /* arg1 = port, arg2 = priority */ -#define BRCMD_SET_PATH_COST 7 /* arg1 = port, arg2 = cost */ -#define BRCMD_DISPLAY_FDB 8 -#define BRCMD_ENABLE_DEBUG 9 -#define BRCMD_DISABLE_DEBUG 10 -#define BRCMD_SET_POLICY 11 /* arg1 = default policy (1==bridge all) */ -#define BRCMD_EXEMPT_PROTOCOL 12 /* arg1 = protocol (see net/if_ether.h) */ -#define BRCMD_ENABLE_PROT_STATS 13 -#define BRCMD_DISABLE_PROT_STATS 14 -#define BRCMD_ZERO_PROT_STATS 15 -#define BRCMD_TOGGLE_STP 16 -#define BRCMD_IF_ENABLE 17 /* arg1 = if_index */ -#define BRCMD_IF_DISABLE 18 /* arg1 = if_index */ -#define BRCMD_SET_IF_PRIORITY 19 /* arg1 = if_index, arg2 = priority */ -#define BRCMD_SET_IF_PATH_COST 20 /* arg1 = if_index, arg2 = cost */ - -/* prototypes of exported bridging functions... */ - -#ifdef __KERNEL__ -void br_init(void); -int br_receive_frame(struct sk_buff *skb); /* 3.5 */ -int br_tx_frame(struct sk_buff *skb); -int brg_init(void); -int br_ioctl(unsigned int cmd, void *arg); -void requeue_fdb(struct fdb *node, int new_port); - -struct fdb *br_avl_find_addr(unsigned char addr[6]); -struct fdb *br_avl_insert (struct fdb * new_node); -void sprintf_avl (char **pbuffer, struct fdb * tree, off_t *pos,int* len, off_t offset, int length); -int br_tree_get_info(char *buffer, char **start, off_t offset, int length, int dummy); -void br_avl_delete_by_port(int port); -int br_call_bridge(struct sk_buff *skb, unsigned short type); -void br_spacedevice_register(void); - -/* externs */ - -extern struct br_stat br_stats; -extern Port_data port_info[]; - -#endif - - - diff -urN linux-2.2.20/net/Config.in linux-2.2.20br/net/Config.in --- linux-2.2.20/net/Config.in Sun Mar 25 18:37:41 2001 +++ linux-2.2.20br/net/Config.in Mon Dec 10 16:25:05 2001 @@ -31,6 +31,7 @@ source net/ipx/Config.in fi tristate 'Appletalk DDP' CONFIG_ATALK +dep_tristate '802.1d Ethernet Bridging' CONFIG_BRIDGE $CONFIG_INET if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then # tristate 'DECnet Support (NOT YET FUNCTIONAL)' CONFIG_DECNET # if [ "$CONFIG_DECNET" != "n" ]; then @@ -38,10 +39,6 @@ # fi tristate 'CCITT X.25 Packet Layer (EXPERIMENTAL)' CONFIG_X25 tristate 'LAPB Data Link Driver (EXPERIMENTAL)' CONFIG_LAPB - bool 'Bridging (EXPERIMENTAL)' CONFIG_BRIDGE - if [ "$CONFIG_BRIDGE" != "n" ]; then - int ' Maximum number of bridged interfaces' CONFIG_BRIDGE_NUM_PORTS 8 - fi bool 'Frame Diverter (EXPERIMENTAL)' CONFIG_NET_DIVERT bool '802.2 LLC (EXPERIMENTAL)' CONFIG_LLC # if [ "$CONFIG_LLC" = "y" ]; then diff -urN linux-2.2.20/net/Makefile linux-2.2.20br/net/Makefile --- linux-2.2.20/net/Makefile Sun Mar 25 18:31:11 2001 +++ linux-2.2.20br/net/Makefile Mon Dec 10 16:24:28 2001 @@ -59,6 +59,10 @@ ifeq ($(CONFIG_BRIDGE),y) SUB_DIRS += bridge +else + ifeq ($(CONFIG_BRIDGE),m) + MOD_SUB_DIRS += bridge + endif endif ifeq ($(CONFIG_IPX),y) diff -urN linux-2.2.20/net/README linux-2.2.20br/net/README --- linux-2.2.20/net/README Sun Mar 25 18:31:11 2001 +++ linux-2.2.20br/net/README Mon Dec 10 16:24:28 2001 @@ -7,6 +7,7 @@ [token ring ] p.norton@computer.org appletalk Jay.Schulist@spacs.k12.wi.us ax25 g4klx@g4klx.demon.co.uk +bridge buytenh@gnu.org core alan@lxorguk.ukuu.org.uk decnet SteveW@ACM.org ethernet alan@lxorguk.ukuu.org.uk diff -urN linux-2.2.20/net/bridge/Makefile linux-2.2.20br/net/bridge/Makefile --- linux-2.2.20/net/bridge/Makefile Sun Mar 25 18:31:13 2001 +++ linux-2.2.20br/net/bridge/Makefile Mon Dec 10 16:24:28 2001 @@ -1,5 +1,5 @@ # -# Makefile for the Linux Bridge layer. +# Makefile for the IEEE 802.1d ethernet bridging layer. # # Note! Dependencies are done automagically by 'make dep', which also # removes any old dependencies. DON'T put your own dependencies here @@ -7,15 +7,10 @@ # # Note 2! The CFLAGS definition is now in the main makefile... -O_TARGET := bridge.o -O_OBJS := br.o br_tree.o -M_OBJS := $(O_TARGET) - -ifeq ($(CONFIG_SYSCTL),y) -O_OBJS += sysctl_net_bridge.o -endif +O_TARGET := bridge.o +O_OBJS := br.o br_device.o br_fdb.o br_forward.o br_if.o br_input.o \ + br_ioctl.o br_notify.o br_stp.o br_stp_bpdu.o \ + br_stp_if.o br_stp_timer.o +M_OBJS := $(O_TARGET) include $(TOPDIR)/Rules.make - -tar: - tar -cvf /dev/f1 . diff -urN linux-2.2.20/net/bridge/br.c linux-2.2.20br/net/bridge/br.c --- linux-2.2.20/net/bridge/br.c Sun Mar 25 18:31:13 2001 +++ linux-2.2.20br/net/bridge/br.c Mon Dec 10 16:25:36 2001 @@ -1,2775 +1,63 @@ /* - * Linux NET3 Bridge Support + * Generic parts + * Linux ethernet bridge * - * Originally by John Hayes (Network Plumbing). - * Minor hacks to get it to run with 1.3.x by Alan Cox - * More hacks to be able to switch protocols on and off by Christoph Lameter - * - * Software and more Documentation for the bridge is available from ftp.debian.org - * in the bridgex package + * Authors: + * Lennert Buytenhek + * + * $Id$ * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * 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 - * 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 ;) - * - * Robert Pintarelli: fixed bug in bpdu time values - * - * Matthew Grant: start ports disabled. - * auto-promiscuous mode on port enable/disable - * fleshed out interface event handling, interfaces - * now register with bridge on module load as well as ifup - * port control ioctls with ifindex support - * brg0 logical ethernet interface - * reworked brcfg to take interface arguments - * added support for changing the hardware address - * generally made bridge a lot more usable. - * - * Todo: - * Use a netlink notifier so a daemon can maintain the bridge - * port group (could we also do multiple groups ????). - * A nice /proc file interface. - * Put the path costs in the port info and devices. - * Put the bridge port number in the device structure for speed. - * Bridge SNMP stats. - * */ - -#include -#include -#include -#include -#include -#include +#include #include -#include -#include -#include -#include -#include -#include +#include #include -#include #include -#include -#include -#include -#include #include +#include #include -#include -#include -#include -#include -#include - -#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); -static void record_config_information(int port_no, Config_bpdu *config); -static void record_config_timeout_values(Config_bpdu *config); -static void config_bpdu_generation(void); -static int designated_port(int port_no); -static void reply(int port_no); -static void transmit_tcn(void); -static void configuration_update(void); -static void root_selection(void); -static void designated_port_selection(void); -static void become_designated_port(int port_no); -static void port_state_selection(void); -static void make_forwarding(int port_no); -static void topology_change_detection(void); -static void topology_change_acknowledged(void); -static void acknowledge_topology_change(int port_no); -static void make_blocking(int port_no); -static void set_port_state(int port_no, int state); -static void received_config_bpdu(int port_no, Config_bpdu *config); -static void received_tcn_bpdu(int port_no, Tcn_bpdu *tcn); -static void hello_timer_expiry(void); -static void message_age_timer_expiry(int port_no); -static void forward_delay_timer_expiry(int port_no); -static int designated_for_some_port(void); -static void tcn_timer_expiry(void); -static void topology_change_timer_expiry(void); -static void hold_timer_expiry(int port_no); -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); -static void set_path_cost(int port_no, unsigned short path_cost); -static void start_hello_timer(void); -static void stop_hello_timer(void); -static int hello_timer_expired(void); -static void start_tcn_timer(void); -static void stop_tcn_timer(void); -static int tcn_timer_expired(void); -static void start_topology_change_timer(void); -static void stop_topology_change_timer(void); -static int topology_change_timer_expired(void); -static void start_message_age_timer(int port_no, unsigned short message_age); -static void stop_message_age_timer(int port_no); -static int message_age_timer_expired(int port_no); -static void start_forward_delay_timer(int port_no); -static void stop_forward_delay_timer(int port_no); -static int forward_delay_timer_expired(int port_no); -static void start_hold_timer(int port_no); -static void stop_hold_timer(int port_no); -static int hold_timer_expired(int port_no); -static int br_device_event(struct notifier_block *dnot, unsigned long event, void *ptr); -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, 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 */ -static int br_protocol_ok(unsigned short protocol); -static int br_find_port(int ifindex); -static void br_get_ifnames(void); -static int brg_rx(struct sk_buff *skb, int port); - -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) */ - -/* MAG: Maximum port registered - used to speed up flooding and to make - * have a large ports array more efficient - */ -static int max_port_used = 0; - -/* 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) */ -static Timer message_age_timer[All_ports]; /* (4.5.6.1) */ -static Timer forward_delay_timer[All_ports]; /* (4.5.6.2) */ -static Timer hold_timer[All_ports]; /* (4.5.6.3) */ - -/* entries timeout after this many seconds */ -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... */ - -/* - * the following structure is required so that we receive - * event notifications when network devices are enabled and - * disabled (ifconfig up and down). - */ -static struct notifier_block br_dev_notifier={ - br_device_event, - NULL, - 0 -}; - - -/* - * the following data is for the bridge network device - */ -struct brg_if { - struct device dev; - char name[IFNAMSIZ]; -}; -static struct brg_if brg_if; - -/* - * Here to save linkage? problems - */ - -static inline int find_port(struct device *dev) -{ - int i; - - for (i = One; i <= No_of_ports; i++) - if (port_info[i].dev == dev) - return(i); - return(0); -} - -/* - * Implementation of Protocol specific bridging - * - * The protocols to be bridged or not to be bridged are stored in a hashed array. This is the old type - * of unlinked hash array where one simply takes the next cell if the one the hash function points to - * is occupied. - */ - -#define BR_PROTOCOL_HASH(x) (x % BR_MAX_PROTOCOLS) - -/* Checks if that protocol type is to be bridged */ - -static int inline 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_PROTOCOLS-2) return -EXFULL; - for (x=BR_PROTOCOL_HASH(p);br_stats.protocols[x]!=0;) { - if (br_stats.protocols[x]==p) return 0; /* Attempt to add the protocol a second time */ - x++; - if (x==BR_MAX_PROTOCOLS) x=0; - } - br_stats.protocols[x]=p; - br_stats.exempt_protocols++; - return 0; -} - -/* Valid Policies are 0=No Protocols bridged 1=Bridge all protocols */ -static int br_set_policy(int policy) -{ - if (policy>1) return -EINVAL; - br_stats.policy=policy; - /* Policy change means initializing the exempt table */ - memset(br_stats.protocols,0,sizeof(br_stats.protocols)); - br_stats.exempt_protocols = 0; - return 0; -} - - -/** Elements of Procedure (4.6) **/ - -/* - * 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 our framework and structure. It - * implements the spanning tree algorithm that is the heart of the - * 802.1d bridging protocol. - */ - -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.type = BPDU_TYPE_CONFIG; - config_bpdu.root_id = bridge_info.designated_root; - /* (4.6.1.3.2(1)) */ - config_bpdu.root_path_cost = bridge_info.root_path_cost; - /* (4.6.1.3.2(2)) */ - config_bpdu.bridge_id = bridge_info.bridge_id; - /* (4.6.1.3.2(3)) */ - config_bpdu.port_id = port_info[port_no].port_id; - /* - * (4.6.1.3.2(4)) - */ - if (root_bridge()) { - config_bpdu.message_age = Zero; /* (4.6.1.3.2(5)) */ - } else { - config_bpdu.message_age - = (message_age_timer[bridge_info.root_port].value - + Message_age_increment) << 8; /* (4.6.1.3.2(6)) */ - } - - config_bpdu.max_age = bridge_info.max_age << 8;/* (4.6.1.3.2(7)) */ - config_bpdu.hello_time = bridge_info.hello_time << 8; - config_bpdu.forward_delay = bridge_info.forward_delay << 8; - 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; - - 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); -} +#include "br_private.h" -static int root_bridge(void) +void br_dec_use_count() { - return (br_cmp(bridge_info.designated_root.BRIDGE_ID, - bridge_info.bridge_id.BRIDGE_ID)?FALSE:TRUE); + MOD_DEC_USE_COUNT; } -static int supersedes_port_info(int port_no, Config_bpdu *config) /* (4.6.2.2) */ +void br_inc_use_count() { - return ( - (br_cmp(config->root_id.BRIDGE_ID, - port_info[port_no].designated_root.BRIDGE_ID) < 0) /* (4.6.2.2.1) */ - || - ((br_cmp(config->root_id.BRIDGE_ID, - port_info[port_no].designated_root.BRIDGE_ID) == 0 - ) - && - ((config->root_path_cost - < port_info[port_no].designated_cost /* (4.6.2.2.2) */ - ) - || - ((config->root_path_cost - == port_info[port_no].designated_cost - ) - && - ((br_cmp(config->bridge_id.BRIDGE_ID, - port_info[port_no].designated_bridge.BRIDGE_ID) < 0 /* (4.6.2.2.3) */ - ) - || - ((br_cmp(config->bridge_id.BRIDGE_ID, - port_info[port_no].designated_bridge.BRIDGE_ID) == 0 - ) /* (4.6.2.2.4) */ - && - ((br_cmp(config->bridge_id.BRIDGE_ID, - bridge_info.bridge_id.BRIDGE_ID) != 0 - ) /* (4.6.2.2.4(1)) */ - || - (config->port_id <= - port_info[port_no].designated_port - ) /* (4.6.2.2.4(2)) */ - )))))) - ); + MOD_INC_USE_COUNT; } -static void record_config_information(int port_no, Config_bpdu *config) /* (4.6.2) */ +__initfunc(int br_init(void)) { - port_info[port_no].designated_root = config->root_id; /* (4.6.2.3.1) */ - port_info[port_no].designated_cost = config->root_path_cost; - port_info[port_no].designated_bridge = config->bridge_id; - port_info[port_no].designated_port = config->port_id; - start_message_age_timer(port_no, config->message_age); /* (4.6.2.3.2) */ -} - -static void record_config_timeout_values(Config_bpdu *config) /* (4.6.3) */ -{ - bridge_info.max_age = config->max_age >> 8; /* (4.6.3.3) */ - bridge_info.hello_time = config->hello_time >> 8; - bridge_info.forward_delay = config->forward_delay >> 8; - bridge_info.top_change = config->top_change >> 8; -} - -static void config_bpdu_generation(void) -{ /* (4.6.4) */ - int port_no; - for (port_no = One; port_no <= No_of_ports; port_no++) { /* (4.6.4.3) */ - if (designated_port(port_no) /* (4.6.4.3) */ - && - (port_info[port_no].state != Disabled) - ) { - transmit_config(port_no); /* (4.6.4.3) */ - } /* (4.6.1.2) */ - } -} - -static int designated_port(int port_no) -{ - return ((br_cmp(port_info[port_no].designated_bridge.BRIDGE_ID, - bridge_info.bridge_id.BRIDGE_ID) == 0 - ) - && - (port_info[port_no].designated_port - == port_info[port_no].port_id - ) - ); -} - -static void reply(int port_no) /* (4.6.5) */ -{ - transmit_config(port_no); /* (4.6.5.3) */ -} - -static void transmit_tcn(void) -{ /* (4.6.6) */ - int port_no; - - port_no = bridge_info.root_port; - 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) */ -{ - root_selection(); /* (4.6.7.3.1) */ - /* (4.6.8.2) */ - designated_port_selection(); /* (4.6.7.3.2) */ - /* (4.6.9.2) */ -} - -static void root_selection(void) -{ /* (4.6.8) */ - int root_port; - int port_no; - root_port = No_port; - for (port_no = One; port_no <= No_of_ports; port_no++) { /* (4.6.8.3.1) */ - if (((!designated_port(port_no)) - && - (port_info[port_no].state != Disabled) - && - (br_cmp(port_info[port_no].designated_root.BRIDGE_ID, - bridge_info.bridge_id.BRIDGE_ID) < 0) - ) - && - ((root_port == No_port) - || - (br_cmp(port_info[port_no].designated_root.BRIDGE_ID, - port_info[root_port].designated_root.BRIDGE_ID) < 0 - ) - || - ((br_cmp(port_info[port_no].designated_root.BRIDGE_ID, - port_info[root_port].designated_root.BRIDGE_ID) == 0 - ) - && - (((port_info[port_no].designated_cost - + port_info[port_no].path_cost - ) - < - (port_info[root_port].designated_cost - + port_info[root_port].path_cost - ) /* (4.6.8.3.1(2)) */ - ) - || - (((port_info[port_no].designated_cost - + port_info[port_no].path_cost - ) - == - (port_info[root_port].designated_cost - + port_info[root_port].path_cost - ) - ) - && - ((br_cmp(port_info[port_no].designated_bridge.BRIDGE_ID, - port_info[root_port].designated_bridge.BRIDGE_ID) < 0 - ) /* (4.6.8.3.1(3)) */ - || - ((br_cmp(port_info[port_no].designated_bridge.BRIDGE_ID, - port_info[root_port].designated_bridge.BRIDGE_ID) == 0 - ) - && - ((port_info[port_no].designated_port - < port_info[root_port].designated_port - ) /* (4.6.8.3.1(4)) */ - || - ((port_info[port_no].designated_port -/* JRP: was missing an "=" ! */ == port_info[root_port].designated_port - ) - && - (port_info[port_no].port_id - < port_info[root_port].port_id - ) /* (4.6.8.3.1(5)) */ - ))))))))) { - root_port = port_no; - } - } - 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)) */ - } else { /* (4.6.8.3.3) */ - bridge_info.designated_root = port_info[root_port].designated_root; - /* (4.6.8.3.3(1)) */ - bridge_info.root_path_cost = (port_info[root_port].designated_cost - + port_info[root_port].path_cost - ); /* (4.6.8.3.3(2)) */ - } -} - -static void designated_port_selection(void) -{ /* (4.6.9) */ - 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) */ - || - ( - br_cmp(port_info[port_no].designated_root.BRIDGE_ID, - bridge_info.designated_root.BRIDGE_ID) != 0 - ) - || - (bridge_info.root_path_cost - < port_info[port_no].designated_cost - ) /* (4.6.9.3.3) */ - || - ((bridge_info.root_path_cost - == port_info[port_no].designated_cost - ) - && - ((br_cmp(bridge_info.bridge_id.BRIDGE_ID, - port_info[port_no].designated_bridge.BRIDGE_ID) < 0 - ) /* (4.6.9.3.4) */ - || - ((br_cmp(bridge_info.bridge_id.BRIDGE_ID, - port_info[port_no].designated_bridge.BRIDGE_ID) == 0 - ) - && - (port_info[port_no].port_id - <= port_info[port_no].designated_port - ) /* (4.6.9.3.5) */ - )))) { - become_designated_port(port_no); /* (4.6.10.3.2.2) */ - } - } -} - -static void become_designated_port(int port_no) -{ /* (4.6.10) */ - - /* (4.6.10.3.1) */ - port_info[port_no].designated_root = bridge_info.designated_root; - /* (4.6.10.3.2) */ - port_info[port_no].designated_cost = bridge_info.root_path_cost; - /* (4.6.10.3.3) */ - port_info[port_no].designated_bridge = bridge_info.bridge_id; - /* (4.6.10.3.4) */ - port_info[port_no].designated_port = port_info[port_no].port_id; -} + printk(KERN_INFO "NET4: Ethernet Bridge 008 for NET4.0\n"); -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) */ - 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 - - } - -} - -static void make_forwarding(int port_no) -{ /* (4.6.12) */ - if (port_info[port_no].state == Blocking) { /* (4.6.12.3) */ - set_port_state(port_no, Listening); /* (4.6.12.3.1) */ - start_forward_delay_timer(port_no); /* (4.6.12.3.2) */ - } -} - -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)) */ - } else if (!(bridge_info.top_change_detected)) { - transmit_tcn(); /* (4.6.14.3.2(1)) */ - start_tcn_timer(); /* (4.6.14.3.2(2)) */ - } - bridge_info.top_change_detected = 1; /* (4.6.14.3.3) */ -} - -static void topology_change_acknowledged(void) -{ /* (4.6.15) */ -#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) */ -} - -static void acknowledge_topology_change(int port_no) -{ /* (4.6.16) */ - port_info[port_no].top_change_ack = 1; - transmit_config(port_no); /* (4.6.16.3.2) */ -} - -static void make_blocking(int port_no) /* (4.6.13) */ -{ - - if ((port_info[port_no].state != Disabled) - && - (port_info[port_no].state != Blocking) - /* (4.6.13.3) */ - ) { - if ((port_info[port_no].state == Forwarding) - || - (port_info[port_no].state == Learning) - ) { - topology_change_detection(); /* (4.6.13.3.1) */ - /* (4.6.14.2.3) */ - } - set_port_state(port_no, Blocking);/* (4.6.13.3.2) */ - stop_forward_delay_timer(port_no);/* (4.6.13.3.3) */ - } -} - -static void set_port_state(int port_no, int state) -{ - port_info[port_no].state = state; -} - -static void received_config_bpdu(int port_no, Config_bpdu *config) /* (4.7.1) */ -{ - 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) */ - /* (4.6.2.2) */ - configuration_update(); /* (4.7.1.1.2) */ - /* (4.6.7.2.1) */ - port_state_selection(); /* (4.7.1.1.3) */ - /* (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 */ - stop_topology_change_timer(); - transmit_tcn(); /* (4.6.6.1) */ - start_tcn_timer(); - } - } - if (port_no == bridge_info.root_port) { - record_config_timeout_values(config); /* (4.7.1.1.6) */ - /* (4.6.3.2) */ - config_bpdu_generation(); /* (4.6.4.2.1) */ - if (config->top_change_ack) { /* (4.7.1.1.7) */ - topology_change_acknowledged(); /* (4.6.15.2) */ - } - } - } else if (designated_port(port_no)) { /* (4.7.1.2) */ - reply(port_no); /* (4.7.1.2.1) */ - /* (4.6.5.2) */ - } - } -} - -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) */ - acknowledge_topology_change(port_no); /* (4.7.2.2) */ - } /* (4.6.16.2) */ - } -} - -static void hello_timer_expiry(void) -{ /* (4.7.3) */ - config_bpdu_generation(); /* (4.6.4.2.2) */ - start_hello_timer(); -} - -static void message_age_timer_expiry(int port_no) /* (4.7.4) */ -{ - int root; - root = root_bridge(); + br_handle_frame_hook = br_handle_frame; + br_ioctl_hook = br_ioctl_deviceless_stub; + register_netdevice_notifier(&br_device_notifier); -#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) */ - /* (4.6.7.2.2) */ - port_state_selection(); /* (4.7.4.3) */ - /* (4.6.11.2.2) */ - if ((root_bridge()) && (!root)) { /* (4.7.4.4) */ - - bridge_info.max_age = bridge_info.bridge_max_age; /* (4.7.4.4.1) */ - bridge_info.hello_time = bridge_info.bridge_hello_time; - bridge_info.forward_delay = bridge_info.bridge_forward_delay; - topology_change_detection(); /* (4.7.4.4.2) */ - /* (4.6.14.2.4) */ - stop_tcn_timer(); /* (4.7.4.4.3) */ - config_bpdu_generation(); /* (4.7.4.4.4) */ - /* (4.6.4.4.3) */ - start_hello_timer(); - } -} - -static void forward_delay_timer_expiry(int port_no) /* (4.7.5) */ -{ - 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) */ - set_port_state(port_no, Forwarding); /* (4.7.5.2.1) */ - if (designated_for_some_port()) - { /* (4.7.5.2.2) */ - topology_change_detection(); /* (4.6.14.2.2) */ - - } - } -} - -static int designated_for_some_port(void) -{ - int 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)) - { - return (TRUE); - } - } - return (FALSE); -} - -static void tcn_timer_expiry(void) -{ /* (4.7.6) */ - transmit_tcn(); /* (4.7.6.1) */ - start_tcn_timer(); /* (4.7.6.2) */ -} - -static void topology_change_timer_expiry(void) -{ /* (4.7.7) */ - 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) - { - transmit_config(port_no); /* (4.7.8.1) */ - } /* (4.6.1.2.3) */ -} - -/* Vova Oksman: Write the buffer (contents of the Bridge table) */ -/* to a PROCfs file */ -int br_tree_get_info(char *buffer, char **start, off_t offset, int length, int dummy) -{ - int size; - int len=0; - off_t pos=0; - char* pbuffer; - - if(0==offset) - { - /* first time write the header */ - size = sprintf(buffer,"%s","MAC address Device Flags Age (sec.)\n"); - len=size; - } - - pbuffer=&buffer[len]; - sprintf_avl(&pbuffer,NULL,&pos,&len,offset,length); - - *start = buffer+len-(pos-offset); /* Start of wanted data */ - len = pos-offset; /* Start slop */ - if (len>length) - len = length; /* Ending slop */ - - return len; -} -#ifdef CONFIG_PROC_FS -struct proc_dir_entry proc_net_bridge= { - PROC_NET_BRIDGE, 6, "bridge", - S_IFREG | S_IRUGO, 1, 0, 0, - 0, &proc_net_inode_operations, - br_tree_get_info -}; -#endif -__initfunc(void br_init(void)) -{ /* (4.8.1) */ - int port_no; - - printk(KERN_INFO "NET4: Ethernet Bridge 007 for NET4.0\n"); - - /* Set up brg device information */ - bridge_info.instance = 0; - brg_init(); - - max_port_used = 0; - - /* - * 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) */ - - 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; - bridge_info.bridge_forward_delay = BRIDGE_FORWARD_DELAY; - bridge_info.hold_time = HOLD_TIME; - - bridge_info.max_age = bridge_info.bridge_max_age; /* (4.8.1.2) */ - bridge_info.hello_time = bridge_info.bridge_hello_time; - bridge_info.forward_delay = bridge_info.bridge_forward_delay; - - bridge_info.top_change_detected = 0; - 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 = Disable */ - 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 */ - br_stats.policy = BR_ACCEPT; /* Enable bridge to accpet all protocols */ - br_stats.exempt_protocols = 0; - /*start_hello_timer();*/ - /* Vova Oksman: register the function for the PROCfs "bridge" file */ -#ifdef CONFIG_PROC_FS - proc_net_register(&proc_net_bridge); -#endif -} - -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; - port_info[port_no].config_pending = FALSE;/* (4.8.1.4.4) */ - stop_message_age_timer(port_no); /* (4.8.1.4.5) */ - stop_forward_delay_timer(port_no); /* (4.8.1.4.6) */ - stop_hold_timer(port_no); /* (4.8.1.4.7) */ -} - -static void enable_port(int port_no) /* (4.8.2) */ -{ - br_init_port(port_no); - port_state_selection(); /* (4.8.2.7) */ -} /* */ - -static void disable_port(int port_no) /* (4.8.3) */ -{ - int root; - - root = root_bridge(); - become_designated_port(port_no); /* (4.8.3.1) */ - set_port_state(port_no, Disabled); /* (4.8.3.2) */ - port_info[port_no].top_change_ack = 0; - port_info[port_no].config_pending = FALSE;/* (4.8.3.4) */ - stop_message_age_timer(port_no); /* (4.8.3.5) */ - stop_forward_delay_timer(port_no); /* (4.8.3.6) */ - configuration_update(); - port_state_selection(); /* (4.8.3.7) */ - if ((root_bridge()) && (!root)) { /* (4.8.3.8) */ - bridge_info.max_age = bridge_info.bridge_max_age; /* (4.8.3.8.1) */ - bridge_info.hello_time = bridge_info.bridge_hello_time; - bridge_info.forward_delay = bridge_info.bridge_forward_delay; - topology_change_detection(); /* (4.8.3.8.2) */ - stop_tcn_timer(); /* (4.8.3.8.3) */ - config_bpdu_generation(); /* (4.8.3.8.4) */ - start_hello_timer(); - } -} - - -static void set_bridge_priority(bridge_id_t *new_bridge_id) - /* (4.8.4) */ -{ - - 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; - } - } - - bridge_info.bridge_id = *new_bridge_id; /* (4.8.4.3) */ - configuration_update(); /* (4.8.4.4) */ - port_state_selection(); /* (4.8.4.5) */ - if ((root_bridge()) && (!root)) { /* (4.8.4.6) */ - bridge_info.max_age = bridge_info.bridge_max_age; /* (4.8.4.6.1) */ - bridge_info.hello_time = bridge_info.bridge_hello_time; - bridge_info.forward_delay = bridge_info.bridge_forward_delay; - topology_change_detection(); /* (4.8.4.6.2) */ - stop_tcn_timer(); /* (4.8.4.6.3) */ - config_bpdu_generation(), /* (4.8.4.6.4) */ - start_hello_timer(); - } -} - -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; - } - port_info[port_no].port_id = new_port_id; /* (4.8.5.3) */ - if ((br_cmp(bridge_info.bridge_id.BRIDGE_ID, - port_info[port_no].designated_bridge.BRIDGE_ID) == 0 - ) - && - (port_info[port_no].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) */ - } -} - -static void set_path_cost(int port_no, unsigned short path_cost) - /* (4.8.6) */ -{ - port_info[port_no].path_cost = path_cost; /* (4.8.6.1) */ - configuration_update(); /* (4.8.6.2) */ - port_state_selection(); /* (4.8.6.3) */ -} - -static void br_tick(unsigned long arg) -{ - int port_no; - - if(!(br_stats.flags & BR_UP)) - return; /* JRP: we have been shot down */ - - if (hello_timer_expired()) - hello_timer_expiry(); - - if (tcn_timer_expired()) - tcn_timer_expiry(); - - if (topology_change_timer_expired()) - topology_change_timer_expiry(); - - 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)) - message_age_timer_expiry(port_no); - - if (hold_timer_expired(port_no)) - hold_timer_expiry(port_no); - } - /* call me again sometime... */ - tl.expires = jiffies+HZ; /* 1 second */ - tl.function = br_tick; - add_timer(&tl); -} - -static void start_hello_timer(void) -{ - hello_timer.value = 0; - hello_timer.active = TRUE; -} - -static void stop_hello_timer(void) -{ - hello_timer.active = FALSE; -} - -static int hello_timer_expired(void) -{ - if (hello_timer.active && (++hello_timer.value >= bridge_info.hello_time)) - { - hello_timer.active = FALSE; - return (TRUE); - } - return (FALSE); -} - -static void start_tcn_timer(void) -{ - tcn_timer.value = 0; - tcn_timer.active = TRUE; -} - -static void stop_tcn_timer(void) -{ - tcn_timer.active = FALSE; -} - -static int tcn_timer_expired(void) -{ - if (tcn_timer.active && (++tcn_timer.value >= bridge_info.bridge_hello_time)) - { - tcn_timer.active = FALSE; - return (TRUE); - } - return (FALSE); - -} - -static void start_topology_change_timer(void) -{ - topology_change_timer.value = 0; - topology_change_timer.active = TRUE; -} - -static void stop_topology_change_timer(void) -{ - topology_change_timer.active = FALSE; -} - -static int topology_change_timer_expired(void) -{ - if (topology_change_timer.active - && (++topology_change_timer.value >= bridge_info.topology_change_time )) - { - topology_change_timer.active = FALSE; - return (TRUE); - } - return (FALSE); -} - -static void start_message_age_timer(int port_no, unsigned short message_age) -{ - message_age_timer[port_no].value = message_age; - message_age_timer[port_no].active = TRUE; -} - -static void stop_message_age_timer(int port_no) -{ - message_age_timer[port_no].active = FALSE; -} - -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)) - { - message_age_timer[port_no].active = FALSE; - return (TRUE); - } - return (FALSE); -} - -static void start_forward_delay_timer(int port_no) -{ - forward_delay_timer[port_no].value = 0; - forward_delay_timer[port_no].active = TRUE; -} - -static void stop_forward_delay_timer(int port_no) -{ - forward_delay_timer[port_no].active = FALSE; -} - -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); -} - -static void start_hold_timer(int port_no) -{ - hold_timer[port_no].value = 0; - hold_timer[port_no].active = TRUE; -} - -static void stop_hold_timer(int port_no) -{ - hold_timer[port_no].active = FALSE; -} - -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].active = FALSE; - return (TRUE); - } - return (FALSE); - -} - -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; - struct ethhdr *eth; - 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->nh.raw = skb_put(skb,size); - memset(skb->nh.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(KERN_DEBUG "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->nh.raw += skb->dev->hard_header_len; - llc_buffer = skb->nh.raw; - *llc_buffer++ = BRIDGE_LLC1_DSAP; - *llc_buffer++ = BRIDGE_LLC1_SSAP; - *llc_buffer++ = BRIDGE_LLC1_CTRL; - /* set nh.raw to where the bpdu starts */ - skb->nh.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; - - /* - * Keep silent when disabled or when STP disabled - */ - - if(!(br_stats.flags & BR_UP) || (br_stats.flags & BR_STP_DISABLED)) - return -1; - - /* - * Create and send the message - */ - - skb = alloc_bridge_skb(port_no, BRIDGE_BPDU_8021_CONFIG_SIZE, - "config"); - if (skb == NULL) - return(-1); - - /* copy fields before "flags" */ - memcpy(skb->nh.raw, config_bpdu, BRIDGE_BPDU_8021_CONFIG_FLAG_OFFSET); - - /* build the "flags" field */ - *(skb->nh.raw+BRIDGE_BPDU_8021_CONFIG_FLAG_OFFSET) = 0; - if (config_bpdu->top_change_ack) - *(skb->nh.raw+BRIDGE_BPDU_8021_CONFIG_FLAG_OFFSET) |= 0x80; - if (config_bpdu->top_change) - *(skb->nh.raw+BRIDGE_BPDU_8021_CONFIG_FLAG_OFFSET) |= 0x01; - - config_bpdu_hton(config_bpdu); - /* copy the rest */ - memcpy(skb->nh.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; - - /* - * Keep silent when disabled or when STP disabled - */ - - if(!(br_stats.flags & BR_UP) || (br_stats.flags & BR_STP_DISABLED)) - return -1; - - - skb = alloc_bridge_skb(port_no, sizeof(Tcn_bpdu), "tcn"); - if (skb == NULL) - return(-1); - - memcpy(skb->nh.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) -{ - struct device *dev = ptr; - int i; - - /* check for loopback devices */ - if (dev->flags & IFF_LOOPBACK) - return(NOTIFY_DONE); - - if (dev == &brg_if.dev) - return(NOTIFY_DONE); /* Don't attach the brg device to a port! */ - - 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(KERN_DEBUG "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 == NULL || port_info[i].dev == dev) - { - port_info[i].dev = dev; - port_info[i].port_id = i; - dev->bridge_port_id = i; - if( i > max_port_used ) - max_port_used = 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); - } - /* Add local MAC address */ - br_add_local_mac(dev->dev_addr); - /* Save MAC address for latter change address events */ - memcpy(port_info[i].ifmac.BRIDGE_ID_ULA, dev->dev_addr, 6); - 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); - if (br_stats.flags & BR_STP_DISABLED) - port_info[i].state = Forwarding; - else - make_forwarding(i); - } - return NOTIFY_DONE; - break; - } - } - break; - case NETDEV_REGISTER: - if (br_stats.flags & BR_DEBUG) - printk(KERN_DEBUG "br_device_event: NETDEV_REGISTER...\n"); - /* printk(KERN_ERR "br_device_event: NETDEV_REGISTER...\n"); */ - /* printk(KERN_ERR "br_device_event: dev->type: 0x%X\n", dev->type); */ - /* Only handle ethernet ports */ - if(dev->type!=ARPHRD_ETHER && dev->type!=ARPHRD_LOOPBACK) - return NOTIFY_DONE; - /* printk(KERN_ERR "br_device_event: Looking for port...\n"); */ - for (i = One; i <= No_of_ports; i++) - { - if (port_info[i].dev == NULL || port_info[i].dev == dev) - { - /* printk(KERN_ERR "br_device_event: Found port %d\n", i); */ - port_info[i].dev = dev; - port_info[i].port_id = i; - dev->bridge_port_id = i; - if( i > max_port_used ) - max_port_used = i; - /* handle local MAC address minuplations */ - br_add_local_mac(dev->dev_addr); - memcpy(port_info[i].ifmac.BRIDGE_ID_ULA, dev->dev_addr, 6); - return NOTIFY_DONE; - break; - } - } - break; - case NETDEV_UNREGISTER: - if (br_stats.flags & BR_DEBUG) - printk(KERN_DEBUG "br_device_event: NETDEV_UNREGISTER...\n"); - i = find_port(dev); - if (i > 0) { - br_avl_delete_by_port(i); - memset(port_info[i].ifmac.BRIDGE_ID_ULA, 0, 6); - port_info[i].dev = NULL; - } - break; - case NETDEV_CHANGEADDR: - if (br_stats.flags & BR_DEBUG) - printk(KERN_DEBUG "br_device_event: NETDEV_CHANGEADDR...\n"); - i = find_port(dev); - if (i <= 0) - break; - if (memcmp(port_info[i].ifmac.BRIDGE_ID_ULA, dev->dev_addr, 6) != 0) - break; /* Don't worry about a change of hardware broadcast address! */ - if (dev->start) { - printk(KERN_CRIT "br_device_event: NETDEV_CHANGEADDR on busy device %s - FIX DRIVER!\n", - dev->name); - /* return NOTIFY_BAD; It SHOULD be this, but I want to be friendly... */ - return NOTIFY_DONE; - } - br_avl_delete_by_port(i); - memset(port_info[i].ifmac.BRIDGE_ID_ULA, 0, 6); - break; - } - return NOTIFY_DONE; -} - -/* Routine to loop over device list and register - * interfaces to bridge. Called from last part of net_dev_init just before - * bootp/rarp interface setup - */ -void br_spacedevice_register(void) -{ - struct device *dev; - for( dev = dev_base; dev != NULL; dev = dev->next) - { - br_device_event(NULL, NETDEV_REGISTER, dev); - } -} - - -/* This is for SPEED in the kernel in net_bh.c */ - -int br_call_bridge(struct sk_buff *skb, unsigned short type) -{ - int port; - struct device *dev; - -#if 0 /* Checked first in handle_bridge to save expense of function call */ - if(!(br_stats.flags & BR_UP)) - return 0; -#endif - - dev = skb->dev; - - /* Check for brg0 device - */ - if (dev == &brg_if.dev) - return 0; - - port = dev->bridge_port_id; - - if(!port) - return 0; - - /* Sanity - make sure we are not leaping off into fairy space! */ - if ( port < 0 || port > max_port_used || port_info[port].dev != dev) { - if (net_ratelimit()) - printk(KERN_CRIT "br_call_bridge: device %s has invalid port ID %d!\n", - dev->name, - dev->bridge_port_id); - return 0; - } - - if(user_port_state[port] == Disabled) - return 0; - - if (!br_protocol_ok(ntohs(type))) - return 0; - - return 1; - -} - - -/* - * following routine is called when a frame is received - * from an interface, it returns 1 when it consumes the - * frame, 0 when it does not - */ - -int br_receive_frame(struct sk_buff *skb) /* 3.5 */ -{ - int port, ret; - Port_data *p; - struct ethhdr *eth; - struct device *dev; - - /* sanity */ - if (!skb) { - printk(KERN_CRIT "br_receive_frame: no skb!\n"); - return(1); - } - - dev = skb->dev; - - /* Check for brg0 device - */ - if (dev == &brg_if.dev) - return 0; - - skb->pkt_bridged = IS_BRIDGED; - - /* check for loopback */ - if (dev->flags & IFF_LOOPBACK) - return 0 ; - -#if 0 - port = find_port(dev); -#else - port = dev->bridge_port_id; -#endif - - if(!port) - return 0; - - /* Hand off to brg_rx BEFORE we screw up the skb */ - if(brg_rx(skb, port)) - return(1); - - skb->nh.raw = skb->mac.raw; - eth = skb->mac.ethernet; - 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) - */ - - /* 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 (p->state) - { - case Learning: - if((ret = br_learn(skb, port))) - { /* 3.8 */ - if (ret > 0) ++br_stats_cnt.drop_multicast; - return br_drop(skb); - } - /* fall through */ - case Listening: - /* fall through */ - case Blocking: - ++br_stats_cnt.notForwarding; - return(br_drop(skb)); - /* - case Disabled: is now handled before this switch ! - Keep the break to allow GCC to use a jmp table. - */ - break; - case Forwarding: - if((ret = br_learn(skb, port))) { /* 3.8 */ - if (ret > 0) ++br_stats_cnt.drop_multicast; - return br_drop(skb); - } - /* Now this frame came from one of bridged - 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, p->state); - ++br_stats_cnt.unknown_state; - return(br_drop(skb)); /* discard frame */ - } -} - -/* - * the following routine is called to transmit frames from the host - * stack. it returns 1 when it consumes the frame and - * 0 when it does not. - */ - -int br_tx_frame(struct sk_buff *skb) /* 3.5 */ -{ - int port; - struct ethhdr *eth; - - /* sanity */ - if (!skb) - { - printk(KERN_CRIT "br_tx_frame: no skb!\n"); - return(0); - } - - if (!skb->dev) - { - printk(KERN_CRIT "br_tx_frame: no dev!\n"); - return(0); - } - - /* check for loopback */ - if (skb->dev->flags & IFF_LOOPBACK) - return(0); - - /* 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 (((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->nh.raw = skb->data; - eth = skb->mac.ethernet; - port = 0; /* an impossible port (locally generated) */ - if (br_stats.flags & BR_DEBUG) - printk(KERN_DEBUG "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], - 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]); - 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(time_after(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 we must dropped the frame due to multicast - * limitations, or -1 because of not enough memory. - * - * NB Can be called when skb->nh.raw is NOT set up when - * receiving frames on brg0 via brg_rx - */ - -static int br_learn(struct sk_buff *skb, int port) /* 3.8 */ -{ - struct fdb *f, *oldfdb; - Port_data *p = &port_info[port]; - struct ethhdr *eth = skb->mac.ethernet; - - /* 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 - */ - - /* 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_WARNING "br_learn: unable to malloc fdb\n"); - return(-1); /* this drops 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; -} - -/* - * this routine always consumes the frame - */ - -static int br_drop(struct sk_buff *skb) -{ - kfree_skb(skb); - return(1); -} - -/* - * this routine always consumes the frame - */ - -static int br_dev_drop(struct sk_buff *skb) -{ - dev_kfree_skb(skb); - return(1); -} - -/* - * Forward the frame SKB to proper port[s]. PORT is the port that the - * frame has come from; we will not send the frame back there. PORT == 0 - * means we have been called from br_tx_fr(), not from br_receive_frame(). - * - * this routine returns 1 if it consumes the frame, 0 - * if not... - */ - -static int br_forward(struct sk_buff *skb, int port) /* 3.7 */ -{ - struct fdb *f; - - /* - * flood all ports with frames destined for a group - * address. If frame came from above, drop it, - * otherwise it will be handled in br_receive_frame() - * Multicast frames will also need to be seen - * by our upper layers. - */ - if (skb->mac.ethernet->h_dest[0] & 0x01) - { - /* group address */ - br_flood(skb, port); - /* - * External groups are fed out via the normal source - * This probably should be dropped since the flood will - * have sent it anyway. - */ - if (port == 0) - { - /* Locally generated */ - ++br_stats_cnt.local_multicast; - return(br_dev_drop(skb)); - } - ++br_stats_cnt.forwarded_multicast; - return(0); - } - 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)) - { - 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) - { - /* timer expired, invalidate entry */ - f->flags &= ~FDB_ENT_VALID; - if (br_stats.flags & BR_DEBUG) - printk(KERN_DEBUG "fdb entry expired...\n"); - /* - * 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; - - /* reset the skb->ip pointer */ - skb->nh.raw = skb->data + ETH_HLEN; - - /* - * Send the buffer out. - */ - - skb->dev=port_info[f->port].dev; - - /* - * We send this still locked - */ - skb->priority = 1; - dev_queue_xmit(skb); - return(1); /* skb has been consumed */ - } - 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(KERN_DEBUG "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 - */ - return(br_dev_drop(skb)); - } - } -} - -/* - * this routine sends a copy of the frame to all forwarding ports - * with the exception of the port given. This routine never - * consumes the original frame. - */ - -static int br_flood(struct sk_buff *skb, int port) -{ - int i; - struct sk_buff *nskb; - - for (i = One; i <= No_of_ports; i++) - { - if (i == port) /* don't send back where we got it */ - continue; - if (i > max_port_used) - /* Don't go scanning empty port entries */ - break; - if (port_info[i].state == Forwarding) - { - nskb = skb_clone(skb, GFP_ATOMIC); - if(nskb==NULL) - continue; - /* mark that's we've been here... */ - nskb->pkt_bridged = IS_BRIDGED; - /* Send to each port in turn */ - nskb->dev= port_info[i].dev; - /* To get here we must have done ARP already, - or have a received valid MAC header */ - -/* printk(KERN_DEBUG "Flood to port %d\n",i);*/ - nskb->nh.raw = nskb->data + ETH_HLEN; - nskb->priority = 1; - dev_queue_xmit(nskb); - } - } - 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, "lec", 3) == 0) /* ATM Lan Emulation (LANE) */ - return(7); /* 155 Mbs */ - if (strncmp(dev->name, "eth", 3) == 0) /* ethernet */ - return(100); - if (strncmp(dev->name, "plip",4) == 0) /* plip */ - return (1600); - return(100); /* default */ -} - -/* - * this routine always consumes the skb - */ - -static void br_bpdu(struct sk_buff *skb, int port) /* consumes skb */ -{ - char *bufp = skb->data + ETH_HLEN; - Tcn_bpdu *bpdu = (Tcn_bpdu *) (bufp + BRIDGE_LLC1_HS); - Config_bpdu rcv_bpdu; - - if(!(br_stats.flags & BR_STP_DISABLED) && - (*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; -} - - -/* Fill in interface names in port_info structure - */ -static void br_get_ifdata(void) { - int i; - - for(i=One;i<=No_of_ports; i++) { - - port_info[i].admin_state = user_port_state[i]; - - /* memset IS needed. Kernel strncpy does NOT NULL terminate - * strings when limit reached - */ - memset(port_info[i].ifname, 0, IFNAMSIZ); - if( port_info[i].dev == 0 ) - continue; - strncpy(port_info[i].ifname, port_info[i].dev->name, IFNAMSIZ-1); - /* Being paranoid */ - port_info[i].ifname[IFNAMSIZ-1] = '\0'; - } -} - -/* Given an interface index, loop over port array to see if configured. If - so, return port number, otherwise error */ -static int br_find_port(int ifindex) -{ - int i; - - for(i=1; i <= No_of_ports; i++) { - if (port_info[i].dev == 0) - continue; - if (port_info[i].dev->ifindex == ifindex) - return(i); - } - - return -EUNATCH; /* Tell me if this is incorrect error code for this case */ -} - - -int br_ioctl(unsigned int cmd, void *arg) -{ - int err, i, ifflags; - struct br_cf bcf; - bridge_id_t new_id; - struct device *dev; - - switch(cmd) - { - case SIOCGIFBR: /* get bridging control blocks */ - memcpy(&br_stats.bridge_data, &bridge_info, sizeof(Bridge_data)); - - /* Fill in interface names in port_info*/ - br_get_ifdata(); - - br_stats.num_ports = No_of_ports; - memcpy(&br_stats.port_data, &port_info, sizeof(Port_data)*All_ports); - - err = copy_to_user(arg, &br_stats, sizeof(struct br_stat)); - if (err) - { - err = -EFAULT; - } - return err; - case SIOCSIFBR: - err = copy_from_user(&bcf, arg, sizeof(struct br_cf)); - if (err) - return -EFAULT; - 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) */ - if (br_stats.flags & BR_STP_DISABLED) - for(i=One;i<=No_of_ports; i++) - if((user_port_state[i] != Disabled) && port_info[i].dev) - port_info[i].state = Forwarding; - 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: - if (!(br_stats.flags & BR_UP)) - return(-EALREADY); - printk(KERN_DEBUG "br: disabling bridging function\n"); - br_stats.flags &= ~BR_UP; /* disable bridge */ - stop_hello_timer(); - for (i = One; i <= No_of_ports; i++) - if (port_info[i].state != Disabled) - disable_port(i); - break; - case BRCMD_TOGGLE_STP: - printk(KERN_DEBUG "br: %s spanning tree protcol\n", - (br_stats.flags & BR_STP_DISABLED) ? "enabling" : "disabling"); - if (br_stats.flags & BR_STP_DISABLED) { /* enable STP */ - for(i=One;i<=No_of_ports; i++) - if((user_port_state[i] != Disabled) && port_info[i].dev) - enable_port(i); - } else { /* STP was enabled, now disable it */ - for (i = One; i <= No_of_ports; i++) - if (port_info[i].state != Disabled && port_info[i].dev) - port_info[i].state = Forwarding; - } - br_stats.flags ^= BR_STP_DISABLED; - break; - case BRCMD_IF_ENABLE: - bcf.arg1 = br_find_port(bcf.arg1); - if ((signed)bcf.arg1 < 0) - return(bcf.arg1); - case BRCMD_PORT_ENABLE: - if (port_info[bcf.arg1].dev == 0) - return(-EINVAL); - if (user_port_state[bcf.arg1] != Disabled) - return(-EALREADY); - printk(KERN_DEBUG "br: enabling port %i\n",bcf.arg1); - dev = port_info[bcf.arg1].dev; - ifflags = (dev->flags&~(IFF_PROMISC|IFF_ALLMULTI)) - |(dev->gflags&(IFF_PROMISC|IFF_ALLMULTI)); - dev_change_flags(dev, ifflags|IFF_PROMISC); - user_port_state[bcf.arg1] = ~Disabled; - if(br_stats.flags & BR_UP) - enable_port(bcf.arg1); - break; - case BRCMD_IF_DISABLE: - bcf.arg1 = br_find_port(bcf.arg1); - if ((signed)bcf.arg1 < 0) - return(bcf.arg1); - case BRCMD_PORT_DISABLE: - if (port_info[bcf.arg1].dev == 0) - return(-EINVAL); - if (user_port_state[bcf.arg1] == Disabled) - return(-EALREADY); - printk(KERN_DEBUG "br: disabling port %i\n",bcf.arg1); - user_port_state[bcf.arg1] = Disabled; - if(br_stats.flags & BR_UP) - disable_port(bcf.arg1); - dev = port_info[bcf.arg1].dev; - ifflags = (dev->flags&~(IFF_PROMISC|IFF_ALLMULTI)) - |(dev->gflags&(IFF_PROMISC|IFF_ALLMULTI)); - dev_change_flags(port_info[bcf.arg1].dev, ifflags & ~IFF_PROMISC); - break; - case BRCMD_SET_BRIDGE_PRIORITY: - new_id = bridge_info.bridge_id; - new_id.BRIDGE_PRIORITY = htons(bcf.arg1); - set_bridge_priority(&new_id); - break; - case BRCMD_SET_IF_PRIORITY: - bcf.arg1 = br_find_port(bcf.arg1); - if ((signed)bcf.arg1 < 0) - return(bcf.arg1); - case BRCMD_SET_PORT_PRIORITY: - if((port_info[bcf.arg1].dev == 0) - || (bcf.arg2 & ~0xff)) - return(-EINVAL); - port_priority[bcf.arg1] = bcf.arg2; - set_port_priority(bcf.arg1); - break; - case BRCMD_SET_IF_PATH_COST: - bcf.arg1 = br_find_port(bcf.arg1); - if ((signed)bcf.arg1 < 0) - return(bcf.arg1); - case BRCMD_SET_PATH_COST: - if (port_info[bcf.arg1].dev == 0) - return(-EINVAL); - set_path_cost(bcf.arg1, bcf.arg2); - break; - case BRCMD_ENABLE_DEBUG: - br_stats.flags |= BR_DEBUG; - break; - case BRCMD_DISABLE_DEBUG: - br_stats.flags &= ~BR_DEBUG; - break; - case BRCMD_SET_POLICY: - return br_set_policy(bcf.arg1); - case BRCMD_EXEMPT_PROTOCOL: - return br_add_exempt_protocol(bcf.arg1); - case BRCMD_ENABLE_PROT_STATS: - br_stats.flags |= BR_PROT_STATS; - break; - case BRCMD_DISABLE_PROT_STATS: - br_stats.flags &= ~BR_PROT_STATS; - break; - case BRCMD_ZERO_PROT_STATS: - 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.arg2cmd_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; - } - return(0); - default: - return -EINVAL; - } - /*NOTREACHED*/ return 0; } -static int br_cmp(unsigned int *a, unsigned int *b) -{ - int i; - for (i=0; i<2; 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); -} - - - - -/* -------------------------------------------------------------------------------- - * - * - * Bridge network device here for future modularization - device structures - * must be 'static' inside bridge instance - * Modelled after sch_teql.c - * - */ - - - -/* - * Index to functions. - */ - -int brg_probe(struct device *dev); -static int brg_open(struct device *dev); -static int brg_start_xmit(struct sk_buff *skb, struct device *dev); -static int brg_close(struct device *dev); -static struct net_device_stats *brg_get_stats(struct device *dev); -static void brg_set_multicast_list(struct device *dev); - -/* - * Board-specific info in dev->priv. - */ - -struct net_local -{ - __u32 groups; - struct net_device_stats stats; -}; - - - - -/* - * To call this a probe is a bit misleading, however for real - * hardware it would have to check what was present. - */ - -__initfunc(int brg_probe(struct device *dev)) -{ - unsigned int bogomips; - struct timeval utime; - - printk(KERN_INFO "%s: network interface for Ethernet Bridge 007/NET4.0\n", dev->name); - - /* - * Initialize the device structure. - */ - - dev->priv = kmalloc(sizeof(struct net_local), GFP_KERNEL); - if (dev->priv == NULL) - return -ENOMEM; - memset(dev->priv, 0, sizeof(struct net_local)); - - /* Set up MAC address based on BogoMIPs figure for first CPU and time - */ - bogomips = (loops_per_jiffy+2500)/500000/HZ ; - get_fast_time(&utime); - - /* Ummmm.... YES! */ - dev->dev_addr[0] = '\xFE'; - dev->dev_addr[1] = '\xFD'; - dev->dev_addr[2] = (bridge_info.instance & 0x0F) << 4; - dev->dev_addr[2] |= ((utime.tv_sec & 0x000F0000) >> 16); - dev->dev_addr[3] = bogomips & 0xFF; - dev->dev_addr[4] = (utime.tv_sec & 0x0000FF00) >> 8; - dev->dev_addr[5] = (utime.tv_sec & 0x000000FF); - - printk(KERN_INFO "%s: generated MAC address %2.2X:%2.2X:%2.2X:%2.2X:%2.2X:%2.2X\n", - dev->name, - dev->dev_addr[0], - dev->dev_addr[1], - dev->dev_addr[2], - dev->dev_addr[3], - dev->dev_addr[4], - dev->dev_addr[5]); - - - printk(KERN_INFO "%s: attached to bridge instance %lu\n", dev->name, dev->base_addr); - - /* - * The brg specific entries in the device structure. - */ - - dev->open = brg_open; - dev->hard_start_xmit = brg_start_xmit; - dev->stop = brg_close; - dev->get_stats = brg_get_stats; - dev->set_multicast_list = brg_set_multicast_list; - - /* - * Setup the generic properties - */ - - ether_setup(dev); - dev->tx_queue_len = 0; - return 0; -} - -/* - * Open/initialize the board. - */ - -static int brg_open(struct device *dev) -{ - if (br_stats.flags & BR_DEBUG) - printk(KERN_DEBUG "%s: Doing brg_open()...", dev->name); - - if (memcmp(dev->dev_addr, "\x00\x00\x00\x00\x00\x00", ETH_ALEN) == 0) - return -EFAULT; - - dev->start = 1; - dev->tbusy = 0; - return 0; -} - -static unsigned brg_mc_hash(__u8 *dest) -{ - unsigned idx = 0; - idx ^= dest[0]; - idx ^= dest[1]; - idx ^= dest[2]; - idx ^= dest[3]; - idx ^= dest[4]; - idx ^= dest[5]; - return 1U << (idx&0x1F); -} - -static void brg_set_multicast_list(struct device *dev) -{ - unsigned groups = ~0; - struct net_local *lp = (struct net_local *)dev->priv; - - if (!(dev->flags&(IFF_PROMISC|IFF_ALLMULTI))) { - struct dev_mc_list *dmi; - - groups = brg_mc_hash(dev->broadcast); - - for (dmi=dev->mc_list; dmi; dmi=dmi->next) { - if (dmi->dmi_addrlen != 6) - continue; - groups |= brg_mc_hash(dmi->dmi_addr); - } - } - lp->groups = groups; -} - -/* - * We transmit by throwing the packet at the bridge. - */ - -static int brg_start_xmit(struct sk_buff *skb, struct device *dev) -{ - struct net_local *lp = (struct net_local *)dev->priv; - struct ethhdr *eth = (struct ethhdr*)skb->data; - int port; - - /* Deal with the bridge being disabled */ - if(!(br_stats.flags & BR_UP)) { - /* Either this */ - /* lp->stats.tx_errors++; */ /* this condition is NOT an error */ - /* or this (implied by RFC 2233) */ - lp->stats.tx_dropped++; - dev_kfree_skb(skb); - return 0; - } - - lp->stats.tx_bytes+=skb->len; - lp->stats.tx_packets++; - -#if 0 - ++br_stats_cnt.port_not_disable; -#endif - skb->mac.raw = skb->nh.raw = skb->data; - eth = skb->mac.ethernet; - port = 0; /* an impossible port (locally generated) */ - - if (br_stats.flags & BR_DEBUG) - printk(KERN_DEBUG "%s: brg_start_xmit - src %02x:%02x:%02x:%02x:%02x:%02x" - " dest %02x:%02x:%02x:%02x:%02x:%02x\n", - dev->name, - 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]); - - /* Forward the packet ! */ - if(br_forward(skb, port)) - return(0); - - /* Throw packet initially */ - dev_kfree_skb(skb); - return 0; -} - - -/* - * The typical workload of the driver: - * Handle the ether interface interrupts. - * - * (In this case handle the packets posted from the bridge) - */ - -static int brg_rx(struct sk_buff *skb, int port) -{ - struct device *dev = &brg_if.dev; - struct net_local *lp = (struct net_local *)dev->priv; - struct ethhdr *eth = (struct ethhdr*)(skb->data); - int len = skb->len; - int clone = 0; - - if (br_stats.flags & BR_DEBUG) - printk(KERN_DEBUG "%s: brg_rx()\n", dev->name); - - /* Get out of here if the bridge interface is not up - */ - if(!(dev->flags & IFF_UP)) - return(0); - - /* Check that the port that this thing came off is in the forwarding state - * We sould only listen to the same address scope we will transmit to. - */ - if(port_info[port].state != Forwarding) - return(0); - - /* Is this for us? - broadcast/mulitcast/promiscuous packets need cloning, - * with uni-cast we eat the packet - */ - clone = 0; - if (dev->flags & IFF_PROMISC) { - clone = 1; - } - else if (eth->h_dest[0]&1) { - if (!(dev->flags&(IFF_ALLMULTI)) - && !(brg_mc_hash(eth->h_dest)&lp->groups)) - return(0); - clone = 1; - } - else if (memcmp(eth->h_dest, dev->dev_addr, ETH_ALEN) != 0) { - return(0); - } - - /* Clone things here - we want to be transparent before we check packet data - * integrity - */ - if(clone) { - struct sk_buff *skb2 = skb; - skb = skb_clone(skb2, GFP_ATOMIC); - if (skb == NULL) { - return(0); - } - - } - else { - /* Learn source addresses for unicast non-promiscuous - * frames for brg0 - */ - if(br_learn(skb, port)) { - return br_drop(skb); - } - } - - /* Check packet length - */ - if (len < 16) { - printk(KERN_DEBUG "%s : rx len = %d\n", dev->name, len); - kfree_skb(skb); - return(!clone); - } - - if (br_stats.flags & BR_DEBUG) - printk(KERN_DEBUG "%s: brg_rx - src %02x:%02x:%02x:%02x:%02x:%02x" - " dest %02x:%02x:%02x:%02x:%02x:%02x\n", - dev->name, - 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]); - - /* Do it */ - skb->pkt_type = PACKET_HOST; - skb->dev = dev; - skb->protocol=eth_type_trans(skb,dev); - memset(skb->cb, 0, sizeof(skb->cb)); - lp->stats.rx_packets++; - lp->stats.rx_bytes+=len; - netif_rx(skb); - return(!clone); -} - -static int brg_close(struct device *dev) -{ - if (br_stats.flags & BR_DEBUG) - printk(KERN_DEBUG "%s: Shutting down.\n", dev->name); - - dev->tbusy = 1; - dev->start = 0; - - return 0; -} - -static struct net_device_stats *brg_get_stats(struct device *dev) -{ - struct net_local *lp = (struct net_local *)dev->priv; - return &lp->stats; -} - -/* - * - */ +#ifdef MODULE +EXPORT_NO_SYMBOLS; -__initfunc(int brg_init(void)) +int init_module(void) { - int err; - - memset(&brg_if, 0, sizeof(brg_if)); - - rtnl_lock(); - - brg_if.dev.base_addr = bridge_info.instance; - sprintf (brg_if.name, "brg%d", bridge_info.instance); - brg_if.dev.name = (void*)&brg_if.name; - if(dev_get(brg_if.name)) { - printk(KERN_INFO "%s already loaded.\n", brg_if.name); - return -EBUSY; - } - brg_if.dev.init = brg_probe; - - err = register_netdevice(&brg_if.dev); - rtnl_unlock(); - return err; + return br_init(); } - -#if 0 /* Its here if we ever need it... */ -#ifdef MODULE - void cleanup_module(void) { - - /* - * Unregister the device - */ - rtnl_lock(); - unregister_netdevice(&the_master.dev); - rtnl_unlock(); - - /* - * Free up the private structure. - */ - - kfree(brg_if.dev.priv); - brg_if.dev.priv = NULL; /* gets re-allocated by brg_probe */ + unregister_netdevice_notifier(&br_device_notifier); + br_ioctl_hook = NULL; + br_handle_frame_hook = NULL; + synchronize_bh(); } -#endif /* MODULE */ - #endif diff -urN linux-2.2.20/net/bridge/br_device.c linux-2.2.20br/net/bridge/br_device.c --- linux-2.2.20/net/bridge/br_device.c Thu Jan 1 01:00:00 1970 +++ linux-2.2.20br/net/bridge/br_device.c Mon Dec 10 16:25:58 2001 @@ -0,0 +1,126 @@ +/* + * Device handling code + * Linux ethernet bridge + * + * Authors: + * Lennert Buytenhek + * + * $Id$ + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ + +#include +#include +#include +#include +#include +#include "br_private.h" + +static int br_dev_do_ioctl(struct device *dev, struct ifreq *rq, int cmd) +{ + unsigned long args[4]; + unsigned long *data; + + if (cmd != SIOCDEVPRIVATE) + return -EOPNOTSUPP; + + data = (unsigned long *)rq->ifr_data; + if (copy_from_user(args, data, 4*sizeof(unsigned long))) + return -EFAULT; + + return br_ioctl(dev->priv, args[0], args[1], args[2], args[3]); +} + +static struct net_device_stats *br_dev_get_stats(struct device *dev) +{ + struct net_bridge *br; + + br = dev->priv; + + return &br->statistics; +} + +int br_dev_xmit(struct sk_buff *skb, struct device *dev) +{ + struct net_bridge *br; + unsigned char *dest; + struct net_bridge_fdb_entry *dst; + + br = dev->priv; + br->statistics.tx_packets++; + br->statistics.tx_bytes += skb->len; + + dest = skb->data; + + if (dest[0] & 1) { + br_flood(br, skb, 0); + return 0; + } + + if ((dst = br_fdb_get(br, dest)) != NULL) { + br_forward(dst->dst, skb); + br_fdb_put(dst); + return 0; + } + + br_flood(br, skb, 0); + return 0; +} + +static int br_dev_open(struct device *dev) +{ + struct net_bridge *br; + + dev->start = 1; + dev->tbusy = 0; + + br = dev->priv; + read_lock(&br->lock); + br_stp_enable_bridge(br); + read_unlock(&br->lock); + + return 0; +} + +static void br_dev_set_multicast_list(struct device *dev) +{ +} + +static int br_dev_stop(struct device *dev) +{ + struct net_bridge *br; + + br = dev->priv; + read_lock(&br->lock); + br_stp_disable_bridge(br); + read_unlock(&br->lock); + + dev->start = 0; + dev->tbusy = 1; + + return 0; +} + +static int br_dev_accept_fastpath(struct device *dev, struct dst_entry *dst) +{ + return -1; +} + +void br_dev_setup(struct device *dev) +{ + memset(dev->dev_addr, 0, ETH_ALEN); + + dev->do_ioctl = br_dev_do_ioctl; + dev->get_stats = br_dev_get_stats; + dev->hard_start_xmit = br_dev_xmit; + dev->open = br_dev_open; + dev->set_multicast_list = br_dev_set_multicast_list; + dev->stop = br_dev_stop; + dev->accept_fastpath = br_dev_accept_fastpath; + dev->tx_queue_len = 0; + dev->set_mac_address = NULL; +} diff -urN linux-2.2.20/net/bridge/br_fdb.c linux-2.2.20br/net/bridge/br_fdb.c --- linux-2.2.20/net/bridge/br_fdb.c Thu Jan 1 01:00:00 1970 +++ linux-2.2.20br/net/bridge/br_fdb.c Mon Dec 10 16:24:28 2001 @@ -0,0 +1,320 @@ +/* + * Forwarding database + * Linux ethernet bridge + * + * Authors: + * Lennert Buytenhek + * + * $Id$ + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ + +#include +#include +#include +#include +#include +#include +#include "br_private.h" + +static __inline__ unsigned long __timeout(struct net_bridge *br) +{ + unsigned long timeout; + + timeout = jiffies - br->ageing_time; + if (br->topology_change) + timeout = jiffies - br->forward_delay; + + return timeout; +} + +static __inline__ int has_expired(struct net_bridge *br, + struct net_bridge_fdb_entry *fdb) +{ + if (!fdb->is_static && + time_before_eq(fdb->ageing_timer, __timeout(br))) + return 1; + + return 0; +} + +static __inline__ void copy_fdb(struct __fdb_entry *ent, struct net_bridge_fdb_entry *f) +{ + memset(ent, 0, sizeof(struct __fdb_entry)); + memcpy(ent->mac_addr, f->addr.addr, ETH_ALEN); + ent->port_no = f->dst?f->dst->port_no:0; + ent->is_local = f->is_local; + ent->ageing_timer_value = 0; + if (!f->is_static) + ent->ageing_timer_value = jiffies - f->ageing_timer; +} + +static __inline__ int br_mac_hash(unsigned char *mac) +{ + unsigned long x; + + x = mac[0]; + x = (x << 2) ^ mac[1]; + x = (x << 2) ^ mac[2]; + x = (x << 2) ^ mac[3]; + x = (x << 2) ^ mac[4]; + x = (x << 2) ^ mac[5]; + + x ^= x >> 8; + + return x & (BR_HASH_SIZE - 1); +} + +static __inline__ void __hash_link(struct net_bridge *br, + struct net_bridge_fdb_entry *ent, + int hash) +{ + ent->next_hash = br->hash[hash]; + if (ent->next_hash != NULL) + ent->next_hash->pprev_hash = &ent->next_hash; + br->hash[hash] = ent; + ent->pprev_hash = &br->hash[hash]; +} + +static __inline__ void __hash_unlink(struct net_bridge_fdb_entry *ent) +{ + *(ent->pprev_hash) = ent->next_hash; + if (ent->next_hash != NULL) + ent->next_hash->pprev_hash = ent->pprev_hash; + ent->next_hash = NULL; + ent->pprev_hash = NULL; +} + + + +void br_fdb_changeaddr(struct net_bridge_port *p, unsigned char *newaddr) +{ + struct net_bridge *br; + int i; + + br = p->br; + write_lock_bh(&br->hash_lock); + for (i=0;ihash[i]; + while (f != NULL) { + if (f->dst == p && f->is_local) { + __hash_unlink(f); + memcpy(f->addr.addr, newaddr, ETH_ALEN); + __hash_link(br, f, br_mac_hash(newaddr)); + write_unlock_bh(&br->hash_lock); + return; + } + f = f->next_hash; + } + } + write_unlock_bh(&br->hash_lock); +} + +void br_fdb_cleanup(struct net_bridge *br) +{ + int i; + unsigned long timeout; + + timeout = __timeout(br); + + write_lock_bh(&br->hash_lock); + for (i=0;ihash[i]; + while (f != NULL) { + struct net_bridge_fdb_entry *g; + + g = f->next_hash; + if (!f->is_static && + time_before_eq(f->ageing_timer, timeout)) { + __hash_unlink(f); + br_fdb_put(f); + } + f = g; + } + } + write_unlock_bh(&br->hash_lock); +} + +void br_fdb_delete_by_port(struct net_bridge *br, struct net_bridge_port *p) +{ + int i; + + write_lock_bh(&br->hash_lock); + for (i=0;ihash[i]; + while (f != NULL) { + struct net_bridge_fdb_entry *g; + + g = f->next_hash; + if (f->dst == p) { + __hash_unlink(f); + br_fdb_put(f); + } + f = g; + } + } + write_unlock_bh(&br->hash_lock); +} + +struct net_bridge_fdb_entry *br_fdb_get(struct net_bridge *br, unsigned char *addr) +{ + struct net_bridge_fdb_entry *fdb; + + read_lock_bh(&br->hash_lock); + fdb = br->hash[br_mac_hash(addr)]; + while (fdb != NULL) { + if (!memcmp(fdb->addr.addr, addr, ETH_ALEN)) { + if (!has_expired(br, fdb)) { + atomic_inc(&fdb->use_count); + read_unlock_bh(&br->hash_lock); + return fdb; + } + + read_unlock_bh(&br->hash_lock); + return NULL; + } + + fdb = fdb->next_hash; + } + + read_unlock_bh(&br->hash_lock); + return NULL; +} + +void br_fdb_put(struct net_bridge_fdb_entry *ent) +{ + if (atomic_dec_and_test(&ent->use_count)) + kfree(ent); +} + +int br_fdb_get_entries(struct net_bridge *br, + unsigned char *_buf, + int maxnum, + int offset) +{ + int i; + int num; + struct __fdb_entry *walk; + + num = 0; + walk = (struct __fdb_entry *)_buf; + + read_lock_bh(&br->hash_lock); + for (i=0;ihash[i]; + while (f != NULL && num < maxnum) { + struct __fdb_entry ent; + int err; + struct net_bridge_fdb_entry *g; + + if (has_expired(br, f)) { + f = f->next_hash; + continue; + } + + if (offset) { + offset--; + f = f->next_hash; + continue; + } + + copy_fdb(&ent, f); + + atomic_inc(&f->use_count); + read_unlock_bh(&br->hash_lock); + err = copy_to_user(walk, &ent, sizeof(struct __fdb_entry)); + read_lock_bh(&br->hash_lock); + + g = f->next_hash; + br_fdb_put(f); + + if (err) + goto out_fault; + + if (f->next_hash == NULL && + f->pprev_hash == NULL) + goto out_disappeared; + + num++; + walk++; + + f = g; + } + } + + out: + read_unlock_bh(&br->hash_lock); + return num; + + out_disappeared: + num = -EAGAIN; + goto out; + + out_fault: + num = -EFAULT; + goto out; +} + +static __inline__ void __fdb_possibly_replace(struct net_bridge_fdb_entry *fdb, + struct net_bridge_port *source, + int is_local) +{ + if (!fdb->is_static || is_local) { + fdb->dst = source; + fdb->is_local = is_local; + fdb->is_static = is_local; + fdb->ageing_timer = jiffies; + } +} + +void br_fdb_insert(struct net_bridge *br, + struct net_bridge_port *source, + unsigned char *addr, + int is_local) +{ + struct net_bridge_fdb_entry *fdb; + int hash; + + hash = br_mac_hash(addr); + + write_lock_bh(&br->hash_lock); + fdb = br->hash[hash]; + while (fdb != NULL) { + if (!memcmp(fdb->addr.addr, addr, ETH_ALEN)) { + __fdb_possibly_replace(fdb, source, is_local); + write_unlock_bh(&br->hash_lock); + return; + } + + fdb = fdb->next_hash; + } + + fdb = kmalloc(sizeof(*fdb), GFP_ATOMIC); + if (fdb == NULL) { + write_unlock_bh(&br->hash_lock); + return; + } + + memcpy(fdb->addr.addr, addr, ETH_ALEN); + atomic_set(&fdb->use_count, 1); + fdb->dst = source; + fdb->is_local = is_local; + fdb->is_static = is_local; + fdb->ageing_timer = jiffies; + + __hash_link(br, fdb, hash); + + write_unlock_bh(&br->hash_lock); +} diff -urN linux-2.2.20/net/bridge/br_forward.c linux-2.2.20br/net/bridge/br_forward.c --- linux-2.2.20/net/bridge/br_forward.c Thu Jan 1 01:00:00 1970 +++ linux-2.2.20br/net/bridge/br_forward.c Mon Dec 10 16:24:28 2001 @@ -0,0 +1,98 @@ +/* + * Forwarding decision + * Linux ethernet bridge + * + * Authors: + * Lennert Buytenhek + * + * $Id$ + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ + +#include +#include +#include +#include +#include +#include +#include +#include "br_private.h" + +static inline int should_forward(struct net_bridge_port *p, struct sk_buff *skb) +{ + if (skb->dev != p->dev && p->state == BR_STATE_FORWARDING) + return 1; + + return 0; +} + +static void __br_forward(struct net_bridge_port *to, struct sk_buff *skb) +{ + skb->dev = to->dev; + dev_queue_xmit(skb); +} + +void br_forward(struct net_bridge_port *to, struct sk_buff *skb) +{ + if (should_forward(to, skb)) { + __br_forward(to, skb); + return; + } + + kfree_skb(skb); +} + +void br_flood(struct net_bridge *br, struct sk_buff *skb, int clone) +{ + struct net_bridge_port *p; + struct net_bridge_port *prev; + + if (clone) { + struct sk_buff *skb2; + + if ((skb2 = skb_clone(skb, GFP_ATOMIC)) == NULL) { + br->statistics.tx_dropped++; + return; + } + + skb = skb2; + } + + prev = NULL; + + read_lock(&br->lock); + p = br->port_list; + while (p != NULL) { + if (should_forward(p, skb)) { + if (prev != NULL) { + struct sk_buff *skb2; + + if ((skb2 = skb_clone(skb, GFP_ATOMIC)) == NULL) { + br->statistics.tx_dropped++; + kfree_skb(skb); + read_unlock(&br->lock); + return; + } + + __br_forward(prev, skb2); + } + + prev = p; + } + + p = p->next; + } + + if (prev != NULL) { + __br_forward(prev, skb); + read_unlock(&br->lock); + return; + } + + read_unlock(&br->lock); + kfree_skb(skb); +} diff -urN linux-2.2.20/net/bridge/br_if.c linux-2.2.20br/net/bridge/br_if.c --- linux-2.2.20/net/bridge/br_if.c Thu Jan 1 01:00:00 1970 +++ linux-2.2.20br/net/bridge/br_if.c Mon Dec 10 16:26:38 2001 @@ -0,0 +1,297 @@ +/* + * Userspace interface + * Linux ethernet bridge + * + * Authors: + * Lennert Buytenhek + * + * $Id$ + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ + +#include +#include +#include +#include +#include +#include +#include "br_private.h" + +static struct net_bridge *bridge_list; + +static int br_initial_port_cost(struct device *dev) +{ + if (!strncmp(dev->name, "lec", 3)) + return 7; + + if (!strncmp(dev->name, "eth", 3)) + return 100; /* FIXME handle 100Mbps */ + + if (!strncmp(dev->name, "plip", 4)) + return 2500; + + return 100; +} + +/* called under bridge lock */ +static int __br_del_if(struct net_bridge *br, struct device *dev) +{ + struct net_bridge_port *p; + struct net_bridge_port **pptr; + + if ((p = dev->br_port) == NULL) + return -EINVAL; + + br_stp_disable_port(p); + + dev_set_promiscuity(dev, -1); + if (dev->ip_ptr != NULL) { + struct in_device *idev = dev->ip_ptr; + idev->cnf.hidden = 0; + } + + dev->br_port = NULL; + + pptr = &br->port_list; + while (*pptr != NULL) { + if (*pptr == p) { + *pptr = p->next; + break; + } + + pptr = &((*pptr)->next); + } + + br_fdb_delete_by_port(br, p); + kfree(p); + + return 0; +} + +static struct net_bridge **__find_br(char *name) +{ + struct net_bridge **b; + struct net_bridge *br; + + b = &bridge_list; + while ((br = *b) != NULL) { + if (!strncmp(br->name, name, IFNAMSIZ)) + return b; + + b = &(br->next); + } + + return NULL; +} + +static void del_ifs(struct net_bridge *br) +{ + write_lock_bh(&br->lock); + while (br->port_list != NULL) + __br_del_if(br, br->port_list->dev); + write_unlock_bh(&br->lock); +} + +static struct net_bridge *new_nb(char *name) +{ + struct net_bridge *br; + struct device *dev; + + if ((br = kmalloc(sizeof(*br), GFP_KERNEL)) == NULL) + return NULL; + + memset(br, 0, sizeof(*br)); + dev = &br->dev; + + strncpy(br->name, name, IFNAMSIZ); + dev->priv = br; + dev->name = br->name; + ether_setup(dev); + br_dev_setup(dev); + + br->lock = RW_LOCK_UNLOCKED; + br->hash_lock = RW_LOCK_UNLOCKED; + + br->bridge_id.prio[0] = 0x80; + br->bridge_id.prio[1] = 0x00; + memset(br->bridge_id.addr, 0, ETH_ALEN); + + br->stp_enabled = 1; + br->designated_root = br->bridge_id; + br->root_path_cost = 0; + br->root_port = 0; + br->bridge_max_age = br->max_age = 20 * HZ; + br->bridge_hello_time = br->hello_time = 2 * HZ; + br->bridge_forward_delay = br->forward_delay = 15 * HZ; + br->topology_change = 0; + br->topology_change_detected = 0; + br_timer_clear(&br->hello_timer); + br_timer_clear(&br->tcn_timer); + br_timer_clear(&br->topology_change_timer); + + br->ageing_time = 300 * HZ; + br->gc_interval = 4 * HZ; + + return br; +} + +/* called under bridge lock */ +static struct net_bridge_port *new_nbp(struct net_bridge *br, struct device *dev) +{ + int i; + struct net_bridge_port *p; + + p = kmalloc(sizeof(*p), GFP_KERNEL); + if (p == NULL) + return p; + + memset(p, 0, sizeof(*p)); + p->br = br; + p->dev = dev; + p->path_cost = br_initial_port_cost(dev); + p->priority = 0x80; + + dev->br_port = p; + + for (i=1;i<255;i++) + if (br_get_port(br, i) == NULL) + break; + + if (i == 255) { + kfree(p); + return NULL; + } + + p->port_no = i; + br_init_port(p); + p->state = BR_STATE_DISABLED; + + p->next = br->port_list; + br->port_list = p; + + return p; +} + +int br_add_bridge(char *name) +{ + struct net_bridge *br; + + if ((br = new_nb(name)) == NULL) + return -ENOMEM; + + if (dev_get(name) != NULL) { + kfree(br); + return -EEXIST; + } + + br->next = bridge_list; + bridge_list = br; + + br_inc_use_count(); + register_netdevice(&br->dev); + + return 0; +} + +int br_del_bridge(char *name) +{ + struct net_bridge **b; + struct net_bridge *br; + + if ((b = __find_br(name)) == NULL) + return -ENXIO; + + br = *b; + + if (br->dev.flags & IFF_UP) + return -EBUSY; + + del_ifs(br); + + *b = br->next; + + unregister_netdevice(&br->dev); + kfree(br); + br_dec_use_count(); + + return 0; +} + +int br_add_if(struct net_bridge *br, struct device *dev) +{ + struct net_bridge_port *p; + + if (dev->br_port != NULL) + return -EBUSY; + + if (dev->flags & IFF_LOOPBACK || dev->type != ARPHRD_ETHER) + return -EINVAL; + + if (dev->hard_start_xmit == br_dev_xmit) + return -ELOOP; + + write_lock_bh(&br->lock); + if ((p = new_nbp(br, dev)) == NULL) { + write_unlock_bh(&br->lock); + return -EXFULL; + } + + if (dev->ip_ptr != NULL) { + struct in_device *idev = dev->ip_ptr; + idev->cnf.hidden = 1; + } + dev_set_promiscuity(dev, 1); + + br_stp_recalculate_bridge_id(br); + br_fdb_insert(br, p, dev->dev_addr, 1); + if ((br->dev.flags & IFF_UP) && (dev->flags & IFF_UP)) + br_stp_enable_port(p); + write_unlock_bh(&br->lock); + + return 0; +} + +int br_del_if(struct net_bridge *br, struct device *dev) +{ + int retval; + + write_lock_bh(&br->lock); + retval = __br_del_if(br, dev); + br_stp_recalculate_bridge_id(br); + write_unlock_bh(&br->lock); + + return retval; +} + +int br_get_bridge_ifindices(int *indices, int num) +{ + struct net_bridge *br; + int i; + + br = bridge_list; + for (i=0;idev.ifindex; + br = br->next; + } + + return i; +} + +/* called under ioctl_lock */ +void br_get_port_ifindices(struct net_bridge *br, int *ifindices) +{ + struct net_bridge_port *p; + + p = br->port_list; + while (p != NULL) { + ifindices[p->port_no] = p->dev->ifindex; + p = p->next; + } +} diff -urN linux-2.2.20/net/bridge/br_input.c linux-2.2.20br/net/bridge/br_input.c --- linux-2.2.20/net/bridge/br_input.c Thu Jan 1 01:00:00 1970 +++ linux-2.2.20br/net/bridge/br_input.c Mon Dec 10 16:31:55 2001 @@ -0,0 +1,134 @@ +/* + * Handle incoming frames + * Linux ethernet bridge + * + * Authors: + * Lennert Buytenhek + * + * $Id$ + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + * + * Changes: 2000/02/03 [AF] Arne Fitzenreiter + * Bugfix - Multicast & Promiscious Mode + * + */ + +#include +#include +#include +#include +#include +#include "br_private.h" + +unsigned char bridge_ula[6] = { 0x01, 0x80, 0xc2, 0x00, 0x00, 0x00 }; + +static void br_pass_frame_up(struct net_bridge *br, struct sk_buff *skb) +{ + br->statistics.rx_packets++; + br->statistics.rx_bytes += skb->len; + + skb->dev = &br->dev; + skb->pkt_type = PACKET_HOST; + skb_pull(skb, skb->mac.raw - skb->data); + skb->protocol = eth_type_trans(skb, &br->dev); + netif_rx(skb); +} + +void br_handle_frame(struct sk_buff *skb) +{ + struct net_bridge *br; + unsigned char *dest; + struct net_bridge_fdb_entry *dst; + struct net_bridge_port *p; + int passedup; + + dest = skb->mac.ethernet->h_dest; + + p = skb->dev->br_port; + br = p->br; + passedup = 0; + + if (!(br->dev.flags & IFF_UP) || + p->state == BR_STATE_DISABLED) + goto freeandout; + + skb_push(skb, skb->data - skb->mac.raw); + + if (br->dev.flags & IFF_PROMISC && p->state == BR_STATE_FORWARDING) { + struct sk_buff *skb2; + + skb2 = skb_clone(skb, GFP_ATOMIC); + if (skb2) { + passedup = 1; + br_pass_frame_up(br, skb2); + } + } + + if (skb->mac.ethernet->h_source[0] & 1) + goto freeandout; + + if (!passedup && + (dest[0] & 1) && + (br->dev.flags & IFF_ALLMULTI || br->dev.mc_list != NULL) && + (p->state == BR_STATE_FORWARDING)) { + struct sk_buff *skb2; + + skb2 = skb_clone(skb, GFP_ATOMIC); + if (skb2) { + passedup = 1; + br_pass_frame_up(br, skb2); + } + } + + if (p->state == BR_STATE_LEARNING || + p->state == BR_STATE_FORWARDING) + br_fdb_insert(br, p, skb->mac.ethernet->h_source, 0); + + if (br->stp_enabled && + !memcmp(dest, bridge_ula, 5) && + !(dest[5] & 0xF0)) + goto handle_special_frame; + + if (p->state != BR_STATE_FORWARDING) + goto freeandout; + + if (dest[0] & 1) { + br_flood(br, skb, !passedup); + if (!passedup) + br_pass_frame_up(br, skb); + return; + } + + dst = br_fdb_get(br, dest); + + if (dst != NULL && dst->is_local) { + if (!passedup) + br_pass_frame_up(br, skb); + else + kfree_skb(skb); + br_fdb_put(dst); + return; + } + + if (dst != NULL) { + br_forward(dst->dst, skb); + br_fdb_put(dst); + return; + } + + br_flood(br, skb, 0); + return; + + handle_special_frame: + if (!dest[5]) { + br_stp_handle_bpdu(skb); + return; + } + + freeandout: + kfree_skb(skb); +} diff -urN linux-2.2.20/net/bridge/br_ioctl.c linux-2.2.20br/net/bridge/br_ioctl.c --- linux-2.2.20/net/bridge/br_ioctl.c Thu Jan 1 01:00:00 1970 +++ linux-2.2.20br/net/bridge/br_ioctl.c Mon Dec 10 16:24:28 2001 @@ -0,0 +1,264 @@ +/* + * Ioctl handler + * Linux ethernet bridge + * + * Authors: + * Lennert Buytenhek + * + * $Id$ + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ + +#include +#include +#include +#include +#include +#include "br_private.h" + +static int br_ioctl_device(struct net_bridge *br, + unsigned int cmd, + unsigned long arg0, + unsigned long arg1, + unsigned long arg2) +{ + if (br == NULL) + return -EINVAL; + + switch (cmd) + { + case BRCTL_ADD_IF: + case BRCTL_DEL_IF: + { + struct device *dev; + + dev = dev_get_by_index(arg0); + if (dev == NULL) + return -EINVAL; + + if (cmd == BRCTL_ADD_IF) + return br_add_if(br, dev); + + return br_del_if(br, dev); + } + + case BRCTL_GET_BRIDGE_INFO: + { + struct __bridge_info b; + + memset(&b, 0, sizeof(struct __bridge_info)); + memcpy(&b.designated_root, &br->designated_root, 8); + memcpy(&b.bridge_id, &br->bridge_id, 8); + b.root_path_cost = br->root_path_cost; + b.max_age = br->max_age; + b.hello_time = br->hello_time; + b.forward_delay = br->forward_delay; + b.bridge_max_age = br->bridge_max_age; + b.bridge_hello_time = br->bridge_hello_time; + b.bridge_forward_delay = br->bridge_forward_delay; + b.topology_change = br->topology_change; + b.topology_change_detected = br->topology_change_detected; + b.root_port = br->root_port; + b.stp_enabled = br->stp_enabled; + b.ageing_time = br->ageing_time; + b.gc_interval = br->gc_interval; + b.hello_timer_value = br_timer_get_residue(&br->hello_timer); + b.tcn_timer_value = br_timer_get_residue(&br->tcn_timer); + b.topology_change_timer_value = br_timer_get_residue(&br->topology_change_timer); + b.gc_timer_value = br_timer_get_residue(&br->gc_timer); + + if (copy_to_user((void *)arg0, &b, sizeof(b))) + return -EFAULT; + + return 0; + } + + case BRCTL_GET_PORT_LIST: + { + int i; + int indices[256]; + + for (i=0;i<256;i++) + indices[i] = 0; + + br_get_port_ifindices(br, indices); + if (copy_to_user((void *)arg0, indices, 256*sizeof(int))) + return -EFAULT; + + return 0; + } + + case BRCTL_SET_BRIDGE_FORWARD_DELAY: + br->bridge_forward_delay = arg0; + if (br_is_root_bridge(br)) + br->forward_delay = arg0; + return 0; + + case BRCTL_SET_BRIDGE_HELLO_TIME: + br->bridge_hello_time = arg0; + if (br_is_root_bridge(br)) + br->hello_time = arg0; + return 0; + + case BRCTL_SET_BRIDGE_MAX_AGE: + br->bridge_max_age = arg0; + if (br_is_root_bridge(br)) + br->max_age = arg0; + return 0; + + case BRCTL_SET_AGEING_TIME: + br->ageing_time = arg0; + return 0; + + case BRCTL_SET_GC_INTERVAL: + br->gc_interval = arg0; + return 0; + + case BRCTL_GET_PORT_INFO: + { + struct __port_info p; + struct net_bridge_port *pt; + + if ((pt = br_get_port(br, arg1)) == NULL) + return -EINVAL; + + memset(&p, 0, sizeof(struct __port_info)); + memcpy(&p.designated_root, &pt->designated_root, 8); + memcpy(&p.designated_bridge, &pt->designated_bridge, 8); + p.port_id = pt->port_id; + p.designated_port = pt->designated_port; + p.path_cost = pt->path_cost; + p.designated_cost = pt->designated_cost; + p.state = pt->state; + p.top_change_ack = pt->topology_change_ack; + p.config_pending = pt->config_pending; + p.message_age_timer_value = br_timer_get_residue(&pt->message_age_timer); + p.forward_delay_timer_value = br_timer_get_residue(&pt->forward_delay_timer); + p.hold_timer_value = br_timer_get_residue(&pt->hold_timer); + + if (copy_to_user((void *)arg0, &p, sizeof(p))) + return -EFAULT; + + return 0; + } + + case BRCTL_SET_BRIDGE_STP_STATE: + br->stp_enabled = arg0?1:0; + return 0; + + case BRCTL_SET_BRIDGE_PRIORITY: + br_stp_set_bridge_priority(br, arg0); + return 0; + + case BRCTL_SET_PORT_PRIORITY: + { + struct net_bridge_port *p; + + if ((p = br_get_port(br, arg0)) == NULL) + return -EINVAL; + br_stp_set_port_priority(p, arg1); + return 0; + } + + case BRCTL_SET_PATH_COST: + { + struct net_bridge_port *p; + + if ((p = br_get_port(br, arg0)) == NULL) + return -EINVAL; + br_stp_set_path_cost(p, arg1); + return 0; + } + + case BRCTL_GET_FDB_ENTRIES: + return br_fdb_get_entries(br, (void *)arg0, arg1, arg2); + } + + return -EOPNOTSUPP; +} + +static int br_ioctl_deviceless(unsigned int cmd, + unsigned long arg0, + unsigned long arg1) +{ + switch (cmd) + { + case BRCTL_GET_VERSION: + return BRCTL_VERSION; + + case BRCTL_GET_BRIDGES: + { + int i; + int indices[64]; + + for (i=0;i<64;i++) + indices[i] = 0; + + if (arg1 > 64) + arg1 = 64; + arg1 = br_get_bridge_ifindices(indices, arg1); + if (copy_to_user((void *)arg0, indices, arg1*sizeof(int))) + return -EFAULT; + + return arg1; + } + + case BRCTL_ADD_BRIDGE: + case BRCTL_DEL_BRIDGE: + { + char buf[IFNAMSIZ]; + + if (copy_from_user(buf, (void *)arg0, IFNAMSIZ)) + return -EFAULT; + + buf[IFNAMSIZ-1] = 0; + + if (cmd == BRCTL_ADD_BRIDGE) + return br_add_bridge(buf); + + return br_del_bridge(buf); + } + } + + return -EOPNOTSUPP; +} + +static struct semaphore ioctl_mutex = MUTEX; + +int br_ioctl_deviceless_stub(unsigned long arg) +{ + int err; + unsigned long i[3]; + + if (!capable(CAP_NET_ADMIN)) + return -EPERM; + + if (copy_from_user(i, (void *)arg, 3*sizeof(unsigned long))) + return -EFAULT; + + down(&ioctl_mutex); + err = br_ioctl_deviceless(i[0], i[1], i[2]); + up(&ioctl_mutex); + + return err; +} + +int br_ioctl(struct net_bridge *br, unsigned int cmd, unsigned long arg0, unsigned long arg1, unsigned long arg2) +{ + int err; + + if (!capable(CAP_NET_ADMIN)) + return -EPERM; + + down(&ioctl_mutex); + err = br_ioctl_deviceless(cmd, arg0, arg1); + if (err == -EOPNOTSUPP) + err = br_ioctl_device(br, cmd, arg0, arg1, arg2); + up(&ioctl_mutex); + + return err; +} diff -urN linux-2.2.20/net/bridge/br_notify.c linux-2.2.20br/net/bridge/br_notify.c --- linux-2.2.20/net/bridge/br_notify.c Thu Jan 1 01:00:00 1970 +++ linux-2.2.20br/net/bridge/br_notify.c Mon Dec 10 16:24:28 2001 @@ -0,0 +1,76 @@ +/* + * Device event handling + * Linux ethernet bridge + * + * Authors: + * Lennert Buytenhek + * + * $Id$ + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ + +#include +#include +#include +#include "br_private.h" + +static int br_device_event(struct notifier_block *unused, unsigned long event, void *ptr); + +struct notifier_block br_device_notifier = +{ + br_device_event, + NULL, + 0 +}; + +static int br_device_event(struct notifier_block *unused, unsigned long event, void *ptr) +{ + struct device *dev; + struct net_bridge_port *p; + + dev = ptr; + p = dev->br_port; + + if (p == NULL) + return NOTIFY_DONE; + + switch (event) + { + case NETDEV_CHANGEADDR: + read_lock(&p->br->lock); + br_fdb_changeaddr(p, dev->dev_addr); + br_stp_recalculate_bridge_id(p->br); + read_unlock(&p->br->lock); + break; + + case NETDEV_GOING_DOWN: + /* extend the protocol to send some kind of notification? */ + break; + + case NETDEV_DOWN: + if (p->br->dev.flags & IFF_UP) { + read_lock(&p->br->lock); + br_stp_disable_port(dev->br_port); + read_unlock(&p->br->lock); + } + break; + + case NETDEV_UP: + if (p->br->dev.flags & IFF_UP) { + read_lock(&p->br->lock); + br_stp_enable_port(dev->br_port); + read_unlock(&p->br->lock); + } + break; + + case NETDEV_UNREGISTER: + br_del_if(dev->br_port->br, dev); + break; + } + + return NOTIFY_DONE; +} diff -urN linux-2.2.20/net/bridge/br_private.h linux-2.2.20br/net/bridge/br_private.h --- linux-2.2.20/net/bridge/br_private.h Thu Jan 1 01:00:00 1970 +++ linux-2.2.20br/net/bridge/br_private.h Mon Dec 10 16:26:19 2001 @@ -0,0 +1,216 @@ +/* + * Linux ethernet bridge + * + * Authors: + * Lennert Buytenhek + * + * $Id$ + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ + +#ifndef _BR_PRIVATE_H +#define _BR_PRIVATE_H + +#include +#include +#include + +#define BR_HASH_BITS 8 +#define BR_HASH_SIZE (1 << BR_HASH_BITS) + +#define BR_HOLD_TIME (1*HZ) + +#ifndef read_lock_bh +#define read_lock_bh(x) { start_bh_atomic(); read_lock(x); } +#define read_unlock_bh(x) { read_unlock(x); end_bh_atomic(); } +#define write_lock_bh(x) { start_bh_atomic(); write_lock(x); } +#define write_unlock_bh(x) { write_unlock(x); end_bh_atomic(); } +#endif + +typedef struct bridge_id bridge_id; +typedef struct mac_addr mac_addr; +typedef __u16 port_id; + +#if 1 +struct br_timer { int running; unsigned long expires; }; +#define br_timer_clear(t) { (t)->running = 0; } +#define br_timer_get_residue(t) (((t)->running)?(jiffies - (t)->expires):0) +#define br_timer_set(t,x) { (t)->expires = (x); (t)->running = 1; } +#define br_timer_is_running(t) ((t)->running) +#define br_timer_has_expired(t,to) ((t)->running && time_after_eq(jiffies, (t)->expires + (to))) +#endif + + + +struct bridge_id +{ + unsigned char prio[2]; + unsigned char addr[6]; +}; + +struct mac_addr +{ + unsigned char addr[6]; + unsigned char pad[2]; +}; + +struct net_bridge_fdb_entry +{ + struct net_bridge_fdb_entry *next_hash; + struct net_bridge_fdb_entry **pprev_hash; + atomic_t use_count; + mac_addr addr; + struct net_bridge_port *dst; + unsigned long ageing_timer; + unsigned is_local:1; + unsigned is_static:1; +}; + +struct net_bridge_port +{ + struct net_bridge_port *next; + struct net_bridge *br; + struct device *dev; + int port_no; + + /* STP */ + port_id port_id; + int state; + int path_cost; + bridge_id designated_root; + int designated_cost; + bridge_id designated_bridge; + port_id designated_port; + unsigned topology_change_ack:1; + unsigned config_pending:1; + int priority; + + struct br_timer forward_delay_timer; + struct br_timer hold_timer; + struct br_timer message_age_timer; +}; + +struct net_bridge +{ + struct net_bridge *next; + rwlock_t lock; + struct net_bridge_port *port_list; + char name[IFNAMSIZ]; + struct device dev; + struct net_device_stats statistics; + rwlock_t hash_lock; + struct net_bridge_fdb_entry *hash[BR_HASH_SIZE]; + struct timer_list tick; + + /* STP */ + bridge_id designated_root; + int root_path_cost; + int root_port; + int max_age; + int hello_time; + int forward_delay; + bridge_id bridge_id; + int bridge_max_age; + int bridge_hello_time; + int bridge_forward_delay; + unsigned stp_enabled:1; + unsigned topology_change:1; + unsigned topology_change_detected:1; + + struct br_timer hello_timer; + struct br_timer tcn_timer; + struct br_timer topology_change_timer; + struct br_timer gc_timer; + + int ageing_time; + int gc_interval; +}; + +struct notifier_block br_device_notifier; +unsigned char bridge_ula[6]; + +/* br.c */ +void br_dec_use_count(void); +void br_inc_use_count(void); + +/* br_device.c */ +void br_dev_setup(struct device *dev); +int br_dev_xmit(struct sk_buff *skb, struct device *dev); + +/* br_fdb.c */ +void br_fdb_changeaddr(struct net_bridge_port *p, + unsigned char *newaddr); +void br_fdb_cleanup(struct net_bridge *br); +void br_fdb_delete_by_port(struct net_bridge *br, + struct net_bridge_port *p); +struct net_bridge_fdb_entry *br_fdb_get(struct net_bridge *br, + unsigned char *addr); +void br_fdb_put(struct net_bridge_fdb_entry *ent); +int br_fdb_get_entries(struct net_bridge *br, + unsigned char *_buf, + int maxnum, + int offset); +void br_fdb_insert(struct net_bridge *br, + struct net_bridge_port *source, + unsigned char *addr, + int is_local); + +/* br_forward.c */ +void br_forward(struct net_bridge_port *to, + struct sk_buff *skb); +void br_flood(struct net_bridge *br, + struct sk_buff *skb, + int clone); + +/* br_if.c */ +int br_add_bridge(char *name); +int br_del_bridge(char *name); +int br_add_if(struct net_bridge *br, + struct device *dev); +int br_del_if(struct net_bridge *br, + struct device *dev); +int br_get_bridge_ifindices(int *indices, + int num); +void br_get_port_ifindices(struct net_bridge *br, + int *ifindices); + +/* br_input.c */ +void br_handle_frame(struct sk_buff *skb); + +/* br_ioctl.c */ +int br_ioctl(struct net_bridge *br, + unsigned int cmd, + unsigned long arg0, + unsigned long arg1, + unsigned long arg2); +int br_ioctl_deviceless_stub(unsigned long arg); + +/* br_stp.c */ +int br_is_root_bridge(struct net_bridge *br); +struct net_bridge_port *br_get_port(struct net_bridge *br, + int port_no); +void br_init_port(struct net_bridge_port *p); +port_id br_make_port_id(struct net_bridge_port *p); +void br_become_designated_port(struct net_bridge_port *p); + +/* br_stp_if.c */ +void br_stp_enable_bridge(struct net_bridge *br); +void br_stp_disable_bridge(struct net_bridge *br); +void br_stp_enable_port(struct net_bridge_port *p); +void br_stp_disable_port(struct net_bridge_port *p); +void br_stp_recalculate_bridge_id(struct net_bridge *br); +void br_stp_set_bridge_priority(struct net_bridge *br, + int newprio); +void br_stp_set_port_priority(struct net_bridge_port *p, + int newprio); +void br_stp_set_path_cost(struct net_bridge_port *p, + int path_cost); + +/* br_stp_bpdu.c */ +void br_stp_handle_bpdu(struct sk_buff *skb); + +#endif diff -urN linux-2.2.20/net/bridge/br_private_stp.h linux-2.2.20br/net/bridge/br_private_stp.h --- linux-2.2.20/net/bridge/br_private_stp.h Thu Jan 1 01:00:00 1970 +++ linux-2.2.20br/net/bridge/br_private_stp.h Mon Dec 10 16:24:28 2001 @@ -0,0 +1,53 @@ +/* + * Linux ethernet bridge + * + * Authors: + * Lennert Buytenhek + * + * $Id$ + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ + +#ifndef _BR_PRIVATE_STP_H +#define _BR_PRIVATE_STP_H + +#define BPDU_TYPE_CONFIG 0 +#define BPDU_TYPE_TCN 0x80 + +struct br_config_bpdu +{ + unsigned topology_change:1; + unsigned topology_change_ack:1; + bridge_id root; + int root_path_cost; + bridge_id bridge_id; + port_id port_id; + int message_age; + int max_age; + int hello_time; + int forward_delay; +}; + +/* br_stp.c */ +void br_become_root_bridge(struct net_bridge *br); +void br_config_bpdu_generation(struct net_bridge *); +void br_configuration_update(struct net_bridge *); +int br_is_designated_port(struct net_bridge_port *p); +int br_is_root_bridge(struct net_bridge *br); +void br_port_state_selection(struct net_bridge *); +void br_received_config_bpdu(struct net_bridge_port *p, struct br_config_bpdu *bpdu); +void br_received_tcn_bpdu(struct net_bridge_port *p); +void br_tick(unsigned long __data); +void br_transmit_config(struct net_bridge_port *p); +void br_transmit_tcn(struct net_bridge *br); +void br_topology_change_detection(struct net_bridge *br); + +/* br_stp_bpdu.c */ +void br_send_config_bpdu(struct net_bridge_port *, struct br_config_bpdu *); +void br_send_tcn_bpdu(struct net_bridge_port *); + +#endif diff -urN linux-2.2.20/net/bridge/br_stp.c linux-2.2.20br/net/bridge/br_stp.c --- linux-2.2.20/net/bridge/br_stp.c Thu Jan 1 01:00:00 1970 +++ linux-2.2.20br/net/bridge/br_stp.c Mon Dec 10 16:24:28 2001 @@ -0,0 +1,467 @@ +/* + * Spanning tree protocol; generic parts + * Linux ethernet bridge + * + * Authors: + * Lennert Buytenhek + * + * $Id$ + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ + +#include +#include +#include +#include +#include +#include "br_private.h" +#include "br_private_stp.h" + + + +/* called under ioctl_lock or bridge lock */ +int br_is_root_bridge(struct net_bridge *br) +{ + return !memcmp(&br->bridge_id, &br->designated_root, 8); +} + +/* called under bridge lock */ +int br_is_designated_port(struct net_bridge_port *p) +{ + return !memcmp(&p->designated_bridge, &p->br->bridge_id, 8) && + (p->designated_port == p->port_id); +} + +/* called under ioctl_lock or bridge lock */ +struct net_bridge_port *br_get_port(struct net_bridge *br, int port_no) +{ + struct net_bridge_port *p; + + p = br->port_list; + while (p != NULL) { + if (p->port_no == port_no) + return p; + + p = p->next; + } + + return NULL; +} + +/* called under bridge lock */ +static int br_should_become_root_port(struct net_bridge_port *p, int root_port) +{ + struct net_bridge *br; + struct net_bridge_port *rp; + int t; + + br = p->br; + if (p->state == BR_STATE_DISABLED || + br_is_designated_port(p)) + return 0; + + if (memcmp(&br->bridge_id, &p->designated_root, 8) <= 0) + return 0; + + if (!root_port) + return 1; + + rp = br_get_port(br, root_port); + + t = memcmp(&p->designated_root, &rp->designated_root, 8); + if (t < 0) + return 1; + else if (t > 0) + return 0; + + if (p->designated_cost + p->path_cost < + rp->designated_cost + rp->path_cost) + return 1; + else if (p->designated_cost + p->path_cost > + rp->designated_cost + rp->path_cost) + return 0; + + t = memcmp(&p->designated_bridge, &rp->designated_bridge, 8); + if (t < 0) + return 1; + else if (t > 0) + return 0; + + if (p->designated_port < rp->designated_port) + return 1; + else if (p->designated_port > rp->designated_port) + return 0; + + if (p->port_id < rp->port_id) + return 1; + + return 0; +} + +/* called under bridge lock */ +static void br_root_selection(struct net_bridge *br) +{ + struct net_bridge_port *p; + int root_port; + + root_port = 0; + + p = br->port_list; + while (p != NULL) { + if (br_should_become_root_port(p, root_port)) + root_port = p->port_no; + + p = p->next; + } + + br->root_port = root_port; + + if (!root_port) { + br->designated_root = br->bridge_id; + br->root_path_cost = 0; + } else { + p = br_get_port(br, root_port); + br->designated_root = p->designated_root; + br->root_path_cost = p->designated_cost + p->path_cost; + } +} + +/* called under bridge lock */ +void br_become_root_bridge(struct net_bridge *br) +{ + br->max_age = br->bridge_max_age; + br->hello_time = br->bridge_hello_time; + br->forward_delay = br->bridge_forward_delay; + br_topology_change_detection(br); + br_timer_clear(&br->tcn_timer); + br_config_bpdu_generation(br); + br_timer_set(&br->hello_timer, jiffies); +} + +/* called under bridge lock */ +void br_transmit_config(struct net_bridge_port *p) +{ + struct br_config_bpdu bpdu; + struct net_bridge *br; + + if (br_timer_is_running(&p->hold_timer)) { + p->config_pending = 1; + return; + } + + br = p->br; + + bpdu.topology_change = br->topology_change; + bpdu.topology_change_ack = p->topology_change_ack; + bpdu.root = br->designated_root; + bpdu.root_path_cost = br->root_path_cost; + bpdu.bridge_id = br->bridge_id; + bpdu.port_id = p->port_id; + bpdu.message_age = 0; + if (!br_is_root_bridge(br)) { + struct net_bridge_port *root; + unsigned long age; + + root = br_get_port(br, br->root_port); + age = br_timer_get_residue(&root->message_age_timer) + 1; + bpdu.message_age = age; + } + bpdu.max_age = br->max_age; + bpdu.hello_time = br->hello_time; + bpdu.forward_delay = br->forward_delay; + + br_send_config_bpdu(p, &bpdu); + + p->topology_change_ack = 0; + p->config_pending = 0; + br_timer_set(&p->hold_timer, jiffies); +} + +/* called under bridge lock */ +static void br_record_config_information(struct net_bridge_port *p, struct br_config_bpdu *bpdu) +{ + p->designated_root = bpdu->root; + p->designated_cost = bpdu->root_path_cost; + p->designated_bridge = bpdu->bridge_id; + p->designated_port = bpdu->port_id; + + br_timer_set(&p->message_age_timer, jiffies - bpdu->message_age); +} + +/* called under bridge lock */ +static void br_record_config_timeout_values(struct net_bridge *br, struct br_config_bpdu *bpdu) +{ + br->max_age = bpdu->max_age; + br->hello_time = bpdu->hello_time; + br->forward_delay = bpdu->forward_delay; + br->topology_change = bpdu->topology_change; +} + +/* called under bridge lock */ +void br_transmit_tcn(struct net_bridge *br) +{ + br_send_tcn_bpdu(br_get_port(br, br->root_port)); +} + +/* called under bridge lock */ +static int br_should_become_designated_port(struct net_bridge_port *p) +{ + struct net_bridge *br; + int t; + + br = p->br; + if (br_is_designated_port(p)) + return 1; + + if (memcmp(&p->designated_root, &br->designated_root, 8)) + return 1; + + if (br->root_path_cost < p->designated_cost) + return 1; + else if (br->root_path_cost > p->designated_cost) + return 0; + + t = memcmp(&br->bridge_id, &p->designated_bridge, 8); + if (t < 0) + return 1; + else if (t > 0) + return 0; + + if (p->port_id < p->designated_port) + return 1; + + return 0; +} + +/* called under bridge lock */ +static void br_designated_port_selection(struct net_bridge *br) +{ + struct net_bridge_port *p; + + p = br->port_list; + while (p != NULL) { + if (p->state != BR_STATE_DISABLED && + br_should_become_designated_port(p)) + br_become_designated_port(p); + + p = p->next; + } +} + +/* called under bridge lock */ +static int br_supersedes_port_info(struct net_bridge_port *p, struct br_config_bpdu *bpdu) +{ + int t; + + t = memcmp(&bpdu->root, &p->designated_root, 8); + if (t < 0) + return 1; + else if (t > 0) + return 0; + + if (bpdu->root_path_cost < p->designated_cost) + return 1; + else if (bpdu->root_path_cost > p->designated_cost) + return 0; + + t = memcmp(&bpdu->bridge_id, &p->designated_bridge, 8); + if (t < 0) + return 1; + else if (t > 0) + return 0; + + if (memcmp(&bpdu->bridge_id, &p->br->bridge_id, 8)) + return 1; + + if (bpdu->port_id <= p->designated_port) + return 1; + + return 0; +} + +/* called under bridge lock */ +static void br_topology_change_acknowledged(struct net_bridge *br) +{ + br->topology_change_detected = 0; + br_timer_clear(&br->tcn_timer); +} + +/* called under bridge lock */ +void br_topology_change_detection(struct net_bridge *br) +{ + printk(KERN_INFO "%s: topology change detected", br->name); + + if (br_is_root_bridge(br)) { + printk(", propagating"); + br->topology_change = 1; + br_timer_set(&br->topology_change_timer, jiffies); + } else if (!br->topology_change_detected) { + printk(", sending tcn bpdu"); + br_transmit_tcn(br); + br_timer_set(&br->tcn_timer, jiffies); + } + + printk("\n"); + br->topology_change_detected = 1; +} + +/* called under bridge lock */ +void br_config_bpdu_generation(struct net_bridge *br) +{ + struct net_bridge_port *p; + + p = br->port_list; + while (p != NULL) { + if (p->state != BR_STATE_DISABLED && + br_is_designated_port(p)) + br_transmit_config(p); + + p = p->next; + } +} + +/* called under bridge lock */ +static void br_reply(struct net_bridge_port *p) +{ + br_transmit_config(p); +} + +/* called under bridge lock */ +void br_configuration_update(struct net_bridge *br) +{ + br_root_selection(br); + br_designated_port_selection(br); +} + +/* called under bridge lock */ +void br_become_designated_port(struct net_bridge_port *p) +{ + struct net_bridge *br; + + br = p->br; + p->designated_root = br->designated_root; + p->designated_cost = br->root_path_cost; + p->designated_bridge = br->bridge_id; + p->designated_port = p->port_id; +} + +/* called under bridge lock */ +static void br_make_blocking(struct net_bridge_port *p) +{ + if (p->state != BR_STATE_DISABLED && + p->state != BR_STATE_BLOCKING) { + if (p->state == BR_STATE_FORWARDING || + p->state == BR_STATE_LEARNING) + br_topology_change_detection(p->br); + + printk(KERN_INFO "%s: port %i(%s) entering %s state\n", + p->br->name, p->port_no, p->dev->name, "blocking"); + + p->state = BR_STATE_BLOCKING; + br_timer_clear(&p->forward_delay_timer); + } +} + +/* called under bridge lock */ +static void br_make_forwarding(struct net_bridge_port *p) +{ + if (p->state == BR_STATE_BLOCKING) { + printk(KERN_INFO "%s: port %i(%s) entering %s state\n", + p->br->name, p->port_no, p->dev->name, "listening"); + + p->state = BR_STATE_LISTENING; + br_timer_set(&p->forward_delay_timer, jiffies); + } +} + +/* called under bridge lock */ +void br_port_state_selection(struct net_bridge *br) +{ + struct net_bridge_port *p; + + p = br->port_list; + while (p != NULL) { + if (p->state != BR_STATE_DISABLED) { + if (p->port_no == br->root_port) { + p->config_pending = 0; + p->topology_change_ack = 0; + br_make_forwarding(p); + } else if (br_is_designated_port(p)) { + br_timer_clear(&p->message_age_timer); + br_make_forwarding(p); + } else { + p->config_pending = 0; + p->topology_change_ack = 0; + br_make_blocking(p); + } + } + + p = p->next; + } +} + +/* called under bridge lock */ +static void br_topology_change_acknowledge(struct net_bridge_port *p) +{ + p->topology_change_ack = 1; + br_transmit_config(p); +} + +/* lock-safe */ +void br_received_config_bpdu(struct net_bridge_port *p, struct br_config_bpdu *bpdu) +{ + struct net_bridge *br; + int was_root; + + if (p->state == BR_STATE_DISABLED) + return; + + br = p->br; + read_lock(&br->lock); + + was_root = br_is_root_bridge(br); + if (br_supersedes_port_info(p, bpdu)) { + br_record_config_information(p, bpdu); + br_configuration_update(br); + br_port_state_selection(br); + + if (!br_is_root_bridge(br) && was_root) { + br_timer_clear(&br->hello_timer); + if (br->topology_change_detected) { + br_timer_clear(&br->topology_change_timer); + br_transmit_tcn(br); + br_timer_set(&br->tcn_timer, jiffies); + } + } + + if (p->port_no == br->root_port) { + br_record_config_timeout_values(br, bpdu); + br_config_bpdu_generation(br); + if (bpdu->topology_change_ack) + br_topology_change_acknowledged(br); + } + } else if (br_is_designated_port(p)) { + br_reply(p); + } + + read_unlock(&br->lock); +} + +/* lock-safe */ +void br_received_tcn_bpdu(struct net_bridge_port *p) +{ + read_lock(&p->br->lock); + if (p->state != BR_STATE_DISABLED && + br_is_designated_port(p)) { + printk(KERN_INFO "%s: received tcn bpdu on port %i(%s)\n", + p->br->name, p->port_no, p->dev->name); + + br_topology_change_detection(p->br); + br_topology_change_acknowledge(p); + } + read_unlock(&p->br->lock); +} diff -urN linux-2.2.20/net/bridge/br_stp_bpdu.c linux-2.2.20br/net/bridge/br_stp_bpdu.c --- linux-2.2.20/net/bridge/br_stp_bpdu.c Thu Jan 1 01:00:00 1970 +++ linux-2.2.20br/net/bridge/br_stp_bpdu.c Mon Dec 10 16:24:28 2001 @@ -0,0 +1,191 @@ +/* + * Spanning tree protocol; BPDU handling + * Linux ethernet bridge + * + * Authors: + * Lennert Buytenhek + * + * $Id$ + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ + +#include +#include +#include +#include +#include "br_private.h" +#include "br_private_stp.h" + +#define JIFFIES_TO_TICKS(j) (((j) << 8) / HZ) +#define TICKS_TO_JIFFIES(j) (((j) * HZ) >> 8) + +static void br_send_bpdu(struct net_bridge_port *p, unsigned char *data, int length) +{ + struct device *dev; + struct sk_buff *skb; + int size; + + if (!p->br->stp_enabled) + return; + + size = length + 2*ETH_ALEN + 2; + if (size < 60) + size = 60; + + dev = p->dev; + + if ((skb = dev_alloc_skb(size)) == NULL) { + printk(KERN_INFO "br: memory squeeze!\n"); + return; + } + + skb->dev = dev; + skb->protocol = htons(ETH_P_802_2); + skb->mac.raw = skb_put(skb, size); + memcpy(skb->mac.raw, bridge_ula, ETH_ALEN); + memcpy(skb->mac.raw+ETH_ALEN, dev->dev_addr, ETH_ALEN); + skb->mac.raw[2*ETH_ALEN] = 0; + skb->mac.raw[2*ETH_ALEN+1] = length; + skb->nh.raw = skb->mac.raw + 2*ETH_ALEN + 2; + memcpy(skb->nh.raw, data, length); + memset(skb->nh.raw + length, 0xa5, size - length - 2*ETH_ALEN - 2); + + dev_queue_xmit(skb); +} + +static __inline__ void br_set_ticks(unsigned char *dest, int jiff) +{ + __u16 ticks; + + ticks = JIFFIES_TO_TICKS(jiff); + dest[0] = (ticks >> 8) & 0xFF; + dest[1] = ticks & 0xFF; +} + +static __inline__ int br_get_ticks(unsigned char *dest) +{ + return TICKS_TO_JIFFIES((dest[0] << 8) | dest[1]); +} + +void br_send_config_bpdu(struct net_bridge_port *p, struct br_config_bpdu *bpdu) +{ + unsigned char buf[38]; + + buf[0] = 0x42; + buf[1] = 0x42; + buf[2] = 0x03; + buf[3] = 0; + buf[4] = 0; + buf[5] = 0; + buf[6] = BPDU_TYPE_CONFIG; + buf[7] = (bpdu->topology_change ? 0x01 : 0) | + (bpdu->topology_change_ack ? 0x80 : 0); + buf[8] = bpdu->root.prio[0]; + buf[9] = bpdu->root.prio[1]; + buf[10] = bpdu->root.addr[0]; + buf[11] = bpdu->root.addr[1]; + buf[12] = bpdu->root.addr[2]; + buf[13] = bpdu->root.addr[3]; + buf[14] = bpdu->root.addr[4]; + buf[15] = bpdu->root.addr[5]; + buf[16] = (bpdu->root_path_cost >> 24) & 0xFF; + buf[17] = (bpdu->root_path_cost >> 16) & 0xFF; + buf[18] = (bpdu->root_path_cost >> 8) & 0xFF; + buf[19] = bpdu->root_path_cost & 0xFF; + buf[20] = bpdu->bridge_id.prio[0]; + buf[21] = bpdu->bridge_id.prio[1]; + buf[22] = bpdu->bridge_id.addr[0]; + buf[23] = bpdu->bridge_id.addr[1]; + buf[24] = bpdu->bridge_id.addr[2]; + buf[25] = bpdu->bridge_id.addr[3]; + buf[26] = bpdu->bridge_id.addr[4]; + buf[27] = bpdu->bridge_id.addr[5]; + buf[28] = (bpdu->port_id >> 8) & 0xFF; + buf[29] = bpdu->port_id & 0xFF; + + br_set_ticks(buf+30, bpdu->message_age); + br_set_ticks(buf+32, bpdu->max_age); + br_set_ticks(buf+34, bpdu->hello_time); + br_set_ticks(buf+36, bpdu->forward_delay); + + br_send_bpdu(p, buf, 38); +} + +void br_send_tcn_bpdu(struct net_bridge_port *p) +{ + unsigned char buf[7]; + + buf[0] = 0x42; + buf[1] = 0x42; + buf[2] = 0x03; + buf[3] = 0; + buf[4] = 0; + buf[5] = 0; + buf[6] = BPDU_TYPE_TCN; + br_send_bpdu(p, buf, 7); +} + +static unsigned char header[6] = {0x42, 0x42, 0x03, 0x00, 0x00, 0x00}; + +void br_stp_handle_bpdu(struct sk_buff *skb) +{ + unsigned char *buf; + struct net_bridge_port *p; + + buf = skb->mac.raw + 14; + p = skb->dev->br_port; + if (!p->br->stp_enabled || memcmp(buf, header, 6)) { + kfree_skb(skb); + return; + } + + if (buf[6] == BPDU_TYPE_CONFIG) { + struct br_config_bpdu bpdu; + + bpdu.topology_change = (buf[7] & 0x01) ? 1 : 0; + bpdu.topology_change_ack = (buf[7] & 0x80) ? 1 : 0; + bpdu.root.prio[0] = buf[8]; + bpdu.root.prio[1] = buf[9]; + bpdu.root.addr[0] = buf[10]; + bpdu.root.addr[1] = buf[11]; + bpdu.root.addr[2] = buf[12]; + bpdu.root.addr[3] = buf[13]; + bpdu.root.addr[4] = buf[14]; + bpdu.root.addr[5] = buf[15]; + bpdu.root_path_cost = + (buf[16] << 24) | + (buf[17] << 16) | + (buf[18] << 8) | + buf[19]; + bpdu.bridge_id.prio[0] = buf[20]; + bpdu.bridge_id.prio[1] = buf[21]; + bpdu.bridge_id.addr[0] = buf[22]; + bpdu.bridge_id.addr[1] = buf[23]; + bpdu.bridge_id.addr[2] = buf[24]; + bpdu.bridge_id.addr[3] = buf[25]; + bpdu.bridge_id.addr[4] = buf[26]; + bpdu.bridge_id.addr[5] = buf[27]; + bpdu.port_id = (buf[28] << 8) | buf[29]; + + bpdu.message_age = br_get_ticks(buf+30); + bpdu.max_age = br_get_ticks(buf+32); + bpdu.hello_time = br_get_ticks(buf+34); + bpdu.forward_delay = br_get_ticks(buf+36); + + kfree_skb(skb); + br_received_config_bpdu(p, &bpdu); + return; + } + + if (buf[6] == BPDU_TYPE_TCN) { + br_received_tcn_bpdu(p); + kfree_skb(skb); + return; + } + + kfree_skb(skb); +} diff -urN linux-2.2.20/net/bridge/br_stp_if.c linux-2.2.20br/net/bridge/br_stp_if.c --- linux-2.2.20/net/bridge/br_stp_if.c Thu Jan 1 01:00:00 1970 +++ linux-2.2.20br/net/bridge/br_stp_if.c Mon Dec 10 16:24:28 2001 @@ -0,0 +1,232 @@ +/* + * Spanning tree protocol; interface code + * Linux ethernet bridge + * + * Authors: + * Lennert Buytenhek + * + * $Id$ + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ + +#include +#include +#include +#include +#include +#include +#include +#include "br_private.h" +#include "br_private_stp.h" + +__u16 br_make_port_id(struct net_bridge_port *p) +{ + return (p->priority << 8) | p->port_no; +} + +/* called under bridge lock */ +void br_init_port(struct net_bridge_port *p) +{ + p->port_id = br_make_port_id(p); + br_become_designated_port(p); + p->state = BR_STATE_BLOCKING; + p->topology_change_ack = 0; + p->config_pending = 0; + br_timer_clear(&p->message_age_timer); + br_timer_clear(&p->forward_delay_timer); + br_timer_clear(&p->hold_timer); +} + +/* called under bridge lock */ +void br_stp_enable_bridge(struct net_bridge *br) +{ + struct net_bridge_port *p; + struct timer_list *timer = &br->tick; + + init_timer(timer); + timer->data = (unsigned long) br; + timer->function = br_tick; + timer->expires = jiffies + 1; + add_timer(timer); + + br_timer_set(&br->hello_timer, jiffies); + br_config_bpdu_generation(br); + + p = br->port_list; + while (p != NULL) { + if (p->dev->flags & IFF_UP) + br_stp_enable_port(p); + + p = p->next; + } + + br_timer_set(&br->gc_timer, jiffies); +} + +/* called under bridge lock */ +void br_stp_disable_bridge(struct net_bridge *br) +{ + struct net_bridge_port *p; + + br->topology_change = 0; + br->topology_change_detected = 0; + br_timer_clear(&br->hello_timer); + br_timer_clear(&br->topology_change_timer); + br_timer_clear(&br->tcn_timer); + br_timer_clear(&br->gc_timer); + br_fdb_cleanup(br); + + p = br->port_list; + while (p != NULL) { + if (p->state != BR_STATE_DISABLED) + br_stp_disable_port(p); + + p = p->next; + } + + del_timer(&br->tick); + synchronize_bh(); +} + +/* called under bridge lock */ +void br_stp_enable_port(struct net_bridge_port *p) +{ + br_init_port(p); + br_port_state_selection(p->br); +} + +/* called under bridge lock */ +void br_stp_disable_port(struct net_bridge_port *p) +{ + struct net_bridge *br; + int wasroot; + + br = p->br; + printk(KERN_INFO "%s: port %i(%s) entering %s state\n", + br->name, p->port_no, p->dev->name, "disabled"); + + wasroot = br_is_root_bridge(br); + br_become_designated_port(p); + p->state = BR_STATE_DISABLED; + p->topology_change_ack = 0; + p->config_pending = 0; + br_timer_clear(&p->message_age_timer); + br_timer_clear(&p->forward_delay_timer); + br_timer_clear(&p->hold_timer); + br_configuration_update(br); + br_port_state_selection(br); + + if (br_is_root_bridge(br) && !wasroot) + br_become_root_bridge(br); +} + +/* called under bridge lock */ +static void br_stp_change_bridge_id(struct net_bridge *br, unsigned char *addr) +{ + unsigned char oldaddr[6]; + struct net_bridge_port *p; + int wasroot; + + wasroot = br_is_root_bridge(br); + + memcpy(oldaddr, br->bridge_id.addr, ETH_ALEN); + memcpy(br->bridge_id.addr, addr, ETH_ALEN); + memcpy(br->dev.dev_addr, addr, ETH_ALEN); + + p = br->port_list; + while (p != NULL) { + if (!memcmp(p->designated_bridge.addr, oldaddr, ETH_ALEN)) + memcpy(p->designated_bridge.addr, addr, ETH_ALEN); + + if (!memcmp(p->designated_root.addr, oldaddr, ETH_ALEN)) + memcpy(p->designated_root.addr, addr, ETH_ALEN); + + p = p->next; + } + + br_configuration_update(br); + br_port_state_selection(br); + if (br_is_root_bridge(br) && !wasroot) + br_become_root_bridge(br); +} + +static unsigned char br_mac_zero[6] = {0,0,0,0,0,0}; + +/* called under bridge lock */ +void br_stp_recalculate_bridge_id(struct net_bridge *br) +{ + unsigned char *addr; + struct net_bridge_port *p; + + addr = br_mac_zero; + + p = br->port_list; + while (p != NULL) { + if (addr == br_mac_zero || + memcmp(p->dev->dev_addr, addr, ETH_ALEN) < 0) + addr = p->dev->dev_addr; + + p = p->next; + } + + if (memcmp(br->bridge_id.addr, addr, ETH_ALEN)) + br_stp_change_bridge_id(br, addr); +} + +/* called under bridge lock */ +void br_stp_set_bridge_priority(struct net_bridge *br, int newprio) +{ + struct net_bridge_port *p; + int wasroot; + + wasroot = br_is_root_bridge(br); + + p = br->port_list; + while (p != NULL) { + if (p->state != BR_STATE_DISABLED && + br_is_designated_port(p)) { + p->designated_bridge.prio[0] = (newprio >> 8) & 0xFF; + p->designated_bridge.prio[1] = newprio & 0xFF; + } + + p = p->next; + } + + br->bridge_id.prio[0] = (newprio >> 8) & 0xFF; + br->bridge_id.prio[1] = newprio & 0xFF; + br_configuration_update(br); + br_port_state_selection(br); + if (br_is_root_bridge(br) && !wasroot) + br_become_root_bridge(br); +} + +/* called under bridge lock */ +void br_stp_set_port_priority(struct net_bridge_port *p, int newprio) +{ + __u16 new_port_id; + + p->priority = newprio & 0xFF; + new_port_id = br_make_port_id(p); + + if (br_is_designated_port(p)) + p->designated_port = new_port_id; + + p->port_id = new_port_id; + if (!memcmp(&p->br->bridge_id, &p->designated_bridge, 8) && + p->port_id < p->designated_port) { + br_become_designated_port(p); + br_port_state_selection(p->br); + } +} + +/* called under bridge lock */ +void br_stp_set_path_cost(struct net_bridge_port *p, int path_cost) +{ + p->path_cost = path_cost; + br_configuration_update(p->br); + br_port_state_selection(p->br); +} diff -urN linux-2.2.20/net/bridge/br_stp_timer.c linux-2.2.20br/net/bridge/br_stp_timer.c --- linux-2.2.20/net/bridge/br_stp_timer.c Thu Jan 1 01:00:00 1970 +++ linux-2.2.20br/net/bridge/br_stp_timer.c Mon Dec 10 16:24:28 2001 @@ -0,0 +1,185 @@ +/* + * Spanning tree protocol; timer-related code + * Linux ethernet bridge + * + * Authors: + * Lennert Buytenhek + * + * $Id$ + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ + +#include +#include +#include +#include +#include +#include "br_private.h" +#include "br_private_stp.h" + +static void dump_bridge_id(bridge_id *id) +{ + printk("%.2x%.2x.%.2x:%.2x:%.2x:%.2x:%.2x:%.2x", id->prio[0], + id->prio[1], id->addr[0], id->addr[1], id->addr[2], id->addr[3], + id->addr[4], id->addr[5]); +} + +/* called under bridge lock */ +static int br_is_designated_for_some_port(struct net_bridge *br) +{ + struct net_bridge_port *p; + + p = br->port_list; + while (p != NULL) { + if (p->state != BR_STATE_DISABLED && + !memcmp(&p->designated_bridge, &br->bridge_id, 8)) + return 1; + + p = p->next; + } + + return 0; +} + +/* called under bridge lock */ +static void br_hello_timer_expired(struct net_bridge *br) +{ + br_config_bpdu_generation(br); + br_timer_set(&br->hello_timer, jiffies); +} + +/* called under bridge lock */ +static void br_message_age_timer_expired(struct net_bridge_port *p) +{ + struct net_bridge *br; + int was_root; + + br = p->br; + printk(KERN_INFO "%s: ", br->name); + printk("neighbour "); + dump_bridge_id(&p->designated_bridge); + printk(" lost on port %i(%s)\n", p->port_no, p->dev->name); + + /* + * According to the spec, the message age timer cannot be + * running when we are the root bridge. So.. this was_root + * check is redundant. I'm leaving it in for now, though. + */ + was_root = br_is_root_bridge(br); + + br_become_designated_port(p); + br_configuration_update(br); + br_port_state_selection(br); + if (br_is_root_bridge(br) && !was_root) + br_become_root_bridge(br); +} + +/* called under bridge lock */ +static void br_forward_delay_timer_expired(struct net_bridge_port *p) +{ + if (p->state == BR_STATE_LISTENING) { + printk(KERN_INFO "%s: port %i(%s) entering %s state\n", + p->br->name, p->port_no, p->dev->name, "learning"); + + p->state = BR_STATE_LEARNING; + br_timer_set(&p->forward_delay_timer, jiffies); + } else if (p->state == BR_STATE_LEARNING) { + printk(KERN_INFO "%s: port %i(%s) entering %s state\n", + p->br->name, p->port_no, p->dev->name, "forwarding"); + + p->state = BR_STATE_FORWARDING; + if (br_is_designated_for_some_port(p->br)) + br_topology_change_detection(p->br); + } +} + +/* called under bridge lock */ +static void br_tcn_timer_expired(struct net_bridge *br) +{ + printk(KERN_INFO "%s: retransmitting tcn bpdu\n", br->name); + br_transmit_tcn(br); + br_timer_set(&br->tcn_timer, jiffies); +} + +/* called under bridge lock */ +static void br_topology_change_timer_expired(struct net_bridge *br) +{ + br->topology_change_detected = 0; + br->topology_change = 0; +} + +/* called under bridge lock */ +static void br_hold_timer_expired(struct net_bridge_port *p) +{ + if (p->config_pending) + br_transmit_config(p); +} + +/* called under bridge lock */ +static void br_check_port_timers(struct net_bridge_port *p) +{ + if (br_timer_has_expired(&p->message_age_timer, p->br->max_age)) { + br_timer_clear(&p->message_age_timer); + br_message_age_timer_expired(p); + } + + if (br_timer_has_expired(&p->forward_delay_timer, p->br->forward_delay)) { + br_timer_clear(&p->forward_delay_timer); + br_forward_delay_timer_expired(p); + } + + if (br_timer_has_expired(&p->hold_timer, BR_HOLD_TIME)) { + br_timer_clear(&p->hold_timer); + br_hold_timer_expired(p); + } +} + +/* called under bridge lock */ +static void br_check_timers(struct net_bridge *br) +{ + struct net_bridge_port *p; + + if (br_timer_has_expired(&br->gc_timer, br->gc_interval)) { + br_timer_set(&br->gc_timer, jiffies); + br_fdb_cleanup(br); + } + + if (br_timer_has_expired(&br->hello_timer, br->hello_time)) { + br_timer_clear(&br->hello_timer); + br_hello_timer_expired(br); + } + + if (br_timer_has_expired(&br->tcn_timer, br->bridge_hello_time)) { + br_timer_clear(&br->tcn_timer); + br_tcn_timer_expired(br); + } + + if (br_timer_has_expired(&br->topology_change_timer, br->bridge_forward_delay + br->bridge_max_age)) { + br_timer_clear(&br->topology_change_timer); + br_topology_change_timer_expired(br); + } + + p = br->port_list; + while (p != NULL) { + if (p->state != BR_STATE_DISABLED) + br_check_port_timers(p); + + p = p->next; + } +} + +void br_tick(unsigned long __data) +{ + struct net_bridge *br = (struct net_bridge *)__data; + + read_lock(&br->lock); + br_check_timers(br); + read_unlock(&br->lock); + + br->tick.expires = jiffies + 1; + add_timer(&br->tick); +} diff -urN linux-2.2.20/net/bridge/br_tree.c linux-2.2.20br/net/bridge/br_tree.c --- linux-2.2.20/net/bridge/br_tree.c Sun Mar 25 18:31:13 2001 +++ linux-2.2.20br/net/bridge/br_tree.c Thu Jan 1 01:00:00 1970 @@ -1,501 +0,0 @@ -/* - * This code is derived from the avl functions in mmap.c - */ - -#include -#include -#include -#include -#include -#include - -#include -#define _DEBUG_AVL - -/* - * Use an AVL (Adelson-Velskii and Landis) tree to speed up this search - * from O(n) to O(log n), where n is the number of ULAs. - * Written by Bruno Haible . - * Taken from mmap.c, extensively modified by John Hayes - * - * 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; -static struct fdb *fhp = &fdb_head; -static struct fdb **fhpp = &fhp; -static int fdb_inited = 0; - -#ifdef DEBUG_AVL -static void printk_avl (struct fdb * tree); -#endif - -static int addr_cmp(unsigned char *a1, unsigned char *a2); - -/* - * fdb_head is the AVL tree corresponding to fdb - * or, more exactly, its root. - * A fdb has the following fields: - * fdb_avl_left left son of a tree node - * fdb_avl_right right son of a tree node - * fdb_avl_height 1+max(heightof(left),heightof(right)) - * The empty tree is represented as NULL. - */ - -#ifndef avl_br_empty -#define avl_br_empty (struct fdb *) NULL -#endif - -/* Since the trees are balanced, their height will never be large. */ -#define avl_maxheight 127 -#define heightof(tree) ((tree) == avl_br_empty ? 0 : (tree)->fdb_avl_height) -/* - * Consistency and balancing rules: - * 1. tree->fdb_avl_height == 1+max(heightof(tree->fdb_avl_left),heightof(tree->fdb_avl_right)) - * 2. abs( heightof(tree->fdb_avl_left) - heightof(tree->fdb_avl_right) ) <= 1 - * 3. foreach node in tree->fdb_avl_left: node->fdb_avl_key <= tree->fdb_avl_key, - * foreach node in tree->fdb_avl_right: node->fdb_avl_key >= tree->fdb_avl_key. - */ - -static int fdb_init(void) -{ - fdb_head.fdb_avl_height = 0; - fdb_head.fdb_avl_left = (struct fdb *)0; - fdb_head.fdb_avl_right = (struct fdb *)0; - fdb_inited = 1; - return(0); -} - -struct fdb *br_avl_find_addr(unsigned char addr[6]) -{ - struct fdb * result = NULL; - struct fdb * tree; - - if (!fdb_inited) - fdb_init(); -#if (DEBUG_AVL) - printk("searching for ula %02x:%02x:%02x:%02x:%02x:%02x\n", - addr[0], - addr[1], - addr[2], - addr[3], - addr[4], - addr[5]); -#endif /* DEBUG_AVL */ - for (tree = fhp ; ; ) { - if (tree == avl_br_empty) { -#if (DEBUG_AVL) - printk("search failed, returning node 0x%x\n", (unsigned int)result); -#endif /* DEBUG_AVL */ - return result; - } - -#if (DEBUG_AVL) - printk("node 0x%x: checking ula %02x:%02x:%02x:%02x:%02x:%02x\n", - (unsigned int)tree, - tree->ula[0], - tree->ula[1], - tree->ula[2], - tree->ula[3], - tree->ula[4], - tree->ula[5]); -#endif /* DEBUG_AVL */ - if (addr_cmp(addr, tree->ula) == 0) { -#if (DEBUG_AVL) - printk("found node 0x%x\n", (unsigned int)tree); -#endif /* DEBUG_AVL */ - return tree; - } - if (addr_cmp(addr, tree->ula) < 0) { - tree = tree->fdb_avl_left; - } else { - tree = tree->fdb_avl_right; - } - } -} - - -/* - * Rebalance a tree. - * After inserting or deleting a node of a tree we have a sequence of subtrees - * nodes[0]..nodes[k-1] such that - * nodes[0] is the root and nodes[i+1] = nodes[i]->{fdb_avl_left|fdb_avl_right}. - */ -static void br_avl_rebalance (struct fdb *** nodeplaces_ptr, int count) -{ - if (!fdb_inited) - fdb_init(); - for ( ; count > 0 ; count--) { - struct fdb ** nodeplace = *--nodeplaces_ptr; - struct fdb * node = *nodeplace; - struct fdb * nodeleft = node->fdb_avl_left; - struct fdb * noderight = node->fdb_avl_right; - int heightleft = heightof(nodeleft); - int heightright = heightof(noderight); - if (heightright + 1 < heightleft) { - /* */ - /* * */ - /* / \ */ - /* n+2 n */ - /* */ - struct fdb * nodeleftleft = nodeleft->fdb_avl_left; - struct fdb * nodeleftright = nodeleft->fdb_avl_right; - int heightleftright = heightof(nodeleftright); - if (heightof(nodeleftleft) >= heightleftright) { - /* */ - /* * n+2|n+3 */ - /* / \ / \ */ - /* n+2 n --> / n+1|n+2 */ - /* / \ | / \ */ - /* n+1 n|n+1 n+1 n|n+1 n */ - /* */ - node->fdb_avl_left = nodeleftright; - nodeleft->fdb_avl_right = node; - nodeleft->fdb_avl_height = 1 + (node->fdb_avl_height = 1 + heightleftright); - *nodeplace = nodeleft; - } else { - /* */ - /* * n+2 */ - /* / \ / \ */ - /* n+2 n --> n+1 n+1 */ - /* / \ / \ / \ */ - /* n n+1 n L R n */ - /* / \ */ - /* L R */ - /* */ - nodeleft->fdb_avl_right = nodeleftright->fdb_avl_left; - node->fdb_avl_left = nodeleftright->fdb_avl_right; - nodeleftright->fdb_avl_left = nodeleft; - nodeleftright->fdb_avl_right = node; - nodeleft->fdb_avl_height = node->fdb_avl_height = heightleftright; - nodeleftright->fdb_avl_height = heightleft; - *nodeplace = nodeleftright; - } - } else if (heightleft + 1 < heightright) { - /* similar to the above, just interchange 'left' <--> 'right' */ - struct fdb * noderightright = noderight->fdb_avl_right; - struct fdb * noderightleft = noderight->fdb_avl_left; - int heightrightleft = heightof(noderightleft); - if (heightof(noderightright) >= heightrightleft) { - node->fdb_avl_right = noderightleft; - noderight->fdb_avl_left = node; - noderight->fdb_avl_height = 1 + (node->fdb_avl_height = 1 + heightrightleft); - *nodeplace = noderight; - } else { - noderight->fdb_avl_left = noderightleft->fdb_avl_right; - node->fdb_avl_right = noderightleft->fdb_avl_left; - noderightleft->fdb_avl_right = noderight; - noderightleft->fdb_avl_left = node; - noderight->fdb_avl_height = node->fdb_avl_height = heightrightleft; - noderightleft->fdb_avl_height = heightright; - *nodeplace = noderightleft; - } - } else { - int height = (heightleftfdb_avl_height) - break; - node->fdb_avl_height = height; - } - } -#ifdef DEBUG_AVL - printk_avl(&fdb_head); -#endif /* DEBUG_AVL */ -} - -/* 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]; - int stack_count = 0; - struct fdb *** stack_ptr = &stack[0]; /* = &stack[stackcount] */ - if (!fdb_inited) - fdb_init(); - for (;;) { - struct fdb *node; - - node = *nodeplace; - if (node == avl_br_empty) - break; - *stack_ptr++ = nodeplace; stack_count++; - 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; - } 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) - printk("node 0x%x: adding ula %02x:%02x:%02x:%02x:%02x:%02x\n", - (unsigned int)new_node, - new_node->ula[0], - new_node->ula[1], - new_node->ula[2], - new_node->ula[3], - new_node->ula[4], - new_node->ula[5]); -#endif /* (DEBUG_AVL) */ - new_node->fdb_avl_left = avl_br_empty; - new_node->fdb_avl_right = avl_br_empty; - new_node->fdb_avl_height = 1; - *nodeplace = new_node; - br_avl_rebalance(stack_ptr,stack_count); -#ifdef DEBUG_AVL - printk_avl(&fdb_head); -#endif /* DEBUG_AVL */ - return NULL; /* this is a new node */ -} - - -/* Removes a node out of a tree. */ -static int br_avl_remove (struct fdb * node_to_delete) -{ - struct fdb ** nodeplace = fhpp; - struct fdb ** stack[avl_maxheight]; - int stack_count = 0; - struct fdb *** stack_ptr = &stack[0]; /* = &stack[stackcount] */ - struct fdb ** nodeplace_to_delete; - if (!fdb_inited) - fdb_init(); - for (;;) { - struct fdb * node = *nodeplace; - if (node == avl_br_empty) { - /* what? node_to_delete not found in tree? */ - printk(KERN_ERR "br: avl_remove: node to delete not found in tree\n"); - return(-1); - } - *stack_ptr++ = nodeplace; stack_count++; - if (addr_cmp(node_to_delete->ula, node->ula) == 0) - break; - if (addr_cmp(node_to_delete->ula, node->ula) < 0) - nodeplace = &node->fdb_avl_left; - else - nodeplace = &node->fdb_avl_right; - } - nodeplace_to_delete = nodeplace; - /* Have to remove node_to_delete = *nodeplace_to_delete. */ - if (node_to_delete->fdb_avl_left == avl_br_empty) { - *nodeplace_to_delete = node_to_delete->fdb_avl_right; - stack_ptr--; stack_count--; - } else { - struct fdb *** stack_ptr_to_delete = stack_ptr; - struct fdb ** nodeplace = &node_to_delete->fdb_avl_left; - struct fdb * node; - for (;;) { - node = *nodeplace; - if (node->fdb_avl_right == avl_br_empty) - break; - *stack_ptr++ = nodeplace; stack_count++; - nodeplace = &node->fdb_avl_right; - } - *nodeplace = node->fdb_avl_left; - /* node replaces node_to_delete */ - node->fdb_avl_left = node_to_delete->fdb_avl_left; - node->fdb_avl_right = node_to_delete->fdb_avl_right; - node->fdb_avl_height = node_to_delete->fdb_avl_height; - *nodeplace_to_delete = node; /* replace node_to_delete */ - *stack_ptr_to_delete = &node->fdb_avl_left; /* replace &node_to_delete->fdb_avl_left */ - } - br_avl_rebalance(stack_ptr,stack_count); - return(0); -} - -#ifdef DEBUG_AVL - -/* print a tree */ -static void printk_avl (struct fdb * tree) -{ - if (tree != avl_br_empty) { - printk("("); - 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->port); - if (tree->fdb_avl_left != avl_br_empty) { - printk_avl(tree->fdb_avl_left); - printk("<"); - } - if (tree->fdb_avl_right != avl_br_empty) { - printk(">"); - printk_avl(tree->fdb_avl_right); - } - printk(")\n"); - } -} - -static char *avl_check_point = "somewhere"; - -/* check a tree's consistency and balancing */ -static void avl_checkheights (struct fdb * tree) -{ - int h, hl, hr; - - if (tree == avl_br_empty) - return; - avl_checkheights(tree->fdb_avl_left); - avl_checkheights(tree->fdb_avl_right); - h = tree->fdb_avl_height; - hl = heightof(tree->fdb_avl_left); - hr = heightof(tree->fdb_avl_right); - if ((h == hl+1) && (hr <= hl) && (hl <= hr+1)) - return; - if ((h == hr+1) && (hl <= hr) && (hr <= hl+1)) - return; - printk("%s: avl_checkheights: heights inconsistent\n",avl_check_point); -} - -/* check that all values stored in a tree are < key */ -static void avl_checkleft (struct fdb * tree, fdb_avl_key_t key) -{ - if (tree == avl_br_empty) - return; - avl_checkleft(tree->fdb_avl_left,key); - avl_checkleft(tree->fdb_avl_right,key); - if (tree->fdb_avl_key < key) - return; - printk("%s: avl_checkleft: left key %lu >= top key %lu\n",avl_check_point,tree->fdb_avl_key,key); -} - -/* check that all values stored in a tree are > key */ -static void avl_checkright (struct fdb * tree, fdb_avl_key_t key) -{ - if (tree == avl_br_empty) - return; - avl_checkright(tree->fdb_avl_left,key); - avl_checkright(tree->fdb_avl_right,key); - if (tree->fdb_avl_key > key) - return; - printk("%s: avl_checkright: right key %lu <= top key %lu\n",avl_check_point,tree->fdb_avl_key,key); -} - -/* check that all values are properly increasing */ -static void avl_checkorder (struct fdb * tree) -{ - if (tree == avl_br_empty) - return; - avl_checkorder(tree->fdb_avl_left); - avl_checkorder(tree->fdb_avl_right); - avl_checkleft(tree->fdb_avl_left,tree->fdb_avl_key); - avl_checkright(tree->fdb_avl_right,tree->fdb_avl_key); -} - -#endif /* DEBUG_AVL */ - -static int addr_cmp(unsigned char a1[], unsigned char a2[]) -{ - int i; - - for (i=0; i<6; i++) { - if (a1[i] > a2[i]) return(1); - if (a1[i] < a2[i]) return(-1); - } - return(0); -} - -/* Vova Oksman: function for copy tree to the buffer */ -void sprintf_avl (char **pbuffer, struct fdb * tree, off_t *pos, - int* len, off_t offset, int length) -{ - int size; - - if( 0 == *pos){ - if(avl_br_empty == tree) - /* begin from the root */ - tree = fhp; - *pos = *len; - } - - if (*pos >= offset+length) - return; - - if (tree != avl_br_empty) { - /* don't write the local device */ - if(tree->port != 0){ - size = sprintf(*pbuffer, - "%02x:%02x:%02x:%02x:%02x:%02x %s %d %ld\n", - tree->ula[0],tree->ula[1],tree->ula[2], - tree->ula[3],tree->ula[4],tree->ula[5], - port_info[tree->port].dev->name, tree->flags,CURRENT_TIME-tree->timer); - - (*pos)+=size; - (*len)+=size; - (*pbuffer)+=size; - } - if (*pos <= offset) - *len=0; - - if (tree->fdb_avl_left != avl_br_empty) { - sprintf_avl (pbuffer,tree->fdb_avl_left,pos,len,offset,length); - } - if (tree->fdb_avl_right != avl_br_empty) { - sprintf_avl (pbuffer,tree->fdb_avl_right,pos,len,offset,length); - } - - } - - return; -} - -/* - * Delete all nodes learnt by the port - */ -void br_avl_delete_by_port(int port) -{ - struct fdb *fdb, *next; - - if (!fdb_inited) - fdb_init(); - - for(fdb = port_info[port].fdb; fdb != NULL; fdb = next) { - next = fdb->fdb_next; - br_avl_remove(fdb); - } - port_info[port].fdb = NULL; - - /* remove the local mac too */ -/* next = br_avl_find_addr(port_info[port].dev->dev_addr); */ - next = br_avl_find_addr(port_info[port].ifmac.BRIDGE_ID_ULA); - if (next != NULL) - br_avl_remove(next); - - return; -} diff -urN linux-2.2.20/net/bridge/sysctl_net_bridge.c linux-2.2.20br/net/bridge/sysctl_net_bridge.c --- linux-2.2.20/net/bridge/sysctl_net_bridge.c Sun Mar 25 18:31:13 2001 +++ linux-2.2.20br/net/bridge/sysctl_net_bridge.c Thu Jan 1 01:00:00 1970 @@ -1,13 +0,0 @@ -/* -*- linux-c -*- - * sysctl_net_bridge.c: sysctl interface to net bridge subsystem. - * - * Begun June 1, 1996, Mike Shaver. - * Added /proc/sys/net/bridge directory entry (empty =) ). [MS] - */ - -#include -#include - -ctl_table bridge_table[] = { - {0} -}; diff -urN linux-2.2.20/net/core/dev.c linux-2.2.20br/net/core/dev.c --- linux-2.2.20/net/core/dev.c Fri Nov 2 17:39:16 2001 +++ linux-2.2.20br/net/core/dev.c Mon Dec 10 16:24:28 2001 @@ -84,7 +84,7 @@ #include #include #include -#include +#include #include #include #include @@ -810,35 +810,8 @@ kfree_skb(skb); } -#ifdef CONFIG_BRIDGE -static inline void handle_bridge(struct sk_buff *skb, unsigned short type) -{ - /* - * The br_stats.flags is checked here to save the expense of a - * function call. - */ - if ((br_stats.flags & BR_UP) && br_call_bridge(skb, type)) - { - /* - * We pass the bridge a complete frame. This means - * recovering the MAC header first. - */ - - int offset; - - skb=skb_clone(skb, GFP_ATOMIC); - if(skb==NULL) - return; - - offset=skb->data-skb->mac.raw; - skb_push(skb,offset); /* Put header back on for bridge */ - - if(br_receive_frame(skb)) - return; - kfree_skb(skb); - } - return; -} +#if defined(CONFIG_BRIDGE) || defined(CONFIG_BRIDGE_MODULE) +void (*br_handle_frame_hook)(struct sk_buff *skb) = NULL; #endif @@ -955,11 +928,6 @@ continue; } - /* - * Fetch the packet protocol ID. - */ - - type = skb->protocol; #ifdef CONFIG_NET_DIVERT /* @@ -970,15 +938,6 @@ #endif /* CONFIG_NET_DIVERT */ -#ifdef CONFIG_BRIDGE - /* - * If we are bridging then pass the frame up to the - * bridging code (if this protocol is to be bridged). - * If it is bridged then move on - */ - handle_bridge(skb, type); -#endif - /* * We got a packet ID. Now loop over the "known protocols" * list. There are two lists. The ptype_all list of taps (normally empty) @@ -986,19 +945,44 @@ */ pt_prev = NULL; - for (ptype = ptype_all; ptype!=NULL; ptype=ptype->next) + for (ptype = ptype_all; ptype != NULL; ptype = ptype->next) { if (!ptype->dev || ptype->dev == skb->dev) { - if(pt_prev) + if (pt_prev) { - struct sk_buff *skb2=skb_clone(skb, GFP_ATOMIC); - if(skb2) - pt_prev->func(skb2,skb->dev, pt_prev); + struct sk_buff *skb2 = skb_clone(skb, GFP_ATOMIC); + if (skb2) + pt_prev->func(skb2, skb->dev, pt_prev); } - pt_prev=ptype; + pt_prev = ptype; } } + /* + * Is there a last item to send to ? + */ + + if (pt_prev) + { + struct sk_buff *skb2 = skb_clone(skb, GFP_ATOMIC); + if (skb2) + pt_prev->func(skb2, skb->dev, pt_prev); + pt_prev = NULL; + } + +#if defined(CONFIG_BRIDGE) || defined(CONFIG_BRIDGE_MODULE) + if (skb->dev->br_port != NULL && br_handle_frame_hook != NULL) { + br_handle_frame_hook(skb); + continue; + } +#endif + + /* + * Fetch the packet protocol ID. + */ + + type = skb->protocol; + for (ptype = ptype_base[ntohs(type)&15]; ptype != NULL; ptype = ptype->next) { if (ptype->type == type && (!ptype->dev || ptype->dev==skb->dev)) @@ -1007,7 +991,8 @@ * We already have a match queued. Deliver * to it and then remember the new match */ - if(pt_prev) + + if (pt_prev) { struct sk_buff *skb2; @@ -1032,13 +1017,14 @@ if(pt_prev) pt_prev->func(skb, skb->dev, pt_prev); + /* * Has an unknown packet has been received ? */ - else { + else kfree_skb(skb); - } + } /* End of queue loop */ /* @@ -1963,12 +1949,11 @@ skb_queue_head_init(&backlog); /* - * The bridge has to be up before the devices + * Initialize the ethernet bridge. */ - -#ifdef CONFIG_BRIDGE +#ifdef CONFIG_BRIDGE br_init(); -#endif +#endif /* * Frame Diverter init @@ -2083,13 +2068,6 @@ dev_mcast_init(); -#ifdef CONFIG_BRIDGE - /* - * Register any statically linked ethernet devices with the bridge - */ - br_spacedevice_register(); -#endif - #ifdef CONFIG_N2 n2_init(); #endif diff -urN linux-2.2.20/net/core/skbuff.c linux-2.2.20br/net/core/skbuff.c --- linux-2.2.20/net/core/skbuff.c Sun Mar 25 18:31:12 2001 +++ linux-2.2.20br/net/core/skbuff.c Mon Dec 10 16:24:28 2001 @@ -184,7 +184,6 @@ skb->destructor = NULL; skb->pkt_type = PACKET_HOST; /* Default type */ - skb->pkt_bridged = 0; /* Not bridged */ skb->prev = skb->next = NULL; skb->list = NULL; skb->sk = NULL; diff -urN linux-2.2.20/net/ipv4/af_inet.c linux-2.2.20br/net/ipv4/af_inet.c --- linux-2.2.20/net/ipv4/af_inet.c Fri Nov 2 17:39:16 2001 +++ linux-2.2.20br/net/ipv4/af_inet.c Mon Dec 10 16:24:28 2001 @@ -106,10 +106,8 @@ #ifdef CONFIG_IP_MASQUERADE #include #endif -#ifdef CONFIG_BRIDGE -#include -#endif - +#include + #ifdef CONFIG_NET_DIVERT #include #endif /* CONFIG_NET_DIVERT */ @@ -139,6 +137,7 @@ int (*dlci_ioctl_hook)(unsigned int, void *) = NULL; #endif +int (*br_ioctl_hook)(unsigned long) = NULL; int (*rarp_ioctl_hook)(unsigned int,void*) = NULL; /* @@ -913,11 +912,15 @@ return(devinet_ioctl(cmd,(void *) arg)); case SIOCGIFBR: case SIOCSIFBR: -#ifdef CONFIG_BRIDGE - return(br_ioctl(cmd,(void *) arg)); -#else +#ifdef CONFIG_KMOD + if (br_ioctl_hook == NULL) + request_module("bridge"); +#endif + + if (br_ioctl_hook != NULL) + return br_ioctl_hook(arg); + return -ENOPKG; -#endif case SIOCGIFDIVERT: case SIOCSIFDIVERT: diff -urN linux-2.2.20/net/netsyms.c linux-2.2.20br/net/netsyms.c --- linux-2.2.20/net/netsyms.c Sun Mar 25 18:37:41 2001 +++ linux-2.2.20br/net/netsyms.c Mon Dec 10 16:24:28 2001 @@ -28,10 +28,7 @@ #endif #include #include - -#ifdef CONFIG_BRIDGE -#include -#endif +#include #ifdef CONFIG_NET_DIVERT #include @@ -225,9 +222,10 @@ EXPORT_SYMBOL(scm_detach_fds); -#ifdef CONFIG_BRIDGE -EXPORT_SYMBOL(br_ioctl); +#if defined(CONFIG_BRIDGE) || defined(CONFIG_BRIDGE_MODULE) +EXPORT_SYMBOL(br_handle_frame_hook); #endif +EXPORT_SYMBOL(br_ioctl_hook); #ifdef CONFIG_NET_DIVERT EXPORT_SYMBOL(alloc_divert_blk); diff -urN linux-2.2.20/net/packet/af_packet.c linux-2.2.20br/net/packet/af_packet.c --- linux-2.2.20/net/packet/af_packet.c Sun Mar 25 18:31:14 2001 +++ linux-2.2.20br/net/packet/af_packet.c Mon Dec 10 16:24:28 2001 @@ -63,14 +63,12 @@ #include #include #include +#include #ifdef CONFIG_INET #include #endif -#ifdef CONFIG_BRIDGE -#include -#endif #ifdef CONFIG_NET_DIVERT #include @@ -1131,11 +1129,15 @@ case SIOCGIFBR: case SIOCSIFBR: -#ifdef CONFIG_BRIDGE - return(br_ioctl(cmd,(void *) arg)); -#else +#ifdef CONFIG_KMOD + if (br_ioctl_hook == NULL) + request_module("bridge"); +#endif + + if (br_ioctl_hook != NULL) + return br_ioctl_hook(arg); + return -ENOPKG; -#endif case SIOCGIFDIVERT: case SIOCSIFDIVERT: diff -urN linux-2.2.20/net/sysctl_net.c linux-2.2.20br/net/sysctl_net.c --- linux-2.2.20/net/sysctl_net.c Sun Mar 25 18:31:13 2001 +++ linux-2.2.20br/net/sysctl_net.c Mon Dec 10 16:24:28 2001 @@ -34,10 +34,6 @@ extern ctl_table ether_table[], e802_table[]; #endif -#ifdef CONFIG_BRIDGE -extern ctl_table bridge_table[]; -#endif - #ifdef CONFIG_IPV6 extern ctl_table ipv6_table[]; #endif @@ -61,9 +57,6 @@ #ifdef CONFIG_IPX {NET_IPX, "ipx", NULL, 0, 0555, ipx_table}, #endif -#ifdef CONFIG_BRIDGE - {NET_BRIDGE, "bridge", NULL, 0, 0555, bridge_table}, -#endif #ifdef CONFIG_IPV6 {NET_IPV6, "ipv6", NULL, 0, 0555, ipv6_table}, #endif