Index: sys/netinet6/in6.c =================================================================== --- sys/netinet6/in6.c +++ sys/netinet6/in6.c @@ -236,6 +236,112 @@ return x * 8 + y; } +int +in6_prefix_rtrequest(int cmd, struct in6_ifaddr *ia) +{ + struct ifnet *ifp = ia->ia_ifp; + uint32_t fibnum = ifp->if_fib; + struct epoch_tracker et; + int error; + + struct sockaddr_in6 dst6 = { + .sin6_family = AF_INET6, + .sin6_len = sizeof(struct sockaddr_in6), + .sin6_addr = ia->ia_addr.sin6_addr, + }; + + struct sockaddr_in6 netmask6 = { + .sin6_family = AF_INET6, + .sin6_len = sizeof(struct sockaddr_in6), + .sin6_addr = ia->ia_prefixmask.sin6_addr, + }; + + struct sockaddr_dl_short sdl = { + .sdl_family = AF_LINK, + .sdl_len = sizeof(struct sockaddr_dl_short), + .sdl_type = ifp->if_type, + .sdl_index = ifp->if_index, + }; + + struct rt_addrinfo info = { + .rti_ifa = &ia->ia_ifa, + .rti_ifp = ifp, + .rti_flags = RTF_PINNED, + .rti_info = { + [RTAX_DST] = (struct sockaddr *)&dst6, + [RTAX_GATEWAY] = (struct sockaddr *)&sdl, + }, + }; + + if (ia->ia_dstaddr.sin6_len != 0) + dst6.sin6_addr = ia->ia_dstaddr.sin6_addr; + else + dst6.sin6_addr = ia->ia_addr.sin6_addr; + IN6_MASK_ADDR(&dst6.sin6_addr, &netmask6.sin6_addr); + + if (in6_mask2len(&netmask6.sin6_addr, NULL) == 128) { + if (ia->ia_dstaddr.sin6_family != AF_INET6) + /* We don't need to install a host route */ + return 0; + info.rti_flags |= RTF_HOST; + } else + info.rti_info[RTAX_NETMASK] = (struct sockaddr *)&netmask6; + + NET_EPOCH_ENTER(et); + error = rib_handle_ifaddr_info(fibnum, cmd, &info); + NET_EPOCH_EXIT(et); + + return error; +} + +/* Delete network prefix route if present. + * Re-add it to another address if the prefix matches. */ +static int +in6_ifremprefix(struct in6_ifaddr *target) +{ + struct rm_priotracker in6_ifa_tracker; + struct in6_ifaddr *ia; + int error = 0; + + if ((target->ia_flags & IFA_ROUTE) == 0) + return 0; + + IN6_IFADDR_RLOCK(&in6_ifa_tracker); + CK_STAILQ_FOREACH(ia, &V_in6_ifaddrhead, ia_link) { + if (target->ia_dstaddr.sin6_len) { + if (ia->ia_dstaddr.sin6_len == 0 || + !IN6_ARE_ADDR_EQUAL(&ia->ia_dstaddr.sin6_addr, + &target->ia_dstaddr.sin6_addr)) + continue; + } else { + if (!IN6_ARE_MASKED_ADDR_EQUAL(&ia->ia_addr.sin6_addr, + &target->ia_addr.sin6_addr, + &target->ia_prefixmask.sin6_addr)) + continue; + } + + /* If we got a matching prefix route, move IFA_ROUTE to it. */ + if ((ia->ia_flags & IFA_ROUTE) == 0) { + in6_prefix_rtrequest(RTM_DELETE, target); + target->ia_flags &= ~IFA_ROUTE; + nd6_rem_ifa_lle(target, 1); + + error = in6_prefix_rtrequest(RTM_ADD, ia); + if (error == 0) + ia->ia_flags |= IFA_ROUTE; + return error; + } + } + IN6_IFADDR_RUNLOCK(&in6_ifa_tracker); + + /* No-one has the prefix, so remove it. */ + in6_prefix_rtrequest(RTM_DELETE, target); + target->ia_flags &= ~IFA_ROUTE; + nd6_rem_ifa_lle(target, 1); + + return error; +} + #ifdef COMPAT_FREEBSD32 struct in6_ndifreq32 { char ifname[IFNAMSIZ]; @@ -252,7 +358,6 @@ struct in6_ifaddr *ia = NULL; struct in6_aliasreq *ifra = (struct in6_aliasreq *)data; struct sockaddr_in6 *sa6; - int carp_attached = 0; int error; u_long ocmd = cmd; @@ -560,10 +665,6 @@ break; case SIOCAIFADDR_IN6: - { - struct nd_prefixctl pr0; - struct nd_prefix *pr; - /* * first, make or update the interface address structure, * and link it to the list. @@ -592,89 +693,8 @@ error = EPROTONOSUPPORT; if (error) goto out; - else - carp_attached = 1; - } - - /* - * then, make the prefix on-link on the interface. - * XXX: we'd rather create the prefix before the address, but - * we need at least one address to install the corresponding - * interface route, so we configure the address first. - */ - - /* - * convert mask to prefix length (prefixmask has already - * been validated in in6_update_ifa(). - */ - bzero(&pr0, sizeof(pr0)); - pr0.ndpr_ifp = ifp; - pr0.ndpr_plen = in6_mask2len(&ifra->ifra_prefixmask.sin6_addr, - NULL); - if (pr0.ndpr_plen == 128) { - /* we don't need to install a host route. */ - goto aifaddr_out; } - pr0.ndpr_prefix = ifra->ifra_addr; - /* apply the mask for safety. */ - IN6_MASK_ADDR(&pr0.ndpr_prefix.sin6_addr, - &ifra->ifra_prefixmask.sin6_addr); - /* - * XXX: since we don't have an API to set prefix (not address) - * lifetimes, we just use the same lifetimes as addresses. - * The (temporarily) installed lifetimes can be overridden by - * later advertised RAs (when accept_rtadv is non 0), which is - * an intended behavior. - */ - pr0.ndpr_raf_onlink = 1; /* should be configurable? */ - pr0.ndpr_raf_auto = - ((ifra->ifra_flags & IN6_IFF_AUTOCONF) != 0); - pr0.ndpr_vltime = ifra->ifra_lifetime.ia6t_vltime; - pr0.ndpr_pltime = ifra->ifra_lifetime.ia6t_pltime; - - /* add the prefix if not yet. */ - if ((pr = nd6_prefix_lookup(&pr0)) == NULL) { - /* - * nd6_prelist_add will install the corresponding - * interface route. - */ - if ((error = nd6_prelist_add(&pr0, NULL, &pr)) != 0) { - if (carp_attached) - (*carp_detach_p)(&ia->ia_ifa, false); - goto out; - } - } - - /* relate the address to the prefix */ - if (ia->ia6_ndpr == NULL) { - ia->ia6_ndpr = pr; - pr->ndpr_addrcnt++; - - /* - * If this is the first autoconf address from the - * prefix, create a temporary address as well - * (when required). - */ - if ((ia->ia6_flags & IN6_IFF_AUTOCONF) && - V_ip6_use_tempaddr && pr->ndpr_addrcnt == 1) { - int e; - if ((e = in6_tmpifadd(ia, 1, 0)) != 0) { - log(LOG_NOTICE, "in6_control: failed " - "to create a temporary address, " - "errno=%d\n", e); - } - } - } - nd6_prefix_rele(pr); - - /* - * this might affect the status of autoconfigured addresses, - * that is, this address might make other addresses detached. - */ - pfxlist_onlink_check(); - -aifaddr_out: /* * Try to clear the flag when a new IPv6 address is added * onto an IFDISABLED interface and it succeeds. @@ -695,7 +715,6 @@ */ } break; - } case SIOCDIFADDR_IN6: in6_purgeifaddr(ia); @@ -1107,12 +1126,15 @@ } /* set prefix mask if any */ - ia->ia_ifa.ifa_netmask = (struct sockaddr *)&ia->ia_prefixmask; if (ifra->ifra_prefixmask.sin6_len != 0) { + if (!!IN6_ARE_ADDR_EQUAL(&ia->ia_prefixmask.sin6_addr, + &ifra->ifra_prefixmask.sin6_addr)) + in6_ifremprefix(ia); ia->ia_prefixmask.sin6_family = AF_INET6; ia->ia_prefixmask.sin6_len = ifra->ifra_prefixmask.sin6_len; ia->ia_prefixmask.sin6_addr = ifra->ifra_prefixmask.sin6_addr; } + ia->ia_ifa.ifa_netmask = (struct sockaddr *)&ia->ia_prefixmask; ia->ia_ifp = ifp; ifa_ref(&ia->ia_ifa); /* if_addrhead */ @@ -1267,49 +1289,6 @@ return (error); } -/* - * Adds or deletes interface route for p2p ifa. - * Returns 0 on success or errno. - */ -static int -in6_handle_dstaddr_rtrequest(int cmd, struct in6_ifaddr *ia) -{ - struct epoch_tracker et; - struct ifaddr *ifa = &ia->ia_ifa; - int error; - - /* Prepare gateway */ - struct sockaddr_dl_short sdl = { - .sdl_family = AF_LINK, - .sdl_len = sizeof(struct sockaddr_dl_short), - .sdl_type = ifa->ifa_ifp->if_type, - .sdl_index = ifa->ifa_ifp->if_index, - }; - - struct sockaddr_in6 dst = { - .sin6_family = AF_INET6, - .sin6_len = sizeof(struct sockaddr_in6), - .sin6_addr = ia->ia_dstaddr.sin6_addr, - }; - - struct rt_addrinfo info = { - .rti_ifa = ifa, - .rti_ifp = ifa->ifa_ifp, - .rti_flags = RTF_PINNED | RTF_HOST, - .rti_info = { - [RTAX_DST] = (struct sockaddr *)&dst, - [RTAX_GATEWAY] = (struct sockaddr *)&sdl, - }, - }; - /* Don't set additional per-gw filters on removal */ - - NET_EPOCH_ENTER(et); - error = rib_handle_ifaddr_info(ifa->ifa_ifp->if_fib, cmd, &info); - NET_EPOCH_EXIT(et); - - return (error); -} - static bool ifa_is_p2p(struct in6_ifaddr *ia) { @@ -1357,14 +1336,9 @@ in6_leavegroup(imm->i6mm_maddr, NULL); free(imm, M_IP6MADDR); } - /* Check if we need to remove p2p route */ - if ((ia->ia_flags & IFA_ROUTE) && ifa_is_p2p(ia)) { - error = in6_handle_dstaddr_rtrequest(RTM_DELETE, ia); - if (error != 0) - log(LOG_INFO, "%s: err=%d, destination address delete " - "failed\n", __func__, error); - ia->ia_flags &= ~IFA_ROUTE; - } + + /* Remove any prefix route */ + in6_ifremprefix(ia); in6_newaddrmsg(ia, RTM_DELETE); in6_unlink_ifa(ia, ifp); @@ -1403,7 +1377,6 @@ static void in6_unlink_ifa(struct in6_ifaddr *ia, struct ifnet *ifp) { - char ip6buf[INET6_ADDRSTRLEN]; int remove_lle; IF_ADDR_WLOCK(ifp); @@ -1422,15 +1395,10 @@ IN6_IFADDR_WUNLOCK(); /* - * Release the reference to the base prefix. There should be a - * positive reference. + * Release the reference to the base prefix. */ remove_lle = 0; - if (ia->ia6_ndpr == NULL) { - nd6log((LOG_NOTICE, - "in6_unlink_ifa: autoconf'ed address " - "%s has no prefix\n", ip6_sprintf(ip6buf, IA6_IN6(ia)))); - } else { + if (ia->ia6_ndpr != NULL) { ia->ia6_ndpr->ndpr_addrcnt--; /* Do not delete lles within prefix if refcont != 0 */ if (ia->ia6_ndpr->ndpr_addrcnt == 0) @@ -1464,7 +1432,6 @@ int error = 0, ifacount = 0; struct ifaddr *ifa; struct sockaddr_in6 *pdst; - char ip6buf[INET6_ADDRSTRLEN]; /* * Give the interface a chance to initialize @@ -1496,14 +1463,7 @@ pdst = &ifra->ifra_dstaddr; if (pdst->sin6_family == AF_INET6 && !IN6_ARE_ADDR_EQUAL(&pdst->sin6_addr, &ia->ia_dstaddr.sin6_addr)) { - if ((ia->ia_flags & IFA_ROUTE) != 0 && - (in6_handle_dstaddr_rtrequest(RTM_DELETE, ia) != 0)) { - nd6log((LOG_ERR, "in6_update_ifa_internal: failed to " - "remove a route to the old destination: %s\n", - ip6_sprintf(ip6buf, &ia->ia_addr.sin6_addr))); - /* proceed anyway... */ - } else - ia->ia_flags &= ~IFA_ROUTE; + in6_ifremprefix(ia); ia->ia_dstaddr = *pdst; } @@ -1514,11 +1474,29 @@ * XXX: the logic below rejects assigning multiple addresses on a p2p * interface that share the same destination. */ - if (!(ia->ia_flags & IFA_ROUTE) && ifa_is_p2p(ia)) { - error = in6_handle_dstaddr_rtrequest(RTM_ADD, ia); - if (error) + if (!(ia->ia_flags & IFA_ROUTE)) { + error = in6_prefix_rtrequest(RTM_ADD, ia); + if (error == 0) { + ia->ia_flags |= IFA_ROUTE; + + /* + * If this is the first autoconf address from the + * prefix, create a temporary address as well + * (when required). + */ + if ((ia->ia6_flags & IN6_IFF_AUTOCONF) && + V_ip6_use_tempaddr) { + int e; + if ((e = in6_tmpifadd(ia, 1, 0)) != 0) { + log(LOG_NOTICE, "in6_control: failed " + "to create a temporary address, " + "errno=%d\n", e); + } + } + } else if (error == EEXIST) + error = 0; + else goto done; - ia->ia_flags |= IFA_ROUTE; } /* Index: sys/netinet6/in6_ifattach.c =================================================================== --- sys/netinet6/in6_ifattach.c +++ sys/netinet6/in6_ifattach.c @@ -422,9 +422,7 @@ { struct in6_ifaddr *ia; struct in6_aliasreq ifra; - struct nd_prefixctl pr0; struct epoch_tracker et; - struct nd_prefix *pr; int error; /* @@ -490,45 +488,13 @@ } ifa_free(&ia->ia_ifa); - /* - * Make the link-local prefix (fe80::%link/64) as on-link. - * Since we'd like to manage prefixes separately from addresses, - * we make an ND6 prefix structure for the link-local prefix, - * and add it to the prefix list as a never-expire prefix. - * XXX: this change might affect some existing code base... - */ - bzero(&pr0, sizeof(pr0)); - pr0.ndpr_ifp = ifp; - /* this should be 64 at this moment. */ - pr0.ndpr_plen = in6_mask2len(&ifra.ifra_prefixmask.sin6_addr, NULL); - pr0.ndpr_prefix = ifra.ifra_addr; - /* apply the mask for safety. (nd6_prelist_add will apply it again) */ - IN6_MASK_ADDR(&pr0.ndpr_prefix.sin6_addr, &in6mask64); - /* - * Initialize parameters. The link-local prefix must always be - * on-link, and its lifetimes never expire. - */ - pr0.ndpr_raf_onlink = 1; - pr0.ndpr_raf_auto = 1; /* probably meaningless */ - pr0.ndpr_vltime = ND6_INFINITE_LIFETIME; - pr0.ndpr_pltime = ND6_INFINITE_LIFETIME; - /* - * Since there is no other link-local addresses, nd6_prefix_lookup() - * probably returns NULL. However, we cannot always expect the result. - * For example, if we first remove the (only) existing link-local - * address, and then reconfigure another one, the prefix is still - * valid with referring to the old link-local address. - */ - if ((pr = nd6_prefix_lookup(&pr0)) == NULL) { - if ((error = nd6_prelist_add(&pr0, NULL, &pr)) != 0) - return (error); - /* Reference prefix */ - ia->ia6_ndpr = pr; - pr->ndpr_addrcnt++; - } else - nd6_prefix_rele(pr); + error = in6_prefix_rtrequest(RTM_ADD, ia); + if (error == 0) + ia->ia_flags |= IFA_ROUTE; + else if (error == EEXIST) + error = 0; - return 0; + return error; } /* Index: sys/netinet6/in6_var.h =================================================================== --- sys/netinet6/in6_var.h +++ sys/netinet6/in6_var.h @@ -881,6 +881,7 @@ #define IN6_IFAUPDATE_DADDELAY 0x1 /* first time to configure an address */ int in6_mask2len(struct in6_addr *, u_char *); +int in6_prefix_rtrequest(int, struct in6_ifaddr *); int in6_control(struct socket *, u_long, void *, struct ifnet *, struct thread *); int in6_update_ifa(struct ifnet *, struct in6_aliasreq *,