工作中最常用的命令应该属 ping命令次数最多了,那么本篇文章的主题是通过 SOCK_RAW 套接字类型,实现简单ping命令功能。
⭐ socket套接字相关的文章:
ping命令是用来测试网络连通性的,其核心使用的ICMP协议实现。
SOCK_RAW是一种原始套接字类型,允许程序在用户空间中直接访问网络层协议(IP协议),绕过传输层(TCP/UDP)的封装,实现对网络数据包的完全控制。
原始套接字接收或发送不包括链路层的原始数据报。
SOCK_RAW基本特性
- 底层访问:可以直接读写IP层及以上的数据包
- 协议定制:可以自定义协议头部(IP头、传输层头等)
- 数据包嗅探:可以接收所有经过网卡的数据包(需root特权)
- 无自动处理:内核不会自动添加/解析协议头部
ping命令简易功能,研发思路:
- 我们执行
ping 192.168.42.173命令,然后通过tcpdump -i 网卡名称 icmp抓包; - 拿到数据包格式内容后,按照IP协议、ICMP协议格式进行组包;
- 最后我们通过SOCK_RAW套接字类型,进行封包,创建消息请求;
下面要了解两种网络协议 IP协议和 ICMP协议。
IP协议(Internet Protocol)是TCP/IP协议族中最核心的协议,提供不可靠的、无连接的、尽力而为的数据报传输服务。
报文格式
0 1 2 3
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|Version| IHL |Type of Service| Total Length |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Identification |Flags| Fragment Offset |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Time to Live | Protocol | Header Checksum |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Source Address |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Destination Address |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Options | Padding |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
字段说明
| 字段 | 长度 | 含义 |
|---|---|---|
| Version | 4比特 | IP版本 |
| IHL | 4比特 | 首部长度,如果不带Option字段,则为20,最长为60。以4字节为一个单位。 |
| Type of Service | 8比特 | 服务类型。只有在有QoS差分服务要求时这个字段才起作用。 |
| Total Length | 16比特 | 总长度,整个IP数据报的长度,包括首部和数据之和,单位为字节,最长65535,总长度必须不超过最大传输单元MTU。 |
| Identification | 16比特 | 标识,主机每发一个报文,加1,分片重组时会用到该字段。 |
| Flags | 3比特 | 标志位,表示能不能分片。 |
| Fragment Offset | 13比特 | 片偏移,以8个字节为偏移单位。 |
| Time to Live | 8比特 | 生存时间:可经过的最多路由数,即数据包在网络中可通过的路由器数的最大值。 |
| Protocol | 8比特 | 下一层协议。 |
| Header Checksum | 16比特 | 首部检验和,只检验数据包的首部,不检验数据部分。 |
| Source Address | 32比特 | 源IP地址。 |
| Destination Address | 32比特 | 目的IP地址。 |
| Options | 可变 | 选项字段,选项字段长度可变,从1字节到40字节不等,取决于所选项的功能。 |
| Padding | 可变 | 填充字段,全填0。 |
ICMP(Internet Control Message Protocol)因特网控制报文协议。它是IPv4协议族中的一个子协议,用于IP主机、路由器之间传递控制消息。控制消息是在网络通不通、主机是否可达、路由是否可用等网络本身的消息。这些控制消息虽然不传输用户数据,但是对于用户数据的传递起着重要的作用。
ICMP靠IP协议来完成任务,所以ICMP报文中要封装IP头部。
ICMP Echo Request/Reply消息格式
用于检测IP网络连通性的Ping/Tracert,是通过发送ICMP Echo消息实现的。
报文格式
+0------7-------15---------------31
| Type | Code | Checksum |
+--------------------------------+
| Identifier | Sequence Number|
+--------------------------------+
| Data |
+--------------------------------+
字段说明
| 字段 | 长度 | 含义 |
|---|---|---|
| Type | 1字节 | 消息类型。 + 0:回显应答报文 + 8:请求回显报文 |
| Code | 1字节 | 消息代码,此处值为0。 |
| Checksum | 2字节 | 检验和。 |
| Identifier | 2字节 | 标识符,发送端标示此发送的报文。 |
| Sequence Number | 2字节 | 序列号,每发送一次顺序号就加1。 |
| Data | 可变 | 选项数据。 |
让我们一起来进入代码的世界~
首先,先封装ping数据包,分为IP报文和ICMP报文两部分。
这里需要注意的是,自定义封装的ping数据包总长度为60字节。 分别为以太帧14字节、IP头部20字节、ICMP报文26字节。
其中 Ethernet II 以太帧占用14字节,因为 SOCK_RAW 套接字类型只能处理IP层及以上协议,这里代码中只包含IP和ICMP协议的填充。
packet变量申请了46字节的长度,包含20字节IP头部、8字节ICMP头部、18字节ICMP数据。以太帧要求数据字段的最小长度必须为46字节,这里符合要求。
最后,将封装的ping数据包通过系统调用,创建 SOCK_RAW 套接字类型的socket网络请求。
代码中使用了
syscall.SetsockoptInt() 函数,用于设置套接字的选项。
syscall.IPPROTO_IP 代表是在IP层中。
syscall.IP_HDRINCL 作用是需要手动填充IP层首部。(如果不选择则由内核自动填充)
完成发送后,目标服务端会将收到的 ping请求,立马返回ping所携带的数据响应请求。
下面代码用于接收ping响应报文, buf 变量接收长度为46,是因为我们发送时定义的报文大小是46,所以服务端也会将按照这个大小返回响应。
完整代码,请在github仓库查阅:github.com/hltfaith/go…
执行ping命令看下效果
最后进行抓包,看下是否是我们预期自定义的内容
技术文章持续更新,请大家多多关注呀~~
搜索微信公众号,关注我【 帽儿山的枪手 】