我正在尝试解析一个数据包。直到有了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表示以太网header.The报头长度ip在被检查时给出了正确的值。但在执行此操作后,我错误地获取了所有字段。当尝试使用uint8_t作为数据类型时(我知道这是错误的!)不知何故,目标端口输出正确。
发布于 2012-02-07 00:14:44
你遇到了endianness。IP数据包的所有字段都是网络字节顺序(也称为"big-endian"),而您的主机系统可能运行little-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);发布于 2012-02-07 00:51:32
我总是对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));发布于 2012-02-07 02:39:21
正如另一个答案所说,您必须处理数据的字节顺序。
您需要处理的另一件事是字节对齐。为了提高速度,当您在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()。
https://stackoverflow.com/questions/9163216
复制相似问题