Chinaunix首页 | 论坛 | 博客
  • 博客访问: 2762163
  • 博文数量: 79
  • 博客积分: 30130
  • 博客等级: 大将
  • 技术积分: 2608
  • 用 户 组: 普通用户
  • 注册时间: 2007-10-22 14:58
个人简介

博所搬至http://xiaogr.com

文章存档

2015年(2)

2009年(3)

2008年(56)

2007年(18)

分类: LINUX

2007-10-25 18:39:27

------------------------------------------

本文系本站原创,欢迎转载!

转载请注明出处:http://ericxiao.cublog.cn/

------------------------------------------

前面已经分析了,将接口添进网桥时,用户空间调用ioctl(br_socket_fd, SIOCBRADDIF, &ifr)

注意到在void br_dev_setup(struct net_device *dev)中已经对dev->do_ioctl进行了赋值,即:

dev->do_ioctl = br_dev_ioctl

进行ioctl进行访问的时候,进入到br_dev_ioctl:                                                                  (net/brige/br_ioctl.c)

int br_dev_ioctl(struct net_device *dev, struct ifreq *rq, int cmd)

{

         struct net_bridge *br = netdev_priv(dev);

         switch(cmd) {

         case SIOCDEVPRIVATE:

                   return old_dev_ioctl(dev, rq, cmd);

         //添加一个接口

         case SIOCBRADDIF:

         //删除一个接口

         case SIOCBRDELIF:

                   return add_del_if(br, rq->ifr_ifindex, cmd == SIOCBRADDIF);

 

         }

 

         pr_debug("Bridge does not support ioctl 0x%x\n", cmd);

         return -EOPNOTSUPP;

}

我们在用户空间使用的标志是SIOCBRADDIF。所以流程进入add_del_if()

static int add_del_if(struct net_bridge *br, int ifindex, int isadd)                   

{

         struct net_device *dev;

         int ret;

         if (!capable(CAP_NET_ADMIN))

                   return -EPERM;

         dev = dev_get_by_index(ifindex);

         if (dev == NULL)

                   return -EINVAL;

         if (isadd)

                   ret = br_add_if(br, dev);

         else

                   ret = br_del_if(br, dev);

         dev_put(dev);

         return ret;

}

因为cmd == SIOCBRADDIF为真,所以调用br_add_if():

int br_add_if(struct net_bridge *br, struct net_device *dev)                           (net/brige/br_if.c))

{

         struct net_bridge_port *p;

         int err = 0;

 

         //回环。或者非以及网接口

         if (dev->flags & IFF_LOOPBACK || dev->type != ARPHRD_ETHER)

                   return -EINVAL;

         //构造数据包函数为网桥类型

         if (dev->hard_start_xmit == br_dev_xmit)

                   return -ELOOP;

         //此接口已经存在于网桥

         if (dev->br_port != NULL)

                   return -EBUSY;

 

         //dev 创建网桥接口.dev->br_port。指向所属网桥端口

         //dev->br_port->br:指向它所属的网桥

         //为该接口创建net_bridge_port

         if (IS_ERR(p = new_nbp(br, dev, br_initial_port_cost(dev))))

                   return PTR_ERR(p);

 

         //更新port->MAC对应表

        if ((err = br_fdb_insert(br, p, dev->dev_addr, 1)))

                   destroy_nbp(p);

 

         else if ((err = br_sysfs_addif(p)))

                   del_nbp(p);

         else {

                   //设置接口为混杂模式

                   dev_set_promiscuity(dev, 1);

                   //p->list更新至br->port_list

                   list_add_rcu(&p->list, &br->port_list);

 

                   spin_lock_bh(&br->lock);

                   br_stp_recalculate_bridge_id(br);

                   if ((br->dev->flags & IFF_UP)

                       && (dev->flags & IFF_UP) && netif_carrier_ok(dev))

                            br_stp_enable_port(p);

                   spin_unlock_bh(&br->lock);

 

                   dev_set_mtu(br->dev, br_min_mtu(br));

         }

 

         return err;

}

为接口创建net_bridge_port的函数为new_nbp。这个函数比较简单:

static struct net_bridge_port *new_nbp(struct net_bridge *br,

                                            struct net_device *dev,

                                            unsigned long cost)

{

         int index;

         struct net_bridge_port *p;

        

         index = find_portno(br);

         if (index < 0)

                   return ERR_PTR(index);

 

         p = kmalloc(sizeof(*p), GFP_KERNEL);

         if (p == NULL)

                   return ERR_PTR(-ENOMEM);

 

         memset(p, 0, sizeof(*p));

         p->br = br;

         dev_hold(dev);

         p->dev = dev;

         p->path_cost = cost;

        p->priority = 0x8000 >> BR_PORT_BITS;

         dev->br_port = p;

         p->port_no = index;

         br_init_port(p);

         p->state = BR_STATE_DISABLED;

         kobject_init(&p->kobj);

 

         return p;

}

之后,把要加入的接口对应的mac与接口作为本机静态项加入到prot—mac对应表。这是在br_fdb_insert()中实现的

int br_fdb_insert(struct net_bridge *br, struct net_bridge_port *source,

                     const unsigned char *addr, int is_local)

