linux网络编程(十二)原始套接字
标准套接字与原始套接字之间的关系。标准套接字与网络协议拢的TCP、UDP层打交道,而原始套接字则与四层级网络协议找核心打交道。

原始套接字提供以下3种标准套接字不具备的功能。
- 使用原始套接字可以读/写ICMP、IGMP分组。例如ping程序就使用原始套接字发送ICMP回显请求,并接受ICMP回显应答。用于多播的守护程序mrouted,同样适用原始套接字来发送和接收IGMP分组。上述功能同样允许使用ICMP或者IGMP构造的应用程序完全作为用户进程处理,而不必再增加过多的内核编码。例如,路由发现守护进程即以这种方式构造。它处理内核完全不知道的两个ICMP消息。
- 使用原始套接字可以读写特殊的IP数据报,内核不处理这些数据报的协议字段。大多数内核只处理1 (ICMP)、2 (IGMP)、3 (TCP)和17 (UDP) 的数据报。但协议字段还可能为其他值。例如,OSPF路由协议就不适用TCP或者UDP,而直接使用IP,将IP数据报的协议字段设为89。因此,由于这些数据报包含内核完全不知道的协议字段,实现OSPF协议的gated 程序必须使用原始套接字来读写它们。
- 使用原始套接字,利用函数setsockopt()设置套接字选项,使用IP_HDRINGCL可以对IP头部进行操作,因此可以修改IP数据和IP层之上的各层数据,构造自己的特定类型的TCP或者UDP.的分组。
一、原始套接字创建
原始套接字的创建使用与通用套接字创建的方法是一致的,只是在套接字类型的选项上使用的是另一个SOCK_RAW。在使用socket()函数进行函数创建完毕的时候,还要进行套接字数据中格式类型的指定,设置从套接字中可以接收到的网络数据格式。
1. SOCK_RAW 选项
创建原始套接字使用socket()函数,第二个参数设置为SOCK_RAW,socket()函数可以创建一个原始套接字。下面的代码创建一个AF_INET协议族中的原始套接字,协议类型为protocol。
int rawsock = socket(AF_INET, SOCK_RAW, protocol);
原始套接字中的protocol,一般情况下不能设置为0,需要用户自己设置想要的类型,是一个形如IPPROTO_xxx的常量,在文件<netinet/in.h>中定义。例如,IPPROTO_ICMP表示是一个ICMP协议。
常用协议的类型和含义如下所示。可以设置不同的协议,在发送和接收数据的时候会得到不同的数据。
- IPPROTO_IP: IP协议,接收或者发送回数据包,包含IP头部。
- IPPROTO_ICMP: ICMP协议,接收或者发送ICMP的数据包,IP的头部不需要处理。
- IPPROTO_TCP: TCP协议,接收或者发送TCP数据包。
- IPPROTO_UDP: UDP协议,接收或者发送UDP数据包。
- IPPROTO_RAW:原始IP包。
2. IP_HDRINCL 套接字选项
使用套接字选项IP_HDRINCL设置套接字,在之后进行的接收和发送时,接收到的数据包含IP的头部。用户之后需要对IP层相关的数据段进行处理,例如IP头部数据的设置和分析,校验和的计算等。
3. 不需要bind() 函数
原始套接字不需要使用 bind() 函数,因为进行发送和接收数据的时候可以指定要发送 和接收的目的地址的 IP。例如,使用函数 sendto() 和 函数 recvfrom() 来发送和接收数据,sendto() 和 recvfrom() 函数分别需要指定IP地址。
二、原始套接字发送报文
原始套接字发送报文有如下原则:
- 通常情况下可以使用sendto()函数并指定发送目的地址来发送数据,当已经指定了bind()目标地址的时候可以使用 write() 或者 send() 发送数据。
- 如果使用 setsockopt() 设置了选项 IP_RINCL,则发送的数据缓冲区指向 IP 头部第一个字节的头部,用户发送的数据包含E头部之后的所有数据,需要用户自己填写 IP 头部和计算校验,并需要对所包含数据进行处理和计算。
- 如果没有设置 IP_RINCL,则发送缓冲区指向 IP 头部后面数据区域的第一个字节,不需要用户填写IP头部,IP头部的填写工作由内核进行,内核还进行校验和的计算。
例如 sendto(rawsock,buffer,len,0,(struct sockaddr*)&to,sizeof(to)),当IP_RINCL 己经设置的时候,buffer 指向的就是用户构建包含 IP 头部在内的数据结构。 如果 IP_RINCL 没有设置, 则 buffer 指向了IP头部后面缓冲区的数据,例如后面为 ICMP 数据报文,则需要填写ICMP的类型、 代码等,并计算其校验和。
三、原始套接字接收报文
与发送报文类似,接收报文也有相似的规则:
- 通常可以使用 recvfrom() 或者 recv() 及 read() 获得数据。
- 当设置了 IP_RINCL 后,接收的缓冲区为 IP头部的第一个字节。
- 当没有设置 IP_RINCL 的时候,接收的缓冲区为IP数据区域的第一个字节。
接收报文还有自己的一些特点,主要有如下几个:
- 对于ICMP的协议,绝大部分数据可以通过原始套接字获得,例如回显请求、 响应、 时间戳请求等。
- 接收的 UDP 和 TCP 协议的数据不会传给任何原始套接字接口,这些协议的数据需要通过数据链路层获得。
- 如果 IP 以分片形式到达,则所有分片都己经接收到并重组后才传给原始套接字。
- 内核不能识别的协议、 格式等传给原始套接字,因此,可以使用原始套接字定义用户自己的协议格式。
原始套接字接收报文的规则如下:如果接收的报文数据中的协议类型与自定义的原始套接字匹配,那么将接收的所有数据复制入套接字中;如果套接字绑定了本地地址,那么只有当接收的报文数据IP头中的目的地址等于本地地址时,接收到的数据才复制到套接字 中;如果套接字定义了远端地址,那么,只有接收数据 IP 头中对应的源地址与远端地址匹配,接收的数据才复制到套接字中。
四、原始套接字报文处理时的结构
进行报文处理时常用的数据结构,包含IP头部、ICMP头部、UDP头部、TCP头部。 使用这些数据格式对原始套接字进行处理,可以从底层获取高层的网络数据。
1. IP头部的结构
在Linux下结构struct ip的数据类型定义如下:

