网卡驱动3-做一个与外界交互的虚拟网卡2(调用真实网卡发送数据)

之前说我我加入一个自己的协议,现在说一下我的协议的意思是每个数据包前加一个32位的数据,前16位是协议号,即ETH_IP_VNIC;

后16位的高八位是’w’(我的姓的第一字母),低八位是虚拟网卡号。所以VNIC_HLEN是4。可以看一下结构体struct vnic_ethhdr

/**
 *	struct vnic_ethhdr - vnic ethernet header (ethhdr + vnic_hdr)
 *	@h_dest: destination ethernet address
 *	@h_source: source ethernet address
 *	@h_vnic_proto: ethernet protocol (always ETH_P_VNIC)
 */
struct vnic_ethhdr {
	unsigned char	h_dest[ETH_ALEN];
	unsigned char	h_source[ETH_ALEN];
	__be16		h_vnic_proto;
	__be16		h_vnic_data;
};

//vnic_put_tag主要是构建我的vnic头
static inline struct sk_buff *vnic_put_tag(struct sk_buff *skb, u16 vnic_data)
{
	struct vnic_ethhdr *beth;

        //skb_cow_head是看skb是否有足够的余量来装入vnic头,不够会重新分配。
	if (skb_cow_head(skb, VNIC_HLEN) < 0) 
        {  
		kfree_skb(skb);
		return NULL;
	}
        //skb_push之后会skb->data -= len;beth = skb->data;
	beth = (struct vnic_ethhdr *)skb_push(skb, VNIC_HLEN);

	/* 把mac地址放到新头的开始*/
        //我们知道mac都在skb->data+VNIC_HLEN的地方,所以要把它考到前面。
	memmove(skb->data, skb->data + VNIC_HLEN, 2 * VNIC_ETH_ALEN);
	skb->mac_header -= VNIC_HLEN;//更新mac头地址

	/* 加上协议类型,这里的赋值会传输到网络上*/
	beth->h_vnic_proto = htons(ETH_P_VNIC);

	beth->h_vnic_data = htons(vnic_data);
	skb->protocol = htons(ETH_P_VNIC);//这里赋值是上报给内核,内核可能不会处理我这个屌丝协议,所以你赋也可以。

	return skb;
}

static netdev_tx_t vnic_dev_hard_start_xmit(struct sk_buff *skb,
					    struct net_device *dev)
{
	struct vnic_ethhdr *veth = (struct vnic_ethhdr *)(skb->data);
	unsigned int len;
	int ret;

	if (veth->h_vnic_proto != htons(ETH_P_VNIC)) //判断一下
	{
        vnic_put_tag(skb, ('w' << 8) | (vnic_dev_info(dev)->vnic_id));
	}

	skb_set_dev(skb, vnic_dev_info(dev)->real_dev);
	len = skb->len;
	ret = dev_queue_xmit(skb);
	if (likely(ret == NET_XMIT_SUCCESS || ret == NET_XMIT_CN)) 
	{
		struct vnic_pcpu_stats *stats;//使用了每cpu量管理网卡的状态
		stats = this_cpu_ptr(vnic_dev_info(dev)->vnic_pcpu_stats);
		u64_stats_update_begin(&stats->syncp);//内存屏蔽,保证上面的获取在下面的赋值前完成
		stats->tx_packets++;//发送报计数
		stats->tx_bytes += len;//发送字节计数
		u64_stats_update_end(&stats->syncp);
	} 
	else 
	{
		this_cpu_inc(vnic_dev_info(dev)->vnic_pcpu_stats->tx_dropped);//失败计数
	}

	return ret;
}


下面我们看一下这几句。

    skb_set_dev(skb,vnic_dev_info(dev)->real_dev);

/*

上面的就一句

    skb->dev = dev;

*/

    len = skb->len;

    ret =dev_queue_xmit(skb);

dev_queue_xmit就是linux网络分层结构中,数据跨层的一个接口。

我们说一下TCP/IP模型四层结构:

列出tcp/ip四层结构,只是与下面linux网络设备四层结构做比较,不要混淆。

linux网络设备层次:

网卡驱动3-做一个与外界交互的虚拟网卡2(调用真实网卡发送数据)_第1张图片

我们再来看dev_queue_xmit()

不贴代码了,简单分析,

虽然函数名有个queue,不过不一定用到队列。这个由struct netdev_queue中的struct netdev_queue  *_tx;决定。_tx[n]->qdisc->enqueue是否为空。如果你没有实现ndo_select_queue(),那么_tx[]和sk对应由sk->sk_tx_queue_mapping(位图关系)决定。

当然用queue会先把skb入队再调度,反之直接发送。

发送时通过调用ops->ndo_start_xmit(skb, dev);这就到了上面的说的网络设备接口层。

这些具体可以看看

http://shaojiashuai123456.iteye.com/blog/842236

从上面可以看出,写实际的网卡驱动主要是设备功能层和网络设备媒介层。

而我的虚拟网卡主要是网络协议接口层和网络设备接口层,对实际网卡的应用。


你可能感兴趣的:(linux,内核,网卡,linux内核)