AF_PACKET и Ethernet - PullRequest
       24

AF_PACKET и Ethernet

0 голосов
/ 06 января 2019

Меня очень смущает, как именно семейство сокетов AF_PACKET (для сокетов SOCK_RAW) конкретно относится к Ethernet (IEEE 802.3).

Что я понимаю до сих пор:

  • Я понимаю модель OSI и то, как технологии уровня 2 Ethernet вписывается в модель.

  • Я понимаю, что AF_PACKET можно использовать с SOCK_RAW сокетом для получать дейтаграммы, содержащие 14-байтовый заголовок Ethernet, за которым следуют некоторые другие заголовки протокола более высокого уровня, такие как IPv4, IPv6 и т. д., сопровождаемый необязательно протоколом транспортного уровня, таким как TCP, и наконец полезная нагрузка.

  • Я понимаю, что вы можете передать флаги, такие как ETH_P_ALL или ETH_P_IP как аргумент протокола socket для пакетов фильтра ядра для вас, только отправляя вам пакеты, содержащие заголовки определенного тип.

  • Я понимаю, что сокеты, созданные с семейством AF_PACKET, могут получать или отправлять на конечные точки типа sockaddr_ll, который связан с конкретным MAC-адресом (адрес EUI-48), а также с конкретным сетевым интерфейсом (таким как eth0 или что угодно).

Что я не понимаю:

  • Я не понимаю, должен ли AF_PACKET работать исключительно с устройствами Ethernet, в отличие от других технологий уровня 2, таких как Wifi, Bluetooth, Token Ring, Infiniband и т. Д.

  • Я не понимаю взаимосвязи между устройствами Ethernet и протоколами уровня 2, которые используют 14-байтовый заголовок Ethernet . Заголовок Ethernet составляет 14 байтов и может быть определен примерно так: struct eth_hdr { char dest_address[6]; char source_address[6]; uint16_t ethertype; }; Другими словами, этот заголовок используется только с физическими устройствами Ethernet? Кажется, ответом является нет , потому что, если я использую AF_PACKET на интерфейсе обратной связи, я все равно получу пакеты, содержащие 14-байтовые Ethernet-заголовки . Но шлейф - это , а не устройство Ethernet. Так почему он получает пакеты, содержащие заголовки Ethernet?

  • Если AF_PACKET можно использовать с устройствами, не поддерживающими Ethernet, указывает ли флаг протокола ETH_P_ALL на прием только тех пакетов, которые имеют 14-байтовый заголовок Ethernet?


Мой вопрос (ы):

Означает ли использование AF_PACKET, что вы гарантированно всегда получаете пакеты с 14-байтовыми заголовками Ethernet?

Если это так, значит ли это, что AF_PACKET предназначен для использования ТОЛЬКО с устройствами Ethernet (в отличие от других технологий уровня 2, таких как Wifi, Token Ring, Bluetooth, Infiniband и т. Д.)?

Если ответ на любой из этих вопросов NO , то как приложение может программно определить, какой тип заголовка уровня 2 следует ожидать при получении дейтаграммы на сокете AF_PACKET?

1 Ответ

0 голосов
/ 06 января 2019

Предупреждение: Это происходит из-за каннибализации некоторого кода, который я написал для производственного программного обеспечения, использующего PF_PACKET, который был только для Ethernet, поэтому может быть неполным / неточным.

Вы используете ETH_P_ALL, который даст вам что-нибудь. Но есть много символов ETH_P_* на выбор (например, ETH_P_802_3_MIN).

Привязка / выбор не основаны не только на вызове socket, но и на заданном интерфейсе .

Сначала вам нужен интерфейс имя , которое вы хотите (например, eth0) из списка, который вы можете получить из ifconfig.

Затем получите интерфейс index , используя ioctl(SIOCGIFINDEX,...) из имени интерфейса [или вы можете просто жестко закодировать его, так как ifconfig распечатает их в порядке индекса].

Затем bind к этому интерфейсу на основе индекса интерфейса.

Поскольку вам известен тип интерфейса (например, вы выбрали eth0 или wifi и т. Д.), После этого вы сможете переваривать заголовок физического уровня, поскольку вы знаете, является ли он struct eth_hdr или нет.

Обратите внимание, что существует ряд других SIOCGIF* ioctl, которые вы можете использовать для получения списка интерфейсов и другой информации, которая может позволить вам различать тип интерфейса [и, следовательно, какой физический заголовок ожидать].

В любом случае, вот пример кода из того, что я сделал:

int
init(const char *intf)
// intf -- interface name (e.g. eth0, etc. -- whatever comes from ifconfig)
{
    int err;

#if 1
    int styp = SOCK_RAW;
#else
    int styp = SOCK_DGRAM;
#endif

    int netsock = socket(PF_PACKET,styp,htons(ETH_P_ALL));

    struct ifreq ifr;
    memset(&ifr,0,sizeof(ifr));
    strncpy(ifr.ifr_name,intf,sizeof(ifr.ifr_name));

    // get the index number of the interface
    err = ioctl(ntc->ntc_sock,SIOCGIFINDEX,&ifr);
    if (err < 0)
        do_whatever;

    printf("init: IFRIDX ifr_ifindex=%d\n",ifr.ifr_ifindex);
    int ifidx = ifr.ifr_ifindex;

    struct sockaddr_ll addr;
    addr.sll_family = AF_PACKET;
    addr.sll_protocol = htons(ETH_P_ALL);
    addr.sll_ifindex = ifidx;

    err = bind(netsock,(struct sockaddr *) &addr,
        sizeof(struct sockaddr_ll));
    if (err < 0)
        do_whatever;

    return netsock;
}
...