tech-net archive

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index][Old Index]

RTM_NEWNEIGH



dhcpcd polls SIOCGNBRINFO_IN6 every second for every IPv6 router it knows about to test neighbour reach-ability.
This isn't exactly optimal, hello battery drain.

Attached is a patch to add RTM_NEWNEIGH so that userland can react to Neighbour Discovery changes, similar to the Linux equivalent. It's designed to be protocol agnostic, (ie could be used for ARP as well). Currently, it only raises RTM_NEWNEIGH on IPv6 neighbour state and flag (is it a router?) changes. There is little point in generating RTM_DELNEIGH or RTM_GETNEIGH as Linux does because our current
implementation sends equivalent messages via RTM_DELETE or RTM_CHANGE.

I have a patch for dhcpcd to use this (attached as well, needs my latest fossil trunk, -current is too old). The end result is that on NetBSD there are no longer any polls for state - the kernel notifies every event change. The only elephants left in the room are drivers that don't set IFF_UP before LINK_STATE_UPi (hi sk(4)) and drivers which could set link state but don't bother to (hi ppp(4)).

Comments, as always, are welcome.

Roy
Index: sys/net/route.h
===================================================================
RCS file: /cvsroot/src/sys/net/route.h,v
retrieving revision 1.84
diff -u -p -r1.84 route.h
--- sys/net/route.h	6 Jun 2014 01:02:47 -0000	1.84
+++ sys/net/route.h	11 Dec 2014 02:43:56 -0000
@@ -201,6 +201,19 @@ struct rt_msghdr {
 				/* metrics themselves */
 };
 
+/* Neighbour Discovery Message */
+struct nd_msghdr {
+	u_short	ndm_msglen __align64;
+				/* to skip over non-understood messages */
+	u_char	ndm_version;	/* future binary compatibility */
+	u_char	ndm_type;	/* message type */
+	int	ndm_addrs;	/* bitmask identifying sockaddrs in msg */
+	u_short	ndm_index;	/* index for associated ifp */
+	int	ndm_state;	/* protocol bitmask for neighbour state */
+	int	ndm_flags;	/* flags, e.g. NDF_ROUTER */
+#define NDF_ROUTER	0x01	/* neighbour is a router */
+};
+
 #undef __align64
 
 #define RTM_VERSION	4	/* Up the ante and ignore older versions */
@@ -230,6 +243,7 @@ struct rt_msghdr {
 				 */
 #define RTM_IFINFO	0x14	/* iface/link going up/down etc. */
 #define RTM_CHGADDR	0x15	/* address properties changed */
+#define RTM_NEWNEIGH	0x16	/* New or updated neighbour entry */
 
 #define RTV_MTU		0x1	/* init or lock _mtu */
 #define RTV_HOPCOUNT	0x2	/* init or lock _hopcount */
@@ -366,6 +380,7 @@ void	 rt_ifmsg(struct ifnet *);
 void	 rt_missmsg(int, const struct rt_addrinfo *, int, int);
 struct mbuf *rt_msg1(int, struct rt_addrinfo *, void *, int);
 void	 rt_newaddrmsg(int, struct ifaddr *, int, struct rtentry *);
+void	 rt_neighmsg(int, const struct rtentry *, int, int);
 
 void	 rt_maskedcopy(const struct sockaddr *,
 	    struct sockaddr *, const struct sockaddr *);
Index: sys/net/rtsock.c
===================================================================
RCS file: /cvsroot/src/sys/net/rtsock.c,v
retrieving revision 1.164
diff -u -p -r1.164 rtsock.c
--- sys/net/rtsock.c	5 Sep 2014 06:00:05 -0000	1.164
+++ sys/net/rtsock.c	11 Dec 2014 02:43:56 -0000
@@ -846,7 +846,8 @@ rt_getlen(int type)
 	case RTM_IFANNOUNCE:
 	case RTM_IEEE80211:
 		return sizeof(struct if_xannouncemsghdr);
-
+	case RTM_NEWNEIGH:
+		return sizeof(struct nd_msghdr);
 	default:
 		return sizeof(struct rt_xmsghdr);
 	}
@@ -1227,6 +1228,34 @@ COMPATNAME(rt_ieee80211msg)(struct ifnet
 	COMPATNAME(route_enqueue)(m, 0);
 }
 
+#if RTM_XVERSION > 3
+void
+rt_neighmsg(int cmd, const struct rtentry *rt, int state, int flags)
+{
+	struct rt_addrinfo info;
+	const struct sockaddr *sa;
+	struct nd_msghdr ndm;
+	struct mbuf *m;
+
+	KASSERT(rt != NULL);
+	memset(&info, 0, sizeof(info));
+	info.rti_info[RTAX_DST] = sa = rt_getkey(rt);
+	info.rti_info[RTAX_GATEWAY] = rt->rt_gateway;
+
+	memset(&ndm, 0, sizeof(ndm));
+	ndm.ndm_type = cmd;
+	ndm.ndm_index = rt->rt_ifp->if_index;
+	ndm.ndm_state = state;
+	ndm.ndm_flags = flags;
+
+	m = rt_msg1(cmd, &info, &ndm, sizeof(ndm));
+	if (m == NULL)
+		return;
+	mtod(m, struct nd_msghdr *)->ndm_addrs = info.rti_addrs;
+	route_enqueue(m, sa->sa_family);
+}
+#endif
+
 /*
  * This is used in dumping the kernel table via sysctl().
  */
Index: sys/netinet6/nd6.c
===================================================================
RCS file: /cvsroot/src/sys/netinet6/nd6.c,v
retrieving revision 1.154
diff -u -p -r1.154 nd6.c
--- sys/netinet6/nd6.c	18 Oct 2014 08:33:29 -0000	1.154
+++ sys/netinet6/nd6.c	11 Dec 2014 02:43:58 -0000
@@ -436,6 +436,7 @@ nd6_llinfo_timer(void *arg)
 	const struct sockaddr_in6 *dst;
 	struct ifnet *ifp;
 	struct nd_ifinfo *ndi = NULL;
+	int oldstate;
 
 	mutex_enter(softnet_lock);
 	KERNEL_LOCK(1, NULL);
@@ -463,6 +464,7 @@ nd6_llinfo_timer(void *arg)
 	if (!dst)
 		panic("dst=0 in nd6_timer(ln=%p)", ln);
 
+	oldstate = ln->ln_state;
 	switch (ln->ln_state) {
 	case ND6_LLINFO_INCOMPLETE:
 		if (ln->ln_asked < nd6_mmaxtries) {
@@ -532,6 +534,11 @@ nd6_llinfo_timer(void *arg)
 		break;
 	}
 
+	/* Announce the state change */
+	if (ln->ln_state != oldstate)
+		rt_neighmsg(RTM_NEWNEIGH, rt,
+		    ln->ln_state, ln->ln_router ? NDF_ROUTER : 0);
+
 	KERNEL_UNLOCK_ONE(NULL);
 	mutex_exit(softnet_lock);
 }
