前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >linux系统网卡驱动编码实例

linux系统网卡驱动编码实例

原创
作者头像
stan1ey
修改2022-06-05 00:08:18
8.8K0
修改2022-06-05 00:08:18
举报
文章被收录于专栏:安全开发记录安全开发记录

通过register_netdev注册网络设备。

实现了网络中断。

模拟网络硬件传输数据包。

检查ip数据包。

记录传输的数据包状态。

手动释放sk_buffer。

处理数据包传输超时情况。

网络驱动接收网络数据包并将数据包放入TCP/IP上层,编写网络驱动接收数据包必须分配sk_buff结构来存储数据,sk_buff将在上层释放。

代码

demo.c

代码语言:javascript
复制
#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_buffe
    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文件

代码语言:javascript
复制
#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

代码语言:javascript
复制
#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

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 代码
    • demo.c
    • 网卡标准接口标记位
    • 代码头文件
    领券
    问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档