本文主要记录使用抓包工具抓取网络数据包的过程,可以更好的理解Linux网络协议栈。

1、实验环境

一个嵌入式开发板:加载好网卡驱动程序

一台笔记本:装好抓包工具,如wireshark,装一个网络通信助手。

开发板通过直连接到PC的网口。

2、抓取UDP通信数据包

数据包包含内容如下(https://xingxingzhihuo.blog.csdn.net/article/details/94360079):

2.1 环境装备

1、配置PC的IP:

2、PC端启动udp server

3、启动抓包工具,抓取本地网卡的数据包

4、启动开发板,并为开发板配置PC同网段IP:

5、查看开发板的arp表,并未开始学习到任何mac

6、开发板中执行udp发包 APP,可通过如下代码交叉编译得到

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <errno.h>
 
int main(int argc, char *argv[])
{
    struct sockaddr_in srvAddr;
    bzero(&srvAddr, sizeof(srvAddr));
    srvAddr.sin_family = AF_INET;
    srvAddr.sin_addr.s_addr = inet_addr("10.100.70.140");
    srvAddr.sin_port = htons(8765);
 
    int iSock = socket(AF_INET, SOCK_DGRAM, 0); // udp
	#define N 2000
	char szBuf[N] = {0};
	for(unsigned int i = 0; i < N; i++) //字符数组最后一个字符不要求是‘\0’
	{
		szBuf[i] = 'a';	
	}
	
    int iRet = sendto(iSock, szBuf, sizeof(szBuf), 0, (struct sockaddr *)&srvAddr, sizeof(srvAddr));
	printf("send size is %d, iRet is %d, errmsg[%s]\n", sizeof(szBuf), iRet, strerror(errno));
    
	close(iSock);
    return 0;
}

7、查看抓包工具抓取的数据包和PC端收到的数据

收到的数据前一部分:

8、查看开发板arp表,里面记录了刚才通信时学习到的MAC

9、分析数据包

客户端主要是向服务端发2000个字符a,数据包分析主要分析,在网络协议栈中数据包被封装的UDP头、IP头、MAC头等

2.2 数据包分析

2.2.1 UDP头【8字节】

udp头内容:e6 a1 22 3d 07 d8 00 83

typedef struct _UDP_HEADER 
{
     unsigned short m_usSourPort;       // 源端口号16bit                    
     unsigned short m_usDestPort;       // 目的端口号16bit
     unsigned short m_usLength;        // 数据包长度16bit
     unsigned short m_usCheckSum;      // 校验和16bit
}__attribute__((packed))UDP_HEADER, *PUDP_HEADER;

源端口号:e6 a1 = 59041

目的端口:22 3d = 8765

数据包长度:07 d8 = 2008 (8字节UDP头+2000数据载荷)

校验和:00 83

2.1.2 IP头【20字节】

ip头内容:45 00 05 dc 84 16 20 00 40 11 4e 83 c0 a8 00 a7 c0 a8 00 80

include/uapi/linux/ip.h

struct iphdr {                           
#if defined(__LITTLE_ENDIAN_BITFIELD)    
    __u8    ihl:4,                       
        version:4;                       
#elif defined (__BIG_ENDIAN_BITFIELD)    
    __u8    version:4,                   
        ihl:4;                           
#else                                    
#error  "Please fix <asm/byteorder.h>"   
#endif                                   
    __u8    tos;                         
    __be16  tot_len;                     
    __be16  id;                          
    __be16  frag_off;                    
    __u8    ttl;                         
    __u8    protocol;                    
    __sum16 check;                       
    __be32  saddr;                       
    __be32  daddr;                       
    /*The options start here. */         
};   

版本:4(iPV4)

首部长度:5(单位是4字节,5*4=20字节头部)

服务类型:00 

为应用程序、主机或路由器处理报文提供一个优先级服务标志。TOS占8 bit,其中3 bit的优先权子字段(现在已被忽略),4 bit的TOS子字段,分别代表:最小时延、最大吞吐量、最高可靠性和最小费用。4 bit中只能置位其中1 bit为1。如果所有4 bit均为0,那么就意味着是一般服务。1 bit未用位但必须置0。交互应用如Telnet和Rlogin要求最小的传输时延(主要用来传输少量的交互数据),FTP文件传输要求有最大的吞吐量,而网络管理(SNMP)和路由选择协议要求有最高可靠性。需要注意的是并非所有的TCP/IP实现都支持TOS特性。

总长度:05 dc(1500)

指整个IP数据报以字节为单位的长度,占16 bit,因此IP数据报最长可达65535字节。由于链路层MTU的限制,较长的IP数据报会被分片。当数据报被分片时,该字段的值也随着变化,因为该值只是表示当前IP数据报的长度。 实际上,大量使用UDP的应用( RIP,TFTP,BOOTP,DNS以及SNMP)都限制用户数据报长度为512字节。 IP数据报中没有数据内容部分的长度,但借助报头中的首部长度可以很容易得出数据内容的长度是总长度减去首部长度。

标识符:84 16

唯一地标识主机发送的每一份数据报,占16 bit。主机为自己发送的IP报文设置一个报文计数器,通常每发送一份报文其值就会加1。标识符字段通常应该由让IP发送数据报的上层来选择。

标志:20 (001)

说明IP报文的分片信息和控制是否允许IP报文分片,占3bit。目前只有后两位有意义。标志字段的最低位是MF (More Fragment),为1表示后面还有分片,即本报文不是分片报文的最后一个分片,为0则表示本报文是最后一个分片。标志字段中间的一位是DF (Don't Fragment),只有当DF为0时才允许分片。

片偏移:(20 00

本片在原分组中的相对位置,占12bit。片偏移以 8 个字节为偏移单位,指示出较长的分组在分片后本片在原分组中的相对位置。

生存时间TTL(time-to-live):40

用于设置数据报可以经过的最多路由器数,占8 bit。TTL的初始值由源主机设置,即指定了数据报的生存时间,推荐的初始TTL值为64。一旦经过一个处理报文的路由器,TTL的值就减去1。当该字段的值为0时,数据报就被丢弃,并发送ICMP报文通知源主机。防止路由成环时,IP被无限次转发。

类型:11 (UDP 17)

也叫协议字段,表示向IP传送数据的上层协议,占8 bit。类型字段实质上是表示IP报文数据区数据的格式,例如创建IP数据的高层协议是TCP还是UDP。需要指出的是IP首部的版本字段指定的是IP报头格式,属于网络层范畴,类型字段指定的是IP数据区数据的格式,属于传输层的范畴。

首部检验和:4e 83

首部数据的二进制反码求和,占16 bit。检验和不对首部后面的数据进行计算。计算时首先把检验和字段置为0,然后,对首部中每个16 bit进行二进制反码求和(整个首部看成是由一串16 bit的字组成)。接收方在收到IP数据报后对首部进行检验时,直接对首部中每个16 bit进行二进制反码求和,若计算结果全为1,则说明首部在传输过程中没有发生任何差错;若计算结果不全为1,则表明检验和错误,那么IP就丢弃收到的数据报,但是不生成差错报文。

源IP地址:c0 a8 00 a7 (192.168.0.167)

目的IP地址: c0 a8 00 80(192.168.0.128)

每一份IP数据报都包含源IP地址和目的IP地址,它们都是32 bit的值。

选项:

IP数据报中的一个可变长的可选信息,作为附加的特殊处理的信息域,以32 bit作为界限,在必要的时候插入值为0的填充字节,保证IP首部始终是32 bit的整数倍。选项包括安全和处理限制、记录路由、时间戳、宽松的源站选路、严格的源站选路等选项。实际上选项被使用的时候并不是很多,而且并非所有的主机和路由器都支持这些选项。

数据:

IP数据报的数据部分,长度由首部的总长度字段和首部长度字段的差值决定。数据通常包含一个完全的TCP段或UDP数据报,也可以包含其他协议的报文,如ICMP报文。

/*IP头定义,共20个字节*/
typedef struct _IP_HEADER 
{
 char m_cVersionAndHeaderLen;       //版本信息(前4位),头长度(后4位)
 char m_cTypeOfService;            // 服务类型8位
 short m_sTotalLenOfPacket;        //数据包长度
 short m_sPacketID;              //数据包标识
 short m_sSliceinfo;               //分片使用
 char m_cTTL;                  //存活时间
 char m_cTypeOfProtocol;          //协议类型
 short m_sCheckSum;             //校验和
 unsigned int m_uiSourIp;          //源ip
 unsigned int m_uiDestIp;          //目的ip
} __attribute__((packed))IP_HEADER, *PIP_HEADER ;

2.2.3 MAC头 【14字节】

mac头:54 ee 75 e8 1b 11 12 49 c9 79 62 aa 08 00

目的MAC:54 ee 75 e8 1b 11

源MAC:12 49 c9 79 62 aa

上一层协议:08 00(ip)

typedef struct _MAC_FRAME_HEADER
{
 char m_cDstMacAddress[6];    //目的mac地址
 char m_cSrcMacAddress[6];    //源mac地址
 short m_cType;            //上一层协议类型,如0x0800代表上一层是IP协议,0x0806为arp
}__attribute__((packed))MAC_FRAME_HEADER,*PMAC_FRAME_HEADER;

 

GitHub 加速计划 / li / linux-dash
10.39 K
1.2 K
下载
A beautiful web dashboard for Linux
最近提交(Master分支:2 个月前 )
186a802e added ecosystem file for PM2 4 年前
5def40a3 Add host customization support for the NodeJS version 4 年前
Logo

旨在为数千万中国开发者提供一个无缝且高效的云端环境,以支持学习、使用和贡献开源项目。

更多推荐