通过register_netdev注册网络设备。
实现了网络中断。
模拟网络硬件传输数据包。
检查ip数据包。
记录传输的数据包状态。
手动释放sk_buffer。
处理数据包传输超时情况。
网络驱动接收网络数据包并将数据包放入TCP/IP上层,编写网络驱动接收数据包必须分配sk_buff结构来存储数据,sk_buff将在上层释放。
代码
demo.c
#include <linux/module.h>
#include <linux/init.h>
#include <linux/moduleparam.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/slab.h>
#include <linux/fs.h>
#include <linux/errno.h>
#include <linux/types.h>
#include <linux/fcntl.h>
#include <linux/seq_file.h>
#include <linux/cdev.h>
#include <linux/version.h>
#include <linux/vmalloc.h>
#include <linux/delay.h>
#include <linux/ctype.h>
#include <linux/sched.h>
#include <linux/kernel.h>
#include <linux/slab.h> // kmalloc()
#include <linux/types.h> // size_t
#include <linux/interrupt.h> // mark_bh
#include <linux/in.h>
#include <linux/netdevice.h>
#include <linux/etherdevice.h>
#include <linux/ip.h>
#include <linux/tcp.h>
#include <linux/skbuff.h>
#include <linux/in6.h>
#include <asm/checksum.h>
#include "demo.h"
#define IRQ_NET_CHIP 200
MODULE_AUTHOR("sun");
MODULE_LICENSE("Dual BSD/GPL");
static char netbuffer[100];
struct net_device *net_devs;
// 接收网络数据包并将数据包放入TCP/IP上层 netif_rx主要实现该功能
void netrx(struct net_device *dev, int len, unsigned char *buf)
{
struct sk_buff *skb;
struct netpriv *priv = (struct netpriv *) dev->priv;
skb = dev_alloc_skb(len+2);
if (!skb) {
printk("netrx can not allocate more memory to store the packet. drop the packet\n");
priv->stats.rx_dropped++;
return;
}
skb_reserve(skb, 2);
memcpy(skb_put(skb, len), buf, len);
skb->dev = dev;
skb->protocol = eth_type_trans(skb, dev);
// 不需要检查checksum
skb->ip_summed = CHECKSUM_UNNECESSARY;
priv->stats.rx_packets++;
netif_rx(skb);
return;
}
static irqreturn_t net_interrupt (int irq, void *dev_id)
{
struct net_device *dev;
dev = (struct net_device *) dev_id;
// 从硬件寄存器获取数据
netrx(dev, 100, netbuffer);
return IRQ_HANDLED;
}
int netopen(struct net_device *dev)
{
int ret=0;
printk("netopen\n");
ret = request_irq(IRQ_NET_CHIP, net_interrupt, IRQF_SHARED, dev->name, dev);
if (ret) return ret;
printk("request_irq ok\n");
netif_start_queue(dev);
return 0;
}
int netrelease(struct net_device *dev)
{
printk("netrelease\n");
netif_stop_queue(dev);
return 0;
}
// 模拟网络硬件传输 把数据input给ed_tx设备即可
void nethw_tx(char *buf, int len, struct net_device *dev)
{
struct netpriv *priv;
// 检查ip数据包长度
if (len < sizeof(struct ethhdr) + sizeof(struct iphdr))
{
printk("packet's size is less then 34!\n");
return;
}
// 记录传输的数据包状态
priv = (struct netpriv *) dev->priv;
priv->stats.tx_packets++;
priv->stats.rx_bytes += len;
// 释放分配的sk_buffer
dev_kfree_skb(priv->skb);
}
// 传输数据包 内核会调用这个函数
int nettx(struct sk_buff *skb, struct net_device *dev)
{
int len;
char *data;
struct netpriv *priv = (struct netpriv *) dev->priv;
len = skb->len < ETH_ZLEN ? ETH_ZLEN : skb->len;
data = skb->data;
// 时间戳
dev->trans_start = jiffies;
// 这个sk_buffer 在nethw_tx 释放
priv->skb = skb;
/* pseudo transmit the packet,hehe */
nethw_tx(data, len, dev);
return 0;
}
// 处理数据包传输超时情况
void nettx_timeout (struct net_device *dev)
{
struct netpriv *priv = (struct netpriv *) dev->priv;
priv->stats.tx_errors++;
netif_wake_queue(dev);
return;
}
int netioctl(struct net_device *dev, struct ifreq *rq, int cmd)
{
return 0;
}
// ifconfig获取数据包传输状态
struct net_device_stats *netstats(struct net_device *dev)
{
struct netpriv *priv = (struct netpriv *) dev->priv;
return &priv->stats;
}
// tcp/ip 握手会调用这个函数
int netchange_mtu(struct net_device *dev, int new_mtu)
{
unsigned long flags;
spinlock_t *lock = &((struct netpriv *) dev->priv)->lock;
// mtu最大传输单元 不能小于68 大于1500
if (new_mtu < 68)
return -EINVAL;
spin_lock_irqsave(lock, flags);
dev->mtu = new_mtu;
spin_unlock_irqrestore(lock, flags);
return 0;
}
void netinit(struct net_device *dev)
{
struct netpriv *priv;
ether_setup(dev);
dev->open = netopen;
dev->stop = netrelease;
dev->hard_start_xmit = nettx;
dev->do_ioctl = netioctl;
dev->get_stats = netstats;
dev->change_mtu = netchange_mtu;
dev->tx_timeout = nettx_timeout;
dev->dev_addr[0] = 0x18;//(0x01 & addr[0])为multicast
dev->dev_addr[1] = 0x02;
dev->dev_addr[2] = 0x03;
dev->dev_addr[3] = 0x04;
dev->dev_addr[4] = 0x05;
dev->dev_addr[5] = 0x06;
// 网卡标准接口标志位
dev->flags |= IFF_NOARP;
dev->features |= NETIF_F_NO_CSUM;
// 初始化priv 包含了统计数据和一些私有字段
priv = netdev_priv(dev);
memset(priv, 0, sizeof(struct netpriv));
spin_lock_init(&priv->lock);
}
void net_module_cleanup(void)
{
if (net_devs)
{
unregister_netdev(net_devs);
free_netdev(net_devs);
}
return;
}
int net_module_init(void)
{
int result,ret = -ENOMEM;
// allocate devices
net_devs=alloc_netdev(sizeof(struct netpriv), "eth%d", netinit);
if (net_devs == NULL)
goto out;
ret = -ENODEV;
if ((result = register_netdev(net_devs)))
printk("demo: error %i registering device \"%s\"\n", result, net_devs->name);
else
ret = 0;
out:
if (ret)
net_module_cleanup();
return ret;
}
module_init(net_module_init);
module_exit(net_module_cleanup);
网卡标准接口标记位
/usr/include/net/if.h文件
#ifdef __USE_MISC
/* Standard interface flags. */
enum
{
IFF_UP = 0x1, /* Interface is up. */
# define IFF_UP IFF_UP
IFF_BROADCAST = 0x2, /* Broadcast address valid. */
# define IFF_BROADCAST IFF_BROADCAST
IFF_DEBUG = 0x4, /* Turn on debugging. */
# define IFF_DEBUG IFF_DEBUG
IFF_LOOPBACK = 0x8, /* Is a loopback net. */
# define IFF_LOOPBACK IFF_LOOPBACK
IFF_POINTOPOINT = 0x10, /* Interface is point-to-point link. */
# define IFF_POINTOPOINT IFF_POINTOPOINT
IFF_NOTRAILERS = 0x20, /* Avoid use of trailers. */
# define IFF_NOTRAILERS IFF_NOTRAILERS
IFF_RUNNING = 0x40, /* Resources allocated. */
# define IFF_RUNNING IFF_RUNNING
IFF_NOARP = 0x80, /* No address resolution protocol. */
# define IFF_NOARP IFF_NOARP
IFF_PROMISC = 0x100, /* Receive all packets. */
# define IFF_PROMISC IFF_PROMISC
/* Not supported */
IFF_ALLMULTI = 0x200, /* Receive all multicast packets. */
# define IFF_ALLMULTI IFF_ALLMULTI
IFF_MASTER = 0x400, /* Master of a load balancer. */
# define IFF_MASTER IFF_MASTER
IFF_SLAVE = 0x800, /* Slave of a load balancer. */
# define IFF_SLAVE IFF_SLAVE
IFF_MULTICAST = 0x1000, /* Supports multicast. */
# define IFF_MULTICAST IFF_MULTICAST
IFF_PORTSEL = 0x2000, /* Can set media type. */
# define IFF_PORTSEL IFF_PORTSEL
IFF_AUTOMEDIA = 0x4000, /* Auto media select active. */
# define IFF_AUTOMEDIA IFF_AUTOMEDIA
IFF_DYNAMIC = 0x8000 /* Dialup device with changing addresses. */
# define IFF_DYNAMIC IFF_DYNAMIC
};
代码头文件
demo.h
#ifndef _ED_DEVICE_H
#define _ED_DEVICE_H
#define ED_REC_DEVICE 0
#define ED_TX_DEVICE 1
// 定义字符设备名
#define ED_REC_DEVICE_NAME "ed_rec"
#define ED_TX_DEVICE_NAME "ed_tx"
#define ED_MTU 192
#define ED_MAGIC 0x999
#define BUFFER_SIZE 2048
#define MAJOR_NUM_REC 200
#define MAJOR_NUM_TX 201
#define IOCTL_SET_BUSY _IOWR(MAJOR_NUM_TX,1,int)
#include <linux/netdevice.h>
struct ed_device{
int magic;
char name[8];
int busy;
unsigned char *buffer;
wait_queue_head_t rwait;
int mtu;
spinlock_t lock;
int tx_len;
int rx_len;
int buffer_size;
struct file *file;
ssize_t (*kernel_write)(const char *buffer,size_t length,int buffer_size);
};
// 自定义的数据结构
struct netpriv
{
struct net_device_stats stats;
struct sk_buff *skb;
spinlock_t lock;
};
#define ED_TIMEOUT 5
#endif