结构体边界对齐问题
在构造DNS应答包的Answer字段时,我定义了一个结构体,如下所示:
typedef struct{
__u16 name;
__u16 type;
__u16 class;
__u32 ttl;
__u16 length;
__u32 ipv4_addr;
}DNS_ANSWER_DATA;
但是在进行数据包的构造的时候,在测试时,发送的数据包总是从length字段开始出现错误,导致DNS数据报文最终不能被正确解析,原因在于结构体的边界对其原则。
在debug的时候发现,sizeof(DNS_ANSWER_DATA) = 20,按理来说应该为16。
C语言中边界对齐原则如下所示:
- 原则1:普通数据成员对齐规则:第一个数据成员放在offset为0的地方,以后每个数据成员存储的起始位置要从该成员大小的整数倍开始(比如int在32位机为4字节,则要从4的整数倍地址开始存储)。
- 原则2:结构体成员对齐规则:如果一个结构里有某些结构体成员,则该结构体成员要从其内部最大元素大小的整数倍地址开始存储。(struct a里存有struct b,b里有char,int,double等元素,那b应该从8的整数倍开始存储。)
- 原则3:结构体大小对齐规则:结构体大小也就是sizeof的结果,必须是其内部成员中最大的对齐参数的整数倍,不足的要补齐。
根据我上面定义的结构体成员的顺序以及其大小可知,其在存储的时候存储结构如下图所示:
在64位系统:
在32位系统
因此在实验中,伪造的DNS报文的结构如下所示:
解决方式:参考博客:
https://blog.csdn.net/zhangxiong2532/article/details/50826917
https://wenku.baidu.com/view/96c2d256f01dc281e53af026.html
当不希望编译器对结构体做对齐处理,而希望按照他原有的大小分配空间时,这里就要用到 attribute((packed)) ,这个意思是告诉编译器不要做对齐处理。
#pragma pack(push,1)
typedef struct{
unsigned short a_name;
unsigned short a_type;
unsigned short a_class;
unsigned int a_ttl;
unsigned short a_data_len;
unsigned int a_ipv4_addr;
}DNS_ANSWER_DATA;
#pragma pack(pop)