diff options
Diffstat (limited to 'net')
55 files changed, 3088 insertions, 3620 deletions
diff --git a/net/Changes b/net/Changes index 6ff0219cc..3e88ee85f 100644 --- a/net/Changes +++ b/net/Changes @@ -6,7 +6,6 @@ o insw_and_csum o Should unix domain connect never block ? o Screend loadable firewall module -o Fix merging the bridge code o Remove kernel RARP and replace with user mode daemon. o Merge ARM half word trap fixes for ethernet headers o Stop route addition to downed interfaces diff --git a/net/Config.in b/net/Config.in index 82f4fe6d9..45f1b1168 100644 --- a/net/Config.in +++ b/net/Config.in @@ -61,11 +61,8 @@ if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then 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 '802.2 LLC (EXPERIMENTAL)' CONFIG_LLC + tristate '802.1d Ethernet Bridging' CONFIG_BRIDGE + bool '802.2 LLC (EXPERIMENTAL)' CONFIG_LLC # if [ "$CONFIG_LLC" = "y" ]; then # bool ' Netbeui (EXPERIMENTAL)' CONFIG_NETBEUI # fi diff --git a/net/Makefile b/net/Makefile index ccaed5154..bf234eae1 100644 --- a/net/Makefile +++ b/net/Makefile @@ -67,6 +67,10 @@ endif ifeq ($(CONFIG_BRIDGE),y) SUB_DIRS += bridge +else + ifeq ($(CONFIG_BRIDGE),m) + MOD_SUB_DIRS += bridge + endif endif ifeq ($(CONFIG_IPX),y) diff --git a/net/README b/net/README index d54d8526b..7490dff2b 100644 --- a/net/README +++ b/net/README @@ -7,6 +7,7 @@ Code Section Bug Report Contact [token ring ] p.norton@computer.org appletalk jschlst@turbolinux.com ax25 g4klx@g4klx.demon.co.uk +bridge buytenh@openrock.net core alan@lxorguk.ukuu.org.uk decnet SteveW@ACM.org ethernet alan@lxorguk.ukuu.org.uk diff --git a/net/atm/clip.c b/net/atm/clip.c index f7e008dd1..43cf3d9a2 100644 --- a/net/atm/clip.c +++ b/net/atm/clip.c @@ -223,10 +223,8 @@ static void clip_pop(struct atm_vcc *vcc,struct sk_buff *skb) DPRINTK("clip_pop(vcc %p)\n",vcc); CLIP_VCC(vcc)->old_pop(vcc,skb); /* skb->dev == NULL in outbound ARP packets */ - if (atm_may_send(vcc,0) && skb->dev) { - skb->dev->tbusy = 0; - mark_bh(NET_BH); - } + if (atm_may_send(vcc,0) && skb->dev) + netif_wake_queue(skb->dev); } @@ -403,7 +401,8 @@ return 0; ((u16 *) here)[3] = skb->protocol; } atomic_add(skb->truesize,&vcc->tx_inuse); - dev->tbusy = !atm_may_send(vcc,0); + if (!atm_may_send(vcc,0)) + netif_stop_queue(dev); ATM_SKB(skb)->iovcnt = 0; ATM_SKB(skb)->atm_options = vcc->atm_options; entry->vccs->last_use = jiffies; diff --git a/net/ax25/af_ax25.c b/net/ax25/af_ax25.c index f39e19d41..a676660dc 100644 --- a/net/ax25/af_ax25.c +++ b/net/ax25/af_ax25.c @@ -1574,8 +1574,10 @@ static int ax25_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg) return -EPERM; return ax25_ctl_ioctl(cmd, (void *)arg); - case SIOCAX25GETINFO: { + case SIOCAX25GETINFO: + case SIOCAX25GETINFOOLD: { struct ax25_info_struct ax25_info; + ax25_info.t1 = sk->protinfo.ax25->t1 / HZ; ax25_info.t2 = sk->protinfo.ax25->t2 / HZ; ax25_info.t3 = sk->protinfo.ax25->t3 / HZ; @@ -1589,8 +1591,28 @@ static int ax25_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg) ax25_info.state = sk->protinfo.ax25->state; ax25_info.rcv_q = atomic_read(&sk->rmem_alloc); ax25_info.snd_q = atomic_read(&sk->wmem_alloc); - if (copy_to_user((void *)arg, &ax25_info, sizeof(ax25_info))) - return -EFAULT; + ax25_info.vs = sk->protinfo.ax25->vs; + ax25_info.vr = sk->protinfo.ax25->vr; + ax25_info.va = sk->protinfo.ax25->va; + ax25_info.vs_max = sk->protinfo.ax25->vs; /* reserved */ + ax25_info.paclen = sk->protinfo.ax25->paclen; + ax25_info.window = sk->protinfo.ax25->window; + + /* old structure? */ + if (cmd == SIOCAX25GETINFOOLD) { + static int warned = 0; + if (!warned) { + printk(KERN_INFO "%s uses old SIOCAX25GETINFO\n", + current->comm); + warned=1; + } + + if (copy_to_user((void *)arg, &ax25_info, sizeof(struct ax25_info_struct_depreciated))) + return -EFAULT; + } else { + if (copy_to_user((void *)arg, &ax25_info, sizeof(struct ax25_info_struct))) + return -EINVAL; + } return 0; } diff --git a/net/bridge/Makefile b/net/bridge/Makefile index bcccefb75..082388fe5 100644 --- a/net/bridge/Makefile +++ b/net/bridge/Makefile @@ -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 --git a/net/bridge/br.c b/net/bridge/br.c index d2279e56d..ec59ff622 100644 --- a/net/bridge/br.c +++ b/net/bridge/br.c @@ -1,2740 +1,67 @@ /* - * 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 <Alan.Cox@linux.org> - * More hacks to be able to switch protocols on and off by Christoph Lameter - * <clameter@debian.org> - * Software and more Documentation for the bridge is available from ftp.debian.org - * in the bridgex package + * Authors: + * Lennert Buytenhek <buytenh@gnu.org> + * + * $Id: br.c,v 1.39 2000/02/18 16:47:11 davem Exp $ * * 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 <linux/module.h> -#include <linux/errno.h> -#include <linux/types.h> -#include <linux/socket.h> -#include <linux/in.h> +#include <linux/module.h> #include <linux/kernel.h> -#include <linux/sched.h> -#include <linux/timer.h> -#include <linux/malloc.h> -#include <linux/string.h> -#include <linux/net.h> -#include <linux/inet.h> +#include <linux/miscdevice.h> #include <linux/netdevice.h> -#include <linux/inetdevice.h> #include <linux/etherdevice.h> -#include <linux/skbuff.h> -#include <linux/if_arp.h> -#include <linux/ip.h> -#include <linux/version.h> #include <linux/init.h> +#include <linux/if_bridge.h> #include <asm/uaccess.h> -#include <asm/system.h> -#include <linux/rtnetlink.h> -#include <net/br.h> -#include <linux/proc_fs.h> -#include <linux/delay.h> - -#ifndef min -#define min(a, b) (((a) <= (b)) ? (a) : (b)) -#endif - -static void transmit_config(int port_no); -static int root_bridge(void); -static int supersedes_port_info(int port_no, Config_bpdu *config); -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 net_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 net_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 net_device dev; - char name[IFNAMSIZ]; -}; -static struct brg_if brg_if; - -/* - * Here to save linkage? problems - */ - -static inline int find_port(struct net_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_PROT_STATS && br_stats.prot_id[x]!=protocol && br_stats.prot_id[x];x++); - if (x<BR_MAX_PROT_STATS) - { - br_stats.prot_id[x]=protocol;br_stats.prot_counter[x]++; - } - } - - for (x=BR_PROTOCOL_HASH(protocol); br_stats.protocols[x]!=0;) - { - if (br_stats.protocols[x]==protocol) - return !br_stats.policy; - x++; - if (x==BR_MAX_PROTOCOLS) - x=0; - } - return br_stats.policy; -} - -/* Add a protocol to be handled opposite to the standard policy of the bridge */ - -static int br_add_exempt_protocol(unsigned short p) -{ - unsigned x; - if (p == 0) return -EINVAL; - if (br_stats.exempt_protocols > 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); -} - -static int root_bridge(void) -{ - return (br_cmp(bridge_info.designated_root.BRIDGE_ID, - bridge_info.bridge_id.BRIDGE_ID)?FALSE:TRUE); -} - -static int supersedes_port_info(int port_no, Config_bpdu *config) /* (4.6.2.2) */ -{ - 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)) */ - )))))) - ); -} - -static void record_config_information(int port_no, Config_bpdu *config) /* (4.6.2) */ -{ - 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; -} - -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(); - -#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 */ -static int br_tree_get_info(char *buffer, char **start, off_t offset, int length) -{ - 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; -} - -void __init br_init(void) -{ /* (4.8.1) */ - int port_no; - - printk(KERN_INFO "NET4: Ethernet Bridge 006 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 */ - proc_net_create("bridge", 0, br_tree_get_info); -} - -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; -} +#include "br_private.h" -static void stop_message_age_timer(int port_no) +void br_dec_use_count() { - message_age_timer[port_no].active = FALSE; + MOD_DEC_USE_COUNT; } -static int message_age_timer_expired(int port_no) +void br_inc_use_count() { - 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); + MOD_INC_USE_COUNT; } -static void start_forward_delay_timer(int port_no) +static int __init br_init(void) { - forward_delay_timer[port_no].value = 0; - forward_delay_timer[port_no].active = TRUE; -} + printk(KERN_INFO "NET4: Ethernet Bridge 008 for NET4.0\n"); -static void stop_forward_delay_timer(int port_no) -{ - forward_delay_timer[port_no].active = FALSE; -} + br_handle_frame_hook = br_handle_frame; + br_ioctl_hook = br_ioctl_deviceless_stub; + register_netdevice_notifier(&br_device_notifier); -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 net_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 net_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 net_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 net_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; - 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; - Port_data *p; - struct ethhdr *eth; - struct net_device *dev; - - /* sanity */ - if (!skb) { - printk(KERN_CRIT "br_receive_frame: no skb!\n"); - return(1); - } - - dev = skb->dev; - - 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(br_learn(skb, port)) - { /* 3.8 */ - ++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(br_learn(skb, port)) { /* 3.8 */ - ++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. - */ - -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_DEBUG "br_learn: unable to malloc fdb\n"); - return(-1); /* this drop the frame */ - } - } - f->port = port; /* source port */ - memcpy(f->ula, eth->h_source, 6); - f->timer = CURRENT_TIME; - f->flags = FDB_ENT_VALID; - /* - * add entity to AVL tree. If entity already - * exists in the tree, update the fields with - * what we have here. - */ - if ((oldfdb = br_avl_insert(f))) - { - /* update if !NULL */ - if((eth->h_dest[0] & 0x01) && /* multicast */ mcast_quench(oldfdb)) - return 1; - return 0; - } - newfdb[port] = NULL; /* force kmalloc next time */ - f->mcast_count = 0; - /* add to head of port chain */ - f->fdb_next = p->fdb; - p->fdb = f; - allocated_fdb_cnt++; - return 0; -} - -/* JRP: always called under br_receive_frame(). No need for Q protection. */ - -void requeue_fdb(struct fdb *node, int new_port) -{ - Port_data *p = &port_info[node->port]; - - /* dequeue */ - if(p->fdb == node) - p->fdb = node->fdb_next; - else - { - struct fdb *prev; - - for(prev = p->fdb; prev; prev = prev->fdb_next) - if (prev->fdb_next == node) - break; - - if(prev != NULL) - prev->fdb_next = node->fdb_next; - else - { - /* Forget about this update. */ - printk(KERN_ERR "br:requeue_fdb\n"); - return; - } - } - /* enqueue */ - node->port = new_port; - node->fdb_next = port_info[new_port].fdb; - port_info[new_port].fdb = node; -} - -/* - * 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 net_device *dev) /* 4.10.2 */ +static void __br_clear_frame_hook(void) { - 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 */ + br_handle_frame_hook = NULL; } -/* - * this routine always consumes the skb - */ - -static void br_bpdu(struct sk_buff *skb, int port) /* consumes skb */ +static void __br_clear_ioctl_hook(void) { - 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); + br_ioctl_hook = NULL; } -struct fdb_info *get_fdb_info(int user_buf_size, int *copied,int *notcopied) +static void __exit br_deinit(void) { - 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_ifnames(void) { - int i; - - for(i=One;i<=No_of_ports; 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 net_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_ifnames(); - - 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 (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 (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 (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 (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.arg2<sizeof(struct fdb_info_hdr)) - return -EINVAL; - put_user(j, &user_buf->cmd_time); - if(allocated_fdb_cnt == 0) - { - put_user(0, &user_buf->copied); - put_user(0, &user_buf->not_copied); - return 0; - } - fdbis = get_fdb_info(bcf.arg2, &copied, ¬copied); - put_user(copied, &user_buf->copied); - put_user(notcopied, &user_buf->not_copied); - if(!fdbis) - return -ENOMEM; - u_fdbs = (struct fdb_info *) (user_buf+1); - err = copy_to_user(u_fdbs, fdbis, copied*sizeof(struct fdb_info)); - kfree(fdbis); - if (err) - { - err = -EFAULT; - } - return err; - } - default: - return -EINVAL; - } - 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 net_device *dev); -static int brg_open(struct net_device *dev); -static int brg_start_xmit(struct sk_buff *skb, struct net_device *dev); -static int brg_close(struct net_device *dev); -static struct net_device_stats *brg_get_stats(struct net_device *dev); -static void brg_set_multicast_list(struct net_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. - */ - -int __init brg_probe(struct net_device *dev) -{ - unsigned int bogomips; - struct timeval utime; - - printk(KERN_INFO "%s: network interface for Ethernet Bridge 006/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_sec+2500)/500000 ; - 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 net_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 net_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 net_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 net_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); - } - - } - - /* 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 net_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 net_device *dev) -{ - struct net_local *lp = (struct net_local *)dev->priv; - return &lp->stats; -} - -/* - * - */ - -int __init brg_init(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; -} - - -#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_call_ioctl_atomic(__br_clear_ioctl_hook); + net_call_rx_atomic(__br_clear_frame_hook); } -#endif /* MODULE */ +EXPORT_NO_SYMBOLS; -#endif +module_init(br_init) +module_exit(br_deinit) diff --git a/net/bridge/br_device.c b/net/bridge/br_device.c new file mode 100644 index 000000000..de14e83a8 --- /dev/null +++ b/net/bridge/br_device.c @@ -0,0 +1,141 @@ +/* + * Device handling code + * Linux ethernet bridge + * + * Authors: + * Lennert Buytenhek <buytenh@gnu.org> + * + * $Id: br_device.c,v 1.1 2000/02/18 16:47:11 davem Exp $ + * + * 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 <linux/config.h> +#include <linux/kernel.h> +#include <linux/netdevice.h> +#include <linux/if_bridge.h> +#include <asm/uaccess.h> +#include "br_private.h" + +static int br_dev_do_ioctl(struct net_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 net_device *dev) +{ + struct net_bridge *br; + + br = dev->priv; + + return &br->statistics; +} + +static int __br_dev_xmit(struct sk_buff *skb, struct net_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_xmit(struct sk_buff *skb, struct net_device *dev) +{ + struct net_bridge *br; + int ret; + + br = dev->priv; + read_lock(&br->lock); + ret = __br_dev_xmit(skb, dev); + read_unlock(&br->lock); + + return ret; +} + +static int br_dev_open(struct net_device *dev) +{ + struct net_bridge *br; + + netif_start_queue(dev); + + 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 net_device *dev) +{ +} + +static int br_dev_stop(struct net_device *dev) +{ + struct net_bridge *br; + + br = dev->priv; + read_lock(&br->lock); + br_stp_disable_bridge(br); + read_unlock(&br->lock); + + netif_stop_queue(dev); + + return 0; +} + +#ifdef CONFIG_NET_FASTROUTE +static int br_dev_accept_fastpath(struct net_device *dev, struct dst_entry *dst) +{ + return -1; +} +#endif + +void br_dev_setup(struct net_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; +#ifdef CONFIG_NET_FASTROUTE + dev->accept_fastpath = br_dev_accept_fastpath; +#endif + + dev->tx_queue_len = 0; +} diff --git a/net/bridge/br_fdb.c b/net/bridge/br_fdb.c new file mode 100644 index 000000000..2ca3c7fdd --- /dev/null +++ b/net/bridge/br_fdb.c @@ -0,0 +1,318 @@ +/* + * Forwarding database + * Linux ethernet bridge + * + * Authors: + * Lennert Buytenhek <buytenh@gnu.org> + * + * $Id: br_fdb.c,v 1.1 2000/02/18 16:47:12 davem Exp $ + * + * 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 <linux/kernel.h> +#include <linux/if_bridge.h> +#include <asm/atomic.h> +#include <asm/spinlock.h> +#include <asm/uaccess.h> +#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) +{ + 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;i<BR_HASH_SIZE;i++) { + struct net_bridge_fdb_entry *f; + + f = br->hash[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;i<BR_HASH_SIZE;i++) { + struct net_bridge_fdb_entry *f; + + f = br->hash[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;i<BR_HASH_SIZE;i++) { + struct net_bridge_fdb_entry *f; + + f = br->hash[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;i<BR_HASH_SIZE;i++) { + struct net_bridge_fdb_entry *f; + + f = br->hash[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 --git a/net/bridge/br_forward.c b/net/bridge/br_forward.c new file mode 100644 index 000000000..e33850fd2 --- /dev/null +++ b/net/bridge/br_forward.c @@ -0,0 +1,95 @@ +/* + * Forwarding decision + * Linux ethernet bridge + * + * Authors: + * Lennert Buytenhek <buytenh@gnu.org> + * + * $Id: br_forward.c,v 1.1 2000/02/18 16:47:12 davem Exp $ + * + * 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 <linux/kernel.h> +#include <linux/netdevice.h> +#include <linux/inetdevice.h> +#include <linux/skbuff.h> +#include <linux/if_bridge.h> +#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 0; + + return 1; +} + +static void __br_forward(struct net_bridge_port *to, struct sk_buff *skb) +{ + skb->dev = to->dev; + dev_queue_xmit(skb); +} + +/* called under bridge lock */ +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); +} + +/* called under bridge lock */ +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; + + 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); + return; + } + + __br_forward(prev, skb2); + } + + prev = p; + } + + p = p->next; + } + + if (prev != NULL) { + __br_forward(prev, skb); + return; + } + + kfree_skb(skb); +} diff --git a/net/bridge/br_if.c b/net/bridge/br_if.c new file mode 100644 index 000000000..cbba5296d --- /dev/null +++ b/net/bridge/br_if.c @@ -0,0 +1,293 @@ +/* + * Userspace interface + * Linux ethernet bridge + * + * Authors: + * Lennert Buytenhek <buytenh@gnu.org> + * + * $Id: br_if.c,v 1.1 2000/02/18 16:47:12 davem Exp $ + * + * 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 <linux/kernel.h> +#include <linux/if_bridge.h> +#include <linux/inetdevice.h> +#include <linux/rtnetlink.h> +#include <asm/uaccess.h> +#include "br_private.h" + +static struct net_bridge *bridge_list; + +static int br_initial_port_cost(struct net_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 net_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); + 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); + dev_put(dev); + + 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 net_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 net_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_by_name(name) != NULL) { + kfree(br); + return -EEXIST; + } + + br->next = bridge_list; + bridge_list = br; + + br_inc_use_count(); + register_netdev(&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_netdev(&br->dev); + kfree(br); + br_dec_use_count(); + + return 0; +} + +int br_add_if(struct net_bridge *br, struct net_device *dev) +{ + struct net_bridge_port *p; + + if (dev->br_port != NULL) + return -EBUSY; + + if (dev->flags & IFF_LOOPBACK) + return -EINVAL; + + dev_hold(dev); + write_lock_bh(&br->lock); + if ((p = new_nbp(br, dev)) == NULL) { + write_unlock_bh(&br->lock); + dev_put(dev); + return -EXFULL; + } + + 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 net_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; + + i = 0; + + br = bridge_list; + for (i=0;i<num;i++) { + if (br == NULL) + break; + + indices[i] = br->dev.ifindex; + br = br->next; + } + + return i; +} + +/* called under ioctl_lock */ +void br_get_port_ifindices(struct net_bridge *br, int *ifindices) +{ + int i; + struct net_bridge_port *p; + + for (i=0;i<256;i++) + ifindices[i] = 0; + + p = br->port_list; + while (p != NULL) { + ifindices[p->port_no] = p->dev->ifindex; + p = p->next; + } +} diff --git a/net/bridge/br_input.c b/net/bridge/br_input.c new file mode 100644 index 000000000..e3175dac9 --- /dev/null +++ b/net/bridge/br_input.c @@ -0,0 +1,113 @@ +/* + * Handle incoming frames + * Linux ethernet bridge + * + * Authors: + * Lennert Buytenhek <buytenh@gnu.org> + * + * $Id: br_input.c,v 1.1 2000/02/18 16:47:12 davem Exp $ + * + * 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 <linux/kernel.h> +#include <linux/netdevice.h> +#include <linux/etherdevice.h> +#include <linux/if_bridge.h> +#include "br_private.h" + +unsigned char bridge_ula[5] = { 0x01, 0x80, 0xc2, 0x00, 0x00 }; + +static void br_pass_frame_up(struct net_bridge *br, struct sk_buff *skb) +{ + if (br->dev.flags & IFF_UP) { + br->statistics.rx_packets++; + br->statistics.rx_bytes += skb->len; + + skb->dev = &br->dev; + skb->pkt_type = PACKET_HOST; + skb->mac.raw = skb->data; + skb_pull(skb, skb->nh.raw - skb->data); + skb->protocol = eth_type_trans(skb, &br->dev); + netif_rx(skb); + + return; + } + + kfree_skb(skb); +} + +static 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; + + skb->nh.raw = skb->mac.raw; + dest = skb->mac.ethernet->h_dest; + + p = skb->dev->br_port; + br = p->br; + + if (p->state == BR_STATE_DISABLED || + skb->mac.ethernet->h_source[0] & 1) + goto freeandout; + + if (!memcmp(dest, bridge_ula, 5) && !(dest[5] & 0xF0)) + goto handle_special_frame; + + skb_push(skb, skb->data - skb->mac.raw); + + if (p->state == BR_STATE_LEARNING || + p->state == BR_STATE_FORWARDING) + br_fdb_insert(br, p, skb->mac.ethernet->h_source, 0); + + if (p->state != BR_STATE_FORWARDING) + goto freeandout; + + if (dest[0] & 1) { + br_flood(br, skb, 1); + br_pass_frame_up(br, skb); + return; + } + + dst = br_fdb_get(br, dest); + + if (dst != NULL && dst->is_local) { + br_pass_frame_up(br, 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); +} + +void br_handle_frame(struct sk_buff *skb) +{ + struct net_bridge *br; + + br = skb->dev->br_port->br; + read_lock(&br->lock); + __br_handle_frame(skb); + read_unlock(&br->lock); +} diff --git a/net/bridge/br_ioctl.c b/net/bridge/br_ioctl.c new file mode 100644 index 000000000..8830cd747 --- /dev/null +++ b/net/bridge/br_ioctl.c @@ -0,0 +1,264 @@ +/* + * Ioctl handler + * Linux ethernet bridge + * + * Authors: + * Lennert Buytenhek <buytenh@gnu.org> + * + * $Id: br_ioctl.c,v 1.1 2000/02/18 16:47:12 davem Exp $ + * + * 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 <linux/kernel.h> +#include <linux/if_bridge.h> +#include <linux/inetdevice.h> +#include <asm/uaccess.h> +#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 net_device *dev; + int ret; + + dev = dev_get_by_index(arg0); + if (dev == NULL) + return -EINVAL; + + if (cmd == BRCTL_ADD_IF) + ret = br_add_if(br, dev); + else + ret = br_del_if(br, dev); + + dev_put(dev); + return ret; + } + + case BRCTL_GET_BRIDGE_INFO: + { + struct __bridge_info b; + + 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 indices[256]; + + 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; + + 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 -EINVAL; + + 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 indices[64]; + + 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; +} + +DECLARE_MUTEX(ioctl_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; +} + +void br_call_ioctl_atomic(void (*fn)(void)) +{ + down(&ioctl_mutex); + fn(); + up(&ioctl_mutex); +} diff --git a/net/bridge/br_notify.c b/net/bridge/br_notify.c new file mode 100644 index 000000000..8a109d7c3 --- /dev/null +++ b/net/bridge/br_notify.c @@ -0,0 +1,75 @@ +/* + * Device event handling + * Linux ethernet bridge + * + * Authors: + * Lennert Buytenhek <buytenh@gnu.org> + * + * $Id: br_notify.c,v 1.1 2000/02/18 16:47:12 davem Exp $ + * + * 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 <linux/kernel.h> +#include <linux/if_bridge.h> +#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 net_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 --git a/net/bridge/br_private.h b/net/bridge/br_private.h new file mode 100644 index 000000000..084e81b4b --- /dev/null +++ b/net/bridge/br_private.h @@ -0,0 +1,199 @@ +/* + * Linux ethernet bridge + * + * Authors: + * Lennert Buytenhek <buytenh@gnu.org> + * + * $Id: br_private.h,v 1.1 2000/02/18 16:47:12 davem Exp $ + * + * 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 <linux/netdevice.h> +#include <linux/miscdevice.h> +#include <linux/if_bridge.h> +#include "br_private_timer.h" + +#define BR_HASH_BITS 8 +#define BR_HASH_SIZE (1 << BR_HASH_BITS) + +#define BR_HOLD_TIME (1*HZ) + +typedef struct bridge_id bridge_id; +typedef struct mac_addr mac_addr; +typedef __u16 port_id; + +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 net_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 net_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[5]; + +/* br.c */ +void br_dec_use_count(void); +void br_inc_use_count(void); + +/* br_device.c */ +void br_dev_setup(struct net_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 net_device *dev); +int br_del_if(struct net_bridge *br, + struct net_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 */ +void br_call_ioctl_atomic(void (*fn)(void)); +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 --git a/net/bridge/br_private_stp.h b/net/bridge/br_private_stp.h new file mode 100644 index 000000000..91faafff5 --- /dev/null +++ b/net/bridge/br_private_stp.h @@ -0,0 +1,53 @@ +/* + * Linux ethernet bridge + * + * Authors: + * Lennert Buytenhek <buytenh@gnu.org> + * + * $Id: br_private_stp.h,v 1.1 2000/02/18 16:47:13 davem Exp $ + * + * 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 1 + +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 --git a/net/bridge/br_private_timer.h b/net/bridge/br_private_timer.h new file mode 100644 index 000000000..6655ab9f5 --- /dev/null +++ b/net/bridge/br_private_timer.h @@ -0,0 +1,54 @@ +/* + * Linux ethernet bridge + * + * Authors: + * Lennert Buytenhek <buytenh@gnu.org> + * + * $Id: br_private_timer.h,v 1.1 2000/02/18 16:47:13 davem Exp $ + * + * 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_TIMER_H +#define _BR_PRIVATE_TIMER_H + +struct br_timer +{ + int running; + unsigned long expires; +}; + +extern __inline__ void br_timer_clear(struct br_timer *t) +{ + t->running = 0; +} + +extern __inline__ unsigned long br_timer_get_residue(struct br_timer *t) +{ + if (t->running) + return jiffies - t->expires; + + return 0; +} + +extern __inline__ void br_timer_set(struct br_timer *t, unsigned long x) +{ + t->expires = x; + t->running = 1; +} + +extern __inline__ int br_timer_is_running(struct br_timer *t) +{ + return t->running; +} + +extern __inline__ int br_timer_has_expired(struct br_timer *t, unsigned long to) +{ + return t->running && time_after_eq(jiffies, t->expires + to); +} + + +#endif diff --git a/net/bridge/br_stp.c b/net/bridge/br_stp.c new file mode 100644 index 000000000..a35d03f20 --- /dev/null +++ b/net/bridge/br_stp.c @@ -0,0 +1,465 @@ +/* + * Spanning tree protocol; generic parts + * Linux ethernet bridge + * + * Authors: + * Lennert Buytenhek <buytenh@gnu.org> + * + * $Id: br_stp.c,v 1.1 2000/02/18 16:47:13 davem Exp $ + * + * 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 <linux/kernel.h> +#include <linux/if_bridge.h> +#include <linux/smp_lock.h> +#include <asm/uaccess.h> +#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\n"); + br->topology_change = 1; + br_timer_set(&br->topology_change_timer, jiffies); + } else if (!br->topology_change_detected) { + printk("sending tcn bpdu\n"); + br_transmit_tcn(br); + br_timer_set(&br->tcn_timer, jiffies); + } + + 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 --git a/net/bridge/br_stp_bpdu.c b/net/bridge/br_stp_bpdu.c new file mode 100644 index 000000000..345792f83 --- /dev/null +++ b/net/bridge/br_stp_bpdu.c @@ -0,0 +1,191 @@ +/* + * Spanning tree protocol; BPDU handling + * Linux ethernet bridge + * + * Authors: + * Lennert Buytenhek <buytenh@gnu.org> + * + * $Id: br_stp_bpdu.c,v 1.1 2000/02/18 16:47:13 davem Exp $ + * + * 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 <linux/kernel.h> +#include <linux/if_bridge.h> +#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 net_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->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]); +} + +/* called under bridge lock */ +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); +} + +/* called under bridge lock */ +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}; + +/* called under bridge lock */ +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 --git a/net/bridge/br_stp_if.c b/net/bridge/br_stp_if.c new file mode 100644 index 000000000..bbffc4126 --- /dev/null +++ b/net/bridge/br_stp_if.c @@ -0,0 +1,225 @@ +/* + * Spanning tree protocol; interface code + * Linux ethernet bridge + * + * Authors: + * Lennert Buytenhek <buytenh@gnu.org> + * + * $Id: br_stp_if.c,v 1.1 2000/02/18 16:47:13 davem Exp $ + * + * 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 <linux/kernel.h> +#include <linux/if_bridge.h> +#include <linux/smp_lock.h> +#include <asm/uaccess.h> +#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); +} + +/* 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) +{ + int new_port_id = ((newprio & 0xFF) << 8) | p->port_no; + + 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 --git a/net/bridge/br_stp_timer.c b/net/bridge/br_stp_timer.c new file mode 100644 index 000000000..ca4e6e568 --- /dev/null +++ b/net/bridge/br_stp_timer.c @@ -0,0 +1,184 @@ +/* + * Spanning tree protocol; timer-related code + * Linux ethernet bridge + * + * Authors: + * Lennert Buytenhek <buytenh@gnu.org> + * + * $Id: br_stp_timer.c,v 1.1 2000/02/18 16:47:13 davem Exp $ + * + * 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 <linux/kernel.h> +#include <linux/if_bridge.h> +#include <linux/smp_lock.h> +#include <asm/uaccess.h> +#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 --git a/net/bridge/br_tree.c b/net/bridge/br_tree.c deleted file mode 100644 index 67efa8f1b..000000000 --- a/net/bridge/br_tree.c +++ /dev/null @@ -1,501 +0,0 @@ -/* - * This code is derived from the avl functions in mmap.c - */ - -#include <linux/kernel.h> -#include <linux/errno.h> -#include <linux/string.h> -#include <linux/malloc.h> -#include <linux/skbuff.h> -#include <linux/netdevice.h> - -#include <net/br.h> -#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 <haible@ma2s2.mathematik.uni-karlsruhe.de>. - * Taken from mmap.c, extensively modified by John Hayes - * <hayes@netplumbing.com> - * 98-02 Modified by Jean-Rene Peulve jr.peulve@aix.pacwan.net - * update port number when topology change - * return oldfdb when updating, for broadcast storm checking - * call addr_cmp once per node - */ - -static struct fdb fdb_head; -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 = (heightleft<heightright ? heightright : heightleft) + 1; - if (height == node->fdb_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 --git a/net/bridge/sysctl_net_bridge.c b/net/bridge/sysctl_net_bridge.c deleted file mode 100644 index 6e2f57d65..000000000 --- a/net/bridge/sysctl_net_bridge.c +++ /dev/null @@ -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 <linux/mm.h> -#include <linux/sysctl.h> - -ctl_table bridge_table[] = { - {0} -}; diff --git a/net/core/dev.c b/net/core/dev.c index 00d5caa2a..d2ed4f523 100644 --- a/net/core/dev.c +++ b/net/core/dev.c @@ -81,7 +81,7 @@ #include <net/slhc.h> #include <linux/proc_fs.h> #include <linux/stat.h> -#include <net/br.h> +#include <linux/if_bridge.h> #include <net/dst.h> #include <net/pkt_sched.h> #include <net/profile.h> @@ -190,14 +190,11 @@ void dev_add_pack(struct packet_type *pt) dev_clear_fastroute(pt->dev); } #endif - if(pt->type==htons(ETH_P_ALL)) - { + if (pt->type == htons(ETH_P_ALL)) { netdev_nit++; pt->next=ptype_all; ptype_all=pt; - } - else - { + } else { hash=ntohs(pt->type)&15; pt->next = ptype_base[hash]; ptype_base[hash] = pt; @@ -216,19 +213,16 @@ void dev_remove_pack(struct packet_type *pt) write_lock_bh(&ptype_lock); - if(pt->type==htons(ETH_P_ALL)) - { + if (pt->type == htons(ETH_P_ALL)) { netdev_nit--; pt1=&ptype_all; - } - else + } else { pt1=&ptype_base[ntohs(pt->type)&15]; + } - for(; (*pt1)!=NULL; pt1=&((*pt1)->next)) - { - if(pt==(*pt1)) - { - *pt1=pt->next; + for (; (*pt1) != NULL; pt1 = &((*pt1)->next)) { + if (pt == (*pt1)) { + *pt1 = pt->next; #ifdef CONFIG_NET_FASTROUTE if (pt->data) netdev_fastroute_obstacles--; @@ -357,14 +351,13 @@ int dev_alloc_name(struct net_device *dev, const char *name) { int i; char buf[32]; + /* * If you need over 100 please also fix the algorithm... */ - for(i=0;i<100;i++) - { + for (i = 0; i < 100; i++) { sprintf(buf,name,i); - if(__dev_get_by_name(buf)==NULL) - { + if (__dev_get_by_name(buf) == NULL) { strcpy(dev->name, buf); return i; } @@ -375,16 +368,14 @@ int dev_alloc_name(struct net_device *dev, const char *name) struct net_device *dev_alloc(const char *name, int *err) { struct net_device *dev=kmalloc(sizeof(struct net_device)+16, GFP_KERNEL); - if(dev==NULL) - { - *err=-ENOBUFS; + if (dev == NULL) { + *err = -ENOBUFS; return NULL; } memset(dev, 0, sizeof(struct net_device)); - dev->name=(char *)(dev+1); /* Name string space */ - *err=dev_alloc_name(dev,name); - if(*err<0) - { + dev->name = (char *)(dev + 1); /* Name string space */ + *err = dev_alloc_name(dev, name); + if (*err < 0) { kfree(dev); return NULL; } @@ -408,7 +399,7 @@ void netdev_state_change(struct net_device *dev) void dev_load(const char *name) { - if(!__dev_get_by_name(name) && capable(CAP_SYS_MODULE)) + if (!__dev_get_by_name(name) && capable(CAP_SYS_MODULE)) request_module(name); } @@ -441,6 +432,12 @@ int dev_open(struct net_device *dev) return 0; /* + * Is it even present? + */ + if (!netif_device_present(dev)) + return -ENODEV; + + /* * Call device private open method */ @@ -458,7 +455,7 @@ int dev_open(struct net_device *dev) */ dev->flags |= IFF_UP; - set_bit(LINK_STATE_START, &dev->state); + set_bit(__LINK_STATE_START, &dev->state); /* * Initialize multicasting status @@ -528,11 +525,14 @@ int dev_close(struct net_device *dev) dev_deactivate(dev); - clear_bit(LINK_STATE_START, &dev->state); + clear_bit(__LINK_STATE_START, &dev->state); /* * Call the device specific close. This cannot fail. * Only if device is UP + * + * We allow it to be called even after a DETACH hot-plug + * event. */ if (dev->stop) @@ -673,7 +673,7 @@ int dev_queue_xmit(struct sk_buff *skb) spin_lock(&dev->xmit_lock); dev->xmit_lock_owner = cpu; - if (!test_bit(LINK_STATE_XOFF, &dev->state)) { + if (!netif_queue_stopped(dev)) { if (netdev_nit) dev_queue_xmit_nit(skb,dev); @@ -781,7 +781,7 @@ void netif_rx(struct sk_buff *skb) struct softnet_data *queue; unsigned long flags; - if(skb->stamp.tv_sec==0) + if (skb->stamp.tv_sec == 0) get_fast_time(&skb->stamp); /* The code is rearranged so that the path is the most @@ -833,37 +833,6 @@ drop: 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; -} -#endif - /* Deliver skb to an old protocol, which is not threaded well or which do not understand shared skbs. */ @@ -911,15 +880,14 @@ static __inline__ void skb_bond(struct sk_buff *skb) static void net_tx_action(struct softirq_action *h) { int cpu = smp_processor_id(); - unsigned long flags; if (softnet_data[cpu].completion_queue) { struct sk_buff *clist; - local_irq_save(flags); + local_irq_disable(); clist = softnet_data[cpu].completion_queue; softnet_data[cpu].completion_queue = NULL; - local_irq_restore(flags); + local_irq_enable(); while (clist != NULL) { struct sk_buff *skb = clist; @@ -933,16 +901,16 @@ static void net_tx_action(struct softirq_action *h) if (softnet_data[cpu].output_queue) { struct net_device *head; - local_irq_save(flags); + local_irq_disable(); head = softnet_data[cpu].output_queue; softnet_data[cpu].output_queue = NULL; - local_irq_restore(flags); + local_irq_enable(); while (head != NULL) { struct net_device *dev = head; head = head->next_sched; - clear_bit(LINK_STATE_SCHED, &dev->state); + clear_bit(__LINK_STATE_SCHED, &dev->state); if (spin_trylock(&dev->queue_lock)) { qdisc_run(dev); @@ -954,6 +922,32 @@ static void net_tx_action(struct softirq_action *h) } } +void net_call_rx_atomic(void (*fn)(void)) +{ + write_lock_bh(&ptype_lock); + fn(); + write_unlock_bh(&ptype_lock); +} + +#if defined(CONFIG_BRIDGE) || defined(CONFIG_BRIDGE_MODULE) +void (*br_handle_frame_hook)(struct sk_buff *skb) = NULL; +#endif + +#define HANDLE_BRIDGE(SKB, PT_PREV) \ +do { \ + if ((SKB)->dev->br_port != NULL && \ + br_handle_frame_hook != NULL) { \ + if (PT_PREV) \ + if (!(PT_PREV->data)) \ + deliver_to_old_ones(PT_PREV, SKB, 1); \ + else \ + pt_prev->func(SKB, SKB->dev, PT_PREV); \ + \ + br_handle_frame_hook(SKB); \ + continue; \ + } \ +} while(0) + static void net_rx_action(struct softirq_action *h) { int this_cpu = smp_processor_id(); @@ -986,9 +980,7 @@ static void net_rx_action(struct softirq_action *h) { struct packet_type *ptype, *pt_prev; unsigned short type = skb->protocol; -#ifdef CONFIG_BRIDGE - handle_bridge(skb, type); -#endif + pt_prev = NULL; for (ptype = ptype_all; ptype; ptype = ptype->next) { if (!ptype->dev || ptype->dev == skb->dev) { @@ -1005,6 +997,11 @@ static void net_rx_action(struct softirq_action *h) pt_prev = ptype; } } + +#if defined(CONFIG_BRIDGE) || defined(CONFIG_BRIDGE_MODULE) + HANDLE_BRIDGE(skb, pt_prev); +#endif + for (ptype=ptype_base[ntohs(type)&15];ptype;ptype=ptype->next) { if (ptype->type == type && (!ptype->dev || ptype->dev == skb->dev)) { @@ -1021,6 +1018,7 @@ static void net_rx_action(struct softirq_action *h) pt_prev = ptype; } } + if (pt_prev) { if (!pt_prev->data) deliver_to_old_ones(pt_prev, skb, 1); @@ -1213,11 +1211,10 @@ static int sprintf_stats(char *buffer, struct net_device *dev) static int dev_get_info(char *buffer, char **start, off_t offset, int length) { - int len=0; - off_t begin=0; - off_t pos=0; + int len = 0; + off_t begin = 0; + off_t pos = 0; int size; - struct net_device *dev; @@ -1225,31 +1222,31 @@ static int dev_get_info(char *buffer, char **start, off_t offset, int length) "Inter-| Receive | Transmit\n" " face |bytes packets errs drop fifo frame compressed multicast|bytes packets errs drop fifo colls carrier compressed\n"); - pos+=size; - len+=size; + pos += size; + len += size; read_lock(&dev_base_lock); for (dev = dev_base; dev != NULL; dev = dev->next) { size = sprintf_stats(buffer+len, dev); - len+=size; - pos=begin+len; + len += size; + pos = begin + len; - if(pos<offset) { - len=0; - begin=pos; + if (pos < offset) { + len = 0; + begin = pos; } - if(pos>offset+length) + if (pos > offset + length) break; } read_unlock(&dev_base_lock); - *start=buffer+(offset-begin); /* Start of wanted data */ - len-=(offset-begin); /* Start slop */ - if(len>length) - len=length; /* Ending slop */ - if (len<0) - len=0; + *start = buffer + (offset - begin); /* Start of wanted data */ + len -= (offset - begin); /* Start slop */ + if (len > length) + len = length; /* Ending slop */ + if (len < 0) + len = 0; return len; } @@ -1281,7 +1278,7 @@ static int dev_proc_stats(char *buffer, char **start, off_t offset, if (len > length) len = length; - if(len < 0) + if (len < 0) len = 0; *start = buffer + offset; @@ -1308,8 +1305,7 @@ static int sprintf_wireless_stats(char *buffer, struct net_device *dev) (struct iw_statistics *) NULL); int size; - if(stats != (struct iw_statistics *) NULL) - { + if (stats != (struct iw_statistics *) NULL) { size = sprintf(buffer, "%6s: %04x %3d%c %3d%c %3d%c %6d %6d %6d\n", dev->name, @@ -1350,30 +1346,30 @@ static int dev_get_wireless_info(char * buffer, char **start, off_t offset, " face | tus | link level noise | nwid crypt misc\n" ); - pos+=size; - len+=size; + pos += size; + len += size; read_lock(&dev_base_lock); - for(dev = dev_base; dev != NULL; dev = dev->next) { - size = sprintf_wireless_stats(buffer+len, dev); - len+=size; - pos=begin+len; - - if(pos < offset) { - len=0; - begin=pos; + for (dev = dev_base; dev != NULL; dev = dev->next) { + size = sprintf_wireless_stats(buffer + len, dev); + len += size; + pos = begin + len; + + if (pos < offset) { + len = 0; + begin = pos; } - if(pos > offset + length) + if (pos > offset + length) break; } read_unlock(&dev_base_lock); *start = buffer + (offset - begin); /* Start of wanted data */ len -= (offset - begin); /* Start slop */ - if(len > length) - len = length; /* Ending slop */ - if (len<0) - len=0; + if (len > length) + len = length; /* Ending slop */ + if (len < 0) + len = 0; return len; } @@ -1517,7 +1513,7 @@ static int dev_ifsioc(struct ifreq *ifr, unsigned int cmd) case SIOCGIFFLAGS: /* Get interface flags */ ifr->ifr_flags = (dev->flags&~(IFF_PROMISC|IFF_ALLMULTI|IFF_RUNNING)) |(dev->gflags&(IFF_PROMISC|IFF_ALLMULTI)); - if (!test_bit(LINK_STATE_DOWN, &dev->state)) + if (netif_running(dev)) ifr->ifr_flags |= IFF_RUNNING; return 0; @@ -1546,6 +1542,9 @@ static int dev_ifsioc(struct ifreq *ifr, unsigned int cmd) if (ifr->ifr_mtu<0) return -EINVAL; + if (!netif_device_present(dev)) + return -ENODEV; + if (dev->change_mtu) err = dev->change_mtu(dev, ifr->ifr_mtu); else { @@ -1562,17 +1561,19 @@ static int dev_ifsioc(struct ifreq *ifr, unsigned int cmd) return 0; case SIOCSIFHWADDR: - if(dev->set_mac_address==NULL) + if (dev->set_mac_address == NULL) return -EOPNOTSUPP; - if(ifr->ifr_hwaddr.sa_family!=dev->type) + if (ifr->ifr_hwaddr.sa_family!=dev->type) return -EINVAL; - err=dev->set_mac_address(dev,&ifr->ifr_hwaddr); + if (!netif_device_present(dev)) + return -ENODEV; + err = dev->set_mac_address(dev, &ifr->ifr_hwaddr); if (!err) notifier_call_chain(&netdev_chain, NETDEV_CHANGEADDR, dev); return err; case SIOCSIFHWBROADCAST: - if(ifr->ifr_hwaddr.sa_family!=dev->type) + if (ifr->ifr_hwaddr.sa_family!=dev->type) return -EINVAL; memcpy(dev->broadcast, ifr->ifr_hwaddr.sa_data, MAX_ADDR_LEN); notifier_call_chain(&netdev_chain, NETDEV_CHANGEADDR, dev); @@ -1588,21 +1589,28 @@ static int dev_ifsioc(struct ifreq *ifr, unsigned int cmd) return 0; case SIOCSIFMAP: - if (dev->set_config) + if (dev->set_config) { + if (!netif_device_present(dev)) + return -ENODEV; return dev->set_config(dev,&ifr->ifr_map); + } return -EOPNOTSUPP; case SIOCADDMULTI: - if(dev->set_multicast_list==NULL || - ifr->ifr_hwaddr.sa_family!=AF_UNSPEC) + if (dev->set_multicast_list == NULL || + ifr->ifr_hwaddr.sa_family != AF_UNSPEC) return -EINVAL; + if (!netif_device_present(dev)) + return -ENODEV; dev_mc_add(dev,ifr->ifr_hwaddr.sa_data, dev->addr_len, 1); return 0; case SIOCDELMULTI: - if(dev->set_multicast_list==NULL || - ifr->ifr_hwaddr.sa_family!=AF_UNSPEC) + if (dev->set_multicast_list == NULL || + ifr->ifr_hwaddr.sa_family!=AF_UNSPEC) return -EINVAL; + if (!netif_device_present(dev)) + return -ENODEV; dev_mc_delete(dev,ifr->ifr_hwaddr.sa_data,dev->addr_len, 1); return 0; @@ -1615,7 +1623,7 @@ static int dev_ifsioc(struct ifreq *ifr, unsigned int cmd) return 0; case SIOCSIFTXQLEN: - if(ifr->ifr_qlen<0) + if (ifr->ifr_qlen<0) return -EINVAL; dev->tx_queue_len = ifr->ifr_qlen; return 0; @@ -1635,17 +1643,23 @@ static int dev_ifsioc(struct ifreq *ifr, unsigned int cmd) */ default: - if(cmd >= SIOCDEVPRIVATE && - cmd <= SIOCDEVPRIVATE + 15) { - if (dev->do_ioctl) + if (cmd >= SIOCDEVPRIVATE && + cmd <= SIOCDEVPRIVATE + 15) { + if (dev->do_ioctl) { + if (!netif_device_present(dev)) + return -ENODEV; return dev->do_ioctl(dev, ifr, cmd); + } return -EOPNOTSUPP; } #ifdef WIRELESS_EXT - if(cmd >= SIOCIWFIRST && cmd <= SIOCIWLAST) { - if (dev->do_ioctl) + if (cmd >= SIOCIWFIRST && cmd <= SIOCIWLAST) { + if (dev->do_ioctl) { + if (!netif_device_present(dev)) + return -ENODEV; return dev->do_ioctl(dev, ifr, cmd); + } return -EOPNOTSUPP; } #endif /* WIRELESS_EXT */ @@ -1869,6 +1883,13 @@ int register_netdevice(struct net_device *dev) if (dev->rebuild_header == NULL) dev->rebuild_header = default_rebuild_header; + /* + * Default initial state at registry is that the + * device is present. + */ + + set_bit(__LINK_STATE_PRESENT, &dev->state); + dev->next = NULL; dev_init_scheduler(dev); write_lock_bh(&dev_base_lock); @@ -2049,15 +2070,6 @@ int __init net_dev_init(void) queue->completion_queue = NULL; } - /* - * The bridge has to be up before the devices - */ - -#ifdef CONFIG_BRIDGE - br_init(); -#endif - - #ifdef CONFIG_NET_PROFILE net_profile_init(); NET_PROFILE_REGISTER(dev_queue_xmit); @@ -2125,22 +2137,11 @@ int __init net_dev_init(void) dst_init(); dev_mcast_init(); -#ifdef CONFIG_BRIDGE - /* - * Register any statically linked ethernet devices with the bridge - */ - br_spacedevice_register(); -#endif - /* * Initialise network devices */ net_device_init(); -#ifdef CONFIG_IP_PNP - ip_auto_config(); -#endif - return 0; } diff --git a/net/core/dev_mcast.c b/net/core/dev_mcast.c index 2facf1bc1..0dcedb142 100644 --- a/net/core/dev_mcast.c +++ b/net/core/dev_mcast.c @@ -71,17 +71,20 @@ static rwlock_t dev_mc_lock = RW_LOCK_UNLOCKED; void dev_mc_upload(struct net_device *dev) { /* Don't do anything till we up the interface - [dev_open will call this function so the list will - stay sane] */ + * [dev_open will call this function so the list will + * stay sane] + */ - if(!(dev->flags&IFF_UP)) + if (!(dev->flags&IFF_UP)) return; /* - * Devices with no set multicast don't get set + * Devices with no set multicast or which have been + * detached don't get set. */ - if(dev->set_multicast_list==NULL) + if (dev->set_multicast_list == NULL || + !netif_device_present(dev)) return; read_lock_bh(&dev_mc_lock); @@ -103,19 +106,20 @@ int dev_mc_delete(struct net_device *dev, void *addr, int alen, int glbl) struct dev_mc_list *dmi, **dmip; write_lock_bh(&dev_mc_lock); - for (dmip=&dev->mc_list; (dmi=*dmip)!=NULL; dmip=&dmi->next) { + for (dmip = &dev->mc_list; (dmi = *dmip) != NULL; dmip = &dmi->next) { /* * Find the entry we want to delete. The device could * have variable length entries so check these too. */ - if (memcmp(dmi->dmi_addr,addr,dmi->dmi_addrlen)==0 && alen==dmi->dmi_addrlen) { + if (memcmp(dmi->dmi_addr, addr, dmi->dmi_addrlen) == 0 && + alen == dmi->dmi_addrlen) { if (glbl) { int old_glbl = dmi->dmi_gusers; dmi->dmi_gusers = 0; if (old_glbl == 0) break; } - if(--dmi->dmi_users) + if (--dmi->dmi_users) goto done; /* @@ -125,7 +129,7 @@ int dev_mc_delete(struct net_device *dev, void *addr, int alen, int glbl) dev->mc_count--; write_unlock_bh(&dev_mc_lock); - kfree_s(dmi,sizeof(*dmi)); + kfree_s(dmi, sizeof(*dmi)); /* * We have altered the list, so the card @@ -153,8 +157,9 @@ int dev_mc_add(struct net_device *dev, void *addr, int alen, int glbl) dmi1 = (struct dev_mc_list *)kmalloc(sizeof(*dmi), GFP_ATOMIC); write_lock_bh(&dev_mc_lock); - for(dmi=dev->mc_list; dmi!=NULL; dmi=dmi->next) { - if (memcmp(dmi->dmi_addr,addr,dmi->dmi_addrlen)==0 && dmi->dmi_addrlen==alen) { + for (dmi = dev->mc_list; dmi != NULL; dmi = dmi->next) { + if (memcmp(dmi->dmi_addr, addr, dmi->dmi_addrlen) == 0 && + dmi->dmi_addrlen == alen) { if (glbl) { int old_glbl = dmi->dmi_gusers; dmi->dmi_gusers = 1; @@ -166,16 +171,16 @@ int dev_mc_add(struct net_device *dev, void *addr, int alen, int glbl) } } - if ((dmi=dmi1)==NULL) { + if ((dmi = dmi1) == NULL) { write_unlock_bh(&dev_mc_lock); return -ENOMEM; } memcpy(dmi->dmi_addr, addr, alen); - dmi->dmi_addrlen=alen; - dmi->next=dev->mc_list; - dmi->dmi_users=1; - dmi->dmi_gusers=glbl ? 1 : 0; - dev->mc_list=dmi; + dmi->dmi_addrlen = alen; + dmi->next = dev->mc_list; + dmi->dmi_users = 1; + dmi->dmi_gusers = glbl ? 1 : 0; + dev->mc_list = dmi; dev->mc_count++; write_unlock_bh(&dev_mc_lock); dev_mc_upload(dev); @@ -195,14 +200,14 @@ done: void dev_mc_discard(struct net_device *dev) { write_lock_bh(&dev_mc_lock); - while (dev->mc_list!=NULL) { - struct dev_mc_list *tmp=dev->mc_list; - dev->mc_list=tmp->next; + while (dev->mc_list != NULL) { + struct dev_mc_list *tmp = dev->mc_list; + dev->mc_list = tmp->next; if (tmp->dmi_users > tmp->dmi_gusers) printk("dev_mc_discard: multicast leakage! dmi_users=%d\n", tmp->dmi_users); - kfree_s(tmp,sizeof(*tmp)); + kfree_s(tmp, sizeof(*tmp)); } - dev->mc_count=0; + dev->mc_count = 0; write_unlock_bh(&dev_mc_lock); } @@ -210,9 +215,9 @@ void dev_mc_discard(struct net_device *dev) static int dev_mc_read_proc(char *buffer, char **start, off_t offset, int length, int *eof, void *data) { - off_t pos=0, begin=0; + off_t pos = 0, begin = 0; struct dev_mc_list *m; - int len=0; + int len = 0; struct net_device *dev; read_lock(&dev_base_lock); @@ -221,20 +226,20 @@ static int dev_mc_read_proc(char *buffer, char **start, off_t offset, for (m = dev->mc_list; m; m = m->next) { int i; - len += sprintf(buffer+len,"%-4d %-15s %-5d %-5d ", dev->ifindex, dev->name, - m->dmi_users, m->dmi_gusers); + len += sprintf(buffer+len,"%-4d %-15s %-5d %-5d ", dev->ifindex, + dev->name, m->dmi_users, m->dmi_gusers); - for (i=0; i<m->dmi_addrlen; i++) + for (i = 0; i < m->dmi_addrlen; i++) len += sprintf(buffer+len, "%02x", m->dmi_addr[i]); - len+=sprintf(buffer+len, "\n"); + len += sprintf(buffer+len, "\n"); - pos=begin+len; + pos = begin + len; if (pos < offset) { - len=0; - begin=pos; + len = 0; + begin = pos; } - if (pos > offset+length) { + if (pos > offset + length) { read_unlock_bh(&dev_mc_lock); goto done; } @@ -245,12 +250,12 @@ static int dev_mc_read_proc(char *buffer, char **start, off_t offset, done: read_unlock(&dev_base_lock); - *start=buffer+(offset-begin); - len-=(offset-begin); - if(len>length) - len=length; - if(len<0) - len=0; + *start = buffer + (offset - begin); + len -= (offset - begin); + if (len > length) + len = length; + if (len < 0) + len = 0; return len; } #endif diff --git a/net/core/profile.c b/net/core/profile.c index 8b2616c50..0408447cb 100644 --- a/net/core/profile.c +++ b/net/core/profile.c @@ -271,12 +271,6 @@ int __init net_profile_init(void) register_netdevice(&whitehole_dev); printk("Evaluating net profiler cost ..."); -#if CPU == 586 || CPU == 686 - if (!(boot_cpu_data.x86_capability & X86_FEATURE_TSC)) { - printk(KERN_ERR "Sorry, your CPU does not support TSC. Net profiler disabled.\n"); - return -1; - } -#endif #ifdef __alpha__ alpha_tick(0); #endif diff --git a/net/core/rtnetlink.c b/net/core/rtnetlink.c index 9cdc290bf..c549162a9 100644 --- a/net/core/rtnetlink.c +++ b/net/core/rtnetlink.c @@ -171,7 +171,7 @@ static int rtnetlink_fill_ifinfo(struct sk_buff *skb, struct net_device *dev, r->ifi_flags = dev->flags; r->ifi_change = change; - if (test_bit(LINK_STATE_DOWN, &dev->state)) + if (! netif_running(dev)) r->ifi_flags &= ~IFF_RUNNING; else r->ifi_flags |= IFF_RUNNING; diff --git a/net/core/skbuff.c b/net/core/skbuff.c index 95e4d8e17..30aa1f6bb 100644 --- a/net/core/skbuff.c +++ b/net/core/skbuff.c @@ -4,7 +4,7 @@ * Authors: Alan Cox <iiitac@pyr.swan.ac.uk> * Florian La Roche <rzsfl@rz.uni-sb.de> * - * Version: $Id: skbuff.c,v 1.66 2000/02/09 21:11:30 davem Exp $ + * Version: $Id: skbuff.c,v 1.68 2000/02/18 16:47:18 davem Exp $ * * Fixes: * Alan Cox : Fixed the worst of the load balancer bugs. @@ -49,6 +49,7 @@ #include <linux/string.h> #include <linux/skbuff.h> #include <linux/slab.h> +#include <linux/cache.h> #include <linux/init.h> #include <net/ip.h> @@ -193,7 +194,6 @@ static inline void skb_headerinit(void *p, kmem_cache_t *cache, 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 --git a/net/decnet/af_decnet.c b/net/decnet/af_decnet.c index a5237a712..1b16e7734 100644 --- a/net/decnet/af_decnet.c +++ b/net/decnet/af_decnet.c @@ -2080,8 +2080,8 @@ void __init decnet_proto_init(struct net_proto *pro) #ifdef CONFIG_DECNET_RAW proc_net_create("decnet_raw", 0, dn_raw_get_info); #endif - dn_dev_init(); dn_neigh_init(); + dn_dev_init(); dn_route_init(); #ifdef CONFIG_DECNET_ROUTER @@ -2149,8 +2149,8 @@ void __exit cleanup_module(void) unregister_netdevice_notifier(&dn_dev_notifier); dn_route_cleanup(); - dn_neigh_cleanup(); dn_dev_cleanup(); + dn_neigh_cleanup(); #ifdef CONFIG_DECNET_ROUTER dn_fib_cleanup(); diff --git a/net/ipv4/af_inet.c b/net/ipv4/af_inet.c index 969fee200..b7512a1c9 100644 --- a/net/ipv4/af_inet.c +++ b/net/ipv4/af_inet.c @@ -5,7 +5,7 @@ * * PF_INET protocol family socket handler. * - * Version: $Id: af_inet.c,v 1.106 2000/02/04 21:04:06 davem Exp $ + * Version: $Id: af_inet.c,v 1.107 2000/02/18 16:47:20 davem Exp $ * * Authors: Ross Biro, <bir7@leland.Stanford.Edu> * Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG> @@ -103,9 +103,7 @@ #ifdef CONFIG_IP_MROUTE #include <linux/mroute.h> #endif -#ifdef CONFIG_BRIDGE -#include <net/br.h> -#endif +#include <linux/if_bridge.h> #ifdef CONFIG_KMOD #include <linux/kmod.h> #endif @@ -137,6 +135,8 @@ extern int dlci_ioctl(unsigned int, void*); int (*dlci_ioctl_hook)(unsigned int, void *) = NULL; #endif +int (*br_ioctl_hook)(unsigned long) = NULL; + /* New destruction routine */ void inet_sock_destruct(struct sock *sk) @@ -837,14 +837,14 @@ static int inet_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg) return(devinet_ioctl(cmd,(void *) arg)); case SIOCGIFBR: case SIOCSIFBR: -#ifdef CONFIG_BRIDGE - lock_kernel(); - err = br_ioctl(cmd,(void *) arg); - unlock_kernel(); - return err; -#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 SIOCADDDLCI: case SIOCDELDLCI: diff --git a/net/ipv4/ipconfig.c b/net/ipv4/ipconfig.c index 82e443d4d..bcdd71354 100644 --- a/net/ipv4/ipconfig.c +++ b/net/ipv4/ipconfig.c @@ -821,7 +821,7 @@ static int __init ic_dynamic(void) * IP Autoconfig dispatcher. */ -int __init ip_auto_config(void) +static int __init ip_auto_config(void) { if (!ic_enable) return 0; @@ -879,6 +879,9 @@ int __init ip_auto_config(void) return 0; } +module_init(ip_auto_config); + + /* * Decode any IP configuration options in the "ip=" or "nfsaddrs=" kernel * command line parameter. It consists of option fields separated by colons in diff --git a/net/ipv4/tcp_ipv4.c b/net/ipv4/tcp_ipv4.c index 470f47e7e..25a71d5f0 100644 --- a/net/ipv4/tcp_ipv4.c +++ b/net/ipv4/tcp_ipv4.c @@ -5,7 +5,7 @@ * * Implementation of the Transmission Control Protocol(TCP). * - * Version: $Id: tcp_ipv4.c,v 1.199 2000/02/08 21:27:17 davem Exp $ + * Version: $Id: tcp_ipv4.c,v 1.200 2000/02/11 22:27:26 davem Exp $ * * IPv4 specific functions * @@ -51,6 +51,7 @@ #include <linux/types.h> #include <linux/fcntl.h> #include <linux/random.h> +#include <linux/cache.h> #include <linux/init.h> #include <net/icmp.h> diff --git a/net/ipv4/tcp_output.c b/net/ipv4/tcp_output.c index f3f1d0bcf..37ab925bf 100644 --- a/net/ipv4/tcp_output.c +++ b/net/ipv4/tcp_output.c @@ -857,11 +857,15 @@ void tcp_send_fin(struct sock *sk) } } else { /* Socket is locked, keep trying until memory is available. */ - do { + for (;;) { skb = sock_wmalloc(sk, MAX_TCP_HEADER + 15, 1, GFP_KERNEL); - } while (skb == NULL); + if (skb) + break; + current->policy |= SCHED_YIELD; + schedule(); + } /* Reserve space for headers and prepare control bits. */ skb_reserve(skb, MAX_TCP_HEADER); diff --git a/net/ipv4/tcp_timer.c b/net/ipv4/tcp_timer.c index 33eea733d..6b3695c3e 100644 --- a/net/ipv4/tcp_timer.c +++ b/net/ipv4/tcp_timer.c @@ -5,7 +5,7 @@ * * Implementation of the Transmission Control Protocol(TCP). * - * Version: $Id: tcp_timer.c,v 1.73 2000/02/09 11:16:42 davem Exp $ + * Version: $Id: tcp_timer.c,v 1.74 2000/02/14 20:56:30 davem Exp $ * * Authors: Ross Biro, <bir7@leland.Stanford.Edu> * Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG> @@ -659,6 +659,7 @@ static void tcp_synack_timer(struct sock *sk) if (req->retrans == 0) lopt->qlen_young--; tcp_openreq_free(req); + continue; } reqp = &req->dl_next; } diff --git a/net/ipv6/af_inet6.c b/net/ipv6/af_inet6.c index 06d620952..09b9c1a11 100644 --- a/net/ipv6/af_inet6.c +++ b/net/ipv6/af_inet6.c @@ -7,7 +7,7 @@ * * Adapted from linux/net/ipv4/af_inet.c * - * $Id: af_inet6.c,v 1.53 2000/02/04 21:04:08 davem Exp $ + * $Id: af_inet6.c,v 1.54 2000/02/12 23:34:45 davem Exp $ * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -451,7 +451,7 @@ struct proto_ops inet6_dgram_ops = { inet6_bind, inet_dgram_connect, /* ok */ sock_no_socketpair, /* a do nothing */ - inet_accept, /* ok */ + sock_no_accept, /* a do nothing */ inet6_getname, datagram_poll, /* ok */ inet6_ioctl, /* must change */ diff --git a/net/irda/irda_device.c b/net/irda/irda_device.c index d1d89b1e4..94345259d 100644 --- a/net/irda/irda_device.c +++ b/net/irda/irda_device.c @@ -426,12 +426,8 @@ int irda_device_setup(struct net_device *dev) memset(dev->broadcast, 0xff, 4); dev->mtu = 2048; - dev->tbusy = 1; - dev_init_buffers(dev); - dev->flags = IFF_NOARP; - return 0; } diff --git a/net/irda/irlan/irlan_client.c b/net/irda/irlan/irlan_client.c index 355bae955..7e17cfe8b 100644 --- a/net/irda/irlan/irlan_client.c +++ b/net/irda/irlan/irlan_client.c @@ -126,7 +126,7 @@ void irlan_client_wakeup(struct irlan_cb *self, __u32 saddr, __u32 daddr) * is up, that means that the "user" really wants to connect. If not * we notify the user about the possibility of an IrLAN connection */ - if (self->dev.start) { + if (netif_running(&self->dev)) { /* Open TSAPs */ irlan_client_open_ctrl_tsap(self); irlan_open_data_tsap(self); diff --git a/net/irda/irlan/irlan_common.c b/net/irda/irlan/irlan_common.c index 9d276e0a1..a9c2cb90f 100644 --- a/net/irda/irlan/irlan_common.c +++ b/net/irda/irlan/irlan_common.c @@ -120,7 +120,7 @@ void irlan_watchdog_timer_expired(void *data) ASSERT(self->magic == IRLAN_MAGIC, return;); /* Check if device still configured */ - if (self->dev.start) { + if (netif_running(&self->dev)) { IRDA_DEBUG(0, __FUNCTION__ "(), notifying irmanager to stop irlan!\n"); mgr_event.event = EVENT_IRLAN_STOP; @@ -363,7 +363,7 @@ void irlan_close(struct irlan_cb *self) ASSERT(self->magic == IRLAN_MAGIC, return;); /* Check if device is still configured */ - if (self->dev.start) { + if (netif_running(&self->dev)) { IRDA_DEBUG(0, __FUNCTION__ "(), Device still configured, closing later!\n"); @@ -420,7 +420,7 @@ void irlan_connect_indication(void *instance, void *sap, struct qos_info *qos, irlan_open_unicast_addr(self); } /* Ready to transfer Ethernet frames (at last) */ - self->dev.tbusy = 0; + netif_start_queue(&self->dev); } void irlan_connect_confirm(void *instance, void *sap, struct qos_info *qos, @@ -454,8 +454,7 @@ void irlan_connect_confirm(void *instance, void *sap, struct qos_info *qos, irlan_set_multicast_filter(self, TRUE); /* Ready to transfer Ethernet frames */ - self->dev.tbusy = 0; - + netif_start_queue(&self->dev); irlan_eth_send_gratuitous_arp(&self->dev); } @@ -1196,7 +1195,7 @@ static int irlan_proc_read(char *buf, char **start, off_t offset, int len) buf+len); len += sprintf(buf+len, "tx busy: %s\n", - self->dev.tbusy ? "TRUE" : "FALSE"); + netif_queue_stopped(&self->dev) ? "TRUE" : "FALSE"); len += sprintf(buf+len, "\n"); } diff --git a/net/irda/irlan/irlan_eth.c b/net/irda/irlan/irlan_eth.c index c2037ad74..5ee81ec03 100644 --- a/net/irda/irlan/irlan_eth.c +++ b/net/irda/irlan/irlan_eth.c @@ -62,8 +62,6 @@ int irlan_eth_init(struct net_device *dev) dev->get_stats = irlan_eth_get_stats; dev->set_multicast_list = irlan_eth_set_multicast_list; - dev->tbusy = 1; - ether_setup(dev); /* @@ -124,9 +122,7 @@ int irlan_eth_open(struct net_device *dev) ASSERT(self != NULL, return -1;); /* Ready to play! */ -/* dev->tbusy = 0; */ /* Wait until data link is ready */ - dev->interrupt = 0; - dev->start = 1; +/* netif_start_queue(dev) */ /* Wait until data link is ready */ self->notify_irmanager = TRUE; @@ -153,9 +149,8 @@ int irlan_eth_close(struct net_device *dev) IRDA_DEBUG(2, __FUNCTION__ "()\n"); /* Stop device */ - dev->tbusy = 1; - dev->start = 0; - + netif_stop_queue(dev); + irlan_mod_dec_use_count(); irlan_close_data_channel(self); @@ -192,10 +187,6 @@ int irlan_eth_xmit(struct sk_buff *skb, struct net_device *dev) ASSERT(self != NULL, return 0;); ASSERT(self->magic == IRLAN_MAGIC, return 0;); - /* Check if IrTTP can accept more frames */ - if (dev->tbusy) - return -EBUSY; - /* skb headroom large enough to contain all IrDA-headers? */ if ((skb_headroom(skb) < self->max_header_size) || (skb_shared(skb))) { struct sk_buff *new_skb = @@ -279,7 +270,7 @@ int irlan_eth_receive(void *instance, void *sap, struct sk_buff *skb) * Function irlan_eth_flow (status) * * Do flow control between IP/Ethernet and IrLAN/IrTTP. This is done by - * controlling the dev->tbusy variable. + * controlling the queue stop/start. */ void irlan_eth_flow_indication(void *instance, void *sap, LOCAL_FLOW flow) { @@ -297,15 +288,13 @@ void irlan_eth_flow_indication(void *instance, void *sap, LOCAL_FLOW flow) switch (flow) { case FLOW_STOP: - dev->tbusy = 1; + netif_stop_queue(dev); break; case FLOW_START: default: /* Tell upper layers that its time to transmit frames again */ - dev->tbusy = 0; - /* Schedule network layer */ - mark_bh(NET_BH); + netif_start_queue(dev); break; } } diff --git a/net/khttpd/sockets.c b/net/khttpd/sockets.c index 0d575abdf..60e66fdf8 100644 --- a/net/khttpd/sockets.c +++ b/net/khttpd/sockets.c @@ -71,8 +71,6 @@ int StartListening(const int Port) /* Grrr... setsockopt() does this. */ sock->sk->reuse = 1; - /* Wow!!! */ - sock->sk->linger = 1; /* Now, start listening on the socket */ diff --git a/net/netlink/netlink_dev.c b/net/netlink/netlink_dev.c index 31b0bd890..d63e1f678 100644 --- a/net/netlink/netlink_dev.c +++ b/net/netlink/netlink_dev.c @@ -25,6 +25,7 @@ #include <linux/netlink.h> #include <linux/poll.h> #include <linux/init.h> +#include <linux/devfs_fs_kernel.h> #include <asm/system.h> #include <asm/uaccess.h> @@ -175,12 +176,35 @@ static struct file_operations netlink_fops = { release: netlink_release, }; +static devfs_handle_t devfs_handle = NULL; + +static void __init make_devfs_entries (const char *name, int minor) +{ + devfs_register (devfs_handle, name, 0, DEVFS_FL_DEFAULT, + NETLINK_MAJOR, minor, + S_IFCHR | S_IRUSR | S_IWUSR, 0, 0, + &netlink_fops, NULL); +} + int __init init_netlink(void) { - if (register_chrdev(NETLINK_MAJOR,"netlink", &netlink_fops)) { + if (devfs_register_chrdev(NETLINK_MAJOR,"netlink", &netlink_fops)) { printk(KERN_ERR "netlink: unable to get major %d\n", NETLINK_MAJOR); return -EIO; } + devfs_handle = devfs_mk_dir (NULL, "netlink", 7, NULL); + /* Someone tell me the official names for the uppercase ones */ + make_devfs_entries ("route", 0); + make_devfs_entries ("skip", 1); + make_devfs_entries ("USERSOCK", 2); + make_devfs_entries ("fwmonitor", 3); + make_devfs_entries ("ARPD", 8); + make_devfs_entries ("ROUTE6", 11); + make_devfs_entries ("IP6_FW", 13); + devfs_register_series (devfs_handle, "tap%u", 16, DEVFS_FL_DEFAULT, + NETLINK_MAJOR, 16, + S_IFCHR | S_IRUSR | S_IWUSR, 0, 0, + &netlink_fops, NULL); return 0; } @@ -194,7 +218,8 @@ int init_module(void) void cleanup_module(void) { - unregister_chrdev(NET_MAJOR,"netlink"); + devfs_unregister (devfs_handle); + devfs_unregister_chrdev(NETLINK_MAJOR, "netlink"); } #endif diff --git a/net/netrom/nr_dev.c b/net/netrom/nr_dev.c index 0e93f6d26..1625d1c90 100644 --- a/net/netrom/nr_dev.c +++ b/net/netrom/nr_dev.c @@ -62,7 +62,7 @@ int nr_rx_ip(struct sk_buff *skb, struct net_device *dev) { struct net_device_stats *stats = (struct net_device_stats *)dev->priv; - if (!dev->start) { + if (!netif_running(dev)) { stats->rx_errors++; return 0; } @@ -181,60 +181,25 @@ static int nr_set_mac_address(struct net_device *dev, void *addr) static int nr_open(struct net_device *dev) { - dev->tbusy = 0; - dev->start = 1; - MOD_INC_USE_COUNT; - + netif_start_queue(dev); ax25_listen_register((ax25_address *)dev->dev_addr, NULL); - return 0; } static int nr_close(struct net_device *dev) { - dev->tbusy = 1; - dev->start = 0; - + netif_stop_queue(dev); ax25_listen_release((ax25_address *)dev->dev_addr, NULL); - MOD_DEC_USE_COUNT; - return 0; } static int nr_xmit(struct sk_buff *skb, struct net_device *dev) { struct net_device_stats *stats = (struct net_device_stats *)dev->priv; - - if (skb == NULL || dev == NULL) - return 0; - - if (!dev->start) { - printk(KERN_ERR "NET/ROM: nr_xmit - called when iface is down\n"); - return 1; - } - - cli(); - - if (dev->tbusy != 0) { - sti(); - stats->tx_errors++; - return 1; - } - - dev->tbusy = 1; - - sti(); - - kfree_skb(skb); - + dev_kfree_skb(skb); stats->tx_errors++; - - dev->tbusy = 0; - - mark_bh(NET_BH); - return 0; } @@ -246,7 +211,6 @@ static struct net_device_stats *nr_get_stats(struct net_device *dev) int nr_init(struct net_device *dev) { dev->mtu = NR_MAX_PACKET_SIZE; - dev->tbusy = 0; dev->hard_start_xmit = nr_xmit; dev->open = nr_open; dev->stop = nr_close; diff --git a/net/netsyms.c b/net/netsyms.c index b6f367df2..fbb07ab5a 100644 --- a/net/netsyms.c +++ b/net/netsyms.c @@ -28,12 +28,9 @@ #endif #include <net/pkt_sched.h> #include <net/scm.h> +#include <linux/if_bridge.h> #include <linux/random.h> -#ifdef CONFIG_BRIDGE -#include <net/br.h> -#endif - #ifdef CONFIG_NET extern __u32 sysctl_wmem_max; extern __u32 sysctl_rmem_max; @@ -217,10 +214,9 @@ EXPORT_SYMBOL(sklist_insert_socket); EXPORT_SYMBOL(scm_detach_fds); -#ifdef CONFIG_BRIDGE -EXPORT_SYMBOL(br_ioctl); -EXPORT_SYMBOL(port_info); -EXPORT_SYMBOL(br_avl_find_addr); +#if defined(CONFIG_BRIDGE) || defined(CONFIG_BRIDGE_MODULE) +EXPORT_SYMBOL(br_handle_frame_hook); +EXPORT_SYMBOL(br_ioctl_hook); #endif #ifdef CONFIG_INET @@ -540,6 +536,7 @@ EXPORT_SYMBOL(unregister_hipdev); #ifdef CONFIG_SYSCTL EXPORT_SYMBOL(sysctl_wmem_max); EXPORT_SYMBOL(sysctl_rmem_max); +EXPORT_SYMBOL(sysctl_ip_default_ttl); #endif #if defined(CONFIG_ATALK) || defined(CONFIG_ATALK_MODULE) @@ -596,6 +593,7 @@ EXPORT_SYMBOL(nf_hooks); EXPORT_SYMBOL(register_gifconf); +EXPORT_SYMBOL(net_call_rx_atomic); EXPORT_SYMBOL(softirq_state); EXPORT_SYMBOL(softnet_data); diff --git a/net/packet/af_packet.c b/net/packet/af_packet.c index 6410e99d7..46439faa3 100644 --- a/net/packet/af_packet.c +++ b/net/packet/af_packet.c @@ -5,7 +5,7 @@ * * PACKET - implements raw packet sockets. * - * Version: $Id: af_packet.c,v 1.30 2000/02/01 12:38:30 freitag Exp $ + * Version: $Id: af_packet.c,v 1.31 2000/02/18 16:47:23 davem Exp $ * * Authors: Ross Biro, <bir7@leland.Stanford.Edu> * Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG> @@ -65,16 +65,12 @@ #include <linux/poll.h> #include <linux/module.h> #include <linux/init.h> +#include <linux/if_bridge.h> #ifdef CONFIG_INET #include <net/inet_common.h> #endif -#ifdef CONFIG_BRIDGE -#include <linux/smp_lock.h> -#include <net/br.h> -#endif - #ifdef CONFIG_DLCI extern int dlci_ioctl(unsigned int, void*); #endif @@ -1442,14 +1438,14 @@ static int packet_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg case SIOCGIFBR: case SIOCSIFBR: -#ifdef CONFIG_BRIDGE - lock_kernel(); - err = br_ioctl(cmd,(void *) arg); - unlock_kernel(); - return err; -#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 #ifdef CONFIG_INET case SIOCADDRT: diff --git a/net/rose/rose_dev.c b/net/rose/rose_dev.c index ed3599a93..88f2d55e7 100644 --- a/net/rose/rose_dev.c +++ b/net/rose/rose_dev.c @@ -57,7 +57,7 @@ int rose_rx_ip(struct sk_buff *skb, struct net_device *dev) struct net_device_stats *stats = (struct net_device_stats *)dev->priv; #ifdef CONFIG_INET - if (!dev->start) { + if (!netif_running(dev)) { stats->rx_errors++; return 0; } @@ -145,25 +145,17 @@ static int rose_set_mac_address(struct net_device *dev, void *addr) static int rose_open(struct net_device *dev) { - dev->tbusy = 0; - dev->start = 1; - MOD_INC_USE_COUNT; - + netif_start_queue(dev); rose_add_loopback_node((rose_address *)dev->dev_addr); - return 0; } static int rose_close(struct net_device *dev) { - dev->tbusy = 1; - dev->start = 0; - - MOD_DEC_USE_COUNT; - + netif_stop_queue(dev); rose_del_loopback_node((rose_address *)dev->dev_addr); - + MOD_DEC_USE_COUNT; return 0; } @@ -171,34 +163,12 @@ static int rose_xmit(struct sk_buff *skb, struct net_device *dev) { struct net_device_stats *stats = (struct net_device_stats *)dev->priv; - if (skb == NULL || dev == NULL) - return 0; - - if (!dev->start) { + if (!netif_running(dev)) { printk(KERN_ERR "ROSE: rose_xmit - called when iface is down\n"); return 1; } - - cli(); - - if (dev->tbusy != 0) { - sti(); - stats->tx_errors++; - return 1; - } - - dev->tbusy = 1; - - sti(); - - kfree_skb(skb); - + dev_kfree_skb(skb); stats->tx_errors++; - - dev->tbusy = 0; - - mark_bh(NET_BH); - return 0; } @@ -210,7 +180,6 @@ static struct net_device_stats *rose_get_stats(struct net_device *dev) int rose_init(struct net_device *dev) { dev->mtu = ROSE_MAX_PACKET_SIZE - 2; - dev->tbusy = 0; dev->hard_start_xmit = rose_xmit; dev->open = rose_open; dev->stop = rose_close; diff --git a/net/sched/sch_api.c b/net/sched/sch_api.c index 1d57af985..9da8dcdcd 100644 --- a/net/sched/sch_api.c +++ b/net/sched/sch_api.c @@ -1139,11 +1139,6 @@ int __init psched_calibrate_clock(void) long rdelay; unsigned long stop; -#if CPU == 586 || CPU == 686 - if (!(boot_cpu_data.x86_capability & X86_FEATURE_TSC)) - return -1; -#endif - #ifdef PSCHED_WATCHER psched_tick(0); #endif diff --git a/net/sched/sch_cbq.c b/net/sched/sch_cbq.c index d3c32be20..0b741fa4e 100644 --- a/net/sched/sch_cbq.c +++ b/net/sched/sch_cbq.c @@ -1053,7 +1053,7 @@ cbq_dequeue(struct Qdisc *sch) if (sch->q.qlen) { sch->stats.overlimits++; - if (q->wd_expires && !test_bit(LINK_STATE_XOFF, &sch->dev->state)) { + if (q->wd_expires && !netif_queue_stopped(sch->dev)) { long delay = PSCHED_US2JIFFIE(q->wd_expires); del_timer(&q->wd_timer); if (delay <= 0) diff --git a/net/sched/sch_generic.c b/net/sched/sch_generic.c index 2a9f9e69e..8ee8ab54f 100644 --- a/net/sched/sch_generic.c +++ b/net/sched/sch_generic.c @@ -88,7 +88,7 @@ int qdisc_restart(struct net_device *dev) /* And release queue */ spin_unlock(&dev->queue_lock); - if (!test_bit(LINK_STATE_XOFF, &dev->state)) { + if (!netif_queue_stopped(dev)) { if (netdev_nit) dev_queue_xmit_nit(skb, dev); @@ -146,7 +146,7 @@ static void dev_watchdog(unsigned long arg) spin_lock(&dev->xmit_lock); if (dev->qdisc != &noop_qdisc) { - if (test_bit(LINK_STATE_XOFF, &dev->state) && + if (netif_queue_stopped(dev) && (jiffies - dev->trans_start) > dev->watchdog_timeo) { printk(KERN_INFO "NETDEV WATCHDOG: %s: transmit timed out\n", dev->name); dev->tx_timeout(dev); @@ -476,7 +476,7 @@ void dev_deactivate(struct net_device *dev) dev_watchdog_down(dev); - if (test_bit(LINK_STATE_SCHED, &dev->state)) { + while (test_bit(__LINK_STATE_SCHED, &dev->state)) { current->policy |= SCHED_YIELD; schedule(); } diff --git a/net/sched/sch_tbf.c b/net/sched/sch_tbf.c index 2681d7129..e2de156ab 100644 --- a/net/sched/sch_tbf.c +++ b/net/sched/sch_tbf.c @@ -226,7 +226,7 @@ tbf_dequeue(struct Qdisc* sch) return skb; } - if (!test_bit(LINK_STATE_XOFF, &sch->dev->state)) { + if (!netif_queue_stopped(sch->dev)) { long delay = PSCHED_US2JIFFIE(max(-toks, -ptoks)); if (delay == 0) diff --git a/net/sched/sch_teql.c b/net/sched/sch_teql.c index e576dbb11..f7fed6c84 100644 --- a/net/sched/sch_teql.c +++ b/net/sched/sch_teql.c @@ -296,8 +296,7 @@ restart: if (slave->qdisc_sleeping != q) continue; - if (test_bit(LINK_STATE_XOFF, &slave->state) || - test_bit(LINK_STATE_DOWN, &slave->state)) { + if (netif_queue_stopped(slave) || ! netif_running(slave)) { busy = 1; continue; } @@ -306,7 +305,7 @@ restart: case 0: if (spin_trylock(&slave->xmit_lock)) { slave->xmit_lock_owner = smp_processor_id(); - if (!test_bit(LINK_STATE_XOFF, &slave->state) && + if (!netif_queue_stopped(slave) && slave->hard_start_xmit(skb, slave) == 0) { slave->xmit_lock_owner = -1; spin_unlock(&slave->xmit_lock); @@ -319,7 +318,7 @@ restart: slave->xmit_lock_owner = -1; spin_unlock(&slave->xmit_lock); } - if (test_bit(LINK_STATE_XOFF, &dev->state)) + if (netif_queue_stopped(dev)) busy = 1; break; case 1: diff --git a/net/socket.c b/net/socket.c index 153fe83f3..edaf48a3b 100644 --- a/net/socket.c +++ b/net/socket.c @@ -69,6 +69,7 @@ #include <linux/wanrouter.h> #include <linux/init.h> #include <linux/poll.h> +#include <linux/cache.h> #if defined(CONFIG_KMOD) && defined(CONFIG_NET) #include <linux/kmod.h> @@ -98,6 +99,10 @@ static unsigned int sock_poll(struct file *file, static int sock_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg); static int sock_fasync(int fd, struct file *filp, int on); +static ssize_t sock_readv(struct file *file, const struct iovec *vector, + unsigned long count, loff_t *ppos); +static ssize_t sock_writev(struct file *file, const struct iovec *vector, + unsigned long count, loff_t *ppos); /* @@ -114,7 +119,9 @@ static struct file_operations socket_file_ops = { mmap: sock_mmap, open: sock_no_open, /* special open code to disallow open via /proc */ release: sock_close, - fasync: sock_fasync + fasync: sock_fasync, + readv: sock_readv, + writev: sock_writev }; /* @@ -512,6 +519,27 @@ int sock_readv_writev(int type, struct inode * inode, struct file * file, return sock_sendmsg(sock, &msg, size); } +static ssize_t sock_readv(struct file *file, const struct iovec *vector, + unsigned long count, loff_t *ppos) +{ + size_t tot_len = 0; + int i; + for (i = 0 ; i < count ; i++) + tot_len += vector[i].iov_len; + return sock_readv_writev(VERIFY_WRITE, file->f_dentry->d_inode, + file, vector, count, tot_len); +} + +static ssize_t sock_writev(struct file *file, const struct iovec *vector, + unsigned long count, loff_t *ppos) +{ + size_t tot_len = 0; + int i; + for (i = 0 ; i < count ; i++) + tot_len += vector[i].iov_len; + return sock_readv_writev(VERIFY_READ, file->f_dentry->d_inode, + file, vector, count, tot_len); +} /* * With an ioctl arg may well be a user mode pointer, but we don't know what to do diff --git a/net/sunrpc/svcsock.c b/net/sunrpc/svcsock.c index 131f37f46..d63024983 100644 --- a/net/sunrpc/svcsock.c +++ b/net/sunrpc/svcsock.c @@ -64,7 +64,6 @@ static int svc_udp_sendto(struct svc_rqst *); static inline void svc_serv_enqueue(struct svc_serv *serv, struct svc_rqst *rqstp) { - BUG_TRAP(spin_is_locked(&serv->sv_lock)); rpc_append_list(&serv->sv_threads, rqstp); } @@ -74,7 +73,6 @@ svc_serv_enqueue(struct svc_serv *serv, struct svc_rqst *rqstp) static inline void svc_serv_dequeue(struct svc_serv *serv, struct svc_rqst *rqstp) { - BUG_TRAP(spin_is_locked(&serv->sv_lock)); rpc_remove_list(&serv->sv_threads, rqstp); } @@ -106,8 +104,6 @@ svc_sock_enqueue(struct svc_sock *svsk) struct svc_serv *serv = svsk->sk_server; struct svc_rqst *rqstp; - BUG_TRAP(spin_is_locked(&svsk->sk_lock)); - /* NOTE: Local BH is already disabled by our caller. */ spin_lock(&serv->sv_lock); @@ -156,8 +152,6 @@ svc_sock_dequeue(struct svc_serv *serv) { struct svc_sock *svsk; - BUG_TRAP(spin_is_locked(&serv->sv_lock)); - if ((svsk = serv->sv_sockets) != NULL) rpc_remove_list(&serv->sv_sockets, svsk); diff --git a/net/sysctl_net.c b/net/sysctl_net.c index 5f5e8593e..e539693fe 100644 --- a/net/sysctl_net.c +++ b/net/sysctl_net.c @@ -34,10 +34,6 @@ extern ctl_table unix_table[]; 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 @@ ctl_table net_table[] = { #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 diff --git a/net/wanrouter/wanmain.c b/net/wanrouter/wanmain.c index af7191563..7f12a1f16 100644 --- a/net/wanrouter/wanmain.c +++ b/net/wanrouter/wanmain.c @@ -526,7 +526,10 @@ static int device_shutdown (wan_device_t* wandev) for (dev = wandev->dev; dev;) { if (delete_interface(wandev, dev->name, 0)) - dev = dev->slave; + { + struct net_device **slave = dev->priv; + dev = *slave; + } } if (wandev->ndev) return -EBUSY; /* there are opened interfaces */ @@ -608,8 +611,10 @@ static int device_new_if (wan_device_t* wandev, wanif_conf_t* u_conf) #endif err = register_netdev(dev); if (!err) { + struct net_device **slave = dev->priv; + cli(); /***** critical section start *****/ - dev->slave = wandev->dev; + *slave = wandev->dev; wandev->dev = dev; ++wandev->ndev; sti(); /****** critical section end ******/ @@ -684,14 +689,19 @@ static int delete_interface (wan_device_t *wandev, char *name, int force) { struct net_device *dev, *prev; - for (dev = wandev->dev, prev = NULL; - dev && strcmp(name, dev->name); - prev = dev, dev = dev->slave); + dev = wandev->dev; + prev = NULL; + while (dev && strcmp(name, dev->name)) { + struct net_device **slave = dev->priv; + + prev = dev; + dev = *slave; + } if (dev == NULL) return -ENODEV; /* interface not found */ - if (dev->start) { + if (netif_running(dev)) { if (force) { printk(KERN_WARNING "%s: deleting opened interface %s!\n", @@ -705,10 +715,16 @@ static int delete_interface (wan_device_t *wandev, char *name, int force) wandev->del_if(wandev, dev); cli(); /***** critical section start *****/ - if (prev) - prev->slave = dev->slave; - else - wandev->dev = dev->slave; + if (prev) { + struct net_device **prev_slave = prev->priv; + struct net_device **slave = dev->priv; + + *prev_slave = *slave; + } else { + struct net_device **slave = dev->priv; + + wandev->dev = *slave; + } --wandev->ndev; sti(); /****** critical section end ******/ |