17.14. 多播

2018-02-24 15:50 更新

17.14.?多播

一個(gè)多播報(bào)文是一個(gè)會(huì)被多個(gè)主機(jī)接收的網(wǎng)絡(luò)報(bào)文, 但不是所有主機(jī). 這個(gè)功能通過(guò)給一組主機(jī)分配特殊的硬件地址來(lái)獲得. 發(fā)向一個(gè)特殊地址的報(bào)文應(yīng)當(dāng)被那個(gè)組當(dāng)中的所有主機(jī)接收. 在以太網(wǎng)的情況下, 一個(gè)多播地址在目的地址的第一個(gè)字節(jié)的最低位為 1, 而每個(gè)設(shè)備板在它自己的硬件地址的這一位上為 0.

處理主機(jī)組和硬件地址的技巧由應(yīng)用程序和內(nèi)核處理, 接口驅(qū)動(dòng)不必處理這個(gè)問(wèn)題.

多播報(bào)文的傳送是一個(gè)簡(jiǎn)單問(wèn)題, 因?yàn)樗鼈兛雌饋?lái)就如同其他的報(bào)文. 接口發(fā)送它們通過(guò)通訊媒介, 不查看目的地址. 內(nèi)核必須要安排一個(gè)正確的硬件目的地址; hard_header 設(shè)備方法, 如果定義了, 不必查看它安排的數(shù)據(jù).

內(nèi)核來(lái)跟蹤在任何給定時(shí)間對(duì)哪些多播地址感興趣. 這個(gè)列表可能經(jīng)常改變, 因?yàn)樗窃谌魏谓o定時(shí)間和按照用戶(hù)意愿運(yùn)行的應(yīng)用程序的功能. 驅(qū)動(dòng)的工作是接收感興趣的多播地址列表并遞交給內(nèi)核任何發(fā)向這些地址的報(bào)文. 驅(qū)動(dòng)如何實(shí)現(xiàn)多播列表是依賴(lài)于底層硬件是如何工作的. 典型地, 在多播的角度上, 硬件屬于 3 類(lèi)中的 1 種:

  • 不能處理多播的接口. 這樣的接口要么接收特別地發(fā)向它們的硬件地址(加上廣播報(bào)文)的報(bào)文, 要么接收每一個(gè)報(bào)文. 它們只能通過(guò)接收每一個(gè)報(bào)文來(lái)接收多播報(bào)文, 因此, 潛在地壓垮操作系統(tǒng), 使用大量的"不感興趣"報(bào)文. 你不經(jīng)常認(rèn)為這樣的接口是有多播能力的, 驅(qū)動(dòng)不會(huì)在 dev->flags 設(shè)置 IFF_MULTICAST.

點(diǎn)對(duì)點(diǎn)接口是特殊情況, 因?yàn)樗鼈円恢苯邮彰總€(gè)報(bào)文, 不進(jìn)行任何硬件過(guò)濾.

  • 能夠區(qū)別多播報(bào)文和其他報(bào)文(主機(jī)到主機(jī), 或者廣播). 這些接口能夠被命令來(lái)接收每個(gè)多播報(bào)文, 讓軟件決定地址是否是主機(jī)感興趣的. 這種情況下的開(kāi)銷(xiāo)是可接受的, 因?yàn)樵谝粋€(gè)典型網(wǎng)絡(luò)上的多播報(bào)文的數(shù)目是少的.

  • 可以進(jìn)行硬件檢測(cè)多播地址的接口. 可以傳遞一個(gè)多播地址的列表給這些接口, 這些地址的報(bào)文接收, 并忽略其他多播地址的報(bào)文. 對(duì)內(nèi)核這是優(yōu)化的情況, 因?yàn)樗焕速M(fèi)處理器時(shí)間來(lái)丟棄接口收到的"不感興趣"的報(bào)文.

內(nèi)核盡力利用高級(jí)接口的能力, 通過(guò)支持第 3 種設(shè)備類(lèi)型, 它是最通用的. 因此, 內(nèi)核通知驅(qū)動(dòng), 在任何有效多播地址列表發(fā)生改變時(shí), 并且它傳遞新的列表給驅(qū)動(dòng), 因此它能夠根據(jù)新的信息來(lái)更新硬件過(guò)濾器.

17.14.1.?多播的內(nèi)核支持

對(duì)多播報(bào)文的支持有幾項(xiàng)組成:一個(gè)設(shè)備方法, 一個(gè)數(shù)據(jù)結(jié)構(gòu), 以及設(shè)備標(biāo)識(shí):

