URL
https://opencores.org/ocsvn/or1k/or1k/trunk
Subversion Repositories or1k
Compare Revisions
- This comparison shows the changes necessary to convert path
/or1k/trunk/linux/linux-2.4/net/bridge
- from Rev 1275 to Rev 1765
- ↔ Reverse comparison
Rev 1275 → Rev 1765
/br_private.h
0,0 → 1,204
/* |
* Linux ethernet bridge |
* |
* Authors: |
* Lennert Buytenhek <buytenh@gnu.org> |
* |
* $Id: br_private.h,v 1.1.1.1 2004-04-15 01:16:27 phoenix 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; |
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; |
}; |
|
extern struct notifier_block br_device_notifier; |
extern unsigned char bridge_ula[6]; |
|
/* br.c */ |
extern void br_dec_use_count(void); |
extern void br_inc_use_count(void); |
|
/* br_device.c */ |
extern void br_dev_setup(struct net_device *dev); |
extern int br_dev_xmit(struct sk_buff *skb, struct net_device *dev); |
|
/* br_fdb.c */ |
extern void br_fdb_changeaddr(struct net_bridge_port *p, |
unsigned char *newaddr); |
extern void br_fdb_cleanup(struct net_bridge *br); |
extern void br_fdb_delete_by_port(struct net_bridge *br, |
struct net_bridge_port *p); |
extern struct net_bridge_fdb_entry *br_fdb_get(struct net_bridge *br, |
unsigned char *addr); |
extern void br_fdb_put(struct net_bridge_fdb_entry *ent); |
extern int br_fdb_get_entries(struct net_bridge *br, |
unsigned char *_buf, |
int maxnum, |
int offset); |
extern void br_fdb_insert(struct net_bridge *br, |
struct net_bridge_port *source, |
unsigned char *addr, |
int is_local); |
|
/* br_forward.c */ |
extern void br_deliver(struct net_bridge_port *to, |
struct sk_buff *skb); |
extern void br_forward(struct net_bridge_port *to, |
struct sk_buff *skb); |
extern void br_flood_deliver(struct net_bridge *br, |
struct sk_buff *skb, |
int clone); |
extern void br_flood_forward(struct net_bridge *br, |
struct sk_buff *skb, |
int clone); |
|
/* br_if.c */ |
extern int br_add_bridge(char *name); |
extern int br_del_bridge(char *name); |
extern int br_add_if(struct net_bridge *br, |
struct net_device *dev); |
extern int br_del_if(struct net_bridge *br, |
struct net_device *dev); |
extern int br_get_bridge_ifindices(int *indices, |
int num); |
extern void br_get_port_ifindices(struct net_bridge *br, |
int *ifindices); |
|
/* br_input.c */ |
extern void br_handle_frame(struct sk_buff *skb); |
|
/* br_ioctl.c */ |
extern void br_call_ioctl_atomic(void (*fn)(void)); |
extern int br_ioctl(struct net_bridge *br, |
unsigned int cmd, |
unsigned long arg0, |
unsigned long arg1, |
unsigned long arg2); |
extern int br_ioctl_deviceless_stub(unsigned long arg); |
|
/* br_stp.c */ |
extern int br_is_root_bridge(struct net_bridge *br); |
extern struct net_bridge_port *br_get_port(struct net_bridge *br, |
int port_no); |
extern void br_init_port(struct net_bridge_port *p); |
extern port_id br_make_port_id(struct net_bridge_port *p); |
extern void br_become_designated_port(struct net_bridge_port *p); |
|
/* br_stp_if.c */ |
extern void br_stp_enable_bridge(struct net_bridge *br); |
extern void br_stp_disable_bridge(struct net_bridge *br); |
extern void br_stp_enable_port(struct net_bridge_port *p); |
extern void br_stp_disable_port(struct net_bridge_port *p); |
extern void br_stp_recalculate_bridge_id(struct net_bridge *br); |
extern void br_stp_set_bridge_priority(struct net_bridge *br, |
int newprio); |
extern void br_stp_set_port_priority(struct net_bridge_port *p, |
int newprio); |
extern void br_stp_set_path_cost(struct net_bridge_port *p, |
int path_cost); |
|
/* br_stp_bpdu.c */ |
extern int br_stp_handle_bpdu(struct sk_buff *skb); |
|
#endif |
/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.1.1 2004-04-15 01:16:26 phoenix 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 |
/br_stp_bpdu.c
0,0 → 1,199
/* |
* Spanning tree protocol; BPDU handling |
* Linux ethernet bridge |
* |
* Authors: |
* Lennert Buytenhek <buytenh@gnu.org> |
* |
* $Id: br_stp_bpdu.c,v 1.1.1.1 2004-04-15 01:16:27 phoenix 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_ether.h> |
#include <linux/if_bridge.h> |
#include <linux/netfilter_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->protocol = htons(ETH_P_802_2); |
skb->mac.raw = skb_put(skb, size); |
memcpy(skb->mac.raw, bridge_ula, ETH_ALEN); |
memcpy(skb->mac.raw+ETH_ALEN, dev->dev_addr, ETH_ALEN); |
skb->mac.raw[2*ETH_ALEN] = 0; |
skb->mac.raw[2*ETH_ALEN+1] = length; |
skb->nh.raw = skb->mac.raw + 2*ETH_ALEN + 2; |
memcpy(skb->nh.raw, data, length); |
memset(skb->nh.raw + length, 0xa5, size - length - 2*ETH_ALEN - 2); |
|
NF_HOOK(PF_BRIDGE, NF_BR_LOCAL_OUT, skb, NULL, skb->dev, |
dev_queue_xmit); |
} |
|
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 */ |
int br_stp_handle_bpdu(struct sk_buff *skb) |
{ |
unsigned char *buf; |
struct net_bridge_port *p; |
|
p = skb->dev->br_port; |
|
if (!p->br->stp_enabled || |
!pskb_may_pull(skb, sizeof(header)+1) || |
memcmp(skb->data, header, sizeof(header))) |
goto err; |
|
buf = skb_pull(skb, sizeof(header)); |
if (buf[0] == BPDU_TYPE_CONFIG) { |
struct br_config_bpdu bpdu; |
|
if (!pskb_may_pull(skb, 32)) |
goto err; |
|
buf = skb->data; |
bpdu.topology_change = (buf[1] & 0x01) ? 1 : 0; |
bpdu.topology_change_ack = (buf[1] & 0x80) ? 1 : 0; |
|
bpdu.root.prio[0] = buf[2]; |
bpdu.root.prio[1] = buf[3]; |
bpdu.root.addr[0] = buf[4]; |
bpdu.root.addr[1] = buf[5]; |
bpdu.root.addr[2] = buf[6]; |
bpdu.root.addr[3] = buf[7]; |
bpdu.root.addr[4] = buf[8]; |
bpdu.root.addr[5] = buf[9]; |
bpdu.root_path_cost = |
(buf[10] << 24) | |
(buf[11] << 16) | |
(buf[12] << 8) | |
buf[13]; |
bpdu.bridge_id.prio[0] = buf[14]; |
bpdu.bridge_id.prio[1] = buf[15]; |
bpdu.bridge_id.addr[0] = buf[16]; |
bpdu.bridge_id.addr[1] = buf[17]; |
bpdu.bridge_id.addr[2] = buf[18]; |
bpdu.bridge_id.addr[3] = buf[19]; |
bpdu.bridge_id.addr[4] = buf[20]; |
bpdu.bridge_id.addr[5] = buf[21]; |
bpdu.port_id = (buf[22] << 8) | buf[23]; |
|
bpdu.message_age = br_get_ticks(buf+24); |
bpdu.max_age = br_get_ticks(buf+26); |
bpdu.hello_time = br_get_ticks(buf+28); |
bpdu.forward_delay = br_get_ticks(buf+30); |
|
br_received_config_bpdu(p, &bpdu); |
} |
|
else if (buf[0] == BPDU_TYPE_TCN) { |
br_received_tcn_bpdu(p); |
} |
|
err: |
kfree_skb(skb); |
return 0; |
} |
/br.c
0,0 → 1,81
/* |
* Generic parts |
* Linux ethernet bridge |
* |
* Authors: |
* Lennert Buytenhek <buytenh@gnu.org> |
* |
* $Id: br.c,v 1.1.1.1 2004-04-15 01:16:25 phoenix 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/module.h> |
#include <linux/kernel.h> |
#include <linux/miscdevice.h> |
#include <linux/netdevice.h> |
#include <linux/etherdevice.h> |
#include <linux/init.h> |
#include <linux/if_bridge.h> |
#include <linux/brlock.h> |
#include <asm/uaccess.h> |
#include "br_private.h" |
|
#if defined(CONFIG_ATM_LANE) || defined(CONFIG_ATM_LANE_MODULE) |
#include "../atm/lec.h" |
#endif |
|
void br_dec_use_count() |
{ |
MOD_DEC_USE_COUNT; |
} |
|
void br_inc_use_count() |
{ |
MOD_INC_USE_COUNT; |
} |
|
static int __init br_init(void) |
{ |
printk(KERN_INFO "NET4: Ethernet Bridge 008 for NET4.0\n"); |
|
br_handle_frame_hook = br_handle_frame; |
br_ioctl_hook = br_ioctl_deviceless_stub; |
#if defined(CONFIG_ATM_LANE) || defined(CONFIG_ATM_LANE_MODULE) |
br_fdb_get_hook = br_fdb_get; |
br_fdb_put_hook = br_fdb_put; |
#endif |
register_netdevice_notifier(&br_device_notifier); |
|
return 0; |
} |
|
static void __br_clear_ioctl_hook(void) |
{ |
br_ioctl_hook = NULL; |
} |
|
static void __exit br_deinit(void) |
{ |
unregister_netdevice_notifier(&br_device_notifier); |
br_call_ioctl_atomic(__br_clear_ioctl_hook); |
|
br_write_lock_bh(BR_NETPROTO_LOCK); |
br_handle_frame_hook = NULL; |
br_write_unlock_bh(BR_NETPROTO_LOCK); |
|
#if defined(CONFIG_ATM_LANE) || defined(CONFIG_ATM_LANE_MODULE) |
br_fdb_get_hook = NULL; |
br_fdb_put_hook = NULL; |
#endif |
} |
|
EXPORT_NO_SYMBOLS; |
|
module_init(br_init) |
module_exit(br_deinit) |
MODULE_LICENSE("GPL"); |
/br_stp_if.c
0,0 → 1,228
/* |
* Spanning tree protocol; interface code |
* Linux ethernet bridge |
* |
* Authors: |
* Lennert Buytenhek <buytenh@gnu.org> |
* |
* $Id: br_stp_if.c,v 1.1.1.1 2004-04-15 01:16:26 phoenix 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->dev.name, p->port_no, p->dev->name, "disabled"); |
|
wasroot = br_is_root_bridge(br); |
br_become_designated_port(p); |
p->state = BR_STATE_DISABLED; |
p->topology_change_ack = 0; |
p->config_pending = 0; |
br_timer_clear(&p->message_age_timer); |
br_timer_clear(&p->forward_delay_timer); |
br_timer_clear(&p->hold_timer); |
br_configuration_update(br); |
br_port_state_selection(br); |
|
if (br_is_root_bridge(br) && !wasroot) |
br_become_root_bridge(br); |
} |
|
/* called under bridge lock */ |
static void br_stp_change_bridge_id(struct net_bridge *br, unsigned char *addr) |
{ |
unsigned char oldaddr[6]; |
struct net_bridge_port *p; |
int wasroot; |
|
wasroot = br_is_root_bridge(br); |
|
memcpy(oldaddr, br->bridge_id.addr, ETH_ALEN); |
memcpy(br->bridge_id.addr, addr, ETH_ALEN); |
memcpy(br->dev.dev_addr, addr, ETH_ALEN); |
|
p = br->port_list; |
while (p != NULL) { |
if (!memcmp(p->designated_bridge.addr, oldaddr, ETH_ALEN)) |
memcpy(p->designated_bridge.addr, addr, ETH_ALEN); |
|
if (!memcmp(p->designated_root.addr, oldaddr, ETH_ALEN)) |
memcpy(p->designated_root.addr, addr, ETH_ALEN); |
|
p = p->next; |
} |
|
br_configuration_update(br); |
br_port_state_selection(br); |
if (br_is_root_bridge(br) && !wasroot) |
br_become_root_bridge(br); |
} |
|
static unsigned char br_mac_zero[6] = {0,0,0,0,0,0}; |
|
/* called under bridge lock */ |
void br_stp_recalculate_bridge_id(struct net_bridge *br) |
{ |
unsigned char *addr; |
struct net_bridge_port *p; |
|
addr = br_mac_zero; |
|
p = br->port_list; |
while (p != NULL) { |
if (addr == br_mac_zero || |
memcmp(p->dev->dev_addr, addr, ETH_ALEN) < 0) |
addr = p->dev->dev_addr; |
|
p = p->next; |
} |
|
if (memcmp(br->bridge_id.addr, addr, ETH_ALEN)) |
br_stp_change_bridge_id(br, addr); |
} |
|
/* called under bridge lock */ |
void br_stp_set_bridge_priority(struct net_bridge *br, int newprio) |
{ |
struct net_bridge_port *p; |
int wasroot; |
|
wasroot = br_is_root_bridge(br); |
|
p = br->port_list; |
while (p != NULL) { |
if (p->state != BR_STATE_DISABLED && |
br_is_designated_port(p)) { |
p->designated_bridge.prio[0] = (newprio >> 8) & 0xFF; |
p->designated_bridge.prio[1] = newprio & 0xFF; |
} |
|
p = p->next; |
} |
|
br->bridge_id.prio[0] = (newprio >> 8) & 0xFF; |
br->bridge_id.prio[1] = newprio & 0xFF; |
br_configuration_update(br); |
br_port_state_selection(br); |
if (br_is_root_bridge(br) && !wasroot) |
br_become_root_bridge(br); |
} |
|
/* called under bridge lock */ |
void br_stp_set_port_priority(struct net_bridge_port *p, int newprio) |
{ |
__u16 new_port_id; |
|
p->priority = newprio & 0xFF; |
new_port_id = br_make_port_id(p); |
|
if (br_is_designated_port(p)) |
p->designated_port = new_port_id; |
|
p->port_id = new_port_id; |
if (!memcmp(&p->br->bridge_id, &p->designated_bridge, 8) && |
p->port_id < p->designated_port) { |
br_become_designated_port(p); |
br_port_state_selection(p->br); |
} |
} |
|
/* called under bridge lock */ |
void br_stp_set_path_cost(struct net_bridge_port *p, int path_cost) |
{ |
p->path_cost = path_cost; |
br_configuration_update(p->br); |
br_port_state_selection(p->br); |
} |
/br_fdb.c
0,0 → 1,331
/* |
* Forwarding database |
* Linux ethernet bridge |
* |
* Authors: |
* Lennert Buytenhek <buytenh@gnu.org> |
* |
* $Id: br_fdb.c,v 1.1.1.1 2004-04-15 01:16:25 phoenix 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/spinlock.h> |
#include <linux/if_bridge.h> |
#include <asm/atomic.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) |
{ |
memset(ent, 0, sizeof(struct __fdb_entry)); |
memcpy(ent->mac_addr, f->addr.addr, ETH_ALEN); |
ent->port_no = f->dst?f->dst->port_no:0; |
ent->is_local = f->is_local; |
ent->ageing_timer_value = 0; |
if (!f->is_static) |
ent->ageing_timer_value = jiffies - f->ageing_timer; |
} |
|
static __inline__ int br_mac_hash(unsigned char *mac) |
{ |
unsigned long x; |
|
x = mac[0]; |
x = (x << 2) ^ mac[1]; |
x = (x << 2) ^ mac[2]; |
x = (x << 2) ^ mac[3]; |
x = (x << 2) ^ mac[4]; |
x = (x << 2) ^ mac[5]; |
|
x ^= x >> 8; |
|
return x & (BR_HASH_SIZE - 1); |
} |
|
static __inline__ void __hash_link(struct net_bridge *br, |
struct net_bridge_fdb_entry *ent, |
int hash) |
{ |
ent->next_hash = br->hash[hash]; |
if (ent->next_hash != NULL) |
ent->next_hash->pprev_hash = &ent->next_hash; |
br->hash[hash] = ent; |
ent->pprev_hash = &br->hash[hash]; |
} |
|
static __inline__ void __hash_unlink(struct net_bridge_fdb_entry *ent) |
{ |
*(ent->pprev_hash) = ent->next_hash; |
if (ent->next_hash != NULL) |
ent->next_hash->pprev_hash = ent->pprev_hash; |
ent->next_hash = NULL; |
ent->pprev_hash = NULL; |
} |
|
|
|
void br_fdb_changeaddr(struct net_bridge_port *p, unsigned char *newaddr) |
{ |
struct net_bridge *br; |
int i; |
|
br = p->br; |
write_lock_bh(&br->hash_lock); |
for (i=0;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; |
struct net_bridge_fdb_entry **pp; |
|
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; |
pp = f->pprev_hash; |
br_fdb_put(f); |
|
if (err) |
goto out_fault; |
|
if (g == NULL && pp == 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)) { |
/* attempt to update an entry for a local interface */ |
if (fdb->is_local) { |
if (is_local) |
printk(KERN_INFO "%s: attempt to add" |
" interface with same source address.\n", |
source->dev->name); |
else if (net_ratelimit()) |
printk(KERN_WARNING "%s: received packet with " |
" own address as source address\n", |
source->dev->name); |
goto out; |
} |
|
__fdb_possibly_replace(fdb, source, is_local); |
goto out; |
} |
|
fdb = fdb->next_hash; |
} |
|
fdb = kmalloc(sizeof(*fdb), GFP_ATOMIC); |
if (fdb == NULL) |
goto out; |
|
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); |
|
out: |
write_unlock_bh(&br->hash_lock); |
} |
/br_if.c
0,0 → 1,295
/* |
* Userspace interface |
* Linux ethernet bridge |
* |
* Authors: |
* Lennert Buytenhek <buytenh@gnu.org> |
* |
* $Id: br_if.c,v 1.1.1.1 2004-04-15 01:16:27 phoenix 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_arp.h> |
#include <linux/if_bridge.h> |
#include <linux/inetdevice.h> |
#include <linux/rtnetlink.h> |
#include <linux/brlock.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 BR_NETPROTO_LOCK and 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->dev.name, name, IFNAMSIZ)) |
return b; |
|
b = &(br->next); |
} |
|
return NULL; |
} |
|
static void del_ifs(struct net_bridge *br) |
{ |
br_write_lock_bh(BR_NETPROTO_LOCK); |
write_lock(&br->lock); |
while (br->port_list != NULL) |
__br_del_if(br, br->port_list->dev); |
write_unlock(&br->lock); |
br_write_unlock_bh(BR_NETPROTO_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(dev->name, name, IFNAMSIZ); |
dev->priv = br; |
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 = 0; |
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_ATOMIC); |
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; |
|
for (i=1;i<255;i++) |
if (br_get_port(br, i) == NULL) |
break; |
|
if (i == 255) { |
kfree(p); |
return NULL; |
} |
|
dev->br_port = p; |
|
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 || dev->type != ARPHRD_ETHER) |
return -EINVAL; |
|
if (dev->hard_start_xmit == br_dev_xmit) |
return -ELOOP; |
|
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; |
|
br_write_lock_bh(BR_NETPROTO_LOCK); |
write_lock(&br->lock); |
retval = __br_del_if(br, dev); |
br_stp_recalculate_bridge_id(br); |
write_unlock(&br->lock); |
br_write_unlock_bh(BR_NETPROTO_LOCK); |
|
return retval; |
} |
|
int br_get_bridge_ifindices(int *indices, int num) |
{ |
struct net_bridge *br; |
int i; |
|
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) |
{ |
struct net_bridge_port *p; |
|
p = br->port_list; |
while (p != NULL) { |
ifindices[p->port_no] = p->dev->ifindex; |
p = p->next; |
} |
} |
/br_input.c
0,0 → 1,171
/* |
* Handle incoming frames |
* Linux ethernet bridge |
* |
* Authors: |
* Lennert Buytenhek <buytenh@gnu.org> |
* |
* $Id: br_input.c,v 1.1.1.1 2004-04-15 01:16:26 phoenix 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 <linux/netfilter_bridge.h> |
#include "br_private.h" |
|
unsigned char bridge_ula[6] = { 0x01, 0x80, 0xc2, 0x00, 0x00, 0x00 }; |
|
static int br_pass_frame_up_finish(struct sk_buff *skb) |
{ |
netif_rx(skb); |
|
return 0; |
} |
|
static void br_pass_frame_up(struct net_bridge *br, struct sk_buff *skb) |
{ |
struct net_device *indev; |
|
br->statistics.rx_packets++; |
br->statistics.rx_bytes += skb->len; |
|
indev = skb->dev; |
skb->dev = &br->dev; |
skb->pkt_type = PACKET_HOST; |
skb_push(skb, ETH_HLEN); |
skb->protocol = eth_type_trans(skb, &br->dev); |
|
NF_HOOK(PF_BRIDGE, NF_BR_LOCAL_IN, skb, indev, NULL, |
br_pass_frame_up_finish); |
} |
|
static int br_handle_frame_finish(struct sk_buff *skb) |
{ |
struct net_bridge *br; |
unsigned char *dest; |
struct net_bridge_fdb_entry *dst; |
struct net_bridge_port *p; |
int passedup; |
|
dest = skb->mac.ethernet->h_dest; |
|
p = skb->dev->br_port; |
if (p == NULL) |
goto err_nolock; |
|
br = p->br; |
read_lock(&br->lock); |
if (skb->dev->br_port == NULL) |
goto err; |
|
passedup = 0; |
if (br->dev.flags & IFF_PROMISC) { |
struct sk_buff *skb2; |
|
skb2 = skb_clone(skb, GFP_ATOMIC); |
if (skb2 != NULL) { |
passedup = 1; |
br_pass_frame_up(br, skb2); |
} |
} |
|
if (dest[0] & 1) { |
br_flood_forward(br, skb, !passedup); |
if (!passedup) |
br_pass_frame_up(br, skb); |
goto out; |
} |
|
dst = br_fdb_get(br, dest); |
if (dst != NULL && dst->is_local) { |
if (!passedup) |
br_pass_frame_up(br, skb); |
else |
kfree_skb(skb); |
br_fdb_put(dst); |
goto out; |
} |
|
if (dst != NULL) { |
br_forward(dst->dst, skb); |
br_fdb_put(dst); |
goto out; |
} |
|
br_flood_forward(br, skb, 0); |
|
out: |
read_unlock(&br->lock); |
return 0; |
|
err: |
read_unlock(&br->lock); |
err_nolock: |
kfree_skb(skb); |
return 0; |
} |
|
void br_handle_frame(struct sk_buff *skb) |
{ |
struct net_bridge *br; |
unsigned char *dest; |
struct net_bridge_port *p; |
|
dest = skb->mac.ethernet->h_dest; |
|
p = skb->dev->br_port; |
if (p == NULL) |
goto err_nolock; |
|
br = p->br; |
read_lock(&br->lock); |
if (skb->dev->br_port == NULL) |
goto err; |
|
if (!(br->dev.flags & IFF_UP) || |
p->state == BR_STATE_DISABLED) |
goto err; |
|
if (skb->mac.ethernet->h_source[0] & 1) |
goto err; |
|
if (p->state == BR_STATE_LEARNING || |
p->state == BR_STATE_FORWARDING) |
br_fdb_insert(br, p, skb->mac.ethernet->h_source, 0); |
|
if (br->stp_enabled && |
!memcmp(dest, bridge_ula, 5) && |
!(dest[5] & 0xF0)) |
goto handle_special_frame; |
|
if (p->state == BR_STATE_FORWARDING) { |
NF_HOOK(PF_BRIDGE, NF_BR_PRE_ROUTING, skb, skb->dev, NULL, |
br_handle_frame_finish); |
read_unlock(&br->lock); |
return; |
} |
|
err: |
read_unlock(&br->lock); |
err_nolock: |
kfree_skb(skb); |
return; |
|
handle_special_frame: |
if (!dest[5]) { |
NF_HOOK(PF_BRIDGE, NF_BR_LOCAL_IN, skb, skb->dev,NULL, |
br_stp_handle_bpdu); |
read_unlock(&br->lock); |
return; |
} |
|
read_unlock(&br->lock); |
kfree_skb(skb); |
} |
/br_device.c
0,0 → 1,137
/* |
* Device handling code |
* Linux ethernet bridge |
* |
* Authors: |
* Lennert Buytenhek <buytenh@gnu.org> |
* |
* $Id: br_device.c,v 1.1.1.1 2004-04-15 01:16:26 phoenix 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/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->mac.raw = skb->data; |
skb_pull(skb, ETH_HLEN); |
|
if (dest[0] & 1) { |
br_flood_deliver(br, skb, 0); |
return 0; |
} |
|
if ((dst = br_fdb_get(br, dest)) != NULL) { |
br_deliver(dst->dst, skb); |
br_fdb_put(dst); |
return 0; |
} |
|
br_flood_deliver(br, skb, 0); |
return 0; |
} |
|
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; |
} |
|
static int br_dev_accept_fastpath(struct net_device *dev, struct dst_entry *dst) |
{ |
return -1; |
} |
|
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; |
dev->accept_fastpath = br_dev_accept_fastpath; |
dev->tx_queue_len = 0; |
dev->set_mac_address = NULL; |
} |
/br_forward.c
0,0 → 1,149
/* |
* Forwarding decision |
* Linux ethernet bridge |
* |
* Authors: |
* Lennert Buytenhek <buytenh@gnu.org> |
* |
* $Id: br_forward.c,v 1.1.1.1 2004-04-15 01:16:26 phoenix 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 <linux/netfilter_bridge.h> |
#include "br_private.h" |
|
static inline int should_deliver(struct net_bridge_port *p, struct sk_buff *skb) |
{ |
if (skb->dev == p->dev || |
p->state != BR_STATE_FORWARDING) |
return 0; |
|
return 1; |
} |
|
static int __dev_queue_push_xmit(struct sk_buff *skb) |
{ |
skb_push(skb, ETH_HLEN); |
dev_queue_xmit(skb); |
|
return 0; |
} |
|
static int __br_forward_finish(struct sk_buff *skb) |
{ |
NF_HOOK(PF_BRIDGE, NF_BR_POST_ROUTING, skb, NULL, skb->dev, |
__dev_queue_push_xmit); |
|
return 0; |
} |
|
static void __br_deliver(struct net_bridge_port *to, struct sk_buff *skb) |
{ |
skb->dev = to->dev; |
NF_HOOK(PF_BRIDGE, NF_BR_LOCAL_OUT, skb, NULL, skb->dev, |
__br_forward_finish); |
} |
|
static void __br_forward(struct net_bridge_port *to, struct sk_buff *skb) |
{ |
struct net_device *indev; |
|
indev = skb->dev; |
skb->dev = to->dev; |
skb->ip_summed = CHECKSUM_NONE; |
|
NF_HOOK(PF_BRIDGE, NF_BR_FORWARD, skb, indev, skb->dev, |
__br_forward_finish); |
} |
|
/* called under bridge lock */ |
void br_deliver(struct net_bridge_port *to, struct sk_buff *skb) |
{ |
if (should_deliver(to, skb)) { |
__br_deliver(to, skb); |
return; |
} |
|
kfree_skb(skb); |
} |
|
/* called under bridge lock */ |
void br_forward(struct net_bridge_port *to, struct sk_buff *skb) |
{ |
if (should_deliver(to, skb)) { |
__br_forward(to, skb); |
return; |
} |
|
kfree_skb(skb); |
} |
|
/* called under bridge lock */ |
static void br_flood(struct net_bridge *br, struct sk_buff *skb, int clone, |
void (*__packet_hook)(struct net_bridge_port *p, struct sk_buff *skb)) |
{ |
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_deliver(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; |
} |
|
__packet_hook(prev, skb2); |
} |
|
prev = p; |
} |
|
p = p->next; |
} |
|
if (prev != NULL) { |
__packet_hook(prev, skb); |
return; |
} |
|
kfree_skb(skb); |
} |
|
/* called under bridge lock */ |
void br_flood_deliver(struct net_bridge *br, struct sk_buff *skb, int clone) |
{ |
br_flood(br, skb, clone, __br_deliver); |
} |
|
/* called under bridge lock */ |
void br_flood_forward(struct net_bridge *br, struct sk_buff *skb, int clone) |
{ |
br_flood(br, skb, clone, __br_forward); |
} |
/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.1.1 2004-04-15 01:16:26 phoenix 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 0x80 |
|
struct br_config_bpdu |
{ |
unsigned topology_change:1; |
unsigned topology_change_ack:1; |
bridge_id root; |
int root_path_cost; |
bridge_id bridge_id; |
port_id port_id; |
int message_age; |
int max_age; |
int hello_time; |
int forward_delay; |
}; |
|
/* br_stp.c */ |
extern void br_become_root_bridge(struct net_bridge *br); |
extern void br_config_bpdu_generation(struct net_bridge *); |
extern void br_configuration_update(struct net_bridge *); |
extern int br_is_designated_port(struct net_bridge_port *p); |
extern int br_is_root_bridge(struct net_bridge *br); |
extern void br_port_state_selection(struct net_bridge *); |
extern void br_received_config_bpdu(struct net_bridge_port *p, struct br_config_bpdu *bpdu); |
extern void br_received_tcn_bpdu(struct net_bridge_port *p); |
extern void br_tick(unsigned long __data); |
extern void br_transmit_config(struct net_bridge_port *p); |
extern void br_transmit_tcn(struct net_bridge *br); |
extern void br_topology_change_detection(struct net_bridge *br); |
|
/* br_stp_bpdu.c */ |
extern void br_send_config_bpdu(struct net_bridge_port *, struct br_config_bpdu *); |
extern void br_send_tcn_bpdu(struct net_bridge_port *); |
|
#endif |
/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.1.1 2004-04-15 01:16:26 phoenix 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->dev.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->dev.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->dev.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->dev.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); |
} |
/br_stp.c
0,0 → 1,475
/* |
* Spanning tree protocol; generic parts |
* Linux ethernet bridge |
* |
* Authors: |
* Lennert Buytenhek <buytenh@gnu.org> |
* |
* $Id: br_stp.c,v 1.1.1.1 2004-04-15 01:16:25 phoenix 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->dev.name); |
|
if (br_is_root_bridge(br)) { |
printk(", propagating"); |
br->topology_change = 1; |
br_timer_set(&br->topology_change_timer, jiffies); |
} else if (!br->topology_change_detected) { |
printk(", sending tcn bpdu"); |
br_transmit_tcn(br); |
br_timer_set(&br->tcn_timer, jiffies); |
} |
|
printk("\n"); |
br->topology_change_detected = 1; |
} |
|
/* called under bridge lock */ |
void br_config_bpdu_generation(struct net_bridge *br) |
{ |
struct net_bridge_port *p; |
|
p = br->port_list; |
while (p != NULL) { |
if (p->state != BR_STATE_DISABLED && |
br_is_designated_port(p)) |
br_transmit_config(p); |
|
p = p->next; |
} |
} |
|
/* called under bridge lock */ |
static void br_reply(struct net_bridge_port *p) |
{ |
br_transmit_config(p); |
} |
|
/* called under bridge lock */ |
void br_configuration_update(struct net_bridge *br) |
{ |
br_root_selection(br); |
br_designated_port_selection(br); |
} |
|
/* called under bridge lock */ |
void br_become_designated_port(struct net_bridge_port *p) |
{ |
struct net_bridge *br; |
|
br = p->br; |
p->designated_root = br->designated_root; |
p->designated_cost = br->root_path_cost; |
p->designated_bridge = br->bridge_id; |
p->designated_port = p->port_id; |
} |
|
/* called under bridge lock */ |
static void br_make_blocking(struct net_bridge_port *p) |
{ |
if (p->state != BR_STATE_DISABLED && |
p->state != BR_STATE_BLOCKING) { |
if (p->state == BR_STATE_FORWARDING || |
p->state == BR_STATE_LEARNING) |
br_topology_change_detection(p->br); |
|
printk(KERN_INFO "%s: port %i(%s) entering %s state\n", |
p->br->dev.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) { |
if (p->br->stp_enabled) { |
printk(KERN_INFO "%s: port %i(%s) entering %s state\n", |
p->br->dev.name, p->port_no, p->dev->name, |
"listening"); |
|
p->state = BR_STATE_LISTENING; |
} else { |
printk(KERN_INFO "%s: port %i(%s) entering %s state\n", |
p->br->dev.name, p->port_no, p->dev->name, |
"learning"); |
|
p->state = BR_STATE_LEARNING; |
} |
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->dev.name, p->port_no, p->dev->name); |
|
br_topology_change_detection(p->br); |
br_topology_change_acknowledge(p); |
} |
read_unlock(&p->br->lock); |
} |
/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.1.1 2004-04-15 01:16:27 phoenix 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; |
} |
/Makefile
0,0 → 1,16
# |
# 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 |
# unless it's something special (ie not a .c file). |
# |
# Note 2! The CFLAGS definition is now in the main makefile... |
|
O_TARGET := bridge.o |
obj-y := 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 |
obj-m := $(O_TARGET) |
|
include $(TOPDIR)/Rules.make |
/br_ioctl.c
0,0 → 1,274
/* |
* Ioctl handler |
* Linux ethernet bridge |
* |
* Authors: |
* Lennert Buytenhek <buytenh@gnu.org> |
* |
* $Id: br_ioctl.c,v 1.1.1.1 2004-04-15 01:16:26 phoenix 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; |
|
memset(&b, 0, sizeof(struct __bridge_info)); |
memcpy(&b.designated_root, &br->designated_root, 8); |
memcpy(&b.bridge_id, &br->bridge_id, 8); |
b.root_path_cost = br->root_path_cost; |
b.max_age = br->max_age; |
b.hello_time = br->hello_time; |
b.forward_delay = br->forward_delay; |
b.bridge_max_age = br->bridge_max_age; |
b.bridge_hello_time = br->bridge_hello_time; |
b.bridge_forward_delay = br->bridge_forward_delay; |
b.topology_change = br->topology_change; |
b.topology_change_detected = br->topology_change_detected; |
b.root_port = br->root_port; |
b.stp_enabled = br->stp_enabled; |
b.ageing_time = br->ageing_time; |
b.gc_interval = br->gc_interval; |
b.hello_timer_value = br_timer_get_residue(&br->hello_timer); |
b.tcn_timer_value = br_timer_get_residue(&br->tcn_timer); |
b.topology_change_timer_value = br_timer_get_residue(&br->topology_change_timer); |
b.gc_timer_value = br_timer_get_residue(&br->gc_timer); |
|
if (copy_to_user((void *)arg0, &b, sizeof(b))) |
return -EFAULT; |
|
return 0; |
} |
|
case BRCTL_GET_PORT_LIST: |
{ |
int i; |
int indices[256]; |
|
for (i=0;i<256;i++) |
indices[i] = 0; |
|
br_get_port_ifindices(br, indices); |
if (copy_to_user((void *)arg0, indices, 256*sizeof(int))) |
return -EFAULT; |
|
return 0; |
} |
|
case BRCTL_SET_BRIDGE_FORWARD_DELAY: |
br->bridge_forward_delay = arg0; |
if (br_is_root_bridge(br)) |
br->forward_delay = arg0; |
return 0; |
|
case BRCTL_SET_BRIDGE_HELLO_TIME: |
br->bridge_hello_time = arg0; |
if (br_is_root_bridge(br)) |
br->hello_time = arg0; |
return 0; |
|
case BRCTL_SET_BRIDGE_MAX_AGE: |
br->bridge_max_age = arg0; |
if (br_is_root_bridge(br)) |
br->max_age = arg0; |
return 0; |
|
case BRCTL_SET_AGEING_TIME: |
br->ageing_time = arg0; |
return 0; |
|
case BRCTL_SET_GC_INTERVAL: |
br->gc_interval = arg0; |
return 0; |
|
case BRCTL_GET_PORT_INFO: |
{ |
struct __port_info p; |
struct net_bridge_port *pt; |
|
if ((pt = br_get_port(br, arg1)) == NULL) |
return -EINVAL; |
|
memset(&p, 0, sizeof(struct __port_info)); |
memcpy(&p.designated_root, &pt->designated_root, 8); |
memcpy(&p.designated_bridge, &pt->designated_bridge, 8); |
p.port_id = pt->port_id; |
p.designated_port = pt->designated_port; |
p.path_cost = pt->path_cost; |
p.designated_cost = pt->designated_cost; |
p.state = pt->state; |
p.top_change_ack = pt->topology_change_ack; |
p.config_pending = pt->config_pending; |
p.message_age_timer_value = br_timer_get_residue(&pt->message_age_timer); |
p.forward_delay_timer_value = br_timer_get_residue(&pt->forward_delay_timer); |
p.hold_timer_value = br_timer_get_residue(&pt->hold_timer); |
|
if (copy_to_user((void *)arg0, &p, sizeof(p))) |
return -EFAULT; |
|
return 0; |
} |
|
case BRCTL_SET_BRIDGE_STP_STATE: |
br->stp_enabled = arg0?1:0; |
return 0; |
|
case BRCTL_SET_BRIDGE_PRIORITY: |
br_stp_set_bridge_priority(br, arg0); |
return 0; |
|
case BRCTL_SET_PORT_PRIORITY: |
{ |
struct net_bridge_port *p; |
|
if ((p = br_get_port(br, arg0)) == NULL) |
return -EINVAL; |
br_stp_set_port_priority(p, arg1); |
return 0; |
} |
|
case BRCTL_SET_PATH_COST: |
{ |
struct net_bridge_port *p; |
|
if ((p = br_get_port(br, arg0)) == NULL) |
return -EINVAL; |
br_stp_set_path_cost(p, arg1); |
return 0; |
} |
|
case BRCTL_GET_FDB_ENTRIES: |
return br_fdb_get_entries(br, (void *)arg0, arg1, arg2); |
} |
|
return -EOPNOTSUPP; |
} |
|
static int br_ioctl_deviceless(unsigned int cmd, |
unsigned long arg0, |
unsigned long arg1) |
{ |
switch (cmd) |
{ |
case BRCTL_GET_VERSION: |
return BRCTL_VERSION; |
|
case BRCTL_GET_BRIDGES: |
{ |
int i; |
int indices[64]; |
|
for (i=0;i<64;i++) |
indices[i] = 0; |
|
if (arg1 > 64) |
arg1 = 64; |
arg1 = br_get_bridge_ifindices(indices, arg1); |
if (copy_to_user((void *)arg0, indices, arg1*sizeof(int))) |
return -EFAULT; |
|
return arg1; |
} |
|
case BRCTL_ADD_BRIDGE: |
case BRCTL_DEL_BRIDGE: |
{ |
char buf[IFNAMSIZ]; |
|
if (copy_from_user(buf, (void *)arg0, IFNAMSIZ)) |
return -EFAULT; |
|
buf[IFNAMSIZ-1] = 0; |
|
if (cmd == BRCTL_ADD_BRIDGE) |
return br_add_bridge(buf); |
|
return br_del_bridge(buf); |
} |
} |
|
return -EOPNOTSUPP; |
} |
|
static 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); |
} |