Skip to content

通用操作

此文档后面如果发现新的用法会不断地进行更新

前面我们已经学习了如何创建 表、链,本章我们重点研究规则如何创建

首先,我们清空表,创建一个空表inet test进行测试

1. 数据过滤

meta

输入接口索引iif <input interface INDEX>

输入接口名称iifname <input interface NAME>

输出接口索引 oif <output interface INDEX>

输出接口名称oifname <output interface NAME>

(oif 和 iif 接受字符串参数并转换为接口索引)

(oifname 和 iifname 更动态,但由于字符串匹配,速度较慢),官方的意思是索引的方式更快

通过ip a 命令可以查看网卡的名称与索引,如下分别是1 lo 2 enss33

shell
[root@localhost ~]# ip a
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
    inet 127.0.0.1/8 scope host lo
       valid_lft forever preferred_lft forever
    inet6 ::1/128 scope host 
       valid_lft forever preferred_lft forever
2: ens33: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc fq_codel state UP group default qlen 1000
    link/ether 00:0c:29:03:19:e8 brd ff:ff:ff:ff:ff:ff
    inet 192.168.1.26/24 brd 192.168.1.255 scope global noprefixroute ens33
       valid_lft forever preferred_lft forever
    inet6 2408:8244:8600:61:20c:29ff:fe03:19e8/64 scope global dynamic noprefixroute 
       valid_lft 232088sec preferred_lft 145688sec
    inet6 fe80::20c:29ff:fe03:19e8/64 scope link noprefixroute 
       valid_lft forever preferred_lft forever
[root@localhost ~]#

我们可以针对所有网卡ens33的输入做策略

shell
[root@localhost ~]# nft add rule inet test chain1 meta iif 2 accept
[root@localhost ~]# nft add rule inet test chain1 meta iifname ens33  accept
[root@localhost ~]# nft -a -n list chain inet test chain1
table inet test {
	chain chain1 { # handle 1
		type filter hook input priority 0; policy accept;
		tcp dport 822 accept # handle 4
		icmp type 0 accept # handle 7
		meta l4proto 1 drop # handle 6
		ip saddr 192.168.1.20 tcp dport 80 drop # handle 14
		ip protocol 6 accept # handle 15
		ip protocol 17 accept # handle 16
		ip protocol 51 accept # handle 17
		ip protocol 6 accept # handle 18
		ip saddr 192.168.1.25 accept # handle 19
		ip daddr 192.168.1.26 accept # handle 20
		iif "ens33" accept # handle 21
		iifname "ens33" accept # handle 22
	}
}
[root@localhost ~]#

ip

协议protocol <protocol>

源地址daddr <destination address>

目的地址saddr <source address>

shell
[root@localhost ~]# nft add rule inet test chain1 ip protocol tcp  accept
[root@localhost ~]# nft add rule inet test chain1 ip saddr 192.168.1.25  accept
[root@localhost ~]# nft add rule inet test chain1 ip daddr 192.168.1.26  accept
[root@localhost ~]# nft -a -n list table inet test
table inet test { # handle 1
	ct timeout customs { # handle 5
		protocol tcp
		l3proto ip
		policy = { established : 200, close : 20 }
	}

	chain chain1 { # handle 1
		type filter hook input priority 0; policy accept;
		tcp dport 822 accept # handle 4
		icmp type 0 accept # handle 7
		meta l4proto 1 drop # handle 6
		ip saddr 192.168.1.20 tcp dport 80 drop # handle 14
		ip protocol 6 accept # handle 15
		ip protocol 17 accept # handle 16
		ip protocol 51 accept # handle 17
		ip protocol 6 accept # handle 18
		ip saddr 192.168.1.25 accept # handle 19
		ip daddr 192.168.1.26 accept # handle 20
	}

	chain chain2 { # handle 2
		type filter hook input priority 0; policy accept;
	}

	chain chain3 { # handle 3
	}
}
[root@localhost ~]#

TCP/UDP

目的端口 dport <destination port>

源端口 sport <source port>

