linux 3.10 网卡驱动程序示例,网卡驱动程序框架以及虚拟网卡驱动程序的实现

本文深入探讨了Linux下的网卡驱动程序设计原理,包括网卡驱动的基本框架、关键函数及其实现细节。此外,还提供了一个虚拟网卡驱动的具体实现案例,通过代码展示了如何创建、配置并注册一个简单的网卡驱动。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

LINUX驱动程序分三大块:字符驱动程序、块设备驱动程序、网络驱动程序。其中网络驱动程序分为好多层协议层+与硬件相关的层。这篇主要介绍网络驱动中与硬件相关的层的框架:网卡驱动程序。

1、网卡驱动程序框,直接上图:

从图中可以知道,首先应用层通过socket编程调用若干的网络协议层,而这网络协议层完成时纯软件的概念,是通过软件分层的。软件层与相关的网卡驱动程序的接口都是通过hard_start_xmit发包函数与netif_rx 收包函数函数联系起来的。而他们传送的数据包的结构是sk_buff结构体。

481ec4b46b72c02fc6d01d7cb5f7c252.png

对于网卡相关的驱动程序,可以直接参考drivers\net\cs89x0.c文件。它的大致的使用过程如下:

首先入口函数init_module分配了一个net_device结构体。然后调用cs89x0_probe1函数,在这个函数中设置了hard_start_xmit发包函数等信息,最后调用register_netdev注册分配的net_device结构体。

接着看到net_interrupt函数,这是一个中断入口函数,它最终调用了netif_rx函数用于接收从网卡芯片中传输过来的数据,上报给网路协议层。

init_module

alloc_etherdev

cs89x0_probe1

dev->hard_start_xmit =net_send_packet;

register_netdev

net_interrupt

net_rx

netif_rx

从以上分析,可以大胆的推测出网卡驱动程序的编写步骤:

1、分配一个net_device结构体

2、设置

a、提供发包函数:hard_start_xmit

(*hard_start_xmit) (struct sk_buff *skb,struct net_device *dev);

b、提供收包的功能:收到数据时在中断处理函数里用netif_rx上报数据

c、其他设置

3、注册:register_netdev

4、硬件相关

2、虚拟网卡驱动程序的编写以及测试

直接贴上代码,此驱动程序的功能可以ping通在同一网段内的不同IP的虚拟设备。

1、首先分析下入口函数Virt_net_init,它分配了vnet_dev结构体,并且设置了结构中的hard_start_xmit 函数为virt_net_sendpacket,设置了MAC地址等信息,最后注册。

2、virt_net_sendpacket函数主要做了发送包信息的统计以及将自己发送出去的包回环给了自己,做到了自发自收的功能,调用函数emulator_rx_packet

3、emulator_rx_packet函数主要将发送包的mac地址与ip地址的目标、源对调,这样做就可以实现自发自收。最后调用netif_rx上传给上层协议层

/** 参考drivers\net\cs89x0.c*/#include#include#include#include#include#include#include#include#include#include#include#include#include#include#include#include#include#include#include#include#include

static struct net_device *vnet_dev;static void emulator_rx_packet(struct sk_buff *skb, struct net_device *dev)

{/*参考LDD3*/unsignedchar *type;struct iphdr *ih;

__be32*saddr, *daddr, tmp;

unsignedchartmp_dev_addr[ETH_ALEN];struct ethhdr *ethhdr;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(structethhdr));

saddr= &ih->saddr;

daddr= &ih->daddr;

tmp= *saddr;*saddr = *daddr;*daddr =tmp;//((u8 *)saddr)[2] ^= 1; /* change the third octet (class C) *///((u8 *)daddr)[2] ^= 1;

type = skb->data + sizeof(struct ethhdr) + sizeof(structiphdr);//printk("tx package type = %02x\n", *type);//修改类型, 原来0x8表示ping

