免费注册 查看新帖 |

Chinaunix

  平台 论坛 博客 文库
12345下一页
最近访问板块 发新帖
查看: 18092 | 回复: 47
打印 上一主题 下一主题

Linux网桥源码框架分析初步 [复制链接]

论坛徽章:
0
跳转到指定楼层
1 [收藏(0)] [报告]
发表于 2006-01-12 11:51 |只看该作者 |正序浏览
[这篇贴子是边看代码边写的,其中有些比较凌乱,有些地方有错误,对于贴子的整理和改正,我会将其陆续贴于我的个人主页上边:http://www.skynet.org.cn/forumdisplay.php?fid=12&page=,希望大家指正。2006-2-12,大年十五]

今天处理网桥的STP的问题遇到了麻烦,对这个东东理论的倒是看了不少,没有真真学习到它的源理,来看Linux的实现,手头没有资料,看了两个钟头,只把网桥的框架结构看完,所以想先贴出来,希望有研究这块的大哥们讨论,继续把它写完,九贱好学习一下:

版本:Linux 2.4.18

一、调用
在src/net/core/dev.c的软中断函数static void net_rx_action(struct softirq_action *h)中:
line 1479

  1. #if defined(CONFIG_BRIDGE) || defined(CONFIG_BRIDGE_MODULE)
  2.                         if (skb->dev->br_port != NULL &&
  3.                             br_handle_frame_hook != NULL) {
  4.                                 handle_bridge(skb, pt_prev);
  5.                                 dev_put(rx_dev);
  6.                                 continue;
  7.                         }
  8. #endif
复制代码

如果定义了网桥或网桥模块,则由handle_bridge函数处理
skb->dev->br_port :接收该数据包的端口是网桥端口组的一员
br_handle_frame_hook :定义了网桥处理函数

二、初始化
src/net/bridge/br.c:

  1. static int __init br_init(void)
  2. {
  3.         printk(KERN_INFO "NET4: Ethernet Bridge 008 for NET4.0\n");

  4.         br_handle_frame_hook = br_handle_frame;
  5.         br_ioctl_hook = br_ioctl_deviceless_stub;
  6. #if defined(CONFIG_ATM_LANE) || defined(CONFIG_ATM_LANE_MODULE)
  7.         br_fdb_get_hook = br_fdb_get;
  8.         br_fdb_put_hook = br_fdb_put;
  9. #endif
  10.         register_netdevice_notifier(&br_device_notifier);

  11.         return 0;
  12. }
复制代码

初始化函数指明了网桥的处理函数是br_handle_frame
ioctl处理函数是:br_ioctl_deviceless_stub

三、br_handle_frame(br_input.c)

  1. /*网桥处理函数*/
  2. void br_handle_frame(struct sk_buff *skb)
  3. {
  4.         struct net_bridge *br;
  5.         unsigned char *dest;
  6.         struct net_bridge_port *p;

  7.         /*获取目的MAC地址*/
  8.         dest = skb->mac.ethernet->h_dest;

  9.         /*skb->dev->br_port用于指定接收该数据包的端口,若不是属于网桥的端口,则为NULL*/
  10.         p = skb->dev->br_port;
  11.         if (p == NULL)                /*端口不是网桥组端口中*/
  12.                 goto err_nolock;

  13.         /*本端口所属的网桥组*/
  14.         br = p->br;
  15.        
  16.         /*加锁,因为在转发中需要读CAM表,所以必须加读锁,避免在这个过程中另外的内核控制路径(如多处理机上另外一个CPU上的系统调用)修改CAM表*/
  17.         read_lock(&br->lock);
  18.         if (skb->dev->br_port == NULL)                /*前面判断过的*/
  19.                 goto err;
  20.        
  21.         /*br->dev是网桥的虚拟网卡,如果它未UP,或网桥DISABLED,p->state实际上是桥的当前端口的STP计算判断后的状态*/
  22.         if (!(br->dev.flags & IFF_UP) ||
  23.             p->state == BR_STATE_DISABLED)
  24.                 goto err;
  25.        
  26.         /*源MAC地址为255.X.X.X,即源MAC是多播或广播,丢弃之*/
  27.         if (skb->mac.ethernet->h_source[0] & 1)
  28.                 goto err;

  29.         /*众所周之,网桥之所以是网桥,比HUB更智能,是因为它有一个MAC-PORT的表,这样转发数据就不用广播,而查表定端口就可以了
  30.         每次收到一个包,网桥都会学习其来源MAC,添加进这个表。Linux中这个表叫CAM表(这个名字是其它资料上看的)。
  31.         如果桥的状态是LEARNING或FORWARDING(学习或转发),则学习该包的源地址skb->mac.ethernet->h_source,
  32.         将其添加到CAM表中,如果已经存在于表中了,则更新定时器,br_fdb_insert完成了这一过程*/
  33.         if (p->state == BR_STATE_LEARNING ||
  34.             p->state == BR_STATE_FORWARDING)
  35.                 br_fdb_insert(br, p, skb->mac.ethernet->h_source, 0);
  36.        
  37.         /*STP协议的BPDU包的目的MAC采用的是多播目标MAC地址:从01-80-c2-00-00-00(Bridge_group_addr:网桥组多播地址)开始
  38.         所以这里是如果开启了STP,而当前数据包又是一个BPDU
  39.         (!memcmp(dest, bridge_ula, 5),        unsigned char bridge_ula[6] = { 0x01, 0x80, 0xc2, 0x00, 0x00, 0x00 };),
  40.         则交由相应函数处理*/
  41.         if (br->stp_enabled &&
  42. /*这里只比较前5个字节,没有仔细研究过STP是使用了全部多播地址(从0 1 : 0 0 : 5 e : 0 0 : 0 0 : 0 0到0 1 : 0 0 : 5 e : 7 f : ff : ff。),还是只使用了一部份,这里看来似乎只是一部份,没去深究了*/
  43.             !memcmp(dest, bridge_ula, 5) &&
  44.             !(dest[5] & 0xF0))                /*01-80-c2-00-00-F0 是一个什么地址?为什么要判断呢?*/
  45.                 goto handle_special_frame;
  46.        
  47.         /*处理钩子函数,然后转交br_handle_frame_finish函数继续处理*/
  48.         if (p->state == BR_STATE_FORWARDING) {
  49.                 NF_HOOK(PF_BRIDGE, NF_BR_PRE_ROUTING, skb, skb->dev, NULL,
  50.                         br_handle_frame_finish);
  51.                 read_unlock(&br->lock);
  52.                 return;
  53.         }

  54. err:
  55.         read_unlock(&br->lock);
  56. err_nolock:
  57.         kfree_skb(skb);
  58.         return;

  59. handle_special_frame:
  60.         if (!dest[5]) {
  61.                 br_stp_handle_bpdu(skb);
  62.                 return;
  63.         }

  64.         kfree_skb(skb);
  65. }
复制代码

四、br_handle_frame_finish

  1. static int br_handle_frame_finish(struct sk_buff *skb)
  2. {
  3.         struct net_bridge *br;
  4.         unsigned char *dest;
  5.         struct net_bridge_fdb_entry *dst;
  6.         struct net_bridge_port *p;
  7.         int passedup;

  8.         /*前面基本相同*/
  9.         dest = skb->mac.ethernet->h_dest;


  10.         p = skb->dev->br_port;
  11.         if (p == NULL)
  12.                 goto err_nolock;

  13.         br = p->br;
  14.         read_lock(&br->lock);
  15.         if (skb->dev->br_port == NULL)
  16.                 goto err;

  17.         passedup = 0;
  18.        
  19.         /*如果网桥的虚拟网卡处于混杂模式,那么每个接收到的数据包都需要克隆一份
  20.         送到AF_PACKET协议处理体(网络软中断函数net_rx_action中ptype_all链的处理)。*/
  21.         if (br->dev.flags & IFF_PROMISC) {
  22.                 struct sk_buff *skb2;

  23.                 skb2 = skb_clone(skb, GFP_ATOMIC);
  24.                 if (skb2 != NULL) {
  25.                         passedup = 1;
  26.                         br_pass_frame_up(br, skb2);
  27.                 }
  28.         }

  29.         /*目的MAC为广播或多播,则需要向本机的上层协议栈传送这个数据包,这里有一个标志变量passedup
  30.         用于表示是否传送过了,如果已传送过,那就算了*/
  31.         if (dest[0] & 1) {
  32.                 br_flood_forward(br, skb, !passedup);
  33.                 if (!passedup)
  34.                         br_pass_frame_up(br, skb);
  35.                 goto out;
  36.         }
  37.        
  38.         /*Linux中的MAC-PORT表是CAM表,这里根据目的地址来查表,以确定由哪个接口把包转发出去
  39.         每一个表项是通过结构struct net_bridge_fdb_entry来描述的:
  40.         struct net_bridge_fdb_entry
  41.         {
  42.                 struct net_bridge_fdb_entry        *next_hash;                //用于CAM表连接的链表指针
  43.                 struct net_bridge_fdb_entry        **pprev_hash;                //为什么是pprev不是prev呢?还没有仔细去研究
  44.                 atomic_t                        use_count;                //此项当前的引用计数器
  45.                 mac_addr                        addr;                        //MAC地址
  46.                 struct net_bridge_port                *dst;                        //此项所对应的物理端口
  47.                 unsigned long                        ageing_timer;                //处理MAC超时
  48.                 unsigned                        is_local:1;                //是否是本机的MAC地址
  49.                 unsigned                        is_static:1;                //是否是静态MAC地址
  50.         };*/
  51.         dst = br_fdb_get(br, dest);
  52.        
  53.         /*查询CAM表后,如果能够找到表项,并且目的MAC是到本机的虚拟网卡的,那么就需要把这个包提交给上层协议,
  54.         这样,我们就可以通过这个虚拟网卡的地址来远程管理网桥了*/
  55.         if (dst != NULL && dst->is_local) {
  56.                 if (!passedup)
  57.                         br_pass_frame_up(br, skb);
  58.                 else
  59.                         kfree_skb(skb);
  60.                 br_fdb_put(dst);
  61.                 goto out;
  62.         }
  63.        
  64.         /*查到表了,且不是本地虚拟网卡的,转发之*/
  65.         if (dst != NULL) {
  66.                 br_forward(dst->dst, skb);
  67.                 br_fdb_put(dst);
  68.                 goto out;
  69.         }

  70.         /*如果表里边查不到,那么只好学习学习HUB了……*/
  71.         br_flood_forward(br, skb, 0);

  72. out:
  73.         read_unlock(&br->lock);
  74.         return 0;

  75. err:
  76.         read_unlock(&br->lock);
  77. err_nolock:
  78.         kfree_skb(skb);
  79.         return 0;
  80. }
复制代码

基本框架就是这样了,与那些讲网桥原理的书上讲的基本差不多……
网桥之所以是网桥,主要靠这两个函数:
br_fdb_insert
br_fdb_get
一个学习,一个查表;
另外,支持STP,处理BPDU,需要用到函数br_stp_handle_bpdu
哪位有这三个函数的细节分析,可否送九贱一份,免得下午那么辛苦再去啃代码……

扫了一下 br_fdb_insert,结构还是很清析,如果当前项已存在于hash表项中,则更新它(__fdb_possibly_replace),如果是新项,则插入,实际是一个双向链表的维护过程(__hash_link):

  1. void br_fdb_insert(struct net_bridge *br,
  2.                    struct net_bridge_port *source,
  3.                    unsigned char *addr,
  4.                    int is_local)
  5. {
  6.         struct net_bridge_fdb_entry *fdb;
  7.         int hash;

  8.         hash = br_mac_hash(addr);

  9.         write_lock_bh(&br->hash_lock);
  10.         fdb = br->hash[hash];
  11.         while (fdb != NULL) {
  12.                 if (!fdb->is_local &&
  13.                     !memcmp(fdb->addr.addr, addr, ETH_ALEN)) {
  14.                         __fdb_possibly_replace(fdb, source, is_local);
  15.                         write_unlock_bh(&br->hash_lock);
  16.                         return;
  17.                 }

  18.                 fdb = fdb->next_hash;
  19.         }

  20.         fdb = kmalloc(sizeof(*fdb), GFP_ATOMIC);
  21.         if (fdb == NULL) {
  22.                 write_unlock_bh(&br->hash_lock);
  23.                 return;
  24.         }

  25.         memcpy(fdb->addr.addr, addr, ETH_ALEN);
  26.         atomic_set(&fdb->use_count, 1);
  27.         fdb->dst = source;
  28.         fdb->is_local = is_local;
  29.         fdb->is_static = is_local;
  30.         fdb->ageing_timer = jiffies;

  31.         __hash_link(br, fdb, hash);

  32.         write_unlock_bh(&br->hash_lock);
  33. }
复制代码

同样,查表也是一个遍历链表,进行地址匹配的过程:

  1. struct net_bridge_fdb_entry *br_fdb_get(struct net_bridge *br, unsigned char *addr)
  2. {
  3.         struct net_bridge_fdb_entry *fdb;

  4.         read_lock_bh(&br->hash_lock);
  5.         fdb = br->hash[br_mac_hash(addr)];
  6.         while (fdb != NULL) {
  7.                 if (!memcmp(fdb->addr.addr, addr, ETH_ALEN)) {
  8.                         if (!has_expired(br, fdb)) {
  9.                                 atomic_inc(&fdb->use_count);
  10.                                 read_unlock_bh(&br->hash_lock);
  11.                                 return fdb;
  12.                         }

  13.                         read_unlock_bh(&br->hash_lock);
  14.                         return NULL;
  15.                 }

  16.                 fdb = fdb->next_hash;
  17.         }

  18.         read_unlock_bh(&br->hash_lock);
  19.         return NULL;
  20. }
复制代码

[ 本帖最后由 platinum 于 2006-6-22 10:05 编辑 ]

论坛徽章:
0
48 [报告]
发表于 2006-05-24 23:13 |只看该作者

非prism2的无线网卡能否设置无线网桥与有线网通信?

我现在linux中,用的无线芯片是philip的bgw200,用它能否构建无线接入点?如果不行,能不能用linux内置的网桥功能构建一个网桥,实现无线网和有线网的桥接?请高手指点!

论坛徽章:
0
47 [报告]
发表于 2006-01-26 11:08 |只看该作者
原帖由 Pagliuca 于 2006-1-25 16:51 发表
/* called under bridge lock */
int br_is_designated_port(struct net_bridge_port *p)
{
        return !memcmp(&p->designated_bridge, &p->br->bridge_id, &&
            ...


前阵子好像讨论过吧,这个函数只是“查看”,不是“判断”……,如果我没有看错的话,最近在忙一个东东,没有时间来搞这个了!

论坛徽章:
0
46 [报告]
发表于 2006-01-25 17:17 |只看该作者
支持一下了

论坛徽章:
0
45 [报告]
发表于 2006-01-25 16:51 |只看该作者
/* 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);
}
这几天没有看网桥,今天有时间又看了一下,觉得对于判断指定端口这个函数还是有些疑惑,希望大侠能说得清楚一些:
    上面的判断应该是:第一步,看端口的所在的网桥ID是否是指定网桥;第二步,看端口ID是否是指定端口ID。
    我看的资料理解的是:判断是否是指定端口,是看这个网桥的BPDU的信息:1根端口ID,2开销,3发送网桥ID。这3个组成的BPDU是否优于网桥从这个端口收到的BPDU。如果网桥的BPDU优,则这个端口是网桥的指定端口,网桥会往这些指定端口发送自己的BPDU。这些指定端口和根端口都是生成树的一部分。
    怎么跟这个函数写的意思不太一样呢?

[ 本帖最后由 Pagliuca 于 2006-1-25 17:01 编辑 ]

论坛徽章:
0
44 [报告]
发表于 2006-01-20 08:57 |只看该作者
原帖由 casonic 于 2006-1-19 18:31 发表
精彩!
不知道大侠有没有研究过brctl这个工具?
可不可以解释一下这个工具
里面有命令类似brctl addif br0 eth1 //eth1 is one of my NIC card
为什么不是brctl addif br0 0x8000


brctl是一个很简单的网桥管理工具,就是通过ioctl与内核交互,我自己重写了一个图形界面的,运行得很好^o^

论坛徽章:
0
43 [报告]
发表于 2006-01-19 18:31 |只看该作者

brctl

精彩!
不知道大侠有没有研究过brctl这个工具?
可不可以解释一下这个工具
里面有命令类似brctl addif br0 eth1 //eth1 is one of my NIC card
为什么不是brctl addif br0 0x8000

论坛徽章:
0
42 [报告]
发表于 2006-01-19 11:45 |只看该作者
原帖由 Pagliuca 于 2006-1-19 11:22 发表
如果对于有3个以上端口的网桥,designated port是可能有2个或者更多个的,难道LINUX网桥就是指的2个端口的网桥吗?


端口是不限的……所以有个遍历所有端口的过程:
br_configuration_update->br_designated_port_selection:

/* 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;
        }
}
br_should_become_designated_port为判断,br_become_designated_port为指定,请注意这个while循环,它是遍历桥中所有的端口;

论坛徽章:
0
41 [报告]
发表于 2006-01-19 11:22 |只看该作者
如果对于有3个以上端口的网桥,designated port是可能有2个或者更多个的,难道LINUX网桥就是指的2个端口的网桥吗?

论坛徽章:
0
40 [报告]
发表于 2006-01-19 09:39 |只看该作者
原帖由 Pagliuca 于 2006-1-18 18:15 发表
br_is_designated_port函数的是看当前桥是否就是指定的根桥,并且当前port 是否就是designate port:
/* called under bridge lock */
int br_is_designated_port(struct net_bridge_port *p)
{
        retu ...


br_is_designated_port函数是看当前端口是否是指定端口,struct net_bridge_port 用来描述一个端口,p就是待断判断的端口……而不是根桥,桥之类的东东,请仔细看我上面的代码分析吧……
  

北京盛拓优讯信息技术有限公司. 版权所有 京ICP备16024965号-6 北京市公安局海淀分局网监中心备案编号:11010802020122 niuxiaotong@pcpop.com 17352615567
未成年举报专区
中国互联网协会会员  联系我们:huangweiwei@itpub.net
感谢所有关心和支持过ChinaUnix的朋友们 转载本站内容请注明原作者名及出处

清除 Cookies - ChinaUnix - Archiver - WAP - TOP