做嗅探器需要对抓来的数据包进行协议分析,一般而言可以做个协议栈什么的,这里展示一下今天下午遇到的问题。
我的协议分析思路是这样子的(拿单个报头):
class EtherHeader {
Byte dstAddr[6];
Byte srcAddr[6];
Byte protocol[2];
};
假设收到的数据包以数组形式存在 content中,则如此可获得其内容:
EtherHeader *ethHeader =(EtherHeader*) content;
我本来的打算是考虑到每个协议头都有相似的行为,如分析下一个协议名、获得下一个协议的偏移等等,因此写了一个HeaderInterface接口及一个header:
class HeaderInterface {
virtual string getNextProtocolName() const =0;
virtual Header *nextProtocolOffset() const =0;
};
class Header :public HeaderInterface{
virtual string getNextProtocolName()....
...
};
class EtherHeader:public Header {
...
class PPPoEHeader:public Header {
...
待到使用时,总是会出现段错误,而且其错误并非因数据越界而起,前思后想,终于发现拷贝到的数据有4个字节的偏移,以EtherHeader为例,本当是dstAddr获得content前6个字节,但现在却是从第5个字节考试,前思后想,终于发现是我的设计在作怪,使用了virtual后,类体中会多出一个v_ptr,显然我用的这个版本的G++是将v_ptr放在类体最前面的,这样指针赋值之后,content的前四个字节就成了v_ptr的内容,调用接口中任何一个函数都会实际访问一个不合法的地址,从而引起段错误。
为了实现协议树,我在每个协议头中加入了一个void *next,结果有一个报头中的字段是这样的:
class SomeProtocol{
...
Byte protocol[2];
void *next;
...
};
结果就是这个next指针导致protocol被迫对齐,整个类多占了2个字节,计算头偏移时使用 this + sizeof(SomeProtocol) -sizeof(next),计算出来的结果是不正确的。
而后,为了避免麻烦,采取计算偏移直接用计算好的数值的方法,形如:
IPv4Header *ipv4Header =(IPv4Header *)( ethHeader +14);
但是结果是不正确的,因为这里的14是14个字节,而前面的ethHeader却暗示其计算按照单个ethHeader的大小作为“1”来算,故而计算出错,应对ethHeader先进行一个强制转换,使其以Byte为单位来计算:
IPv4Header *ipv4Header =(IPv4Header *)( (Byte*)ethHeader +14);
计算结果正确。
指针这厮当真不是个好东西,只是我目前掌握的协议分析方法中只有这一个能够很好的解决重复拷贝等问题(嗅探器需要防备较大的网络流量,要尽量减少协议分析在时间及空间上造成的负担),也只好暂且凑合着用了。若哪位仁兄有好办法,也可留言给我,定当重谢。