2. ICMP 头部结构
ICMP的头部结构比较复杂,主要包含消息类型icmp_type、消息代码icmp_code、校验和icmp_cksum等,不同的ICMP类型其他部分有不同的实现。ICMP的头部结构如图13.5所示。
常用的 ICMP报文包括 ECHO-REQUEST(响应请求消息)、 ECHO-REPLY(响应应答消息)、Destination Unreachable(目标不可到达消息)、Time Exceeded(超时消息)、Parameter Problems(参数错误消息)、Source Quenchs(源抑制消息)、Redirects(重定向消息)、Timestamps(时间戳消息)、Timestamp Replies(时间戳响应消息)、Address Masks(地址掩码请求消息)、Address Mask Replies(地址掩码响应消息)等,是Internet上十分重要的消息。
2. 不同类型的ICMP请求
子网掩码请求协议的位置参见图13.7所示,增加了标识符icmp_id、序列号icmp_seq 和掩码icmp_mask。
在Linux中,时间戳请求协议的示意图参见图13.8,相对于通用示意图,增加了标识符icmp_id、序列号icmp_seq及表示请求时间的icmp_ctime、接收时间的icmp_rtime和传输时间icmp_ttime。

3. UDP头部结构
UDP的头部结构包含发送端的源端口号、数据接收端的目的端口号、UDP数据的长度,以及UDP的校验和等信息。UDP头部结构如图13.9所示。
在Linux下UDP头部的结构类型为struct udphdr,代码如下所示,主要包含源端口、目的端口、UDP长度和校验和。在Linux下有两套定义,如果喜欢BSD样式,需要定义宏 FAVOR_BSD。

4. TCP 头部结构
TCP的头部结构主要包含发送端的源端口、接收端的目的端口、数据的序列号、上一个数据的确认号、滑动窗口大小、数据的校验和、紧急数据的偏移指针,以及一些控制位等信息。 TCP头部结构如图 13.11 所示。
在Linux下TCP头部结构struct tcphdr代码定义如下,主要包含源端口、目的端口、序列号、确认号、滑动窗口、校验和、紧急指针,以及一些控制位(连接请求、连接终止、连接重置、连接快速复制、确认序号、紧急标志、拥塞标志等)。对于小端和大端系统,有两套不一致的定义。