@@ -1870,6 +1877,7 @@ nd6_cache_lladdr(
 	int olladdr;
 	int llchange;
 	int newstate = 0;
+	int oldrouter;
 
 	if (ifp == NULL)
 		panic("ifp == NULL in nd6_cache_lladdr");
@@ -2023,6 +2031,7 @@ fail:
 	 *
 	 *					(c=clear s=set)
 	 */
+	oldrouter = ln->ln_router;
 	switch (type & 0xff) {
 	case ND_NEIGHBOR_SOLICIT:
 		/*
@@ -2059,6 +2068,11 @@ fail:
 		break;
 	}
 
+	/* Announce the new entry */
+	if (do_update || ln->ln_router != oldrouter)
+		rt_neighmsg(RTM_NEWNEIGH, rt,
+		    ln->ln_state, ln->ln_router ? NDF_ROUTER : 0);
+
 	/*
 	 * When the link-layer address of a router changes, select the
 	 * best router again.  In particular, when the neighbor entry is newly
Index: sbin/route/route.c
===================================================================
RCS file: /cvsroot/src/sbin/route/route.c,v
retrieving revision 1.147
diff -u -p -r1.147 route.c
--- sbin/route/route.c	12 Nov 2014 03:34:08 -0000	1.147
+++ sbin/route/route.c	11 Dec 2014 02:43:59 -0000
@@ -1283,6 +1283,7 @@ const char * const msgtypes[] = {
 	[RTM_IEEE80211] = "RTM_IEEE80211: IEEE80211 wireless event",
 	[RTM_IFINFO] = "RTM_IFINFO: iface status change",
 	[RTM_CHGADDR] = "RTM_CHGADDR: address being changed on iface",
+	[RTM_NEWNEIGH] = "RTM_NEWNEIGH: neighbour address added",
 };
 
 const char metricnames[] =
@@ -1328,6 +1329,7 @@ print_rtmsg(struct rt_msghdr *rtm, int m
 		struct ieee80211_replay_event replay;
 		struct ieee80211_michael_event michael;
 	} ev;
+	struct nd_msghdr *ndm;
 	size_t evlen = 0;
 
 	if (verbose == 0)
@@ -1446,6 +1448,12 @@ print_rtmsg(struct rt_msghdr *rtm, int m
 		}
 		printf("\n");
 		break;
+	case RTM_NEWNEIGH:
+		ndm = (struct nd_msghdr *)rtm;
+		(void)printf("if# %d, flags %d, state %d",
+		    ndm->ndm_index, ndm->ndm_flags, ndm->ndm_state);
+		pmsg_addrs((char *)(ndm + 1), ndm->ndm_addrs);
+		break;
 	default:
 		(void)printf("pid %d, seq %d, errno %d, flags: ",
 			rtm->rtm_pid, rtm->rtm_seq, rtm->rtm_errno);
Index: if-bsd.c
==================================================================
--- if-bsd.c
+++ if-bsd.c
@@ -808,10 +808,21 @@
 	struct rt6 rt6;
 	struct in6_addr ia6;
 	struct sockaddr_in6 *sin6;
 	int ifa_flags;
 #endif
+#ifdef RTM_NEWNEIGH
+	struct nd_msghdr *ndm;
+	int nd_flags;
+#endif
+#ifdef __KAME__
+#define DESCOPE(ia6)							\
+	if (IN6_IS_ADDR_LINKLOCAL(ia6))					\
+		(ia6)->s6_addr[2] = (ia6)->s6_addr[3] = '\0';
+#else
+#define DESCOPE(ia6)
+#endif
 
 	bytes = read(ctx->link_fd, msg, sizeof(msg));
 	if (bytes == -1)
 		return -1;
 	if (bytes == 0)
@@ -933,14 +944,11 @@
 #ifdef INET6
 			case AF_INET6:
 				sin6 = (struct sockaddr_in6*)(void *)
 				    rti_info[RTAX_IFA];
 				ia6 = sin6->sin6_addr;
-#ifdef __KAME__
-				if (IN6_IS_ADDR_LINKLOCAL(&ia6))
-					ia6.s6_addr[2] = ia6.s6_addr[3] = '\0';
-#endif
+				DESCOPE(&ia6);
 				if (rtm->rtm_type == RTM_NEWADDR) {
 					ifa_flags = if_addrflags6(&ia6, ifp);
 					if (ifa_flags == -1)
 						break;
 				} else
@@ -949,10 +957,41 @@
 				    ifp->name, &ia6, ifa_flags);
 				break;
 #endif
 			}
 			break;
+#ifdef RTM_NEWNEIGH
+		case RTM_NEWNEIGH:
+#ifdef RTM_DELNEIGH
+		case RTM_DELNEIGH:
+#endif
+			ndm = (struct nd_msghdr *)(void *)p;
+			if ((ifp = if_findindex(ctx, ndm->ndm_index)) == NULL)
+				break;
+			cp = (char *)(void *)(ndm + 1);
+			get_addrs(ndm->ndm_addrs, cp, rti_info);
+			if (rti_info[RTAX_DST] == NULL)
+				break;
+			switch (rti_info[RTAX_DST]->sa_family) {
+#ifdef INET6
+			case AF_INET6:
+				sin6 = (struct sockaddr_in6*)(void *)
+				    rti_info[RTAX_DST];
+				ia6 = sin6->sin6_addr;
+				DESCOPE(&ia6);
+				if (ndm->ndm_flags & NDF_ROUTER)
+					nd_flags = IPV6ND_ROUTER;
+				else
+					nd_flags = 0;
+				if (ndm->ndm_type == RTM_NEWNEIGH &&
+				    ndm->ndm_state > ND6_LLINFO_INCOMPLETE)
+					nd_flags |= IPV6ND_REACHABLE;
+				ipv6nd_neighbour(ctx, &ia6, nd_flags);
+				break;
+			}
+#endif
+#endif
 		}
 	}
 
 	return 0;
 }
@@ -1036,10 +1075,11 @@
 eexit:
 	close(s);
 	return error;
 }
 
+#ifndef RTM_NEWNEIGH
 int
 if_nd6reachable(const char *ifname, struct in6_addr *addr)
 {
 	int s, flags;
 	struct in6_nbrinfo nbi;
@@ -1072,10 +1112,11 @@
 			flags |= IPV6ND_ROUTER;
 	}
 	close(s);
 	return flags;
 }
+#endif
 
 static int
 if_raflush(void)
 {
 	int s;



Home | Main Index | Thread Index | Old Index