之前说我我加入一个自己的协议,现在说一下我的协议的意思是每个数据包前加一个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
从上面可以看出,写实际的网卡驱动主要是设备功能层和网络设备媒介层。
而我的虚拟网卡主要是网络协议接口层和网络设备接口层,对实际网卡的应用。