网桥在内核的实现

我们知道netdevice有一个priv域,这个域用来保存设备的私有数据,当这个设备是一个网桥的时候,priv域也就指向一个struct net_bridge. 

接下来看net_bridge以及相关的数据结构: 
Java代码   收藏代码
  1. struct net_bridge  
  2. {  
  3. ///自旋锁  
  4.     spinlock_t          lock;  
  5. ///网桥所有端口的链表,其中每个元素都是一个net_bridge_port结构。  
  6.     struct list_head        port_list;  
  7. ///加到这个网桥的物理设备  
  8.     struct net_device       *dev;  
  9. ///这个锁是用来保护下面的那个hash链表。  
  10.     spinlock_t          hash_lock;  
  11. ///保存forwarding database的一个hash链表(这个也就是地址学习的东东,所以通过hash能 快速定位),这里每个元素都是一个net_bridge_fsb_entry结构  
  12.     struct hlist_head       hash[BR_HASH_SIZE];  
  13. ///这个结构没有被使用  
  14.     struct list_head        age_list;  
  15.     unsigned long           feature_mask;  
  16. #ifdef CONFIG_BRIDGE_NETFILTER  
  17.     struct rtable           fake_rtable;  
  18. #endif  
  19.     unsigned long           flags;  
  20. #define BR_SET_MAC_ADDR     0x00000001  
  21.   
  22. ///stp相关的一些东西  
  23.     bridge_id           designated_root;  
  24.     bridge_id           bridge_id;  
  25.     u32             root_path_cost;  
  26.     unsigned long           max_age;  
  27.     unsigned long           hello_time;  
  28.     unsigned long           forward_delay;  
  29.     unsigned long           bridge_max_age;  
  30.     unsigned long           ageing_time;  
  31.     unsigned long           bridge_hello_time;  
  32.     unsigned long           bridge_forward_delay;  
  33.   
  34.     u8              group_addr[ETH_ALEN];  
  35.     u16             root_port;  
  36. ///当前使用的协议。  
  37.     enum {  
  38.         BR_NO_STP,      /* no spanning tree */  
  39.         BR_KERNEL_STP,      /* old STP in kernel */  
  40.         BR_USER_STP,        /* new RSTP in userspace */  
  41.     } stp_enabled;  
  42.   
  43.     unsigned char           topology_change;  
  44.     unsigned char           topology_change_detected;  
  45.   
  46. ///stp要用的一些定时器列表。  
  47.     struct timer_list       hello_timer;  
  48.     struct timer_list       tcn_timer;  
  49.     struct timer_list       topology_change_timer;  
  50.     struct timer_list       gc_timer;  
  51.     struct kobject          *ifobj;  
  52. };  


Java代码   收藏代码
  1. struct net_bridge_port  
  2. {  
  3. ///从属于的网桥设备  
  4.     struct net_bridge       *br;  
  5. ///表示链接到这个端口的物理设备  
  6.     struct net_device       *dev;  
  7.     struct list_head        list;  
  8. ///stp相关的一些参数。  
  9.     u8              priority;  
  10.     u8              state;  
  11.     u16             port_no;  
  12.     unsigned char           topology_change_ack;  
  13.     unsigned char           config_pending;  
  14.     port_id             port_id;  
  15.     port_id             designated_port;  
  16.     bridge_id           designated_root;  
  17.     bridge_id           designated_bridge;  
  18.     u32             path_cost;  
  19.     u32             designated_cost;  
  20. ///端口定时器,也就是stp控制超时的一些定时器列表.(详细的需要去看stp的协议).  
  21.     struct timer_list       forward_delay_timer;  
  22.     struct timer_list       hold_timer;  
  23.     struct timer_list       message_age_timer;  
  24.     struct kobject          kobj;  
  25.     struct rcu_head         rcu;  
  26. };  


Java代码   收藏代码
  1. struct net_bridge_fdb_entry  
  2. {  
  3.     struct hlist_node       hlist;  
  4. ///桥的端口(最主要的两个域就是这个域和下面的mac地址域)  
  5.     struct net_bridge_port      *dst;  
  6. ///当使用RCU策略,才用到  
  7.     struct rcu_head         rcu;  
  8. ///引用计数  
  9.     atomic_t            use_count;  
  10.     unsigned long           ageing_timer;  
  11. ///mac地址。  
  12.     mac_addr            addr;  
  13.     unsigned char           is_local;  
  14.     unsigned char           is_static;  
  15. };  

通过下面的图能更好的理解这个结构: 

 

接下来简要的介绍一下网桥的初始化。 

网桥的初始化和一般网络设备的初始化很相似,只不过由于它是虚拟设备,因此这里还有一点不同。 

首先来看内核的网络模块的初始化br_init,也就是初始化上面介绍的数据结构: 

