简介
前面我们已经介绍了不少的扩展模块,例如multiport、iprange、string、time、connlimit模块,但是在tcp扩展模块中只介绍了tcp扩展模块中的”--sport”与--dport”选项,并没有介绍”--tcp-flags”选项,那么这篇文章,我们就来认识一下tcp扩展模块中的”--tcp-flags”和icmp。
--tcp-flags
TCP报文介绍
”--tcp-flags”指的就是tcp头中的标志位,在使用iptables时,我们可以通过此扩展匹配条件,去匹配tcp报文的头部的标识位,然后根据标识位的实际情况实现访问控制的功能。
那我们来看下tcp的报头结构
标志符(9比特长)
NS—ECN-nonce。ECN显式拥塞通知(Explicit Congestion Notification)是对TCP的扩展,定义于 RFC 3540 (2003)。ECN允许拥塞控制的端对端通知而避免丢包。ECN为一项可选功能,如果底层网络设施支持,则可能被启用ECN的两个端点使用。在ECN成功协商的情况下,ECN感知路由器可以在IP头中设置一个标记来代替丢弃数据包,以标明阻塞即将发生。数据包的接收端回应发送端的表示,降低其传输速率,就如同在往常中检测到包丢失那样。
CWR—Congestion Window Reduced,定义于 RFC 3168(2001)。
ECE—ECN-Echo有两种意思,取决于SYN标志的值,定义于 RFC 3168(2001)。
URG—为1表示高优先级数据包,紧急指针字段有效。
ACK—为1表示确认号字段有效
PSH—为1表示是带有PUSH标志的数据,指示接收方应该尽快将这个报文段交给应用层而不用等待缓冲区装满。
RST—为1表示出现严重差错。可能需要重新建立TCP连接。还可以用于拒绝非法的报文段和拒绝连接请求。
SYN—为1表示这是连接请求或是连接接受请求,用于建立连接和使顺序号同步
FIN—为1表示发送方没有数据要传输了,要求释放连接。
在使用iptables时,使用tcp扩展模块的”–tcp-flags”选项,即可对上图中的标志位进行匹配,判断指定的标志位的值是否为”1″,在tcp协议建立连接的过程中,需要先进行三次握手,而三次握手就要依靠tcp头中的标志位进行。
TCP三次握手过程
- 客户端(通过执行connect函数)向服务器端发送一个SYN包,请求一个主动打开。该包携带客户端为这个连接请求而设定的随机数A作为消息序列号。
- 服务器端收到一个合法的SYN包后,把该包放入SYN队列中;回送一个SYN/ACK。ACK的确认码应为A+1,SYN/ACK包本身携带一个随机产生的序号B。
- 客户端收到SYN/ACK包后,发送一个ACK包,该包的序号被设定为A+1,而ACK的确认码则为B+1。然后客户端的connect函数成功返回。当服务器端收到这个ACK包的时候,把请求帧从SYN队列中移出,放至ACCEPT队列中;这时accept函数如果处于阻塞状态,可以被唤醒,从ACCEPT队列中取出ACK包,重新建立一个新的用于双向通信的sockfd,并返回。
如果服务器端接到了客户端发的SYN后回了SYN-ACK后客户端掉线了,服务器端没有收到客户端回来的ACK,那么,这个连接处于一个中间状态,既没成功,也没失败。于是,服务器端如果在一定时间内没有收到的TCP会重发SYN-ACK。
“三次握手”的目的是“为了防止已失效的连接(connect)请求报文段传送到了服务端,因而产生错误”,也即为了解决“网络中存在延迟的重复分组”问题。例如:client发出的第一个连接请求报文段并没有丢失,而是在某个网络结点长时间的滞留了,以致延误到连接释放以后的某个时间才到达server。本来这是一个早已失效的报文段。但server收到此失效的连接请求报文段后,就误认为是client发出的一个新的连接请求。于是就向client发出确认报文段,同意建立连接。假设不采用“三次握手”,那么只要server发出确认,新的连接就建立了。由于现在client并没有发出建立连接的请求,因此不会理睬server的确认,也不会向server发送数据。但server却以为新的运输连接已经建立,并一直等待client发来数据。这样,server的很多资源就白白浪费掉了。采用“三次握手”的办法可以防止上述现象发生,client不会向server的确认发出确认。server由于收不到确认,就知道client并没有要求建立连接。
抓包查看下tcp建立连接的具体过程

分别看下不同步骤的flags信息



