FreeBSD/sys/netinet6/nd6.c
/*
 * Create neighbor cache entry and cache link-layer address,
 * on reception of inbound ND6 packets.  (RS/RA/NS/redirect)
 *
 * type - ICMP6 type
 * code - type dependent information
 *
 */
Create neighbor cache entry and cache link-layer address,
on reception of inbound ND6 packets.  (RS/RA/NS/redirect)

ICMP6 type
code - type dependent information
Create neighbor cache entry and cache link - layer address on reception of inbound ND6 packets . ( RS / RA / NS / redirect ) ICMP6 type code - type dependent information
void
nd6_cache_lladdr(struct ifnet *ifp, struct in6_addr *from, char *lladdr,
    int lladdrlen, int type, int code)
{
	struct llentry *ln = NULL, *ln_tmp;
	int is_newentry;
	int do_update;
	int olladdr;
	int llchange;
	int flags;
	uint16_t router = 0;
	struct sockaddr_in6 sin6;
	struct mbuf *chain = NULL;
	u_char linkhdr[LLE_MAX_LINKHDR];
	size_t linkhdrsize;
	int lladdr_off;
	IF_AFDATA_UNLOCK_ASSERT(ifp);
	KASSERT(ifp != NULL, ("%s: ifp == NULL", __func__));
	KASSERT(from != NULL, ("%s: from == NULL", __func__));
	if (IN6_IS_ADDR_UNSPECIFIED(from))
		return;
	flags = lladdr ? LLE_EXCLUSIVE : 0;
	IF_AFDATA_RLOCK(ifp);
	ln = nd6_lookup(from, flags, ifp);
	IF_AFDATA_RUNLOCK(ifp);
	is_newentry = 0;
	if (ln == NULL) {
		flags |= LLE_EXCLUSIVE;
		ln = nd6_alloc(from, 0, ifp);
		if (ln == NULL)
			return;
		if (lladdr != NULL) {
			linkhdrsize = sizeof(linkhdr);
			if (lltable_calc_llheader(ifp, AF_INET6, lladdr,
			    linkhdr, &linkhdrsize, &lladdr_off) != 0)
				return;
			lltable_set_entry_addr(ifp, ln, linkhdr, linkhdrsize,
			    lladdr_off);
		}
		IF_AFDATA_WLOCK(ifp);
		LLE_WLOCK(ln);
		ln_tmp = nd6_lookup(from, LLE_EXCLUSIVE, ifp);
		if (ln_tmp == NULL)
			lltable_link_entry(LLTABLE6(ifp), ln);
		IF_AFDATA_WUNLOCK(ifp);
		if (ln_tmp == NULL) {
			is_newentry = 1;
			nd6_llinfo_setstate(ln, ND6_LLINFO_STALE);
			if (lladdr != NULL)	
				EVENTHANDLER_INVOKE(lle_event, ln,
				    LLENTRY_RESOLVED);
		} else {
			lltable_free_entry(LLTABLE6(ifp), ln);
			ln = ln_tmp;
			ln_tmp = NULL;
		}
	} 
	if ((ln->la_flags & LLE_STATIC)) {
		if (flags & LLE_EXCLUSIVE)
			LLE_WUNLOCK(ln);
		else
			LLE_RUNLOCK(ln);
		return;
	}
	olladdr = (ln->la_flags & LLE_VALID) ? 1 : 0;
	if (olladdr && lladdr) {
		llchange = bcmp(lladdr, ln->ll_addr,
		    ifp->if_addrlen);
	} else if (!olladdr && lladdr)
		llchange = 1;
	else
		llchange = 0;
	do_update = 0;
	if (is_newentry == 0 && llchange != 0) {
		do_update = 1;	
		linkhdrsize = sizeof(linkhdr);
		if (lltable_calc_llheader(ifp, AF_INET6, lladdr,
		    linkhdr, &linkhdrsize, &lladdr_off) != 0)
			return;
		if (lltable_try_set_entry_addr(ifp, ln, linkhdr, linkhdrsize,
		    lladdr_off) == 0) {
			return;
		}
		nd6_llinfo_setstate(ln, ND6_LLINFO_STALE);
		EVENTHANDLER_INVOKE(lle_event, ln, LLENTRY_RESOLVED);
		if (ln->la_hold != NULL)
			nd6_grab_holdchain(ln, &chain, &sin6);
	}
	router = nd6_is_router(type, code, is_newentry, olladdr,
	    lladdr != NULL ? 1 : 0, ln->ln_router);
	ln->ln_router = router;
	if ((type & 0xFF) == ND_REDIRECT && code != ND_REDIRECT_ROUTER)
		ln->la_flags |= LLE_REDIRECT;
	if (flags & LLE_EXCLUSIVE)
		LLE_WUNLOCK(ln);
	else
		LLE_RUNLOCK(ln);
	if (chain != NULL)
		nd6_flush_holdchain(ifp, ifp, chain, &sin6);
	if ((do_update || is_newentry) && router &&
	    ND_IFINFO(ifp)->flags & ND6_IFF_ACCEPT_RTADV) {
		defrouter_select();
	}
}
void nd6_cache_lladdr ( struct ifnet * ifp , struct in6_addr * from , char * lladdr , int lladdrlen , int type , int code ) { struct llentry * ln = NULL , * ln_tmp ; int is_newentry ; int do_update ; int olladdr ; int llchange ; int flags ; uint16_t router = 0 ; struct sockaddr_in6 sin6 ; struct mbuf * chain = NULL ; u_char linkhdr [ LLE_MAX_LINKHDR ] ; size_t linkhdrsize ; int lladdr_off ; IF_AFDATA_UNLOCK_ASSERT ( ifp ) ; KASSERT ( ifp != NULL , ( " " , __func__ ) ) ; KASSERT ( from != NULL , ( " " , __func__ ) ) ; if ( IN6_IS_ADDR_UNSPECIFIED ( from ) ) return ; flags = lladdr ? LLE_EXCLUSIVE : 0 ; IF_AFDATA_RLOCK ( ifp ) ; ln = nd6_lookup ( from , flags , ifp ) ; IF_AFDATA_RUNLOCK ( ifp ) ; is_newentry = 0 ; if ( ln == NULL ) { flags |= LLE_EXCLUSIVE ; ln = nd6_alloc ( from , 0 , ifp ) ; if ( ln == NULL ) return ; if ( lladdr != NULL ) { linkhdrsize = sizeof ( linkhdr ) ; if ( lltable_calc_llheader ( ifp , AF_INET6 , lladdr , linkhdr , & linkhdrsize , & lladdr_off ) != 0 ) return ; lltable_set_entry_addr ( ifp , ln , linkhdr , linkhdrsize , lladdr_off ) ; } IF_AFDATA_WLOCK ( ifp ) ; LLE_WLOCK ( ln ) ; ln_tmp = nd6_lookup ( from , LLE_EXCLUSIVE , ifp ) ; if ( ln_tmp == NULL ) lltable_link_entry ( LLTABLE6 ( ifp ) , ln ) ; IF_AFDATA_WUNLOCK ( ifp ) ; if ( ln_tmp == NULL ) { is_newentry = 1 ; nd6_llinfo_setstate ( ln , ND6_LLINFO_STALE ) ; if ( lladdr != NULL ) EVENTHANDLER_INVOKE ( lle_event , ln , LLENTRY_RESOLVED ) ; } else { lltable_free_entry ( LLTABLE6 ( ifp ) , ln ) ; ln = ln_tmp ; ln_tmp = NULL ; } } if ( ( ln -> la_flags & LLE_STATIC ) ) { if ( flags & LLE_EXCLUSIVE ) LLE_WUNLOCK ( ln ) ; else LLE_RUNLOCK ( ln ) ; return ; } olladdr = ( ln -> la_flags & LLE_VALID ) ? 1 : 0 ; if ( olladdr && lladdr ) { llchange = bcmp ( lladdr , ln -> ll_addr , ifp -> if_addrlen ) ; } else if ( ! olladdr && lladdr ) llchange = 1 ; else llchange = 0 ; do_update = 0 ; if ( is_newentry == 0 && llchange != 0 ) { do_update = 1 ; linkhdrsize = sizeof ( linkhdr ) ; if ( lltable_calc_llheader ( ifp , AF_INET6 , lladdr , linkhdr , & linkhdrsize , & lladdr_off ) != 0 ) return ; if ( lltable_try_set_entry_addr ( ifp , ln , linkhdr , linkhdrsize , lladdr_off ) == 0 ) { return ; } nd6_llinfo_setstate ( ln , ND6_LLINFO_STALE ) ; EVENTHANDLER_INVOKE ( lle_event , ln , LLENTRY_RESOLVED ) ; if ( ln -> la_hold != NULL ) nd6_grab_holdchain ( ln , & chain , & sin6 ) ; } router = nd6_is_router ( type , code , is_newentry , olladdr , lladdr != NULL ? 1 : 0 , ln -> ln_router ) ; ln -> ln_router = router ; if ( ( type & 0xFF ) == ND_REDIRECT && code != ND_REDIRECT_ROUTER ) ln -> la_flags |= LLE_REDIRECT ; if ( flags & LLE_EXCLUSIVE ) LLE_WUNLOCK ( ln ) ; else LLE_RUNLOCK ( ln ) ; if ( chain != NULL ) nd6_flush_holdchain ( ifp , ifp , chain , & sin6 ) ; if ( ( do_update || is_newentry ) && router && ND_IFINFO ( ifp ) -> flags & ND6_IFF_ACCEPT_RTADV ) { defrouter_select ( ) ; } }
Create neighbor cache entry and cache link-layer address,
 on reception of inbound ND6 packets.
