Chinaunix首页 | 论坛 | 博客
  • 博客访问: 22508
  • 博文数量: 6
  • 博客积分: 245
  • 博客等级: 二等列兵
  • 技术积分: 70
  • 用 户 组: 普通用户
  • 注册时间: 2010-10-29 16:46
文章分类
文章存档

2010年(6)

我的朋友

分类: LINUX

2010-10-29 16:57:52

    Linux Bridge 基于 MAC 地址来转发包, MAC 地址表是 Linux Bridge 的核心数据结构,在 br_fdb.c 中定义了一组操作 MAC 地址表的接口函数。

    MAC 地址表的数据结构定义在 br_private.h 中。

kernel 2.6.21.7 /net/bridge/br_private.h

struct net_bridge_fdb_entry
{
    struct hlist_node hlist;
    struct net_bridge_port *dst;


    struct rcu_head rcu;
    atomic_t use_count;
    unsigned long ageing_timer;
    mac_addr addr;
    unsigned char is_local;
    unsigned char is_static;
};

struct net_bridge_port
{
    struct net_bridge *br;
    struct net_device *dev;
    struct list_head list;
    ......
};

struct net_bridge
{
    spinlock_t lock;
    struct list_head port_list;
    struct net_device *dev;
    struct net_device_stats statistics;
    spinlock_t hash_lock;
    struct hlist_head hash[BR_HASH_SIZE];
    struct list_head age_list;
    unsigned long feature_mask;
    ......
};

kmem_cache_alloc()

kmem_cache_free()


    net_bridge 上的 MAC 地址表以 Hash 表的形式存放在数据成员 hash 中,它是一个链表数组。由 MAC 地址进行 Hash,以此值作为数组下表,就得到 MAC 地址所在的链表,链表的节点是就是结构体 net_bridge_fdb_entry,即为 MAC 地址表中的表项。

    结构体 net_bridge_fdb_entry 中:
    hlist 为此表项所在的链表;
    dst 为此学习到此 MAC 地址的端口,学习时,它是源端口,转发时,它是目的端口;
    rcu 用于同步;
    use_count 为引用计数,每个表项都是采用 kmem_cache_alloc() 动态分配,当引用计数为0时,表项被 kmem_cache_free() 释放;
     ageing_timer 用于 MAC 地址表老化;
    addr 为 MAC 地址;
     is_local 为 1 表示这是一个本地 MAC 地址,即桥或桥上的端口对应的 MAC 地址。桥接收到的包,如果根据目的 MAC 地址找到的表项为本地 MAC,则将其上交协议栈。桥不可能通过学习得到本地 MAC 地址(收到源 MAC 地址等于设备自己的 MAC 地址的包是一个错误),向桥上添加端口时,桥才会得到本地 MAC 地址表项,此表项同时也是一个静态表项(见下文)。端口的 MAC 地址改变而触发 MAC 地址表的更新时需要处理本地 MAC 地址表项,删除桥上的端口或是直接删除桥时,需要删除本地 MAC 地址表项。这时需要特别注意,桥上的多个端口可能具有相同的 MAC 地址,而一个 MAC 地址在 MAC 地址表中只能对应一个表项,表项的 dst 可能为任一个端口,当一个端口的 MAC 地址改变或是某个端口被从桥上删除时,老的 MAC 地址对用的表项可能需要删除(桥上没有其它端口具有此 MAC 地址),也可能只需要更新其 dst (桥上还有其它端口具有此 MAC 地址);
    is_static 为 1 表示这是一个静态 MAC 地址表项。静态 MAC 地址表项不会被老化。

    初始化和清理函数:

kernel 2.6.21.7 /net/bridge/br_fdb.c


/* 后备高速缓存,每个 MAC 地址表项都从其中分配。 */

static struct kmem_cache *br_fdb_cache __read_mostly;


/* 创建后备高速缓存,br.c: br_init() 中调用。 */
void __init br_fdb_init(void);


/* 释放后备高速缓存,br.c: br_deinit() 中调用。 */
void __exit br_fdb_fini(void);


/* 释放 MAC 地址表项,br.c: br_deinit() 中调用。 */
static void fdb_rcu_free(struct rcu_head *head)



    辅助函数:

kernel 2.6.21.7 /net/bridge/br_fdb.c


/* 返回老化延迟时间。 */

static __inline__ unsigned long hold_time(const struct net_bridge *br);


/* MAC 地址表项老化,返回1;否则返回0。 */
static __inline__ int has_expired(const struct net_bridge *br, 

    const struct net_bridge_fdb_entry *fdb);


/* 返回 MAC 地址的 Hash 值,用于随意对应的链表头。 */
static __inline__ int br_mac_hash(const unsigned char *mac);



    主要函数:

kernel 2.6.21.7 /net/bridge/br_fdb.c

