天天看點

linux政策路由iproute2

政策性路由

  政策性是指對于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工具包。這個軟體包是由Alexey Kuznetsov開發的,軟體包所在的主要網址為ftp://ftp.inr.ac.ru/ip-routing/。

這裡簡單介紹政策性路由的配置方法,以便能更好了解第二部分的内容。詳細的使用方法請參考Alexey Kuznetsov寫的 ip-cfref文檔。政策性路由的配置主要包括接口位址的配置、路由的配置、規則的配置。

接口位址的配置IP Addr

對于接口的配置可以用下面的指令進行:

Usage: ip addr [ add | del ] IFADDR dev STRING

  例如:

router># ip addr add 192.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 list SELECTOR ip route { change | del | add | append | replace | monitor } ROUTE

  如果想檢視路由表的内容,可以通過指令:

  ip route list table table_number

  對于路由的操作包括change、del、add 、append 、replace 、 monitor這些。例如添加路由可以用:

router># ip route add 0/0 via 192.168.0.4 table main router># ip route add 192.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 rule list 0: from all lookup local 32766: from all lookup main 32767: from all lookup default <code>   規則0,它是優先級别最高的規則,規則規定,所有的包,都必須首先使用local表(254)進行路由。本規則不能被更改和删除。   規則32766,規定所有的包,使用表main進行路由。本規則可以被更改和删除。   規則32767,規定所有的包,使用表default進行路由。本規則可以被更改和删除。   在預設情況下進行路由時,首先會根據規則0在本地路由表裡尋找路由,如果目的位址是本網絡,或是廣播位址的話,在這裡就可以找到合适的路由;如果路由失敗,就會比對下一個不空的規則,在這裡隻有32766規則,在這裡将會在主路由表裡尋找路由;如果失敗,就會比對32767規則,即尋找預設路由表。如果失敗,路由将失敗。重這裡可以看出,政策性路由是往前相容的。   還可以添加規則: <code> router># ip rule add [from 0/0] table 1 pref 32800 router >#ip rule add from 192.168.3.112/32 [tos 0x10] table 2 pref 1500 prohibit

  第一條指令将向規則鍊增加一條規則,規則比對的對象是所有的資料包,動作是選用路由表1的路由,這條規則的優先級是32800。

  第二條指令将向規則鍊增加一條規則,規則比對的對象是IP為192.168.3.112,tos等于0x10的包,使用路由表2,這條規則的優先級是1500,動作是prohibit。添加以後,我們可以看看系統規則的變化。

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 lookup default 32800: from all lookup 1

  上面的規則是以源位址為關鍵字,作為是否比對的依據的。除了源位址外,還可以用以下的資訊:

  From -- 源位址

  To -- 目的位址(這裡是選擇規則時使用,查找路由表時也使用)

  Tos -- IP標頭的TOS(type of sevice)域

  Dev -- 實體接口

  Fwmark -- 防火牆參數

  采取的動作除了指定表,還可以指定下面的動作:

   Table 指明所使用的表

   Nat 透明網關

   Action prohibit 丢棄該包,并發送 COMM.ADM.PROHIITED的ICMP資訊

   Reject 單純丢棄該包

   Unreachable丢棄該包, 并發送 NET UNREACHABLE的ICMP資訊

政策性路由的應用

  基于源位址選路( Source-Sensitive Routing)

  如果一個網絡通過兩條線路接入網際網路,一條是比較快的ADSL,另外一條是比較慢的普通的數據機。這樣的話,網絡管理者既可以提供無差别的路由服務,也可以根據源位址的不同,使一些特定的位址使用較快的線路,而普通使用者則使用較慢的線路,即基于源址的選路。

  根據服務級别選路(Quality of Service)

  網絡管理者可以根據IP報頭的服務級别域,對于不同的服務要求可以分别對待對于傳送速率、吞吐量以及可靠性的有不同要求的資料報根據網絡的狀況進行不同的路由。

  節省費用的應用

  網絡管理者可以根據通信的狀況,讓一些比較大的陣發性通信使用一些帶寬比較高但是比較貴的路徑一段短的時間,然後讓基本的通信繼續使用原來比較便宜的基本線路。例如,管理者知道,某一台主機與一個特定的位址通信通常是伴随着大量的陣發性通信的,那麼網絡管理者可以安排一些政策,使得這些主機使用特别的路由,這些路由是按需撥号,帶寬比較高的線路,通信完成以後就停止使用,而普通的通信則不受影響。這樣既提高網絡的性能,又能節省費用。

  負載平衡(Load Sharing)

  根據網絡交通的特征,網絡管理者可以在不同的路徑之間配置設定負荷實作負載平衡。

Linux下政策性路由的實作--RPDB(Routing Policy DataBase)

  在Linux下,政策性路由是由RPDB實作的。對于RPDB的内部機制的了解,可以加深對于政策性路由使用的了解。這裡分析的是linux 2.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_lookup fib_hash.c 269 插入路由到路由表 fn_hash_insert fib_hash.c 341 删除路由表的路由 fn_hash_delete fn_hash_dump fib_hash.c 433 fib_hash.c 614 更新路由表的路由 fn_hash_flush fib_hash.c 729 顯示路由表的路由資訊 fn_hash_get_info fib_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.c 286;" 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_rule local_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_rule main_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_rule default_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(const struct 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 321 read_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 #ifdef CONFIG_IP_ROUTE_TOS 326 (r->r_tos && r->r_tos != key->tos) || 327 #endif 328 #ifdef CONFIG_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: /*動作nat ADDRESS*/ 338 policy = r; 339 break; 340 case RTN_UNREACHABLE: /*動作unreachable*/ 341 read_unlock(&fib_rules_lock); 342 return -ENETUNREACH; 343 default: 344 case RTN_BLACKHOLE:/* 動作reject */ 345 read_unlock(&fib_rules_lock); 346 return -EINVAL; 347 case RTN_PROHIBIT:/* 動作prohibit */ 348 read_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) 358 atomic_inc(&policy->r_clntref); 359 read_unlock(&fib_rules_lock); 360 return 0; 361 } 362 if (err < 0 && err != -EAGAIN) {/*路由失敗*/ 363 read_unlock(&fib_rules_lock); 364 return err; 365 } 366 } 368 read_unlock(&fib_rules_lock); 369 return -ENETUNREACH; 370 }

  上面的這段代碼的思路是非常的清晰的。首先程式從優先級高到低掃描所有的規則,如果規則比對,處理該規則的動作。如果是普通的路由尋址或者是nat位址轉換的換,首先從規則得到路由表,然後對該路由表進行操作。這樣RPDB終于清晰的顯現出來了。

IP層路由适配(IP route)

  路由表以及規則組成的系統,可以完成路由的管理以及查找的工作,但是為了使得IP層的路由工作更加的高效,linux的路由體系裡,route.c裡完成大多數IP層與RPDB的适配工作,以及路由緩沖(route cache)的功能。

調用接口

  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.c 1984; ip_route_output_slow route.c 1690;"

接收路由接口

  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.c 1312;" f ip_route_input route.c 1622;" f

cache

繼續閱讀