16、基于状态的iptable+高级路由(重点)
--
基于状态的iptables
如果按照tcp/ip来划分连接状态,有12种之多
但iptables里只有4种状态;ESTABLISHED、NEW、RELATED及INVALID
这两个分类是两个不相干的定义。例如在TCP/IP标准描述下UDP及ICMP数据包是没有连接状态的,但在state模块的描述下,任何数据包都有连接状态。
new(第一次)
| |-------------------->| |
| | established(回来) | |
| 客户机|<--------------------| 服务器|
|------| |------|
| 35 | | 32 |
|------| |------|
1、ESTABLISHED
(1)与TCP数据包的关系:首先在防火墙主机上执行SSH Client,并且对网络上的SSH服务器提出服务请求,而这时送出的第一个数据包就是服务请求的数据包,如果这个数据包能够成功的穿越防火墙,那么接下来SSH Server与SSH Client之间的所有SSH数据包的状态都会是ESTABLISHED。
(2)与UDP数据包的关系:假设我们在防火墙主机上用firefox应用程序来浏览网页(通过域名方式),而浏览网页的动作需要DNS服务器的帮助才能完成,因此firefox会送出一个UDP数据包给DNS Server,以请求名称解析服务,如果这个数据包能够成功的穿越防火墙,那么接下来DNS Server与firefox之间的所有数据包的状态都会是ESTABLISHED。
(3)与ICMP数据包的关系:假设我们在防火墙主机ping指令来检测网络上的其他主机时,ping指令所送出的第一个ICMP数据包如果能够成功的穿越防火墙,那么接下来刚才ping的那个主机与防火墙主机之间的所有ICMP数据包的状态都会是ESTABLISHED。
由以上的解释可知,只要数据包能够成功的穿越防火墙,那么之后的所有数据包(包含反向的所有数据包)状态都会是ESTABLISHED。
2、NEW
首先我们知道,NEW与协议无关,其所指的是每一条连接中的第一个数据包,假如我们使用SSH client连接SSH server时,这条连接中的第一个数据包的状态就是NEW。
3、RELATED
RELATED状态的数据包是指被动产生的数据包。而且这个连接是不属于现在任何连接的。RELATED状态的数据包与协议无关,只要回应回来的数据包是因为本机送出一个数据包导致另一个连接的产生,而这一条新连接上的所有数据包都是属于RELATED状态的数据包。
4、INVALID
INVALID状态是指状态不明的数据包,也就是不属于以上三种状态的封包。凡是属于INVALID状态的数据包都视为恶意的数据包,因此所有INVALID状态的数据包都应丢弃掉,匹配INVALID状态的数据包的方法如下:
iptables -A INPUT -p all -m state INVALID -j DROP
我们应将INVALID状态的数据包放在第一条。
例1:
有下面两台机
172.16.2.35 172.16.2.32
35是可以ssh访问32,也可以elinks访问32
1,在32上
iptables -P INPUT DROP
iptables -P OUTPUT DROP
这里就把双链都关掉,35任何访问都过不来了
2,
按以前的做法
在32上允许别人ssh进来
iptables -A INPUT -p tcp --dport 22 -j ACCEPT
iptables -A OUTPUT -p tcp --sport 22 -j ACCEPT
在32上允许别人elinks进来
iptables -A INPUT -p tcp --dport 80 -j ACCEPT
iptables -A OUTPUT -p tcp --sport 80 -j ACCEPT
按state的做法
--进来你可以用下面两边搞定
iptables -A INPUT -p tcp --dport 22 -j ACCEPT
iptables -A INPUT -p tcp --dport 80 -j ACCEPT
--但出去的话你只用下面一条搞定所有
iptables -A OUTPUT -p tcp -m state --state ESTABLISHED,RELATED -j ACCEPT
--上面这条的意思其实就是:只要能进来的tcp访问,我都能出去找到你(无论他是用哪个随机端口访问进来的;因为只要能进来,那么后续的包都属于ESTABLISHED状态了)
例2:
--有些服务器,可能希望不能你ping通他,但是他可以ping你
--解决方法:
一种:
修改proc的内核参数,禁ping
二种:使用状态iptables
有下面两台机
172.16.2.35 172.16.2.32
在32上实现32能ping通35和所有人.但35和所有人不能ping通32
1,在32上
iptables -P INPUT DROP
iptables -P OUTPUT DROP
这里就把双链都关掉,35任何访问都过不来了
2,在32上
iptables -A INPUT -p icmp -m state --state ESTABLISHED -j ACCEPT
iptables -A OUTPUT -p icmp -m state --state NEW,ESTABLISHED -j ACCEPT
--重点是INPUT那条不能允许NEW状态的;
--注意第二步的第二条(也就是output这条),如果只写了NEW状态,那么32ping所有人,都只能通第一个包;加上ESTABLISHED状态,所有包都能通
练习:
1,实现我想访问别人的服务(任何服务),都可以成功;
iptables -P INPUT DROP
iptables -P OUTPUT DROP
iptables -A OUTPUT -m state --state new,established -j ACCEPT
iptables -A INPUT -m state --state established -j ACCEPT
--这两句就实现了,服务器想出去访问的任何服务都OK
2,在上面的基础上加上需求:但别人想访问我的服务,只允许可以访问我的80
iptables -A INPUT -p tcp --dport 80 -j ACCEPT
iptables -A OUTPUT -p tcp --sport 80 -j ACCEPT --这条不需要了
--在没有做练习1里那条OUTPUT的状态规则的情况下,我们需要在这里加上OUTPUT的这条;但在练习1的基础之后,这里只需要INPUT的这一条就可以了
3,继续加需求:允许ping任何人,但只允许35ping我
iptables -A INPUT -p icmp -s 172.16.2.35 -j ACCEPT
把上面的需求合起来,一共需要的规则只有下面6条:
iptables -P INPUT DROP
iptables -P OUTPUT DROP
iptables -A OUTPUT -m state --state new,established -j ACCEPT
iptables -A INPUT -m state --state established -j ACCEPT
iptables -A INPUT -p tcp --dport 80 -j ACCEPT
iptables -A INPUT -p icmp -s 172.16.2.35 -j ACCEPT
==================================
tcpdump
tcpdump - dump traffic on a network
参数的分类:
协议 tcp udp arp icmp
数据内容 端口,Ip :
src port 80 --源
dst port 22 --目标
tcpdump tcp dst port 80 -n
tcpdump -i eth1 tcp dst port 80 -n --小写i参数指定哪个网卡
tcpdump tcp dst port 80 -n -w tcpdump.txt --把dump出来的信息保存到tcpdump.txt文件
tcpdump -r tcpdump.txt --然后需要使用tcpdump -r去读取
关系参数 : ! and or
tcpdump tcp dst port 80 -n and src 10.1.1.35
tcpdump tcp dst port 80 -n and ! src 10.1.1.35
tcpdump tcp dst port 80 -n and host 10.1.1.35
-vv <---把数据包的详细内容都记录下来
-w <---把数据保存到某个文件
练习:
捕捉所有访问本机ftp服务器的连接,并且数据包是来自10.1.1.35
tcpdump tcp dst port 21 and src 10.1.1.35 -n
捕捉所有来自10.1.1.1的arp协议的数据包,并且捕捉的显示要求输出以太网数据帧的信息 -e
tcpdump arp and src 10.1.1.1 -n -e
捕捉所有本机web服务回应10.1.1.35的数据包
tcpdump tcp src port 80 and dst 10.1.1.35
还有一个图形的抓包工具:wireshark
===================================
对mangle表的mark的讨论
MARK用来给包设置特殊的标记。iproute2能识别这些标记,并根据不同的标记(或没有标记)决定不同的路由。用这些标记我们可以做带宽限制和基于请求的分类。
用处一:
在lvs集群里,可以对进入的数据包打mark标记,然后针对标记再来进行分发
使用到iptables 的 set-mark和ipvsadm -f参数
--下面这段就是网上的一个例子,学到LVS后,可以对比这个做出来
==========================================
例子:LVS的 NAT 模式配置使用-f 选项
OS: Redhat 9 内核:2.4.20
Load Balancer: eth0: 192.168.1.110/32 (负载服务器对外VIP)
eth1: 192.168.3.1/24
Realserver1: 192.168.3.2 (集群服务器)
掩码: 255.255.255.0
网关: 192.168.3.1
Realserver2: 192.168.3.3 (集群服务器)
掩码: 255.255.255.0
网关: 192.168.3.1
实验目的:在负载服务器上,配置LVS(NAT模式),做远程登陆服务器的集群。
实现最终用户访问负载服务器的对外VIP,负载服务器,将请求分发到集群下的服务器,
由集群服务器轮流提供ssh和telnet服务。
在Load Balancer上设置:
#echo "1" > /proc/sys/net/ipv4/ip_forward #确保转发功能已打开
# iptables -t mangle -A PREROUTING -p tcp -d 192.168.1.110/32 --dport 22 -j MARK --set-mark 2
# iptables -t mangle -A PREROUTING -p tcp -d 192.168.1.110/32 --dport 23 -j MARK --set-mark 2
# iptables -t mangle -L PREROUTING #查看上述两条规则是否加入到mangle表的PREROUTING链中
# ipvsadm -A -f 2 -s rr #添加一个LVS服务,数据类型为fwmark为2的数据包,使用轮询策略
# ipvsadm -a -f 2 -r 192.168.3.2 -m -w 1 #添加真实服务器192.168.3.2到LVS(NAT模式)
#数据类型为fwmark为2的服务中
# ipvsadm -a -f 2 -r 192.168.3.3 -m -w 1 #添加真实服务器192.168.3.3到LVS(NAT模式)
#数据类型为fwmark为2的服务中
==========================================================
用处二:
可以和策略路由结合使用,进行路由的标记分发
参考:
http://bbs.chinaunix.net/thread-2228370-1-1.html
http://bbs.chinaunix.net/thread-2251893-1-1.html
用处三:
单服务器多个网卡同网段,想进行从哪个网卡来的数据就从哪个网卡出去
--一般来说,单服务器多网卡不要使用同一个网段的IP
用一个虚拟机两个host-only网卡,同网段
用真实机ping这两个网卡,然后在真实机上查看
[root@li ~]# arp -a |grep 212
bogon (192.168.212.146) at 00:0C:29:55:CC:D7 [ether] on vmnet1
bogon (192.168.212.147) at 00:0C:29:55:CC:D7 [ether] on vmnet1
--结果是两个网卡都是对应eth0的MAC
关掉eth1 147,然后arp -d 192.168.212.147清除,ping 192.168.212.147还是可以ping通
解决办法:
默认,linux所有的网卡都会回应arp,
echo 1 > /proc/sys/net/ipv4/conf/eth0/arp_ignore
echo 1 > /proc/sys/net/ipv4/conf/eth1/arp_ignore
--这样修改后,如果本网卡没有这个IP,则不回应arp
--这个参数,在LVS集群里也有应用
再ping146和147测试,得到的结果就是自己的IP对应自己的MAC地址
[root@li ~]# arp -a |grep 212
bogon (192.168.212.146) at 00:0C:29:55:CC:D7 [ether] on vmnet1
bogon (192.168.212.147) at 00:0C:29:55:CC:E1 [ether] on vmnet1
===============================================================
================================================================
linux 高级路由
lartc.org
# ip rule list
0: from all lookup local
32766: from all lookup main
32767: from all lookup default
# cat /etc/iproute2/rt_tables
--上面的local对应数字255,在rhel5.7里ip rule list看到的不是local,而是255;这只是对应的两种名称而已
#
# reserved values
#
255 local
254 main
253 default
0 unspec
#
# local
#
#1 inr.ruhep
# ip route list table local/255
# ip route list table main/254
# ip route list table default/253
============================================================
应用实例1:
----------》快线路
|
|
内网用户 ----linux路由
|
|
---------》慢线路
linux路由器有两条上网线路,一个快,一个慢
有这样的需求:内网用户需要给钱共享上网,有人给钱多,需要快线路,有人给钱少,需要快线路,这样的话,我们就可以使用策略路由了
模拟的话使用下面的图:
172.16.2.20
----------》VM2(bridge)
|
81.1.1.128 | 172.16.2.35 eth0
VM1(hostonly)----linux路由81.1.1.1 vmnet1 公网
| 71.1.1.1 vmnet8
|
---------》VM3 (nat)
71.1.1.128
上图架构中:
1.把VM1网关指向81.1.1.1
2,把linux路由器的网关指向172.16.2.20
3.linux路由打开ip_forward
先测试:在VM1上ping一个外网IP(如 ping 8.8.8.8),这个时候只能在VM2上抓到相关的包,表示数据包从VM2出去
下面就是在linux路由上进行操作来实现:
操作命令:
echo 200 t1 >> /etc/iproute2/rt_tables
ip rule add from 81.1.1.128 table t1
ip route add default via 71.1.1.128 dev vmnet8 table t1
ip route flush cache
操作完后,测试
1,在内网ping 8.8.8.8
2,在两个模拟外网路由器的机器上抓包
# tcpdump -i eth0 -p icmp
3,结果这次只能在VM3上抓到包,OK
应用实例2:
172.16.2.20
----------》VM2(bridge)
|
81.1.1.128 | 172.16.2.35 eth0
VM1(hostonly)----linux路由81.1.1.1 vmnet1
| 71.1.1.1 vmnet8
|
---------》VM3 (nat)
71.1.1.128
要实现不同类型的包走不同的线路:如80的访问走一条线,其它的走另外一条线路
实现不同类型的包的策略路由,就要借助于iptables的mangle链的set mark功能
1,在linux路由器上使用策略路由实现
# iptables -t mangle -A PREROUTING -i vmnet1 -p tcp --dport 80 -j MARK --set-mark 1 --把从内网进来要出去的80的包打标记为1
# echo 100 http.out >> /etc/iproute2/rt_tables --建一张叫http.out的表,表编号100
# ip rule add fwmark 1 table http.out pref 20000 --指定打了标记为1的所有包都走http.out这张路由表,并指定优先级为20000(这里优先级可以不指,因为只有这一条策略)
# ip route add default via 71.1.1.128 dev vmnet8 table http.out --指定http.out表从vmnet8出去找71.1.1.128
# ip route flush cache --刷新路由缓存
2,测试
测试一:在内网81.1.1.128客户端上
elinks 172.16.2.20
在172.16.2.20和71.1.1.128上都执行下面的命令
tcpdump -i eth0 tcp port 80
--只有71.1.1.128上能抓到包,OK
测试二:在内网81.1.1.128客户端上
ping 172.16.2.20
在172.16.2.20和71.1.1.128上都执行下面的命令
tcpdump -i eth0 -p icmp
--只有172.16.2.20上能抓到包,OK
--从上面就可以看到出去的80端口的包和其它的包走的路线不一致
=============================================================
应用实例3:
172.16.2.51 bridge 客户端 hostonly2 91.1.1.129
| |
| |
| |
172.16.2.41 bridge hostonly2 91.1.1.128
电信路由器1 网通路由器2
81.1.1.130 hostonly1 nat 71.1.1.131
| |
| |
81.1.1.136 hostonly1 服务器 nat 71.1.1.132
进来
电信客户端
SIP:192.168.1.100
DIP:172.16.2.41
被客户端家里的路由器SNAT
SIP:172.16.2.51
DIP:172.16.2.41
被服务器端的电信路由器DNAT
SIP:172.16.2.51
DIP:81.1.1.136
我们下面所做的就是回去时,由两个不同ISP运营商进来的包,从两个不同的网关回去
回去:
SIP:81.1.1.136
DIP:172.16.2.51
被服务器端的电信路由器SNAT
SIP:172.16.2.41
DIP:172.16.2.51
被电信客户端家里的路由器DNAT
SIP:172.16.2.41
DIP:192.168.1.100
上图中:
客户端用虚拟机(linux或xp都可以,但不能使用真实机,因为真实机可以直接访问到上图中的服务器)
eth0 172.16.2.51 bridge
eth1 91.1.1.129 hostonly2
电信路由器1用虚拟机
eth0 172.16.2.41 bridge
eth1 81.1.1.130 hostonly1
网通路由器2用虚拟机
eth0 91.1.1.128 hostonly2
eth1 71.1.1.131 nat
服务器用虚拟机
eth0 81.1.1.136 hostonly1
eth1 71.1.1.132 nat
--假设上图里的客户端为内网用户,服务器为公网服务器;
--因为是模拟环境,所以这里两个路由器打开ip_forward
--我们实现的是当客户端用电信线路访问,也只能从电信线路返回;网通线路访问,也只能从网通线路返回
1,在服务器上使用策略路由实现
# ip rule
0: from all lookup local
32766: from all lookup main
32767: from all lookup default
# echo 100 dianxin >> /etc/iproute2/rt_tables
# echo 200 wangtong >> /etc/iproute2/rt_tables
# ip route add 81.1.1.0 dev eth0 src 81.1.1.136 table dianxin
# ip route add default via 81.1.1.130 table dianxin
# ip route add 71.1.1.0 dev eth1 src 71.1.1.132 table wangtong
# ip route add default via 71.1.1.131 table wangtong
# ip rule add from 81.1.1.136 table dianxin
# ip rule add from 71.1.1.132 table wangtong
# ip rule
0: from all lookup local
32764: from 71.1.1.132 lookup wangtong
32765: from 81.1.1.136 lookup dianxin
32766: from all lookup main
32767: from all lookup default
测试:
客户端模拟电信线路:
把网关指向172.16.2.41
ping 81.1.1.136 可以ping通
traceroute 81.1.1.136 结果为两跳,第一跳172.16.2.42,第二跳到目地81.1.1.136
在服务器上打开两个终端,去tcpdump icmp的包
tcpdump -i eth0 -p icmp --这里有包
tcpdump -i eth1 -p icmp --这里没包
测试OK
客户端模拟网通线路:
和上面的一样
==============================
应用实例4:
有些情况一个服务器多张网卡配置成同一个网段(这种做法是不正确的),
因为同一个网段,那么这多张网卡使用的路由是同一条,无法将他们区分开来
(从不同网卡进来的包,一般都会从eth0出去)
如果你想要实现从哪里进,从哪里去,使用iproute2对多张网卡设定不同的网关就可以(优先级要高于main表,也就是route -n看到的)
做法参考实例3
====================================
静态路由:
真实机A 虚拟机B 虚拟机C 虚拟机D
eth1 ----> eth0 eth1
2.2.2.2 <---- 2.2.2.3 4.4.4.4
ip_forward ip_forward
eth0 eth0 eth1 eth0
1.1.1.1 ---> 1.1.1.2 3.3.3.3 <--3.3.3.4
上图中,所有IP同网段的网卡在物理上也在一个交换机上
所有IP不同网段的网卡在物理上不在同一个交换机上
也就是说,上面有四个物理网段
请问:我在1.1.1.1上,如何能ping通4.4.4.4?
[root@li ~]# ls /share/soft/route/zebra-0.95a.tar.gz
# tar xvf /zebra-0.95a.tar.gz -C /usr/src/
# cd /usr/src/zebra-0.95a/
# ./configure --enable-irdp --enable-netlink --enable-tcp-zebra
# make ;make install
安装完后,就可以在下面的路径找到配置模版
# ls /usr/local/etc/
bgpd.conf.sample ospf6d.conf.sample ripd.conf.sample zebra.conf.sample
bgpd.conf.sample2 ospfd.conf.sample ripngd.conf.sample
# cd /usr/local/etc/
# cp zebra.conf.sample zebra.conf
# vim zebra.conf --主配置文件,修改下面三行
hostname Router1 --路由器前的提示符
password 123 --密码
enable password 123 --enable模式的密码
# zebra -d --启动zebra,-d表示以daemon形式启动
# netstat -ntlup |grep 2601 --2601端口
tcp 0 0 :::2601 :::* LISTEN 10979/zebra
# telnet localhost 2601
Password: --密码为zebra.conf里password后的密码
Router1> --这是view only 模式
Router1> show ip route --可以查看路由
Codes: K - kernel route, C - connected, S - static, R - RIP, O - OSPF,
B - BGP, > - selected route, * - FIB route
K>* 0.0.0.0/0 via 1.1.1.2, eth1
C>* 1.1.1.0/24 is directly connected, eth1
C>* 10.1.1.0/24 is directly connected, eth0
C>* 127.0.0.0/8 is directly connected, lo
Router1> enable --进入管理模式
Password: --密码为zebra.conf里的enable password后的密码
Router1#
Router1# configure terminal --进入配置模式
Router1(config)# interface eth0 --进入eth0端口的子配置模式
Router1(config-if)# ip address 10.1.1.214/24
Router1(config-if)# exit
Router1(config)# interface eth1
Router1(config-if)# ip address 1.1.1.1/24
配置路由协议:rip
# cp ripd.conf.sample ripd.conf
# vim ripd.conf
hostname ripd
password 123
# ripd -d
# netstat -ntlup |grep 2602
tcp 0 0 :::2602 :::* LISTEN 11005/ripd
# telnet localhost 2602
Password:
ripd> enable
ripd# configure terminal
ripd(config)# router rip
ripd(config-router)# network eth0
ripd(config-router)# network 10.1.1.0/24
ripd(config-router)# network eth1
ripd(config-router)# network 1.1.1.0/24
ripd(config-router)# version 2
# cp ospfd.conf.sample ospfd.conf
# vim ospfd.conf
hostname ospfd
password 123
enable password 123
# netstat -ntlup |grep 2604
tcp 0 0 :::2604 :::* LISTEN 11024/ospfd
# telnet localhost 2604
Password:
ospfd> enable
Password:
ospfd# configure terminal
ospfd(config)# router ospf
ospfd(config-router)# network 10.1.1.0/24 area 0
ospfd(config-router)# network 1.1.1.0/24 area 0
===================================
IP tunnel
172.16.2.35 172.16.2.80
外1 外2
81.1.1.1 10.1.1.1
内1 内2
81.1.1.128 10.1.1.133
建立外1和外2之间的tunnel
1,外1上面操作
# modprobe ip_gre --tunnel协议
# lsmod |grep gre
ip_gre 15341 0
ip tunnel add tun0 mode gre remote 172.16.2.80 local 172.16.2.35
ip link set tun0 up
ip addr add 1.1.1.1 peer 1.1.1.2 dev tun0
ip route add 10.1.1.0/24 dev tun0
2,外2上的操作
# modprobe ip_gre
# lsmod |grep gre
ip_gre 15341 0
ip tunnel add tun0 mode gre remote 172.16.2.35 local 172.16.2.80
ip link set tun0 up
ip addr add 1.1.1.2 peer 1.1.1.1 dev tun0
ip route add 81.1.1.0/24 dev tun0
3,内1上的操作
把网关指向外1的内网IP
route add default gw 81.1.1.1
4,内2上的操作
把网关指向外2的内网IP
route add default gw 10.1.1.1
5,测试,内1和内2两个内网可以互相ping通,并且可以使用traceroute 命令接IP,查看确实走了tunnel
测试完后,删除tunnel的方法 ip tunnel del tun0