到這裡為止,網橋的配置已經講述完了。我們來看一下網橋是怎麼對數據包進行處理的
網橋對接收數據的處理:
回到本章的開始的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_type為PACKET_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_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為傳送標志,為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: