文/朱尚明 李京 黨齊民
鋪設IPv4/IPv6“雙行道”
文/朱尚明 李京 黨齊民
在Linux系統下加載并運行4over6隧道驅動,有效解決IPv4網絡通過IPv6主干網的互聯互通。
目前,大多數隧道機制都集中在解決IPv6邊緣網絡通過IPv4主干網互通的問題,而關于IPv4網絡如何通過IPv6主干網互通問題的解決方案卻很少提及。而且,由于當前IPv6網絡還處于起步階段,IPv6業務的大規模部署還有待時日,造成IPv6骨干網中的大量帶寬閑置,利用隧道穿越IPv6骨干網來傳輸現有IPv4業務的數據也可以實現IPv6帶寬資源的充分利用。因此大量的IPv4網絡通過IPv6骨干網實現互聯是目前研究的一個熱點,4over6隧道就是一種用于解決現存的IPv4網絡通過IPv6主干網互聯互通的機制。
4over6隧道機制主要包括兩方面的功能:控制平面和數據平面。控制平面解決的問題是如何通過隧道端點發現機制來建立4over6隧道,而數據平面則主要關注包括封裝和解封裝的分組轉發處理,如圖1所示。
4over6控制平面處理
4over6機制中,控制平面主要解決隧道端點發現和網絡可達性信息傳輸的問題。4over6控制平面通過對邊界網關協議多協議擴展MP-BGP,再進行4over6擴展來實現。當4over6路由器的IPv4路由信息發生變化時,路由管理模塊通知BGP協議,啟動了4over6功能的BGP協議發送Update分組到其他對端路由器。對端4over6路由器收到Update分組后,在更新本地維護的封裝表的同時,更新本地IPv4路由表,把相應目的I P v 4地址的出接口設置為本地4over6虛接口。控制平面的數據流動方向見圖1的Control flow方向。
4over6數據平面處理
4over6數據平面主要包括3個部分:1.入口PE路由器將接收到的IPv4分組用IPv6頭部進行封裝;2.封裝后的分組在IPv6骨干網中進行傳輸;3.出口PE路由器將分組解封裝為原IPv4分組格式。基于4over6分組傳輸的特點,數據分組的封裝和解封裝在邊界路由器PE(雙棧路由器)上進行。在實現時,每個4over6 PE路由器維護一個4over6虛接口,該虛接口通過維護4over6封裝表來處理分組的封裝和解封裝。該封裝表的每個表項中包含了目的IPv4網絡的地址和掩碼,以及需要轉發到的出口PE路由器的4over6虛接口的IPv6地址。
當IPv4數據分組到達入口邊界路由器PE1時,PE1通過查找轉發表,發現并進入本地虛接口進行處理。在4over6虛接口處理中,通過查找4over6封裝表對分組進行封裝,封裝的目的IPv6地址是出口邊界路由器PE2的虛接口IPv6地址,源地址為PE1的虛接口IPv6地址。經過封裝后的分組通過IPv6骨干網絡傳輸到出口邊界路由器PE2。在收到分組后,PE2對分組進行解封裝,再通過查找IPv4轉發表將原始分組轉發給相應的IPv4網絡。數據平面的數據流動方向見圖1的Data flow方向。

圖1 4over6機制
Linux中的設備分為字符設備、塊設備和網絡設備。4over6隧道設備是一種軟件設備,同時也是一種網絡設備。作為一個模塊,它既可被動態地連接到正在運行的內核,也可以動態地解除連接。圖2為4over6隧道虛擬設備內核驅動的結構(見下頁)。從圖中可以看到,4over6隧道虛擬設備內核驅動主要包括基本隧道、隧道協議以及特定隧道。
內核模塊加載與內核模塊卸載這兩個功能模塊是讓整個隧道正常工作的前提,前者進行一些隧道虛擬設備的初始化工作,后者進行與之對應的清理工作。隧道協議注冊模塊產生的隧道協議實體是報文解封裝模塊的基礎,基本隧道實體注冊模塊產生的基本隧道實體是隧道創建模塊的基礎,而由隧道創建模塊產生的特定隧道是隧道錯誤處理、報文封裝及解封裝等模塊的共同基礎。
隧道實體的內核定義
4over6隧道設備實質上是一種IPv6隧道,在Linux Kernel 2.4.20中,將IP隧道用結構ip_tunnel來表示,但是該結構的定義無法滿足IPv6隧道的需求。參考struct ip_tunnel的定義,本文將IPv6隧道在內核

圖2 4over6隧道虛擬設備內核驅動的結構
中用結構ip6_tnl 來定義,其定義如下:

