閒談IPv6-沒有選項勝有選項的TLV

夏末18844發表於2019-03-13

IPv6協議頭固定,並且非常簡單,去掉了IPv6的選項欄位,把變長頭部統改成了定長。這有什麼益處就不多說了,肯定百分百地是提升了處理效率。然而,這裡我要說的是,在獲得這些收益的同時,其實並沒有付出任何代價!

這也就是說,IPv4的設計在某種意義上是錯誤的設計!不過呢,也不必因為我這句話而大跌眼鏡,進化嘛,缺陷總是有的。

我們乍看IPv6,好像是缺失了options選項,這是不是意味著它和IPv4相比,功能弱化了很多呢?非也!

相反,IPv6強化了很多。看似矛盾的兩個IP版本,其實並不矛盾!

IPv6並不是簡單地說一句 “不支援選項了” ,它的意思實際上是在說, “IPv6可以無限支援選項!” 它之所以把IP選項從IP頭裡面剝離開來,其實是想更好地去處理選項,不然,最多40位元組的選項實在是太雞肋了,脫離IP頭部而自立門戶顯然是一個更好的選擇。

鏈式協議頭

為了處理這些少到無,多到無窮盡的 選項 ,IPv6採用了鏈式處理。如果可能,則將每一個選項邏輯都單列為一個 上層協議 ,以next header來標識,這便為IPv6的擴充套件提供了無限的可能。比如下面的鏈式處理:

我們看一下IPv4的情況:

TLV結構

也許是我接觸TLV之前沒有接觸過其它什麼其它的表示型語法,但我就是喜歡TLV,相反,我討厭json,XML,HTML這些。

我並不怎麼懂程式設計,也不懂什麼程式語言,加上我感興趣的最初的網路協議也都不是用於表示內容的,而是僅僅規定格式的,所以,我第一次接觸TLV是很晚了,大概是在2010年左右,我當時在做X.509證書的相關工作,涉及到ASN.1編碼。

第一次接觸TLV,我感覺這太棒了!它像是一個俄羅斯套娃,竟然可以上下無限擴充套件,使我可以開心顏!簡直太棒了!

說了這麼多,其實我想說,IPv6的諸多options擴充套件頭,都是採用了TLV的編碼格式。

我們看看 HAO擴充套件,大概就是Home Address Option的縮寫,它其實就是一個IPv6地址,代表在移動IP中的 家鄉IPv6地址 的概念。它就是一個TLV結構表示的結構體:

/*

* home address option in destination options header

*/

struct ipv6_destopt_hao {

__u8 type;

__u8 length;

struct in6_addr addr;

} __attribute__((packed));

它被封裝在了一個叫做ipv6_destopt_hdr的擴充套件頭裡:

struct ipv6_opt_hdr {

__u8 nexthdr;

__u8 hdrlen;

/*

* TLV encoded option data follows.

*/

} __attribute__((packed)); /* required for some archs */

#define ipv6_destopt_hdr ipv6_opt_hdr

在IPv6裡,ipv6_destopt_hdr這個擴充套件選項頭的協議號(***記住,在IP看來,它是一個“上層協議”***)是60:

#define IPPROTO_DSTOPTS 60 /* IPv6 destination options */

因此,假設一個節點的Home地址是240e:111::123,其轉交地址care-of為2007:111::123,那麼它發往2009:111::123的TCP封包就應該是:

hop-by-hop 與TLV

事實上,你可以在任何支援TLV的擴充套件頭裡去拼裝任何的TLV,關於該TLV的解釋,你只需要自行實現一個回撥函式即可。

這裡要提到的就是 逐跳頭 。

IPv6的逐跳頭,意思是說, 每一個經由的路由器都要去解析這個頭部所包含的每一個選項。 這個是協議層面的實現決定的,如果你的實現中沒有顯式解析這個頭部的選項,那就意味著你沒有完備實現IPv6協議,看起來很死板,不是嗎?但是逐跳頭對其包含的選項的解釋確實靈活多變的。

逐跳頭包含一系列的TLV選項,一個或者多個。每一個TLV選項都有一個特定的處理方式, 包括但不限於:

直接處理,更新協議棧資料結構。

將資料交由應用層去處理

無論如何,協議棧必須對逐跳頭有所反應,也就是說,如果這個逐跳頭是錯誤的,有問題的,那麼就必須要返回錯誤。

你想特殊處理逐跳頭嗎?

你想新增新的逐跳選項麼?簡單註冊一個TLV處理函式即可!

我們來看一下Linux核心是如何處理逐跳頭的,首先在ipv6_rcv中,顯式處理逐跳頭:

if (hdr->nexthdr == NEXTHDR_HOP) {

if (ipv6_parse_hopopts(skb) < 0) {

IP6_INC_STATS_BH(net, idev, IPSTATS_MIB_INHDRERRORS);

rcu_read_unlock();

return NET_RX_DROP;

這就是顯式呼叫。

其中ipv6_parse_hopopts完成了一切。在該函式中,內部呼叫ip6_parse_tlv函式:

1在一個迴圈解析中,呼叫可能的func回撥函式!

反正就是說,只要資料包到達,並且其中有逐跳頭,那你就必須去處理它,至於說怎麼處理,標準就不管了。雖然標準不管,我們還是可以理解一個應用,那就是資源預留,即RSVP執行的場景。

來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/31557905/viewspace-2638298/,如需轉載,請註明出處,否則將追究法律責任。

相關文章