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

📄 ip6_output.c

📁 ipv6地址转换器
💻 C
📖 第 1 页 / 共 2 页
字号:
/* *	IPv6 output functions *	Linux INET6 implementation  * *	Authors: *	Pedro Roque		<roque@di.fc.ul.pt>	 * *	$Id: ip6_output.c,v 1.17 1999/04/22 10:07:42 davem Exp $ * *	Based on linux/net/ipv4/ip_output.c * *	This program is free software; you can redistribute it and/or *      modify it under the terms of the GNU General Public License *      as published by the Free Software Foundation; either version *      2 of the License, or (at your option) any later version. * *	Changes: *	A.N.Kuznetsov	:	airthmetics in fragmentation. *				extension headers are implemented. *				route changes now work. *				ip6_forward does not confuse sniffers. *				etc. *				 */#include <linux/errno.h>#include <linux/types.h>#include <linux/socket.h>#include <linux/net.h>#include <linux/netdevice.h>#include <linux/if_arp.h>#include <linux/in6.h>#include <linux/route.h>#include <net/sock.h>#include <net/snmp.h>#include <net/ipv6.h>#include <net/ndisc.h>#include <net/protocol.h>#include <net/ip6_route.h>#include <net/addrconf.h>#include <net/rawv6.h>#include <net/icmp.h>static u32	ipv6_fragmentation_id = 1;int ip6_output(struct sk_buff *skb){	struct dst_entry *dst = skb->dst;	struct device *dev = dst->dev;	struct hh_cache *hh = dst->hh;	skb->protocol = __constant_htons(ETH_P_IPV6);	skb->dev = dev;	if (ipv6_addr_is_multicast(&skb->nh.ipv6h->daddr)) {		if (!(dev->flags&IFF_LOOPBACK) &&		    (skb->sk == NULL || skb->sk->net_pinfo.af_inet6.mc_loop) &&		    ipv6_chk_mcast_addr(dev, &skb->nh.ipv6h->daddr)) {			/* Do not check for IFF_ALLMULTI; multicast routing			   is not supported in any case.			 */			dev_loopback_xmit(skb);			if (skb->nh.ipv6h->hop_limit == 0) {				kfree_skb(skb);				return 0;			}		}		ipv6_statistics.Ip6OutMcastPkts++;	}	if (hh) {#ifdef __alpha__		/* Alpha has disguisting memcpy. Help it. */	        u64 *aligned_hdr = (u64*)(skb->data - 16);		u64 *aligned_hdr0 = hh->hh_data;		read_lock_irq(&hh->hh_lock);		aligned_hdr[0] = aligned_hdr0[0];		aligned_hdr[1] = aligned_hdr0[1];#else		read_lock_irq(&hh->hh_lock);		memcpy(skb->data - 16, hh->hh_data, 16);#endif		read_unlock_irq(&hh->hh_lock);	        skb_push(skb, dev->hard_header_len);		return hh->hh_output(skb);	} else if (dst->neighbour)		return dst->neighbour->output(skb);	kfree_skb(skb);	return -EINVAL;}/* *	xmit an sk_buff (used by TCP) */int ip6_xmit(struct sock *sk, struct sk_buff *skb, struct flowi *fl,	     struct ipv6_txoptions *opt){	struct ipv6_pinfo * np = sk ? &sk->net_pinfo.af_inet6 : NULL;	struct in6_addr *first_hop = fl->nl_u.ip6_u.daddr;	struct dst_entry *dst = skb->dst;	struct ipv6hdr *hdr;	u8  proto = fl->proto;	int seg_len = skb->len;	int hlimit;	if (opt) {		int head_room;		/* First: exthdrs may take lots of space (~8K for now)		   MAX_HEADER is not enough.		 */		head_room = opt->opt_nflen + opt->opt_flen;		seg_len += head_room;		head_room += sizeof(struct ipv6hdr) + ((dst->dev->hard_header_len + 15)&~15);		if (skb_headroom(skb) < head_room) {			struct sk_buff *skb2 = skb_realloc_headroom(skb, head_room);			kfree(skb);			skb = skb2;			if (skb == NULL)				return -ENOBUFS;			if (sk)				skb_set_owner_w(skb, sk);		}		if (opt->opt_flen)			ipv6_push_frag_opts(skb, opt, &proto);		if (opt->opt_nflen)			ipv6_push_nfrag_opts(skb, opt, &proto, &first_hop);	}	hdr = skb->nh.ipv6h = (struct ipv6hdr*)skb_push(skb, sizeof(struct ipv6hdr));	/*	 *	Fill in the IPv6 header	 */	*(u32*)hdr = __constant_htonl(0x60000000) | fl->fl6_flowlabel;	hlimit = -1;	if (np)		hlimit = np->hop_limit;	if (hlimit < 0)		hlimit = ((struct rt6_info*)dst)->rt6i_hoplimit;	hdr->payload_len = htons(seg_len);	hdr->nexthdr = proto;	hdr->hop_limit = hlimit;	ipv6_addr_copy(&hdr->saddr, fl->nl_u.ip6_u.saddr);	ipv6_addr_copy(&hdr->daddr, first_hop);	if (skb->len <= dst->pmtu) {		ipv6_statistics.Ip6OutRequests++;		dst->output(skb);		return 0;	}	printk(KERN_DEBUG "IPv6: sending pkt_too_big to self\n");	start_bh_atomic();	icmpv6_send(skb, ICMPV6_PKT_TOOBIG, 0, dst->pmtu, skb->dev);	end_bh_atomic();	kfree_skb(skb);	return -EMSGSIZE;}/* *	To avoid extra problems ND packets are send through this *	routine. It's code duplication but I really want to avoid *	extra checks since ipv6_build_header is used by TCP (which *	is for us performace critical) */int ip6_nd_hdr(struct sock *sk, struct sk_buff *skb, struct device *dev,	       struct in6_addr *saddr, struct in6_addr *daddr,	       int proto, int len){	struct ipv6_pinfo *np = &sk->net_pinfo.af_inet6;	struct ipv6hdr *hdr;	int totlen;	skb->protocol = __constant_htons(ETH_P_IPV6);	skb->dev = dev;	totlen = len + sizeof(struct ipv6hdr);	hdr = (struct ipv6hdr *) skb_put(skb, sizeof(struct ipv6hdr));	skb->nh.ipv6h = hdr;	*(u32*)hdr = htonl(0x60000000);	hdr->payload_len = htons(len);	hdr->nexthdr = proto;	hdr->hop_limit = np->hop_limit;	ipv6_addr_copy(&hdr->saddr, saddr);	ipv6_addr_copy(&hdr->daddr, daddr);	return 0;}static struct ipv6hdr * ip6_bld_1(struct sock *sk, struct sk_buff *skb, struct flowi *fl,				  int hlimit, unsigned pktlength){	struct ipv6hdr *hdr;		skb->nh.raw = skb_put(skb, sizeof(struct ipv6hdr));	hdr = skb->nh.ipv6h;		*(u32*)hdr = fl->fl6_flowlabel | htonl(0x60000000);	hdr->payload_len = htons(pktlength - sizeof(struct ipv6hdr));	hdr->hop_limit = hlimit;	hdr->nexthdr = fl->proto;	ipv6_addr_copy(&hdr->saddr, fl->nl_u.ip6_u.saddr);	ipv6_addr_copy(&hdr->daddr, fl->nl_u.ip6_u.daddr);	return hdr;}static __inline__ u8 * ipv6_build_fraghdr(struct sk_buff *skb, u8* prev_hdr, unsigned offset){	struct frag_hdr *fhdr;	fhdr = (struct frag_hdr *) skb_put(skb, sizeof(struct frag_hdr));	fhdr->nexthdr  = *prev_hdr;	*prev_hdr = NEXTHDR_FRAGMENT;	prev_hdr = &fhdr->nexthdr;	fhdr->reserved = 0;	fhdr->frag_off = htons(offset);	fhdr->identification = ipv6_fragmentation_id++;	return &fhdr->nexthdr;}static int ip6_frag_xmit(struct sock *sk, inet_getfrag_t getfrag,			 const void *data, struct dst_entry *dst,			 struct flowi *fl, struct ipv6_txoptions *opt,			 struct in6_addr *final_dst,			 int hlimit, int flags, unsigned length, int mtu){	struct ipv6hdr *hdr;	struct sk_buff *last_skb;	u8 *prev_hdr;	int unfrag_len;	int frag_len;	int last_len;	int nfrags;	int fhdr_dist;	int frag_off;	int data_off;	int err;	/*	 *	Fragmentation	 *	 *	Extension header order:	 *	Hop-by-hop -> Dest0 -> Routing -> Fragment -> Auth -> Dest1 -> rest (...)	 *		 *	We must build the non-fragmented part that	 *	will be in every packet... this also means	 *	that other extension headers (Dest, Auth, etc)	 *	must be considered in the data to be fragmented	 */	unfrag_len = sizeof(struct ipv6hdr) + sizeof(struct frag_hdr);	last_len = length;	if (opt) {		unfrag_len += opt->opt_nflen;		last_len += opt->opt_flen;	}	/*	 *	Length of fragmented part on every packet but 	 *	the last must be an:	 *	"integer multiple of 8 octects".	 */	frag_len = (mtu - unfrag_len) & ~0x7;	/* Unfragmentable part exceeds mtu. */	if (frag_len <= 0) {		ipv6_local_error(sk, EMSGSIZE, fl, mtu);		return -EMSGSIZE;	}	nfrags = last_len / frag_len;	/*	 *	We must send from end to start because of 	 *	UDP/ICMP checksums. We do a funny trick:	 *	fill the last skb first with the fixed	 *	header (and its data) and then use it	 *	to create the following segments and send it	 *	in the end. If the peer is checking the M_flag	 *	to trigger the reassembly code then this 	 *	might be a good idea.	 */	frag_off = nfrags * frag_len;	last_len -= frag_off;	if (last_len == 0) {		last_len = frag_len;		frag_off -= frag_len;		nfrags--;	}	data_off = frag_off;	/* And it is implementation problem: for now we assume, that	   all the exthdrs will fit to the first fragment.	 */	if (opt) {		if (frag_len < opt->opt_flen) {			ipv6_local_error(sk, EMSGSIZE, fl, mtu);			return -EMSGSIZE;		}		data_off = frag_off - opt->opt_flen;	}	last_skb = sock_alloc_send_skb(sk, unfrag_len + frag_len +				       dst->dev->hard_header_len + 15,				       0, flags & MSG_DONTWAIT, &err);	if (last_skb == NULL)		return err;	last_skb->dst = dst_clone(dst);	skb_reserve(last_skb, (dst->dev->hard_header_len + 15) & ~15);	hdr = ip6_bld_1(sk, last_skb, fl, hlimit, frag_len+unfrag_len);	prev_hdr = &hdr->nexthdr;	if (opt && opt->opt_nflen)		prev_hdr = ipv6_build_nfrag_opts(last_skb, prev_hdr, opt, final_dst, 0);	prev_hdr = ipv6_build_fraghdr(last_skb, prev_hdr, frag_off);	fhdr_dist = prev_hdr - last_skb->data;	err = getfrag(data, &hdr->saddr, last_skb->tail, data_off, last_len);	if (!err) {		while (nfrags--) {			struct sk_buff *skb;						struct frag_hdr *fhdr2;							skb = skb_copy(last_skb, sk->allocation);			if (skb == NULL) {				ipv6_statistics.Ip6FragFails++;				kfree_skb(last_skb);				return -ENOMEM;			}

⌨️ 快捷键说明

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