patch-2.3.47 linux/net/bridge/br_fdb.c
Next file: linux/net/bridge/br_forward.c
Previous file: linux/net/bridge/br_device.c
Back to the patch index
Back to the overall index
- Lines: 319
- Date:
Sun Feb 20 10:41:18 2000
- Orig file:
v2.3.46/linux/net/bridge/br_fdb.c
- Orig date:
Wed Dec 31 16:00:00 1969
diff -u --recursive --new-file v2.3.46/linux/net/bridge/br_fdb.c linux/net/bridge/br_fdb.c
@@ -0,0 +1,318 @@
+/*
+ * Forwarding database
+ * Linux ethernet bridge
+ *
+ * Authors:
+ * Lennert Buytenhek <buytenh@gnu.org>
+ *
+ * $Id: br_fdb.c,v 1.1 2000/02/18 16:47:12 davem Exp $
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ */
+
+#include <linux/kernel.h>
+#include <linux/if_bridge.h>
+#include <asm/atomic.h>
+#include <asm/spinlock.h>
+#include <asm/uaccess.h>
+#include "br_private.h"
+
+static __inline__ unsigned long __timeout(struct net_bridge *br)
+{
+ unsigned long timeout;
+
+ timeout = jiffies - br->ageing_time;
+ if (br->topology_change)
+ timeout = jiffies - br->forward_delay;
+
+ return timeout;
+}
+
+static __inline__ int has_expired(struct net_bridge *br,
+ struct net_bridge_fdb_entry *fdb)
+{
+ if (!fdb->is_static &&
+ time_before_eq(fdb->ageing_timer, __timeout(br)))
+ return 1;
+
+ return 0;
+}
+
+static __inline__ void copy_fdb(struct __fdb_entry *ent, struct net_bridge_fdb_entry *f)
+{
+ memcpy(ent->mac_addr, f->addr.addr, ETH_ALEN);
+ ent->port_no = f->dst?f->dst->port_no:0;
+ ent->is_local = f->is_local;
+ ent->ageing_timer_value = 0;
+ if (!f->is_static)
+ ent->ageing_timer_value = jiffies - f->ageing_timer;
+}
+
+static __inline__ int br_mac_hash(unsigned char *mac)
+{
+ unsigned long x;
+
+ x = mac[0];
+ x = (x << 2) ^ mac[1];
+ x = (x << 2) ^ mac[2];
+ x = (x << 2) ^ mac[3];
+ x = (x << 2) ^ mac[4];
+ x = (x << 2) ^ mac[5];
+
+ x ^= x >> 8;
+
+ return x & (BR_HASH_SIZE - 1);
+}
+
+static __inline__ void __hash_link(struct net_bridge *br,
+ struct net_bridge_fdb_entry *ent,
+ int hash)
+{
+ ent->next_hash = br->hash[hash];
+ if (ent->next_hash != NULL)
+ ent->next_hash->pprev_hash = &ent->next_hash;
+ br->hash[hash] = ent;
+ ent->pprev_hash = &br->hash[hash];
+}
+
+static __inline__ void __hash_unlink(struct net_bridge_fdb_entry *ent)
+{
+ *(ent->pprev_hash) = ent->next_hash;
+ if (ent->next_hash != NULL)
+ ent->next_hash->pprev_hash = ent->pprev_hash;
+ ent->next_hash = NULL;
+ ent->pprev_hash = NULL;
+}
+
+
+
+void br_fdb_changeaddr(struct net_bridge_port *p, unsigned char *newaddr)
+{
+ struct net_bridge *br;
+ int i;
+
+ br = p->br;
+ write_lock_bh(&br->hash_lock);
+ for (i=0;i<BR_HASH_SIZE;i++) {
+ struct net_bridge_fdb_entry *f;
+
+ f = br->hash[i];
+ while (f != NULL) {
+ if (f->dst == p && f->is_local) {
+ __hash_unlink(f);
+ memcpy(f->addr.addr, newaddr, ETH_ALEN);
+ __hash_link(br, f, br_mac_hash(newaddr));
+ write_unlock_bh(&br->hash_lock);
+ return;
+ }
+ f = f->next_hash;
+ }
+ }
+ write_unlock_bh(&br->hash_lock);
+}
+
+void br_fdb_cleanup(struct net_bridge *br)
+{
+ int i;
+ unsigned long timeout;
+
+ timeout = __timeout(br);
+
+ write_lock_bh(&br->hash_lock);
+ for (i=0;i<BR_HASH_SIZE;i++) {
+ struct net_bridge_fdb_entry *f;
+
+ f = br->hash[i];
+ while (f != NULL) {
+ struct net_bridge_fdb_entry *g;
+
+ g = f->next_hash;
+ if (!f->is_static &&
+ time_before_eq(f->ageing_timer, timeout)) {
+ __hash_unlink(f);
+ br_fdb_put(f);
+ }
+ f = g;
+ }
+ }
+ write_unlock_bh(&br->hash_lock);
+}
+
+void br_fdb_delete_by_port(struct net_bridge *br, struct net_bridge_port *p)
+{
+ int i;
+
+ write_lock_bh(&br->hash_lock);
+ for (i=0;i<BR_HASH_SIZE;i++) {
+ struct net_bridge_fdb_entry *f;
+
+ f = br->hash[i];
+ while (f != NULL) {
+ struct net_bridge_fdb_entry *g;
+
+ g = f->next_hash;
+ if (f->dst == p) {
+ __hash_unlink(f);
+ br_fdb_put(f);
+ }
+ f = g;
+ }
+ }
+ write_unlock_bh(&br->hash_lock);
+}
+
+struct net_bridge_fdb_entry *br_fdb_get(struct net_bridge *br, unsigned char *addr)
+{
+ struct net_bridge_fdb_entry *fdb;
+
+ read_lock_bh(&br->hash_lock);
+ fdb = br->hash[br_mac_hash(addr)];
+ while (fdb != NULL) {
+ if (!memcmp(fdb->addr.addr, addr, ETH_ALEN)) {
+ if (!has_expired(br, fdb)) {
+ atomic_inc(&fdb->use_count);
+ read_unlock_bh(&br->hash_lock);
+ return fdb;
+ }
+
+ read_unlock_bh(&br->hash_lock);
+ return NULL;
+ }
+
+ fdb = fdb->next_hash;
+ }
+
+ read_unlock_bh(&br->hash_lock);
+ return NULL;
+}
+
+void br_fdb_put(struct net_bridge_fdb_entry *ent)
+{
+ if (atomic_dec_and_test(&ent->use_count))
+ kfree(ent);
+}
+
+int br_fdb_get_entries(struct net_bridge *br,
+ unsigned char *_buf,
+ int maxnum,
+ int offset)
+{
+ int i;
+ int num;
+ struct __fdb_entry *walk;
+
+ num = 0;
+ walk = (struct __fdb_entry *)_buf;
+
+ read_lock_bh(&br->hash_lock);
+ for (i=0;i<BR_HASH_SIZE;i++) {
+ struct net_bridge_fdb_entry *f;
+
+ f = br->hash[i];
+ while (f != NULL && num < maxnum) {
+ struct __fdb_entry ent;
+ int err;
+ struct net_bridge_fdb_entry *g;
+
+ if (has_expired(br, f)) {
+ f = f->next_hash;
+ continue;
+ }
+
+ if (offset) {
+ offset--;
+ f = f->next_hash;
+ continue;
+ }
+
+ copy_fdb(&ent, f);
+
+ atomic_inc(&f->use_count);
+ read_unlock_bh(&br->hash_lock);
+ err = copy_to_user(walk, &ent, sizeof(struct __fdb_entry));
+ read_lock_bh(&br->hash_lock);
+
+ g = f->next_hash;
+ br_fdb_put(f);
+
+ if (err)
+ goto out_fault;
+
+ if (f->next_hash == NULL &&
+ f->pprev_hash == NULL)
+ goto out_disappeared;
+
+ num++;
+ walk++;
+
+ f = g;
+ }
+ }
+
+ out:
+ read_unlock_bh(&br->hash_lock);
+ return num;
+
+ out_disappeared:
+ num = -EAGAIN;
+ goto out;
+
+ out_fault:
+ num = -EFAULT;
+ goto out;
+}
+
+static __inline__ void __fdb_possibly_replace(struct net_bridge_fdb_entry *fdb,
+ struct net_bridge_port *source,
+ int is_local)
+{
+ if (!fdb->is_static || is_local) {
+ fdb->dst = source;
+ fdb->is_local = is_local;
+ fdb->is_static = is_local;
+ fdb->ageing_timer = jiffies;
+ }
+}
+
+void br_fdb_insert(struct net_bridge *br,
+ struct net_bridge_port *source,
+ unsigned char *addr,
+ int is_local)
+{
+ struct net_bridge_fdb_entry *fdb;
+ int hash;
+
+ hash = br_mac_hash(addr);
+
+ write_lock_bh(&br->hash_lock);
+ fdb = br->hash[hash];
+ while (fdb != NULL) {
+ if (!memcmp(fdb->addr.addr, addr, ETH_ALEN)) {
+ __fdb_possibly_replace(fdb, source, is_local);
+ write_unlock_bh(&br->hash_lock);
+ return;
+ }
+
+ fdb = fdb->next_hash;
+ }
+
+ fdb = kmalloc(sizeof(*fdb), GFP_ATOMIC);
+ if (fdb == NULL) {
+ write_unlock_bh(&br->hash_lock);
+ return;
+ }
+
+ memcpy(fdb->addr.addr, addr, ETH_ALEN);
+ atomic_set(&fdb->use_count, 1);
+ fdb->dst = source;
+ fdb->is_local = is_local;
+ fdb->is_static = is_local;
+ fdb->ageing_timer = jiffies;
+
+ __hash_link(br, fdb, hash);
+
+ write_unlock_bh(&br->hash_lock);
+}
FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen (who was at: slshen@lbl.gov)