git: 576ee62dd2e5 - main - netinet6: store ND context directly in struct in6_ifextra

From: Gleb Smirnoff <glebius_at_FreeBSD.org>
Date: Fri, 23 Jan 2026 22:36:41 UTC
The branch main has been updated by glebius:

URL: https://cgit.FreeBSD.org/src/commit/?id=576ee62dd2e5e0454a5316eb9207f4ebaa543171

commit 576ee62dd2e5e0454a5316eb9207f4ebaa543171
Author:     Gleb Smirnoff <glebius@FreeBSD.org>
AuthorDate: 2026-01-23 22:17:40 +0000
Commit:     Gleb Smirnoff <glebius@FreeBSD.org>
CommitDate: 2026-01-23 22:17:40 +0000

    netinet6: store ND context directly in struct in6_ifextra
    
    Stop using struct nd_ifinfo for that, because it is an API struct for
    SIOCGIFINFO_IN6.  The functional changes are isolated to the protocol
    attach and detach: in6_ifarrival(), nd6_ifattach(), in6_ifdeparture(),
    nd6_ifdetach(), as well as to the nd6_ioctl(), nd6_ra_input(),
    nd6_slowtimo() and in6_ifmtu().
    
    The dad_failures member was just renamed to match the rest.  The M_IP6NDP
    malloc(9) type declaration moved to files that actually use it.
    
    The rest of the changes are mechanical substitution of double pointer
    dereference via ND_IFINFO() to a single pointer dereference.  This was
    achieved with a sed(1) script:
    
    s/ND_IFINFO\(([a-z0-9>_.-]+)\)->(flags|linkmtu|basereachable|reachable|retrans|chlim)/\1->if_inet6->nd_\2/g
    s/nd_chlim/nd_curhoplimit/g
    
    Reviewed by:            tuexen, madpilot
    Differential Revision:  https://reviews.freebsd.org/D54725
---
 sys/dev/wg/if_wg.c          |   4 +-
 sys/net/if_ethersubr.c      |   4 +-
 sys/netinet/sctp_output.c   |   6 +-
 sys/netinet6/icmp6.c        |   3 +-
 sys/netinet6/in6.c          |  32 ++++-----
 sys/netinet6/in6_ifattach.c |  12 ++--
 sys/netinet6/in6_src.c      |   6 +-
 sys/netinet6/in6_var.h      |  15 +++-
 sys/netinet6/ip6_input.c    |   2 +-
 sys/netinet6/ip6_output.c   |   2 +-
 sys/netinet6/nd6.c          | 172 ++++++++++++++++++++++----------------------
 sys/netinet6/nd6.h          |  12 +---
 sys/netinet6/nd6_nbr.c      |  24 ++++---
 sys/netinet6/nd6_rtr.c      |  56 ++++++++-------
 14 files changed, 177 insertions(+), 173 deletions(-)

