Разрешение MAC адреса

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

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

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

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

 

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

Подмена 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; /* назначение - это мы xor 1 */

    return (dev->hard_header_len);

}

 

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

 

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

 

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

 

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

 

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

Не-Ethernet заголовки

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

 

Стоит отметить, что не вся информация будет предоставлена каждым протоколом. Соединение точка-точка, такое как plip или snull, могло бы избежать передачи целого заголовка Ethernet без потери общности. Метод устройства hard_header, показанный ранее как реализованный функцией snull_header, получает информацию по доставке, на уровне протокола и аппаратных адресов, от ядра. Он также получает 16-ти разрядный номер протокола аргументе type; IP, например, идентифицируется как ETH_P_IP. Драйвер ожидает правильность доставки данных пакета и номера протокола к принимающему устройству. Соединение точка-точка может пропустить адреса своего аппаратного заголовка, передавая только номер протокола, так как доставка гарантируется независимо от адресов источника и назначения. Соединение только на основе IP могло бы даже совсем избежать передачи всех аппаратных заголовков.

 

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

 

skb->mac.raw является символьным указателем, используемым механизмом разрешения адреса, реализованном на верхних уровнях сетевого кода (например, net/ipv4/arp.c). Он должен указывать на адрес машины, который соответствует dev->type. Возможные значения типа устройства определены в <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 может указывать на статический буфер, содержащий аппаратный адрес этого интерфейса, protocol может быть установлен в 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).

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