《Linux设备驱动程序 第三版》,snull源码,linux-2.6.10

来源:互联网 发布:linux ping脚本 编辑:程序博客网 时间:2024/06/04 18:49
 曾经一段时间在看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。
阅读全文
0 0
原创粉丝点击