您的位置:首页 > 运维架构 > Linux

使用linux构建一台路由器

2016-05-16 11:16 1036 查看

声明:本文为原创作品,因为编写的时间比较久远所以不记得是否在别处发表过了。如有在其它地方发现有此文,也一定是本人发表,否则定是剽窃。

本文主要目的不是让大家使用linux来搭建路由器使用,而是为大家提供一个从linux环境下看网络的视角。通过系统中对数据包更加细致的操纵,从而能了解到一个数据包在系统内核情况下的状态。理论上经过linux系统的所有数据包都能进行操作,实现负载均衡,重定向,IP伪装等都是可能的。本文只是一个学习工具的方法,不承担读者使用文中提及的知识来犯罪的责任。请各位遵纪守法,正确使用。

附件有一些脚本和本文的原文内容。

此文比较粗略,请没有基础或者有强迫症的读者回避。作者不承担详细解答并帮助读者学习的义务。


Linux路由器设置

  • linux路由器的简单配置

如果只是需要一台路由功能的主机提供网络共享让客户机可以上网,设置非常简单。整个过程只需要几个步骤。

1、  安装好一台linux服务器以后配置好两个网卡的IP地址或者PPPOE的拨号方式确保两个网卡都有IP地址。一个连接公网,一个连接内网。在配置连接公网网卡的时候写入GATEWAY=XXX.XXX.XXX.XXX。连接内网的网卡切忌填写GATEWAY。

2、  启用linux的包转发功能。具体方法为修改/etc/sysctl.conf文件中net.ipv4.ip_forward 项为 1。如:net.ipv4.ip_forward = 1然后保存使用sysctl  –p命令是命令生效。这样的好处是系统重启以后配置也能生效。

3、  因为是私网地址,在公网上没有明确的路由信息所以必须进行IP地址伪装才能有路由信息到公网。使用linux的防火墙iptables能做到这一点,当然iptables不是只有这个功能以后再说。具体操作方式为iptables -t nat -A POSTROUTING -o eth0  -j MASQUERADE。

4、  如果是PPPOE拨号方式,请为每个ppp链接添加一条nat伪装。保证链接的外网地址一直性。如:iptables -tnat -A POSTROUTING -o ppp0 -j MASQUERADE

做完以上步骤,一台具有路由功能的linux主机就能正常使用了。内网主机设置IP地址与linux主机一个网段,然后网关指向linux主机的IP地址。这样就可以上网了。

 

  • ADSL拨号的网络共享方式

静态的IP地址方式很简单,就是配置好ISP给的IP地址和网关等信息就行了。在平时的应用中ADSL的接入方式在linux主机上还是比较少见。所以在这里略提一下。要进行pppoe拨号需要安装rp-pppoe这个linux包。安装完以后运行pppoe-setup命令进行配置,根据提示进行操作就行了。

在linux默认只能有一个pppoe拨号,要实现多个pppoe帐号同时使用需要对配置文件进行更改。

修改/etc/ppp/pap-secrets文件,"ppp1"  *       "ppp1"每个帐号和密码一行。第一个是帐号,第二个为密码。多少个帐号就多少行。

修改/etc/ppp/chap-secrets文件,格式和上面一样。

复制ifcfg-ppp0文件命名为ifcfg-ppp1,修改两个配置文件的PIDFILE参数。PIDFILE=/var/run/pppoe-adsl0.pid每个配置文件的PIDFILE名字不能重复。否则就会拨号部成功。这样能实现同一个网卡拨多个ADSL帐号。在网上找了很多方式都不行,最后发现既然如此简单。

  • linux分类标记mangle应用

如果需要对数据包进行QOS,策略路由,多ISP线路负载均衡等。都需要mangle功能对数据包进行分类,为后续的处理提供依据。否则一切将是空。

分类标记路由需要在/etc/sysconfig/network文件中添加NOZEROCONF=no选项。否则无法使用。

为保证使用效果,简单介绍mangle表的结构。Mangle表总共有5条链,我们称为数据包进入时所经过的链路。5条链分别是:

PREROUTING: 路由前,常用于标记策略和端口进行路由

INPUT:进入路由器的数据

FORWARD:通过路由转发,用于修改TTL、TCP-MSS和流量控制规则。

OUTPUT:数据输出

POSTROUTING:路由后

 

对数据包进行分类标记:

1、iptables -A PREROUTING -tmangle -i eth1 -m state --state RELATED,ESTABLISHED -j CONNMARK --restore-mark

重新保存CONNMARK上的标记。实际上,该条规则只是简单的将链接跟踪上记录的CONNMARK值赋给数据包上的MARK。