其中next為指向下一個IPv6隧道實體的指針,dev指向該隧道實體所包含的虛擬設備對象,recursion為數據報遞歸封裝的深度,parms則是隧道實體的屬性對象,包括隧道名稱、下一報頭、隧道起始地址以及流標簽等屬性的IPv6隧道報頭。
4over6隧道協議實體定義為:
其中,ip46_rcv是隧道報文的接收處理函數,專門負責將收到的IPv6數據包進行重組(如果數據包在進入隧道端點時進行了分段處理),并解封裝還原成為IPv4數據包后轉交給上層模塊來處理;ip46_err是隧道報文傳輸過程中的錯誤處理函數。在隧道設備的初始化的過程中,調用inet6_add_protocol函數,將該協議實體注冊到Linux IPv6內核協議棧中。
由于在隧道設備實際運行時,隧道實體的定位與查找比較頻繁,為了提高訪問速度,將隧道實體以哈希表的形式進行組織。其中哈希表大小(HASHSIZE)為32,哈希函數以128位的IPv6地址為參數,將其劃分為4個部分(每一部分為32位),HASH(IPv6地址) = Part1 ^ Part2 ^ Part3 ^ Par&t4(HASHSIZE - 1)。當一個起始地址被分別定位為local和remote的隧道時,通過TUNNEL(HASH(local)^HASH(Remote))便能確定其所在的桶。
隧道實體的配置接口
隧道實體為內核中的數據結構,而管理員在用戶態下對其進行配置,因此需要一種用戶態與內核態信息交互的機制。由于內核態和用戶態使用不同的內存定義,所以二者之間不能直接訪問對方的內存。而應該使用Linux中的用戶和內核態內存交互函數:

這兩個函數均返回不能被復制的字節數,因此,如果完全復制成功,則返回值為0。4over6隧道實體在內核中主要體現為虛擬網絡設備,因此配置程序采用傳統網絡設備的配置方法,即用ioctl系統調用來配置隧道。ioctl系統調用為設備驅動程序執行“命令”提供了一個設備特定的入口點。在用戶空間內調用的ioctl函數一般具有如下原型:

通常原型中的“…”代表可變數目的參數表,cmd為命令字。在實際系統中,系統調用不會真正使用可變數目的參數,而是必須有精確定義的參數個數。每一個設備都可以定義自己的ioctl命令字,命令編號的范圍是由SIOCDEVPRIVATE到SIOCDEVPRIVATE + 15。如果是上述這些命令,則會調用相關接口驅動程序的dev->do_ioctl。該函數接收和通用ioctl函數相同的struct ifreq *指針,其原型如下:

針對IPv6隧道,它定義了4個命令字,這些命令字分別是SIOCGETTUNNEL、SIOCADDTUNNEL、SIOCCHGTUNNEL、SIOCDELTUNNEL。其中SIOCGETTUNNEL命令用于獲取隧道設備的相關屬性信息,其他3個命令分別對應創建、更新以及刪除隧道。用戶空間通過ioctl系統調用,最終調用到內核中定義的函數ip6ip6_tnl_ioctl。管理員提供的信息通過copy_from_user函數,從用戶態復制到內核態,而配置程序的反饋信息則通過copy_to_user函數,由內核態復制到用戶態應用程序。
編程實現
基于上述設計思想,本文對4over6隧道驅動進行了編程實現。編程實現的操作系統采用Red Hat Linux 9 + Kernel 2.4.20作為源碼,以GNU GCC為集成環境,開發語言采用C語言。
由于該驅動為內核態驅動,故以內核模塊的方式實現并加載。基本隧道實體以哈希表的形式進行組織,提高了隧道訪問速度。
隧道虛擬設備調用ip46_tnl_xmit函數對到達隧道入口處的原始IPv4報文進行封裝,使其進入隧道;解封裝函數ip46_rcv對到達隧道出口處的報文進行解封裝處理,并通過4over6隧道協議實體注冊到內核IPv6協議棧中;函數ip46_err對隧道傳輸中所出現的錯誤進行處理,并翻譯為ICMPv4報文,發送給隧道入口節點;隧道配置接口定義為ip46_tnl_ioctl函數,它針對特定的命令執行相應的配置操作。
通過在Linux系統下加載并運行該4over6隧道驅動,跨越校園IPv6網絡的兩個IPv4主機之間不但能夠成功地運行Web、FTP、Telnet等各種不同類型的應用,而且延時、丟包率、吞吐量等性能指標都接近甚至優于這兩臺IPv4主機之間直接互訪的性能。
(作者單位為華東理工大學信息化辦公室)