2016-12-30 3 views
0

Сегодня я инвестировал немного больше времени, чтобы узнать о ARP-пакетах. Чтобы понять его структуру, я попытался построить один на себе в C, используя libpcap. Я структурировал простой пакет запроса ARP и использовал функцию pcap_inject для отправки пакета. Эта функция возвращает количество отправленных байтов.Как структурировать и пакет запроса ARP в C

Когда я отлаживаю свой код, я увидел, что мой пакет был длиной 42 байта. Я немного искал в Интернете и не смог найти ответ, который говорит мне, является ли это подходящим размером для запроса ARP или нет. Даже wikipedia entry немного смутил меня. И я открыл этот пост. Из ответа, предоставленного пользователем:

  • Если сообщение ARP должны быть отправлено в немаркированном кадре, то сам кадр накладного расходы составляют 18 байт. Это приведет к созданию кадра 28 + 18 = 46 байт без заполнения. Дополнительные 18 байтов заполнения - , необходимые в этом случае, чтобы раздуть рамку до длины в 64 байта.
  • Если сообщение ARP должно быть отправлено в фрейме с отметкой 802.1Q, то служебные данные кадра составляют 22 байта, в результате чего общий размер кадра 28 + 22 = 50 байт. В этом случае прокладка должна быть 14 байтов.
  • Если сообщение ARP должно быть отправлено в двойном теге, тогда служебные данные кадра составляют 26 байтов, в результате общий размер кадра составляет 54 байт. В этом случае длина прокладки должна быть 10 байтов.

Вопрос в том, что мне нужно сделать в этой ситуации. Нужно ли использовать прокладку или нет?

Bellow Я размещаю структуру своего пакета.

#define ETH_P_ARP 0x0806 /* Address Resolution packet */ 
#define ARP_HTYPE_ETHER 1 /* Ethernet ARP type */ 
#define ARP_PTYPE_IPv4 0x0800 /* Internet Protocol packet */ 

/* Ethernet frame header */ 
typedef struct { 
    uint8_t dest_addr[ETH_ALEN]; /* Destination hardware address */ 
    uint8_t src_addr[ETH_ALEN]; /* Source hardware address */ 
    uint16_t frame_type; /* Ethernet frame type */ 
} ether_hdr; 

/* Ethernet ARP packet from RFC 826 */ 
typedef struct { 
    uint16_t htype; /* Format of hardware address */ 
    uint16_t ptype; /* Format of protocol address */ 
    uint8_t hlen; /* Length of hardware address */ 
    uint8_t plen; /* Length of protocol address */ 
    uint16_t op; /* ARP opcode (command) */ 
    uint8_t sha[ETH_ALEN]; /* Sender hardware address */ 
    uint32_t spa; /* Sender IP address */ 
    uint8_t tha[ETH_ALEN]; /* Target hardware address */ 
    uint32_t tpa; /* Target IP address */ 
} arp_ether_ipv4; 

В конце концов, я просто скопировать каждый элемент структуры в сильфона порядке и отправить пакет:

void packageARP(unsigned char *buffer, ether_hdr *frameHeader, arp_ether_ipv4 *arp_packet, size_t *bufferSize) { 
    unsigned char *cp; 
    size_t packet_size; 

    cp = buffer; 

    packet_size = sizeof(frameHeader->dest_addr) 
       + sizeof(frameHeader->src_addr) 
       + sizeof(frameHeader->frame_type) 
       + sizeof(arp_packet->htype)  
       + sizeof(arp_packet->ptype)  
       + sizeof(arp_packet->hlen)   
       + sizeof(arp_packet->plen)  
       + sizeof(arp_packet->op)   
       + sizeof(arp_packet->sha)   
       + sizeof(arp_packet->spa)   
       + sizeof(arp_packet->tha)   
       + sizeof(arp_packet->tpa); 
    /* 
    * Copy the Ethernet frame header to the buffer. 
    */ 
    memcpy(cp, &(frameHeader->dest_addr), sizeof(frameHeader->dest_addr)); 
    cp += sizeof(frameHeader->dest_addr); 

    memcpy(cp, &(frameHeader->src_addr), sizeof(frameHeader->src_addr)); 
    cp += sizeof(frameHeader->src_addr); 

    /* Normal Ethernet-II framing */ 
    memcpy(cp, &(frameHeader->frame_type), sizeof(frameHeader->frame_type)); 
    cp += sizeof(frameHeader->frame_type); 


    /* 
    * Add the ARP data. 
    */ 
    memcpy(cp, &(arp_packet->htype), sizeof(arp_packet->htype)); 
    cp += sizeof(arp_packet->htype); 

    memcpy(cp, &(arp_packet->ptype), sizeof(arp_packet->ptype)); 
    cp += sizeof(arp_packet->ptype); 

    memcpy(cp, &(arp_packet->hlen), sizeof(arp_packet->hlen)); 
    cp += sizeof(arp_packet->hlen); 

    memcpy(cp, &(arp_packet->plen), sizeof(arp_packet->plen)); 
    cp += sizeof(arp_packet->plen); 

    memcpy(cp, &(arp_packet->op), sizeof(arp_packet->op)); 
    cp += sizeof(arp_packet->op); 

    memcpy(cp, &(arp_packet->sha), sizeof(arp_packet->sha)); 
    cp += sizeof(arp_packet->sha); 

    memcpy(cp, &(arp_packet->spa), sizeof(arp_packet->spa)); 
    cp += sizeof(arp_packet->spa); 

    memcpy(cp, &(arp_packet->tha), sizeof(arp_packet->tha)); 
    cp += sizeof(arp_packet->tha); 

    memcpy(cp, &(arp_packet->tpa), sizeof(arp_packet->tpa)); 
    cp += sizeof(arp_packet->tpa); 

    *bufferSize = packet_size; 
} 

Является ли это правильный способ структурирования пакет запроса ARP?

ответ

0

Это правильная структура - за исключением, что компилятор C может свободно вставлять прокладку, чтобы гарантировать, что элементы структуры размещены на наиболее эффективных границах. В частности, spa и tpa не имеют естественных 32-разрядных границ (из-за предыдущих полей 6-байтовых MAC-адресов), и поэтому компилятор может захотеть вставить два байта заполнения перед каждым.

Если вы используете GCC, вы можете быть уверены, что не происходит с __attribute__((packed)):

struct { 
     [fields] 
} __attribute__((packed)) arp_ether_ipv4; 

Другие компиляторы могут иметь различный, но эквивалентный механизм (а #pragma директиву, например).

Полезная нагрузка ARP должна быть 28 байт. Добавление 14-байтового заголовка Ethernet, который дает 42 байта. Как сказал ваш сайт, заголовок 802.1Q (VLAN) вставляет дополнительные 4 байта, а «двойной тегированный» фрейм (не общий за пределами поставщиков интернет-услуг) добавит 2 X 4 = 8 байт. Если вы находитесь на обычной машине с конечными точками, вы обычно не добавляете эти заголовки. ИТ-отдел настроит ваши коммутаторы для автоматического вставки/удаления этих заголовков по мере необходимости.

42 байта автоматически загружают - 64 байта (размер минимального пакета Ethernet) вашим сетевым драйвером. 64 на самом деле 60 + 4-байтовый FCS FC2 [контрольная сумма кадров]. (Сообщение, которое вы цитируется по-видимому, в том числе 4-байтный FCS в своих расчетах, поэтому их число, кажется, сильный удар.)

Кроме того, не забывайте использовать сетевой порядок байтов для всех uint16_t и uint32_t полей: (ntohs и ntohl)

+0

Привет, Гил, спасибо за ваш ответ. Я добавил поле 'attribute', как вы предложили. Для полей 'uint16_t' и' uint32_t' я использую 'htons'. Это неправильно? Я также хотел спросить, какова должна быть ценность целевого MAC, поскольку это то, что мы хотим узнать. Я использую 'unsigned char arp_tha [6] = {0, 0, 0, 0, 0, 0};' –

+0

'htons' является хостом для сети- * short * (16-бит/uint16_t). 'htonl' является длиной от сети к сети (32-бит/uint32_t). Установка 'tha' для всех нулей правильная. Его значение в запросе следует игнорировать в любом случае. Это (адрес целевого оборудования) - это то, что вы ищете, совершая транзакцию ARP. –

Смежные вопросы