Linux下的虛擬網卡驅動

網上有虛擬網卡驅動,拿來學習下網卡驅動知識,運行環境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)

 

發佈了202 篇原創文章 · 獲贊 103 · 訪問量 60萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章