我试图解析一个数据包。直到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端口以某种方式出现正确。
答案 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));
假设您正在使用GCC,则必须通过在结构定义之前添加#pragma pack(1)
来禁用填充字节。要重新启用填充速度,您应该使用#pragma pack()
。