/* * Input a Neighbor Solicitation Message. * * Based on RFC 2461 * Based on RFC 2462 (duplicate address detection) */ void nd6_ns_input(struct mbuf *m, int off, int icmp6len) { struct ifnet *ifp = m->m_pkthdr.rcvif; struct ip6_hdr *ip6 = mtod(m, struct ip6_hdr *); struct nd_neighbor_solicit *nd_ns; struct in6_addr saddr6 = ip6->ip6_src; struct in6_addr daddr6 = ip6->ip6_dst; struct in6_addr taddr6; struct in6_addr myaddr6; char *lladdr = NULL; struct ifaddr *ifa = NULL; int lladdrlen = 0; int anycast = 0, proxy = 0, tentative = 0; int tlladdr; int rflag; union nd_opts ndopts; struct sockaddr_dl proxydl; char ip6bufs[INET6_ADDRSTRLEN], ip6bufd[INET6_ADDRSTRLEN]; rflag = (V_ip6_forwarding) ? ND_NA_FLAG_ROUTER : 0; if (ND_IFINFO(ifp)->flags & ND6_IFF_ACCEPT_RTADV && V_ip6_norbit_raif) rflag = 0; #ifndef PULLDOWN_TEST IP6_EXTHDR_CHECK(m, off, icmp6len,); nd_ns = (struct nd_neighbor_solicit *)((caddr_t)ip6 + off); #else IP6_EXTHDR_GET(nd_ns, struct nd_neighbor_solicit *, m, off, icmp6len); if (nd_ns == NULL) { ICMP6STAT_INC(icp6s_tooshort); return; } #endif ip6 = mtod(m, struct ip6_hdr *); /* adjust pointer for safety */ taddr6 = nd_ns->nd_ns_target; if (in6_setscope(&taddr6, ifp, NULL) != 0) goto bad; if (ip6->ip6_hlim != 255) { nd6log((LOG_ERR, "nd6_ns_input: invalid hlim (%d) from %s to %s on %s\n", ip6->ip6_hlim, ip6_sprintf(ip6bufs, &ip6->ip6_src), ip6_sprintf(ip6bufd, &ip6->ip6_dst), if_name(ifp))); goto bad; } if (IN6_IS_ADDR_UNSPECIFIED(&saddr6)) { /* dst has to be a solicited node multicast address. */ if (daddr6.s6_addr16[0] == IPV6_ADDR_INT16_MLL && /* don't check ifindex portion */ daddr6.s6_addr32[1] == 0 && daddr6.s6_addr32[2] == IPV6_ADDR_INT32_ONE && daddr6.s6_addr8[12] == 0xff) { ; /* good */ } else { nd6log((LOG_INFO, "nd6_ns_input: bad DAD packet " "(wrong ip6 dst)\n")); goto bad; } } else if (!V_nd6_onlink_ns_rfc4861) { struct sockaddr_in6 src_sa6; /* * According to recent IETF discussions, it is not a good idea * to accept a NS from an address which would not be deemed * to be a neighbor otherwise. This point is expected to be * clarified in future revisions of the specification. */ bzero(&src_sa6, sizeof(src_sa6)); src_sa6.sin6_family = AF_INET6; src_sa6.sin6_len = sizeof(src_sa6); src_sa6.sin6_addr = saddr6; if (nd6_is_addr_neighbor(&src_sa6, ifp) == 0) { nd6log((LOG_INFO, "nd6_ns_input: " "NS packet from non-neighbor\n")); goto bad; } } if (IN6_IS_ADDR_MULTICAST(&taddr6)) { nd6log((LOG_INFO, "nd6_ns_input: bad NS target (multicast)\n")); goto bad; } icmp6len -= sizeof(*nd_ns); nd6_option_init(nd_ns + 1, icmp6len, &ndopts); if (nd6_options(&ndopts) < 0) { nd6log((LOG_INFO, "nd6_ns_input: invalid ND option, ignored\n")); /* nd6_options have incremented stats */ goto freeit; } if (ndopts.nd_opts_src_lladdr) { lladdr = (char *)(ndopts.nd_opts_src_lladdr + 1); lladdrlen = ndopts.nd_opts_src_lladdr->nd_opt_len << 3; } if (IN6_IS_ADDR_UNSPECIFIED(&ip6->ip6_src) && lladdr) { nd6log((LOG_INFO, "nd6_ns_input: bad DAD packet " "(link-layer address option)\n")); goto bad; } /* * Attaching target link-layer address to the NA? * (RFC 2461 7.2.4) * * NS IP dst is unicast/anycast MUST NOT add * NS IP dst is solicited-node multicast MUST add * * In implementation, we add target link-layer address by default. * We do not add one in MUST NOT cases. */ if (!IN6_IS_ADDR_MULTICAST(&daddr6)) tlladdr = 0; else tlladdr = 1; /* * Target address (taddr6) must be either: * (1) Valid unicast/anycast address for my receiving interface, * (2) Unicast address for which I'm offering proxy service, or * (3) "tentative" address on which DAD is being performed. */ /* (1) and (3) check. */ if (ifp->if_carp) ifa = (*carp_iamatch6_p)(ifp, &taddr6); else ifa = (struct ifaddr *)in6ifa_ifpwithaddr(ifp, &taddr6); /* (2) check. */ if (ifa == NULL) { struct route_in6 ro; int need_proxy; bzero(&ro, sizeof(ro)); ro.ro_dst.sin6_len = sizeof(struct sockaddr_in6); ro.ro_dst.sin6_family = AF_INET6; ro.ro_dst.sin6_addr = taddr6; /* Always use the default FIB. */ #ifdef RADIX_MPATH rtalloc_mpath_fib((struct route *)&ro, RTF_ANNOUNCE, RT_DEFAULT_FIB); #else in6_rtalloc(&ro, RT_DEFAULT_FIB); #endif need_proxy = (ro.ro_rt && (ro.ro_rt->rt_flags & RTF_ANNOUNCE) != 0 && ro.ro_rt->rt_gateway->sa_family == AF_LINK); if (ro.ro_rt != NULL) { if (need_proxy) proxydl = *SDL(ro.ro_rt->rt_gateway); RTFREE(ro.ro_rt); } if (need_proxy) { /* * proxy NDP for single entry */ ifa = (struct ifaddr *)in6ifa_ifpforlinklocal(ifp, IN6_IFF_NOTREADY|IN6_IFF_ANYCAST); if (ifa) proxy = 1; } } if (ifa == NULL) { /* * We've got an NS packet, and we don't have that adddress * assigned for us. We MUST silently ignore it. * See RFC2461 7.2.3. */ goto freeit; } myaddr6 = *IFA_IN6(ifa); anycast = ((struct in6_ifaddr *)ifa)->ia6_flags & IN6_IFF_ANYCAST; tentative = ((struct in6_ifaddr *)ifa)->ia6_flags & IN6_IFF_TENTATIVE; if (((struct in6_ifaddr *)ifa)->ia6_flags & IN6_IFF_DUPLICATED) goto freeit; if (lladdr && ((ifp->if_addrlen + 2 + 7) & ~7) != lladdrlen) { nd6log((LOG_INFO, "nd6_ns_input: lladdrlen mismatch for %s " "(if %d, NS packet %d)\n", ip6_sprintf(ip6bufs, &taddr6), ifp->if_addrlen, lladdrlen - 2)); goto bad; } if (IN6_ARE_ADDR_EQUAL(&myaddr6, &saddr6)) { nd6log((LOG_INFO, "nd6_ns_input: duplicate IP6 address %s\n", ip6_sprintf(ip6bufs, &saddr6))); goto freeit; } /* * We have neighbor solicitation packet, with target address equals to * one of my tentative address. * * src addr how to process? * --- --- * multicast of course, invalid (rejected in ip6_input) * unicast somebody is doing address resolution -> ignore * unspec dup address detection * * The processing is defined in RFC 2462. */ if (tentative) { /* * If source address is unspecified address, it is for * duplicate address detection. * * If not, the packet is for addess resolution; * silently ignore it. */ if (IN6_IS_ADDR_UNSPECIFIED(&saddr6)) nd6_dad_ns_input(ifa, ndopts.nd_opts_nonce); goto freeit; } /* * If the source address is unspecified address, entries must not * be created or updated. * It looks that sender is performing DAD. Output NA toward * all-node multicast address, to tell the sender that I'm using * the address. * S bit ("solicited") must be zero. */ if (IN6_IS_ADDR_UNSPECIFIED(&saddr6)) { struct in6_addr in6_all; in6_all = in6addr_linklocal_allnodes; if (in6_setscope(&in6_all, ifp, NULL) != 0) goto bad; nd6_na_output_fib(ifp, &in6_all, &taddr6, ((anycast || proxy || !tlladdr) ? 0 : ND_NA_FLAG_OVERRIDE) | rflag, tlladdr, proxy ? (struct sockaddr *)&proxydl : NULL, M_GETFIB(m)); goto freeit; } nd6_cache_lladdr(ifp, &saddr6, lladdr, lladdrlen, ND_NEIGHBOR_SOLICIT, 0); nd6_na_output_fib(ifp, &saddr6, &taddr6, ((anycast || proxy || !tlladdr) ? 0 : ND_NA_FLAG_OVERRIDE) | rflag | ND_NA_FLAG_SOLICITED, tlladdr, proxy ? (struct sockaddr *)&proxydl : NULL, M_GETFIB(m)); freeit: if (ifa != NULL) ifa_free(ifa); m_freem(m); return; bad: nd6log((LOG_ERR, "nd6_ns_input: src=%s\n", ip6_sprintf(ip6bufs, &saddr6))); nd6log((LOG_ERR, "nd6_ns_input: dst=%s\n", ip6_sprintf(ip6bufs, &daddr6))); nd6log((LOG_ERR, "nd6_ns_input: tgt=%s\n", ip6_sprintf(ip6bufs, &taddr6))); ICMP6STAT_INC(icp6s_badns); if (ifa != NULL) ifa_free(ifa); m_freem(m); }
/* * Input a Neighbor Solicitation Message. * * Based on RFC 2461 * Based on RFC 2462 (duplicate address detection) */ void nd6_ns_input(struct mbuf *m, int off, int icmp6len) { struct ifnet *ifp = m->m_pkthdr.rcvif; struct ip6_hdr *ip6 = mtod(m, struct ip6_hdr *); struct nd_neighbor_solicit *nd_ns; struct in6_addr saddr6 = ip6->ip6_src; struct in6_addr daddr6 = ip6->ip6_dst; struct in6_addr taddr6; struct in6_addr myaddr6; char *lladdr = NULL; struct ifaddr *ifa; int lladdrlen = 0; int anycast = 0, proxy = 0, tentative = 0; int router = ip6_forwarding; int tlladdr; union nd_opts ndopts; const struct sockaddr_dl *proxydl = NULL; IP6_EXTHDR_GET(nd_ns, struct nd_neighbor_solicit *, m, off, icmp6len); if (nd_ns == NULL) { ICMP6_STATINC(ICMP6_STAT_TOOSHORT); return; } ip6 = mtod(m, struct ip6_hdr *); /* adjust pointer for safety */ taddr6 = nd_ns->nd_ns_target; if (in6_setscope(&taddr6, ifp, NULL) != 0) goto bad; if (ip6->ip6_hlim != 255) { nd6log((LOG_ERR, "nd6_ns_input: invalid hlim (%d) from %s to %s on %s\n", ip6->ip6_hlim, ip6_sprintf(&ip6->ip6_src), ip6_sprintf(&ip6->ip6_dst), if_name(ifp))); goto bad; } if (IN6_IS_ADDR_UNSPECIFIED(&saddr6)) { /* dst has to be a solicited node multicast address. */ /* don't check ifindex portion */ if (daddr6.s6_addr16[0] == IPV6_ADDR_INT16_MLL && daddr6.s6_addr32[1] == 0 && daddr6.s6_addr32[2] == IPV6_ADDR_INT32_ONE && daddr6.s6_addr8[12] == 0xff) { ; /* good */ } else { nd6log((LOG_INFO, "nd6_ns_input: bad DAD packet " "(wrong ip6 dst)\n")); goto bad; } } else { struct sockaddr_in6 ssin6; /* * Make sure the source address is from a neighbor's address. */ sockaddr_in6_init(&ssin6, &saddr6, 0, 0, 0); if (nd6_is_addr_neighbor(&ssin6, ifp) == 0) { nd6log((LOG_INFO, "nd6_ns_input: " "NS packet from non-neighbor\n")); goto bad; } } if (IN6_IS_ADDR_MULTICAST(&taddr6)) { nd6log((LOG_INFO, "nd6_ns_input: bad NS target (multicast)\n")); goto bad; } icmp6len -= sizeof(*nd_ns); nd6_option_init(nd_ns + 1, icmp6len, &ndopts); if (nd6_options(&ndopts) < 0) { nd6log((LOG_INFO, "nd6_ns_input: invalid ND option, ignored\n")); /* nd6_options have incremented stats */ goto freeit; } if (ndopts.nd_opts_src_lladdr) { lladdr = (char *)(ndopts.nd_opts_src_lladdr + 1); lladdrlen = ndopts.nd_opts_src_lladdr->nd_opt_len << 3; } if (IN6_IS_ADDR_UNSPECIFIED(&ip6->ip6_src) && lladdr) { nd6log((LOG_INFO, "nd6_ns_input: bad DAD packet " "(link-layer address option)\n")); goto bad; } /* * Attaching target link-layer address to the NA? * (RFC 2461 7.2.4) * * NS IP dst is multicast MUST add * Otherwise MAY be omitted * * In this implementation, we omit the target link-layer address * in the "MAY" case. */ #if 0 /* too much! */ ifa = (struct ifaddr *)in6ifa_ifpwithaddr(ifp, &daddr6); if (ifa && (((struct in6_ifaddr *)ifa)->ia6_flags & IN6_IFF_ANYCAST)) tlladdr = 0; else #endif if (!IN6_IS_ADDR_MULTICAST(&daddr6)) tlladdr = 0; else tlladdr = 1; /* * Target address (taddr6) must be either: * (1) Valid unicast/anycast address for my receiving interface, * (2) Unicast address for which I'm offering proxy service, or * (3) "tentative" address on which DAD is being performed. */ /* (1) and (3) check. */ #if NCARP > 0 if (ifp->if_carp && ifp->if_type != IFT_CARP) ifa = carp_iamatch6(ifp->if_carp, &taddr6); else ifa = NULL; if (!ifa) ifa = (struct ifaddr *)in6ifa_ifpwithaddr(ifp, &taddr6); #else ifa = (struct ifaddr *)in6ifa_ifpwithaddr(ifp, &taddr6); #endif /* (2) check. */ if (ifa == NULL) { struct rtentry *rt; struct sockaddr_in6 tsin6; sockaddr_in6_init(&tsin6, &taddr6, 0, 0, 0); rt = rtalloc1((struct sockaddr *)&tsin6, 0); if (rt && (rt->rt_flags & RTF_ANNOUNCE) != 0 && rt->rt_gateway->sa_family == AF_LINK) { /* * proxy NDP for single entry */ ifa = (struct ifaddr *)in6ifa_ifpforlinklocal(ifp, IN6_IFF_NOTREADY|IN6_IFF_ANYCAST); if (ifa) { proxy = 1; proxydl = satocsdl(rt->rt_gateway); router = 0; /* XXX */ } } if (rt) rtfree(rt); } if (ifa == NULL) { /* * We've got an NS packet, and we don't have that address * assigned for us. We MUST silently ignore it. * See RFC2461 7.2.3. */ goto freeit; } myaddr6 = *IFA_IN6(ifa); anycast = ((struct in6_ifaddr *)ifa)->ia6_flags & IN6_IFF_ANYCAST; tentative = ((struct in6_ifaddr *)ifa)->ia6_flags & IN6_IFF_TENTATIVE; if (((struct in6_ifaddr *)ifa)->ia6_flags & IN6_IFF_DUPLICATED) goto freeit; if (lladdr && ((ifp->if_addrlen + 2 + 7) & ~7) != lladdrlen) { nd6log((LOG_INFO, "nd6_ns_input: lladdrlen mismatch for %s " "(if %d, NS packet %d)\n", ip6_sprintf(&taddr6), ifp->if_addrlen, lladdrlen - 2)); goto bad; } if (IN6_ARE_ADDR_EQUAL(&myaddr6, &saddr6)) { nd6log((LOG_INFO, "nd6_ns_input: duplicate IP6 address %s\n", ip6_sprintf(&saddr6))); goto freeit; } /* * We have neighbor solicitation packet, with target address equals to * one of my tentative address. * * src addr how to process? * --- --- * multicast of course, invalid (rejected in ip6_input) * unicast somebody is doing address resolution -> ignore * unspec dup address detection * * The processing is defined in RFC 2462. */ if (tentative) { /* * If source address is unspecified address, it is for * duplicate address detection. * * If not, the packet is for addess resolution; * silently ignore it. */ if (IN6_IS_ADDR_UNSPECIFIED(&saddr6)) nd6_dad_ns_input(ifa); goto freeit; } /* * If the source address is unspecified address, entries must not * be created or updated. * It looks that sender is performing DAD. Output NA toward * all-node multicast address, to tell the sender that I'm using * the address. * S bit ("solicited") must be zero. */ if (IN6_IS_ADDR_UNSPECIFIED(&saddr6)) { struct in6_addr in6_all; in6_all = in6addr_linklocal_allnodes; if (in6_setscope(&in6_all, ifp, NULL) != 0) goto bad; nd6_na_output(ifp, &in6_all, &taddr6, ((anycast || proxy || !tlladdr) ? 0 : ND_NA_FLAG_OVERRIDE) | (ip6_forwarding ? ND_NA_FLAG_ROUTER : 0), tlladdr, (const struct sockaddr *)proxydl); goto freeit; } nd6_cache_lladdr(ifp, &saddr6, lladdr, lladdrlen, ND_NEIGHBOR_SOLICIT, 0); nd6_na_output(ifp, &saddr6, &taddr6, ((anycast || proxy || !tlladdr) ? 0 : ND_NA_FLAG_OVERRIDE) | (router ? ND_NA_FLAG_ROUTER : 0) | ND_NA_FLAG_SOLICITED, tlladdr, (const struct sockaddr *)proxydl); freeit: m_freem(m); return; bad: nd6log((LOG_ERR, "nd6_ns_input: src=%s\n", ip6_sprintf(&saddr6))); nd6log((LOG_ERR, "nd6_ns_input: dst=%s\n", ip6_sprintf(&daddr6))); nd6log((LOG_ERR, "nd6_ns_input: tgt=%s\n", ip6_sprintf(&taddr6))); ICMP6_STATINC(ICMP6_STAT_BADNS); m_freem(m); }
ER nd6_output (T_IFNET *ifp, T_NET_BUF *output, T_IN6_ADDR *dst, T_LLINFO_ND6 *ln, TMO tmout) { ER error = E_OK; SYSTIM now; /* * あて先アドレスがマルチキャストか、近隣キャッシュが * 不要の場合は直ちに送信する。 */ if (!(IN6_IS_ADDR_MULTICAST(dst) || !IF_IN6_NEED_CACHE(ifp))) { if (ln == NULL) { /* 近隣キャッシュが未定義の場合 */ if (nd6_is_addr_neighbor(ifp, dst)) { /* * 指定されたアドレスのノードが同一リンクにある時は、 * 近隣キャッシュを探索し、未定義の場合は登録する。 */ ln = nd6_lookup(dst, true); } else { error = E_CLS; goto err_ret; } } /* * 近隣キャッシュの状態が、データリンク層のアドレスが分かっていても、 * 到達可能性が確認されていないときは、到達不能通知を開始する。 */ if (ln->state == ND6_LLINFO_STALE) { syscall(get_tim(&now)); ln->expire = now + ND6_DELAY_TIME; ln->asked = 0; ln->state = ND6_LLINFO_DELAY; } /* * 近隣キャッシュの状態が、データリンク層のアドレス未解決の場合は * 近隣探索を行う。 */ if (ln->state <= ND6_LLINFO_INCOMPLETE) { /* タイムアウトの指定がポーリングならタイムアウトで終了する。*/ if (tmout == TMO_POL) { error = E_TMOUT; goto err_ret; } if (ln->state == ND6_LLINFO_NO_STATE) ln->state = ND6_LLINFO_INCOMPLETE; /* すでに、未解決のネットワークバッファがあれば、開放する。*/ if (ln->hold != NULL) { syscall(rel_net_buf(ln->hold)); } /* * 送信をペンディングする。 * IF でネットワークバッファを開放しないフラグが設定されているときは、 * 送信をペンディングしない。 */ if ((output->flags & NB_FLG_NOREL_IFOUT) == 0) { ln->hold = output; /* タイムアウトの設定 */ if (tmout == TMO_FEVR) ln->tmout = tmout; else { syscall(get_tim(&now)); ln->tmout = now + tmout; } } else { output->flags &= ~NB_FLG_NOREL_IFOUT; syscall(get_tim(&now)); ln->tmout = now + tmout; } if (ln->expire) { if (ln->asked < ND6_MAX_MCAST_QUERY) { syscall(get_tim(&now)); if ((int32_t)(ln->expire - now) < 0) { /* ln->expire < now */ ln->expire += ND6_RETRANS_TIME; /* 近隣探索送信間隔 */ ln->asked ++; nd6_ns_output(ifp, &in6_addr_unspecified, dst, ln, false); } } } return error; } } /* * 重複が検出されたインタフェースには出力しない。 */ if ((ifp->flags & ND6_IFF_IFDISABLED) != 0) { return E_OK; } IF_SET_PROTO(output, IF_PROTO_IPV6); error = IF_OUTPUT(output, dst, ln ? & ln->ifaddr : NULL, tmout); return error; err_ret: syscall(rel_net_buf(output)); return error; }
void ip6_forward(struct mbuf *m, int srcrt) { struct ip6_hdr *ip6 = mtod(m, struct ip6_hdr *); const struct sockaddr_in6 *dst; struct rtentry *rt; int error = 0, type = 0, code = 0; struct mbuf *mcopy = NULL; struct ifnet *origifp; /* maybe unnecessary */ uint32_t inzone, outzone; struct in6_addr src_in6, dst_in6; #ifdef IPSEC int needipsec = 0; struct secpolicy *sp = NULL; #endif /* * Clear any in-bound checksum flags for this packet. */ m->m_pkthdr.csum_flags = 0; /* * Do not forward packets to multicast destination (should be handled * by ip6_mforward(). * Do not forward packets with unspecified source. It was discussed * in July 2000, on ipngwg mailing list. */ if ((m->m_flags & (M_BCAST|M_MCAST)) != 0 || IN6_IS_ADDR_MULTICAST(&ip6->ip6_dst) || IN6_IS_ADDR_UNSPECIFIED(&ip6->ip6_src)) { ip6_cantforward(ip6, m->m_pkthdr.rcvif, NULL, ((m->m_flags & (M_BCAST|M_MCAST)) != 0) ? "bcast/mcast" : IN6_IS_ADDR_MULTICAST(&ip6->ip6_dst) ? "mcast/dst" : "unspec/src"); m_freem(m); return; } if (ip6->ip6_hlim <= IPV6_HLIMDEC) { /* XXX in6_ifstat_inc(rt->rt_ifp, ifs6_in_discard) */ icmp6_error(m, ICMP6_TIME_EXCEEDED, ICMP6_TIME_EXCEED_TRANSIT, 0); return; } ip6->ip6_hlim -= IPV6_HLIMDEC; /* * Save at most ICMPV6_PLD_MAXLEN (= the min IPv6 MTU - * size of IPv6 + ICMPv6 headers) bytes of the packet in case * we need to generate an ICMP6 message to the src. * Thanks to M_EXT, in most cases copy will not occur. * * It is important to save it before IPsec processing as IPsec * processing may modify the mbuf. */ mcopy = m_copy(m, 0, imin(m->m_pkthdr.len, ICMPV6_PLD_MAXLEN)); #ifdef IPSEC if (ipsec_used) { /* Check the security policy (SP) for the packet */ sp = ipsec6_check_policy(m, NULL, 0, &needipsec, &error); if (error != 0) { /* * Hack: -EINVAL is used to signal that a packet * should be silently discarded. This is typically * because we asked key management for an SA and * it was delayed (e.g. kicked up to IKE). */ if (error == -EINVAL) error = 0; goto freecopy; } } #endif /* IPSEC */ if (srcrt) { union { struct sockaddr dst; struct sockaddr_in6 dst6; } u; sockaddr_in6_init(&u.dst6, &ip6->ip6_dst, 0, 0, 0); if ((rt = rtcache_lookup(&ip6_forward_rt, &u.dst)) == NULL) { IP6_STATINC(IP6_STAT_NOROUTE); /* XXX in6_ifstat_inc(rt->rt_ifp, ifs6_in_noroute) */ if (mcopy) { icmp6_error(mcopy, ICMP6_DST_UNREACH, ICMP6_DST_UNREACH_NOROUTE, 0); } m_freem(m); return; } } else if ((rt = rtcache_validate(&ip6_forward_rt)) == NULL && (rt = rtcache_update(&ip6_forward_rt, 1)) == NULL) { /* * rtcache_getdst(ip6_forward_rt)->sin6_addr was equal to * ip6->ip6_dst */ IP6_STATINC(IP6_STAT_NOROUTE); /* XXX in6_ifstat_inc(rt->rt_ifp, ifs6_in_noroute) */ if (mcopy) { icmp6_error(mcopy, ICMP6_DST_UNREACH, ICMP6_DST_UNREACH_NOROUTE, 0); } m_freem(m); return; } dst = satocsin6(rtcache_getdst(&ip6_forward_rt)); /* * Source scope check: if a packet can't be delivered to its * destination for the reason that the destination is beyond the scope * of the source address, discard the packet and return an icmp6 * destination unreachable error with Code 2 (beyond scope of source * address). We use a local copy of ip6_src, since in6_setscope() * will possibly modify its first argument. * [draft-ietf-ipngwg-icmp-v3-07, Section 3.1] */ src_in6 = ip6->ip6_src; inzone = outzone = ~0; if (in6_setscope(&src_in6, rt->rt_ifp, &outzone) != 0 || in6_setscope(&src_in6, m->m_pkthdr.rcvif, &inzone) != 0 || inzone != outzone) { ip6_cantforward(ip6, m->m_pkthdr.rcvif, rt->rt_ifp, "src[%s] inzone %d outzone %d", in6_getscopename(&ip6->ip6_src), inzone, outzone); if (mcopy) icmp6_error(mcopy, ICMP6_DST_UNREACH, ICMP6_DST_UNREACH_BEYONDSCOPE, 0); m_freem(m); return; } #ifdef IPSEC /* * If we need to encapsulate the packet, do it here * ipsec6_proces_packet will send the packet using ip6_output */ if (needipsec) { int s = splsoftnet(); error = ipsec6_process_packet(m, sp->req); splx(s); if (mcopy) goto freecopy; } #endif /* * Destination scope check: if a packet is going to break the scope * zone of packet's destination address, discard it. This case should * usually be prevented by appropriately-configured routing table, but * we need an explicit check because we may mistakenly forward the * packet to a different zone by (e.g.) a default route. */ dst_in6 = ip6->ip6_dst; inzone = outzone = ~0; if (in6_setscope(&dst_in6, m->m_pkthdr.rcvif, &inzone) != 0 || in6_setscope(&dst_in6, rt->rt_ifp, &outzone) != 0 || inzone != outzone) { ip6_cantforward(ip6, m->m_pkthdr.rcvif, rt->rt_ifp, "dst[%s] inzone %d outzone %d", in6_getscopename(&ip6->ip6_dst), inzone, outzone); if (mcopy) icmp6_error(mcopy, ICMP6_DST_UNREACH, ICMP6_DST_UNREACH_BEYONDSCOPE, 0); m_freem(m); return; } if (m->m_pkthdr.len > IN6_LINKMTU(rt->rt_ifp)) { in6_ifstat_inc(rt->rt_ifp, ifs6_in_toobig); if (mcopy) { u_long mtu; mtu = IN6_LINKMTU(rt->rt_ifp); icmp6_error(mcopy, ICMP6_PACKET_TOO_BIG, 0, mtu); } m_freem(m); return; } if (rt->rt_flags & RTF_GATEWAY) dst = (struct sockaddr_in6 *)rt->rt_gateway; /* * If we are to forward the packet using the same interface * as one we got the packet from, perhaps we should send a redirect * to sender to shortcut a hop. * Only send redirect if source is sending directly to us, * and if packet was not source routed (or has any options). * Also, don't send redirect if forwarding using a route * modified by a redirect. */ if (rt->rt_ifp == m->m_pkthdr.rcvif && !srcrt && ip6_sendredirects && (rt->rt_flags & (RTF_DYNAMIC|RTF_MODIFIED)) == 0) { if ((rt->rt_ifp->if_flags & IFF_POINTOPOINT) && nd6_is_addr_neighbor( satocsin6(rtcache_getdst(&ip6_forward_rt)), rt->rt_ifp)) { /* * If the incoming interface is equal to the outgoing * one, the link attached to the interface is * point-to-point, and the IPv6 destination is * regarded as on-link on the link, then it will be * highly probable that the destination address does * not exist on the link and that the packet is going * to loop. Thus, we immediately drop the packet and * send an ICMPv6 error message. * For other routing loops, we dare to let the packet * go to the loop, so that a remote diagnosing host * can detect the loop by traceroute. * type/code is based on suggestion by Rich Draves. * not sure if it is the best pick. */ icmp6_error(mcopy, ICMP6_DST_UNREACH, ICMP6_DST_UNREACH_ADDR, 0); m_freem(m); return; } type = ND_REDIRECT; } /* * Fake scoped addresses. Note that even link-local source or * destinaion can appear, if the originating node just sends the * packet to us (without address resolution for the destination). * Since both icmp6_error and icmp6_redirect_output fill the embedded * link identifiers, we can do this stuff after making a copy for * returning an error. */ if ((rt->rt_ifp->if_flags & IFF_LOOPBACK) != 0) { /* * See corresponding comments in ip6_output. * XXX: but is it possible that ip6_forward() sends a packet * to a loopback interface? I don't think so, and thus * I bark here. ([email protected]) * XXX: it is common to route invalid packets to loopback. * also, the codepath will be visited on use of ::1 in * rthdr. (itojun) */ #if 1 if (0) #else if ((rt->rt_flags & (RTF_BLACKHOLE|RTF_REJECT)) == 0) #endif { printf("ip6_forward: outgoing interface is loopback. " "src %s, dst %s, nxt %d, rcvif %s, outif %s\n", ip6_sprintf(&ip6->ip6_src), ip6_sprintf(&ip6->ip6_dst), ip6->ip6_nxt, if_name(m->m_pkthdr.rcvif), if_name(rt->rt_ifp)); } /* we can just use rcvif in forwarding. */ origifp = m->m_pkthdr.rcvif; } else origifp = rt->rt_ifp; /* * 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); /* * Run through list of hooks for output packets. */ if ((error = pfil_run_hooks(inet6_pfil_hook, &m, rt->rt_ifp, PFIL_OUT)) != 0) goto senderr; if (m == NULL) goto freecopy; ip6 = mtod(m, struct ip6_hdr *); error = nd6_output(rt->rt_ifp, origifp, m, dst, rt); if (error) { in6_ifstat_inc(rt->rt_ifp, ifs6_out_discard); IP6_STATINC(IP6_STAT_CANTFORWARD); } else { IP6_STATINC(IP6_STAT_FORWARD); in6_ifstat_inc(rt->rt_ifp, ifs6_out_forward); if (type) IP6_STATINC(IP6_STAT_REDIRECTSENT); else { #ifdef GATEWAY if (m->m_flags & M_CANFASTFWD) ip6flow_create(&ip6_forward_rt, m); #endif if (mcopy) goto freecopy; } } senderr: if (mcopy == NULL) return; switch (error) { case 0: if (type == ND_REDIRECT) { icmp6_redirect_output(mcopy, rt); return; } goto freecopy; case EMSGSIZE: /* xxx MTU is constant in PPP? */ goto freecopy; case ENOBUFS: /* Tell source to slow down like source quench in IP? */ goto freecopy; case ENETUNREACH: /* shouldn't happen, checked above */ case EHOSTUNREACH: case ENETDOWN: case EHOSTDOWN: default: type = ICMP6_DST_UNREACH; code = ICMP6_DST_UNREACH_ADDR; break; } icmp6_error(mcopy, type, code, 0); return; freecopy: m_freem(mcopy); return; }