Create neighbor cache entry and cache link - layer address on reception of inbound ND6 packets .
/* nothing must be updated for unspecified address */ /*
	 * Validation about ifp->if_addrlen and lladdrlen must be done in
	 * the caller.
	 *
	 * XXX If the link does not have link-layer adderss, what should
	 * we do? (ifp->if_addrlen == 0)
	 * Spec says nothing in sections for RA, RS and NA.  There's small
	 * description on it in NS section (RFC 2461 7.2.3).
	 */ /*
		 * Since we already know all the data for the new entry,
		 * fill it before insertion.
		 */ /* Prefer any existing lle over newly-created one */ /* No existing lle, mark as new entry (6,7) */ /* (7) */ /* do nothing if static ndp is set */ /*
	 * newentry olladdr  lladdr  llchange	(*=record)
	 *	0	n	n	--	(1)
	 *	0	y	n	--	(2)
	 *	0	n	y	y	(3) * STALE
	 *	0	y	y	n	(4) *
	 *	0	y	y	y	(5) * STALE
	 *	1	--	n	--	(6)   NOSTATE(= PASSIVE)
	 *	1	--	y	--	(7) * STALE
	 */ /* (3,5) */ /*
		 * Record source link-layer address
		 * XXX is it dependent to ifp->if_type?
		 */ /* Entry was deleted */ /* Calculates new router status */ /* Mark non-router redirects with special flag */ /*
	 * When the link-layer address of a router changes, select the
	 * best router again.  In particular, when the neighbor entry is newly
	 * created, it might affect the selection policy.
	 * Question: can we restrict the first condition to the "is_newentry"
	 * case?
	 * XXX: when we hear an RA from a new router with the link-layer
	 * address option, defrouter_select() is called twice, since
	 * defrtrlist_update called the function as well.  However, I believe
	 * we can compromise the overhead, since it only happens the first
	 * time.
	 * XXX: although defrouter_select() should not have a bad effect
	 * for those are not autoconfigured hosts, we explicitly avoid such
	 * cases for safety.
	 */ /*
		 * guaranteed recursion
		 */