?? (ldd) ch14-網(wǎng)絡(luò)驅(qū)動(dòng)程序(下)(轉(zhuǎn)載).txt
字號:
void kfree_skb(struct sk_buff *skb, int rw);
void dev_kfree)skb(struct sk_buff *skb, int rw);
釋放一個(gè)緩沖區(qū)。kfree_skb被核心內(nèi)部使用。驅(qū)動(dòng)程序應(yīng)該使用dev_kfree_skb,在擁
有緩沖區(qū)的套接字需要再次使用它的情況下,它可以正確地處理緩沖區(qū)加鎖。兩個(gè)函數(shù)
有緩沖區(qū)的套接字需要再次使用它的情況下,它可以正確地處理緩沖區(qū)加鎖。兩個(gè)函數(shù)
的rw參數(shù)是FREE_READ或FREE_WRITE。這個(gè)值用來跟蹤套接字的內(nèi)存。外出緩沖區(qū)應(yīng)用
FREE_WRITE來釋放,而進(jìn)來的則使用FREE_READ。
unsigned char *skb_put(struct sk_buff *skb, int len);
這個(gè)線入函數(shù)更新結(jié)構(gòu)sk_buff的tail和len域,它被用來在緩沖區(qū)尾加入數(shù)據(jù)。其返回
值是skb->tail以前的值(或者說,它指向剛生成的數(shù)據(jù)空間)。有些驅(qū)動(dòng)程序通過調(diào)用
ins(ioaddr,skb_put(…))或memcpy(skb_put(…), data,len)來使用這個(gè)返回值。這個(gè)
函數(shù)及下面的一些在為Linux1.2構(gòu)造模塊是不存在。
unsigned char *skb_push(struct sk_buff *skb, int len);
這個(gè)函數(shù)減小skb->data,增加skb->len。類似于skb_put,除了數(shù)據(jù)是加在包開始而不
是結(jié)尾。返回值指向剛生成的空間。
int skb_tailroom(struct sk_buff *skb);
這個(gè)函數(shù)返回為在緩沖區(qū)中放置數(shù)據(jù)的可用空間量。如果驅(qū)動(dòng)程序在緩沖區(qū)中放了多于
它能承載的數(shù)據(jù),系統(tǒng)可能回崩潰。你也許會(huì)反對并認(rèn)為,用printk指出這個(gè)錯(cuò)誤已經(jīng)
足夠了,而內(nèi)存崩潰對系統(tǒng)太有害了,開發(fā)者肯定要采取一些措施。但實(shí)際上,如果緩
沖區(qū)被正確分配了,你根本不必檢查可用空間。因?yàn)轵?qū)動(dòng)程序通常在分配緩沖區(qū)之前獲
得包大小,只有有嚴(yán)重缺陷的驅(qū)動(dòng)程序才可能在緩沖區(qū)內(nèi)放太多的數(shù)據(jù),崩潰可以認(rèn)為
得包大小,只有有嚴(yán)重缺陷的驅(qū)動(dòng)程序才可能在緩沖區(qū)內(nèi)放太多的數(shù)據(jù),崩潰可以認(rèn)為
是應(yīng)得的懲罰。
int skb_headroom(struct sk_buff *skb);
返回?cái)?shù)據(jù)前面得可用空間量,也就是可以向緩沖區(qū)中“推”多少八元組。
void skb_reserve(struct sk_buff *skb, int len);
這個(gè)函數(shù)增加data和tail。它可以用來在填充緩沖區(qū)前預(yù)留空間。大多數(shù)以太網(wǎng)接口在
包前預(yù)留兩個(gè)字節(jié);這樣IP頭可以在一個(gè)4字節(jié)以太網(wǎng)頭之后,在16字節(jié)邊界對齊。snul
l完成得很好,盡管在“包接收”中并未提到這一點(diǎn),那主要是為了避免彼時(shí)引入過多得
概念。
unsigned char *skb)pull(struct sk_buff *skb, int len);
從包頭中刪除數(shù)據(jù)。驅(qū)動(dòng)程序并不用這個(gè)函數(shù),但為了完整性也包含在這里。它減少skb
->len,增加skb->data;這是從進(jìn)來包的開始剝出以太網(wǎng)包頭的方法。
核心還定義了幾個(gè)在套接字緩沖區(qū)上操作得別的函數(shù),但它們主要應(yīng)用于網(wǎng)絡(luò)代碼得高
層,驅(qū)動(dòng)程序并不需要它們。
層,驅(qū)動(dòng)程序并不需要它們。
地址解析
以太網(wǎng)通信最急迫得問題之一是硬件地址(接口得唯一標(biāo)志符)與IP號碼之間的關(guān)聯(lián)。
大多數(shù)協(xié)議都有類似問題,但我只向重點(diǎn)討論一下以太網(wǎng)類得情況。我力圖給出一個(gè)全
面得描述,因此我將顯示三種情況:ARP,沒有ARP的以太網(wǎng)頭(象plip),以及非以太網(wǎng)
包頭。
在以太網(wǎng)上使用ARP
地址解析得一般方法是ARP,即地址解析協(xié)議。幸運(yùn)的是,ARP由核心管理,以太網(wǎng)接口
不必為支持ARP做任何特殊工作。只要在打開時(shí)正確地設(shè)置了dev->addr和dev->addr_len
,驅(qū)動(dòng)程序不需擔(dān)心任何從IP號碼到物理地址的轉(zhuǎn)換;ether_setup將正確的設(shè)備方法賦
給dev->hard_header和dev->rebuild_header。
當(dāng)一個(gè)包被構(gòu)造時(shí),以太網(wǎng)包頭由dev->hard_header來布局,并由dev->rebuild_header
在后來填充,它使用ARP協(xié)議將未知的IP號碼映射到地址上。驅(qū)動(dòng)程序作者不必知道這個(gè)
過程的細(xì)節(jié)去寫一個(gè)可工作得驅(qū)動(dòng)程序。
越過ARP
越過ARP
簡單得點(diǎn)到點(diǎn)網(wǎng)絡(luò)接口如plip可以從以太網(wǎng)包頭獲益,但卻要避免來回發(fā)送ARP包得開銷
。snull中得示例代碼就屬于這一類網(wǎng)絡(luò)設(shè)備。snull不能使用ARP,因?yàn)轵?qū)動(dòng)程序修改被
發(fā)送得包得IP地址,而ARP包也交換IP地址。
如果你的設(shè)備想用一般的硬件包頭,卻不想運(yùn)行ARP,你需要越過缺省的dev->rebuild_h
eader方法。這就是snull實(shí)現(xiàn)的方法,這個(gè)簡單的函數(shù)有三條語句:
(代碼329)
事實(shí)上,并沒有指定eth->h_source和eth->h_dest內(nèi)容的實(shí)際需要,因?yàn)檫@些值只被用
來進(jìn)行包得物理傳送,而一個(gè)點(diǎn)到點(diǎn)得連接保證能將包發(fā)送到它的目的地,而與硬件地
址無關(guān)。snull重構(gòu)包頭的原因是向你演示,當(dāng)eth_rebuild_header不可用時(shí),一個(gè)真實(shí)
的網(wǎng)絡(luò)接口的重構(gòu)函數(shù)是如何實(shí)現(xiàn)的,
當(dāng)接口收到一個(gè)包時(shí),硬件包頭只被eth_type_trans使用。我們在snull_rx中已經(jīng)見過
這個(gè)調(diào)用:
skb->protocol=eth_type_trans(skb,dev);
這個(gè)函數(shù)從以太網(wǎng)包頭中抽取協(xié)議標(biāo)志符(在這里是ETH_P_IP);它還要賦值skb->mac.
raw,從包數(shù)據(jù)中刪去硬件包頭,并設(shè)置skb->pkt_type。最后一項(xiàng)在skb分配時(shí)缺省為PA
raw,從包數(shù)據(jù)中刪去硬件包頭,并設(shè)置skb->pkt_type。最后一項(xiàng)在skb分配時(shí)缺省為PA
CKET_HOST(表明包被指向這個(gè)主機(jī)),當(dāng)然它也可以改為符合以太網(wǎng)目的地址得其它值
。
如果你的接口是點(diǎn)到點(diǎn)連接,你將無法收到未想到的選播包。為避免這個(gè),你必須記住
那些第一個(gè)八元組的最低位(LSB)是0的目的地址將被指向單個(gè)主機(jī)(也就是說,它是P
ACKET_HOST或PACKET_OTHERHOST)。plip驅(qū)動(dòng)程序用0xfc作為它的硬件地址的第一個(gè)八
元組,而snull用0x00。這兩個(gè)地址都導(dǎo)致一個(gè)可工作的以太網(wǎng)類的點(diǎn)到點(diǎn)連接。
非以太網(wǎng)包頭
這節(jié)簡要地介紹硬件包頭是如何用來封裝相關(guān)信息的。如果你希望了解細(xì)節(jié),你可以從
核心源碼或特別傳輸介質(zhì)的技術(shù)文檔中得到。我們剛才已經(jīng)看到硬件包頭除了含有目的
地址外,還有一些信息,其中最重要的是通信協(xié)議。
不過,并不是每個(gè)協(xié)議都要提供所有的信息。象plip或snull之類的點(diǎn)到點(diǎn)連接可以避免
傳送整個(gè)以太網(wǎng)包頭,同時(shí)不失去一般性。hard_header設(shè)備方法從核心接收傳送信息--
--包括協(xié)議級和硬件地址。它也收到16位的協(xié)議號。例如IP由ETH_P_IP標(biāo)志。驅(qū)動(dòng)程序
應(yīng)能正確地象接收主機(jī)傳送包數(shù)據(jù)和協(xié)議號。點(diǎn)到點(diǎn)連接可以在硬件包頭中省略地址,
只傳送協(xié)議號,因?yàn)閭魉褪怯斜WC的,與源和目的地址無關(guān)。一個(gè)只有IP的連接甚至什
么硬件頭都不傳送。兩種情況下,所有的工作都由hard_header完成,rebuild_header除
了返回0外什么都不做。
當(dāng)包在連接的另一端被撿起時(shí),接收函數(shù)將正確地設(shè)置skb->protocol,skb->pkt_type
,和skb->mac.raw。
skb->mac.raw是一個(gè)被網(wǎng)絡(luò)高層代碼實(shí)現(xiàn)的地址解析機(jī)制使用的字符指針(例如,net/i
pv4/arp.c)。它必須指向一個(gè)與dev->type匹配的機(jī)器地址。設(shè)備類型的可能值被定義
在<linux/if_arp.h>;以太網(wǎng)接口用ARPHRD_ETHER。例如,下面是eth_type_trans處理
收到包的以太網(wǎng)包頭的方法:
skb->mac.raw=skb->data;
skb_pull(skb, dev->hard_header_len);
在最簡單的情況下(無包頭的點(diǎn)到點(diǎn)連接),skb->mac.raw可以指向一個(gè)含有這個(gè)接口
的硬件地址的靜態(tài)緩沖區(qū),protocol可以被置為ETH_P_IP,packet_type仍維持其缺省值
PACKET_HOST。
加載時(shí)配置
用戶可以用幾個(gè)標(biāo)準(zhǔn)的關(guān)鍵字來配置接口。任何新的網(wǎng)絡(luò)模塊都應(yīng)遵循這個(gè)標(biāo)準(zhǔn):
io=
為接口設(shè)置I/O端口的基地址。如果系統(tǒng)中安裝了不只一個(gè)接口,那么可以用一個(gè)由逗號
分隔的列表來指定。
irq=
設(shè)置中斷號。和上面一樣,可以指定不止一個(gè)值。
換句話說,一個(gè)裝了兩個(gè)own_eth接口的Linux用戶可能用下面的命令行來加載模塊:
insmod own_eth.o io=0x300, 0x320 irq=5,7
如果指定0值,那么io=和irq=選項(xiàng)都要被探測。因此用戶可以通過指定io=0來強(qiáng)制探測
。如果用戶名優(yōu)指定任何選項(xiàng),多數(shù)驅(qū)動(dòng)程序通常都探測一個(gè)接口,但有時(shí),模塊可能
被禁止探測。(見ne.c中關(guān)于NE2000設(shè)備的探測)。
設(shè)備驅(qū)動(dòng)程序應(yīng)該象剛才描述的這樣工作。ISA設(shè)備的典型實(shí)現(xiàn)如下所示,假設(shè)驅(qū)動(dòng)程序
設(shè)備驅(qū)動(dòng)程序應(yīng)該象剛才描述的這樣工作。ISA設(shè)備的典型實(shí)現(xiàn)如下所示,假設(shè)驅(qū)動(dòng)程序
最多可以支持四個(gè)接口:
(代碼331)
這段代碼缺省探測一個(gè)板子,并總是自動(dòng)探測中斷,但用戶可以改變這種行為。例如,i
o=0,0,0將探測三塊板子。
除了使用io和irq外,驅(qū)動(dòng)程序的作者可以隨意增加其它加載時(shí)配置參數(shù)。也沒有已建立
的命名標(biāo)準(zhǔn)。
運(yùn)行時(shí)配置
用戶有時(shí)可能希望在運(yùn)行時(shí)改變接口的配置。例如,當(dāng)中斷號無法探測時(shí),想對它正確
配置的唯一辦法就是“嘗試—錯(cuò)誤”技術(shù)。一個(gè)用戶空間的程序可以獲取設(shè)備的當(dāng)前配
置,并通過在一個(gè)打開的套接字上調(diào)用ioctl來設(shè)置一個(gè)新的配置。例如,應(yīng)用ifconfig
使用ioctl為接口設(shè)置I/O端口。
我們前面知道一個(gè)為網(wǎng)絡(luò)接口定義的方法中的一個(gè)是set_config。這個(gè)方法被用來在運(yùn)
我們前面知道一個(gè)為網(wǎng)絡(luò)接口定義的方法中的一個(gè)是set_config。這個(gè)方法被用來在運(yùn)
行時(shí)設(shè)置或改變一些接口特征。
當(dāng)一個(gè)程序詢問當(dāng)前配置時(shí),核心從結(jié)構(gòu)device中抽取相關(guān)信息,而不通知驅(qū)動(dòng)程序;
另一方面,當(dāng)一個(gè)新的配置被傳遞給接口時(shí),set_config被調(diào)用,這樣驅(qū)動(dòng)程序就可以
檢查這些值并采取相應(yīng)的動(dòng)作。這個(gè)驅(qū)動(dòng)程序方法對應(yīng)下面的原形:
int (*set_config)(struct device *dev, struct ifmap *map);
map參數(shù)指向一個(gè)由用戶程序傳遞的結(jié)構(gòu)的拷貝;這個(gè)拷貝已經(jīng)在核心空間,所以驅(qū)動(dòng)程
序不需要調(diào)用memcpy_from _fs。
結(jié)構(gòu)ifmap的域是:
unsigned long mem_start;
unsigned long mem_end;
unsigned short base_addr;
unsigned char irq;
unsigned char dma;
unsigned char dma;
這些域?qū)?yīng)著結(jié)構(gòu)device中的域。
Unsigned char prot;
這個(gè)域?qū)?yīng)著dev中的if_port。map->port的含義是設(shè)備特定的。
當(dāng)一個(gè)進(jìn)程為設(shè)備發(fā)出ioctl(SIOCSIFMAP)(即Socket I/O Control Set InterFace
MAP)時(shí),set_config設(shè)備方法被調(diào)用。這個(gè)進(jìn)程在時(shí)強(qiáng)制使用新值之前,應(yīng)該發(fā)出ioctl
(SIOCGIFMAP)(即Socket I/O Control Get InterFace MAP),這樣驅(qū)動(dòng)程序只需要查看d
ev和ifmap結(jié)構(gòu)不匹配的地方。Map中任何不被驅(qū)動(dòng)程序使用的域均可以略過。例如,一
個(gè)不使用DMA的網(wǎng)絡(luò)設(shè)備可以忽略map->dma。
snull實(shí)現(xiàn)被設(shè)計(jì)成可以顯示驅(qū)動(dòng)程序是如何針對配置改變而動(dòng)作的。對snull來說,沒
有一個(gè)域由物理意義。但出于說明的目的,代碼禁止改變I/O地址,允許改變IRQ號,并
忽略其它選項(xiàng),從而顯示這些改變是如何被響應(yīng)、拒絕、或是忽略的。
(代碼333 #1)
這個(gè)方法的返回值被作為發(fā)出的ioctl系統(tǒng)調(diào)用的返回值,對于沒有實(shí)現(xiàn)set_config的驅(qū)
這個(gè)方法的返回值被作為發(fā)出的ioctl系統(tǒng)調(diào)用的返回值,對于沒有實(shí)現(xiàn)set_config的驅(qū)
動(dòng)程序則返回-EOPNOTSUPP。
如果你對接口配置如何從用戶空間訪問感到好奇,請看misc-progs/netifconfig.c,它
可以用來與set_config比較。下面是一個(gè)示例運(yùn)行的輸出:
(代碼333 #2)
自定義ioctl命令
我們已經(jīng)看到ioctl系統(tǒng)調(diào)用是為套接字實(shí)現(xiàn)的。SIOCSIFADDR和SIOCSIFMAP是“套接字i
octl”的例子現(xiàn)在讓我們看看這個(gè)系統(tǒng)調(diào)用的第三個(gè)參數(shù)是如何被網(wǎng)絡(luò)代碼使用的。
當(dāng)ioctl系統(tǒng)調(diào)用在套接字上被調(diào)用時(shí),其命令號是在<linux/sockios.h>定義的符號之
一,并且函數(shù)sock_ioctl直接調(diào)用一個(gè)協(xié)議特定的函數(shù)(這里協(xié)議指使用的主要網(wǎng)絡(luò)協(xié)
議,如IP或AppleTalk)。
任何協(xié)議層不認(rèn)識的ioctl命令被傳遞給設(shè)備層。這些設(shè)備相關(guān)的ioctl命令從用戶空間
接收第三個(gè)參數(shù),即結(jié)構(gòu)ifreq*;這個(gè)結(jié)構(gòu)在<linux/if.h>中定義。SIOCSIFADDR和SIOC
接收第三個(gè)參數(shù),即結(jié)構(gòu)ifreq*;這個(gè)結(jié)構(gòu)在<linux/if.h>中定義。SIOCSIFADDR和SIOC
SIFMAP命令實(shí)際上是工作在ifreq結(jié)構(gòu)上。SIOCSIFMAP的額外參數(shù),盡管定義為ifmap,
是ifreq的一個(gè)域。
?? 快捷鍵說明
復(fù)制代碼
Ctrl + C
搜索代碼
Ctrl + F
全屏模式
F11
切換主題
Ctrl + Shift + D
顯示快捷鍵
?
增大字號
Ctrl + =
減小字號
Ctrl + -