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

📄 ip6_output.c

📁 嵌入式操作系统ECOS的网络开发包
💻 C
📖 第 1 页 / 共 5 页
字号:
	/* check policy */
	switch (sp->policy) {
	case IPSEC_POLICY_DISCARD:
		/*
		 * This packet is just discarded.
		 */
		ipsec6stat.out_polvio++;
		goto freehdrs;

	case IPSEC_POLICY_BYPASS:
	case IPSEC_POLICY_NONE:
		/* no need to do IPsec. */
		needipsec = 0;
		break;
	
	case IPSEC_POLICY_IPSEC:
		if (sp->req == NULL) {
			/* acquire a policy */
			error = key_spdacquire(sp);
			goto freehdrs;
		}
		needipsec = 1;
		break;

	case IPSEC_POLICY_ENTRUST:
	default:
		printf("ip6_output: Invalid policy found. %d\n", sp->policy);
	}
#endif /* OpenBSD */
#endif /* IPSEC */

	/*
	 * Calculate the total length of the extension header chain.
	 * Keep the length of the unfragmentable part for fragmentation.
	 */
	optlen = 0;
	if (exthdrs.ip6e_hbh) optlen += exthdrs.ip6e_hbh->m_len;
	if (exthdrs.ip6e_dest1) optlen += exthdrs.ip6e_dest1->m_len;
	if (exthdrs.ip6e_rthdr) optlen += exthdrs.ip6e_rthdr->m_len;
#ifdef MIP6
	if (exthdrs.ip6e_haddr) optlen += exthdrs.ip6e_haddr->m_len;
#endif /* MIP6 */
	unfragpartlen = optlen + sizeof(struct ip6_hdr);
	/* NOTE: we don't add AH/ESP length here. do that later. */
	if (exthdrs.ip6e_dest2) optlen += exthdrs.ip6e_dest2->m_len;

	/*
	 * If we need IPsec, or there is at least one extension header,
	 * separate IP6 header from the payload.
	 */
#ifdef __OpenBSD__
	if ((sproto || optlen) && !hdrsplit)
#else
	if ((needipsec || optlen) && !hdrsplit)
