Многоадресность

Предыдущая  Содержание  Следующая V*D*V

Многоадресный (multicast) пакет - это сетевой пакет предназначенный для получения более чем одним сетевым устройством, но не всеми. Эта функциональность получена присвоением специальных аппаратных адресов группам сетевых устройств. Пакеты, направляемые на один из специальных адресов должны быть получены всеми устройствами такой группы. В случае Ethernet, групповой адрес имеет установленный младший значащий бит первого октета адреса в адресе назначения, тогда как все платы устройств в своём аппаратном адресе имеют этот бит очищенным.

 

Хитрая часть общения с группами устройств и аппаратными адресами выполняется приложениями и ядром, и драйверу интерфейса не придётся иметь дело с этими проблемами.

 

Передача многоадресных пакетов является простой задачей, поскольку они выглядят так же, как любые другие пакеты. Интерфейс передаёт их через среду коммуникации не глядя на адрес назначения. Это ядро должно назначить корректный аппаратный адрес назначения; методу устройства hard_header, если определён, не требуется смотреть в данные, им скомпонованные.

 

Ядро выполняет работу по отслеживанию групповых адресов, представляющих интерес в любой момент времени. Список может часто меняться, так как это является функцией приложений, которые работают в любое время, и пользовательского интереса. Работой драйвера является принять список интересующих групповых адресов и доставлять в ядро любые пакеты, посланные на эти адреса. Как драйвер реализует многоадресный список несколько зависит от того, как работает используемое оборудование. Как правило, оборудование принадлежит к одному из трёх классов в том, что касается многоадресности:

 

Интерфейсы, которые не могут иметь дело с многоадресной рассылкой. Эти интерфейсы либо получают пакеты, направленные только на их аппаратный адрес (плюс широковещательные (broadcast) пакеты) или получать каждый пакет. Они могут получать многоадресные пакеты только принимая каждый пакет, таким образом, потенциально перегружая операционную систему огромным числом "неинтересных" пакетов. Обычно такие интерфейсы не считаются совместимыми с многоадресностью и драйвер не будет устанавливать IFF_MULTICAST в dev->flags. Интерфейсы точка-точка представляют собой особый случай, потому что они всегда принимают каждый пакет без выполнения какой-либо аппаратной фильтрации.

Интерфейсы, которые могут отделять многоадресные пакеты от других пакетов (компьютер-компьютер или широковещательного). Эти интерфейсы могут быть проинструктированы для получения каждого многоадресного пакета и дать программному обеспечению определить, интересен ли адрес для данного сетевого устройства. Накладные расходы, добавляемые в данном случае, являются приемлемыми, поскольку число многоадресных пакетов в типичной сети является небольшим.

Интерфейсы, которые могут выполнять аппаратное обнаружение групповых адресов. Этим интерфейсам может быть передан список групповых адресов для которых пакеты должны быть получены и они игнорируют другие многоадресные пакеты. Это оптимальный случай для ядра, потому что оно не тратит время процессора на отбрасывание "неинтересных" пакетов, полученных интерфейсом.

 

Ядро пытается использовать возможности высокоуровневых интерфейсов, поддерживая третий класс устройств, который наиболее универсален и наилучший. Таким образом, ядро уведомляет драйвер при каждом изменении в списке действительных групповых адресов и передаёт новый список в драйвер, чтобы он мог обновить аппаратные фильтры в соответствии с новой информацией.

Поддержка многоадресности в ядре

Поддержка многоадресных пакетов состоит из нескольких элементов: метод устройства, структура данных и флаги устройства:

 

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;

Связный список всех групповых адресов, ассоциированный с этим устройством. Фактическое определение этой структуры приведено в конце этого раздела.

 

int dev->mc_count;

Количество объектов в связном списке. Эта информация несколько излишняя, но проверка mc_count на 0 является полезной для проверки списка.

 

IFF_MULTICAST

Пока драйвер не установит этот флаг в dev->flags, интерфейсу не будет предложено обрабатывать многоадресные пакеты. Тем не менее, ядро вызывает метод драйвера set_multicast_list, когда dev->flags изменяется, потому что многоадресный список может быть изменён, пока интерфейс был неактивен.

 

IFF_ALLMULTI

Флаг устанавливается в dev->flags сетевым программным обеспечением, чтобы сообщить драйверу получать из сети все многоадресные пакеты. Это происходит при включении многоадресной маршрутизации. Если этот флаг установлен, dev->mc_list не должен использоваться для фильтрации многоадресных пакетов.

 

IFF_PROMISC

Флаг устанавливается в dev->flags, когда интерфейс переключен неразборчивый режим (promiscuous mode). Интерфейсом должен быть получен каждый пакет, независимо от dev->mc_list.

 

Последним кусочком информации, необходимой разработчику драйвера, является определение struct dev_mc_list, которое находится в <linux/netdevice.h>:

 

struct dev_mc_list {

    struct        dev_mc_list *next;      /* Следующий адрес в списке */

    __u8          dmi_addr[MAX_ADDR_LEN]; /* Аппаратный адрес */

    unsigned char dmi_addrlen;            /* Длина адреса */

    int           dmi_users;              /* Число пользователей */

    int           dmi_gusers;             /* Число групп */

};

 

Поскольку групповые и аппаратные адреса не зависят от фактической передачи пакетов, эта структура является переносимой между сетевыми реализациями и каждый адрес определяется строкой октетов и длиной, как и в dev->dev_addr.

Типичная реализация

Лучшим способом описать разработку set_multicast_list является показать вам некоторый псевдокод.

 

Следующая функция представляет собой типичной реализацией функции в полнофункциональном (full-featured, 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 (dev->flags & IFF_ALLMULTI || dev->mc_count > FF_TABLE_SIZE) {

        ff_get_all_multicast_packets( );

        return;

    }

    /* Нет многоадресного списка? Просто выполняем свою работу */

    if (dev->mc_count == 0) {

        ff_get_only_own_packets( );

        return;

    }

    /* Запоминаем все групповые адреса в аппаратном фильтре */

    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 и последние четыре строки кода не требуются.

 

Как уже говорилось ранее, даже интерфейсам, которые не могут иметь дело с многоадресными пакетами, необходимо реализовать метод set_multicast_list для получения уведомлений об изменениях в dev->flags. Этот подход можно было бы назвать "неполной" (nonfeatured, 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, поскольку пользователи в любом случае получают каждый пакет.

Предыдущая  Содержание  Следующая