OpenCores
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);
}

powered by: WebSVN 2.1.0

© copyright 1999-2024 OpenCores.org, equivalent to Oliscience, all rights reserved. OpenCores®, registered trademark.