分享

linux协议栈之网桥实现之二

 enchen008 2012-04-10

面已经分析接口添进网桥时,户空间调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%xn", 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 addressn",
                            source->dev->name);
                   return -EEXIST;
              }
              //如果添加本机IP
              if (is_local) {
                   printk(KERN_WARNING "%s adding interface with same address "
                       "as a received packetn",
                       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;};

这样,就往桥添加个接口,从以反应出接口关系。brctl show指令看所有桥,以及桥里相应接口。ifconfig br0以看当状态,如果细心以看,bro已经有MAC。这怎么呢?桥MAC地址更新:注意br_add_if函数br_stp_recalculate_bridge_id()代码分析简化分析,stp相关流程忽略掉,现们看这个函数做些什么/* called under bridge lock */
void br_stp_recalculate_bridge_id(struct net_bridge *br)
{
     const unsigned char *addr = br_mac_zero;
     struct net_bridge_port *p;
     //遍历桥所有端口
     list_for_each_entry(p, &br->port_list, list) {
          //所有接口MAC最少值
          if (addr == br_mac_zero ||
            memcmp(p->dev->dev_addr, addr, ETH_ALEN) < 0)
              addr = p->dev->dev_addr;
     }
     //如果不MAC相同
     if (memcmp(br->bridge_id.addr, addr, ETH_ALEN))
          br_stp_change_bridge_id(br, addr);
}

这个函数比较简单,遍历桥所有接口,然MAC。然判断最MAC跟现MAC否相同继续跟踪br_stp_change_bridge_idstatic void br_stp_change_bridge_id(struct net_bridge *br,
                     const unsigned char *addr)
{
     unsigned char oldaddr[6];
     struct net_bridge_port *p;
     int wasroot;
     wasroot = br_is_root_bridge(br);
     memcpy(oldaddr, br->bridge_id.addr, ETH_ALEN);
     memcpy(br->bridge_id.addr, addr, ETH_ALEN);
     //注意这里,呵呵,桥MAC更新
     memcpy(br->dev->dev_addr, addr, ETH_ALEN);
     list_for_each_entry(p, &br->port_list, list) {
          if (!memcmp(p->designated_bridge.addr, oldaddr, ETH_ALEN))
              memcpy(p->designated_bridge.addr, addr, ETH_ALEN);
          if (!memcmp(p->designated_root.addr, oldaddr, ETH_ALEN))
              memcpy(p->designated_root.addr, addr, ETH_ALEN);
     }
     br_configuration_update(br);
     br_port_state_selection(br);
     if (br_is_root_bridge(br) && !wasroot)
          br_become_root_bridge(br);
}

  看注释吧,桥MAC就这里得更新,所以,桥MAC地址所有接口

网桥接收数据处理:

  回本章开始handle_bridge函数,br_handle_frame_hook进行接收数据处理网桥始化代码br_handle_frame_hook赋值br_handle_frame没,这就网桥处理函数。跟进个函数nt br_handle_frame(struct net_bridge_port *p, struct sk_buff **pskb)
{
     struct sk_buff *skb = *pskb;
     //目mac地址
     const unsigned char *dest = eth_hdr(skb)->h_dest;
     //端口禁
     if (p->state == BR_STATE_DISABLED)
          goto err;
     //源mac 播或广播,丢弃
     //FF.XX.XX.XX.XX.XX形式
     if (eth_hdr(skb)->h_source[0] & 1)
          goto err;
     //如果状态学习或转发,则学习源mac 更新CAM 表
     if (p->state == BR_STATE_LEARNING ||
       p->state == BR_STATE_FORWARDING)
          // br_fdb_insert函数面已经分析过
          br_fdb_insert(p->br, p, eth_hdr(skb)->h_source, 0);
     //stp 处理,stp-enabled 否启stp 协议
     //bridge_ula stp使播mac地址
     if (p->br->stp_enabled &&
       !memcmp(dest, bridge_ula, 5) &&
       !(dest[5] & 0xF0)) {
          if (!dest[5]) {
              NF_HOOK(PF_BRIDGE, NF_BR_LOCAL_IN, skb, skb->dev,
                   NULL, br_stp_handle_bpdu);
              return 1;
          }
     }
     else if (p->state == BR_STATE_FORWARDING) {
          //始化,并末br_should_route_hook进行赋值
          //所以br_should_route_hook
          if (br_should_route_hook) {
              if (br_should_route_hook(pskb))
                   return 0;
              skb = *pskb;
              dest = eth_hdr(skb)->h_dest;
          }
          //目地址桥地址相同。则传层处理
          //置skb->pkt_type = PACKET_HOST
          if (!memcmp(p->br->dev->dev_addr, dest, ETH_ALEN))
              skb->pkt_type = PACKET_HOST;
          //网桥NF_BR_PRE_ROUTING点netfiter处理
          NF_HOOK(PF_BRIDGE, NF_BR_PRE_ROUTING, skb, skb->dev, NULL,
              br_handle_frame_finish);
          return 1;
     }
err:
     kfree_skb(skb);
     return 1;
}

这个函数里,进行相关入口判断数据包源MAC接口应更新CAM表,更新函数br_fdb_insert()面已经分析过,不太明白以倒过去看,不过注意,这静态项插入。接着判断包传给本机,如果,则置包pkt_typePACKET_HOST关于NF_HOOK()宏,netfiter有专题分析。这们只知道,数据包流进br_handle_frame_finish()进行处理/* note: already called with rcu_read_lock (preempt_disabled) */
int br_handle_frame_finish(struct sk_buff *skb)
{
     //得目MAC地址
const unsigned char *dest = eth_hdr(skb)->h_dest;
     struct net_bridge_port *p = skb->dev->br_port;
     struct net_bridge *br = p->br;
     struct net_bridge_fdb_entry *dst;
     int passedup = 0;
     //混杂模式
     /*如果网桥虚拟网卡处于混杂模式,那么每个接收数据包都需克隆
    送AF_PACKET协议处理体(网络软断函数net_rx_actionptype_all链处理)。*/
     if (br->dev->flags & IFF_PROMISC) {
          struct sk_buff *skb2;
          skb2 = skb_clone(skb, GFP_ATOMIC);
          if (skb2 != NULL) {
              passedup = 1;
              br_pass_frame_up(br, skb2);
          }
     }
     //目mac 播或广播,则需传至层进行处理
     //passedup传送标志,1 时表示已经传过
     if (dest[0] & 1) {
          br_flood_forward(br, skb, !passedup);
          if (!passedup)
              br_pass_frame_up(br, skb);
          goto out;
     }
     //查询CAM 表
     dst = __br_fdb_get(br, dest);
     //本机? 传至层协议处理
     if (dst != NULL && dst->is_local) {
          if (!passedup)
              br_pass_frame_up(br, skb);
          else
              kfree_skb(skb);
          goto out;
     }
     //不本机数据,则转发
     if (dst != NULL) {
          br_forward(dst->dst, skb);
          goto out;
     }
     //如果查询不端口都发送此包
     br_flood_forward(br, skb, 0);
out:
     return 0;
}

这里函数里,通过查找CAM表,得发送端口,如果当CAM表里没有MAC端口,则端口都发送此数据包。这个函数里,们看,查询CAM表函数:__br_fdb_get()接着分析此函数struct net_bridge_fdb_entry *__br_fdb_get(struct net_bridge *br,
                         const unsigned char *addr)
{
     struct hlist_node *h;
     struct net_bridge_fdb_entry *fdb;
     //遍历应MAC哈希项fdb
     hlist_for_each_entry_rcu(fdb, h, &br->hash[br_mac_hash(addr)], hlist) {
          if (!memcmp(fdb->addr.addr, addr, ETH_ALEN)) {
              if (unlikely(has_expired(br, fdb)))
                   break;
              return fdb;
          }
     }
     return NULL;
}

  这个函数非常容易,首先得目MAC哈希项。然再遍历里面数据,查看否含有目地址项。如果送给本机数据包,则传至层协议,如不,则需转发。

    本站是提供个人知识管理的网络存储空间,所有内容均由用户发布,不代表本站观点。请注意甄别内容中的联系方式、诱导购买等信息,谨防诈骗。如发现有害或侵权内容,请点击一键举报。
    转藏 分享 献花(0

    0条评论

    发表

    请遵守用户 评论公约

    类似文章 更多