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

📄 ip_gre.c

📁 ARM 嵌入式 系统 设计与实例开发 实验教材 二源码
💻 C
📖 第 1 页 / 共 3 页
字号:
	default:		return;	case ICMP_PARAMETERPROB:		if (skb->h.icmph->un.gateway < (iph->ihl<<2))			return;		/* So... This guy found something strange INSIDE encapsulated		   packet. Well, he is fool, but what can we do ?		 */		rel_type = ICMP_PARAMETERPROB;		rel_info = skb->h.icmph->un.gateway - grehlen;		break;	case ICMP_DEST_UNREACH:		switch (code) {		case ICMP_SR_FAILED:		case ICMP_PORT_UNREACH:			/* Impossible event. */			return;		case ICMP_FRAG_NEEDED:			/* And it is the only really necesary thing :-) */			rel_info = ntohs(skb->h.icmph->un.frag.mtu);			if (rel_info < grehlen+68)				return;			rel_info -= grehlen;			/* BSD 4.2 MORE DOES NOT EXIST IN NATURE. */			if (rel_info > ntohs(eiph->tot_len))				return;			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);	skb2->nh.raw = skb2->data;	/* Try to guess incoming interface */	if (ip_route_output(&rt, eiph->saddr, 0, RT_TOS(eiph->tos), 0)) {		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;		if (ip_route_output(&rt, eiph->daddr, eiph->saddr, eiph->tos, 0) ||		    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 (rel_info > skb2->dst->pmtu) {			kfree_skb(skb2);			return;		}		skb2->dst->pmtu = rel_info;		rel_info = htonl(rel_info);	} else if (type == ICMP_TIME_EXCEEDED) {		struct ip_tunnel *t = (struct ip_tunnel*)skb2->dev->priv;		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 == __constant_htons(ETH_P_IP)) {			if (INET_ECN_is_not_ce(skb->nh.iph->tos))				IP_ECN_set_ce(skb->nh.iph);		} else if (skb->protocol == __constant_htons(ETH_P_IPV6)) {			if (INET_ECN_is_not_ce(ip6_get_dsfield(skb->nh.ipv6h)))				IP6_ECN_set_ce(skb->nh.ipv6h);		}	}}static inline u8ipgre_ecn_encapsulate(u8 tos, struct iphdr *old_iph, struct sk_buff *skb){	u8 inner = 0;	if (skb->protocol == __constant_htons(ETH_P_IP))		inner = old_iph->tos;	else if (skb->protocol == __constant_htons(ETH_P_IPV6))		inner = ip6_get_dsfield((struct ipv6hdr*)old_iph);	return INET_ECN_encapsulate(tos, inner);}int ipgre_rcv(struct sk_buff *skb){	struct iphdr *iph;	u8     *h;	u16    flags;	u16    csum = 0;	u32    key = 0;	u32    seqno = 0;	struct ip_tunnel *tunnel;	int    offset = 4;	if (!pskb_may_pull(skb, 16))		goto drop_nolock;	iph = skb->nh.iph;	h = skb->data;	flags = *(u16*)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) {			if (skb->ip_summed == CHECKSUM_HW) {				csum = (u16)csum_fold(skb->csum);				if (csum)					skb->ip_summed = CHECKSUM_NONE;			}			if (skb->ip_summed == CHECKSUM_NONE) {				skb->csum = skb_checksum(skb, 0, skb->len, 0);				skb->ip_summed = CHECKSUM_HW;				csum = (u16)csum_fold(skb->csum);			}			offset += 4;		}		if (flags&GRE_KEY) {			key = *(u32*)(h + offset);			offset += 4;		}		if (flags&GRE_SEQ) {			seqno = ntohl(*(u32*)(h + offset));			offset += 4;		}	}	read_lock(&ipgre_lock);	if ((tunnel = ipgre_tunnel_lookup(iph->saddr, iph->daddr, key)) != NULL) {		skb->mac.raw = skb->nh.raw;		skb->nh.raw = __pskb_pull(skb, offset);		memset(&(IPCB(skb)->opt), 0, sizeof(struct ip_options));		if (skb->ip_summed == CHECKSUM_HW)			skb->csum = csum_sub(skb->csum,					     csum_partial(skb->mac.raw, skb->nh.raw-skb->mac.raw, 0));		skb->protocol = *(u16*)(h + 2);		skb->pkt_type = PACKET_HOST;#ifdef CONFIG_NET_IPGRE_BROADCAST		if (MULTICAST(iph->daddr)) {			/* Looped back packet, drop it! */			if (((struct rtable*)skb->dst)->key.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;#ifdef CONFIG_NETFILTER		nf_conntrack_put(skb->nfct);		skb->nfct = NULL;#ifdef CONFIG_NETFILTER_DEBUG		skb->nf_debug = 0;#endif#endif		ipgre_ecn_decapsulate(iph, skb);		netif_rx(skb);		read_unlock(&ipgre_lock);		return(0);	}	icmp_send(skb, ICMP_DEST_UNREACH, ICMP_PROT_UNREACH, 0);drop:	read_unlock(&ipgre_lock);drop_nolock:	kfree_skb(skb);	return(0);}/* Need this wrapper because NF_HOOK takes the function address */static inline int do_ip_send(struct sk_buff *skb){	return ip_send(skb);}static int ipgre_tunnel_xmit(struct sk_buff *skb, struct net_device *dev){	struct ip_tunnel *tunnel = (struct ip_tunnel*)dev->priv;	struct net_device_stats *stats = &tunnel->stat;	struct iphdr  *old_iph = skb->nh.iph;	struct iphdr  *tiph;	u8     tos;	u16    df;	struct rtable *rt;     			/* Route to the other host */	struct net_device *tdev;			/* Device to other host */	struct iphdr  *iph;			/* Our new IP header */	int    max_headroom;			/* The extra header space needed */	int    gre_hlen;	u32    dst;	int    mtu;	if (tunnel->recursion++) {		tunnel->stat.collisions++;		goto tx_error;	}	if (dev->hard_header) {		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 == __constant_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 == __constant_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 = &skb->nh.ipv6h->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 == __constant_htons(ETH_P_IP))			tos = old_iph->tos;		tos &= ~1;	}	if (ip_route_output(&rt, dst, tiph->saddr, RT_TOS(tos), tunnel->parms.link)) {		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;	mtu = rt->u.dst.pmtu - tunnel->hlen;	if (skb->protocol == __constant_htons(ETH_P_IP)) {		if (skb->dst && mtu < skb->dst->pmtu && mtu >= 68)			skb->dst->pmtu = mtu;		df |= (old_iph->frag_off&__constant_htons(IP_DF));		if ((old_iph->frag_off&__constant_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 == __constant_htons(ETH_P_IPV6)) {		struct rt6_info *rt6 = (struct rt6_info*)skb->dst;		if (rt6 && mtu < rt6->u.dst.pmtu && 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->pmtu = 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;	}	skb->h.raw = skb->nh.raw;	max_headroom = ((tdev->hard_header_len+15)&~15)+ gre_hlen;	if (skb_headroom(skb) < max_headroom || skb_cloned(skb) || skb_shared(skb)) {		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;	}	skb->nh.raw = skb_push(skb, gre_hlen);	memset(&(IPCB(skb)->opt), 0, sizeof(IPCB(skb)->opt));	dst_release(skb->dst);	skb->dst = &rt->u.dst;	/*	 *	Push down and install the IPIP header.	 */	iph 			=	skb->nh.iph;	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 == __constant_htons(ETH_P_IP))			iph->ttl = old_iph->ttl;#ifdef CONFIG_IPV6		else if (skb->protocol == __constant_htons(ETH_P_IPV6))			iph->ttl = ((struct ipv6hdr*)old_iph)->hop_limit;#endif		else			iph->ttl = sysctl_ip_default_ttl;	}	((u16*)(iph+1))[0] = tunnel->parms.o_flags;	((u16*)(iph+1))[1] = skb->protocol;	if (tunnel->parms.o_flags&(GRE_KEY|GRE_CSUM|GRE_SEQ)) {		u32 *ptr = (u32*)(((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) {

⌨️ 快捷键说明

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