FreeBSD/sys/netinet6/nd6_rtr.c
/*
 * Check if each prefix in the prefix list has at least one available router
 * that advertised the prefix (a router is "available" if its neighbor cache
 * entry is reachable or probably reachable).
 * If the check fails, the prefix may be off-link, because, for example,
 * we have moved from the network but the lifetime of the prefix has not
 * expired yet.  So we should not use the prefix if there is another prefix
 * that has an available router.
 * But, if there is no prefix that has an available router, we still regards
 * all the prefixes as on-link.  This is because we can't tell if all the
 * routers are simply dead or if we really moved from the network and there
 * is no router around us.
 */
Check if each prefix in the prefix list has at least one available router
that advertised the prefix (a router is "available" if its neighbor cache
entry is reachable or probably reachable).
If the check fails, the prefix may be off-link, because, for example,
we have moved from the network but the lifetime of the prefix has not
expired yet.  So we should not use the prefix if there is another prefix
that has an available router.
But, if there is no prefix that has an available router, we still regards
all the prefixes as on-link.  This is because we can't tell if all the
routers are simply dead or if we really moved from the network and there
is no router around us.
Check if each prefix in the prefix list has at least one available router that advertised the prefix ( a router is " available " if its neighbor cache entry is reachable or probably reachable ) . If the check fails the prefix may be off - link because for example we have moved from the network but the lifetime of the prefix has not expired yet . So we should not use the prefix if there is another prefix that has an available router . But if there is no prefix that has an available router we still regards all the prefixes as on - link . This is because we can ' t tell if all the routers are simply dead or if we really moved from the network and there is no router around us .
void
pfxlist_onlink_check(void)
{
	struct nd_prefix *pr;
	struct in6_ifaddr *ifa;
	struct nd_defrouter *dr;
	struct nd_pfxrouter *pfxrtr = NULL;
	struct rm_priotracker in6_ifa_tracker;
	LIST_FOREACH(pr, &V_nd_prefix, ndpr_entry) {
		if (pr->ndpr_raf_onlink && find_pfxlist_reachable_router(pr))
			break;
	}
	if (pr == NULL) {
		ND6_RLOCK();
		TAILQ_FOREACH(dr, &V_nd_defrouter, dr_entry) {
			struct nd_prefix *pr0;
			LIST_FOREACH(pr0, &V_nd_prefix, ndpr_entry) {
				if ((pfxrtr = pfxrtr_lookup(pr0, dr)) != NULL)
					break;
			}
			if (pfxrtr != NULL)
				break;
		}
		ND6_RUNLOCK();
	}
	if (pr != NULL || (!TAILQ_EMPTY(&V_nd_defrouter) && pfxrtr == NULL)) {
		LIST_FOREACH(pr, &V_nd_prefix, ndpr_entry) {
			if (IN6_IS_ADDR_LINKLOCAL(&pr->ndpr_prefix.sin6_addr))
				continue;
			if (pr->ndpr_raf_onlink == 0)
				continue;
			if (pr->ndpr_raf_auto == 0)
				continue;
			if ((pr->ndpr_stateflags & NDPRF_DETACHED) == 0 &&
			    find_pfxlist_reachable_router(pr) == NULL)
				pr->ndpr_stateflags |= NDPRF_DETACHED;
			if ((pr->ndpr_stateflags & NDPRF_DETACHED) != 0 &&
			    find_pfxlist_reachable_router(pr) != NULL)
				pr->ndpr_stateflags &= ~NDPRF_DETACHED;
		}
	} else {
		LIST_FOREACH(pr, &V_nd_prefix, ndpr_entry) {
			if (IN6_IS_ADDR_LINKLOCAL(&pr->ndpr_prefix.sin6_addr))
				continue;
			if (pr->ndpr_raf_onlink == 0)
				continue;
			if (pr->ndpr_raf_auto == 0)
				continue;
			if ((pr->ndpr_stateflags & NDPRF_DETACHED) != 0)
				pr->ndpr_stateflags &= ~NDPRF_DETACHED;
		}
	}
	LIST_FOREACH(pr, &V_nd_prefix, ndpr_entry) {
		int e;
		char ip6buf[INET6_ADDRSTRLEN];
		if (IN6_IS_ADDR_LINKLOCAL(&pr->ndpr_prefix.sin6_addr))
			continue;
		if (pr->ndpr_raf_onlink == 0)
			continue;
		if (pr->ndpr_raf_auto == 0)
			continue;
		if ((pr->ndpr_stateflags & NDPRF_DETACHED) != 0 &&
		    (pr->ndpr_stateflags & NDPRF_ONLINK) != 0) {
			if ((e = nd6_prefix_offlink(pr)) != 0) {
				nd6log((LOG_ERR,
				    "pfxlist_onlink_check: failed to "
				    "make %s/%d offlink, errno=%d\n",
				    ip6_sprintf(ip6buf,
					    &pr->ndpr_prefix.sin6_addr),
					    pr->ndpr_plen, e));
			}
		}
		if ((pr->ndpr_stateflags & NDPRF_DETACHED) == 0 &&
		    (pr->ndpr_stateflags & NDPRF_ONLINK) == 0 &&
		    pr->ndpr_raf_onlink) {
			if ((e = nd6_prefix_onlink(pr)) != 0) {
				nd6log((LOG_ERR,
				    "pfxlist_onlink_check: failed to "
				    "make %s/%d onlink, errno=%d\n",
				    ip6_sprintf(ip6buf,
					    &pr->ndpr_prefix.sin6_addr),
					    pr->ndpr_plen, e));
			}
		}
	}
	IN6_IFADDR_RLOCK(&in6_ifa_tracker);
	TAILQ_FOREACH(ifa, &V_in6_ifaddrhead, ia_link) {
		if (!(ifa->ia6_flags & IN6_IFF_AUTOCONF))
			continue;
		if (ifa->ia6_ndpr == NULL) {
			continue;
		}
		if (find_pfxlist_reachable_router(ifa->ia6_ndpr))
			break;
	}
	if (ifa) {
		TAILQ_FOREACH(ifa, &V_in6_ifaddrhead, ia_link) {
			if ((ifa->ia6_flags & IN6_IFF_AUTOCONF) == 0)
				continue;
			if (ifa->ia6_ndpr == NULL) 
				continue;
			if (find_pfxlist_reachable_router(ifa->ia6_ndpr)) {
				if (ifa->ia6_flags & IN6_IFF_DETACHED) {
					ifa->ia6_flags &= ~IN6_IFF_DETACHED;
					ifa->ia6_flags |= IN6_IFF_TENTATIVE;
					nd6_dad_start((struct ifaddr *)ifa, 0);
				}
			} else {
				ifa->ia6_flags |= IN6_IFF_DETACHED;
			}
		}
	} else {
		TAILQ_FOREACH(ifa, &V_in6_ifaddrhead, ia_link) {
			if ((ifa->ia6_flags & IN6_IFF_AUTOCONF) == 0)
				continue;
			if (ifa->ia6_flags & IN6_IFF_DETACHED) {
				ifa->ia6_flags &= ~IN6_IFF_DETACHED;
				ifa->ia6_flags |= IN6_IFF_TENTATIVE;
				nd6_dad_start((struct ifaddr *)ifa, 0);
			}
		}
	}
	IN6_IFADDR_RUNLOCK(&in6_ifa_tracker);
}
void pfxlist_onlink_check ( void ) { struct nd_prefix * pr ; struct in6_ifaddr * ifa ; struct nd_defrouter * dr ; struct nd_pfxrouter * pfxrtr = NULL ; struct rm_priotracker in6_ifa_tracker ; LIST_FOREACH ( pr , & V_nd_prefix , ndpr_entry ) { if ( pr -> ndpr_raf_onlink && find_pfxlist_reachable_router ( pr ) ) break ; } if ( pr == NULL ) { ND6_RLOCK ( ) ; TAILQ_FOREACH ( dr , & V_nd_defrouter , dr_entry ) { struct nd_prefix * pr0 ; LIST_FOREACH ( pr0 , & V_nd_prefix , ndpr_entry ) { if ( ( pfxrtr = pfxrtr_lookup ( pr0 , dr ) ) != NULL ) break ; } if ( pfxrtr != NULL ) break ; } ND6_RUNLOCK ( ) ; } if ( pr != NULL || ( ! TAILQ_EMPTY ( & V_nd_defrouter ) && pfxrtr == NULL ) ) { LIST_FOREACH ( pr , & V_nd_prefix , ndpr_entry ) { if ( IN6_IS_ADDR_LINKLOCAL ( & pr -> ndpr_prefix . sin6_addr ) ) continue ; if ( pr -> ndpr_raf_onlink == 0 ) continue ; if ( pr -> ndpr_raf_auto == 0 ) continue ; if ( ( pr -> ndpr_stateflags & NDPRF_DETACHED ) == 0 && find_pfxlist_reachable_router ( pr ) == NULL ) pr -> ndpr_stateflags |= NDPRF_DETACHED ; if ( ( pr -> ndpr_stateflags & NDPRF_DETACHED ) != 0 && find_pfxlist_reachable_router ( pr ) != NULL ) pr -> ndpr_stateflags &= ~ NDPRF_DETACHED ; } } else { LIST_FOREACH ( pr , & V_nd_prefix , ndpr_entry ) { if ( IN6_IS_ADDR_LINKLOCAL ( & pr -> ndpr_prefix . sin6_addr ) ) continue ; if ( pr -> ndpr_raf_onlink == 0 ) continue ; if ( pr -> ndpr_raf_auto == 0 ) continue ; if ( ( pr -> ndpr_stateflags & NDPRF_DETACHED ) != 0 ) pr -> ndpr_stateflags &= ~ NDPRF_DETACHED ; } } LIST_FOREACH ( pr , & V_nd_prefix , ndpr_entry ) { int e ; char ip6buf [ INET6_ADDRSTRLEN ] ; if ( IN6_IS_ADDR_LINKLOCAL ( & pr -> ndpr_prefix . sin6_addr ) ) continue ; if ( pr -> ndpr_raf_onlink == 0 ) continue ; if ( pr -> ndpr_raf_auto == 0 ) continue ; if ( ( pr -> ndpr_stateflags & NDPRF_DETACHED ) != 0 && ( pr -> ndpr_stateflags & NDPRF_ONLINK ) != 0 ) { if ( ( e = nd6_prefix_offlink ( pr ) ) != 0 ) { nd6log ( ( LOG_ERR , " " " \n " , ip6_sprintf ( ip6buf , & pr -> ndpr_prefix . sin6_addr ) , pr -> ndpr_plen , e ) ) ; } } if ( ( pr -> ndpr_stateflags & NDPRF_DETACHED ) == 0 && ( pr -> ndpr_stateflags & NDPRF_ONLINK ) == 0 && pr -> ndpr_raf_onlink ) { if ( ( e = nd6_prefix_onlink ( pr ) ) != 0 ) { nd6log ( ( LOG_ERR , " " " \n " , ip6_sprintf ( ip6buf , & pr -> ndpr_prefix . sin6_addr ) , pr -> ndpr_plen , e ) ) ; } } } IN6_IFADDR_RLOCK ( & in6_ifa_tracker ) ; TAILQ_FOREACH ( ifa , & V_in6_ifaddrhead , ia_link ) { if ( ! ( ifa -> ia6_flags & IN6_IFF_AUTOCONF ) ) continue ; if ( ifa -> ia6_ndpr == NULL ) { continue ; } if ( find_pfxlist_reachable_router ( ifa -> ia6_ndpr ) ) break ; } if ( ifa ) { TAILQ_FOREACH ( ifa , & V_in6_ifaddrhead , ia_link ) { if ( ( ifa -> ia6_flags & IN6_IFF_AUTOCONF ) == 0 ) continue ; if ( ifa -> ia6_ndpr == NULL ) continue ; if ( find_pfxlist_reachable_router ( ifa -> ia6_ndpr ) ) { if ( ifa -> ia6_flags & IN6_IFF_DETACHED ) { ifa -> ia6_flags &= ~ IN6_IFF_DETACHED ; ifa -> ia6_flags |= IN6_IFF_TENTATIVE ; nd6_dad_start ( ( struct ifaddr * ) ifa , 0 ) ; } } else { ifa -> ia6_flags |= IN6_IFF_DETACHED ; } } } else { TAILQ_FOREACH ( ifa , & V_in6_ifaddrhead , ia_link ) { if ( ( ifa -> ia6_flags & IN6_IFF_AUTOCONF ) == 0 ) continue ; if ( ifa -> ia6_flags & IN6_IFF_DETACHED ) { ifa -> ia6_flags &= ~ IN6_IFF_DETACHED ; ifa -> ia6_flags |= IN6_IFF_TENTATIVE ; nd6_dad_start ( ( struct ifaddr * ) ifa , 0 ) ; } } } IN6_IFADDR_RUNLOCK ( & in6_ifa_tracker ) ; }
Check if each prefix in the prefix list has at least one available router
 that advertised the prefix (a router is "available" if its neighbor cache
 entry is reachable or probably reachable).
