網上有虛擬網卡驅動,拿來學習下網卡驅動知識,運行環境kernel_4.15.0-72
#include <linux/module.h>
#include <linux/netdevice.h>
#include <linux/etherdevice.h>
#include <linux/skbuff.h>
#include <linux/ip.h>
#include <linux/netdev_features.h>
static struct net_device *virt_net;
/*
pr_info("h_source=%x:%x:%x:%x:%x:%x\n",ethhdr->h_source[0],ethhdr->h_source[1],ethhdr->h_source[2],ethhdr->h_source[3],ethhdr->h_source[4],ethhdr->h_source[5]);
pr_info("h_dest=%x:%x:%x:%x:%x:%x\n",ethhdr->h_dest[0],ethhdr->h_dest[1],ethhdr->h_dest[2],ethhdr->h_dest[3],ethhdr->h_dest[4],ethhdr->h_dest[5]);
pr_info("saddr %d.%d.%d.%d\n",(*saddr)&0x000000ff,(*saddr>>8)&0x000000ff,(*saddr>>16)&0x000000ff,(*saddr>>24)&0x000000ff);
pr_info("daddr %d.%d.%d.%d\n",(*daddr)&0x000000ff,(*daddr>>8)&0x000000ff,(*daddr>>16)&0x000000ff,(*daddr>>24)&0x000000ff);
*/
static void virt_rs_packet(struct sk_buff *skb, struct net_device *dev){
unsigned char *type;
struct iphdr *ih;
__be32 *saddr, *daddr, tmp;
unsigned char tmp_dev_addr[ETH_ALEN];
struct ethhdr *ethhdr;
struct sk_buff *rx_skb;
int ret;
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);//對調ethhdr結構體 "源/目的"MAC地址*/
ih = (struct iphdr *)(skb->data + sizeof(struct ethhdr));
saddr = &ih->saddr;
daddr = &ih->daddr;
tmp = *saddr;
*saddr = *daddr;
*daddr = tmp;//對調iphdr結構體"源/目的" IP地址
ih->check=0;
ih->check = ip_fast_csum((unsigned char *)ih,ih->ihl);
type = skb->data + sizeof(struct ethhdr) + sizeof(struct iphdr);
*type = 0; //之前是發送ping包0x08,需要改爲0x00,表示接收ping包
rx_skb = dev_alloc_skb(skb->len + 2);
skb_reserve(rx_skb, 2);
memcpy(skb_put(rx_skb, skb->len), skb->data, skb->len);
rx_skb->dev = dev;
rx_skb->ip_summed = CHECKSUM_UNNECESSARY;
rx_skb->protocol = eth_type_trans(rx_skb, dev);
ret=netif_rx(rx_skb);
dev->stats.rx_packets++;
dev->stats.rx_bytes += skb->len;
pr_info("rx_packets=%ld rx_bytes=%ld ret=%d\n",dev->stats.rx_packets,dev->stats.rx_bytes,ret);
}
static int virt_send_packet(struct sk_buff *skb, struct net_device *dev){
netif_stop_queue(dev);
virt_rs_packet(skb,dev);
dev_kfree_skb(skb);
dev->stats.tx_packets++;
dev->stats.tx_bytes+=skb->len;
pr_info("tx_packets=%ld tx_bytes=%ld\n",dev->stats.tx_packets,dev->stats.tx_bytes);
netif_wake_queue(dev);
return NETDEV_TX_OK;
}
static int set_mac_address(struct net_device *dev,void *p){
struct sockaddr *addr = p;
pr_info("set_mac_address\n");
if (netif_running(dev))
return -EBUSY;
memcpy(dev->dev_addr, addr->sa_data, dev->addr_len);
return 0;
}
void virt_tx_timeout(struct net_device *net){
pr_info("virt_tx_timeout\n");
}
static const struct net_device_ops net_ops = {
.ndo_start_xmit = virt_send_packet,
.ndo_set_mac_address =set_mac_address,
.ndo_tx_timeout = virt_tx_timeout,
};
static int virt_net_init(void){
virt_net= alloc_netdev(sizeof(struct net_device), "virt_net", NET_NAME_UNKNOWN,ether_setup);
virt_net->netdev_ops= &net_ops;
virt_net->flags |= IFF_NOARP;
virt_net->dev_addr[0] = 0x88;
virt_net->dev_addr[1] = 0x88;
virt_net->dev_addr[2] = 0x88;
virt_net->dev_addr[3] = 0x88;
virt_net->dev_addr[4] = 0x88;
virt_net->dev_addr[5] = 0x88;
register_netdev(virt_net);
return 0;
}
static void virt_net_exit(void){
unregister_netdev(virt_net);
free_netdev(virt_net);
}
module_init(virt_net_init);
module_exit(virt_net_exit);
MODULE_LICENSE("GPL");
用wireshark抓包分析
設置mac地址
ifconfig
ifconfig virt_net down
ifconfig virt_net hw ether 00:0C:29:36:97:20
ifconfig virt_net up
ifconfig
在virt_send_packet加上 WARN_ON(1)把相應的堆棧打印出來,查看調用關係
關鍵流程如下
dev_queue_xmit
__dev_queue_xmit
dev_hard_start_xmit
xmit_one
netdev_start_xmit
__netdev_start_xmit
ops->ndo_start_xmit(skb, dev)