zoukankan      html  css  js  c++  java
  • 网卡驱动_WDS

    参考 cs89x0.c


    1.网卡驱动程序与网络驱动程序的区别
    网卡驱动程序:网络驱动程序中最底层的驱动,
    主要工作:把上面发下来的数据发送出去,收到数据后构造一个包抛给上层。有收发能力就可以了。

    2.网卡设备驱动框架

    app:  socket
    --------------------------------------------------
               ---------------
               --------------- 若干层网络协议--纯软件
               ---------------
               ---------------
    ndo_start_xmit ||  / 
                   /  ||  netif_rx   sk_buff
               ---------------
              硬件相关的驱动程序(要提供hard_start_xmit, 有数据时要用netif_rx上报)           
    --------------------------------------------------
                   硬件  

    (1).分配net_device结构
    (2).设置
      ①提供发包函数:netdev_tx_t net_send_packet(struct sk_buff *skb, struct net_device *dev);
      ②提供收包函数:int netif_rx(struct sk_buff *skb);
    (3).注册:register_netdevice
    (4).硬件相关操作

    由上可知,网卡驱动与上层软件之间的数据交换是通过struct sk_buff进行的,上层传下来一个sk_buff给到网卡驱动,网卡发出去;网卡驱动收到数据后构造一个sk_buff包发给上层软件。

    3.当应用程序ping自己网卡的ip的时候,根本就不会调用到网卡驱动的ndo_start_xmit和netif_rx函数,在网卡驱动的上层就已经返回了。这也是为什么只注册一个网卡设备结构体就能ping通自己的原因。也就表明了Ip是个纯软件的概念,此时ifconfig列举出的RX/TX统计信息是不会变化的。当ping其它网卡的Ip地址的时候,上层软件才会把数据包丢给驱动。

    4.当有多个网卡的时候,不同的网卡最好不要处于同一个网段中,因为若存在于同一个网段中,主机根本就不知道应该从哪个网卡中把数据发送出去。主机会优先选择与目的Ip处于同一网段的网卡。

    5.可以在ndo_start_xmit()加dump_stack()来查调试上层软件的发包机制。

    6.示例驱动
    伪造一个目的ip网卡进行应答,使能ping通其它Ip的网卡

    /*
     * 参考 drivers
    etcs89x0.c
     */
    
    #include <linux/module.h>
    #include <linux/printk.h>
    #include <linux/errno.h>
    #include <linux/netdevice.h>
    #include <linux/etherdevice.h>
    #include <linux/of.h>
    #include <linux/of_device.h>
    #include <linux/platform_device.h>
    #include <linux/kernel.h>
    #include <linux/types.h>
    #include <linux/fcntl.h>
    #include <linux/interrupt.h>
    #include <linux/ioport.h>
    #include <linux/in.h>
    #include <linux/jiffies.h>
    #include <linux/skbuff.h>
    #include <linux/spinlock.h>
    #include <linux/string.h>
    #include <linux/init.h>
    #include <linux/bitops.h>
    #include <linux/delay.h>
    #include <linux/gfp.h>
    #include <linux/io.h>
    #include <linux/ip.h>
    
    
    #include <asm/irq.h>
    #include <linux/atomic.h>
    
    static struct net_device *vnet_dev;
    
    /*
    iph = ip_hdr(skb);
    static inline struct ethhdr *eth_hdr(const struct sk_buff *skb)
    */
    
    static void emulator_rx_packet(struct sk_buff *skb, struct net_device *dev)
    {
        /* 参考LDD3 */
        unsigned char *type;
        struct iphdr *ih = ip_hdr(skb);
        __be32 tmp;
        unsigned char    tmp_dev_addr[ETH_ALEN];
        struct ethhdr *ethhdr = eth_hdr(skb);
    
        struct sk_buff *rx_skb;
    
        // 从硬件读出/保存数据
        /* 对调"源/目的"的mac地址 */
        //ethhdr = (struct ethhdr *)skb->data;
        memcpy(tmp_dev_addr, ethhdr->h_dest, ETH_ALEN);
        memcpy(ethhdr->h_dest, ethhdr->h_source, ETH_ALEN);
        memcpy(ethhdr->h_source, tmp_dev_addr, ETH_ALEN);
    
        /* 对调"源/目的"的ip地址 */
        //ih = (struct iphdr *)(skb->data + sizeof(struct ethhdr));
        tmp = ih->saddr;;
        ih->saddr = ih->daddr;
        ih->daddr = tmp;
    
        //((u8 *)saddr)[2] ^= 1; /* change the third octet (class C) */
        //((u8 *)daddr)[2] ^= 1;
        type = skb->data + sizeof(struct ethhdr) + sizeof(struct iphdr);
        //printk("tx package type = %02x
    ", *type);
        /* 修改类型, 原来0x8表示ping, 0表示reply */
        *type = 0;
    
        ih->check = 0; /* and rebuild the checksum (ip needs it) */
        ih->check = ip_fast_csum((unsigned char *)ih, ih->ihl); /*here oops: Unable to handle kernel paging request at virtual address ffff800600000000*/
    
        /* 构造一个sk_buff */
        rx_skb = dev_alloc_skb(skb->len + 2);
        skb_reserve(rx_skb, 2); /* align IP on 16B boundary */
        memcpy(skb_put(rx_skb, skb->len), skb->data, skb->len);
    
        /* Write metadata, and then pass to the receive level */
        rx_skb->dev = dev;
        rx_skb->protocol = eth_type_trans(rx_skb, dev);
        rx_skb->ip_summed = CHECKSUM_UNNECESSARY; /* don't check it */
    
        /* 假装接收到数据更新统计信息 */
        dev->stats.rx_packets++;
        dev->stats.rx_bytes += skb->len;
    
        /* 提交sk_buff */
        netif_rx(rx_skb);
    }
    
    static int virt_net_send_packet(struct sk_buff *skb, struct net_device *dev)
    {
        static int cnt = 0;
        printk("virt_net_send_packet cnt = %d
    ", ++cnt);
    
        /* 对于真实的网卡, 把skb里的数据通过网卡发送出去 */
        netif_stop_queue(dev); /* 停止该网卡的队列 */
        /* ...... */             /* 把skb的数据写入网卡 */
    
        /* 构造一个假的sk_buff,上报,类型回环,使ping其它Ip也能成功*/
        emulator_rx_packet(skb, dev); /*tmp commit out!!!!!!!!!!!!!!!!!!!!!!!!!!!*/
    
        netif_wake_queue(dev); /* 数据全部发送出去后,唤醒网卡的队列,实际的网卡是在发送完后产生中断,在中断服务函数中唤醒队列*/
    
        /* 更新统计信息 这些统计信息ifconfig可以看到 */
        dev->stats.tx_packets++;
        dev->stats.tx_bytes += skb->len;
    
        dev_kfree_skb (skb);   /* 释放skb */
    
        return 0;
    }
    
    static const struct net_device_ops virt_net_ndops = {
        .ndo_start_xmit        = virt_net_send_packet,
    };
    
    
    static int virt_net_init(void)
    {
        /* 1. 分配一个net_device结构体 */
        vnet_dev = alloc_netdev(0, "vnet%d", NET_NAME_UNKNOWN, ether_setup); /* alloc_etherdev */
    
        /* 2. 设置 */
        vnet_dev->netdev_ops = &virt_net_ndops; /*上层软件通过这个函数把sk_buff丢给网卡驱动*/
    
        /* 设置MAC地址 */
        vnet_dev->dev_addr[0] = 0x11; /*vnet_dev->dev_addr在alloc_netdev时已经分配了空间的*/
        vnet_dev->dev_addr[1] = 0x22;
        vnet_dev->dev_addr[2] = 0x33;
        vnet_dev->dev_addr[3] = 0x44;
        vnet_dev->dev_addr[4] = 0x55;
        vnet_dev->dev_addr[5] = 0x66;
    
        /* 设置下面两项才能ping通  why? */
        vnet_dev->flags           |= IFF_NOARP;
        //vnet_dev->features        |= NETIF_F_NO_CSUM;
    
        /* 3. 注册 */
        //register_netdevice(vnet_dev);
        register_netdev(vnet_dev);
    
        return 0;
    }
    
    static void virt_net_exit(void)
    {
        unregister_netdev(vnet_dev);
        free_netdev(vnet_dev);
    }
    
    module_init(virt_net_init);
    module_exit(virt_net_exit);
    
    MODULE_AUTHOR("thisway.diy@163.com,17653039@qq.com");
    MODULE_LICENSE("GPL");
  • 相关阅读:
    无线鼠标换电池了
    Jython Interactive Servlet Console YOU WILL NEVER KNOW IT EXECLLENT!!! GOOD
    Accessing Jython from Java Without Using jythonc
    jython podcast cool isnt't it?
    Python里pycurl使用记录
    Creating an Interactive JRuby Console for the Eclipse Environment
    微软为AJAX和jQuery类库提供CDN服务
    Download A File Using Cygwin and cURL
    What is JMRI?这个是做什么用的,我真没看懂但看着又很强大
    用curl 发送指定的大cookie的http/https request
  • 原文地址:https://www.cnblogs.com/hellokitty2/p/10163129.html
Copyright © 2011-2022 走看看