diff --git a/sys/dev/wg/if_wg.c b/sys/dev/wg/if_wg.c
index 17aedee0e6b0..611314883643 100644
--- a/sys/dev/wg/if_wg.c
+++ b/sys/dev/wg/if_wg.c
@@ -3033,8 +3033,8 @@ wg_clone_create(struct if_clone *ifc, char *name, size_t len,
 	if_attach(ifp);
 	bpfattach(ifp, DLT_NULL, sizeof(uint32_t));
 #ifdef INET6
-	ND_IFINFO(ifp)->flags &= ~ND6_IFF_AUTO_LINKLOCAL;
-	ND_IFINFO(ifp)->flags |= ND6_IFF_NO_DAD;
+	if_getinet6(ifp)->nd_flags &= ~ND6_IFF_AUTO_LINKLOCAL;
+	if_getinet6(ifp)->nd_flags |= ND6_IFF_NO_DAD;
 #endif
 	sx_xlock(&wg_sx);
 	LIST_INSERT_HEAD(&wg_list, sc, sc_entry);
diff --git a/sys/net/if_ethersubr.c b/sys/net/if_ethersubr.c
index da9264aa4a23..812a31595df9 100644
--- a/sys/net/if_ethersubr.c
+++ b/sys/net/if_ethersubr.c
@@ -479,7 +479,7 @@ ether_output_frame(struct ifnet *ifp, struct mbuf *m)
 #if defined(INET6) && defined(INET)
 	/* draft-ietf-6man-ipv6only-flag */
 	/* Catch ETHERTYPE_IP, and ETHERTYPE_[REV]ARP if we are v6-only. */
-	if ((ND_IFINFO(ifp)->flags & ND6_IFF_IPV6_ONLY_MASK) != 0) {
+	if ((ifp->if_inet6->nd_flags & ND6_IFF_IPV6_ONLY_MASK) != 0) {
 		struct ether_header *eh;
 
 		eh = mtod(m, struct ether_header *);
@@ -545,7 +545,7 @@ ether_input_internal(struct ifnet *ifp, struct mbuf *m)
 #if defined(INET6) && defined(INET)
 	/* draft-ietf-6man-ipv6only-flag */
 	/* Catch ETHERTYPE_IP, and ETHERTYPE_[REV]ARP if we are v6-only. */
-	if ((ND_IFINFO(ifp)->flags & ND6_IFF_IPV6_ONLY_MASK) != 0) {
+	if ((ifp->if_inet6->nd_flags & ND6_IFF_IPV6_ONLY_MASK) != 0) {
 		switch (etype) {
 		case ETHERTYPE_IP:
 		case ETHERTYPE_ARP:
diff --git a/sys/netinet/sctp_output.c b/sys/netinet/sctp_output.c
index e4bdb4291972..4f6fbc6be783 100644
--- a/sys/netinet/sctp_output.c
+++ b/sys/netinet/sctp_output.c
@@ -4596,9 +4596,9 @@ sctp_lowlevel_chunk_output(struct sctp_inpcb *inp,
 						}
 					}
 				} else if (ifp != NULL) {
-					if ((ND_IFINFO(ifp)->linkmtu > 0) &&
-					    (stcb->asoc.smallest_mtu > ND_IFINFO(ifp)->linkmtu)) {
-						sctp_pathmtu_adjustment(stcb, ND_IFINFO(ifp)->linkmtu, false);
+					if ((ifp->if_inet6->nd_linkmtu > 0) &&
+					    (stcb->asoc.smallest_mtu > ifp->if_inet6->nd_linkmtu)) {
+						sctp_pathmtu_adjustment(stcb, ifp->if_inet6->nd_linkmtu, false);
 					}
 				}
 			}
diff --git a/sys/netinet6/icmp6.c b/sys/netinet6/icmp6.c
index c05850f1477b..5b5f7b83623e 100644
--- a/sys/netinet6/icmp6.c
+++ b/sys/netinet6/icmp6.c
@@ -2080,7 +2080,8 @@ icmp6_reflect(struct mbuf *m, size_t off)
 
 			if (m->m_pkthdr.rcvif != NULL) {
 				/* XXX: This may not be the outgoing interface */
-				hlim = ND_IFINFO(m->m_pkthdr.rcvif)->chlim;
+				hlim =
+				    m->m_pkthdr.rcvif->if_inet6->nd_curhoplimit;
 			} else
 				hlim = V_ip6_defhlim;
 		}
diff --git a/sys/netinet6/in6.c b/sys/netinet6/in6.c
index 07f1198cd283..d283aba62dcc 100644
--- a/sys/netinet6/in6.c
+++ b/sys/netinet6/in6.c
@@ -1090,7 +1090,7 @@ in6_update_ifa_internal(struct ifnet *ifp, struct in6_aliasreq *ifra,
 	 * an interface with ND6_IFF_IFDISABLED.
 	 */
 	if (in6if_do_dad(ifp) &&
-	    (hostIsNew || (ND_IFINFO(ifp)->flags & ND6_IFF_IFDISABLED)))
+	    (hostIsNew || (ifp->if_inet6->nd_flags & ND6_IFF_IFDISABLED)))
 		ia->ia6_flags |= IN6_IFF_TENTATIVE;
 
 	/* notify other subsystems */
@@ -1386,11 +1386,11 @@ aifaddr_out:
 	 * Try to clear the flag when a new IPv6 address is added
 	 * onto an IFDISABLED interface and it succeeds.
 	 */
-	if (ND_IFINFO(ifp)->flags & ND6_IFF_IFDISABLED) {
+	if (ifp->if_inet6->nd_flags & ND6_IFF_IFDISABLED) {
 		struct in6_ndireq nd;
 
 		memset(&nd, 0, sizeof(nd));
-		nd.ndi.flags = ND_IFINFO(ifp)->flags;
+		nd.ndi.flags = ifp->if_inet6->nd_flags;
 		nd.ndi.flags &= ~ND6_IFF_IFDISABLED;
 		if (nd6_ioctl(SIOCSIFINFO_FLAGS, (caddr_t)&nd, ifp) < 0)
 			log(LOG_NOTICE, "SIOCAIFADDR_IN6: "
@@ -1712,7 +1712,7 @@ in6ifa_llaonifp(struct ifnet *ifp)
 	struct sockaddr_in6 *sin6;
 	struct ifaddr *ifa;
 
-	if (ND_IFINFO(ifp)->flags & ND6_IFF_IFDISABLED)
+	if (ifp->if_inet6->nd_flags & ND6_IFF_IFDISABLED)
 		return (NULL);
 	NET_EPOCH_ENTER(et);
 	CK_STAILQ_FOREACH(ifa, &ifp->if_addrhead, ifa_link) {
@@ -2136,7 +2136,7 @@ in6if_do_dad(struct ifnet *ifp)
 		return (0);
 	if ((ifp->if_flags & IFF_MULTICAST) == 0)
 		return (0);
-	if ((ND_IFINFO(ifp)->flags &
+	if ((ifp->if_inet6->nd_flags &
 	    (ND6_IFF_IFDISABLED | ND6_IFF_NO_DAD)) != 0)
 		return (0);
 	return (1);
@@ -2607,21 +2607,17 @@ in6_ifarrival(void *arg __unused, struct ifnet *ifp)
 		ifp->if_inet6 = NULL;
 		return;
 	}
-	ext = (struct in6_ifextra *)malloc(sizeof(*ext), M_IFADDR, M_WAITOK);
-	bzero(ext, sizeof(*ext));
-
+	ext = ifp->if_inet6 = malloc(sizeof(*ext), M_IFADDR, M_WAITOK | M_ZERO);
 	COUNTER_ARRAY_ALLOC(ext->in6_ifstat,
 	    sizeof(struct in6_ifstat) / sizeof(uint64_t), M_WAITOK);
 	COUNTER_ARRAY_ALLOC(ext->icmp6_ifstat,
 	    sizeof(struct icmp6_ifstat) / sizeof(uint64_t), M_WAITOK);
+	nd6_ifattach(ifp);
 
-	ext->nd_ifinfo = nd6_ifattach(ifp);
 	ext->scope6_id = scope6_ifattach(ifp);
 	ext->lltable = in6_lltattach(ifp);
 
 	ext->mld_ifinfo = mld_domifattach(ifp);
-
-	ifp->if_inet6 = ext;
 }
 EVENTHANDLER_DEFINE(ifnet_arrival_event, in6_ifarrival, NULL,
     EVENTHANDLER_PRI_ANY);
@@ -2629,12 +2625,16 @@ EVENTHANDLER_DEFINE(ifnet_arrival_event, in6_ifarrival, NULL,
 uint32_t
 in6_ifmtu(const struct ifnet *ifp)
 {
-	struct nd_ifinfo *ndi = ND_IFINFO(ifp);
+	const uint32_t
+	    linkmtu = ifp->if_inet6->nd_linkmtu,
+	    maxmtu = ifp->if_inet6->nd_maxmtu,
+	    ifmtu = ifp->if_mtu;
 
-	return (
-	    (ndi->linkmtu > 0 && ndi->linkmtu < ifp->if_mtu) ? ndi->linkmtu :
-	    ((ndi->maxmtu > 0 && ndi->maxmtu < ifp->if_mtu) ? ndi->maxmtu :
-	    ifp->if_mtu));
+	if (linkmtu > 0 && linkmtu < ifmtu)
+		return (linkmtu);
+	if (maxmtu > 0 && maxmtu < ifmtu)
+		return (maxmtu);
+	return (ifmtu);
 }
 
 /*
diff --git a/sys/netinet6/in6_ifattach.c b/sys/netinet6/in6_ifattach.c
index 194033ee7808..f21f96cd86c8 100644
--- a/sys/netinet6/in6_ifattach.c
+++ b/sys/netinet6/in6_ifattach.c
@@ -465,7 +465,7 @@ in6_get_ifid(struct ifnet *ifp0, struct ifnet *altifp,
 	NET_EPOCH_ASSERT();
 
 	/* first, try to get it from the interface itself, with stable algorithm, if configured */
-	if ((ND_IFINFO(ifp0)->flags & ND6_IFF_STABLEADDR) && in6_get_stableifid(ifp0, in6, 64) == 0) {
+	if ((ifp0->if_inet6->nd_flags & ND6_IFF_STABLEADDR) && in6_get_stableifid(ifp0, in6, 64) == 0) {
 		nd6log((LOG_DEBUG, "%s: got interface identifier from itself (stable private)\n",
 		    if_name(ifp0)));
 		goto success;
@@ -799,8 +799,8 @@ in6_ifattach(struct ifnet *ifp, struct ifnet *altifp)
 		 * linklocals for 6to4 interface, but there's no use and
 		 * it is rather harmful to have one.
 		 */
-		ND_IFINFO(ifp)->flags &= ~ND6_IFF_AUTO_LINKLOCAL;
-		ND_IFINFO(ifp)->flags |= ND6_IFF_NO_DAD;
+		ifp->if_inet6->nd_flags &= ~ND6_IFF_AUTO_LINKLOCAL;
+		ifp->if_inet6->nd_flags |= ND6_IFF_NO_DAD;
 		break;
 	default:
 		break;
@@ -831,8 +831,8 @@ in6_ifattach(struct ifnet *ifp, struct ifnet *altifp)
 	/*
 	 * assign a link-local address, if there's none.
 	 */
-	if (!(ND_IFINFO(ifp)->flags & ND6_IFF_IFDISABLED) &&
-	    ND_IFINFO(ifp)->flags & ND6_IFF_AUTO_LINKLOCAL) {
+	if (!(ifp->if_inet6->nd_flags & ND6_IFF_IFDISABLED) &&
+	    ifp->if_inet6->nd_flags & ND6_IFF_AUTO_LINKLOCAL) {
 		struct epoch_tracker et;
 
 		NET_EPOCH_ENTER(et);
@@ -918,7 +918,7 @@ in6_ifdeparture(void *arg __unused, struct ifnet *ifp)
 		_in6_ifdetach(ifp, 1);
 	mld_domifdetach(ifp);
 	scope6_ifdetach(ext->scope6_id);
-	nd6_ifdetach(ifp, ext->nd_ifinfo);
+	nd6_ifdetach(ifp);
 	lltable_free(ext->lltable);
 	COUNTER_ARRAY_FREE(ext->in6_ifstat,
 	    sizeof(struct in6_ifstat) / sizeof(uint64_t));
diff --git a/sys/netinet6/in6_src.c b/sys/netinet6/in6_src.c
index 5171bc1d4ea6..d5e8e0f952c6 100644
--- a/sys/netinet6/in6_src.c
+++ b/sys/netinet6/in6_src.c
@@ -366,7 +366,7 @@ in6_selectsrc(uint32_t fibnum, struct sockaddr_in6 *dstsock,
 		 */
 
 		/* Rule 5: Prefer outgoing interface */
-		if (!(ND_IFINFO(ifp)->flags & ND6_IFF_NO_PREFER_IFACE)) {
+		if (!(ifp->if_inet6->nd_flags & ND6_IFF_NO_PREFER_IFACE)) {
 			if (ia_best->ia_ifp == ifp && ia->ia_ifp != ifp)
 				NEXT(5);
 			if (ia_best->ia_ifp != ifp && ia->ia_ifp == ifp)
@@ -868,7 +868,7 @@ in6_selecthlim(struct inpcb *inp, struct ifnet *ifp)
 	if (inp && inp->in6p_hops >= 0)
 		return (inp->in6p_hops);
 	else if (ifp)
-		return (ND_IFINFO(ifp)->chlim);
+		return (ifp->if_inet6->nd_curhoplimit);
 	else if (inp && !IN6_IS_ADDR_UNSPECIFIED(&inp->in6p_faddr)) {
 		struct nhop_object *nh;
 		struct in6_addr dst;
@@ -879,7 +879,7 @@ in6_selecthlim(struct inpcb *inp, struct ifnet *ifp)
 		in6_splitscope(&inp->in6p_faddr, &dst, &scopeid);
 		nh = fib6_lookup(fibnum, &dst, scopeid, 0, 0);
 		if (nh != NULL) {
-			hlim = ND_IFINFO(nh->nh_ifp)->chlim;
+			hlim = nh->nh_ifp->if_inet6->nd_curhoplimit;
 			return (hlim);
 		}
 	}
diff --git a/sys/netinet6/in6_var.h b/sys/netinet6/in6_var.h
index f22b75ce71db..514030ed594d 100644
--- a/sys/netinet6/in6_var.h
+++ b/sys/netinet6/in6_var.h
@@ -495,15 +495,24 @@ struct in6_ifextra {
 	counter_u64_t in6_ifstat[sizeof(struct in6_ifstat) / sizeof(uint64_t)];
 	counter_u64_t icmp6_ifstat[sizeof(struct icmp6_ifstat) /
 				   sizeof(uint64_t)];
-	struct nd_ifinfo *nd_ifinfo;
+	/* ND6 */
+	uint32_t	nd_linkmtu;
+	uint32_t	nd_maxmtu;
+	uint32_t	nd_basereachable;
+	uint32_t	nd_reachable;
+	uint32_t	nd_retrans;
+	uint32_t	nd_flags;
+	int		nd_recalc_timer;
+	u_int		nd_dad_failures;
+	uint8_t		nd_curhoplimit;
+
 	struct scope6_id *scope6_id;
 	struct lltable *lltable;
 	struct mld_ifsoftc *mld_ifinfo;
-	u_int dad_failures;	/* DAD failures when using RFC 7217 stable addresses */
 };
 
 #define	LLTABLE6(ifp)	((ifp)->if_inet6->lltable)
-#define	DAD_FAILURES(ifp)	((ifp)->if_inet6->dad_failures)
+#define	DAD_FAILURES(ifp)	((ifp)->if_inet6->nd_dad_failures)
 
 VNET_DECLARE(struct in6_ifaddrhead, in6_ifaddrhead);
 VNET_DECLARE(struct in6_ifaddrlisthead *, in6_ifaddrhashtbl);
diff --git a/sys/netinet6/ip6_input.c b/sys/netinet6/ip6_input.c
index 29fa4741a509..62e03a189650 100644
--- a/sys/netinet6/ip6_input.c
+++ b/sys/netinet6/ip6_input.c
@@ -551,7 +551,7 @@ ip6_input(struct mbuf *m)
 	 * Drop the packet if IPv6 operation is disabled on the interface.
 	 */
 	rcvif = m->m_pkthdr.rcvif;
-	if ((ND_IFINFO(rcvif)->flags & ND6_IFF_IFDISABLED))
+	if ((rcvif->if_inet6->nd_flags & ND6_IFF_IFDISABLED))
 		goto bad;
 
 #if defined(IPSEC) || defined(IPSEC_SUPPORT)
diff --git a/sys/netinet6/ip6_output.c b/sys/netinet6/ip6_output.c
index 25fe8f347e35..a9b31ac8061f 100644
--- a/sys/netinet6/ip6_output.c
+++ b/sys/netinet6/ip6_output.c
@@ -2922,7 +2922,7 @@ ip6_setpktopt(int optname, u_char *buf, int len, struct ip6_pktopts *opt,
 				return (ENXIO);
 		}
 		if (ifp != NULL && (ifp->if_inet6 == NULL ||
-		    (ND_IFINFO(ifp)->flags & ND6_IFF_IFDISABLED) != 0))
+		    (ifp->if_inet6->nd_flags & ND6_IFF_IFDISABLED) != 0))
 			return (ENETDOWN);
 
 		if (ifp != NULL &&
diff --git a/sys/netinet6/nd6.c b/sys/netinet6/nd6.c
index 2e59f96a95c3..969b32032a60 100644
--- a/sys/netinet6/nd6.c
+++ b/sys/netinet6/nd6.c
@@ -88,8 +88,6 @@
 #define ND6_SLOWTIMER_INTERVAL (60 * 60) /* 1 hour */
 #define ND6_RECALC_REACHTM_INTERVAL (60 * 120) /* 2 hours */
 
-MALLOC_DEFINE(M_IP6NDP, "ip6ndp", "IPv6 Neighbor Discovery");
-
 VNET_DEFINE_STATIC(int, nd6_prune) = 1;
 #define	V_nd6_prune	VNET(nd6_prune)
 SYSCTL_INT(_net_inet6_icmp6, ICMPV6CTL_ND6_PRUNE, nd6_prune,
@@ -150,7 +148,6 @@ int	(*send_sendso_input_hook)(struct mbuf *, struct ifnet *, int, int);
 
 static bool nd6_is_new_addr_neighbor(const struct sockaddr_in6 *,
 	struct ifnet *);
-static void nd6_setmtu0(struct ifnet *, struct nd_ifinfo *);
 static void nd6_slowtimo(void *);
 static int regen_tmpaddr(struct in6_ifaddr *);
 static void nd6_free(struct llentry **, int);
@@ -277,24 +274,30 @@ nd6_destroy(void)
 }
 #endif
 
-struct nd_ifinfo *
+void
 nd6_ifattach(struct ifnet *ifp)
 {
-	struct nd_ifinfo *nd;
-
-	nd = malloc(sizeof(*nd), M_IP6NDP, M_WAITOK | M_ZERO);
-	nd->initialized = 1;
+	struct in6_ifextra *nd = ifp->if_inet6;
 
-	nd->chlim = IPV6_DEFHLIM;
-	nd->basereachable = REACHABLE_TIME;
-	nd->reachable = ND_COMPUTE_RTIME(nd->basereachable);
-	nd->retrans = RETRANS_TIMER;
+	nd->nd_linkmtu = 0;
+	nd->nd_maxmtu = ifp->if_mtu;
+	nd->nd_basereachable = REACHABLE_TIME;
+	nd->nd_reachable = ND_COMPUTE_RTIME(nd->nd_basereachable);
+	nd->nd_retrans = RETRANS_TIMER;
+	nd->nd_recalc_timer = 0;
+	nd->nd_dad_failures = 0;
+	nd->nd_curhoplimit = IPV6_DEFHLIM;
 
-	nd->flags = ND6_IFF_PERFORMNUD;
+	nd->nd_flags = ND6_IFF_PERFORMNUD;
 
 	/* Set IPv6 disabled on all interfaces but loopback by default. */
-	if ((ifp->if_flags & IFF_LOOPBACK) == 0)
-		nd->flags |= ND6_IFF_IFDISABLED;
+	if ((ifp->if_flags & IFF_LOOPBACK) == 0) {
+		nd->nd_flags |= ND6_IFF_IFDISABLED;
+		if (V_ip6_no_radr)
+			nd->nd_flags |= ND6_IFF_NO_RADR;
+		if (V_ip6_use_stableaddr)
+			nd->nd_flags |= ND6_IFF_STABLEADDR;
+	}
 
 	/* A loopback interface always has ND6_IFF_AUTO_LINKLOCAL.
 	 * XXXHRS: Clear ND6_IFF_AUTO_LINKLOCAL on an IFT_BRIDGE interface by
@@ -303,7 +306,7 @@ nd6_ifattach(struct ifnet *ifp)
 	 */
 	if ((V_ip6_auto_linklocal && ifp->if_type != IFT_BRIDGE &&
 	    ifp->if_type != IFT_WIREGUARD) || (ifp->if_flags & IFF_LOOPBACK))
-		nd->flags |= ND6_IFF_AUTO_LINKLOCAL;
+		nd->nd_flags |= ND6_IFF_AUTO_LINKLOCAL;
 	/*
 	 * A loopback interface does not need to accept RTADV.
 	 * XXXHRS: Clear ND6_IFF_ACCEPT_RTADV on an IFT_BRIDGE interface by
@@ -314,26 +317,14 @@ nd6_ifattach(struct ifnet *ifp)
 	if (V_ip6_accept_rtadv &&
 	    !(ifp->if_flags & IFF_LOOPBACK) &&
 	    (ifp->if_type != IFT_BRIDGE)) {
-			nd->flags |= ND6_IFF_ACCEPT_RTADV;
+			nd->nd_flags |= ND6_IFF_ACCEPT_RTADV;
 			/* If we globally accept rtadv, assume IPv6 on. */
-			nd->flags &= ~ND6_IFF_IFDISABLED;
+			nd->nd_flags &= ~ND6_IFF_IFDISABLED;
 	}
-	if (V_ip6_no_radr && !(ifp->if_flags & IFF_LOOPBACK))
-		nd->flags |= ND6_IFF_NO_RADR;
-
-	/* XXX: we cannot call nd6_setmtu since ifp is not fully initialized */
-	nd6_setmtu0(ifp, nd);
-
-	/* Configure default value for stable addresses algorithm, skip loopback interface */
-	if (V_ip6_use_stableaddr && !(ifp->if_flags & IFF_LOOPBACK)) {
-		nd->flags |= ND6_IFF_STABLEADDR;
-	}
-
-	return nd;
 }
 
 void
-nd6_ifdetach(struct ifnet *ifp, struct nd_ifinfo *nd)
+nd6_ifdetach(struct ifnet *ifp)
 {
 	struct epoch_tracker et;
 	struct ifaddr *ifa, *next;
@@ -347,32 +338,25 @@ nd6_ifdetach(struct ifnet *ifp, struct nd_ifinfo *nd)
 		nd6_dad_stop(ifa);
 	}
 	NET_EPOCH_EXIT(et);
-
-	free(nd, M_IP6NDP);
 }
 
 /*
  * Reset ND level link MTU. This function is called when the physical MTU
  * changes, which means we might have to adjust the ND level MTU.
+ * XXX todo: do not maintain copy of ifp->if_mtu in if_inet6->nd_maxmtu.
  */
 void
 nd6_setmtu(struct ifnet *ifp)
 {
-	/* XXXGL: ??? */
-	if (ifp->if_inet6 == NULL)
-		return;
-
-	nd6_setmtu0(ifp, ND_IFINFO(ifp));
-}
+	struct in6_ifextra *ndi = ifp->if_inet6;
+	uint32_t omaxmtu;
 
-/* XXX todo: do not maintain copy of ifp->if_mtu in ndi->maxmtu */
-void
-nd6_setmtu0(struct ifnet *ifp, struct nd_ifinfo *ndi)
-{
-	u_int32_t omaxmtu;
+	/* XXXGL: safety against IFT_PFSYNC & IFT_PFLOG */
+	if (ndi == NULL)
+		return;
 
-	omaxmtu = ndi->maxmtu;
-	ndi->maxmtu = ifp->if_mtu;
+	omaxmtu = ndi->nd_maxmtu;
+	ndi->nd_maxmtu = ifp->if_mtu;
 
 	/*
 	 * Decreasing the interface MTU under IPV6 minimum MTU may cause
@@ -380,10 +364,10 @@ nd6_setmtu0(struct ifnet *ifp, struct nd_ifinfo *ndi)
 	 * explicitly.  The check for omaxmtu is necessary to restrict the
 	 * log to the case of changing the MTU, not initializing it.
 	 */
-	if (omaxmtu >= IPV6_MMTU && ndi->maxmtu < IPV6_MMTU) {
-		log(LOG_NOTICE, "nd6_setmtu0: "
+	if (omaxmtu >= IPV6_MMTU && ndi->nd_maxmtu < IPV6_MMTU) {
+		log(LOG_NOTICE, "%s: "
 		    "new link MTU on %s (%lu) is too small for IPv6\n",
-		    if_name(ifp), (unsigned long)ndi->maxmtu);
+		    __func__, if_name(ifp), (unsigned long)ndi->nd_maxmtu);
 	}
 }
 
@@ -714,12 +698,12 @@ nd6_llinfo_setstate(struct llentry *lle, int newstate)
 	switch (newstate) {
 	case ND6_LLINFO_INCOMPLETE:
 		ifp = lle->lle_tbl->llt_ifp;
-		delay = (long)ND_IFINFO(ifp)->retrans * hz / 1000;
+		delay = (long)ifp->if_inet6->nd_retrans * hz / 1000;
 		break;
 	case ND6_LLINFO_REACHABLE:
 		if (!ND6_LLINFO_PERMANENT(lle)) {
 			ifp = lle->lle_tbl->llt_ifp;
-			delay = (long)ND_IFINFO(ifp)->reachable * hz;
+			delay = (long)ifp->if_inet6->nd_reachable * hz;
 		}
 		break;
 	case ND6_LLINFO_STALE:
@@ -756,7 +740,7 @@ nd6_llinfo_timer(void *arg)
 	struct llentry *ln;
 	struct in6_addr *dst, *pdst, *psrc, src;
 	struct ifnet *ifp;
-	struct nd_ifinfo *ndi;
+	struct in6_ifextra *ndi;
 	int do_switch, send_ns;
 	long delay;
 
@@ -790,7 +774,7 @@ nd6_llinfo_timer(void *arg)
 		return;
 	}
 	NET_EPOCH_ENTER(et);
-	ndi = ND_IFINFO(ifp);
+	ndi = ifp->if_inet6;
 	send_ns = 0;
 	dst = &ln->r_l3addr.addr6;
 	pdst = dst;
@@ -892,7 +876,7 @@ nd6_llinfo_timer(void *arg)
 		/* FALLTHROUGH */
 
 	case ND6_LLINFO_DELAY:
-		if (ndi && (ndi->flags & ND6_IFF_PERFORMNUD) != 0) {
+		if ((ndi->nd_flags & ND6_IFF_PERFORMNUD) != 0) {
 			/* We need NUD */
 			ln->la_asked = 1;
 			nd6_llinfo_setstate(ln, ND6_LLINFO_PROBE);
@@ -916,7 +900,8 @@ done:
 	if (ln != NULL)
 		ND6_RUNLOCK();
 	if (send_ns != 0) {
-		nd6_llinfo_settimer_locked(ln, (long)ndi->retrans * hz / 1000);
+		nd6_llinfo_settimer_locked(ln,
+		    (long)ndi->nd_retrans * hz / 1000);
 		psrc = nd6_llinfo_get_holdsrc(ln, &src);
 		LLE_FREE_LOCKED(ln);
 		ln = NULL;
@@ -1027,10 +1012,10 @@ nd6_timer(void *arg)
 			 * mark the address as tentative for future DAD.
 			 */
 			ifp = ia6->ia_ifp;
-			if ((ND_IFINFO(ifp)->flags & ND6_IFF_NO_DAD) == 0 &&
+			if ((ifp->if_inet6->nd_flags & ND6_IFF_NO_DAD) == 0 &&
 			    ((ifp->if_flags & IFF_UP) == 0 ||
 			    (ifp->if_drv_flags & IFF_DRV_RUNNING) == 0 ||
-			    (ND_IFINFO(ifp)->flags & ND6_IFF_IFDISABLED) != 0)){
+			    (ifp->if_inet6->nd_flags & ND6_IFF_IFDISABLED))){
 				ia6->ia6_flags &= ~IN6_IFF_DUPLICATED;
 				ia6->ia6_flags |= IN6_IFF_TENTATIVE;
 			}
@@ -1198,7 +1183,7 @@ nd6_purge(struct ifnet *ifp)
 	if (V_nd6_defifindex == ifp->if_index)
 		nd6_setdefaultiface(0);
 
-	if (ND_IFINFO(ifp)->flags & ND6_IFF_ACCEPT_RTADV) {
+	if (ifp->if_inet6->nd_flags & ND6_IFF_ACCEPT_RTADV) {
 		/* Refresh default router list. */
 		defrouter_select_fib(ifp->if_fib);
 	}
@@ -1324,7 +1309,7 @@ nd6_is_new_addr_neighbor(const struct sockaddr_in6 *addr, struct ifnet *ifp)
 	 * If the default router list is empty, all addresses are regarded
 	 * as on-link, and thus, as a neighbor.
 	 */
-	if (ND_IFINFO(ifp)->flags & ND6_IFF_ACCEPT_RTADV &&
+	if (ifp->if_inet6->nd_flags & ND6_IFF_ACCEPT_RTADV &&
 	    nd6_defrouter_list_empty() &&
 	    V_nd6_defifindex == ifp->if_index) {
 		return (1);
@@ -1448,7 +1433,7 @@ nd6_free(struct llentry **lnp, int gc)
 	KASSERT((ln->la_flags & LLE_CHILD) == 0, ("child lle"));
 
 	ifp = lltable_get_ifp(ln->lle_tbl);
-	if ((ND_IFINFO(ifp)->flags & ND6_IFF_ACCEPT_RTADV) != 0)
+	if ((ifp->if_inet6->nd_flags & ND6_IFF_ACCEPT_RTADV) != 0)
 		dr = defrouter_lookup_locked(&ln->r_l3addr.addr6, ifp);
 	else
 		dr = NULL;
@@ -1465,7 +1450,7 @@ nd6_free(struct llentry **lnp, int gc)
 	/* cancel timer */
 	nd6_llinfo_settimer_locked(ln, -1);
 
-	if (ND_IFINFO(ifp)->flags & ND6_IFF_ACCEPT_RTADV) {
+	if (ifp->if_inet6->nd_flags & ND6_IFF_ACCEPT_RTADV) {
 		if (dr != NULL && dr->expire &&
 		    ln->ln_state == ND6_LLINFO_STALE && gc) {
 			/*
@@ -1640,19 +1625,30 @@ nd6_subscription_cb(struct rib_head *rnh, struct rib_cmd_info *rc, void *arg)
 int
 nd6_ioctl(u_long cmd, caddr_t data, struct ifnet *ifp)
 {
+	struct epoch_tracker et;
 	struct in6_ndireq *ndi = (struct in6_ndireq *)data;
 	struct in6_nbrinfo *nbi = (struct in6_nbrinfo *)data;
 	struct in6_ndifreq *ndif = (struct in6_ndifreq *)data;
-	struct epoch_tracker et;
+	struct in6_ifextra *ext = ifp->if_inet6;
 	int error = 0;
 
-	/* XXXGL: ??? */
-	if (ifp->if_inet6 == NULL)
+	/* XXXGL: safety against IFT_PFSYNC & IFT_PFLOG */
+	if (ext == NULL)
 		return (EPFNOSUPPORT);
 #define ND	ndi->ndi
 	switch (cmd) {
 	case SIOCGIFINFO_IN6:
-		ND = *ND_IFINFO(ifp);
+		ND = (struct nd_ifinfo){
+			.linkmtu = ext->nd_linkmtu,
+			.maxmtu = ext->nd_maxmtu,
+			.basereachable = ext->nd_basereachable,
+			.reachable = ext->nd_reachable,
+			.retrans = ext->nd_retrans,
+			.flags = ext->nd_flags,
+			.recalctm = ext->nd_recalc_timer,
+			.chlim = ext->nd_curhoplimit,
+			.initialized = 1,
+		};
 		break;
 	case SIOCSIFINFO_IN6:
 		/*
@@ -1666,28 +1662,28 @@ nd6_ioctl(u_long cmd, caddr_t data, struct ifnet *ifp)
 				error = EINVAL;
 				break;
 			}
-			ND_IFINFO(ifp)->linkmtu = ND.linkmtu;
+			ext->nd_linkmtu = ND.linkmtu;
 		}
 
 		if (ND.basereachable != 0) {
-			int obasereachable = ND_IFINFO(ifp)->basereachable;
+			uint32_t obasereachable = ext->nd_basereachable;
 
-			ND_IFINFO(ifp)->basereachable = ND.basereachable;
+			ext->nd_basereachable = ND.basereachable;
 			if (ND.basereachable != obasereachable)
-				ND_IFINFO(ifp)->reachable =
+				ext->nd_reachable =
 				    ND_COMPUTE_RTIME(ND.basereachable);
 		}
 		if (ND.retrans != 0)
-			ND_IFINFO(ifp)->retrans = ND.retrans;
+			ext->nd_retrans = ND.retrans;
 		if (ND.chlim != 0)
-			ND_IFINFO(ifp)->chlim = ND.chlim;
+			ext->nd_curhoplimit = ND.chlim;
 		/* FALLTHROUGH */
 	case SIOCSIFINFO_FLAGS:
 	{
 		struct ifaddr *ifa;
 		struct in6_ifaddr *ia;
 
-		if ((ND_IFINFO(ifp)->flags & ND6_IFF_IFDISABLED) &&
+		if ((ext->nd_flags & ND6_IFF_IFDISABLED) &&
 		    !(ND.flags & ND6_IFF_IFDISABLED)) {
 			/* ifdisabled 1->0 transision */
 
@@ -1715,18 +1711,18 @@ nd6_ioctl(u_long cmd, caddr_t data, struct ifnet *ifp)
 				    " with a link-local address marked"
 				    " duplicate.\n");
 			} else {
-				ND_IFINFO(ifp)->flags &= ~ND6_IFF_IFDISABLED;
+				ext->nd_flags &= ~ND6_IFF_IFDISABLED;
 				if (ifp->if_flags & IFF_UP)
 					in6_if_up(ifp);
 			}
-		} else if (!(ND_IFINFO(ifp)->flags & ND6_IFF_IFDISABLED) &&
+		} else if (!(ext->nd_flags & ND6_IFF_IFDISABLED) &&
 			    (ND.flags & ND6_IFF_IFDISABLED)) {
 			/* ifdisabled 0->1 transision */
 			/* Mark all IPv6 address as tentative. */
 
-			ND_IFINFO(ifp)->flags |= ND6_IFF_IFDISABLED;
+			ext->nd_flags |= ND6_IFF_IFDISABLED;
 			if (V_ip6_dad_count > 0 &&
-			    (ND_IFINFO(ifp)->flags & ND6_IFF_NO_DAD) == 0) {
+			    (ext->nd_flags & ND6_IFF_NO_DAD) == 0) {
 				NET_EPOCH_ENTER(et);
 				CK_STAILQ_FOREACH(ifa, &ifp->if_addrhead,
 				    ifa_link) {
@@ -1741,11 +1737,11 @@ nd6_ioctl(u_long cmd, caddr_t data, struct ifnet *ifp)
 		}
 
 		if (ND.flags & ND6_IFF_AUTO_LINKLOCAL) {
-			if (!(ND_IFINFO(ifp)->flags & ND6_IFF_AUTO_LINKLOCAL)) {
+			if (!(ext->nd_flags & ND6_IFF_AUTO_LINKLOCAL)) {
 				/* auto_linklocal 0->1 transision */
 
 				/* If no link-local address on ifp, configure */
-				ND_IFINFO(ifp)->flags |= ND6_IFF_AUTO_LINKLOCAL;
+				ext->nd_flags |= ND6_IFF_AUTO_LINKLOCAL;
 				in6_ifattach(ifp, NULL);
 			} else if (!(ND.flags & ND6_IFF_IFDISABLED) &&
 			    ifp->if_flags & IFF_UP) {
@@ -1771,7 +1767,7 @@ nd6_ioctl(u_long cmd, caddr_t data, struct ifnet *ifp)
 					in6_ifattach(ifp, NULL);
 			}
 		}
-		ND_IFINFO(ifp)->flags = ND.flags;
+		ext->nd_flags = ND.flags;
 		break;
 	}
 #undef ND
@@ -2108,7 +2104,7 @@ nd6_cache_lladdr(struct ifnet *ifp, struct in6_addr *from, char *lladdr,
 	 * cases for safety.
 	 */
 	if ((do_update || is_newentry) && router &&
-	    ND_IFINFO(ifp)->flags & ND6_IFF_ACCEPT_RTADV) {
+	    ifp->if_inet6->nd_flags & ND6_IFF_ACCEPT_RTADV) {
 		/*
 		 * guaranteed recursion
 		 */
@@ -2121,26 +2117,26 @@ nd6_slowtimo(void *arg)
 {
 	struct epoch_tracker et;
 	CURVNET_SET((struct vnet *) arg);
-	struct nd_ifinfo *nd6if;
+	struct in6_ifextra *nd6if;
 	struct ifnet *ifp;
 
 	callout_reset(&V_nd6_slowtimo_ch, ND6_SLOWTIMER_INTERVAL * hz,
 	    nd6_slowtimo, curvnet);
 	NET_EPOCH_ENTER(et);
 	CK_STAILQ_FOREACH(ifp, &V_ifnet, if_link) {
-		if (ifp->if_inet6 == NULL)
+		if ((nd6if = ifp->if_inet6) == NULL)
 			continue;
-		nd6if = ND_IFINFO(ifp);
-		if (nd6if->basereachable && /* already initialized */
-		    (nd6if->recalctm -= ND6_SLOWTIMER_INTERVAL) <= 0) {
+		if (nd6if->nd_basereachable && /* already initialized */
+		    (nd6if->nd_recalc_timer -= ND6_SLOWTIMER_INTERVAL) <= 0) {
 			/*
 			 * Since reachable time rarely changes by router
 			 * advertisements, we SHOULD insure that a new random
 			 * value gets recomputed at least once every few hours.
 			 * (RFC 2461, 6.3.4)
 			 */
-			nd6if->recalctm = V_nd6_recalc_reachtm_interval;
-			nd6if->reachable = ND_COMPUTE_RTIME(nd6if->basereachable);
+			nd6if->nd_recalc_timer = V_nd6_recalc_reachtm_interval;
+			nd6if->nd_reachable =
+			    ND_COMPUTE_RTIME(nd6if->nd_basereachable);
 		}
 	}
 	NET_EPOCH_EXIT(et);
@@ -2248,7 +2244,7 @@ nd6_resolve(struct ifnet *ifp, int gw_flags, struct mbuf *m,
 	dst6 = (const struct sockaddr_in6 *)sa_dst;
 
 	/* discard the packet if IPv6 operation is disabled on the interface */
-	if ((ND_IFINFO(ifp)->flags & ND6_IFF_IFDISABLED)) {
+	if ((ifp->if_inet6->nd_flags & ND6_IFF_IFDISABLED)) {
 		m_freem(m);
 		return (ENETDOWN); /* better error? */
 	}
diff --git a/sys/netinet6/nd6.h b/sys/netinet6/nd6.h
index c500f236d6ff..a22a0e24735b 100644
--- a/sys/netinet6/nd6.h
+++ b/sys/netinet6/nd6.h
@@ -81,10 +81,6 @@ struct llentry;
 #define	ND6_IFF_IPV6_ONLY_MASK	(ND6_IFF_IPV6_ONLY|ND6_IFF_IPV6_ONLY_MANUAL)
 #endif
 
-#ifdef _KERNEL
-#define ND_IFINFO(ifp)	((if_getinet6(ifp))->nd_ifinfo)
-#endif
-
 struct in6_nbrinfo {
 	char ifname[IFNAMSIZ];	/* if name, e.g. "en0" */
 	struct in6_addr addr;	/* IPv6 address of the neighbor */
@@ -235,10 +231,6 @@ struct nd_pfxrouter {
 	struct nd_defrouter *router;
 };
 
-#ifdef MALLOC_DECLARE
-MALLOC_DECLARE(M_IP6NDP);
-#endif
-
 /* nd6.c */
 VNET_DECLARE(int, nd6_mmaxtries);
 VNET_DECLARE(struct nd_prhead, nd_prefix);
@@ -331,8 +323,8 @@ void nd6_init(void);
 #ifdef VIMAGE
 void nd6_destroy(void);
 #endif
-struct nd_ifinfo *nd6_ifattach(struct ifnet *);
-void nd6_ifdetach(struct ifnet *, struct nd_ifinfo *);
+void nd6_ifattach(struct ifnet *);
+void nd6_ifdetach(struct ifnet *);
 int nd6_is_addr_neighbor(const struct sockaddr_in6 *, struct ifnet *);
 void nd6_option_init(void *, int, union nd_opts *);
 struct nd_opt_hdr *nd6_option(union nd_opts *);
diff --git a/sys/netinet6/nd6_nbr.c b/sys/netinet6/nd6_nbr.c
index aa7cb3b41973..4da62575eaac 100644
--- a/sys/netinet6/nd6_nbr.c
+++ b/sys/netinet6/nd6_nbr.c
@@ -80,6 +80,8 @@
 
 #define SDL(s) ((struct sockaddr_dl *)s)
 
+MALLOC_DECLARE(M_IP6NDP);
+
 struct dadq;
 static struct dadq *nd6_dad_find(struct ifaddr *, struct nd_opt_nonce *);
 static void nd6_dad_add(struct dadq *dp);
@@ -173,7 +175,7 @@ nd6_ns_input(struct mbuf *m, int off, int icmp6len)
 		goto bad;
 
 	rflag = (V_ip6_forwarding) ? ND_NA_FLAG_ROUTER : 0;
-	if (ND_IFINFO(ifp)->flags & ND6_IFF_ACCEPT_RTADV && V_ip6_norbit_raif)
+	if (ifp->if_inet6->nd_flags & ND6_IFF_ACCEPT_RTADV && V_ip6_norbit_raif)
 		rflag = 0;
 
 	if (IN6_IS_ADDR_UNSPECIFIED(&saddr6)) {
@@ -910,7 +912,7 @@ nd6_na_input(struct mbuf *m, int off, int icmp6len)
 
 			nd6_ifp = lltable_get_ifp(ln->lle_tbl);
 			if (!defrouter_remove(&ln->r_l3addr.addr6, nd6_ifp) &&
-			    (ND_IFINFO(nd6_ifp)->flags &
+			    (nd6_ifp->if_inet6->nd_flags &
 			     ND6_IFF_ACCEPT_RTADV) != 0)
 				/*
 				 * Even if the neighbor is not in the default
@@ -1281,13 +1283,13 @@ nd6_dad_start(struct ifaddr *ifa, int delay)
 	 */
 	if ((ia->ia6_flags & IN6_IFF_ANYCAST) != 0 ||
 	    V_ip6_dad_count == 0 ||
-	    (ND_IFINFO(ifa->ifa_ifp)->flags & ND6_IFF_NO_DAD) != 0) {
+	    (ifa->ifa_ifp->if_inet6->nd_flags & ND6_IFF_NO_DAD) != 0) {
 		ia->ia6_flags &= ~IN6_IFF_TENTATIVE;
 		return;
 	}
 	if ((ifa->ifa_ifp->if_flags & IFF_UP) == 0 ||
 	    (ifa->ifa_ifp->if_drv_flags & IFF_DRV_RUNNING) == 0 ||
-	    (ND_IFINFO(ifa->ifa_ifp)->flags & ND6_IFF_IFDISABLED) != 0)
+	    (ifa->ifa_ifp->if_inet6->nd_flags & ND6_IFF_IFDISABLED) != 0)
 		return;
 
 	DADQ_WLOCK();
@@ -1377,7 +1379,7 @@ nd6_dad_timer(void *arg)
 	KASSERT(ia != NULL, ("DAD entry %p with no address", dp));
 
 	NET_EPOCH_ENTER(et);
-	if (ND_IFINFO(ifp)->flags & ND6_IFF_IFDISABLED) {
+	if (ifp->if_inet6->nd_flags & ND6_IFF_IFDISABLED) {
 		/* Do not need DAD for ifdisabled interface. */
 		log(LOG_ERR, "nd6_dad_timer: cancel DAD on %s because of "
 		    "ND6_IFF_IFDISABLED.\n", ifp->if_xname);
@@ -1414,7 +1416,7 @@ nd6_dad_timer(void *arg)
 		 * We have more NS to go.  Send NS packet for DAD.
 		 */
 		nd6_dad_starttimer(dp,
-		    (long)ND_IFINFO(ifa->ifa_ifp)->retrans * hz / 1000);
+		    (long)ifa->ifa_ifp->if_inet6->nd_retrans * hz / 1000);
 		nd6_dad_ns_output(dp);
 		goto done;
 	} else {
@@ -1446,7 +1448,7 @@ nd6_dad_timer(void *arg)
 			dp->dad_count =
 			    dp->dad_ns_ocount + V_nd6_mmaxtries - 1;
 			nd6_dad_starttimer(dp,
-			    (long)ND_IFINFO(ifa->ifa_ifp)->retrans * hz / 1000);
+			    (long)ifa->ifa_ifp->if_inet6->nd_retrans * hz / 1000);
 			nd6_dad_ns_output(dp);
 			goto done;
 		} else {
@@ -1458,9 +1460,9 @@ nd6_dad_timer(void *arg)
 			 *
 			 * Reset DAD failures counter if using stable addresses.
 			 */
-			if ((ND_IFINFO(ifp)->flags & ND6_IFF_IFDISABLED) == 0) {
+			if ((ifp->if_inet6->nd_flags & ND6_IFF_IFDISABLED) == 0) {
 				ia->ia6_flags &= ~IN6_IFF_TENTATIVE;
-				if ((ND_IFINFO(ifp)->flags & ND6_IFF_STABLEADDR) && !(ia->ia6_flags & IN6_IFF_TEMPORARY))
+				if ((ifp->if_inet6->nd_flags & ND6_IFF_STABLEADDR) && !(ia->ia6_flags & IN6_IFF_TEMPORARY))
 					atomic_store_int(&DAD_FAILURES(ifp), 0);
 			}
 
@@ -1509,7 +1511,7 @@ nd6_dad_duplicated(struct ifaddr *ifa, struct dadq *dp)
 	 * For RFC 7217 stable addresses, increment failure counter here if we still have retries.
 	 * More addresses will be generated as long as retries are not exhausted.
 	 */
-	if ((ND_IFINFO(ifp)->flags & ND6_IFF_STABLEADDR) && !(ia->ia6_flags & IN6_IFF_TEMPORARY)) {
+	if ((ifp->if_inet6->nd_flags & ND6_IFF_STABLEADDR) && !(ia->ia6_flags & IN6_IFF_TEMPORARY)) {
 		u_int dad_failures = atomic_load_int(&DAD_FAILURES(ifp));
 
 		if (dad_failures <= V_ip6_stableaddr_maxretries) {
@@ -1547,7 +1549,7 @@ nd6_dad_duplicated(struct ifaddr *ifa, struct dadq *dp)
 			in6 = ia->ia_addr.sin6_addr;
 			if (in6_get_hw_ifid(ifp, &in6) == 0 &&
 			    IN6_ARE_ADDR_EQUAL(&ia->ia_addr.sin6_addr, &in6)) {
-				ND_IFINFO(ifp)->flags |= ND6_IFF_IFDISABLED;
+				ifp->if_inet6->nd_flags |= ND6_IFF_IFDISABLED;
 				log(LOG_ERR, "%s: possible hardware address "
 				    "duplication detected, disable IPv6\n",
 				    if_name(ifp));
diff --git a/sys/netinet6/nd6_rtr.c b/sys/netinet6/nd6_rtr.c
index 821d6b27bc43..0ca97125110c 100644
--- a/sys/netinet6/nd6_rtr.c
+++ b/sys/netinet6/nd6_rtr.c
@@ -75,6 +75,8 @@
 
 #include <machine/atomic.h>
 
+MALLOC_DEFINE(M_IP6NDP, "ip6ndp", "IPv6 Neighbor Discovery");
+
 static struct nd_defrouter *defrtrlist_update(struct nd_defrouter *);
 static int prelist_update(struct nd_prefixctl *, struct nd_defrouter *,
     struct mbuf *, int);
@@ -175,7 +177,7 @@ nd6_rs_input(struct mbuf *m, int off, int icmp6len)
 	 * Accept RS only when V_ip6_forwarding=1 and the interface has
 	 * no ND6_IFF_ACCEPT_RTADV.
 	 */
-	if (!V_ip6_forwarding || ND_IFINFO(ifp)->flags & ND6_IFF_ACCEPT_RTADV)
+	if (!V_ip6_forwarding || ifp->if_inet6->nd_flags & ND6_IFF_ACCEPT_RTADV)
 		goto freeit;
 
 	/* RFC 6980: Nodes MUST silently ignore fragments */   
@@ -280,7 +282,7 @@ defrtr_ipv6_only_ifp(struct ifnet *ifp)
 	ND6_RUNLOCK();
 
 	IF_ADDR_WLOCK(ifp);
-	ipv6_only_old = ND_IFINFO(ifp)->flags & ND6_IFF_IPV6_ONLY;
+	ipv6_only_old = ifp->if_inet6->nd_flags & ND6_IFF_IPV6_ONLY;
 	IF_ADDR_WUNLOCK(ifp);
 
 	/* If nothing changed, we have an early exit. */
@@ -317,9 +319,9 @@ defrtr_ipv6_only_ifp(struct ifnet *ifp)
 
 	IF_ADDR_WLOCK(ifp);
 	if (ipv6_only)
-		ND_IFINFO(ifp)->flags |= ND6_IFF_IPV6_ONLY;
+		ifp->if_inet6->nd_flags |= ND6_IFF_IPV6_ONLY;
 	else
-		ND_IFINFO(ifp)->flags &= ~ND6_IFF_IPV6_ONLY;
+		ifp->if_inet6->nd_flags &= ~ND6_IFF_IPV6_ONLY;
 	IF_ADDR_WUNLOCK(ifp);
 
 #ifdef notyet
@@ -332,7 +334,7 @@ defrtr_ipv6_only_ipf_down(struct ifnet *ifp)
 {
 
 	IF_ADDR_WLOCK(ifp);
-	ND_IFINFO(ifp)->flags &= ~ND6_IFF_IPV6_ONLY;
+	ifp->if_inet6->nd_flags &= ~ND6_IFF_IPV6_ONLY;
 	IF_ADDR_WUNLOCK(ifp);
 }
 #endif	/* EXPERIMENTAL */
@@ -364,7 +366,7 @@ void
 nd6_ra_input(struct mbuf *m, int off, int icmp6len)
 {
 	struct ifnet *ifp;
-	struct nd_ifinfo *ndi;
+	struct in6_ifextra *ndi;
 	struct ip6_hdr *ip6;
 	struct nd_router_advert *nd_ra;
 	struct in6_addr saddr6;
@@ -378,8 +380,8 @@ nd6_ra_input(struct mbuf *m, int off, int icmp6len)
 	 * ND6_IFF_ACCEPT_RTADV is on the receiving interface.
 	 */
 	ifp = m->m_pkthdr.rcvif;
-	ndi = ND_IFINFO(ifp);
-	if (!(ndi->flags & ND6_IFF_ACCEPT_RTADV))
+	ndi = ifp->if_inet6;
+	if (!(ndi->nd_flags & ND6_IFF_ACCEPT_RTADV))
 		goto freeit;
 
 	/* RFC 6980: Nodes MUST silently ignore fragments */
@@ -441,7 +443,7 @@ nd6_ra_input(struct mbuf *m, int off, int icmp6len)
 	 * ND6_IFF_NO_RADR enabled on the receiving interface or
 	 * (ip6.forwarding == 1 && ip6.rfc6204w3 != 1).
 	 */
-	if (ndi->flags & ND6_IFF_NO_RADR)
+	if (ndi->nd_flags & ND6_IFF_NO_RADR)
 		dr0.rtlifetime = 0;
*** 88 LINES SKIPPED ***