summaryrefslogtreecommitdiffstats
path: root/net
diff options
context:
space:
mode:
Diffstat (limited to 'net')
-rw-r--r--net/Changes1
-rw-r--r--net/Config.in7
-rw-r--r--net/Makefile4
-rw-r--r--net/README1
-rw-r--r--net/atm/clip.c9
-rw-r--r--net/ax25/af_ax25.c28
-rw-r--r--net/bridge/Makefile17
-rw-r--r--net/bridge/br.c2733
-rw-r--r--net/bridge/br_device.c141
-rw-r--r--net/bridge/br_fdb.c318
-rw-r--r--net/bridge/br_forward.c95
-rw-r--r--net/bridge/br_if.c293
-rw-r--r--net/bridge/br_input.c113
-rw-r--r--net/bridge/br_ioctl.c264
-rw-r--r--net/bridge/br_notify.c75
-rw-r--r--net/bridge/br_private.h199
-rw-r--r--net/bridge/br_private_stp.h53
-rw-r--r--net/bridge/br_private_timer.h54
-rw-r--r--net/bridge/br_stp.c465
-rw-r--r--net/bridge/br_stp_bpdu.c191
-rw-r--r--net/bridge/br_stp_if.c225
-rw-r--r--net/bridge/br_stp_timer.c184
-rw-r--r--net/bridge/br_tree.c501
-rw-r--r--net/bridge/sysctl_net_bridge.c13
-rw-r--r--net/core/dev.c287
-rw-r--r--net/core/dev_mcast.c81
-rw-r--r--net/core/profile.c6
-rw-r--r--net/core/rtnetlink.c2
-rw-r--r--net/core/skbuff.c4
-rw-r--r--net/decnet/af_decnet.c4
-rw-r--r--net/ipv4/af_inet.c22
-rw-r--r--net/ipv4/ipconfig.c5
-rw-r--r--net/ipv4/tcp_ipv4.c3
-rw-r--r--net/ipv4/tcp_output.c8
-rw-r--r--net/ipv4/tcp_timer.c3
-rw-r--r--net/ipv6/af_inet6.c4
-rw-r--r--net/irda/irda_device.c4
-rw-r--r--net/irda/irlan/irlan_client.c2
-rw-r--r--net/irda/irlan/irlan_common.c11
-rw-r--r--net/irda/irlan/irlan_eth.c23
-rw-r--r--net/khttpd/sockets.c2
-rw-r--r--net/netlink/netlink_dev.c29
-rw-r--r--net/netrom/nr_dev.c44
-rw-r--r--net/netsyms.c14
-rw-r--r--net/packet/af_packet.c22
-rw-r--r--net/rose/rose_dev.c43
-rw-r--r--net/sched/sch_api.c5
-rw-r--r--net/sched/sch_cbq.c2
-rw-r--r--net/sched/sch_generic.c6
-rw-r--r--net/sched/sch_tbf.c2
-rw-r--r--net/sched/sch_teql.c7
-rw-r--r--net/socket.c30
-rw-r--r--net/sunrpc/svcsock.c6
-rw-r--r--net/sysctl_net.c7
-rw-r--r--net/wanrouter/wanmain.c36
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, &notcopied);
- put_user(copied, &user_buf->copied);
- put_user(notcopied, &user_buf->not_copied);
- if(!fdbis)
- return -ENOMEM;
- u_fdbs = (struct fdb_info *) (user_buf+1);
- err = copy_to_user(u_fdbs, fdbis, copied*sizeof(struct fdb_info));
- kfree(fdbis);
- if (err)
- {
- err = -EFAULT;
- }
- return err;
- }
- default:
- return -EINVAL;
- }
- 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 ******/