[转载] Linux Bridge 实现

原文:http://blog.chinaunix.net/uid-26321024-id-2938338.html    

分类: LINUX

 

一、基本数据结构及相互关系
  net_bridge   网桥的结构信息
  net_bridge_port   接在网桥上的一个port
  net_bridge_fdb_entry   mac->port 映射对,用于根据目的mac确定发往哪个端口
1)net_bridge紧随bridge的net_device后面
  1. br = netdev_priv(dev);
2)net_bridge中有所有挂接在该bridge上的net_bridge_port的链表
   net_bridge中有所有net_bridge_fdb_entry的hash表,hash表按mac地址进行hash
  1. struct net_bridge{
  2.   struct list_head port_list;
  3.   ......
  4.   struct hlist_head             hash[BR_HASH_SIZE];
  5. }
3) net_bridge_port 指向它关联的net_device及它attach 的net_bridge
  1. struct net_bridge_port{
  2.  struct net_bridge *br;
  3.  struct net_device *dev;
  4.  u16 port_no;
  5. ...
  6. }
4) net_bridge_fdb_entry 关联了一个mac->port的映射对
  1. struct net_bridge_fdb_entry
  2. {
  3.         struct hlist_node               hlist;
  4.         struct net_bridge_port          *dst;
  5.         struct rcu_head                 rcu;
  6.         unsigned long                   ageing_timer;
  7.         mac_addr                        addr;
  8.         unsigned char                   is_local;  //若为1,表示是本地的,若转往此端口,则直接向上走协议栈
  9.         unsigned char                   is_static; //若为1,表示这个entry是静态的,不会被timer动态超时掉
  10. };
二. 增加一个bridge(brctl addbr)
     1. 通过对一个socket的ioctl来设置

          sock_ioctl -> br_ioctl_hook

        其中br_ioctl_hook在br_init中被设置为br_ioctl_deviceless_stub

          brioctl_set(br_ioctl_deviceless_stub);

2. br_ioctl_deviceless_stub -> br_add_bridge

       生成一个新的net_device

           dev = new_bridge_dev(net, name);

       注册网络设备 register_netdevice(dev)

        增加sysfs entry: br_sysfs_addbr(dev)

三、attatch 一个网络设备到bridge上 (brctl addif )

 1.通过对bridge ioctl来设置

 

  1. br_dev_ioctl:
  2.      case SIOCBRADDIF:
  3.      case SIOCBRDELIF:
  4.         return add_del_if(br, rq->ifr_ifindex, cmd == SIOCBRADDIF);

 

  2. add_del_if -> br_add_if

   Loopback设备和非ethernet设备不能接到网桥上。

  1. if ((dev->flags & IFF_LOOPBACK) ||
  2.        dev->type != ARPHRD_ETHER || dev->addr_len != ETH_ALEN)                return -EINVAL;

 

   不能将网桥接到网桥上:

  1. if (dev->netdev_ops->ndo_start_xmit == br_dev_xmit)
  2.                 return -ELOOP;

 

   不能将一个设备接到多个网桥上:

  1. if (dev->br_port != NULL) //不能将一个设备接在两个bridge上
  2.                 return -EBUSY;

 

   分配一个端口号,创建一个net_bridge_port,将port和dev绑定在一起

  1. p = new_nbp(br, dev);

 

   设置接到网桥上网络设备的混杂模式,否则网络设备就会丢dst mac不是自己的包,就不能起到网桥转发的作用。

  1. err = dev_set_promiscuity(dev, 1);

 

    针对此设备,插入一个fdb项:

        err = br_fdb_insert(br, p, dev->dev_addr);  -> fdb_insert ->fdb_create

   插入的这个fdb项是个静态的: timer触发的过期fdb删除永远不会影响到静态fdb

                       Local的: 命中这个fdb的skb直接走本机协议栈,不用转发。

  1. if (!fdb_create(head, source, addr, 1))
  2.                 return -ENOMEM; 

 

  1. static struct net_bridge_fdb_entry *fdb_create(struct hlist_head *head,
  2.                                                struct net_bridge_port *source,
  3.                                                const unsigned char *addr,
  4.                                                int is_local)
  5. {
  6.         struct net_bridge_fdb_entry *fdb;
  7.         fdb = kmem_cache_alloc(br_fdb_cache, GFP_ATOMIC);
  8.         if (fdb) {
  9.                 memcpy(fdb->addr.addr, addr, ETH_ALEN);
  10.                 hlist_add_head_rcu(&fdb->hlist, head);
  11.  
  12.                 fdb->dst = source;
  13.                 fdb->is_local = is_local;
  14.                 fdb->is_static = is_local;
  15.                 fdb->ageing_timer = jiffies;
  16.         }
  17.         return fdb;
  18. }

 

相互关系设置:

  1. rcu_assign_pointer(dev->br_port, p);
  2.         dev_disable_lro(dev);
  3.  
  4.         list_add_rcu(&p->list, &br->port_list); 

 

通过STP设置port状态:

  1. if ((dev->flags & IFF_UP) && netif_carrier_ok(dev) &&
  2.             (br->dev->flags & IFF_UP))
  3.                 br_stp_enable_port(p);

 

设置bridge的mtu为所连接net_device MTU的最小值

  1. dev_set_mtu(br->dev, br_min_mtu(br));
posted @ 2016-03-30 10:54  YBHello  阅读(598)  评论(0)    收藏  举报