{

         int ret;

 

         spin_lock_bh(&br->hash_lock);

         ret = fdb_insert(br, source, addr, is_local);

         spin_unlock_bh(&br->hash_lock);

         return ret;

}

操作存在异步性,在插入之前加锁。具体的插入在fdb_insert中实现

static int fdb_insert(struct net_bridge *br, struct net_bridge_port *source,

                     const unsigned char *addr, int is_local)

{

         struct hlist_node *h;

         struct net_bridge_fdb_entry *fdb;

         int hash = br_mac_hash(addr);

 

         //判断是否为有效的mac 地址

         if (!is_valid_ether_addr(addr))

                   return -EADDRNOTAVAIL;

 

         hlist_for_each_entry(fdb, h, &br->hash[hash], hlist) {

                   //如果表中已经包含了此项

                   if (!memcmp(fdb->addr.addr, addr, ETH_ALEN)) {

                            //如果为本机MAC

                            /* attempt to update an entry for a local interface */

                            if (fdb->is_local) {

                                     /* it is okay to have multiple ports with same

                                      * address, just don't allow to be spoofed.

                                      */

                                     if (is_local)

                                               return 0;

 

                                     if (net_ratelimit())

                                               printk(KERN_WARNING "%s: received packet with "

                                                      " own address as source address\n",

                                                      source->dev->name);

                                     return -EEXIST;

                            }

 

                            //如果添加的是本机IP

                            if (is_local) {

                                     printk(KERN_WARNING "%s adding interface with same address "

                                            "as a received packet\n",

                                            source->dev->name);

                                     goto update;

                            }

 

                            //如果添加的是静态MAC

                            //则不更新相关的信息

                            if (fdb->is_static)

                                     return 0;

 

                            /* move to end of age list */

                            list_del(&fdb->u.age_list);

                            goto update;

                   }

         }

 

         fdb = kmem_cache_alloc(br_fdb_cache, GFP_ATOMIC);

         if (!fdb)

                   return ENOMEM;

 

         memcpy(fdb->addr.addr, addr, ETH_ALEN);

         atomic_set(&fdb->use_count, 1);

         hlist_add_head_rcu(&fdb->hlist, &br->hash[hash]);

 

         if (!timer_pending(&br->gc_timer)) {

                   br->gc_timer.expires = jiffies + hold_time(br);

                   add_timer(&br->gc_timer);

         }

 

 update:

         fdb->dst = source;

         fdb->is_local = is_local;

         fdb->is_static = is_local;

         fdb->ageing_timer = jiffies;

         if (!is_local)

                   list_add_tail(&fdb->u.age_list, &br->age_list);

 

         return 0;

}

此函数先判断要插入项是否存在,若是已存在,且不为静态项,具更新对应项。若不存在该项,则分配一个net_bridge_fdb_entry,插入到CAM

 

先来分析一下net_bridge_port的结构:

struct net_bridge_port

{

         //当前端口所在的briage

         struct net_bridge          *br;

         //此端口对应的物理端口

         struct net_device          *dev;

         //同一桥内的端口链表?

         struct list_head             list;

 

         /* STP */

         u8                                 priority;

         u8                                 state;

         u16                               port_no;

         unsigned char                         topology_change_ack;

         unsigned char                         config_pending;

         port_id                                   port_id;

         port_id                                   designated_port;

         bridge_id                       designated_root;

         bridge_id                       designated_bridge;

         u32                               path_cost;

         u32                               designated_cost;

 

         struct timer_list            forward_delay_timer;

         struct timer_list            hold_timer;

         struct timer_list            message_age_timer;

         struct kobject                         kobj;

         struct rcu_head                      rcu;

};

 对应的net_bridge_fdb_entry结构:

//CAM表中对应的数据结构

struct net_bridge_fdb_entry

{

         //用于CAM表连接的链表指针

         struct hlist_node          hlist;           

         //此项对应的物理出口

         struct net_bridge_port           *dst;

         union {

                   struct list_head   age_list;

                   struct rcu_head             rcu;

         } u;

 

         //此项的当前的引用计数

         atomic_t                       use_count;

         //超时时间

         unsigned long                         ageing_timer;

         //MAC地址

         mac_addr                      addr;

         //是否为主机地址

         unsigned char                         is_local;

         //是否为静态地址

         unsigned char                         is_static;

};

 

struct net_bridge_port

{

         //当前端口所在的briage

         struct net_bridge          *br;

         //此端口对应的物理端口

         struct net_device          *dev;

         //同一桥内的端口链表?

         struct list_head            list;

 

         /* STP */

         u8                                 priority;

         u8                                 state;

         u16                               port_no;

         unsigned char                         topology_change_ack;

         unsigned char                         config_pending;

         port_id                                   port_id;

         port_id                                   designated_port;

         bridge_id                       designated_root;

         bridge_id                       designated_bridge;

         u32                                path_cost;

         u32                               designated_cost;

 

         struct timer_list            forward_delay_timer;

         struct timer_list            hold_timer;

         struct timer_list            message_age_timer;

         struct kobject                         kobj;

         struct rcu_head                      rcu;

};

桥模块中的结构太多,一一总结下,免的下次看

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