1. 程式人生 > >《TCP/IP詳解卷2:實現》筆記--介面層:乙太網和環回

《TCP/IP詳解卷2:實現》筆記--介面層:乙太網和環回

1.乙太網介面

Net/3乙太網裝置驅動程式都遵循同樣的設計。對於大多數Unix裝置驅動程式來說,都是這樣,因為寫一個新介面卡的驅動 程式總是在一個已有的驅動程式的基礎上修改而來的。下面我們簡要地概述一下乙太網的標準和一個乙太網驅動程式的設 計。下圖是一個IP分組的乙太網封裝。
我們所討論的最初的乙太網組幀的標準在1982年由Digital裝置公司,intel公司以及施樂公司釋出,並作為今天在TCP/IP網路 中最常使用的格式,另一個可選的格式是IEEE規定的802.2和802.3標準。 下圖列舉了乙太網介面的資料結構和函式。
上圖中,橢圓標識一個函式,方框標識資料結構,圓角方框標識一組函式。

1.1.leintr函式

我們從乙太網幀的接收開始。現在,假設硬體已初始化並且系統已完成配置,當介面產生一個硬體中斷時,leintr被呼叫。在 正常操作中,一個乙太網介面接收發送到它的單播地址和乙太網廣播地址的幀。當一個完整的幀可用時,介面就產生一個 中斷,並且核心呼叫leintr。 leintr檢測硬體,並且如果有一個幀到達,就呼叫leread把這個幀從介面轉移到一個mbuf中(用m_devget),如果硬體報告 一個幀已傳輸完成或發現一個差錯,則leintr跟新相應的介面統計。復位這個硬體,並呼叫lestart來傳輸另一個幀。 所有乙太網裝置驅動程式將他們接受到的幀傳給ether_input做進一步處理。裝置驅動程式構造的mbuf不包括乙太網首部, 乙太網首部作為一個獨立的引數傳遞給ether_input。 乙太網CRC並不總是可用。它由介面硬體來計算和檢驗,介面硬體丟棄到達的CRC差錯幀。

1.2.leread函式

leread函式的開始是由leintr傳給它的一個連續的記憶體緩衝區,並且構造了一個ether_header結構和一個mbuf鏈。這個連結串列 儲存來自乙太網幀的資料。leread還將輸入幀傳給BPF(Berkeley Packet Filter)。 leread有三個引數:unit,標識接收到此幀的特定介面;buf,它指向介面接收到的幀;len,它是幀的位元組數。 主要處理如下: 構造ether_header              ↓ 判斷目的地址是多播地址還是廣播地址,如果是廣播地址,設定M_BCAST,如果是多播地址,設定M_MCAST。這些標誌位以後 會在mbuf中體現。              ↓
如果介面有BPF,則將幀直接傳給BPF。              ↓
呼叫m_devget構造mbuf,並將資料幀內容複製給mbuf。              ↓
呼叫ether_input處理這個分組。

1.3.ether_input函式

函式ether_input檢查結構ether_header來判斷接收到的資料型別,並將接收到的分組加入到佇列中等待處理。 傳給ehter_input的引數有:ifp,指向接收分組介面的ifnet結構的指標;eh,指向接收分組的乙太網首部的指標;m,一個 指向接收分組的指標(不包含乙太網首部)。 函式處理如下: 根據乙太網型別欄位跳轉。對於一個IP分組,schednetisr排程一個IP軟體中斷,並選擇輸入佇列,ipintrq。對於一個ARP分組, 排程ARP軟體中斷,並選擇arpintrq。              ↓
將分組放置到選擇的佇列中,若佇列為空,則丟棄分組。 當ether_input返回時,裝置驅動程式通知硬體它準備接收下一分組。

1.4.ether_output函式

下面檢視乙太網幀的輸出。當一個網路層協議,如IP,呼叫此介面ifnet結構中指定的函式if_output時,開始處理輸出。所有 乙太網裝置的if_output是ether_output,ether_output用14位元組乙太網首部封裝一個乙太網幀的資料部分,並將它放置到介面 的傳送佇列中。 ether_output的引數有:ifp,它指向輸出介面的ifnet結構;m0,要傳送的分組,dst,分組的目的地址;rt0,路由資訊。
函式主要分為四個部分: 驗證 特定協議處理 構造幀 介面排隊

1.5. lestart函式

函式lestart從介面輸出佇列中取出排隊的幀,並交給乙太網卡傳送。如果裝置空閒,呼叫此函式開始傳送幀。ether_output函式 中呼叫介面if_start函式呼叫lestart。 函式的介面如下: 將幀從輸出佇列中退隊              ↓
傳輸幀並傳遞給BPF              ↓
如果裝置準備好,重複傳送多幀              ↓
將裝置標記為忙

2.ioctl系統呼叫

ioctl系統呼叫提供一個通用的命令介面,一個程序用來訪問一個裝置的標準系統呼叫所不支援的特性。ioctl原型為:
int ioctl(int fd, unsigned long com, ...);
本書中所有的ioctl命令如下:
第一列顯示的是符號常量標識ioctl命令(第二個引數,com)。第二列顯示傳遞給第一列所顯示的命令的系統呼叫的第三個 引數的型別。第三個列是實現這個命令的函式的名稱。 下圖顯示處理ioctl命令的各種函式的組織。

2.1.ifioctl函式

函式處理如下:
對於SIOCGIFCONF,ifioctl呼叫ifconf來構造一個可變的ifreq結構的表。ifreq的結構如下:
             ↓ 對於其他的ioctl命令,資料引數是指向一個ifreq結構的指標,ifunit函式在ifnet列表中查詢名稱為程序在ifr->ifr_name中提供的 文字名稱的介面,如果沒有匹配的介面,ifioctl返回ENXIO。              ↓
剩下的處理依賴cmd,在下面的通用介面ioctl命令中會進行說明。              ↓
如果介面ioctl命令不能被識別,ifioctl把命令傳送給與所請求插口關聯的協議的使用者要求函式。對於IP,這些命令以一個UDP插 口傳送並呼叫udp_usrreq。

2.2.ifconf函式

ifconf為程序提供了一個標準的方法來發現一個系統中的介面和配置的地址。

2.3.通用介面ioctl命令

剩下的四個介面命令由函式ifioctl處理。 1.SIOCGIFLAGS和SIOCGIFMETRIC ifioctl將每個介面的if_flags(ifr_flags)或者if_metric(ifr_metric)值複製到ifreq介面。 2.SIOCSIFFLAGS 為了改變介面的標誌,呼叫程序必須有超級使用者許可權。如果程序正在關閉一個執行的介面或啟動一個未執行的介面,分別呼叫 if_down和if_up。 3.SIOCSIFMETRIC 程序需要超級使用者許可權,ifioctl將介面新的度量複製到if_metric中。 4.SIOCSIFMETRIC 為了改變介面的度量,程序需要有超級使用者許可權,ifioctl將介面信的度量複製到if_metric中。

3.環回介面

任何傳送給環回介面的分組立即排入輸入佇列。介面完全用軟體實現。
換回介面的if_output指向的函式looutput,將輸出分組放置到分組的目的地址指明協議的輸入佇列中。