当前位置:操作系统 > Unix/Linux >>

Linux网桥源码框架分析初

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

  版本:Linux 2.4.18

  一、调用

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

  line 1479

  #if defined(CONFIG_BRIDGE) || defined(CONFIG_BRIDGE_MODULE)

   if (skb->dev->br_port != NULL &&

   br_handle_frame_hook != NULL) {

   handle_bridge(skb, pt_prev);

   dev_put(rx_dev);

   continue;

   }

  #endif

  如果定义了网桥或网桥模块,则由handle_bridge函数处理

  skb->dev->br_port :接收该数据包的端口是网桥端口组的一员

  br_handle_frame_hook :定义了网桥处理函数

  二、初始化

  src/net/bridge/br.c:

  static int __init br_init(void)

  {

   printk(KERN_INFO "NET4: Ethernet Bridge 008 for NET4.0\n";

  br_handle_frame_hook = br_handle_frame;

   br_ioctl_hook = br_ioctl_deviceless_stub;

  #if defined(CONFIG_ATM_LANE) || defined(CONFIG_ATM_LANE_MODULE)

   br_fdb_get_hook = br_fdb_get;

   br_fdb_put_hook = br_fdb_put;

  #endif

   register_netdevice_notifier(&br_device_notifier);

  return 0;

  }

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

  ioctl处理函数是:br_ioctl_deviceless_stub

  三、br_handle_frame(br_input.c)

  /*网桥处理函数*/

  void br_handle_frame(struct sk_buff *skb)

  {

   struct net_bridge *br;

   unsigned char *dest;

   struct net_bridge_port *p;

  /*获取目的MAC地址*/

   dest = skb->mac.ethernet->h_dest;

  /*skb->dev->br_port用于指定接收该数据包的端口,若不是属于网桥的端口,则为NULL*/

   p = skb->dev->br_port;

   if (p == NULL) /*端口不是网桥组端口中*/

   goto err_nolock;

  /*本端口所属的网桥组*/

   br = p->br;

  

   /*加锁,因为在转发中需要读CAM表,所以必须加读锁,避免在这个过程中另外的内核控制路径(如多处理机上另外一个CPU上的系统调用)修改CAM表*/

   read_lock(&br->lock);

   if (skb->dev->br_port == NULL) /*前面判断过的*/

   goto err;

  

   /*br->dev是网桥的虚拟网卡,如果它未UP,或网桥DISABLED,p->state实际上是桥的当前端口的STP计算判断后的状态*/

   if (!(br->dev.flags & IFF_UP) ||

   p->state == BR_STATE_DISABLED)

   goto err;

  

   /*源MAC地址为255.X.X.X,即源MAC是多播或广播,丢弃之*/

   if (skb->mac.ethernet->h_source[0] & 1)

   goto err;

  /*众所周之,网桥之所以是网桥,比HUB更智能,是因为它有一个MAC-PORT的表,这样转发数据就不用广播,而查表定端口就可以了

   每次收到一个包,网桥都会学习其来源MAC,添加进这个表。Linux中这个表叫CAM表(这个名字是其它资料上看的)。

   如果桥的状态是LEARNING或FORWARDING(学习或转发),则学习该包的源地址skb->mac.ethernet->h_source,

   将其添加到CAM表中,如果已经存在于表中了,则更新定时器,br_fdb_insert完成了这一过程*/

   if (p->state == BR_STATE_LEARNING ||

   p->state == BR_STATE_FORWARDING)

   br_fdb_insert(br, p, skb->mac.ethernet->h_source, 0);

  

   /*STP协议的BPDU包的目的MAC采用的是多播目标MAC地址:从01-80-c2-00-00-00(Bridge_group_addr:网桥组多播地址)开始

   所以这里是如果开启了STP,而当前数据包又是一个BPDU

   (!memcmp(dest, bridge_ula, 5), unsigned char bridge_ula[6] = { 0x01, 0x80, 0xc2, 0x00, 0x00, 0x00 }

   则交由相应函数处理*/

   if (br->stp_enabled &&

  /*这里只比较前5个字节,没有仔细研究过STP是使用了全部多播地址(从0 1 : 0 0 : 5 e : 0 0 : 0 0 : 0 0到0 1 : 0 0 : 5 e : 7 f : ff : ff。),还是只使用了一部份,这里看来似乎只是一部份,没去深究了*/

   !memcmp(dest, bridge_ula, 5) &&

   !(dest[5] & 0xF0)) /*01-80-c2-00-00-F0 是一个什么地址?为什么要判断呢?*/

   goto handle_special_frame;

  

   /*处理钩子函数,然后转交br_handle_frame_finish函数继续处理*/

   if (p->state == BR_STATE_FORWARDING) {

   NF_HOOK(PF_BRIDGE, NF_BR_PRE_ROUTING, skb, skb->dev, NULL,

   br_handle_frame_finish);

   read_unlock(&br->lock);

   return;

   }

  err:

   read_unlock(&br->lock);

  err_nolock:

   kfree_skb(skb);

   return;

  handle_special_frame:

   if (!dest[5]) {

   br_stp_handle_bpdu(skb);

   return;

   }

  kfree_skb(skb);

  }

  四、br_handle_frame_finish

  static int br_handle_frame_finish(struct sk_buff *skb)

  {

   struct net_bridge *br;

   unsigned char *dest;

   struct net_bridge_fdb_entry *dst;

   struct net_bridge_port *p;

   int passedup;

  /*前面基本相同*/

   dest = skb->mac.ethernet->h_dest;

  p = skb->dev->br_port;

   if (p == NULL)

   goto err_nolock;

  br = p->br;

   read_lock(&br->lock);

   if (skb->dev->br_port == NULL)

   goto err;

  passedup = 0;

  

   /*如果网桥的虚拟网卡处于混杂模式,那么每个接收到的数据包都需要克隆一份

   送到AF_PACKET协议处理体(网络软中断函数net_rx_action中ptype_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

   用于表示是否传送过了,如果已传送过,那就算了*/

   if (dest[0] & 1) {

   br_flood_forward(br, skb, !passedup);

   if (!passedup)

   br_pass_frame_up(br, skb);

   goto out;

   }

  

   /*Linux中的MAC-PORT表是CAM表,这里根据目的地址来查表,以确定由哪个接口把包转发出去

   每一个表项是通过结构struct net_bridge_fdb_entry来描述的:

   struct net_bridge_fdb_entry

   {

   struct net_bridge_fdb_entry *next_hash; //用于CAM表连接的链表指针

   struct net_bridge_fdb_entry **pprev_hash; //为什么是pprev不是prev呢?
CopyRight © 2012 站长网 编程知识问答 www.zzzyk.com All Rights Reserved
部份技术文章来自网络,