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

📄 ip6_output.c

📁 linux 内核源代码
💻 C
📖 第 1 页 / 共 3 页
字号:
 *	As a result, this function can only be used in process context. * *	It returns zero on success, or a standard errno code on error. */int ip6_sk_dst_lookup(struct sock *sk, struct dst_entry **dst, struct flowi *fl){	*dst = NULL;	if (sk) {		*dst = sk_dst_check(sk, inet6_sk(sk)->dst_cookie);		*dst = ip6_sk_dst_check(sk, *dst, fl);	}	return ip6_dst_lookup_tail(sk, dst, fl);}EXPORT_SYMBOL_GPL(ip6_sk_dst_lookup);static inline int ip6_ufo_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 hh_len, int fragheaderlen,			int transhdrlen, int mtu,unsigned int flags){	struct sk_buff *skb;	int err;	/* There is support for UDP large send offload by network	 * device, so create one single skb packet containing complete	 * udp datagram	 */	if ((skb = skb_peek_tail(&sk->sk_write_queue)) == NULL) {		skb = sock_alloc_send_skb(sk,			hh_len + fragheaderlen + transhdrlen + 20,			(flags & MSG_DONTWAIT), &err);		if (skb == NULL)			return -ENOMEM;		/* reserve space for Hardware header */		skb_reserve(skb, hh_len);		/* create space for UDP/IP header */		skb_put(skb,fragheaderlen + transhdrlen);		/* initialize network header pointer */		skb_reset_network_header(skb);		/* initialize protocol header pointer */		skb->transport_header = skb->network_header + fragheaderlen;		skb->ip_summed = CHECKSUM_PARTIAL;		skb->csum = 0;		sk->sk_sndmsg_off = 0;	}	err = skb_append_datato_frags(sk,skb, getfrag, from,				      (length - transhdrlen));	if (!err) {		struct frag_hdr fhdr;		/* specify the length of each IP datagram fragment*/		skb_shinfo(skb)->gso_size = mtu - fragheaderlen -					    sizeof(struct frag_hdr);		skb_shinfo(skb)->gso_type = SKB_GSO_UDP;		ipv6_select_ident(skb, &fhdr);		skb_shinfo(skb)->ip6_frag_id = fhdr.identification;		__skb_queue_tail(&sk->sk_write_queue, skb);		return 0;	}	/* There is not enough support do UPD LSO,	 * so follow normal path	 */	kfree_skb(skb);	return err;}int ip6_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,	int hlimit, int tclass, struct ipv6_txoptions *opt, struct flowi *fl,	struct rt6_info *rt, unsigned int flags){	struct inet_sock *inet = inet_sk(sk);	struct ipv6_pinfo *np = inet6_sk(sk);	struct sk_buff *skb;	unsigned int maxfraglen, fragheaderlen;	int exthdrlen;	int hh_len;	int mtu;	int copy;	int err;	int offset = 0;	int csummode = CHECKSUM_NONE;	if (flags&MSG_PROBE)		return 0;	if (skb_queue_empty(&sk->sk_write_queue)) {		/*		 * setup for corking		 */		if (opt) {			if (np->cork.opt == NULL) {				np->cork.opt = kmalloc(opt->tot_len,						       sk->sk_allocation);				if (unlikely(np->cork.opt == NULL))					return -ENOBUFS;			} else if (np->cork.opt->tot_len < opt->tot_len) {				printk(KERN_DEBUG "ip6_append_data: invalid option length\n");				return -EINVAL;			}			memcpy(np->cork.opt, opt, opt->tot_len);			inet->cork.flags |= IPCORK_OPT;			/* need source address above miyazawa*/		}		dst_hold(&rt->u.dst);		np->cork.rt = rt;		inet->cork.fl = *fl;		np->cork.hop_limit = hlimit;		np->cork.tclass = tclass;		mtu = np->pmtudisc == IPV6_PMTUDISC_PROBE ?		      rt->u.dst.dev->mtu : dst_mtu(rt->u.dst.path);		if (np->frag_size < mtu) {			if (np->frag_size)				mtu = np->frag_size;		}		inet->cork.fragsize = mtu;		if (dst_allfrag(rt->u.dst.path))			inet->cork.flags |= IPCORK_ALLFRAG;		inet->cork.length = 0;		sk->sk_sndmsg_page = NULL;		sk->sk_sndmsg_off = 0;		exthdrlen = rt->u.dst.header_len + (opt ? opt->opt_flen : 0);		length += exthdrlen;		transhdrlen += exthdrlen;	} else {		rt = np->cork.rt;		fl = &inet->cork.fl;		if (inet->cork.flags & IPCORK_OPT)			opt = np->cork.opt;		transhdrlen = 0;		exthdrlen = 0;		mtu = inet->cork.fragsize;	}	hh_len = LL_RESERVED_SPACE(rt->u.dst.dev);	fragheaderlen = sizeof(struct ipv6hdr) + rt->u.dst.nfheader_len + (opt ? opt->opt_nflen : 0);	maxfraglen = ((mtu - fragheaderlen) & ~7) + fragheaderlen - sizeof(struct frag_hdr);	if (mtu <= sizeof(struct ipv6hdr) + IPV6_MAXPLEN) {		if (inet->cork.length + length > sizeof(struct ipv6hdr) + IPV6_MAXPLEN - fragheaderlen) {			ipv6_local_error(sk, EMSGSIZE, fl, mtu-exthdrlen);			return -EMSGSIZE;		}	}	/*	 * Let's try using as much space as possible.	 * Use MTU if total length of the message fits into the MTU.	 * Otherwise, we need to reserve fragment header and	 * fragment alignment (= 8-15 octects, in total).	 *	 * Note that we may need to "move" the data from the tail of	 * of the buffer to the new fragment when we split	 * the message.	 *	 * FIXME: It may be fragmented into multiple chunks	 *        at once if non-fragmentable extension headers	 *        are too large.	 * --yoshfuji	 */	inet->cork.length += length;	if (((length > mtu) && (sk->sk_protocol == IPPROTO_UDP)) &&	    (rt->u.dst.dev->features & NETIF_F_UFO)) {		err = ip6_ufo_append_data(sk, getfrag, from, length, hh_len,					  fragheaderlen, transhdrlen, mtu,					  flags);		if (err)			goto error;		return 0;	}	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 = (inet->cork.length <= mtu && !(inet->cork.flags & IPCORK_ALLFRAG) ? mtu : maxfraglen) - 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;			/* There's no room in the current 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 > (inet->cork.length <= mtu && !(inet->cork.flags & IPCORK_ALLFRAG) ? mtu : maxfraglen) - 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: we overallocate on fragments with MSG_MODE			 * because we have no idea if we're the last one.			 */			if (datalen == length + fraggap)				alloclen += rt->u.dst.trailer_len;			/*			 * We just reserve space for fragment header.			 * Note: this may be overallocation if the message			 * (without MSG_MORE) fits into the MTU.			 */			alloclen += sizeof(struct frag_hdr);			if (transhdrlen) {				skb = sock_alloc_send_skb(sk,						alloclen + hh_len,						(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, 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;			/* reserve for fragmentation */			skb_reserve(skb, hh_len+sizeof(struct frag_hdr));			/*			 *	Find where to start putting bytes			 */			data = skb_put(skb, fraglen);			skb_set_network_header(skb, exthdrlen);			data += fragheaderlen;			skb->transport_header = (skb->network_header +						 fragheaderlen);			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;				pskb_trim_unique(skb_prev, maxfraglen);			}			copy = datalen - transhdrlen - fraggap;			if (copy < 0) {				err = -EINVAL;				kfree_skb(skb);				goto error;			} else 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)) {			unsigned int off;			off = skb->len;			if (getfrag(from, skb_put(skb, copy),						offset, copy, off, skb) < 0) {				__skb_trim(skb, off);				err = -EFAULT;				goto error;			}		} else {			int i = skb_shinfo(skb)->nr_frags;			skb_frag_t *frag = &skb_shinfo(skb)->frags[i-1];			struct page *page = sk->sk_sndmsg_page;			int off = sk->sk_sndmsg_off;			unsigned int left;			if (page && (left = PAGE_SIZE - off) > 0) {				if (copy >= left)					copy = left;				if (page != frag->page) {					if (i == MAX_SKB_FRAGS) {						err = -EMSGSIZE;						goto error;					}					get_page(page);					skb_fill_page_desc(skb, i, page, sk->sk_sndmsg_off, 0);					frag = &skb_shinfo(skb)->frags[i];				}			} else if(i < MAX_SKB_FRAGS) {				if (copy > PAGE_SIZE)					copy = PAGE_SIZE;				page = alloc_pages(sk->sk_allocation, 0);				if (page == NULL) {					err = -ENOMEM;					goto error;				}				sk->sk_sndmsg_page = page;				sk->sk_sndmsg_off = 0;				skb_fill_page_desc(skb, i, page, 0, 0);				frag = &skb_shinfo(skb)->frags[i];			} else {				err = -EMSGSIZE;				goto error;			}			if (getfrag(from, page_address(frag->page)+frag->page_offset+frag->size, offset, copy, skb->len, skb) < 0) {				err = -EFAULT;				goto error;			}			sk->sk_sndmsg_off += copy;			frag->size += copy;			skb->len += copy;			skb->data_len += copy;			skb->truesize += copy;			atomic_add(copy, &sk->sk_wmem_alloc);		}		offset += copy;		length -= copy;	}	return 0;error:	inet->cork.length -= length;	IP6_INC_STATS(rt->rt6i_idev, IPSTATS_MIB_OUTDISCARDS);	return err;}static void ip6_cork_release(struct inet_sock *inet, struct ipv6_pinfo *np){	inet->cork.flags &= ~IPCORK_OPT;	kfree(np->cork.opt);	np->cork.opt = NULL;	if (np->cork.rt) {		dst_release(&np->cork.rt->u.dst);		np->cork.rt = NULL;		inet->cork.flags &= ~IPCORK_ALLFRAG;	}	memset(&inet->cork.fl, 0, sizeof(inet->cork.fl));}int ip6_push_pending_frames(struct sock *sk){	struct sk_buff *skb, *tmp_skb;	struct sk_buff **tail_skb;	struct in6_addr final_dst_buf, *final_dst = &final_dst_buf;	struct inet_sock *inet = inet_sk(sk);	struct ipv6_pinfo *np = inet6_sk(sk);	struct ipv6hdr *hdr;	struct ipv6_txoptions *opt = np->cork.opt;	struct rt6_info *rt = np->cork.rt;	struct flowi *fl = &inet->cork.fl;	unsigned char proto = fl->proto;	int err = 0;	if ((skb = __skb_dequeue(&sk->sk_write_queue)) == NULL)		goto out;	tail_skb = &(skb_shinfo(skb)->frag_list);	/* move skb->data to ip header from ext header */	if (skb->data < skb_network_header(skb))		__skb_pull(skb, skb_network_offset(skb));	while ((tmp_skb = __skb_dequeue(&sk->sk_write_queue)) != NULL) {		__skb_pull(tmp_skb, skb_network_header_len(skb));		*tail_skb = tmp_skb;		tail_skb = &(tmp_skb->next);		skb->len += tmp_skb->len;		skb->data_len += tmp_skb->len;		skb->truesize += tmp_skb->truesize;		__sock_put(tmp_skb->sk);		tmp_skb->destructor = NULL;		tmp_skb->sk = NULL;	}	ipv6_addr_copy(final_dst, &fl->fl6_dst);	__skb_pull(skb, skb_network_header_len(skb));	if (opt && opt->opt_flen)		ipv6_push_frag_opts(skb, opt, &proto);	if (opt && opt->opt_nflen)		ipv6_push_nfrag_opts(skb, opt, &proto, &final_dst);	skb_push(skb, sizeof(struct ipv6hdr));	skb_reset_network_header(skb);	hdr = ipv6_hdr(skb);	*(__be32*)hdr = fl->fl6_flowlabel |		     htonl(0x60000000 | ((int)np->cork.tclass << 20));	if (skb->len <= sizeof(struct ipv6hdr) + IPV6_MAXPLEN)		hdr->payload_len = htons(skb->len - sizeof(struct ipv6hdr));	else		hdr->payload_len = 0;	hdr->hop_limit = np->cork.hop_limit;	hdr->nexthdr = proto;	ipv6_addr_copy(&hdr->saddr, &fl->fl6_src);	ipv6_addr_copy(&hdr->daddr, final_dst);	skb->priority = sk->sk_priority;	skb->dst = dst_clone(&rt->u.dst);	IP6_INC_STATS(rt->rt6i_idev, IPSTATS_MIB_OUTREQUESTS);	if (proto == IPPROTO_ICMPV6) {		struct inet6_dev *idev = ip6_dst_idev(skb->dst);		ICMP6MSGOUT_INC_STATS_BH(idev, icmp6_hdr(skb)->icmp6_type);		ICMP6_INC_STATS_BH(idev, ICMP6_MIB_OUTMSGS);	}	err = NF_HOOK(PF_INET6, NF_IP6_LOCAL_OUT, skb, NULL, skb->dst->dev, dst_output);	if (err) {		if (err > 0)			err = np->recverr ? net_xmit_errno(err) : 0;		if (err)			goto error;	}out:	ip6_cork_release(inet, np);	return err;error:	goto out;}void ip6_flush_pending_frames(struct sock *sk){	struct sk_buff *skb;	while ((skb = __skb_dequeue_tail(&sk->sk_write_queue)) != NULL) {		if (skb->dst)			IP6_INC_STATS(ip6_dst_idev(skb->dst),				      IPSTATS_MIB_OUTDISCARDS);		kfree_skb(skb);	}	ip6_cork_release(inet_sk(sk), inet6_sk(sk));}

⌨️ 快捷键说明

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