ip6_output.c

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

C
2,527
字号
			goto badscope;		}		/* XXX: in6_recoverscope will clear the embedded ID */		if ((error = in6_recoverscope(&dst0, &ip6->ip6_dst, NULL))		    != 0) {			goto badscope;		}		if ((zone = in6_addr2zoneid(ifp0, &dst0.sin6_addr)) < 0 ||		    zone != dst0.sin6_scope_id) {#ifdef SCOPEDEBUG		/* will be removed shortly */			printf("ip6 output: bad dst scope %s on %s\n",			       ip6_sprintf(&dst0.sin6_addr), if_name(ifp0));#endif			goto badscope;		}		/* scope check is done. */		goto routefound;	  badscope:		ip6stat.ip6s_badscope++;		in6_ifstat_inc(ifp0, ifs6_out_discard);		if (error == 0)			error = EHOSTUNREACH; /* XXX */		goto bad;	}  routefound:	if (rt) {		if (opt && opt->ip6po_nextroute.ro_rt) {			/*			 * The nexthop is explicitly specified by the			 * application.  We assume the next hop is an IPv6			 * address.			 */			dst = (struct sockaddr_in6 *)opt->ip6po_nexthop;		}		else if ((rt->rt_flags & RTF_GATEWAY))			dst = (struct sockaddr_in6 *)rt->rt_gateway;	}	if (!IN6_IS_ADDR_MULTICAST(&ip6->ip6_dst)) {		m->m_flags &= ~(M_BCAST | M_MCAST); /* just in case */	} else {		struct	in6_multi *in6m;		m->m_flags = (m->m_flags & ~M_BCAST) | M_MCAST;		in6_ifstat_inc(ifp, ifs6_out_mcast);		/*		 * Confirm that the outgoing interface supports multicast.		 */		if (!(ifp->if_flags & IFF_MULTICAST)) {			ip6stat.ip6s_noroute++;			in6_ifstat_inc(ifp, ifs6_out_discard);			error = ENETUNREACH;			goto bad;		}		IN6_LOOKUP_MULTI(ip6->ip6_dst, ifp, in6m);		if (in6m != NULL &&		   (im6o == NULL || im6o->im6o_multicast_loop)) {			/*			 * If we belong to the destination multicast group			 * on the outgoing interface, and the caller did not			 * forbid loopback, loop back a copy.			 */			ip6_mloopback(ifp, m, dst);		} else {			/*			 * If we are acting as a multicast router, perform			 * multicast forwarding as if the packet had just			 * arrived on the interface to which we are about			 * to send.  The multicast forwarding function			 * recursively calls this function, using the			 * IPV6_FORWARDING flag to prevent infinite recursion.			 *			 * Multicasts that are looped back by ip6_mloopback(),			 * above, will be forwarded by the ip6_input() routine,			 * if necessary.			 */			if (ip6_mrouter && (flags & IPV6_FORWARDING) == 0) {				/*				 * XXX: ip6_mforward expects that rcvif is NULL				 * when it is called from the originating path.				 * However, it is not always the case, since				 * some versions of MGETHDR() does not				 * initialize the field.  				 */				m->m_pkthdr.rcvif = NULL;				if (ip6_mforward(ip6, ifp, m) != 0) {					m_freem(m);					goto done;				}			}		}		/*		 * Multicasts with a hoplimit of zero may be looped back,		 * above, but must not be transmitted on a network.		 * Also, multicasts addressed to the loopback interface		 * are not sent -- the above call to ip6_mloopback() will		 * loop back a copy if this host actually belongs to the		 * destination group on the loopback interface.		 */		if (ip6->ip6_hlim == 0 || (ifp->if_flags & IFF_LOOPBACK) ||		    IN6_IS_ADDR_MC_INTFACELOCAL(&ip6->ip6_dst)) {			m_freem(m);			goto done;		}	}	/*	 * Fill the outgoing inteface to tell the upper layer	 * to increment per-interface statistics.	 */	if (ifpp)		*ifpp = ifp;	/*	 * Upper-layer reachability confirmation	 */	if (opt && (opt->ip6po_flags & IP6PO_REACHCONF))		nd6_nud_hint(rt, NULL, 0);	/* Determine path MTU. */	if ((error = ip6_getpmtu(ro_pmtu, ro, ifp, &finaldst, &mtu)) != 0)		goto bad;	/*	 * An advanced API option (IPV6_USE_MIN_MTU) overrides mtu setting.	 * We ignore the specified MTU if it is larger than the already-known	 * path MTU.	 */	if (mtu > IPV6_MMTU && opt && (opt->ip6po_flags & IP6PO_MINMTU))		mtu = IPV6_MMTU;	/* Fake scoped addresses */	if ((ifp->if_flags & IFF_LOOPBACK) != 0) {		/*		 * If source or destination address is a scoped address, and		 * the packet is going to be sent to a loopback interface,		 * we should keep the original interface.		 */		/*		 * XXX: this is a very experimental and temporary solution.		 * We eventually have sockaddr_in6 and use the sin6_scope_id		 * field of the structure here.		 * We rely on the consistency between two scope zone ids		 * of source and destination, which should already be assured.		 * Larger scopes than link will be supported in the future. 		 */		origifp = NULL;		if (IN6_IS_SCOPE_LINKLOCAL(&ip6->ip6_src)) {#if defined(__FreeBSD__) && __FreeBSD__ >= 5			origifp = ifnet_byindex(ntohs(ip6->ip6_src.s6_addr16[1]));#else			origifp = ifindex2ifnet[ntohs(ip6->ip6_src.s6_addr16[1])];#endif		} else if (IN6_IS_SCOPE_LINKLOCAL(&ip6->ip6_dst)) {#if defined(__FreeBSD__) && __FreeBSD__ >= 5			origifp = ifnet_byindex(ntohs(ip6->ip6_dst.s6_addr16[1]));#else			origifp = ifindex2ifnet[ntohs(ip6->ip6_dst.s6_addr16[1])];#endif		} else if (IN6_IS_ADDR_MC_INTFACELOCAL(&ip6->ip6_dst)) {#if defined(__FreeBSD__) && __FreeBSD__ >= 5			origifp = ifnet_byindex(ntohs(ip6->ip6_dst.s6_addr16[1]));#else			origifp = ifindex2ifnet[ntohs(ip6->ip6_dst.s6_addr16[1])];#endif		}		/*		 * XXX: origifp can be NULL even in those two cases above.		 * For example, if we remove the (only) link-local address		 * from the loopback interface, and try to send a link-local		 * address without link-id information.  Then the source		 * address is ::1, and the destination address is the		 * link-local address with its s6_addr16[1] being zero.		 * What is worse, if the packet goes to the loopback interface		 * by a default rejected route, the null pointer would be		 * passed to looutput, and the kernel would hang.		 * The following last resort would prevent such disaster.		 */		if (origifp == NULL)			origifp = ifp;	}	else		origifp = ifp;#ifndef SCOPEDROUTING	/*	 * clear embedded scope identifiers if necessary.	 * in6_clearscope will touch the addresses only when necessary.	 */	in6_clearscope(&ip6->ip6_src);	in6_clearscope(&ip6->ip6_dst);#endif#if defined(IPV6FIREWALL) || (defined(__FreeBSD__) && __FreeBSD__ >= 4)	/*	 * Check with the firewall...	 */#if defined(__FreeBSD__) && __FreeBSD__ >= 4	if (ip6_fw_enable && ip6_fw_chk_ptr) {#else	if (ip6_fw_chk_ptr) {#endif		m->m_pkthdr.rcvif = NULL;	/* XXX */		/* If ipfw says divert, we have to just drop packet */		if ((*ip6_fw_chk_ptr)(&ip6, ifp, &m)) {			m_freem(m);			goto done;		}		if (!m) {			error = EACCES;			goto done;		}	}#endif	/*	 * If the outgoing packet contains a hop-by-hop options header,	 * it must be examined and processed even by the source node.	 * (RFC 2460, section 4.)	 */	if (exthdrs.ip6e_hbh) {		struct ip6_hbh *hbh = mtod(exthdrs.ip6e_hbh, struct ip6_hbh *);		u_int32_t dummy; /* XXX unused */		u_int32_t plen = 0; /* XXX: ip6_process will check the value */#ifdef DIAGNOSTIC		if ((hbh->ip6h_len + 1) << 3 > exthdrs.ip6e_hbh->m_len)			panic("ip6e_hbh is not continuous");#endif		/*		 *  XXX: if we have to send an ICMPv6 error to the sender,		 *       we need the M_LOOP flag since icmp6_error() expects		 *       the IPv6 and the hop-by-hop options header are		 *       continuous unless the flag is set.		 */		m->m_flags |= M_LOOP;		m->m_pkthdr.rcvif = ifp;		if (ip6_process_hopopts(m,					(u_int8_t *)(hbh + 1),					((hbh->ip6h_len + 1) << 3) -					sizeof(struct ip6_hbh),					&dummy, &plen) < 0) {			/* m was already freed at this point */			error = EINVAL;/* better error? */			goto done;		}		m->m_flags &= ~M_LOOP; /* XXX */		m->m_pkthdr.rcvif = NULL;	}#if defined(__NetBSD__) && defined(PFIL_HOOKS)	/*	 * Run through list of hooks for output packets.	 */	m1 = m;	pfh = pfil_hook_get(PFIL_OUT, &inetsw[ip_protox[IPPROTO_IPV6]].pr_pfh);	for (; pfh; pfh = pfh->pfil_link.tqe_next)		if (pfh->pfil_func) {		    	rv = pfh->pfil_func(ip6, sizeof(*ip6), ifp, 1, &m1);			if (rv) {				error = EHOSTUNREACH;				goto done;			}			m = m1;			if (m == NULL)				goto done;			ip6 = mtod(m, struct ip6_hdr *);		}#endif /* PFIL_HOOKS */#if defined(__OpenBSD__) && NPF > 0	if (pf_test6(PF_OUT, ifp, &m) != PF_PASS) {		error = EHOSTUNREACH;		m_freem(m);		goto done;	}	ip6 = mtod(m, struct ip6_hdr *);#endif 	/*	 * Send the packet to the outgoing interface.	 * If necessary, do IPv6 fragmentation before sending.	 */	tlen = m->m_pkthdr.len;	/*	 * Even if the DONTFRAG option is specified, we cannot send the packet	 * when the data length is larger than the MTU of the outgoing	 * interface.	 * Notify the error by sending IPV6_PATHMTU ancillary data as well	 * as returning an error code (the latter is not described in the API	 * spec.)	 */	if (opt && (opt->ip6po_flags & IP6PO_DONTFRAG) && tlen > ifp->if_mtu#ifdef notyet	    && !(ifp->if_flags & IFF_FRAGMENTABLE)#endif		) {		u_int32_t mtu32;		struct ip6ctlparam ip6cp;		mtu32 = (u_int32_t)mtu;		bzero(&ip6cp, sizeof(ip6cp));		ip6cp.ip6c_cmdarg = (void *)&mtu32;		pfctlinput2(PRC_MSGSIZE, &ro_pmtu->ro_dst, (void *)&ip6cp);		error = EMSGSIZE;		goto bad;	}	if (tlen <= mtu || (opt && (opt->ip6po_flags & IP6PO_DONTFRAG))#ifdef notyet	    /*	     * On any link that cannot convey a 1280-octet packet in one piece,	     * link-specific fragmentation and reassembly must be provided at	     * a layer below IPv6. [RFC 2460, sec.5]	     * Thus if the interface has ability of link-level fragmentation,	     * we can just send the packet even if the packet size is	     * larger than the link's MTU.	     * XXX: IFF_FRAGMENTABLE (or such) flag has not been defined yet...	     */		    || (ifp->if_flags & IFF_FRAGMENTABLE)#endif	    )	{		struct in6_ifaddr *ia6;		ip6 = mtod(m, struct ip6_hdr *);		ia6 = in6_ifawithifp(ifp, &ip6->ip6_src);		if (ia6) {			/* Record statistics for this interface address. */#if defined(__NetBSD__) && defined(IFA_STATS)			ia6->ia_ifa.ifa_data.ifad_outbytes +=				m->m_pkthdr.len;#elif defined(__FreeBSD__) && __FreeBSD__ >= 4			ia6->ia_ifa.if_opackets++;			ia6->ia_ifa.if_obytes += m->m_pkthdr.len;#elif defined(__bsdi__) && _BSDI_VERSION >= 199802			ia6->ia_ifa.ifa_opackets++;			ia6->ia_ifa.ifa_obytes += m->m_pkthdr.len;#endif		}#if defined(IPSEC) && !defined(__OpenBSD__)		/* clean ipsec history once it goes out of the node */		ipsec_delaux(m);#endif		error = nd6_output(ifp, origifp, m, dst, rt);		goto done;	} else if (mtu < IPV6_MMTU) {		/*		 * note that path MTU is never less than IPV6_MMTU		 * (see icmp6_input).		 */		error = EMSGSIZE;		in6_ifstat_inc(ifp, ifs6_out_fragfail);		goto bad;	} else if (ip6->ip6_plen == 0) { /* jumbo payload cannot be fragmented */		error = EMSGSIZE;		in6_ifstat_inc(ifp, ifs6_out_fragfail);		goto bad;	} else {		struct mbuf **mnext, *m_frgpart;		struct ip6_frag *ip6f;		u_int32_t id = htonl(ip6_id++);		u_char nextproto;		struct ip6ctlparam ip6cp;		u_int32_t mtu32;		/*		 * Too large for the destination or interface;		 * fragment if possible.		 * Must be able to put at least 8 bytes per fragment.		 */		hlen = unfragpartlen;		if (mtu > IPV6_MAXPACKET)			mtu = IPV6_MAXPACKET;		/* Notify a proper path MTU to applications. */		mtu32 = (u_int32_t)mtu;		bzero(&ip6cp, sizeof(ip6cp));		ip6cp.ip6c_cmdarg = (void *)&mtu32;		pfctlinput2(PRC_MSGSIZE, &ro_pmtu->ro_dst, (void *)&ip6cp);		len = (mtu - hlen - sizeof(struct ip6_frag)) & ~7;		if (len < 8) {			error = EMSGSIZE;			in6_ifstat_inc(ifp, ifs6_out_fragfail);			goto bad;		}		mnext = &m->m_nextpkt;		/*		 * Change the next header field of the last header in the		 * unfragmentable part.		 */#ifdef MIP6		if (exthdrs.ip6e_haddr) {			nextproto = *mtod(exthdrs.ip6e_haddr, u_char *);			*mtod(exthdrs.ip6e_haddr, u_char *) = IPPROTO_FRAGMENT;		} else#endif /* MIP6 */		if (exthdrs.ip6e_rthdr) {			nextproto = *mtod(exthdrs.ip6e_rthdr, u_char *);			*mtod(exthdrs.ip6e_rthdr, u_char *) = IPPROTO_FRAGMENT;		} else if (exthdrs.ip6e_dest1) {			nextproto = *mtod(exthdrs.ip6e_dest1, u_char *);			*mtod(exthdrs.ip6e_dest1, u_char *) = IPPROTO_FRAGMENT;		} else if (exthdrs.ip6e_hbh) {			nextproto = *mtod(exthdrs.ip6e_hbh, u_char *);			*mtod(exthdrs.ip6e_hbh, u_char *) = IPPROTO_FRAGMENT;		} else {			nextproto = ip6->ip6_nxt;			ip6->ip6_nxt = IPPROTO_FRAGMENT;		}		/*		 * Loop through length of segment after first fragment,		 * make new header and copy data of each part and link onto		 * chain.		 */		m0 = m;		for (off = hlen; off < tlen; off += len) {			MGETHDR(m, M_DONTWAIT, MT_HEADER);			if (!m) {				error = ENOBUFS;				ip6stat.ip6s_odropped++;				goto sendorfree;			}			m->m_pkthdr.rcvif = NULL;			m->m_flags = m0->m_flags & M_COPYFLAGS;			*mnext = m;			mnext = &m->m_nextpkt;			m->m_data += max_linkhdr;			mhip6 = mtod(m, struct ip6_hdr *);			*mhip6 = *ip6;			m->m_len = sizeof(*mhip6);			error = ip6_insertfraghdr(m0, m, hlen, &ip6f);			if (error) {				ip6stat.ip6s_odropped++;				goto sendorfree;			}			ip6f->ip6f_offlg = htons((u_short)((off - hlen) & ~7));			if (off + len >= tlen)				len = tlen - off;			else				ip6f->ip6f_offlg |= IP6F_MORE_FRAG;			mhip6->ip6_plen = htons((u_short)(len + hlen +							  sizeof(*ip6f) -							  sizeof(struct ip6_hdr)));			if ((m_frgpart = m_copy(m0, off, len)) == 0) {				error = ENOBUFS;				ip6stat.ip6s_odropped++;				goto sendorfree;			}			m_cat(m, m_frgpart);			m->m_pkthdr.len = len + hlen + sizeof(*ip6f);			m->m_pkthdr.rcvif = (struct ifnet *)0;			ip6f->ip6f_reserved = 0;			ip6f->ip6f_ident = id;			ip6f->ip6f_nxt = nextproto;			ip6stat.ip6s_ofragments++;			in6_ifstat_inc(ifp, ifs6_out_fragcreat);		}		in6_ifstat_inc(ifp, ifs6_out_fragok);	}	/*	 * Remove leading garbages.	 */sendorfree:	m = m0->m_nextpkt;	m0->m_nextpkt = 0;	m_freem(m0);	for (m0 = m; m; m = m0) {		m0 = m->m_nextpkt;		m->m_nextpkt = 0;		if (error == 0) {			struct in6_ifaddr *ia6;			ip6 = mtod(m, struct ip6_hdr *);			ia6 = in6_ifawithifp(ifp, &ip6->ip6_src);			if (ia6) {				/*				 * Record statistics for this interface				 * address.				 */#if defined(__NetBSD__) && defined(IFA_STATS)				ia6->ia_ifa.ifa_data.ifad_outbytes +=					m->m_pkthdr.len;#elif defined(__FreeBSD__) && __FreeBSD__ >= 4				ia6->ia_ifa.if_opackets++;				ia6->ia_ifa.if_obytes += m->m_pkthdr.len;#elif defined(__bsdi__) && _BSDI_VERSION >= 199802				ia6->ia_ifa.ifa_opackets++;				ia6->ia_ifa.ifa_obytes += m->m_pkthdr.len;#endif			}#if defined(IPSEC) && !defined(__OpenBSD__)			/* clean ipsec history once it goes out of the node */			ipsec_delaux(m);

⌨️ 快捷键说明

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