datapath/linux/compat/ip_fragment.c
/* Build a new IP datagram from all its fragments. */
Build a new IP datagram from all its fragments.
Build a new IP datagram from all its fragments .
static int ip_frag_reasm(struct ipq *qp, struct sk_buff *prev,
			 struct net_device *dev)
{
	struct net *net = get_net_from_netns_frags(qp->q.net);
	struct iphdr *iph;
	struct sk_buff *fp, *head = qp->q.fragments;
	int len;
	int ihlen;
	int err;
	u8 ecn;
	ipq_kill(qp);
	ecn = ip_frag_ecn_table[qp->ecn];
	if (unlikely(ecn == 0xff)) {
		err = -EINVAL;
		goto out_fail;
	}
	if (prev) {
		head = prev->next;
		fp = skb_clone(head, GFP_ATOMIC);
		if (!fp)
			goto out_nomem;
		fp->next = head->next;
		if (!fp->next)
			qp->q.fragments_tail = fp;
		prev->next = fp;
		skb_morph(head, qp->q.fragments);
		head->next = qp->q.fragments->next;
		consume_skb(qp->q.fragments);
		qp->q.fragments = head;
	}
	WARN_ON(!head);
	WARN_ON(FRAG_CB(head)->offset != 0);
	ihlen = ip_hdrlen(head);
	len = ihlen + qp->q.len;
	err = -E2BIG;
	if (len > 65535)
		goto out_oversize;
	if (skb_unclone(head, GFP_ATOMIC))
		goto out_nomem;
	if (skb_has_frag_list(head)) {
		struct sk_buff *clone;
		int i, plen = 0;
		clone = alloc_skb(0, GFP_ATOMIC);
		if (!clone)
			goto out_nomem;
		clone->next = head->next;
		head->next = clone;
		skb_shinfo(clone)->frag_list = skb_shinfo(head)->frag_list;
		skb_frag_list_init(head);
		for (i = 0; i < skb_shinfo(head)->nr_frags; i++)
			plen += skb_frag_size(&skb_shinfo(head)->frags[i]);
		clone->len = clone->data_len = head->data_len - plen;
		head->data_len -= clone->len;
		head->len -= clone->len;
		clone->csum = 0;
		clone->ip_summed = head->ip_summed;
		add_frag_mem_limit(qp->q.net, clone->truesize);
	}
	skb_shinfo(head)->frag_list = head->next;
	skb_push(head, head->data - skb_network_header(head));
	for (fp=head->next; fp; fp = fp->next) {
		head->data_len += fp->len;
		head->len += fp->len;
		if (head->ip_summed != fp->ip_summed)
			head->ip_summed = CHECKSUM_NONE;
		else if (head->ip_summed == CHECKSUM_COMPLETE)
			head->csum = csum_add(head->csum, fp->csum);
		head->truesize += fp->truesize;
	}
	sub_frag_mem_limit(qp->q.net, head->truesize);
	head->next = NULL;
	head->dev = dev;
	head->tstamp = qp->q.stamp;
	IPCB(head)->frag_max_size = max(qp->max_df_size, qp->q.max_size);
	iph = ip_hdr(head);
	iph->tot_len = htons(len);
	iph->tos |= ecn;
	if (qp->max_df_size == qp->q.max_size) {
		IPCB(head)->flags |= IPSKB_FRAG_PMTU;
		iph->frag_off = htons(IP_DF);
	} else {
		iph->frag_off = 0;
	}
	ip_send_check(iph);
	IP_INC_STATS_BH(net, IPSTATS_MIB_REASMOKS);
	qp->q.fragments = NULL;
	qp->q.fragments_tail = NULL;
	return 0;
out_nomem:
	net_dbg_ratelimited("queue_glue: no memory for gluing queue %p\n", qp);
	err = -ENOMEM;
	goto out_fail;
out_oversize:
	net_info_ratelimited("Oversized IP packet from %pI4\n", &qp->saddr);
out_fail:
	IP_INC_STATS_BH(net, IPSTATS_MIB_REASMFAILS);
	return err;
}
static int ip_frag_reasm ( struct ipq * qp , struct sk_buff * prev , struct net_device * dev ) { struct net * net = get_net_from_netns_frags ( qp -> q . net ) ; struct iphdr * iph ; struct sk_buff * fp , * head = qp -> q . fragments ; int len ; int ihlen ; int err ; u8 ecn ; ipq_kill ( qp ) ; ecn = ip_frag_ecn_table [ qp -> ecn ] ; if ( unlikely ( ecn == 0xff ) ) { err = - EINVAL ; goto out_fail ; } if ( prev ) { head = prev -> next ; fp = skb_clone ( head , GFP_ATOMIC ) ; if ( ! fp ) goto out_nomem ; fp -> next = head -> next ; if ( ! fp -> next ) qp -> q . fragments_tail = fp ; prev -> next = fp ; skb_morph ( head , qp -> q . fragments ) ; head -> next = qp -> q . fragments -> next ; consume_skb ( qp -> q . fragments ) ; qp -> q . fragments = head ; } WARN_ON ( ! head ) ; WARN_ON ( FRAG_CB ( head ) -> offset != 0 ) ; ihlen = ip_hdrlen ( head ) ; len = ihlen + qp -> q . len ; err = - E2BIG ; if ( len > 65535 ) goto out_oversize ; if ( skb_unclone ( head , GFP_ATOMIC ) ) goto out_nomem ; if ( skb_has_frag_list ( head ) ) { struct sk_buff * clone ; int i , plen = 0 ; clone = alloc_skb ( 0 , GFP_ATOMIC ) ; if ( ! clone ) goto out_nomem ; clone -> next = head -> next ; head -> next = clone ; skb_shinfo ( clone ) -> frag_list = skb_shinfo ( head ) -> frag_list ; skb_frag_list_init ( head ) ; for ( i = 0 ; i < skb_shinfo ( head ) -> nr_frags ; i ++ ) plen += skb_frag_size ( & skb_shinfo ( head ) -> frags [ i ] ) ; clone -> len = clone -> data_len = head -> data_len - plen ; head -> data_len -= clone -> len ; head -> len -= clone -> len ; clone -> csum = 0 ; clone -> ip_summed = head -> ip_summed ; add_frag_mem_limit ( qp -> q . net , clone -> truesize ) ; } skb_shinfo ( head ) -> frag_list = head -> next ; skb_push ( head , head -> data - skb_network_header ( head ) ) ; for ( fp = head -> next ; fp ; fp = fp -> next ) { head -> data_len += fp -> len ; head -> len += fp -> len ; if ( head -> ip_summed != fp -> ip_summed ) head -> ip_summed = CHECKSUM_NONE ; else if ( head -> ip_summed == CHECKSUM_COMPLETE ) head -> csum = csum_add ( head -> csum , fp -> csum ) ; head -> truesize += fp -> truesize ; } sub_frag_mem_limit ( qp -> q . net , head -> truesize ) ; head -> next = NULL ; head -> dev = dev ; head -> tstamp = qp -> q . stamp ; IPCB ( head ) -> frag_max_size = max ( qp -> max_df_size , qp -> q . max_size ) ; iph = ip_hdr ( head ) ; iph -> tot_len = htons ( len ) ; iph -> tos |= ecn ; if ( qp -> max_df_size == qp -> q . max_size ) { IPCB ( head ) -> flags |= IPSKB_FRAG_PMTU ; iph -> frag_off = htons ( IP_DF ) ; } else { iph -> frag_off = 0 ; } ip_send_check ( iph ) ; IP_INC_STATS_BH ( net , IPSTATS_MIB_REASMOKS ) ; qp -> q . fragments = NULL ; qp -> q . fragments_tail = NULL ; return 0 ; out_nomem : net_dbg_ratelimited ( " \n " , qp ) ; err = - ENOMEM ; goto out_fail ; out_oversize : net_info_ratelimited ( " \n " , & qp -> saddr ) ; out_fail : IP_INC_STATS_BH ( net , IPSTATS_MIB_REASMFAILS ) ; return err ; }
Build a new IP datagram from all its fragments.
Build a new IP datagram from all its fragments .
/* Make the one we just received the head. */ /* Allocate a new buffer for the datagram. */ /* Head of list must not be cloned. */ /* If the first fragment is fragmented itself, we split
	 * it to two chunks: the first with data and paged part
	 * and the second, holding only fragments. */ /* When we set IP_DF on a refragmented skb we must also force a
	 * call to ip_fragment to avoid forwarding a DF-skb of size s while
	 * original sender only sent fragments of size f (where f < s).
	 *
	 * We only set DF/IPSKB_FRAG_PMTU if such DF fragment was the largest
	 * frag seen to avoid sending tiny DF-fragments in case skb was built
	 * from one very small df-fragment and one large non-df frag.
	 */