OpenCores
URL https://opencores.org/ocsvn/or1k/or1k/trunk

Subversion Repositories or1k

[/] [or1k/] [trunk/] [linux/] [linux-2.4/] [net/] [bridge/] [br_fdb.c] - Diff between revs 1275 and 1765

Only display areas with differences | Details | Blame | View Log

Rev 1275 Rev 1765
/*
/*
 *      Forwarding database
 *      Forwarding database
 *      Linux ethernet bridge
 *      Linux ethernet bridge
 *
 *
 *      Authors:
 *      Authors:
 *      Lennert Buytenhek               <buytenh@gnu.org>
 *      Lennert Buytenhek               <buytenh@gnu.org>
 *
 *
 *      $Id: br_fdb.c,v 1.1.1.1 2004-04-15 01:16:25 phoenix Exp $
 *      $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
 *      This program is free software; you can redistribute it and/or
 *      modify it under the terms of the GNU General Public License
 *      modify it under the terms of the GNU General Public License
 *      as published by the Free Software Foundation; either version
 *      as published by the Free Software Foundation; either version
 *      2 of the License, or (at your option) any later version.
 *      2 of the License, or (at your option) any later version.
 */
 */
 
 
#include <linux/kernel.h>
#include <linux/kernel.h>
#include <linux/spinlock.h>
#include <linux/spinlock.h>
#include <linux/if_bridge.h>
#include <linux/if_bridge.h>
#include <asm/atomic.h>
#include <asm/atomic.h>
#include <asm/uaccess.h>
#include <asm/uaccess.h>
#include "br_private.h"
#include "br_private.h"
 
 
static __inline__ unsigned long __timeout(struct net_bridge *br)
static __inline__ unsigned long __timeout(struct net_bridge *br)
{
{
        unsigned long timeout;
        unsigned long timeout;
 
 
        timeout = jiffies - br->ageing_time;
        timeout = jiffies - br->ageing_time;
        if (br->topology_change)
        if (br->topology_change)
                timeout = jiffies - br->forward_delay;
                timeout = jiffies - br->forward_delay;
 
 
        return timeout;
        return timeout;
}
}
 
 
static __inline__ int has_expired(struct net_bridge *br,
static __inline__ int has_expired(struct net_bridge *br,
                                  struct net_bridge_fdb_entry *fdb)
                                  struct net_bridge_fdb_entry *fdb)
{
{
        if (!fdb->is_static &&
        if (!fdb->is_static &&
            time_before_eq(fdb->ageing_timer, __timeout(br)))
            time_before_eq(fdb->ageing_timer, __timeout(br)))
                return 1;
                return 1;
 
 
        return 0;
        return 0;
}
}
 
 
static __inline__ void copy_fdb(struct __fdb_entry *ent, struct net_bridge_fdb_entry *f)
static __inline__ void copy_fdb(struct __fdb_entry *ent, struct net_bridge_fdb_entry *f)
{
{
        memset(ent, 0, sizeof(struct __fdb_entry));
        memset(ent, 0, sizeof(struct __fdb_entry));
        memcpy(ent->mac_addr, f->addr.addr, ETH_ALEN);
        memcpy(ent->mac_addr, f->addr.addr, ETH_ALEN);
        ent->port_no = f->dst?f->dst->port_no:0;
        ent->port_no = f->dst?f->dst->port_no:0;
        ent->is_local = f->is_local;
        ent->is_local = f->is_local;
        ent->ageing_timer_value = 0;
        ent->ageing_timer_value = 0;
        if (!f->is_static)
        if (!f->is_static)
                ent->ageing_timer_value = jiffies - f->ageing_timer;
                ent->ageing_timer_value = jiffies - f->ageing_timer;
}
}
 
 
static __inline__ int br_mac_hash(unsigned char *mac)
static __inline__ int br_mac_hash(unsigned char *mac)
{
{
        unsigned long x;
        unsigned long x;
 
 
        x = mac[0];
        x = mac[0];
        x = (x << 2) ^ mac[1];
        x = (x << 2) ^ mac[1];
        x = (x << 2) ^ mac[2];
        x = (x << 2) ^ mac[2];
        x = (x << 2) ^ mac[3];
        x = (x << 2) ^ mac[3];
        x = (x << 2) ^ mac[4];
        x = (x << 2) ^ mac[4];
        x = (x << 2) ^ mac[5];
        x = (x << 2) ^ mac[5];
 
 
        x ^= x >> 8;
        x ^= x >> 8;
 
 
        return x & (BR_HASH_SIZE - 1);
        return x & (BR_HASH_SIZE - 1);
}
}
 
 
static __inline__ void __hash_link(struct net_bridge *br,
static __inline__ void __hash_link(struct net_bridge *br,
                                   struct net_bridge_fdb_entry *ent,
                                   struct net_bridge_fdb_entry *ent,
                                   int hash)
                                   int hash)
{
{
        ent->next_hash = br->hash[hash];
        ent->next_hash = br->hash[hash];
        if (ent->next_hash != NULL)
        if (ent->next_hash != NULL)
                ent->next_hash->pprev_hash = &ent->next_hash;
                ent->next_hash->pprev_hash = &ent->next_hash;
        br->hash[hash] = ent;
        br->hash[hash] = ent;
        ent->pprev_hash = &br->hash[hash];
        ent->pprev_hash = &br->hash[hash];
}
}
 
 
static __inline__ void __hash_unlink(struct net_bridge_fdb_entry *ent)
static __inline__ void __hash_unlink(struct net_bridge_fdb_entry *ent)
{
{
        *(ent->pprev_hash) = ent->next_hash;
        *(ent->pprev_hash) = ent->next_hash;
        if (ent->next_hash != NULL)
        if (ent->next_hash != NULL)
                ent->next_hash->pprev_hash = ent->pprev_hash;
                ent->next_hash->pprev_hash = ent->pprev_hash;
        ent->next_hash = NULL;
        ent->next_hash = NULL;
        ent->pprev_hash = NULL;
        ent->pprev_hash = NULL;
}
}
 
 
 
 
 
 
void br_fdb_changeaddr(struct net_bridge_port *p, unsigned char *newaddr)
void br_fdb_changeaddr(struct net_bridge_port *p, unsigned char *newaddr)
{
{
        struct net_bridge *br;
        struct net_bridge *br;
        int i;
        int i;
 
 
        br = p->br;
        br = p->br;
        write_lock_bh(&br->hash_lock);
        write_lock_bh(&br->hash_lock);
        for (i=0;i<BR_HASH_SIZE;i++) {
        for (i=0;i<BR_HASH_SIZE;i++) {
                struct net_bridge_fdb_entry *f;
                struct net_bridge_fdb_entry *f;
 
 
                f = br->hash[i];
                f = br->hash[i];
                while (f != NULL) {
                while (f != NULL) {
                        if (f->dst == p && f->is_local) {
                        if (f->dst == p && f->is_local) {
                                __hash_unlink(f);
                                __hash_unlink(f);
                                memcpy(f->addr.addr, newaddr, ETH_ALEN);
                                memcpy(f->addr.addr, newaddr, ETH_ALEN);
                                __hash_link(br, f, br_mac_hash(newaddr));
                                __hash_link(br, f, br_mac_hash(newaddr));
                                write_unlock_bh(&br->hash_lock);
                                write_unlock_bh(&br->hash_lock);
                                return;
                                return;
                        }
                        }
                        f = f->next_hash;
                        f = f->next_hash;
                }
                }
        }
        }
        write_unlock_bh(&br->hash_lock);
        write_unlock_bh(&br->hash_lock);
}
}
 
 
void br_fdb_cleanup(struct net_bridge *br)
void br_fdb_cleanup(struct net_bridge *br)
{
{
        int i;
        int i;
        unsigned long timeout;
        unsigned long timeout;
 
 
        timeout = __timeout(br);
        timeout = __timeout(br);
 
 
        write_lock_bh(&br->hash_lock);
        write_lock_bh(&br->hash_lock);
        for (i=0;i<BR_HASH_SIZE;i++) {
        for (i=0;i<BR_HASH_SIZE;i++) {
                struct net_bridge_fdb_entry *f;
                struct net_bridge_fdb_entry *f;
 
 
                f = br->hash[i];
                f = br->hash[i];
                while (f != NULL) {
                while (f != NULL) {
                        struct net_bridge_fdb_entry *g;
                        struct net_bridge_fdb_entry *g;
 
 
                        g = f->next_hash;
                        g = f->next_hash;
                        if (!f->is_static &&
                        if (!f->is_static &&
                            time_before_eq(f->ageing_timer, timeout)) {
                            time_before_eq(f->ageing_timer, timeout)) {
                                __hash_unlink(f);
                                __hash_unlink(f);
                                br_fdb_put(f);
                                br_fdb_put(f);
                        }
                        }
                        f = g;
                        f = g;
                }
                }
        }
        }
        write_unlock_bh(&br->hash_lock);
        write_unlock_bh(&br->hash_lock);
}
}
 
 
void br_fdb_delete_by_port(struct net_bridge *br, struct net_bridge_port *p)
void br_fdb_delete_by_port(struct net_bridge *br, struct net_bridge_port *p)
{
{
        int i;
        int i;
 
 
        write_lock_bh(&br->hash_lock);
        write_lock_bh(&br->hash_lock);
        for (i=0;i<BR_HASH_SIZE;i++) {
        for (i=0;i<BR_HASH_SIZE;i++) {
                struct net_bridge_fdb_entry *f;
                struct net_bridge_fdb_entry *f;
 
 
                f = br->hash[i];
                f = br->hash[i];
                while (f != NULL) {
                while (f != NULL) {
                        struct net_bridge_fdb_entry *g;
                        struct net_bridge_fdb_entry *g;
 
 
                        g = f->next_hash;
                        g = f->next_hash;
                        if (f->dst == p) {
                        if (f->dst == p) {
                                __hash_unlink(f);
                                __hash_unlink(f);
                                br_fdb_put(f);
                                br_fdb_put(f);
                        }
                        }
                        f = g;
                        f = g;
                }
                }
        }
        }
        write_unlock_bh(&br->hash_lock);
        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 *br_fdb_get(struct net_bridge *br, unsigned char *addr)
{
{
        struct net_bridge_fdb_entry *fdb;
        struct net_bridge_fdb_entry *fdb;
 
 
        read_lock_bh(&br->hash_lock);
        read_lock_bh(&br->hash_lock);
        fdb = br->hash[br_mac_hash(addr)];
        fdb = br->hash[br_mac_hash(addr)];
        while (fdb != NULL) {
        while (fdb != NULL) {
                if (!memcmp(fdb->addr.addr, addr, ETH_ALEN)) {
                if (!memcmp(fdb->addr.addr, addr, ETH_ALEN)) {
                        if (!has_expired(br, fdb)) {
                        if (!has_expired(br, fdb)) {
                                atomic_inc(&fdb->use_count);
                                atomic_inc(&fdb->use_count);
                                read_unlock_bh(&br->hash_lock);
                                read_unlock_bh(&br->hash_lock);
                                return fdb;
                                return fdb;
                        }
                        }
 
 
                        read_unlock_bh(&br->hash_lock);
                        read_unlock_bh(&br->hash_lock);
                        return NULL;
                        return NULL;
                }
                }
 
 
                fdb = fdb->next_hash;
                fdb = fdb->next_hash;
        }
        }
 
 
        read_unlock_bh(&br->hash_lock);
        read_unlock_bh(&br->hash_lock);
        return NULL;
        return NULL;
}
}
 
 
void br_fdb_put(struct net_bridge_fdb_entry *ent)
void br_fdb_put(struct net_bridge_fdb_entry *ent)
{
{
        if (atomic_dec_and_test(&ent->use_count))
        if (atomic_dec_and_test(&ent->use_count))
                kfree(ent);
                kfree(ent);
}
}
 
 
int br_fdb_get_entries(struct net_bridge *br,
int br_fdb_get_entries(struct net_bridge *br,
                       unsigned char *_buf,
                       unsigned char *_buf,
                       int maxnum,
                       int maxnum,
                       int offset)
                       int offset)
{
{
        int i;
        int i;
        int num;
        int num;
        struct __fdb_entry *walk;
        struct __fdb_entry *walk;
 
 
        num = 0;
        num = 0;
        walk = (struct __fdb_entry *)_buf;
        walk = (struct __fdb_entry *)_buf;
 
 
        read_lock_bh(&br->hash_lock);
        read_lock_bh(&br->hash_lock);
        for (i=0;i<BR_HASH_SIZE;i++) {
        for (i=0;i<BR_HASH_SIZE;i++) {
                struct net_bridge_fdb_entry *f;
                struct net_bridge_fdb_entry *f;
 
 
                f = br->hash[i];
                f = br->hash[i];
                while (f != NULL && num < maxnum) {
                while (f != NULL && num < maxnum) {
                        struct __fdb_entry ent;
                        struct __fdb_entry ent;
                        int err;
                        int err;
                        struct net_bridge_fdb_entry *g;
                        struct net_bridge_fdb_entry *g;
                        struct net_bridge_fdb_entry **pp;
                        struct net_bridge_fdb_entry **pp;
 
 
                        if (has_expired(br, f)) {
                        if (has_expired(br, f)) {
                                f = f->next_hash;
                                f = f->next_hash;
                                continue;
                                continue;
                        }
                        }
 
 
                        if (offset) {
                        if (offset) {
                                offset--;
                                offset--;
                                f = f->next_hash;
                                f = f->next_hash;
                                continue;
                                continue;
                        }
                        }
 
 
                        copy_fdb(&ent, f);
                        copy_fdb(&ent, f);
 
 
                        atomic_inc(&f->use_count);
                        atomic_inc(&f->use_count);
                        read_unlock_bh(&br->hash_lock);
                        read_unlock_bh(&br->hash_lock);
                        err = copy_to_user(walk, &ent, sizeof(struct __fdb_entry));
                        err = copy_to_user(walk, &ent, sizeof(struct __fdb_entry));
                        read_lock_bh(&br->hash_lock);
                        read_lock_bh(&br->hash_lock);
 
 
                        g = f->next_hash;
                        g = f->next_hash;
                        pp = f->pprev_hash;
                        pp = f->pprev_hash;
                        br_fdb_put(f);
                        br_fdb_put(f);
 
 
                        if (err)
                        if (err)
                                goto out_fault;
                                goto out_fault;
 
 
                        if (g == NULL && pp == NULL)
                        if (g == NULL && pp == NULL)
                                goto out_disappeared;
                                goto out_disappeared;
 
 
                        num++;
                        num++;
                        walk++;
                        walk++;
 
 
                        f = g;
                        f = g;
                }
                }
        }
        }
 
 
 out:
 out:
        read_unlock_bh(&br->hash_lock);
        read_unlock_bh(&br->hash_lock);
        return num;
        return num;
 
 
 out_disappeared:
 out_disappeared:
        num = -EAGAIN;
        num = -EAGAIN;
        goto out;
        goto out;
 
 
 out_fault:
 out_fault:
        num = -EFAULT;
        num = -EFAULT;
        goto out;
        goto out;
}
}
 
 
static __inline__ void __fdb_possibly_replace(struct net_bridge_fdb_entry *fdb,
static __inline__ void __fdb_possibly_replace(struct net_bridge_fdb_entry *fdb,
                                              struct net_bridge_port *source,
                                              struct net_bridge_port *source,
                                              int is_local)
                                              int is_local)
{
{
        if (!fdb->is_static || is_local) {
        if (!fdb->is_static || is_local) {
                fdb->dst = source;
                fdb->dst = source;
                fdb->is_local = is_local;
                fdb->is_local = is_local;
                fdb->is_static = is_local;
                fdb->is_static = is_local;
                fdb->ageing_timer = jiffies;
                fdb->ageing_timer = jiffies;
        }
        }
}
}
 
 
void br_fdb_insert(struct net_bridge *br,
void br_fdb_insert(struct net_bridge *br,
                   struct net_bridge_port *source,
                   struct net_bridge_port *source,
                   unsigned char *addr,
                   unsigned char *addr,
                   int is_local)
                   int is_local)
{
{
        struct net_bridge_fdb_entry *fdb;
        struct net_bridge_fdb_entry *fdb;
        int hash;
        int hash;
 
 
        hash = br_mac_hash(addr);
        hash = br_mac_hash(addr);
 
 
        write_lock_bh(&br->hash_lock);
        write_lock_bh(&br->hash_lock);
        fdb = br->hash[hash];
        fdb = br->hash[hash];
        while (fdb != NULL) {
        while (fdb != NULL) {
                if (!memcmp(fdb->addr.addr, addr, ETH_ALEN)) {
                if (!memcmp(fdb->addr.addr, addr, ETH_ALEN)) {
                        /* attempt to update an entry for a local interface */
                        /* attempt to update an entry for a local interface */
                        if (fdb->is_local) {
                        if (fdb->is_local) {
                                if (is_local)
                                if (is_local)
                                        printk(KERN_INFO "%s: attempt to add"
                                        printk(KERN_INFO "%s: attempt to add"
                                               " interface with same source address.\n",
                                               " interface with same source address.\n",
                                               source->dev->name);
                                               source->dev->name);
                                else if (net_ratelimit())
                                else if (net_ratelimit())
                                        printk(KERN_WARNING "%s: received packet with "
                                        printk(KERN_WARNING "%s: received packet with "
                                               " own address as source address\n",
                                               " own address as source address\n",
                                               source->dev->name);
                                               source->dev->name);
                                goto out;
                                goto out;
                        }
                        }
 
 
                        __fdb_possibly_replace(fdb, source, is_local);
                        __fdb_possibly_replace(fdb, source, is_local);
                        goto out;
                        goto out;
                }
                }
 
 
                fdb = fdb->next_hash;
                fdb = fdb->next_hash;
        }
        }
 
 
        fdb = kmalloc(sizeof(*fdb), GFP_ATOMIC);
        fdb = kmalloc(sizeof(*fdb), GFP_ATOMIC);
        if (fdb == NULL)
        if (fdb == NULL)
                goto out;
                goto out;
 
 
        memcpy(fdb->addr.addr, addr, ETH_ALEN);
        memcpy(fdb->addr.addr, addr, ETH_ALEN);
        atomic_set(&fdb->use_count, 1);
        atomic_set(&fdb->use_count, 1);
        fdb->dst = source;
        fdb->dst = source;
        fdb->is_local = is_local;
        fdb->is_local = is_local;
        fdb->is_static = is_local;
        fdb->is_static = is_local;
        fdb->ageing_timer = jiffies;
        fdb->ageing_timer = jiffies;
 
 
        __hash_link(br, fdb, hash);
        __hash_link(br, fdb, hash);
 
 
 out:
 out:
        write_unlock_bh(&br->hash_lock);
        write_unlock_bh(&br->hash_lock);
}
}
 
 

powered by: WebSVN 2.1.0

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