2、iptables -A PREROUTING -t mangle -i eth1 -m mark ! --mark 0 -jACCEPT

如果数据包上已经有了MARK,则直接接受该数据包,不需要匹配下面的规则了。

3、iptables-A PREROUTING -t mangle -i eth1 -m state--state NEW -m statistic--mode nth --every 2  --packet 0  -j MARK --set-mark 1

       iptables -A PREROUTING -t mangle -i eth1 -mstate --state NEW -m statistic --mode nth --every 2 --packet 1 -j MARK--set-mark 2

    按照数据包依次进行标记。

4、iptables -A PREROUTING -tmangle -i eth1 -j CONNMARK --save-mark

把上面标记过的链接上所有数据包都打上标记。

按照网上的说法,使用nth的方式会造成网页登录后如果刷新网页就会失效要求重新登录。如果这样,网银根本就无法使用,只要登录后点击任何一个页面都会提示登录过期。但是在测试中发现网银没有任何问题。不知道是不是测试环境中使用的公网只有一个,而多PPP链接的方式是在内部重新搭建了一台PPPOE-server来实现的。还希望有测试了的朋友给个结果发往mail:denwork#qq.com把#改成@谢谢!不过按照上面的配置方法因为使用了-m state参数,所以只针对新的链接。如果链接建立以后整个链接的数据包都将打上相同的标志,也就使用相同的接口出去。所以应该也不会掉线。

 

以目标IP地址均衡的方式:

可以使用u32模块进行匹配,思路为目标IP地址的最后一位与数字进行匹配。

因为需要使用掩码的方式来判断所以条件只能是2的次方,比如2、4、8、16、32这几种可能。如果你刚好需要mark出2、4、8等个标记那么恭喜你刚好够,那么假如你刚好只有20根ADSL需要做mark标记然后均衡怎么办?答案是,当在标记的时候选32种可能然后mark一遍后还剩12种再均衡的分到相同的mark标记上。比如前面1-20分别标好mark后,从13开始再标记成mark 1再循环一遍。这就变得不太均衡,不过差异不太大将就用了。等运行一段时间后可以使用iptables -L -t mangle -v命令查看那些mark数据比较多,然后再调整mark标记。

前面用nth基于数据包的方式来负载,但很多人说这会造成登录网银、论坛等网页要求重新登录的问题。所以这里u32模块的实现是基于ip流的负载均衡,这能很好的解决网页重新登录问题。

这里使用目标地址加目标端口的方式来进行负载分担,当然你也可以使用像ROS一样基于源、目标地址加源、目标端口来实现。具体方式可以参照着修改。

假设现在有8条ADSL需要均衡,那么现在需要使用目标IP地址2种可能加目标端口的4种可能来实现。

先帖出取u32的规则:

 

-m u32 --u32"16&0x1 = 0" 第一种目标地址

-m u32 --u32"16&0x1 = 1" 第二种目标地址

-m u32 --u32 "0>>22&0xff@0&0x3= 0"  第一种目标端口

-m u32 --u32 "0>>22&0xff@0&0x3= 1"  第二种目标端口

-m u32 --u32 "0>>22&0xff@0&0x3= 2"  第三种目标端口

-m u32 --u32 "0>>22&0xff@0&0x3= 3"  第四种目标端口

根据以上组合就形成了8种可能的方式。解释一下上面的命令,16&0x1:16是指IP包头第0字节开始数一直到第16字节就是目标地址所在的区域。看看下面这个图

每行是4个字节,目标地址在第五行。我们需要取到第五行的内容,所以需要从第五行的头开始读取数据,源地址结束是第15个字节位置,那么读目标地址就是从第16个字节开始。

0>>22&0xff@0&0x3 这个解释起来很麻烦,大概意思就是从第0字节开始把第一行的数据全部读取,然后偏移22位就得到了整个IP包头的长度。然后以这个长度开始读取数据,也就是TCP或者UDP包源端口与目标端口的位置。以上匹配的0x3是个16进制的数字,那就是最后2个字节的内容。&符号是按位与得出来的结果和=后面的值对比,相等就满足条件。

下面填写具体的规则:

1、iptables -A PREROUTING -t mangle -i eth1 -m state --stateRELATED,ESTABLISHED -j CONNMARK --restore-mark

重新保存CONNMARK上的标记。实际上,该条规则只是只是简单的将链接跟踪上记录的CONNMARK值赋给数据包上的MARK。

2、iptables -A PREROUTING -t mangle -i eth1 -m mark ! --mark 0 -jACCEPT

如果数据包上已经有了MARK,则直接接受该数据包,不需要匹配下面的规则了。