*type = 0; /*0表示reply*/ih->check = 0; /*and rebuild the checksum (ip needs it)*/ih->check = ip_fast_csum((unsigned char *)ih,ih->ihl);//构造一个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_sendpacket(struct sk_buff *skb, struct net_device *dev)

{static int cnt = 0;

printk("virt_net_sendpacket cnt = %d\n", ++cnt);/*对于真实的网口,把skb里的数据通过网口发送出去*/netif_stop_queue(dev);/*停止该网口的队列*/

/*..........*/ /*把skb的数据写入网口*/

/*构造一个假的sk_buff,上报*/emulator_rx_packet(skb, dev);

dev_kfree_skb (skb);/*释放skb*/netif_wake_queue(dev);/*数据全部发送出去之后,唤醒网口的队列*/

/*更新统计信息*/dev->stats.tx_packets++;

dev->stats.tx_bytes += skb->len;return 0;

}static int Virt_net_init(void)

{/*1、分配一个net_device结构体*/vnet_dev= alloc_netdev(0, "vnet%d", ether_setup);/*2、设置*/vnet_dev->hard_start_xmit =virt_net_sendpacket;/*设置MAC地址*/vnet_dev->dev_addr[0] = 0x08;

vnet_dev->dev_addr[1] = 0x89;

vnet_dev->dev_addr[2] = 0x89;

vnet_dev->dev_addr[3] = 0x89;

vnet_dev->dev_addr[4] = 0x89;

vnet_dev->dev_addr[5] = 0x11;/*设置下面两项才能ping通*/vnet_dev->flags |=IFF_NOARP;

vnet_dev->features |=NETIF_F_NO_CSUM;/*3、注册*/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("andy");

MODULE_LICENSE("GPL");

接着将代码编译后进行测试:注意在测试时网络设备需要存在lo网络才能实现自发自收

1、insmod virt_net.ko

2、ifconfig vnet0 3.3.3.3

ifconfig //查看

2d37c5dbb18a7d37a4b0ecea7e3e3897.png

3、ping 3.3.3.3 //成功(只有从网络文件系统启动才可以成功,猜是只有有lo网络才行)

608f93832cd97394f25cae47d89a96f9.png

4、ping 3.3.3.4 //成功

caec519ca4070c3ace51d3a6b8f696c6.png

到这里,虚拟网卡驱动程序测试成功。

虚拟网卡驱动源代码(原版): /* * snull.c -- the Simple Network Utility * * Copyright (C) 2001 Alessandro Rubini and Jonathan Corbet * Copyright (C) 2001 O'Reilly & Associates * * The source code in this file can be freely used, adapted, * and redistributed in source or binary form, so long as an * acknowledgment appears in derived source files. The citation * should list that the code comes from the book "Linux Device * Drivers" by Alessandro Rubini and Jonathan Corbet, published * by O'Reilly & Associates. No warranty is attached; * we cannot take responsibility for errors or fitness for use. * * $Id: snull.c,v 1.21 2004/11/05 02:36:03 rubini Exp $ */ #include #include #include #include #include #include /* printk() */ #include /* kmalloc() */ #include /* error codes */ #include /* size_t */ #include /* mark_bh */ #include #include /* struct device, and other headers */ #include /* eth_type_trans */ #include /* struct iphdr */ #include /* struct tcphdr */ #include #include "snull.h" #include #include MODULE_AUTHOR("Alessandro Rubini, Jonathan Corbet"); MODULE_LICENSE("Dual BSD/GPL"); /* * Transmitter lockup simulation, normally disabled. */ static int lockup = 0; module_param(lockup, int, 0); static int timeout = SNULL_TIMEOUT; module_param(timeout, int, 0); /* * Do we run in NAPI mode? */ static int use_napi = 0; module_param(use_napi, int, 0); /* * A structure representing an in-flight packet. */ struct snull_packet { struct snull_packet *next; struct net_device *dev; int datalen; u8 data[ETH_DATA_LEN]; }; int pool_size = 8; module_param(pool_size, int, 0); /* * This structure is private to each device. It is used to
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值