ip6_output.c

来自「eCos操作系统源码」· C语言 代码 · 共 2,527 行 · 第 1/5 页

C
2,527
字号
	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;		}		if (rt == NULL) {			/*			 * If in6_selectroute() does not return a route entry,			 * dst may not have been updated. 			 */			*dst = dst0;	/* XXX */		}		/*		 * then rt (for unicast) and ifp must be non-NULL valid values.		 */		if ((flags & IPV6_FORWARDING) == 0) {			/* XXX: the FORWARDING flag can be set for mrouting. */			in6_ifstat_inc(ifp, ifs6_out_request);		}		if (rt != NULL) {			ia = (struct in6_ifaddr *)(rt->rt_ifa);			rt->rt_use++;		}		/*		 * The outgoing interface must be in the zone of source and		 * destination addresses.  We should use ia_ifp to support the		 * case of sending packets to an address of our own.		 */		if (ia != NULL && ia->ia_ifp)			ifp0 = ia->ia_ifp;		else			ifp0 = ifp;		/* XXX: we should not do this conversion for every packet. */		bzero(&src, sizeof(src));		src.sin6_family = AF_INET6;		src.sin6_len = sizeof(src);		src.sin6_addr = ip6->ip6_src;		if ((error = in6_recoverscope(&src, &ip6->ip6_src, NULL))		    != 0) {			goto badscope;		}		if ((zone = in6_addr2zoneid(ifp0, &src.sin6_addr)) < 0 ||		    zone != src.sin6_scope_id) {#ifdef SCOPEDEBUG		/* will be removed shortly */			printf("ip6 output: bad source scope %s for %s on %s\n",			       ip6_sprintf(&ip6->ip6_src),			       ip6_sprintf(&ip6->ip6_dst), if_name(ifp0));#endif

⌨️ 快捷键说明

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