3、iptables-A PREROUTING -t mangle -i eth1 -m state --state NEW -m u32 --u32 "16&0x1= 0 && 0>>22&0xff@0&0x3 = 0"  -j MARK --set-mark 1

iptables -A PREROUTING-t mangle -i eth1 -m state --state NEW -m u32 --u32 "16&0x1 = 0&& 0>>22&0xff@0&0x3 = 1"  -j MARK --set-mark 2

目标地址和端口匹配一轮一直到"0>>22&0xff@0&0x3= 3"

iptables -APREROUTING -t mangle -i eth1 -m state --state NEW -m u32 --u32 "16&0x1= 1 && 0>>22&0xff@0&0x3 = 2"  -j MARK --set-mark 7

iptables -APREROUTING -t mangle -i eth1 -m state --state NEW -m u32 --u32 "16&0x1= 1 && 0>>22&0xff@0&0x3 = 3"  -j MARK --set-mark 8

第二轮,这样下来就有了8种标记。

4、iptables-A PREROUTING -t mangle -i eth1 -j CONNMARK --save-mark

 

  • 负载均衡

1、使用默认网关负载模式,配置比较简单只需要在路由表中添加一条路由信息包含两个接口就行了。ip route add default nexthop dev ppp0 weight 1 nexthop dev ppp1weight 1

这样就达到了负载均衡的模式。但此模式是居于目标地址的方式,比如目标地址为不同IP地址的才能从不同的接口路由数据。如果目标地址都是相同的网站,那么就会造成所有数据都从相同接口路由数据,而其它接口非常空闲。

2、使用分类数据包标记功能实现负载均衡。

按照上面的方法把数据包进行标记以后就可以使用路由表功能实现负载均衡,编辑/etc/iproute2/rt_tables文件写入252     ppp0这样的表。前面是表的顺序数字越小表示越靠前,后面是表名。ip route add default dev ppp0 table ppp0在每个表中添加路由信息。iprule add fwmark 1 table ppp0添加路由条目,把标记为1的数据包交给table ppp0的路由表处理。以此类推就能实现负载均衡。

在使用路由分类标记功能的负载均衡时,如果多条ISP链路的时候其中某一条失效可能会导致被标记的包找不到路由而断网,此时可与第一种负载均衡模式同时使用。两种路由均衡技术可同时使用并且不会冲突。这样被标记的数据包在进入相应的路由表时发现路由失效而转为使用默认路由方式。因为链路在中断和恢复的时候可能会导致默认路由条目发生变化,所以需要使用一个脚本来检测接口信息并修改默认路由。

脚本如下:

 

#!/bin/bash

while [ 1]

 do

   a=""

   incline=`ifconfig | grep ppp | wc -l`

   routline=`ip route | grep weight | wc -l`

   if [ "$incline" !="$routline" ]; then

      for ETH in `ifconfig | grep ppp | awk'{print $1}'`

       do

        a="$a nexthop dev $ETH"

      done

      ip route replace default $a

   fi

 sleep 1

done

以上是以pppoe拨号链接为例,呈现一种动态添加的思路。如果是静态IP地址请自行修改,因为是eth物理网卡就算ip失效也能使用ifconfig查看到,所以以上脚本不适用这种情况。可以编写更完善的脚本,到/etc/sysconfig/network-scripts目录下把网卡文件名进行轮寻,然后使用cat命令加grep读取到IP地址行的内容使用awk或sed命令取得IP地址。然后使用ping命令对外网网关进行检测来判断外网是否正常,最后把地址或者网卡名添加到变量中。检查完所有外网网卡后使用ip route replace default命令对默认网关进行更改。

3、如果内核加载condition模块后可以编写脚本来实现对每个网卡的负载进行跟踪,动态的分配带宽到每个网卡上。可惜的时候目前没有找到condition模块的源代码,希望有人找到后可以把相关代码发给我mail:denwork#qq.com把#改成@谢谢!非常感谢。

4、可以使用脚本,动态修改iptables列表来实现动态负载均衡。取得每个接口的带宽使用比例,找出带宽比例最低的和最高的,找出最高和最低带宽的接口名和对应的mark标记名。找出iptables中最高带宽的mark标记在第几行,然后把最高带宽接口的标记修改成最低带宽的接口标记。

5、模拟ROS PPC基于流的功能,进行源地址、目标地址、源端口、目标端口。四维元素进行检查,与设置因子模,得出的结果进行匹配打上数据包标记。经过测试使用u32模块可以做到数据包的均衡,但目前只能有两个标签。u32模块可以实现对数据包头进行检查,从而得到以上四维元素进行运算。

 

 

  • 多pppoe拨号问题

