摘要
本文以大量實例為基礎介紹了基於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個路由表。(Linux 2.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 table local),ID為255。
local表是保留路由表,包含了到本地和廣播地址的路由。規則0是特殊的規則,不可被刪除或修改。
規則 32766: 優先級 32766 選擇器 = 匹配所有數據報
動作 = 察看主路由表(routing table main), ID為254。
main路由表是默認的標准路由表,其包含所有非策略路由,main表是存放舊的路由命令(route命令)創建的路由。而且任何由ip route命令創建的沒有明確指定路由表的路由都被加入到該路由表中。該規則不能被刪除和被其他規則覆蓋。
規則 32767: 優先級 32767 選擇器 = 匹配所有數據報
動作 = 察看默認路由表(routing table default),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} [ dev STRING ] [ scope SCOPE-ID ] [ to PREFIX ] [ FLAG-LIST ] [ label PATTERN ] IFADDR := PREFIX | ADDR peer PREFIX [ broadcast ADDR ] [ anycast ADDR ] [ label STRING ] [ scope SCOPE-ID ] SCOPE-ID := [ host | link | global | NUMBER ] FLAG-LIST := [ FLAG-LIST ] FLAG FLAG := [ permanent | dynamic | secondary | primary | tentative | deprecated ] Example - ip addr add 192.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 [ from ADDRESS 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 ] [ proto RTPROTO ] [ type TYPE ] [ scope SCOPE ] ROUTE := NODE_SPEC [ INFO_SPEC ] NODE_SPEC := [ TYPE ] PREFIX [ tos TOS ] [ table TABLE_ID ] [ proto RTPROTO ] [ scope SCOPE ] [ metric METRIC ] INFO_SPEC := NH OPTIONS FLAGS [ nexthop NH ]... NH := [ via ADDRESS ] [ dev STRING ] [ 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 add 192.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 from 192.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 list table 254 default via 192.168.254.254 dev eth0 proto static
下面將針對該子網創建一條策略路由規則:
ip rule add from 192.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 from 192.168.2.0/24 priority 5000 prohibit ip route flush cache
這個命令流通過首先刪除5000號規則來確保當前系統沒有該規則然後再添加新的5000號規則。如果系統本來不存在5000號的規則則會返回一個錯誤信息。然後添加5000號規則並通過清空運行時的路由緩存來重置RPDB,則新規則將馬上產生作用。
多路由表和IP地址
為了完全理解基於策略路由的使用,就需要學會使用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 via 192.168.1.2 table goodnet1 ip route add 10.10.11.0/24 via 192.168.1.2 table goodnet2 ip route add 10.10.12.0/24 via 192.168.1.2 table badnet1 ip route add 10.10.13.0/24 via 192.168.1.2 table badnet1 ip route add default via 192.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 dev eth0 ip addr add 192.168.1.128/32 dev eth0 ip addr add 192.168.3.1/24 dev eth0
這時候如果察看主路由表則會發現內核只為網絡192.168.3.0/24添加了路由表,而沒有為網絡192.168.1.0/24添加路由。
通過ip addr命令可以察看系統的所有IP地址信息:
root@netmonster# ip addr 1: lo: <LOOPBACK,UP> mtu 3924 qdisc noqueue link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00 inet 127.0.0.1/8 brd 127.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:bc brd ff:ff:ff:ff:ff:ff inet 192.168.1.1/32 scope global eth0 inet 192.168.1.128/32 scope global eth0 inet 192.168.3.1/24 scope global eth0
下面我們將討論幾個更加復雜的例子。
例 4:多路由表和IP地址
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/30 dev eth0 ip addr add 172.17.1.128/24 dev eth0
下一步將討論創建哪些路由表,解決這個問題的最好辦法是想到基於策略的路由能實現根據源地址來決定使用哪個路由表。策略規則具有劃分內部網絡的能力,因此首先可以在路由表中添加基於目的的路由,在示例中我們將使用前面創建的新的路由表。
當在路由表中添加路由時下面的方法有助於澄清應該采取的步驟。假設你配置的是只有兩個接口的路由器,外接口直接連接Interet出口路由器,而內接口直接連接內部網絡。配置這樣的路由器是非常簡單的,為了說明我們這裡在表1-goodnet1這個路由表進行操作:
ip route add 10.10.0.0/16 via 10.254.254.2 table goodnet1 proto static ip route add default via 172.17.1.254 table goodnet1 proto static
對於goodnet2進行配置則是:
ip route add 172.18.0.0/16 via 172.17.1.1 table goodnet2 proto static ip route add default via 172.17.1.254 table goodnet2 proto static
可以看到對三個目標網絡只需要兩個路由表,這裡對於連接Internet的默認路由在兩個表中都進行了設置。為什麼不將默認路由存放在第三個路由表中呢?首先應該考慮到規則和路由表之間的交互,而且規則是實現對基於策略路由的定義。多條規則可以指向同一個路由表。然而,一旦進入某個路由表以後,則只能是匹配一條路由或返回一條路由鏈。如果匹配到一條規則則就認為你已經擁有一條正確的策略匹配,則該規則指向的路由表包含了該數據報所有的路由可能。
如果有三個路由表,則需要添加一條規則來察看數據報目的。而檢查數據報目的地址是標准路由的功能。為什麼要對每種源、目的的組合都需要一個規則呢?通過使用路由表,可以實現定義盡量少的規則來達到目的。當然 系統的靈活性允許通過多條方法來實現這樣的路由。可以根據在自己的喜好來決定那種方案最適合:
ip rule add from 192.168.1.32/27 to 172.18.0.0/16 pref 15000 table goodnet1 ip rule add from 192.168.2.64/28 to 10.10.0.0/16 pref 15001 table goodnet2 ip rule add from 192.168.1.0/24 pref 15002 table goodnet1 ip rule add from 192.168.2.0/24 pref 15003 table goodnet2
上面的例子中使用了優先級參數設定來定義數據報匹配規則的順序。現在來看看當一個數據報從內部網絡經過路由系統時會發生什麼情況。首先,它會通過優先級為0的規則檢查;隨後會遇到優先級為15000的規則,若匹配則會被goodnet1這個路由表進行操作,否則將會分別經過15001、15002、15003的規則。它肯定會被15000-15003幾個規則中的一個所匹配。