一、igmpsnooping相關的資料結構
組播相關的資料結構主要有三個,下面分別分析:
1、struct net_bridge_mdb_htable
/*組播組資料庫轉寄表,該結構體將所有的組播組資料庫轉寄項通過hash數組串連到一起*/
struct net_bridge_mdb_htable
{
structhlist_head *mhash; //hash數組,將所有的net_bridge_mdb_entry連結到一起
structrcu_head rcu;
structnet_bridge_mdb_htable *old;
u32 size; //hash數組中所有hash鏈表中存在的所有net_bridge_mdb_entry項的總數
u32 max;//hash數組的最大值
u32 secret;
u32 ver;
};
2、net_bridge_mdb_entry
/*一個組播組資料庫轉寄項,描述一個組播組的詳細資料*/
struct net_bridge_mdb_entry
{
structhlist_node hlist[2];
structhlist_node mglist;
structnet_bridge *br;//橋
structnet_bridge_port_group *ports;//
structrcu_head rcu;
structtimer_list timer;//組播組資料庫項失效定時器,若逾時,則會將該組播連接埠從組播組資料庫項的組播連接埠列表中刪除
structtimer_list query_timer;//查詢定時
__be32 addr;//組播組地址
u32 queries_sent;
};
其中port指向所有加入到組播組addr的組播連接埠
mglist用於將通過橋br接收到的igmp 加入報文建立的組播組資料庫轉寄項串連起來
3、struct net_bridge_port_group
/*加入一個組播組的組播連接埠資訊結構體*/
struct net_bridge_port_group {
structnet_bridge_port *port;//加入該組播組的橋連接埠
structnet_bridge_port_group *next;//下一個組播組詳細參數結構體(可以有多個連接埠加入到同一個組播組)
structhlist_node mglist;
structrcu_head rcu;//rcu表頭
structtimer_list timer;//組播連接埠失效定時器,若逾時,則會將該組播連接埠從組播組資料庫項的組播連接埠列表中刪除
structtimer_list query_timer;//查詢計時器
__be32 addr;//組播組地址,每一個組播連接埠也要一個組播組的原因是,通過這個值可以快速尋找到其關聯的組播組資料庫項
u32 queries_sent;//已發送查詢包的次數
};
其中,port鏈表用於將所有加入到組播組addr的對應橋端
Next指向下一個組播連接埠,該下一個組播組地址也為addr,僅僅是port值不同。
其中mglist用於將該組播連接埠加入到port的mglist鏈表中,通過橋連接埠的mglist鏈表,能夠尋找到該橋連接埠加入的所有組播組。
下面是這3個資料結構之間的關係
二、Igmpsnooping的初始化
Igmp snooping的初始化是使用函數br_multicast_init來實現的,在該函數裡,主要組播組轉寄資料庫表的hash數組的最大值進行設定,設定發送查詢報文的間隔時間,以及發送查詢報文的次數,並初始化橋的igmp查詢的定時器。
void br_multicast_init(struct net_bridge*br)
{
br->hash_elasticity= 4;//每個組播組 ip中所能關聯的連接埠個數
br->hash_max= 512; //mdb中hash數組的最大值
br->multicast_router= 1;
br->multicast_last_member_count= 2;
br->multicast_startup_query_count= 2;
br->multicast_last_member_interval= HZ;
br->multicast_query_response_interval= 10 * HZ; //組播查詢最大回複時間
br->multicast_startup_query_interval= 125 * HZ / 4; //開啟發送查詢報文的間隔時間
br->multicast_query_interval= 125 * HZ; //查詢包的發送間隔時間
br->multicast_querier_interval= 255 * HZ;
br->multicast_membership_interval= 260 * HZ;
spin_lock_init(&br->multicast_lock);
setup_timer(&br->multicast_router_timer,
br_multicast_local_router_expired, 0);
setup_timer(&br->multicast_querier_timer,
br_multicast_local_router_expired, 0);
setup_timer(&br->multicast_query_timer,br_multicast_query_expired,
(unsigned long)br);
}
2、橋與橋連接埠的查詢定時器
對於橋與橋連接埠的查詢定時器,主要是用來實現igmp snooping組播資料庫轉寄表的持續更新的。
a)Br橋的查詢定時器
對於定時器br->multicast_query_timer,在開啟igmp snooping功能後就會開啟橋連接埠的組播查詢功能,主要分為如下情況:
i)當br裝置up且開啟igmp snooping時,就會調用函數br_multicast_open,開啟一個立即逾時的查詢定時器
ii) 重新開始igmp snooping時,也會調用函數br_multicast_open,開啟一個立即逾時的查詢定時器。
/*
開啟一個橋的igmpsnooping功能
1、重設發送查詢資料包計數
2、修改橋的查詢定時器
*/
void br_multicast_open(struct net_bridge*br)
{
br->multicast_startup_queries_sent= 0;
if(br->multicast_disabled)
return;
mod_timer(&br->multicast_query_timer,jiffies);
}
當橋的查詢定時器逾時,就會調用函數br_multicast_query_expired進行處理,對於橋裝置,該函數會發送一個組播通用查詢包給上層進行處理,這時如果上層協議棧開啟了igmp proxy功能,就會觸發上層協議棧對lan側的裝置萬用群組播查詢功能。對於上層開啟igmp proxy功能,通過橋的查詢定時器就能對lan側裝置進行周期的通用查詢,然後就可以間接實現igmp snooping組播轉寄資料庫的更新。
對於橋的查詢定時器,因為其需要上層開啟igmp proxy功能,且當上層開啟igmp proxy功能後,如果開啟了igmp snooping功能,就能通過發送通用的組播查詢報文,實現快速的建立組播組資料庫轉寄表。
從上面的程式可以看出,其是依賴上層是否開啟igmp proxy,而不是直接發送萬用群組播組查詢報文,這樣也滿足了igmp協議的要求,即只有組播組路由器才能發送組播查詢報文。
B)橋連接埠的查詢定時器
那如果上層協議棧不支援igmpproxy功能,上層協議棧收到通用查詢報文就會丟棄掉,就不會觸發上層協議棧對lan側裝置的通用查詢,那不就沒有辦法更新igmp snooping的組播組轉寄資料庫了。
代碼編寫者顯然也注意到了這個問題,所以又增加了橋連接埠的組播查詢定時器功能,在開啟igmp snooping功能後就會開啟橋連接埠的組播查詢功能,分為下面兩種情況:
1、 在一個橋連接埠up起來且開啟igmp snooping時,通過間接調用__br_multicast_enable_port,啟動一個立即到期的橋連接埠的查詢定時器
2、 在重新開啟igmp snooping功能時,通過調用__br_multicast_enable_port,啟動一個立即到期的橋連接埠的查詢定時器。
下面我們分析一下函數__br_multicast_enable_port。
static void__br_multicast_enable_port(struct net_bridge_port *port)
{
port->multicast_startup_queries_sent= 0;
if(try_to_del_timer_sync(&port->multicast_query_timer) >= 0 ||
del_timer(&port->multicast_query_timer))
mod_timer(&port->multicast_query_timer,jiffies);
}
該函數還是比較簡單的,主要是開啟一個立即逾時的橋連接埠定時器,逾時後即會執行逾時處理函數br_multicast_port_query_expired。
static voidbr_multicast_port_query_expired(unsigned long data)
{
structnet_bridge_port *port = (void *)data;
structnet_bridge *br = port->br;
spin_lock(&br->multicast_lock);
if(port->state == BR_STATE_DISABLED ||
port->state == BR_STATE_BLOCKING)
gotoout;
if(port->multicast_startup_queries_sent <
br->multicast_startup_query_count)
port->multicast_startup_queries_sent++;
br_multicast_send_query(port->br,port,
port->multicast_startup_queries_sent);
out:
spin_unlock(&br->multicast_lock);
}
該函數的結構還是比較簡單的,主要是判斷橋連接埠是否處於forward狀態,接著就會調用函數br_multicast_send_query發送一個通用查詢報文。
對於函數br_multicast_send_query,其既可以向本地上層協議棧發送組播組查詢報文,也可以將組播組通用查詢報文從指定的橋連接埠發送出去。
下面分析一下函數br_multicast_send_query
/*
1、 調用函數br_multicast_alloc_query構造一個萬用群組播組查詢報文
2、 對於橋連接埠不為空白時,則將該查詢報文從相應的連接埠發送出去
3、 對於橋連接埠為空白時,則將該查詢報文發送給上層協議棧
4、 更新橋或者橋連接埠的查詢定時器。
*/
static void br_multicast_send_query(structnet_bridge *br,
struct net_bridge_port *port, u32 sent)
{
unsignedlong time;
structsk_buff *skb;
if(!netif_running(br->dev) || br->multicast_disabled ||
timer_pending(&br->multicast_querier_timer))
return;
/*構造一個通用查詢資料包*/
skb= br_multicast_alloc_query(br, 0);
if(!skb)
gototimer;
if(port) {
__skb_push(skb,sizeof(struct ethhdr));
skb->dev= port->dev;
NF_HOOK(PF_BRIDGE,NF_BR_LOCAL_OUT, skb, NULL, skb->dev,
dev_queue_xmit);
}else
netif_rx(skb);
timer:
time= jiffies;
time+= sent < br->multicast_startup_query_count ?
br->multicast_startup_query_interval:
br->multicast_query_interval;
mod_timer(port? &port->multicast_query_timer :
&br->multicast_query_timer, time);
}
以上就是橋與橋連接埠的組播查詢定時器的工作原理。
三、igmpsnooping 的開啟與關閉
Igmp snooping的開啟與關閉是通過函數br_multicast_toggle實現
對於igmp snooping的開啟與關閉,目前是通過sysfs實現igmpsnooping的開啟與關閉。
我們可以通過添加ioctl介面,實現通過brctl來開啟或者關閉igmp SNOOPING
/*
功能:開啟或者關閉igmpSNOOPING的函數。
只有當要設定的值與br->multicast_disabled的當前值不同時才允許修改。
當開啟igmpsnooping功能時,則重新建立組播組轉寄資料庫表,並調用br_multicast_open與__br_multicast_enable_port啟動橋與橋連接埠的組播查詢定時器。
*/
int br_multicast_toggle(struct net_bridge*br, unsigned long val)
{
structnet_bridge_port *port;
interr = -ENOENT;
spin_lock(&br->multicast_lock);
if(!netif_running(br->dev))
gotounlock;
err= 0;
/*如果要設定的值與當前的值相同,則直接返回*/
if(br->multicast_disabled == !val)
gotounlock;
br->multicast_disabled= !val;
/*當關閉igmp SNOOPING時,並沒有釋放br->mdb*/
if(br->multicast_disabled)
gotounlock;
/*
1、當開啟IGMP SNOOPING時,如果br->mdb、br->mdb->old均不為0,
則說明br->mdb值有問題,此時則會關閉IGMP SNOOPING功能
2、當開啟IGMP SNOOPING時,如果br->mdb不為0,且br->mdb->old為0,
則調用br_mdb_rehash,將原br->mdb中的組播組項重新計算hash值
並存放在新的br->mdb中,釋放原br->mdb佔用的空間*/
if(br->mdb) {
if(br->mdb->old) {
err= -EEXIST;
rollback:
br->multicast_disabled= !!val;
gotounlock;
}
err= br_mdb_rehash(&br->mdb, br->mdb->max,
br->hash_elasticity);
if(err)
gotorollback;
}
/*
1、reset br的發送查詢包統計計數
2、修改br的組播查詢定時器multicast_query_timer的值
*/
br_multicast_open(br);
/*
對於br橋組上的每一個連接埠
1、reset port的發送查詢包統計計數
2、刪除連接埠的查詢定時器,並重新調度
*/
list_for_each_entry(port,&br->port_list, list) {
if(port->state == BR_STATE_DISABLED ||
port->state == BR_STATE_BLOCKING)
continue;
__br_multicast_enable_port(port);
}
unlock:
spin_unlock(&br->multicast_lock);
returnerr;
}
至此完成了igmpsnooping的資料結構與初始化的介紹,下節分析igmp snooping的接收處理函數。