昨天看到了最重要的一部分,分析数据包。
这个分析UDP的程序基本上前面都能看得懂,主要还是对报文的分析这一部分。
在blog里找到的图,对于这个过程,反过来也就是:应用层数据,封装成UDP或者TCP报文,再加上IP首部,然后再加上以太网首部,就成为了可以在链路层传播的数据帧。
以太网驱动程序首先根据以太网首部中的“上层协议”字段确定该数据帧的有效载荷(payload,指除去协议首部之外实际传输的数据)是IP、ARP 还是RARP 协议的数据报,然后交给相应的协议处理。假如是IP 数据报,IP 协议再根据IP 首部中的“上层协议”字段确定该数据报的有效载荷是TCP、UDP、ICMP 还是IGMP,然后交给相应的协议处理。假如是TCP 段或UDP段,TCP 或UDP 协议再根据TCP 首部或UDP 首部的“端口号”字段确定应该将应用层数据交给哪个用户进程。IP 地址是标识网络中不同主机的地址,而端口号就是同一台主机上标识不同进程的地址,IP 地址和端口号合起来标识网络中唯一的进程。
虽然IP、ARP 和RARP 数据报都需要以太网驱动程序来封装成帧,但是从功能上划分,ARP 和RARP 属于链路层,IP 属于网络层。虽然ICMP、IGMP、TCP、UDP 的数据都需要IP 协议来封装成数据报,但是从功能上划分,ICMP、IGMP 与IP 同属于网络层,TCP 和UDP属于传输层。
所以这个程序里先捕获链路层的数据帧:
再看一下以太网帧的结构
以太网头部是14字节,14字节之后的部分为IP数据报,让这个ip首部指针 ih 指向ip数据报首部
再看一下ip首部结构
根据ip_header结构定义,ver_ihl是4位版本号+4位首部长度,现在要获取ip报文的首部长度 ip_len,然后用 ih 加上这个长度就得到了UDP报文的首部。那么只需要简单的用这个字节和0xf相与,就得到了低4位(注意是低4位,不是高4位)的首部长度。
这个就涉及到大小端的问题了,intel机器一般是小端存储,所以需要进行字节序列转换
在C/C++写网络程序的时候,往往会遇到字节的网络顺序和主机顺序的问题。这是就可能用到htons(), ntohl(), ntohs(),htons()这4个函数。
网络字节顺序与本地字节顺序之间的转换函数:
htonl()--"Host to Network Long"
ntohl()--"Network to Host Long"
htons()--"Host to Network Short"
ntohs()--"Network to Host Short"
之所以需要这些函数是因为计算机数据表示存在两种字节顺序:NBO与HBO
网络字节顺序NBO(Network Byte Order): 按从高到低的顺序存储,在网络上使用统一的网络字节顺序,可以避免兼容性问题。
主机字节顺序(HBO,Host Byte Order): 不同的机器HBO不相同,与CPU设计有关,数据的顺序是由cpu决定的,而与操作系统无关。
如 Intel x86结构下, short型数0x1234表示为34 12, int型数0x12345678表示为78 56 34 12
如 IBM power PC结构下, short型数0x1234表示为12 34, int型数0x12345678表示为12 34 56 78
由于这个原因不同体系结构的机器之间无法通信,所以要转换成一种约定的数序,也就是网络字节顺序,其实就是如同power pc那样的顺序. 在PC开发中有ntohl和htonl函数可以用来进行网络字节和主机字节的转换.
对sport = ntohs( uh->sport); 这一句单步调试一下,
可见经过ntohs()函数处理之后,值44366变成了20141
转换成16进制,可见确实是把高低字节转换了一下,也就是说网络字节顺序中,从高到低存储,AD是高字节,4E是低字节,而在intel机器中,用小端存储,所以应该表示为4EAD(从低到高存储)。
最后是打印IP地址和UDP端口。
这个程序其实还是很简单,现在我需要在这个程序的基础上修改,以适用于毕设的需求。