Ответ 1
ETH_P_ALL и любой другой протокол
Протокол ETH_P_ALL
имеет особую роль для сбора исходящих пакетов.
Сокет приемника с любым протоколом, который не равен ETH_P_ALL
, принимает пакеты этого протокола, которые поступают от драйвера устройства.
Socket с протоколом ETH_P_ALL
получает все пакеты перед отправкой исходящих пакетов в драйвер устройства и все входящие пакеты, полученные от драйвера устройства.
Устройство Loopback vs Ethernet device
Пакеты, отправленные на устройство loopback, выходят из этого устройства, а затем одни и те же пакеты принимаются с устройства как входящие.
Таким образом, когда CUSTOM_PROTO
используется с loopback, сокет захватывает пакеты с настраиваемым протоколом как входящие.
Обратите внимание, что если ETH_P_ALL
используется с устройством loopback, каждый пакет принимается дважды. После того, как он будет захвачен как исходящий, а второй - как входящий.
В случае eth0
пакет передается с устройства. Таким образом, такие пакеты идут в драйвер устройства, а затем их можно увидеть на другой стороне физического Ethernet-порта. Например, с сетевым адаптером VirtualBox "Только для хоста" эти пакеты могут быть захвачены некоторым снифером в хост-системе.
Однако пакеты, переданные на физический порт (или его эмуляция), не перенаправляются обратно на этот порт. Таким образом, они не принимаются как входящие с устройства. Вот почему такие пакеты могут быть записаны только в ETH_P_ALL
в исходящем направлении, и они не могут быть видны с помощью CUSTOM_PROTO
во входящем направлении.
Технически необходимо подготовить специальную настройку для выполнения внешней петли пакета (пакеты с порта устройства должны быть отправлены обратно в этот порт). В этом случае поведение должно быть аналогично устройству loopback.
Реализация ядра
Смотрите файл ядра net/core/dev.c
. Существует два разных списка:
struct list_head ptype_base[PTYPE_HASH_SIZE] __read_mostly;
struct list_head ptype_all __read_mostly; /* Taps */
Список ptype_all
предназначен для обработчиков сокетов с протоколом ETH_P_ALL
. Список ptype_base
предназначен для обработчиков с обычными протоколами.
Существует крючок для исходящих пакетов в xmit_one()
, вызванный из dev_hard_start_xmit()
:
if (!list_empty(&ptype_all))
dev_queue_xmit_nit(skb, dev);
Для исходящих пакетов функция dev_queue_xmit_nit()
вызывается для ETH_P_ALL
обработки каждого элемента ptype_all
. Наконец, сокеты типа AF_SOCKET
с протоколом ETH_P_ALL
фиксируют этот исходящий пакет.
Таким образом, наблюдаемое поведение не связано с каким-либо обычным протоколом. Такое же поведение наблюдается при ETH_P_IP
. В этом случае приемник способен захватывать все входящие IP-пакеты, однако он не может захватывать IP-пакеты из sender.c
, которые отправляют с "eth0"
на MAC-адрес устройства "eth0"
.
Это также можно увидеть на tcpdump
. Пакеты, отправленные отправителем, не записываются, если вызывается tcpdump
с возможностью захвата только входящих пакетов (разные версии tcpdump
используют другой аргумент командной строки для включения такой фильтрации).
Первоначальная задача, когда на тех же машинах необходимо различать пакеты по идентификаторам протокола, может быть решена с помощью ETH_P_ALL
. Приемник должен захватить все пакеты и проверить протокол, например:
while (1) {
int len = recvfrom(sockfd, recvbuf, 1514, 0, NULL, NULL);
if (ntohs(*(uint16_t*)(recvbuf + ETH_ALEN + ETH_ALEN)) == CUSTOM_PROTO) {
printf("I received: \n");
break;
}
}
Полезная ссылка "kernel_flow" с хорошей диаграммой http://www.linuxfoundation.org/images/1/1c/Network_data_flow_through_kernel.png
Он основан на ядре 2.6.20, однако в современных ядрах ETH_P_ALL
обрабатывается одинаково.