Check if each prefix in the prefix list has at least one available router that advertised the prefix ( a router is " available " if its neighbor cache entry is reachable or probably reachable ) .
/*
	 * Check if there is a prefix that has a reachable advertising
	 * router.
	 */ /*
	 * If we have no such prefix, check whether we still have a router
	 * that does not advertise any prefixes.
	 */ /*
		 * There is at least one prefix that has a reachable router,
		 * or at least a router which probably does not advertise
		 * any prefixes.  The latter would be the case when we move
		 * to a new link where we have a router that does not provide
		 * prefixes and we configure an address by hand.
		 * Detach prefixes which have no reachable advertising
		 * router, and attach other prefixes.
		 */ /* XXX: a link-local prefix should never be detached */ /*
			 * we aren't interested in prefixes without the L bit
			 * set.
			 */ /* there is no prefix that has a reachable router */ /*
	 * Remove each interface route associated with a (just) detached
	 * prefix, and reinstall the interface route for a (just) attached
	 * prefix.  Note that all attempt of reinstallation does not
	 * necessarily success, when a same prefix is shared among multiple
	 * interfaces.  Such cases will be handled in nd6_prefix_onlink,
	 * so we don't have to care about them.
	 */ /*
	 * Changes on the prefix status might affect address status as well.
	 * Make sure that all addresses derived from an attached prefix are
	 * attached, and that all addresses derived from a detached prefix are
	 * detached.  Note, however, that a manually configured address should
	 * always be attached.
	 * The precise detection logic is same as the one for prefixes.
	 */ /*
			 * This can happen when we first configure the address
			 * (i.e. the address exists, but the prefix does not).
			 * XXX: complicated relationships...
			 */ /* XXX: see above. */ /* Do we need a delay in this case? */