数据包解析出错

时间:2012-02-06 16:12:37

标签: c libpcap networkstream packet-capture

我试图解析一个数据包。直到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); 

Packet是指向数据包开头的指针。 14用于以太网头。检查时的头长度ip给出正确的值。但在执行此操作后,我错误地得到了所有字段。当用uint8_t作为数据类型尝试(我知道它错了!)时,destintion端口以某种方式出现正确。

3 个答案:

答案 0 :(得分:3)

您已遇到endianness。 IP数据包具有网络字节顺序的所有字段(也称为“big-endian”),并且您的主机系统可能运行little-endian。查看ntohs()和朋友的一种方法。

正确的方法是不按原样从网络数据中复制结构,而是手动提取每个字段并在必要时进行字节交换。这也适用于填充和对齐的任何问题,不能保证您的struct映射到计算机的内存中的方式与数据包序列化完全相同。

所以你会这样做。:

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

这也有点不确定,因为它假定结果地址可以有效地转换为指向unsigned short的指针。在x86上可行,但它不是史诗。

在我看来,更好的是放弃指针的使用,而是编写一个名为eg的函数。 unsigned short read_u16(void *packet, size_t offset)逐字节提取值并返回它。那你就做了:

udp_header.sport = read_u16(packet, 14 + 4 * ip_hdr->ip_hl);

答案 1 :(得分:2)

我总是将这个结构用于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));

答案 2 :(得分:0)

正如另一个答案所说,你必须处理数据的字节顺序。

您需要处理的另一件事是字节对齐。对于速度,当您在C中定义结构时,如下所示:

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

C编译器可能会在这些字段之间留下填充字节,以便可以使用更快的单指令存储器访问汇编指令完成成员访问。您可以通过printf("struct size is: %u\n", sizeof(struct udp_header));

检查您的c编译器是否正在执行此操作

假设您正在使用GCC,则必须通过在结构定义之前添加#pragma pack(1)来禁用填充字节。要重新启用填充速度,您应该使用#pragma pack()