#endif
	{
		if ((error = ip6_splithdr(m, &exthdrs)) != 0) {
			m = NULL;
			goto freehdrs;
		}
		m = exthdrs.ip6e_ip6;
		hdrsplit++;
	}

	/* adjust pointer */
	ip6 = mtod(m, struct ip6_hdr *);

	/* adjust mbuf packet header length */
	m->m_pkthdr.len += optlen;
	plen = m->m_pkthdr.len - sizeof(*ip6);

	/* If this is a jumbo payload, insert a jumbo payload option. */
	if (plen > IPV6_MAXPACKET) {
		if (!hdrsplit) {
			if ((error = ip6_splithdr(m, &exthdrs)) != 0) {
				m = NULL;
				goto freehdrs;
			}
			m = exthdrs.ip6e_ip6;
			hdrsplit++;
		}
		/* adjust pointer */
		ip6 = mtod(m, struct ip6_hdr *);
		if ((error = ip6_insert_jumboopt(&exthdrs, plen)) != 0)
			goto freehdrs;
		ip6->ip6_plen = 0;
	} else
		ip6->ip6_plen = htons(plen);

	/*
	 * Concatenate headers and fill in next header fields.
	 * Here we have, on "m"
	 *	IPv6 payload
	 * and we insert headers accordingly.  Finally, we should be getting:
	 *	IPv6 hbh dest1 rthdr ah* [esp* dest2 payload]
	 *
	 * during the header composing process, "m" points to IPv6 header.
	 * "mprev" points to an extension header prior to esp.
	 */
	{
		u_char *nexthdrp = &ip6->ip6_nxt;
		struct mbuf *mprev = m;

		/*
		 * we treat dest2 specially.  this makes IPsec processing
		 * much easier.  the goal here is to make mprev point the
		 * mbuf prior to dest2.
		 *
		 * result: IPv6 dest2 payload
		 * m and mprev will point to IPv6 header.
		 */
		if (exthdrs.ip6e_dest2) {
			if (!hdrsplit)
				panic("assumption failed: hdr not split");
			exthdrs.ip6e_dest2->m_next = m->m_next;
			m->m_next = exthdrs.ip6e_dest2;
			*mtod(exthdrs.ip6e_dest2, u_char *) = ip6->ip6_nxt;
			ip6->ip6_nxt = IPPROTO_DSTOPTS;
		}

#define MAKE_CHAIN(m, mp, p, i)\
    do {\
	if (m) {\
		if (!hdrsplit) \
			panic("assumption failed: hdr not split"); \
		*mtod((m), u_char *) = *(p);\
		*(p) = (i);\
		p = mtod((m), u_char *);\
		(m)->m_next = (mp)->m_next;\
		(mp)->m_next = (m);\
		(mp) = (m);\
	}\
    } while (0)
		/*
		 * result: IPv6 hbh dest1 rthdr dest2 payload
		 * m will point to IPv6 header.  mprev will point to the
		 * extension header prior to dest2 (rthdr in the above case).
		 */
		MAKE_CHAIN(exthdrs.ip6e_hbh, mprev,
			   nexthdrp, IPPROTO_HOPOPTS);
		MAKE_CHAIN(exthdrs.ip6e_dest1, mprev,
			   nexthdrp, IPPROTO_DSTOPTS);
		MAKE_CHAIN(exthdrs.ip6e_rthdr, mprev,
			   nexthdrp, IPPROTO_ROUTING);
#ifdef MIP6
		/*
		 * XXX
		 * MIP6 homeaddress destination option must reside
		 * after rthdr and before ah/esp/frag hdr.
		 * this order is not recommended in the ipv6 spec of course.
		 * result: IPv6 hbh dest1 rthdr ha dest2 payload.
		 */
		MAKE_CHAIN(exthdrs.ip6e_haddr, mprev,
			   nexthdrp, IPPROTO_DSTOPTS);
#endif /* MIP6 */

#if defined(IPSEC) && !defined(__OpenBSD__)
		if (!needipsec)
			goto skip_ipsec2;

		/*
		 * pointers after IPsec headers are not valid any more.
		 * other pointers need a great care too.
		 * (IPsec routines should not mangle mbufs prior to AH/ESP)
		 */
		exthdrs.ip6e_dest2 = NULL;

	    {
		struct ip6_rthdr *rh = NULL;
		int segleft_org = 0;
		struct ipsec_output_state state;

		if (exthdrs.ip6e_rthdr) {
			rh = mtod(exthdrs.ip6e_rthdr, struct ip6_rthdr *);
			segleft_org = rh->ip6r_segleft;
			rh->ip6r_segleft = 0;
		}

		bzero(&state, sizeof(state));
		state.m = m;
		error = ipsec6_output_trans(&state, nexthdrp, mprev, sp, flags,
			&needipsectun);
		m = state.m;
		if (error) {
			/* mbuf is already reclaimed in ipsec6_output_trans. */
			m = NULL;
			switch (error) {
			case EHOSTUNREACH:
			case ENETUNREACH:
			case EMSGSIZE:
			case ENOBUFS:
			case ENOMEM:
				break;
			default:
				printf("ip6_output (ipsec): error code %d\n", error);
				/* fall through */
			case ENOENT:
				/* don't show these error codes to the user */
				error = 0;
				break;
			}
			goto bad;
		}
		if (exthdrs.ip6e_rthdr) {
			/* ah6_output doesn't modify mbuf chain */
			rh->ip6r_segleft = segleft_org;
		}
	    }
skip_ipsec2:;
#endif
	}

#ifdef MIP6
	if ((flags & IPV6_FORWARDING) == 0) {
		/*
		 * After the IPsec processing the IPv6 header source
		 * address (this is the homeaddress of this node) and
		 * the address currently stored in the Home Address
		 * destination option (this is the coa of this node)
		 * must be swapped.
		 */
		if ((error = mip6_addr_exchange(m, exthdrs.ip6e_haddr)) != 0) {
			mip6log((LOG_ERR,
				 "%s:%d: "
				 "addr exchange between haddr and "
				 "coa failed.\n",
				 __FILE__, __LINE__));
			goto bad;
		}
	} else {
		/*
		 * this is the forwarding packet.  The typical (and
		 * only ?) case is multicast packet forwarding.  The
		 * swapping has been already done before (if
		 * necessary).  we must not touch any extension
		 * headers at all.
		 */
	}