设置iptables
通过以上描述,我们简单介绍了tcp的三次握手和标志位信息,那么我们该如何设置iptables呢。
--tcp-flags设置的规则如下:
使用 --tcp-flags
选项时,你需要指定两个用空格分隔的字符串。第一个字符串是你想要检查的标志位,而第二个字符串是你希望这些标志位中的哪些位被设置(或者未被设置)类似
--tcp-flags <flags> <mask>
此时如果想要匹配三次握手中第一次握手的tcp flags报文,应该使用一下命令
iptables -A INPUT -p tcp --tcp-flags SYN,ACK,FIN,RST SYN -j ACCEPT
--tcp-flags用于匹配报文tcp头部的标志位,”SYN,ACK,FIN,RST,URG,PSH SYN”这串字符就是用于配置要匹配的标志位的,可以把这串字符拆成两部分,第一部分为”SYN,ACK,FIN,RST,URG,PSH”,第二部分为”SYN”。
第一部分表示:我们需要匹配报文tcp头中的哪些标志位,那么上例的配置表示,我们需要匹配报文tcp头中的6个标志位,这6个标志位分别为为”SYN、ACK、FIN、RST、URG、PSH”。
第二部分表示:第一部分的标志位列表中,哪些标志位必须为1
第二次握手应该使用如下方式
iptables -A INPUT -p tcp --tcp-flags SYN,ACK,FIN,RST SYN,ACK -j ACCEPT
第二部分匹配syn,ack
第三次握手
iptables -A INPUT -p tcp --tcp-flags SYN,ACK,FIN,RST ACK -j ACCEPT
上面的所有命令都可以将前面第一部分进行简写即
iptables -A INPUT -p tcp --tcp-flags ALL SYN -j ACCEPT
iptables -A INPUT -p tcp --tcp-flags ALL SYN,ACK -j ACCEPT
iptables -A INPUT -p tcp --tcp-flags ALL ACK -j ACCEPT
可以用ALL表示所有的标志位
tcp扩展模块专门提供了一个选项,可以匹配上文中提到的”第一次握手”,那就是–syn选项
使用”–syn”选项相当于使用”–tcp-flags SYN,RST,ACK,FIN SYN”,也就是说,可以使用”–syn”选项去匹配tcp新建连接的请求报文。
iptables -A INPUT -p tcp --dport 443 --syn -j ACCEPT
测试


--tcp-flags的设置非常灵活,大家在设置的时候一定要多做测试,举一反三,注意应用的位置是INPUT还是OUTPUT,比如三次握手的第二次,那么是服务器进行响应,如果我们匹配第二次的数据包,是服务器主动发出的,就需要应用的OUTPUT链上。类似的情况在前面的文章中配置string扩展模块时也有介绍。
ICMP扩展
ICMP协议的全称为Internet Control Message Protocol,互联网控制报文协议,它主要用于探测网络上的主机是否可用,目标是否可达,网络是否通畅,路由是否可用等。
当使用ping命令ping某主机时,如果主机可达,对应主机会对我们的ping请求做出回应(此处不考虑禁ping等情况),也就是说,我们发出ping请求,对方回应ping请求,虽然ping请求报文与ping回应报文都属于ICMP类型的报文,但是细分的话,它们所属的类型还是不同的,我们发出的ping请求属于类型8的icmp报文,而对方主机的ping回应报文则属于类型0的icmp报文,根据应用场景的不同,icmp报文被细分为如下各种类型。
上图中所有表示”目标不可达”的icmp报文的type码为3,而”目标不可达”又可以细分为网络不可达、主机不可达、端口不可达等,所以,为了更加细化的区分它们,icmp对每种type又细分了对应的code,用不同的code对应具体的场景, 所以,我们可以使用type/code去匹配具体类型的ICMP报文,比如可以使用”3/1″表示主机不可达的icmp报文。
上图中的第一行就表示ping回应报文,它的type为0,code也为0,从上图可以看出,ping回应报文属于查询类(query)的ICMP报文,从大类上分,ICMP报文还能分为查询类与错误类两大类,目标不可达类的icmp报文则属于错误类报文。
而我们发出的ping请求报文对应的type为8,code为0。
我们现在想要禁止所有icmp类型的报文进入本机,可以进行如下设置



如果只想要ping通别人,但是不想让别人ping通我,需要如下设置


除了能够使用对应type/code匹配到具体类型的icmp报文以外,我们还能用icmp报文的描述名称去匹配对应类型的报文