shell
[root@localhost ~]# nft add rule inet test chain1 tcp dport 443  accept
[root@localhost ~]# nft add rule inet test chain1 tcp sport 443  accept
[root@localhost ~]# nft -a -n list chain inet test chain1
table inet test {
	chain chain1 { # handle 1
		type filter hook input priority 0; policy accept;
		tcp dport 822 accept # handle 4
		icmp type 0 accept # handle 7
		meta l4proto 1 drop # handle 6
		ip saddr 192.168.1.20 tcp dport 80 drop # handle 14
		ip protocol 6 accept # handle 15
		ip protocol 17 accept # handle 16
		ip protocol 51 accept # handle 17
		ip protocol 6 accept # handle 18
		ip saddr 192.168.1.25 accept # handle 19
		ip daddr 192.168.1.26 accept # handle 20
		iif "ens33" accept # handle 21
		iifname "ens33" accept # handle 22
		tcp dport 443 accept # handle 23
		tcp sport 443 accept # handle 24
	}
}
[root@localhost ~]#

icmp

常用的 icmp type就两个,其他的做了解就行

type说明
echo-reply0回显应答
destination-unreachable3目的地不可达
source-quench4当接收方无法处理所有传入的数据包时,源站抑制报文会发送到发送方,以通知其减慢数据传输速度
redirect5用于通知发送方,其正在使用的路由不再是最佳路由,建议使用另一条路由
echo-request8回显请求
time-exceeded11超时

常用报文类型

类型TYPE代码CODE用途|描述 Description查询类Query差错类Error
00Echo Reply——回显应答(Ping应答)x
30Network Unreachable——网络不可达x
32Protocol Unreachable——协议不可达x
31Host Unreachable——主机不可达x
33Port Unreachable——端口不可达x
34Fragmentation needed but no frag. bit set——需要进行分片但设置不分片比特x
313Communication administratively prohibited by filtering——由于过滤,通信被强制禁止x
51Redirect for host——对主机重定向
80Echo request——回显请求(Ping请求)x
110TTL equals 0 during transit——传输期间生存时间为0x
120IP header bad (catchall error)——坏的IP首部(包括各种差错)x

禁用所有icmp

nft add rule inet <table> <chain> meta l4proto icmp drop

禁止别人ping自己

nft add rule inet test chain1 handle 4 icmp type 8 drop

禁止并回复 with icmp code

nft add rule inet test chain1 handle 4 icmp type 8 reject with icmp 3

2. NAT

2.1 透明代理

nat与其他的策略不同之处在于,一般用于类似路由器,或者防火墙设备,需要对数据包进行转发,并且转发的过程中需要对源地址或者目的地址进行转换

首先我们需要在Linux中开启数据转发的功能

shell
# 编辑sysctl配置文件
sudo vi /etc/sysctl.conf
 
# 添加以下行来启用IP转发
net.ipv4.ip_forward = 1
 
# 应用配置
sudo sysctl -p

实际测试中 可能需要关闭 SELINUX

shell
[root@client ~]# sudo sed -i 's/SELINUX=enforcing/SELINUX=disabled/g' /etc/selinux/config #
[root@client ~]# cat /etc/selinux/config 

# This file controls the state of SELinux on the system.
# SELINUX= can take one of these three values:
#     enforcing - SELinux security policy is enforced.
#     permissive - SELinux prints warnings instead of enforcing.
#     disabled - No SELinux policy is loaded.
SELINUX=enforcing #
SELINUX=disabled #
# SELINUXTYPE= can take one of three values:
#     targeted - Targeted processes are protected,
#     minimum - Modification of targeted policy. Only selected processes are protected. 
#     mls - Multi Level Security protection.
SELINUXTYPE=targeted 


[root@client ~]#

网络拓扑结构为,配置完此功能以后,网络已经可以通过中间设备正常通信

NAT

首先创建好表与链

shell
[root@router ~]# nft add table ip test 
[root@router ~]# nft add chain test test_dnat '{type nat hook prerouting priority 0 ; }'
[root@router ~]# nft add chain test test_snat '{type nat hook postrouting priority 0 ; }'
[root@router ~]# nft list table test
table ip test {
	chain test_dnat {
		type nat hook prerouting priority filter; policy accept;
	}

	chain test_snat {
		type nat hook postrouting priority filter; policy accept;
	}
}
[root@router ~]#

