一.网络设备驱动框架
接收
将报文从设备驱动接受并送入协议栈
老API netif_if
编写网络设备驱动
步骤
1.注册一个网络设备
2.填充net_device_ops结构体
3.编写接收发送函数
// SPDX-License-Identifier: GPL-2.0-only
/*
* This module emits "Hello, world" on printk when loaded.
*
* It is designed to be used for basic evaluation of the module loading
* subsystem (for example when validating module signing/verification). It
* lacks any extra dependencies, and will not normally be loaded by the
* system unless explicitly requested by name.
*/
#include <linux/kernel.h>
#include <linux/jiffies.h>
#include <linux/module.h>
#include <linux/interrupt.h>
#include <linux/fs.h>
#include <linux/types.h>
#include <linux/string.h>
#include <linux/socket.h>
#include <linux/errno.h>
#include <linux/fcntl.h>
#include <linux/in.h>
#include <linux/uaccess.h>
#include <linux/io.h>
#include <linux/inet.h>
#include <linux/netdevice.h>
#include <linux/etherdevice.h>
#include <linux/skbuff.h>
#include <linux/ethtool.h>
#include <net/sock.h>
#include <net/checksum.h>
#include <linux/if_ether.h> /* For the statistics structure. */
#include <linux/if_arp.h> /* For ARPHRD_ETHER */
#include <linux/ip.h>
#include <linux/tcp.h>
#include <linux/percpu.h>
#include <linux/net_tstamp.h>
#include <net/net_namespace.h>
#include <linux/u64_stats_sync.h>
#include <linux/printk.h>
#include <linux/icmp.h>
#define DEBUG_TEST_NETDEV
#define TEST_NETDEV_NAME "test%d"
#define MAX_JUMBO_FRAME_SIZE 0x3F00
static struct net_device *test_netdev;
#ifdef DEBUG_TEST_NETDEV
static void dump_skb (struct sk_buff * skb)
{
print_hex_dump(KERN_ERR, "data: ", DUMP_PREFIX_OFFSET,
16, 1, skb->data, skb->len, true);
return;
}
#endif
static int test_open(struct net_device *dev)
{
printk("<%s:%d>\n", __FUNCTION__,__LINE__);
return 0;
}
static int test_close(struct net_device *dev)
{
printk("<%s:%d>\n", __FUNCTION__,__LINE__);
return 0;
}
netdev_tx_t test_xmit_frame(struct sk_buff *skb, struct net_device *dev)
{
int len;
struct ethhdr *ethh = eth_hdr(skb);
struct iphdr *iph = ip_hdr(skb);
struct icmphdr *icmph = icmp_hdr(skb);
char mac_tmp[6];
__be32 addr_tmp;
printk("<%s:%d>\n", __FUNCTION__,__LINE__);
#ifdef DEBUG_TEST_NETDEV
dump_skb(skb);
#endif
memcpy(mac_tmp, ethh->h_source, 6);
memcpy(ethh->h_source, ethh->h_dest, 6);
memcpy(ethh->h_dest, mac_tmp, 6);
addr_tmp = iph->saddr;
iph->saddr = iph->daddr;
iph->daddr = addr_tmp;
icmph->type = 0;
icmph->checksum = 0;
icmph->checksum = ip_compute_csum(icmph, iph->tot_len - iph->ihl);
iph->check = 0;
iph->check = ip_compute_csum(iph, iph->tot_len);
#ifdef DEBUG_TEST_NETDEV
dump_skb(skb);
#endif
skb_tx_timestamp(skb);
/* do not fool net_timestamp_check() with various clock bases */
skb->tstamp = 0;
skb_orphan(skb);
/* Before queueing this packet to netif_rx(),
* make sure dst is refcounted.
*/
skb_dst_force(skb);
skb->protocol = eth_type_trans(skb, dev);
len = skb->len;
if (likely(netif_rx(skb) == NET_RX_SUCCESS)) {
test_netdev->stats.rx_bytes += len;
test_netdev->stats.tx_bytes += len;
test_netdev->stats.rx_packets++;
test_netdev->stats.tx_packets++;
}
return NETDEV_TX_OK;
}
static void test_set_rx_mode(struct net_device *dev)
{
printk("<%s:%d>\n", __FUNCTION__,__LINE__);
return;
}
static int test_set_mac(struct net_device *dev, void *addr)
{
struct sockaddr *saddr = addr;
printk("<%s:%d> mac: %02x:%02x:%02x:%02x:%02x:%02x\n",__FUNCTION__,__LINE__,
saddr->sa_data[0],saddr->sa_data[1],saddr->sa_data[2],saddr->sa_data[3],saddr->sa_data[4],saddr->sa_data[5]);
memcpy(dev->dev_addr, saddr->sa_data, dev->addr_len);
return 0;
}
static void test_tx_timeout (struct net_device *dev)
{
printk("<%s:%d>\n", __FUNCTION__,__LINE__);
return;
}
static int test_change_mtu(struct net_device *dev, int new_mtu)
{
printk("<%s:%d>: mtu: %d\n", __FUNCTION__,__LINE__, new_mtu);
return 0;
}
static int test_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
{
printk("<%s:%d> cmd %d\n", __FUNCTION__,__LINE__, cmd);
return 0;
}
static void test_netpoll(struct net_device *dev)
{
printk("<%s:%d>\n", __FUNCTION__,__LINE__);
return;
}
static netdev_features_t test_fix_features(struct net_device *dev, netdev_features_t features)
{
printk("<%s:%d> features %llx\n", __FUNCTION__,__LINE__, dev->features);
return features;
}
static int test_set_features(struct net_device *dev, netdev_features_t features)
{
printk("<%s:%d> features %llx\n", __FUNCTION__,__LINE__, features);
dev->features = features;
return 1;
}
static void test_change_rx_flags(struct net_device *dev, int flags)
{
printk("<%s:%d> flags %d\n", __FUNCTION__,__LINE__, flags);
}
static const struct net_device_ops test_netdev_ops = {
.ndo_open = test_open,
.ndo_stop = test_close,
.ndo_start_xmit = test_xmit_frame,
.ndo_set_rx_mode = test_set_rx_mode,
.ndo_set_mac_address = test_set_mac,
.ndo_tx_timeout = test_tx_timeout,
.ndo_change_mtu = test_change_mtu,
.ndo_do_ioctl = test_ioctl,
.ndo_validate_addr = eth_validate_addr,
#ifdef CONFIG_NET_POLL_CONTROLLER
.ndo_poll_controller = test_netpoll,
#endif
.ndo_fix_features = test_fix_features,
.ndo_set_features = test_set_features,
.ndo_change_rx_flags = test_change_rx_flags,
};
static int __init test_netdev_init(void)
{
int ret = 0;
pr_warn("init test netdev\n");
test_netdev = alloc_etherdev(sizeof(struct net_device));
if (!test_netdev)
goto out;
test_netdev->netdev_ops = &test_netdev_ops;
strcpy(test_netdev->name, TEST_NETDEV_NAME);
ret = register_netdev(test_netdev);
if(ret)
goto failed;
test_netdev->min_mtu = ETH_ZLEN - ETH_HLEN;
test_netdev->max_mtu = MAX_JUMBO_FRAME_SIZE - (ETH_HLEN + ETH_FCS_LEN);
memset(test_netdev->dev_addr, 0x60, test_netdev->addr_len);
return 0;
failed:
free_netdev(test_netdev);
out:
return 0;
}
module_init(test_netdev_init);
static void __exit test_netdev_exit(void)
{
pr_warn("exit test netdev\n");
unregister_netdev(test_netdev);
free_netdev(test_netdev);
}
module_exit(test_netdev_exit);
MODULE_AUTHOR("Kees Cook <keescook@chromium.org>");
MODULE_LICENSE("GPL");
test0就是我们的驱动