ipv6 埠號 翻譯 IPv6 與 Rust

2021-10-11 10:39:35 字數 4058 閱讀 4703

rust是一門旨在將記憶體安全和高效能相結合的語言,同時它也提供了能夠在高效、友好的環境中編寫正確**的工具。它是mozilla內部在2023年代末的乙個研究專案,到2023年趨於穩定並可以用於生產環境。

在nlnet labs, 我們在準備啟動兩個與路由安全框架相關的兩個專案時選擇了rust,這兩個專案是pki: the certification authority kril,以及依賴軟體,routinator。 最初只是被保證記憶體安全這一特點所吸引,但是後來我們開始喜歡上它出色的原生構建工具和豐富的型別系統,該型別系統能夠在編譯期預防許多常見錯誤。

但是,使用rust及其生態系統來寫支援ipv6的網路應用程式難度有多大呢?

儘管rust的標準庫非常小,它憑藉十分易用的依賴管理,除了最基礎的功能外,都將其轉移到外部庫,但是它確實包含構建網路應用程式所必須的完整的原語集。這些原語是按照berkeley套接字型檔建模的:如果您熟悉c語言中的網路程式設計,您就會很快熟悉rust的原語。

當然,除此之外,rust的標準庫使用了該語言的型別系統。沒有簡單地使用無型別的檔案描述符,每種socket都有它自己的型別:tcpstream和udpstream分別用於使用tcp和udp協議的socket,tcplistener用於socket監聽即將到來的tcp連線。與berkeley sockets非常相似,這些型別具有連線到遠端位址或繫結到本地位址的connect和bind方法。

對於這些位址,socket型別同時接受ipv4和ipv6位址協議族。型別系統再次發揮了作用。

rust強大的列舉型別——本質上是一種原生的、被語言良好支援構建的標記聯合型別——允許在協議族之間進行選擇。ip位址的型別,即ipaddr,是一種包含ipv4addr或者ipv6addr的列舉。 socketaddr型別在這種ip位址型別之上新增了乙個埠號。

對乙個給定的ipv6位址可以選擇手動來建立,但是這樣有些枯燥:

use std::net::;

let addr = ipaddr::v6(ipv6addr::new(

0x2001, 0xdb8, 0, 0, 0, 0, 0,1

));

幸運的是,這種事情幾乎不再是必須的。相反,位址通常是從使用者輸入讀取,需要從字串形式進行轉換。使用fromstr trait進行這種操作就變得很容易。這個fromstrtrait相當於rust中的乙個介面,可以被所有的address型別來實現:

use std::net::ipaddr;

use std::str::fromstr;

let addr = ipaddr::from_str("2001:db8::1").unwrap();

注意這裡ipaddr的使用,沒有顯式地指明位址協議族。大多數情況下,網路應用程式不應該關心乙個使用者提供的位址是ipv4還是ipv6。這個介面提供了這種透明度。

socket位址也可以由字串來建立。fromstr的實現要求主機和埠之間使用冒號分隔開,ipv6位址應該用方括號來包含。儘管如此,還是有乙個單獨的trait可以建立socket位址,即tosocketaddrs。這個trait不僅能對字串表示的位址進行轉換,而且還能處理使用pair來表示的位址和埠號:

use std::net::;

let addrs = "[2001:db8::1]:443".to_socket_addrs().unwrap();

let addrs = ("2001:db8::1", 443).to_socket_addrs().unwrap();

更好的是,tosocketaddrs還能通過本地的解析器(resolver)來查詢主機名。因為這樣的查詢可以得到不止乙個位址,所以tosocketaddrs實際上返回的是乙個socket位址的迭代器。這是否意味著當呼叫,比如說tcpstream::connect的時候,不得不去遍歷所有的位址?不,這個方法已經幫你做了這一步。並且它甚至還能接受任何實現了tosocketaddrs的事物來作為它的引數。所以,要連線到apnic(亞太網際網路絡資訊中心)的web伺服器的443埠,你只需要做下面這些:

use std::net::tcpstream;

let sock = tcpstream::connect("apnic.net:443").unwrap();

在內部,該實現從底層c庫呼叫getaddrinfo,而沒有指定位址族。因此,如果ipv4和ipv6都可用,它將對ipv4和ipv6位址產生乙個序列並且會連線到第乙個可以工作的位址。至於選擇的是ipv4還是ipv6則取決於底層c庫的實現以及本地的網路。

對於大多數應用程式,這樣都可以完美工作。但是,僅使用標準庫,無法對ipv6 socket進行主機解析。幸運地是,net2 crate為網路原語增加了更多選項。 儘管設定socket選項通常是必要的,但是它還提供了socket構建器-tcpbuilder和udpbuilder,通過它們可以設定位址協議族。 使用net2, 通過ipv6連線到apnic就相當簡單了:

use net2::tcpbuilder;

let builder = tcpbuilder::new_v6().unwrap();

let sock = builder.connect("apnic.net:443").unwrap();

儘管解析器仍然會產生ipv4和ipv6位址,但是使用僅ipv6的socket連線前者會失敗,從而將這些位址進行過濾。

對於tcplistenerudpsocket繫結本地位址,過程也是類似。bind方法接收實現了tosocketaddrs的型別作為引數,嘗試為每乙個產生的位址進行bind,直到它操作成功:

use std::net::tcplistener;

let sock = tcplistener::bind("[::1]:8080").unwrap();

同樣的,在這裡,提供的是ipv4位址還是ipv6位址並不重要,標準庫會負責辨別使用哪個位址協議族。

如果乙個socket已經連線,查詢它的本地和遠端位址也是可以的。每乙個socket型別都有乙個local_addr方法返回socket被繫結的本地位址。tcpstreamudpstream還可以通過peer_addr返回遠端位址。這兩個方法都返回乙個ipaddr的值,即使是通過net2指定位址協議族建立的socket也是如此。儘管這個值可以被直接顯示,比如需要打日誌,但是如果需要指定的ipv6處理,它需要進行匹配(match):

use std::net::;

let sock = tcpstream::connect("apnic.net:443").unwrap();

let addr = sock.peer_addr().unwrap();

println!(「peer address: {}」);

if let ipaddr::v6(addr) = addr

重申一次,模式是相同的:如果你正在開發一款對位址協議和位址解析沒有特殊要求的網路應用程式,你不需要關心位址協議族和位址之間的差異以及主機名解析-標準庫會做出正確的選擇。

乙個現代開發平台應該如何對待ipv6: 假定情況被悄悄地考慮了。這或許是rust及其生態系統最令人震驚的乙個例子:許多經過仔細思考,平衡的設計決策。在用rust開發了兩年可用於生產的軟體之後,我們對自己的決定非常滿意。

原文作者martin hoffmann是nlnet labs的實驗室,他專注於軟體和標準路由安全性。

IPv6技術 什麼是IPv6

ipv6技術 1 引言 ipv4位址即將耗盡,因此需要移轉至ipv6的討論,過去數年來一直不曾中斷。ipv4和ipv6在報文結構 報文字段意義以及位址配置諸多方面都有顯著的不同,這給從ipv4到ipv6過渡時期internet的運作帶來了極大的困難。當前接入網技術上幾乎所有的應用都是構建在ipv4上...

IPv6知識概述 IPv6位址

首選格式 x x x x x x x x x表示乙個4位十六進製制數 典型的例子如下所示 2000 0000 0000 0000 0001 2345 6789 abcd ipv6位址每段中的前導0是可以去掉的,但是至少要保證每一段有乙個數字。將不必要的前導0去掉後,上述位址可以表示為 2000 0 ...

IPV6基礎知識 IPV6鄰居發現協議

ipv6鄰居發現協議 neighbor discovery protocol,ndp 是用於替代ipv4中的arp協議的,用於實現網路層位址與鏈路層位址之間的對映,ndp實現效率要比arp高。ipv6鄰居發現協議可提供以下功能 1 無伺服器的自動配置 2 路由發現 3 位址解析 4 鄰居不可達檢測 ...