#endif /* MIP6 */

	/*
	 * If there is a routing header, replace destination address field
	 * with the first hop of the routing header.
	 */
	if (exthdrs.ip6e_rthdr) {
		struct ip6_rthdr *rh =
			(struct ip6_rthdr *)(mtod(exthdrs.ip6e_rthdr,
						  struct ip6_rthdr *));
		struct ip6_rthdr0 *rh0;
		struct in6_addr *addr;

		finaldst = ip6->ip6_dst;
		switch (rh->ip6r_type) {
		case IPV6_RTHDR_TYPE_0:
			 rh0 = (struct ip6_rthdr0 *)rh;
			 addr = (struct in6_addr *)(rh0 + 1);

			 ip6->ip6_dst = *addr;
			 bcopy((caddr_t)(addr + 1), (caddr_t)addr,
				 sizeof(struct in6_addr) * (rh0->ip6r0_segleft - 1)
				 );
			 *(addr + rh0->ip6r0_segleft - 1) = finaldst;
			 break;
		default:	/* is it possible? */
			 error = EINVAL;
			 goto bad;
		}
	}

	/* Source address validation */
	if (!(flags & IPV6_UNSPECSRC) &&
	    IN6_IS_ADDR_UNSPECIFIED(&ip6->ip6_src)) {
		/*
		 * XXX: we can probably assume validation in the caller, but
		 * we explicitly check the address here for safety.
		 */
		error = EOPNOTSUPP;
		ip6stat.ip6s_badscope++;
		goto bad;
	}
	if (IN6_IS_ADDR_MULTICAST(&ip6->ip6_src)) {
		error = EOPNOTSUPP;
		ip6stat.ip6s_badscope++;
		goto bad;
	}

	ip6stat.ip6s_localout++;

	/*
	 * Route packet.
	 */
	if (ro == 0) {
		ro = &ip6route;
		bzero((caddr_t)ro, sizeof(*ro));
	}
	ro_pmtu = ro;
	if (opt && opt->ip6po_rthdr)
		ro = &opt->ip6po_route;
#ifdef MIP6
	else if (exthdrs.ip6e_rthdr) {
		struct sockaddr_in6 *firsthop;
		struct ip6_hdr *ip6
			= mtod(m, struct ip6_hdr *); /* needed ? */;

		ro = &mip6_ip6route;
		bzero((caddr_t)ro, sizeof(*ro));
		firsthop = (struct sockaddr_in6 *)&ro->ro_dst;
		bzero(firsthop, sizeof(*firsthop));
		firsthop->sin6_family = AF_INET6;
		firsthop->sin6_len = sizeof(struct sockaddr_in6);
		firsthop->sin6_addr = ip6->ip6_dst;
	}
#endif /* MIP6 */
	dst = (struct sockaddr_in6 *)&ro->ro_dst;

#ifdef IPSEC
#ifdef __OpenBSD__
	/*
	 * Check if the packet needs encapsulation.
	 * ipsp_process_packet will never come back to here.
	 */
	if (sproto != 0) {
	        s = splnet();

		/* fill in IPv6 header which would be filled later */
		if (!IN6_IS_ADDR_MULTICAST(&ip6->ip6_dst)) {
			if (opt && opt->ip6po_hlim != -1)
				ip6->ip6_hlim = opt->ip6po_hlim & 0xff;
		} else {
			if (im6o != NULL)
				ip6->ip6_hlim = im6o->im6o_multicast_hlim;
			else
				ip6->ip6_hlim = ip6_defmcasthlim;
			if (opt && opt->ip6po_hlim != -1)
				ip6->ip6_hlim = opt->ip6po_hlim & 0xff;

			/*
			 * XXX what should we do if ip6_hlim == 0 and the packet
			 * gets tunnelled?
			 */
		}

		tdb = gettdb(sspi, &sdst, sproto);
		if (tdb == NULL) {
			splx(s);
			error = EHOSTUNREACH;
			m_freem(m);
			goto done;
		}

		/* Latch to PCB */
		if (inp)
			tdb_add_inp(tdb, inp, 0);

		m->m_flags &= ~(M_BCAST | M_MCAST);	/* just in case */

		/* Callee frees mbuf */
		error = ipsp_process_packet(m, tdb, AF_INET6, 0);
		splx(s);

		return error;  /* Nothing more to be done */
	}