void (dev->set_multicast_list)(struct net_device dev);
設(shè)備方法, 在與設(shè)備相關(guān)的機(jī)器地址改變時(shí)調(diào)用. 它也在 dev->flags 被修改時(shí)調(diào)用, 因?yàn)橐恍?biāo)志(例如, IFF_PROMISC) 可能也要求你重新編程硬件過(guò)濾器. 這個(gè)方法接收一個(gè) struct net_device 指針作為一個(gè)參數(shù), 并返回 void. 一個(gè)對(duì)實(shí)現(xiàn)這個(gè)方法不感興趣的驅(qū)動(dòng)可以聽(tīng)任它為 NULL.

struct dev_mc_list *dev->mc_list;
所有設(shè)備相關(guān)的多播地址的列表. 這個(gè)結(jié)構(gòu)的實(shí)際定義在本節(jié)的末尾介紹.

int dev->mc_count;
鏈表里的項(xiàng)數(shù). 這個(gè)信息有些重復(fù), 但是用 0 來(lái)檢查 mc_count 是檢查這個(gè)列表的有用的方法.

IFF_MULTICAST
除非驅(qū)動(dòng)在 dev->flags 中設(shè)置這個(gè)標(biāo)志, 接口不會(huì)被要求來(lái)處理多播報(bào)文. 然而, 內(nèi)核調(diào)用驅(qū)動(dòng)的 set_multicast_list 方法, 當(dāng) dev->flags 改變時(shí), 因?yàn)槎嗖チ斜砜赡茉诮涌谖醇せ顣r(shí)改變了.

IFF_ALLMULTI
在 dev->flags 中設(shè)置的標(biāo)志, 網(wǎng)絡(luò)軟件來(lái)告知驅(qū)動(dòng)從網(wǎng)絡(luò)上接收所有多播報(bào)文. 這發(fā)生在當(dāng)多播路由激活時(shí). 如果標(biāo)志設(shè)置了, dev->ma_list 不該用來(lái)過(guò)濾多播報(bào)文.

IFF_PROMISC

在 dev->flags 中設(shè)置的標(biāo)志, 當(dāng)接口在混雜模式下. 接口應(yīng)當(dāng)接收每個(gè)報(bào)文, 不管 dev->ma_list.

驅(qū)動(dòng)開(kāi)發(fā)者需要的最后一點(diǎn)信息是 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 */
};

因?yàn)槎嗖ズ陀布刂肥仟?dú)立于真正的報(bào)文發(fā)送, 這個(gè)結(jié)構(gòu)在網(wǎng)絡(luò)實(shí)現(xiàn)中是可移植的, 每個(gè)地址由一個(gè)字符串和一個(gè)長(zhǎng)度標(biāo)識(shí), 就像 dev->dev_addr.

17.14.2.?典型實(shí)現(xiàn)

描述 set_multicast_list 的設(shè)計(jì)的最好方法是給你看一些偽碼.

下面的函數(shù)是一個(gè)典型函數(shù)實(shí)現(xiàn)在一個(gè)全特性(ff)驅(qū)動(dòng)中. 這個(gè)驅(qū)動(dòng)是全模式的, 它控制的接口有一個(gè)復(fù)雜的硬件報(bào)文過(guò)濾器, 它能夠持有一個(gè)主機(jī)要接收的多播地址表. 表的最大尺寸是 FF_TABLE_SIZE.

所有以 ff_ 前綴的函數(shù)是給特定硬件操作的占位者:


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();
}

這個(gè)實(shí)現(xiàn)可以簡(jiǎn)化, 如果接口不能為進(jìn)入報(bào)文存儲(chǔ)多播表在硬件過(guò)濾器中. 這種情況下, FF_TABLE_SIZE 減為 0, 并且代碼的最后 4 行不需要了.

如同前面提過(guò)的, 不能處理多播報(bào)文的接口不需要實(shí)現(xiàn) set_multicast_list 方法來(lái)獲取 dev->flags 改變的通知. 這個(gè)辦法可能被稱(chēng)為一個(gè)"非特性的"(nf)實(shí)現(xiàn). 實(shí)現(xiàn)非常簡(jiǎn)單, 如下面代碼所示:


void nf_set_multicast_list(struct net_device *dev)
{
    if (dev->flags & IFF_PROMISC)
        nf_get_all_packets();
    else
        nf_get_only_own_packets();
}

實(shí)現(xiàn) IFF_PROMISC 是非常重要的, 因?yàn)椴贿@樣用戶(hù)就不能運(yùn)行 tcpdump 或任何其他網(wǎng)絡(luò)分析器. 如果接口運(yùn)行一個(gè)點(diǎn)對(duì)點(diǎn)連接, 另一方面, 根本沒(méi)有必要實(shí)現(xiàn) set_multicast_list, 因?yàn)橛脩?hù)接收每個(gè)報(bào)文.

以上內(nèi)容是否對(duì)您有幫助:
在線(xiàn)筆記
App下載
App下載

掃描二維碼

下載編程獅App

公眾號(hào)
微信公眾號(hào)

編程獅公眾號(hào)