Структура net_device в деталях

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

Структура net_device находится в самом центре уровня сетевого драйвера и заслуживает полного описания. Этот список описывает все поля, но больше предоставлен как справочная информация, чем как материал для запоминания. Оставшаяся часть этой главы кратко описывает каждое поле, как только оно будет использовано в коде примера, так что вам не потребуется возвращаться к этому разделу.

Общая информация

Первая часть struct net_device состоит из следующих полей:

 

char name[IFNAMSIZ];

Имя этого устройства. Если имя устанавливаемое драйвером содержит строку форматирования %d, register_netdev заменяет  её числом, чтобы сделать имя уникальным; присваиваемые номера начинаются с 0.

 

unsigned long state;

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

 

struct net_device *next;

Указатель на следующее устройство в глобальном связном списке. Это поле не должно быть затронуто драйвером.

 

int (*init)(struct net_device *dev);

Функция инициализации. Если этот указатель установлен, эта функция вызывается register_netdev для завершения инициализации структуры net_device. Большинство современных сетевых драйверов больше не используют эту функцию; вместо этого инициализация выполняется перед регистрацией интерфейса.

Информация об оборудовании

Следующие поля содержат низкоуровневую информацию об оборудовании для относительно простых устройств. Они являются пережитком первых дней сетевой работы в Linux; самые современные драйверы их не используют (с возможным исключением if_port). Мы перечисляем их здесь для полноты.

 

unsigned long rmem_end;

unsigned long rmem_start;

unsigned long mem_end;

unsigned long mem_start;

Информация о памяти устройства. Эти поля хранят адреса начала и окончания разделяемой памяти, используемой устройством. Если устройство имеет разную память для приёма и передачи, поля mem используются для памяти передачи и поля rmem для памяти приёма. Поля rmem никогда не ссылаются за пределы самого драйвера. По соглашению, в поля end настроены так, что end - start является объёмом доступной на борту памяти.

 

unsigned long base_addr;

Базовый адрес ввода/вывода сетевого интерфейса. Это поле, как и предыдущие, задаётся драйвером в течение зондирования устройства. Для просмотра и изменения текущего значения может быть использована команда ifconfig. base_addr может быть задан явно командной строкой ядра при загрузке системы (с помощью параметра netdev=) или во время загрузки модуля. Это поле, как и поля памяти, описанные выше, ядром не используется.

 

unsigned char irq;

Назначенный номер прерывания. Значение dev->irq печатается ifconfig при перечислении интерфейсов. Эта значение обычно можно установить во время старта системы или загрузки модуля и изменить позже с помощью ifconfig.

 

unsigned char if_port;

Порт, используемый в мультипортовых устройствах. Это поле используется, например, устройствами, которые поддерживают оба Ethernet соединения: как коаксиальную (IF_PORT_10BASE2), так и витую пару (IF_PORT_100BASET). Полный набор известных типов портов определён в <linux/netdevice.h>.

 

unsigned char dma;

Канал DMA, выделенный устройством. Поле имеет смысл лишь для некоторых периферийных шин, таких как ISA. Оно не используется за пределами самого драйвера устройства, только для информационных целей (в ifconfig).

Информация об интерфейсе

Большая часть информации об интерфейсе устанавливается должным образом функцией ether_setup (или любой другой функцией установки, соответствующей данному типу оборудования). Сетевые карты могут рассчитывать на эту функцию общего назначения для большинства из этих полей, но поля flags и dev_addr являются зависимыми от устройства и должны быть заданы явно во время инициализации.

 

Некоторые не-Ethernet интерфейсы могут использовать вспомогательные функции, аналогичные ether_setup. drivers/net/net_init.c экспортирует ряд таких функций, включая следующие:

 

void ltalk_setup(struct net_device *dev);

Устанавливает поля для устройства LocalTalk.

 

void fc_setup(struct net_device *dev);

Инициализирует поля для волоконно-оптических устройств.

 

void fddi_setup(struct net_device *dev);

Конфигурирует интерфейс для сети с Fiber Distributed Data Interface (распределенным интерфейсом передачи данных по волоконно-оптическим каналам, FDDI).

 

void hippi_setup(struct net_device *dev);

Готовит полей для High-Performance Parallel Interface (высокопроизводительного параллельного интерфейса, HIPPI) драйвера высокоскоростного соединения.

 

void tr_setup(struct net_device *dev);

Выполняет настройку для сетевых интерфейсов token ring (маркерное кольцо).

 

Большинство устройств охватываются одним из этих классов. Однако, если у вас что-то радикально новое и отличающееся, необходимо определить следующие поля вручную:

 

unsigned short hard_header_len;

Длина аппаратного заголовка, то есть число октетов, которые предваряют передаваемый пакет до заголовка IP или другой протокольной информации. Для Ethernet интерфейсов значением hard_header_len является 14 (ETH_HLEN).

 

unsigned mtu;

Максимальный блок передачи (maximum transfer unit, MTU). Это поле используется сетевым уровнем чтобы управлять передачей пакета. Ethernet имеет MTU из 1500 октетов (ETH_DATA_LEN). Это значение может быть изменено с помощью ifconfig.

 

unsigned long tx_queue_len;

Максимальное количество кадров, которые могут быть поставлены в очередь в очередь передачи устройства. Это значение устанавливается ether_setup в 1000, но вы можете его изменить. Например, plip использует 10, чтобы избежать потери памяти (plip имеет меньшую пропускную способность, чем настоящий Ethernet интерфейс).

 

unsigned short type;

Аппаратный тип интерфейса. Поле type используется ARP для определения, какой тип аппаратного адреса поддерживает интерфейс. Верным значением для Ethernet интерфейсов является ARPHRD_ETHER и это значение устанавливается ether_setup. Распознаваемые типы определены в <linux/if_arp.h>.

 

unsigned char addr_len;

unsigned char broadcast[MAX_ADDR_LEN];

unsigned char dev_addr[MAX_ADDR_LEN];

Длина аппаратного (MAC) адреса и адреса оборудования устройства. Длиной Ethernet адреса является шесть октетов (мы имеем в виду аппаратный идентификатор интерфейсной платы), а широковещательный (broadcast) адрес состоит из шести октетов 0xff; ether_setup принимает меры, чтобы эти значения были корректными. Адрес устройства, с другой стороны, должен быть прочитан из интерфейсной платы зависимым от устройства способом и драйвер должен скопировать его в dev_addr. Аппаратный адрес используется для генерации корректных заголовков Ethernet, до того, как пакет передан драйверу для передачи. Устройство snull не использует физический интерфейс и он изобретает свой собственный аппаратный адрес.

 

unsigned short flags;

int features;

Флаги интерфейса (подробности ниже).

 

Поле flags является битовой маской, включающей следующие битовые значения. Префикс IFF_ означает "интерфейсные флаги". Некоторыми флагами управляет ядро, а некоторые устанавливаются интерфейсом во время инициализации, чтобы заявить о различных возможностях и других особенностях интерфейса. Действительными флагами, которые определены в <linux/if.h>, являются:

 

IFF_UP

Драйвер может только читать этот флаг. Ядро устанавливает его, когда интерфейс активен и готов к передаче пакетов.

 

IFF_BROADCAST

Этот флаг (поддерживаемый сетевым кодом) сообщает, что интерфейс позволяет широковещательные (broadcast) запросы. Ethernet платы делают это.

 

IFF_DEBUG

Он отмечает режим отладки. Этот флаг может быть использован для управления многословностью ваших вызовов printk или для других целей отладки. Хотя ни один драйвер в дереве в настоящее время не использует этот флаг, он может быть установлен и сброшен пользовательскими программами через ioctl и ваш драйвер может использовать его. Для включения и выключения этого флага может быть использована программа misc-progs/netifdebug.

 

IFF_LOOPBACK

Этот флаг должен быть установлен только в интерфейсе обратной связи. Ядро проверяет IFF_LOOPBACK вместо аппаратного имени lo как отдельный интерфейс.

 

IFF_POINTOPOINT

Этот флаг сигнализирует, что интерфейс подключен к соединению "точка-точка". Он устанавливается драйвером или, иногда, ifconfig. Например, его устанавливают plip и драйвер PPP.

 

IFF_NOARP

Это означает, что интерфейс не может выполнять ARP. Например, интерфейсам точка-точка не требуется запускать ARP, который лишь создаст дополнительный трафик без извлечения полезной информации. snull работает без возможностей ARP, поэтому он устанавливает флаг.

 

IFF_PROMISC

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

 

IFF_MULTICAST

Этот флаг устанавливается драйвером, чтобы отметить интерфейсы, которые поддерживают многоадресную (multicast) передачу. ether_setup устанавливает IFF_MULTICAST по умолчанию, поэтому если ваш драйвер не поддерживает многоадресность, он должен во время инициализации очистить этот флаг.

 

IFF_ALLMULTI

Этот флаг говорит интерфейсу получать все многоадресные (multicast) пакеты. Ядро устанавливает его, когда сетевое устройство выполняет многоадресную маршрутизацию, только если установлен IFF_MULTICAST. Драйвера может только читать IFF_ALLMULTI. Флаги многоадресности используются в разделе "Многоадресность" далее в этой главе.

 

IFF_MASTER

IFF_SLAVE

Эти флаги используются кодом выравнивания нагрузки. Драйверу интерфейса не требуется знать о них.

 

IFF_PORTSEL

IFF_AUTOMEDIA

Эти флаги сигнализируют о том, что устройство способен переключаться между несколькими типами носителей; например, между неэкранированной витой парой (unshielded twisted pair, UTP) и коаксиальными кабелями Ethernet. Если IFF_AUTOMEDIA установлен, устройство выбирает необходимый тип носителя автоматически. На практике ядро не использует ни один из этих флагов.

 

IFF_DYNAMIC

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

 

IFF_RUNNING

Этот флаг показывает, что этот интерфейс подключен и работает. Он присутствует в основном для совместимости с BSD; ядро мало использует его. Большинству сетевых драйверов нет необходимости беспокоиться о IFF_RUNNING.

 

IFF_NOTRAILERS

Этот флаг не используется в Linux, но он существует для совместимости с BSD.

 

Когда программа изменяет IFF_UP, вызывается метод устройства open или stop. Кроме того, при изменении IFF_UP или любого другого флага вызывается метод set_multicast_list. Если драйвер должен выполнять какие-то действия в ответ на модификацию этих флагов, он должен выполнить это действие в set_multicast_list. Например, когда установлен или сброшен IFF_PROMISC, set_multicast_list должен уведомить об этом фильтр оборудования на плате. Обязанности этого метода устройства изложены в разделе "Многоадресность".

 

Поле features в структуре net_device устанавливается драйвером, чтобы сообщить ядру о любых специальных аппаратных возможностях, которые имеет этот интерфейс. Мы обсудим некоторые из этих возможностей; другие выходят за рамки этой книги. Полный набор состоит из:

 

NETIF_F_SG

NETIF_F_FRAGLIST

Оба этих флага управляют использованием ввода/вывода с разборкой/сборкой. Если ваш интерфейс может передать пакет, который был разбит на несколько отдельных сегментов памяти, вам следует установить NETIF_F_SG. Конечно, необходимо реально реализовать ввод/вывод с разборкой/сборкой (мы расскажем, как это делается в разделе "Ввод/вывод с разборкой/сборкой"). NETIF_F_FRAGLIST заявляет, что ваш интерфейс может справиться с пакетами, которые были фрагментированы; в версии 2.6 это делает только драйвер обратной связи (loopback).

Заметим, что ядро не выполняет ввод/вывод c разборкой/сборкой для вашего устройства, если оно не обеспечивает также в той или иной форме подсчёт контрольной суммы. Причина в том, что если ядро должно сделать проход по фрагментированному ("нелинейному") пакету для расчёта контрольной суммы, оно может также в одно и тоже время копировать данные и объединять пакет.

 

NETIF_F_IP_CSUM

NETIF_F_NO_CSUM

NETIF_F_HW_CSUM

Эти флаги всеми способами говорят ядру, что нет необходимости применять контрольные суммы к некоторым или всем пакетам,  покидающим систему через этот интерфейс. Установите NETIF_F_IP_CSUM, если ваш интерфейс может рассчитывать контрольную сумму IP пакетов, но не других. Если для этого интерфейса даже не требуется расчёт контрольных сумм, установите NETIF_F_NO_CSUM. Драйвер обратной связи (loopback) устанавливает этот флаг и snull это тоже делает; как только пакеты переданы через системную память, нет (надо надеяться!) возможности для их повреждения и нет необходимости в их проверке. Если ваше оборудование само рассчитывает контрольную сумму, установите NETIF_F_HW_CSUM.

 

NETIF_F_HIGHDMA

Установите этот флаг, если ваше устройство может выполнять DMA в верхней памяти. В отсутствие этого флага, все пакетные буферы, предоставляемые вашему драйверу, выделяются в нижней памяти.

 

NETIF_F_HW_VLAN_TX

NETIF_F_HW_VLAN_RX

NETIF_F_HW_VLAN_FILTER

NETIF_F_VLAN_CHALLENGED

Эти параметры описывают, что ваше оборудование поддерживает пакеты 802.1q VLAN. Поддержка VLAN выходит за границы того, что мы можем охватить в этой главе. Если пакеты VLAN сбивают с толка ваше устройство (что они в действительности делать не должны), установите флаг NETIF_F_VLAN_CHALLENGED.

 

NETIF_F_TSO

Установите этот флаг, если ваше устройство может выполнять избавление от сегментации TCP (TCP segmentation offloading). TSO является дополнительной функцией, которую мы не можем здесь описать.

Методы устройства

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

 

Методы устройства для сетевого интерфейса можно разделить на две группы: основные и необязательные. Основные методы включают в себя те, которые необходимы, чтобы было возможно использовать интерфейс; необязательные методы реализуют более расширенную функциональность, которая не требуется в обязательном порядке. Ниже приведены основные методы:

 

int (*open)(struct net_device *dev);

Открывает интерфейс. Интерфейс открывается всякий раз, когда его активирует ifconfig. Метод open должен зарегистрировать все необходимые системные ресурсы (порты ввода/вывода, IRQ, DMA и так далее), включить оборудование и выполнить все другие настройки, требующиеся вашему устройству.

 

int (*stop)(struct net_device *dev);

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

 

int (*hard_start_xmit) (struct sk_buff *skb, struct net_device *dev);

Метод, который инициирует передачу пакета. Полный пакет (заголовки протокола и всё остальное) содержится в структуре буфера сокета (sk_buff). Буферы сокетов описываются позже в этой главе.

 

int (*hard_header) (struct sk_buff *skb, struct net_device *dev, unsigned short type, void *daddr, void *saddr, unsigned len);

Функция (вызываемая перед hard_start_xmit), которая строит аппаратный заголовок из аппаратных адресов источника и назначения, которые были получены ранее; её работа заключается в организации информации, переданной ей в качестве аргументов в соответствующем зависимом от устройства аппаратном заголовке. eth_header является функцией по умолчанию для интерфейсов, подобных Ethernet, и ether_setup устанавливает это поле соответственно.

 

int (*rebuild_header)(struct sk_buff *skb);

Функция, используемая для перестройки аппаратного заголовка после завершения разрешения (определения адреса) протоколом ARP, но перед передачей пакета. Функция по умолчанию, используемая устройствами Ethernet, для заполнения пакета недостающей информацией использует код поддержки ARP.

 

void (*tx_timeout)(struct net_device *dev);

Метод, вызываемый сетевым кодом, когда передача пакета не была завершена в течение разумного периода, исходя из предположения, что было пропущено прерывание или интерфейс заблокирован. Он должен справиться с этой проблемой и возобновить передачу пакетов.

 

struct net_device_stats *(*get_stats)(struct net_device *dev);

Всякий раз, когда приложению необходимо получить статистику для интерфейса, вызывается этот метод. Это происходит, например, когда запускается ifconfig или netstat -i. Пример реализации для snull приводится в разделе "Статистическая  информация".

 

int (*set_config)(struct net_device *dev, struct ifmap *map);

Изменяет конфигурацию интерфейса. Этот метод является точкой входа для настройки драйвера. Во время выполнения с помощью set_config может быть изменён адрес ввода/вывода для устройства и его номер прерывания. Данная возможность может быть использована администратором системы, если интерфейс не может быть для этого прозондирован. Драйверам для современного оборудования обычно не требуется реализовывать этот метод.

 

Остальные операции устройства являются необязательными:

 

int weight;

int (*poll)(struct net_device *dev; int *quota);

Метод, предусмотренный NAPI-совместимыми драйверами, для работы интерфейса в режиме опроса, с запрещёнными прерываниями. NAPI (и его поле weight) рассматривается в разделе "Уменьшение числа прерываний".

 

void (*poll_controller)(struct net_device *dev);

Функция, которая просит драйвер проверить события на интерфейсе в ситуациях, когда прерывания запрещены. Она используется особых сетевых задач внутри ядра, таких как удалённое управление и отладка ядра через сеть.

 

int (*do_ioctl)(struct net_device *dev, struct ifreq *ifr, int cmd);

Выполняет специфичные для интерфейса команды ioctl. (Реализация этих команд описана в разделе "Дополнительные команды ioctl".) Соответствующее поле в struct net_device можно оставить как NULL, если интерфейс не требует каких-либо команд, специфичных для интерфейса.

 

void (*set_multicast_list)(struct net_device *dev);

Метод вызывается, когда изменяется список многоадресного запроса для устройства и когда изменяются флаги. Смотрите раздел "Многоадресность" для более подробной информации и примера реализации.

 

int (*set_mac_address)(struct net_device *dev, void *addr);

Функция, которая может быть реализована, если интерфейс поддерживает возможность изменить свой аппаратный адрес. Многие интерфейсы совсем не поддерживают эту возможность. Другие используют реализацию по умолчанию eth_mac_addr (из drivers/net/net_init.c). eth_mac_addr только копирует новый адрес в dev->dev_addr и делает это только тогда, когда интерфейс не работает. Драйверам, которые используют eth_mac_addr, следует устанавливать аппаратный MAC адрес из dev->dev_addr в их методе open.

 

int (*change_mtu)(struct net_device *dev, int new_mtu);

Функция, которая выполняет действия, если произошло изменение максимального блока передачи (MTU) для интерфейса. Если драйверу необходимо сделать что-то особенное при изменении пользователем MTU, он должен объявить эту свою функцию; в противном случае, функция по умолчанию сделает это по-своему. snull имеет шаблон для данной функции, если вам это интересно.

 

int (*header_cache) (struct neighbour *neigh, struct hh_cache *hh);

header_cache вызывается для заполнения структуры hh_cache результатами запроса ARP. Почти все драйверы, подобные  работающим с Ethernet, могут использовать для eth_header_cache реализацию по умолчанию .

 

int (*header_cache_update) (struct hh_cache *hh, struct net_device *dev, unsigned char *haddr);

Метод, который обновляет адрес назначения в структуре hh_cache в ответ на изменение. Ethernet устройства используют eth_header_cache_update.

 

int (*hard_header_parse) (struct sk_buff *skb, unsigned char *haddr);

Метод hard_header_parse извлекает адрес источника из пакета, содержащегося в skb, скопирует его в буфер по haddr. Возвращаемым значением этой функции является длина этого адреса. Ethernet устройства обычно используют eth_header_parse.

Вспомогательные поля

Остальные поля данных struct net_device используются интерфейсом для хранения полезной информации о состоянии. Некоторые из этих полей используются ifconfig и netstat для предоставления пользователю информации о текущей конфигурации. Таким образом, интерфейс должен присвоить значения этим полям:

 

unsigned long trans_start;

unsigned long last_rx;

Поля, которые содержат число тиков (jiffies). Драйвер несёт ответственность за обновление этих значений при начале передачи и когда получен пакет, соответственно. Значение trans_start используется сетевой подсистемой для обнаружения блокировки передатчика. last_rx настоящее время не используется, однако драйвер должен поддерживать это поле в любом случае, чтобы быть готовым для его использования в будущем.

 

int watchdog_timeo;

Минимальное время (в тиках), которое должно пройти, прежде чем сетевой уровень примет решение о том, что при передаче произошёл таймаут, и вызовет функцию драйвера tx_timeout.

 

void *priv;

Эквивалент filp->private_data. В современных драйверах это поле задается функцией alloc_netdev и не должно быть доступно непосредственно; используйте для этого netdev_priv.

 

struct dev_mc_list *mc_list;

int mc_count;

Поля, которые управляют многоадресной передачей. mc_count является числом элементов в mc_list. Для более подробной информации смотрите раздел "Многоадресность" .

 

spinlock_t xmit_lock;

int xmit_lock_owner;

xmit_lock используется, чтобы избежать множества одновременных вызовов функции драйвера hard_start_xmit. xmit_lock_owner является числом процессоров, которые получили xmit_lock. Драйвер не должен делать изменений в этих  полях.

 

В struct net_device есть и другие поля, но они не используются сетевыми драйверами.

 

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