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