虛擬網(wǎng)卡 TUN/TAP 驅(qū)動(dòng)程序設(shè)計(jì)原理
麻利輝
研究生, 電子科技大學(xué)計(jì)算機(jī)學(xué)院研究生部
2004 年 11 月
本文將介紹 TUN/TAP 驅(qū)動(dòng)的使用并分析虛擬網(wǎng)卡 TUN/TAP 驅(qū)動(dòng)程序在 Linux 環(huán)境下的設(shè)計(jì)思路。
簡(jiǎn)介
虛擬網(wǎng)卡Tun/tap驅(qū)動(dòng)是一個(gè)開(kāi)源項(xiàng)目,支持很多的類(lèi)UNIX平臺(tái),OpenVPN和Vtun都是基于它實(shí)現(xiàn)隧道包封裝。本文將介紹tun/tap驅(qū)動(dòng)的使用并分析虛擬網(wǎng)卡tun/tap驅(qū)動(dòng)程序在linux環(huán)境下的設(shè)計(jì)思路。
tun/tap驅(qū)動(dòng)程序?qū)崿F(xiàn)了虛擬網(wǎng)卡的功能,tun表示虛擬的是點(diǎn)對(duì)點(diǎn)設(shè)備,tap表示虛擬的是以太網(wǎng)設(shè)備,這兩種設(shè)備針對(duì)網(wǎng)絡(luò)包實(shí)施不同的封裝。利用tun/tap驅(qū)動(dòng),可以將tcp/ip協(xié)議棧處理好的網(wǎng)絡(luò)分包傳給任何一個(gè)使用tun/tap驅(qū)動(dòng)的進(jìn)程,由進(jìn)程重新處理后再發(fā)到物理鏈路中。開(kāi)源項(xiàng)目openvpn( http://openvpn.sourceforge.net)和Vtun( http://vtun.sourceforge.net)都是利用tun/tap驅(qū)動(dòng)實(shí)現(xiàn)的隧道封裝。
使用tun/tap驅(qū)動(dòng)
在linux 2.4內(nèi)核版本及以后版本中,tun/tap驅(qū)動(dòng)是作為系統(tǒng)默認(rèn)預(yù)先編譯進(jìn)內(nèi)核中的。在使用之前,確保已經(jīng)裝載了tun/tap模塊并建立設(shè)備文件:
|
參數(shù)c表示是字符設(shè)備, 10和200分別是主設(shè)備號(hào)和次設(shè)備號(hào)。
這樣,我們就可以在程序中使用該驅(qū)動(dòng)了。
使用tun/tap設(shè)備的示例程序(摘自openvpn開(kāi)源項(xiàng)目 http://openvpn.sourceforge.net,tun.c文件)
|
調(diào)用上述函數(shù)后,就可以在shell命令行下使用ifconfig 命令配置虛擬網(wǎng)卡了,通過(guò)生成的字符設(shè)備描述符,在程序中使用read和write函數(shù)就可以讀取或者發(fā)送給虛擬的網(wǎng)卡數(shù)據(jù)了。
Tun/tap驅(qū)動(dòng)程序工作原理
做為虛擬網(wǎng)卡驅(qū)動(dòng),Tun/tap驅(qū)動(dòng)程序的數(shù)據(jù)接收和發(fā)送并不直接和真實(shí)網(wǎng)卡打交道,而是通過(guò)用戶(hù)態(tài)來(lái)轉(zhuǎn)交。在linux下,要實(shí)現(xiàn)核心態(tài)和用戶(hù)態(tài)數(shù)據(jù)的交互,有多種方式:可以通用socket創(chuàng)建特殊套接字,利用套接字實(shí)現(xiàn)數(shù)據(jù)交互;通過(guò)proc文件系統(tǒng)創(chuàng)建文件來(lái)進(jìn)行數(shù)據(jù)交互;還可以使用設(shè)備文件的方式,訪問(wèn)設(shè)備文件會(huì)調(diào)用設(shè)備驅(qū)動(dòng)相應(yīng)的例程,設(shè)備驅(qū)動(dòng)本身就是核心態(tài)和用戶(hù)態(tài)的一個(gè)接口,Tun/tap驅(qū)動(dòng)就是利用設(shè)備文件實(shí)現(xiàn)用戶(hù)態(tài)和核心態(tài)的數(shù)據(jù)交互。
從結(jié)構(gòu)上來(lái)說(shuō),Tun/tap驅(qū)動(dòng)并不單純是實(shí)現(xiàn)網(wǎng)卡驅(qū)動(dòng),同時(shí)它還實(shí)現(xiàn)了字符設(shè)備驅(qū)動(dòng)部分。以字符設(shè)備的方式連接用戶(hù)態(tài)和核心態(tài)。下面是示意圖:
Tun/tap驅(qū)動(dòng)程序中包含兩個(gè)部分,一部分是字符設(shè)備驅(qū)動(dòng),還有一部分是網(wǎng)卡驅(qū)動(dòng)部分。利用網(wǎng)卡驅(qū)動(dòng)部分接收來(lái)自TCP/IP協(xié)議棧的網(wǎng)絡(luò)分包并發(fā)送或者反過(guò)來(lái)將接收到的網(wǎng)絡(luò)分包傳給協(xié)議棧處理,而字符驅(qū)動(dòng)部分則將網(wǎng)絡(luò)分包在內(nèi)核與用戶(hù)態(tài)之間傳送,模擬物理鏈路的數(shù)據(jù)接收和發(fā)送。Tun/tap驅(qū)動(dòng)很好的實(shí)現(xiàn)了兩種驅(qū)動(dòng)的結(jié)合。
下面是定義的tun/tap設(shè)備結(jié)構(gòu):
|
struct net_device結(jié)構(gòu)是linux內(nèi)核提供的統(tǒng)一網(wǎng)絡(luò)設(shè)備結(jié)構(gòu),定義了系統(tǒng)統(tǒng)一的訪問(wèn)接口。
Tun/tap驅(qū)動(dòng)中實(shí)現(xiàn)的網(wǎng)卡驅(qū)動(dòng)的處理例程:
static int tun_net_open(struct net_device *dev);
static int tun_net_close(struct net_device *dev);
static int tun_net_xmit(struct sk_buff *skb, struct net_device *dev);//數(shù)據(jù)包發(fā)送例程
static void tun_net_mclist(struct net_device *dev);//設(shè)置多點(diǎn)傳輸?shù)牡刂锋湵?
static struct net_device_stats *tun_net_stats(struct net_device *dev);//當(dāng)一個(gè)應(yīng)用程序需要知道網(wǎng)絡(luò)接口的一些統(tǒng)計(jì)數(shù)據(jù)時(shí),可調(diào)用該函數(shù),如ifconfig、netstat等。
int tun_net_init(struct net_device *dev);//網(wǎng)絡(luò)設(shè)備初始例程
字符設(shè)備部分:
在Linux中,字符設(shè)備和塊設(shè)備統(tǒng)一以文件的方式訪問(wèn),訪問(wèn)它們的接口是統(tǒng)一的,都是使用open()函數(shù)打開(kāi)設(shè)備文件或普通文件,用read()和write()函數(shù)實(shí)現(xiàn)讀寫(xiě)文件等等。Tun/tap驅(qū)動(dòng)定義的字符設(shè)備的訪問(wèn)接口如下:
static struct file_operations tun_fops = {
owner: THIS_MODULE,
llseek: tun_chr_lseek,
read tun_chr_read,
write: tun_chr_write,
poll: tun_chr_poll,
ioctl: tun_chr_ioctl,
open: tun_chr_open,
release: tun_chr_close,
fasync: tun_chr_fasync
};
在內(nèi)核中利用misc_register() 函數(shù)將該驅(qū)動(dòng)注冊(cè)為非標(biāo)準(zhǔn)字符設(shè)備驅(qū)動(dòng),提供字符設(shè)備具有的各種程序接口。代碼摘自linux-2.4.20\linux-2.4.20\drivers\net\tun.c
|
當(dāng)打開(kāi)一個(gè)tun/tap設(shè)備時(shí),open 函數(shù)將調(diào)用tun_chr_open()函數(shù),其中將完成一些重要的初始化過(guò)程,包括設(shè)置網(wǎng)卡驅(qū)動(dòng)部分的初始化函數(shù)以及網(wǎng)絡(luò)緩沖區(qū)鏈表的初始化和等待隊(duì)列的初始化。Tun/tap驅(qū)動(dòng)中網(wǎng)卡的注冊(cè)被嵌入了字符驅(qū)動(dòng)的ioctl例程中,它是通過(guò)對(duì)字符設(shè)備文件描述符利用自定義的ioctl設(shè)置標(biāo)志TUNSETIFF完成網(wǎng)卡的注冊(cè)的。下面是函數(shù)調(diào)用關(guān)系示意圖:
使用ioctl()函數(shù)操作字符設(shè)備文件描述符,將調(diào)用字符設(shè)備中tun_chr_ioctl 來(lái)設(shè)置已經(jīng)open好的tun/tap設(shè)備,如果設(shè)置標(biāo)志為T(mén)UNSETIFF,則調(diào)用tun_set_iff() 函數(shù),此函數(shù)將完成很重要的一步操作,就是對(duì)網(wǎng)卡驅(qū)動(dòng)進(jìn)行注冊(cè)register_netdev(&tun->dev),網(wǎng)卡驅(qū)動(dòng)的各個(gè)處理例程的掛接在open操作時(shí)由tun_chr_open()函數(shù)初始化好了。
Tun/tap設(shè)備的工作過(guò)程:
Tun/tap設(shè)備提供的虛擬網(wǎng)卡驅(qū)動(dòng),從tcp/ip協(xié)議棧的角度而言,它與真實(shí)網(wǎng)卡驅(qū)動(dòng)并沒(méi)有區(qū)別。從驅(qū)動(dòng)程序的角度來(lái)說(shuō),它與真實(shí)網(wǎng)卡的不同表現(xiàn)在tun/tap設(shè)備獲取的數(shù)據(jù)不是來(lái)自物理鏈路,而是來(lái)自用戶(hù)區(qū),Tun/tap設(shè)備驅(qū)動(dòng)通過(guò)字符設(shè)備文件來(lái)實(shí)現(xiàn)數(shù)據(jù)從用戶(hù)區(qū)的獲取。發(fā)送數(shù)據(jù)時(shí)tun/tap設(shè)備也不是發(fā)送到物理鏈路,而是通過(guò)字符設(shè)備發(fā)送至用戶(hù)區(qū),再由用戶(hù)區(qū)程序通過(guò)其他渠道發(fā)送。
發(fā)送過(guò)程:
使用tun/tap網(wǎng)卡的程序經(jīng)過(guò)協(xié)議棧把數(shù)據(jù)傳送給驅(qū)動(dòng)程序,驅(qū)動(dòng)程序調(diào)用注冊(cè)好的hard_start_xmit函數(shù)發(fā)送,hard_start_xmit函數(shù)又會(huì)調(diào)用tun_net_xmit函數(shù),其中skb將會(huì)被加入skb鏈表,然后喚醒被阻塞的使用tun/tap設(shè)備字符驅(qū)動(dòng)讀數(shù)據(jù)的進(jìn)程,接著tun/tap設(shè)備的字符驅(qū)動(dòng)部分調(diào)用其tun_chr_read()過(guò)程讀取skb鏈表,并將每一個(gè)讀到的skb發(fā)往用戶(hù)區(qū),完成虛擬網(wǎng)卡的數(shù)據(jù)發(fā)送。
接收數(shù)據(jù)的過(guò)程:
當(dāng)我們使用write()系統(tǒng)調(diào)用向tun/tap設(shè)備的字符設(shè)備文件寫(xiě)入數(shù)據(jù)時(shí),tun_chr_write函數(shù)將被調(diào)用,它使用tun_get_user從用戶(hù)區(qū)接受數(shù)據(jù),其中將數(shù)據(jù)存入skb中,然后調(diào)用關(guān)鍵的函數(shù)netif_rx(skb) 將skb送給tcp/ip協(xié)議棧處理,完成虛擬網(wǎng)卡的數(shù)據(jù)接收。
小結(jié)
tun/tap驅(qū)動(dòng)很巧妙的將字符驅(qū)動(dòng)和網(wǎng)卡驅(qū)動(dòng)糅合在一起,本文重點(diǎn)分析兩種驅(qū)動(dòng)之間的銜接,具體的驅(qū)動(dòng)處理細(xì)節(jié)沒(méi)有一一列出,請(qǐng)讀者參考相關(guān)文檔。
關(guān)于作者 麻利輝 電子科技大學(xué)計(jì)算機(jī)學(xué)院2003級(jí)研究生,研究方向?yàn)榫W(wǎng)絡(luò)安全,對(duì)網(wǎng)絡(luò)安全的各種技術(shù)都有著濃厚的興趣,主要研究方向?yàn)榉阑饓夹g(shù)和攻擊技術(shù)。長(zhǎng)期在linux下從事軟件開(kāi)發(fā),您可以通過(guò)電子郵件 mlhdyx@21cn.com和他取得聯(lián)系。 |
聯(lián)系客服