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 + -
显示快捷键?