2.2 SNAT

首先我们的中间机器是能访问互联网的,但是我们的 192.168.132.3这台机器是不能上网,如果需要访问互联网,就需要对地址做转换

nft add rule nat postrouting ip saddr 192.168.132.0/24 oif ens33 snat to 192.168.177.31

在这里指定输出的接口,是为了精准的做源地址转换,而不会影响回包地址

shell
[root@mimt ~]# nft add table test 
[root@mimt ~]# nft add chain test chain_snat '{type nat hook postrouting priority 100;}'
[root@mimt ~]# nft add rule test chain_snat ip saddr 192.168.132.0/24 oif ens33 snat to 192.168.177.31

此时在 192.168.132.3这台机器上已经可以上公网了

shell
[root@client ~]# ping 114.114.114.114
PING 114.114.114.114 (114.114.114.114) 56(84) bytes of data.
64 bytes from 114.114.114.114: icmp_seq=1 ttl=127 time=24.9 ms
64 bytes from 114.114.114.114: icmp_seq=2 ttl=127 time=25.2 ms
64 bytes from 114.114.114.114: icmp_seq=3 ttl=127 time=25.3 ms

可以指定源 NAT 池,在这里就不演示了

nft add rule inet nat postrouting snat ip to 10.0.0.2/31
nft add rule inet nat postrouting snat ip to 10.0.0.4-10.0.0.127

使用传输协议源端口映射:

nft add rule inet nat postrouting ip protocol tcp snat ip to 10.0.0.1-10.0.0.100:3000-4000

2.3 DNAT

现在举一个场景的例子,在公网上,我们需要访问内网的某个服务,怎么办呢,此时就需要将内网的服务ip+端口映射到公网出口的ip+端口上面

shell
[root@mimt ~]# nft add chain  test chain_dnat '{type nat hook prerouting priority 100;}'
[root@mimt ~]# nft add rule test chain_dnat iif 2   tcp dport 123 dnat to 192.168.132.3:80
[root@mimt ~]# nft -a list table test
table ip test { # handle 3
	chain chain_snat { # handle 1
		type nat hook postrouting priority srcnat; policy accept;
		ip saddr 192.168.132.0/24 oif "ens33" snat to 192.168.177.31 # handle 4
	}

	chain chain_dnat { # handle 5
		type nat hook prerouting priority 100; policy accept;
		iif "ens33" tcp dport 123 dnat to 192.168.132.3:80 # handle 8
	}
}
[root@mimt ~]#

此时我们在192.168.177.3服务器访问 http://192.168.177.31:123即可访问到 192.168.132.3的服务

通过现象,我们可以发现,SNAT时,目标服务器获取的是转换后的地址,而DNAT时,目标服务器可以获取真实的ip地址

思考一个问题,如果在同一个内网里访问转后的地址能访问到吗?

通过实际测试,我们发现同一个内网访问转换后的地址无法访问,这是为什么呢

其实是DNAT不会修改源IP,这样就导致服务器在回包的时候,会在内网直接回复回去,但是客户端的发起访问目的ip就不一致了,客户端会丢弃数据包

因此,如果需要内网也能使用公网IP访问的话,我们还需要做源地址转换

shell
[root@mimt ~]# nft add rule test chain_snat ip daddr 192.168.132.3 tcp dport 80 oif 3 snat to 192.168.177.31
[root@mimt ~]# nft -a list table test
table ip test { # handle 3
	chain chain_snat { # handle 1
		type nat hook postrouting priority srcnat; policy accept;
		ip saddr 192.168.132.0/24 oif "ens33" snat to 192.168.177.31 # handle 4
		ip daddr 192.168.132.3 tcp dport 80 oif "ens34" snat to 192.168.177.31 # handle 12
	}

	chain chain_dnat { # handle 5
		type nat hook prerouting priority 100; policy accept;
		tcp dport 123 dnat to 192.168.132.3:80 # handle 8
	}
}
[root@mimt ~]#

但是缺点是什么呢,就是内网服务器记录的都是公网的地址为源IP了,因此不建议内网直接访问公网IP

3. CT(连接跟踪)

3.1 state

一般用于表示TCP连接状态

ct state { new, established, related, untracked }

