⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 ip_output.c

📁 Linux Kernel 2.6.9 for OMAP1710
💻 C
📖 第 1 页 / 共 3 页
字号:
		kfree_skb(skb);		return -EMSGSIZE;	}	/*	 *	Setup starting values.	 */	hlen = iph->ihl * 4;	mtu = dst_pmtu(&rt->u.dst) - hlen;	/* Size of data space */	/* When frag_list is given, use it. First, check its validity:	 * some transformers could create wrong frag_list or break existing	 * one, it is not prohibited. In this case fall back to copying.	 *	 * LATER: this step can be merged to real generation of fragments,	 * we can switch to copy when see the first bad fragment.	 */	if (skb_shinfo(skb)->frag_list) {		struct sk_buff *frag;		int first_len = skb_pagelen(skb);		if (first_len - hlen > mtu ||		    ((first_len - hlen) & 7) ||		    (iph->frag_off & htons(IP_MF|IP_OFFSET)) ||		    skb_cloned(skb))			goto slow_path;		for (frag = skb_shinfo(skb)->frag_list; frag; frag = frag->next) {			/* Correct geometry. */			if (frag->len > mtu ||			    ((frag->len & 7) && frag->next) ||			    skb_headroom(frag) < hlen)			    goto slow_path;			/* Partially cloned skb? */			if (skb_shared(frag))				goto slow_path;		}		/* Everything is OK. Generate! */		err = 0;		offset = 0;		frag = skb_shinfo(skb)->frag_list;		skb_shinfo(skb)->frag_list = NULL;		skb->data_len = first_len - skb_headlen(skb);		skb->len = first_len;		iph->tot_len = htons(first_len);		iph->frag_off |= htons(IP_MF);		ip_send_check(iph);		for (;;) {			/* Prepare header of the next frame,			 * before previous one went down. */			if (frag) {				frag->h.raw = frag->data;				frag->nh.raw = __skb_push(frag, hlen);				memcpy(frag->nh.raw, iph, hlen);				iph = frag->nh.iph;				iph->tot_len = htons(frag->len);				ip_copy_metadata(frag, skb);				if (offset == 0)					ip_options_fragment(frag);				offset += skb->len - hlen;				iph->frag_off = htons(offset>>3);				if (frag->next != NULL)					iph->frag_off |= htons(IP_MF);				/* Ready, complete checksum */				ip_send_check(iph);			}			err = output(skb);			if (err || !frag)				break;			skb = frag;			frag = skb->next;			skb->next = NULL;		}		if (err == 0) {			IP_INC_STATS(IPSTATS_MIB_FRAGOKS);			return 0;		}		while (frag) {			skb = frag->next;			kfree_skb(frag);			frag = skb;		}		IP_INC_STATS(IPSTATS_MIB_FRAGFAILS);		return err;	}slow_path:	left = skb->len - hlen;		/* Space per frame */	ptr = raw + hlen;		/* Where to start from */#ifdef CONFIG_BRIDGE_NETFILTER	/* for bridged IP traffic encapsulated inside f.e. a vlan header,	 * we need to make room for the encapsulating header */	ll_rs = LL_RESERVED_SPACE_EXTRA(rt->u.dst.dev, nf_bridge_pad(skb));	mtu -= nf_bridge_pad(skb);#else	ll_rs = LL_RESERVED_SPACE(rt->u.dst.dev);#endif	/*	 *	Fragment the datagram.	 */	offset = (ntohs(iph->frag_off) & IP_OFFSET) << 3;	not_last_frag = iph->frag_off & htons(IP_MF);	/*	 *	Keep copying data until we run out.	 */	while(left > 0)	{		len = left;		/* IF: it doesn't fit, use 'mtu' - the data space left */		if (len > mtu)			len = mtu;		/* IF: we are not sending upto and including the packet end		   then align the next start on an eight byte boundary */		if (len < left)	{			len &= ~7;		}		/*		 *	Allocate buffer.		 */		if ((skb2 = alloc_skb(len+hlen+ll_rs, GFP_ATOMIC)) == NULL) {			NETDEBUG(printk(KERN_INFO "IP: frag: no memory for new fragment!\n"));			err = -ENOMEM;			goto fail;		}		/*		 *	Set up data on packet		 */		ip_copy_metadata(skb2, skb);		skb_reserve(skb2, ll_rs);		skb_put(skb2, len + hlen);		skb2->nh.raw = skb2->data;		skb2->h.raw = skb2->data + 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.		 */		memcpy(skb2->nh.raw, skb->data, hlen);		/*		 *	Copy a block of the IP datagram.		 */		if (skb_copy_bits(skb, ptr, skb2->h.raw, len))			BUG();		left -= len;		/*		 *	Fill in the new header fields.		 */		iph = skb2->nh.iph;		iph->frag_off = htons((offset >> 3));		/* ANK: dirty, but effective trick. Upgrade options only if		 * the segment to be fragmented was THE FIRST (otherwise,		 * options are already fixed) and make it ONCE		 * on the initial skb, so that all the following fragments		 * will inherit fixed options.		 */		if (offset == 0)			ip_options_fragment(skb);		/*		 *	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.		 */		IP_INC_STATS(IPSTATS_MIB_FRAGCREATES);		iph->tot_len = htons(len + hlen);		ip_send_check(iph);		err = output(skb2);		if (err)			goto fail;	}	kfree_skb(skb);	IP_INC_STATS(IPSTATS_MIB_FRAGOKS);	return err;fail:	kfree_skb(skb); 	IP_INC_STATS(IPSTATS_MIB_FRAGFAILS);	return err;}intip_generic_getfrag(void *from, char *to, int offset, int len, int odd, struct sk_buff *skb){	struct iovec *iov = from;	if (skb->ip_summed == CHECKSUM_HW) {		if (memcpy_fromiovecend(to, iov, offset, len) < 0)			return -EFAULT;	} else {		unsigned int csum = 0;		if (csum_partial_copy_fromiovecend(to, iov, offset, len, &csum) < 0)			return -EFAULT;		skb->csum = csum_block_add(skb->csum, csum, odd);	}	return 0;}static inline unsigned intcsum_page(struct page *page, int offset, int copy){	char *kaddr;	unsigned int csum;	kaddr = kmap(page);	csum = csum_partial(kaddr + offset, copy, 0);	kunmap(page);	return csum;}/* *	ip_append_data() and ip_append_page() can make one large IP datagram *	from many pieces of data. Each pieces will be holded on the socket *	until ip_push_pending_frames() is called. Each piece can be a page *	or non-page data. *	 *	Not only UDP, other transport protocols - e.g. raw sockets - can use *	this interface potentially. * *	LATER: length must be adjusted by pad at tail, when it is required. */int ip_append_data(struct sock *sk,		   int getfrag(void *from, char *to, int offset, int len,			       int odd, struct sk_buff *skb),		   void *from, int length, int transhdrlen,		   struct ipcm_cookie *ipc, struct rtable *rt,		   unsigned int flags){	struct inet_opt *inet = inet_sk(sk);	struct sk_buff *skb;	struct ip_options *opt = NULL;	int hh_len;	int exthdrlen;	int mtu;	int copy;	int err;	int offset = 0;	unsigned int maxfraglen, fragheaderlen;	int csummode = CHECKSUM_NONE;	if (flags&MSG_PROBE)		return 0;	if (skb_queue_empty(&sk->sk_write_queue)) {		/*		 * setup for corking.		 */		opt = ipc->opt;		if (opt) {			if (inet->cork.opt == NULL) {				inet->cork.opt = kmalloc(sizeof(struct ip_options) + 40, sk->sk_allocation);				if (unlikely(inet->cork.opt == NULL))					return -ENOBUFS;			}			memcpy(inet->cork.opt, opt, sizeof(struct ip_options)+opt->optlen);			inet->cork.flags |= IPCORK_OPT;			inet->cork.addr = ipc->addr;		}		dst_hold(&rt->u.dst);		inet->cork.fragsize = mtu = dst_pmtu(&rt->u.dst);		inet->cork.rt = rt;		inet->cork.length = 0;		sk->sk_sndmsg_page = NULL;		sk->sk_sndmsg_off = 0;		if ((exthdrlen = rt->u.dst.header_len) != 0) {			length += exthdrlen;			transhdrlen += exthdrlen;		}	} else {		rt = inet->cork.rt;		if (inet->cork.flags & IPCORK_OPT)			opt = inet->cork.opt;		transhdrlen = 0;		exthdrlen = 0;		mtu = inet->cork.fragsize;	}	hh_len = LL_RESERVED_SPACE(rt->u.dst.dev);	fragheaderlen = sizeof(struct iphdr) + (opt ? opt->optlen : 0);	maxfraglen = ((mtu - fragheaderlen) & ~7) + fragheaderlen;	if (inet->cork.length + length > 0xFFFF - fragheaderlen) {		ip_local_error(sk, EMSGSIZE, rt->rt_dst, inet->dport, mtu-exthdrlen);		return -EMSGSIZE;	}	/*	 * transhdrlen > 0 means that this is the first fragment and we wish	 * it won't be fragmented in the future.	 */	if (transhdrlen &&	    length + fragheaderlen <= mtu &&	    rt->u.dst.dev->features&(NETIF_F_IP_CSUM|NETIF_F_NO_CSUM|NETIF_F_HW_CSUM) &&	    !exthdrlen)		csummode = CHECKSUM_HW;	inet->cork.length += length;	/* So, what's going on in the loop below?	 *	 * We use calculated fragment length to generate chained skb,	 * each of segments is IP fragment ready for sending to network after	 * adding appropriate IP header.	 */	if ((skb = skb_peek_tail(&sk->sk_write_queue)) == NULL)		goto alloc_new_skb;	while (length > 0) {		/* Check if the remaining data fits into current packet. */		copy = mtu - skb->len;		if (copy < length)			copy = maxfraglen - skb->len;		if (copy <= 0) {			char *data;			unsigned int datalen;			unsigned int fraglen;			unsigned int fraggap;			unsigned int alloclen;			struct sk_buff *skb_prev;alloc_new_skb:			skb_prev = skb;			if (skb_prev)				fraggap = skb_prev->len - maxfraglen;			else				fraggap = 0;			/*			 * If remaining data exceeds the mtu,			 * we know we need more fragment(s).			 */			datalen = length + fraggap;			if (datalen > mtu - fragheaderlen)				datalen = maxfraglen - fragheaderlen;			fraglen = datalen + fragheaderlen;			if ((flags & MSG_MORE) && 			    !(rt->u.dst.dev->features&NETIF_F_SG))				alloclen = mtu;			else				alloclen = datalen + fragheaderlen;			/* The last fragment gets additional space at tail.			 * Note, with MSG_MORE we overallocate on fragments,			 * because we have no idea what fragment will be			 * the last.			 */			if (datalen == length)				alloclen += rt->u.dst.trailer_len;			if (transhdrlen) {				skb = sock_alloc_send_skb(sk, 						alloclen + hh_len + 15,						(flags & MSG_DONTWAIT), &err);			} else {				skb = NULL;				if (atomic_read(&sk->sk_wmem_alloc) <=				    2 * sk->sk_sndbuf)					skb = sock_wmalloc(sk, 							   alloclen + hh_len + 15, 1,							   sk->sk_allocation);				if (unlikely(skb == NULL))					err = -ENOBUFS;			}			if (skb == NULL)				goto error;			/*			 *	Fill in the control structures			 */			skb->ip_summed = csummode;			skb->csum = 0;			skb_reserve(skb, hh_len);			/*			 *	Find where to start putting bytes.			 */			data = skb_put(skb, fraglen);			skb->nh.raw = data + exthdrlen;			data += fragheaderlen;			skb->h.raw = data + exthdrlen;			if (fraggap) {				skb->csum = skb_copy_and_csum_bits(					skb_prev, maxfraglen,					data + transhdrlen, fraggap, 0);				skb_prev->csum = csum_sub(skb_prev->csum,							  skb->csum);				data += fraggap;				skb_trim(skb_prev, maxfraglen);			}			copy = datalen - transhdrlen - fraggap;			if (copy > 0 && getfrag(from, data + transhdrlen, offset, copy, fraggap, skb) < 0) {				err = -EFAULT;				kfree_skb(skb);				goto error;			}			offset += copy;			length -= datalen - fraggap;			transhdrlen = 0;			exthdrlen = 0;			csummode = CHECKSUM_NONE;			/*			 * Put the packet on the pending queue.			 */			__skb_queue_tail(&sk->sk_write_queue, skb);			continue;		}		if (copy > length)			copy = length;		if (!(rt->u.dst.dev->features&NETIF_F_SG)) {

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -