W3Cschool
恭喜您成為首批注冊用戶
獲得88經驗值獎勵
一個多播報文是一個會被多個主機接收的網絡報文, 但不是所有主機. 這個功能通過給一組主機分配特殊的硬件地址來獲得. 發(fā)向一個特殊地址的報文應當被那個組當中的所有主機接收. 在以太網的情況下, 一個多播地址在目的地址的第一個字節(jié)的最低位為 1, 而每個設備板在它自己的硬件地址的這一位上為 0.
處理主機組和硬件地址的技巧由應用程序和內核處理, 接口驅動不必處理這個問題.
多播報文的傳送是一個簡單問題, 因為它們看起來就如同其他的報文. 接口發(fā)送它們通過通訊媒介, 不查看目的地址. 內核必須要安排一個正確的硬件目的地址; hard_header 設備方法, 如果定義了, 不必查看它安排的數據.
內核來跟蹤在任何給定時間對哪些多播地址感興趣. 這個列表可能經常改變, 因為它是在任何給定時間和按照用戶意愿運行的應用程序的功能. 驅動的工作是接收感興趣的多播地址列表并遞交給內核任何發(fā)向這些地址的報文. 驅動如何實現多播列表是依賴于底層硬件是如何工作的. 典型地, 在多播的角度上, 硬件屬于 3 類中的 1 種:
不能處理多播的接口. 這樣的接口要么接收特別地發(fā)向它們的硬件地址(加上廣播報文)的報文, 要么接收每一個報文. 它們只能通過接收每一個報文來接收多播報文, 因此, 潛在地壓垮操作系統, 使用大量的"不感興趣"報文. 你不經常認為這樣的接口是有多播能力的, 驅動不會在 dev->flags 設置 IFF_MULTICAST.
點對點接口是特殊情況, 因為它們一直接收每個報文, 不進行任何硬件過濾.
能夠區(qū)別多播報文和其他報文(主機到主機, 或者廣播). 這些接口能夠被命令來接收每個多播報文, 讓軟件決定地址是否是主機感興趣的. 這種情況下的開銷是可接受的, 因為在一個典型網絡上的多播報文的數目是少的.
可以進行硬件檢測多播地址的接口. 可以傳遞一個多播地址的列表給這些接口, 這些地址的報文接收, 并忽略其他多播地址的報文. 對內核這是優(yōu)化的情況, 因為它不浪費處理器時間來丟棄接口收到的"不感興趣"的報文.
內核盡力利用高級接口的能力, 通過支持第 3 種設備類型, 它是最通用的. 因此, 內核通知驅動, 在任何有效多播地址列表發(fā)生改變時, 并且它傳遞新的列表給驅動, 因此它能夠根據新的信息來更新硬件過濾器.
對多播報文的支持有幾項組成:一個設備方法, 一個數據結構, 以及設備標識:
void (dev->set_multicast_list)(struct net_device dev);
設備方法, 在與設備相關的機器地址改變時調用. 它也在 dev->flags 被修改時調用, 因為一些標志(例如, IFF_PROMISC) 可能也要求你重新編程硬件過濾器. 這個方法接收一個 struct net_device 指針作為一個參數, 并返回 void. 一個對實現這個方法不感興趣的驅動可以聽任它為 NULL.
struct dev_mc_list *dev->mc_list;
所有設備相關的多播地址的列表. 這個結構的實際定義在本節(jié)的末尾介紹.
int dev->mc_count;
鏈表里的項數. 這個信息有些重復, 但是用 0 來檢查 mc_count 是檢查這個列表的有用的方法.
IFF_MULTICAST
除非驅動在 dev->flags 中設置這個標志, 接口不會被要求來處理多播報文. 然而, 內核調用驅動的 set_multicast_list 方法, 當 dev->flags 改變時, 因為多播列表可能在接口未激活時改變了.
IFF_ALLMULTI
在 dev->flags 中設置的標志, 網絡軟件來告知驅動從網絡上接收所有多播報文. 這發(fā)生在當多播路由激活時. 如果標志設置了, dev->ma_list 不該用來過濾多播報文.
IFF_PROMISC
在 dev->flags 中設置的標志, 當接口在混雜模式下. 接口應當接收每個報文, 不管 dev->ma_list.
驅動開發(fā)者需要的最后一點信息是 struct dev_mc_list 的定義, 在 <linux/netdevice.h>:
struct dev_mc_list { struct dev_mc_list *next; /* Next address in list */
__u8 dmi_addr[MAX_ADDR_LEN]; /* Hardware address */
unsigned char dmi_addrlen; /* Address length */
int dmi_users; /* Number of users */
int dmi_gusers; /* Number of groups */
};
因為多播和硬件地址是獨立于真正的報文發(fā)送, 這個結構在網絡實現中是可移植的, 每個地址由一個字符串和一個長度標識, 就像 dev->dev_addr.
描述 set_multicast_list 的設計的最好方法是給你看一些偽碼.
下面的函數是一個典型函數實現在一個全特性(ff)驅動中. 這個驅動是全模式的, 它控制的接口有一個復雜的硬件報文過濾器, 它能夠持有一個主機要接收的多播地址表. 表的最大尺寸是 FF_TABLE_SIZE.
所有以 ff_ 前綴的函數是給特定硬件操作的占位者:
void ff_set_multicast_list(struct net_device *dev) { struct dev_mc_list *mcptr;
if (dev->flags & IFF_PROMISC) {
ff_get_all_packets();
return;
}
/* If there's more addresses than we handle, get all multicast
packets and sort them out in software. */
if (dev->flags & IFF_ALLMULTI || dev->mc_count > FF_TABLE_SIZE) {
ff_get_all_multicast_packets();
return;
}
/* No multicast? Just get our own stuff */
if (dev->mc_count == 0) {
ff_get_only_own_packets();
return;
}
/* Store all of the multicast addresses in the hardware filter */
ff_clear_mc_list();
for (mc_ptr = dev->mc_list; mc_ptr; mc_ptr = mc_ptr->next)
ff_store_mc_address(mc_ptr->dmi_addr);
ff_get_packets_in_multicast_list();
}
這個實現可以簡化, 如果接口不能為進入報文存儲多播表在硬件過濾器中. 這種情況下, FF_TABLE_SIZE 減為 0, 并且代碼的最后 4 行不需要了.
如同前面提過的, 不能處理多播報文的接口不需要實現 set_multicast_list 方法來獲取 dev->flags 改變的通知. 這個辦法可能被稱為一個"非特性的"(nf)實現. 實現非常簡單, 如下面代碼所示:
void nf_set_multicast_list(struct net_device *dev)
{
if (dev->flags & IFF_PROMISC)
nf_get_all_packets();
else
nf_get_only_own_packets();
}
實現 IFF_PROMISC 是非常重要的, 因為不這樣用戶就不能運行 tcpdump 或任何其他網絡分析器. 如果接口運行一個點對點連接, 另一方面, 根本沒有必要實現 set_multicast_list, 因為用戶接收每個報文.
Copyright©2021 w3cschool編程獅|閩ICP備15016281號-3|閩公網安備35020302033924號
違法和不良信息舉報電話:173-0602-2364|舉報郵箱:jubao@eeedong.com
掃描二維碼
下載編程獅App
編程獅公眾號
聯系方式:
更多建議: