Готовый перевод Linux Device Drivers 3 / Драйверы устройств Линукс (третье издание): 17.11. MAC Address Resolution

Внимание! Этот перевод, возможно, ещё не готов, так как модераторы установили для него статус «перевод редактируется»

17.11. Разрешение MAC адресов

Возникает вопрос, как ассоциировать MAC-адрес (уникальный
аппаратный идентификатор) и IP-адрес. Многие протоколы сталкиваются
с подобной проблемой, но мы рассмотрим только протокол Ethernet. Мы
попытаемся дать исчерпывающий ответ, и покажем три ситуации: ARP,
Ethernet-заголовки без ARP (как plip), и не Ethernet-заголовки.

17.11.1. Использование ARP с Ethernet

Обычно для определения адресов используется ARP (Address
Resolution Protocol – протокол определения адресов). К счастью, ARP
управляется ядром, Ethernet-интерфейсу не нужно ничего делать для
поддержки ARP. Если dev->addr и dev->addr_len корректно назначены
при открытии, драйверу не требуется что-либо делать для преобразования
IP в MAC-адрес. ether_setup назначает устройству методы
dev->hard_header и dev->rebuild_header.

Хотя ядро и определяет адрес (и кэширует результат), для
построения пакета требуется помощь драйвера интерфейса. Ведь,
драйверу известен заголовок физического уровня, а авторы сетевого кода
надежно изолировали оставшуюся часть ядра от этой информации. В
конце ядро вызывает метод драйвера hard_header, для посылки пакета с
результатом ARP-запроса. Обычно разработчик Ethernet-драйвера не
интересуется этим процессом, имеющийся код Ethernet решает эту
проблему.

17.11.2. Переопределение ARP

Простой интерфейс точка-точка, такой как plip, может извлечь
выгоду из использования Ethernet-заголовков: избежать накладных
расходов при пересылки ARP пакетов туда-сюда. Простой интерфейс
snull получает еще больший выигрыш для этого класса сетевых
устройств. snull не может использовать ARP, потому как драйвер меняет
IP адреса пакета при передаче, и ARP пакеты заменяют IP адреса. Хотя
мы могли бы реализовать простой генератор ARP ответов, чтобы лучше
показать непосредственную обработку заголовков физического уровня.

Если ваше устройство использует обычные аппаратные заголовки
без использования ARP, вы должны перекрыть метод по-умолчанию
dev->hard_header. В snull-реализации, это короткая функция:

int snull_header(struct sk_buff *skb, struct net_device *dev,
unsigned short type, void *daddr, void *saddr,
unsigned int len)
{
struct ethhdr *eth = (struct ethhdr *)skb_push(skb,ETH_HLEN);
eth->h_proto = htons(type);
memcpy(eth->h_source, saddr ? saddr : dev->dev_addr, dev->addr_len);
memcpy(eth->h_dest, daddr ? daddr : dev->dev_addr, dev->addr_len);
eth->h_dest[ETH_ALEN-1] ^= 0x01; /* dest is us xor 1 */
return (dev->hard_header_len);
}

Функция просто берет информацию предоставляемую ядром, и
преобразует в стандартный заголовок Ethernet. Также переключается
один бит в ethernet-адресе получателя, позже мы скажем, зачем это
делается.

Когда пакет получен интерфейсом, аппаратный заголовок
используется несколькими способами в eth_type_trans. Мы уже видели
этот вызов в snull_rx:

skb->protocol = eth_type_trans(skb, dev);

Функция извлекает идентификатор протокола (в данном случае
ETH_P_IP) из Ethernet-заголовка. Также назначается skb->mac.raw,
удаляются аппаратные заголовки из пакета (с помощью skb_pull), и
устанавливатся skb->pkt_type. По умолчанию, при выделении skb, skb-
>pkt_type устанавливается в значение PACKET_HOST (это показывает,
что пакет предназначен локальному хосту). eth_type_trans меняет это
значение в зависимости от Ethernet-адреса получателя: если адрес не
совпадает с адресом принявшего интерфейса, поле pkt_type
устанавливается в PACKET_OTHERHOST. Впоследствии, если интерфейс
не находится в неразборчивом (promiscuous) режиме, или отправка
пакета разрешена в ядре, netif_rx не принимает любой пакет типа
PACKET_OTHERHOST. Поэтому snull_header назначает аппаратный адрес
получателя, который принимается интерфейсом.

Если ваш интерфейс – канал точка-точка, он не принимает
групповые пакеты. Для устранения этой проблемы, вспомните, что если в
адресе назначения младший бит первого октета равен нулю, то пакет
предназначен одному хосту (то есть, является или PACKET_HOST или
PACKET_OTHERHOST). plip драйвер использует значение 0xfc, в первом
октете аппаратного адреса, когда как snull использует 0x00.

Оба адреса работают в Ethernet-ориентированном канале точка-точка.

17.11.3. Не Ethernet заголовки

Мы увидели, что аппаратный заголовок, помимо адреса назначения
пакета, содержит и другую информацию коммуникационного протокола.
Теперь мы расскажем, как получить полезную информацию из
аппаратных заголовков. Для получения более полной информации,
можете ознакомиться с исходным кодом ядра, или технической
документацией средств передачи. Большинство разработчиков могут
игнорировать эту тему и использовать Ethernet-реализацию.

Здесь не приведена вся информация о каждом протоколе.
Соединение точка-точка, такое как plip или snull, не передает
заголовок Ethernet. В метод устройства hard_header, показанный ранее в
реализации snull_header, передается информация (протокольного уровня
и аппаратные адреса) из ядра. Также предается 16-разрядный номер
протокола в аргументе type. На пример, IP идентифицируется значением
ETH_P_IP. Предполагается, что драйвер корректно передаст и пакет
данных, и номер протокола на принимающий хост. Соединение точка-
точка опускает адрес из аппаратного заголовка, предавая только номер
протокола, потому что гарантируется независимость адресов отправителя
и адресата. Только IP-соединения могут не передавать аппаратные
заголовки.

Когда пакет получен, функция приема в драйвере корректно
установит поля skb->protocol, skb->pkt_type и skb->mac.raw.

kb->mac.raw – символьный указатель, используемый для механизма
определения адреса, реализованного на верхних уровнях сетевого кода
(на пример net/ipv4/arp.c). Должен указывать на адрес,
соответствующий dev->type. Возможные значения dev->type приведены
в <linux></linux>if_arp.h>. Для интерфейсов Ethernet используется
ARPHRD_ETHER. В качестве примера посмотрите, как eth_type_trans
работает с Ethernet-заголовками для приема пакетов:

skb->mac.raw = skb->data;
skb_pull(skb, dev->hard_header_len);

В простейшем случае (соединение точка-точка без заголовков), skb-
>mac.raw может указывать на статический буфер содержащий
аппаратный адрес интерфейса, протокол может быть установлен в
значение ETH_P_IP, и packet_type может содержать значение по
умолчанию PACKET_HOST.

Т.к. каждый тип аппаратуры – уникален, тяжело добавить к уже
сказанному что-то еще. Много примеров можно увидеть в ядре. На
пример, посмотрите драйвер AppleTalk (drivers/net/appletalk/cops.c),
драйвер инфракрасного порта (drivers/net/irda/smc_ircc.c), или
драйвер PPP (drivers/net/ppp_generic.c).

Внимание! Этот перевод, возможно, ещё не готов.
Его статус: перевод редактируется

Переведено на Нотабеноиде
http://notabenoid.org/book/11832/38392

Переводчики: hardorsoft, ximen

Настройки

Готово:

100.00% КП = 1.2