之前说我我加入一个自己的协议,现在说一下我的协议的意思是每个数据包前加一个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网络设备层次:我们再来看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
从上面可以看出,写实际的网卡驱动主要是设备功能层和网络设备媒介层。
而我的虚拟网卡主要是网络协议接口层和网络设备接口层,对实际网卡的应用。
网卡驱动3-做一个与外界交互的虚拟网卡2(调用真实网卡发送数据)
原文:http://blog.csdn.net/xxxxxlllllxl/article/details/18982297