Linux 2.4 Advanced Routing HOWTO <author>Netherlabs BV (bert hubert <bert.hubert@netherlabs.nl>)&nl; Gregory Maxwell <greg@linuxpower.cx> &nl; Remco van Mook <remco@virtu.nl> &nl; Martijn van Oosterhout <kleptog@cupid.suninternet.com> &nl; Paul B Schroeder <paulsch@us.ibm.com> &nl; Jasper Spaans <jasper@spaans.ds9a.nl> &nl; howto@ds9a.nl <newline> <newline> 譯者﹕網中人 <netmanforever@yahoo.com> <newline> <date>v0.3.0 $Date: 2001/06/16 12:48:31 $ <abstract> 動手實作 iproute2 --- 流量管制及部份網路過濾 </abstract> <!-- Table of contents --> <toc> <!-- Begin the document --> <sect>獻題 (Dedication) <p> 本文謹作為我的一點小回報﹐以奉獻給眾多的朋友﹐恐難以一一致謝了﹐僅列如下諸公﹕ <p> <itemize> <item>Rusty Russell <item>Alexey N. Kuznetsov <item>來自 Google 的優秀成員 <item>Casema Internet 的同仁 </itemize> <sect>前言 (Introduction) <p> 各位親愛讀者﹐歡迎您﹗ <p> 但願本文件能助您大大提增 Linux 2.2/2.4 上面的路由(routing) 功力。士別三日﹐刮目相看﹐當您用這些工具做出亮麗成績的時候﹐定會令眾人眼前一亮的﹗什麼 'route' 及 'ifconfig' 這些命令﹐跟強大的 iproute2 機制比起來﹐簡直就是小巫見大巫啦。 <p> 我希望本 HOWTO 的可讀性﹐夠得上 Rusty Rusell 兄之 netfilter 體制(另一領域)的其他文章。 如閣下有任何疑問或指教﹐歡迎隨時與我們聯絡﹕ <url name="HOWTO team" url="mailto:HOWTO@ds9a.nl">.然而﹐如果閣下的問題並非與本 HOWTO 直接相關﹐請寄至我們的郵件論壇(參考相關章節)。 假如您單純的只想要做流量引導(traffic shaping)﹐在您於本 HOWTO 迷失之前﹐大可略過全部內容﹐直接跳至 '其它可行性(Other possibilties)' 章節﹐只閱讀 CBQ.init 即可。 <sect1>責任與版權 (Disclaimer & License) <p> 本文之散佈僅以助人為本﹐然並不保證任何事情﹐也不對諸如銷售或特殊應用之類的隱晦性承諾做出保證。 簡而言之﹐如果您的 STM-64 backbone 掛掉及影響到您的最大主顧﹐抱歉﹐概與我們無關。 Copyright (c) 2001 by bert hubert, Gregory Maxwell, Martijn van Oosterhout, Remco van Mook, Paul B. Schroeder and others. 請以任何形式自由散佈(販賣或贈送)本文件之副本。然而所有修正及/或建議﹐皆須知會文件的維護者。只要您符合如下條件﹐也可以創建自己的派生版本(derivative work )﹕ <enum> <item>將您的派生版本(以 sgml 格式) 寄給 LDP (Linux Documentation Project) ﹐或 Internet 上的相類刊登機構。如果不交由 LDP 的話﹐必須知會 LDP 經由哪裡可以獲得。 <newline>Send your derivative work (in the most suitable format such as sgml) to the LDP (Linux Documentation Project) or the like for posting on the Internet. If not the LDP, then let the LDP know where it is available. <item>派生版本必須以相同版權或 GPL 發行﹐且具備版權聲明﹐以及至少要有一個所用版權的指引。 <newline>License the derivative work with this same license or use GPL. Include a copyright notice and at least a pointer to the license used. <item>原作者及主要散佈者均享有預期的權益。 <newline>Give due credit to previous authors and major contributors. </enum> 假如您有意製作派生版本而非進行翻譯﹐必須與當前維護者協商。 如您要以印刷件(hardcopy) 發行本 HOWTO﹐您必須寄幾份清樣給作者們﹐僅供校驗而已 :-) <sect1>必備知識 (Prior knowledge) <p> 正如文章題目所示﹐此文為 '進階 (Advanced)' HOWTO。當然﹐不是說您要曉得弄火箭﹐但一些必具的知識還是要有的。 當然囉﹐如果您還想多學一點﹐這裡還有一些文獻或許會有所幫助的﹕ <descrip> <tag> <url url="http://netfilter.kernelnotes.org/unreliable-guides/networking-concepts-HOWTO.html" name="Rusty Russell's networking-concepts-HOWTO"></tag> 非常好的介紹性文件﹐講解何謂網路﹐以及網路之間是如何連接一起的。 <p><tag>Linux Networking-HOWTO (Previously the Net-3 HOWTO)</tag> 非常棒的東西﹐只是略為長了點。假如您已經連上 internet 的話﹐您會學到許多已經設定好的東西。您可在本機的 <file>/usr/doc/HOWTO/NET3-4-HOWTO.txt</file> 這個位置找到它﹐但也可以到 <url url="http://www.linuxports.com/howto/networking" name="這裡"> 找找。 </descrip> <sect1>Linux 可以為您做些什麼 (What Linux can do for you) <p> 下面所列只是一小部份而已﹕ <p> <itemize> <item>為特定的電腦調節頻寬 <item>讓您分享頻寬 <item>保護您的網路防範 DoS 攻擊 <item>保護 internet 免受顧客干擾 <item>合多台伺服器為一﹐諸如平衡負載或是提昇使用效能 <item>制約進入貴電腦的連線 <item>限制用戶連線至其他主機 <item>根據 user id(沒錯﹗)、MAC 位址、來源 IP 位址、port、服務類型(type of service)、以及每天固定時間或連線時間﹐等因素進行路由。 </itemize> <p> 目前還不是很多人使用這些進階功能﹐或許是有其原因的。例如﹐雖然提供的文件都很詳盡﹐然卻流於非實戰(hands on)基礎﹔流量管制也似乎還未被述之於文字。 <sect1>整理須知 (Housekeeping notes) <p> 關於本文﹐尚有某些地方是需要留意的。雖說我已經寫完了﹐說實在﹐還總是覺得有點差強人意。我是一個極端 Open Source 信徒﹐所以非常期待您能提供回饋、更新、修補、諸如此類的意見。如有誤筆或白爛之處﹐一定要及時指正哦。假如發現我的英文詞不達意﹐請原諒我這不是母語之故。任何建議﹐都是無任歡迎的。 假如閣下行有餘力﹐幫忙維護某一章節﹐或是覺得可以撰寫並維護新的章節﹐那更是求之不得。本 HOWTO 的 SGML 版本可以透過 CVS 獲得﹐若能廣邀聖賢共襄勝舉﹐弟非常樂見其成。 為此﹐您會發現許多 FIXME 提示﹐隨時恭候修補。當您碰到一個 FIXME 之時﹐您就知道那是一片未墾之野。沒人敢包說其他地方就沒有錯誤存在﹐只是小心為上啦。如果您驗證過某些東西﹐那就不要客氣告知我們﹐然後就可以拿掉該 FIXME 提示了。 關於本 HOWTO﹐有時我會比較天馬行空的。比方說﹐我會假設一條 10M bit 的 internet 連線﹐儘管我非常清楚這對尋常百姓人家而言﹐仍是王府堂前燕。 <sect1>連線、CVS﹐及提交最新信息 (Access, CVS & submitting updates) <p> 本 HOWTO 的官方網站位於 <url url="http://www.ds9a.nl/2.4Routing" name="這裡">。 我們已有對全球開放的匿名 CVS 連線﹐從不同角度考量均皆大歡喜。您可以輕輕鬆鬆的下載最新版本﹐提交修補也變得不費吹灰之力。 而且﹐也能讓各位作者獨立處理源碼(source)﹐何樂不為。 <tscreen><verb> $ export CVSROOT=:pserver:anon@outpost.ds9a.nl:/var/cvsroot $ cvs login CVS password: [enter 'cvs' (without 's)] $ cvs co 2.4routing cvs server: Updating 2.4routing U 2.4routing/2.4routing.sgml </verb></tscreen> 如果您發現有錯誤之處﹐或是想增加某些內容﹐可以先在本機上完成﹐然後執行 cvs diff -u﹐再將結果寄給我們即可。 您也可以借助我們所提供的 Makefile 來創建 postscript、dvi、pdf、html、還有純文字等各種格式。不過﹐您或許需要安裝 sgml-tools、ghostscript、還有 tetex 等工具﹐才能獲得所有格式。 <sect1>郵件論壇 (Mailing list) <p> <label id="MLIST"> 我們的作者們均收到不斷增加中的本 HOWTO 相關的郵件。有見於社群的濃厚興趣﹐我們已決定成立一個郵件論壇﹐讓大家彼此交流進階路由及流量控管的心得。歡迎您來 <url url="http://mailman.ds9a.nl/mailman/listinfo/lartc" name="這裡"> 訂閱本論壇。 <p> 這樣說好了﹐作者們並不熱衷於回答非論壇所提出的問題。我們希望能將論壇的文章彙整起來﹐成為某一形式的知識庫。假如您有任何問題﹐請先搜索一下先前文章﹐才好丟到論壇上來。 <sect1>本文佈局 (Layout of this document) <p> 我們馬上就要進行一些有趣的事情﹐不過﹐同時也將接觸到某些解釋不詳或未臻完美的部份。請暫時不要太鑽牛角尖而視之將會完善。 路由(routing)跟過濾(filtering)是兩碼子事情。在 Rustry 的 HOWTO 裡面已經對過濾有完整的闡述了﹐請參考﹕ <itemize> <item><url url="http://netfilter.kernelnotes.org/unreliable-guides/" name="Rusty's Remarkably Unreliable Guides"> </itemize> 我們這裡儘量集中在 netfilter 和 iproute2 的整合上面。 <sect>介紹 iproute2 (Introduction to iproute2) <sect1>為何要 iproute2? (Why iproute2?) <p> 大部份的 Linux 發行廠商 ﹐以及多數 UNIX 系統﹐目前均沿用歷史悠久的 'arp'、'ifconfig'、和 'route' 等命令。雖說這些工具都仍能使用﹐然而在 Linux 2.2 及以後的版本中﹐他們卻會導致一些非預期的效果。例如﹐目前 GRE tunnel 已成路由中的密不可分的一部份﹐但卻需要完全不一樣的工具才做得到。 用 iproute2 的話﹐其自身的工具組裡面就包含了 tunnels 了。 Linux 2.2 及以後的核心內含一個完全重新設計的網路子系統。此一全新的網路程式碼(networking code) 讓 Linux 在效能和功能上﹐較其它 OS 略勝一籌。事實上﹐新的路由過濾還有分類(classifying) 程式 ﹐比起一些專用路由器以及防火牆和流量管制產品﹐在功能上甚至有過之而無不及。 隨著新網路概念的不斷涌現﹐人們無不各顯神通將之安插於現有 OS 的架構之上。然而﹐這些持續的堆積﹐卻導致網路程式碼變的無奇不有﹐有如我們人類的各式語言。在過去﹐Linux 模仿 SunOS 來處理這些事情﹐但終非良策。 此一新架構將可詳盡闡釋遠非 Linux 所能及的功能行為。 <sect1>iproute2 導覽 (iproute2 tour) <p> Linux 本身有一個複雜異常的系統用於頻寬界定﹐叫做 Traffic Control。此系統支援多種方法﹐以應付傳入及傳出交通的分類(classfying)、優先排序(prioritizing)、分享(sharing)、以及限制(limiting)﹐等處理。 下面﹐我們就讓我們粗略溜灠一下 iproute2 的能耐吧。 <sect1>前提 (Prerequisites) <p> 首先﹐請確定必須的使用者工具已經安全妥當。在 RedHat 和 Debian 上﹐此套件都叫做 'iproute'﹐或是直接抓 <tt>ftp://ftp.inr.ac.ru/ip-routing/iproute2-2.2.4-now-ss??????.tar.gz"</tt> 也可以。 您也可以嘗試到 <url name="這裡" url="ftp://ftp.inr.ac.ru/ip-routing/iproute2-current.tar.gz"> 獲得最新的版本。 iproute 中的某些部份還需要將特定的核心選項打開。另外﹐您還要知道﹐RedHat 6.2 之前的所有發行版本﹐其核心裡面﹐大部份的流量控管功能都付諸闕如。 FIXME: 有誰可以幫忙確認一下 7.0 已經將所需核心編譯進來了嗎﹖ 同時﹐您還要確認您有支援 netlink ﹐您或許需要打造自己的核心﹐因為 iproute2 需要這個功能。 <sect1>探查當前設定 (Exploring your current configuration) <p> 給您一個驚喜﹐其實 iproute2 已經設定好了﹗當前命令如 <tt>ifconfig</tt> 和 <tt>route</tt> ﹐都已經使用進階的 syscall﹐但通常都使用預設值。 其中 <tt>ip</tt> 這工具最為重要﹐下面用它將我們的界面列示出來。 <sect2>用 <tt>ip</tt> 顯示連線 (<tt>ip</tt> shows us our links) <p> <tscreen><verb> [ahu@home ahu]$ ip link list 1: lo: <LOOPBACK,UP> mtu 3924 qdisc noqueue link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00 2: dummy: <BROADCAST,NOARP> mtu 1500 qdisc noop link/ether 00:00:00:00:00:00 brd ff:ff:ff:ff:ff:ff 3: eth0: <BROADCAST,MULTICAST,PROMISC,UP> mtu 1400 qdisc pfifo_fast qlen 100 link/ether 48:54:e8:2a:47:16 brd ff:ff:ff:ff:ff:ff 4: eth1: <BROADCAST,MULTICAST,PROMISC,UP> mtu 1500 qdisc pfifo_fast qlen 100 link/ether 00:e0:4c:39:24:78 brd ff:ff:ff:ff:ff:ff 3764: ppp0: <POINTOPOINT,MULTICAST,NOARP,UP> mtu 1492 qdisc pfifo_fast qlen 10 link/ppp </verb></tscreen> <p>您所讀到的資料或許有所不同﹐這裡所顯示的是在我家裡的 NAT router 上的資料。我下面只會解釋輸出結果的一部份而已﹐因為並非全部資料都是相關的。 讓我們先看看 loopback 界面。有可能您的電腦並沒裝任何界面﹐但我建議您起碼將它設定起來。其中的 MTU (Maximum Transfer Unit) 體積是 3924 octet﹐同時並沒被佇列(queued) 起來。這是合理的﹐因為 loopback 界面是由核心虛構出來的。 現在而言﹐我暫時略過 dummy 界面不談﹐而且它也未必出現在您的電腦上面。接下來是我的兩張實體網路界面﹐一個連接我的 cable modem﹐另一個接到我的家中網路去。最後﹐我們也看到一個 ppp0 的界面。 注意﹐這裡並沒有 IP 位址。iproute 並沒有將 'links' 和 'IP addresses' 這兩概念連在一起。如果用 IP aliasing 的話﹐那麼 IP 位址的概念就似乎變得不怎麼貼切了。 不過﹐它會將 MAC 位址顯示出來﹐也就是我們的 ethernet 界面的實體辨別位址啦。 <sect2>用 <tt>ip</tt> 顯示 IP 位址 (<tt>ip</tt> shows us our IP addresses) <p> <tscreen><verb> [ahu@home ahu]$ ip address show 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: dummy: <BROADCAST,NOARP> mtu 1500 qdisc noop link/ether 00:00:00:00:00:00 brd ff:ff:ff:ff:ff:ff 3: eth0: <BROADCAST,MULTICAST,PROMISC,UP> mtu 1400 qdisc pfifo_fast qlen 100 link/ether 48:54:e8:2a:47:16 brd ff:ff:ff:ff:ff:ff inet 10.0.0.1/8 brd 10.255.255.255 scope global eth0 4: eth1: <BROADCAST,MULTICAST,PROMISC,UP> mtu 1500 qdisc pfifo_fast qlen 100 link/ether 00:e0:4c:39:24:78 brd ff:ff:ff:ff:ff:ff 3764: ppp0: <POINTOPOINT,MULTICAST,NOARP,UP> mtu 1492 qdisc pfifo_fast qlen 10 link/ppp inet 212.64.94.251 peer 212.64.94.1/32 scope global ppp0 </verb></tscreen> <p> 這次包含的資訊就比較多了。它將所有的位址﹐以及所屬的網卡都顯示出來。'inet' 代表 Internet (IPv4)。當然﹐還有許多其它的位址族群﹐不過目前來說﹐暫時與我們無關。 讓我們仔細的看看 eth0 吧。它目前被分配的 inet 位址是 '10.0.0.1/8'。這是什麼意思呢﹖先看 /8 好了﹐它所代表的是網路位址 bit 數目。因為一個 IP 位址有 32 bit﹐所以我們就剩下 24 bit 給我的網路(主機)使用了。10.0.0.1 的前面 8 個 bit 所對應的是 10.0.0.0﹐也就是我們的網路位址(Network Address)﹐這樣我們的 netmask 是 255.0.0.0。 那剩下的 bits (所對應的位址)就直接連線到這個界面﹐拿例子來說明﹕可以直接在這個界面連上 10.250.3.13 ﹐和 10.0.0.1 一樣。 至於 ppp0 呢﹐也是同樣的原理啦﹐只是號碼之別而已。它的位址是 212.64.94.251﹐並沒有 subnet mask。這表示我們有一個 point-to-point 的連線﹐並且所有位址﹐除了這個 212.64.94.251 之外﹐都在遠端那邊。除此之外﹐還有更多的資訊﹐告訴我們在連線的另一端也同樣只有一個位址﹕212.64.94.1。那個 /32 ﹐是告訴我們沒有 'network bits' 的意思。 牢靠的掌握前述概念是至關重要的。假如您在理解上有困難﹐請參考本 HOWTO 前面所介紹的資料。 (譯者按﹕關於 IP 和 netmask 的關係﹐主要是用來判斷出‘網路位址’和‘主機位址’之用的。詳細的判斷規則﹐可以參考譯者的網站﹕ <url url="http://www.study-area.org/network/network_ipadd.htm" name="http://www.study-area.org/network/network_ipadd.htm">。) 同時﹐還請留意 'qdisc' 這個東東﹐它是 Queueing Discipline (佇列戒律﹖) 的意思。這個概念也至為重要。 <sect2>用 <tt>ip</tt> 顯示路由 (<tt>ip</tt> shows us our routes) <p> 好了﹐我們已經曉得如何查找 10.x.y.z 這樣的位址了﹐而且也能夠連上 212.64.94.1。只是﹐光是這幾路散手是不能出師的﹐所以我們還要教您如何殺出木人巷。我們可以透過 ppp 連線接上 internet﹐同時封包會以 212.64.94.1 這個位址丟到外面去﹐然後別人也是以這個位址將結果送回來。 <tscreen><verb> [ahu@home ahu]$ ip route show 212.64.94.1 dev ppp0 proto kernel scope link src 212.64.94.251 10.0.0.0/8 dev eth0 proto kernel scope link src 10.0.0.1 127.0.0.0/8 dev lo scope link default via 212.64.94.1 dev ppp0 </verb></tscreen> 輸出結果本身就說得很清楚了。前面 4 行精確的描述出我們從 <tt>ip address show</tt> 所揭示的意義﹐而最後一行則告訴我們我們可以透過 212.64.94.1﹐也就是我們的預設網關(default gateway)﹐和外面的世界做連線。我們之所以將之視為網關﹐是因為我們將封包送給 212.64.94.1﹐然後它就會幫我們善後了。 為方便比較﹐如下是舊的 'route' 命令所顯示的內容﹕ <tscreen><verb> [ahu@home ahu]$ route -n Kernel IP routing table Destination Gateway Genmask Flags Metric Ref Use Iface 212.64.94.1 0.0.0.0 255.255.255.255 UH 0 0 0 ppp0 10.0.0.0 0.0.0.0 255.0.0.0 U 0 0 0 eth0 127.0.0.0 0.0.0.0 255.0.0.0 U 0 0 0 lo 0.0.0.0 212.64.94.1 0.0.0.0 UG 0 0 0 ppp0 </verb></tscreen> <sect1>ARP <p> ARP 就是 Address Resolution Protocol﹐其定義可以參考 <url url="http://www.faqs.org/rfcs/rfc826.html" name="RFC 826">。ARP 是用來查找同一本地網路上﹐其它主機之實體位址/位置用的。而在 Internet 上的機器﹐則通常用它的名稱來判別﹐並用它查詢出 IP 位址。這就是 foo.com 網路的機器﹐為何能與 bar.net 網路的機器進行溝通的原理。不過﹐光得到一個 IP 位址﹐是沒辦法告訴您該機器的實體位置在哪裡。這時﹐ARP 就派得上用場了。 或許﹐我們找個例子來說說會比較好理解。假設我用幾台機器組成一個網路。其中兩台目前在我的網路上﹐一台叫 foo﹐其 IP 位址是 10.0.0.1﹔另一台叫 bar﹐其 IP 位址是 10.0.0.2。現在﹐foo 想要 ping 一下 bar﹐看看他是否在線上﹐然而頭痛的是﹐foo 並不知道 bar 在哪裡。這樣﹐當 foo 決定要對 bar 做 ping 的時候﹐它需要先丟出一個 ARP 請求(request)。 這個 ARP 請求就好像 foo 在大聲喊﹕“Bar(10.0.0.2)﹗您在哪裡啊﹖”其結果是﹐在網路上的所有機器都聽到這個呼叫﹐但卻只有 bar (10.0.0.2) 會作出回應。然後 bar 會丟出一個 ARP 回應(reply) 直接送回給 foo ﹕“Foo(10.0.0.1)﹐我在 00:60:94:E9:08:12 這裡啦。”用這個簡單的交流來找出網路上的同伴之後﹐foo 就可以和 bar 溝通了﹐直到它(其 arp cache)忘記 bar 在何方為止。 現在就讓我們看看這是如何工作的。您可以先檢查機器目前的 arp table ﹐應該會有點像這樣﹕ <tscreen><verb> [root@espa041 /home/src/iputils]# ip neigh show 9.3.76.42 dev eth0 lladdr 00:60:08:3f:e9:f9 nud reachable 9.3.76.1 dev eth0 lladdr 00:06:29:21:73:c8 nud reachable </verb></tscreen> 正如您所見到的﹐我的機器 espa041(9.3.76.41) 已知道如何找出 espa042(9.3.76.42)﹐還有 espagate(9.3.76.1)。然後讓我們將另一台機器加入 arp cache 中。 <tscreen><verb> [root@espa041 /home/paulsch/.gnome-desktop]# ping -c 1 espa043 PING espa043.austin.ibm.com (9.3.76.43) from 9.3.76.41 : 56(84) bytes of data. 64 bytes from 9.3.76.43: icmp_seq=0 ttl=255 time=0.9 ms --- espa043.austin.ibm.com ping statistics --- 1 packets transmitted, 1 packets received, 0% packet loss round-trip min/avg/max = 0.9/0.9/0.9 ms [root@espa041 /home/src/iputils]# ip neigh show 9.3.76.43 dev eth0 lladdr 00:06:29:21:80:20 nud reachable 9.3.76.42 dev eth0 lladdr 00:60:08:3f:e9:f9 nud reachable 9.3.76.1 dev eth0 lladdr 00:06:29:21:73:c8 nud reachable </verb></tscreen> 結果如上﹐espa041 先嘗試連接 espa043﹐然後 espa043 的實體位址也已被加進 arp cache 中。這樣﹐除非 espa043 的記錄逾時(例如他們之間再無溝通)﹐espa041 就知道到哪裡找 espa043﹐而無需再送 ARP 請求了。﹐ 現在﹐讓我們將 espa043 從 arp cache 中刪除看看﹕ <tscreen><verb> [root@espa041 /home/src/iputils]# ip neigh delete 9.3.76.43 dev eth0 [root@espa041 /home/src/iputils]# ip neigh show 9.3.76.43 dev eth0 nud failed 9.3.76.42 dev eth0 lladdr 00:60:08:3f:e9:f9 nud reachable 9.3.76.1 dev eth0 lladdr 00:06:29:21:73:c8 nud stale </verb></tscreen> 好了﹐現在 espa041 再次忘記到哪裡找 espa043﹐而在下次要和 espa043 溝通的時候﹐就需要送出另外的 ARP 請求。同時﹐您還會從上面的輸出結果中發現﹕ espagate(9.3.76.1) 已經變為 "stale" 狀態。這是說﹐該顯示位置仍然有效﹐但在第一次向該機器傳送的時候﹐必須要先確認就是了。 <sect>規則 - 路由原則資料庫 (Rules - routing policy database) <p> 假如您有一個強大的路由器﹐那您或許要滿足各適其式的不同人等之需。路由原則就能讓您透過多組路由表格﹐見招拆招。 如果您真的需要這個功能﹐請確定您的核心有將 "IP: advanced router" 和 "IP: policy routing" 功能編譯進來。 在核心進行路由判斷的時候﹐它要找出哪一個表格以便查詢。在預設情況之下﹐會有三個表格。舊的 'route' 工具會修改 main 和 local 表格﹐(預設上)和 ip 工具一樣就是了。 預設規則如下﹕ <tscreen><verb> [ahu@home ahu]$ ip rule list 0: from all lookup local 32766: from all lookup main 32767: from all lookup default </verb></tscreen> 這個表格列示出全部規則的優先次序。我們不難發現所有規則都適用於所有封包('from all')。'main' 表格我們前面已經看過 了﹐可以用 <tt>ip route ls</tt> 看到其輸出結果﹐不過﹐ 'local' 和 'default' 表格則是新的。 若想玩些高難度動作﹐我們可以產生新規則﹐然後指向不同表格﹐而改寫整個系統的路由規則。 當存在更多對應規則時﹐核心是如何確切處置的﹐請參考 Alexey 兄之 ip-cref 文件。 <sect1>簡易源路由 (Simple source routing) <p>還是讓我們再用一個真實範例來說好了﹐我有兩個(實際上在我退掉之前有 3 個) cable modem ﹐連接到一台 Linux NAT ('masqauerading') router 上面去。住在這裡的人付錢給我使用 internet。假設其中一個租客只單純的探訪 hotmail 而想少付一點錢。對我來說當然沒問題啦﹐只是您會被分配到較低階的 cable modem 而已。 那台 '較快' 的 cable mode 的位址是 212.64.94.251﹐同時以 PPP 連接到 212.64.94.1 那裡去。而那台 '較慢' 的 cable modem 使用的是變動位址﹐目前在此範例中是 212.64.78.148﹐同時連接到 195.96.98.253 去。 關於本機的表格﹐如下﹕ <tscreen><verb> [ahu@home ahu]$ ip route list table local broadcast 127.255.255.255 dev lo proto kernel scope link src 127.0.0.1 local 10.0.0.1 dev eth0 proto kernel scope host src 10.0.0.1 broadcast 10.0.0.0 dev eth0 proto kernel scope link src 10.0.0.1 local 212.64.94.251 dev ppp0 proto kernel scope host src 212.64.94.251 broadcast 10.255.255.255 dev eth0 proto kernel scope link src 10.0.0.1 broadcast 127.0.0.0 dev lo proto kernel scope link src 127.0.0.1 local 212.64.78.148 dev ppp2 proto kernel scope host src 212.64.78.148 local 127.0.0.1 dev lo proto kernel scope host src 127.0.0.1 local 127.0.0.0/8 dev lo proto kernel scope host src 127.0.0.1 </verb></tscreen> 大部份我們都知道是什麼了﹐只有其中一部份需要特別指定而已。嗯﹐也不難找啦。其 '預設(default)' 表格目前是空的。 那先讓我們看看 'main' 表格吧﹕ <tscreen><verb> [ahu@home ahu]$ ip route list table main 195.96.98.253 dev ppp2 proto kernel scope link src 212.64.78.148 212.64.94.1 dev ppp0 proto kernel scope link src 212.64.94.251 10.0.0.0/8 dev eth0 proto kernel scope link src 10.0.0.1 127.0.0.0/8 dev lo scope link default via 212.64.94.1 dev ppp0 </verb></tscreen> 我們現在先產生一條新規則﹐取名為 'John'﹐是為我們假設的房客而設的。雖然﹐我們可以全部用號碼來做﹐不過﹐如果我們將表格加入 <file>/etc/iproute2/rt_tables</file> 的話﹐會更加容易些。 <tscreen><verb> # echo 200 John >> /etc/iproute2/rt_tables # ip rule add from 10.0.0.10 table John # ip rule ls 0: from all lookup local 32765: from 10.0.0.10 lookup John 32766: from all lookup main 32767: from all lookup default </verb></tscreen> 接下來﹐我們只需增加 Johns 表格﹐同時清空(flush) route cache 就好﹕ <tscreen><verb> # ip route add default via 195.96.98.253 dev ppp2 table John # ip route flush cache </verb></tscreen> 好了﹐這樣就行了。剩下的﹐就作為我們讀者的練習﹐在 ip-up 下做做看吧。 <sect>GRE 及其它通道技術 (GRE and other tunnels) <p> 在 Linux 裡面的 tunnel 共分三類﹐即 IP in IP tunneling、 GRE tunneling、以及核心以外的 tunnels (例如﹕PPTP)。 <sect1>關於 tunnels 的幾項注意 (A few general remarks about tunnels): <p>Tunnels 可以用來做一些非比尋常的酷玩意。不過﹐如果設定不慎﹐也會將事情弄得一塌糊涂。如果您並非 <bf>胸有成竹</bf>﹐那就千萬不要將您的預設網關指向 tunnel 設備。還有﹐tunneling 也會增加額外的 overhead﹐因為它需要額外的 IP 標頭(header)。一般而言﹐每一個封包大概 20 byte 左右﹐這樣﹐一個正常的網路封包體積 (MTU) 會是 1500 bytes﹐但如封包果經 tunnel 傳送﹐卻只有 14800 byte 而已。這其實也不算是什麼問題﹐但是當您有機會用 tunnels 來連接大型網路的時候﹐請務必檢查 IP 封包的碎片和重組(fragmentation/reassembly)細節。當然囉﹐建立 tunnel 的話﹐最好是從 tunnel 的兩端同時著手﹐雙管齊下。 <p> <sect1>IP in IP tunneling <p> 此類 tunneling 技術其實在 Linux 裡已行之有年了。它需要兩個核心模組﹕ipip.o 和 new_tunnel.o。 比方說﹐您有 3 個網路﹕A 和 B 皆為內部網路﹐而透過網路 C (或曰 Internet) 將兩者連接起來。這樣﹐我們的網路 A 會是如此﹕ <tscreen><verb> network 10.0.1.0 netmask 255.255.255.0 router 10.0.1.1 </verb></tscreen> 接網路 C 的網關位址為 172.16.17.18。 而網路 B 則這般﹕ <tscreen><verb> network 10.0.2.0 netmask 255.255.255.0 router 10.0.2.1 </verb></tscreen> 接網路 C 的網關位址為 172.19.20.21 。 對網路 C 而言﹐我們假設它能夠雙向的將網路 A 和 B 之間的封包送遞對方。您甚至可以用 Internet 啦。 好了﹐然後您要做的事情是﹕ 首先﹐確定所有模組都裝好了﹕ <tscreen><verb> insmod ipip.o insmod new_tunnel.o </verb></tscreen> 然後﹐在網路 A 的 router 上﹐這樣做﹕ <tscreen><verb> ifconfig tunl0 10.0.1.1 pointopoint 172.19.20.21 route add -net 10.0.2.0 netmask 255.255.255.0 dev tunl0 </verb></tscreen> 然後在網路 B 的 router 上﹕ <tscreen><verb> ifconfig tunl0 10.0.2.1 pointopoint 172.16.17.18 route add -net 10.0.1.0 netmask 255.255.255.0 dev tunl0 </verb></tscreen> 完成後﹐將 tunnel 暫時關閉﹕ <tscreen><verb> ifconfig tunl0 down </verb></tscreen> 彈指間﹐就這樣搞定了。然而﹐您不能透過 IP-in-iP tunnel 傳送廣播或 IPv6 資訊。您剛纔將兩個本來不能相互溝通的 IPv4 網路連接起來了﹐然僅此而已。基於兼容考量﹐此程式碼由來已久﹐因而逆溯至 1.3 版核心皆兼籌並顧。據我所知﹐ Linux 之 IP-in-IP tunneling 並不能在其它的作業系統或 router 上工作。然而它短小精悍﹐實屬首選﹐除非您考慮用 GRE。 <sect1>GRE tunneling <p> GRE 是一個原本由 Cisco 開發的 tunneling 協定﹐較於 IP-in-IP tunneling﹐它略為能耐一些。例如﹐您能夠透過 GRE tunnel 傳送 multicast 和 IPv6 資訊。 在 Linux 裡﹐您得借助 ip_gre.o 模組。 <sect2>IPv4 Tunneling <p> 不如先讓我們將 IPv4 tunneling 做起來吧﹕ 比方說﹐您有 3 個網路﹕A 和 B 皆為內部網路﹐而透過網路 C (或曰 Internet) 將兩者連接起來。 關於網路 A ﹐如下﹕ <tscreen><verb> network 10.0.1.0 netmask 255.255.255.0 router 10.0.1.1 </verb></tscreen> 在網路 C 上的 router 位址為 172.16.17.18。 然後讓我們稱這個網路為 neta (好了﹐萬事起頭難)。 然後﹐關於網路 B﹕ <tscreen><verb> network 10.0.2.0 netmask 255.255.255.0 router 10.0.2.1 </verb></tscreen> 在網路 C 上的 router 位址為 172.19.20.21。 然後讓我們稱這網路為 netb 好了(革命尚未成功﹐同志仍需努力)。 對網路 C 而言﹐我們假設它能夠雙向的將網路 A 和 B 之間的封包送遞對方。至於何以然及何所以然﹐則非我們所要操心的。 <p> 接下來﹐在網路 A 的 router 上﹐您如此做﹕ <tscreen><verb> ip tunnel add netb mode gre remote 172.19.20.21 local 172.16.17.18 ttl 255 ip link set netb up ip addr add 10.0.1.1 dev netb ip route add 10.0.2.0/24 dev netb </verb></tscreen> 這裡﹐我們不妨研究一下。在第一行裡面﹐我們新增了一個 tunnel 設備﹐稱之為 netb (顯而易見﹐因為這正是吾之所欲也)。再來﹐我們讓它使用 GRE 協定(mode gre)﹐其遠端位址為 172.19.20.21 (於另一端的 router)﹐這樣我們的 tunneling 封包將從 172.16.17.18 從出(您的 router 在網路 C 上可以具有好幾個 IP 位址﹐並由您決定用哪一個來做 tunneling)﹐而且﹐封包的 TTL 欄位被設定為 255 (ttl 255)。 第 2 行我們將這個設備啟動起來。 在第 3 行﹐我們為新增的界面 netb 設定一個位址為 10.0.1.1。用這個在小網路上也未嘗不可﹐只是當您踏上採金旅程之際﹐您或需用其它 IP 範圍來給 tunneling 界面就是了(例如在此範例中﹐您可以使用 10.0.3.0)。 <p>在第 4 行﹐我們為網路 B 設定好 router。請留意﹐此處使用的 netmask 表示法並不一樣。如果您不太了解其意所在﹐可以這樣來理解﹕將 netmask 換算為二進位(binary)﹐然後數一數有多少個 1 就是了。如果您連這個也不會﹐萬一又想知道﹐那就這樣強記即可﹕255.0.0.0 是 /8、255.255.0.0 是 /16、還有 255.255.255.0 是 /24、而 255.255.253.0 則是 /23。 <p> 夠了真是的﹐還是讓我們看看網路 B 的 router 吧。 <tscreen><verb> ip tunnel add neta mode gre remote 172.16.17.18 local 172.19.20.21 ttl 255 ip link set neta up ip addr add 10.0.2.1 dev neta ip route add 10.0.1.0/24 dev neta </verb></tscreen> 假如您要拿掉 router A 上的 tunnel﹐則﹕ <tscreen><verb> ip link set netb down ip tunnel del netb </verb></tscreen> 當然﹐您也可以替 router B 將 netb 換成 neta。 <p> GRE tunnels 目前所選用的 tunneling 類型。它已成標準並且也被廣泛的移植到 Linux 社群之外﹐誠是美事一樁。 <p> <sect1>Userland tunnels <p> 在核心之外﹐還有成打形形色色的 tunneling 實作﹐絕非誇張之談。其中表表者非 PPP 和 PPTP 莫屬﹐然而其它的也為數眾多 (有的為專屬的﹐有的是保密的﹐有的甚至不是使用 IP)﹐不過﹐這些實在超過本 HOWTO 的範疇了。 <sect>用 Cisco 和/或 6bone 進行 IPv6 tunneling (IPv6 tunneling with Cisco and/or 6bone) <p> By Marco Davids <marco@sara.nl> 維護者需知﹕ 據我所知﹐目前這個 IPv6-IPv4 tunneling 並非由 GRE tunneling 定義。雖然﹐您可以用 GRE tunnel 設備進行 IPv6 over IPv4 的 tunnel (GRE 可在 IPv4 上作任何的 tunnel)﹐但在這裡使用的設備("sit")﹐僅對 IPv6 over IPv4 進行 tunnel 而已﹐因此或和其它技術有別。 <sect1>IPv6 Tunneling <p> 這是 Linux 在 tunneling 技術上的另一應用。這在已經使用 IPv6 的界面上很普遍﹐當然囉﹐您認為很前衛也未嘗不可。後面的 '實作' 範例絕對不是唯一的 IPv6 tunneling 方法。不過﹐它卻是在 Linux 與 Cisco IPv6 兼容路由器之間進行 tunnel 的最常用武器﹐而實驗證明許多人對此也趨之若騖。我不怕以一賠十和您賭一賭 ;-) <p> <bf>關於 IPv6 的小秘訣</bf> <p> 比較起 IPv4 位址﹐IPv6 位址顯如龐然大物﹕128 bits 對比 32 bits。這同時也只提供我們需要的事物﹕就是許多、許多的 IP 位址﹕340,282,266,920,938,463,463,374,607,431,768,211,465 是一個十分精確的例子。除此以外﹐ IPv6(或 IPng﹐也就是 IP Next Generation)﹐被認為可以在 Internet backbone 路由器上維護更小的路由表、更簡單的設備設定、更好的 IP 層級安全、以及更佳的 QoS 支援。 例如﹕2002:836b:9820:0000:0000:0000:836b:9886 每次寫這麼長的 IPv6 位址﹐事實上蠻累人的。因此﹐不妨參考如下規則來簡化一下﹕ <itemize> <item>不要以零開始(和 IPv4 一樣)。 <item>用冒號來區隔這 16 個 bit (或兩個 byte)。 <item>當遇到連續的零的時候﹐您可以這樣寫 :: ﹐但是只能在一個位址上做一次﹐而且位址數量一定要為 16 個 bit。 </itemize> 參考此規則﹐一個如 2002:836b:9820:0000:0000:0000:836b:9886 的位址﹐也可以寫成 2002:836b:9820::836b:9886﹐顯然簡潔得多。 <p> <bf>用於 tunnels 上面</bf> IPv6 一致被認為能夠取代 IPv4 的地位。由於它相對而言還是一種嶄新技術﹐目前尚難找到純 IPv6 的原始網路。為了讓我們更迅速的過渡﹐於是有了 6bone 的出現。 原始的 IPv6 網路聯結﹐是透過將 IPv6 協定封裝在 IPv4 封包中﹐然後利用現有的 IPv4 架構從一個 IPv6 站點送到其它站點去。 恰好﹐這正是 tunnels 切入之處。 為了使用 IPv6﹐我們必須要先有一個支援它的核心。目前已經有非常多的優秀文件告訴我們如何做到這點﹐不過﹐似乎全都不外乎這幾路板斧﹕ <itemize> <item>使用新的 Linux 版本﹐以及合適的 blibc。 <item>然後抓取最新的核心原始碼。 </itemize> 如果您全都準備妥當﹐那您就可以繼續上路﹐編譯出一個 IPv6 兼容的核心﹕ <itemize> <item>轉到 /usr/src/linux 並輸入﹕ <item>make menuconfig <item>選擇 "Networking Options" <item>選擇 "The IPv6 protocol",﹐"IPv6: enable EUI-64 token format"﹐"IPv6: disable provider based addresses" </itemize> 提示﹕不要使用‘模組’的方式﹐這通常工作起來不很完善。 總而言之﹐將 IPv6 以 '內建' 方式編進核心就是了。然後您可以如常般將設定保存好﹐再繼續核心的編譯。 提示﹕在真正編譯之前﹐不妨修改一下 Makefie﹕ EXTRAVERSION = -x ; --> ; EXTRAVERSION = -x-IPv6 關於核心的編譯和安裝﹐應該有很多優秀文件了﹐本文件就不再贅言。如果您在這裡碰到麻煩﹐請按照您自己的規格參考 Linux 核心編譯的相關文件。 /usr/src/linux/README 這個檔應該是個不錯的起點。等您過了這一關﹐再用新的核心重新啟動系統﹐然後您可以輸入 '/sbin/ifconfig -a' 命令﹐或許會發現一個全新的'sit0-device'。 SIT 就是 Simple Internet Transition 的意思。果真如此的話﹐不妨開香檳自我慶祝一番﹕您已經距離下一代的 IP 邁出了很大一步了 ;-) 接下來﹐您或許想要將機器連接起來﹐或是甚至將整個網路連接到其它 IPv6 兼容網路去。而 "6bone" 正是應此運而生的。 假設您獲得一個這樣的的 IPv6 網路﹕3ffe:604:6:8::/64 ﹐而您想要連接 6bone ﹐或是您的朋友。請注意﹕那個 /64 子網標記的使用辦法參照常規的 IP 位址界定方式就可以了。 您的 IPv4 位址為 172.16.17.181﹐而 6bone 的 router 也有一個位址為 145.100.1.5。 <tscreen><verb> # ip tunnel add sixbone mode sit remote 145.100.1.5 [local 145.100.24.181 ttl 225] # ip link set sixbone up # ip addr add 3FFE:604:6:7::2/126 dev sixbone # ip route add 3ffe::0/16 dev sixbone </verb></tscreen> 讓們研究一下上面的句子。第一行﹐我們建立了一個 tunnel 設備﹐叫做 sixbone。然後設定為 sit 模式(IPv6 in IPv4 tunneling)﹐並且要連到哪裡去 (remote)﹐還有從哪裡來 (local)。而 TTL 呢﹐已設到最高﹕255。 接下來﹐我們將設備跑起來 (up)。再下來﹐我們新增自己的網路位址﹐同時透過這個 tunnel 為 3ffe::/15 設定一個路由 (目前均為 6bone)。如果您目前的執行主機是您的 IPv6 網關的話﹐請增加如下數行﹕ <tscreen><verb> # echo 1 >/proc/sys/net/ipv6/conf/all/forwarding # /usr/local/sbin/radvd </verb></tscreen> 在最後面的 radvd 是一個路由器廣告程式(advertisement daemon) - like zebra -﹐以支援 IPv6 的自動設定功能。如果您有興趣﹐請用搜索引擎尋找它的相關資訊。您可以用如下命令來檢查一下﹕ <tscreen><verb> # /sbin/ip -f inet6 addr </verb></tscreen> 假如您已經在 IPv6 網關上將 radvd 跑起來﹐並在本地網路上啟動 IPv6 兼容的 Linux 機器﹐那您應該可以享受到 IPv6 的自動設定功能了﹕ <tscreen><verb> # /sbin/ip -f inet6 addr 1: lo: <LOOPBACK,UP> mtu 3924 qdisc noqueue inet6 ::1/128 scope host 3: eth0: <BROADCAST,MULTICAST,UP> mtu 1500 qdisc pfifo_fast qlen 100 inet6 3ffe:604:6:8:5054:4cff:fe01:e3d6/64 scope global dynamic valid_lft forever preferred_lft 604646sec inet6 fe80::5054:4cff:fe01:e3d6/10 scope link </verb></tscreen> 您還可以進而用 bind 設定 IPv6 位址。原來的 A 記錄﹐在 IPv6 裡面為﹕AAAA。而 in-addr.arpa 則改為 ip6.int。呵﹐光是這個題目就有一大堆資料了。 越來越多的應用程式已經開始支援 IPv6 了﹐包括 secure shell、telnet、inetd、Mozilla 瀏灠器、Apache 網站伺服器、以及其它﹐數不勝數。不過﹐這些都不是本路由文件所要討論的啦 ;-) 在 Cisco 那邊﹐設定檔或許會長得有點像下面的樣子﹕ <tscreen><verb> ! interface Tunnel1 description IPv6 tunnel no ip address no ip directed-broadcast ipv6 enable ipv6 address 3FFE:604:6:7::1/126 tunnel source Serial0 tunnel destination 145.100.24.181 tunnel mode ipv6ip ! ipv6 route 3FFE:604:6:8::/64 Tunnel1 </verb></tscreen> 但是﹐如果您沒有 Cisco 可供設定﹐那就嘗試找 Internet 上的眾多 IPv6 tunnel 經紀幫忙。相信他們都非常樂意在他們的 Cisco 上面為您設定額外的 tunnel 的。而且大部份都可以透過友善的 web 界面進行。您可以用搜索引擎找找 "ipv6 tunnel broker" 看。 <sect>IPsec﹕ internet 上之安全 IP (IPsec: secure IP over the internet) <p> FIXME﹕編輯空缺。請參考﹕ <url url="http://www.freeswan.org/" name="The FreeS/WAN project">。 <sect>多重傳播路由 (Multicast routing) <p> FIXME﹕編輯空缺﹗ <sect>用 CBQ 進行頻寬管理 (Using Class Based Queueing for bandwidth management) <p> 現階段由我來發掘這功能﹐<em>委實</em>是折殺小弟了。Linux 2.2 本身就自帶有許多頻寬管理辦法﹐其實已夠得上那些高級專屬的頻寬管理系統了。 Linux 所提供的﹐甚至比 Frame 及 ATM 還要更多。 流量控管的兩個基本單元是過濾器(filters) 和佇列(queues)。前者將流量安排至後者﹐後者則收集流量然後決定哪些要先送、哪些要晚送、或是丟棄掉(drop)。而兩者都各有其不同系列。 最常見的過濾器是 fwmark 和 u32﹐前者讓您可以使用 Linux netfilter 程式選擇流量﹔而後者則允許您依據任何標頭(header)選擇流量。至於最常聽到的佇列演算法就是 Class Based Queue 了。CBQ 可以說是一個 super-queue﹐在其裡面包含了其它佇列 (甚至其它 CBQs)。 或許﹐關於佇列演算法(queueing)在頻寬管理上的運用﹐一時間尚難一窺全豹﹐不過﹐它還真的不負眾望就是了。 作為我們的參考資料框架﹐我把目前這個章節套用在一個 ISP 模式之上﹐也就是我偷師學藝之所﹐暫且稱之為 Casema Internet in The Netherlands 吧。Casema﹐事實上是一家 cable 公司﹐他們的客戶和他們自己的辦公室都有 internet 的需求。大多數公司電腦都有 internet 的連線。實際上﹐他們有大把金錢可以花而不是用 Linux 來做頻寬管理的。 那讓我們看看我們的 ISP 是怎樣用 Linux 來管理其頻寬吧。 <sect1> 佇列演算法是什麼東東﹖(What is queueing?) <p> 我們用佇列演算法來判定數據 <em>被傳送</em> 的順序。有一個很重要的概念我們要知道﹐就是我們只能處理那些我們要傳送的數據。那它又是如何影響傳送速度之判定順序呢﹖您可以想像為一位收款員每一分鐘可以處理 3 個客人。 然後要付款的客人就要跑到隊列的 '尾巴' 去排隊。這是所謂的 'FIFO quequeing' (First IN, First Out --- 先入先出)。不過﹐假設我們讓某些客人插入隊列中間﹐而不是排在最後﹐然後這些客人就可以花更少時間在隊列中﹐因而也可以更快的購物。 在 inetnet 的環境中﹐我們沒辦法直接控制別人要送什麼東西過來。這有點像您家中的(實體)信箱﹐您沒辦法影響全世界去修改他們要送信給您的數量﹐除非您有能力通知所有人。 然而﹐internet 賴以為繼的 TCP/IP﹐卻有某些功能可以協助我們的。TCP/IP 本身是沒辦法知道兩台主機之間的網路容量的﹐所以它會越來越快('剛開始很慢')的將數據送出去﹐而當封包開始丟失的時候﹐因為沒有空間將他們送出去了﹐然後就會開始減慢下來。 這情形﹐就有點像您還沒讀完一半信件的時候﹐會希望別人不要再寄信給您一樣。不同之處﹐這裡是在 internet 上面而已。 FIXME﹕解釋擁塞視窗 (congestion windows) <tscreen><verb> [The Internet] ---<E3, T3, whatever>--- [Linux router] --- [Office+ISP] eth1 eth0 </verb></tscreen> 現在﹐我的 Linux router 有兩張界面﹐eth0 和 eth1。eth1 用來連接我們的 router﹐它負責將封包送至光纖線路以及將封包接收進來。 eth0 則用來連接另外一個 subnet﹐內有公司防火牆及我們的網路前端﹐我們透過它來連接客戶。 由於我們僅能限制送出的部份﹐我們需要兩套獨立但非常近似的規則。透過修改 eth0 的佇列﹐我們可以判定資訊要多快送至我們客戶那邊﹐因而要分配多少下傳(downstream) 頻寬給他們﹐所謂的‘下載速度’是也。 在 eth1 上面﹐我們判定數據要多快送給 Internet、多快給我們的使用者﹐而且公司內部和業務方面均需要上傳數據。 <sect1>先搞定頻寬切割 (First attempt at bandwidth division) <p> CBQ 可以讓我們產生好些不同類別(classes)﹐甚至類別中的類別。至於更大的切割﹐或許可以稱為 '代理(agencies)'。而在這些類別裡面﹐或者可以找到諸如 'bulk' 或 'interactive' 之類的名稱。 例如﹐我們或有一條 10 megabit 的 'internet' 連線﹐以分享給我們的客戶及公司之需。但我們不能讓辦公室裡的少數同仁盜取大量的本來要賣給客戶的頻寬。 在另一邊﹐比方說我們的客戶﹐也不能佔用本來給我們的門市通往客戶資料庫的頻寬。 在過去﹐用來解決的辦法﹐莫過於使用 Frame Relay/ATM 或建立虛擬電路(Virtual Circuits)。這的確能解決問題﹐只不過 frame 並不容易十分細緻調整﹐而 ATM 在攜帶 IP 流量上的效能也非常差強人意﹐而且﹐兩者都未有標準法則以產生不同類型的流量進入不同的 VCs 。 不過﹐假如您真的使用 ATM 的話﹐Linux 同樣能架輕就熟的為您表演高難度的流量分類技巧。而另一方法是牽兩條獨立的線路﹐但似乎不怎麼實用﹐也不十分完善﹐且也不見得能完全解決您的所有問題。 這時候﹐您就要求助於 CBQ 這尊菩薩了。 顯而易見﹐我們這裡主要有兩個類別﹕ 'ISP' 和 'Office' 。剛開始的時候﹐我們真的不必十分講究他們頻寬的切割﹐所以我們就不再於其類別下細分了。 我們決定客戶的下傳流量必須保證在 8 megabits 的範圍﹐而我們的辦公室只有 2 megabits。 可以用 iproute2 之 <tt>tc</tt> 工具來設定起流量控管。 <tscreen><verb> # tc qdisc add dev eth0 root handle 10: cbq bandwidth 10Mbit avpkt 1000 </verb></tscreen> <p>好了﹐這裡有一堆數字。究竟如何呢﹖我們已經設定起 eth0 的 '佇列戒律 (queueing discipline)' 了。我們以 'root' 來宣告這是頂層(root) discipline。我們還將其 handle 設為 '10'。因為我們這裡要做 CBQ﹐所以我們也同時在命令行中指明。我們告訴核心﹐它可以支配 10M bit ﹐同時平均封包體積大約為 1000 個 octet。 好﹐現在我們就產生我們的頂層類別﹐它凌架於其它的所有類別之上。 <tscreen><verb> # tc class add dev eth0 parent 10:0 classid 10:1 cbq bandwidth 10Mbit rate \ 10Mbit allot 1514 weight 1Mbit prio 8 maxburst 20 avpkt 1000 </verb></tscreen> <p> 哇﹐這裡的數字更多﹗Linux 的 CBQ 在實作上其實蠻具通用性的。我們用 'parent 10:0' 來指定此類別是源自我們剛纔產生的 qdisc handel '10:' 這個頂層類別而來的。而 'classid 10:1' 呢﹐則是我們授予這個類別的名稱。 我們這裡無須告訴核心更多資訊﹐單純產生一個類別以滿足可用設備就是了。我們同時還指定出 MTU(外加一些 overhead) 為 1514 個 octet。而且﹐我們量定此類別的 '比重(weight) ' 為 1Mbit﹐此為一個微調參數而已。 現在﹐再讓我們產生 ISP 類別吧﹕ <tscreen><verb> # tc class add dev eth0 parent 10:1 classid 10:100 cbq bandwidth 10Mbit rate \ 8Mbit allot 1514 weight 800Kbit prio 5 maxburst 20 avpkt 1000 \ bounded </verb></tscreen> <p> 我們先撥出 8Mbit﹐同時用 'bounded' 參數來指定此類別一定不能超過此限制。否則此類別或會從其它類別那裡借用頻寬﹐我們後面將會討論到。 還是讓我們產生Office 之頂層類別﹐趕快收鑼吧﹕ <tscreen><verb> # tc class add dev eth0 parent 10:1 classid 10:200 cbq bandwidth 10Mbit rate \ 2Mbit allot 1514 weight 200Kbit prio 5 maxburst 20 avpkt 1000 \ bounded </verb></tscreen> 為了幫助我們更好的理解﹐下面的圖示列出了所有的類別﹕ <tscreen><verb> +-------------[10: 10Mbit]-------------------------+ |+-------------[10:1 root 10Mbit]-----------------+| || || || +-----[10:100 8Mbit]---------+ [10:200 2Mbit] || || | | | | || || | ISP | | Office | || || | | | | || || +----------------------------+ +------------+ || || || |+------------------------------------------------+| +--------------------------------------------------+ </verb></tscreen> 好了﹐現在我們已經告訴核心有什麼類別存在﹐但還沒說要怎樣去管理佇列。我們不如馬上起而行﹐一次搞定兩個類別吧。 <tscreen><verb> # tc qdisc add dev eth0 parent 10:100 sfq quantum 1514b perturb 15 # tc qdisc add dev eth0 parent 10:200 sfq quantum 1514b perturb 15 </verb></tscreen> 目前的範例中﹐我們安裝的是 Stochastic Fairness Queueing discipline (sfq)﹐雖然不是很貼切﹐不過它卻可以在不耗費更多 CPU 運轉之下﹐很好的處理大量頻寬。而我們通常使用的會是 The Token Bucket Filter。 到目前為止﹐我們僅剩一件事情要做而已﹐就是向核心解釋什麼樣的封包屬於什麼樣的類別。剛開始﹐我們純粹用 iproute2 來做就好﹐但是﹐配合 netfilter 雙劍合璧的話﹐更是如虎添翼。 <tscreen><verb> # tc filter add dev eth0 parent 10:0 protocol ip prio 100 u32 match ip dst \ 150.151.23.24 flowid 10:200 # tc filter add dev eth0 parent 10:0 protocol ip prio 25 u32 match ip dst \ 150.151.0.0/16 flowid 10:100 </verb></tscreen> 這裡﹐我們假設辦公室網路躲在位址為 150.151.23.24 的防火牆之後﹐而我們其它 IP 位址則屬於 ISP 的。 使用 u32 比對(match) 更是容易﹐而用 netfilter 來標識封包也更能夠設定出精密的比對規則﹐然後我們可以在 tc 裡進行比對。 好了﹐現在我們已經將下傳頻寬切割完畢﹐同樣的﹐我們在上傳頻寬上面依樣畫葫蘆。為求簡捷﹐這次讓我們一鼓作氣﹕ <tscreen><verb> # tc qdisc add dev eth1 root handle 20: cbq bandwidth 10Mbit avpkt 1000 # tc class add dev eth1 parent 20:0 classid 20:1 cbq bandwidth 10Mbit rate \ 10Mbit allot 1514 weight 1Mbit prio 8 maxburst 20 avpkt 1000 # tc class add dev eth1 parent 20:1 classid 20:100 cbq bandwidth 10Mbit rate \ 8Mbit allot 1514 weight 800Kbit prio 5 maxburst 20 avpkt 1000 \ bounded # tc class add dev eth1 parent 20:1 classid 20:200 cbq bandwidth 10Mbit rate \ 2Mbit allot 1514 weight 200Kbit prio 5 maxburst 20 avpkt 1000 \ bounded # tc qdisc add dev eth1 parent 20:100 sfq quantum 1514b perturb 15 # tc qdisc add dev eth1 parent 20:200 sfq quantum 1514b perturb 15 # tc filter add dev eth1 parent 20:0 protocol ip prio 100 u32 match ip src \ 150.151.23.24 flowid 20:200 # tc filter add dev eth1 parent 20:0 protocol ip prio 25 u32 match ip src \ 150.151.0.0/16 flowid 20:100 </verb></tscreen> <sect1>如何處理頻寬透支 (What to do with excess bandwidth) <p> 在我們這個虛構範例中﹐我們發現一個現象﹐就是在 ISP 客戶大都離線的時候(比方說﹐早上 8 點)﹐我們的辦公室卻只有 2Mbit﹐顯然是極其浪費的。 如果將 'bounded' 敘述拿掉﹐那麼類別就能夠借取其它類別的頻寬來用。 而有些類別或許不願意將他們的頻寬外借﹐例如兩個租用同一線路的敵對 ISP﹐是絕對不會向對方進貢的。在那樣的狀況下﹐您可以在 'tc class add' 的句子後端﹐加上一個關鍵詞 'isolated' 即可。 <sect1>類別再分 (Class subdivisions) <p> FIXME﹕此構思並沒經過測試﹗小心嘗試﹗ 我們這裡還可精益求精。如果所有辦公室員工同時開啟他們的股票程式﹐還是有可能把資料庫的頻寬給吃掉的。所以﹐我們可以再建兩個子類別﹕'Human' 和 'Database'。 我們的資料庫永遠需要 500Kbit﹐所以就剩下 1.5M 給我們的員工來花費。 我們要在 Office 這個類別裡面再建兩個類別﹕ <tscreen><verb> # tc class add dev eth0 parent 10:200 classid 10:250 cbq bandwidth 10Mbit rate \ 500Kbit allot 1514 weight 50Kbit prio 5 maxburst 20 avpkt 1000 \ bounded # tc class add dev eth0 parent 10:200 classid 10:251 cbq bandwidth 10Mbit rate \ 1500Kbit allot 1514 weight 150Kbit prio 5 maxburst 20 avpkt 1000 \ bounded </verb></tscreen> FIXME﹕尚需範例﹗ <sect1>多界面的負載分流 (Loadsharing over multiple interfaces) <p> 有好些方法都可以做到這點。其中最簡單明了就是 'TEQL' --- "True" (or "trivial") link equalizer。和大多數用來做佇列的辦法一樣﹐負載分流也是雙向的。線路的兩端都需要參與﹐才能獲得完整的效果。 發揮一下想像力吧﹕ <tscreen><verb> +-------+ eth1 +-------+ | |==========| | 'network 1' ----| A | | B |---- 'network 2' | |==========| | +-------+ eth2 +-------+ </verb></tscreen> A 和 B 都是路由器﹐而且﹐我們目前假設它們都是跑 Linux 的。如果流量從 network 1 送到 network 2 那邊﹐那麼 router A 就需要將封包分送至 B 的兩條線路去。而 router B 則需要設定為能夠接受這樣的安排。調過來也一樣﹐當封包從 network 2 流向 network 1﹐router B 也需要將封包分送至 eth1 和 eth2 去。 負責分送的部份﹐就是由 'TEQL' 設備來做的﹐參考如下劍訣(易如削蠟)﹕ <tscreen><verb> # tc qdisc add dev eth1 root teql0 # tc qdisc add dev eth2 root teql0 </verb></tscreen> 這需要在兩邊的機器上做啦。那個 teql0 的設備﹐基本上以 roundrobin 的方式向 eth1 和 eth2 進行分送﹐來發送封包。數據並不是從 teql 設備進入的﹐僅僅以 'raw' 格式出現在 eth1 和 eth2 上面。 然而﹐我們現在只是把設備弄起來而已﹐我們還需要適當的路由。方法之一是用一個 /31 的網路給這兩條線路﹐另外一個 /31 網路給 teql0 設備﹕ FIXME: 不知道是否需要某些諸如 'nobroadcast' 的設定呢﹖用一個 /31 同時包含網路位址和廣播位址似乎太小了 --- 如果這個設計不可行﹐那就試用 /30 吧﹐然後相應的調整 IP 位址就是了。您或許甚至連 eth1 和 eth2 的 IP 位址也不想給呢﹗ On router A: <tscreen><verb> # ip addr add dev eth1 10.0.0.0/31 # ip addr add dev eth2 10.0.0.2/31 # ip addr add dev teql0 10.0.0.4/31 </verb></tscreen> On router B: <tscreen><verb> # ip addr add dev eth1 10.0.0.1/31 # ip addr add dev eth2 10.0.0.3/31 # ip addr add dev teql0 10.0.0.5/31 </verb></tscreen> 現在﹐router A 應該可以在這 2 條真實線路和 1 個均衡設備上 ping 10.0.0.1、10.0.0.3、和 10.0.0.5 了。而 router B 也應該可以透過線路 ping 10.0.0.0、10.0.0.2、和 10.0.0.4 。 如果上面的都成功了﹐router A 要將 10.0.0.5 設定為連接 network 2 的路由﹐同時 router B 則要將 10.0.0.4 設為連接 network 1 的路由。在一些特殊環境中﹐例如 network 1 是您家中的網路﹐而 network 2 是 internet﹐那麼 router A 就要將 10.0.0.5 設定為預設網關了。 <sect2>一些注意地方 (Caveats) <p> 任何事情都是知易行難。在 router A 和 router B 雙方﹐它們的 eth1 和 eth2 都必須將返回路徑的過濾關閉﹐否則它們會將那些不是以它們本身為目標 IP 位址的封包丟棄掉﹕ <tscreen><verb> # echo 0 > /proc/net/ipv4/conf/eth1/rp_filter # echo 0 > /proc/net/ipv4/conf/eth2/rp_filter </verb></tscreen> 然後﹐就是討厭的封包重排序的問題了。比方說﹐有 6 個封包需要從 A 送到 B 去 --- eth1 或會得到 1、3、和 5 ﹔而 eth2 或會得到 2、4、和 6 。假如一切理想﹐router B 會收到這樣的順序﹕1、2、3、4、5、6。不過﹐現實中有很大機會將會是﹐核心按這樣的順序接收﹕2、1、4、3、6、5。問題是這樣會讓 TCP/IP 感到困擾。透過線路攜帶多個不同的 TCP/IP 連線並不至於有什麼問題﹐然而您卻不能合併多條線路讓下載單一檔案的 FTP 變得大幅加快﹐除非您用來傳送和接收的作業系統都是 Linux﹐因為在某些簡單的重排序中﹐連線的交握並不容易處理。 不過﹐對許多應用程式而言﹐線路的負載分流的確是一個非常厲害的武器。 <sect>更多的 qdisc (More queueing disciplines) <p> 其實 Linux 核心已經提供我們許多 queueing disciplines 了。到目前為止﹐最為廣泛應用的是 pfifo_fast 佇列演算法﹐也是預設使用的。這也解釋了為何這些進階功能如此強悍。他們除了只是 '另外的佇列' 以外﹐別無它物。 每一種佇列演算法都各有千秋。不過並非全部都經嚴密測試過的就是了。 <sect1>pfifo_fast <p> 望文生意﹐此佇列演算法就是先入先出(First In First Out)﹐接收到的封包﹐均一視同仁。這種佇列演算法有 3 個所謂的 'bands'。在每一個 band 中﹐均奉行 FIFO 的規則。然而﹐z只有在 band0 中的封包處理完之後﹐才輪到 band1 裡面的。在 band1 和 band2 之間﹐也是同樣的情形。 <sect1>Stochastic Fairness Queueing <p> 一如前述﹐SFQ 也不是放之四海皆準的﹐但(平均來說)還是可用的就是了。它主要的好處是它所佔用的 CPU 和記憶體都很少﹐而‘真正’的平等(fair) 佇列法需要核心追蹤所有運作中的連線。 在平等佇列演算法家族中﹐Stochastic Fairness Queueing (SFQ) 算是較為簡明的實作。雖然它不如其它演算法那麼精確﹐不過在保持相當平等的前提下﹐它所需要的運算也較少。 在 SFQ 中的關鍵詞是會談(conversation)(或曰流程(flow))﹐在一連串的數據封包中找到足夠的公約數﹐以區分出不同的 conversation。IP 封包裡面都帶有來源和目的位址﹐以及協定號碼﹐此情形下﹐這些參數就派得上用場了。 SFQ 由動態分配的 FIFO queues 組成﹐一個 queue 負責一個 conversation。而 discipline 則以 round-robin 的形式執行﹐每次從一個 FIFO 中送出一個封包﹐這就是為何稱之為平等(fair)的原因了。SFQ 的主要好處在於它讓不同的程式平等的分享連線﹐而避免頻寬被單一的客戶程式所佔據。然而﹐SFQ 卻不能從 bulk flows 中判定出互動(interactive)的部份 --- 這需要先於前面的 CBQ 進行篩選﹐然後才將 bulk 流量導入 SFQ 中。 <sect1>Token Bucket Filter <p> Token Bucket Filter(TBF)是一種簡單的網路佇列演算法。當網路封包經過這個佇列時﹐就會受到一個預先設定的速率限制(封包數/時間)﹐利用這個﹐可以來減緩網路瞬增流量(buffer short bursts)所造成的網路效能降低。 至於 TBF 的實作﹐則由一個緩衝區(bucket) 構成﹐不斷的被一些虛擬資訊﹐稱為 tokens ﹐按特定的速率(token rate) 填充。而緩衝區的重要參數是其體積﹐也就是它所能存儲的 token 數目。 每一個抵達的 token 抵消一個離開佇列的傳入數據封包﹐然後從緩衝區中清掉。另外﹐有兩個流程(flow) 與此演算法息息相關的﹕ token 和 data﹐這一共會帶出三種狀況﹕ <itemize> <item>數據按 <em>等於(equal)</em> 傳入 token 速率抵達 TBF。此情形之下﹐每一個傳入封包都各自獲得其匹配的 token﹐同時無延遲的通過。 <item>數據按 <em>小於(smaller)</em> token 速率抵達 TBF。每一個數據封包都自佇列送出﹐但只有一部分 token 在傳出時被刪除﹐因而 token 則會不斷的積累﹐直到填滿緩衝區的體積為止。而那些被存儲起來的 token ﹐則在瞬增流量(short data burst)出現的時候﹐以高於 token 的速率傳送數據。 <item>數據按 <em>大於(bigger)</em> token 速率抵達 TBF。此情形下則會引致過濾器出現超負荷(filter overrun) --- 傳入數據只能在不丟失前提下送出﹐直到所有積累的 token 全數耗盡為止。然之後﹐超過限制的封包就會被丟棄。 </itemize> <p>最後一種情形千萬不能掉以輕心﹐因為這會強行將頻寬撥給數據﹐而通過過濾器。至於 token 的積聚﹐則允許超出限制之瞬增流量仍可不被遺失地通過﹐但其後的持續過載﹐均會導致封包被持續的丟棄。 (譯者註﹕這個 TBF 其實不難理解﹐就是 no token no data 原則。只有當 data 獲得相應的 token 才能通過﹐而 token 的載入速率則是固定的。) Linux 核心看起來似乎超過此一規格﹐而且還允許我們限制瞬增流量的速度。然而﹐Alexey 警告我們說﹕ <tscreen> 注意﹕TBF 的最高峰值(peak rate) 相當高﹕當 MTU 為 1500 的時候﹐P_crit = 150Kbytes/sec。 所以﹐如果您需要更大的峰值﹐使用 HZ=1000 的 alpha 機器囉 :-) </tscreen> FIXME﹕不清楚是否仍有 TSC (pentium+) ﹖嗯﹐看來有那麼點兒 <!-- It's like this (from sch_tbf.c): Note that the minimal timer resolution is 1/HZ. If no new packets arrive during this period, or if the device is not awaken by EOI for some previous packet. So it's true as long as the box is relativly quiet. On a busily routing box, it's limited by the bandwidth of the interface --> FIXME﹕若不然﹐要為提高的 HZ 另闢章節 <sect1>Random Early Detect <p> RED 可算是身懷絕技。當一個 TCP/IP 連線建立起來的時候﹐連線兩端都不清楚到底頻寬會有多大。所以 TCP/IP 會先由低速開始然後逐漸加快腳步﹐最後受制於 ACKs 回應的延遲。 當一條線路滿載的時候﹐RED 就會開始丟棄封包﹐告知 TCP/IP 這條線路已達擁塞狀態﹐需要減低速度了。聰明之處在於 RED 會模擬真正的擁塞﹐同時在線路完全滿載之前開始丟棄封包。一旦線路完全飽和﹐它就擔當起交通警察的角色。 如需更詳細的資料﹐請參考 Backbone 那章。 <sect1>Ingress policer qdisc <p> 如果您不想借助於 router 或其它 Linux 機器﹐而想要限制特定的主機﹐那麼 Ingress qdisc 會是您的隨身暗器。當頻寬超過您所設定的比率的時候﹐您可以管制入向頻寬及丟棄封包。比方說﹐可以保護您的主機抵禦 SYN flood 的攻擊﹐而且也可以用來降低 TCP/IP 速度﹐也就是以丟棄封包的方法來減速。 FIXME﹕除了丟棄之外﹐我們是否可以將之分配給一個真實的 queue 呢﹖ FIXME﹕以丟棄封包來進行管制似乎並非上上之舉﹐倒不如用 token 緩衝區過濾器。不敢莽斷啦﹐Cisco CAR 也都用這個﹐而且人們似乎也受之若然。 請參考本文最後面的 <ref id="CAR" name="IOS Committed Access Rate"> 。 <!-- FIXME: This fixme was a question, not a stated fact :-) - ahu Old text: For TCP/IP connections it is desirable to simply delay the packets rather than drop them. TCP/IP responds to lost packets by abruptly reducing speed, while if the packet is simply delayed, the slowdown is much more gradual. It's also better for bandwidth usage, since dropped packets must be resent. This is not quite true, see W. Stevens TCP/IP Illustrated series - ahu --> 簡而言之﹕您可以用之來限制您電腦下載檔案有多快﹐而騰出更多頻寬給其它用途。 請參考 <ref id="SYN" name="幫貴主機抵禦 SYN floods"> 那章﹐那裡有一個例子告訴您它是如何做到的。 <sect1>DSMARK <p> 本章由 Esteve Camps <esteve@hades.udg.es> 撰寫。 <sect2>簡介 (Introduction) <p> 首先﹐再首先﹐您最好先到<url url="http://www.ietf.org/html.carters/diffserv-charter.html" name="IETF DiffServ working Group web site"> 和 <url url="http://ica1www.epfl.ch/~almesber" name="Werner Almesberger web site">(在 Linux 支援 Differentiated Services 的程式正是由他寫的) ﹐讀一讀 RFC 文件(RFC2474、RFC2475、RFC2597、以及 RFC2598)。 <sect2>Dsmark 是蝦米哇哥﹖ (What is Dsmark related to?) <p> Dsmark 是一種佇列演算戒律(discipline)﹐主要用於 Differentiated Services (也稱為 DiffServ 或簡稱 DS)。DiffServ 是兩種 actual QoS 架構之一 (另外一個叫做 Intergrated Services)﹐主要依靠 IP 封包標頭中的 DS 欄位所攜帶的數值進行判斷。 <p> 最早在 IP 設計上所提供的 QoS 層級解決方案﹐其中之一就是 IP 標頭中的 Type of Service 欄位(TOS byte)。改變這些數值﹐我們可以選擇一個 高/低 等級的吞吐量、延遲、或是可靠度。但是這並不能提供足夠的靈活性﹐以滿足較新服務(如 real-time 應用程式、互動程式、和其它)的需求。有鑒於此﹐新的架構出現了。其一就是 DiffSserv﹐它會保留 TOS bits ﹐同時重新命名 DS 欄位。 <sect2>DiffServ 指南 (Differentiated Services guidelines) <p> DiffServ 是以群組為導向的(group-oriented)。我是說﹐我們無須知道流向(flows)是如何運作的(這是 Intergrated Services 的事情)﹔我們只知道流向聚集(flow aggregations)﹐以及根據封包所屬的聚集如何應用不同的行為特性。 <p> 當封包抵達一個邊緣節點(即 DiffServ domain 的入口節點)﹐並進入 DiffSer Domain 的時候﹐我們就需要建立一些原則(policy)﹐引導 和/或 標識這些封包(所謂標識﹐就是設定 DS 欄位的數值)。然後 DiffSer Domain 的內部/核心節點就檢查這些標識/數值 ﹐以判定應用什麼樣的行為特性或 QoS 等級。 <p> 正如您所推斷的﹐DiffServ 意味著一個應用到所有 DS 規則的 domain。事實上﹐您可以這樣想像&dquot;我們會對所有進入 domain 的封包進行分類。一旦它們進入 domain﹐它們就逮屬於分類所指定的規則﹐並且每一個穿越節點都會應用這個 QoS 等級&dquot;。 <p> 實際而言﹐您可以在本地 domains 裡面應用您自定的原則﹐但是﹐當您連接到其它 DS domains 的時候﹐就需要顧及到某些 <em>服務等級協議(Service Level Agreements)</em> 。 <p> 至此﹐您或許滿腹疑團吧。DiffServ 遠比我所解釋的要複雜得多。事實上﹐您不難想像﹐我可沒那能耐將 3 個以上的 RFC 壓縮在短短 50 行裡面哦 :-) <sect2>運用 Dsmark (Working with Dsmark) <p> 根據 DiffServ 學科所指定﹐我們要區別出邊界(boundary)節點和內部(interior)節點。在流量路徑上有兩個關鍵點﹐兩者在封包到達的時候均會進行分類。在封包真正送出網路之前﹐其結果會在 DS 處理過程中的不同地方使用到。這是因為 DiffServ 程式提供了一個稱為 sk_buff 的機制﹐包括一個新的欄位﹐稱為 skb->tc_index﹐用來儲存初始分類的結果﹐用於 DS 處理中的不同目的。 <p> 該 skb->tc_index 數值會被 DSMARK qdisc 用來做初始設定﹐從每一個接收封包的 IP 標頭之 DS 欄位就可以獲得。另外﹐cls_tcindex 分類器會讀取全部或部份的 skb->tcindex 數值﹐用來選擇等級(classes)。 <p> 然而﹐首先﹐請參閱一下 DSMARK qdisc 命令以及它的參數﹕ <tscreen><verb> ... dsmark indices INDICES [ default_index DEFAULT_INDEX ] [ set_tc_index ] </verb></tscreen> 這些參數究竟代表什麼呢﹖ <itemize> <item><bf>indices</bf>: 表格體積 (mask, value)﹐一對使用。最大值為 2^n﹐且 n>=0。 <item><bf>Default_index</bf>: 如果分類器找不到符合的比對﹐則使用預設表格項目索引。 <item><bf>Set_tc_index</bf>: 指示 dsmark discipline 讀取 DS 欄位並將之存放於 skb->tc_index 上面。 </itemize> 然後讓我們看看 DSMARK 的運作。 <sect2>SCH_DSMARK 是如何工作的 (How SCH_DSMARK works.) <p> 此 qdisc 的步驟如下﹕﹕ <itemize> <item>如果我們已在 qdisc 命令中宣告 set_tc_index 選項﹐DS 欄位就會被讀取並存放於 skb->tc_index 變數中。 <item>呼叫分類器。分類器會被執行﹐並返回一個 class ID﹐存放於 skb->tc_index 變數中。假如沒有發現匹配的過濾器﹐那麼將以 default_index 選項作為 classID ﹐作為存放。假如 set_tc_index 或 default_index 都沒有被宣告﹐那後果就實在難料了。 <item>當送至內部 qdisc (您可以於此重複使用過濾結果)之後﹐從內部 qdisc 所返回的 classid 將被存放於 skb->tc_index 之內。以後我們還可以用這個數值來檢索 mask-value 表格。該封包獲得的最終分配結果﹐則取決於下一個運算的結果﹕ <tscreen><verb> New_Ds_field = ( Old_DS_field & mask ) | value </verb></tscreen> <item>這樣﹐新的數值由後面這些元素產生﹕ "anding" 這個 DS 欄位、mask values、還有這個 "ORed" 結果、以及數值參數。請參考下面這個圖例﹐以更好理解這個處理過程﹕ </itemize> <tscreen> <verb> skb->ihp->tos - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - > | | ^ | -- If you declare set_tc_index, we set DS | | <-----May change | value into skb->tc_index variable | |O DS field | A| |R +-|-+ +------+ +---+-+ Internal +-+ +---N|-----|----+ | | | | tc |--->| | |--> . . . -->| | | D| | | | | |----->|index |--->| | | Qdisc | |---->| v | | | | | |filter|--->| | | +---------------+ | ---->(mask,value) | -->| O | +------+ +-|-+--------------^----+ / | (. , .) | | | | ^ | | | | (. , .) | | | +----------|---------|----------------|-------|--+ (. , .) | | | sch_dsmark | | | | | +-|------------|---------|----------------|-------|------------------+ | | | <- tc_index -> | | | |(read) | may change | | <--------------Index to the | | | | | (mask,value) v | v v | pairs table - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -> skb->tc_index </verb> </tscreen> <P> 那如何做標識呢﹖只需修改您想重新標識的分類(class)之 mask 和 value 就可以了。請參考下一行程式﹕ <tscreen> tc class change dev eth0 classid 1:1 dsmark mask 0x3 value 0xb8 </tscreen> 這會改變那對存於雜湊表格內的 mask 和 value ﹐並重新將封包標識為屬於 class 1:1 。您必須 "修改" 這個數值﹐因為預設的數值 (mask, value) 會獲得初始值(看後面的表格)。 <p> 現在﹐我們將解釋一下 TC_INDEX 過濾器是如何工作的﹐以及如何滿足它的需求。另外﹐TCINDEX 過濾器還可以用在其它設定上﹐並不只限於 DS 服務之中。 <sect2>TC_INDEX 過濾器 (TC_INDEX Filter) <p> 宣告一個 TC_INDEX 過濾器的基本命令如下﹕ <tscreen> <verb> ... tcindex [ hash SIZE ] [ mask MASK ] [ shift SHIFT ] [ pass_on | fall_through ] [ classid CLASSID ] [ police POLICE_SPEC ] </verb> </tscreen> 這裡﹐我們沿用過往例子解釋 TC_INDEX 的運算模式。請特別留意標為粗體的文字﹕ &nl;&nl; tc qdisc add dev eth0 handle 1:0 root dsmark indices 64 <bf>set_tc_index</bf>&nl; tc filter add dev eth0 parent 1:0 protocol ip prio 1 tcindex <bf>mask 0xfc shift 2</bf>&nl; tc qdisc add dev eth0 parent 1:0 handle 2:0 cbq bandwidth 10Mbit cell 8 avpkt 1000 mpu 64&nl; # EF traffic class&nl; tc class add dev eth0 parent 2:0 classid 2:1 cbq bandwidth 10Mbit rate 1500Kbit avpkt 1000 prio 1 bounded isolated allot 1514 weight 1 maxburst 10&nl; # Packet fifo qdisc for EF traffic&nl; tc qdisc add dev eth0 parent 2:1 pfifo limit 5&nl; tc filter add dev eth0 parent 2:0 protocol ip prio 1 <bf>handle 0x2e</bf> tcindex <bf>classid 2:1 pass_on</bf>&nl; &nl;&nl; (這個程式尚不完整。它僅僅是取材於 iproute2 套件中的 EFCBQ 範例而已)。 <p> 首先﹐假設我們收到一個標識為 EF 的封包。如果您讀過 RFC2598﹐那您應該看到 EF 流量的 DSCP 建議值為 101110。這表示 DS 欄位將會是 10111000 (要知道 TOS byte 中的次重要位元並不會用於 DS 中)﹐或是以十六進位表示為 0xb8 。 <p> <tscreen> <verb> TC INDEX FILTER +---+ +-------+ +---+-+ +------+ +-+ +-------+ | | | | | | | |FILTER| +-+ +-+ | | | | | |----->| MASK | -> | | | -> |HANDLE|->| | | | -> | | -> | | | | . | =0xfc | | | | |0x2E | | +----+ | | | | | | | . | | | | | +------+ +--------+ | | | | | | . | | | | | | | | | -->| | . | SHIFT | | | | | | | |--> | | . | =2 | | | +----------------------------+ | | | | | | | | | CBQ 2:0 | | | | | +-------+ +---+--------------------------------+ | | | | | | | +-------------------------------------------------------------+ | | DSMARK 1:0 | +-------------------------------------------------------------------------+ </verb> </tscreen> <p> 封包抵達之後﹐將 DS 欄位設定為 0xb8。正如我們前面所解釋的﹐例中的 dsmark qdisc 被鑒定為 id 1:0 ﹐讀取 DS 欄位﹐並存放於 skb->tc_index 變數之內。例中的下一步﹐相當於該 qdisc 關聯的過濾器(例中第 2 行)。這將進行下一個運算﹕ <tscreen> <verb> Value1 = skb->tc_index & MASK Key = Value1 >> SHIFT </verb> </tscreen> <p> 例中﹐MASK=0xFC i SHIFT=2。 <tscreen> <verb> Value1 = 10111000 & 11111100 = 10111000 Key = 10111000 >> 2 = 00101110 -> 0x2E in hexadecimal </verb> </tscreen> <p> 所返回的數值就是 qdisc 內部過濾器之 handle(本例中為 identifier 2:0)。假如找到這個 ID 的過濾器﹐原則和測量條件就獲得確認 (本例中的過濾器就包括這個)﹐同時會返回 classid(本例中為﹕classid 2:1)﹐並且存放於 skb->tc_index 變數裡面。 <P> 不過﹐假如找到任何帶此 ID 的過濾器﹐其結果將取決於 fall_through 旗標的宣告。然則﹐返回的 classid 則以數值鍵值(value key)為準。否則﹐將返回一個錯誤﹐同時處理程序將繼續剩餘的過濾器。小心哦﹐如果您使用 fall_through 旗標的話﹐假如 skb->tc_index 變數之數值和 class id 之間存在一個簡單的關聯﹐即可完成。 <P> 最後要註解的參數是 hash 和 pass_on。前者關乎雜湊表格的體積﹔而 pass_on 則指示﹕如果沒有發現與該過濾器結果相等的 classid﹐則嘗試下一個過濾器。預設動作為 fall_through (看下一個表格) <P> 最後﹐讓我們看看有哪些可能的數值﹐是可以用來設定全部 TCINDEX 數值的﹕ <tscreen> <verb> TC Name Value Default ----------------------------------------------------------------- Hash 1...0x10000 Implementation dependent Mask 0...0xffff 0xffff Shift 0...15 0 Fall through / Pass_on Flag Fall_through Classid Major:minor None Police ..... None </verb> </tscreen> <P> 此類過濾器實在非常厲害。它必須探勘所有的可能性。另外﹐這個過濾器不僅可以用在 DiffServ 設定中﹐還可以應用在其它種類的過濾器上面。 <p> 我建議您抽空看看 iproute2 套件中的所有 DiffServ 範例。我向各位保證﹐我將會竭盡全力儘快完成文字的部份。另外﹐我所作的解釋全都是許許多多測試的結果。 <sect>其它資訊(Other information) <p> Esteve Camps Chust <marvin@grn.es>&nl; 本文取材於我的論文"QoS Support in Linux", September 2000.&nl; <P> 源文件﹕&nl; <itemize> <item><url url="http://ica1www.epfl.ch/~almesber" name="Draft-almesberger-wajhak-diffserv-linux-01.txt">。 <item>iptoute2 套件中的範例。 <item><url url="http://www.qosforum.com/white-papers/qosprot_v3.pdf" name="White Paper-QoS protocols and architectures"> 和 <url url="http://www.qosforum.com/docs/faq" name="IP QoS Frequently Asked Questions">。兩者均屬於 <em>Quality of Service Forum</em>。 </itemize> <sect1>WRR <p> 此一 qdisc 並不包括在標準的核心裡面﹐但可以從 <url url="http://wipl-wrr.dkik.dk/wrr/" name="這裡"> 下載。目前來說﹐此 qdisc 僅在 Linux 2.2 核心上面測試過﹐應該也可以在 2.4 核心工作的。 WWR qdisc 將頻寬以 weighted round robin 的形式分配給各類別。也就是﹐像 CBQ qdisc 那樣﹐它將類別(classes)包含在可插入的多樣化 qdiscs 中。所有類別均有足夠的請求能力獲得頻寬﹐再均衡的按比重分配給相關類別。比重通常可以用 <tt>tc</tt> 程式來設定。但是它們(比重)能夠自動的降低﹐以讓給進行大量數據傳送的類別使用。 此 qdisc 內建有一個分類器(classifier)﹐將來自或送往不同機器的封包分配至不同的類別裡去。無論 MAC 還是 IP、不管來源還是目的位址﹐都可以使用。不過﹐MAC 位址只能在這台 Linux 主機作為 ethernet bridge 的時候﹐才能使用。所有類別均自動的依據所見封包分配給不同的機器。 在那些諸如宿舍那樣的站台﹐存在為數眾多且又互不相干的個體分享同一 Internet 連線環境中﹐此 qdisc 簡直是靈丹妙藥。透過一套 script﹐為這樣一個站台設定出一個恰如其分的運轉狀態﹐正是 WRR 套件的精髓所在。 <sect>Netfilter 與 iproute - 封包標識 (Netfilter & iproute - marking packets) <p> 到目前為止﹐我們已經見識過 iproute 的能耐了﹐而 netfilter 也曾數度提及。現在是要好好的看看<url name="Rusty's Remarkably Unreliable Guides" url="http://netfilter.samba.org/unreliable-guides/"> 的時候了。而 netfilter 的芳蹤﹐可以從 <url name="這裡" url="http://netfilter.filewatcher.org/"> 覓得。 Netfilter 可以讓我們過濾封包﹐或是修改它們的標頭(headers)。其中一絕是我們可以用號碼將封包標識起來﹐用 --set-mark 就可以做到。 舉個例子﹐下面的命令將所有送給 port 25 的封包標﹐也就是外送郵件﹐給識起來﹕ <tscreen><verb> # iptables -A PREROUTING -i eth0 -t mangle -p tcp --dport 25 \ -j MARK --set-mark 1 </verb></tscreen> 讓我們假設有多條連線﹐其中一個比較快(當然每一 magabyte 也較貴)﹐而另一個比較慢﹐但使用固定價格。我們理所應當想把外送郵件走那個較便宜的 router 。 我們已經將封包標識為 '1' 了﹐現在就指引路由原則資料庫做如下事情﹕ <tscreen><verb> # echo 201 mail.out >> /etc/iproute2/rt_tables # ip rule add fwmark 1 table mail.out # ip rule ls 0: from all lookup local 32764: from all fwmark 1 lookup mail.out 32766: from all lookup main 32767: from all lookup default </verb></tscreen> 現在我們產生 mail.out 表格﹐將個路由指向較慢但也較便宜的線路﹕ <tscreen><verb> # /sbin/ip route add default via 195.96.98.253 dev ppp0 table mail.out </verb></tscreen> 大功告成﹗假如我們想要網開一面﹐有很多辦法可以做到的。我們可以修改 netfilter 的敘述﹐將某些特定主機豁免出來﹐或是為那些豁免主機插入一條規則﹐使用較低的優先值指向 main 表格。 我們還可以運用此一絕技來實踐 TOS 位元(bits)﹐只需將封包以不同數字標識為不同等服務類型﹐同時建立規則將之跑起來即可。用此辦法﹐我們甚至指定一條 ISDN 線路給互動連線呢。 值得一提的是﹐此法在 NAT('masquerading') 主機上也同具功效哦。 非常重要﹕我們收到一個報告指出 MASQ 和 SNAT 至少和封包標識會有所抵觸。Rusty Russel 已在如下網頁作過解釋﹕<url url="http://lists.samba.org/pipermail/netfilter/2000-November/006089.html" name="this posting">。請關閉逆向路徑過濾功能﹐以解決此一問題。 請留意﹕您需要將某些核心選項打開才行﹕ <tscreen><verb> IP: advanced router (CONFIG_IP_ADVANCED_ROUTER) [Y/n/?] IP: policy routing (CONFIG_IP_MULTIPLE_TABLES) [Y/n/?] IP: use netfilter MARK value as routing key (CONFIG_IP_ROUTE_FWMARK) [Y/n/?] </verb></tscreen> 另外﹐還請看看 Cookbook 中的 <ref id="SQUID" name="Transparent web-caching using netfilter, iproute2, ipchains and squid"> 。 <sect>更多的分類器 (More classifiers) <p> 分類器(classifier)就是核心用來決定封包需要送入哪一個佇列的方法。有許多形形色色的分類器﹐各司其職。 <descrip> <tag>fw</tag> 根據防火牆如何標識封包來作判斷。 <tag>u32</tag> 根據封包裡面的欄位作判斷 (例如﹕來源 IP 位址﹐等)。 <tag>route</tag> 根據封包之路由作判斷。 <tag>rsvp, rsvp6</tag> 根據目標(目的位址﹐協定)﹐或以來源作判斷。 <tag>tcindex</tag> FIXME﹕有待補充 </descrip> 請注意﹐大體上您有多種辦法來分類封包﹐但會降低系統之整體執行效能。 一般而言﹐分類器都能接受不同參數。為方便起見﹐茲列如下﹕ <descrip> <tag>protocol</tag> 分類器所能接受的協定。通常您可能只會接受 IP 流量。必須指定。 <tag>parent</tag> 為分類器指定接管(handle)﹐只能是已經存在的類別(class)。必須指定。 <tag>prio</tag> 分類器的優先等級。數值越高越快。 <tag>handle</tag> 指定不同事物到不同的過濾器去。 FIXME﹕增加選項 </descrip> 下面的章節均假設您要將流量引導至 <tt>HostA</tt> ﹐還假設頂層(root)類別已經設定為 1﹕﹐同時您要將挑選出來的流量送至 1:1 去。 <sect1>"fw" 分類器 (The "fw" classifier) <p> "fw" 分類器要依靠防火牆把需要引導的封包標識起來。所以﹐我們必須先設定好防火牆﹐為它們打標籤。 <tscreen><verb> # iptables -I PREROUTING -t mangle -p tcp -d HostA \ -j MARK --set-mark 1 </verb></tscreen> 好了﹐所有傳給該主機的封包都被標識為 1。現在我們建立規則﹐以真正引導封包﹐我們只要指定好被標識為 1 的封包要送到類別 1:1 那邊去。透過如下命令就可以﹕ <tscreen><verb> # tc filter add dev eth1 protocol ip parent 1:0 prio 1 handle 1 fw classid 1:1 </verb></tscreen> 命令本身應該說得蠻清楚的了。附在 1:0 類別的過濾器獲得的優先值為 1﹐過濾那些被防火牆標識為 1 的封包﹐再送到類別 1:1 那邊去。注意﹐這裡的 handle 要如何使用﹐取決於封包如何標識。 不經寒徹骨﹐哪得梅花香﹖這已是較為簡單的方法了﹐至於其它方法﹐我覺得更為難懂。注意﹐您可以完全將防火牆程式的功能應用在此分類器上面﹐包括比對 MAC 位址、用戶身份、以及所有其它防火牆所能比對的事物。 <sect1>"u32" 分類器 (The "u32" classifier) <p> U32 過濾器是目前實作中所能找到的最強勁的過濾器。它完全依靠雜湊表(hashing tables)﹐配合眾多過濾規則而變得扎實耐用。 最簡單的做法是﹐U32 過濾器有一整列的記錄(records)﹐各自包含兩個欄位﹕選擇器(selector)和動作(action)。選擇器(後面會介紹)﹐會與當前處理的 IP 封包做比較﹐當碰到第一個符合的封包﹐然後就作出相應的動作。最簡單的動作﹐是將封包送至指定的 CBQ 類別去。 我們可用 <tt>tc filter</tt> 命令程式去設定過濾器﹐一共有 3 個部份﹕過濾器的規格(specification)、選擇器、以及動作。過濾器規則可以如下定義﹕ <tscreen><verb> tc filter add dev IF [ protocol PROTO ] [ (preference|priority) PRIO ] [ parent CBQ ] </verb></tscreen> 其中的 <tt>protocol</tt> 欄位說明過濾器所適用的協定﹐我們這裡只就 <tt>ip</tt> 協定進行討論。至於 <tt>preference(偏好值)</tt> 欄位(也可以用<tt>priority</tt> 來代替)﹐設定當前過濾器的優先值。這很重要﹐因為您或許會有好幾個過濾器(規則列表)﹐各自擁有不同的優先值。每一列規則按照規則新增順序通過﹐然後才處理低優先值(高偏好值)的規則列。最後的 <tt>parent</tt> 欄位定義出過濾器所屬的 CBQ 樹頂(如 1:0)。 以上選項均適用於所有過濾器﹐非 U32 獨美。 <sect2>U32 選擇器 (U32 selector) <p> U32 選擇器包含式樣(pattern)定義﹐以比對當前處理的封包。它一絲不苟的定義出哪些封包標頭的位元(bits) 要用來比對﹐心無旁騖﹐然卻四兩撥千斤。不如讓我們看看以下範例﹐直接取自一個複雜且真實的過濾器﹕ <tscreen><verb> # filter parent 1: protocol ip pref 10 u32 fh 800::800 order 2048 key ht 800 bkt 0 flowid 1:3 \ match 00100000/00ff0000 at 0 </verb></tscreen> <p> 我們暫時不用管第一行 --- 全部參數度是用來描述過濾器之雜湊表而已。且讓我們仔細看看關於選擇器那一行﹐也就是帶 <tt>match</tt> 關鍵字的那行。選擇器會比對第 2 個 byte 為 0x10(0010) 的 IP 標頭。或許您已猜到﹐那個 00ff 數字就是比對遮罩(mask)。目前為 0xff﹐所以﹐這個 byte 只能是 0x10。然後 <tt>at</tt> 關鍵字意思是說﹐這個比對是從指定的 offset(以 bytes 算)開始 --- 在目前範例中﹐在封包的一開始處。換成我們的人類語言來說的話﹐如果封包的 Type of Serice 欄位帶有 `low delay` 位元設定﹐那這個比對就符合了。讓我們在看看另一個規則吧﹕ <tscreen><verb> # filter parent 1: protocol ip pref 10 u32 fh 800::803 order 2051 key ht 800 bkt 0 flowid 1:3 \ match 00000016/0000ffff at nexthdr+0 </verb></tscreen> <p> 此處﹐有一個 <tt>nexthdr</tt> 選項﹐代表 IP 封包裡面的下一個標頭﹐例如上層協定的標頭。這個比對也是從下一個標頭的一開始處進行。比對應該出現在標頭首 32-bit 中的第二個字(word)。在 TCP 和 UDP 協定裡﹐此欄位包含封包目的端埠口(port)。此數字以 big-endian 格式顯示﹐例如 older 位元排前面﹐所以我們只要將 0x0016 換成十進位就是 22 了﹐換而言之﹐如果這是 TCP 的話﹐那他就是 SSH 服務。正如您所猜的﹐如果離開相關承接(我們後面再述)﹐這個比對會變得不明所以。 <p> 當我們對前述都有一定了解之後﹐就會發現如下這個選擇器其實蠻好理解的﹕<tt>match c0a80100/ffffff00 at 16</tt> 。我們只要從 IP 標頭開始﹐比對第 17 個 byte 起的 3 個 byte 即可。這會比對所有目的地為網路 192.168.1/24 的封包。看過這些範例之後﹐然後讓我們將所學的歸納一下吧。 <sect2>通用選擇器 (General selectors) <p> 通用選擇器定義出式樣(pattern)、遮罩(mask)、還有封包內容裡關於比對式樣的 offsest。使用通用選擇器﹐您實際上可以比對 IP (或上層) 標頭裡的每一個單獨的位元(bit) 。比較後面介紹的特定選擇器﹐它們更難讀寫。通用選擇器的語法如下﹕ <tscreen><verb> match [ u32 | u16 | u8 ] PATTERN MASK [ at OFFSET | nexthdr+OFFSET] </verb></tscreen> <p> <tt>u32</tt>、<tt>u16</tt>、或 <tt>u8</tt> 這些關鍵字﹐各自指定位元裡的式樣長度。PATTERN 和 MASK 必須接在前面關鍵詞定義的長度後面。至於 OFFSET 參數﹐則以 byte 為單位﹐指定開始比對的 offset 所在。如果 <tt>nexthdr+</tt> 有設定﹐那麼 offset 則相對的從上層協定標頭開始算起。 <p> 範例﹕ <tscreen><verb> # tc filter add dev ppp14 parent 1:0 prio 10 u32 \ match u8 64 0xff at 8 \ flowid 1:4 </verb></tscreen> <p> 如果存活期(TTL)為 64 的話﹐封包就符合比對。從 IP 標頭數起﹐第 8 個 byte 開始就是 TTL 欄位了。 <tscreen><verb> # tc filter add dev ppp14 parent 1:0 prio 10 u32 \ match u8 0x10 0xff at nexthdr+13 \ protocol tcp \ flowid 1:3 \ </verb></tscreen> <p> 此規則僅比對帶 ACK 位元設定的 TCP 封包。這裡我們可以看到一個範例中使用兩個選擇器﹐最終結果則將兩個結果用 AND 邏輯運算得出。如果我們仔細的看看 TCP 標頭結構圖﹐我們會發現 ACK 位元﹐是 TCP 標頭第 14 個 byte 算起(<tt>at nexthdr+13</tt>)第 2 個 older 位元(0x10)。至於第二個選擇器﹐如果我們想試試克難的方法﹐不指定選擇器使用 <tt>protocol tcp</tt>﹐可這樣寫﹕ <tt>match u8 0x06 0xff at 9</tt>﹐因為 TCP 的協定號碼是 6﹐位於 IP 標頭的第 10 個 byte。相對而言﹐在此範例中﹐我們不能用特定選擇器來做第一個比對 --- 這是因為沒有特定選擇器可以比對 TCP ACK 位於之故。 <sect2>特定選擇器 (Specific selectors) <p> 如下表格列出了本章作者所從 <tt>tc</tt> 程式中發現的全部特定選擇器。他們幫您省掉許多工作﹐同時讓您的過濾器設定更具可讀性。 FIXME: 表格存放位置 --- 存於另外的檔案﹕selector.html。 FIXME: 只有波蘭語 :-( FIXME: 有待轉成 sgml 格式。 範例: <tscreen><verb> # tc filter add dev ppp0 parent 1:0 prio 10 u32 \ match ip tos 0x10 0xff \ flowid 1:4 </verb></tscreen> 以上規則會比對那些 TOS 欄位設為 0x10 的封包。TOS 欄位從封包的第 2 個 byte 開始﹐並佔一個 byte 的長度﹐這樣好比我們另寫一個相等的通用選擇器﹕<tt>match u8 0x10 0xff at 1</tt>。由此﹐我們就可以一窺 U32 過濾器的內裡乾坤 --- 特定規則都會被轉換為通用規則﹐同時以此形式存於核心記憶體之內。舉一反三 --- <tt>tcp</tt> 和 <tt>udp</tt> 選擇器也是如法泡制﹐這也是為何您不能用單獨的 <tt>match tcp dst 53 0xffff</tt> 選擇器﹐來比對那些送給特定埠口(port) 的 TCP 封包 --- 它們也同時比對送至此埠口的 UDP 封包。另外﹐您不要忘了指定協定哦﹐並且最後產生如下規則﹕ <tscreen><verb> # tc filter add dev ppp0 parent 1:0 prio 10 u32 \ match tcp dst 53 0xffff \ match ip protocol 0x6 0xff \ flowid 1:2 </verb></tscreen> <!-- TODO: describe more options match offset hashkey classid | flowid divisor order link ht sample police --> <sect1>"route" 分類器 (The "route" classifier) <p> 此分類過濾器根據路由表格(routing tables)而定。當一個封包穿越分類(classes)抵達被標識為 "route" 的過濾器之後﹐它會按照路由表格的資訊分離(split)這個封包。 <tscreen><verb> # tc filter add dev eth1 parent 1:0 protocol ip prio 100 route </verb></tscreen> 這裡﹐我們新增一個 route 分類器給上游節點(parent node) 1:0 ﹐其優先值為 100。當封包抵達這個節點後(因為這是頂層(root) 分類﹐所以馬上生效)﹐它會查詢路由表格﹐並且﹐如果有一個比對符合的話﹐就把它送給指定的分類﹐同時賦予 100 的優先值。最後﹐您增加一個恰當的路由記錄﹐交由動作(action)處理﹕ 這裡的訣竅是﹐要根據目的(destination)或來源(source)來定義一個所謂的 'realm' ﹐參考如下﹕ <tscreen><verb> # ip route add Host/Network via Gateway dev Device realm RealmNumber </verb></tscreen> 比方說﹐我們可以定義目的網路 192.168.10.0﹐它的 realm 號碼是 10﹕ <tscreen><verb> # ip route add 192.168.10.0/24 via 192.168.10.1 dev eth1 realm 10 </verb></tscreen> 在增加 route 過濾器之後﹐我們可以使用 realm 號碼來代表網路或主機﹐同時指定路由如何配對(match)過濾器。 <tscreen><verb> # tc filter add dev eth1 parent 1:0 protocol ip prio 100 \ route to 10 classid 1:10 </verb></tscreen> 以上規則是說﹐送給網路 192.168.10.0 的封包﹐會配對 class id 1:10。 Route 過濾器還可以用來比對源路由(source routes)。例如﹐有一個子網接到 Linux router 的 eth2 界面﹕ <tscreen><verb> # ip route add 192.168.2.0/24 dev eth2 realm 2 # tc filter add dev eth1 parent 1:0 protocol ip prio 100 \ route from 2 classid 1:2 </verb></tscreen> 這裡﹐過濾器指定出來自子網 192.168.2.0 (realm 2) 的封包﹐就配對 class id 1:2 。 <sect1>"rsvp" 分類器 (The "rsvp" classifier) <p>FIXME: 有待補充 <sect1>"tcindex" 分類器 (The "tcindex" classifier) <p>FIXME: 有待補充 <sect>核心網路參數 (Kernel network parameters) <p> 核心有很多參數可以調節﹐以適用於不同的狀況。通常﹐ 99% 的安裝﹐使用預設參數就可以了﹐不過﹐與其這樣的話﹐此稱為 Advanced 的 HOWTO 就浪得虛名了﹗ 有空請看看 /proc/sys/net﹐或有意外收穫哦。只是﹐剛開始的時候﹐並非所有東西都會寫在這裡﹐我們盡力而為就是了。 <sect1>逆向路徑過濾 (Reverse Path Filtering) <p> 預設情況下﹐router 會路由所有東西﹐就算該封包‘顯然’不屬於貴網路的。常見的例子﹐莫過於將私有 IP 泄漏到 internet 上去。假如您有一個界面﹐其上設定的路由為 195.96.96.0/24﹐那您不會預期來自 212.64.94.1 的封包會到達這裡。 許多人都想關閉此一功能﹐因此核心設計者也打開了方便之門。在 <file>/proc</file> 裡面有些檔案﹐透過它們您可以讓核心為您做到這點。此方法被稱為 "逆向路徑過濾(Reverse Path Filtering)"。基本上﹐假如對此封包作出的回應﹐不是循其進入的界面送出去﹐那它就被視之為一個 bogus 封包﹐而被置之不理。 <tscreen><verb> # for i in /proc/sys/net/ipv4/conf/*/rp_filter ; do { echo 2 > $i } done </verb></tscreen> 我們用上面的例子來看看﹐如果一個封包從 eth1 到達 Linux router﹐自稱來自 Office+ISP 這個 subnet﹐那它就會被丟棄。同樣地﹐如果封包來自 Office 這個 subnet﹐卻自稱來自防火牆外部﹐那它也同樣會被丟棄。 上面是完全(full) 逆向路徑過濾。預設是只根據直接相連網路的 IP 進行過濾﹐這時因為完全過濾會打斷所謂的非對稱路(asymmetric routing)情形 (也就是﹐封包從一端進入﹐而從另一端出去﹐例如衛星流量傳送﹐或是您的網路使用動態 (bgp、ospf、rip)路由。資料從衛星接受碟下傳進來﹐而回應則從正常的專線送回去)。 假如您屬於這種例外(您應該心中有數)﹐那您可以簡單的在衛星數據進入的界面上關閉 <file>rp_filter</file>。如果您想要看看封包是否真的會被丟棄﹐那可以透過同一目錄的 <file>log_martians</file> 檔案﹐告訴核心將之記錄到 syslog 上面去。 <tscreen><verb> # echo 1 >/proc/sys/net/ipv4/conf/<interfacename>/log_martians </verb></tscreen> FIXME: 不清楚設定 conf/{default,all}/* 下面的檔案是否足夠﹖ --- martijn <sect1> 尚不明確的設定 (Obscure settings) <p> 嗯﹐太多參數其實都可以修改啦。我們會試著將它們儘數羅列出來﹐而且(部份)會寫在<file>Documentation/ip-sysctl.txt</file> 中。 其中有些設定的預設值會有不同﹐看您在編譯核心的時候﹐是否以 'Yes' 來回答 'Configure as router and not host' 囉。 <sect2>一般的 ipv4 (Generic ipv4) <p> 請留意﹐大多數的速率(rate)限制功能都不適用於 loopback 之上﹐所以請勿在本機上進行測試。這個限制受制於所謂的 'jiffies' ﹐且它們硬性的只能使用前述的 token bucket filter。 核心有一個內部時鐘﹐每秒跑 'HZ' 跳(或曰 'jiffies' )。在 intel 上面﹐'HZ' 大概為 100 (譯者註﹕我猜這裡指所謂的 '內頻' 而言吧﹖新的主機板大都支援 133 這個速度)。比方說﹐設定一個 *_rate 檔案為 50 好了﹐則可每秒處理 2 個封包。而 token bucket filter 也可以被設定為﹐如果有足夠的 tokens 存儲的話﹐能讓每一個 burst (瞬增流量)可達 6 個封包。 如下列表中的一些項目﹐拷貝自 /usr/src/linux/Documentation/networking/ip-sysctl.txt﹐由 Alexey Kuznetsov <kuznet@ms2.inr.ac.ru> and Andi Kleen <ak@muc.de> 所撰寫。 <descrip> <tag>/proc/sys/net/ipv4/icmp_destunreach_rate</tag> 如果核心認為它不能傳送某一封包﹐則將之丟棄﹐同時向封包來源送出一個 ICMP 告知其結果。 <tag>/proc/sys/net/ipv4/icmp_echo_ignore_all</tag> 對所有封包均不作出 echo 。請勿將預設值設為啟動﹐除非您被利用為一個 DoS 工具的跳站﹐那或許有用。 <tag>/proc/sys/net/ipv4/icmp_echo_ignore_broadcasts [Useful]</tag> 如果您 ping 網路的廣播位址﹐那所有主機都會做出回應﹐這將是一個非常便利的 dinal-of-servie 工具。將這裡設定為 1 則會忽略這些廣播訊息。 <tag>/proc/sys/net/ipv4/icmp_echoreply_rate</tag> 送出 echo 回應至任意目的端之速率 <tag>/proc/sys/net/ipv4/icmp_ignore_bogus_error_responses</tag> 設定它會忽略因網路主機對那些送至理解為廣播位址之框包作出抵抗而引起的 ICMP 錯誤。 <tag>/proc/sys/net/ipv4/icmp_paramprob_rate</tag> 一個相對未知的 ICMP 信息﹐其發送是因損毀的 IP 或 TCP 標頭封包而引發的。透過這個檔﹐您可以控制其發送的速率。 <tag>/proc/sys/net/ipv4/icmp_timeexceed_rate</tag> 這可以成為導致 traceroute 出現 '星星(* * *)' 的主因。這裡限制送出 ICMP Time Exceeded 訊息的數目。 <tag>/proc/sys/net/ipv4/igmp_max_memberships</tag> 主機上所傾聽的最大 igmp (multicast) socket 數量。&nl; FIXME: 不知道是否真的? <tag>/proc/sys/net/ipv4/inet_peer_gc_maxtime</tag> FIXME: 增加一個關於 inet peer storage 之簡短說明?&nl; 廢物收集(garbage collection)通過的最長間隔。這個間隔會影響到 pool 中記憶體的低位(或缺少)壓力(pressure )。 以 jiffies 測量。 <tag>/proc/sys/net/ipv4/inet_peer_gc_mintime</tag> 廢物收集(GC)通過的最短間隔。這個間隔會影響到 pool 中記憶體的高位壓力。 以 jiffies 測量。 &nl;&nl; (譯者註﹕不清楚上面兩個解釋是否正確﹐因為原文中﹐作者均解釋為"Minimum interval between garbage collection passes"﹐我這裡私自將第一個改為“最長間隔”。) <tag>/proc/sys/net/ipv4/inet_peer_maxttl</tag> 最高存活期項目。在此期限到達之後﹐如果沒有在 pool 上構成記憶體壓力的話(例如﹐pool 中的項目數目非常少)﹐不使用的項目將會逾時。以 jiffies 測量。 <tag>/proc/sys/net/ipv4/inet_peer_minttl</tag> 最低存活期項目。在重組端必須要有足夠的碎片(fragment)存活期。這個最低存活期必須保證 pool 體積是否少於 inet_peer_threshold。以 jiffies 測量。 <tag>/proc/sys/net/ipv4/inet_peer_threshold</tag> INET peer storage 的適當體積。由此出發點開始的項目都會被強制拋棄。此出發點還判定項目的存活期﹐以及廢物收集通過的時間間隔。項目越多﹐存活期越低﹐GC 間隔越短。 <tag>/proc/sys/net/ipv4/ip_autoconfig</tag> 如果主機透過 RARP、BOOTP、DHCP、或類似方式獲得其 IP 設定﹐這檔會設為 1﹐否則為 0。 <tag>/proc/sys/net/ipv4/ip_default_ttl</tag> 封包的存活期。設為 64 應是安全的。如果您的網路超大的﹐那就提昇此值。千萬不要隨便亂玩 --- 如遇到路由迴圈(routing loop)將導致更嚴重的災難。在某些情形之下﹐您甚至會降低此值。 <tag>/proc/sys/net/ipv4/ip_dynaddr</tag> 假如您要用動態界面位址做 dial-on-demand ﹐那就設定它。一旦您的請求界面起來之後﹐所有看不到回應的本地 TCP socket 都會重新捆綁(rebound)﹐以獲得正確的位址。假如遇到啟動界面的連線自己不工作﹐但再試一次卻又可以的情形﹐設定這個可解決這個問題。 <tag>/proc/sys/net/ipv4/ip_forward</tag> 是否要核心傳送封包。預設是關閉的。 <tag>/proc/sys/net/ipv4/ip_local_port_range</tag> 對外連線時所使用的本地埠口範圍。預設值事實上蠻小的﹕1024 到 4999。 <tag>/proc/sys/net/ipv4/ip_no_pmtu_disc</tag> 如果您要關閉 Path MTU discovery --- 一種在路徑上判定可接受的最大傳送單位(Maximum Transfer Unit)數值的技術﹐那就設定這裡。 <tag>/proc/sys/net/ipv4/ipfrag_high_thresh</tag> IP 碎片重組所需的最大記憶體。當記憶體的 ipfrag_high_thresh bytes 因此目的獲得分配之後﹐碎片處理程序會擱置封包﹐直到達到 ipfrag_low_thresh 為止。 <tag>/proc/sys/net/ipv4/ip_nonlocal_bind</tag> 如果您想讓應用程式能夠捆綁到一個不屬於該系統的位址﹐就需要設定這裡。當機器使用非固定(或是動態)線路的時候﹐這功能就很有用了﹐因而﹐當您的線路斷掉之後﹐您的服務仍可啟動而且捆綁到特定的位址之上。 <tag>/proc/sys/net/ipv4/ipfrag_low_thresh</tag> IP 碎片重組所需的最小記憶體。 <tag>/proc/sys/net/ipv4/ipfrag_time</tag> IP 碎片保留在記憶體中的秒數。 <tag>/proc/sys/net/ipv4/tcp_abort_on_overflow</tag> 一個 boolean 旗標﹐處於大量進入連線的時候控制其行為特性。當打開之後﹐會讓核心在服務出現超載的時候主動送出 RST 封包。 <tag>/proc/sys/net/ipv4/tcp_fin_timeout</tag> 如果 socket 是有我們這端結束的﹐socket 保持 FIN-WAIT-2 狀態的時間。連線端或會掛斷且不會從它那端結束﹐或是甚至意外終結(died)。預設值為 60 秒。過去在 2.2 核心中是 180 秒﹐您可以復原它﹐但請記住﹐如果您的機器為一台盈負載(underloaded) 網頁伺服器﹐您可能要冒著記憶體被大量死亡封包填滿的風險﹐FIN-WAIT-2 sockets 的危險性低於 FIN-WAIT-1 ﹐因為它們最多只吃 1.5K 的記憶體﹐但是它們存在時間更長。另外參考 tcp_max_orphans。 <tag>/proc/sys/net/ipv4/tcp_keepalive_time</tag> 當 keepalive 打開之後﹐TCP 要多久送出 keepalive 信息。&nl; 預設為 2 小時。 <tag>/proc/sys/net/ipv4/tcp_keepalive_intvl</tag> 當一個探測(probe)沒有獲得確認之後﹐隔多久要被重送。&nl; 預設是 75 秒。 <tag>/proc/sys/net/ipv4/tcp_keepalive_probes</tag> 在決定掛斷連線之前﹐要送出多少個 keepalive 探測。&nl; 預設值為﹕9 。&nl; 再乘上 tcp_keepalive_intvl﹐就是在一條線路在送出 keepalive 之後﹐所允許的不回應時間。 <tag>/proc/sys/net/ipv4/tcp_max_orphans</tag> 對於那些沒有附屬於任何使用者檔案 handle 的 TCP sockets﹐系統所能處理的最大數量。假如超過這個數量﹐那麼這些無人看管的連線將會被立即重設(reset)﹐並同時顯示警告信息。之所以要設定這個限制﹐純粹為了抵禦那些簡單的 DoS 攻擊﹐千萬不要依賴這個或是人為的降低這個限制﹐不過﹐如果網路條件需要比預設值更多﹐而且調整網路服務獲得拖延並強制性砍掉這類狀態﹐則可以提高它(或許還要增加記憶體)。再提醒一次﹕每一個這樣的 orphan 都會吃掉 ~64K 不能置換的記憶體。 <tag>/proc/sys/net/ipv4/tcp_orphan_retries</tag> 在我們這端砍掉 TCP 連線之前﹐要進行多少次重試。預設是 7 個﹐相當於 ~50秒 - 16分鐘﹐視 RTO 而定。如果您機器為負載(loaded)網頁伺服器﹐您或許會打算降低這個數值﹐這類 sockets 可能會耗費大量的資源。另外參考 tcp_max_orphans 。 <tag>/proc/sys/net/ipv4/tcp_max_syn_backlog</tag> 對於那些依然還未獲得客戶端確認的連線請求﹐需要記憶的最大數目。對於超過 128Mb 記憶體的系統﹐預設值是 1024 ﹐低於 128Mb 的則為 128。如果伺服器經常出現過載﹐可以嘗試增加這個數字。警告﹗假如您將此值設為大於 1024﹐最好修改 include/net/tcp.h 裡面的 TCP_SYNQ_HSIZE ﹐以保持 TCP_SYNQ_HSIZE*16<=tcp_max_syn_backlog ﹐並且編進核心之內。 <tag>/proc/sys/net/ipv4/tcp_max_tw_buckets</tag> 系統在同一時間內所處理的最大 timewait sockets 數目。如果超過此數字的話﹐time-wait socket 會被立即砍除並且顯示警告信息。之所以要設定這個限制﹐純粹為了抵禦那些簡單的 DoS 攻擊﹐千萬不要人為的降低這個限制﹐不過﹐如果網路條件需要比預設值更多﹐則可以提高它(或許還要增加記憶體)。 <tag>/proc/sys/net/ipv4/tcp_retrans_collapse</tag> 針對某些損壞印表機的 bug-to-bug 兼容性。於重傳上嘗試送出更大的封包﹐以解決特定 TCP 堆疊中的臭蟲。 <tag>/proc/sys/net/ipv4/tcp_retries1</tag> 當某些事情不對勁而必須向網路層報告這個可疑狀況之前﹐需要進行多少次重試。最低的 RFC 數值是 3 ﹐這也是預設值﹐相當於 ~3秒 - 8分鐘﹐視 RTO 而定。 <tag>/proc/sys/net/ipv4/tcp_retries2</tag> 在砍掉存活 TCP 連線之前﹐需要進行多少次重試。RFC1122 裡面說﹐這個限制必須大於 100秒。此數字非常小。預設值為 15 ﹐相當於 ~13-30分鐘﹐視 RTO 而定。 <tag>/proc/sys/net/ipv4/tcp_rfc1337</tag> 這個 boolean 會啟動關於 'time-wait assassination hazards in tcp' 的修正﹐請參考 RFC 1337。如果被打開﹐會讓核心丟棄那些處於 time-wait 狀態 sockets 之 RST 封包。&nl; 預設為 0 。 <tag>/proc/sys/net/ipv4/tcp_sack</tag> 使用 Selective ACK﹐它可以用來找出特定的遺失封包 --- 因而有助於迅速復原。 <tag>/proc/sys/net/ipv4/tcp_stdurg</tag> 使用 TCP urg pointer 欄位中的主機請求詮釋功能。&nl; 大部份的主機都使用老舊的 BSD 詮釋﹐因而﹐如果您在 Linux 打開它﹐或會導致不能和它們正確溝通。&nl; 預設為﹕FALSE <tag>/proc/sys/net/ipv4/tcp_syn_retries</tag> 對於一個新建連線﹐核心要送多少個 SYN 封包數才決定放棄。 <tag>/proc/sys/net/ipv4/tcp_synack_retries</tag> 為了與另一端建立連線﹐核心會連同 SYN 一起送出 ACK ﹐以確認收到上一個 SYN。這是所謂的三段交握( threeway handshake) 的第二個步驟。這裡決定核心在放棄連線之前所送出的 SYN+ACK 數目。 <tag>/proc/sys/net/ipv4/tcp_timestamps</tag> Timestamps 用在其它一些東西中﹐可以防範那些偽造的 sequence 號碼。一條 1 gigabit 的線路或許會重遇到帶 out-of-line 數值的舊 sequence 號碼(假如它是由於上次產生的)。Timestamp 會讓它知道這是個 '舊封包'。 <tag>/proc/sys/net/ipv4/tcp_tw_recycle</tag> 啟動快速 TIME-WAIT sockets 回收。預設值是 1 。除非得到技術專家的建議或要求﹐請不要修改這個值。 <tag>/proc/sys/net/ipv4/tcp_window_scaling</tag> 正常來說﹐TCP/IP 可以接受大至 65535 byte 的 windows。對於高速網路﹐這或許還是不夠的。這個 windows scaling 選項則允許接近 gigabyte 的 windows﹐這將有助改善高頻寬延遲產品。 </descrip> <sect2>逐個設備設定 (Per device settings) <p> DEV 可以只代表一個真實界面﹐也可以是 '全部' 或 '預設值'。而預設值也或許會改變建立的界面設定。 <descrip> <tag>/proc/sys/net/ipv4/conf/DEV/accept_redirects</tag> 如果 router 認為您在不適當的使用它(ie﹐需要在同一界面上重送封包)﹐那它就會送出一個 ICMP Redirect。這或許會造成輕度的安全風險﹐所以您最好關閉它﹐或是使用 secure redirects。 <tag>/proc/sys/net/ipv4/conf/DEV/accept_source_route</tag> 此功能並不常用。過去﹐您可以給出一個 IP 位址列表﹐讓它提供此功能。Linux 也可以實踐這個 IP 選項。 <tag>/proc/sys/net/ipv4/conf/DEV/bootp_relay</tag> FIXME: 有待補充 <tag>/proc/sys/net/ipv4/conf/DEV/forwarding</tag> FIXME: 有待補充 <tag>/proc/sys/net/ipv4/conf/DEV/log_martians</tag> 請參考逆向路徑過濾章節。 <tag>/proc/sys/net/ipv4/conf/DEV/mc_forwarding</tag> 是否在該界面上進行 multicast forwarding。 <tag>/proc/sys/net/ipv4/conf/DEV/proxy_arp</tag> 如果設為 1﹐那麼所有其它界面都會回應以此界面為目的之 arp 請求。當架設 'ip pseudo bridges' 時相當有用。啟用此功能之前﹐請確定您的 netmasks 要非常正確。 <tag>/proc/sys/net/ipv4/conf/DEV/rp_filter</tag> 請參考逆向路徑過濾章節。 <tag>/proc/sys/net/ipv4/conf/DEV/secure_redirects</tag> FIXME: 有待補充 <tag>/proc/sys/net/ipv4/conf/DEV/send_redirects</tag> 是否要送出前面提到的 redirects。 <tag>/proc/sys/net/ipv4/conf/DEV/shared_media</tag> FIXME: 有待補充 <tag>/proc/sys/net/ipv4/conf/DEV/tag</tag> FIXME: 有待補充 </descrip> <sect2>鄰接原則 (Neighbor pollicy) <p> DEV 可以只代表一個真實界面﹐也可以是 '全部' 或 '預設值'。而預設值也或許會改變建立的界面設定。 <descrip> <tag>/proc/sys/net/ipv4/neigh/DEV/anycast_delay</tag> FIXME: 有待補充 <tag>/proc/sys/net/ipv4/neigh/DEV/app_solicit</tag> FIXME: 有待補充 <tag>/proc/sys/net/ipv4/neigh/DEV/base_reachable_time</tag> FIXME: 有待補充 <tag>/proc/sys/net/ipv4/neigh/DEV/delay_first_probe_time</tag> FIXME: 有待補充 <tag>/proc/sys/net/ipv4/neigh/DEV/gc_stale_time</tag> FIXME: 有待補充 <tag>/proc/sys/net/ipv4/neigh/DEV/locktime</tag> FIXME: 有待補充 <tag>/proc/sys/net/ipv4/neigh/DEV/mcast_solicit</tag> FIXME: 有待補充 <tag>/proc/sys/net/ipv4/neigh/DEV/proxy_delay</tag> FIXME: 有待補充 <tag>/proc/sys/net/ipv4/neigh/DEV/proxy_qlen</tag> FIXME: 有待補充 <tag>/proc/sys/net/ipv4/neigh/DEV/retrans_time</tag> FIXME: 有待補充 <tag>/proc/sys/net/ipv4/neigh/DEV/ucast_solicit</tag> FIXME: 有待補充 <tag>/proc/sys/net/ipv4/neigh/DEV/unres_qlen</tag> FIXME: 有待補充 </descrip> <sect2>路由設定 (Routing settings) <p> <descrip> <tag>/proc/sys/net/ipv4/route/error_burst</tag> FIXME: 有待補充 <tag>/proc/sys/net/ipv4/route/error_cost</tag> FIXME: 有待補充 <tag>/proc/sys/net/ipv4/route/flush</tag> FIXME: 有待補充 <tag>/proc/sys/net/ipv4/route/gc_elasticity</tag> FIXME: 有待補充 <tag>/proc/sys/net/ipv4/route/gc_interval</tag> FIXME: 有待補充 <tag>/proc/sys/net/ipv4/route/gc_min_interval</tag> FIXME: 有待補充 <tag>/proc/sys/net/ipv4/route/gc_thresh</tag> FIXME: 有待補充 <tag>/proc/sys/net/ipv4/route/gc_timeout</tag> FIXME: 有待補充 <tag>/proc/sys/net/ipv4/route/max_delay</tag> FIXME: 有待補充 <tag>/proc/sys/net/ipv4/route/max_size</tag> FIXME: 有待補充 <tag>/proc/sys/net/ipv4/route/min_adv_mss</tag> FIXME: 有待補充 <tag>/proc/sys/net/ipv4/route/min_delay</tag> FIXME: 有待補充 <tag>/proc/sys/net/ipv4/route/min_pmtu</tag> FIXME: 有待補充 <tag>/proc/sys/net/ipv4/route/mtu_expires</tag> FIXME: 有待補充 <tag>/proc/sys/net/ipv4/route/redirect_load</tag> FIXME: 有待補充 <tag>/proc/sys/net/ipv4/route/redirect_number</tag> FIXME: 有待補充 <tag>/proc/sys/net/ipv4/route/redirect_silence</tag> FIXME: 有待補充 </descrip> <sect>流量控管之 backbone 程式 (Backbone applications of traffic control) <p> 本章僅作為 backbone routing 的淺介﹐通常用在 >100 megabit 頻寬上﹐而且它所需的技術和您家中的 ADSL 大相徑庭。 <sect1>路由器佇列 (Router queues) <p> 在 Internet 上面﹐router queue 的正常行為特性稱為 tail-drop。Tail-drop 就是當佇列達到一定數量之後﹐就開始將所有 '溢出' 的流量丟棄掉。這其實很不公平﹐而且也會導致同步重傳(retransmit synchronisation)。當同步重傳發生之後﹐已經滿載的路由所丟棄的瞬間流量﹐會導致另一波延遲的瞬間流量重傳﹐這樣又會重新填滿已經擁塞的 router。 為了妥善處理線路的瞬間擁塞﹐backbone routers 通常會裝備較大的佇列。然不幸的是﹐這些佇列雖然可改善吞吐量(throughput)﹐但卻大大的加重延遲﹐且導致 TCP 連線在擁塞的時候變得經常性的流量瞬增。 這些伴隨著 tail-drop 而來的弊端﹐已在 Internet 上不斷的造成困擾﹐這是由於一些不十分友善(unfriendly)的網路程式使用量不斷增加之故。有見及此﹐Linux 核心向我們提供所謂的 RED (Random Early Detect) 機制。 RED 事實上也不是什麼萬靈藥﹐不慎淪為實作指數倒退(implement exponential backoff)的程式﹐依然引起頻寬的分配不公﹐然而﹐有了 RED﹐可讓他們不至對其它連線的吞吐量和延遲造成太大傷害。 統計上﹐ RED 在它達到硬界限(hard limit)之前﹐就會從流程(flows)上丟棄封包。這會讓已擁塞的骨幹線路溫和地放慢﹐並防止同步重傳。這也有助於 TCP 更迅速的丟棄部份封包﹐以保持佇列體積於較低值﹐及有效控制延遲﹐從而更快的找出其 '公平' 速度。封包從特定連線上被丟棄的機率﹐也成比例的相應於其頻寬使用量﹐而非封包的傳送數量。 關於 backbone﹐光是公平性佇列所需的 per-session 狀態追蹤之複雜性﹐就令您敬而遠之﹐就這點而言﹐ RED 無疑是非常優秀的佇列技術。 如要使用 RED﹐您必須決定好三個參數﹕Min、Max、及 burst。Min 用來設定開始丟棄流量之前的最小佇列體積﹐以 byte 為單位﹔Max 則是此演算法所能保持的軟性(soft)最大值﹔而 burst 則設定 '瞬增吞吐流量' 的最大封包數目。 您要根據預計的佇列延遲﹐再乘以您的頻寬﹐來計算出 min 的設定值。比方說﹐在我的 64kbit/s 的 ISDN 線路上﹐我想要佇列的基本延遲為 200ms﹐那我就將 min 設為 1600 bytes。如果 min 設得太小﹐會降低吞吐量﹔如太大﹐則加重延遲。在一條低速線路上﹐以降低 MTU 來改進互動回應(interactive response)的辦法﹐並不能靠降低 min 設定來作為替代方案。 您最好將 max 設為 min 的起碼兩倍以上﹐以預防同步。在較低的 min 值的低速線路上﹐將 max 設為 min 的四倍或更多﹐應是明智之舉。 至於 burst﹐則是用來控制 RED 演算法如何對瞬增流量做出反應。Busrt 必須設定為大於 min/avpkt。實驗上﹐我發現 (min+min+max)/(3*avpkt) 也行得通。 另外﹐您還要設定 limit 和 avpkt 。Limit 是一個安全值﹐當佇列到達 limit bytes 之後﹐RED 就會 '變成' tail-drop 模式。我通常將 limit 設為 max 的 8 倍數。而 avpkt 就是平均封包體積。在 1500byte MTU 的高速 Internet 線路上設為 1000 是可以接受的。 相關技術資料﹐請參考 Sally Floyd 和 Van Jacobson 的 <url url="http://www.aciri.org/floyd/papers/red/red.html" name="the paper on RED queueing"> 。 FIXME: 多多益善。沒錯﹐就是 greg 兄 *您* 啦 :-) - 哈 <sect>食譜 (Cookbook) <p> 本章所包含的 'cookbook' 單元﹐應可幫您解決一些問題。然而﹐離開了原理的理解﹐縱有 cookbook 也枉然﹐所以﹐請先溫故而知新。 <!-- <sect1>Reserving bandwidth for your IRC server <p> Recently the IRC networks have been plagued by distributed denial of service attacks. The aim of some of these attacks is to disrupt communication between servers which split the network. You then join the splitted part of the network. Because nobody else is there, the server assigns you operator status. You then stop the disruption, the network rejoins and voila, you can take over the channel. This silly behaviour is seriously damaging IRC, and luckily, Linux is there to protect it :-) We need to be smarter than your average scriptkid, so we'll use some advanced netfilter features to help us. --> <sect1>以不同的 SLA 來跑多個站點 (Running multiple sites with different SLAs) <p> 您可以用好多方法來做啦。Apache 就可以透過模組來達到某些支援﹐不過我們這裡要教您用 Linux 怎樣做﹐並且舉一反三﹐將其它服務也一併搞定。這些命令都偷師自下面提到的 Jamal Hadi 的演講 。 假設我們有兩個客戶﹐要提供 http、ftp、還有 streaming audio 服務﹐我們只打算賣給他們一定數量的頻寬而已。那我們可以在伺服器上面做手腳。 客戶 A 最多只有 2 megabits﹐而客戶 B 已經支付 5 megabits 的錢了。那我們在伺服器上建立虛擬 IP 位址﹐來將客戶分開來。 <tscreen><verb> # ip address add 188.177.166.1 dev eth0 # ip address add 188.177.166.2 dev eth0 </verb></tscreen> 以哪些適合的位址來分配給不同的伺服器﹐則悉從尊便了。所有常用 daemon 都支援這玩意。 接著﹐我們首先將 CBQ adisc 指派給 eth0﹕ <tscreen><verb> # tc qdisc add dev eth0 root handle 1: cbq bandwidth 10Mbit cell 8 avpkt 1000 \ mpu 64 </verb></tscreen> 然後﹐為我們的客戶建立類別(classes)﹕ <tscreen><verb> # tc class add dev eth0 parent 1:0 classid 1:1 cbq bandwidth 10Mbit rate \ 2MBit avpkt 1000 prio 5 bounded isolated allot 1514 weight 1 maxburst 21 # tc class add dev eth0 parent 1:0 classid 1:2 cbq bandwidth 10Mbit rate \ 5Mbit avpkt 1000 prio 5 bounded isolated allot 1514 weight 1 maxburst 21 </verb></tscreen> 再來﹐我們為這兩個類別增加過濾器﹕ <tscreen><verb> ##FIXME: Why this line, what does it do?, what is a divisor?: ##FIXME: A divisor has something to do with a hash table, and the number of ## buckets - ahu # tc filter add dev eth0 parent 1:0 protocol ip prio 5 handle 1: u32 divisor 1 # tc filter add dev eth0 parent 1:0 prio 5 u32 match ip src 188.177.166.1 flowid 1:1 # tc filter add dev eth0 parent 1:0 prio 5 u32 match ip src 188.177.166.2 flowid 1:2 </verb></tscreen> 這樣﹐就大功告成了啦。 FIXME: 為何不用 token bucket 過濾器﹖還是預設的 pfifo_fast 撤出了﹖ <label id="SYN"> <sect1>幫貴主機抵禦 SYN floods (Protecting your host from SYN floods) <p>根據 Alexey 的 iproute 文件﹐已可以和 netfiler 搭配了﹐且有好些看來不錯的途經。如果您要使用這個﹐請小心調整那些數字﹐針對您的系統給予合理的數值。 假如您想要保護整個網路﹐可以跳過這裡﹐這裡是針對單一主機而已的。 <tscreen><verb> #! /bin/sh -x # # sample script on using the ingress capabilities # this script shows how one can rate limit incoming SYNs # Useful for TCP-SYN attack protection. You can use # IPchains to have more powerful additions to the SYN (eg # in addition the subnet) # #path to various utilities; #change to reflect yours. # TC=/sbin/tc IP=/sbin/ip IPTABLES=/sbin/iptables INDEV=eth2 # # tag all incoming SYN packets through $INDEV as mark value 1 ############################################################ $iptables -A PREROUTING -i $INDEV -t mangle -p tcp --syn \ -j MARK --set-mark 1 ############################################################ # # install the ingress qdisc on the ingress interface ############################################################ $TC qdisc add dev $INDEV handle ffff: ingress ############################################################ # # # SYN packets are 40 bytes (320 bits) so three SYNs equals # 960 bits (approximately 1kbit); so we rate limit below # the incoming SYNs to 3/sec (not very useful really; but #serves to show the point - JHS ############################################################ $TC filter add dev $INDEV parent ffff: protocol ip prio 50 handle 1 fw \ police rate 1kbit burst 40 mtu 9k drop flowid :1 ############################################################ # echo "---- qdisc parameters Ingress ----------" $TC qdisc ls dev $INDEV echo "---- Class parameters Ingress ----------" $TC class ls dev $INDEV echo "---- filter parameters Ingress ----------" $TC filter ls dev $INDEV parent ffff: #deleting the ingress qdisc #$TC qdisc del $INDEV ingress </verb></tscreen> <sect1>以 ICMP 速率限制抵擋 dDoS (Ratelimit ICMP to prevent dDoS) <p> 目前來說﹐分散式服務癱瘓(distributed denial of service)攻擊已經成為 Internet 上頭號騷擾行為。對貴網路使用適當的過濾和速率限制﹐可讓您既避免成為砲灰﹐也避免成為砲手﹐一箭雙雕。 如果您要對網路做過濾﹐那您或許會不讓非本地 IP 來源位址的封包離開貴網路。這可以制止別人以匿名身份對 internet 發送垃圾。 <!-- FIXME: netfilter one liner. Is there a netfilter one-liner? Martijn --> 如前面介紹的﹐速率限制考慮得更是週長。再看看下面的 ASCII 圖例﹐幫您重溫一下﹕ <tscreen><verb> [The Internet] ---<E3, T3, whatever>--- [Linux router] --- [Office+ISP] eth1 eth0 </verb></tscreen> 讓我們先將前提部份設定起來吧﹕ <tscreen><verb> # tc qdisc add dev eth0 root handle 10: cbq bandwidth 10Mbit avpkt 1000 # tc class add dev eth0 parent 10:0 classid 10:1 cbq bandwidth 10Mbit rate \ 10Mbit allot 1514 prio 5 maxburst 20 avpkt 1000 </verb></tscreen> 假如您有 100Mbit﹐或是更快的界面﹐請調整這些數字。現在您需要判定要允許多大的 ICMP 流量。您可以用 tcpdump 進行測量﹐將結果寫進一個檔案﹐然後過一會看看有多少 ICMP 封包通過網路。請不要忘記將測量時間拉長一點。 如果測量結果看起來不切實際﹐那您可以以可用頻寬的 5% 來計算。那就讓我們把類別設好吧﹕ <tscreen><verb> # tc class add dev eth0 parent 10:1 classid 10:100 cbq bandwidth 10Mbit rate \ 100Kbit allot 1514 weight 800Kbit prio 5 maxburst 20 avpkt 250 \ bounded </verb></tscreen> 目前的限制為 100Kbit。接下來我們需要一個過濾器﹐將 ICMP 流量撥給這個類別﹕ <tscreen><verb> # tc filter add dev eth0 parent 10:0 protocol ip prio 100 u32 match ip protocol 1 0xFF flowid 10:100 </verb></tscreen> <sect1>為互動流量排優先次序 (Prioritizing interactive traffic) <p> 如果有大量的數據傳入您的線路﹐或是反向傳出﹐而您需要用 telnet 或 ssh 進行某些維護工作﹐這或許不十分理想﹐因為其它封包或會打斷您的鍵盤操作。假如有辦法讓這些互動封包從這些大塊的流量底下暗渡陳倉﹐就最好不過了。Linux 可以幫您做到哦﹗ 如前﹐我們需要操縱雙向的流量。顯然﹐如果線路兩端都有 Linux 機器就最理想了﹐當然其它 UNIX's 也可以做得到啦。這點﹐就請教您身邊的 Solaris/BSD 高手囉。 標準的 pfifo_fast 排程方法帶有 3 個不同的 'bands'。當 band1 和 band2 都流量獲得之後﹐在 band0 的流量會先傳送。至為關鍵的是﹐我們的互動流量一定要在 band0 裡面﹗ 我們乾脆明目張膽的改編(即將淘汰的) ipchains HOWTO 好了﹕ 在 IP 標頭中﹐有 4 個位元並不常用的﹐稱為 Type of Service (TOS) 位元。它們會影響封包被處理的程序﹔這 4 個位元分別是﹕"Minimum Delay"、"Maximum Throughput"、"Maximum Reliability"、和 "Minimum"。它們四者﹐只能設定其一。Ipchains 之 TOS-mangling 作者﹐Rob van Nieuwkerk﹐曾作如下述﹕ <tscreen> 對我而言﹐"Minimum Delay" 猶為重要。我為了那些“互動”封包 在我的上傳(Linux) router 上將之打開。我只用一條 33k6 的 modem 線路而已。Linux 將封包的優先順序排進 3 個佇列中。這樣﹐ 在我進行大量下載的時候﹐還可以獲得一個可接受的互動效能。 </tscreen> 最常見的做法是將 telnet 和 ftp control 連線設為 "Minimum Delay" ﹐而 FTP data 設為 "Maximum Throughput"。在您的上傳 router 上﹐可以如下那樣動作﹕ <tscreen><verb> # iptables -A PREROUTING -t mangle -p tcp --sport telnet \ -j TOS --set-tos Minimize-Delay # iptables -A PREROUTING -t mangle -p tcp --sport ftp \ -j TOS --set-tos Minimize-Delay # iptables -A PREROUTING -t mangle -p tcp --sport ftp-data \ -j TOS --set-tos Maximize-Throughput </verb></tscreen> 好了﹐這只對那些 telnet 及從外面送來本地主機的數據有效而已。其它您也要一一設好﹐例如 telnet、ssh、朋友的﹐所有外送封包的 TOS 欄位全自動設好。 如果客戶端並非設定如此﹐那您可以用 netfilter 來做。於您的本機上﹕ <tscreen><verb> # iptables -A OUTPUT -t mangle -p tcp --dport telnet \ -j TOS --set-tos Minimize-Delay # iptables -A OUTPUT -t mangle -p tcp --dport ftp \ -j TOS --set-tos Minimize-Delay # iptables -A OUTPUT -t mangle -p tcp --dport ftp-data \ -j TOS --set-tos Maximize-Throughput </verb></tscreen> <sect1>用 netfilter、iproute2、ipchains、及 squid 做通透性 web-caching (Transparent web-caching using netfilter, iproute2, ipchains and squid) <p> <label id="SQUID"> 本章節由 Internet for Education (泰國) 的讀者 Ram Narula 提供。 以 Linux 來做的話﹐常規技巧上或會採用 ipchains﹐﹐將 "外送" 的 port 80(web) 流量送往伺服器所跑的 squid 服務程式。 有 3 種常見辦法可確定 "外送" 的 port 80(web) 流量會送往伺服器所跑的 squid 服務程式﹐而第 4 種將是這裡的重頭戲。 <descrip> <tag>用網關 router 來做</tag> 告訴網關 router 將外送目的端埠口為 80 的封包﹐送到 squid 伺服器的 IP 位址上。 <p> 但是 <p> 這會額外增加 router 的負載﹐而且有些商業 routers 未必支援這種做法。 <tag>用 Layer 4 switch 來做</tag> Layer 4 switch 絕對能勝任此項工作。 <p> 但是 <p> 此類設備的成本非常高。一般而言﹐Layer 4 switch 的成本甚至比 router 加一台好的 Linux 伺服器還要高。 <tag>用 cache 伺服器來做網關</tag> 您可以強迫所有流量都經過 cache 伺服器。 <p> 但是 <p> 這有點冒險﹐因為 Squid 會耗掉相當的 cpu 資源﹐或會造成整體的網路效能降低﹐或是伺服器當掉而誰也連不上 internet。 <tag>Linux+NetFilter router</tag> NetFilter 可以提供另一可行手段﹐就是用 NetFilter 來將那些目的端埠口為 80 的封包 "標識" 起來﹐同時用 iproute2 將已 "標識" 的封包送到 Squid 伺服器那裡。 </descrip> <tscreen><verb> |----------------| | Implementation | |----------------| Addresses used 10.0.0.1 naret (NetFilter server) 10.0.0.2 silom (Squid server) 10.0.0.3 donmuang (Router connected to the internet) 10.0.0.4 kaosarn (other server on network) 10.0.0.5 RAS 10.0.0.0/24 main network 10.0.0.0/19 total network |---------------| |Network diagram| |---------------| Internet | donmuang | ------------hub/switch---------- | | | | naret silom kaosarn RAS etc. </verb></tscreen> 首先﹐確定 naret 為預設網關(除 silom 外)﹐讓所有流量均通過它。而 silom 的預設網關必須是 donmuang(10.0.0.3)﹐要不然會產生流量迴圈(loop)。 <p> (網路中的所有伺服器都以 10.0.0.1 為預設網關﹐也就是 donmuang router 的舊 IP 位址﹐所以我將 donmuang 的 IP 設為 10.0.0.3﹐而將 10.0.0.1 給 naret 用。) <tscreen><verb> Silom ----- -setup squid and ipchains </verb></tscreen> <p> 在 silom 上面設定 Squid 伺服器﹐要確定它能支援通透性 caching/proxying﹐其預設埠口通常為 3128﹐然後﹐所有給 port 80 的流量都會被轉送到本機埠口 3128。用 ipchains 的話﹐可以這樣做﹕ <tscreen><verb> silom# ipchains -N allow1 silom# ipchains -A allow1 -p TCP -s 10.0.0.0/19 -d 0/0 80 -j REDIRECT 3128 silom# ipchains -I input -j allow1 </verb></tscreen> <p> 或是﹐netfilter 來做﹕ <tscreen><verb> silom# iptables -t nat -A PREROUTING -i eth0 -p tcp --dport 80 -j REDIRECT --to-port 3128 </verb></tscreen> (注意﹕您或許還有其它項目的設定) <p> 關於 Squid 伺服器的更多設定資料﹐請參考 Squid 的 faq ﹕<url url="http://squid.nlanr.net" name="http://squid.nlanr.net">)。 <p> 請確定在這台伺服器上面將 ip forwarding 功能打開﹐還有﹐這個伺服器的預設網關是 donmuang (而不是 naret)。 <tscreen><verb> Naret ----- -setup iptables and iproute2 -disable icmp REDIRECT messages (if needed) </verb></tscreen> <enum> <item>將目的埠口為 80 的封包 "標識" 為數值 2 <tscreen><verb> naret# iptables -A PREROUTING -i eth0 -t mangle -p tcp --dport 80 \ -j MARK --set-mark 2 </verb></tscreen> </item> <item>設定好 iproute2﹐將 "標識" 為 2 的封包送到 silom 那邊 <tscreen><verb> naret# echo 202 www.out >> /etc/iproute2/rt_tables naret# ip rule add fwmark 2 table www.out naret# ip route add default via 10.0.0.2 dev eth0 table www.out naret# ip route flush cache </verb></tscreen> <p> 如果 donmuang 和 naret 都在同一 subnet 上的話﹐那 naret 就不要送出 REDIRECT 的 icmp 訊息了。這時候﹐按如下方法將 icmp REDIRECT 關閉﹕ <tscreen><verb> naret# echo 0 > /proc/sys/net/ipv4/conf/all/send_redirects naret# echo 0 > /proc/sys/net/ipv4/conf/default/send_redirects naret# echo 0 > /proc/sys/net/ipv4/conf/eth0/send_redirects </verb></tscreen> </item> </enum> 如此﹐所有設定都完成了﹐請回去檢查一下﹕ <tscreen><verb> On naret: naret# iptables -t mangle -L Chain PREROUTING (policy ACCEPT) target prot opt source destination MARK tcp -- anywhere anywhere tcp dpt:www MARK set 0x2 Chain OUTPUT (policy ACCEPT) target prot opt source destination naret# ip rule ls 0: from all lookup local 32765: from all fwmark 2 lookup www.out 32766: from all lookup main 32767: from all lookup default naret# ip route list table www.out default via 203.114.224.8 dev eth0 naret# ip route 10.0.0.1 dev eth0 scope link 10.0.0.0/24 dev eth0 proto kernel scope link src 10.0.0.1 127.0.0.0/8 dev lo scope link default via 10.0.0.3 dev eth0 (make sure silom belongs to one of the above lines, in this case it's the line with 10.0.0.0/24) |------| |-DONE-| |------| </verb></tscreen> <sect2>實作之後的流量流程圖 (Traffic flow diagram after implementation) <p> <tscreen><verb> |-----------------------------------------| |Traffic flow diagram after implementation| |-----------------------------------------| INTERNET /\ || \/ -----------------donmuang router--------------------- /\ /\ || || || || || \/ || naret silom || *destination port 80 traffic=========>(cache) || /\ || || || \/ \/ \\===================================kaosarn, RAS, etc. 注意﹕ 因為在正常外送路徑上﹐多了一個額外的跳站(hop)﹐ 所以此網路為非對稱的。 這樣﹐在 kaosarn 和 internet 之間的封包﹐到這裡都會受到管制。 對於 web/http 流量﹕ kaosarn http request->naret->silom->donmuang->internet http replies from internet->donmuang->silom->kaosarn 對於 非 web/http 請求(如﹐telnet)﹕ kaosarn outgoing data->naret->donmuang->internet incoming data from internet->donmuang->kaosarn </verb></tscreen> <sect1>以每個路由 MTU 設定來防範 Path MTU Discovery 問題 (Circumventing Path MTU Discovery issues with per route MTU settings) <p> 為了傳送 bulk 數據﹐internet 通常會使用大型封包以獲得更好的效果。每一個封包都必須進行路由判斷﹐假如傳送一份 1 magabyte 的文件﹐如果僅可能的使用最大封包進行傳送﹐那大概需要 700 個封包﹔如果預設使用最小封包﹐則近 4000 個。 然而﹐internet 上並非所有部份都支援完整的每個封包 1460 bytes 的過載。因此﹐必須找出 '合身' 的最大封包體積﹐以進行連線最佳化。 這個處理稱為 '路徑 MTU 發現(Path MTU Discovery)'﹐MTU 就是 'Maximum Transfer Unit' 的意思。 當路由器收到一個封包﹐因為太大而不能一次送出﹐但同時它的 "Don't Fragment" 位元又被設定起來﹐則會回送一個 ICMP 信息﹐說明它因此被迫丟棄這個封包。發送端主機收到這條提示之候﹐會改送較小的封包﹐並且﹐透過如此反復﹐就能於特定路徑上找出最佳的封包體積。 這原本工作得非常順利﹐不過﹐當 internet 上充斥眾多無所不用其極進行連線破壞的不肖之徒之後﹐情況就一落千丈了。許多管理員透過封鎖或引導錯誤導入的 ICMP 流量﹐來改善安全或增強他們的 internet 服務。 這樣的結局是﹐Path MTU Discovery 在某些路由器上每下愈況﹐而導致奇怪的 TCP/IP 連線過一會就掛掉了。 儘管我還沒有直接的證據來證明這點﹐我過去接觸過兩個站台﹐在受影響系統之前執行 Alteon Acedirectors 都有這樣的問題 --- 或許對此更了解的朋友可以為我們畫龍點睛。 <sect2>解決方案 (Solution) <p> 當您碰到站台受此問題困擾的時候﹐您可以手工的關閉 Path MTU discovery 功能。Koos van den Hout 曾如此寫過(稍作修改)﹕ <tscreen> <p> 下列問題﹕我把 ppp 專線之 mtu/mrg 設定為 296﹐因為它只有 33k6 而已﹐而且我也不能影響另一端的佇列。在 296 的水平﹐鍵擊回應尚能保持在合理的時間之內。 同時﹐在我這邊﹐我用 Linux (當然啦)﹐進行偽裝。 我目前將 '伺服器' 和 '路由器' 分開﹐所以大部份的應用程式都在另外的機器上面跑﹐而路由則依舊進行。 然後﹐當我要連上 irc 的時候卻碰到問題。超麻煩的就是了﹗透過探查﹐發現我可以連接上 irc﹐而且在 irc 上也顯示為 'connected' ﹐但就是收不到 irc 的信息。我開始檢查是什麼地方出槌了﹐而且也注意到在以前連接某些和 MTU 相關的網站也有問題﹐因為當 MTU 是 1500 的時候並沒有問題﹐而 MTU 為 296 的時候就會出狀況。因為 irc 伺服器會封鎖所有即時作業不需要的流量﹐而且也擋掉 icmp。 我曾說服過某個有此問題的網站管理員﹐但 irc 伺服器的管理員卻不大願意修正它。 因此﹐我必須確定外送的偽裝流量走外部線路要從低 mtu 開始。但是我要本地的 ethernet 流量可以獲得正常的 mtu (諸如 nfs 流量)。 解決辦法﹕ <tscreen><verb> ip route add default via 10.0.0.1 mtu 296 </verb></tscreen> (10.0.0.1 為預設網關﹐是偽裝路由器的內部位址) </tscreen> 通常﹐對特定路由進行修改﹐可以來蓋寫 PMTU Discovery 的值。比方說﹐假如只有某一特定 subnet 會有問題﹐那下面的辦法或許有助﹕ <tscreen><verb> ip route add 195.96.96.0/24 via 10.0.0.1 mtu 1000 </verb></tscreen> <sect1>以 MMS 鉗制來防範 Path MTU Discovery 問題 (Circumventing Path MTU Discovery issues with MSS Clamping) (for ADSL, cable, PPPoE & PPtP users) <p> 如前面所述﹐Path MTU Discovery 已經不再隨心所欲了。如果您知道事實上網路中某個跳站(hop)有限制 MTU(<1500)﹐您是不能依靠 PMTU Discovery 來發現它的。 在 MTU 之外﹐還有另外的方法設定封包的最大體積﹐這個就是傳說中的 Maximum Segment Size。這是屬於 SYN 封包的 TCP 選項中的欄位。 目前的 Linux 核心﹐以及少部份 pppoe 驅動程式﹐都提供 'MSS 鉗制' 功能。 設定 MSS 數值所帶來的好處是﹐您可以明確的告訴遠端﹕'送來的封包不要超過這個數值'﹐而無須倚重 ICMP 流量。 其壞處是﹐他是一個非常明顯的破解(hack) --- 透過修改封包而打斷 '端對端(end to end)' 狀態。必須指出﹐我們在許多場合中使用這個技巧﹐而它就如孫悟空頭上的緊箍咒。 為了讓它工作﹐您起碼需要 iptables-1.2.1a 和 Linux 2.4.3 或更新版本。基本命令如下﹕ <tscreen><verb> # iptables -A FORWARD -p tcp --tcp-flags SYN,RST SYN -j TCPMSS --clamp-mss-to-pmtu </verb></tscreen> 這會為您的線路計算適合的 MSS。如果您有冒險精神﹐或自認藝高膽大﹐您也可以再做這樣的動作﹕ <tscreen><verb> # iptables -A FORWARD -p tcp --tcp-flags SYN,RST SYN -j TCPMSS --set-mss 128 </verb></tscreen> 這樣會將所通過的 SYN 封包之 MMS 設為 128。如果您要用微型封包攜載 VoIP﹐同時﹐龐大的 httpd 封包會削減您的語音通訊﹐那您就可以使用它了。 <sect>以 Proxy ARP 構建橋接器和偽橋接器 (Building bridges, and pseudo-bridges with Proxy ARP) <p> 橋接器(bridges)是一種可以安裝在網路中而無須任何重設定(reconfiguration)的設備。一個網路交換器(switch)基本上是一個多埠口橋接器而已。單一的橋接器通常是一個雙埠口的交換器。Linux 也支援帶多張界面的單一橋接器﹐而成為一台真正的交換器。 橋接器的部署﹐主要是應用於如下情形﹕當面臨一個衰弱的(broken)網路﹐需要進行修復﹐但又不能進行任何變更(alterations)。因為橋接器是一個 layer-2 設備﹐在 IP 的下一層﹐路由器和伺服器是感應不到它的存在的。換而言之﹐您可以通透性的封鎖或修改特定的封包﹐或是進行引導。 橋接器的另一好處是﹐一個橋接器在掛掉之後﹐可以用一條跳接線(cross cable)或是一個集線器(hub)來代替。 而壞消息是﹐除非它被明晰的寫在文件上﹐否則一個橋接器可能會導致非常大的困擾。在 traceroute 中不會顯示它﹐但封包卻因為某些緣故神奇失蹤﹐或從點A 變成點B。另外﹐您還要知道﹐公司是否願意修改現行狀況。 關於 Linux 2.4 橋接器的文件﹐整理於﹕<url url="http://www.math.leidenuniv.nl/~buytenh/bridge" name="this page">。 <sect1>橋接和 iptalbe 的說明 (State of bridging and iptables) <p> 對於 Linux 2.4.3﹐橋接器和 iptables 在沒有外援的情況下﹐彼此都 '看' 不到對方。如果您將封包從 eth0 橋接到 eth1﹐他們並不能 '通過' iptalbes 哦。換句話說﹐您不能進行過濾、或 NAT、或重整(mangling)、或諸如此類。 目前已有數個計劃進行對此問題的修正﹐而最準確的應算是 Linux 2.4 橋接程式的作者 Lennert Buytenhek 了。不過﹐修補程式尚未完成﹐但看來是可行的。它可以從這個地方找到﹕<url url="http://www.math.leidenuniv.nl/~buytenh/bridge/devel/bridge-nf/" name="the experimental bridge patches page">。 我們期待這個問題可以儘快解決。 <sect1>橋接與引導 (Bridging and shaping) <p> 這個正如所其標榜的那樣﹐是可以工作的。您要確保設定好每個界面是在哪一端之上﹐否則﹐您可能會將外送的流量引導至內部界面上﹐這顯然南轅北轍。必要時請使用 tcpdump 。 <sect1>帶 Poxy-ARP 的偽橋接器 (Pseudo-bridges with Proxy-ARP) <p> 如果您只想實作一台偽橋接器(Pseudo-bridge)﹐儘可跳過後面數個段落﹐直接到 '把它實作出來' 那裡﹐不過﹐看一看實習中它是如何工作的﹐絕對是聰明之舉。 一個偽橋接器有所不同。預設上﹐橋接器從一個界面將未修改的封包傳遞到另外的界面去。它只參考封包的實體位址而判定要送至何方。換而言之﹐您可以橋接 Linux 不了解的流量﹐只要它能抓到實體位址就可以了。 而一個 '偽橋接器' 則略有不同﹐它看起來更像一台隱藏的路由器而非橋接器﹐但卻又有橋接器的能力﹐它在網路設計上會構成一些影響。 事實上的一個優點是﹐因為它不是一個橋接器﹐而封包實際上會經過核心﹐因而可以被過濾、修改、重導向、或是重路由。 一台真正的橋接器﹐透過設定也能如法泡制﹐但它需要某些特殊的程式﹐例如 Ethernet Frame Diverter﹐或前面提到的修補。 偽橋接器的另外一個優點是﹐它不會傳遞它不明白的封包 --- 因此可以清理掉網路中許多流量。假如在您需要這些流量的情況下(例如 SAP 封包﹐或 Netbeui)﹐那就使用真正的橋接器吧。 <sect2>ARP & Proxy-ARP <p> 當一台主機想要呼叫同一實體網段上的另一台主機的時候﹐它會送出一個 Address Resolution Protocol 封包﹐說顯淺一點﹐聽起來會像這樣﹕“誰有 10.0.0.1 這個位址﹐請告訴 10.0.0.7”。而作為對這個查詢的回應﹐10.0.0.1 會回報一個簡單的“我在這裡”的封包。 然後 10.0.0.7 就將封包送到“我在這裡”那個封包所提到的實體位址。同時﹐它也把硬體位址存放在 cache 中﹐保留一段相對較長的時間﹐等到 cache 逾期之後﹐則再重新發問就是了。 當要架設一個偽橋接器的時候﹐我們要讓橋接器回應這類的 ARP 封包﹐這樣﹐網路上的主機才會將它們的封包送到橋接器這邊來。然後橋接器處理這些封包﹐再送到相關界面去。 所以﹐簡而言之﹐當一台位於橋接器一端的主機﹐查詢另一端主機的時候﹐橋接器就會回應一個封包﹐告之“交給我就好”。 透過這個辦法﹐所有數據流量都能夠送到正確的地方去﹐而且都會經過該橋接器。 <sect2>把它實作出來 (Implementing it) <p>在過去﹐或許會讓 Linux 核心對所有 subnet 進行 'proxy-ARP'。然則﹐要架設一台偽橋接器﹐您必須為兩端指定正確的路由﹐並且﹐建立匹配的 proxy-ARP 規則。這實在不是十分好玩﹐因為光打字就夠累人的了﹐且也極容易出錯而導致橋接器為不知道如何路由的網路作出 ARP 回應。 在 Linux 2.4 上面(或許也包括 2.2)﹐這個可能性已經排除﹐並且被一個 /proc 目錄中的旗標所取代﹐稱為 'proxy_arp'。下面﹐是架設偽橋接器的步驟﹕ <enum> <item>為兩端的界面分配一個 IP 位址﹕ '左' 青龍、'右' 白虎。 <item>將路由建立起來﹐讓機器知道哪些主機在左邊﹐哪些在右邊。 <item>將兩端的界面之 prox-ARP 打開﹕ echo 1 >/proc/sys/net/ipv4/conf/ethL/proxy_arp; &nl; echo 1 > /proc/sys/net/ipv4/conf/ethR/proxy_arp&nl; 其中的 L 和 R 分別為左右兩邊的界面號碼。 </enum> 然後﹐不要忘記將 ip_forwarding 旗標打開﹗假如從真正橋接器轉換過濾﹐您或許會發現這旗標是關閉的﹐因為進行橋接的時候並不需要它。 另外﹐您在進行轉換的時候還要注意﹐您需要清空網路中各電腦的 arp cache --- 因為 arp cache 或許仍保留著先前橋接器的實體位址﹐而事實上卻已成昨日黃花了。 如在一台 Cisco 上面﹐可以用 'clear arp-cache' 命令來完成﹐在 Linux 上﹐則使用 'arp -d ip.address' 。當然﹐您也可以等 cache 逾時﹐這樣需時更久。 <sect>進階 Linux Routing (Advanced Linux Routing) <p> 本章的對象是﹕想了解整個系統是怎樣工作的﹐或是現有設定太過怪異而想刨根問底﹐將之搞定。 本章完全是選讀章節而已。這章節應會相當複雜﹐而且真的並非針對普通使用者而設。有言在先就是了。 FIXME: 決定這裡真正需要的是什麼。 <sect1>封包佇列究竟如何工作的﹖(How does packet queueing really work?) <p>這裡將一探究竟﹐封包佇列系統是如何工作的。 先將核心分類(classify)封包的步驟列出來﹐例如... FIXME: 有待補充 <sect1>封包佇列系統的進階使用 (Advanced uses of the packet queueing system) <p>參考 Alexey 那個極端詭異(tricky)的範例吧﹐那裡牽涉到一些在 TOS 欄位中尚未用到的位元。 FIXME: 有待補充 <sect1>其它封包引導系統 (Other packet shaping systems) <p>我只想扼要的說明一下其它作業系統上的封包引導系統﹐以及它們與 Linux 上的比較。由於 Linux 是少數完全基於 TCP/IP 架構(非 BSD派系)的作業系統之一﹐我認為參考一下別人的做法是非常有價值的。 然遺憾的是﹐我並沒有其它作業系統的經驗﹐所以沒法成文。 FIXME: 誰自告奮勇一下﹖ - Martijn <sect>動態路由 - OSPF 和 BGP (Dynamic routing - OSPF and BGP) <p> 一旦貴網路成長到很大的時候﹐或是您要將貴網路當成 'internet' 來玩﹐您就需要一些工具來動態的路由數據。各站點之間通常都會透過多條線路相互連接起來﹐且有日益增加的趨勢。 我們的 Internet 一般都採用 OSPF 和 BGP4 (rfc1771) 為標準。Linux 對兩者均能支援﹐是用 <tt>gated</tt> 和 <tt>zebra</tt> 來做到的。 目前來說﹐這已經超出本文件的範疇了﹐我們只想告訴您去哪裡做功課而已﹕ Overview: Cisco Systems <url url="http://www.cisco.com/univercd/cc/td/doc/cisintwk/idg4/nd2003.htm" name="Designing large-scale IP internetworks"> For OSPF: Moy, John T. "OSPF. The anatomy of an Internet routing protocol" Addison Wesley. Reading, MA. 1998. Halabi 也寫過一個非常棒的關於 OSPF 路由設計的指南﹐ 不過似乎已被 Cisco 網站拿掉了。 For BGP: Halabi, Bassam "Internet routing architectures" Cisco Press (New Riders Publishing). Indianapolis, IN. 1997. also Cisco Systems <url url="http://www.cisco.com/univercd/cc/td/doc/cisintwk/ics/icsbgp4.htm" name="Using the Border Gateway Protocol for interdomain routing"> 雖然範例都是 Cisco 特定的﹐不過大都跟 Zebra 的設定語言很相像 :-) <sect>其它可行性 (Other possibilities) <p> 本章所列均為 Linux 路由和流量引導所需進行的計劃。關於這些聯結﹐有些可能要完全參考原文件﹔有些則自身就寫得非常好﹐而無須更多的 HOWTO。 <p> <descrip> <tag>802.1Q VLAN Implementation for Linux <url url="http://scry.wanfear.com/~greear/vlan.html" name="(site)"></tag> <p> VLAN 是一個非常勁酷的工具﹐能夠更為虛擬而非實體的隔離您的網路。關於 VLAN 的優秀資料可以到這裡發掘﹕ <url url="ftp://ftp.netlab.ohio-state.edu/pub/jain/courses/cis788-97/virtual_lans/index.htm" name="here">。按照這個實作﹐您也可以讓您的 Linux 機器和諸如 Cisco Catalyst、3Com: {Corebuilder, Netbuilder II、SuperStack II switch 630}、Extreme Ntwks Summit 48、Foundry: ServerIronXL, FastIron}﹐這些機器進行 VLAN 連接。 <tag>Alternate 802.1Q VLAN Implementation for Linux <url url="http://vlan.sourceforge.net " name="(site)"></tag>。Alternative VLAN implementation for linux﹐該計劃的起源是因為不認同 '已建立的' VLAN 計劃之架構及編程﹐而另起爐灶﹐進行通盤的全新設計。 <tag>Linux Virtual Server <url url="http://www.LinuxVirtualServer.org/" name="(site)"></tag> <p> 這些高手個個武藝超群。其中 Linux Virtual Server 是構建在真實伺服器族叢之上的伺服器﹐在 Linux 作業系統上執行平行負載﹐極具高伸展性和高可用性。其族叢架構對終端用戶來說是透明的﹐他們只看到單一的虛擬伺服器而已。 一言以蔽之﹐不管您需要在什麼等級的流量上執行什麼樣的平行負載﹐LVS 都有辦法做到。他們某些技術堪稱天才之作﹐實在匪夷所思﹗比方說﹐他們讓數台伺服器在一個網段上使用同一個 IP 位址﹐但把它們的 ARP 關閉掉。只有 LVS 機器進行 ARP --- 它會決定進入封包交由哪台後端主機處理﹐然後直接將他送到正確的後端伺服器之 MAC 位址上。而外送的流量則直接流經路由器﹐而不再經過 LVS 機器﹐如此竟看不到 5Gbit/s 的容量到處亂竄﹐而不會構成瓶頸。 LVS 是用 Linux 2.0 和 2.2 的核心修補來實作﹐然而﹐2.4 裡因為本身自帶 Netfilter 模組﹐所以並不需要核心修補﹗他們的 2.4 支援尚處於初期開發階段﹐所以不妨與他們切磋一下並提供回饋或修補。 <tag>CBQ.init <url url="ftp://ftp.equinox.gu.net/pub/linux/cbq/" name="(site)"></tag> 要設定 CBQ 或會讓人有怯步之感﹐尤其是您所要的僅僅是在一台路由器後面引導某些電腦而已。CBQ.init 可以幫您用簡單的語法設定好 Linux。 例如﹐如果您想要 192.168.1.0/24 這個 subnet(接在 10mbit eth1 之上) 裡面的所有電腦限制在 28kbit/s 的下載速度﹐只需將下面的內容放進 CBQ.init 設定檔裡面﹕ <tscreen><verb> DEVICE=eth1,10Mbit,1Mbit RATE=28Kbit WEIGHT=2Kbit PRIO=5 RULE=192.168.1.0/24 </verb></tscreen> 總的來講﹐如果您對 'how and why' 不感興趣﹐用此程式足矣。我們在真實的營運環境中使用 CBQ.init﹐發現它還真的不負眾望。它甚至還能應付更進階的事情呢﹐例如按時引導之類。它的文件已嵌入在 script 之中﹐這也就是為甚麼您找不到 README 的原因。 <tag>Chronox easy shaping scripts <url url="http://www.chronox.de" name="(site)"></tag> Stephan Mueller (smueller@chronox.de) 寫了兩個非常好用的 script﹕'limit.conn' 和 'shaper'。前者讓您輕鬆的對單一下載連線進行節流﹐只需如此則可﹕ <tscreen><verb> # limit.conn -s SERVERIP -p SERVERPORT -l LIMIT </verb></tscreen> 它在 Linux 2.2 和 2.4 上面都可以使用。 第二個 script 則更為複雜﹐可以根據 iptables 規則做出許多不同的佇列﹐也就是先將封包標識起來﹐再進行引導。 <tag>Virtual Router Redundancy Protocol implementation <url url="http://w3.arobas.net/~jetienne/vrrpd/index.html" name="(site)"></tag> <p> 這個完完全全是為了冗餘(redundancy)所需。用兩台各自擁有 IP 位址和 MAC 位址的機器﹐共同建立起第三個 IP 位址和 MAC 位址﹐當然﹐它是虛擬的。如此設計本來純粹是為路由器而設計的﹐它需要恆定的 MAC 位址﹐但同時也替伺服器工作。 <p> 這個方法的精妙之處是﹐設定起來不費吹灰之力。不需要核心編譯或修補﹐全為使用者空間。 只需在所有參與服務的機器上執行﹕ <tscreen><verb> # vrrpd -i eth0 -v 50 10.0.0.22 </verb></tscreen> 您已漸入佳境了﹗10.0.0.22 現在由您其中一台伺服器擔當﹐很可能是首先執行 vrrp daemon 的機器。現在斷掉這台電腦的連線﹐在極短瞬間之內﹐其它一台電腦就會接管這個 10.0.0.22 位址﹐還有它的 MAC 位址。 我曾在這裡嘗試過﹐而且不到一分鐘就設定起來了。然而不知道什麼原因﹐它會將預設網關丟棄掉﹐不過可以用 -n 旗標來避免啦。 這是一個 '活生生' 的 failover 機制﹕ <tscreen><verb> 64 bytes from 10.0.0.22: icmp_seq=3 ttl=255 time=0.2 ms 64 bytes from 10.0.0.22: icmp_seq=4 ttl=255 time=0.2 ms 64 bytes from 10.0.0.22: icmp_seq=5 ttl=255 time=16.8 ms 64 bytes from 10.0.0.22: icmp_seq=6 ttl=255 time=1.8 ms 64 bytes from 10.0.0.22: icmp_seq=7 ttl=255 time=1.7 ms </verb></tscreen> 竟然沒有丟失 *任何一個* ping 封包﹗我在第 4 個封包之後將我的 P200 連線切斷﹐緊接著我的 486 就接手了﹐您可以發現延遲的增加。 </descrip> <sect>進階參閱 (Further reading) <p> <descrip> <tag><url url="http://snafu.freedom.org/linux2.2/iproute-notes.html" name="http://snafu.freedom.org/linux2.2/iproute-notes.html"></tag> 內有大量技術資料和建議﹐從核心進行解釋。 <tag><url url="http://www.davin.ottawa.on.ca/ols/" name="http://www.davin.ottawa.on.ca/ols/"></tag> Jamal Hadi Salim 的講義﹐他是 Linux 流量控管的作者之一。 <tag><url url="http://defiant.coinet.com/iproute2/ip-cref/" name="http://defiant.coinet.com/iproute2/ip-cref/"></tag> Alexeys LaTeX 兄之 HTML 版本文件 --- 非常細緻的解釋部份 iproute2 技術。 <tag><url url="http://www.aciri.org/floyd/cbq.html" name="http://www.aciri.org/floyd/cbq.html"></tag> Sally Floyd 曾寫過非常棒的 CDQ 文件﹐包括她原始手稿。並沒有任何東西是 Linux 特定的﹐但關於 CBQ 的理論和使用卻有相當正面的論述。 非常技術性﹐如有興趣的話﹐卻非常值得一讀。 <tag><url url="http://ceti.pl/~kravietz/cbq/NET4_tc.html" name="http://ceti.pl/~kravietz/cbq/NET4_tc.html"></tag> 另外一份 HOWTO 文件﹐是波蘭語的﹗不過﹐那些命令行都可以照抄啦﹐不管是用什麼語言都一樣工作。其作者正和我們合作中﹐相信很快會為我們這個 HOWTO 另寫一些章節。 <tag><url url="http://snafu.freedom.org/linux2.2/docs/draft-almesberger-wajhak-diffserv-linux-00.txt" name="Differentiated Services on Linux"></tag> 討論如何在 diffserv compliant 環境中使用 Linux 。和您日常的路由需求相差甚遠﹐不過非常有趣就是了。我們或許稍後會為此另闢章節。 <tag><url url="http://www.cisco.com/univercd/cc/td/doc/product/software/ios111/cc111/car.htm" name="IOS Committed Access Rate"></tag> <label id="CAR"> 來自 Cisco 的熱心成員﹐他們經常將文件公佈在網站上﹐是十分值得讚賞的。Cisco 的語法或許有別﹐但概念都是一脈相承的﹐只是我們沒有用貴如跑車的 router﹐ 而需要花更多功夫而已 :-) <tag>TCP/IP Illustrated, volume 1, W. Richard Stevens, ISBN 0-201-63346-9</tag> 如果閣下真的想要了解 TCP/IP﹐這是必讀的聖經。當然﹐若能自得其樂於其中也不錯啦。 </descrip> <sect>感謝 (Acknowledgements) <p> 所有對本 HOWTO 做過貢獻﹐或是幫我們揭開事物神秘面紗的每位朋友﹐我們都會把他們的芳名列出來。目前來說﹐我們還沒計劃做一個類似 Netfilter 他們那樣的計分板﹐不過﹐對於所有幫助過我們的朋友﹐將永遠銘感於心。 <itemize> <item>Ron Brinker <service%emcis.com> <item>Lennert Buytenhek <buytenh@gnu.org> <item>Esteve Camps <esteve@hades.udg.es> <item>Marco Davids <marco@sara.nl> <item>Stephan "Kobold" Gehring <Stephan.Gehring@bechtle.de> <item>Nadeem Hasan <nhasan@usa.net> <item>Koos van den Hout <koos@kzdoos.xs4all.nl> <item>Philippe Latu <philippe.latu%linux-france.org> <item>Jason Lunz <j@cc.gatech.edu> <item>Alexey Mahotkin <alexm@formulabez.ru> <item>Pawel Krawczyk <kravietz%alfa.ceti.pl> <item>Wim van der Most <item>Stephan Mueller <smueller@chronox.de> <item>Ram Narula <ram@princess1.net> <item>Jorge Novo <jnovo@educanet.net> <item>Patrik <ph@kurd.nu> <item>Jason Pyeron <jason%pyeron.com> <item>Rusty Russell (with apologies for always misspelling your name) <item>Jamal Hadi Salim <hadi%cyberus.ca> <item>Sheharyar Suleman Shaikh <sss23@drexel.edu> <item>Nick Silberstein <nhsilber%yahoo.com> <item>Konrads Smelkov <konrads@interbaltika.com> <item>Jason Tackaberry <tack@linux.com> <item>Charles Tassell <ctassell%isn.net> <item>Glen Turner <glen.turner%aarnet.edu.au> <item>Song Wang <wsong@ece.uci.edu> </itemize> </article>