Linux虛擬網卡實現__Linux
最後更新:2018-07-30
來源:互聯網
上載者:User
本文檔的Copyleft歸rosetta所有,使用GPL發布,可以自由拷貝、轉載,轉載時請保持文檔的完整性。
參考資料:《Linux裝置驅動程式 第三版》,snull源碼,linux-2.6.10
曾經一段時間在看openswan源碼,有一個問題迷惑我很久,就是它的那個ipsec虛擬網卡介面是怎麼實現的。當時沒有思路、沒有想法,因為不知道從何著手去解決這個問題,最近接觸到了核心模組的編寫,又接觸到openswan的klips模組,原來這一切全屬於網路驅動程式編寫範疇。現在我迫不及待的想去瞭解下它的實現,然後就有了這篇學習筆記……
本文只是初步講解虛擬網卡實現的過程,最終實現一個虛擬網卡,對於具體體細節和資料包的發送和傳送等等問題沒有涉及。對於klips的ipsec0的實現大體上類似這個過程。
本文檔注重實際實現過程,缺少理論知識。
本文檔以《Linux裝置驅動程式 第三版》為理論知識;以snull源碼為學習對象。為貪圖省力,所帖源碼來至snull源碼和linux-2.6.10核心源碼。
一、最終的效果,實現了一個名為sn0的虛擬網卡介面
[root@xxx snull]# cat /proc/net/dev
Inter-| Receive
face |bytes packets errs drop fifo
lo: 6528 76 0 0 0
eth0:148681882 216304 0 0 0
eth1: 0 0 0 0 0
eth2: 0 0 0 0 0
sit0: 0 0 0 0 0
sn0: 0 0 0 0 0
sn1: 210 3 0 0 0
[root@xxx snull]# ifconfig sn0 up
[root@xxx snull]# ifconfig sn0
sn0 Link encap:Ethernet HWaddr 00:53:4E:55:4C:30
inet6 addr: fe80::253:4eff:fe55:4c30/64 Scope:Link
UP BROADCAST RUNNING NOARP MULTICAST MTU:1500 Metric:1
RX packets:0 errors:0 dropped:0 overruns:0 frame:0
TX packets:1 errors:0 dropped:0 overruns:0 carrier:0
collisions:0 txqueuelen:1000
RX bytes:0 (0.0 b) TX bytes:70 (70.0 b)
二、總體過程
首先需要核心分配一個net_device結構(以下簡稱dev結構),並初始化dev結構。
struct net_device *snull_devs[2]; //snull 實現的是兩個虛擬網卡,用一個兩個大小的結構體數組儲存net_device結構,我只關注一個。
snull_devs[0] = alloc_netdev(sizeof(struct snull_priv), "sn%d",
snull_init);//核心分配一個dev,此dev由snull_init初始化。然後由alloc_netdev函數返回此dev指標。
for (i = 0; i < 2; i++)
if ((result = register_netdev(snull_devs[i])))//dev結構初始化後調用register_netdev向核心註冊,註冊後就可以對裝置(虛擬網卡)操作了。
printk("snull: error %i registering device \"%s\"\n",
result, snull_devs[i]->name);
else
ret = 0;
三、初始化過程
為了便於理解初始化過程,帖上一段alloc_netdev的核心實現:
struct net_device *alloc_netdev(int sizeof_priv, const char *mask,
void (*setup)(struct net_device *))//最後一個參數為函數指標,即指向傳進來的snull_init
{
void *p;
struct net_device *dev;
int alloc_size;
/* ensure 32-byte alignment of both the device and private area */
alloc_size = (sizeof(struct net_device) + NETDEV_ALIGN_CONST)
& ~NETDEV_ALIGN_CONST;
alloc_size += sizeof_priv + NETDEV_ALIGN_CONST;
p = kmalloc (alloc_size, GFP_KERNEL);
if (!p) {
printk(KERN_ERR "alloc_dev: Unable to allocate device.\n");
return NULL;
}
memset(p, 0, alloc_size);
dev = (struct net_device *)(((long)p + NETDEV_ALIGN_CONST)
& ~NETDEV_ALIGN_CONST);//為dev結構分配記憶體空間
dev->padded = (char *)dev - (char *)p;
if (sizeof_priv)
dev->priv = netdev_priv(dev);
setup(dev);//實現調用了snull_init(dev)對dev結構初始化。
strcpy(dev->name, mask);
return dev;//返回已初始化的dev
}
下面看下這個重要的初始化函數snull_init(),dev的一些重要函數指標均在此賦值
void snull_init(struct net_device *dev)
{
struct snull_priv *priv;
/*
* Then, assign other fields in dev, using ether_setup() and some
* hand assignments
*/
ether_setup(dev); /* assign some of the fields *///這個函數初始化了dev的許多成員
dev->open = snull_open; //開啟裝置
dev->stop = snull_release;
dev->set_config = snull_config;
dev->hard_start_xmit = snull_tx; //資料發送函數
dev->do_ioctl = snull_ioctl;
dev->get_stats = snull_stats;
dev->change_mtu = snull_change_mtu;
dev->rebuild_header = snull_rebuild_header;
dev->hard_header = snull_header;//建立硬體頭,包含源和目的mac地址
dev->tx_timeout = snull_tx_timeout;//數發送逾時處理。
dev->watchdog_timeo = timeout;
if (use_napi) {
dev->poll = snull_poll;
dev->weight = 2;
}
/* keep the default flags, just add NOARP */
dev->flags |= IFF_NOARP;
dev->features |= NETIF_F_NO_CSUM;
dev->hard_header_cache = NULL; /* Disable caching */
/*
* Then, initialize the priv field. This encloses the statistics
* and a few private fields.
*/
priv = netdev_priv(dev);
memset(priv, 0, sizeof(struct snull_priv));
spin_lock_init(&priv->lock);
snull_rx_ints(dev, 1); /* enable receive interrupts */
snull_setup_pool(dev);
}
以上內容已經把架構搭出來了,至於net結構成員的理解和細節、資料包的發送的接收、逾時傳輸中斷處理及sk_buff資料結構等
資訊可參考給出的參考資料。
類似的實現網路驅動程式的源碼還有核心源碼中的loopback.c、plip.c、e100.c。