#else
	if (needipsec && needipsectun) {
		struct ipsec_output_state state;

		/*
		 * All the extension headers will become inaccessible
		 * (since they can be encrypted).
		 * Don't panic, we need no more updates to extension headers
		 * on inner IPv6 packet (since they are now encapsulated).
		 *
		 * IPv6 [ESP|AH] IPv6 [extension headers] payload
		 */
		bzero(&exthdrs, sizeof(exthdrs));
		exthdrs.ip6e_ip6 = m;

		bzero(&state, sizeof(state));
		state.m = m;
		state.ro = (struct route *)ro;
		state.dst = (struct sockaddr *)dst;

		error = ipsec6_output_tunnel(&state, sp, flags);

		m = state.m;
#ifdef NEW_STRUCT_ROUTE
		ro = state.ro;
#else
		ro = (struct route_in6 *)state.ro;
#endif
		dst = (struct sockaddr_in6 *)state.dst;
		if (error) {
			/* mbuf is already reclaimed in ipsec6_output_tunnel. */
			m0 = m = NULL;
			m = NULL;
			switch (error) {
			case EHOSTUNREACH:
			case ENETUNREACH:
			case EMSGSIZE:
			case ENOBUFS:
			case ENOMEM:
				break;
			default:
				printf("ip6_output (ipsec): error code %d\n", error);
				/* fall through */
			case ENOENT:
				/* don't show these error codes to the user */
				error = 0;
				break;
			}
			goto bad;
		}

		exthdrs.ip6e_ip6 = m;
	}
#endif /* OpenBSD */
#endif /* IPSEC */

	/* if specified, fill in the traffic class field. */
	if (opt) {
		ip6->ip6_flow &= ~htonl(0xff << 20);
		if (opt->ip6po_tclass >= 0)
			ip6->ip6_flow |=
			    htonl((opt->ip6po_tclass & 0xff) << 20);
	}
	/* fill in or override the hop limit field, if necessary. */
	if (opt && opt->ip6po_hlim != -1)
		ip6->ip6_hlim = opt->ip6po_hlim & 0xff;
	else if (IN6_IS_ADDR_MULTICAST(&ip6->ip6_dst)) {
		if (im6o != NULL)
			ip6->ip6_hlim = im6o->im6o_multicast_hlim;
		else
			ip6->ip6_hlim = ip6_defmcasthlim;
	}

	{
		/*
		 * XXX: using a block just to define a local variables is not
		 * a good style....
		 */
		struct ifnet *ifp0 = NULL;
		struct sockaddr_in6 src;
		struct sockaddr_in6 dst0;
		int clone = 0;
		int64_t zone;

		/*
		 * XXX: sockaddr_in6 for the destination should be passed
		 * from the upper layer with a proper scope zone ID, in order
		 * to make a copy here. 
		 */
		bzero(&dst0, sizeof(dst0));
		dst0.sin6_family = AF_INET6;
		dst0.sin6_len = sizeof(dst0);
		dst0.sin6_addr = ip6->ip6_dst;
#ifdef SCOPEDROUTING
		/* XXX: in6_recoverscope will clear the embedded ID */
		error = in6_recoverscope(&dst0, &dst0.sin6_addr, NULL);
		if (error != 0) {
			ip6stat.ip6s_badscope++;
			in6_ifstat_inc(ifp, ifs6_out_discard);
			goto bad;
		}
#endif

#if defined(__bsdi__) || defined(__FreeBSD__)
		if (ro != &ip6route && !IN6_IS_ADDR_MULTICAST(&ip6->ip6_dst))
			clone = 1;
#endif

		if ((error = in6_selectroute(&dst0, opt, im6o, ro,
					     &ifp, &rt, clone)) != 0) {
			switch (error) {
			case EHOSTUNREACH:
				ip6stat.ip6s_noroute++;
				break;
			case EADDRNOTAVAIL:
			default:
				break; /* XXX statistics? */
			}
			if (ifp != NULL)
				in6_ifstat_inc(ifp, ifs6_out_discard);
			goto bad;

⌨️ 快捷键说明

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