/* 此函数处理 MAC 地址学习。

 * 此函数在桥处理接收到的数据包时调用,br_input.c。

 ********************************************************************

 *  br_input.c: br_handle_frame() 处理桥收到的数据包。

 *  对于目的 MAC 为 01:80:C2:00:00:0X 的包它调用 br_handle_local_finish(),

 *      该函数调用 br_fdb_update() 学习源 MAC,然后由上层继续处理,

 *  对于其它的包,它调用 br_br_handle_frame_finish()处理,

 *     该函数调用 br_fdb_update() 学习源 MAC,然后 或转发或洪泛或交协议栈。

 ********************************************************************

 *  此函数首先调用 fdb_find() 在 MAC 地址表中查找对应的表项。

 *  如已存在该表项,如果该表项为本地 MAC 地址表项,则打印警告信息,

 * "%s: received packet with own address as source address\n"

* 如前所述,桥不可能通过学习得到本地 MAC 地址(收到源 MAC 地址等于设

 * 备自己的 MAC 地址的包是一个错误)。否则,刷新该表项,设置该表项中

 * 的端口为本次接收到数据包的端口,重置老化时间(ageing_timer)。

 * 对于我们的设备,此时通过钩子调用相应的函数刷新设备的硬件表项。

 *  如不存在该表项,则调用 fdb_create() 创建并插入新的 MAC 地址表项,注意

 * 新表项被设为非本地非静态表项。

 *      同样fdb_create() 此时通过钩子调用相应的函数创建设备的硬件表项。

 * /

void br_fdb_update(struct net_bridge *br, struct net_bridge_port *source,
          const unsigned char *addr);


/* 此函数清理 MAC 地址表中的过时表项,重置有效表项。

 * 此函数由内核定时器驱动,创建桥时将创建此定时器,net_bridge.gc_timer

 * 此函数被 EXPORT_SYMBOL(br_fdb_cleanup)。

 * 此函数遍历整个 MAC 地址表,检查每个表项是否过期(静态表项永不过期)。

 * 对于没有过期的表项,此函数不做任何处理。

 * 对于过期表项,此函数通过钩子调用相应函数检查设备中的硬件表项是否有效。

 * 如果有效,则重置该表项的老化时间ageing_timer)。

 * 如果无效,则调用 fdb_delete() 删除该表项。

 * fdb_delete() 又调用 br_fdb_put() 做进一步处理。

 * br_fdb_put() 通过钩子调用相应的函数删除设备的硬件表项。

 *  并且检查该表项的引用计数,决定是否释放该表项。

 * /
void br_fdb_cleanup(unsigned long _data);


/* 桥端口的 MAC 地址改变时,此函数被触发,br_notify.c: br_device_event()。

 * 此函数插入新的 MAC 地址表项,删除或修改原 MAC 地址对应的表项。

 * 此函数遍历整个 MAC 地址表,寻找桥端口对应的本地 MAC 地址表项。

 * 如果存在该表项,当桥上还有其它端口具有原 MAC 地址,则更新表项,使此

 * 表项属于其它端口(表项的 dst)。如果没有其它端口具有原 MAC

 * 地址,则调用 fdb_delete() 删除该表项。

 * br_fdb_put() 通过钩子调用相应的函数删除设备的硬件表项。

 * 之后调用 fdb_insert() 插入新的本地静态 MAC 地址表项。

 ********************************************************************

 * fdb_insert() 首先在 MAC 地址表中查找新 MAC 地址对应的表项。

如果存在对应的本地表项,什么也不做,函数返回。

如果存在非本地表项,则调用 fdb_delete() 删除该表项,并打印

错误信息(网络上可能有设备的 MAC 地址与新设定的

桥端口 MAC 地址相同,MAC地址表中存在非本地表项,

表示这可能是从收到的数据包中学习到的)。

br_fdb_put() 通过钩子删除设备的硬件表项。

 * 之后调用 fdb_create() 创建并插入新的 MAC 地址表项,注意新表项

 * 被设为本地静态表项。

 * 同样fdb_create() 此时通过钩子调用相应的函数创建设备的硬件表项。

 ********************************************************************

 * /

void br_fdb_changeaddr(struct net_bridge_port *p, 

const unsigned char *newaddr);



    配置接口函数:

kernel 2.6.21./net/bridge/br_fdb.c


/* 此函数在 MAC 地址表中插入一个本地静态表项。

 * 此函数在向桥上新增端口时使用,使用新增端口的 MAC 地址构造本地静态表项。

 * 此函数由 br_if.c: br_add_if() 调用。

 * 此函数调用 fdb_insert() 插入新的本地静态 MAC 地址表项。

 * 同样fdb_create() 此时通过钩子调用相应的函数创建设备的硬件表项。

 * /

int br_fdb_insert(struct net_bridge *br, struct net_bridge_port *source,
                 const unsigned char *addr);


/* 此函数删除 MAC 地址表中对应某个桥端口的非静态表项。

 * 此函数在删除桥端口或是直接删除桥本身时使用,还用于禁用某个桥端口或桥时。

 * 此函数由 br_if.c: del_nbp() 和 br_stp_if.c: br_stp_disable_port() 调用。

 * 此函数遍历整个 MAC 地址表项,忽略静态表项和对应其他桥端口的表项,

 * 对于目标端口的本地非静态表项,如桥上还有其它端口的 MAC 地址与目标

 * 端口相同,则更新该表项,使此表项属于其它端口(表项的 dst)。

 * 此函数调用 fdb_delete() 删除对应目标端口的非静态表项。

 * 这时,br_fdb_put() 通过钩子删除设备的硬件表项。

 ********************************************************************

 * 注意:MAC 地址表中的本地表项一般添加桥端口时通过 fdb_insert() 创建

 * 本地表项一般都是静态表项,桥通过学习得到的是非本地非静态表项,

 * 但可以手工向 MAC 地址表中增加非本地静态表项。

 * 当 do_all 为0时,忽略静态表项,此时本地表项也被忽略,

 * 当 do_all 为1时,静态表项不被忽略,也要被 fdb_delete() 删除,

 * 而本地静态表项则按上述讨论处理。

 * /

void br_fdb_delete_by_port(struct net_bridge *br,
                     const struct net_bridge_port *p, int do_all);


/* 此函数向 MAC 地址表中增加非本地静态表项。

 * 此函数被 EXPORT_SYMBOL(br_fdb_add),但未见被调用处。

 * 此函数调用 fdb_find() 在 MAC 地址表中查找对应的表项,

 * 如果找到该表项,再检查该表项是否为静态表项,对于静态表项,函数

 * 不做处理,直接返回;对于非静态表项,先调用 fdb_delete()

 * 删除该表项,再调用 fdb_add() 插入新的非本地静态表项。

 * 如果没找到表项,调用 fdb_add() 插入新的非本地静态表项。

 * 注意fdb_add() 通过钩子调用相应的函数创建设备的硬件表项。

 * br_fdb_put() 通过钩子删除设备的硬件表项。

 * /

void br_fdb_add(struct net_bridge *br,
              struct net_bridge_port *source,
            const unsigned char *addr);



/* 此函数删除 MAC 地址表中的表项。

 * 此函数被 EXPORT_SYMBOL(br_fdb_delete),但未见被调用处。

 * 此函数调用 fdb_find() 在 MAC 地址表中查找对应的表项。如存在该表项,

 * 调用 fdb_delete() 删除该表项。

 *  br_fdb_put() 通过钩子删除设备的硬件表项。

 * /

void br_fdb_delete(struct net_bridge *br,
              struct net_bridge_port *source,
            const unsigned char *addr);


/* 此函数取出 MAC 地址表中的若干项。

 * 此函数由 br_ioctl.c: get_fdb_entries() 调用。

 * 此函数遍历整个 MAC 地址表,忽略过期表项,跳过前skip项,取出maxnum项。

 *  将取出的表项填入调用者提供的 buf 中。

 * 

 * 此函数不再调用其他内部接口函数。

 * /

int br_fdb_fillbuf(struct net_bridge *br, void *buf,
                 unsigned long maxnum, unsigned long skip);


/* 此函数根据 MAC 地址查找对应的未过期表项,通过遍历 MAC 地址表实现。

 * 桥发包时需要调用此函数,br_device.c: br_dev_xmit();

 * 桥收包时也需调用此函数,br_input.c: br_handle_frmae_finish();

 * 桥收发包都是用此函数查找目的 MAC 地址对应的表项,来转发数据包。

 * br_fdb_get() 是此函数的再封装,增加处理同步和引用计数,用在ATM中。

 * 此函数不再调用其他内部接口函数。

 * /

struct net_bridge_fdb_entry *__br_fdb_get(struct net_bridge *br,
                     const unsigned char *addr);


/* 此函数根据 MAC 地址查找对应的未过期表项。

 * 此函数是 __br_fdb_get() 的再封装,增加处理同步和引用计数,用在ATM中。

 * 钩子 br_fdb_get_hook 指向此函数,在 br.c: br_init() 中初始化;

 * 钩子 br_fdb_put_hook 指向 br_fdb_put()在 br.c: br_init() 中初始化;

 * 这两个钩子在 ATM 中使用。

 * /

struct net_bridge_fdb_entry *br_fdb_get(struct net_bridge *br,
                                    unsigned char *addr)


    内部接口函数:


kernel 2.6.21./net/bridge/br_fdb.c


/* 此函数 通过钩子调用相应的函数删除设备的硬件表项。

 *  并且检查该表项的引用计数,为0时释放该表项。

 * 此函数被 fdb_delete() 调用,fdb_delete() 删除 MAC 地址表中的表项。

 * 此函数不再调用其他内部接口函数。

 */

void br_fdb_put(struct net_bridge_fdb_entry *ent);



/* 此函数删除 MAC 地址表中的表项。

 *  并调用 br_fdb_put() 处理硬件表项和必要时释放该表项。

 *   br_fdb_put() 通过钩子删除设备的硬件表项。 

 *  此函数为删除 MAC 地址表项的唯一接口,被多出调用。

 * / 

static __inline__ void fdb_delete(struct net_bridge_fdb_entry *f);


/* 此函数创建并插入新的 MAC 地址表项,此函数处理表项的内存分配。

 * 此函数 通过钩子调用相应的函数创建设备的硬件表项。

 * 此函数插入的表项为本地静态,或者是非本地非静态。

 * fdb_insert() 调用此函数插入本地静态表项。

 * br_fdb_update() 调用此函数插入非本地非静态表项。

 * 此函数不再调用其他内部接口函数。

 *********************************************************************

 * 创建新表项的总结,及几个接口函数的区别如下:

 * 桥通过 MAC 地址学习得到非本地非静态表项

 * 这通过 br_fdb_update() 调用 fdb_create() 来实现;

 * 当端口加入桥时,或者桥端口 MAC 地址改变时,桥得到本地静态表项

 * 这通过 fdb_insert() 调用 fdb_create() 来实现;

 * fdb_insert() 被 br_fdb_insert()  br_fdb_changeaddr() 调用。

 * 桥只有通过手动配置能得到非本地静态表项

 * 这通过 br_fdb_add() 调用 fdb_add() 来实现;

 * /

static struct net_bridge_fdb_entry *fdb_create(struct hlist_head *head,
                     struct net_bridge_port *source,
                     const unsigned char *addr,
                     int is_local);


/* 此函数创建并插入新的 MAC 地址表项,此函数处理表项的内存分配。

 * 此函数 通过钩子调用相应的函数创建设备的硬件表项。

 * 此函数插入的表项,本地(is_local)和静态(is_static)标志是相互独立的。

 * br_fdb_add() 调用此函数插入非本地静态表项。

 * 此函数不再调用其他内部接口函数。

 * /

static struct net_bridge_fdb_entry *fdb_add(struct hlist_head *head,
                     struct net_bridge_port *source,
                     const unsigned char *addr,
                     int is_local,
                     int is_static);


/* 此函数插入新的本地静态 MAC 地址表项,需要先检查 MAC 地址表项。

 * 此函数首先在 MAC 地址表中查找新 MAC 地址对应的表项。

 * 如果存在对应的本地表项,什么也不做,函数返回。

 * 如果存在非本地表项,则调用 fdb_delete() 删除该表项,并打印

 * 错误信息(网络上可能有设备的 MAC 地址与新设定的桥端口

 * MAC 地址相同,MAC地址表中存在非本地表项,表示这可能是

 * 从收到的数据包中学习到的。

 * br_fdb_put() 通过钩子删除设备的硬件表项。

 * 之后调用 fdb_create() 创建并插入新的本地静态 MAC 地址表项。

 * 此函数 br_fdb_changeaddr()  br_fdb_insert() 调用。

 * /

static int fdb_insert(struct net_bridge *br, struct net_bridge_port *source,
         const unsigned char *addr);

/* 此函数遍历 MAC 地址表项,根据 MAC 地址查找对应的表项。

 * 此函数的调用者包括:

 * br_fdb_changeaddr() 学习 MAC 地址;

 * fdb_insert() 插入新的本地静态 MAC 地址表项;

 * br_fdb_add() 向 MAC 地址表中增加非本地静态表项,未见被调用处;

 * br_fdb_delte() 删除 MAC 地址表中的表项,未见被调用处。

 * 此函数不再调用其他内部接口函数。

 **********************************************************************

 * 此函数与 __br_fdb_get() 的区别在于:

 * __br_fdb_get() 根据 MAC 地址计算 Hash 值,此函数忽略过期表项;

 * fdb_find() 由调用者根据 MAC 地址计算 Hash 值,此函数不忽略过期表项。

 * __br_fdb_get() 根在桥收发包时调用,根据目的 MAC 地址查找表项;

 * fdb_find() 维护 MAC 地址时使用,根据源 MAC 或设备 MAC 维护地址表。

 * /

static inline struct net_bridge_fdb_entry *fdb_find(struct hlist_head *head,
                         const unsigned char *addr);


阅读(5567) | 评论(0) | 转发(1) |
给主人留下些什么吧!~~