例如,以下禁止新的ssh连接,原有的ssh则不会受影响

shell
[root@mimt ~]# nft add rule test chain1 tc state new drop
[root@mimt ~]# nft -a -n list ruleset
table ip test { # handle 1
	chain chain1 { # handle 1
		type filter hook input priority 100; policy accept;
		ct state 0x8 drop # handle 4
	}
}
[root@mimt ~]#

3.2 限流(limit rate)

limit rate 400/minute
limit rate 400/hour # 无over表示匹配低于后面结果
limit rate over 40/day # over表示匹配高于后面结果
limit rate over 400/week
limit rate over 1023/second burst 10 packets
limit rate 1025 kbytes/second
limit rate 1023000 mbytes/second
limit rate 1025 bytes/second burst 512 bytes # burst表示可以突破最多限制速度,在over中不生效
limit rate 1025 kbytes/second burst 1023 kbytes
limit rate 1025 mbytes/second burst 1025 kbytes
limit rate 1025000 mbytes/second burst 1023 mbytes

我们在服务器上下载可以发现,不限速的情况下,我们可以满速度下载

shell
[root@mimt ~]# wget https://go.dev/dl/go1.23.0.linux-amd64.tar.gz
--2024-08-17 00:03:45--  https://go.dev/dl/go1.23.0.linux-amd64.tar.gz
Resolving go.dev (go.dev)... 216.239.38.21, 216.239.36.21, 216.239.32.21, ...
Connecting to go.dev (go.dev)|216.239.38.21|:443... connected.
HTTP request sent, awaiting response... 302 Found
Location: https://dl.google.com/go/go1.23.0.linux-amd64.tar.gz [following]
--2024-08-17 00:03:45--  https://dl.google.com/go/go1.23.0.linux-amd64.tar.gz
Resolving dl.google.com (dl.google.com)... 114.250.67.33
Connecting to dl.google.com (dl.google.com)|114.250.67.33|:443... connected.
HTTP request sent, awaiting response... 200 OK
Length: 73590011 (70M) [application/x-gzip]
Saving to: ‘go1.23.0.linux-amd64.tar.gz’

go1.23.0.linux-amd64.tar.gz                           100%[======================================================================================================================>]  70.18M  45.0MB/s    in 1.6s    

2024-08-17 00:03:47 (45.0 MB/s) - ‘go1.23.0.linux-amd64.tar.gz’ saved [73590011/73590011]

[root@mimt ~]#

当我们需要进行下载限速时,则可以在input链进行限制,可以做如下策略,不过此一般用于限制从服务器下载的限流,保证服务器的带宽

shell
[root@mimt ~]# nft add rule test chain1 limit rate over 1 mbytes/second drop
[root@mimt ~]# 
[root@mimt ~]# nft -a -n list ruleset
table ip test { # handle 2
	chain chain1 { # handle 1
		type filter hook input priority 100; policy accept;
		limit rate over 1 mbytes/second drop # handle 2
	}
}
[root@mimt ~]# wget https://go.dev/dl/go1.23.0.linux-amd64.tar.gz
--2024-08-17 00:23:37--  https://go.dev/dl/go1.23.0.linux-amd64.tar.gz
Resolving go.dev (go.dev)... 216.239.36.21, 216.239.38.21, 216.239.34.21, ...
Connecting to go.dev (go.dev)|216.239.36.21|:443... failed: Connection refused.
Connecting to go.dev (go.dev)|216.239.38.21|:443... connected.
HTTP request sent, awaiting response... 302 Found
Location: https://dl.google.com/go/go1.23.0.linux-amd64.tar.gz [following]
--2024-08-17 00:23:58--  https://dl.google.com/go/go1.23.0.linux-amd64.tar.gz
Resolving dl.google.com (dl.google.com)... 114.250.63.33
Connecting to dl.google.com (dl.google.com)|114.250.63.33|:443... connected.
HTTP request sent, awaiting response... 200 OK
Length: 73590011 (70M) [application/x-gzip]
Saving to: ‘go1.23.0.linux-amd64.tar.gz.5’

go1.23.0.linux-amd64.tar.gz.5                         100%[======================================================================================================================>]  70.18M  1016KB/s    in 72s     

2024-08-17 00:25:10 (1005 KB/s) - ‘go1.23.0.linux-amd64.tar.gz.5’ saved [73590011/73590011]

[root@mimt ~]#

外发限速

shell
[root@mimt ~]# nft add table test
[root@mimt ~]# nft add chain test chain1 '{type filter hook output priority 100;}'
[root@mimt ~]# nft add rule test chain1 limit rate over 1 mbytes/second drop 
[root@mimt ~]# nft -a list ruleset
table ip test { # handle 1
	chain chain1 { # handle 1
		type filter hook output priority 100; policy accept;
		limit rate over 1 mbytes/second drop # handle 4
	}
}
[root@mimt ~]#

限速前后对比

cmd
C:\Users\more>curl  -o d:\go.tar.gz  http://192.168.132.31/download/go.tar.gz
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
100 70.1M  100 70.1M    0     0   131M      0 --:--:-- --:--:-- --:--:--  131M

C:\Users\more>curl  -o d:\go.tar.gz  http://192.168.132.31/download/go.tar.gz
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
100 70.1M  100 70.1M    0     0  1036k      0  0:01:09  0:01:09 --:--:-- 1073k

C:\Users\more>

3.3 计数器(Counters)

我们可以统计指定想要去统计的流量,比如网卡,服务,端口等等

匿名计数器

比如我想统计所有访问80端口的流量

shell
[root@mimt ~]# nft add table test
[root@mimt ~]# nft add chain test chain_output '{type filter hook output priority 100;}'
[root@mimt ~]# nft add chain test chain_input '{type filter hook input priority 100;}'
[root@mimt ~]# nft add rule test chain_input tcp dport 80 counter
[root@mimt ~]# nft add rule test chain_output tcp sport 80 counter
[root@mimt ~]# 
[root@mimt ~]# nft -a list ruleset
table ip test { # handle 2
	chain chain_output { # handle 1
		type filter hook output priority 100; policy accept;
		tcp sport 80 counter packets 0 bytes 0 # handle 6
	}

	chain chain_input { # handle 2
		type filter hook input priority 100; policy accept;
		tcp dport 80 counter packets 0 bytes 0 # handle 5
	}
}
[root@mimt ~]# 
# 下载访问完成后统计的流量
[root@mimt ~]# nft -a list ruleset
table ip test { # handle 2
	chain chain_output { # handle 1
		type filter hook output priority 100; policy accept;
		tcp sport 80 counter packets 2320 bytes 73690747 # handle 6
	}

	chain chain_input { # handle 2
		type filter hook input priority 100; policy accept;
		tcp dport 80 counter packets 25289 bytes 1033737 # handle 5
	}
}
[root@mimt ~]#

命名计数器

我们也可以单独声明一个计数器,以便后期进行同一维护,适用于有多个需要计数场景,这里为了方便,就不分开进行流量统计了

shell
[root@mimt ~]# nft add counter test nginx '{comment "NGINX counter";}'
[root@mimt ~]# nft add rule test chain_input tcp dport 80 counter name nginx 
[root@mimt ~]# nft add rule test chain_output tcp sport 80 counter name nginx 
[root@mimt ~]# 
[root@mimt ~]# nft -a list ruleset
table ip test { # handle 2
	counter nginx { # handle 7
		comment "NGINX counter"
		packets 0 bytes 0
	}

	chain chain_output { # handle 1
		type filter hook output priority 100; policy accept;
		tcp sport 80 counter name "nginx" # handle 11
	}

	chain chain_input { # handle 2
		type filter hook input priority 100; policy accept;
		tcp dport 80 counter name "nginx" # handle 10
	}
}
[root@mimt ~]# nft -a list ruleset
table ip test { # handle 2
	counter nginx { # handle 7
		comment "NGINX counter"
		packets 7620 bytes 73895192
	}

	chain chain_output { # handle 1
		type filter hook output priority 100; policy accept;
		tcp sport 80 counter name "nginx" # handle 11
	}

	chain chain_input { # handle 2
		type filter hook input priority 100; policy accept;
		tcp dport 80 counter name "nginx" # handle 10
	}
}
[root@mimt ~]#