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

📄 ip_gre.c

📁 linux 内核源代码
💻 C
📖 第 1 页 / 共 3 页
字号:
			n = ntohs(icmp_hdr(skb)->un.frag.mtu);			if (n < grehlen+68)				return;			n -= grehlen;			/* BSD 4.2 MORE DOES NOT EXIST IN NATURE. */			if (n > ntohs(eiph->tot_len))				return;			rel_info = htonl(n);			break;		default:			/* All others are translated to HOST_UNREACH.			   rfc2003 contains "deep thoughts" about NET_UNREACH,			   I believe, it is just ether pollution. --ANK			 */			rel_type = ICMP_DEST_UNREACH;			rel_code = ICMP_HOST_UNREACH;			break;		}		break;	case ICMP_TIME_EXCEEDED:		if (code != ICMP_EXC_TTL)			return;		break;	}	/* Prepare fake skb to feed it to icmp_send */	skb2 = skb_clone(skb, GFP_ATOMIC);	if (skb2 == NULL)		return;	dst_release(skb2->dst);	skb2->dst = NULL;	skb_pull(skb2, skb->data - (u8*)eiph);	skb_reset_network_header(skb2);	/* Try to guess incoming interface */	memset(&fl, 0, sizeof(fl));	fl.fl4_dst = eiph->saddr;	fl.fl4_tos = RT_TOS(eiph->tos);	fl.proto = IPPROTO_GRE;	if (ip_route_output_key(&rt, &fl)) {		kfree_skb(skb2);		return;	}	skb2->dev = rt->u.dst.dev;	/* route "incoming" packet */	if (rt->rt_flags&RTCF_LOCAL) {		ip_rt_put(rt);		rt = NULL;		fl.fl4_dst = eiph->daddr;		fl.fl4_src = eiph->saddr;		fl.fl4_tos = eiph->tos;		if (ip_route_output_key(&rt, &fl) ||		    rt->u.dst.dev->type != ARPHRD_IPGRE) {			ip_rt_put(rt);			kfree_skb(skb2);			return;		}	} else {		ip_rt_put(rt);		if (ip_route_input(skb2, eiph->daddr, eiph->saddr, eiph->tos, skb2->dev) ||		    skb2->dst->dev->type != ARPHRD_IPGRE) {			kfree_skb(skb2);			return;		}	}	/* change mtu on this route */	if (type == ICMP_DEST_UNREACH && code == ICMP_FRAG_NEEDED) {		if (n > dst_mtu(skb2->dst)) {			kfree_skb(skb2);			return;		}		skb2->dst->ops->update_pmtu(skb2->dst, n);	} else if (type == ICMP_TIME_EXCEEDED) {		struct ip_tunnel *t = netdev_priv(skb2->dev);		if (t->parms.iph.ttl) {			rel_type = ICMP_DEST_UNREACH;			rel_code = ICMP_HOST_UNREACH;		}	}	icmp_send(skb2, rel_type, rel_code, rel_info);	kfree_skb(skb2);#endif}static inline void ipgre_ecn_decapsulate(struct iphdr *iph, struct sk_buff *skb){	if (INET_ECN_is_ce(iph->tos)) {		if (skb->protocol == htons(ETH_P_IP)) {			IP_ECN_set_ce(ip_hdr(skb));		} else if (skb->protocol == htons(ETH_P_IPV6)) {			IP6_ECN_set_ce(ipv6_hdr(skb));		}	}}static inline u8ipgre_ecn_encapsulate(u8 tos, struct iphdr *old_iph, struct sk_buff *skb){	u8 inner = 0;	if (skb->protocol == htons(ETH_P_IP))		inner = old_iph->tos;	else if (skb->protocol == htons(ETH_P_IPV6))		inner = ipv6_get_dsfield((struct ipv6hdr *)old_iph);	return INET_ECN_encapsulate(tos, inner);}static int ipgre_rcv(struct sk_buff *skb){	struct iphdr *iph;	u8     *h;	__be16    flags;	__sum16   csum = 0;	__be32 key = 0;	u32    seqno = 0;	struct ip_tunnel *tunnel;	int    offset = 4;	if (!pskb_may_pull(skb, 16))		goto drop_nolock;	iph = ip_hdr(skb);	h = skb->data;	flags = *(__be16*)h;	if (flags&(GRE_CSUM|GRE_KEY|GRE_ROUTING|GRE_SEQ|GRE_VERSION)) {		/* - Version must be 0.		   - We do not support routing headers.		 */		if (flags&(GRE_VERSION|GRE_ROUTING))			goto drop_nolock;		if (flags&GRE_CSUM) {			switch (skb->ip_summed) {			case CHECKSUM_COMPLETE:				csum = csum_fold(skb->csum);				if (!csum)					break;				/* fall through */			case CHECKSUM_NONE:				skb->csum = 0;				csum = __skb_checksum_complete(skb);				skb->ip_summed = CHECKSUM_COMPLETE;			}			offset += 4;		}		if (flags&GRE_KEY) {			key = *(__be32*)(h + offset);			offset += 4;		}		if (flags&GRE_SEQ) {			seqno = ntohl(*(__be32*)(h + offset));			offset += 4;		}	}	read_lock(&ipgre_lock);	if ((tunnel = ipgre_tunnel_lookup(iph->saddr, iph->daddr, key)) != NULL) {		secpath_reset(skb);		skb->protocol = *(__be16*)(h + 2);		/* WCCP version 1 and 2 protocol decoding.		 * - Change protocol to IP		 * - When dealing with WCCPv2, Skip extra 4 bytes in GRE header		 */		if (flags == 0 &&		    skb->protocol == htons(ETH_P_WCCP)) {			skb->protocol = htons(ETH_P_IP);			if ((*(h + offset) & 0xF0) != 0x40)				offset += 4;		}		skb->mac_header = skb->network_header;		__pskb_pull(skb, offset);		skb_reset_network_header(skb);		skb_postpull_rcsum(skb, skb_transport_header(skb), offset);		skb->pkt_type = PACKET_HOST;#ifdef CONFIG_NET_IPGRE_BROADCAST		if (MULTICAST(iph->daddr)) {			/* Looped back packet, drop it! */			if (((struct rtable*)skb->dst)->fl.iif == 0)				goto drop;			tunnel->stat.multicast++;			skb->pkt_type = PACKET_BROADCAST;		}#endif		if (((flags&GRE_CSUM) && csum) ||		    (!(flags&GRE_CSUM) && tunnel->parms.i_flags&GRE_CSUM)) {			tunnel->stat.rx_crc_errors++;			tunnel->stat.rx_errors++;			goto drop;		}		if (tunnel->parms.i_flags&GRE_SEQ) {			if (!(flags&GRE_SEQ) ||			    (tunnel->i_seqno && (s32)(seqno - tunnel->i_seqno) < 0)) {				tunnel->stat.rx_fifo_errors++;				tunnel->stat.rx_errors++;				goto drop;			}			tunnel->i_seqno = seqno + 1;		}		tunnel->stat.rx_packets++;		tunnel->stat.rx_bytes += skb->len;		skb->dev = tunnel->dev;		dst_release(skb->dst);		skb->dst = NULL;		nf_reset(skb);		ipgre_ecn_decapsulate(iph, skb);		netif_rx(skb);		read_unlock(&ipgre_lock);		return(0);	}	icmp_send(skb, ICMP_DEST_UNREACH, ICMP_PORT_UNREACH, 0);drop:	read_unlock(&ipgre_lock);drop_nolock:	kfree_skb(skb);	return(0);}static int ipgre_tunnel_xmit(struct sk_buff *skb, struct net_device *dev){	struct ip_tunnel *tunnel = netdev_priv(dev);	struct net_device_stats *stats = &tunnel->stat;	struct iphdr  *old_iph = ip_hdr(skb);	struct iphdr  *tiph;	u8     tos;	__be16 df;	struct rtable *rt;     			/* Route to the other host */	struct net_device *tdev;			/* Device to other host */	struct iphdr  *iph;			/* Our new IP header */	unsigned int max_headroom;		/* The extra header space needed */	int    gre_hlen;	__be32 dst;	int    mtu;	if (tunnel->recursion++) {		tunnel->stat.collisions++;		goto tx_error;	}	if (dev->header_ops) {		gre_hlen = 0;		tiph = (struct iphdr*)skb->data;	} else {		gre_hlen = tunnel->hlen;		tiph = &tunnel->parms.iph;	}	if ((dst = tiph->daddr) == 0) {		/* NBMA tunnel */		if (skb->dst == NULL) {			tunnel->stat.tx_fifo_errors++;			goto tx_error;		}		if (skb->protocol == htons(ETH_P_IP)) {			rt = (struct rtable*)skb->dst;			if ((dst = rt->rt_gateway) == 0)				goto tx_error_icmp;		}#ifdef CONFIG_IPV6		else if (skb->protocol == htons(ETH_P_IPV6)) {			struct in6_addr *addr6;			int addr_type;			struct neighbour *neigh = skb->dst->neighbour;			if (neigh == NULL)				goto tx_error;			addr6 = (struct in6_addr*)&neigh->primary_key;			addr_type = ipv6_addr_type(addr6);			if (addr_type == IPV6_ADDR_ANY) {				addr6 = &ipv6_hdr(skb)->daddr;				addr_type = ipv6_addr_type(addr6);			}			if ((addr_type & IPV6_ADDR_COMPATv4) == 0)				goto tx_error_icmp;			dst = addr6->s6_addr32[3];		}#endif		else			goto tx_error;	}	tos = tiph->tos;	if (tos&1) {		if (skb->protocol == htons(ETH_P_IP))			tos = old_iph->tos;		tos &= ~1;	}	{		struct flowi fl = { .oif = tunnel->parms.link,				    .nl_u = { .ip4_u =					      { .daddr = dst,						.saddr = tiph->saddr,						.tos = RT_TOS(tos) } },				    .proto = IPPROTO_GRE };		if (ip_route_output_key(&rt, &fl)) {			tunnel->stat.tx_carrier_errors++;			goto tx_error;		}	}	tdev = rt->u.dst.dev;	if (tdev == dev) {		ip_rt_put(rt);		tunnel->stat.collisions++;		goto tx_error;	}	df = tiph->frag_off;	if (df)		mtu = dst_mtu(&rt->u.dst) - tunnel->hlen;	else		mtu = skb->dst ? dst_mtu(skb->dst) : dev->mtu;	if (skb->dst)		skb->dst->ops->update_pmtu(skb->dst, mtu);	if (skb->protocol == htons(ETH_P_IP)) {		df |= (old_iph->frag_off&htons(IP_DF));		if ((old_iph->frag_off&htons(IP_DF)) &&		    mtu < ntohs(old_iph->tot_len)) {			icmp_send(skb, ICMP_DEST_UNREACH, ICMP_FRAG_NEEDED, htonl(mtu));			ip_rt_put(rt);			goto tx_error;		}	}#ifdef CONFIG_IPV6	else if (skb->protocol == htons(ETH_P_IPV6)) {		struct rt6_info *rt6 = (struct rt6_info*)skb->dst;		if (rt6 && mtu < dst_mtu(skb->dst) && mtu >= IPV6_MIN_MTU) {			if ((tunnel->parms.iph.daddr && !MULTICAST(tunnel->parms.iph.daddr)) ||			    rt6->rt6i_dst.plen == 128) {				rt6->rt6i_flags |= RTF_MODIFIED;				skb->dst->metrics[RTAX_MTU-1] = mtu;			}		}		if (mtu >= IPV6_MIN_MTU && mtu < skb->len - tunnel->hlen + gre_hlen) {			icmpv6_send(skb, ICMPV6_PKT_TOOBIG, 0, mtu, dev);			ip_rt_put(rt);			goto tx_error;		}	}#endif	if (tunnel->err_count > 0) {		if (jiffies - tunnel->err_time < IPTUNNEL_ERR_TIMEO) {			tunnel->err_count--;			dst_link_failure(skb);		} else			tunnel->err_count = 0;	}	max_headroom = LL_RESERVED_SPACE(tdev) + gre_hlen;	if (skb_headroom(skb) < max_headroom || skb_shared(skb)||	    (skb_cloned(skb) && !skb_clone_writable(skb, 0))) {		struct sk_buff *new_skb = skb_realloc_headroom(skb, max_headroom);		if (!new_skb) {			ip_rt_put(rt);			stats->tx_dropped++;			dev_kfree_skb(skb);			tunnel->recursion--;			return 0;		}		if (skb->sk)			skb_set_owner_w(new_skb, skb->sk);		dev_kfree_skb(skb);		skb = new_skb;		old_iph = ip_hdr(skb);	}	skb->transport_header = skb->network_header;	skb_push(skb, gre_hlen);	skb_reset_network_header(skb);	memset(&(IPCB(skb)->opt), 0, sizeof(IPCB(skb)->opt));	IPCB(skb)->flags &= ~(IPSKB_XFRM_TUNNEL_SIZE | IPSKB_XFRM_TRANSFORMED |			      IPSKB_REROUTED);	dst_release(skb->dst);	skb->dst = &rt->u.dst;	/*	 *	Push down and install the IPIP header.	 */	iph 			=	ip_hdr(skb);	iph->version		=	4;	iph->ihl		=	sizeof(struct iphdr) >> 2;	iph->frag_off		=	df;	iph->protocol		=	IPPROTO_GRE;	iph->tos		=	ipgre_ecn_encapsulate(tos, old_iph, skb);	iph->daddr		=	rt->rt_dst;	iph->saddr		=	rt->rt_src;	if ((iph->ttl = tiph->ttl) == 0) {		if (skb->protocol == htons(ETH_P_IP))			iph->ttl = old_iph->ttl;#ifdef CONFIG_IPV6		else if (skb->protocol == htons(ETH_P_IPV6))			iph->ttl = ((struct ipv6hdr*)old_iph)->hop_limit;#endif		else			iph->ttl = dst_metric(&rt->u.dst, RTAX_HOPLIMIT);	}	((__be16*)(iph+1))[0] = tunnel->parms.o_flags;	((__be16*)(iph+1))[1] = skb->protocol;	if (tunnel->parms.o_flags&(GRE_KEY|GRE_CSUM|GRE_SEQ)) {		__be32 *ptr = (__be32*)(((u8*)iph) + tunnel->hlen - 4);		if (tunnel->parms.o_flags&GRE_SEQ) {			++tunnel->o_seqno;			*ptr = htonl(tunnel->o_seqno);			ptr--;		}		if (tunnel->parms.o_flags&GRE_KEY) {			*ptr = tunnel->parms.o_key;			ptr--;		}		if (tunnel->parms.o_flags&GRE_CSUM) {			*ptr = 0;			*(__sum16*)ptr = ip_compute_csum((void*)(iph+1), skb->len - sizeof(struct iphdr));		}	}

⌨️ 快捷键说明

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