struct rtentry * in6_selectroute(struct sockaddr_in6 *dstsock, struct ip6_pktopts *opts, struct route_in6 *ro, unsigned int rtableid) { struct in6_addr *dst; dst = &dstsock->sin6_addr; /* * Use a cached route if it exists and is valid, else try to allocate * a new one. Note that we should check the address family of the * cached destination, in case of sharing the cache with IPv4. */ if (ro) { if (!rtisvalid(ro->ro_rt) || sin6tosa(&ro->ro_dst)->sa_family != AF_INET6 || !IN6_ARE_ADDR_EQUAL(&ro->ro_dst.sin6_addr, dst)) { rtfree(ro->ro_rt); ro->ro_rt = NULL; } if (ro->ro_rt == NULL) { struct sockaddr_in6 *sa6; /* No route yet, so try to acquire one */ bzero(&ro->ro_dst, sizeof(struct sockaddr_in6)); ro->ro_tableid = rtableid; sa6 = &ro->ro_dst; *sa6 = *dstsock; sa6->sin6_scope_id = 0; ro->ro_tableid = rtableid; ro->ro_rt = rtalloc_mpath(sin6tosa(&ro->ro_dst), NULL, ro->ro_tableid); } /* * Check if the outgoing interface conflicts with * the interface specified by ipi6_ifindex (if specified). * Note that loopback interface is always okay. * (this may happen when we are sending a packet to one of * our own addresses.) */ if (opts && opts->ip6po_pktinfo && opts->ip6po_pktinfo->ipi6_ifindex) { if (ro->ro_rt != NULL && !ISSET(ro->ro_rt->rt_flags, RTF_LOCAL) && ro->ro_rt->rt_ifidx != opts->ip6po_pktinfo->ipi6_ifindex) { return (NULL); } } return (ro->ro_rt); } return (NULL); }
/* * Delete a mif from the mif table */ static int del_m6if(mifi_t *mifip) { struct mif6 *mifp = mif6table + *mifip; mifi_t mifi; struct ifnet *ifp; struct sockaddr_in6 sin6; int s; if (*mifip >= nummifs) return EINVAL; if (mifp->m6_ifp == NULL) return EINVAL; s = splsoftnet(); if (!(mifp->m6_flags & MIFF_REGISTER)) { /* * XXX: what if there is yet IPv4 multicast daemon * using the interface? */ ifp = mifp->m6_ifp; sin6.sin6_family = AF_INET6; sin6.sin6_addr = in6addr_any; if_mcast_op(ifp, SIOCDELMULTI, sin6tosa(&sin6)); } else { if (reg_mif_num != (mifi_t)-1) { if_detach(&multicast_register_if6); reg_mif_num = (mifi_t)-1; } } #ifdef notyet memset((void *)qtable[*mifip], 0, sizeof(qtable[*mifip])); memset((void *)mifp->m6_tbf, 0, sizeof(*(mifp->m6_tbf))); #endif memset((void *)mifp, 0, sizeof (*mifp)); /* Adjust nummifs down */ for (mifi = nummifs; mifi > 0; mifi--) if (mif6table[mifi - 1].m6_ifp) break; nummifs = mifi; splx(s); #ifdef MRT6DEBUG if (mrt6debug) log(LOG_DEBUG, "del_m6if %d, nummifs %d\n", *mifip, nummifs); #endif return 0; }
int socket6_send(struct socket *s, struct mbuf *mm, struct sockaddr_in6 *src) { if (s) { if (sbappendaddr(&s->so_rcv, sin6tosa(src), mm, NULL) != 0) { sorwakeup(s); return 0; } } m_freem(mm); return -1; }
void mroute6pr() { struct mf6c *mf6ctable[MF6CTBLSIZ], *mfcp; struct mif6_sctl mif6table[MAXMIFS]; struct mf6c mfc; struct rtdetq rte, *rtep; struct mif6_sctl *mifp; mifi_t mifi; int i; int banner_printed; int saved_numeric_addr; mifi_t maxmif = 0; long int waitings; size_t len; if (live == 0) return; len = sizeof(mif6table); if (sysctlbyname("net.inet6.ip6.mif6table", mif6table, &len, NULL, 0) < 0) { xo_warn("sysctl: net.inet6.ip6.mif6table"); return; } saved_numeric_addr = numeric_addr; numeric_addr = 1; banner_printed = 0; for (mifi = 0, mifp = mif6table; mifi < MAXMIFS; ++mifi, ++mifp) { char ifname[IFNAMSIZ]; if (mifp->m6_ifp == 0) continue; maxmif = mifi; if (!banner_printed) { xo_open_list("multicast-interface"); xo_emit("\n{T:IPv6 Multicast Interface Table}\n" "{T: Mif Rate PhyIF Pkts-In Pkts-Out}\n"); banner_printed = 1; } xo_open_instance("multicast-interface"); xo_emit(" {:mif/%2u} {:rate-limit/%4d}", mifi, mifp->m6_rate_limit); xo_emit(" {:ifname/%5s}", (mifp->m6_flags & MIFF_REGISTER) ? "reg0" : if_indextoname(mifp->m6_ifp, ifname)); xo_emit(" {:received-packets/%9ju} {:sent-packets/%9ju}\n", (uintmax_t)mifp->m6_pkt_in, (uintmax_t)mifp->m6_pkt_out); xo_close_instance("multicast-interface"); } if (banner_printed) xo_open_list("multicast-interface"); else xo_emit("\n{T:IPv6 Multicast Interface Table is empty}\n"); len = sizeof(mf6ctable); if (sysctlbyname("net.inet6.ip6.mf6ctable", mf6ctable, &len, NULL, 0) < 0) { xo_warn("sysctl: net.inet6.ip6.mf6ctable"); return; } banner_printed = 0; for (i = 0; i < MF6CTBLSIZ; ++i) { mfcp = mf6ctable[i]; while(mfcp) { kread((u_long)mfcp, (char *)&mfc, sizeof(mfc)); if (!banner_printed) { xo_open_list("multicast-forwarding-cache"); xo_emit("\n" "{T:IPv6 Multicast Forwarding Cache}\n"); xo_emit(" {T:%-*.*s} {T:%-*.*s} {T:%s}", WID_ORG, WID_ORG, "Origin", WID_GRP, WID_GRP, "Group", " Packets Waits In-Mif Out-Mifs\n"); banner_printed = 1; } xo_open_instance("multicast-forwarding-cache"); xo_emit(" {:origin/%-*.*s}", WID_ORG, WID_ORG, routename(sin6tosa(&mfc.mf6c_origin), numeric_addr)); xo_emit(" {:group/%-*.*s}", WID_GRP, WID_GRP, routename(sin6tosa(&mfc.mf6c_mcastgrp), numeric_addr)); xo_emit(" {:total-packets/%9ju}", (uintmax_t)mfc.mf6c_pkt_cnt); for (waitings = 0, rtep = mfc.mf6c_stall; rtep; ) { waitings++; /* XXX KVM */ kread((u_long)rtep, (char *)&rte, sizeof(rte)); rtep = rte.next; } xo_emit(" {:waitings/%3ld}", waitings); if (mfc.mf6c_parent == MF6C_INCOMPLETE_PARENT) xo_emit(" --- "); else xo_emit(" {:parent/%3d} ", mfc.mf6c_parent); xo_open_list("mif"); for (mifi = 0; mifi <= maxmif; mifi++) { if (IF_ISSET(mifi, &mfc.mf6c_ifset)) xo_emit(" {l:%u}", mifi); } xo_close_list("mif"); xo_emit("\n"); mfcp = mfc.mf6c_next; xo_close_instance("multicast-forwarding-cache"); } } if (banner_printed) xo_close_list("multicast-forwarding-cache"); else xo_emit("\n{T:IPv6 Multicast Forwarding Table is empty}\n"); xo_emit("\n"); numeric_addr = saved_numeric_addr; }
/* * Input an Neighbor Solicitation Message. * * Based on RFC 2461 * Based on RFC 2462 (duplicated 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 router = ip6_forwarding; int tlladdr; union nd_opts ndopts; struct sockaddr_dl *proxydl = NULL; char addr[INET6_ADDRSTRLEN], addr0[INET6_ADDRSTRLEN]; IP6_EXTHDR_GET(nd_ns, struct nd_neighbor_solicit *, m, off, icmp6len); if (nd_ns == NULL) { icmp6stat.icp6s_tooshort++; return; } ip6 = mtod(m, struct ip6_hdr *); /* adjust pointer for safety */ taddr6 = nd_ns->nd_ns_target; if (ip6->ip6_hlim != 255) { nd6log((LOG_ERR, "nd6_ns_input: invalid hlim (%d) from %s to %s on %s\n", ip6->ip6_hlim, inet_ntop(AF_INET6, &ip6->ip6_src, addr, sizeof(addr)), inet_ntop(AF_INET6, &ip6->ip6_dst, addr0, sizeof(addr0)), ifp->if_xname)); goto bad; } if (IN6_IS_ADDR_UNSPECIFIED(&saddr6)) { /* dst has to be 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 { /* * Make sure the source address is from a neighbor's address. */ if (!in6_ifpprefix(ifp, &saddr6)) { 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; } if (IN6_IS_SCOPE_EMBED(&taddr6)) taddr6.s6_addr16[1] = htons(ifp->if_index); 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 0 /* too much! */ ifa = &in6ifa_ifpwithaddr(ifp, &daddr6)->ia_ifa; if (ifa && (ifatoia6(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. */ ifa = &in6ifa_ifpwithaddr(ifp, &taddr6)->ia_ifa; #if NCARP > 0 if (ifp->if_type == IFT_CARP && ifa && !carp_iamatch6(ifp, lladdr, &proxydl)) ifa = NULL; #endif /* (2) check. */ if (!ifa) { struct rtentry *rt; struct sockaddr_in6 tsin6; bzero(&tsin6, sizeof tsin6); tsin6.sin6_len = sizeof(struct sockaddr_in6); tsin6.sin6_family = AF_INET6; tsin6.sin6_addr = taddr6; rt = rtalloc1(sin6tosa(&tsin6), 0, m->m_pkthdr.ph_rtableid); if (rt && (rt->rt_flags & RTF_ANNOUNCE) != 0 && rt->rt_gateway->sa_family == AF_LINK) { /* * proxy NDP for single entry */ ifa = &in6ifa_ifpforlinklocal(ifp, IN6_IFF_NOTREADY | IN6_IFF_ANYCAST)->ia_ifa; if (ifa) { proxy = 1; proxydl = SDL(rt->rt_gateway); router = 0; /* XXX */ } } if (rt) rtfree(rt); } if (!ifa) { /* * 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 = ifatoia6(ifa)->ia6_flags & IN6_IFF_ANYCAST; tentative = ifatoia6(ifa)->ia6_flags & IN6_IFF_TENTATIVE; if (ifatoia6(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", inet_ntop(AF_INET6, &taddr6, addr, sizeof(addr)), ifp->if_addrlen, lladdrlen - 2)); goto bad; } if (IN6_ARE_ADDR_EQUAL(&myaddr6, &saddr6)) { log(LOG_INFO, "nd6_ns_input: duplicate IP6 address %s\n", inet_ntop(AF_INET6, &saddr6, addr, sizeof(addr))); 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 * duplicated address detection. * * If not, the packet is for address 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)) { saddr6 = in6addr_linklocal_allnodes; saddr6.s6_addr16[1] = htons(ifp->if_index); nd6_na_output(ifp, &saddr6, &taddr6, ((anycast || proxy || !tlladdr) ? 0 : ND_NA_FLAG_OVERRIDE) | (router ? ND_NA_FLAG_ROUTER : 0), tlladdr, (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, (struct sockaddr *)proxydl); freeit: m_freem(m); return; bad: nd6log((LOG_ERR, "nd6_ns_input: src=%s\n", inet_ntop(AF_INET6, &saddr6, addr, sizeof(addr)))); nd6log((LOG_ERR, "nd6_ns_input: dst=%s\n", inet_ntop(AF_INET6, &daddr6, addr, sizeof(addr)))); nd6log((LOG_ERR, "nd6_ns_input: tgt=%s\n", inet_ntop(AF_INET6, &taddr6, addr, sizeof(addr)))); icmp6stat.icp6s_badns++; m_freem(m); }
/* * Add a mif to the mif table */ static int add_m6if(struct mif6ctl *mifcp) { struct mif6 *mifp; struct ifnet *ifp; struct sockaddr_in6 sin6; int error, s; #ifdef notyet struct tbf *m_tbf = tbftable + mifcp->mif6c_mifi; #endif if (mifcp->mif6c_mifi >= MAXMIFS) return EINVAL; mifp = mif6table + mifcp->mif6c_mifi; if (mifp->m6_ifp) return EADDRINUSE; /* XXX: is it appropriate? */ if (mifcp->mif6c_pifi == 0 || mifcp->mif6c_pifi >= if_indexlim) return ENXIO; /* * XXX: some OSes can remove ifp and clear ifindex2ifnet[id] * even for id between 0 and if_index. */ if ((ifp = ifindex2ifnet[mifcp->mif6c_pifi]) == NULL) return ENXIO; if (mifcp->mif6c_flags & MIFF_REGISTER) { ifp = &multicast_register_if6; if (reg_mif_num == (mifi_t)-1) { strlcpy(ifp->if_xname, "register_mif", sizeof(ifp->if_xname)); ifp->if_flags |= IFF_LOOPBACK; ifp->if_index = mifcp->mif6c_mifi; reg_mif_num = mifcp->mif6c_mifi; if_attach(ifp); } } /* if REGISTER */ else { /* Make sure the interface supports multicast */ if ((ifp->if_flags & IFF_MULTICAST) == 0) return EOPNOTSUPP; s = splsoftnet(); /* * Enable promiscuous reception of all IPv6 multicasts * from the interface. */ sin6.sin6_family = AF_INET6; sin6.sin6_addr = in6addr_any; error = if_mcast_op(ifp, SIOCADDMULTI, sin6tosa(&sin6)); splx(s); if (error) return error; } s = splsoftnet(); mifp->m6_flags = mifcp->mif6c_flags; mifp->m6_ifp = ifp; #ifdef notyet /* scaling up here allows division by 1024 in critical code */ mifp->m6_rate_limit = mifcp->mif6c_rate_limit * 1024 / 1000; #endif /* initialize per mif pkt counters */ mifp->m6_pkt_in = 0; mifp->m6_pkt_out = 0; mifp->m6_bytes_in = 0; mifp->m6_bytes_out = 0; splx(s); /* Adjust nummifs up if the mifi is higher than nummifs */ if (nummifs <= mifcp->mif6c_mifi) nummifs = mifcp->mif6c_mifi + 1; #ifdef MRT6DEBUG if (mrt6debug) log(LOG_DEBUG, "add_mif #%d, phyint %s\n", mifcp->mif6c_mifi, ifp->if_xname); #endif return 0; }
/* * Return an IPv6 address, which is the most appropriate for a given * destination and user specified options. * If necessary, this function lookups the routing table and returns * an entry to the caller for later use. */ int in6_selectsrc(struct in6_addr **in6src, struct sockaddr_in6 *dstsock, struct ip6_pktopts *opts, struct ip6_moptions *mopts, struct route_in6 *ro, struct in6_addr *laddr, u_int rtableid) { struct ifnet *ifp = NULL; struct in6_addr *dst; struct in6_ifaddr *ia6 = NULL; struct in6_pktinfo *pi = NULL; int error; dst = &dstsock->sin6_addr; /* * If the source address is explicitly specified by the caller, * check if the requested source address is indeed a unicast address * assigned to the node, and can be used as the packet's source * address. If everything is okay, use the address as source. */ if (opts && (pi = opts->ip6po_pktinfo) && !IN6_IS_ADDR_UNSPECIFIED(&pi->ipi6_addr)) { struct sockaddr_in6 sa6; /* get the outgoing interface */ error = in6_selectif(dstsock, opts, mopts, ro, &ifp, rtableid); if (error) return (error); bzero(&sa6, sizeof(sa6)); sa6.sin6_family = AF_INET6; sa6.sin6_len = sizeof(sa6); sa6.sin6_addr = pi->ipi6_addr; if (ifp && IN6_IS_SCOPE_EMBED(&sa6.sin6_addr)) sa6.sin6_addr.s6_addr16[1] = htons(ifp->if_index); if_put(ifp); /* put reference from in6_selectif */ ia6 = ifatoia6(ifa_ifwithaddr(sin6tosa(&sa6), rtableid)); if (ia6 == NULL || (ia6->ia6_flags & (IN6_IFF_ANYCAST | IN6_IFF_NOTREADY))) return (EADDRNOTAVAIL); pi->ipi6_addr = sa6.sin6_addr; /* XXX: this overrides pi */ *in6src = &pi->ipi6_addr; return (0); } /* * If the source address is not specified but the socket(if any) * is already bound, use the bound address. */ if (laddr && !IN6_IS_ADDR_UNSPECIFIED(laddr)) { *in6src = laddr; return (0); } /* * If the caller doesn't specify the source address but * the outgoing interface, use an address associated with * the interface. */ if (pi && pi->ipi6_ifindex) { ifp = if_get(pi->ipi6_ifindex); if (ifp == NULL) return (ENXIO); /* XXX: better error? */ ia6 = in6_ifawithscope(ifp, dst, rtableid); if_put(ifp); if (ia6 == NULL) return (EADDRNOTAVAIL); *in6src = &ia6->ia_addr.sin6_addr; return (0); } /* * If the destination address is a link-local unicast address or * a link/interface-local multicast address, and if the outgoing * interface is specified by the sin6_scope_id filed, use an address * associated with the interface. * XXX: We're now trying to define more specific semantics of * sin6_scope_id field, so this part will be rewritten in * the near future. */ if ((IN6_IS_ADDR_LINKLOCAL(dst) || IN6_IS_ADDR_MC_LINKLOCAL(dst) || IN6_IS_ADDR_MC_INTFACELOCAL(dst)) && dstsock->sin6_scope_id) { ifp = if_get(dstsock->sin6_scope_id); if (ifp == NULL) return (ENXIO); /* XXX: better error? */ ia6 = in6_ifawithscope(ifp, dst, rtableid); if_put(ifp); if (ia6 == NULL) return (EADDRNOTAVAIL); *in6src = &ia6->ia_addr.sin6_addr; return (0); } /* * If the destination address is a multicast address and * the outgoing interface for the address is specified * by the caller, use an address associated with the interface. * Even if the outgoing interface is not specified, we also * choose a loopback interface as the outgoing interface. */ if (IN6_IS_ADDR_MULTICAST(dst)) { ifp = mopts ? if_get(mopts->im6o_ifidx) : NULL; if (!ifp && dstsock->sin6_scope_id) ifp = if_get(htons(dstsock->sin6_scope_id)); if (ifp) { ia6 = in6_ifawithscope(ifp, dst, rtableid); if_put(ifp); if (ia6 == NULL) return (EADDRNOTAVAIL); *in6src = &ia6->ia_addr.sin6_addr; return (0); } } /* * If route is known or can be allocated now, * our src addr is taken from the i/f, else punt. */ if (ro) { if (!rtisvalid(ro->ro_rt) || (ro->ro_tableid != rtableid) || !IN6_ARE_ADDR_EQUAL(&ro->ro_dst.sin6_addr, dst)) { rtfree(ro->ro_rt); ro->ro_rt = NULL; } if (ro->ro_rt == NULL) { struct sockaddr_in6 *sa6; /* No route yet, so try to acquire one */ bzero(&ro->ro_dst, sizeof(struct sockaddr_in6)); ro->ro_tableid = rtableid; sa6 = &ro->ro_dst; sa6->sin6_family = AF_INET6; sa6->sin6_len = sizeof(struct sockaddr_in6); sa6->sin6_addr = *dst; sa6->sin6_scope_id = dstsock->sin6_scope_id; ro->ro_rt = rtalloc(sin6tosa(&ro->ro_dst), RT_RESOLVE, ro->ro_tableid); } /* * in_pcbconnect() checks out IFF_LOOPBACK to skip using * the address. But we don't know why it does so. * It is necessary to ensure the scope even for lo0 * so doesn't check out IFF_LOOPBACK. */ if (ro->ro_rt) { ifp = if_get(ro->ro_rt->rt_ifidx); if (ifp != NULL) { ia6 = in6_ifawithscope(ifp, dst, rtableid); if_put(ifp); } if (ia6 == NULL) /* xxx scope error ?*/ ia6 = ifatoia6(ro->ro_rt->rt_ifa); } if (ia6 == NULL) return (EHOSTUNREACH); /* no route */ *in6src = &ia6->ia_addr.sin6_addr; return (0); } return (EADDRNOTAVAIL); }
void ip6_forward(struct mbuf *m, int srcrt) { struct ip6_hdr *ip6 = mtod(m, struct ip6_hdr *); struct sockaddr_in6 *dst; struct rtentry *rt; int error = 0, type = 0, code = 0; struct mbuf *mcopy = NULL; #ifdef IPSEC u_int8_t sproto = 0; struct m_tag *mtag; union sockaddr_union sdst; struct tdb_ident *tdbi; u_int32_t sspi; struct tdb *tdb; #if NPF > 0 struct ifnet *encif; #endif #endif /* IPSEC */ u_int rtableid = 0; char src6[INET6_ADDRSTRLEN], dst6[INET6_ADDRSTRLEN]; /* * 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)) { ip6stat.ip6s_cantforward++; /* XXX in6_ifstat_inc(rt->rt_ifp, ifs6_in_discard) */ if (ip6_log_time + ip6_log_interval < time_second) { ip6_log_time = time_second; inet_ntop(AF_INET6, &ip6->ip6_src, src6, sizeof(src6)); inet_ntop(AF_INET6, &ip6->ip6_dst, dst6, sizeof(dst6)); log(LOG_DEBUG, "cannot forward " "from %s to %s nxt %d received on inteface %u\n", src6, dst6, ip6->ip6_nxt, m->m_pkthdr.ph_ifidx); } 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_copym(m, 0, imin(m->m_pkthdr.len, ICMPV6_PLD_MAXLEN), M_NOWAIT); #if NPF > 0 reroute: #endif #ifdef IPSEC if (!ipsec_in_use) goto done_spd; /* * Check if there was an outgoing SA bound to the flow * from a transport protocol. */ /* Do we have any pending SAs to apply ? */ tdb = ipsp_spd_lookup(m, AF_INET6, sizeof(struct ip6_hdr), &error, IPSP_DIRECTION_OUT, NULL, NULL, 0); if (tdb == NULL) { if (error == 0) { /* * No IPsec processing required, we'll just send the * packet out. */ sproto = 0; /* Fall through to routing/multicast handling */ } else { /* * -EINVAL is used to indicate that the packet should * be silently dropped, typically because we've asked * key management for an SA. */ if (error == -EINVAL) /* Should silently drop packet */ error = 0; m_freem(m); goto freecopy; } } else { /* Loop detection */ for (mtag = m_tag_first(m); mtag != NULL; mtag = m_tag_next(m, mtag)) { if (mtag->m_tag_id != PACKET_TAG_IPSEC_OUT_DONE) continue; tdbi = (struct tdb_ident *)(mtag + 1); if (tdbi->spi == tdb->tdb_spi && tdbi->proto == tdb->tdb_sproto && tdbi->rdomain == tdb->tdb_rdomain && !bcmp(&tdbi->dst, &tdb->tdb_dst, sizeof(union sockaddr_union))) { sproto = 0; /* mark as no-IPsec-needed */ goto done_spd; } } /* We need to do IPsec */ bcopy(&tdb->tdb_dst, &sdst, sizeof(sdst)); sspi = tdb->tdb_spi; sproto = tdb->tdb_sproto; } /* Fall through to the routing/multicast handling code */ done_spd: #endif /* IPSEC */ #if NPF > 0 rtableid = m->m_pkthdr.ph_rtableid; #endif dst = &ip6_forward_rt.ro_dst; if (!srcrt) { /* * ip6_forward_rt.ro_dst.sin6_addr is equal to ip6->ip6_dst */ if (ip6_forward_rt.ro_rt == NULL || (ip6_forward_rt.ro_rt->rt_flags & RTF_UP) == 0 || ip6_forward_rt.ro_tableid != rtableid) { if (ip6_forward_rt.ro_rt) { rtfree(ip6_forward_rt.ro_rt); ip6_forward_rt.ro_rt = NULL; } /* this probably fails but give it a try again */ ip6_forward_rt.ro_tableid = rtableid; ip6_forward_rt.ro_rt = rtalloc_mpath( sin6tosa(&ip6_forward_rt.ro_dst), &ip6->ip6_src.s6_addr32[0], ip6_forward_rt.ro_tableid); } if (ip6_forward_rt.ro_rt == NULL) { ip6stat.ip6s_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 (ip6_forward_rt.ro_rt == NULL || (ip6_forward_rt.ro_rt->rt_flags & RTF_UP) == 0 || !IN6_ARE_ADDR_EQUAL(&ip6->ip6_dst, &dst->sin6_addr) || ip6_forward_rt.ro_tableid != rtableid) { if (ip6_forward_rt.ro_rt) { rtfree(ip6_forward_rt.ro_rt); ip6_forward_rt.ro_rt = NULL; } bzero(dst, sizeof(*dst)); dst->sin6_len = sizeof(struct sockaddr_in6); dst->sin6_family = AF_INET6; dst->sin6_addr = ip6->ip6_dst; ip6_forward_rt.ro_tableid = rtableid; ip6_forward_rt.ro_rt = rtalloc_mpath( sin6tosa(&ip6_forward_rt.ro_dst), &ip6->ip6_src.s6_addr32[0], ip6_forward_rt.ro_tableid); if (ip6_forward_rt.ro_rt == NULL) { ip6stat.ip6s_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; } } rt = ip6_forward_rt.ro_rt; /* * 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). * [draft-ietf-ipngwg-icmp-v3-00.txt, Section 3.1] */ if (in6_addr2scopeid(m->m_pkthdr.ph_ifidx, &ip6->ip6_src) != in6_addr2scopeid(rt->rt_ifp->if_index, &ip6->ip6_src)) { ip6stat.ip6s_cantforward++; ip6stat.ip6s_badscope++; in6_ifstat_inc(rt->rt_ifp, ifs6_in_discard); if (ip6_log_time + ip6_log_interval < time_second) { ip6_log_time = time_second; inet_ntop(AF_INET6, &ip6->ip6_src, src6, sizeof(src6)); inet_ntop(AF_INET6, &ip6->ip6_dst, dst6, sizeof(dst6)); log(LOG_DEBUG, "cannot forward " "src %s, dst %s, nxt %d, rcvif %u, outif %u\n", src6, dst6, ip6->ip6_nxt, m->m_pkthdr.ph_ifidx, rt->rt_ifp->if_index); } if (mcopy) icmp6_error(mcopy, ICMP6_DST_UNREACH, ICMP6_DST_UNREACH_BEYONDSCOPE, 0); m_freem(m); goto freert; } #ifdef IPSEC /* * Check if the packet needs encapsulation. * ipsp_process_packet will never come back to here. * XXX ipsp_process_packet() calls ip6_output(), and there'll be no * PMTU notification. is it okay? */ if (sproto != 0) { tdb = gettdb(rtable_l2(m->m_pkthdr.ph_rtableid), sspi, &sdst, sproto); if (tdb == NULL) { error = EHOSTUNREACH; m_freem(m); goto senderr; /*XXX*/ } #if NPF > 0 if ((encif = enc_getif(tdb->tdb_rdomain, tdb->tdb_tap)) == NULL || pf_test(AF_INET6, PF_FWD, encif, &m) != PF_PASS) { error = EHOSTUNREACH; m_freem(m); goto senderr; } if (m == NULL) goto senderr; ip6 = mtod(m, struct ip6_hdr *); /* * PF_TAG_REROUTE handling or not... * Packet is entering IPsec so the routing is * already overruled by the IPsec policy. * Until now the change was not reconsidered. * What's the behaviour? */ #endif in6_proto_cksum_out(m, encif); m->m_flags &= ~(M_BCAST | M_MCAST); /* just in case */ /* Callee frees mbuf */ error = ipsp_process_packet(m, tdb, AF_INET6, 0); m_freem(mcopy); goto freert; }