Linux 策略路由
基于策略的路由比传统路由在功能上更强大,使用更灵活,它使网络管理员不仅能够根据目的地址而且能够根据报文大小、应用或IP源地址来选择转发路径...
#/etc/iproute2/rt_tables 此文件存有linux 系统路由表默认表有255254 253三张表
255 local 本地路由表 存有本地接口地址,广播地址,已及NAT地址.
local表由系统自动维护..管理员不能操作此表...
254 main 主路由表 传统路由表,ip route若没指定表亦操作表254.一般存所有的路由..
注:平时用ip ro sh查看的亦是此表设置的路由.
253 default 默认路由表一般存放默认路由...
注:rt_tables文件中表以数字来区分表0保留最多支持255张表
路由表的查看可有以下二种方法:
#ip route list table table_number
#ip route list table table_name
路由表添加完毕,即可在策略路由表内添加路由例:
#ip route add 192.168.1.0/24 dev eth0 via192.168.1.66 realm 4
注:发往子网192.168.1.0/24的数据包通过分类4转发配合tc使用,后文有介绍讲解...
#ip route add default via 192.168.1.1table int1
#ip route add 192.168.1.0/24 via192.168.1.1 table int2
#ip route add 172.16.0.2/16 via172.16.0.1 table int3
注:各路由表中应当指明默认路由,尽量不回查路由表.路由添加完毕,即可在路由规则中应用..
#ip rule ls 显示路由规则
0: from all lookup local
32766: from all lookup main
32767: from all lookup default
进行路由时,正是根据路由规则来进行匹配,按优先级(pref后数值)从高到低匹配,直到找到合适的规则.所以在应用中配置默认路由是必要的..
策略路由一般手工添加路由表,路由表的添加只需编辑rt_tables文件,规定表序号,表名即可..
ip rule规则添加示例:
#ip rule add from 192.168.1.112/32 [tos0x10] table test2 pref 999 prohibit
#ip rule add to 192.168.1.2 pref 1000table test1
#ip rule add from 192.168.1.0/24 pref1001 table test1
#ip rule add [from 0/0] table test1 pref1003
#iprule add fwmark 1 pref 1002 table test2 此句型配合iptables-t mangle应用.如先对数据
#################################################################################################################################
Linux策略性路由应用及深入分析
策略性路由
策略性是指对于IP包的路由是以网络管理员根据需要定下的一些策略为主要依据进行路由的。例如我们可以有这样的策略:“所有来直自网A的包,选择X路径;其他选择Y路径”,或者是“所有TOS为A的包选择路径F;其他选者路径K”。
Cisco 的网络操作系统 (Cisco IOS) 从11.0开始就采用新的策略性路由机制。而Linux是在内核2.1开始采用策略性路由机制的。策略性路由机制与传统的路由算法相比主要是引入了多路由表以及规则的概念。
多路由表(multiple Routing Tables)
传统的路由算法是仅使用一张路由表的。但是在有些情形底下,我们是需要使用多路由表的。例如一个子网通过一个路由器与外界相连,路由器与外界有两条线路相连,其中一条的速度比较快,一条的速度比较慢。对于子网内的大多数用户来说对速度并没有特殊的要求,所以可以让他们用比较慢的路由;但是子网内有一些 特殊的用户却是对速度的要求比较苛刻,所以他们需要使用速度比较快的路由。如果使用一张路由表上述要求是无法实现的,而如果根据源地址或其它参数,对不同的用户使用不同的路由表,这样就可以大大提高路由器的性能。
规则(rule)
规则是策略性的关键性的新的概念。我们可以用自然语言这样描述规则,例如我门可以指定这样的规则:
规则一:“所有来自192.16.152.24的IP包,使用路由表10, 本规则的优先级别是1500”
规则二:“所有的包,使用路由表253,本规则的优先级别是32767”
我们可以看到,规则包含3个要素:
什么样的包,将应用本规则(所谓的SELECTOR,可能是filter更能反映其作用);
符合本规则的包将对其采取什么动作(ACTION),例如用那个表;
本规则的优先级别。优先级别越高的规则越先匹配(数值越小优先级别越高)。
策略性路由的配置方法
传统的linux下配置路由的工具是route,而实现策略性路由配置的工具是iproute2工具包。这个软件包是由AlexeyKuznetsov开发的,软件包所在的主要网址为ftp://ftp.inr.ac.ru/ip-routing/。
这里简单介绍策略性路由的配置方法,以便能更好理解第二部分的内容。详细的使用方法请参考AlexeyKuznetsov写的 ip-cfref文档。策略性路由的配置主要包括接口地址的配置、路由的配置、规则的配置。
接口地址的配置IP Addr
对于接口的配置可以用下面的命令进行:
Usage: ip addr [ add | del ]IFADDR dev STRING
例如:
router># ip addr add192.168.0.1/24 broadcast 192.168.0.255 label eth0 dev eth0
上面表示,给接口eth0赋予地址192.168.0.1 掩码是255.255.255.0(24代表掩码中1的个数),广播地址是192.168.0.255
路由的配置IP Route
Linux最多可以支持255张路由表,其中有3张表是内置的:
表255 本地路由表(Local table) 本地接口地址,广播地址,已及NAT地址都放在这个表。该路由表由系统自动维护,管理员不能直接修改。
表254 主路由表(Main table) 如果没有指明路由所属的表,所有的路由都默认都放在这个表里,一般来说,旧的路由工具(如route)所添加的路由都会加到这个表。一般是普通的路由。
表253 默认路由表 (Default table) 一般来说默认的路由都放在这张表,但是如果特别指明放的也可以是所有的网关路由。
表 0 保留
路由配置命令的格式如下:
Usage: ip route listSELECTOR
ip route { change | del |add | append | replace | monitor } ROUTE
如果想查看路由表的内容,可以通过命令:
ip route list tabletable_number
对于路由的操作包括change、del、add 、append、replace 、 monitor这些。例如添加路由可以用:
router># ip route add 0/0via 192.168.0.4 table main
router># ip route add192.168.3.0/24 via 192.168.0.3 table 1
第一条命令是向主路由表(main table)即表254添加一条路由,路由的内容是设置192.168.0.4成为网关。
第二条命令代表向路由表1添加一条路由,子网192.168.3.0(子网掩码是255.255.255.0)的网关是192.168.0.3。
在多路由表的路由体系里,所有的路由的操作,例如网路由表添加路由,或者在路由表里寻找特定的路由,需要指明要操作的路由表,所有没有指明路由表,默认是对主路由表(表254)进行操作。而在单表体系里,路由的操作是不用指明路由表的。
规则的配置IP Rule
在Linux里,总共可以定义 个优先级的规则,一个优先级别只能有一条规则,即理论上总共可以有条规则。其中有3个规则是默认的。命令用法如下:
Usage: ip rule [ list | add| del ] SELECTOR ACTION
SELECTOR := [ from PREFIX ][ to PREFIX ] [ tos TOS ]
[ dev STRING ] [ pref NUMBER]
ACTION := [ table TABLE_ID ][ nat ADDRESS ]
[ prohibit | reject |unreachable ]
[ flowid CLASSID ]
TABLE_ID := [ local | main |default | new | NUMBER
首先我们可以看看路由表默认的所有规则:
root@netmonster# ip rulelist
0: from all lookup local
32766: from all lookup main
32767: from all lookupdefault
规则0,它是优先级别最高的规则,规则规定,所有的包,都必须首先使用local表(254)进行路由。本规则不能被更改和删除。
规则32766,规定所有的包,使用表main进行路由。本规则可以被更改和删除。
规则32767,规定所有的包,使用表default进行路由。本规则可以被更改和删除。
在默认情况下进行路由时,首先会根据规则0在本地路由表里寻找路由,如果目的地址是本网络,或是广播地址的话,在这里就可以找到合适的路由;如果路由失败,就会匹配下一个不空的规则,在这里只有32766规则,在这里将会在主路由表里寻找路由;如果失败,就会匹配32767规则,即寻找默认路由表。如 果失败,路由将失败。重这里可以看出,策略性路由是往前兼容的。
还可以添加规则:
router># ip rule add[from 0/0] table 1 pref 32800
router >#ip rule add from192.168.3.112/32 [tos 0x10] table 2 pref 1500 prohibit
第一条命令将向规则链增加一条规则,规则匹配的对象是所有的数据包,动作是选用路由表1的路由,这条规则的优先级是32800。
第二条命令将向规则链增加一条规则,规则匹配的对象是IP为192.168.3.112,tos等于0x10的包,使用路由表2,这条规则的优先级是1500,动作是。添加以后,我们可以看看系统规则的变化。
router># ip rule
0: from all lookup local
1500 from 192.168.3.112/32[tos 0x10] lookup 2
32766: from all lookup main
32767: from all lookupdefault
32800: from all lookup 1
上面的规则是以源地址为关键字,作为是否匹配的依据的。除了源地址外,还可以用以下的信息:
From -- 源地址
To -- 目的地址(这里是选择规则时使用,查找路由表时也使用)
Tos -- IP包头的TOS(type ofsevice)域
Dev -- 物理接口
Fwmark -- 防火墙参数
采取的动作除了指定表,还可以指定下面的动作:
Table 指明所使用的表
Nat 透明网关
Action prohibit 丢弃该包,并发送COMM.ADM.PROHIITED的ICMP信息
Reject 单纯丢弃该包
Unreachable丢弃该包, 并发送 NETUNREACHABLE的ICMP信息
策略性路由的应用
基于源地址选路( Source-SensitiveRouting)
如果一个网络通过两条线路接入互联网,一条是比较快的ADSL,另外一条是比较慢的普通的调制解调器。这样的话,网络管理员既可以提供无差别的路由服务,也可以根据源地址的不同,使一些特定的地址使用较快的线路,而普通用户则使用较慢的线路,即基于源址的选路。
根据服务级别选路(Quality of Service)
网络管理员可以根据IP报头的服务级别域,对于不同的服务要求可以分别对待对于传送速率、吞吐量以及可靠性的有不同要求的数据报根据网络的状况进行不同的路由。
节省费用的应用
网络管理员可以根据通信的状况,让一些比较大的阵发性通信使用一些带宽比较高但是比较贵的路径一段短的时间,然后让基本的通信继续使用原来比较便宜的基本线路。例如,管理员知道,某一台主机与一个特定的地址通信通常是伴随着大量的阵发性通信的,那么网络管理员可以安排一些策略,使得这些主机使用特别的 路由,这些路由是按需拨号,带宽比较高的线路,通信完成以后就停止使用,而普通的通信则不受影响。这样既提高网络的性能,又能节省费用。
负载平衡(Load Sharing)
根据网络交通的特征,网络管理员可以在不同的路径之间分配负荷实现负载平衡。
Linux下策略性路由的实现--RPDB(Routing PolicyDataBase)
在Linux下,策略性路由是由RPDB实现的。对于RPDB的内部机制的理解,可以加深对于策略性路由使用的理解。这里分析的是linux2.4.18的RPDB实现的细节。主要的实现文件包括:
fib_hash.c
fib_rules.c
fib_sematic
fib_frontend.c
route.c
RDPB主要由多路由表和规则组成。路由表以及对其的操作和其对外的接口是整个RPDB的核心部分。路由表主要由table,zone,node这些主要的数据结构构成。对路由表的操作主要包含物理的操作以及语义的操作。路由表除了向IP层提供路由寻找的接口以外还必须与几个元素提供接口:与用户的接 口(即更改路由)、proc的接口、IP层控制接口、以及和硬件的接口(网络接口的改变会导致路由表内容的改变)。处在RDPB的中心的规则,由规则选取表。IP层并不直接使用路由表,而是通过一个路由适配层,路由适配层提供为IP层提供高性能的路由服务。
路由表(Fib Table)
数据结构:
在整个策略性路由的框架里,路由表是最重要的的数据结构,我们在上面以及对路由表的概念和结构进行了清楚的说明。Linux里通过下面这些主要的数据结构进行实现的。
主要的数据结构 作用 位置
struct fib_table 路由表ip_fib.h 116
struct fn_hash 路由表的哈希数据fib_hash.c 104
struct fn_zone zone域fib_hash.c 85
struct fib_node 路由节点fib_hash.c 68
struct fib_info 路由信息ip_fib.h 57
struct fib_result 路由结果ip_fib.h 86
数据结构之间的主要关系如下。路由表由路由表号以及路由表的操作函数指针还有表数据组成。这里需要注意的是,路由表结构里并不直接定义zone域,而是通过一个数据指针指向fn_hash。只有当zone里有数据才会连接到fn_zone_list里。(如图)
系统的所有的路由表由数组变量*fib_tables[RT_TABLE_MAX+1]维护,其中系统定义RT_TABLE_MAX为254,也就是说系统最大的路由表为255张,所有的路由表的操作都是对这个数组进行的。。同时系统还定义了三长路由表*local_table; *main_table。
路由表的操作:
Linux策略路由代码的主要部分是对路由表的操作。对于路由表的操作,物理操作是直观的和易于理解的。对于表的操作不外乎就是添加、删除、更新等的操作。还有一种操作,是所谓的语义操作,语义操作主要是指诸如计算下一条的地址,把节点转换为路由项,寻找指定信息的路由等。
1、物理操作(operation):
路由表的物理操作主要包括如下这些函数:
路由标操作 实现函数 位置
新建路由表
删除路由表
搜索路由 fn_hash_lookupfib_hash.c 269
插入路由到路由表 fn_hash_insertfib_hash.c 341
删除路由表的路由 fn_hash_delete
fn_hash_dump
fib_hash.c 433
fib_hash.c 614
更新路由表的路由 fn_hash_flushfib_hash.c 729
显示路由表的路由信息 fn_hash_get_infofib_hash.c 750
选择默认路由fn_hash_select_default fib_hash.c 842
2、语义操作(semantics operation):
语义操作并不涉及路由表整体框架的理解,而且,函数名也是不言自明的,所以请大家参考fib_semantics.c。
3、接口(front end)
对于路由表接口的理解,关键在于理解那里有
IP
首先是路由表于IP层的接口。路由在目前linux的意义上来说,最主要的还是IP层的路由,所以和IP层的的接口是最主要的接口。和ip层的衔接主要是向IP层提供寻找路由、路由控制、寻找指定ip的接口。
Fil_lookup
ip_rt_ioctl fib_frontend.c286;" f
ip_dev_find 145
Inet
路由表还必须提供配置接口,即用户直接操作路由的接口,例如增加和删除一条路由。当然在策略性路由里,还有规则的添加和删除。
inet_rtm_delroute 351
inet_rtm_newroute 366
inet_check_attr 335
proc
在/proc/net/route里显示路由信息。
fib_get_procinfo
4、网络设备(net dev event)
路由是和硬件关联的,当网络设备启动或关闭的时候,必须通知路由表的管理程序,更新路由表的信息。
fib_disable_ip 567
fib_inetaddr_event 575
fib_netdev_event
5、内部维护( magic)
上面我们提到,本地路由表(local table)的维护是由系统自动进行的。也就是说当用户为硬件设置IP地址等的时候,系统自动在本地路由表里添加本地接口地址以及广播地址。
fib_magic 417
fib_add_ifaddr 459
fib_del_ifaddr 498
Rule
1、数据结构
规则在fib_rules.c的52行里定义为struct fib_rule。而RPDB里所有的路由是保存在101行的变量fib_rules里的,注意这个变量很关键,它掌管着所有的规则,规则的添加和删除都是对这个变量进行的。
2、系统定义规则:
fib_rules被定义以后被赋予了三条默认的规则:默认规则,本地规则以及主规则。
u 本地规则local_rule
94 static struct fib_rulelocal_rule = {
r_next: &main_rule, /*下一条规则是主规则*/
r_clntref: ATOMIC_INIT(2),
r_table: RT_TABLE_LOCAL, /*指向本地路由表*/
r_action: RTN_UNICAST, /*动作是返回路由*/
};
u 主规则main_rule
86 static struct fib_rulemain_rule = {
r_next: &default_rule,/*下一条规则是默认规则*/
r_clntref: ATOMIC_INIT(2),
r_preference: 0x7FFE, /*默认规则的优先级32766*/
r_table: RT_TABLE_MAIN, /*指向主路由表*/
r_action: RTN_UNICAST, /*动作是返回路由*/
};
u 默认规则default rule
79 static struct fib_ruledefault_rule = {
r_clntref: ATOMIC_INIT(2),
r_preference: 0x7FFF,/*默认规则的优先级32767*/
r_table: RT_TABLE_DEFAULT,/*指默认路由表*/
r_action: RTN_UNICAST,/*动作是返回路由*/
};
规则链的链头指向本地规则。
RPDB的中心函数fib_lookup
现在到了讨论RPDB的实现的的中心函数fib_lookup了。RPDB通过提供接口函数fib_lookup,作为寻找路由的入口点,在这里有必要详细讨论这个函数,下面是源代码:,
310 int fib_lookup(conststruct rt_key *key, struct fib_result *res)
311 {
312 int err;
313 struct fib_rule *r,*policy;
314 struct fib_table *tb;
315
316 u32 daddr = key->dst;
317 u32 saddr = key->src;
318
321read_lock(&fib_rules_lock);
322 for (r = fib_rules; r;r=r->r_next) {/*扫描规则链fib_rules里的每一条规则直到匹配为止*/
323 if (((saddr^r->r_src)& r->r_srcmask) ||
324 ((daddr^r->r_dst)& r->r_dstmask) ||
325 #ifdefCONFIG_IP_ROUTE_TOS
326 (r->r_tos &&r->r_tos != key->tos) ||
327 #endif
328 #ifdefCONFIG_IP_ROUTE_FWMARK
329 (r->r_fwmark&& r->r_fwmark != key->fwmark) ||
330 #endif
331 (r->r_ifindex&& r->r_ifindex != key->iif))
332 continue;/*以上为判断规则是否匹配,如果不匹配则扫描下一条规则,否则继续*/
335 switch (r->r_action){/*好了,开始处理动作了*/
336 case RTN_UNICAST:/*没有设置动作*/
337 case RTN_NAT: /*动作natADDRESS*/
338 policy = r;
339 break;
340 case RTN_UNREACHABLE: /*动作unreachable*/
341read_unlock(&fib_rules_lock);
342 return -ENETUNREACH;
343 default:
344 case RTN_BLACKHOLE:/* 动作reject*/
345read_unlock(&fib_rules_lock);
346 return -EINVAL;
347 case RTN_PROHIBIT:/* 动作prohibit*/
348read_unlock(&fib_rules_lock);
349 return -EACCES;
350 }
351 /*选择路由表*/
352 if ((tb =fib_get_table(r->r_table)) == NULL)
353 continue;
/*在路由表里寻找指定的路由*/
354 err =tb->tb_lookup(tb, key, res);
355 if (err == 0) {/*命中目标*/
356 res->r = policy;
357 if (policy)
358atomic_inc(&policy->r_clntref);
359read_unlock(&fib_rules_lock);
360 return 0;
361 }
362 if (err < 0&& err != -EAGAIN) {/*路由失败*/
363read_unlock(&fib_rules_lock);
364 return err;
365 }
366 }
368read_unlock(&fib_rules_lock);
369 return -ENETUNREACH;
370 }
上面的这段代码的思路是非常的清晰的。首先程序从优先级高到低扫描所有的规则,如果规则匹配,处理该规则的动作。如果是普通的路由寻址或者是nat地址转换的换,首先从规则得到路由表,然后对该路由表进行操作。这样RPDB终于清晰的显现出来了。
IP层路由适配(IP route)
路由表以及规则组成的系统,可以完成路由的管理以及查找的工作,但是为了使得IP层的路由工作更加的高效,linux的路由体系里,route.c里完成大多数IP层与RPDB的适配工作,以及路由缓冲(routecache)的功能。
调用接口
IP层的路由接口分为发送路由接口以及接收路由接口:
发送路由接口
IP层在发送数据时如果需要进行路由工作的时候,就会调用ip_route_out函数。这个函数在完成一些键值的简单转换以后,就会调用ip_route_output_key函数,这个函数首先在缓存里寻找路由,如果失败就会调用 ip_route_output_slow,ip_route_output_slow里调用fib_lookup在路由表里寻找路由,如果命中,首先在缓存里添加这个路由,然后返回结果。
ip_route_out route.h
ip_route_output_key route.c1984;
ip_route_output_slow route.c1690;"
接收路由接口
IP层接到一个数据包以后,如果需要进行路由,就调用函数ip_route_input,ip_route_input现在缓存里寻找,如果失败则ip_route_inpu调用ip_route_input_slow, ip_route_input_slow里调用fib_lookup在路由表里寻找路由,如果命中,首先在缓存里添加这个路由,然后返回结果。
ip_route_input_slow route.c1312;" f
ip_route_input route.c1622;" f
cache
路由缓存保存的是最近使用的路由。当IP在路由表进行路由以后,如果命中就会在路由缓存里增加该路由。同时系统还会定时检查路由缓存里的项目是否失效,如果失效则清除。
#################################################################################################################################
Linux环境下基于策略的路由
本文以大量实例为基础介绍了基于Linux2.2的强大路由功能,它提供的路由功能是很多路由器产品都是难以匹敌的,如果考虑到它的免费性,它的性能价格比更是没有任何产品可以相比。
目前在计算机网络中使用的传统路由算法都是根据IP包目的地址进行路由选择.然而在现实应用中经常有这样的需求:进行路由选择时不仅仅根据数据报的目的地址,而且根据数据报的其他一些特性如:源地址、IP协议、传输层端口,甚至是数据包的负载部分内容,这种类型的路由选择被称作基于策略的路由。
在Linux中,从2.1版本的内核开始就实现了对基于策略的路由的支持,它是通过使用路由策略数据库(RPDB,routing policy database)替代传统的、基于目的地址的路由表来实现的。RPDB通过包含的一些规则来选定合适的IP路由。这些规则可能会包含很多各种不同类型的健值(key),因此这些规则没有默认的特定次序,规则查找次序或规则优先级都是由网络或系统管理员设定的。
Linux的RPDB是一个由数字优先级值进行排序的线性规则列表。RPDB能匹配数据报源地址、目的地址、TOS、进入接和fwmark值等。每个路由策略规则由一个选择器和一个动作指示组成。RPDB按照优先级递增的顺序被扫描,RPDB包含的每条规则的选择器被应用于数据报的源地址、目的地址、进入接口、TOS和fwmark值。若数据报匹配该规则对应于该规则的动作被执行。若动作成功返回,则规则输出将是一个有效的路由或是路由查找失败指示;否则查找RPDB的下一条规则。
当选择器和一个数据报匹配成功,会执行哪些动作呢?路由软件的标准动作一般是选择下一跳地址和输出接口,可以称这种动作为“匹配&设置”类型动作。然而Linux采取了更加灵活的方法,在Linux中有多种动作可供选择。默认的动作是查询特定的基于目的地址的路由表。
因此“匹配&设置”动作就成为Linux路由选择的最简单情况。Linux支持多个路由表,每个路由表都包含多条路由信息。也就是Linux的每个路由表都相当于其他操作系统的系统路由表。Linux支持多达255个路由表。(Linux2.2.12 支持255个路由表,255个汇聚域和232个策略规则优先级。
对于Linux2.1/2.2,启动时内核将包含一个由三条策略规则组成的默认的RPDB,察看这些默认规则的一个方法是使用命令来列出系统的所有规则:
root@netmonster ip rule list
0: from all lookup local
32766: from all lookup main
32767: from all lookup default
下面的默认规则在对于理解启动复杂路由系统是非常重要的。
首先是最高级别的优先级规则,规则策略0:
规则0: 优先级 0 选择器 = 匹配任何数据报
动作=察看本地路由表(routing tablelocal),ID为255。
local表是保留路由表,包含了到本地和广播地址的路由。规则0是特殊的规则,不可被删除或修改。
规则 32766: 优先级 32766 选择器 = 匹配所有数据报
动作 = 察看主路由表(routing tablemain), ID为254。
main路由表是默认的标准路由表,其包含所有非策略路由,main表是存放旧的路由命令(route命令)创建的路由。而且任何由iproute命令创建的没有明确指定路由表的路由都被加入到该路由表中。该规则不能被删除和被其他规则覆盖。
规则 32767: 优先级 32767 选择器 = 匹配所有数据报
动作 = 察看默认路由表(routing tabledefault),ID为253。
default路由表是空的,为最后处理(post-processing)所预留,若前面的默认规则没有选择该数据报时保留用作最后的处理。该规则可以被删除。
不要将路由表和规则混淆,规则是指向路由表的。也许会出现多个规则指向同一个路由表,而有些路由表可能并不被任何规则指向。如果删除了指向某个路由表的所有规则,则该表将不发生作用,但是表将仍然存在。一个路由表只有在其中包含的所有路由信息被删除才会消失。
前面提到,Linux策略规对应的动作除了指向一个路由表以外还能是若干种不同的动作。当创建一个策略规则,有如下类型的动作可以选择:
unicast -- 在该规则指向的路由表中进行标准的路由查找。当一个路由表被指定,这是默认的动作。
blackhole -- 规则动作将仅仅直接丢弃该数据报。
unreachable -- 规则动作产生一条网络不可达错误信息,一个类型为3,代码为0的ICMP消息被返回给发送者。
prohibit -- 规则动作产生一个通信被禁止的错误消息,一个类型为3,代码为13的ICMP消息被返回给发送者。
其他类型的动作也可以被使用,但是都和策略路由没有关系。它们被用来在内核中实现其他高级流控制和数据报操作。因为只有一个工具命令:IP,所有的这些类型都是可运用于该命令,但我们仅仅使用和上面有关的部分,可以是返回一条路由或其他若干个动作。
在解释示例以前,首先看看ip工具命令的语法。ip命令可以用在很多地方,这里仅仅讨论和策略路由相关的部分。都是由root在命令行直接运行的。
首先,看ip addr命令语法:
root@netmonster# ip addr help
Usage: ip addr {add|del}IFADDR dev STRING
ip addr {show|flush} [ devSTRING ] [ scope SCOPE-ID ]
[ to PREFIX ] [ FLAG-LIST ] [label PATTERN ]
IFADDR := PREFIX | ADDR peerPREFIX
[ broadcast ADDR ] [ anycastADDR ]
[ label STRING ] [ scopeSCOPE-ID ]
SCOPE-ID := [ host | link |global | NUMBER ]
FLAG-LIST := [ FLAG-LIST ]FLAG
FLAG := [ permanent | dynamic| secondary | primary |
tentative | deprecated ]
Example - ip addr add192.168.1.1/24 dev eth0
该命令将添加IP地址192.168.2.2/24到eth0网卡上.
下面看看ip route命令:
root@netmonster# ip route help
Usage: ip route { list | flush} SELECTOR
ip route get ADDRESS [ fromADDRESS iif STRING ]
[ oif STRING ] [ tos TOS ]
ip route { add | del | replace| change | append | replace |
monitor} ROUTE
SELECTOR := [ root PREFIX ] [match PREFIX ] [ exact PREFIX ]
[ table TABLE_ID ] [ protoRTPROTO ]
[ type TYPE ] [ scope SCOPE ]
ROUTE := NODE_SPEC [ INFO_SPEC]
NODE_SPEC := [ TYPE ] PREFIX [tos TOS ]
[ table TABLE_ID ] [ protoRTPROTO ]
[ scope SCOPE ] [ metricMETRIC ]
INFO_SPEC := NH OPTIONS FLAGS[ nexthop NH ]...
NH := [ via ADDRESS ] [ devSTRING ] [ weight NUMBER ] NHFLAGS
OPTIONS := FLAGS [ mtu NUMBER] [ advmss NUMBER ]
[ rtt NUMBER ] [ rttvar NUMBER]
[ window NUMBER] [ cwnd NUMBER] [ ssthresh REALM ]
[ realms REALM ]
TYPE := [ unicast | local |broadcast | multicast | throw |
unreachable | prohibit |blackhole | nat ]
TABLE_ID := [ local | main |default | all | NUMBER ]
SCOPE := [ host | link |global | NUMBER ]
FLAGS := [ equalize ]
NHFLAGS := [ onlink |pervasive ]
RTPROTO := [ kernel | boot |static | NUMBER ]
Example - ip route add192.168.2.0/24 via 192.168.1.254
该示例将添加一条通过192.168.1.254到网络192.168.2.0/24的路由。
最后,看看ip rule命令:
root@netmonster# ip rule help
Usage: ip rule [ list | add |del ] SELECTOR ACTION
SELECTOR := [ from PREFIX ] [to PREFIX ] [ tos TOS ] [ fwmark FWMARK ]
[ dev STRING ] [ pref NUMBER ]
ACTION := [ table TABLE_ID ] [nat ADDRESS ]
[ prohibit | reject |unreachable ]
[ realms [SRCREALM/]DSTREALM ]
TABLE_ID := [ local | main |default | NUMBER ]
Example - ip rule add from192.168.2.0/24 prio 32777 reject
该命令将丢弃源地址属于192.168.2.0/24网络的所有数据报。
在讨论了命令语法以后,下面是一些上面命令的示例。
例 1:拒绝访问Internet
假设有一个防火墙连接本地局域网和Internet,你希望禁止局域网的一个子网访问Internet。当然这可以通过Linux数据报过滤防火墙来实现。但是下面我们将介绍另外一种实现方法。首先我们来假设有如下的网络配置:
内部网络地址 192.168.0.0/16
被拒绝访问的子网 192.168.2.0/24
当前主路由表(Routing Table Main,表254):
root@netmonster# ip route listtable 254
default via 192.168.254.254dev eth0 proto static
下面将针对该子网创建一条策略路由规则:
ip rule add from192.168.2.0/24 priority 5000 prohibit
现在任何从192.168.2.0/24子网发送来的数据报将得到一个类型为3,代码为13的ICMP消息,同时该数据报将被丢弃。
应该注意的是,在运行任何这些命令都需要发送“ip route flush cache"命令来刷新路由缓冲,否则命令在一段时间以后才会生效,这段时间的长短依赖于路由表结构的大小和负载。
将上面的例子需要的命令放在一起就如下所示:
ip rule del priority 5000
ip rule add from192.168.2.0/24 priority 5000 prohibit
ip route flush cache
这个命令流通过首先删除5000号规则来确保当前系统没有该规则然后再添加新的5000号规则。如果系统本来不存在5000号的规则则会返回一个错误信息。然后添加5000号规则并通过清空运行时的路由缓存来重置RPDB,则新规则将马上产生作用。
为了完全理解基于策略路由的使用,就需要学会使用Linux多路由表和IP地址,这包括多个方面的知识,下面通过示例来加以说明。
当获得ip工具,你可能会注意到在发布中有一个名为etc的子目录,其中有一个名为iproute2的子目录。应该拷贝该子目录到/etc目录下或在/etc目录下创建iproute2子目录。该目录包含用来命名路由表及策略路由结构的其他方面的文件。在该目录中创建rt_tables文件,其中示例文件一般已经包含了某些内容,并为路由表1提供了示例名。
下面首先编辑该文件来创建若干在下面的示例中使用的表:
# reserved values
#
255 local
254 main
253 default
0 unspec
#
# local
#
1 goodnet1
2 goodnet2
3 badnet1
4 badnet2
5 internet
可以看到通过为路由表命名,就可以使用表数字ID或表名来引用路由表。例如下面两个命令将对同一个路由表进行操作:
ip route list table 1
ip route list table goodnet1
通过表名可以更好的理解在对哪个路由表进行操作。
例 2:创建多个路由表
如果已经创建了如上所示的rt_table信息,可以通过下面的命令来察看各个路由表的内容:
ip route list table<name>
当然即使没有定义rt_table文件同样可以使用数字来引用所有的0-255个路由表,因为它们都是存在的,只是其中大部分没有路由信息数据罢了。在上面的例子中通过rt_table文件来将表1定义为goodnet1。
下面的例子是向定义的每个路由表都添加一条路由:
ip route add 10.10.10.0/24 via192.168.1.2 table goodnet1
ip route add 10.10.11.0/24 via192.168.1.2 table goodnet2
ip route add 10.10.12.0/24 via192.168.1.2 table badnet1
ip route add 10.10.13.0/24 via192.168.1.2 table badnet1
ip route add default via192.168.1.254 table internet
然后再通过ip rouite list talbe
<name>
命令就可以察看路由表中的各个路由。
例3:建立多个IP地址
这里的多个IP地址并不同于IP别名,在Linux2.1及更高版本中已经反对使用":#"的IP别名方式。而应该用新的方式来使用多IP地址。
假设eth0输出接口应该具有三个不同的IP地址,其中的两个应该属于同一个子网,但是应该被独立地设置,示例同样说明了在Linux2.2及以上版本的关闭自动路由添加的方法。在Linux2.2及以上版本内核的系统中,当为一个接口赋予一个IP地址时,内核将自动为该IP地址属于的网络在路由表中添加一条对应的路由。而由于这里将为同一个接口赋予属于同一个子网的两个不同的IP,所以在添加IP地址时不希望添加路由,否则会造成路由冲突,因此就需要添加该地址为一个主机地址。只需要设置该地址时指定完全主机地址掩码,然后手工添加必要的路由。
为接口设置如下地址:
192.168.1.1
192.168.1.128
192.168.3.1
在添加192.168.1.0/24的两个地址时需要关闭自动路由添加,而允许对192.168.3.0/24时允许自动路由添加功能。
ip addr add 192.168.1.1/32 deveth0
ip addr add 192.168.1.128/32dev eth0
ip addr add 192.168.3.1/24 deveth0
这时候如果察看主路由表则会发现内核只为网络192.168.3.0/24添加了路由表,而没有为网络192.168.1.0/24添加路由。
通过ip addr命令可以察看系统的所有IP地址信息:
root@netmonster# ip addr
1: lo: <LOOPBACK,UP> mtu3924 qdisc noqueue
link/loopback00:00:00:00:00:00 brd 00:00:00:00:00:00
inet 127.0.0.1/8 brd127.255.255.255 scope host lo
2: eth0:<BROADCAST,MULTICAST,UP> mtu 1500 qdisc pfifo_fast qlen 100
link/ether 00:00:49:61:32:bcbrd ff:ff:ff:ff:ff:ff
inet 192.168.1.1/32 scopeglobal eth0
inet 192.168.1.128/32 scopeglobal eth0
inet 192.168.3.1/24 scopeglobal eth0
下面我们将讨论几个更加复杂的例子。
Linux内核路由代码最强大的特色就是基于策略的路由和使用多地址、多路由表的结合使用。下面的示例讨论的是一个充当路由器的连接三个不同的网络的系统。
参考文章开头的图片,可以看到核心系统的外部接口连接了三个外部网络。每个网络都有自己的路由器和自己的IP地址空间。但是其中的两个地址空间是重叠的,因此增加了情况的复杂性。这里我们设置如下规则的路由表来实现互联:
从任何内部网络到Inetent的数据流都是允许的。
从内部网B到网络A的数据流是允许的。
从内部网A到网络C的数据流是允许的。
内部网A的地址为33-62的主机允许访问网络A。
内部网B的地址为65-78的主机允许访问网络C。
首先,配置两个外部IP地址-在DMZ以太网接口eth0的两个地址:
ip addr add 10.254.254.2/30dev eth0
ip addr add 172.17.1.128/24dev eth0
下一步将讨论创建哪些路由表,解决这个问题的最好办法是想到基于策略的路由能实现根据源地址来决定使用哪个路由表。策略规则具有划分内部网络的能力,因此首先可以在路由表中添加基于目的的路由,在示例中我们将使用前面创建的新的路由表。
当在路由表中添加路由时下面的方法有助于澄清应该采取的步骤。假设你配置的是只有两个接口的路由器,外接口直接连接Interet出口路由器,而内接口直接连接内部网络。配置这样的路由器是非常简单的,为了说明我们这里在表1-goodnet1这个路由表进行操作:
ip route add 10.10.0.0/16 via10.254.254.2 table goodnet1 proto static
ip route add default via172.17.1.254 table goodnet1 proto static
对于goodnet2进行配置则是:
ip route add 172.18.0.0/16 via172.17.1.1 table goodnet2 proto static
ip route add default via172.17.1.254 table goodnet2 proto static
可以看到对三个目标网络只需要两个路由表,这里对于连接Internet的默认路由在两个表中都进行了设置。为什么不将默认路由存放在第三个路由表中呢?首先应该考虑到规则和路由表之间的交互,而且规则是实现对基于策略路由的定义。多条规则可以指向同一个路由表。然而,一旦进入某个路由表以后,则只能是匹配一条路由或返回一条路由链。如果匹配到一条规则则就认为你已经拥有一条正确的策略匹配,则该规则指向的路由表包含了该数据报所有的路由可能。
如果有三个路由表,则需要添加一条规则来察看数据报目的。而检查数据报目的地址是标准路由的功能。为什么要对每种源、目的的组合都需要一个规则呢?通过使用路由表,可以实现定义尽量少的规则来达到目的。当然系统的灵活性允许通过多条方法来实现这样的路由。可以根据在自己的喜好来决定那种方案最适合:
ip rule add from192.168.1.32/27 to 172.18.0.0/16 pref 15000 table goodnet1
ip rule add from192.168.2.64/28 to 10.10.0.0/16 pref 15001 table goodnet2
ip rule add from192.168.1.0/24 pref 15002 table goodnet1
ip rule add from192.168.2.0/24 pref 15003 table goodnet2
上面的例子中使用了优先级参数设定来定义数据报匹配规则的顺序。现在来看看当一个数据报从内部网络经过路由系统时会发生什么情况。首先,它会通过优先级为0的规则检查;随后会遇到优先级为15000的规则,若匹配则会被goodnet1这个路由表进行操作,否则将会分别经过15001、15002、15003的规则。它肯定会被15000-15003几个规则中的一个所匹配。
下面为了说明定义路由结构的灵活性,我们将从另外一个角度来解决这个问题。Linux路由器的详细情况如下:
eth0 - DMZ ethernet -addresses: 10.254.254.2/30, 172.17.1.128/24
eth1 - Internal A - addresses:192.168.1.254/24
eth2 - Internal B - addresses:192.168.2.254/24
首先假设重新开始,将重新定义路由和规则,首先编辑/etc/iproute2/rt_tables:
# reserved values
#
255 local
254 main
253 default
0 unspec
#
# Local Tables
#
1 int1
2 int2
创建路由和规则:
ip route add 10.10.0.0/16 via10.254.254.1 table int1 proto static
ip route add throw 0/0 tableint1 proto static
ip route add 172.18.0.0/16 via172.17.1.1 table int2 proto static
ip route add throw 0/0 tableint2 proto static
ip route add 0/0 via172.17.1.254 table main proto static
ip rule add pref 15000 tableint1 iif eth1
ip rule add pref 15001 tableint2 iif eth2
ip rule add pref 15002 to10.10.0.0/16 table int1
ip rule add pref 15003 to172.18.0.0/16 table int2
这些路由和规实施和前面示例一样的操作(仔细研究这些规则和路由并理解它们)。
使用ipchains实现高级策略路由
在指定策略规则时可以使用的一个选项就是允许通过fwmark值来匹配某个规则。fwmark是一个数字标签,数据报过滤工具ipchains能将fwmark值附加给某个数据报。如果你对ipchains并不是很熟悉,你需要首先阅读ipchains-howto.
例 5:简单基于fwmard的策略路由
首先从一个简单的例子开始-利用上面示例中的多路由表,希望实现来自内部网B的、目的端口为80的数据发送到Internet,但是来自内部网A的、目的端口为80的数据则被禁止。首先清空这些路由表:
ip route flush table goodnet1
ip route flush table goodnet2
ip route flush table badnet1
ip route flush table badnet2
ip route flush table internet
ip route flush cache
而目前删除策略规则的方法就是将其一一列出来然后将其手工删除,也就是首先通过iprule list命令将策略规则列出来然后使用ip rule del priority <#>将其删除。但这里假设当前没有任何规则并且路由表也是空的。
为了使用fwmark标记,首先应该指定希望使用ipchains标记的数据报,然后使用标记值来指定一条策略规则来处理该数据报。应该设定ipchians自动将十进制的标记转化为十六进制。iprule希望输入为一个十六值。
首先配置ipchains规则使用合适的值标记输入数据报。假设当前没有其他防火墙规则:
ipchains -I input -p tcp -s192.168.2.0/24 -d 0/0 80 -m 2
ipchains -I input -p tcp -s192.168.1.0/24 -d 0/0 80 -m 16
现在设立策略规则,在上面为内部网A的标记值为十机制的16,下面定义相关的策略(应该注意到策略定义中使用的是十六进制,因此为10):
ip rule add fwmark 2 tablegoodnet1
ip rule add fwmark 10 prohibit
最后为路由表goodnet1定义如下的路由:
ip route add default via172.17.1.254 table goodnet1
关于策略路由的一个常见问题是策略路由和IP伪装之间如何交互,这里我们不对该问题进行深入研究但是通过一个快速的示例来加以说明。需要注意的是在转发链之前对路由表进行查询。这意味着如果使用IP伪装,则路由选择器返回的任何源地址都将被作为进行IP伪装的地址。
使用上面的网络配置,我这里将对到三个网络的连接进行伪装处理,希望从系统中得到如下的输出:
从内部网A到网络C的数据报被伪装为10.254.254.2
从内部网络B到网络A的数据报被伪装为172.17.1.2
内部网到互联网的数据报都被伪装为172.17.1.128
eth0配置有如下地址:
10.254.254.2/30
172.17.1.128/24
因此为了满足条件2,给eth0添加地址:
ip addr add 172.17.1.2/32 deveth0
假设系统被设置为对所有的输出数据报都进行IP伪装。首先清空旧的策略规则,然后创建新的规则如下:
ip route add 10.10.0.0/16 via10.254.254.2 src 10.254.254.2
table goodnet1 proto static
ip route add default via172.17.1.254 src 172.17.1.128
table goodnet1 proto static
ip route add 172.18.0.0/16 via172.17.1.1 src 172.17.1.2
table goodnet2 proto static
ip route add default via172.17.1.254 src 172.17.1.128
table goodnet2 proto static
ip rule add from192.168.1.0/24 pref 15000 table goodnet2
ip rule add from 192.168.2.0/24pref 15001 table goodnet1
例7:综合实例
假设例6中的路由、规则和地址仍然在起作用,我希望实现下面的需求:
Internal A Hosts 33-62 toNetwork A Masq as 172.17.1.3
Internal A Hosts 65-78 to tcpport 80 on Network A Masq as 172.17.1.4
Internal B Hosts 33-62 to tcpport 80 on Network A Deny Access
Internal B Hosts 65-78 to tcpport 80 on Network C Masq as 10.254.254.2
应该记得我们仍然允许例6中的连接性,下面就是解决方案:
ip addr add 172.17.1.3/32 deveth0
ip addr add 172.17.1.4/32 deveth0
ip route del default tablegoodnet1
ip route del default tablegoodnet2
ip route add throw 0/0 tablegoodnet1 proto static
ip route add throw 0/0 tablegoodnet2 proto static
ip route add default via172.17.1.254 src 172.17.1.128
table internet proto static
ip route add 172.18.0.0/16 via172.17.1.1 src 172.17.1.3
table badnet1 proto static
ip route add 172.18.0.0/16 via172.17.1.1 src 172.17.1.4
table badnet2 proto static
ip rule add from192.168.1.32/27 to 172.18.0.0/16 pref 14999 table badnet1
ip rule add fwmark 1 pref14998 table badnet2
ip rule add fwmark 2 pref14997 table goodnet1
ip rule add fwmark 3 pref14996 blackhole
ip rule add pref 15003 tableinternet
ipchains -I input -p tcp -s192.168.1.64/28 -d 172.18.0.0/16 80 -m 1
ipchains -I input -p tcp -s192.168.2.64/28 -d 10.10.0.0/16 80 -m 2
ipchains -I input -p tcp -s192.168.2.32/27 -d 172.18.0.0/16 80 -m 3
其实有很多的有效方法,但是虽然这里使用名为badnet1和badnet2的表,但是名字是没有实际意义的,只是用来引用表3和表4的符号。
总结
希望你通过该文章能享受Linux2.2的强大路由功能,它提供的路由功能是很多路由器产品都是难以匹敌的,如果考虑到它的免费性,它的性能价格比更是没有任何产品可以相比。例如例7中的例子可以很好的运行在只有16M内存的486/33的机器上。
#################################################################################################################################
#################################################################################################################################
#################################################################################################################################