产品需要支持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;
}
}