Linux 在指定的报文中插入VLAN头部然后分片发送(QINQ)

产品需要支持QINQ,所以需要在二层为接收到的报文插入VLAN头部,然后再转发出去
//如果只扩展VLAN_HLEN 会导致发送时的 skb_push空间不够
if (skb_cow_head(skb, VLAN_HLEN + VLAN_HLEN + ETH_HLEN) < 0)
{
    return skb;
}

struct vlan_ethhdr *veth;
skb_push(skb, VLAN_HLEN);
skb->mac_header -= VLAN_HLEN;
skb->network_header   -= VLAN_HLEN;
veth = (struct vlan_ethhdr *)skb->mac_header;
memmove(skb->mac_header, skb->mac_header + VLAN_HLEN, 12);
veth->h_vlan_proto = htons(ETH_P_8021Q);
veth->h_vlan_TCI   = htons(e->priv_vlan & VLAN_VID_MASK);
如果只有一个VLAN头部,那不需要做分片处理,但可能原始报文本来就有一个VLAN头,再增加一个VLAN就需要做分片处理
struct sk_buff * ip_fragment(struct sk_buff *skb)
{
	struct iphdr *iph;
	struct sk_buff *skb2 = NULL;
	int ptr = 0;
	int offset = 0;
	uint16_t not_last_frag;
	unsigned int mtu, hlen, left, len, ll_rs;
	struct rtable *rt = skb_rtable(skb);
	struct net_device *dev = rt->dst.dev;


	len = VLAN_HLEN;
	ll_rs = 14 + 4;//etherhdr + vhdr + vhdr
	mtu = skb->dev->mtu;


	//if (skb->len > skb->dev->mtu)
	{
		iph = (struct iphdr *)skb->data;
		hlen = iph->ihl * 4;
		left = skb->len - hlen;		/* Space per frame */
		ptr = hlen;		/* Where to start from */
		/*
		 *	Fragment the datagram.
		 */


		offset = (ntohs(iph->frag_off) & IP_OFFSET) << 3;
		not_last_frag = iph->frag_off & htons(IP_MF);
		/*
		** first fragment
		** len = left - 64;
		**
		*/


		while(left > 0)
		{
			skb2 = NULL;
			len = left;
			if (len+hlen+ll_rs > mtu)
			{
				len = left - 64;
			}
			if ((skb2 = alloc_skb(len+hlen+ll_rs, GFP_ATOMIC)) == NULL) {
				return skb;
			}
			/*
			 *	Set up data on packet
			 */


			ip_copy_metadata(skb2, skb);
			skb_reserve(skb2, ll_rs);
			skb_put(skb2, len + hlen);
			skb_reset_network_header(skb2);
			skb2->transport_header = skb2->network_header + hlen;


			/*
			 *	Charge the memory for the fragment to any owner
			 *	it might possess
			 */


			if (skb->sk)
				skb_set_owner_w(skb2, skb->sk);


			/*
			 *	Copy the packet header into the new buffer.
			 */


			skb_copy_from_linear_data(skb, skb_network_header(skb2), hlen);


			/*
			 *	Copy a block of the IP datagram.
			 */
			if (skb_copy_bits(skb, ptr, skb_transport_header(skb2), len))
			{
				kfree_skb(skb2);
				return skb;
			}
			left -= len;


			/*
			 *	Fill in the new header fields.
			 */
			iph = ip_hdr(skb2);
			iph->frag_off = htons((offset >> 3));


			/*
			 *	Added AC : If we are fragmenting a fragment that's not the
			 *		   last fragment then keep MF on each bit
			 */
			if (left > 0 || not_last_frag)
				iph->frag_off |= htons(IP_MF);
			ptr += len;
			offset += len;


			/*
			 *	Put this fragment into the sending queue.
			 */
			iph->tot_len = htons(len + hlen);


			iph->check = 0;
			iph->check = ip_fast_csum((unsigned char *)iph, iph->ihl);


			skb2->vlan_tci = skb->vlan_tci;
			skb_push(skb2, ETH_HLEN + VLAN_HLEN);
			skb_reset_mac_header(skb2);
			memcpy(skb2->mac_header, skb->mac_header, ETH_HLEN + VLAN_HLEN);


			if (0)
			{
				int i = 0;
				for (i = 0; i < 32 && (skb2->mac_header != NULL); i+=8)
				{
					printk("mac_header1:%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x\n",
						skb2->mac_header[i + 0], skb2->mac_header[i + 1], skb2->mac_header[i + 2],
						skb2->mac_header[i + 3], skb2->mac_header[i + 4], skb2->mac_header[i + 5],
						skb2->mac_header[i + 6], skb2->mac_header[i + 7]);
				}
				for (i = 0; i < 32 && (skb2->data != NULL); i+=8)
				{
					printk("data1:%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x\n",
						skb2->data[i + 0], skb2->data[i + 1], skb2->data[i + 2],
						skb2->data[i + 3], skb2->data[i + 4], skb2->data[i + 5],
						skb2->data[i + 6], skb2->data[i + 7]);
				}
			}


			br_drop_fake_rtable(skb2);
			dev_queue_xmit(skb2);
			IP_INC_STATS(dev_net(dev), IPSTATS_MIB_FRAGCREATES);
		}
		consume_skb(skb);
		IP_INC_STATS(dev_net(dev), IPSTATS_MIB_FRAGOKS);
		return NULL;
			
	}
}

你可能感兴趣的:(工作笔记)