Ошибка при разборе пакета - PullRequest
0 голосов
/ 06 февраля 2012

я пытаюсь разобрать пакет. пока заголовок ip все в порядке (я могу получить все значения правильно). но для заголовка udp (проверяется, если протокол 17), значения оказываются неправильными (все 4 поля). Я пытаюсь сделать это:

struct udp_header{
uint16_t sport;
uint16_t dport;
uint16_t len;
uint16_t chksum;
};
 struct udp_header* udp= (struct udp_header*)(packet + 14 + ip_hdr->ip_hl*4); 

Пакет - это указатель, указывающий на начало пакета. 14 - для заголовка ethernet. Длина заголовка ip при проверке выдает правильное значение. Но после выполнения этой операции я получаю все поля неправильно. при попытке использовать uint8_t в качестве типа данных (я знаю, что это неправильно!) порт назначения как-то получается правильным.

Ответы [ 3 ]

3 голосов
/ 06 февраля 2012

Вы столкнулись с порядком байтов . IP-пакеты имеют все поля в порядке байтов в сети (он же «big-endian»), и ваша хост-система, вероятно, работает с прямым порядком байтов. Посмотрите на ntohs() и друзей для одного подхода.

Правильный подход состоит в том, чтобы не копировать структуру как есть из сетевых данных, а вместо этого извлекать каждое поле вручную и заменять его, если необходимо. Это также решает любые проблемы с заполнением и выравниванием, нет никакой гарантии, что ваш struct сопоставлен с памятью вашего компьютера точно так же, как пакет сериализуется.

Так вы бы сделали, например ::

udp_header.sport = ntohs(*(unsigned short*) (packet + 14 + 4 * ip_hdr->ip_hl));

Это также немного ненадежно, поскольку предполагается, что результирующий адрес может быть корректно приведен к указателю на unsigned short. На x86 это будет работать, но не эпично.

Еще лучше, на мой взгляд, отказаться от использования указателей и вместо этого написать функцию под названием, например, unsigned short read_u16(void *packet, size_t offset), который извлекает побайтовое значение и возвращает его. Тогда вы просто сделаете:

udp_header.sport = read_u16(packet, 14 + 4 * ip_hdr->ip_hl);
2 голосов
/ 06 февраля 2012

Я всегда использую эту структуру для заголовка IP:

struct sniff_ip {
    u_char  ip_vhl;                 /* version << 4 | header length >> 2 */
    u_char  ip_tos;                 /* type of service */
    u_short ip_len;                 /* total length */
    u_short ip_id;                  /* identification */
    u_short ip_off;                 /* fragment offset field */
    #define IP_RF 0x8000            /* reserved fragment flag */
    #define IP_DF 0x4000            /* dont fragment flag */
    #define IP_MF 0x2000            /* more fragments flag */
    #define IP_OFFMASK 0x1fff       /* mask for fragmenting bits */
    u_char  ip_ttl;                 /* time to live */
    u_char  ip_p;                   /* protocol */
    u_short ip_sum;                 /* checksum */
    struct  in_addr ip_src,ip_dst;  /* source and dest address */
};
#define IP_HL(ip)               (((ip)->ip_vhl) & 0x0f)
#define IP_V(ip)                (((ip)->ip_vhl) >> 4)

И чтобы получить указатель структуры UDP:

udp = (struct sniff_udp*)(packet + SIZE_ETHERNET + (IP_HL(ip)*4));
0 голосов
/ 06 февраля 2012

Как заметил другой ответ, вам приходится иметь дело с порядком байтов ваших данных.

Другая вещь, с которой вам нужно иметь дело, это выравнивание байтов.Для скорости, когда вы определяете структуру в C следующим образом:

struct udp_header{
uint16_t sport;
uint16_t dport;
uint16_t len;
uint16_t chksum;
};

Компилятор C может оставлять байты заполнения между этими полями, чтобы доступ к элементам можно было выполнять с помощью более быстрых инструкций по сборке для доступа к памяти одной инструкции.Вы можете проверить, делает ли ваш компилятор c это printf("struct size is: %u\n", sizeof(struct udp_header));

Предполагая, что вы используете GCC, вы должны отключить байты заполнения, добавив #pragma pack(1) перед определением структуры.Чтобы снова включить заполнение для скорости, вы должны использовать #pragma pack().

...