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

📄 ip6_tunnel.c

📁 linux 内核源代码
💻 C
📖 第 1 页 / 共 3 页
字号:
	struct iphdr *eiph;	struct flowi fl;	struct rtable *rt;	err = ip6_tnl_err(skb, IPPROTO_IPIP, opt, &rel_type, &rel_code,			  &rel_msg, &rel_info, offset);	if (err < 0)		return err;	if (rel_msg == 0)		return 0;	switch (rel_type) {	case ICMPV6_DEST_UNREACH:		if (rel_code != ICMPV6_ADDR_UNREACH)			return 0;		rel_type = ICMP_DEST_UNREACH;		rel_code = ICMP_HOST_UNREACH;		break;	case ICMPV6_PKT_TOOBIG:		if (rel_code != 0)			return 0;		rel_type = ICMP_DEST_UNREACH;		rel_code = ICMP_FRAG_NEEDED;		break;	default:		return 0;	}	if (!pskb_may_pull(skb, offset + sizeof(struct iphdr)))		return 0;	skb2 = skb_clone(skb, GFP_ATOMIC);	if (!skb2)		return 0;	dst_release(skb2->dst);	skb2->dst = NULL;	skb_pull(skb2, offset);	skb_reset_network_header(skb2);	eiph = ip_hdr(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_IPIP;	if (ip_route_output_key(&rt, &fl))		goto out;	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_TUNNEL) {			ip_rt_put(rt);			goto out;		}	} else {		ip_rt_put(rt);		if (ip_route_input(skb2, eiph->daddr, eiph->saddr, eiph->tos,				   skb2->dev) ||		    skb2->dst->dev->type != ARPHRD_TUNNEL)			goto out;	}	/* change mtu on this route */	if (rel_type == ICMP_DEST_UNREACH && rel_code == ICMP_FRAG_NEEDED) {		if (rel_info > dst_mtu(skb2->dst))			goto out;		skb2->dst->ops->update_pmtu(skb2->dst, rel_info);	}	icmp_send(skb2, rel_type, rel_code, htonl(rel_info));out:	kfree_skb(skb2);	return 0;}static intip6ip6_err(struct sk_buff *skb, struct inet6_skb_parm *opt,	   int type, int code, int offset, __be32 info){	int rel_msg = 0;	int rel_type = type;	int rel_code = code;	__u32 rel_info = ntohl(info);	int err;	err = ip6_tnl_err(skb, IPPROTO_IPV6, opt, &rel_type, &rel_code,			  &rel_msg, &rel_info, offset);	if (err < 0)		return err;	if (rel_msg && pskb_may_pull(skb, offset + sizeof(struct ipv6hdr))) {		struct rt6_info *rt;		struct sk_buff *skb2 = skb_clone(skb, GFP_ATOMIC);		if (!skb2)			return 0;		dst_release(skb2->dst);		skb2->dst = NULL;		skb_pull(skb2, offset);		skb_reset_network_header(skb2);		/* Try to guess incoming interface */		rt = rt6_lookup(&ipv6_hdr(skb2)->saddr, NULL, 0, 0);		if (rt && rt->rt6i_dev)			skb2->dev = rt->rt6i_dev;		icmpv6_send(skb2, rel_type, rel_code, rel_info, skb2->dev);		if (rt)			dst_release(&rt->u.dst);		kfree_skb(skb2);	}	return 0;}static void ip4ip6_dscp_ecn_decapsulate(struct ip6_tnl *t,					struct ipv6hdr *ipv6h,					struct sk_buff *skb){	__u8 dsfield = ipv6_get_dsfield(ipv6h) & ~INET_ECN_MASK;	if (t->parms.flags & IP6_TNL_F_RCV_DSCP_COPY)		ipv4_change_dsfield(ip_hdr(skb), INET_ECN_MASK, dsfield);	if (INET_ECN_is_ce(dsfield))		IP_ECN_set_ce(ip_hdr(skb));}static void ip6ip6_dscp_ecn_decapsulate(struct ip6_tnl *t,					struct ipv6hdr *ipv6h,					struct sk_buff *skb){	if (t->parms.flags & IP6_TNL_F_RCV_DSCP_COPY)		ipv6_copy_dscp(ipv6h, ipv6_hdr(skb));	if (INET_ECN_is_ce(ipv6_get_dsfield(ipv6h)))		IP6_ECN_set_ce(ipv6_hdr(skb));}static inline int ip6_tnl_rcv_ctl(struct ip6_tnl *t){	struct ip6_tnl_parm *p = &t->parms;	int ret = 0;	if (p->flags & IP6_TNL_F_CAP_RCV) {		struct net_device *ldev = NULL;		if (p->link)			ldev = dev_get_by_index(&init_net, p->link);		if ((ipv6_addr_is_multicast(&p->laddr) ||		     likely(ipv6_chk_addr(&p->laddr, ldev, 0))) &&		    likely(!ipv6_chk_addr(&p->raddr, NULL, 0)))			ret = 1;		if (ldev)			dev_put(ldev);	}	return ret;}/** * ip6_tnl_rcv - decapsulate IPv6 packet and retransmit it locally *   @skb: received socket buffer *   @protocol: ethernet protocol ID *   @dscp_ecn_decapsulate: the function to decapsulate DSCP code and ECN * * Return: 0 **/static int ip6_tnl_rcv(struct sk_buff *skb, __u16 protocol,		       __u8 ipproto,		       void (*dscp_ecn_decapsulate)(struct ip6_tnl *t,						    struct ipv6hdr *ipv6h,						    struct sk_buff *skb)){	struct ip6_tnl *t;	struct ipv6hdr *ipv6h = ipv6_hdr(skb);	read_lock(&ip6_tnl_lock);	if ((t = ip6_tnl_lookup(&ipv6h->saddr, &ipv6h->daddr)) != NULL) {		if (t->parms.proto != ipproto && t->parms.proto != 0) {			read_unlock(&ip6_tnl_lock);			goto discard;		}		if (!xfrm6_policy_check(NULL, XFRM_POLICY_IN, skb)) {			read_unlock(&ip6_tnl_lock);			goto discard;		}		if (!ip6_tnl_rcv_ctl(t)) {			t->stat.rx_dropped++;			read_unlock(&ip6_tnl_lock);			goto discard;		}		secpath_reset(skb);		skb->mac_header = skb->network_header;		skb_reset_network_header(skb);		skb->protocol = htons(protocol);		skb->pkt_type = PACKET_HOST;		memset(skb->cb, 0, sizeof(struct inet6_skb_parm));		skb->dev = t->dev;		dst_release(skb->dst);		skb->dst = NULL;		nf_reset(skb);		dscp_ecn_decapsulate(t, ipv6h, skb);		t->stat.rx_packets++;		t->stat.rx_bytes += skb->len;		netif_rx(skb);		read_unlock(&ip6_tnl_lock);		return 0;	}	read_unlock(&ip6_tnl_lock);	return 1;discard:	kfree_skb(skb);	return 0;}static int ip4ip6_rcv(struct sk_buff *skb){	return ip6_tnl_rcv(skb, ETH_P_IP, IPPROTO_IPIP,			   ip4ip6_dscp_ecn_decapsulate);}static int ip6ip6_rcv(struct sk_buff *skb){	return ip6_tnl_rcv(skb, ETH_P_IPV6, IPPROTO_IPV6,			   ip6ip6_dscp_ecn_decapsulate);}struct ipv6_tel_txoption {	struct ipv6_txoptions ops;	__u8 dst_opt[8];};static void init_tel_txopt(struct ipv6_tel_txoption *opt, __u8 encap_limit){	memset(opt, 0, sizeof(struct ipv6_tel_txoption));	opt->dst_opt[2] = IPV6_TLV_TNL_ENCAP_LIMIT;	opt->dst_opt[3] = 1;	opt->dst_opt[4] = encap_limit;	opt->dst_opt[5] = IPV6_TLV_PADN;	opt->dst_opt[6] = 1;	opt->ops.dst0opt = (struct ipv6_opt_hdr *) opt->dst_opt;	opt->ops.opt_nflen = 8;}/** * ip6_tnl_addr_conflict - compare packet addresses to tunnel's own *   @t: the outgoing tunnel device *   @hdr: IPv6 header from the incoming packet * * Description: *   Avoid trivial tunneling loop by checking that tunnel exit-point *   doesn't match source of incoming packet. * * Return: *   1 if conflict, *   0 else **/static inline intip6_tnl_addr_conflict(struct ip6_tnl *t, struct ipv6hdr *hdr){	return ipv6_addr_equal(&t->parms.raddr, &hdr->saddr);}static inline int ip6_tnl_xmit_ctl(struct ip6_tnl *t){	struct ip6_tnl_parm *p = &t->parms;	int ret = 0;	if (p->flags & IP6_TNL_F_CAP_XMIT) {		struct net_device *ldev = NULL;		if (p->link)			ldev = dev_get_by_index(&init_net, p->link);		if (unlikely(!ipv6_chk_addr(&p->laddr, ldev, 0)))			printk(KERN_WARNING			       "%s xmit: Local address not yet configured!\n",			       p->name);		else if (!ipv6_addr_is_multicast(&p->raddr) &&			 unlikely(ipv6_chk_addr(&p->raddr, NULL, 0)))			printk(KERN_WARNING			       "%s xmit: Routing loop! "			       "Remote address found on this node!\n",			       p->name);		else			ret = 1;		if (ldev)			dev_put(ldev);	}	return ret;}/** * ip6_tnl_xmit2 - encapsulate packet and send *   @skb: the outgoing socket buffer *   @dev: the outgoing tunnel device *   @dsfield: dscp code for outer header *   @fl: flow of tunneled packet *   @encap_limit: encapsulation limit *   @pmtu: Path MTU is stored if packet is too big * * Description: *   Build new header and do some sanity checks on the packet before sending *   it. * * Return: *   0 on success *   -1 fail *   %-EMSGSIZE message too big. return mtu in this case. **/static int ip6_tnl_xmit2(struct sk_buff *skb,			 struct net_device *dev,			 __u8 dsfield,			 struct flowi *fl,			 int encap_limit,			 __u32 *pmtu){	struct ip6_tnl *t = netdev_priv(dev);	struct net_device_stats *stats = &t->stat;	struct ipv6hdr *ipv6h = ipv6_hdr(skb);	struct ipv6_tel_txoption opt;	struct dst_entry *dst;	struct net_device *tdev;	int mtu;	unsigned int max_headroom = sizeof(struct ipv6hdr);	u8 proto;	int err = -1;	int pkt_len;	if ((dst = ip6_tnl_dst_check(t)) != NULL)		dst_hold(dst);	else {		dst = ip6_route_output(NULL, fl);		if (dst->error || xfrm_lookup(&dst, fl, NULL, 0) < 0)			goto tx_err_link_failure;	}	tdev = dst->dev;	if (tdev == dev) {		stats->collisions++;		if (net_ratelimit())			printk(KERN_WARNING			       "%s: Local routing loop detected!\n",			       t->parms.name);		goto tx_err_dst_release;	}	mtu = dst_mtu(dst) - sizeof (*ipv6h);	if (encap_limit >= 0) {		max_headroom += 8;		mtu -= 8;	}	if (mtu < IPV6_MIN_MTU)		mtu = IPV6_MIN_MTU;	if (skb->dst)		skb->dst->ops->update_pmtu(skb->dst, mtu);	if (skb->len > mtu) {		*pmtu = mtu;		err = -EMSGSIZE;		goto tx_err_dst_release;	}	/*	 * Okay, now see if we can stuff it in the buffer as-is.	 */	max_headroom += LL_RESERVED_SPACE(tdev);	if (skb_headroom(skb) < max_headroom || skb_shared(skb) ||	    (skb_cloned(skb) && !skb_clone_writable(skb, 0))) {		struct sk_buff *new_skb;		if (!(new_skb = skb_realloc_headroom(skb, max_headroom)))			goto tx_err_dst_release;		if (skb->sk)			skb_set_owner_w(new_skb, skb->sk);		kfree_skb(skb);		skb = new_skb;	}	dst_release(skb->dst);	skb->dst = dst_clone(dst);	skb->transport_header = skb->network_header;	proto = fl->proto;	if (encap_limit >= 0) {		init_tel_txopt(&opt, encap_limit);		ipv6_push_nfrag_opts(skb, &opt.ops, &proto, NULL);	}	skb_push(skb, sizeof(struct ipv6hdr));	skb_reset_network_header(skb);	ipv6h = ipv6_hdr(skb);	*(__be32*)ipv6h = fl->fl6_flowlabel | htonl(0x60000000);	dsfield = INET_ECN_encapsulate(0, dsfield);	ipv6_change_dsfield(ipv6h, ~INET_ECN_MASK, dsfield);	ipv6h->payload_len = htons(skb->len - sizeof(struct ipv6hdr));	ipv6h->hop_limit = t->parms.hop_limit;	ipv6h->nexthdr = proto;	ipv6_addr_copy(&ipv6h->saddr, &fl->fl6_src);	ipv6_addr_copy(&ipv6h->daddr, &fl->fl6_dst);	nf_reset(skb);	pkt_len = skb->len;	err = NF_HOOK(PF_INET6, NF_IP6_LOCAL_OUT, skb, NULL,		      skb->dst->dev, dst_output);	if (net_xmit_eval(err) == 0) {		stats->tx_bytes += pkt_len;		stats->tx_packets++;	} else {		stats->tx_errors++;		stats->tx_aborted_errors++;	}	ip6_tnl_dst_store(t, dst);	return 0;tx_err_link_failure:	stats->tx_carrier_errors++;	dst_link_failure(skb);tx_err_dst_release:	dst_release(dst);	return err;}static inline intip4ip6_tnl_xmit(struct sk_buff *skb, struct net_device *dev){	struct ip6_tnl *t = netdev_priv(dev);	struct iphdr  *iph = ip_hdr(skb);	int encap_limit = -1;	struct flowi fl;	__u8 dsfield;	__u32 mtu;	int err;	if ((t->parms.proto != IPPROTO_IPIP && t->parms.proto != 0) ||	    !ip6_tnl_xmit_ctl(t))		return -1;	if (!(t->parms.flags & IP6_TNL_F_IGN_ENCAP_LIMIT))		encap_limit = t->parms.encap_limit;	memcpy(&fl, &t->fl, sizeof (fl));	fl.proto = IPPROTO_IPIP;	dsfield = ipv4_get_dsfield(iph);	if ((t->parms.flags & IP6_TNL_F_USE_ORIG_TCLASS))		fl.fl6_flowlabel |= htonl((__u32)iph->tos << IPV6_TCLASS_SHIFT)					  & IPV6_TCLASS_MASK;	err = ip6_tnl_xmit2(skb, dev, dsfield, &fl, encap_limit, &mtu);	if (err != 0) {		/* XXX: send ICMP error even if DF is not set. */		if (err == -EMSGSIZE)			icmp_send(skb, ICMP_DEST_UNREACH, ICMP_FRAG_NEEDED,				  htonl(mtu));		return -1;	}	return 0;

⌨️ 快捷键说明

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