在实际应用环境中多pppoe拨号会因为虚拟出来的vlan MAC地址都一样无法拨号到电信的base服务器中,一样的MAC地址会造成其它帐号不定期的掉线重拨。具体解决方法是让每个拨号的mac地址不同。

在ROS中解决的方法是添加桥接网卡,修改桥接网卡的MAC地址。然后桥接到不同的VLAN中,在添加拨号时选择bridge的网卡为接口。

在LINUX中添加桥接网卡需要安装brctl工具

安装方式yum install  bridge-utils

网上介绍可以使用brctl命令进行添加桥接网卡,但这样非常的麻烦。每次都需要运行命令,所以这里我们可以写入配置文件。/etc/sysconfig/network-scripts/在这个目录下面复制cpifcfg-eth0 ifcfg-br0修改br0文件:

DEVICE=br0

TYPE=Bridge

ONBOOT=yes

NM_CONTROLLED=yes

BOOTPROTO=none

DELAY=0

这样就添加了一个桥接网卡。有多少个ADSL线路就添加多少个桥接网卡。

要正常使用还需要添加vlan信息

还是一样复制网卡信息cp  ifcfg-eth0 ifcfg-vlan1001

DEVICE= vlan1001

TYPE=Ethernet

ONBOOT=yes

NM_CONTROLLED=yes

BOOTPROTO=none

PHYSDEV=eth0

BRIDGE=br0

VLAN=yes

VLAN_NAME_TYPE=VLAN_PLUS_VID_NO_PAD

然后重新启动网络就行了,service network restart

这里设置完以后ifconfig查看mac地址全是eth0的地址。所以需要使用ip link set dev br0 address 78:2B:CB:4B:1D:A8命令对桥接的网卡地址和vlan接口地址进行更改。实际测试中,需要br网卡地址和vlan网卡地址一样才能拨号。

然后拨号的配置文件中填写的绑定网卡信息为桥接网卡的名字br0….

添加静态路由:

/etc/sysconfig/network-scripts/route-interface

如为网卡 eth1 配置路由,则建立 route-eth1 文件:

[root@localhost ~]# cat/etc/sysconfig/network-scripts/route-eth1

10.0.0.0/8     via 10.20.101.1 dev eth1

192.168.0.0/16 via 10.20.101.1 dev eth1

格式:

需要路由的网段 via  下一跳网关IPdev  与下一跳网关相连的网卡

之后重启系统、重启network服务、ifdown && ifup 网卡,都将 自动生成该路由。

  • NAT日志记录

在一些情况下需要记录nat日志用于日后的审查、或者审计等。所以开启日志也是必要的,可以配置专门的日志服务器来做这事。

启用日志记录功能需要先配置一下rsyslog服务:

配置/etc/rsyslog.conf

kern.=info                                              /var/log/firewall.log

这里设置的日志级别为info

因为日志中没有NAT后的地址信息,所以需要把出口接口的IP地址也写入日志。需要如下方式。

记录UDP数据包,应为数据是没有链接状态的,UDP包的传输一定是一出一进。所以只需要记录一个方向的数据包就行了。这里指定了-i参数限定了从本地网络进入的数据包才记录。

iptables -A FORWARD -i eth1 -p udp !--dport 53 -j LOG --log-level info --log-prefix "OUT=`ifconfig eth0  | grep "inet addr" | sed 's/^.*addr://g'| sed 's/ .*//g'` "

为了减少NAT日志的大小,可以对TCP链接进行区分。

iptables -A FORWARD -i eth1 -p tcp--tcp-flags ALL SYN  -j LOG --log-levelinfo --log-prefix "ORC=`ifconfig eth0 | grep "inet addr" | sed 's/^.*addr://g' | sed 's/ .*//g'` "

使用上面的命令确定tcp链接的第一个包。

iptables -A FORWARD -i eth1 -p tcp--tcp-flags ALL FIN,ACK  -j LOG--log-level info --log-prefix "ORC=`ifconfig eth0  | grep "inet addr" | sed's/^.*addr://g' | sed 's/ .*//g'` "

使用上面的命令确定tcp链接的最后一个结束包。

命令解释:

--tcp-flags中有SYN ACK FIN RST URGPSH ALL这些选项。

SYN表示发送包,ACK表示确认,FIN表示结束,RST表示重置链接,URG表示紧急数据包,PSH表示发送数据,ALL表示所有前面的选项。

在配置时需要两个参数,第一个参数指定范围。第二个参数确定匹配的位置。

比如上面的信息表示指定所有范围,检查SYN位置为1的数据包。

当然你可以把第一个和第二个参数写成一样也能达到目的。

其它类型应该如何设置可以抓包查看相应的字段值。


附件:http://down.51cto.com/data/2367704
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息