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

📄 ip6_output.c

📁 嵌入式操作系统ECOS的网络开发包
💻 C
📖 第 1 页 / 共 5 页
字号:
		}
		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
			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;

⌨️ 快捷键说明

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