LInux下桥接模式详解二
上篇文章导入博客园的比较早,而这篇自己在写的时候才发现内部复杂的很,以至于没能按时完成,造成两篇文章的间隔时间有点长!
话不多说,言归正传!
前面的文章介绍了桥接模式下的基础理论知识,其实本节想结合LInux源代码分析下桥接模式下的数据包的转发流程,但是看了源码才发现,这部分内容太多,非一篇文章可以描述的清楚的,所以决定本篇文章主要介绍下linux网络相关的主要结构体,以及各个结构之间的关当一个网络数据包来到host的物理网卡,由于此时网卡已经是混杂模式,所以此数据包的目的地不一定就是host本身。那么此时网卡的 设备控制器就会向host的APIC发送中断信号。CPU收到中断信号后,会自动进入处理该中断的流程,调用IDT中网卡驱动注册的中断处理函数进行处理。
1 struct net_device{ 2 …… 3 unsigned long state; 4 5 …… 6 unsigned int flags; /* interface flags (a la BSD) */ 7 unsigned int priv_flags; /* Like 'flags' but invisible to userspace. 8 9 …… 10 11 #if IS_ENABLED(CONFIG_VLAN_8021Q) 12 struct vlan_info __rcu *vlan_info; /* VLAN info */ 13 #endif 14 15 …… 16 unsigned char *dev_addr 17 rx_handler_func_t __rcu *rx_handler; 18 void __rcu *rx_handler_data; 19 20 …… 21 22 } 23
net_device结构代表一个网络设备,每一个物理网卡还有linux内部都有一个独立的net_device结构与之对应。
state表示设备的状态
flag表示设备的特性,而priv_flag则表示设备的私有特性,对用户空间是不可见的。
dev_addr表示设备的mac地址
rx_handler代表一个钩子函数,在网卡混杂模式开启时,此函数会被初始化成一个转发数据包的函数br_handle_frame
rx_hander_data 指向一个net_bridge_port结构,该端口是正是skb->dev在开启网桥特性后的代表的端口结构。说起来由点绕,还是看下代码
1 static inline struct net_bridge_port *br_port_get_rcu(const struct net_device *dev) 2 { 3 struct net_bridge_port *port = 4 rcu_dereference_rtnl(dev->rx_handler_data); 5 6 return br_port_exists(dev) ? port : NULL; 7 }
该函数从一个net_device获取桥接端口,可以看到正是通过其rx_handler_data指针。而dev是否存在这个端口即其结构里面是否设置了此指针就要看dev的私有特性了
1 #define br_port_exists(dev) (dev->priv_flags & IFF_BRIDGE_PORT)
1 struct sk_buffer{ 2 struct sk_buff *next; 3 struct sk_buff *prev; 4 5 …… 6 7 struct net_device *dev; 8 9 …… 1115 __u16 transport_header;//传输层头部偏移 16 __u16 network_header; //IP头部偏移 17 __u16 mac_header;//MAC地址偏移 18 /* These elements must be at the end, see alloc_skb() for details. */ 19 sk_buff_data_t tail; 20 sk_buff_data_t end; 21 unsigned char *head,//buffer header指针 22 *data; //数据指针 23 unsigned int truesize; 24 atomic_t users; 25 26 }
该结构是数据包逐层交付所必须的结构,其中Next和prev分别指向下一个和上一个buffer,dev表示这个buffer从哪个设备进入,data指向buffer的数据区,head指向buffer的最开始的头部,
mac_header是以太网头部到head指针的偏移,network_header是Ip数据包头部到head指针的偏移,transport_header是传送层头部到head指针的偏移,tail指向数据部分的结束,end指向buffer的结束。truesize是buffer实际的大小,user记录用户数,主要表明是否共享。
1 struct net_bridge{ 2 struct list_head port_list;//所有端口组成的链表头 3 struct net_device *dev; //对应的物理设备 4 5 …… 6 7 struct net_bridge_mdb_htable __rcu *mdb; 8 …… 9 }
这是linux内部的网桥对应的结构,port_list连接网桥所有的端口,dev指向网桥的设备结构体,mdb指向网桥的组播数据库转发表
1 struct net_bridge_port 2 { 3 struct net_bridge *br; //对应的网桥 4 struct net_device *dev; //端口对应的设备 5 struct list_head list; 6 …… 7 u8 state; 8 …… 9 unsigned long flags; 10 …… 11 struct hlist_head mglist; 12 …… 13 }
net_bridge_port结构对应于网桥的一个端口,state表明端口的状态,flags表明端口本身的特性,dev指向它关联的设备,br指向它attach的网桥,mglist连接所有port加入的组,flag记录了端口的某些特性,state表明了端口的某一个状态,如转发、学习等。
1 struct net_bridge_fdb_entry 2 { 3 struct hlist_node hlist; 4 struct net_bridge_port *dst; 5 6 struct rcu_head rcu; 7 unsigned long updated; 8 unsigned long used; 9 mac_addr addr; 10 unsigned char is_local; 11 unsigned char is_static; 12 __u16 vlan_id; 13 };
这是网桥内部转发表的表项,hlist表明表项作为一个节点存在于某张表中,这张表就是转发表。dst指向目的端口,addr是表项的mac地址,isLocal表明是否是本地端口,本地端口我猜想是网桥的数据流入端口,即当目的mac是本地端口表明这是发往本地的数据包;isstatic表明是否是静态地址,静态地址不能自动更新。
1 struct net_bridge_mdb_htable 2 { 3 struct hlist_head *mhash; 4 struct rcu_head rcu; 5 struct net_bridge_mdb_htable *old; 6 u32 size; 7 u32 max; 8 u32 secret; 9 u32 ver; 10 };
该结构表示一个组播数据库转发表,连接了所有的组播数据库转发项.size表示表的大小,max表示最大容量。
1 struct net_port_vlans { 2 u16 port_idx; 3 u16 pvid; 4 union { 5 struct net_bridge_port *port; 6 struct net_bridge *br; 7 } parent; 8 struct rcu_head rcu; 9 unsigned long vlan_bitmap[BR_VLAN_BITMAP_LEN]; 10 unsigned long untagged_bitmap[BR_VLAN_BITMAP_LEN]; 11 u16 num_vlans; 12 };
struct net_bridge_mdb_entry { struct hlist_node hlist[2]; struct hlist_node mglist; struct net_bridge *br;//桥 struct net_bridge_port_group *ports;// struct rcu_head rcu; struct timer_list timer;//组播组数据库项失效定时器,若超时,则会将该组播端口从组播组数据库项的组播端口列表中删除 struct timer_list query_timer;//查询定时 __be32 addr;//组播组地址 u32 queries_sent; };
1 struct net_bridge_port_group { 2 struct net_bridge_port *port; 3 struct net_bridge_port_group __rcu *next; 4 struct hlist_node mglist; 5 struct rcu_head rcu; 6 struct timer_list timer; 7 struct br_ip addr; 8 unsigned char state; 9 };
这是用于组播的组结构,一个组绑定一个组播地址addr,next指向下一个组播组,port指向组的端口,timer是定时器,mgList用于连接一个端口加入的所有的group,表头保存在port结构里
1 struct mac_addr 2 { 3 unsigned char addr[6]; 4 };
可以看到内核中MAC地址用6个字节表示