Linux 定义的数据结构在TCP示意图中的表示,如图13.12表示:

三、洪水攻击
洪水攻击(FLOOD ATTACK)指的是利用计算机网络技术向目标机发送大量的无用数据报文,使得目标主机忙于处理无用的数据报文而无法提供正常服务的网络行为。
攻击的手段主要是使用畸形的报文来让目标机进行处理或者等待,一般都是在原始套接字层进行程序设计。 洪水攻击主要分为ICMP、UDP和SYN攻击3种类型。
- ICMP回显攻击利用原始套接字向目标机发送大量的回显请求或者回显响应数据,由于此数据协议核默认是必须处理的,因此可以对目标机造成影响。
- UDP攻击则向目标机UDP服务端口发送UDP报文,由于目标机需要对端口进行处理,如果知道目标机的基本数据格式,则可以构建十分有效的代码来对目标机造成很大伤害。
- SYN攻击利用了TCP连接中的三次握手,在发送一个SYN原始报文后,目标机需要对发送的报文进行处理并等待超时。
1. ICMP洪水攻击
ICMP Flood 是一种在 ping 基础上形成的,但是用 ping 程序很少能造成目标机宕机的问题。这里边最大的问题是提高处理的速度。ICMP洪水攻击主要有以下 3 种方式。
- 直接洪水攻击:这样做需要本地主机的带宽与目标主机之间带宽进行比拼。 可以采用多线程的方法一次性地发送多个ICMP请求报文,让目标主机处理过程出现问题而速度缓慢或者岩机。直接攻击的方法有一个缺点,就是可以根据来源的 IP 地址屏蔽攻击源,并且目标源容易暴露,被对方反攻击。 攻击的过程如图 13.16所示。
- 伪装IP攻击: 在直接洪水攻击的基础上,将发送方的IP地址用伪装的IP地址代替。 将直接洪水攻击的缺点进行了改进。 攻击的过程如图 13.17 所示。
![]()
- 反射攻击:与直接攻击和伪装攻击不同,反射攻击不是直接对目标机进行攻击,而是让其他一群主机误认为目标机在向它们发送ICMP。请求包,一群主机向目标机发送ICMP应答包。攻击方向一群主机发送ICMP请求,将请求的源地址伪装成目标机的IP地址,这样目标机就成了ICMP回显反射的焦点,如图 13.18 所示。
攻击方的IP地址为10.10.10.10,向一组服务器a、b、...等发送ICMP回显请求报文,并将发送方的 IP 地址伪装成目标机的 IP 地址10.10.8.10。服务器组在经过处理后,会发送ICMP的回显报文给目标机。

2. UDP洪水攻击
将IP头部和UDP头部均包含在内,设置数据完毕后,可以直接发向目标机。
3. SYN洪水攻击
SYN洪水攻击也称为拒绝服务攻击,它利用了TCP的三次握手,利用大量的TCP连接请求造成目标机的资源耗尽,而不能提供正常的服务或者服务质量下降。
SYN 洪水攻击的原理
一般情况下的TCP连接函数connect(),经历了三次握手,如果从IP层协议来看,客户端先发送SYN请求,服务器对客户端的SYN进行响应,而客户端对服务器的响应再次进行确认后才建立了一个TCP的连接。在服务器发送响应后,要等待一段时间才能获得客户端的确认,即第二次和第三次握手之间有一个超时时间,SYN攻击就利用了这个时机 。
SYN攻击利用第二次握手的手段如下:
- 主机A发送ICMP的SYN请求给主机B,主机A发送的报文的源地址IP是一个伪造的IP。主机B的第二次握手之后要等待一段时间,接收主机A的确认包,在超时时间内此资源一直在占用 。如果主机B的处理TCP三次握手的资源不能满足处理主机A的SYN请求数量,则主机B的可用资源就会慢慢减少,直到耗尽。
- 主机A发送的报文是原始报文,发送报文的速度可以达到很高,因此有足够的资源能对目标机造成影响。


浙公网安备 33010602011771号