+
+ if (ifp->if_eflags & IFEF_IPV6_ROUTER) {
+ nd6log2((LOG_INFO,
+ "%s:%d: Return early. "
+ "Default router select called for interface"
+ " %s with IFEF_IPV6_ROUTER flag set\n",
+ __func__, __LINE__, if_name(ifp)));
+ return;
+ }
+
+ /*
+ * Let's handle easy case (3) first:
+ * If default router list is empty, there's nothing to be done.
+ */
+ if (!TAILQ_FIRST(&nd_defrouter)) {
+ nd6log2((LOG_INFO,
+ "%s:%d: Return early. "
+ "Default router is empty.\n", __func__, __LINE__));
+ return;
+ }
+
+ /*
+ * Take an early exit if number of routers in nd_ifinfo is
+ * 0 for the interface.
+ */
+ ndi = ND_IFINFO(ifp);
+ if (!ndi || !ndi->initialized) {
+ nd6log2((LOG_INFO,
+ "%s:%d: Return early. "
+ "Interface %s's nd_ifinfo not initialized.\n",
+ __func__, __LINE__, if_name(ifp)));
+ return;
+ }
+
+ if (ndi->ndefrouters == 0) {
+ nd6log2((LOG_INFO,
+ "%s:%d: Return early. "
+ "%s does not have any default routers.\n",
+ __func__, __LINE__, if_name(ifp)));
+ return;
+ }
+
+ /*
+ * Due to the number of times we drop nd6_mutex, we need to
+ * serialize this function.
+ */
+ while (nd_defrouter_busy) {
+ nd_defrouter_waiters++;
+ msleep(nd_defrouter_waitchan, nd6_mutex, (PZERO-1),
+ __func__, NULL);
+ lck_mtx_assert(nd6_mutex, LCK_MTX_ASSERT_OWNED);
+ }
+ nd_defrouter_busy = TRUE;
+
+ /*
+ * Search for a (probably) reachable router from the list.
+ * We just pick up the first reachable one (if any), assuming that
+ * the ordering rule of the list described in defrtrlist_update().
+ *
+ * For all intents and purposes of Scoped Routing:
+ * selected_dr = candidate for primary router
+ * installed_dr = currently installed primary router
+ */
+ genid = nd6_defrouter_genid;
+ dr = TAILQ_FIRST(&nd_defrouter);
+
+ while (dr != NULL) {
+ struct in6_addr rtaddr;
+ struct ifnet *drifp = NULL;
+ struct nd_defrouter *drrele = NULL;
+
+ NDDR_LOCK(dr);
+ drifp = dr->ifp;
+ if (drifp != ifp) {
+ NDDR_UNLOCK(dr);
+ dr = TAILQ_NEXT(dr, dr_entry);
+ continue;
+ }
+
+ /*
+ * Optimize for the common case.
+ * When the interface has only one default router
+ * there's no point checking for reachability as
+ * there's nothing else to choose from.
+ */
+ if (ndi->ndefrouters == 1) {
+ nd6log2((LOG_INFO,
+ "%s:%d: Fast forward default router selection "
+ "as interface %s has learned only one default "
+ "router and there's nothing else to choose from.\n",
+ __func__, __LINE__, if_name(ifp)));
+ VERIFY(selected_dr == NULL && installed_dr == NULL);
+ selected_dr = dr;
+ if (dr->stateflags & NDDRF_INSTALLED)
+ installed_dr = dr;
+ NDDR_ADDREF_LOCKED(selected_dr);
+ NDDR_UNLOCK(dr);
+ goto install_route;
+ }
+
+ if (dr->stateflags & NDDRF_MAPPED)
+ rtaddr = dr->rtaddr_mapped;
+ else
+ rtaddr = dr->rtaddr;
+
+ NDDR_ADDREF_LOCKED(dr); /* for this for loop */
+ NDDR_UNLOCK(dr);
+
+ /* Callee returns a locked route upon success */
+ if (selected_dr == NULL) {
+ lck_mtx_unlock(nd6_mutex);
+ if ((rt = nd6_lookup(&rtaddr, 0, drifp, 0)) != NULL &&
+ (ln = rt->rt_llinfo) != NULL &&
+ ND6_IS_LLINFO_PROBREACH(ln)) {
+ RT_LOCK_ASSERT_HELD(rt);
+ selected_dr = dr;
+ NDDR_ADDREF(selected_dr);
+ }
+ lck_mtx_lock(nd6_mutex);
+ }
+
+ if (rt) {
+ RT_REMREF_LOCKED(rt);
+ RT_UNLOCK(rt);
+ rt = NULL;
+ }
+
+ /*
+ * Handle case (b)
+ * When there are more than one routers on the same link, the one with
+ * the highest router preference will be installed.
+ * Since the list is in decreasing order of preference:
+ * 1) If selected_dr is not NULL, only use dr if it is static and has
+ * equal preference and selected_dr is not static.
+ * 2) Else if selected_dr is NULL, and dr is static make selected_dr = dr
+ */
+ NDDR_LOCK(dr);
+ if (((selected_dr && (rtpref(dr) >= rtpref(selected_dr)) &&
+ !(selected_dr->stateflags & NDDRF_STATIC)) ||
+ (selected_dr == NULL)) &&
+ (dr->stateflags & NDDRF_STATIC)) {
+ if (selected_dr) {
+ /* Release it later on */
+ VERIFY(drrele == NULL);
+ drrele = selected_dr;
+ }
+ selected_dr = dr;
+ NDDR_ADDREF_LOCKED(selected_dr);
+ }
+
+ /* Record the currently installed router */
+ if (dr->stateflags & NDDRF_INSTALLED) {
+ if (installed_dr == NULL) {
+ installed_dr = dr;
+ NDDR_ADDREF_LOCKED(installed_dr);
+ if (dr->stateflags & NDDRF_MAPPED)
+ rtaddr = installed_dr->rtaddr_mapped;
+ else
+ rtaddr = installed_dr->rtaddr;
+ NDDR_UNLOCK(dr);
+ lck_mtx_unlock(nd6_mutex);
+ /* Callee returns a locked route upon success */
+ if ((rt = nd6_lookup(&rtaddr, 0, ifp, 0)) != NULL) {
+ RT_LOCK_ASSERT_HELD(rt);
+ if ((ln = rt->rt_llinfo) != NULL &&
+ ND6_IS_LLINFO_PROBREACH(ln))
+ is_installed_reachable = TRUE;
+
+ RT_REMREF_LOCKED(rt);
+ RT_UNLOCK(rt);
+ rt = NULL;
+ }
+ lck_mtx_lock(nd6_mutex);
+ } else {
+ /* this should not happen; warn for diagnosis */
+ nd6log((LOG_ERR, "defrouter_select: more than one "
+ "default router is installed for interface :%s.\n",
+ if_name(ifp)));
+ NDDR_UNLOCK(dr);
+ }
+ } else
+ NDDR_UNLOCK(dr);
+
+ NDDR_REMREF(dr); /* for this for loop */
+ if (drrele != NULL)
+ NDDR_REMREF(drrele);
+
+ /*
+ * Check if the list changed when we gave up
+ * the nd6_mutex lock
+ */
+ if(genid != nd6_defrouter_genid) {
+ if (selected_dr) {
+ NDDR_REMREF(selected_dr);
+ selected_dr = NULL;
+ }
+
+ if (installed_dr) {
+ NDDR_REMREF(installed_dr);
+ installed_dr = NULL;
+ }
+
+ if (ndi->ndefrouters == 0) {
+ nd6log2((LOG_INFO,
+ "%s:%d: Interface %s no longer "
+ "has any default routers. Abort.\n",
+ __func__, __LINE__, if_name(ifp)));
+ goto out;
+ }
+ nd6log2((LOG_INFO,
+ "%s:%d: Iterate default router list again "
+ "for interface %s, as the list seems to have "
+ "changed during release-reaquire of global "
+ "nd6_mutex lock.\n",
+ __func__, __LINE__, if_name(ifp)));
+
+ is_installed_reachable = FALSE;
+ genid = nd6_defrouter_genid;
+ dr = TAILQ_FIRST(&nd_defrouter);
+ } else {
+ dr = TAILQ_NEXT(dr, dr_entry);
+ }
+ }
+
+ /*
+ * If none of the default routers was found to be reachable,
+ * round-robin the list regardless of preference.
+ * Please note selected_dr equal to NULL implies that even
+ * installed default router is not reachable
+ */
+ if (selected_dr == NULL) {
+ if (installed_dr) {
+ for (dr = TAILQ_NEXT(installed_dr, dr_entry); dr;
+ dr = TAILQ_NEXT(dr, dr_entry)) {
+ if (installed_dr->ifp != dr->ifp)
+ continue;
+ selected_dr = dr;
+ break;
+ }
+ }
+
+ /*
+ * If none was installed or the installed one if the last
+ * one on the list, select the first one from the list
+ */
+ if ((installed_dr == NULL) || (selected_dr == NULL)) {
+ for (dr = TAILQ_FIRST(&nd_defrouter); dr;
+ dr = TAILQ_NEXT(dr, dr_entry)) {
+ if (dr->ifp == ifp) {
+ selected_dr = dr;
+ break;
+ }
+ }
+ }
+
+ if ((selected_dr == NULL) && (installed_dr == NULL)) {
+ nd6log2((LOG_INFO,
+ "%s:%d: Between release and reaquire of global "
+ "nd6_mutex lock, the list seems to have changed "
+ "and it does not have any default routers for "
+ "interface %s.\n",
+ __func__, __LINE__, if_name(ifp)));
+ goto out;
+ }
+
+ if (selected_dr != installed_dr)
+ NDDR_ADDREF(selected_dr);
+ } else if (installed_dr != NULL) {
+ if (installed_dr != selected_dr) {