diff options
author | Sebastian Huber <sebastian.huber@embedded-brains.de> | 2013-11-04 11:33:00 +0100 |
---|---|---|
committer | Sebastian Huber <sebastian.huber@embedded-brains.de> | 2013-11-04 15:28:21 +0100 |
commit | af5333e0a02b2295304d4e029b15ee15a4fe2b3a (patch) | |
tree | c5c43680d374f58b487eeeaf18fb7ec6b84ba074 /freebsd/sys/netinet6 | |
parent | BUS_SPACE(9): Use simple memory model for ARM (diff) | |
download | rtems-libbsd-af5333e0a02b2295304d4e029b15ee15a4fe2b3a.tar.bz2 |
Update to FreeBSD 8.4
Diffstat (limited to 'freebsd/sys/netinet6')
29 files changed, 1508 insertions, 1520 deletions
diff --git a/freebsd/sys/netinet6/frag6.c b/freebsd/sys/netinet6/frag6.c index 391f2a78..f176ffb6 100644 --- a/freebsd/sys/netinet6/frag6.c +++ b/freebsd/sys/netinet6/frag6.c @@ -223,6 +223,19 @@ frag6_input(struct mbuf **mp, int *offp, int proto) /* offset now points to data portion */ offset += sizeof(struct ip6_frag); + /* + * XXX-BZ RFC XXXX (draft-gont-6man-ipv6-atomic-fragments) + * Handle "atomic" fragments (offset and m bit set to 0) upfront, + * unrelated to any reassembly. Just skip the fragment header. + */ + if ((ip6f->ip6f_offlg & ~IP6F_RESERVED_MASK) == 0) { + /* XXX-BZ we want dedicated counters for this. */ + V_ip6stat.ip6s_reassembled++; + in6_ifstat_inc(dstifp, ifs6_reass_ok); + *offp = offset; + return (ip6f->ip6f_nxt); + } + IP6Q_LOCK(); /* diff --git a/freebsd/sys/netinet6/icmp6.c b/freebsd/sys/netinet6/icmp6.c index 54758cd4..f73dee04 100644 --- a/freebsd/sys/netinet6/icmp6.c +++ b/freebsd/sys/netinet6/icmp6.c @@ -361,7 +361,7 @@ icmp6_error(struct mbuf *m, int type, int code, int param) m_adj(m, ICMPV6_PLD_MAXLEN - m->m_pkthdr.len); preplen = sizeof(struct ip6_hdr) + sizeof(struct icmp6_hdr); - M_PREPEND(m, preplen, M_DONTWAIT); + M_PREPEND(m, preplen, M_DONTWAIT); /* FIB is also copied over. */ if (m && m->m_len < preplen) m = m_pullup(m, preplen); if (m == NULL) { @@ -583,7 +583,7 @@ icmp6_input(struct mbuf **mp, int *offp, int proto) MGETHDR(n, M_DONTWAIT, n0->m_type); n0len = n0->m_pkthdr.len; /* save for use below */ if (n) - M_MOVE_PKTHDR(n, n0); + M_MOVE_PKTHDR(n, n0); /* FIB copied. */ if (n && maxlen >= MHLEN) { MCLGET(n, M_DONTWAIT); if ((n->m_flags & M_EXT) == 0) { @@ -1429,7 +1429,7 @@ ni6_input(struct mbuf *m, int off) m_freem(m); return (NULL); } - M_MOVE_PKTHDR(n, m); /* just for recvif */ + M_MOVE_PKTHDR(n, m); /* just for recvif and FIB */ if (replylen > MHLEN) { if (replylen > MCLBYTES) { /* @@ -1711,9 +1711,9 @@ ni6_addrs(struct icmp6_nodeinfo *ni6, struct mbuf *m, struct ifnet **ifpp, } IFNET_RLOCK_NOSLEEP(); - for (ifp = TAILQ_FIRST(&V_ifnet); ifp; ifp = TAILQ_NEXT(ifp, if_list)) { + TAILQ_FOREACH(ifp, &V_ifnet, if_list) { addrsofif = 0; - IF_ADDR_LOCK(ifp); + IF_ADDR_RLOCK(ifp); TAILQ_FOREACH(ifa, &ifp->if_addrhead, ifa_link) { if (ifa->ifa_addr->sa_family != AF_INET6) continue; @@ -1764,7 +1764,7 @@ ni6_addrs(struct icmp6_nodeinfo *ni6, struct mbuf *m, struct ifnet **ifpp, } addrsofif++; /* count the address */ } - IF_ADDR_UNLOCK(ifp); + IF_ADDR_RUNLOCK(ifp); if (iffound) { *ifpp = ifp; IFNET_RUNLOCK_NOSLEEP(); @@ -1782,7 +1782,7 @@ static int ni6_store_addrs(struct icmp6_nodeinfo *ni6, struct icmp6_nodeinfo *nni6, struct ifnet *ifp0, int resid) { - struct ifnet *ifp = ifp0 ? ifp0 : TAILQ_FIRST(&V_ifnet); + struct ifnet *ifp; struct in6_ifaddr *ifa6; struct ifaddr *ifa; struct ifnet *ifp_dep = NULL; @@ -1795,10 +1795,11 @@ ni6_store_addrs(struct icmp6_nodeinfo *ni6, struct icmp6_nodeinfo *nni6, return (0); /* needless to copy */ IFNET_RLOCK_NOSLEEP(); + ifp = ifp0 ? ifp0 : TAILQ_FIRST(&V_ifnet); again: for (; ifp; ifp = TAILQ_NEXT(ifp, if_list)) { - IF_ADDR_LOCK(ifp); + IF_ADDR_RLOCK(ifp); TAILQ_FOREACH(ifa, &ifp->if_addrhead, ifa_link) { if (ifa->ifa_addr->sa_family != AF_INET6) continue; @@ -1853,7 +1854,7 @@ ni6_store_addrs(struct icmp6_nodeinfo *ni6, struct icmp6_nodeinfo *nni6, /* now we can copy the address */ if (resid < sizeof(struct in6_addr) + sizeof(u_int32_t)) { - IF_ADDR_UNLOCK(ifp); + IF_ADDR_RUNLOCK(ifp); /* * We give up much more copy. * Set the truncate flag and return. @@ -1900,7 +1901,7 @@ ni6_store_addrs(struct icmp6_nodeinfo *ni6, struct icmp6_nodeinfo *nni6, resid -= (sizeof(struct in6_addr) + sizeof(u_int32_t)); copied += (sizeof(struct in6_addr) + sizeof(u_int32_t)); } - IF_ADDR_UNLOCK(ifp); + IF_ADDR_RUNLOCK(ifp); if (ifp0) /* we need search only on the specified IF */ break; } @@ -2175,10 +2176,6 @@ icmp6_reflect(struct mbuf *m, size_t off) } } - if ((srcp != NULL) && - (in6_addrscope(srcp) != in6_addrscope(&ip6->ip6_src))) - srcp = NULL; - if (srcp == NULL) { int e; struct sockaddr_in6 sin6; @@ -2284,8 +2281,6 @@ icmp6_redirect_input(struct mbuf *m, int off) int icmp6len = ntohs(ip6->ip6_plen); char *lladdr = NULL; int lladdrlen = 0; - u_char *redirhdr = NULL; - int redirhdrlen = 0; struct rtentry *rt = NULL; int is_router; int is_onlink; @@ -2351,7 +2346,7 @@ icmp6_redirect_input(struct mbuf *m, int off) sin6.sin6_family = AF_INET6; sin6.sin6_len = sizeof(struct sockaddr_in6); bcopy(&reddst6, &sin6.sin6_addr, sizeof(reddst6)); - rt = rtalloc1((struct sockaddr *)&sin6, 0, 0UL); + rt = in6_rtalloc1((struct sockaddr *)&sin6, 0, 0UL, RT_DEFAULT_FIB); if (rt) { if (rt->rt_gateway == NULL || rt->rt_gateway->sa_family != AF_INET6) { @@ -2421,11 +2416,6 @@ icmp6_redirect_input(struct mbuf *m, int off) lladdrlen = ndopts.nd_opts_tgt_lladdr->nd_opt_len << 3; } - if (ndopts.nd_opts_rh) { - redirhdrlen = ndopts.nd_opts_rh->nd_opt_rh_len; - redirhdr = (u_char *)(ndopts.nd_opts_rh + 1); /* xxx */ - } - if (lladdr && ((ifp->if_addrlen + 2 + 7) & ~7) != lladdrlen) { nd6log((LOG_INFO, "icmp6_redirect_input: lladdrlen mismatch for %s " @@ -2445,6 +2435,7 @@ icmp6_redirect_input(struct mbuf *m, int off) struct sockaddr_in6 sdst; struct sockaddr_in6 sgw; struct sockaddr_in6 ssrc; + u_int fibnum; bzero(&sdst, sizeof(sdst)); bzero(&sgw, sizeof(sgw)); @@ -2455,9 +2446,11 @@ icmp6_redirect_input(struct mbuf *m, int off) bcopy(&redtgt6, &sgw.sin6_addr, sizeof(struct in6_addr)); bcopy(&reddst6, &sdst.sin6_addr, sizeof(struct in6_addr)); bcopy(&src6, &ssrc.sin6_addr, sizeof(struct in6_addr)); - rtredirect((struct sockaddr *)&sdst, (struct sockaddr *)&sgw, - (struct sockaddr *)NULL, RTF_GATEWAY | RTF_HOST, - (struct sockaddr *)&ssrc); + for (fibnum = 0; fibnum < rt_numfibs; fibnum++) + in6_rtredirect((struct sockaddr *)&sdst, + (struct sockaddr *)&sgw, (struct sockaddr *)NULL, + RTF_GATEWAY | RTF_HOST, (struct sockaddr *)&ssrc, + fibnum); } /* finally update cached route in each socket via pfctlinput */ { @@ -2541,6 +2534,7 @@ icmp6_redirect_output(struct mbuf *m0, struct rtentry *rt) MCLGET(m, M_DONTWAIT); if (!m) goto fail; + M_SETFIB(m, rt->rt_fibnum); m->m_pkthdr.rcvif = NULL; m->m_len = 0; maxlen = M_TRAILINGSPACE(m); @@ -2620,9 +2614,9 @@ icmp6_redirect_output(struct mbuf *m0, struct rtentry *rt) struct nd_opt_hdr *nd_opt; char *lladdr; - IF_AFDATA_LOCK(ifp); + IF_AFDATA_RLOCK(ifp); ln = nd6_lookup(router_ll6, 0, ifp); - IF_AFDATA_UNLOCK(ifp); + IF_AFDATA_RUNLOCK(ifp); if (ln == NULL) goto nolladdropt; diff --git a/freebsd/sys/netinet6/in6.c b/freebsd/sys/netinet6/in6.c index 5d9f85ba..a57f3975 100644 --- a/freebsd/sys/netinet6/in6.c +++ b/freebsd/sys/netinet6/in6.c @@ -200,6 +200,11 @@ in6_control(struct socket *so, u_long cmd, caddr_t data, switch (cmd) { case SIOCGETSGCNT_IN6: case SIOCGETMIFCNT_IN6: + /* + * XXX mrt_ioctl has a 3rd, unused, FIB argument in route.c. + * We cannot see how that would be needed, so do not adjust the + * KPI blindly; more likely should clean up the IPv4 variant. + */ return (mrt6_ioctl ? mrt6_ioctl(cmd, data) : EOPNOTSUPP); } @@ -341,6 +346,18 @@ in6_control(struct socket *so, u_long cmd, caddr_t data, case SIOCGIFSTAT_ICMP6: sa6 = &ifr->ifr_addr; break; + case SIOCSIFADDR: + case SIOCSIFBRDADDR: + case SIOCSIFDSTADDR: + case SIOCSIFNETMASK: + /* + * Although we should pass any non-INET6 ioctl requests + * down to driver, we filter some legacy INET requests. + * Drivers trust SIOCSIFADDR et al to come from an already + * privileged layer, and do not perform any credentials + * checks or input validation. + */ + return (EINVAL); default: sa6 = NULL; break; @@ -698,6 +715,169 @@ out: } /* + * Join necessary multicast groups. Factored out from in6_update_ifa(). + * This entire work should only be done once, for the default FIB. + */ +static int +in6_update_ifa_join_mc(struct ifnet *ifp, struct in6_aliasreq *ifra, + struct in6_ifaddr *ia, int flags, struct in6_multi **in6m_sol) +{ + char ip6buf[INET6_ADDRSTRLEN]; + struct sockaddr_in6 mltaddr, mltmask; + struct in6_addr llsol; + struct in6_multi_mship *imm; + struct rtentry *rt; + int delay, error; + + KASSERT(in6m_sol != NULL, ("%s: in6m_sol is NULL", __func__)); + + /* Join solicited multicast addr for new host id. */ + bzero(&llsol, sizeof(struct in6_addr)); + llsol.s6_addr32[0] = IPV6_ADDR_INT32_MLL; + llsol.s6_addr32[1] = 0; + llsol.s6_addr32[2] = htonl(1); + llsol.s6_addr32[3] = ifra->ifra_addr.sin6_addr.s6_addr32[3]; + llsol.s6_addr8[12] = 0xff; + if ((error = in6_setscope(&llsol, ifp, NULL)) != 0) { + /* XXX: should not happen */ + log(LOG_ERR, "%s: in6_setscope failed\n", __func__); + goto cleanup; + } + delay = 0; + if ((flags & IN6_IFAUPDATE_DADDELAY)) { + /* + * We need a random delay for DAD on the address being + * configured. It also means delaying transmission of the + * corresponding MLD report to avoid report collision. + * [RFC 4861, Section 6.3.7] + */ + delay = arc4random() % (MAX_RTR_SOLICITATION_DELAY * hz); + } + imm = in6_joingroup(ifp, &llsol, &error, delay); + if (imm == NULL) { + nd6log((LOG_WARNING, "%s: addmulti failed for %s on %s " + "(errno=%d)\n", __func__, ip6_sprintf(ip6buf, &llsol), + if_name(ifp), error)); + goto cleanup; + } + LIST_INSERT_HEAD(&ia->ia6_memberships, imm, i6mm_chain); + *in6m_sol = imm->i6mm_maddr; + + bzero(&mltmask, sizeof(mltmask)); + mltmask.sin6_len = sizeof(struct sockaddr_in6); + mltmask.sin6_family = AF_INET6; + mltmask.sin6_addr = in6mask32; +#define MLTMASK_LEN 4 /* mltmask's masklen (=32bit=4octet) */ + + /* + * Join link-local all-nodes address. + */ + bzero(&mltaddr, sizeof(mltaddr)); + mltaddr.sin6_len = sizeof(struct sockaddr_in6); + mltaddr.sin6_family = AF_INET6; + mltaddr.sin6_addr = in6addr_linklocal_allnodes; + if ((error = in6_setscope(&mltaddr.sin6_addr, ifp, NULL)) != 0) + goto cleanup; /* XXX: should not fail */ + + /* + * XXX: do we really need this automatic routes? We should probably + * reconsider this stuff. Most applications actually do not need the + * routes, since they usually specify the outgoing interface. + */ + rt = in6_rtalloc1((struct sockaddr *)&mltaddr, 0, 0UL, RT_DEFAULT_FIB); + if (rt != NULL) { + /* XXX: only works in !SCOPEDROUTING case. */ + if (memcmp(&mltaddr.sin6_addr, + &((struct sockaddr_in6 *)rt_key(rt))->sin6_addr, + MLTMASK_LEN)) { + RTFREE_LOCKED(rt); + rt = NULL; + } + } + if (rt == NULL) { + error = in6_rtrequest(RTM_ADD, (struct sockaddr *)&mltaddr, + (struct sockaddr *)&ia->ia_addr, + (struct sockaddr *)&mltmask, RTF_UP, + (struct rtentry **)0, RT_DEFAULT_FIB); + if (error) + goto cleanup; + } else + RTFREE_LOCKED(rt); + + imm = in6_joingroup(ifp, &mltaddr.sin6_addr, &error, 0); + if (imm == NULL) { + nd6log((LOG_WARNING, "%s: addmulti failed for %s on %s " + "(errno=%d)\n", __func__, ip6_sprintf(ip6buf, + &mltaddr.sin6_addr), if_name(ifp), error)); + goto cleanup; + } + LIST_INSERT_HEAD(&ia->ia6_memberships, imm, i6mm_chain); + + /* + * Join node information group address. + */ + delay = 0; + if ((flags & IN6_IFAUPDATE_DADDELAY)) { + /* + * The spec does not say anything about delay for this group, + * but the same logic should apply. + */ + delay = arc4random() % (MAX_RTR_SOLICITATION_DELAY * hz); + } + if (in6_nigroup(ifp, NULL, -1, &mltaddr.sin6_addr) == 0) { + /* XXX jinmei */ + imm = in6_joingroup(ifp, &mltaddr.sin6_addr, &error, delay); + if (imm == NULL) + nd6log((LOG_WARNING, "%s: addmulti failed for %s on %s " + "(errno=%d)\n", __func__, ip6_sprintf(ip6buf, + &mltaddr.sin6_addr), if_name(ifp), error)); + /* XXX not very fatal, go on... */ + else + LIST_INSERT_HEAD(&ia->ia6_memberships, imm, i6mm_chain); + } + + /* + * Join interface-local all-nodes address. + * (ff01::1%ifN, and ff01::%ifN/32) + */ + mltaddr.sin6_addr = in6addr_nodelocal_allnodes; + if ((error = in6_setscope(&mltaddr.sin6_addr, ifp, NULL)) != 0) + goto cleanup; /* XXX: should not fail */ + /* XXX: again, do we really need the route? */ + rt = in6_rtalloc1((struct sockaddr *)&mltaddr, 0, 0UL, RT_DEFAULT_FIB); + if (rt != NULL) { + if (memcmp(&mltaddr.sin6_addr, + &((struct sockaddr_in6 *)rt_key(rt))->sin6_addr, + MLTMASK_LEN)) { + RTFREE_LOCKED(rt); + rt = NULL; + } + } + if (rt == NULL) { + error = in6_rtrequest(RTM_ADD, (struct sockaddr *)&mltaddr, + (struct sockaddr *)&ia->ia_addr, + (struct sockaddr *)&mltmask, RTF_UP, + (struct rtentry **)0, RT_DEFAULT_FIB); + if (error) + goto cleanup; + } else + RTFREE_LOCKED(rt); + + imm = in6_joingroup(ifp, &mltaddr.sin6_addr, &error, 0); + if (imm == NULL) { + nd6log((LOG_WARNING, "%s: addmulti failed for %s on %s " + "(errno=%d)\n", __func__, ip6_sprintf(ip6buf, + &mltaddr.sin6_addr), if_name(ifp), error)); + goto cleanup; + } + LIST_INSERT_HEAD(&ia->ia6_memberships, imm, i6mm_chain); +#undef MLTMASK_LEN + +cleanup: + return (error); +} + +/* * Update parameters of an IPv6 interface address. * If necessary, a new entry is created and linked into address chains. * This function is separated from in6_control(). @@ -710,9 +890,7 @@ in6_update_ifa(struct ifnet *ifp, struct in6_aliasreq *ifra, int error = 0, hostIsNew = 0, plen = -1; struct sockaddr_in6 dst6; struct in6_addrlifetime *lt; - struct in6_multi_mship *imm; struct in6_multi *in6m_sol; - struct rtentry *rt; int delay; char ip6buf[INET6_ADDRSTRLEN]; @@ -851,9 +1029,9 @@ in6_update_ifa(struct ifnet *ifp, struct in6_aliasreq *ifra, ia->ia_ifa.ifa_netmask = (struct sockaddr *)&ia->ia_prefixmask; ia->ia_ifp = ifp; ifa_ref(&ia->ia_ifa); /* if_addrhead */ - IF_ADDR_LOCK(ifp); + IF_ADDR_WLOCK(ifp); TAILQ_INSERT_TAIL(&ifp->if_addrhead, &ia->ia_ifa, ifa_link); - IF_ADDR_UNLOCK(ifp); + IF_ADDR_WUNLOCK(ifp); ifa_ref(&ia->ia_ifa); /* in6_ifaddrhead */ IN6_IFADDR_WLOCK(); @@ -956,178 +1134,17 @@ in6_update_ifa(struct ifnet *ifp, struct in6_aliasreq *ifra, * not just go to unlink. */ - /* Join necessary multicast groups */ + /* Join necessary multicast groups. */ in6m_sol = NULL; if ((ifp->if_flags & IFF_MULTICAST) != 0) { - struct sockaddr_in6 mltaddr, mltmask; - struct in6_addr llsol; - - /* join solicited multicast addr for new host id */ - bzero(&llsol, sizeof(struct in6_addr)); - llsol.s6_addr32[0] = IPV6_ADDR_INT32_MLL; - llsol.s6_addr32[1] = 0; - llsol.s6_addr32[2] = htonl(1); - llsol.s6_addr32[3] = ifra->ifra_addr.sin6_addr.s6_addr32[3]; - llsol.s6_addr8[12] = 0xff; - if ((error = in6_setscope(&llsol, ifp, NULL)) != 0) { - /* XXX: should not happen */ - log(LOG_ERR, "in6_update_ifa: " - "in6_setscope failed\n"); + error = in6_update_ifa_join_mc(ifp, ifra, ia, flags, &in6m_sol); + if (error) goto cleanup; - } - delay = 0; - if ((flags & IN6_IFAUPDATE_DADDELAY)) { - /* - * We need a random delay for DAD on the address - * being configured. It also means delaying - * transmission of the corresponding MLD report to - * avoid report collision. - * [draft-ietf-ipv6-rfc2462bis-02.txt] - */ - delay = arc4random() % - (MAX_RTR_SOLICITATION_DELAY * hz); - } - imm = in6_joingroup(ifp, &llsol, &error, delay); - if (imm == NULL) { - nd6log((LOG_WARNING, - "in6_update_ifa: addmulti failed for " - "%s on %s (errno=%d)\n", - ip6_sprintf(ip6buf, &llsol), if_name(ifp), - error)); - goto cleanup; - } - LIST_INSERT_HEAD(&ia->ia6_memberships, - imm, i6mm_chain); - in6m_sol = imm->i6mm_maddr; - - bzero(&mltmask, sizeof(mltmask)); - mltmask.sin6_len = sizeof(struct sockaddr_in6); - mltmask.sin6_family = AF_INET6; - mltmask.sin6_addr = in6mask32; -#define MLTMASK_LEN 4 /* mltmask's masklen (=32bit=4octet) */ - - /* - * join link-local all-nodes address - */ - bzero(&mltaddr, sizeof(mltaddr)); - mltaddr.sin6_len = sizeof(struct sockaddr_in6); - mltaddr.sin6_family = AF_INET6; - mltaddr.sin6_addr = in6addr_linklocal_allnodes; - if ((error = in6_setscope(&mltaddr.sin6_addr, ifp, NULL)) != - 0) - goto cleanup; /* XXX: should not fail */ - - /* - * XXX: do we really need this automatic routes? - * We should probably reconsider this stuff. Most applications - * actually do not need the routes, since they usually specify - * the outgoing interface. - */ - rt = rtalloc1((struct sockaddr *)&mltaddr, 0, 0UL); - if (rt) { - /* XXX: only works in !SCOPEDROUTING case. */ - if (memcmp(&mltaddr.sin6_addr, - &((struct sockaddr_in6 *)rt_key(rt))->sin6_addr, - MLTMASK_LEN)) { - RTFREE_LOCKED(rt); - rt = NULL; - } - } - if (!rt) { - error = rtrequest(RTM_ADD, (struct sockaddr *)&mltaddr, - (struct sockaddr *)&ia->ia_addr, - (struct sockaddr *)&mltmask, RTF_UP, - (struct rtentry **)0); - if (error) - goto cleanup; - } else { - RTFREE_LOCKED(rt); - } - - imm = in6_joingroup(ifp, &mltaddr.sin6_addr, &error, 0); - if (!imm) { - nd6log((LOG_WARNING, - "in6_update_ifa: addmulti failed for " - "%s on %s (errno=%d)\n", - ip6_sprintf(ip6buf, &mltaddr.sin6_addr), - if_name(ifp), error)); - goto cleanup; - } - LIST_INSERT_HEAD(&ia->ia6_memberships, imm, i6mm_chain); - - /* - * join node information group address - */ - delay = 0; - if ((flags & IN6_IFAUPDATE_DADDELAY)) { - /* - * The spec doesn't say anything about delay for this - * group, but the same logic should apply. - */ - delay = arc4random() % - (MAX_RTR_SOLICITATION_DELAY * hz); - } - if (in6_nigroup(ifp, NULL, -1, &mltaddr.sin6_addr) == 0) { - imm = in6_joingroup(ifp, &mltaddr.sin6_addr, &error, - delay); /* XXX jinmei */ - if (!imm) { - nd6log((LOG_WARNING, "in6_update_ifa: " - "addmulti failed for %s on %s " - "(errno=%d)\n", - ip6_sprintf(ip6buf, &mltaddr.sin6_addr), - if_name(ifp), error)); - /* XXX not very fatal, go on... */ - } else { - LIST_INSERT_HEAD(&ia->ia6_memberships, - imm, i6mm_chain); - } - } - - /* - * join interface-local all-nodes address. - * (ff01::1%ifN, and ff01::%ifN/32) - */ - mltaddr.sin6_addr = in6addr_nodelocal_allnodes; - if ((error = in6_setscope(&mltaddr.sin6_addr, ifp, NULL)) - != 0) - goto cleanup; /* XXX: should not fail */ - /* XXX: again, do we really need the route? */ - rt = rtalloc1((struct sockaddr *)&mltaddr, 0, 0UL); - if (rt) { - if (memcmp(&mltaddr.sin6_addr, - &((struct sockaddr_in6 *)rt_key(rt))->sin6_addr, - MLTMASK_LEN)) { - RTFREE_LOCKED(rt); - rt = NULL; - } - } - if (!rt) { - error = rtrequest(RTM_ADD, (struct sockaddr *)&mltaddr, - (struct sockaddr *)&ia->ia_addr, - (struct sockaddr *)&mltmask, RTF_UP, - (struct rtentry **)0); - if (error) - goto cleanup; - } else - RTFREE_LOCKED(rt); - - imm = in6_joingroup(ifp, &mltaddr.sin6_addr, &error, 0); - if (!imm) { - nd6log((LOG_WARNING, "in6_update_ifa: " - "addmulti failed for %s on %s " - "(errno=%d)\n", - ip6_sprintf(ip6buf, &mltaddr.sin6_addr), - if_name(ifp), error)); - goto cleanup; - } - LIST_INSERT_HEAD(&ia->ia6_memberships, imm, i6mm_chain); -#undef MLTMASK_LEN } /* * Perform DAD, if needed. - * XXX It may be of use, if we can administratively - * disable DAD. + * XXX It may be of use, if we can administratively disable DAD. */ if (in6if_do_dad(ifp) && ((ifra->ifra_flags & IN6_IFF_NODAD) == 0) && (ia->ia6_flags & IN6_IFF_TENTATIVE)) @@ -1185,87 +1202,29 @@ in6_update_ifa(struct ifnet *ifp, struct in6_aliasreq *ifra, return error; } -void -in6_purgeaddr(struct ifaddr *ifa) +/* + * Leave multicast groups. Factored out from in6_purgeaddr(). + * This entire work should only be done once, for the default FIB. + */ +static int +in6_purgeaddr_mc(struct ifnet *ifp, struct in6_ifaddr *ia, struct ifaddr *ifa0) { - struct ifnet *ifp = ifa->ifa_ifp; - struct in6_ifaddr *ia = (struct in6_ifaddr *) ifa; - struct in6_multi_mship *imm; struct sockaddr_in6 mltaddr, mltmask; - struct rtentry rt0; - struct sockaddr_dl gateway; - struct sockaddr_in6 mask, addr; - int plen, error; + struct in6_multi_mship *imm; struct rtentry *rt; - struct ifaddr *ifa0, *nifa; - - /* - * find another IPv6 address as the gateway for the - * link-local and node-local all-nodes multicast - * address routes - */ - IF_ADDR_LOCK(ifp); - TAILQ_FOREACH_SAFE(ifa0, &ifp->if_addrhead, ifa_link, nifa) { - if ((ifa0->ifa_addr->sa_family != AF_INET6) || - memcmp(&satosin6(ifa0->ifa_addr)->sin6_addr, - &ia->ia_addr.sin6_addr, - sizeof(struct in6_addr)) == 0) - continue; - else - break; - } - if (ifa0 != NULL) - ifa_ref(ifa0); - IF_ADDR_UNLOCK(ifp); - - /* - * Remove the loopback route to the interface address. - * The check for the current setting of "nd6_useloopback" - * is not needed. - */ - if (ia->ia_flags & IFA_RTSELF) { - error = ifa_del_loopback_route((struct ifaddr *)ia, - (struct sockaddr *)&ia->ia_addr); - if (error == 0) - ia->ia_flags &= ~IFA_RTSELF; - } - - /* stop DAD processing */ - nd6_dad_stop(ifa); - - IF_AFDATA_LOCK(ifp); - lla_lookup(LLTABLE6(ifp), (LLE_DELETE | LLE_IFADDR), - (struct sockaddr *)&ia->ia_addr); - IF_AFDATA_UNLOCK(ifp); - - /* - * initialize for rtmsg generation - */ - bzero(&gateway, sizeof(gateway)); - gateway.sdl_len = sizeof(gateway); - gateway.sdl_family = AF_LINK; - gateway.sdl_nlen = 0; - gateway.sdl_alen = ifp->if_addrlen; - /* */ - bzero(&rt0, sizeof(rt0)); - rt0.rt_gateway = (struct sockaddr *)&gateway; - memcpy(&mask, &ia->ia_prefixmask, sizeof(ia->ia_prefixmask)); - memcpy(&addr, &ia->ia_addr, sizeof(ia->ia_addr)); - rt_mask(&rt0) = (struct sockaddr *)&mask; - rt_key(&rt0) = (struct sockaddr *)&addr; - rt0.rt_flags = RTF_HOST | RTF_STATIC; - rt_newaddrmsg(RTM_DELETE, ifa, 0, &rt0); + struct sockaddr_in6 sin6; + int error; /* - * leave from multicast groups we have joined for the interface + * Leave from multicast groups we have joined for the interface. */ - while ((imm = ia->ia6_memberships.lh_first) != NULL) { + while ((imm = LIST_FIRST(&ia->ia6_memberships)) != NULL) { LIST_REMOVE(imm, i6mm_chain); in6_leavegroup(imm); } /* - * remove the link-local all-nodes address + * Remove the link-local all-nodes address. */ bzero(&mltmask, sizeof(mltmask)); mltmask.sin6_len = sizeof(struct sockaddr_in6); @@ -1277,43 +1236,51 @@ in6_purgeaddr(struct ifaddr *ifa) mltaddr.sin6_family = AF_INET6; mltaddr.sin6_addr = in6addr_linklocal_allnodes; - if ((error = in6_setscope(&mltaddr.sin6_addr, ifp, NULL)) != - 0) - goto cleanup; + if ((error = in6_setscope(&mltaddr.sin6_addr, ifp, NULL)) != 0) + return (error); - rt = rtalloc1((struct sockaddr *)&mltaddr, 0, 0UL); + /* + * As for the mltaddr above, proactively prepare the sin6 to avoid + * rtentry un- and re-locking. + */ + if (ifa0 != NULL) { + bzero(&sin6, sizeof(sin6)); + sin6.sin6_len = sizeof(sin6); + sin6.sin6_family = AF_INET6; + memcpy(&sin6.sin6_addr, &satosin6(ifa0->ifa_addr)->sin6_addr, + sizeof(sin6.sin6_addr)); + error = in6_setscope(&sin6.sin6_addr, ifa0->ifa_ifp, NULL); + if (error != 0) + return (error); + } + + rt = in6_rtalloc1((struct sockaddr *)&mltaddr, 0, 0UL, RT_DEFAULT_FIB); if (rt != NULL && rt->rt_gateway != NULL && (memcmp(&satosin6(rt->rt_gateway)->sin6_addr, &ia->ia_addr.sin6_addr, sizeof(ia->ia_addr.sin6_addr)) == 0)) { /* - * if no more IPv6 address exists on this interface - * then remove the multicast address route + * If no more IPv6 address exists on this interface then + * remove the multicast address route. */ if (ifa0 == NULL) { memcpy(&mltaddr.sin6_addr, &satosin6(rt_key(rt))->sin6_addr, sizeof(mltaddr.sin6_addr)); RTFREE_LOCKED(rt); - error = rtrequest(RTM_DELETE, (struct sockaddr *)&mltaddr, - (struct sockaddr *)&ia->ia_addr, - (struct sockaddr *)&mltmask, RTF_UP, - (struct rtentry **)0); + error = in6_rtrequest(RTM_DELETE, + (struct sockaddr *)&mltaddr, + (struct sockaddr *)&ia->ia_addr, + (struct sockaddr *)&mltmask, RTF_UP, + (struct rtentry **)0, RT_DEFAULT_FIB); if (error) - log(LOG_INFO, "in6_purgeaddr: link-local all-nodes" - "multicast address deletion error\n"); + log(LOG_INFO, "%s: link-local all-nodes " + "multicast address deletion error\n", + __func__); } else { /* - * replace the gateway of the route + * Replace the gateway of the route. */ - struct sockaddr_in6 sa; - - bzero(&sa, sizeof(sa)); - sa.sin6_len = sizeof(struct sockaddr_in6); - sa.sin6_family = AF_INET6; - memcpy(&sa.sin6_addr, &satosin6(ifa0->ifa_addr)->sin6_addr, - sizeof(sa.sin6_addr)); - in6_setscope(&sa.sin6_addr, ifa0->ifa_ifp, NULL); - memcpy(rt->rt_gateway, &sa, sizeof(sa)); + memcpy(rt->rt_gateway, &sin6, sizeof(sin6)); RTFREE_LOCKED(rt); } } else { @@ -1322,48 +1289,40 @@ in6_purgeaddr(struct ifaddr *ifa) } /* - * remove the node-local all-nodes address + * Remove the node-local all-nodes address. */ mltaddr.sin6_addr = in6addr_nodelocal_allnodes; - if ((error = in6_setscope(&mltaddr.sin6_addr, ifp, NULL)) != - 0) - goto cleanup; + if ((error = in6_setscope(&mltaddr.sin6_addr, ifp, NULL)) != 0) + return (error); - rt = rtalloc1((struct sockaddr *)&mltaddr, 0, 0UL); + rt = in6_rtalloc1((struct sockaddr *)&mltaddr, 0, 0UL, RT_DEFAULT_FIB); if (rt != NULL && rt->rt_gateway != NULL && (memcmp(&satosin6(rt->rt_gateway)->sin6_addr, &ia->ia_addr.sin6_addr, sizeof(ia->ia_addr.sin6_addr)) == 0)) { /* - * if no more IPv6 address exists on this interface - * then remove the multicast address route + * If no more IPv6 address exists on this interface then + * remove the multicast address route. */ if (ifa0 == NULL) { memcpy(&mltaddr.sin6_addr, &satosin6(rt_key(rt))->sin6_addr, sizeof(mltaddr.sin6_addr)); RTFREE_LOCKED(rt); - error = rtrequest(RTM_DELETE, (struct sockaddr *)&mltaddr, - (struct sockaddr *)&ia->ia_addr, - (struct sockaddr *)&mltmask, RTF_UP, - (struct rtentry **)0); - + error = in6_rtrequest(RTM_DELETE, + (struct sockaddr *)&mltaddr, + (struct sockaddr *)&ia->ia_addr, + (struct sockaddr *)&mltmask, RTF_UP, + (struct rtentry **)0, RT_DEFAULT_FIB); if (error) - log(LOG_INFO, "in6_purgeaddr: node-local all-nodes" - "multicast address deletion error\n"); + log(LOG_INFO, "%s: node-local all-nodes" + "multicast address deletion error\n", + __func__); } else { /* - * replace the gateway of the route + * Replace the gateway of the route. */ - struct sockaddr_in6 sa; - - bzero(&sa, sizeof(sa)); - sa.sin6_len = sizeof(struct sockaddr_in6); - sa.sin6_family = AF_INET6; - memcpy(&sa.sin6_addr, &satosin6(ifa0->ifa_addr)->sin6_addr, - sizeof(sa.sin6_addr)); - in6_setscope(&sa.sin6_addr, ifa0->ifa_ifp, NULL); - memcpy(rt->rt_gateway, &sa, sizeof(sa)); + memcpy(rt->rt_gateway, &sin6, sizeof(sin6)); RTFREE_LOCKED(rt); } } else { @@ -1371,31 +1330,91 @@ in6_purgeaddr(struct ifaddr *ifa) RTFREE_LOCKED(rt); } -cleanup: + return (0); +} + +void +in6_purgeaddr(struct ifaddr *ifa) +{ + struct ifnet *ifp = ifa->ifa_ifp; + struct in6_ifaddr *ia = (struct in6_ifaddr *) ifa; + struct sockaddr_dl gateway; + struct sockaddr_in6 mask, addr; + struct rtentry rt0; + int plen, error; + struct ifaddr *ifa0; + + /* + * find another IPv6 address as the gateway for the + * link-local and node-local all-nodes multicast + * address routes + */ + IF_ADDR_RLOCK(ifp); + TAILQ_FOREACH(ifa0, &ifp->if_addrhead, ifa_link) { + if ((ifa0->ifa_addr->sa_family != AF_INET6) || + memcmp(&satosin6(ifa0->ifa_addr)->sin6_addr, + &ia->ia_addr.sin6_addr, + sizeof(struct in6_addr)) == 0) + continue; + else + break; + } + if (ifa0 != NULL) + ifa_ref(ifa0); + IF_ADDR_RUNLOCK(ifp); + + /* + * Remove the loopback route to the interface address. + * The check for the current setting of "nd6_useloopback" + * is not needed. + */ + if (ia->ia_flags & IFA_RTSELF) { + error = ifa_del_loopback_route((struct ifaddr *)ia, + (struct sockaddr *)&ia->ia_addr); + if (error == 0) + ia->ia_flags &= ~IFA_RTSELF; + } + + /* stop DAD processing */ + nd6_dad_stop(ifa); + + /* Remove local address entry from lltable. */ + IF_AFDATA_LOCK(ifp); + lla_lookup(LLTABLE6(ifp), (LLE_DELETE | LLE_IFADDR), + (struct sockaddr *)&ia->ia_addr); + IF_AFDATA_UNLOCK(ifp); + + /* + * initialize for rtmsg generation + */ + bzero(&gateway, sizeof(gateway)); + gateway.sdl_len = sizeof(gateway); + gateway.sdl_family = AF_LINK; + gateway.sdl_nlen = 0; + gateway.sdl_alen = ifp->if_addrlen; + /* */ + bzero(&rt0, sizeof(rt0)); + rt0.rt_gateway = (struct sockaddr *)&gateway; + memcpy(&mask, &ia->ia_prefixmask, sizeof(ia->ia_prefixmask)); + memcpy(&addr, &ia->ia_addr, sizeof(ia->ia_addr)); + rt_mask(&rt0) = (struct sockaddr *)&mask; + rt_key(&rt0) = (struct sockaddr *)&addr; + rt0.rt_flags = RTF_HOST | RTF_STATIC; + rt_newaddrmsg(RTM_DELETE, ifa, 0, &rt0); + + /* Leave multicast groups. */ + error = in6_purgeaddr_mc(ifp, ia, ifa0); + if (ifa0 != NULL) ifa_free(ifa0); plen = in6_mask2len(&ia->ia_prefixmask.sin6_addr, NULL); /* XXX */ if ((ia->ia_flags & IFA_ROUTE) && plen == 128) { - int error; - struct sockaddr *dstaddr; - - /* - * use the interface address if configuring an - * interface address with a /128 prefix len - */ - if (ia->ia_dstaddr.sin6_family == AF_INET6) - dstaddr = (struct sockaddr *)&ia->ia_dstaddr; - else - dstaddr = (struct sockaddr *)&ia->ia_addr; - - error = rtrequest(RTM_DELETE, - (struct sockaddr *)dstaddr, - (struct sockaddr *)&ia->ia_addr, - (struct sockaddr *)&ia->ia_prefixmask, - ia->ia_flags | RTF_HOST, NULL); + error = rtinit(&(ia->ia_ifa), RTM_DELETE, ia->ia_flags | + (ia->ia_dstaddr.sin6_family == AF_INET6) ? RTF_HOST : 0); if (error != 0) - return; + log(LOG_INFO, "%s: err=%d, destination address delete " + "failed\n", __func__, error); ia->ia_flags &= ~IFA_ROUTE; } @@ -1407,9 +1426,9 @@ in6_unlink_ifa(struct in6_ifaddr *ia, struct ifnet *ifp) { int s = splnet(); - IF_ADDR_LOCK(ifp); + IF_ADDR_WLOCK(ifp); TAILQ_REMOVE(&ifp->if_addrhead, &ia->ia_ifa, ifa_link); - IF_ADDR_UNLOCK(ifp); + IF_ADDR_WUNLOCK(ifp); ifa_free(&ia->ia_ifa); /* if_addrhead */ /* @@ -1641,7 +1660,7 @@ in6_lifaddr_ioctl(struct socket *so, u_long cmd, caddr_t data, } } - IF_ADDR_LOCK(ifp); + IF_ADDR_RLOCK(ifp); TAILQ_FOREACH(ifa, &ifp->if_addrhead, ifa_link) { if (ifa->ifa_addr->sa_family != AF_INET6) continue; @@ -1662,7 +1681,9 @@ in6_lifaddr_ioctl(struct socket *so, u_long cmd, caddr_t data, if (IN6_ARE_ADDR_EQUAL(&candidate, &match)) break; } - IF_ADDR_UNLOCK(ifp); + if (ifa != NULL) + ifa_ref(ifa); + IF_ADDR_RUNLOCK(ifp); if (!ifa) return EADDRNOTAVAIL; ia = ifa2ia6(ifa); @@ -1674,16 +1695,20 @@ in6_lifaddr_ioctl(struct socket *so, u_long cmd, caddr_t data, bcopy(&ia->ia_addr, &iflr->addr, ia->ia_addr.sin6_len); error = sa6_recoverscope( (struct sockaddr_in6 *)&iflr->addr); - if (error != 0) + if (error != 0) { + ifa_free(ifa); return (error); + } if ((ifp->if_flags & IFF_POINTOPOINT) != 0) { bcopy(&ia->ia_dstaddr, &iflr->dstaddr, ia->ia_dstaddr.sin6_len); error = sa6_recoverscope( (struct sockaddr_in6 *)&iflr->dstaddr); - if (error != 0) + if (error != 0) { + ifa_free(ifa); return (error); + } } else bzero(&iflr->dstaddr, sizeof(iflr->dstaddr)); @@ -1691,6 +1716,7 @@ in6_lifaddr_ioctl(struct socket *so, u_long cmd, caddr_t data, in6_mask2len(&ia->ia_prefixmask.sin6_addr, NULL); iflr->flags = ia->ia6_flags; /* XXX */ + ifa_free(ifa); return 0; } else { @@ -1714,6 +1740,7 @@ in6_lifaddr_ioctl(struct socket *so, u_long cmd, caddr_t data, ia->ia_prefixmask.sin6_len); ifra.ifra_flags = ia->ia6_flags; + ifa_free(ifa); return in6_control(so, SIOCDIFADDR_IN6, (caddr_t)&ifra, ifp, td); } @@ -1724,8 +1751,7 @@ in6_lifaddr_ioctl(struct socket *so, u_long cmd, caddr_t data, } /* - * Initialize an interface's intetnet6 address - * and routing table entry. + * Initialize an interface's IPv6 address and routing table entry. */ static int in6_ifinit(struct ifnet *ifp, struct in6_ifaddr *ia, @@ -1740,13 +1766,13 @@ in6_ifinit(struct ifnet *ifp, struct in6_ifaddr *ia, * if this is its first address, * and to validate the address if necessary. */ - IF_ADDR_LOCK(ifp); + IF_ADDR_RLOCK(ifp); TAILQ_FOREACH(ifa, &ifp->if_addrhead, ifa_link) { if (ifa->ifa_addr->sa_family != AF_INET6) continue; ifacount++; } - IF_ADDR_UNLOCK(ifp); + IF_ADDR_RUNLOCK(ifp); ia->ia_addr = *sin6; @@ -1775,30 +1801,28 @@ in6_ifinit(struct ifnet *ifp, struct in6_ifaddr *ia, if (!(ia->ia_flags & IFA_ROUTE) && plen == 128 && ia->ia_dstaddr.sin6_family == AF_INET6) { int rtflags = RTF_UP | RTF_HOST; - - error = rtrequest(RTM_ADD, - (struct sockaddr *)&ia->ia_dstaddr, - (struct sockaddr *)&ia->ia_addr, - (struct sockaddr *)&ia->ia_prefixmask, - ia->ia_flags | rtflags, NULL); - if (error != 0) + error = rtinit(&ia->ia_ifa, RTM_ADD, ia->ia_flags | rtflags); + if (error) return (error); ia->ia_flags |= IFA_ROUTE; + /* + * Handle the case for ::1 . + */ + if (ifp->if_flags & IFF_LOOPBACK) + ia->ia_flags |= IFA_RTSELF; } /* * add a loopback route to self */ - if (!(ia->ia_flags & IFA_ROUTE) - && (V_nd6_useloopback - || (ifp->if_flags & IFF_LOOPBACK))) { + if (!(ia->ia_flags & IFA_RTSELF) && V_nd6_useloopback) { error = ifa_add_loopback_route((struct ifaddr *)ia, (struct sockaddr *)&ia->ia_addr); if (error == 0) ia->ia_flags |= IFA_RTSELF; } - /* Add ownaddr as loopback rtentry, if necessary (ex. on p2p link). */ + /* Add local address to lltable, if necessary (ex. on p2p link). */ if (newhost) { struct llentry *ln; struct rtentry rt; @@ -1806,11 +1830,7 @@ in6_ifinit(struct ifnet *ifp, struct in6_ifaddr *ia, struct sockaddr_in6 mask, addr; IF_AFDATA_LOCK(ifp); - ia->ia_ifa.ifa_rtrequest = NULL; - - /* XXX QL - * we need to report rt_newaddrmsg - */ + ia->ia_ifa.ifa_rtrequest = nd6_rtrequest; ln = lla_lookup(LLTABLE6(ifp), (LLE_CREATE | LLE_IFADDR | LLE_EXCLUSIVE), (struct sockaddr *)&ia->ia_addr); IF_AFDATA_UNLOCK(ifp); @@ -1837,6 +1857,7 @@ in6_ifinit(struct ifnet *ifp, struct in6_ifaddr *ia, rt_mask(&rt) = (struct sockaddr *)&mask; rt_key(&rt) = (struct sockaddr *)&addr; rt.rt_flags = RTF_UP | RTF_HOST | RTF_STATIC; + /* Announce arrival of local address to all FIBs. */ rt_newaddrmsg(RTM_ADD, &ia->ia_ifa, 0, &rt); } @@ -1852,7 +1873,7 @@ in6ifa_ifpforlinklocal(struct ifnet *ifp, int ignoreflags) { struct ifaddr *ifa; - IF_ADDR_LOCK(ifp); + IF_ADDR_RLOCK(ifp); TAILQ_FOREACH(ifa, &ifp->if_addrhead, ifa_link) { if (ifa->ifa_addr->sa_family != AF_INET6) continue; @@ -1864,7 +1885,7 @@ in6ifa_ifpforlinklocal(struct ifnet *ifp, int ignoreflags) break; } } - IF_ADDR_UNLOCK(ifp); + IF_ADDR_RUNLOCK(ifp); return ((struct in6_ifaddr *)ifa); } @@ -1879,7 +1900,7 @@ in6ifa_ifpwithaddr(struct ifnet *ifp, struct in6_addr *addr) { struct ifaddr *ifa; - IF_ADDR_LOCK(ifp); + IF_ADDR_RLOCK(ifp); TAILQ_FOREACH(ifa, &ifp->if_addrhead, ifa_link) { if (ifa->ifa_addr->sa_family != AF_INET6) continue; @@ -1888,7 +1909,7 @@ in6ifa_ifpwithaddr(struct ifnet *ifp, struct in6_addr *addr) break; } } - IF_ADDR_UNLOCK(ifp); + IF_ADDR_RUNLOCK(ifp); return ((struct in6_ifaddr *)ifa); } @@ -2091,7 +2112,7 @@ in6_ifawithifp(struct ifnet *ifp, struct in6_addr *dst) * If two or more, return one which matches the dst longest. * If none, return one of global addresses assigned other ifs. */ - IF_ADDR_LOCK(ifp); + IF_ADDR_RLOCK(ifp); TAILQ_FOREACH(ifa, &ifp->if_addrhead, ifa_link) { if (ifa->ifa_addr->sa_family != AF_INET6) continue; @@ -2125,12 +2146,10 @@ in6_ifawithifp(struct ifnet *ifp, struct in6_addr *dst) } if (besta) { ifa_ref(&besta->ia_ifa); - IF_ADDR_UNLOCK(ifp); + IF_ADDR_RUNLOCK(ifp); return (besta); } - IF_ADDR_UNLOCK(ifp); - IN6_IFADDR_RLOCK(); TAILQ_FOREACH(ifa, &ifp->if_addrhead, ifa_link) { if (ifa->ifa_addr->sa_family != AF_INET6) continue; @@ -2148,23 +2167,23 @@ in6_ifawithifp(struct ifnet *ifp, struct in6_addr *dst) if (ifa != NULL) ifa_ref(ifa); - IN6_IFADDR_RUNLOCK(); + IF_ADDR_RUNLOCK(ifp); return (struct in6_ifaddr *)ifa; } /* use the last-resort values, that are, deprecated addresses */ if (dep[0]) { ifa_ref((struct ifaddr *)dep[0]); - IN6_IFADDR_RUNLOCK(); + IF_ADDR_RUNLOCK(ifp); return dep[0]; } if (dep[1]) { ifa_ref((struct ifaddr *)dep[1]); - IN6_IFADDR_RUNLOCK(); + IF_ADDR_RUNLOCK(ifp); return dep[1]; } - IN6_IFADDR_RUNLOCK(); + IF_ADDR_RUNLOCK(ifp); return NULL; } @@ -2177,7 +2196,7 @@ in6_if_up(struct ifnet *ifp) struct ifaddr *ifa; struct in6_ifaddr *ia; - IF_ADDR_LOCK(ifp); + IF_ADDR_RLOCK(ifp); TAILQ_FOREACH(ifa, &ifp->if_addrhead, ifa_link) { if (ifa->ifa_addr->sa_family != AF_INET6) continue; @@ -2193,7 +2212,7 @@ in6_if_up(struct ifnet *ifp) arc4random() % (MAX_RTR_SOLICITATION_DELAY * hz)); } } - IF_ADDR_UNLOCK(ifp); + IF_ADDR_RUNLOCK(ifp); /* * special cases, like 6to4, are handled in in6_ifattach @@ -2248,8 +2267,7 @@ in6_setmaxmtu(void) struct ifnet *ifp; IFNET_RLOCK_NOSLEEP(); - for (ifp = TAILQ_FIRST(&V_ifnet); ifp; - ifp = TAILQ_NEXT(ifp, if_list)) { + TAILQ_FOREACH(ifp, &V_ifnet, if_list) { /* this function can be called during ifnet initialization */ if (!ifp->if_afdata[AF_INET6]) continue; @@ -2364,19 +2382,25 @@ in6_lltable_free(struct lltable *llt, struct llentry *lle) static void in6_lltable_prefix_free(struct lltable *llt, const struct sockaddr *prefix, - const struct sockaddr *mask) + const struct sockaddr *mask, + u_int flags) { const struct sockaddr_in6 *pfx = (const struct sockaddr_in6 *)prefix; const struct sockaddr_in6 *msk = (const struct sockaddr_in6 *)mask; struct llentry *lle, *next; register int i; + /* + * (flags & LLE_STATIC) means deleting all entries + * including static ND6 entries + */ for (i=0; i < LLTBL_HASHTBL_SIZE; i++) { LIST_FOREACH_SAFE(lle, &llt->lle_head[i], lle_next, next) { if (IN6_ARE_MASKED_ADDR_EQUAL( &((struct sockaddr_in6 *)L3_ADDR(lle))->sin6_addr, &pfx->sin6_addr, - &msk->sin6_addr)) { + &msk->sin6_addr) && + ((flags & LLE_STATIC) || !(lle->la_flags & LLE_STATIC))) { int canceled; canceled = callout_drain(&lle->la_timer); @@ -2400,8 +2424,10 @@ in6_lltable_rtcheck(struct ifnet *ifp, KASSERT(l3addr->sa_family == AF_INET6, ("sin_family %d", l3addr->sa_family)); + /* Our local addresses are always only installed on the default FIB. */ /* XXX rtalloc1 should take a const param */ - rt = rtalloc1(__DECONST(struct sockaddr *, l3addr), 0, 0); + rt = in6_rtalloc1(__DECONST(struct sockaddr *, l3addr), 0, 0, + RT_DEFAULT_FIB); if (rt == NULL || (rt->rt_flags & RTF_GATEWAY) || rt->rt_ifp != ifp) { struct ifaddr *ifa; /* @@ -2594,10 +2620,8 @@ in6_domifattach(struct ifnet *ifp) ext->scope6_id = scope6_ifattach(ifp); ext->lltable = lltable_init(ifp, AF_INET6); if (ext->lltable != NULL) { - ext->lltable->llt_new = in6_lltable_new; ext->lltable->llt_free = in6_lltable_free; ext->lltable->llt_prefix_free = in6_lltable_prefix_free; - ext->lltable->llt_rtcheck = in6_lltable_rtcheck; ext->lltable->llt_lookup = in6_lltable_lookup; ext->lltable->llt_dump = in6_lltable_dump; } diff --git a/freebsd/sys/netinet6/in6.h b/freebsd/sys/netinet6/in6.h index eadfc090..7abbfa40 100644 --- a/freebsd/sys/netinet6/in6.h +++ b/freebsd/sys/netinet6/in6.h @@ -78,7 +78,7 @@ /* * IPv6 port allocation rules should mirror the IPv4 rules and are controlled - * by the the net.inet.ip.portrange sysctl tree. The following defines exist + * by the net.inet.ip.portrange sysctl tree. The following defines exist * for compatibility with userland applications that need them. */ #if __BSD_VISIBLE diff --git a/freebsd/sys/netinet6/in6_gif.c b/freebsd/sys/netinet6/in6_gif.c index be915827..488a0106 100644 --- a/freebsd/sys/netinet6/in6_gif.c +++ b/freebsd/sys/netinet6/in6_gif.c @@ -230,6 +230,8 @@ in6_gif_output(struct ifnet *ifp, ip6->ip6_flow &= ~htonl(0xff << 20); ip6->ip6_flow |= htonl((u_int32_t)otos << 20); + M_SETFIB(m, sc->gif_fibnum); + if (dst->sin6_family != sin6_dst->sin6_family || !IN6_ARE_ADDR_EQUAL(&dst->sin6_addr, &sin6_dst->sin6_addr)) { /* cache route doesn't match */ @@ -247,7 +249,7 @@ in6_gif_output(struct ifnet *ifp, } if (sc->gif_ro6.ro_rt == NULL) { - rtalloc((struct route *)&sc->gif_ro6); + in6_rtalloc(&sc->gif_ro6, sc->gif_fibnum); if (sc->gif_ro6.ro_rt == NULL) { m_freem(m); return ENETUNREACH; @@ -404,7 +406,8 @@ gif_validate6(const struct ip6_hdr *ip6, struct gif_softc *sc, sin6.sin6_addr = ip6->ip6_src; sin6.sin6_scope_id = 0; /* XXX */ - rt = rtalloc1((struct sockaddr *)&sin6, 0, 0UL); + rt = in6_rtalloc1((struct sockaddr *)&sin6, 0, 0UL, + sc->gif_fibnum); if (!rt || rt->rt_ifp != ifp) { #if 0 char ip6buf[INET6_ADDRSTRLEN]; diff --git a/freebsd/sys/netinet6/in6_ifattach.c b/freebsd/sys/netinet6/in6_ifattach.c index c2134c6f..bb150eb1 100644 --- a/freebsd/sys/netinet6/in6_ifattach.c +++ b/freebsd/sys/netinet6/in6_ifattach.c @@ -249,7 +249,7 @@ in6_get_hw_ifid(struct ifnet *ifp, struct in6_addr *in6) static u_int8_t allone[8] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }; - IF_ADDR_LOCK(ifp); + IF_ADDR_RLOCK(ifp); TAILQ_FOREACH(ifa, &ifp->if_addrhead, ifa_link) { if (ifa->ifa_addr->sa_family != AF_LINK) continue; @@ -261,7 +261,7 @@ in6_get_hw_ifid(struct ifnet *ifp, struct in6_addr *in6) goto found; } - IF_ADDR_UNLOCK(ifp); + IF_ADDR_RUNLOCK(ifp); return -1; @@ -273,6 +273,7 @@ found: /* get EUI64 */ switch (ifp->if_type) { case IFT_ETHER: + case IFT_L2VLAN: case IFT_FDDI: case IFT_ISO88025: case IFT_ATM: @@ -287,7 +288,7 @@ found: /* look at IEEE802/EUI64 only */ if (addrlen != 8 && addrlen != 6) { - IF_ADDR_UNLOCK(ifp); + IF_ADDR_RUNLOCK(ifp); return -1; } @@ -297,11 +298,11 @@ found: * card insertion. */ if (bcmp(addr, allzero, addrlen) == 0) { - IF_ADDR_UNLOCK(ifp); + IF_ADDR_RUNLOCK(ifp); return -1; } if (bcmp(addr, allone, addrlen) == 0) { - IF_ADDR_UNLOCK(ifp); + IF_ADDR_RUNLOCK(ifp); return -1; } @@ -322,11 +323,11 @@ found: case IFT_ARCNET: if (addrlen != 1) { - IF_ADDR_UNLOCK(ifp); + IF_ADDR_RUNLOCK(ifp); return -1; } if (!addr[0]) { - IF_ADDR_UNLOCK(ifp); + IF_ADDR_RUNLOCK(ifp); return -1; } @@ -350,17 +351,17 @@ found: * identifier source (can be renumbered). * we don't do this. */ - IF_ADDR_UNLOCK(ifp); + IF_ADDR_RUNLOCK(ifp); return -1; default: - IF_ADDR_UNLOCK(ifp); + IF_ADDR_RUNLOCK(ifp); return -1; } /* sanity check: g bit must not indicate "group" */ if (EUI64_GROUP(in6)) { - IF_ADDR_UNLOCK(ifp); + IF_ADDR_RUNLOCK(ifp); return -1; } @@ -373,11 +374,11 @@ found: */ if ((in6->s6_addr[8] & ~(EUI64_GBIT | EUI64_UBIT)) == 0x00 && bcmp(&in6->s6_addr[9], allzero, 7) == 0) { - IF_ADDR_UNLOCK(ifp); + IF_ADDR_RUNLOCK(ifp); return -1; } - IF_ADDR_UNLOCK(ifp); + IF_ADDR_RUNLOCK(ifp); return 0; } @@ -410,7 +411,7 @@ get_ifid(struct ifnet *ifp0, struct ifnet *altifp, /* next, try to get it from some other hardware interface */ IFNET_RLOCK_NOSLEEP(); - for (ifp = V_ifnet.tqh_first; ifp; ifp = ifp->if_list.tqe_next) { + TAILQ_FOREACH(ifp, &V_ifnet, if_list) { if (ifp == ifp0) continue; if (in6_get_hw_ifid(ifp, in6) != 0) @@ -518,12 +519,8 @@ in6_ifattach_linklocal(struct ifnet *ifp, struct ifnet *altifp) } ia = in6ifa_ifpforlinklocal(ifp, 0); /* ia must not be NULL */ -#ifdef DIAGNOSTIC - if (!ia) { - panic("ia == NULL in in6_ifattach_linklocal"); - /* NOTREACHED */ - } -#endif + KASSERT(ia != NULL, ("%s: ia == NULL, ifp=%p", __func__, ifp)); + ifa_free(&ia->ia_ifa); /* @@ -798,7 +795,6 @@ in6_ifdetach(struct ifnet *ifp) struct ifaddr *ifa, *next; struct radix_node_head *rnh; struct rtentry *rt; - short rtflags; struct sockaddr_in6 sin6; struct in6_multi_mship *imm; @@ -824,26 +820,19 @@ in6_ifdetach(struct ifnet *ifp) /* * leave from multicast groups we have joined for the interface */ - while ((imm = ia->ia6_memberships.lh_first) != NULL) { + while ((imm = LIST_FIRST(&ia->ia6_memberships)) != NULL) { LIST_REMOVE(imm, i6mm_chain); in6_leavegroup(imm); } - /* remove from the routing table */ - if ((ia->ia_flags & IFA_ROUTE) && - (rt = rtalloc1((struct sockaddr *)&ia->ia_addr, 0, 0UL))) { - rtflags = rt->rt_flags; - RTFREE_LOCKED(rt); - rtrequest(RTM_DELETE, (struct sockaddr *)&ia->ia_addr, - (struct sockaddr *)&ia->ia_addr, - (struct sockaddr *)&ia->ia_prefixmask, - rtflags, (struct rtentry **)0); - } + /* Remove link-local from the routing table. */ + if (ia->ia_flags & IFA_ROUTE) + (void)rtinit(&ia->ia_ifa, RTM_DELETE, ia->ia_flags); /* remove from the linked list */ - IF_ADDR_LOCK(ifp); + IF_ADDR_WLOCK(ifp); TAILQ_REMOVE(&ifp->if_addrhead, ifa, ifa_link); - IF_ADDR_UNLOCK(ifp); + IF_ADDR_WUNLOCK(ifp); ifa_free(ifa); /* if_addrhead */ IN6_IFADDR_WLOCK(); @@ -867,7 +856,10 @@ in6_ifdetach(struct ifnet *ifp) */ nd6_purge(ifp); - /* remove route to link-local allnodes multicast (ff02::1) */ + /* + * Remove route to link-local allnodes multicast (ff02::1). + * These only get automatically installed for the default FIB. + */ bzero(&sin6, sizeof(sin6)); sin6.sin6_len = sizeof(struct sockaddr_in6); sin6.sin6_family = AF_INET6; @@ -876,10 +868,11 @@ in6_ifdetach(struct ifnet *ifp) /* XXX: should not fail */ return; /* XXX grab lock first to avoid LOR */ - rnh = rt_tables_get_rnh(0, AF_INET6); + rnh = rt_tables_get_rnh(RT_DEFAULT_FIB, AF_INET6); if (rnh != NULL) { RADIX_NODE_HEAD_LOCK(rnh); - rt = rtalloc1((struct sockaddr *)&sin6, 0, RTF_RNH_LOCKED); + rt = in6_rtalloc1((struct sockaddr *)&sin6, 0, RTF_RNH_LOCKED, + RT_DEFAULT_FIB); if (rt) { if (rt->rt_ifp == ifp) rtexpunge(rt); @@ -927,8 +920,7 @@ in6_tmpaddrtimer(void *arg) V_ip6_temp_regen_advance) * hz, in6_tmpaddrtimer, curvnet); bzero(nullbuf, sizeof(nullbuf)); - for (ifp = TAILQ_FIRST(&V_ifnet); ifp; - ifp = TAILQ_NEXT(ifp, if_list)) { + TAILQ_FOREACH(ifp, &V_ifnet, if_list) { ndi = ND_IFINFO(ifp); if (bcmp(ndi->randomid, nullbuf, sizeof(nullbuf)) != 0) { /* @@ -959,7 +951,7 @@ in6_purgemaddrs(struct ifnet *ifp) * We need to do this as IF_ADDR_LOCK() may be re-acquired * by code further down. */ - IF_ADDR_LOCK(ifp); + IF_ADDR_RLOCK(ifp); TAILQ_FOREACH(ifma, &ifp->if_multiaddrs, ifma_link) { if (ifma->ifma_addr->sa_family != AF_INET6 || ifma->ifma_protospec == NULL) @@ -967,7 +959,7 @@ in6_purgemaddrs(struct ifnet *ifp) inm = (struct in6_multi *)ifma->ifma_protospec; LIST_INSERT_HEAD(&purgeinms, inm, in6m_entry); } - IF_ADDR_UNLOCK(ifp); + IF_ADDR_RUNLOCK(ifp); LIST_FOREACH_SAFE(inm, &purgeinms, in6m_entry, tinm) { LIST_REMOVE(inm, in6m_entry); diff --git a/freebsd/sys/netinet6/in6_mcast.c b/freebsd/sys/netinet6/in6_mcast.c index 4d0637aa..86162cf8 100644 --- a/freebsd/sys/netinet6/in6_mcast.c +++ b/freebsd/sys/netinet6/in6_mcast.c @@ -401,7 +401,7 @@ in6_mc_get(struct ifnet *ifp, const struct in6_addr *group, * re-acquire around the call. */ IN6_MULTI_LOCK_ASSERT(); - IF_ADDR_LOCK(ifp); + IF_ADDR_WLOCK(ifp); inm = in6m_lookup_locked(ifp, group); if (inm != NULL) { @@ -425,11 +425,11 @@ in6_mc_get(struct ifnet *ifp, const struct in6_addr *group, * Check if a link-layer group is already associated * with this network-layer group on the given ifnet. */ - IF_ADDR_UNLOCK(ifp); + IF_ADDR_WUNLOCK(ifp); error = if_addmulti(ifp, (struct sockaddr *)&gsin6, &ifma); if (error != 0) return (error); - IF_ADDR_LOCK(ifp); + IF_ADDR_WLOCK(ifp); /* * If something other than netinet6 is occupying the link-layer @@ -456,7 +456,7 @@ in6_mc_get(struct ifnet *ifp, const struct in6_addr *group, goto out_locked; } - IF_ADDR_LOCK_ASSERT(ifp); + IF_ADDR_WLOCK_ASSERT(ifp); /* * A new in6_multi record is needed; allocate and initialize it. @@ -488,7 +488,7 @@ in6_mc_get(struct ifnet *ifp, const struct in6_addr *group, *pinm = inm; out_locked: - IF_ADDR_UNLOCK(ifp); + IF_ADDR_WUNLOCK(ifp); return (error); } @@ -1626,6 +1626,8 @@ in6p_get_source_filters(struct inpcb *inp, struct sockopt *sopt) * has asked for, but we always tell userland how big the * buffer really needs to be. */ + if (msfr.msfr_nsrcs > in6_mcast_maxsocksrc) + msfr.msfr_nsrcs = in6_mcast_maxsocksrc; tss = NULL; if (msfr.msfr_srcs != NULL && msfr.msfr_nsrcs > 0) { tss = malloc(sizeof(struct sockaddr_storage) * msfr.msfr_nsrcs, @@ -1715,7 +1717,7 @@ ip6_getmoptions(struct inpcb *inp, struct sockopt *sopt) if (im6o == NULL) optval = V_ip6_defmcasthlim; else - optval = im6o->im6o_multicast_loop; + optval = im6o->im6o_multicast_hlim; INP_WUNLOCK(inp); error = sooptcopyout(sopt, &optval, sizeof(u_int)); break; @@ -1765,7 +1767,7 @@ ip6_getmoptions(struct inpcb *inp, struct sockopt *sopt) * Returns NULL if no ifp could be found. */ static struct ifnet * -in6p_lookup_mcast_ifp(const struct inpcb *in6p __unused, +in6p_lookup_mcast_ifp(const struct inpcb *in6p, const struct sockaddr_in6 *gsin6) { struct route_in6 ro6; @@ -1781,11 +1783,8 @@ in6p_lookup_mcast_ifp(const struct inpcb *in6p __unused, ifp = NULL; memset(&ro6, 0, sizeof(struct route_in6)); memcpy(&ro6.ro_dst, gsin6, sizeof(struct sockaddr_in6)); -#ifdef notyet - rtalloc_ign_fib(&ro6, 0, inp ? inp->inp_inc.inc_fibnum : 0); -#else - rtalloc_ign((struct route *)&ro6, 0); -#endif + rtalloc_ign_fib((struct route *)&ro6, 0, + in6p ? in6p->inp_inc.inc_fibnum : RT_DEFAULT_FIB); if (ro6.ro_rt != NULL) { ifp = ro6.ro_rt->rt_ifp; KASSERT(ifp != NULL, ("%s: null ifp", __func__)); @@ -2720,7 +2719,7 @@ sysctl_ip6_mcast_filters(SYSCTL_HANDLER_ARGS) IN6_MULTI_LOCK(); - IF_ADDR_LOCK(ifp); + IF_ADDR_RLOCK(ifp); TAILQ_FOREACH(ifma, &ifp->if_multiaddrs, ifma_link) { if (ifma->ifma_addr->sa_family != AF_INET6 || ifma->ifma_protospec == NULL) @@ -2749,7 +2748,7 @@ sysctl_ip6_mcast_filters(SYSCTL_HANDLER_ARGS) break; } } - IF_ADDR_UNLOCK(ifp); + IF_ADDR_RUNLOCK(ifp); IN6_MULTI_UNLOCK(); diff --git a/freebsd/sys/netinet6/in6_pcb.c b/freebsd/sys/netinet6/in6_pcb.c index 4a1f65c5..44783f3c 100644 --- a/freebsd/sys/netinet6/in6_pcb.c +++ b/freebsd/sys/netinet6/in6_pcb.c @@ -272,8 +272,11 @@ in6_pcbbind(register struct inpcb *inp, struct sockaddr *nam, inp->in6p_laddr = sin6->sin6_addr; } if (lport == 0) { - if ((error = in6_pcbsetport(&inp->in6p_laddr, inp, cred)) != 0) + if ((error = in6_pcbsetport(&inp->in6p_laddr, inp, cred)) != 0) { + /* Undo an address bind that may have occurred. */ + inp->in6p_laddr = in6addr_any; return (error); + } } else { inp->inp_lport = lport; if (in_pcbinshash(inp) != 0) { diff --git a/freebsd/sys/netinet6/in6_proto.c b/freebsd/sys/netinet6/in6_proto.c index 39611537..1139c7d9 100644 --- a/freebsd/sys/netinet6/in6_proto.c +++ b/freebsd/sys/netinet6/in6_proto.c @@ -130,6 +130,7 @@ __FBSDID("$FreeBSD$"); /* * TCP/IP protocol family: IP6, ICMP6, UDP, TCP. */ +FEATURE(inet6, "Internet Protocol version 6"); extern struct domain inet6domain; static struct pr_usrreqs nousrreqs; @@ -185,38 +186,29 @@ struct ip6protosw inet6sw[] = { }, #ifdef SCTP { - .pr_type = SOCK_DGRAM, - .pr_domain = &inet6domain, - .pr_protocol = IPPROTO_SCTP, - .pr_flags = PR_WANTRCVD, - .pr_input = sctp6_input, - .pr_ctlinput = sctp6_ctlinput, - .pr_ctloutput = sctp_ctloutput, - .pr_drain = sctp_drain, - .pr_usrreqs = &sctp6_usrreqs + .pr_type = SOCK_SEQPACKET, + .pr_domain = &inet6domain, + .pr_protocol = IPPROTO_SCTP, + .pr_flags = PR_WANTRCVD, + .pr_input = sctp6_input, + .pr_ctlinput = sctp6_ctlinput, + .pr_ctloutput = sctp_ctloutput, + .pr_drain = sctp_drain, +#ifndef INET /* Do not call initialization twice. */ + .pr_init = sctp_init, +#endif + .pr_usrreqs = &sctp6_usrreqs }, { - .pr_type = SOCK_SEQPACKET, - .pr_domain = &inet6domain, - .pr_protocol = IPPROTO_SCTP, - .pr_flags = PR_WANTRCVD, - .pr_input = sctp6_input, - .pr_ctlinput = sctp6_ctlinput, - .pr_ctloutput = sctp_ctloutput, - .pr_drain = sctp_drain, - .pr_usrreqs = &sctp6_usrreqs -}, - -{ - .pr_type = SOCK_STREAM, - .pr_domain = &inet6domain, - .pr_protocol = IPPROTO_SCTP, - .pr_flags = PR_WANTRCVD, - .pr_input = sctp6_input, - .pr_ctlinput = sctp6_ctlinput, - .pr_ctloutput = sctp_ctloutput, - .pr_drain = sctp_drain, - .pr_usrreqs = &sctp6_usrreqs + .pr_type = SOCK_STREAM, + .pr_domain = &inet6domain, + .pr_protocol = IPPROTO_SCTP, + .pr_flags = PR_WANTRCVD, + .pr_input = sctp6_input, + .pr_ctlinput = sctp6_ctlinput, + .pr_ctloutput = sctp_ctloutput, + .pr_drain = sctp_drain, + .pr_usrreqs = &sctp6_usrreqs }, #endif /* SCTP */ { diff --git a/freebsd/sys/netinet6/in6_rmx.c b/freebsd/sys/netinet6/in6_rmx.c index fee58221..a4bdb3af 100644 --- a/freebsd/sys/netinet6/in6_rmx.c +++ b/freebsd/sys/netinet6/in6_rmx.c @@ -61,19 +61,6 @@ * */ -/* - * This code does two things necessary for the enhanced TCP metrics to - * function in a useful manner: - * 1) It marks all non-host routes as `cloning', thus ensuring that - * every actual reference to such a route actually gets turned - * into a reference to a host route to the specific destination - * requested. - * 2) When such routes lose all their references, it arranges for them - * to be deleted in some random collection of circumstances, so that - * a large quantity of stale routing data is not kept in kernel memory - * indefinitely. See in6_rtqtimo() below for the exact mechanism. - */ - #include <sys/cdefs.h> __FBSDID("$FreeBSD$"); @@ -113,8 +100,6 @@ extern int in6_inithead(void **head, int off); extern int in6_detachhead(void **head, int off); #endif -#define RTPRF_OURS RTF_PROTO3 /* set on routes we manage */ - /* * Do what we need to do when inserting a route. */ @@ -170,7 +155,8 @@ in6_addroute(void *v_arg, void *n_arg, struct radix_node_head *head, * net route entry, 3ffe:0501:: -> if0. * This case should not raise an error. */ - rt2 = rtalloc1((struct sockaddr *)sin6, 0, RTF_RNH_LOCKED); + rt2 = in6_rtalloc1((struct sockaddr *)sin6, 0, RTF_RNH_LOCKED, + rt->rt_fibnum); if (rt2) { if (((rt2->rt_flags & (RTF_HOST|RTF_GATEWAY)) == 0) && rt2->rt_gateway @@ -184,42 +170,8 @@ in6_addroute(void *v_arg, void *n_arg, struct radix_node_head *head, return (ret); } -/* - * This code is the inverse of in6_clsroute: on first reference, if we - * were managing the route, stop doing so and set the expiration timer - * back off again. - */ -static struct radix_node * -in6_matroute(void *v_arg, struct radix_node_head *head) -{ - struct radix_node *rn = rn_match(v_arg, head); - struct rtentry *rt = (struct rtentry *)rn; - - if (rt) { - RT_LOCK(rt); - if (rt->rt_flags & RTPRF_OURS) { - rt->rt_flags &= ~RTPRF_OURS; - rt->rt_rmx.rmx_expire = 0; - } - RT_UNLOCK(rt); - } - return rn; -} - SYSCTL_DECL(_net_inet6_ip6); -static VNET_DEFINE(int, rtq_reallyold6) = 60*60; - /* one hour is ``really old'' */ -#define V_rtq_reallyold6 VNET(rtq_reallyold6) -SYSCTL_VNET_INT(_net_inet6_ip6, IPV6CTL_RTEXPIRE, rtexpire, CTLFLAG_RW, - &VNET_NAME(rtq_reallyold6) , 0, ""); - -static VNET_DEFINE(int, rtq_minreallyold6) = 10; - /* never automatically crank down to less */ -#define V_rtq_minreallyold6 VNET(rtq_minreallyold6) -SYSCTL_VNET_INT(_net_inet6_ip6, IPV6CTL_RTMINEXPIRE, rtminexpire, CTLFLAG_RW, - &VNET_NAME(rtq_minreallyold6) , 0, ""); - static VNET_DEFINE(int, rtq_toomany6) = 128; /* 128 cached routes is ``too many'' */ #define V_rtq_toomany6 VNET(rtq_toomany6) @@ -237,114 +189,6 @@ struct rtqk_arg { }; /* - * Get rid of old routes. When draining, this deletes everything, even when - * the timeout is not expired yet. When updating, this makes sure that - * nothing has a timeout longer than the current value of rtq_reallyold6. - */ -static int -in6_rtqkill(struct radix_node *rn, void *rock) -{ - struct rtqk_arg *ap = rock; - struct rtentry *rt = (struct rtentry *)rn; - int err; - - RADIX_NODE_HEAD_WLOCK_ASSERT(ap->rnh); - - if (rt->rt_flags & RTPRF_OURS) { - ap->found++; - - if (ap->draining || rt->rt_rmx.rmx_expire <= time_uptime) { - if (rt->rt_refcnt > 0) - panic("rtqkill route really not free"); - - err = rtrequest(RTM_DELETE, - (struct sockaddr *)rt_key(rt), - rt->rt_gateway, rt_mask(rt), - rt->rt_flags|RTF_RNH_LOCKED, 0); - if (err) { - log(LOG_WARNING, "in6_rtqkill: error %d", err); - } else { - ap->killed++; - } - } else { - if (ap->updating - && (rt->rt_rmx.rmx_expire - time_uptime - > V_rtq_reallyold6)) { - rt->rt_rmx.rmx_expire = time_uptime - + V_rtq_reallyold6; - } - ap->nextstop = lmin(ap->nextstop, - rt->rt_rmx.rmx_expire); - } - } - - return 0; -} - -#define RTQ_TIMEOUT 60*10 /* run no less than once every ten minutes */ -static VNET_DEFINE(int, rtq_timeout6) = RTQ_TIMEOUT; -static VNET_DEFINE(struct callout, rtq_timer6); - -#define V_rtq_timeout6 VNET(rtq_timeout6) -#define V_rtq_timer6 VNET(rtq_timer6) - -static void -in6_rtqtimo(void *rock) -{ - CURVNET_SET_QUIET((struct vnet *) rock); - struct radix_node_head *rnh; - struct rtqk_arg arg; - struct timeval atv; - static time_t last_adjusted_timeout = 0; - - rnh = rt_tables_get_rnh(0, AF_INET6); - if (rnh == NULL) { - CURVNET_RESTORE(); - return; - } - arg.found = arg.killed = 0; - arg.rnh = rnh; - arg.nextstop = time_uptime + V_rtq_timeout6; - arg.draining = arg.updating = 0; - RADIX_NODE_HEAD_LOCK(rnh); - rnh->rnh_walktree(rnh, in6_rtqkill, &arg); - RADIX_NODE_HEAD_UNLOCK(rnh); - - /* - * Attempt to be somewhat dynamic about this: - * If there are ``too many'' routes sitting around taking up space, - * then crank down the timeout, and see if we can't make some more - * go away. However, we make sure that we will never adjust more - * than once in rtq_timeout6 seconds, to keep from cranking down too - * hard. - */ - if ((arg.found - arg.killed > V_rtq_toomany6) - && (time_uptime - last_adjusted_timeout >= V_rtq_timeout6) - && V_rtq_reallyold6 > V_rtq_minreallyold6) { - V_rtq_reallyold6 = 2*V_rtq_reallyold6 / 3; - if (V_rtq_reallyold6 < V_rtq_minreallyold6) { - V_rtq_reallyold6 = V_rtq_minreallyold6; - } - - last_adjusted_timeout = time_uptime; -#ifdef DIAGNOSTIC - log(LOG_DEBUG, "in6_rtqtimo: adjusted rtq_reallyold6 to %d", - V_rtq_reallyold6); -#endif - arg.found = arg.killed = 0; - arg.updating = 1; - RADIX_NODE_HEAD_LOCK(rnh); - rnh->rnh_walktree(rnh, in6_rtqkill, &arg); - RADIX_NODE_HEAD_UNLOCK(rnh); - } - - atv.tv_usec = 0; - atv.tv_sec = arg.nextstop - time_uptime; - callout_reset(&V_rtq_timer6, tvtohz(&atv), in6_rtqtimo, rock); - CURVNET_RESTORE(); -} - -/* * Age old PMTUs. */ struct mtuex_arg { @@ -379,31 +223,33 @@ in6_mtuexpire(struct radix_node *rn, void *rock) #define MTUTIMO_DEFAULT (60*1) static void -in6_mtutimo(void *rock) +in6_mtutimo_one(struct radix_node_head *rnh) { - CURVNET_SET_QUIET((struct vnet *) rock); - struct radix_node_head *rnh; struct mtuex_arg arg; - struct timeval atv; - rnh = rt_tables_get_rnh(0, AF_INET6); - if (rnh == NULL) { - CURVNET_RESTORE(); - return; - } arg.rnh = rnh; arg.nextstop = time_uptime + MTUTIMO_DEFAULT; RADIX_NODE_HEAD_LOCK(rnh); rnh->rnh_walktree(rnh, in6_mtuexpire, &arg); RADIX_NODE_HEAD_UNLOCK(rnh); +} - atv.tv_usec = 0; - atv.tv_sec = arg.nextstop - time_uptime; - if (atv.tv_sec < 0) { - printf("invalid mtu expiration time on routing table\n"); - arg.nextstop = time_uptime + 30; /* last resort */ - atv.tv_sec = 30; +static void +in6_mtutimo(void *rock) +{ + CURVNET_SET_QUIET((struct vnet *) rock); + struct radix_node_head *rnh; + struct timeval atv; + u_int fibnum; + + for (fibnum = 0; fibnum < rt_numfibs; fibnum++) { + rnh = rt_tables_get_rnh(fibnum, AF_INET6); + if (rnh != NULL) + in6_mtutimo_one(rnh); } + + atv.tv_sec = MTUTIMO_DEFAULT; + atv.tv_usec = 0; callout_reset(&V_rtq_mtutimer, tvtohz(&atv), in6_mtutimo, rock); CURVNET_RESTORE(); } @@ -415,6 +261,9 @@ in6_mtutimo(void *rock) * value should be so just use that).. FIX AFTER RELENG_7 is MFC'd * see also comments in in_inithead() vfs_export.c and domain.h */ +static VNET_DEFINE(int, _in6_rt_was_here); +#define V__in6_rt_was_here VNET(_in6_rt_was_here) + int in6_inithead(void **head, int off) { @@ -427,13 +276,14 @@ in6_inithead(void **head, int off) return 1; /* only do the rest for the real thing */ rnh = *head; - KASSERT(rnh == rt_tables_get_rnh(0, AF_INET6), ("rnh?")); rnh->rnh_addaddr = in6_addroute; - rnh->rnh_matchaddr = in6_matroute; - callout_init(&V_rtq_timer6, CALLOUT_MPSAFE); - callout_init(&V_rtq_mtutimer, CALLOUT_MPSAFE); - in6_rtqtimo(curvnet); /* kick off timeout first time */ - in6_mtutimo(curvnet); /* kick off timeout first time */ + + if (V__in6_rt_was_here == 0) { + callout_init(&V_rtq_mtutimer, CALLOUT_MPSAFE); + in6_mtutimo(curvnet); /* kick off timeout first time */ + V__in6_rt_was_here = 1; + } + return 1; } @@ -442,8 +292,47 @@ int in6_detachhead(void **head, int off) { - callout_drain(&V_rtq_timer6); callout_drain(&V_rtq_mtutimer); return (1); } #endif + +/* + * Extended API for IPv6 FIB support. + */ +void +in6_rtredirect(struct sockaddr *dst, struct sockaddr *gw, struct sockaddr *nm, + int flags, struct sockaddr *src, u_int fibnum) +{ + + rtredirect_fib(dst, gw, nm, flags, src, fibnum); +} + +int +in6_rtrequest(int req, struct sockaddr *dst, struct sockaddr *gw, + struct sockaddr *mask, int flags, struct rtentry **ret_nrt, u_int fibnum) +{ + + return (rtrequest_fib(req, dst, gw, mask, flags, ret_nrt, fibnum)); +} + +void +in6_rtalloc(struct route_in6 *ro, u_int fibnum) +{ + + rtalloc_ign_fib((struct route *)ro, 0ul, fibnum); +} + +void +in6_rtalloc_ign(struct route_in6 *ro, u_long ignflags, u_int fibnum) +{ + + rtalloc_ign_fib((struct route *)ro, ignflags, fibnum); +} + +struct rtentry * +in6_rtalloc1(struct sockaddr *dst, int report, u_long ignflags, u_int fibnum) +{ + + return (rtalloc1_fib(dst, report, ignflags, fibnum)); +} diff --git a/freebsd/sys/netinet6/in6_src.c b/freebsd/sys/netinet6/in6_src.c index f7cd642e..4f20ee54 100644 --- a/freebsd/sys/netinet6/in6_src.c +++ b/freebsd/sys/netinet6/in6_src.c @@ -131,9 +131,10 @@ VNET_DEFINE(int, ip6_prefer_tempaddr) = 0; static int selectroute __P((struct sockaddr_in6 *, struct ip6_pktopts *, struct ip6_moptions *, struct route_in6 *, struct ifnet **, - struct rtentry **, int)); + struct rtentry **, int, u_int)); static int in6_selectif __P((struct sockaddr_in6 *, struct ip6_pktopts *, - struct ip6_moptions *, struct route_in6 *ro, struct ifnet **)); + struct ip6_moptions *, struct route_in6 *ro, struct ifnet **, + struct ifnet *, u_int)); static struct in6_addrpolicy *lookup_addrsel_policy(struct sockaddr_in6 *); @@ -184,7 +185,7 @@ in6_selectsrc(struct sockaddr_in6 *dstsock, struct ip6_pktopts *opts, struct ifnet **ifpp, struct in6_addr *srcp) { struct in6_addr dst, tmp; - struct ifnet *ifp = NULL; + struct ifnet *ifp = NULL, *oifp = NULL; struct in6_ifaddr *ia = NULL, *ia_best = NULL; struct in6_pktinfo *pi = NULL; int dst_scope = -1, best_scope = -1, best_matchlen = -1; @@ -197,8 +198,18 @@ in6_selectsrc(struct sockaddr_in6 *dstsock, struct ip6_pktopts *opts, KASSERT(srcp != NULL, ("%s: srcp is NULL", __func__)); dst = dstsock->sin6_addr; /* make a copy for local operation */ - if (ifpp) + if (ifpp) { + /* + * Save a possibly passed in ifp for in6_selectsrc. Only + * neighbor discovery code should use this feature, where + * we may know the interface but not the FIB number holding + * the connected subnet in case someone deleted it from the + * default FIB and we need to check the interface. + */ + if (*ifpp != NULL) + oifp = *ifpp; *ifpp = NULL; + } if (inp != NULL) { INP_LOCK_ASSERT(inp); @@ -219,7 +230,9 @@ in6_selectsrc(struct sockaddr_in6 *dstsock, struct ip6_pktopts *opts, struct in6_ifaddr *ia6; /* get the outgoing interface */ - if ((error = in6_selectif(dstsock, opts, mopts, ro, &ifp)) != 0) + if ((error = in6_selectif(dstsock, opts, mopts, ro, &ifp, oifp, + (inp != NULL) ? inp->inp_inc.inc_fibnum : RT_DEFAULT_FIB)) + != 0) return (error); /* @@ -283,7 +296,8 @@ in6_selectsrc(struct sockaddr_in6 *dstsock, struct ip6_pktopts *opts, * the outgoing interface and the destination address. */ /* get the outgoing interface */ - if ((error = in6_selectif(dstsock, opts, mopts, ro, &ifp)) != 0) + if ((error = in6_selectif(dstsock, opts, mopts, ro, &ifp, oifp, + (inp != NULL) ? inp->inp_inc.inc_fibnum : RT_DEFAULT_FIB)) != 0) return (error); #ifdef DIAGNOSTIC @@ -506,7 +520,7 @@ in6_selectsrc(struct sockaddr_in6 *dstsock, struct ip6_pktopts *opts, static int selectroute(struct sockaddr_in6 *dstsock, struct ip6_pktopts *opts, struct ip6_moptions *mopts, struct route_in6 *ro, - struct ifnet **retifp, struct rtentry **retrt, int norouteok) + struct ifnet **retifp, struct rtentry **retrt, int norouteok, u_int fibnum) { int error = 0; struct ifnet *ifp = NULL; @@ -583,7 +597,7 @@ selectroute(struct sockaddr_in6 *dstsock, struct ip6_pktopts *opts, if (ron->ro_rt == NULL) { - rtalloc((struct route *)ron); /* multi path case? */ + in6_rtalloc(ron, fibnum); /* multi path case? */ if (ron->ro_rt == NULL) { if (ron->ro_rt) { RTFREE(ron->ro_rt); @@ -596,9 +610,9 @@ selectroute(struct sockaddr_in6 *dstsock, struct ip6_pktopts *opts, rt = ron->ro_rt; ifp = rt->rt_ifp; - IF_AFDATA_LOCK(ifp); + IF_AFDATA_RLOCK(ifp); la = lla_lookup(LLTABLE6(ifp), 0, (struct sockaddr *)&sin6_next->sin6_addr); - IF_AFDATA_UNLOCK(ifp); + IF_AFDATA_RUNLOCK(ifp); if (la != NULL) LLE_RUNLOCK(la); else { @@ -618,7 +632,7 @@ selectroute(struct sockaddr_in6 *dstsock, struct ip6_pktopts *opts, *satosin6(&ron->ro_dst) = *sin6_next; } if (ron->ro_rt == NULL) { - rtalloc((struct route *)ron); /* multi path case? */ + in6_rtalloc(ron, fibnum); /* multi path case? */ if (ron->ro_rt == NULL || !(ron->ro_rt->rt_flags & RTF_LLINFO)) { if (ron->ro_rt) { @@ -663,11 +677,11 @@ selectroute(struct sockaddr_in6 *dstsock, struct ip6_pktopts *opts, sa6->sin6_scope_id = 0; #ifdef RADIX_MPATH - rtalloc_mpath((struct route *)ro, - ntohl(sa6->sin6_addr.s6_addr32[3])); + rtalloc_mpath_fib((struct route *)ro, + ntohl(sa6->sin6_addr.s6_addr32[3]), fibnum); #else - ro->ro_rt = rtalloc1(&((struct route *)ro) - ->ro_dst, 0, 0UL); + ro->ro_rt = in6_rtalloc1((struct sockaddr *) + &ro->ro_dst, 0, 0UL, fibnum); if (ro->ro_rt) RT_UNLOCK(ro->ro_rt); #endif @@ -748,21 +762,29 @@ selectroute(struct sockaddr_in6 *dstsock, struct ip6_pktopts *opts, static int in6_selectif(struct sockaddr_in6 *dstsock, struct ip6_pktopts *opts, - struct ip6_moptions *mopts, struct route_in6 *ro, struct ifnet **retifp) + struct ip6_moptions *mopts, struct route_in6 *ro, struct ifnet **retifp, + struct ifnet *oifp, u_int fibnum) { int error; struct route_in6 sro; struct rtentry *rt = NULL; + KASSERT(retifp != NULL, ("%s: retifp is NULL", __func__)); + if (ro == NULL) { bzero(&sro, sizeof(sro)); ro = &sro; } if ((error = selectroute(dstsock, opts, mopts, ro, retifp, - &rt, 1)) != 0) { + &rt, 1, fibnum)) != 0) { if (ro == &sro && rt && rt == sro.ro_rt) RTFREE(rt); + /* Help ND. See oifp comment in in6_selectsrc(). */ + if (oifp != NULL && fibnum == RT_DEFAULT_FIB) { + *retifp = oifp; + error = 0; + } return (error); } @@ -797,7 +819,10 @@ in6_selectif(struct sockaddr_in6 *dstsock, struct ip6_pktopts *opts, } /* - * clone - meaningful only for bsdi and freebsd + * Public wrapper function to selectroute(). + * + * XXX-BZ in6_selectroute() should and will grow the FIB argument. The + * in6_selectroute_fib() function is only there for backward compat on stable. */ int in6_selectroute(struct sockaddr_in6 *dstsock, struct ip6_pktopts *opts, @@ -806,9 +831,21 @@ in6_selectroute(struct sockaddr_in6 *dstsock, struct ip6_pktopts *opts, { return (selectroute(dstsock, opts, mopts, ro, retifp, - retrt, 0)); + retrt, 0, RT_DEFAULT_FIB)); } +#ifndef BURN_BRIDGES +int +in6_selectroute_fib(struct sockaddr_in6 *dstsock, struct ip6_pktopts *opts, + struct ip6_moptions *mopts, struct route_in6 *ro, + struct ifnet **retifp, struct rtentry **retrt, u_int fibnum) +{ + + return (selectroute(dstsock, opts, mopts, ro, retifp, + retrt, 0, fibnum)); +} +#endif + /* * Default hop limit selection. The precedence is as follows: * 1. Hoplimit value specified via ioctl. @@ -832,7 +869,7 @@ in6_selecthlim(struct inpcb *in6p, struct ifnet *ifp) ro6.ro_dst.sin6_family = AF_INET6; ro6.ro_dst.sin6_len = sizeof(struct sockaddr_in6); ro6.ro_dst.sin6_addr = in6p->in6p_faddr; - rtalloc((struct route *)&ro6); + in6_rtalloc(&ro6, in6p->inp_inc.inc_fibnum); if (ro6.ro_rt) { lifp = ro6.ro_rt->rt_ifp; RTFREE(ro6.ro_rt); @@ -852,9 +889,11 @@ int in6_pcbsetport(struct in6_addr *laddr, struct inpcb *inp, struct ucred *cred) { struct socket *so = inp->inp_socket; - u_int16_t lport = 0, first, last, *lastport; - int count, error, wild = 0, dorandom; + u_int16_t lport = 0; + int error, wild = 0; +#ifdef INVARIANTS struct inpcbinfo *pcbinfo = inp->inp_pcbinfo; +#endif INP_INFO_WLOCK_ASSERT(pcbinfo); INP_WLOCK_ASSERT(inp); @@ -870,74 +909,9 @@ in6_pcbsetport(struct in6_addr *laddr, struct inpcb *inp, struct ucred *cred) inp->inp_flags |= INP_ANONPORT; - if (inp->inp_flags & INP_HIGHPORT) { - first = V_ipport_hifirstauto; /* sysctl */ - last = V_ipport_hilastauto; - lastport = &pcbinfo->ipi_lasthi; - } else if (inp->inp_flags & INP_LOWPORT) { - error = priv_check_cred(cred, PRIV_NETINET_RESERVEDPORT, 0); - if (error) - return error; - first = V_ipport_lowfirstauto; /* 1023 */ - last = V_ipport_lowlastauto; /* 600 */ - lastport = &pcbinfo->ipi_lastlow; - } else { - first = V_ipport_firstauto; /* sysctl */ - last = V_ipport_lastauto; - lastport = &pcbinfo->ipi_lastport; - } - - /* - * For UDP, use random port allocation as long as the user - * allows it. For TCP (and as of yet unknown) connections, - * use random port allocation only if the user allows it AND - * ipport_tick() allows it. - */ - if (V_ipport_randomized && - (!V_ipport_stoprandom || pcbinfo == &V_udbinfo)) - dorandom = 1; - else - dorandom = 0; - /* - * It makes no sense to do random port allocation if - * we have the only port available. - */ - if (first == last) - dorandom = 0; - /* Make sure to not include UDP packets in the count. */ - if (pcbinfo != &V_udbinfo) - V_ipport_tcpallocs++; - - /* - * Instead of having two loops further down counting up or down - * make sure that first is always <= last and go with only one - * code path implementing all logic. - */ - if (first > last) { - u_int16_t aux; - - aux = first; - first = last; - last = aux; - } - - if (dorandom) - *lastport = first + (arc4random() % (last - first)); - - count = last - first; - - do { - if (count-- < 0) { /* completely used? */ - /* Undo an address bind that may have occurred. */ - inp->in6p_laddr = in6addr_any; - return (EADDRNOTAVAIL); - } - ++*lastport; - if (*lastport < first || *lastport > last) - *lastport = first; - lport = htons(*lastport); - } while (in6_pcblookup_local(pcbinfo, &inp->in6p_laddr, - lport, wild, cred)); + error = in_pcb_lport(inp, NULL, &lport, cred, wild); + if (error != 0) + return (error); inp->inp_lport = lport; if (in_pcbinshash(inp) != 0) { diff --git a/freebsd/sys/netinet6/in6_var.h b/freebsd/sys/netinet6/in6_var.h index 00342fde..793eb540 100644 --- a/freebsd/sys/netinet6/in6_var.h +++ b/freebsd/sys/netinet6/in6_var.h @@ -704,9 +704,9 @@ in6m_lookup(struct ifnet *ifp, const struct in6_addr *mcaddr) struct in6_multi *inm; IN6_MULTI_LOCK(); - IF_ADDR_LOCK(ifp); + IF_ADDR_RLOCK(ifp); inm = in6m_lookup_locked(ifp, mcaddr); - IF_ADDR_UNLOCK(ifp); + IF_ADDR_RUNLOCK(ifp); IN6_MULTI_UNLOCK(); return (inm); @@ -779,8 +779,18 @@ void in6_ifremloop(struct ifaddr *); void in6_ifaddloop(struct ifaddr *); int in6_is_addr_deprecated __P((struct sockaddr_in6 *)); -struct inpcb; -int in6_src_ioctl __P((u_long, caddr_t)); +int in6_src_ioctl __P((u_long, caddr_t)); + +/* + * Extended API for IPv6 FIB support. + */ +void in6_rtredirect(struct sockaddr *, struct sockaddr *, struct sockaddr *, + int, struct sockaddr *, u_int); +int in6_rtrequest(int, struct sockaddr *, struct sockaddr *, + struct sockaddr *, int, struct rtentry **, u_int); +void in6_rtalloc(struct route_in6 *, u_int); +void in6_rtalloc_ign(struct route_in6 *, u_long, u_int); +struct rtentry *in6_rtalloc1(struct sockaddr *, int, u_long, u_int); #endif /* _KERNEL */ #endif /* _NETINET6_IN6_VAR_H_ */ diff --git a/freebsd/sys/netinet6/ip6_forward.c b/freebsd/sys/netinet6/ip6_forward.c index 2f9073c8..a3d3d8f4 100644 --- a/freebsd/sys/netinet6/ip6_forward.c +++ b/freebsd/sys/netinet6/ip6_forward.c @@ -354,7 +354,7 @@ skip_ipsec: dst->sin6_family = AF_INET6; dst->sin6_addr = ip6->ip6_dst; - rin6.ro_rt = rtalloc1((struct sockaddr *)dst, 0, 0); + rin6.ro_rt = in6_rtalloc1((struct sockaddr *)dst, 0, 0, M_GETFIB(m)); if (rin6.ro_rt != NULL) RT_UNLOCK(rin6.ro_rt); else { diff --git a/freebsd/sys/netinet6/ip6_input.c b/freebsd/sys/netinet6/ip6_input.c index 568ec167..47d9e690 100644 --- a/freebsd/sys/netinet6/ip6_input.c +++ b/freebsd/sys/netinet6/ip6_input.c @@ -544,10 +544,10 @@ passin: dst6.sin6_len = sizeof(struct sockaddr_in6); dst6.sin6_addr = ip6->ip6_dst; ifp = m->m_pkthdr.rcvif; - IF_AFDATA_LOCK(ifp); + IF_AFDATA_RLOCK(ifp); lle = lla_lookup(LLTABLE6(ifp), 0, (struct sockaddr *)&dst6); - IF_AFDATA_UNLOCK(ifp); + IF_AFDATA_RUNLOCK(ifp); if ((lle != NULL) && (lle->la_flags & LLE_IFADDR)) { struct ifaddr *ifa; struct in6_ifaddr *ia6; @@ -556,7 +556,7 @@ passin: bad = 1; #define sa_equal(a1, a2) \ (bcmp((a1), (a2), ((a1))->sin6_len) == 0) - IF_ADDR_LOCK(ifp); + IF_ADDR_RLOCK(ifp); TAILQ_FOREACH(ifa, &ifp->if_addrhead, ifa_link) { if (ifa->ifa_addr->sa_family != dst6.sin6_family) continue; @@ -588,7 +588,7 @@ passin: ip6_sprintf(ip6bufs, &ip6->ip6_src), ip6_sprintf(ip6bufd, &ip6->ip6_dst))); } - IF_ADDR_UNLOCK(ifp); + IF_ADDR_RUNLOCK(ifp); LLE_RUNLOCK(lle); if (bad) goto bad; @@ -605,7 +605,7 @@ passin: dst->sin6_len = sizeof(struct sockaddr_in6); dst->sin6_family = AF_INET6; dst->sin6_addr = ip6->ip6_dst; - rin6.ro_rt = rtalloc1((struct sockaddr *)dst, 0, 0); + rin6.ro_rt = in6_rtalloc1((struct sockaddr *)dst, 0, 0, M_GETFIB(m)); if (rin6.ro_rt) RT_UNLOCK(rin6.ro_rt); @@ -1238,19 +1238,28 @@ ip6_savecontrol_v4(struct inpcb *inp, struct mbuf *m, struct mbuf **mp, } #endif - if ((ip6->ip6_vfc & IPV6_VERSION_MASK) != IPV6_VERSION) { - if (v4only != NULL) - *v4only = 1; - return (mp); - } - #define IS2292(inp, x, y) (((inp)->inp_flags & IN6P_RFC2292) ? (x) : (y)) /* RFC 2292 sec. 5 */ if ((inp->inp_flags & IN6P_PKTINFO) != 0) { struct in6_pktinfo pi6; - bcopy(&ip6->ip6_dst, &pi6.ipi6_addr, sizeof(struct in6_addr)); - in6_clearscope(&pi6.ipi6_addr); /* XXX */ + if ((ip6->ip6_vfc & IPV6_VERSION_MASK) != IPV6_VERSION) { +#ifdef INET + struct ip *ip; + + ip = mtod(m, struct ip *); + pi6.ipi6_addr.s6_addr32[0] = 0; + pi6.ipi6_addr.s6_addr32[1] = 0; + pi6.ipi6_addr.s6_addr32[2] = IPV6_ADDR_INT32_SMP; + pi6.ipi6_addr.s6_addr32[3] = ip->ip_dst.s_addr; +#else + /* We won't hit this code */ + bzero(&pi6.ipi6_addr, sizeof(struct in6_addr)); +#endif + } else { + bcopy(&ip6->ip6_dst, &pi6.ipi6_addr, sizeof(struct in6_addr)); + in6_clearscope(&pi6.ipi6_addr); /* XXX */ + } pi6.ipi6_ifindex = (m && m->m_pkthdr.rcvif) ? m->m_pkthdr.rcvif->if_index : 0; @@ -1262,8 +1271,21 @@ ip6_savecontrol_v4(struct inpcb *inp, struct mbuf *m, struct mbuf **mp, } if ((inp->inp_flags & IN6P_HOPLIMIT) != 0) { - int hlim = ip6->ip6_hlim & 0xff; + int hlim; + + if ((ip6->ip6_vfc & IPV6_VERSION_MASK) != IPV6_VERSION) { +#ifdef INET + struct ip *ip; + ip = mtod(m, struct ip *); + hlim = ip->ip_ttl; +#else + /* We won't hit this code */ + hlim = 0; +#endif + } else { + hlim = ip6->ip6_hlim & 0xff; + } *mp = sbcreatecontrol((caddr_t) &hlim, sizeof(int), IS2292(inp, IPV6_2292HOPLIMIT, IPV6_HOPLIMIT), IPPROTO_IPV6); @@ -1271,8 +1293,40 @@ ip6_savecontrol_v4(struct inpcb *inp, struct mbuf *m, struct mbuf **mp, mp = &(*mp)->m_next; } - if (v4only != NULL) - *v4only = 0; + if ((inp->inp_flags & IN6P_TCLASS) != 0) { + int tclass; + + if ((ip6->ip6_vfc & IPV6_VERSION_MASK) != IPV6_VERSION) { +#ifdef INET + struct ip *ip; + + ip = mtod(m, struct ip *); + tclass = ip->ip_tos; +#else + /* We won't hit this code */ + tclass = 0; +#endif + } else { + u_int32_t flowinfo; + + flowinfo = (u_int32_t)ntohl(ip6->ip6_flow & IPV6_FLOWINFO_MASK); + flowinfo >>= 20; + tclass = flowinfo & 0xff; + } + *mp = sbcreatecontrol((caddr_t) &tclass, sizeof(int), + IPV6_TCLASS, IPPROTO_IPV6); + if (*mp) + mp = &(*mp)->m_next; + } + + if (v4only != NULL) { + if ((ip6->ip6_vfc & IPV6_VERSION_MASK) != IPV6_VERSION) { + *v4only = 1; + } else { + *v4only = 0; + } + } + return (mp); } @@ -1286,20 +1340,6 @@ ip6_savecontrol(struct inpcb *in6p, struct mbuf *m, struct mbuf **mp) if (v4only) return; - if ((in6p->inp_flags & IN6P_TCLASS) != 0) { - u_int32_t flowinfo; - int tclass; - - flowinfo = (u_int32_t)ntohl(ip6->ip6_flow & IPV6_FLOWINFO_MASK); - flowinfo >>= 20; - - tclass = flowinfo & 0xff; - *mp = sbcreatecontrol((caddr_t) &tclass, sizeof(tclass), - IPV6_TCLASS, IPPROTO_IPV6); - if (*mp) - mp = &(*mp)->m_next; - } - /* * IPV6_HOPOPTS socket option. Recall that we required super-user * privilege for the option (see ip6_ctloutput), but it might be too diff --git a/freebsd/sys/netinet6/ip6_ipsec.c b/freebsd/sys/netinet6/ip6_ipsec.c index e1477123..8ed4eec2 100644 --- a/freebsd/sys/netinet6/ip6_ipsec.c +++ b/freebsd/sys/netinet6/ip6_ipsec.c @@ -97,7 +97,7 @@ SYSCTL_VNET_INT(_net_inet6_ipsec6, OID_AUTO, /* * Check if we have to jump over firewall processing for this packet. - * Called from ip_input(). + * Called from ip6_input(). * 1 = jump over firewall, 0 = packet goes through firewall. */ int @@ -106,7 +106,7 @@ ip6_ipsec_filtertunnel(struct mbuf *m) #if defined(IPSEC) /* - * Bypass packet filtering for packets from a tunnel. + * Bypass packet filtering for packets previously handled by IPsec. */ if (!V_ip6_ipsec6_filtertunnel && m_tag_find(m, PACKET_TAG_IPSEC_IN_DONE, NULL) != NULL) @@ -118,7 +118,7 @@ ip6_ipsec_filtertunnel(struct mbuf *m) /* * Check if this packet has an active SA and needs to be dropped instead * of forwarded. - * Called from ip_input(). + * Called from ip6_input(). * 1 = drop packet, 0 = forward packet. */ int @@ -141,7 +141,7 @@ ip6_ipsec_fwd(struct mbuf *m) if (sp == NULL) { /* NB: can happen if error */ splx(s); /*XXX error stat???*/ - DPRINTF(("ip_input: no SP for forwarding\n")); /*XXX*/ + DPRINTF(("%s: no SP for forwarding\n", __func__)); /*XXX*/ return 1; } @@ -163,7 +163,7 @@ ip6_ipsec_fwd(struct mbuf *m) * Check if protocol type doesn't have a further header and do IPSEC * decryption or reject right now. Protocols with further headers get * their IPSEC treatment within the protocol specific processing. - * Called from ip_input(). + * Called from ip6_input(). * 1 = drop packet, 0 = continue processing packet. */ int @@ -206,7 +206,7 @@ ip6_ipsec_input(struct mbuf *m, int nxt) } else { /* XXX error stat??? */ error = EINVAL; - DPRINTF(("ip_input: no SP, packet discarded\n"));/*XXX*/ + DPRINTF(("%s: no SP, packet discarded\n", __func__));/*XXX*/ return 1; } splx(s); @@ -263,7 +263,7 @@ ip6_ipsec_output(struct mbuf **m, struct inpcb *inp, int *flags, int *error, mtag->m_tag_id != PACKET_TAG_IPSEC_OUT_CRYPTO_NEEDED) continue; /* - * Check if policy has an SA associated with it. + * Check if policy has no SA associated with it. * This can happen when an SP has yet to acquire * an SA; e.g. on first reference. If it occurs, * then we let ipsec4_process_packet do its thing. diff --git a/freebsd/sys/netinet6/ip6_mroute.c b/freebsd/sys/netinet6/ip6_mroute.c index 3be1d480..44a96cb5 100644 --- a/freebsd/sys/netinet6/ip6_mroute.c +++ b/freebsd/sys/netinet6/ip6_mroute.c @@ -83,7 +83,6 @@ #include <sys/cdefs.h> __FBSDID("$FreeBSD$"); -#include <rtems/bsd/local/opt_inet.h> #include <rtems/bsd/local/opt_inet6.h> #include <rtems/bsd/sys/param.h> diff --git a/freebsd/sys/netinet6/ip6_output.c b/freebsd/sys/netinet6/ip6_output.c index d6d7eb6e..19c430a6 100644 --- a/freebsd/sys/netinet6/ip6_output.c +++ b/freebsd/sys/netinet6/ip6_output.c @@ -137,7 +137,7 @@ static int ip6_insertfraghdr __P((struct mbuf *, struct mbuf *, int, static int ip6_insert_jumboopt(struct ip6_exthdrs *, u_int32_t); static int ip6_splithdr(struct mbuf *, struct ip6_exthdrs *); static int ip6_getpmtu __P((struct route_in6 *, struct route_in6 *, - struct ifnet *, struct in6_addr *, u_long *, int *)); + struct ifnet *, struct in6_addr *, u_long *, int *, u_int)); static int copypktopts(struct ip6_pktopts *, struct ip6_pktopts *, int); @@ -232,6 +232,9 @@ ip6_output(struct mbuf *m0, struct ip6_pktopts *opt, goto bad; } + if (inp != NULL) + M_SETFIB(m, inp->inp_inc.inc_fibnum); + finaldst = ip6->ip6_dst; bzero(&exthdrs, sizeof(exthdrs)); @@ -259,11 +262,11 @@ ip6_output(struct mbuf *m0, struct ip6_pktopts *opt, MAKE_EXTHDR(opt->ip6po_dest2, &exthdrs.ip6e_dest2); } +#ifdef IPSEC /* * IPSec checking which handles several cases. * FAST IPSEC: We re-injected the packet. */ -#ifdef IPSEC switch(ip6_ipsec_output(&m, inp, &flags, &error, &ifp, &sp)) { case 1: /* Bad packet */ @@ -578,8 +581,8 @@ again: dst_sa.sin6_family = AF_INET6; dst_sa.sin6_len = sizeof(dst_sa); dst_sa.sin6_addr = ip6->ip6_dst; - if ((error = in6_selectroute(&dst_sa, opt, im6o, ro, - &ifp, &rt)) != 0) { + if ((error = in6_selectroute_fib(&dst_sa, opt, im6o, ro, + &ifp, &rt, inp ? inp->inp_inc.inc_fibnum : M_GETFIB(m))) != 0) { switch (error) { case EHOSTUNREACH: V_ip6stat.ip6s_noroute++; @@ -747,7 +750,7 @@ again: /* Determine path MTU. */ if ((error = ip6_getpmtu(ro_pmtu, ro, ifp, &finaldst, &mtu, - &alwaysfrag)) != 0) + &alwaysfrag, inp ? inp->inp_inc.inc_fibnum : M_GETFIB(m))) != 0) goto bad; /* @@ -1011,7 +1014,7 @@ passout: goto sendorfree; } m->m_pkthdr.rcvif = NULL; - m->m_flags = m0->m_flags & M_COPYFLAGS; + m->m_flags = m0->m_flags & M_COPYFLAGS; /* incl. FIB */ *mnext = m; mnext = &m->m_nextpkt; m->m_data += max_linkhdr; @@ -1266,7 +1269,7 @@ ip6_insertfraghdr(struct mbuf *m0, struct mbuf *m, int hlen, static int ip6_getpmtu(struct route_in6 *ro_pmtu, struct route_in6 *ro, struct ifnet *ifp, struct in6_addr *dst, u_long *mtup, - int *alwaysfragp) + int *alwaysfragp, u_int fibnum) { u_int32_t mtu = 0; int alwaysfrag = 0; @@ -1288,7 +1291,7 @@ ip6_getpmtu(struct route_in6 *ro_pmtu, struct route_in6 *ro, sa6_dst->sin6_len = sizeof(struct sockaddr_in6); sa6_dst->sin6_addr = *dst; - rtalloc((struct route *)ro_pmtu); + in6_rtalloc(ro_pmtu, fibnum); } } if (ro_pmtu->ro_rt) { @@ -1366,7 +1369,23 @@ ip6_ctloutput(struct socket *so, struct sockopt *sopt) optval = 0; uproto = (int)so->so_proto->pr_protocol; - if (level == IPPROTO_IPV6) { + if (level != IPPROTO_IPV6) { + error = EINVAL; + + if (sopt->sopt_level == SOL_SOCKET && + sopt->sopt_dir == SOPT_SET) { + switch (sopt->sopt_name) { + case SO_SETFIB: + INP_WLOCK(in6p); + in6p->inp_inc.inc_fibnum = so->so_fibnum; + INP_WUNLOCK(in6p); + error = 0; + break; + default: + break; + } + } + } else { switch (op) { case SOPT_SET: @@ -1455,18 +1474,22 @@ ip6_ctloutput(struct socket *so, struct sockopt *sopt) break; #define OPTSET(bit) \ do { \ + INP_WLOCK(in6p); \ if (optval) \ in6p->inp_flags |= (bit); \ else \ in6p->inp_flags &= ~(bit); \ + INP_WUNLOCK(in6p); \ } while (/*CONSTCOND*/ 0) #define OPTSET2292(bit) \ do { \ + INP_WLOCK(in6p); \ in6p->inp_flags |= IN6P_RFC2292; \ if (optval) \ in6p->inp_flags |= (bit); \ else \ in6p->inp_flags &= ~(bit); \ + INP_WUNLOCK(in6p); \ } while (/*CONSTCOND*/ 0) #define OPTBIT(bit) (in6p->inp_flags & (bit) ? 1 : 0) @@ -1720,6 +1743,7 @@ do { \ if (error) break; + INP_WLOCK(in6p); switch (optval) { case IPV6_PORTRANGE_DEFAULT: in6p->inp_flags &= ~(INP_LOWPORT); @@ -1740,6 +1764,7 @@ do { \ error = EINVAL; break; } + INP_WUNLOCK(in6p); break; #ifdef IPSEC @@ -1889,7 +1914,8 @@ do { \ * the outgoing interface. */ error = ip6_getpmtu(&sro, NULL, NULL, - &in6p->in6p_faddr, &pmtu, NULL); + &in6p->in6p_faddr, &pmtu, NULL, + so->so_fibnum); if (sro.ro_rt) RTFREE(sro.ro_rt); if (error) @@ -1989,8 +2015,6 @@ do { \ } break; } - } else { /* level != IPPROTO_IPV6 */ - error = EINVAL; } return (error); } @@ -2312,6 +2336,8 @@ copypktopts(struct ip6_pktopts *dst, struct ip6_pktopts *src, int canwait) dst->ip6po_hlim = src->ip6po_hlim; dst->ip6po_tclass = src->ip6po_tclass; dst->ip6po_flags = src->ip6po_flags; + dst->ip6po_minmtu = src->ip6po_minmtu; + dst->ip6po_prefer_tempaddr = src->ip6po_prefer_tempaddr; if (src->ip6po_pktinfo) { dst->ip6po_pktinfo = malloc(sizeof(*dst->ip6po_pktinfo), M_IP6OPT, canwait); diff --git a/freebsd/sys/netinet6/ip6_var.h b/freebsd/sys/netinet6/ip6_var.h index 51ae1f74..9e210e2d 100644 --- a/freebsd/sys/netinet6/ip6_var.h +++ b/freebsd/sys/netinet6/ip6_var.h @@ -437,6 +437,9 @@ int in6_selectsrc(struct sockaddr_in6 *, struct ip6_pktopts *, int in6_selectroute __P((struct sockaddr_in6 *, struct ip6_pktopts *, struct ip6_moptions *, struct route_in6 *, struct ifnet **, struct rtentry **)); +int in6_selectroute_fib(struct sockaddr_in6 *, struct ip6_pktopts *, + struct ip6_moptions *, struct route_in6 *, struct ifnet **, + struct rtentry **, u_int); u_int32_t ip6_randomid __P((void)); u_int32_t ip6_randomflowlabel __P((void)); #endif /* _KERNEL */ diff --git a/freebsd/sys/netinet6/mld6.c b/freebsd/sys/netinet6/mld6.c index b1133d69..5f9b4ffa 100644 --- a/freebsd/sys/netinet6/mld6.c +++ b/freebsd/sys/netinet6/mld6.c @@ -123,7 +123,8 @@ static int mld_v1_input_query(struct ifnet *, const struct ip6_hdr *, /*const*/ struct mld_hdr *); static int mld_v1_input_report(struct ifnet *, const struct ip6_hdr *, /*const*/ struct mld_hdr *); -static void mld_v1_process_group_timer(struct in6_multi *, const int); +static void mld_v1_process_group_timer(struct mld_ifinfo *, + struct in6_multi *); static void mld_v1_process_querier_timers(struct mld_ifinfo *); static int mld_v1_transmit_report(struct in6_multi *, const int); static void mld_v1_update_group(struct in6_multi *, const int); @@ -543,7 +544,7 @@ mld_ifdetach(struct ifnet *ifp) mli = MLD_IFINFO(ifp); if (mli->mli_version == MLD_VERSION_2) { - IF_ADDR_LOCK(ifp); + IF_ADDR_RLOCK(ifp); TAILQ_FOREACH(ifma, &ifp->if_multiaddrs, ifma_link) { if (ifma->ifma_addr->sa_family != AF_INET6 || ifma->ifma_protospec == NULL) @@ -555,7 +556,7 @@ mld_ifdetach(struct ifnet *ifp) } in6m_clear_recorded(inm); } - IF_ADDR_UNLOCK(ifp); + IF_ADDR_RUNLOCK(ifp); SLIST_FOREACH_SAFE(inm, &mli->mli_relinmhead, in6m_nrele, tinm) { SLIST_REMOVE_HEAD(&mli->mli_relinmhead, in6m_nrele); @@ -682,7 +683,6 @@ mld_v1_input_query(struct ifnet *ifp, const struct ip6_hdr *ip6, IN6_MULTI_LOCK(); MLD_LOCK(); - IF_ADDR_LOCK(ifp); /* * Switch to MLDv1 host compatibility mode. @@ -695,6 +695,7 @@ mld_v1_input_query(struct ifnet *ifp, const struct ip6_hdr *ip6, if (timer == 0) timer = 1; + IF_ADDR_RLOCK(ifp); if (is_general_query) { /* * For each reporting group joined on this @@ -726,7 +727,7 @@ mld_v1_input_query(struct ifnet *ifp, const struct ip6_hdr *ip6, in6_clearscope(&mld->mld_addr); } - IF_ADDR_UNLOCK(ifp); + IF_ADDR_RUNLOCK(ifp); MLD_UNLOCK(); IN6_MULTI_UNLOCK(); @@ -890,7 +891,6 @@ mld_v2_input_query(struct ifnet *ifp, const struct ip6_hdr *ip6, IN6_MULTI_LOCK(); MLD_LOCK(); - IF_ADDR_LOCK(ifp); mli = MLD_IFINFO(ifp); KASSERT(mli != NULL, ("%s: no mld_ifinfo for ifp %p", __func__, ifp)); @@ -938,14 +938,18 @@ mld_v2_input_query(struct ifnet *ifp, const struct ip6_hdr *ip6, * Queries for groups we are not a member of on this * link are simply ignored. */ + IF_ADDR_RLOCK(ifp); inm = in6m_lookup_locked(ifp, &mld->mld_addr); - if (inm == NULL) + if (inm == NULL) { + IF_ADDR_RUNLOCK(ifp); goto out_locked; + } if (nsrc > 0) { if (!ratecheck(&inm->in6m_lastgsrtv, &V_mld_gsrdelay)) { CTR1(KTR_MLD, "%s: GS query throttled.", __func__); + IF_ADDR_RUNLOCK(ifp); goto out_locked; } } @@ -963,10 +967,10 @@ mld_v2_input_query(struct ifnet *ifp, const struct ip6_hdr *ip6, /* XXX Clear embedded scope ID as userland won't expect it. */ in6_clearscope(&mld->mld_addr); + IF_ADDR_RUNLOCK(ifp); } out_locked: - IF_ADDR_UNLOCK(ifp); MLD_UNLOCK(); IN6_MULTI_UNLOCK(); @@ -1173,7 +1177,7 @@ mld_v1_input_report(struct ifnet *ifp, const struct ip6_hdr *ip6, IN6_MULTI_LOCK(); MLD_LOCK(); - IF_ADDR_LOCK(ifp); + IF_ADDR_RLOCK(ifp); /* * MLDv1 report suppression. @@ -1221,8 +1225,8 @@ mld_v1_input_report(struct ifnet *ifp, const struct ip6_hdr *ip6, } out_locked: + IF_ADDR_RUNLOCK(ifp); MLD_UNLOCK(); - IF_ADDR_UNLOCK(ifp); IN6_MULTI_UNLOCK(); /* XXX Clear embedded scope ID as userland won't expect it. */ @@ -1334,8 +1338,8 @@ mld_fasttimo_vnet(void) struct ifqueue qrq; /* Query response packets */ struct ifnet *ifp; struct mld_ifinfo *mli; - struct ifmultiaddr *ifma, *tifma; - struct in6_multi *inm; + struct ifmultiaddr *ifma; + struct in6_multi *inm, *tinm; int uri_fasthz; uri_fasthz = 0; @@ -1398,25 +1402,15 @@ mld_fasttimo_vnet(void) IFQ_SET_MAXLEN(&scq, MLD_MAX_STATE_CHANGE_PACKETS); } - IF_ADDR_LOCK(ifp); - TAILQ_FOREACH_SAFE(ifma, &ifp->if_multiaddrs, ifma_link, - tifma) { + IF_ADDR_RLOCK(ifp); + TAILQ_FOREACH(ifma, &ifp->if_multiaddrs, ifma_link) { if (ifma->ifma_addr->sa_family != AF_INET6 || ifma->ifma_protospec == NULL) continue; inm = (struct in6_multi *)ifma->ifma_protospec; switch (mli->mli_version) { case MLD_VERSION_1: - /* - * XXX Drop IF_ADDR lock temporarily to - * avoid recursion caused by a potential - * call by in6ifa_ifpforlinklocal(). - * rwlock candidate? - */ - IF_ADDR_UNLOCK(ifp); - mld_v1_process_group_timer(inm, - mli->mli_version); - IF_ADDR_LOCK(ifp); + mld_v1_process_group_timer(mli, inm); break; case MLD_VERSION_2: mld_v2_process_group_timers(mli, &qrq, @@ -1424,11 +1418,27 @@ mld_fasttimo_vnet(void) break; } } - IF_ADDR_UNLOCK(ifp); - - if (mli->mli_version == MLD_VERSION_2) { - struct in6_multi *tinm; + IF_ADDR_RUNLOCK(ifp); + switch (mli->mli_version) { + case MLD_VERSION_1: + /* + * Transmit reports for this lifecycle. This + * is done while not holding IF_ADDR_LOCK + * since this can call + * in6ifa_ifpforlinklocal() which locks + * IF_ADDR_LOCK internally as well as + * ip6_output() to transmit a packet. + */ + SLIST_FOREACH_SAFE(inm, &mli->mli_relinmhead, + in6m_nrele, tinm) { + SLIST_REMOVE_HEAD(&mli->mli_relinmhead, + in6m_nrele); + (void)mld_v1_transmit_report(inm, + MLD_LISTENER_REPORT); + } + break; + case MLD_VERSION_2: mld_dispatch_queue(&qrq, 0); mld_dispatch_queue(&scq, 0); @@ -1442,6 +1452,7 @@ mld_fasttimo_vnet(void) in6m_nrele); in6m_release_locked(inm); } + break; } } @@ -1455,7 +1466,7 @@ out_locked: * Will update the global pending timer flags. */ static void -mld_v1_process_group_timer(struct in6_multi *inm, const int version) +mld_v1_process_group_timer(struct mld_ifinfo *mli, struct in6_multi *inm) { int report_timer_expired; @@ -1482,8 +1493,8 @@ mld_v1_process_group_timer(struct in6_multi *inm, const int version) case MLD_REPORTING_MEMBER: if (report_timer_expired) { inm->in6m_state = MLD_IDLE_MEMBER; - (void)mld_v1_transmit_report(inm, - MLD_LISTENER_REPORT); + SLIST_INSERT_HEAD(&mli->mli_relinmhead, inm, + in6m_nrele); } break; case MLD_G_QUERY_PENDING_MEMBER: @@ -1654,7 +1665,7 @@ mld_v2_cancel_link_timers(struct mld_ifinfo *mli) { struct ifmultiaddr *ifma; struct ifnet *ifp; - struct in6_multi *inm; + struct in6_multi *inm, *tinm; CTR3(KTR_MLD, "%s: cancel v2 timers on ifp %p(%s)", __func__, mli->mli_ifp, mli->mli_ifp->if_xname); @@ -1675,7 +1686,7 @@ mld_v2_cancel_link_timers(struct mld_ifinfo *mli) ifp = mli->mli_ifp; - IF_ADDR_LOCK(ifp); + IF_ADDR_RLOCK(ifp); TAILQ_FOREACH(ifma, &ifp->if_multiaddrs, ifma_link) { if (ifma->ifma_addr->sa_family != AF_INET6) continue; @@ -1693,14 +1704,9 @@ mld_v2_cancel_link_timers(struct mld_ifinfo *mli) * If we are leaving the group and switching * version, we need to release the final * reference held for issuing the INCLUDE {}. - * - * SMPNG: Must drop and re-acquire IF_ADDR_LOCK - * around in6m_release_locked(), as it is not - * a recursive mutex. */ - IF_ADDR_UNLOCK(ifp); - in6m_release_locked(inm); - IF_ADDR_LOCK(ifp); + SLIST_INSERT_HEAD(&mli->mli_relinmhead, inm, + in6m_nrele); /* FALLTHROUGH */ case MLD_G_QUERY_PENDING_MEMBER: case MLD_SG_QUERY_PENDING_MEMBER: @@ -1717,7 +1723,11 @@ mld_v2_cancel_link_timers(struct mld_ifinfo *mli) break; } } - IF_ADDR_UNLOCK(ifp); + IF_ADDR_RUNLOCK(ifp); + SLIST_FOREACH_SAFE(inm, &mli->mli_relinmhead, in6m_nrele, tinm) { + SLIST_REMOVE_HEAD(&mli->mli_relinmhead, in6m_nrele); + in6m_release_locked(inm); + } } /* @@ -2974,7 +2984,7 @@ mld_v2_merge_state_changes(struct in6_multi *inm, struct ifqueue *ifscq) static void mld_v2_dispatch_general_query(struct mld_ifinfo *mli) { - struct ifmultiaddr *ifma, *tifma; + struct ifmultiaddr *ifma; struct ifnet *ifp; struct in6_multi *inm; int retval; @@ -2987,8 +2997,8 @@ mld_v2_dispatch_general_query(struct mld_ifinfo *mli) ifp = mli->mli_ifp; - IF_ADDR_LOCK(ifp); - TAILQ_FOREACH_SAFE(ifma, &ifp->if_multiaddrs, ifma_link, tifma) { + IF_ADDR_RLOCK(ifp); + TAILQ_FOREACH(ifma, &ifp->if_multiaddrs, ifma_link) { if (ifma->ifma_addr->sa_family != AF_INET6 || ifma->ifma_protospec == NULL) continue; @@ -3018,7 +3028,7 @@ mld_v2_dispatch_general_query(struct mld_ifinfo *mli) break; } } - IF_ADDR_UNLOCK(ifp); + IF_ADDR_RUNLOCK(ifp); mld_dispatch_queue(&mli->mli_gq, MLD_MAX_RESPONSE_BURST); diff --git a/freebsd/sys/netinet6/nd6.c b/freebsd/sys/netinet6/nd6.c index 999855ca..89b15e56 100644 --- a/freebsd/sys/netinet6/nd6.c +++ b/freebsd/sys/netinet6/nd6.c @@ -372,6 +372,14 @@ nd6_options(union nd_opts *ndopts) ndopts->nd_opts_pi_end = (struct nd_opt_prefix_info *)nd_opt; break; + /* What about ND_OPT_ROUTE_INFO? RFC 4191 */ + case ND_OPT_RDNSS: /* RFC 6106 */ + case ND_OPT_DNSSL: /* RFC 6106 */ + /* + * Silently ignore options we know and do not care about + * in the kernel. + */ + break; default: /* * Unknown options must be silently ignored, @@ -564,26 +572,18 @@ nd6_timer(void *arg) { CURVNET_SET((struct vnet *) arg); int s; - struct nd_defrouter *dr; - struct nd_prefix *pr; + struct nd_defrouter *dr, *ndr; + struct nd_prefix *pr, *npr; struct in6_ifaddr *ia6, *nia6; - struct in6_addrlifetime *lt6; callout_reset(&V_nd6_timer_ch, V_nd6_prune * hz, nd6_timer, curvnet); /* expire default router list */ s = splnet(); - dr = TAILQ_FIRST(&V_nd_defrouter); - while (dr) { - if (dr->expire && dr->expire < time_second) { - struct nd_defrouter *t; - t = TAILQ_NEXT(dr, dr_entry); + TAILQ_FOREACH_SAFE(dr, &V_nd_defrouter, dr_entry, ndr) { + if (dr->expire && dr->expire < time_second) defrtrlist_del(dr); - dr = t; - } else { - dr = TAILQ_NEXT(dr, dr_entry); - } } /* @@ -597,7 +597,6 @@ nd6_timer(void *arg) addrloop: TAILQ_FOREACH_SAFE(ia6, &V_in6_ifaddrhead, ia_link, nia6) { /* check address lifetime */ - lt6 = &ia6->ia6_lifetime; if (IFA6_IS_INVALID(ia6)) { int regen = 0; @@ -660,8 +659,7 @@ nd6_timer(void *arg) } /* expire prefix list */ - pr = V_nd_prefix.lh_first; - while (pr) { + LIST_FOREACH_SAFE(pr, &V_nd_prefix, ndpr_entry, npr) { /* * check prefix lifetime. * since pltime is just for autoconf, pltime processing for @@ -669,18 +667,13 @@ nd6_timer(void *arg) */ if (pr->ndpr_vltime != ND6_INFINITE_LIFETIME && time_second - pr->ndpr_lastupdate > pr->ndpr_vltime) { - struct nd_prefix *t; - t = pr->ndpr_next; /* * address expiration and prefix expiration are * separate. NEVER perform in6_purgeaddr here. */ - prelist_remove(pr); - pr = t; - } else - pr = pr->ndpr_next; + } } splx(s); CURVNET_RESTORE(); @@ -697,7 +690,7 @@ regen_tmpaddr(struct in6_ifaddr *ia6) struct in6_ifaddr *public_ifa6 = NULL; ifp = ia6->ia_ifa.ifa_ifp; - IF_ADDR_LOCK(ifp); + IF_ADDR_RLOCK(ifp); TAILQ_FOREACH(ifa, &ifp->if_addrhead, ifa_link) { struct in6_ifaddr *it6; @@ -739,7 +732,7 @@ regen_tmpaddr(struct in6_ifaddr *ia6) if (public_ifa6 != NULL) ifa_ref(&public_ifa6->ia_ifa); } - IF_ADDR_UNLOCK(ifp); + IF_ADDR_RUNLOCK(ifp); if (public_ifa6 != NULL) { int e; @@ -773,8 +766,7 @@ nd6_purge(struct ifnet *ifp) * in the routing table, in order to keep additional side effects as * small as possible. */ - for (dr = TAILQ_FIRST(&V_nd_defrouter); dr; dr = ndr) { - ndr = TAILQ_NEXT(dr, dr_entry); + TAILQ_FOREACH_SAFE(dr, &V_nd_defrouter, dr_entry, ndr) { if (dr->installed) continue; @@ -782,8 +774,7 @@ nd6_purge(struct ifnet *ifp) defrtrlist_del(dr); } - for (dr = TAILQ_FIRST(&V_nd_defrouter); dr; dr = ndr) { - ndr = TAILQ_NEXT(dr, dr_entry); + TAILQ_FOREACH_SAFE(dr, &V_nd_defrouter, dr_entry, ndr) { if (!dr->installed) continue; @@ -792,8 +783,7 @@ nd6_purge(struct ifnet *ifp) } /* Nuke prefix list entries toward ifp */ - for (pr = V_nd_prefix.lh_first; pr; pr = npr) { - npr = pr->ndpr_next; + LIST_FOREACH_SAFE(pr, &V_nd_prefix, ndpr_entry, npr) { if (pr->ndpr_ifp == ifp) { /* * Because if_detach() does *not* release prefixes @@ -908,13 +898,16 @@ nd6_is_new_addr_neighbor(struct sockaddr_in6 *addr, struct ifnet *ifp) * If the address matches one of our on-link prefixes, it should be a * neighbor. */ - for (pr = V_nd_prefix.lh_first; pr; pr = pr->ndpr_next) { + LIST_FOREACH(pr, &V_nd_prefix, ndpr_entry) { if (pr->ndpr_ifp != ifp) continue; if (!(pr->ndpr_stateflags & NDPRF_ONLINK)) { struct rtentry *rt; - rt = rtalloc1((struct sockaddr *)&pr->ndpr_prefix, 0, 0); + + /* Always use the default FIB here. */ + rt = in6_rtalloc1((struct sockaddr *)&pr->ndpr_prefix, + 0, 0, RT_DEFAULT_FIB); if (rt == NULL) continue; /* @@ -959,7 +952,7 @@ nd6_is_new_addr_neighbor(struct sockaddr_in6 *addr, struct ifnet *ifp) * XXX: we restrict the condition to hosts, because routers usually do * not have the "default router list". */ - if (!V_ip6_forwarding && TAILQ_FIRST(&V_nd_defrouter) == NULL && + if (!V_ip6_forwarding && TAILQ_EMPTY(&V_nd_defrouter) && V_nd6_defifindex == ifp->if_index) { return (1); } @@ -986,12 +979,12 @@ nd6_is_addr_neighbor(struct sockaddr_in6 *addr, struct ifnet *ifp) * Even if the address matches none of our addresses, it might be * in the neighbor cache. */ - IF_AFDATA_LOCK(ifp); + IF_AFDATA_RLOCK(ifp); if ((lle = nd6_lookup(&addr->sin6_addr, 0, ifp)) != NULL) { LLE_RUNLOCK(lle); rc = 1; } - IF_AFDATA_UNLOCK(ifp); + IF_AFDATA_RUNLOCK(ifp); return (rc); } @@ -1172,6 +1165,46 @@ done: } +/* + * Rejuvenate this function for routing operations related + * processing. + */ +void +nd6_rtrequest(int req, struct rtentry *rt, struct rt_addrinfo *info) +{ + struct sockaddr_in6 *gateway = (struct sockaddr_in6 *)rt->rt_gateway; + struct nd_defrouter *dr; + struct ifnet *ifp = rt->rt_ifp; + + RT_LOCK_ASSERT(rt); + + switch (req) { + case RTM_ADD: + break; + + case RTM_DELETE: + if (!ifp) + return; + /* + * Only indirect routes are interesting. + */ + if ((rt->rt_flags & RTF_GATEWAY) == 0) + return; + /* + * check for default route + */ + if (IN6_ARE_ADDR_EQUAL(&in6addr_any, + &SIN6(rt_key(rt))->sin6_addr)) { + + dr = defrouter_lookup(&gateway->sin6_addr, ifp); + if (dr != NULL) + dr->installed = 0; + } + break; + } +} + + int nd6_ioctl(u_long cmd, caddr_t data, struct ifnet *ifp) { @@ -1192,8 +1225,9 @@ nd6_ioctl(u_long cmd, caddr_t data, struct ifnet *ifp) */ bzero(drl, sizeof(*drl)); s = splnet(); - dr = TAILQ_FIRST(&V_nd_defrouter); - while (dr && i < DRLSTSIZ) { + TAILQ_FOREACH(dr, &V_nd_defrouter, dr_entry) { + if (i >= DRLSTSIZ) + break; drl->defrouter[i].rtaddr = dr->rtaddr; in6_clearscope(&drl->defrouter[i].rtaddr); @@ -1202,7 +1236,6 @@ nd6_ioctl(u_long cmd, caddr_t data, struct ifnet *ifp) drl->defrouter[i].expire = dr->expire; drl->defrouter[i].if_index = dr->ifp->if_index; i++; - dr = TAILQ_NEXT(dr, dr_entry); } splx(s); break; @@ -1221,11 +1254,12 @@ nd6_ioctl(u_long cmd, caddr_t data, struct ifnet *ifp) */ bzero(oprl, sizeof(*oprl)); s = splnet(); - pr = V_nd_prefix.lh_first; - while (pr && i < PRLSTSIZ) { + LIST_FOREACH(pr, &V_nd_prefix, ndpr_entry) { struct nd_pfxrouter *pfr; int j; + if (i >= PRLSTSIZ) + break; oprl->prefix[i].prefix = pr->ndpr_prefix.sin6_addr; oprl->prefix[i].raflags = pr->ndpr_raf; oprl->prefix[i].prefixlen = pr->ndpr_plen; @@ -1250,9 +1284,8 @@ nd6_ioctl(u_long cmd, caddr_t data, struct ifnet *ifp) oprl->prefix[i].expire = maxexpire; } - pfr = pr->ndpr_advrtrs.lh_first; j = 0; - while (pfr) { + LIST_FOREACH(pfr, &pr->ndpr_advrtrs, pfr_entry) { if (j < DRLSTSIZ) { #define RTRADDR oprl->prefix[i].advrtr[j] RTRADDR = pfr->router->rtaddr; @@ -1260,13 +1293,11 @@ nd6_ioctl(u_long cmd, caddr_t data, struct ifnet *ifp) #undef RTRADDR } j++; - pfr = pfr->pfr_next; } oprl->prefix[i].advrtrs = j; oprl->prefix[i].origin = PR_ORIG_RA; i++; - pr = pr->ndpr_next; } splx(s); @@ -1330,11 +1361,9 @@ nd6_ioctl(u_long cmd, caddr_t data, struct ifnet *ifp) struct nd_prefix *pr, *next; s = splnet(); - for (pr = V_nd_prefix.lh_first; pr; pr = next) { + LIST_FOREACH_SAFE(pr, &V_nd_prefix, ndpr_entry, next) { struct in6_ifaddr *ia, *ia_next; - next = pr->ndpr_next; - if (IN6_IS_ADDR_LINKLOCAL(&pr->ndpr_prefix.sin6_addr)) continue; /* XXX */ @@ -1360,8 +1389,7 @@ nd6_ioctl(u_long cmd, caddr_t data, struct ifnet *ifp) s = splnet(); defrouter_reset(); - for (dr = TAILQ_FIRST(&V_nd_defrouter); dr; dr = next) { - next = TAILQ_NEXT(dr, dr_entry); + TAILQ_FOREACH_SAFE(dr, &V_nd_defrouter, dr_entry, next) { defrtrlist_del(dr); } defrouter_select(); @@ -1376,9 +1404,9 @@ nd6_ioctl(u_long cmd, caddr_t data, struct ifnet *ifp) if ((error = in6_setscope(&nb_addr, ifp, NULL)) != 0) return (error); - IF_AFDATA_LOCK(ifp); + IF_AFDATA_RLOCK(ifp); ln = nd6_lookup(&nb_addr, 0, ifp); - IF_AFDATA_UNLOCK(ifp); + IF_AFDATA_RUNLOCK(ifp); if (ln == NULL) { error = EINVAL; @@ -1683,8 +1711,7 @@ nd6_slowtimo(void *arg) callout_reset(&V_nd6_slowtimo_ch, ND6_SLOWTIMER_INTERVAL * hz, nd6_slowtimo, curvnet); IFNET_RLOCK_NOSLEEP(); - for (ifp = TAILQ_FIRST(&V_ifnet); ifp; - ifp = TAILQ_NEXT(ifp, if_list)) { + TAILQ_FOREACH(ifp, &V_ifnet, if_list) { nd6if = ND_IFINFO(ifp); if (nd6if->basereachable && /* already initialized */ (nd6if->recalctm -= ND6_SLOWTIMER_INTERVAL) <= 0) { @@ -1919,14 +1946,15 @@ nd6_output_lle(struct ifnet *ifp, struct ifnet *origifp, struct mbuf *m0, if (*chain == NULL) *chain = m; else { - struct mbuf *m = *chain; + struct mbuf *mb; /* * append mbuf to end of deferred chain */ - while (m->m_nextpkt != NULL) - m = m->m_nextpkt; - m->m_nextpkt = m; + mb = *chain; + while (mb->m_nextpkt != NULL) + mb = mb->m_nextpkt; + mb->m_nextpkt = m; } return (error); } @@ -2070,9 +2098,9 @@ nd6_storelladdr(struct ifnet *ifp, struct mbuf *m, /* * the entry should have been created in nd6_store_lladdr */ - IF_AFDATA_LOCK(ifp); + IF_AFDATA_RLOCK(ifp); ln = lla_lookup(LLTABLE6(ifp), 0, dst); - IF_AFDATA_UNLOCK(ifp); + IF_AFDATA_RUNLOCK(ifp); if ((ln == NULL) || !(ln->la_flags & LLE_VALID)) { if (ln != NULL) LLE_RUNLOCK(ln); @@ -2120,130 +2148,101 @@ SYSCTL_VNET_INT(_net_inet6_icmp6, ICMPV6CTL_ND6_MAXQLEN, nd6_maxqueuelen, static int nd6_sysctl_drlist(SYSCTL_HANDLER_ARGS) { - int error; - char buf[1024] __aligned(4); - struct in6_defrouter *d, *de; + struct in6_defrouter d; struct nd_defrouter *dr; + int error; if (req->newptr) - return EPERM; - error = 0; - - for (dr = TAILQ_FIRST(&V_nd_defrouter); dr; - dr = TAILQ_NEXT(dr, dr_entry)) { - d = (struct in6_defrouter *)buf; - de = (struct in6_defrouter *)(buf + sizeof(buf)); - - if (d + 1 <= de) { - bzero(d, sizeof(*d)); - d->rtaddr.sin6_family = AF_INET6; - d->rtaddr.sin6_len = sizeof(d->rtaddr); - d->rtaddr.sin6_addr = dr->rtaddr; - error = sa6_recoverscope(&d->rtaddr); - if (error != 0) - return (error); - d->flags = dr->flags; - d->rtlifetime = dr->rtlifetime; - d->expire = dr->expire; - d->if_index = dr->ifp->if_index; - } else - panic("buffer too short"); - - error = SYSCTL_OUT(req, buf, sizeof(*d)); - if (error) - break; - } + return (EPERM); - return (error); + bzero(&d, sizeof(d)); + d.rtaddr.sin6_family = AF_INET6; + d.rtaddr.sin6_len = sizeof(d.rtaddr); + + /* + * XXX locking + */ + TAILQ_FOREACH(dr, &V_nd_defrouter, dr_entry) { + d.rtaddr.sin6_addr = dr->rtaddr; + error = sa6_recoverscope(&d.rtaddr); + if (error != 0) + return (error); + d.flags = dr->flags; + d.rtlifetime = dr->rtlifetime; + d.expire = dr->expire; + d.if_index = dr->ifp->if_index; + error = SYSCTL_OUT(req, &d, sizeof(d)); + if (error != 0) + return (error); + } + return (0); } static int nd6_sysctl_prlist(SYSCTL_HANDLER_ARGS) { - int error; - char buf[1024] __aligned(4); - struct in6_prefix *p, *pe; + struct in6_prefix p; + struct sockaddr_in6 s6; struct nd_prefix *pr; + struct nd_pfxrouter *pfr; + time_t maxexpire; + int error; char ip6buf[INET6_ADDRSTRLEN]; if (req->newptr) - return EPERM; - error = 0; - - for (pr = V_nd_prefix.lh_first; pr; pr = pr->ndpr_next) { - u_short advrtrs; - size_t advance; - struct sockaddr_in6 *sin6, *s6; - struct nd_pfxrouter *pfr; + return (EPERM); - p = (struct in6_prefix *)buf; - pe = (struct in6_prefix *)(buf + sizeof(buf)); + bzero(&p, sizeof(p)); + p.origin = PR_ORIG_RA; + bzero(&s6, sizeof(s6)); + s6.sin6_family = AF_INET6; + s6.sin6_len = sizeof(s6); - if (p + 1 <= pe) { - bzero(p, sizeof(*p)); - sin6 = (struct sockaddr_in6 *)(p + 1); - - p->prefix = pr->ndpr_prefix; - if (sa6_recoverscope(&p->prefix)) { + /* + * XXX locking + */ + LIST_FOREACH(pr, &V_nd_prefix, ndpr_entry) { + p.prefix = pr->ndpr_prefix; + if (sa6_recoverscope(&p.prefix)) { + log(LOG_ERR, "scope error in prefix list (%s)\n", + ip6_sprintf(ip6buf, &p.prefix.sin6_addr)); + /* XXX: press on... */ + } + p.raflags = pr->ndpr_raf; + p.prefixlen = pr->ndpr_plen; + p.vltime = pr->ndpr_vltime; + p.pltime = pr->ndpr_pltime; + p.if_index = pr->ndpr_ifp->if_index; + if (pr->ndpr_vltime == ND6_INFINITE_LIFETIME) + p.expire = 0; + else { + /* XXX: we assume time_t is signed. */ + maxexpire = (-1) & + ~((time_t)1 << ((sizeof(maxexpire) * 8) - 1)); + if (pr->ndpr_vltime < maxexpire - pr->ndpr_lastupdate) + p.expire = pr->ndpr_lastupdate + + pr->ndpr_vltime; + else + p.expire = maxexpire; + } + p.refcnt = pr->ndpr_refcnt; + p.flags = pr->ndpr_stateflags; + p.advrtrs = 0; + LIST_FOREACH(pfr, &pr->ndpr_advrtrs, pfr_entry) + p.advrtrs++; + error = SYSCTL_OUT(req, &p, sizeof(p)); + if (error != 0) + return (error); + LIST_FOREACH(pfr, &pr->ndpr_advrtrs, pfr_entry) { + s6.sin6_addr = pfr->router->rtaddr; + if (sa6_recoverscope(&s6)) log(LOG_ERR, "scope error in prefix list (%s)\n", - ip6_sprintf(ip6buf, &p->prefix.sin6_addr)); - /* XXX: press on... */ - } - p->raflags = pr->ndpr_raf; - p->prefixlen = pr->ndpr_plen; - p->vltime = pr->ndpr_vltime; - p->pltime = pr->ndpr_pltime; - p->if_index = pr->ndpr_ifp->if_index; - if (pr->ndpr_vltime == ND6_INFINITE_LIFETIME) - p->expire = 0; - else { - time_t maxexpire; - - /* XXX: we assume time_t is signed. */ - maxexpire = (-1) & - ~((time_t)1 << - ((sizeof(maxexpire) * 8) - 1)); - if (pr->ndpr_vltime < - maxexpire - pr->ndpr_lastupdate) { - p->expire = pr->ndpr_lastupdate + - pr->ndpr_vltime; - } else - p->expire = maxexpire; - } - p->refcnt = pr->ndpr_refcnt; - p->flags = pr->ndpr_stateflags; - p->origin = PR_ORIG_RA; - advrtrs = 0; - for (pfr = pr->ndpr_advrtrs.lh_first; pfr; - pfr = pfr->pfr_next) { - if ((void *)&sin6[advrtrs + 1] > (void *)pe) { - advrtrs++; - continue; - } - s6 = &sin6[advrtrs]; - bzero(s6, sizeof(*s6)); - s6->sin6_family = AF_INET6; - s6->sin6_len = sizeof(*sin6); - s6->sin6_addr = pfr->router->rtaddr; - if (sa6_recoverscope(s6)) { - log(LOG_ERR, - "scope error in " - "prefix list (%s)\n", - ip6_sprintf(ip6buf, - &pfr->router->rtaddr)); - } - advrtrs++; - } - p->advrtrs = advrtrs; - } else - panic("buffer too short"); - - advance = sizeof(*p) + sizeof(*sin6) * advrtrs; - error = SYSCTL_OUT(req, buf, advance); - if (error) - break; + ip6_sprintf(ip6buf, &pfr->router->rtaddr)); + error = SYSCTL_OUT(req, &s6, sizeof(s6)); + if (error != 0) + return (error); + } } - - return (error); + return (0); } diff --git a/freebsd/sys/netinet6/nd6.h b/freebsd/sys/netinet6/nd6.h index 9874f6ad..0893065e 100644 --- a/freebsd/sys/netinet6/nd6.h +++ b/freebsd/sys/netinet6/nd6.h @@ -276,8 +276,6 @@ struct nd_prefix { int ndpr_refcnt; /* reference couter from addresses */ }; -#define ndpr_next ndpr_entry.le_next - #define ndpr_raf ndpr_flags #define ndpr_raf_onlink ndpr_flags.onlink #define ndpr_raf_auto ndpr_flags.autonomous @@ -311,7 +309,6 @@ struct inet6_ndpr_msghdr { struct nd_pfxrouter { LIST_ENTRY(nd_pfxrouter) pfr_entry; -#define pfr_next pfr_entry.le_next struct nd_defrouter *router; }; @@ -404,6 +401,7 @@ void nd6_purge __P((struct ifnet *)); void nd6_nud_hint __P((struct rtentry *, struct in6_addr *, int)); int nd6_resolve __P((struct ifnet *, struct rtentry *, struct mbuf *, struct sockaddr *, u_char *)); +void nd6_rtrequest __P((int, struct rtentry *, struct rt_addrinfo *)); int nd6_ioctl __P((u_long, caddr_t, struct ifnet *)); struct llentry *nd6_cache_lladdr __P((struct ifnet *, struct in6_addr *, char *, int, int, int)); @@ -434,15 +432,12 @@ void nd6_dad_duplicated __P((struct ifaddr *)); void nd6_rs_input __P((struct mbuf *, int, int)); void nd6_ra_input __P((struct mbuf *, int, int)); void prelist_del __P((struct nd_prefix *)); -void defrouter_addreq __P((struct nd_defrouter *)); void defrouter_reset __P((void)); void defrouter_select __P((void)); void defrtrlist_del __P((struct nd_defrouter *)); void prelist_remove __P((struct nd_prefix *)); int nd6_prelist_add __P((struct nd_prefixctl *, struct nd_defrouter *, struct nd_prefix **)); -int nd6_prefix_onlink __P((struct nd_prefix *)); -int nd6_prefix_offlink __P((struct nd_prefix *)); void pfxlist_onlink_check __P((void)); struct nd_defrouter *defrouter_lookup __P((struct in6_addr *, struct ifnet *)); struct nd_prefix *nd6_prefix_lookup __P((struct nd_prefixctl *)); diff --git a/freebsd/sys/netinet6/nd6_nbr.c b/freebsd/sys/netinet6/nd6_nbr.c index 599c133f..63fb25c8 100644 --- a/freebsd/sys/netinet6/nd6_nbr.c +++ b/freebsd/sys/netinet6/nd6_nbr.c @@ -86,6 +86,8 @@ static void nd6_dad_timer(struct dadq *); static void nd6_dad_ns_output(struct dadq *, struct ifaddr *); static void nd6_dad_ns_input(struct ifaddr *); static void nd6_dad_na_input(struct ifaddr *); +static void nd6_na_output_fib(struct ifnet *, const struct in6_addr *, + const struct in6_addr *, u_long, int, struct sockaddr *, u_int); VNET_DEFINE(int, dad_ignore_ns) = 0; /* ignore NS in DAD - specwise incorrect*/ VNET_DEFINE(int, dad_maxtry) = 15; /* max # of *tries* to transmit DAD packet */ @@ -114,7 +116,7 @@ nd6_ns_input(struct mbuf *m, int off, int icmp6len) int anycast = 0, proxy = 0, tentative = 0; int tlladdr; union nd_opts ndopts; - struct sockaddr_dl *proxydl = NULL; + struct sockaddr_dl proxydl; char ip6bufs[INET6_ADDRSTRLEN], ip6bufd[INET6_ADDRSTRLEN]; #ifndef PULLDOWN_TEST @@ -239,28 +241,38 @@ nd6_ns_input(struct mbuf *m, int off, int icmp6len) tsin6.sin6_family = AF_INET6; tsin6.sin6_addr = taddr6; + /* Always use the default FIB. */ #ifdef RADIX_MPATH bzero(&ro, sizeof(ro)); ro.ro_dst = tsin6; - rtalloc_mpath((struct route *)&ro, RTF_ANNOUNCE); + rtalloc_mpath_fib((struct route *)&ro, RTF_ANNOUNCE, + RT_DEFAULT_FIB); rt = ro.ro_rt; #else - rt = rtalloc1((struct sockaddr *)&tsin6, 0, 0); + rt = in6_rtalloc1((struct sockaddr *)&tsin6, 0, 0, + RT_DEFAULT_FIB); #endif need_proxy = (rt && (rt->rt_flags & RTF_ANNOUNCE) != 0 && rt->rt_gateway->sa_family == AF_LINK); - if (rt) + if (rt != NULL) { + /* + * Make a copy while we can be sure that rt_gateway + * is still stable before unlocking to avoid lock + * order problems. proxydl will only be used if + * proxy will be set in the next block. + */ + if (need_proxy) + proxydl = *SDL(rt->rt_gateway); RTFREE_LOCKED(rt); + } if (need_proxy) { /* * proxy NDP for single entry */ ifa = (struct ifaddr *)in6ifa_ifpforlinklocal(ifp, IN6_IFF_NOTREADY|IN6_IFF_ANYCAST); - if (ifa) { + if (ifa) proxy = 1; - proxydl = SDL(rt->rt_gateway); - } } } if (ifa == NULL) { @@ -331,20 +343,21 @@ nd6_ns_input(struct mbuf *m, int off, int icmp6len) in6_all = in6addr_linklocal_allnodes; if (in6_setscope(&in6_all, ifp, NULL) != 0) goto bad; - nd6_na_output(ifp, &in6_all, &taddr6, + nd6_na_output_fib(ifp, &in6_all, &taddr6, ((anycast || proxy || !tlladdr) ? 0 : ND_NA_FLAG_OVERRIDE) | (V_ip6_forwarding ? ND_NA_FLAG_ROUTER : 0), - tlladdr, (struct sockaddr *)proxydl); + 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(ifp, &saddr6, &taddr6, + nd6_na_output_fib(ifp, &saddr6, &taddr6, ((anycast || proxy || !tlladdr) ? 0 : ND_NA_FLAG_OVERRIDE) | (V_ip6_forwarding ? ND_NA_FLAG_ROUTER : 0) | ND_NA_FLAG_SOLICITED, - tlladdr, (struct sockaddr *)proxydl); + tlladdr, proxy ? (struct sockaddr *)&proxydl : NULL, M_GETFIB(m)); freeit: if (ifa != NULL) ifa_free(ifa); @@ -495,14 +508,16 @@ nd6_ns_output(struct ifnet *ifp, const struct in6_addr *daddr6, int error; struct sockaddr_in6 dst_sa; struct in6_addr src_in; + struct ifnet *oifp; bzero(&dst_sa, sizeof(dst_sa)); dst_sa.sin6_family = AF_INET6; dst_sa.sin6_len = sizeof(dst_sa); dst_sa.sin6_addr = ip6->ip6_dst; + oifp = ifp; error = in6_selectsrc(&dst_sa, NULL, - NULL, &ro, NULL, NULL, &src_in); + NULL, &ro, NULL, &oifp, &src_in); if (error) { char ip6buf[INET6_ADDRSTRLEN]; nd6log((LOG_DEBUG, @@ -917,12 +932,13 @@ nd6_na_input(struct mbuf *m, int off, int icmp6len) * tlladdr - 1 if include target link-layer address * sdl0 - sockaddr_dl (= proxy NA) or NULL */ -void -nd6_na_output(struct ifnet *ifp, const struct in6_addr *daddr6_0, +static void +nd6_na_output_fib(struct ifnet *ifp, const struct in6_addr *daddr6_0, const struct in6_addr *taddr6, u_long flags, int tlladdr, - struct sockaddr *sdl0) + struct sockaddr *sdl0, u_int fibnum) { struct mbuf *m; + struct ifnet *oifp; struct ip6_hdr *ip6; struct nd_neighbor_advert *nd_na; struct ip6_moptions im6o; @@ -958,6 +974,7 @@ nd6_na_output(struct ifnet *ifp, const struct in6_addr *daddr6_0, if (m == NULL) return; m->m_pkthdr.rcvif = NULL; + M_SETFIB(m, fibnum); if (IN6_IS_ADDR_MULTICAST(&daddr6)) { m->m_flags |= M_MCAST; @@ -999,7 +1016,8 @@ nd6_na_output(struct ifnet *ifp, const struct in6_addr *daddr6_0, * Select a source whose scope is the same as that of the dest. */ bcopy(&dst_sa, &ro.ro_dst, sizeof(dst_sa)); - error = in6_selectsrc(&dst_sa, NULL, NULL, &ro, NULL, NULL, &src); + oifp = ifp; + error = in6_selectsrc(&dst_sa, NULL, NULL, &ro, NULL, &oifp, &src); if (error) { char ip6buf[INET6_ADDRSTRLEN]; nd6log((LOG_DEBUG, "nd6_na_output: source can't be " @@ -1079,6 +1097,18 @@ nd6_na_output(struct ifnet *ifp, const struct in6_addr *daddr6_0, return; } +#ifndef BURN_BRIDGES +void +nd6_na_output(struct ifnet *ifp, const struct in6_addr *daddr6_0, + const struct in6_addr *taddr6, u_long flags, int tlladdr, + struct sockaddr *sdl0) +{ + + nd6_na_output_fib(ifp, daddr6_0, taddr6, flags, tlladdr, sdl0, + RT_DEFAULT_FIB); +} +#endif + caddr_t nd6_ifptomac(struct ifnet *ifp) { @@ -1126,11 +1156,11 @@ nd6_dad_find(struct ifaddr *ifa) { struct dadq *dp; - for (dp = V_dadq.tqh_first; dp; dp = dp->dad_list.tqe_next) { + TAILQ_FOREACH(dp, &V_dadq, dad_list) if (dp->dad_ifa == ifa) - return dp; - } - return NULL; + return (dp); + + return (NULL); } static void diff --git a/freebsd/sys/netinet6/nd6_rtr.c b/freebsd/sys/netinet6/nd6_rtr.c index f2a07d96..4da54fa6 100644 --- a/freebsd/sys/netinet6/nd6_rtr.c +++ b/freebsd/sys/netinet6/nd6_rtr.c @@ -86,6 +86,9 @@ static int in6_init_prefix_ltimes(struct nd_prefix *); static void in6_init_address_ltimes __P((struct nd_prefix *, struct in6_addrlifetime *)); +static int nd6_prefix_onlink(struct nd_prefix *); +static int nd6_prefix_offlink(struct nd_prefix *); + static int rt6_deleteroute(struct radix_node *, void *); VNET_DECLARE(int, nd6_recalc_reachtm_interval); @@ -443,21 +446,21 @@ nd6_rtmsg(int cmd, struct rtentry *rt) info.rti_info[RTAX_NETMASK] = rt_mask(rt); ifp = rt->rt_ifp; if (ifp != NULL) { - IF_ADDR_LOCK(ifp); + IF_ADDR_RLOCK(ifp); ifa = TAILQ_FIRST(&ifp->if_addrhead); info.rti_info[RTAX_IFP] = ifa->ifa_addr; ifa_ref(ifa); - IF_ADDR_UNLOCK(ifp); + IF_ADDR_RUNLOCK(ifp); info.rti_info[RTAX_IFA] = rt->rt_ifa->ifa_addr; } else ifa = NULL; - rt_missmsg(cmd, &info, rt->rt_flags, 0); + rt_missmsg_fib(cmd, &info, rt->rt_flags, 0, rt->rt_fibnum); if (ifa != NULL) ifa_free(ifa); } -void +static void defrouter_addreq(struct nd_defrouter *new) { struct sockaddr_in6 def, mask, gate; @@ -475,9 +478,9 @@ defrouter_addreq(struct nd_defrouter *new) gate.sin6_addr = new->rtaddr; s = splnet(); - error = rtrequest(RTM_ADD, (struct sockaddr *)&def, + error = in6_rtrequest(RTM_ADD, (struct sockaddr *)&def, (struct sockaddr *)&gate, (struct sockaddr *)&mask, - RTF_GATEWAY, &newrt); + RTF_GATEWAY, &newrt, RT_DEFAULT_FIB); if (newrt) { nd6_rtmsg(RTM_ADD, newrt); /* tell user process */ RTFREE(newrt); @@ -493,8 +496,7 @@ defrouter_lookup(struct in6_addr *addr, struct ifnet *ifp) { struct nd_defrouter *dr; - for (dr = TAILQ_FIRST(&V_nd_defrouter); dr; - dr = TAILQ_NEXT(dr, dr_entry)) { + TAILQ_FOREACH(dr, &V_nd_defrouter, dr_entry) { if (dr->ifp == ifp && IN6_ARE_ADDR_EQUAL(addr, &dr->rtaddr)) return (dr); } @@ -522,9 +524,9 @@ defrouter_delreq(struct nd_defrouter *dr) def.sin6_family = gate.sin6_family = AF_INET6; gate.sin6_addr = dr->rtaddr; - rtrequest(RTM_DELETE, (struct sockaddr *)&def, + in6_rtrequest(RTM_DELETE, (struct sockaddr *)&def, (struct sockaddr *)&gate, - (struct sockaddr *)&mask, RTF_GATEWAY, &oldrt); + (struct sockaddr *)&mask, RTF_GATEWAY, &oldrt, RT_DEFAULT_FIB); if (oldrt) { nd6_rtmsg(RTM_DELETE, oldrt); RTFREE(oldrt); @@ -541,8 +543,7 @@ defrouter_reset(void) { struct nd_defrouter *dr; - for (dr = TAILQ_FIRST(&V_nd_defrouter); dr; - dr = TAILQ_NEXT(dr, dr_entry)) + TAILQ_FOREACH(dr, &V_nd_defrouter, dr_entry) defrouter_delreq(dr); /* @@ -573,7 +574,7 @@ defrtrlist_del(struct nd_defrouter *dr) /* * Also delete all the pointers to the router in each prefix lists. */ - for (pr = V_nd_prefix.lh_first; pr; pr = pr->ndpr_next) { + LIST_FOREACH(pr, &V_nd_prefix, ndpr_entry) { struct nd_pfxrouter *pfxrtr; if ((pfxrtr = pfxrtr_lookup(pr, dr)) != NULL) pfxrtr_del(pfxrtr); @@ -637,7 +638,7 @@ defrouter_select(void) * Let's handle easy case (3) first: * If default router list is empty, there's nothing to be done. */ - if (!TAILQ_FIRST(&V_nd_defrouter)) { + if (TAILQ_EMPTY(&V_nd_defrouter)) { splx(s); return; } @@ -647,15 +648,14 @@ defrouter_select(void) * We just pick up the first reachable one (if any), assuming that * the ordering rule of the list described in defrtrlist_update(). */ - for (dr = TAILQ_FIRST(&V_nd_defrouter); dr; - dr = TAILQ_NEXT(dr, dr_entry)) { - IF_AFDATA_LOCK(dr->ifp); + TAILQ_FOREACH(dr, &V_nd_defrouter, dr_entry) { + IF_AFDATA_RLOCK(dr->ifp); if (selected_dr == NULL && (ln = nd6_lookup(&dr->rtaddr, 0, dr->ifp)) && ND6_IS_LLINFO_PROBREACH(ln)) { selected_dr = dr; } - IF_AFDATA_UNLOCK(dr->ifp); + IF_AFDATA_RUNLOCK(dr->ifp); if (ln != NULL) { LLE_RUNLOCK(ln); ln = NULL; @@ -683,13 +683,13 @@ defrouter_select(void) else selected_dr = TAILQ_NEXT(installed_dr, dr_entry); } else if (installed_dr) { - IF_AFDATA_LOCK(installed_dr->ifp); + IF_AFDATA_RLOCK(installed_dr->ifp); if ((ln = nd6_lookup(&installed_dr->rtaddr, 0, installed_dr->ifp)) && ND6_IS_LLINFO_PROBREACH(ln) && rtpref(selected_dr) <= rtpref(installed_dr)) { selected_dr = installed_dr; } - IF_AFDATA_UNLOCK(installed_dr->ifp); + IF_AFDATA_RUNLOCK(installed_dr->ifp); if (ln != NULL) LLE_RUNLOCK(ln); } @@ -757,9 +757,10 @@ defrtrlist_update(struct nd_defrouter *new) /* * If the preference does not change, there's no need - * to sort the entries. + * to sort the entries. Also make sure the selected + * router is still installed in the kernel. */ - if (rtpref(new) == oldpref) { + if (dr->installed && rtpref(new) == oldpref) { splx(s); return (dr); } @@ -804,8 +805,7 @@ insert: */ /* insert at the end of the group */ - for (dr = TAILQ_FIRST(&V_nd_defrouter); dr; - dr = TAILQ_NEXT(dr, dr_entry)) { + TAILQ_FOREACH(dr, &V_nd_defrouter, dr_entry) { if (rtpref(n) > rtpref(dr)) break; } @@ -826,7 +826,7 @@ pfxrtr_lookup(struct nd_prefix *pr, struct nd_defrouter *dr) { struct nd_pfxrouter *search; - for (search = pr->ndpr_advrtrs.lh_first; search; search = search->pfr_next) { + LIST_FOREACH(search, &pr->ndpr_advrtrs, pfr_entry) { if (search->router == dr) break; } @@ -862,8 +862,7 @@ nd6_prefix_lookup(struct nd_prefixctl *key) { struct nd_prefix *search; - for (search = V_nd_prefix.lh_first; - search; search = search->ndpr_next) { + LIST_FOREACH(search, &V_nd_prefix, ndpr_entry) { if (key->ndpr_ifp == search->ndpr_ifp && key->ndpr_plen == search->ndpr_plen && in6_are_prefix_equal(&key->ndpr_prefix.sin6_addr, @@ -969,9 +968,7 @@ prelist_remove(struct nd_prefix *pr) LIST_REMOVE(pr, ndpr_entry); /* free list of routers that adversed the prefix */ - for (pfr = pr->ndpr_advrtrs.lh_first; pfr; pfr = next) { - next = pfr->pfr_next; - + LIST_FOREACH_SAFE(pfr, &pr->ndpr_advrtrs, pfr_entry, next) { free(pfr, M_IP6NDP); } splx(s); @@ -1119,7 +1116,7 @@ prelist_update(struct nd_prefixctl *new, struct nd_defrouter *dr, * consider autoconfigured addresses while RFC2462 simply said * "address". */ - IF_ADDR_LOCK(ifp); + IF_ADDR_RLOCK(ifp); TAILQ_FOREACH(ifa, &ifp->if_addrhead, ifa_link) { struct in6_ifaddr *ifa6; u_int32_t remaininglifetime; @@ -1242,7 +1239,7 @@ prelist_update(struct nd_prefixctl *new, struct nd_defrouter *dr, ifa6->ia6_lifetime = lt6_tmp; ifa6->ia6_updatetime = time_second; } - IF_ADDR_UNLOCK(ifp); + IF_ADDR_RUNLOCK(ifp); if (ia6_match == NULL && new->ndpr_vltime) { int ifidlen; @@ -1334,11 +1331,10 @@ find_pfxlist_reachable_router(struct nd_prefix *pr) struct llentry *ln; int canreach; - for (pfxrtr = LIST_FIRST(&pr->ndpr_advrtrs); pfxrtr != NULL; - pfxrtr = LIST_NEXT(pfxrtr, pfr_entry)) { - IF_AFDATA_LOCK(pfxrtr->router->ifp); + LIST_FOREACH(pfxrtr, &pr->ndpr_advrtrs, pfr_entry) { + IF_AFDATA_RLOCK(pfxrtr->router->ifp); ln = nd6_lookup(&pfxrtr->router->rtaddr, 0, pfxrtr->router->ifp); - IF_AFDATA_UNLOCK(pfxrtr->router->ifp); + IF_AFDATA_RUNLOCK(pfxrtr->router->ifp); if (ln == NULL) continue; canreach = ND6_IS_LLINFO_PROBREACH(ln); @@ -1374,7 +1370,7 @@ pfxlist_onlink_check() * Check if there is a prefix that has a reachable advertising * router. */ - for (pr = V_nd_prefix.lh_first; pr; pr = pr->ndpr_next) { + LIST_FOREACH(pr, &V_nd_prefix, ndpr_entry) { if (pr->ndpr_raf_onlink && find_pfxlist_reachable_router(pr)) break; } @@ -1384,12 +1380,10 @@ pfxlist_onlink_check() * that does not advertise any prefixes. */ if (pr == NULL) { - for (dr = TAILQ_FIRST(&V_nd_defrouter); dr; - dr = TAILQ_NEXT(dr, dr_entry)) { + TAILQ_FOREACH(dr, &V_nd_defrouter, dr_entry) { struct nd_prefix *pr0; - for (pr0 = V_nd_prefix.lh_first; pr0; - pr0 = pr0->ndpr_next) { + LIST_FOREACH(pr0, &V_nd_prefix, ndpr_entry) { if ((pfxrtr = pfxrtr_lookup(pr0, dr)) != NULL) break; } @@ -1397,7 +1391,7 @@ pfxlist_onlink_check() break; } } - if (pr != NULL || (TAILQ_FIRST(&V_nd_defrouter) && pfxrtr == NULL)) { + if (pr != NULL || (!TAILQ_EMPTY(&V_nd_defrouter) && pfxrtr == NULL)) { /* * There is at least one prefix that has a reachable router, * or at least a router which probably does not advertise @@ -1407,7 +1401,7 @@ pfxlist_onlink_check() * Detach prefixes which have no reachable advertising * router, and attach other prefixes. */ - for (pr = V_nd_prefix.lh_first; pr; pr = pr->ndpr_next) { + LIST_FOREACH(pr, &V_nd_prefix, ndpr_entry) { /* XXX: a link-local prefix should never be detached */ if (IN6_IS_ADDR_LINKLOCAL(&pr->ndpr_prefix.sin6_addr)) continue; @@ -1431,7 +1425,7 @@ pfxlist_onlink_check() } } else { /* there is no prefix that has a reachable router */ - for (pr = V_nd_prefix.lh_first; pr; pr = pr->ndpr_next) { + LIST_FOREACH(pr, &V_nd_prefix, ndpr_entry) { if (IN6_IS_ADDR_LINKLOCAL(&pr->ndpr_prefix.sin6_addr)) continue; @@ -1454,7 +1448,7 @@ pfxlist_onlink_check() * interfaces. Such cases will be handled in nd6_prefix_onlink, * so we don't have to care about them. */ - for (pr = V_nd_prefix.lh_first; pr; pr = pr->ndpr_next) { + LIST_FOREACH(pr, &V_nd_prefix, ndpr_entry) { int e; char ip6buf[INET6_ADDRSTRLEN]; @@ -1552,19 +1546,92 @@ pfxlist_onlink_check() } } -int +static int +nd6_prefix_onlink_rtrequest(struct nd_prefix *pr, struct ifaddr *ifa) +{ + static struct sockaddr_dl null_sdl = {sizeof(null_sdl), AF_LINK}; + struct radix_node_head *rnh; + struct rtentry *rt; + struct sockaddr_in6 mask6; + u_long rtflags; + int error, a_failure, fibnum; + + /* + * in6_ifinit() sets nd6_rtrequest to ifa_rtrequest for all ifaddrs. + * ifa->ifa_rtrequest = nd6_rtrequest; + */ + bzero(&mask6, sizeof(mask6)); + mask6.sin6_len = sizeof(mask6); + mask6.sin6_addr = pr->ndpr_mask; + rtflags = (ifa->ifa_flags & ~IFA_RTSELF) | RTF_UP; + + a_failure = 0; + for (fibnum = 0; fibnum < rt_numfibs; fibnum++) { + + rt = NULL; + error = in6_rtrequest(RTM_ADD, + (struct sockaddr *)&pr->ndpr_prefix, ifa->ifa_addr, + (struct sockaddr *)&mask6, rtflags, &rt, fibnum); + if (error == 0) { + KASSERT(rt != NULL, ("%s: in6_rtrequest return no " + "error(%d) but rt is NULL, pr=%p, ifa=%p", __func__, + error, pr, ifa)); + + rnh = rt_tables_get_rnh(rt->rt_fibnum, AF_INET6); + /* XXX what if rhn == NULL? */ + RADIX_NODE_HEAD_LOCK(rnh); + RT_LOCK(rt); + if (rt_setgate(rt, rt_key(rt), + (struct sockaddr *)&null_sdl) == 0) { + struct sockaddr_dl *dl; + + dl = (struct sockaddr_dl *)rt->rt_gateway; + dl->sdl_type = rt->rt_ifp->if_type; + dl->sdl_index = rt->rt_ifp->if_index; + } + RADIX_NODE_HEAD_UNLOCK(rnh); + nd6_rtmsg(RTM_ADD, rt); + RT_UNLOCK(rt); + pr->ndpr_stateflags |= NDPRF_ONLINK; + } else { + char ip6buf[INET6_ADDRSTRLEN]; + char ip6bufg[INET6_ADDRSTRLEN]; + char ip6bufm[INET6_ADDRSTRLEN]; + struct sockaddr_in6 *sin6; + + sin6 = (struct sockaddr_in6 *)ifa->ifa_addr; + nd6log((LOG_ERR, "nd6_prefix_onlink: failed to add " + "route for a prefix (%s/%d) on %s, gw=%s, mask=%s, " + "flags=%lx errno = %d\n", + ip6_sprintf(ip6buf, &pr->ndpr_prefix.sin6_addr), + pr->ndpr_plen, if_name(pr->ndpr_ifp), + ip6_sprintf(ip6bufg, &sin6->sin6_addr), + ip6_sprintf(ip6bufm, &mask6.sin6_addr), + rtflags, error)); + + /* Save last error to return, see rtinit(). */ + a_failure = error; + } + + if (rt != NULL) { + RT_LOCK(rt); + RT_REMREF(rt); + RT_UNLOCK(rt); + } + } + + /* Return the last error we got. */ + return (a_failure); +} + +static int nd6_prefix_onlink(struct nd_prefix *pr) { struct ifaddr *ifa; struct ifnet *ifp = pr->ndpr_ifp; - struct sockaddr_in6 mask6; struct nd_prefix *opr; - u_long rtflags; int error = 0; - struct radix_node_head *rnh; - struct rtentry *rt = NULL; char ip6buf[INET6_ADDRSTRLEN]; - struct sockaddr_dl null_sdl = {sizeof(null_sdl), AF_LINK}; /* sanity check */ if ((pr->ndpr_stateflags & NDPRF_ONLINK) != 0) { @@ -1582,7 +1649,7 @@ nd6_prefix_onlink(struct nd_prefix *pr) * Although such a configuration is expected to be rare, we explicitly * allow it. */ - for (opr = V_nd_prefix.lh_first; opr; opr = opr->ndpr_next) { + LIST_FOREACH(opr, &V_nd_prefix, ndpr_entry) { if (opr == pr) continue; @@ -1603,14 +1670,14 @@ nd6_prefix_onlink(struct nd_prefix *pr) IN6_IFF_NOTREADY | IN6_IFF_ANYCAST); if (ifa == NULL) { /* XXX: freebsd does not have ifa_ifwithaf */ - IF_ADDR_LOCK(ifp); + IF_ADDR_RLOCK(ifp); TAILQ_FOREACH(ifa, &ifp->if_addrhead, ifa_link) { if (ifa->ifa_addr->sa_family == AF_INET6) break; } if (ifa != NULL) ifa_ref(ifa); - IF_ADDR_UNLOCK(ifp); + IF_ADDR_RUNLOCK(ifp); /* should we care about ia6_flags? */ } if (ifa == NULL) { @@ -1628,64 +1695,24 @@ nd6_prefix_onlink(struct nd_prefix *pr) return (0); } - /* - * in6_ifinit() sets nd6_rtrequest to ifa_rtrequest for all ifaddrs. - * ifa->ifa_rtrequest = nd6_rtrequest; - */ - bzero(&mask6, sizeof(mask6)); - mask6.sin6_len = sizeof(mask6); - mask6.sin6_addr = pr->ndpr_mask; - rtflags = (ifa->ifa_flags & ~IFA_RTSELF) | RTF_UP; - error = rtrequest(RTM_ADD, (struct sockaddr *)&pr->ndpr_prefix, - ifa->ifa_addr, (struct sockaddr *)&mask6, rtflags, &rt); - if (error == 0) { - if (rt != NULL) /* this should be non NULL, though */ { - rnh = rt_tables_get_rnh(rt->rt_fibnum, AF_INET6); - /* XXX what if rhn == NULL? */ - RADIX_NODE_HEAD_LOCK(rnh); - RT_LOCK(rt); - if (!rt_setgate(rt, rt_key(rt), (struct sockaddr *)&null_sdl)) { - ((struct sockaddr_dl *)rt->rt_gateway)->sdl_type = - rt->rt_ifp->if_type; - ((struct sockaddr_dl *)rt->rt_gateway)->sdl_index = - rt->rt_ifp->if_index; - } - RADIX_NODE_HEAD_UNLOCK(rnh); - nd6_rtmsg(RTM_ADD, rt); - RT_UNLOCK(rt); - } - pr->ndpr_stateflags |= NDPRF_ONLINK; - } else { - char ip6bufg[INET6_ADDRSTRLEN], ip6bufm[INET6_ADDRSTRLEN]; - nd6log((LOG_ERR, "nd6_prefix_onlink: failed to add route for a" - " prefix (%s/%d) on %s, gw=%s, mask=%s, flags=%lx " - "errno = %d\n", - ip6_sprintf(ip6buf, &pr->ndpr_prefix.sin6_addr), - pr->ndpr_plen, if_name(ifp), - ip6_sprintf(ip6bufg, &((struct sockaddr_in6 *)ifa->ifa_addr)->sin6_addr), - ip6_sprintf(ip6bufm, &mask6.sin6_addr), rtflags, error)); - } + error = nd6_prefix_onlink_rtrequest(pr, ifa); - if (rt != NULL) { - RT_LOCK(rt); - RT_REMREF(rt); - RT_UNLOCK(rt); - } if (ifa != NULL) ifa_free(ifa); return (error); } -int +static int nd6_prefix_offlink(struct nd_prefix *pr) { int error = 0; struct ifnet *ifp = pr->ndpr_ifp; struct nd_prefix *opr; struct sockaddr_in6 sa6, mask6; - struct rtentry *rt = NULL; + struct rtentry *rt; char ip6buf[INET6_ADDRSTRLEN]; + int fibnum, a_failure; /* sanity check */ if ((pr->ndpr_stateflags & NDPRF_ONLINK) == 0) { @@ -1705,15 +1732,28 @@ nd6_prefix_offlink(struct nd_prefix *pr) mask6.sin6_family = AF_INET6; mask6.sin6_len = sizeof(sa6); bcopy(&pr->ndpr_mask, &mask6.sin6_addr, sizeof(struct in6_addr)); - error = rtrequest(RTM_DELETE, (struct sockaddr *)&sa6, NULL, - (struct sockaddr *)&mask6, 0, &rt); + + a_failure = 0; + for (fibnum = 0; fibnum < rt_numfibs; fibnum++) { + rt = NULL; + error = in6_rtrequest(RTM_DELETE, (struct sockaddr *)&sa6, NULL, + (struct sockaddr *)&mask6, 0, &rt, fibnum); + if (error == 0) { + /* report the route deletion to the routing socket. */ + if (rt != NULL) + nd6_rtmsg(RTM_DELETE, rt); + } else { + /* Save last error to return, see rtinit(). */ + a_failure = error; + } + if (rt != NULL) { + RTFREE(rt); + } + } + error = a_failure; if (error == 0) { pr->ndpr_stateflags &= ~NDPRF_ONLINK; - /* report the route deletion to the routing socket. */ - if (rt != NULL) - nd6_rtmsg(RTM_DELETE, rt); - /* * There might be the same prefix on another interface, * the prefix which could not be on-link just because we have @@ -1721,7 +1761,7 @@ nd6_prefix_offlink(struct nd_prefix *pr) * If there's one, try to make the prefix on-link on the * interface. */ - for (opr = V_nd_prefix.lh_first; opr; opr = opr->ndpr_next) { + LIST_FOREACH(opr, &V_nd_prefix, ndpr_entry) { if (opr == pr) continue; @@ -1761,10 +1801,6 @@ nd6_prefix_offlink(struct nd_prefix *pr) if_name(ifp), error)); } - if (rt != NULL) { - RTFREE(rt); - } - return (error); } @@ -2081,6 +2117,7 @@ void rt6_flush(struct in6_addr *gateway, struct ifnet *ifp) { struct radix_node_head *rnh; + u_int fibnum; int s = splnet(); /* We'll care only link-local addresses */ @@ -2089,13 +2126,16 @@ rt6_flush(struct in6_addr *gateway, struct ifnet *ifp) return; } - rnh = rt_tables_get_rnh(0, AF_INET6); - if (rnh == NULL) - return; + /* XXX Do we really need to walk any but the default FIB? */ + for (fibnum = 0; fibnum < rt_numfibs; fibnum++) { + rnh = rt_tables_get_rnh(fibnum, AF_INET6); + if (rnh == NULL) + continue; - RADIX_NODE_HEAD_LOCK(rnh); - rnh->rnh_walktree(rnh, rt6_deleteroute, (void *)gateway); - RADIX_NODE_HEAD_UNLOCK(rnh); + RADIX_NODE_HEAD_LOCK(rnh); + rnh->rnh_walktree(rnh, rt6_deleteroute, (void *)gateway); + RADIX_NODE_HEAD_UNLOCK(rnh); + } splx(s); } @@ -2128,8 +2168,8 @@ rt6_deleteroute(struct radix_node *rn, void *arg) if ((rt->rt_flags & RTF_HOST) == 0) return (0); - return (rtrequest(RTM_DELETE, rt_key(rt), rt->rt_gateway, - rt_mask(rt), rt->rt_flags, 0)); + return (in6_rtrequest(RTM_DELETE, rt_key(rt), rt->rt_gateway, + rt_mask(rt), rt->rt_flags, NULL, rt->rt_fibnum)); #undef SIN6 } diff --git a/freebsd/sys/netinet6/raw_ip6.c b/freebsd/sys/netinet6/raw_ip6.c index 9ea1895d..45645e29 100644 --- a/freebsd/sys/netinet6/raw_ip6.c +++ b/freebsd/sys/netinet6/raw_ip6.c @@ -197,6 +197,7 @@ rip6_input(struct mbuf **mp, int *offp, int proto) &ip6->ip6_dst) != 0) continue; } + INP_RLOCK(in6p); if (in6p->in6p_cksum != -1) { V_rip6stat.rip6s_isum++; if (in6_cksum(m, proto, *offp, @@ -206,7 +207,6 @@ rip6_input(struct mbuf **mp, int *offp, int proto) continue; } } - INP_RLOCK(in6p); /* * If this raw socket has multicast state, and we * have received a multicast, check if this socket @@ -564,6 +564,7 @@ rip6_output(m, va_alist) int rip6_ctloutput(struct socket *so, struct sockopt *sopt) { + struct inpcb *inp; int error; if (sopt->sopt_level == IPPROTO_ICMPV6) @@ -572,8 +573,17 @@ rip6_ctloutput(struct socket *so, struct sockopt *sopt) * from protosw? */ return (icmp6_ctloutput(so, sopt)); - else if (sopt->sopt_level != IPPROTO_IPV6) + else if (sopt->sopt_level != IPPROTO_IPV6) { + if (sopt->sopt_level == SOL_SOCKET && + sopt->sopt_name == SO_SETFIB) { + inp = sotoinpcb(so); + INP_WLOCK(inp); + inp->inp_inc.inc_fibnum = so->so_fibnum; + INP_WUNLOCK(inp); + return (0); + } return (EINVAL); + } error = 0; diff --git a/freebsd/sys/netinet6/scope6.c b/freebsd/sys/netinet6/scope6.c index 655022d6..a5c7d7b1 100644 --- a/freebsd/sys/netinet6/scope6.c +++ b/freebsd/sys/netinet6/scope6.c @@ -123,11 +123,11 @@ scope6_set(struct ifnet *ifp, struct scope6_id *idlist) int error = 0; struct scope6_id *sid = NULL; - IF_AFDATA_LOCK(ifp); + IF_AFDATA_WLOCK(ifp); sid = SID(ifp); if (!sid) { /* paranoid? */ - IF_AFDATA_UNLOCK(ifp); + IF_AFDATA_WUNLOCK(ifp); return (EINVAL); } @@ -141,7 +141,6 @@ scope6_set(struct ifnet *ifp, struct scope6_id *idlist) * interface addresses, routing table entries, PCB entries... */ - SCOPE6_LOCK(); for (i = 0; i < 16; i++) { if (idlist->s6id_list[i] && idlist->s6id_list[i] != sid->s6id_list[i]) { @@ -151,8 +150,7 @@ scope6_set(struct ifnet *ifp, struct scope6_id *idlist) */ if (i == IPV6_ADDR_SCOPE_INTFACELOCAL && idlist->s6id_list[i] != ifp->if_index) { - IF_AFDATA_UNLOCK(ifp); - SCOPE6_UNLOCK(); + IF_AFDATA_WUNLOCK(ifp); return (EINVAL); } @@ -164,8 +162,7 @@ scope6_set(struct ifnet *ifp, struct scope6_id *idlist) * IDs, but we check the consistency for * safety in later use. */ - IF_AFDATA_UNLOCK(ifp); - SCOPE6_UNLOCK(); + IF_AFDATA_WUNLOCK(ifp); return (EINVAL); } @@ -177,8 +174,7 @@ scope6_set(struct ifnet *ifp, struct scope6_id *idlist) sid->s6id_list[i] = idlist->s6id_list[i]; } } - SCOPE6_UNLOCK(); - IF_AFDATA_UNLOCK(ifp); + IF_AFDATA_WUNLOCK(ifp); return (error); } @@ -186,20 +182,19 @@ scope6_set(struct ifnet *ifp, struct scope6_id *idlist) int scope6_get(struct ifnet *ifp, struct scope6_id *idlist) { - /* We only need to lock the interface's afdata for SID() to work. */ - IF_AFDATA_LOCK(ifp); - struct scope6_id *sid = SID(ifp); + struct scope6_id *sid; + /* We only need to lock the interface's afdata for SID() to work. */ + IF_AFDATA_RLOCK(ifp); + sid = SID(ifp); if (sid == NULL) { /* paranoid? */ - IF_AFDATA_UNLOCK(ifp); + IF_AFDATA_RUNLOCK(ifp); return (EINVAL); } - SCOPE6_LOCK(); *idlist = *sid; - SCOPE6_UNLOCK(); - IF_AFDATA_UNLOCK(ifp); + IF_AFDATA_RUNLOCK(ifp); return (0); } @@ -390,7 +385,7 @@ sa6_recoverscope(struct sockaddr_in6 *sin6) zoneid = ntohs(sin6->sin6_addr.s6_addr16[1]); if (zoneid) { /* sanity check */ - if (zoneid < 0 || V_if_index < zoneid) + if (V_if_index < zoneid) return (ENXIO); if (!ifnet_byindex(zoneid)) return (ENXIO); @@ -416,7 +411,7 @@ in6_setscope(struct in6_addr *in6, struct ifnet *ifp, u_int32_t *ret_id) u_int32_t zoneid = 0; struct scope6_id *sid; - IF_AFDATA_LOCK(ifp); + IF_AFDATA_RLOCK(ifp); sid = SID(ifp); @@ -433,19 +428,17 @@ in6_setscope(struct in6_addr *in6, struct ifnet *ifp, u_int32_t *ret_id) */ if (IN6_IS_ADDR_LOOPBACK(in6)) { if (!(ifp->if_flags & IFF_LOOPBACK)) { - IF_AFDATA_UNLOCK(ifp); + IF_AFDATA_RUNLOCK(ifp); return (EINVAL); } else { if (ret_id != NULL) *ret_id = 0; /* there's no ambiguity */ - IF_AFDATA_UNLOCK(ifp); + IF_AFDATA_RUNLOCK(ifp); return (0); } } scope = in6_addrscope(in6); - - SCOPE6_LOCK(); switch (scope) { case IPV6_ADDR_SCOPE_INTFACELOCAL: /* should be interface index */ zoneid = sid->s6id_list[IPV6_ADDR_SCOPE_INTFACELOCAL]; @@ -467,8 +460,7 @@ in6_setscope(struct in6_addr *in6, struct ifnet *ifp, u_int32_t *ret_id) zoneid = 0; /* XXX: treat as global. */ break; } - SCOPE6_UNLOCK(); - IF_AFDATA_UNLOCK(ifp); + IF_AFDATA_RUNLOCK(ifp); if (ret_id != NULL) *ret_id = zoneid; @@ -496,3 +488,16 @@ in6_clearscope(struct in6_addr *in6) return (modified); } + +/* + * Return the scope identifier or zero. + */ +uint16_t +in6_getscope(struct in6_addr *in6) +{ + + if (IN6_IS_SCOPE_LINKLOCAL(in6) || IN6_IS_ADDR_MC_INTFACELOCAL(in6)) + return (in6->s6_addr16[1]); + + return (0); +} diff --git a/freebsd/sys/netinet6/scope6_var.h b/freebsd/sys/netinet6/scope6_var.h index 55e0c8ed..2248037f 100644 --- a/freebsd/sys/netinet6/scope6_var.h +++ b/freebsd/sys/netinet6/scope6_var.h @@ -49,12 +49,12 @@ int scope6_set __P((struct ifnet *, struct scope6_id *)); int scope6_get __P((struct ifnet *, struct scope6_id *)); void scope6_setdefault __P((struct ifnet *)); int scope6_get_default __P((struct scope6_id *)); -u_int32_t scope6_in6_addrscope __P((struct in6_addr *)); u_int32_t scope6_addr2default __P((struct in6_addr *)); int sa6_embedscope __P((struct sockaddr_in6 *, int)); int sa6_recoverscope __P((struct sockaddr_in6 *)); int in6_setscope __P((struct in6_addr *, struct ifnet *, u_int32_t *)); int in6_clearscope __P((struct in6_addr *)); +uint16_t in6_getscope(struct in6_addr *); #endif /* _KERNEL */ #endif /* _NETINET6_SCOPE6_VAR_H_ */ diff --git a/freebsd/sys/netinet6/sctp6_usrreq.c b/freebsd/sys/netinet6/sctp6_usrreq.c index 16b9e4fd..0551d48b 100644 --- a/freebsd/sys/netinet6/sctp6_usrreq.c +++ b/freebsd/sys/netinet6/sctp6_usrreq.c @@ -2,16 +2,18 @@ /*- * Copyright (c) 2001-2007, by Cisco Systems, Inc. All rights reserved. + * Copyright (c) 2008-2012, by Randall Stewart. All rights reserved. + * Copyright (c) 2008-2012, by Michael Tuexen. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * a) Redistributions of source code must retain the above copyright notice, - * this list of conditions and the following disclaimer. + * this list of conditions and the following disclaimer. * * b) Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in - * the documentation and/or other materials provided with the distribution. + * the documentation and/or other materials provided with the distribution. * * c) Neither the name of Cisco Systems, Inc. nor the names of its * contributors may be used to endorse or promote products derived @@ -29,17 +31,17 @@ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF * THE POSSIBILITY OF SUCH DAMAGE. */ -/* $KAME: sctp6_usrreq.c,v 1.38 2005/08/24 08:08:56 suz Exp $ */ #include <sys/cdefs.h> __FBSDID("$FreeBSD$"); #include <netinet/sctp_os.h> +#ifdef INET6 #include <sys/proc.h> #include <netinet/sctp_pcb.h> #include <netinet/sctp_header.h> #include <netinet/sctp_var.h> -#if defined(INET6) +#ifdef INET6 #include <netinet6/sctp6_var.h> #endif #include <netinet/sctp_sysctl.h> @@ -58,7 +60,7 @@ __FBSDID("$FreeBSD$"); #ifdef IPSEC #include <netipsec/ipsec.h> -#if defined(INET6) +#ifdef INET6 #include <netipsec/ipsec6.h> #endif /* INET6 */ #endif /* IPSEC */ @@ -66,222 +68,140 @@ __FBSDID("$FreeBSD$"); extern struct protosw inetsw[]; int -sctp6_input(struct mbuf **i_pak, int *offp, int proto) +sctp6_input_with_port(struct mbuf **i_pak, int *offp, uint16_t port) { struct mbuf *m; + int iphlen; + uint32_t vrf_id; + uint8_t ecn_bits; + struct sockaddr_in6 src, dst; struct ip6_hdr *ip6; struct sctphdr *sh; - struct sctp_inpcb *in6p = NULL; - struct sctp_nets *net; - int refcount_up = 0; - uint32_t vrf_id = 0; - -#ifdef IPSEC - struct inpcb *in6p_ip; - -#endif struct sctp_chunkhdr *ch; - int length, offset, iphlen; - uint8_t ecn_bits; - struct sctp_tcb *stcb = NULL; - int pkt_len = 0; + int length, offset; #if !defined(SCTP_WITH_NO_CSUM) - uint32_t check, calc_check; + uint8_t compute_crc; #endif - int off = *offp; - uint16_t port = 0; + uint32_t mflowid; + uint8_t use_mflowid; - /* get the VRF and table id's */ + iphlen = *offp; if (SCTP_GET_PKT_VRFID(*i_pak, vrf_id)) { SCTP_RELEASE_PKT(*i_pak); - return (-1); + return (IPPROTO_DONE); } m = SCTP_HEADER_TO_CHAIN(*i_pak); - pkt_len = SCTP_HEADER_LEN((*i_pak)); - -#ifdef SCTP_PACKET_LOGGING - sctp_packet_log(m, pkt_len); +#ifdef SCTP_MBUF_LOGGING + /* Log in any input mbufs */ + if (SCTP_BASE_SYSCTL(sctp_logging_level) & SCTP_MBUF_LOGGING_ENABLE) { + struct mbuf *mat; + + for (mat = m; mat; mat = SCTP_BUF_NEXT(mat)) { + if (SCTP_BUF_IS_EXTENDED(mat)) { + sctp_log_mb(mat, SCTP_MBUF_INPUT); + } + } + } #endif +#ifdef SCTP_PACKET_LOGGING + if (SCTP_BASE_SYSCTL(sctp_logging_level) & SCTP_LAST_PACKET_TRACING) { + sctp_packet_log(m); + } +#endif + SCTPDBG(SCTP_DEBUG_CRCOFFLOAD, + "sctp6_input(): Packet of length %d received on %s with csum_flags 0x%x.\n", + m->m_pkthdr.len, + if_name(m->m_pkthdr.rcvif), + m->m_pkthdr.csum_flags); + if (m->m_flags & M_FLOWID) { + mflowid = m->m_pkthdr.flowid; + use_mflowid = 1; + } else { + mflowid = 0; + use_mflowid = 0; + } + SCTP_STAT_INCR(sctps_recvpackets); + SCTP_STAT_INCR_COUNTER64(sctps_inpackets); + /* Get IP, SCTP, and first chunk header together in the first mbuf. */ + offset = iphlen + sizeof(struct sctphdr) + sizeof(struct sctp_chunkhdr); ip6 = mtod(m, struct ip6_hdr *); - /* Ensure that (sctphdr + sctp_chunkhdr) in a row. */ - IP6_EXTHDR_GET(sh, struct sctphdr *, m, off, - (int)(sizeof(*sh) + sizeof(*ch))); + IP6_EXTHDR_GET(sh, struct sctphdr *, m, iphlen, + (int)(sizeof(struct sctphdr) + sizeof(struct sctp_chunkhdr))); if (sh == NULL) { SCTP_STAT_INCR(sctps_hdrops); - return IPPROTO_DONE; + return (IPPROTO_DONE); } ch = (struct sctp_chunkhdr *)((caddr_t)sh + sizeof(struct sctphdr)); - iphlen = off; - offset = iphlen + sizeof(*sh) + sizeof(*ch); - SCTPDBG(SCTP_DEBUG_INPUT1, - "sctp6_input() length:%d iphlen:%d\n", pkt_len, iphlen); - - -#if defined(NFAITH) && NFAITH > 0 - - if (faithprefix_p != NULL && (*faithprefix_p) (&ip6->ip6_dst)) { + offset -= sizeof(struct sctp_chunkhdr); + memset(&src, 0, sizeof(struct sockaddr_in6)); + src.sin6_family = AF_INET6; + src.sin6_len = sizeof(struct sockaddr_in6); + src.sin6_port = sh->src_port; + src.sin6_addr = ip6->ip6_src; + if (in6_setscope(&src.sin6_addr, m->m_pkthdr.rcvif, NULL) != 0) { + goto out; + } + memset(&dst, 0, sizeof(struct sockaddr_in6)); + dst.sin6_family = AF_INET6; + dst.sin6_len = sizeof(struct sockaddr_in6); + dst.sin6_port = sh->dest_port; + dst.sin6_addr = ip6->ip6_dst; + if (in6_setscope(&dst.sin6_addr, m->m_pkthdr.rcvif, NULL) != 0) { + goto out; + } + if (faithprefix_p != NULL && (*faithprefix_p) (&dst.sin6_addr)) { /* XXX send icmp6 host/port unreach? */ - goto bad; + goto out; + } + length = ntohs(ip6->ip6_plen) + iphlen; + /* Validate mbuf chain length with IP payload length. */ + if (SCTP_HEADER_LEN(m) != length) { + SCTPDBG(SCTP_DEBUG_INPUT1, + "sctp6_input() length:%d reported length:%d\n", length, SCTP_HEADER_LEN(m)); + SCTP_STAT_INCR(sctps_hdrops); + goto out; } -#endif /* NFAITH defined and > 0 */ - SCTP_STAT_INCR(sctps_recvpackets); - SCTP_STAT_INCR_COUNTER64(sctps_inpackets); - SCTPDBG(SCTP_DEBUG_INPUT1, "V6 input gets a packet iphlen:%d pktlen:%d\n", - iphlen, pkt_len); if (IN6_IS_ADDR_MULTICAST(&ip6->ip6_dst)) { - /* No multi-cast support in SCTP */ - goto bad; + goto out; } - /* destination port of 0 is illegal, based on RFC2960. */ - if (sh->dest_port == 0) - goto bad; - - SCTPDBG(SCTP_DEBUG_CRCOFFLOAD, - "sctp_input(): Packet of length %d received on %s with csum_flags 0x%x.\n", - m->m_pkthdr.len, - if_name(m->m_pkthdr.rcvif), - m->m_pkthdr.csum_flags); + ecn_bits = ((ntohl(ip6->ip6_flow) >> 20) & 0x000000ff); #if defined(SCTP_WITH_NO_CSUM) SCTP_STAT_INCR(sctps_recvnocrc); #else if (m->m_pkthdr.csum_flags & CSUM_SCTP_VALID) { SCTP_STAT_INCR(sctps_recvhwcrc); - goto sctp_skip_csum; - } - check = sh->checksum; /* save incoming checksum */ - if ((check == 0) && (SCTP_BASE_SYSCTL(sctp_no_csum_on_loopback)) && - (IN6_ARE_ADDR_EQUAL(&ip6->ip6_src, &ip6->ip6_dst))) { - SCTP_STAT_INCR(sctps_recvnocrc); - goto sctp_skip_csum; - } - sh->checksum = 0; /* prepare for calc */ - calc_check = sctp_calculate_cksum(m, iphlen); - SCTP_STAT_INCR(sctps_recvswcrc); - if (calc_check != check) { - SCTPDBG(SCTP_DEBUG_INPUT1, "Bad CSUM on SCTP packet calc_check:%x check:%x m:%p phlen:%d\n", - calc_check, check, m, iphlen); - stcb = sctp_findassociation_addr(m, iphlen, offset - sizeof(*ch), - sh, ch, &in6p, &net, vrf_id); - if ((net) && (port)) { - if (net->port == 0) { - sctp_pathmtu_adjustment(in6p, stcb, net, net->mtu - sizeof(struct udphdr)); - } - net->port = port; - } - /* in6p's ref-count increased && stcb locked */ - if ((in6p) && (stcb)) { - sctp_send_packet_dropped(stcb, net, m, iphlen, 1); - sctp_chunk_output((struct sctp_inpcb *)in6p, stcb, SCTP_OUTPUT_FROM_INPUT_ERROR, SCTP_SO_NOT_LOCKED); - } else if ((in6p != NULL) && (stcb == NULL)) { - refcount_up = 1; - } - SCTP_STAT_INCR(sctps_badsum); - SCTP_STAT_INCR_COUNTER32(sctps_checksumerrors); - goto bad; + compute_crc = 0; + } else { + SCTP_STAT_INCR(sctps_recvswcrc); + compute_crc = 1; } - sh->checksum = calc_check; - -sctp_skip_csum: #endif - net = NULL; - /* - * Locate pcb and tcb for datagram sctp_findassociation_addr() wants - * IP/SCTP/first chunk header... - */ - stcb = sctp_findassociation_addr(m, iphlen, offset - sizeof(*ch), - sh, ch, &in6p, &net, vrf_id); - if ((net) && (port)) { - if (net->port == 0) { - sctp_pathmtu_adjustment(in6p, stcb, net, net->mtu - sizeof(struct udphdr)); - } - net->port = port; - } - /* in6p's ref-count increased */ - if (in6p == NULL) { - struct sctp_init_chunk *init_chk, chunk_buf; - - SCTP_STAT_INCR(sctps_noport); - if (ch->chunk_type == SCTP_INITIATION) { - /* - * we do a trick here to get the INIT tag, dig in - * and get the tag from the INIT and put it in the - * common header. - */ - init_chk = (struct sctp_init_chunk *)sctp_m_getptr(m, - iphlen + sizeof(*sh), sizeof(*init_chk), - (uint8_t *) & chunk_buf); - if (init_chk) - sh->v_tag = init_chk->init.initiate_tag; - else - sh->v_tag = 0; - } - if (ch->chunk_type == SCTP_SHUTDOWN_ACK) { - sctp_send_shutdown_complete2(m, iphlen, sh, vrf_id, port); - goto bad; - } - if (ch->chunk_type == SCTP_SHUTDOWN_COMPLETE) { - goto bad; - } - if (ch->chunk_type != SCTP_ABORT_ASSOCIATION) - sctp_send_abort(m, iphlen, sh, 0, NULL, vrf_id, port); - goto bad; - } else if (stcb == NULL) { - refcount_up = 1; - } -#ifdef IPSEC - /* - * Check AH/ESP integrity. - */ - in6p_ip = (struct inpcb *)in6p; - if (in6p_ip && (ipsec6_in_reject(m, in6p_ip))) { -/* XXX */ - MODULE_GLOBAL(ipsec6stat).in_polvio++; - goto bad; - } -#endif /* IPSEC */ - - /* - * CONTROL chunk processing - */ - offset -= sizeof(*ch); - ecn_bits = ((ntohl(ip6->ip6_flow) >> 20) & 0x000000ff); - - /* Length now holds the total packet length payload + iphlen */ - length = ntohs(ip6->ip6_plen) + iphlen; - - /* sa_ignore NO_NULL_CHK */ - sctp_common_input_processing(&m, iphlen, offset, length, sh, ch, - in6p, stcb, net, ecn_bits, vrf_id, port); - /* inp's ref-count reduced && stcb unlocked */ - /* XXX this stuff below gets moved to appropriate parts later... */ - if (m) + sctp_common_input_processing(&m, iphlen, offset, length, + (struct sockaddr *)&src, + (struct sockaddr *)&dst, + sh, ch, +#if !defined(SCTP_WITH_NO_CSUM) + compute_crc, +#endif + ecn_bits, + use_mflowid, mflowid, + vrf_id, port); +out: + if (m) { sctp_m_freem(m); - if ((in6p) && refcount_up) { - /* reduce ref-count */ - SCTP_INP_WLOCK(in6p); - SCTP_INP_DECR_REF(in6p); - SCTP_INP_WUNLOCK(in6p); - } - return IPPROTO_DONE; - -bad: - if (stcb) { - SCTP_TCB_UNLOCK(stcb); } - if ((in6p) && refcount_up) { - /* reduce ref-count */ - SCTP_INP_WLOCK(in6p); - SCTP_INP_DECR_REF(in6p); - SCTP_INP_WUNLOCK(in6p); - } - if (m) - sctp_m_freem(m); - return IPPROTO_DONE; + return (IPPROTO_DONE); } +int +sctp6_input(struct mbuf **i_pak, int *offp, int proto SCTP_UNUSED) +{ + return (sctp6_input_with_port(i_pak, offp, 0)); +} + static void sctp6_notify_mbuf(struct sctp_inpcb *inp, struct icmp6_hdr *icmp6, struct sctphdr *sh, struct sctp_tcb *stcb, struct sctp_nets *net) @@ -367,14 +287,12 @@ sctp6_notify(struct sctp_inpcb *inp, struct sctp_tcb *stcb, struct sctp_nets *net) { -#if defined (__APPLE__) || defined(SCTP_SO_LOCK_TESTING) +#if defined(__APPLE__) || defined(SCTP_SO_LOCK_TESTING) struct socket *so; #endif - /* protection */ - int reason; - + /* protection */ if ((inp == NULL) || (stcb == NULL) || (net == NULL) || (sh == NULL) || (to == NULL)) { if (stcb) @@ -408,36 +326,10 @@ sctp6_notify(struct sctp_inpcb *inp, */ if (net->dest_state & SCTP_ADDR_REACHABLE) { /* Ok that destination is NOT reachable */ - SCTP_PRINTF("ICMP (thresh %d/%d) takes interface %p down\n", - net->error_count, - net->failure_threshold, - net); - net->dest_state &= ~SCTP_ADDR_REACHABLE; - net->dest_state |= SCTP_ADDR_NOT_REACHABLE; - /* - * JRS 5/14/07 - If a destination is unreachable, - * the PF bit is turned off. This allows an - * unambiguous use of the PF bit for destinations - * that are reachable but potentially failed. If the - * destination is set to the unreachable state, also - * set the destination to the PF state. - */ - /* - * Add debug message here if destination is not in - * PF state. - */ - /* Stop any running T3 timers here? */ - if ((stcb->asoc.sctp_cmt_on_off == 1) && - (stcb->asoc.sctp_cmt_pf > 0)) { - net->dest_state &= ~SCTP_ADDR_PF; - SCTPDBG(SCTP_DEBUG_TIMER4, "Destination %p moved from PF to unreachable.\n", - net); - } - net->error_count = net->failure_threshold + 1; + net->dest_state &= ~SCTP_ADDR_PF; sctp_ulp_notify(SCTP_NOTIFY_INTERFACE_DOWN, - stcb, SCTP_FAILED_THRESHOLD, - (void *)net, SCTP_SO_NOT_LOCKED); + stcb, 0, (void *)net, SCTP_SO_NOT_LOCKED); } SCTP_TCB_UNLOCK(stcb); } else if ((icmph->icmp6_code == ICMP_UNREACH_PROTOCOL) || @@ -449,9 +341,8 @@ sctp6_notify(struct sctp_inpcb *inp, * now is dead. In either case treat it like a OOTB abort * with no TCB */ - reason = SCTP_PEER_FAULTY; - sctp_abort_notification(stcb, reason, SCTP_SO_NOT_LOCKED); -#if defined (__APPLE__) || defined(SCTP_SO_LOCK_TESTING) + sctp_abort_notification(stcb, 1, 0, NULL, SCTP_SO_NOT_LOCKED); +#if defined(__APPLE__) || defined(SCTP_SO_LOCK_TESTING) so = SCTP_INP_SO(inp); atomic_add_int(&stcb->asoc.refcnt, 1); SCTP_TCB_UNLOCK(stcb); @@ -460,7 +351,7 @@ sctp6_notify(struct sctp_inpcb *inp, atomic_subtract_int(&stcb->asoc.refcnt, 1); #endif (void)sctp_free_assoc(inp, stcb, SCTP_NORMAL_PROC, SCTP_FROM_SCTP_USRREQ + SCTP_LOC_2); -#if defined (__APPLE__) || defined(SCTP_SO_LOCK_TESTING) +#if defined(__APPLE__) || defined(SCTP_SO_LOCK_TESTING) SCTP_SOCKET_UNLOCK(so, 1); /* SCTP_TCB_UNLOCK(stcb); MT: I think this is not needed. */ #endif @@ -524,8 +415,8 @@ sctp6_ctlinput(int cmd, struct sockaddr *pktdst, void *d) final.sin6_family = AF_INET6; final.sin6_addr = ((struct sockaddr_in6 *)pktdst)->sin6_addr; final.sin6_port = sh.dest_port; - stcb = sctp_findassociation_addr_sa((struct sockaddr *)ip6cp->ip6c_src, - (struct sockaddr *)&final, + stcb = sctp_findassociation_addr_sa((struct sockaddr *)&final, + (struct sockaddr *)ip6cp->ip6c_src, &inp, &net, 1, vrf_id); /* inp's ref-count increased && stcb locked */ if (stcb != NULL && inp && (inp->sctp_socket != NULL)) { @@ -593,8 +484,8 @@ sctp6_getcred(SYSCTL_HANDLER_ARGS) if (error) return (error); - stcb = sctp_findassociation_addr_sa(sin6tosa(&addrs[0]), - sin6tosa(&addrs[1]), + stcb = sctp_findassociation_addr_sa(sin6tosa(&addrs[1]), + sin6tosa(&addrs[0]), &inp, &net, 1, vrf_id); if (stcb == NULL || inp == NULL || inp->sctp_socket == NULL) { if ((inp != NULL) && (stcb == NULL)) { @@ -642,7 +533,7 @@ sctp6_abort(struct socket *so) uint32_t flags; inp = (struct sctp_inpcb *)so->so_pcb; - if (inp == 0) { + if (inp == NULL) { SCTP_LTRACE_ERR_RET(inp, NULL, NULL, SCTP_FROM_SCTP6_USRREQ, EINVAL); return; } @@ -678,7 +569,7 @@ sctp_must_try_again: } static int -sctp6_attach(struct socket *so, int proto, struct thread *p) +sctp6_attach(struct socket *so, int proto SCTP_UNUSED, struct thread *p SCTP_UNUSED) { struct in6pcb *inp6; int error; @@ -688,16 +579,16 @@ sctp6_attach(struct socket *so, int proto, struct thread *p) inp = (struct sctp_inpcb *)so->so_pcb; if (inp != NULL) { SCTP_LTRACE_ERR_RET(inp, NULL, NULL, SCTP_FROM_SCTP6_USRREQ, EINVAL); - return EINVAL; + return (EINVAL); } if (so->so_snd.sb_hiwat == 0 || so->so_rcv.sb_hiwat == 0) { error = SCTP_SORESERVE(so, SCTP_BASE_SYSCTL(sctp_sendspace), SCTP_BASE_SYSCTL(sctp_recvspace)); if (error) - return error; + return (error); } error = sctp_inpcb_alloc(so, vrf_id); if (error) - return error; + return (error); inp = (struct sctp_inpcb *)so->so_pcb; SCTP_INP_WLOCK(inp); inp->sctp_flags |= SCTP_PCB_FLAGS_BOUND_V6; /* I'm v6! */ @@ -719,7 +610,7 @@ sctp6_attach(struct socket *so, int proto, struct thread *p) * sctp_attach()? */ SCTP_INP_WUNLOCK(inp); - return 0; + return (0); } static int @@ -730,68 +621,94 @@ sctp6_bind(struct socket *so, struct sockaddr *addr, struct thread *p) int error; inp = (struct sctp_inpcb *)so->so_pcb; - if (inp == 0) { + if (inp == NULL) { SCTP_LTRACE_ERR_RET(inp, NULL, NULL, SCTP_FROM_SCTP6_USRREQ, EINVAL); - return EINVAL; + return (EINVAL); } if (addr) { - if ((addr->sa_family == AF_INET6) && - (addr->sa_len != sizeof(struct sockaddr_in6))) { - SCTP_LTRACE_ERR_RET(inp, NULL, NULL, SCTP_FROM_SCTP6_USRREQ, EINVAL); - return EINVAL; - } - if ((addr->sa_family == AF_INET) && - (addr->sa_len != sizeof(struct sockaddr_in))) { + switch (addr->sa_family) { +#ifdef INET + case AF_INET: + if (addr->sa_len != sizeof(struct sockaddr_in)) { + SCTP_LTRACE_ERR_RET(inp, NULL, NULL, SCTP_FROM_SCTP6_USRREQ, EINVAL); + return (EINVAL); + } + break; +#endif +#ifdef INET6 + case AF_INET6: + if (addr->sa_len != sizeof(struct sockaddr_in6)) { + SCTP_LTRACE_ERR_RET(inp, NULL, NULL, SCTP_FROM_SCTP6_USRREQ, EINVAL); + return (EINVAL); + } + break; +#endif + default: SCTP_LTRACE_ERR_RET(inp, NULL, NULL, SCTP_FROM_SCTP6_USRREQ, EINVAL); - return EINVAL; + return (EINVAL); } } inp6 = (struct in6pcb *)inp; inp6->inp_vflag &= ~INP_IPV4; inp6->inp_vflag |= INP_IPV6; if ((addr != NULL) && (SCTP_IPV6_V6ONLY(inp6) == 0)) { - if (addr->sa_family == AF_INET) { + switch (addr->sa_family) { +#ifdef INET + case AF_INET: /* binding v4 addr to v6 socket, so reset flags */ inp6->inp_vflag |= INP_IPV4; inp6->inp_vflag &= ~INP_IPV6; - } else { - struct sockaddr_in6 *sin6_p; - - sin6_p = (struct sockaddr_in6 *)addr; + break; +#endif +#ifdef INET6 + case AF_INET6: + { + struct sockaddr_in6 *sin6_p; - if (IN6_IS_ADDR_UNSPECIFIED(&sin6_p->sin6_addr)) { - inp6->inp_vflag |= INP_IPV4; - } else if (IN6_IS_ADDR_V4MAPPED(&sin6_p->sin6_addr)) { - struct sockaddr_in sin; + sin6_p = (struct sockaddr_in6 *)addr; - in6_sin6_2_sin(&sin, sin6_p); - inp6->inp_vflag |= INP_IPV4; - inp6->inp_vflag &= ~INP_IPV6; - error = sctp_inpcb_bind(so, (struct sockaddr *)&sin, NULL, p); - return error; + if (IN6_IS_ADDR_UNSPECIFIED(&sin6_p->sin6_addr)) { + inp6->inp_vflag |= INP_IPV4; + } +#ifdef INET + if (IN6_IS_ADDR_V4MAPPED(&sin6_p->sin6_addr)) { + struct sockaddr_in sin; + + in6_sin6_2_sin(&sin, sin6_p); + inp6->inp_vflag |= INP_IPV4; + inp6->inp_vflag &= ~INP_IPV6; + error = sctp_inpcb_bind(so, (struct sockaddr *)&sin, NULL, p); + return (error); + } +#endif + break; } +#endif + default: + break; } } else if (addr != NULL) { + struct sockaddr_in6 *sin6_p; + /* IPV6_V6ONLY socket */ +#ifdef INET if (addr->sa_family == AF_INET) { /* can't bind v4 addr to v6 only socket! */ SCTP_LTRACE_ERR_RET(inp, NULL, NULL, SCTP_FROM_SCTP6_USRREQ, EINVAL); - return EINVAL; - } else { - struct sockaddr_in6 *sin6_p; - - sin6_p = (struct sockaddr_in6 *)addr; + return (EINVAL); + } +#endif + sin6_p = (struct sockaddr_in6 *)addr; - if (IN6_IS_ADDR_V4MAPPED(&sin6_p->sin6_addr)) { - /* can't bind v4-mapped addrs either! */ - /* NOTE: we don't support SIIT */ - SCTP_LTRACE_ERR_RET(inp, NULL, NULL, SCTP_FROM_SCTP6_USRREQ, EINVAL); - return EINVAL; - } + if (IN6_IS_ADDR_V4MAPPED(&sin6_p->sin6_addr)) { + /* can't bind v4-mapped addrs either! */ + /* NOTE: we don't support SIIT */ + SCTP_LTRACE_ERR_RET(inp, NULL, NULL, SCTP_FROM_SCTP6_USRREQ, EINVAL); + return (EINVAL); } } error = sctp_inpcb_bind(so, addr, NULL, p); - return error; + return (error); } @@ -837,7 +754,7 @@ sctp6_send(struct socket *so, int flags, struct mbuf *m, struct sockaddr *addr, } SCTP_RELEASE_PKT(m); SCTP_LTRACE_ERR_RET(inp, NULL, NULL, SCTP_FROM_SCTP6_USRREQ, EINVAL); - return EINVAL; + return (EINVAL); } inp6 = (struct in6pcb *)inp; /* @@ -866,11 +783,11 @@ sctp6_send(struct socket *so, int flags, struct mbuf *m, struct sockaddr *addr, */ if (addr->sa_family == AF_INET) { SCTP_LTRACE_ERR_RET(inp, NULL, NULL, SCTP_FROM_SCTP6_USRREQ, EINVAL); - return EINVAL; + return (EINVAL); } if (IN6_IS_ADDR_V4MAPPED(&sin6->sin6_addr)) { SCTP_LTRACE_ERR_RET(inp, NULL, NULL, SCTP_FROM_SCTP6_USRREQ, EINVAL); - return EINVAL; + return (EINVAL); } } if (IN6_IS_ADDR_V4MAPPED(&sin6->sin6_addr)) { @@ -879,12 +796,12 @@ sctp6_send(struct socket *so, int flags, struct mbuf *m, struct sockaddr *addr, /* convert v4-mapped into v4 addr and send */ in6_sin6_2_sin(&sin, sin6); - return sctp_sendm(so, flags, m, (struct sockaddr *)&sin, - control, p); + return (sctp_sendm(so, flags, m, (struct sockaddr *)&sin, + control, p)); } else { /* mapped addresses aren't enabled */ SCTP_LTRACE_ERR_RET(inp, NULL, NULL, SCTP_FROM_SCTP6_USRREQ, EINVAL); - return EINVAL; + return (EINVAL); } } #endif /* INET */ @@ -940,11 +857,11 @@ sctp6_connect(struct socket *so, struct sockaddr *addr, struct thread *p) struct sockaddr_in6 *sin6; struct sockaddr_storage ss; -#endif /* INET */ +#endif inp6 = (struct in6pcb *)so->so_pcb; inp = (struct sctp_inpcb *)so->so_pcb; - if (inp == 0) { + if (inp == NULL) { SCTP_LTRACE_ERR_RET(inp, NULL, NULL, SCTP_FROM_SCTP6_USRREQ, ECONNRESET); return (ECONNRESET); /* I made the same as TCP since we are * not setup? */ @@ -953,14 +870,28 @@ sctp6_connect(struct socket *so, struct sockaddr *addr, struct thread *p) SCTP_LTRACE_ERR_RET(inp, NULL, NULL, SCTP_FROM_SCTP6_USRREQ, EINVAL); return (EINVAL); } - if ((addr->sa_family == AF_INET6) && (addr->sa_len != sizeof(struct sockaddr_in6))) { - SCTP_LTRACE_ERR_RET(inp, NULL, NULL, SCTP_FROM_SCTP6_USRREQ, EINVAL); - return (EINVAL); - } - if ((addr->sa_family == AF_INET) && (addr->sa_len != sizeof(struct sockaddr_in))) { + switch (addr->sa_family) { +#ifdef INET + case AF_INET: + if (addr->sa_len != sizeof(struct sockaddr_in)) { + SCTP_LTRACE_ERR_RET(inp, NULL, NULL, SCTP_FROM_SCTP6_USRREQ, EINVAL); + return (EINVAL); + } + break; +#endif +#ifdef INET6 + case AF_INET6: + if (addr->sa_len != sizeof(struct sockaddr_in6)) { + SCTP_LTRACE_ERR_RET(inp, NULL, NULL, SCTP_FROM_SCTP6_USRREQ, EINVAL); + return (EINVAL); + } + break; +#endif + default: SCTP_LTRACE_ERR_RET(inp, NULL, NULL, SCTP_FROM_SCTP6_USRREQ, EINVAL); return (EINVAL); } + vrf_id = inp->def_vrf_id; SCTP_ASOC_CREATE_LOCK(inp); SCTP_INP_RLOCK(inp); @@ -995,13 +926,13 @@ sctp6_connect(struct socket *so, struct sockaddr *addr, struct thread *p) SCTP_INP_RUNLOCK(inp); SCTP_ASOC_CREATE_UNLOCK(inp); SCTP_LTRACE_ERR_RET(inp, NULL, NULL, SCTP_FROM_SCTP6_USRREQ, EINVAL); - return EINVAL; + return (EINVAL); } if (IN6_IS_ADDR_V4MAPPED(&sin6->sin6_addr)) { SCTP_INP_RUNLOCK(inp); SCTP_ASOC_CREATE_UNLOCK(inp); SCTP_LTRACE_ERR_RET(inp, NULL, NULL, SCTP_FROM_SCTP6_USRREQ, EINVAL); - return EINVAL; + return (EINVAL); } } if (IN6_IS_ADDR_V4MAPPED(&sin6->sin6_addr)) { @@ -1014,12 +945,10 @@ sctp6_connect(struct socket *so, struct sockaddr *addr, struct thread *p) SCTP_INP_RUNLOCK(inp); SCTP_ASOC_CREATE_UNLOCK(inp); SCTP_LTRACE_ERR_RET(inp, NULL, NULL, SCTP_FROM_SCTP6_USRREQ, EINVAL); - return EINVAL; + return (EINVAL); } - } else + } #endif /* INET */ - addr = addr; /* for true v6 address case */ - /* Now do we connect? */ if (inp->sctp_flags & SCTP_PCB_FLAGS_CONNECTED) { stcb = LIST_FIRST(&inp->sctp_asoc_list); @@ -1067,7 +996,7 @@ sctp6_connect(struct socket *so, struct sockaddr *addr, struct thread *p) sctp_send_initiate(inp, stcb, SCTP_SO_LOCKED); SCTP_TCB_UNLOCK(stcb); - return error; + return (error); } static int @@ -1083,9 +1012,9 @@ sctp6_getaddr(struct socket *so, struct sockaddr **addr) /* * Do the malloc first in case it blocks. */ - SCTP_MALLOC_SONAME(sin6, struct sockaddr_in6 *, sizeof *sin6); + SCTP_MALLOC_SONAME(sin6, struct sockaddr_in6 *, sizeof(*sin6)); if (sin6 == NULL) - return ENOMEM; + return (ENOMEM); sin6->sin6_family = AF_INET6; sin6->sin6_len = sizeof(*sin6); @@ -1093,7 +1022,7 @@ sctp6_getaddr(struct socket *so, struct sockaddr **addr) if (inp == NULL) { SCTP_FREE_SONAME(sin6); SCTP_LTRACE_ERR_RET(inp, NULL, NULL, SCTP_FROM_SCTP6_USRREQ, ECONNRESET); - return ECONNRESET; + return (ECONNRESET); } SCTP_INP_RLOCK(inp); sin6->sin6_port = inp->sctp_lport; @@ -1155,7 +1084,7 @@ sctp6_getaddr(struct socket *so, struct sockaddr **addr) SCTP_FREE_SONAME(sin6); SCTP_INP_RUNLOCK(inp); SCTP_LTRACE_ERR_RET(inp, NULL, NULL, SCTP_FROM_SCTP6_USRREQ, ENOENT); - return ENOENT; + return (ENOENT); } } SCTP_INP_RUNLOCK(inp); @@ -1171,36 +1100,28 @@ sctp6_getaddr(struct socket *so, struct sockaddr **addr) static int sctp6_peeraddr(struct socket *so, struct sockaddr **addr) { - struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)*addr; + struct sockaddr_in6 *sin6; int fnd; struct sockaddr_in6 *sin_a6; struct sctp_inpcb *inp; struct sctp_tcb *stcb; struct sctp_nets *net; - int error; - /* - * Do the malloc first in case it blocks. - */ - inp = (struct sctp_inpcb *)so->so_pcb; - if ((inp->sctp_flags & SCTP_PCB_FLAGS_CONNECTED) == 0) { - /* UDP type and listeners will drop out here */ - SCTP_LTRACE_ERR_RET(inp, NULL, NULL, SCTP_FROM_SCTP6_USRREQ, ENOTCONN); - return (ENOTCONN); - } + /* Do the malloc first in case it blocks. */ SCTP_MALLOC_SONAME(sin6, struct sockaddr_in6 *, sizeof *sin6); if (sin6 == NULL) return (ENOMEM); sin6->sin6_family = AF_INET6; sin6->sin6_len = sizeof(*sin6); - /* We must recapture incase we blocked */ inp = (struct sctp_inpcb *)so->so_pcb; - if (inp == NULL) { + if ((inp == NULL) || + ((inp->sctp_flags & SCTP_PCB_FLAGS_CONNECTED) == 0)) { + /* UDP type and listeners will drop out here */ SCTP_FREE_SONAME(sin6); - SCTP_LTRACE_ERR_RET(inp, NULL, NULL, SCTP_FROM_SCTP6_USRREQ, ECONNRESET); - return ECONNRESET; + SCTP_LTRACE_ERR_RET(inp, NULL, NULL, SCTP_FROM_SCTP6_USRREQ, ENOTCONN); + return (ENOTCONN); } SCTP_INP_RLOCK(inp); stcb = LIST_FIRST(&inp->sctp_asoc_list); @@ -1211,7 +1132,7 @@ sctp6_peeraddr(struct socket *so, struct sockaddr **addr) if (stcb == NULL) { SCTP_FREE_SONAME(sin6); SCTP_LTRACE_ERR_RET(inp, NULL, NULL, SCTP_FROM_SCTP6_USRREQ, ECONNRESET); - return ECONNRESET; + return (ECONNRESET); } fnd = 0; TAILQ_FOREACH(net, &stcb->asoc.nets, sctp_next) { @@ -1228,7 +1149,7 @@ sctp6_peeraddr(struct socket *so, struct sockaddr **addr) /* No IPv4 address */ SCTP_FREE_SONAME(sin6); SCTP_LTRACE_ERR_RET(inp, NULL, NULL, SCTP_FROM_SCTP6_USRREQ, ENOENT); - return ENOENT; + return (ENOENT); } if ((error = sa6_recoverscope(sin6)) != 0) return (error); @@ -1239,16 +1160,20 @@ sctp6_peeraddr(struct socket *so, struct sockaddr **addr) static int sctp6_in6getaddr(struct socket *so, struct sockaddr **nam) { +#ifdef INET struct sockaddr *addr; + +#endif struct in6pcb *inp6 = sotoin6pcb(so); int error; if (inp6 == NULL) { SCTP_LTRACE_ERR_RET(NULL, NULL, NULL, SCTP_FROM_SCTP6_USRREQ, EINVAL); - return EINVAL; + return (EINVAL); } /* allow v6 addresses precedence */ error = sctp6_getaddr(so, nam); +#ifdef INET if (error) { /* try v4 next if v6 failed */ error = sctp_ingetaddr(so, nam); @@ -1262,9 +1187,9 @@ sctp6_in6getaddr(struct socket *so, struct sockaddr **nam) in6_sin_2_v4mapsin6((struct sockaddr_in *)addr, &sin6); memcpy(addr, &sin6, sizeof(struct sockaddr_in6)); - } } +#endif return (error); } @@ -1272,22 +1197,27 @@ sctp6_in6getaddr(struct socket *so, struct sockaddr **nam) static int sctp6_getpeeraddr(struct socket *so, struct sockaddr **nam) { - struct sockaddr *addr = *nam; +#ifdef INET + struct sockaddr *addr; + +#endif struct in6pcb *inp6 = sotoin6pcb(so); int error; if (inp6 == NULL) { SCTP_LTRACE_ERR_RET(NULL, NULL, NULL, SCTP_FROM_SCTP6_USRREQ, EINVAL); - return EINVAL; + return (EINVAL); } /* allow v6 addresses precedence */ error = sctp6_peeraddr(so, nam); +#ifdef INET if (error) { /* try v4 next if v6 failed */ error = sctp_peeraddr(so, nam); if (error) { return (error); } + addr = *nam; /* if I'm V6ONLY, convert it to v4-mapped */ if (SCTP_IPV6_V6ONLY(inp6)) { struct sockaddr_in6 sin6; @@ -1296,7 +1226,8 @@ sctp6_getpeeraddr(struct socket *so, struct sockaddr **nam) memcpy(addr, &sin6, sizeof(struct sockaddr_in6)); } } - return error; +#endif + return (error); } struct pr_usrreqs sctp6_usrreqs = { @@ -1319,3 +1250,5 @@ struct pr_usrreqs sctp6_usrreqs = { .pru_sosend = sctp_sosend, .pru_soreceive = sctp_soreceive }; + +#endif diff --git a/freebsd/sys/netinet6/sctp6_var.h b/freebsd/sys/netinet6/sctp6_var.h index a05f6b03..53d1d4c5 100644 --- a/freebsd/sys/netinet6/sctp6_var.h +++ b/freebsd/sys/netinet6/sctp6_var.h @@ -1,15 +1,17 @@ /*- * Copyright (c) 2001-2007, by Cisco Systems, Inc. All rights reserved. + * Copyright (c) 2008-2012, by Randall Stewart. All rights reserved. + * Copyright (c) 2008-2012, by Michael Tuexen. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * a) Redistributions of source code must retain the above copyright notice, - * this list of conditions and the following disclaimer. + * this list of conditions and the following disclaimer. * * b) Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in - * the documentation and/or other materials provided with the distribution. + * the documentation and/or other materials provided with the distribution. * * c) Neither the name of Cisco Systems, Inc. nor the names of its * contributors may be used to endorse or promote products derived @@ -27,15 +29,13 @@ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF * THE POSSIBILITY OF SUCH DAMAGE. */ -/* $KAME: sctp6_var.h,v 1.7 2004/08/17 04:06:22 itojun Exp $ */ - -#ifndef _NETINET6_SCTP6_VAR_H_ -#define _NETINET6_SCTP6_VAR_H_ #include <sys/cdefs.h> __FBSDID("$FreeBSD$"); -/* TODO __Userspace__ IPv6 stuff... */ +#ifndef _NETINET6_SCTP6_VAR_H_ +#define _NETINET6_SCTP6_VAR_H_ + #if defined(_KERNEL) SYSCTL_DECL(_net_inet6_sctp6); @@ -43,6 +43,7 @@ extern struct pr_usrreqs sctp6_usrreqs; int sctp6_input __P((struct mbuf **, int *, int)); +int sctp6_input_with_port __P((struct mbuf **, int *, uint16_t)); int sctp6_output __P((struct sctp_inpcb *, struct mbuf *, struct sockaddr *, struct mbuf *, struct proc *)); diff --git a/freebsd/sys/netinet6/udp6_usrreq.c b/freebsd/sys/netinet6/udp6_usrreq.c index 941b3950..2c2559fb 100644 --- a/freebsd/sys/netinet6/udp6_usrreq.c +++ b/freebsd/sys/netinet6/udp6_usrreq.c @@ -275,6 +275,13 @@ udp6_input(struct mbuf **mp, int *offp, int proto) } /* + * Detached PCBs can linger in the list if someone + * holds a reference. (e.g. udp_pcblist) + */ + if (inp->inp_socket == NULL) + continue; + + /* * Handle socket delivery policy for any-source * and source-specific multicast. [RFC3678] */ @@ -398,6 +405,15 @@ udp6_input(struct mbuf **mp, int *offp, int proto) } INP_RLOCK(inp); INP_INFO_RUNLOCK(&V_udbinfo); + + /* + * Detached PCBs can linger in the hash table if someone holds a + * reference. (e.g. udp_pcblist) + */ + if (inp->inp_socket == NULL) { + INP_RUNLOCK(inp); + goto badunlocked; + } up = intoudpcb(inp); if (up->u_tun_func == NULL) { udp6_append(inp, m, off, &fromsa); @@ -658,8 +674,11 @@ udp6_output(struct inpcb *inp, struct mbuf *m, struct sockaddr *addr6, goto release; } if (inp->inp_lport == 0 && - (error = in6_pcbsetport(laddr, inp, td->td_ucred)) != 0) + (error = in6_pcbsetport(laddr, inp, td->td_ucred)) != 0) { + /* Undo an address bind that may have occurred. */ + inp->in6p_laddr = in6addr_any; goto release; + } } else { if (IN6_IS_ADDR_UNSPECIFIED(&inp->in6p_faddr)) { error = ENOTCONN; @@ -908,43 +927,41 @@ udp6_connect(struct socket *so, struct sockaddr *nam, struct thread *td) INP_INFO_WLOCK(&V_udbinfo); INP_WLOCK(inp); - if ((inp->inp_flags & IN6P_IPV6_V6ONLY) == 0 && - IN6_IS_ADDR_V4MAPPED(&sin6->sin6_addr)) { + if (IN6_IS_ADDR_V4MAPPED(&sin6->sin6_addr)) { struct sockaddr_in sin; + if ((inp->inp_flags & IN6P_IPV6_V6ONLY) != 0) { + error = EINVAL; + goto out; + } if (inp->inp_faddr.s_addr != INADDR_ANY) { error = EISCONN; goto out; } in6_sin6_2_sin(&sin, sin6); + inp->inp_vflag |= INP_IPV4; + inp->inp_vflag &= ~INP_IPV6; error = prison_remote_ip4(td->td_ucred, &sin.sin_addr); if (error != 0) goto out; error = in_pcbconnect(inp, (struct sockaddr *)&sin, td->td_ucred); - if (error == 0) { - inp->inp_vflag |= INP_IPV4; - inp->inp_vflag &= ~INP_IPV6; + if (error == 0) soisconnected(so); - } goto out; } if (!IN6_IS_ADDR_UNSPECIFIED(&inp->in6p_faddr)) { error = EISCONN; goto out; } + inp->inp_vflag &= ~INP_IPV4; + inp->inp_vflag |= INP_IPV6; error = prison_remote_ip6(td->td_ucred, &sin6->sin6_addr); if (error != 0) goto out; error = in6_pcbconnect(inp, nam, td->td_ucred); - if (error == 0) { - if ((inp->inp_flags & IN6P_IPV6_V6ONLY) == 0) { - /* should be non mapped addr */ - inp->inp_vflag &= ~INP_IPV4; - inp->inp_vflag |= INP_IPV6; - } + if (error == 0) soisconnected(so); - } out: INP_WUNLOCK(inp); INP_INFO_WUNLOCK(&V_udbinfo); @@ -1046,18 +1063,6 @@ udp6_send(struct socket *so, int flags, struct mbuf *m, if (hasv4addr) { struct pr_usrreqs *pru; - if (!IN6_IS_ADDR_UNSPECIFIED(&inp->in6p_laddr) && - !IN6_IS_ADDR_V4MAPPED(&inp->in6p_laddr)) { - /* - * When remote addr is IPv4-mapped address, - * local addr should not be an IPv6 address; - * since you cannot determine how to map IPv6 - * source address to IPv4. - */ - error = EINVAL; - goto out; - } - /* * XXXRW: We release UDP-layer locks before calling * udp_send() in order to avoid recursion. However, @@ -1081,7 +1086,6 @@ udp6_send(struct socket *so, int flags, struct mbuf *m, mac_inpcb_create_mbuf(inp, m); #endif error = udp6_output(inp, m, addr, control, td); -out: INP_WUNLOCK(inp); INP_INFO_WUNLOCK(&V_udbinfo); return (error); |