Java代码   收藏代码
  1. static int __init br_init(void)  
  2. {  
  3.     int err;  
  4. ///stp的注册。  
  5.     err = stp_proto_register(&br_stp_proto);  
  6.     if (err < 0) {  
  7.         printk(KERN_ERR "bridge: can't register sap for STP\n");  
  8.         return err;  
  9.     }  
  10.   
  11. ///forwarding database的初始化  
  12.     err = br_fdb_init();  
  13.     if (err)  
  14.         goto err_out;  
  15. ///网桥的netfilter钩子函数的初始化。  
  16.     err = br_netfilter_init();  
  17.     if (err)  
  18.         goto err_out1;  
  19. ///注册到netdevice的通知链上  
  20.     err = register_netdevice_notifier(&br_device_notifier);  
  21.     if (err)  
  22.         goto err_out2;  
  23.   
  24.     err = br_netlink_init();  
  25.     if (err)  
  26.         goto err_out3;  
  27. ///安装网络设备的do_ioctl函数,也就是提供给用户空间ioctl接口。  
  28.     brioctl_set(br_ioctl_deviceless_stub);  
  29.     br_handle_frame_hook = br_handle_frame;  
  30.   
  31.     br_fdb_get_hook = br_fdb_get;  
  32.     br_fdb_put_hook = br_fdb_put;  
  33.   
  34.     return 0;  
  35. .........................................  
  36.     return err;  
  37. }  


我们新建一个网桥,使用br_add_bridge,在这个函数中,主要是调用new_bridge_dev函数,下面我们主要就来看这个函数: 

Java代码   收藏代码
  1. static struct net_device *new_bridge_dev(const char *name)  
  2. {  
  3.     struct net_bridge *br;  
  4.     struct net_device *dev;  
  5.   
  6. ///这里看到setup回调函数,是br_dev_setup(也就是网桥设备专用的)。setup函数的用途,可以看我以前写的网络设备初始化的blog。  
  7.     dev = alloc_netdev(sizeof(struct net_bridge), name,  
  8.                br_dev_setup);  
  9.   
  10.     if (!dev)  
  11.         return NULL;  
  12. ///得到priv数据。  
  13.     br = netdev_priv(dev);  
  14.   
  15. ///接下来初始化br数据结构。  
  16.     br->dev = dev;  
  17.   
  18.     spin_lock_init(&br->lock);  
  19.     INIT_LIST_HEAD(&br->port_list);  
  20.     spin_lock_init(&br->hash_lock);  
  21.   
  22. ///网桥优先级 32768(也就是默认是0x8000)  
  23.     br->bridge_id.prio[0] = 0x80;  
  24.     br->bridge_id.prio[1] = 0x00;  
  25.   
  26.     memcpy(br->group_addr, br_group_address, ETH_ALEN);  
  27.   
  28.     br->feature_mask = dev->features;  
  29.     br->stp_enabled = BR_NO_STP;  
  30.     br->designated_root = br->bridge_id;  
  31.     br->root_path_cost = 0;  
  32.     br->root_port = 0;  
  33.     br->bridge_max_age = br->max_age = 20 * HZ;  
  34.     br->bridge_hello_time = br->hello_time = 2 * HZ;  
  35.     br->bridge_forward_delay = br->forward_delay = 15 * HZ;  
  36.     br->topology_change = 0;  
  37.     br->topology_change_detected = 0;  
  38.     br->ageing_time = 300 * HZ;  
  39. ///初始化网桥设备的netfilter相关域。  
  40.     br_netfilter_rtable_init(br);  
  41.   
  42.     INIT_LIST_HEAD(&br->age_list);  
  43.   
  44.     br_stp_timer_init(br);  
  45.   
  46.     return dev;  
  47. }  


加一个新端口到一个网桥使用br_add_if方法。这里就不详细介绍这个方法了,不过这里要注意,他会在sys文件系统下,生成一些相关的东西。要看sysfs的介绍,去看kernel的文档。 

最后来看一下网桥的子系统在这个网络子系统的位置: 




可以看到这里有很多的hook在ip层,基本都是netfilter子系统的东西。 

这里网桥的输入帧的处理是通过br_handle_frame来处理的。而网桥的输出帧是通过br_dev_xmit来处理的。 

当网络帧通过NIC的设备驱动被接收了之后,skb->dev被实例化为真实的设备,然后这个帧被放入网络栈,然后当be_handle_frame_finish之后调用br_pass_frame_up。我们来看这个函数的实现: 

引用
static void br_pass_frame_up(struct net_bridge *br, struct sk_buff *skb) 

struct net_device *indev, *brdev = br->dev; 

brdev->stats.rx_packets++; 
brdev->stats.rx_bytes += skb->len; 

indev = skb->dev; 
///这步将真实的物理设备替换为虚拟的网桥设备。因此对3层来说就完全不知道物理设备的存在了。 
skb->dev = brdev; 
///调用netfiltel的相关hook。 
NF_HOOK(PF_BRIDGE, NF_BR_LOCAL_IN, skb, indev, NULL, 
netif_receive_skb); 
}


当这个函数执行完毕后,会再次调用netif_receive_skb,他则会再次调用handle_bridge,而此时由于设备已经替换为虚拟的网桥设备,因此就会直接将包发往下层正确的协议处理。 


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值