曾经一段时间在看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。
《Linux设备驱动程序 第三版》,snull源码,linux-2.6.10
最新推荐文章于 2022-11-20 22:24:44 发布