什么是网络设备
(1)网络设备:软件层面的,如操作系统中看到的eth0等。
(2)物理网卡: 真正的硬件网卡设备。
(3)/dev下没有设备文件,也不通过/sys下的属性文件访问。直观看来,应用层都是通过一些特殊的命令(如ifconfig、ping等)来访问网卡硬件(调用驱动)的。
(4)struct net_device来管理所有网络接口。
Linux网络设备驱动架构
Linux网络设备驱动程序体系结构分为四层:网络协议接口层、网络设备接口层、提供实际功能的设备驱动层以及网络设备与媒介层。
(1)网络协议接口层向网络层协议提供统一的数据包收发接口,不论上层协议是ARP还是IP,都通过dev_queue_xmit()函数发送数据,并通过netif_rx()函数接收数据。这一层的存在使得上层协议独立于具体的设备。
(2)网络设备接口层向协议接口层提供的用于描述具体网络设备属性和操作的结构体net_device,该结构体是设备驱动功能层各函数的容器。
(3)设备驱动功能层的各函数是网络设备接口层net_device数据结构的具体成员,是驱使网络设备硬件完成相应动作的程序,它通过hard_start_xmit()函数启动发送操作,并通过网络设备上的中断触发net_interupt接收操作。
(4)网络设备与媒介层是完成数据包发送和接收的物理实体,包括网络适配器和具体的传输媒介,网络适配器被设备驱动功能层中的函数在物理上驱动。
驱动工程师的工作:在设计具体的网络设备驱动程序时,需要完成的主要工作是编写设备驱动功能层的相关函数以填充net_device数据结构的内容并将net_device注册入内核。
虚拟网卡驱动
#include <linux/module.h>
#include <linux/sched.h>
#include <linux/kernel.h> /* printk() */
#include <linux/slab.h> /* kmalloc() */
#include <linux/errno.h> /* error codes */
#include <linux/types.h> /* size_t */
#include <linux/interrupt.h> /* mark_bh */
#include <linux/in.h>
#include <linux/netdevice.h> /* struct device, and other headers */
#include <linux/etherdevice.h> /* eth_type_trans */
#include <linux/ip.h> /* struct iphdr */
#include <linux/tcp.h> /* struct tcphdr */
#include <linux/skbuff.h>
#include <linux/if_ether.h>
#include <linux/in6.h>
#include <asm/uaccess.h>
#include <asm/checksum.h>
#include <linux/platform_device.h>
// 如果需要随机MAC地址则定义该宏
//#define MAC_AUTO
static struct net_device *xxx_net_devs;
//网络设备结构体,作为net_device->priv
struct xxx_net_priv {
struct net_device_stats stats; //有用的统计信息
int status; //网络设备的状态信息,是发完数据包,还是接收到网络数据包
int rx_packetlen; //接收到的数据包长度
u8 *rx_packetdata; //接收到的数据
int tx_packetlen; //发送的数据包长度
u8 *tx_packetdata; //发送的数据
struct sk_buff *skb; //socket buffer结构体,网络各层之间传送数据都是通过这个结构体来实现的
spinlock_t lock; //自旋锁
};
//网络接口的打开函数
int xxx_net_open(struct net_device *dev)
{
printk("xxx_net_open\n");
#ifndef MAC_AUTO
int i;
for (i=0; i<6; i++)
dev->dev_addr[i] = 0xaa;
#else
random_ether_addr(dev->dev_addr); //随机源地址
#endif
netif_start_queue(dev); //打开传输队列,这样才能进行数据传输
return 0;
}
int xxx_net_release(struct net_device *dev)
{
printk("xxx_net_release\n");
//当网络接口关闭的时候,调用stop方法,这个函数表示不能再发送数据
netif_stop_queue(dev);
return 0;
}
//接包函数
void xxx_net_rx(struct net_device *dev, int len, unsigned char *buf)
{
struct sk_buff *skb;
struct xxx_net_priv *priv = (struct xxx_net_priv *) dev->ml_priv;
skb = dev_alloc_skb(len+2);//分配一个socket buffer,并且初始化skb->data,skb->tail和skb->head
if (!skb) {
printk("gecnet rx: low on mem - packet dropped\n");
priv->stats.rx_dropped++;
return;
}
skb_reserve(skb, 2); /* align IP on 16B boundary */
memcpy(skb_put(skb, len), buf, len);//skb_put是把数据写入到socket buffer
/* Write metadata, and then pass to the receive level */
skb->dev = dev;
skb->protocol = eth_type_trans(skb, dev);//返回的是协议号
skb->ip_summed = CHECKSUM_UNNECESSARY; //此处不校验
priv->stats.rx_packets++;//接收到包的个数+1
priv->stats.rx_bytes += len;//接收到包的长度
printk("xxx_net rx \n");
netif_rx(skb);//通知内核已经接收到包,并且封装成socket buffer传到上层
return;
}
//真正的处理的发送数据包
//模拟从一个网络向另一个网络发送数据包
void xxx_net_hw_tx(char *buf, int len, struct net_device *dev)
{
struct net_device *dest;//目标设备结构体,net_device存储一个网络接口的重要信息,是网络驱动程序的核心
struct xxx_net_priv *priv;
if (len < sizeof(struct ethhdr) + sizeof(struct iphdr))
{
printk("xxx_net: Hmm... packet too short (%i octets)\n", len);
return;
}
dest = xxx_net_devs;
priv = (struct xxx_net_priv *)dest->ml_priv; //目标dest中的priv
priv->rx_packetlen = len;
priv->rx_packetdata = buf;
printk("xxx_net tx \n");
printk("len %d, buf %s\n", len, buf);
dev_kfree_skb(priv->skb);
}
//发包函数
int xxx_net_tx(struct sk_buff *skb, struct net_device *dev)
{
int len;
char *data;
struct xxx_net_priv *priv = (struct xxx_net_priv *)dev->ml_priv;
if (skb == NULL)
{
printk("net_device %p, skb %p\n", dev, skb);
return 0;
}
len = skb->len < ETH_ZLEN ? ETH_ZLEN : skb->len;//ETH_ZLEN是所发的最小数据包的长度
data = skb->data;//将要发送的数据包中数据部分
priv->skb = skb;
xxx_net_hw_tx(data, len, dev);//真正的发送函数
return 0;
}
//设备初始化函数
int xxx_net_init(struct net_device *dev)
{
printk("xxx_cnet_init\n");
ether_setup(dev);//填充一些以太网中的设备结构体的项
/* keep the default flags, just add NOARP */
dev->flags |= IFF_NOARP;
//为priv分配内存
dev->ml_priv = kmalloc(sizeof(struct xxx_net_priv), GFP_KERNEL);
if (dev->ml_priv == NULL)
return -ENOMEM;
memset(dev->ml_priv, 0, sizeof(struct xxx_net_priv));
spin_lock_init(&((struct xxx_net_priv *)dev->ml_priv)->lock);
return 0;
}
static const struct net_device_ops xxx_net_netdev_ops = {
.ndo_open = xxx_net_open, // 打开网卡 对应 ifconfig xx up
.ndo_stop = xxx_net_release, // 关闭网卡 对应 ifconfig xx down
.ndo_start_xmit = xxx_net_tx, // 开启数据包传输
.ndo_init = xxx_net_init, // 初始化网卡硬件
};
static void xxx_plat_net_release(struct device *dev)
{
printk("xxx_plat_net_release\n");
}
static int __devinit xxx_net_probe(struct platform_device *pdev)
{
int result=0;
xxx_net_devs = alloc_etherdev(sizeof(struct net_device));
xxx_net_devs->netdev_ops = &xxx_net_netdev_ops;
strcpy(xxx_net_devs->name, "xxx_net0");
if ((result = register_netdev(xxx_net_devs)))
printk("xxx_net: error %i registering device \"%s\"\n", result, xxx_net_devs->name);
return 0;
}
static int __devexit xxx_net_remove(struct platform_device *pdev) //设备移除接口
{
kfree(xxx_net_devs->ml_priv);
unregister_netdev(xxx_net_devs);
return 0;
}
static struct platform_device xxx_net= {
.name = "xxx_net",
.id = -1,
.dev = {
.release = xxx_plat_net_release,
},
};
static struct platform_driver xxx_net_driver = {
.probe = xxx_net_probe,
.remove = __devexit_p(xxx_net_remove),
.driver = {
.name ="xxx_net",
.owner = THIS_MODULE,
},
};
static int __init xxx_net_init(void)
{
printk("xxx_net_init \n");
platform_device_register(&xxx_net);
return platform_driver_register(&xxx_net_driver);
}
static void __exit xxx_net_cleanup(void)
{
platform_driver_unregister(&xxx_net_driver);
platform_device_unregister(&xxx_net);
}
module_init(xxx_net_init);
module_exit(xxx_net_cleanup);
MODULE_LICENSE("GPL");
虚拟网卡驱动分析:
1、该驱动通过平台总线来实现,这里把platform_device和platform_driver写在了同一个文件里面。注意device和driver的.name字段是一致的,那么就可以在probe函数中相遇;
2、probe函数中,通过alloc_etherdev创建一个struct net_device的实例,然后填充其netdev_ops网络设备的操作接口,最后通过register_netdev注册网络设备(platform中.remove中会执行对应的unregister_netdev函数,注销该网络设备)。其实这个platform框架没多大意义,只是为了让probe函数执行而已;
static const struct net_device_ops xxx_net_netdev_ops = {
.ndo_open = xxx_net_open, // 打开网卡 对应 ifconfig xx up
.ndo_stop = xxx_net_release, // 关闭网卡 对应 ifconfig xx down
.ndo_start_xmit = xxx_net_tx, // 开启数据包传输
.ndo_init = xxx_net_init, // 初始化网卡硬件
};
3、分析ops接口。注释都有,提几点重要的吧:
a、定义了一个struct xxx_net_priv作为net_device->priv私有变量,一般网络设备驱动中均有类似类型的变量。
b、私有变量中最重要的是struct sk_buff *skb,socket buffer结构体,网络各层之间传送数据都是通过这个结构体来实现的(在发送函数中和接收函数中均会涉及)。socket buffer是数据在多层模型中传输的载体,其被处理的最终结果就是网络数据包(发送时)或网络数据包被解析成socket buffer(接收时)并传到上层。