Module Name:    src
Committed By:   martin
Date:           Fri Jul  7 13:57:27 UTC 2017

Modified Files:
        src/sbin/route [netbsd-8]: route.8 route.c rtutil.c
        src/sys/net [netbsd-8]: if_llatbl.c if_llatbl.h route.c route.h
            rtsock.c
        src/sys/netinet [netbsd-8]: if_arp.c in.c
        src/sys/netinet6 [netbsd-8]: in6.c nd6.c nd6.h
        src/tests/net [netbsd-8]: net_common.sh
        src/tests/net/arp [netbsd-8]: t_arp.sh
        src/tests/net/ndp [netbsd-8]: t_ndp.sh t_ra.sh
        src/tests/net/net [netbsd-8]: t_ipv6address.sh
        src/tests/net/route [netbsd-8]: t_flags.sh t_flags6.sh t_route.sh
        src/usr.sbin/arp [netbsd-8]: arp.c
        src/usr.sbin/ndp [netbsd-8]: ndp.c

Log Message:
Pull up following revision(s) (requested by ozaki-r in ticket #107):
        usr.sbin/arp/arp.c: revision 1.56
        sys/net/rtsock.c: revision 1.218
        sys/net/if_llatbl.c: revision 1.20
        usr.sbin/arp/arp.c: revision 1.57
        sys/net/rtsock.c: revision 1.219
        sys/net/if_llatbl.c: revision 1.21
        usr.sbin/arp/arp.c: revision 1.58
        tests/net/net_common.sh: revision 1.19
        sys/netinet6/nd6.h: revision 1.84
        sys/netinet6/nd6.h: revision 1.85
        tests/net/arp/t_arp.sh: revision 1.23
        sys/netinet6/in6.c: revision 1.246
        tests/net/arp/t_arp.sh: revision 1.24
        sys/netinet6/in6.c: revision 1.247
        tests/net/arp/t_arp.sh: revision 1.25
        sys/netinet6/in6.c: revision 1.248
        tests/net/arp/t_arp.sh: revision 1.26
        usr.sbin/ndp/ndp.c: revision 1.49
        tests/net/arp/t_arp.sh: revision 1.27
        tests/net/ndp/t_ndp.sh: revision 1.20
        tests/net/arp/t_arp.sh: revision 1.28
        tests/net/ndp/t_ndp.sh: revision 1.21
        tests/net/arp/t_arp.sh: revision 1.29
        tests/net/ndp/t_ndp.sh: revision 1.22
        tests/net/ndp/t_ndp.sh: revision 1.23
        tests/net/route/t_flags6.sh: revision 1.13
        tests/net/ndp/t_ndp.sh: revision 1.24
        tests/net/route/t_flags6.sh: revision 1.14
        tests/net/ndp/t_ndp.sh: revision 1.25
        tests/net/route/t_flags6.sh: revision 1.15
        tests/net/ndp/t_ndp.sh: revision 1.26
        sbin/route/rtutil.c: revision 1.9
        tests/net/ndp/t_ndp.sh: revision 1.27
        tests/net/ndp/t_ndp.sh: revision 1.28
        tests/net/net/t_ipv6address.sh: revision 1.14
        tests/net/ndp/t_ra.sh: revision 1.28
        tests/net/ndp/t_ndp.sh: revision 1.29
        sys/net/route.h: revision 1.113
        tests/net/ndp/t_ra.sh: revision 1.29
        sys/net/rtsock.c: revision 1.220
        sys/net/rtsock.c: revision 1.221
        sys/net/rtsock.c: revision 1.222
        sys/net/rtsock.c: revision 1.223
        tests/net/route/t_route.sh: revision 1.13
        sys/net/rtsock.c: revision 1.224
        sys/net/route.c: revision 1.196
        sys/net/if_llatbl.c: revision 1.19
        sys/net/route.c: revision 1.197
        sbin/route/route.c: revision 1.156
        tests/net/route/t_flags.sh: revision 1.16
        tests/net/route/t_flags.sh: revision 1.17
        usr.sbin/ndp/ndp.c: revision 1.50
        tests/net/route/t_flags.sh: revision 1.18
        sys/netinet/in.c: revision 1.204
        tests/net/route/t_flags.sh: revision 1.19
        sys/netinet/in.c: revision 1.205
        tests/net/arp/t_arp.sh: revision 1.30
        tests/net/arp/t_arp.sh: revision 1.31
        sys/net/if_llatbl.h: revision 1.11
        tests/net/arp/t_arp.sh: revision 1.32
        sys/net/if_llatbl.h: revision 1.12
        tests/net/arp/t_arp.sh: revision 1.33
        sys/netinet6/nd6.c: revision 1.233
        sys/netinet6/nd6.c: revision 1.234
        sys/netinet/if_arp.c: revision 1.251
        sys/netinet6/nd6.c: revision 1.235
        sys/netinet/if_arp.c: revision 1.252
        sbin/route/route.8: revision 1.57
        sys/net/rtsock.c: revision 1.214
        sys/net/rtsock.c: revision 1.215
        sys/net/rtsock.c: revision 1.216
        sys/net/rtsock.c: revision 1.217
whitespace police
Simplify
We can assume that rt_ifp is always non-NULL.
Sending a routing message (RTM_ADD) on adding an llentry
A message used to be sent on adding a cloned route. Restore the
behavior for backward compatibility.
Requested by ryo@
Drop RTF_CONNECTED from a result of RTM_GET for ARP/NDP entries
ARP/NDP entries aren't connected routes.
Reported by ryo@
Support -c <count> option for route monitor
route command exits if it receives <count> routing messages where
<count> is a value specified by -c.
The option is useful to get only particular message(s) in a test script.
Test routing messages emitted on operations of ARP/NDP entries
Do netstat -a for an appropriate protocol
Add missing declarations for cleanup
Set net.inet.arp.keep only if it's required
Don't create a permanent L2 cache entry on adding an address to an interface
It was created to copy FreeBSD, however actually the cache isn't
necessary. Remove it to simplify the code and reduce the cost to
maintain it (e.g., keep a consistency with a corresponding local
route).
Fix typo
Fix in_lltable_match_prefix
The function has not been used but will be used soon.
Remove unused function (nd6_rem_ifa_lle)
Allow in6_lltable_free_entry to be called without holding the afdata lock of 
ifp as well as in_lltable_free_entry
This behavior is a bit odd and should be fixed in the future...
Purge ARP/NDP entries on an interface when the interface is down
Fix PR kern/51179
Purge all related L2 caches on removing a route
The change addresses situations similar to PR 51179.
Purge L2 caches on changing an interface of a route
The change addresses situations similar to PR 51179.
Test implicit removals of ARP/NDP entries
One test case reproudces PR 51179.
Fix build of kernels without both INET and INET6
Tweak lltable_sysctl_dumparp
- Rename lltable_sysctl_dumparp to lltable_sysctl_dump
  because it's not only for ARP
- Enable it not only for INET but also for INET6
Fix usage of routing messages on arp -d and ndp -d
It didn't work as we expected; we should set RTA_GATEWAY not
RTA_IFP on RTM_GET to return an if_index and the kernel should
use it on RTM_DELETE.
Improve backward compatibility of (fake) routing messages on adding an ARP/NDP 
entry
A message originally included only DST and GATEWAY. Restore it.
Fix ifdef; care about a case w/ INET6 and w/o INET
Drop RTF_UP from a routing message of a deleted ARP/NDP entry
Check existence of ARP/NDP entries
Checking ARP/NDP entries is valid rather than checking routes.
Fix wrong comment
Drop RTF_LLINFO flag (now it's RTF_LLDATA) from local routes
They don't have llinfo anymore. And also the change fixes unexpected
behavior of ARP proxy.
Restore ARP/NDP entries to route show and netstat -r
Requested by dyoung@ some time ago
Enable to remove multiple ARP/NDP entries for one destination
The kernel can have multiple ARP/NDP entries which have an indentical
destination on different interfaces. This is normal and can be
reproduce easily by ping -I or ping6 -S. We should be able to remove
such entries.
arp -d <ip> and ndp -d <ip> are changed to fetch all ARP/NDP entries
and remove matched entries. So we can remove multiple entries
described above. This fetch all and selective removal behavior is
the same as arp <ip> and ndp <ip>; they also do fetch all entries
and show only matched entries.
Related to PR 51179
Check if ARP/NDP entries are purged when a related route is deleted


To generate a diff of this commit:
cvs rdiff -u -r1.56 -r1.56.8.1 src/sbin/route/route.8
cvs rdiff -u -r1.155 -r1.155.4.1 src/sbin/route/route.c
cvs rdiff -u -r1.8 -r1.8.8.1 src/sbin/route/rtutil.c
cvs rdiff -u -r1.18 -r1.18.6.1 src/sys/net/if_llatbl.c
cvs rdiff -u -r1.10 -r1.10.8.1 src/sys/net/if_llatbl.h
cvs rdiff -u -r1.194.6.1 -r1.194.6.2 src/sys/net/route.c
cvs rdiff -u -r1.112 -r1.112.4.1 src/sys/net/route.h
cvs rdiff -u -r1.213 -r1.213.2.1 src/sys/net/rtsock.c
cvs rdiff -u -r1.250.2.1 -r1.250.2.2 src/sys/netinet/if_arp.c
cvs rdiff -u -r1.203 -r1.203.2.1 src/sys/netinet/in.c
cvs rdiff -u -r1.245 -r1.245.2.1 src/sys/netinet6/in6.c
cvs rdiff -u -r1.232 -r1.232.2.1 src/sys/netinet6/nd6.c
cvs rdiff -u -r1.83 -r1.83.6.1 src/sys/netinet6/nd6.h
cvs rdiff -u -r1.18 -r1.18.2.1 src/tests/net/net_common.sh
cvs rdiff -u -r1.22 -r1.22.6.1 src/tests/net/arp/t_arp.sh
cvs rdiff -u -r1.19 -r1.19.2.1 src/tests/net/ndp/t_ndp.sh
cvs rdiff -u -r1.27 -r1.27.2.1 src/tests/net/ndp/t_ra.sh
cvs rdiff -u -r1.13 -r1.13.2.1 src/tests/net/net/t_ipv6address.sh
cvs rdiff -u -r1.15 -r1.15.6.1 src/tests/net/route/t_flags.sh
cvs rdiff -u -r1.12 -r1.12.6.1 src/tests/net/route/t_flags6.sh
cvs rdiff -u -r1.12 -r1.12.4.1 src/tests/net/route/t_route.sh
cvs rdiff -u -r1.55 -r1.55.8.1 src/usr.sbin/arp/arp.c
cvs rdiff -u -r1.48 -r1.48.6.1 src/usr.sbin/ndp/ndp.c

Please note that diffs are not public domain; they are subject to the
copyright notices on the relevant files.

Modified files:

Index: src/sbin/route/route.8
diff -u src/sbin/route/route.8:1.56 src/sbin/route/route.8:1.56.8.1
--- src/sbin/route/route.8:1.56	Mon Apr  4 07:37:07 2016
+++ src/sbin/route/route.8	Fri Jul  7 13:57:26 2017
@@ -1,4 +1,4 @@
-.\"	$NetBSD: route.8,v 1.56 2016/04/04 07:37:07 ozaki-r Exp $
+.\"	$NetBSD: route.8,v 1.56.8.1 2017/07/07 13:57:26 martin Exp $
 .\"
 .\" Copyright (c) 1983, 1991, 1993
 .\"	The Regents of the University of California.  All rights reserved.
@@ -29,7 +29,7 @@
 .\"
 .\"     @(#)route.8	8.4 (Berkeley) 6/1/94
 .\"
-.Dd March 30, 2016
+.Dd June 16, 2017
 .Dt ROUTE 8
 .Os
 .Sh NAME
@@ -139,8 +139,17 @@ The monitor command has the syntax
 .Nm
 .Op Fl n
 .Cm monitor
+.Op Fl c Ar count
 .Ed
 .Pp
+If
+.Ar count
+is specified,
+.Nm
+exits after receiving
+.Ar count
+routing messages.
+.Pp
 The flush command has the syntax
 .Pp
 .Bd -filled -offset indent -compact

Index: src/sbin/route/route.c
diff -u src/sbin/route/route.c:1.155 src/sbin/route/route.c:1.155.4.1
--- src/sbin/route/route.c:1.155	Fri Mar 17 16:13:44 2017
+++ src/sbin/route/route.c	Fri Jul  7 13:57:26 2017
@@ -1,4 +1,4 @@
-/*	$NetBSD: route.c,v 1.155 2017/03/17 16:13:44 roy Exp $	*/
+/*	$NetBSD: route.c,v 1.155.4.1 2017/07/07 13:57:26 martin Exp $	*/
 
 /*
  * Copyright (c) 1983, 1989, 1991, 1993
@@ -39,7 +39,7 @@ __COPYRIGHT("@(#) Copyright (c) 1983, 19
 #if 0
 static char sccsid[] = "@(#)route.c	8.6 (Berkeley) 4/28/95";
 #else
-__RCSID("$NetBSD: route.c,v 1.155 2017/03/17 16:13:44 roy Exp $");
+__RCSID("$NetBSD: route.c,v 1.155.4.1 2017/07/07 13:57:26 martin Exp $");
 #endif
 #endif /* not lint */
 
@@ -108,7 +108,7 @@ static char *netmask_string(const struct
 static int prefixlen(const char *, struct sou *);
 #ifndef SMALL
 static void interfaces(void);
-__dead static void monitor(void);
+static void monitor(int, char * const *);
 static int print_getmsg(struct rt_msghdr *, int, struct sou *);
 static const char *linkstate(struct if_msghdr *);
 static sup readtag(sup, const char *);
@@ -236,7 +236,7 @@ main(int argc, char * const *argv)
 
 #ifndef SMALL
 	case K_MONITOR:
-		monitor();
+		monitor(argc, argv);
 		return 0;
 
 #endif /* SMALL */
@@ -1105,20 +1105,37 @@ interfaces(void)
 }
 
 static void
-monitor(void)
+monitor(int argc, char * const *argv)
 {
-	int n;
+	int i, n;
 	union {
 		char msg[2048];
 		struct rt_msghdr hdr;
 	} u;
+	int count = 0;
+
+	/* usage: route monitor [-c <count>] */
+
+	/* eat "monitor" */
+	argc -= 1;
+	argv += 1;
+
+	/* parse [-c <count>] */
+	if (argc > 0) {
+		if (argc != 2)
+			usage(argv[0]);
+		if (strcmp(argv[0], "-c") != 0)
+			usage(argv[0]);
+
+		count = atoi(argv[1]);
+	}
 
 	verbose = 1;
 	if (debugonly) {
 		interfaces();
 		exit(0);
 	}
-	for(;;) {
+	for(i = 0; count == 0 || i < count; i++) {
 		time_t now;
 		n = prog_read(sock, &u, sizeof(u));
 		now = time(NULL);

Index: src/sbin/route/rtutil.c
diff -u src/sbin/route/rtutil.c:1.8 src/sbin/route/rtutil.c:1.8.8.1
--- src/sbin/route/rtutil.c:1.8	Mon Apr  4 07:37:07 2016
+++ src/sbin/route/rtutil.c	Fri Jul  7 13:57:26 2017
@@ -1,4 +1,4 @@
-/*	$NetBSD: rtutil.c,v 1.8 2016/04/04 07:37:07 ozaki-r Exp $	*/
+/*	$NetBSD: rtutil.c,v 1.8.8.1 2017/07/07 13:57:26 martin Exp $	*/
 /*	$OpenBSD: show.c,v 1.1 2006/05/27 19:16:37 claudio Exp $	*/
 
 /*
@@ -59,6 +59,11 @@
 #include "prog_ops.h"
 #include "rtutil.h"
 
+/*
+ * Keep to handle ARP/NDP entries (fake routes)
+ * for backward compatibility.
+ */
+#define RTF_LLINFO	0x400
 
 #define PLEN    (LONG_BIT / 4 + 2)
 #define PFKEYV2_CHUNK sizeof(u_int64_t)
@@ -84,7 +89,7 @@ static const struct bits bits[] = {
 	/* { RTF_CLONING,	'C' }, */
 	{ RTF_CONNECTED, 'C' },
 	/* { RTF_XRESOLVE,	'X' }, */
-	/* { RTF_LLINFO,	'L' }, */
+	{ RTF_LLINFO,	'L' },
 	{ RTF_STATIC,	'S' },
 	{ RTF_PROTO1,	'1' },
 	{ RTF_PROTO2,	'2' },
@@ -263,6 +268,9 @@ p_rtentry(struct rt_msghdr *rtm, int fla
 	char		 ifbuf[IF_NAMESIZE];
 #endif
 
+	if ((flags & RT_LFLAG) && (rtm->rtm_flags & RTF_LLINFO))
+		return;
+
 	if (old_af != sa->sa_family) {
 		old_af = sa->sa_family;
 		p_family(sa->sa_family);

Index: src/sys/net/if_llatbl.c
diff -u src/sys/net/if_llatbl.c:1.18 src/sys/net/if_llatbl.c:1.18.6.1
--- src/sys/net/if_llatbl.c:1.18	Fri Mar  3 06:27:20 2017
+++ src/sys/net/if_llatbl.c	Fri Jul  7 13:57:26 2017
@@ -1,4 +1,4 @@
-/*	$NetBSD: if_llatbl.c,v 1.18 2017/03/03 06:27:20 msaitoh Exp $	*/
+/*	$NetBSD: if_llatbl.c,v 1.18.6.1 2017/07/07 13:57:26 martin Exp $	*/
 /*
  * Copyright (c) 2004 Luigi Rizzo, Alessandro Cerri. All rights reserved.
  * Copyright (c) 2004-2008 Qing Li. All rights reserved.
@@ -80,6 +80,8 @@ int
 lltable_dump_entry(struct lltable *llt, struct llentry *lle,
     struct rt_walkarg *w, struct sockaddr *sa)
 {
+#define RTF_LLINFO	0x400
+#define RTF_CLONED	0x2000
 	struct ifnet *ifp = llt->llt_ifp;
 	int error;
 	void *a;
@@ -107,9 +109,14 @@ lltable_dump_entry(struct lltable *llt, 
 		struct rt_msghdr *rtm = (struct rt_msghdr *)w->w_tmem;
 
 		/* Need to copy by myself */
+		rtm->rtm_index = ifp->if_index;
+		rtm->rtm_rmx.rmx_mtu = 0;
 		rtm->rtm_rmx.rmx_expire =
 		    (lle->la_flags & LLE_STATIC) ? 0 : lle->la_expire;
+		rtm->rtm_flags = RTF_UP;
 		rtm->rtm_flags |= RTF_HOST; /* For ndp */
+		/* For backward compatibility */
+		rtm->rtm_flags |= RTF_LLINFO | RTF_CLONED;
 		rtm->rtm_flags |= (lle->la_flags & LLE_STATIC) ? RTF_STATIC : 0;
 		if (lle->la_flags & LLE_PUB)
 			rtm->rtm_flags |= RTF_ANNOUNCE;
@@ -121,6 +128,8 @@ lltable_dump_entry(struct lltable *llt, 
 	}
 
 	return error;
+#undef RTF_LLINFO
+#undef RTF_CLONED
 }
 
 /*
@@ -149,7 +158,7 @@ lltable_dump_af(struct lltable *llt, str
  * Dump arp state for a specific address family.
  */
 int
-lltable_sysctl_dumparp(int af, struct rt_walkarg *w)
+lltable_sysctl_dump(int af, struct rt_walkarg *w)
 {
 	struct lltable *llt;
 	int error = 0;
@@ -486,8 +495,8 @@ lltable_drain(int af)
 }
 
 void
-lltable_prefix_free(int af, struct sockaddr *prefix, struct sockaddr *mask,
-    u_int flags)
+lltable_prefix_free(const int af, const struct sockaddr *prefix,
+    const struct sockaddr *mask, const u_int flags)
 {
 	struct lltable *llt;
 

Index: src/sys/net/if_llatbl.h
diff -u src/sys/net/if_llatbl.h:1.10 src/sys/net/if_llatbl.h:1.10.8.1
--- src/sys/net/if_llatbl.h:1.10	Wed Dec 21 08:47:02 2016
+++ src/sys/net/if_llatbl.h	Fri Jul  7 13:57:26 2017
@@ -1,4 +1,4 @@
-/*	$NetBSD: if_llatbl.h,v 1.10 2016/12/21 08:47:02 ozaki-r Exp $	*/
+/*	$NetBSD: if_llatbl.h,v 1.10.8.1 2017/07/07 13:57:26 martin Exp $	*/
 /*
  * Copyright (c) 2004 Luigi Rizzo, Alessandro Cerri. All rights reserved.
  * Copyright (c) 2004-2008 Qing Li. All rights reserved.
@@ -254,11 +254,11 @@ void lltableinit(void);
 struct lltable *lltable_allocate_htbl(uint32_t hsize);
 void		lltable_free(struct lltable *);
 void		lltable_link(struct lltable *llt);
-void		lltable_prefix_free(int, struct sockaddr *,
-		    struct sockaddr *, u_int);
+void		lltable_prefix_free(const int, const struct sockaddr *,
+		    const struct sockaddr *, const u_int);
 void		lltable_drain(int);
 void		lltable_purge_entries(struct lltable *);
-int		lltable_sysctl_dumparp(int, struct rt_walkarg *);
+int		lltable_sysctl_dump(int, struct rt_walkarg *);
 int		lltable_dump_entry(struct lltable *, struct llentry *,
 		    struct rt_walkarg *, struct sockaddr *);
 

Index: src/sys/net/route.c
diff -u src/sys/net/route.c:1.194.6.1 src/sys/net/route.c:1.194.6.2
--- src/sys/net/route.c:1.194.6.1	Sun Jun 25 06:31:58 2017
+++ src/sys/net/route.c	Fri Jul  7 13:57:26 2017
@@ -1,4 +1,4 @@
-/*	$NetBSD: route.c,v 1.194.6.1 2017/06/25 06:31:58 snj Exp $	*/
+/*	$NetBSD: route.c,v 1.194.6.2 2017/07/07 13:57:26 martin Exp $	*/
 
 /*-
  * Copyright (c) 1998, 2008 The NetBSD Foundation, Inc.
@@ -97,7 +97,7 @@
 #endif
 
 #include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: route.c,v 1.194.6.1 2017/06/25 06:31:58 snj Exp $");
+__KERNEL_RCSID(0, "$NetBSD: route.c,v 1.194.6.2 2017/07/07 13:57:26 martin Exp $");
 
 #include <sys/param.h>
 #ifdef RTFLUSH_DEBUG
@@ -123,6 +123,9 @@ __KERNEL_RCSID(0, "$NetBSD: route.c,v 1.
 #include <net/if.h>
 #include <net/if_dl.h>
 #include <net/route.h>
+#if defined(INET) || defined(INET6)
+#include <net/if_llatbl.h>
+#endif
 
 #include <netinet/in.h>
 #include <netinet/in_var.h>
@@ -1249,6 +1252,10 @@ rtrequest1(int req, struct rt_addrinfo *
 		need_unlock = false;
 		rt_timer_remove_all(rt);
 		rtcache_clear_rtentry(dst->sa_family, rt);
+#if defined(INET) || defined(INET6)
+		if (netmask != NULL)
+			lltable_prefix_free(dst->sa_family, dst, netmask, 0);
+#endif
 		if (ret_nrt == NULL) {
 			/* Adjust the refcount */
 			rt_ref(rt);
@@ -1587,8 +1594,6 @@ rt_ifa_addlocal(struct ifaddr *ifa)
 
 		memset(&info, 0, sizeof(info));
 		info.rti_flags = RTF_HOST | RTF_LOCAL;
-		if (!(ifa->ifa_ifp->if_flags & (IFF_LOOPBACK|IFF_POINTOPOINT)))
-			info.rti_flags |= RTF_LLDATA;
 		info.rti_info[RTAX_DST] = ifa->ifa_addr;
 		info.rti_info[RTAX_GATEWAY] =
 		    (const struct sockaddr *)ifa->ifa_ifp->if_sadl;

Index: src/sys/net/route.h
diff -u src/sys/net/route.h:1.112 src/sys/net/route.h:1.112.4.1
--- src/sys/net/route.h:1.112	Tue Apr 11 13:55:54 2017
+++ src/sys/net/route.h	Fri Jul  7 13:57:26 2017
@@ -1,4 +1,4 @@
-/*	$NetBSD: route.h,v 1.112 2017/04/11 13:55:54 roy Exp $	*/
+/*	$NetBSD: route.h,v 1.112.4.1 2017/07/07 13:57:26 martin Exp $	*/
 
 /*
  * Copyright (c) 1980, 1986, 1993
@@ -500,6 +500,10 @@ int	rt_msg3(int, struct rt_addrinfo *, v
 void	rt_newaddrmsg(int, struct ifaddr *, int, struct rtentry *);
 void	route_enqueue(struct mbuf *, int);
 
+struct llentry;
+void	rt_clonedmsg(const struct sockaddr *, const struct ifnet *,
+	    const struct rtentry *);
+
 /* rtbl */
 int	rt_addaddr(rtbl_t *, struct rtentry *, const struct sockaddr *);
 void	rt_assert_inactive(const struct rtentry *);

Index: src/sys/net/rtsock.c
diff -u src/sys/net/rtsock.c:1.213 src/sys/net/rtsock.c:1.213.2.1
--- src/sys/net/rtsock.c:1.213	Thu Jun  1 02:45:14 2017
+++ src/sys/net/rtsock.c	Fri Jul  7 13:57:26 2017
@@ -1,4 +1,4 @@
-/*	$NetBSD: rtsock.c,v 1.213 2017/06/01 02:45:14 chs Exp $	*/
+/*	$NetBSD: rtsock.c,v 1.213.2.1 2017/07/07 13:57:26 martin Exp $	*/
 
 /*
  * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project.
@@ -61,7 +61,7 @@
  */
 
 #include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: rtsock.c,v 1.213 2017/06/01 02:45:14 chs Exp $");
+__KERNEL_RCSID(0, "$NetBSD: rtsock.c,v 1.213.2.1 2017/07/07 13:57:26 martin Exp $");
 
 #ifdef _KERNEL_OPT
 #include "opt_inet.h"
@@ -474,7 +474,7 @@ COMPATNAME(route_purgeif)(struct socket 
 	return EOPNOTSUPP;
 }
 
-#ifdef INET
+#if defined(INET) || defined(INET6)
 static int
 route_get_sdl_index(struct rt_addrinfo *info, int *sdl_index)
 {
@@ -493,7 +493,7 @@ route_get_sdl_index(struct rt_addrinfo *
 
 	return 0;
 }
-#endif /* INET */
+#endif
 
 static void
 route_get_sdl(const struct ifnet *ifp, const struct sockaddr *dst,
@@ -536,12 +536,11 @@ route_output_report(struct rtentry *rt, 
     struct rt_xmsghdr *rtm, struct rt_xmsghdr **new_rtm)
 {
 	int len;
-	struct ifnet *ifp;
 
-	if ((rtm->rtm_addrs & (RTA_IFP | RTA_IFA)) == 0)
-		;
-	else if ((ifp = rt->rt_ifp) != NULL) {
+	if (rtm->rtm_addrs & (RTA_IFP | RTA_IFA)) {
 		const struct ifaddr *rtifa;
+		const struct ifnet *ifp = rt->rt_ifp;
+
 		info->rti_info[RTAX_IFP] = ifp->if_dl->ifa_addr;
 		/* rtifa used to be simply rt->rt_ifa.
 		 * If rt->rt_ifa != NULL, then
@@ -569,9 +568,6 @@ route_output_report(struct rtentry *rt, 
 		else
 			info->rti_info[RTAX_BRD] = NULL;
 		rtm->rtm_index = ifp->if_index;
-	} else {
-		info->rti_info[RTAX_IFP] = NULL;
-		info->rti_info[RTAX_IFA] = NULL;
 	}
 	(void)rt_msg2(rtm->rtm_type, info, NULL, NULL, &len);
 	if (len > rtm->rtm_msglen) {
@@ -645,7 +641,7 @@ route_output_change(struct rtentry *rt, 
 	struct ifnet *ifp = NULL, *new_ifp;
 	struct ifaddr *ifa = NULL, *new_ifa;
 	struct psref psref_ifa, psref_new_ifa, psref_ifp;
-	bool newgw;
+	bool newgw, ifp_changed = false;
 
 	/*
 	 * New gateway could require new ifaddr, ifp;
@@ -695,13 +691,16 @@ route_output_change(struct rtentry *rt, 
 				oifa->ifa_rtrequest(RTM_DELETE, rt, info);
 			rt_replace_ifa(rt, ifa);
 			rt->rt_ifp = new_ifp;
+			ifp_changed = true;
 		}
 		if (new_ifa == NULL)
 			ifa_release(ifa, &psref_ifa);
 	}
 	ifa_release(new_ifa, &psref_new_ifa);
-	if (new_ifp && rt->rt_ifp != new_ifp && !if_is_deactivated(new_ifp))
+	if (new_ifp && rt->rt_ifp != new_ifp && !if_is_deactivated(new_ifp)) {
 		rt->rt_ifp = new_ifp;
+		ifp_changed = true;
+	}
 	rt_setmetrics(rtm->rtm_inits, rtm, rt);
 	if (rt->rt_flags != info->rti_flags) {
 		rt->rt_flags = (info->rti_flags & ~PRESERVED_RTF) |
@@ -709,6 +708,13 @@ route_output_change(struct rtentry *rt, 
 	}
 	if (rt->rt_ifa && rt->rt_ifa->ifa_rtrequest)
 		rt->rt_ifa->ifa_rtrequest(RTM_ADD, rt, info);
+#if defined(INET) || defined(INET6)
+	if (ifp_changed && rt_mask(rt) != NULL)
+		lltable_prefix_free(rt_getkey(rt)->sa_family, rt_getkey(rt),
+		    rt_mask(rt), 0);
+#else
+	(void)ifp_changed; /* XXX gcc */
+#endif
 out:
 	if_put(ifp, &psref_ifp);
 
@@ -793,8 +799,8 @@ COMPATNAME(route_output)(struct mbuf *m,
 		if (info.rti_info[RTAX_GATEWAY] == NULL) {
 			senderr(EINVAL);
 		}
-#ifdef INET
-		/* support for new ARP code with keeping backcompat */
+#if defined(INET) || defined(INET6)
+		/* support for new ARP/NDP code with keeping backcompat */
 		if (info.rti_info[RTAX_GATEWAY]->sa_family == AF_LINK) {
 			const struct sockaddr_dl *sdlp =
 			    satocsdl(info.rti_info[RTAX_GATEWAY]);
@@ -836,7 +842,7 @@ COMPATNAME(route_output)(struct mbuf *m,
 			break;
 		}
 	fallback:
-#endif /* INET */
+#endif /* defined(INET) || defined(INET6) */
 		error = rtrequest1(rtm->rtm_type, &info, &saved_nrt);
 		if (error == 0) {
 			rt_setmetrics(rtm->rtm_inits, rtm, saved_nrt);
@@ -845,16 +851,19 @@ COMPATNAME(route_output)(struct mbuf *m,
 		break;
 
 	case RTM_DELETE:
-#ifdef INET
-		/* support for new ARP code */
+#if defined(INET) || defined(INET6)
+		/* support for new ARP/NDP code */
 		if (info.rti_info[RTAX_GATEWAY] &&
 		    (info.rti_info[RTAX_GATEWAY]->sa_family == AF_LINK) &&
 		    (rtm->rtm_flags & RTF_LLDATA) != 0) {
+			const struct sockaddr_dl *sdlp =
+			    satocsdl(info.rti_info[RTAX_GATEWAY]);
 			error = lla_rt_output(rtm->rtm_type, rtm->rtm_flags,
-			    rtm->rtm_rmx.rmx_expire, &info, 0);
+			    rtm->rtm_rmx.rmx_expire, &info, sdlp->sdl_index);
+			rtm->rtm_flags &= ~RTF_UP;
 			break;
 		}
-#endif /* INET */
+#endif
 		error = rtrequest1(rtm->rtm_type, &info, &saved_nrt);
 		if (error != 0)
 			break;
@@ -920,6 +929,7 @@ COMPATNAME(route_output)(struct mbuf *m,
 				rtm = new_rtm;
 			}
 			rtm->rtm_flags |= RTF_LLDATA;
+			rtm->rtm_flags &= ~RTF_CONNECTED;
 			rtm->rtm_flags |= (ll_flags & LLE_STATIC) ? RTF_STATIC : 0;
 			break;
 		}
@@ -1632,6 +1642,42 @@ COMPATNAME(rt_ieee80211msg)(struct ifnet
 	COMPATNAME(route_enqueue)(m, 0);
 }
 
+#ifndef COMPAT_RTSOCK
+/*
+ * Send a routing message as mimicing that a cloned route is added.
+ */
+void
+rt_clonedmsg(const struct sockaddr *dst, const struct ifnet *ifp,
+    const struct rtentry *rt)
+{
+	struct rt_addrinfo info;
+	/* Mimic flags exactly */
+#define RTF_LLINFO	0x400
+#define RTF_CLONED	0x2000
+	int flags = RTF_UP | RTF_HOST | RTF_DONE | RTF_LLINFO | RTF_CLONED;
+	union {
+		struct sockaddr sa;
+		struct sockaddr_storage ss;
+		struct sockaddr_dl sdl;
+	} u;
+	uint8_t namelen = strlen(ifp->if_xname);
+	uint8_t addrlen = ifp->if_addrlen;
+
+	if (rt == NULL)
+		return; /* XXX */
+
+	memset(&info, 0, sizeof(info));
+	info.rti_info[RTAX_DST] = dst;
+	sockaddr_dl_init(&u.sdl, sizeof(u.ss), ifp->if_index, ifp->if_type,
+	    NULL, namelen, NULL, addrlen);
+	info.rti_info[RTAX_GATEWAY] = &u.sa;
+
+	rt_missmsg(RTM_ADD, &info, flags, 0);
+#undef RTF_LLINFO
+#undef RTF_CLONED
+}
+#endif /* COMPAT_RTSOCK */
+
 /*
  * This is used in dumping the kernel table via sysctl().
  */
@@ -1871,7 +1917,7 @@ again:
 
 	case NET_RT_DUMP:
 	case NET_RT_FLAGS:
-#ifdef INET
+#if defined(INET) || defined(INET6)
 		/*
 		 * take care of llinfo entries, the caller must
 		 * specify an AF
@@ -1879,17 +1925,29 @@ again:
 		if (w.w_op == NET_RT_FLAGS &&
 		    (w.w_arg == 0 || w.w_arg & RTF_LLDATA)) {
 			if (af != 0)
-				error = lltable_sysctl_dumparp(af, &w);
+				error = lltable_sysctl_dump(af, &w);
 			else
 				error = EINVAL;
 			break;
 		}
-#endif /* INET */
+#endif
 
-		for (i = 1; i <= AF_MAX; i++)
-			if ((af == 0 || af == i) &&
-			    (error = rt_walktree(i, sysctl_dumpentry, &w)))
-				break;
+		for (i = 1; i <= AF_MAX; i++) {
+			if (af == 0 || af == i) {
+				error = rt_walktree(i, sysctl_dumpentry, &w);
+				if (error != 0)
+					break;
+#if defined(INET) || defined(INET6)
+				/*
+				 * Return ARP/NDP entries too for
+				 * backward compatibility.
+				 */
+				error = lltable_sysctl_dump(i, &w);
+				if (error != 0)
+					break;
+#endif
+			}
+		}
 		break;
 
 #ifdef COMPAT_14

Index: src/sys/netinet/if_arp.c
diff -u src/sys/netinet/if_arp.c:1.250.2.1 src/sys/netinet/if_arp.c:1.250.2.2
--- src/sys/netinet/if_arp.c:1.250.2.1	Sat Jul  1 08:56:06 2017
+++ src/sys/netinet/if_arp.c	Fri Jul  7 13:57:27 2017
@@ -1,4 +1,4 @@
-/*	$NetBSD: if_arp.c,v 1.250.2.1 2017/07/01 08:56:06 snj Exp $	*/
+/*	$NetBSD: if_arp.c,v 1.250.2.2 2017/07/07 13:57:27 martin Exp $	*/
 
 /*-
  * Copyright (c) 1998, 2000, 2008 The NetBSD Foundation, Inc.
@@ -68,7 +68,7 @@
  */
 
 #include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: if_arp.c,v 1.250.2.1 2017/07/01 08:56:06 snj Exp $");
+__KERNEL_RCSID(0, "$NetBSD: if_arp.c,v 1.250.2.2 2017/07/07 13:57:27 martin Exp $");
 
 #ifdef _KERNEL_OPT
 #include "opt_ddb.h"
@@ -760,8 +760,13 @@ notfound:
 		IF_AFDATA_WUNLOCK(ifp);
 		if (la == NULL)
 			ARP_STATINC(ARP_STAT_ALLOCFAIL);
-		else
+		else {
+			struct sockaddr_in sin;
+
 			arp_init_llentry(ifp, la);
+			sockaddr_in_init(&sin, &la->r_l3addr.addr4, 0);
+			rt_clonedmsg(sintosa(&sin), ifp, rt);
+		}
 	} else if (LLE_TRY_UPGRADE(la) == 0) {
 		create_lookup = "lookup";
 		LLE_RUNLOCK(la);
@@ -1468,41 +1473,13 @@ arpioctl(u_long cmd, void *data)
 void
 arp_ifinit(struct ifnet *ifp, struct ifaddr *ifa)
 {
-	struct in_addr *ip;
 	struct in_ifaddr *ia = (struct in_ifaddr *)ifa;
 
-	/*
-	 * Warn the user if another station has this IP address,
-	 * but only if the interface IP address is not zero.
-	 */
-	ip = &IA_SIN(ifa)->sin_addr;
-	if (!in_nullhost(*ip) &&
-	    (ia->ia4_flags & (IN_IFF_NOTREADY | IN_IFF_DETACHED)) == 0) {
-		struct llentry *lle;
-
-		/*
-		 * interface address is considered static entry
-		 * because the output of the arp utility shows
-		 * that L2 entry as permanent
-		 */
-		IF_AFDATA_WLOCK(ifp);
-		lle = lla_create(LLTABLE(ifp), (LLE_IFADDR | LLE_STATIC),
-				 (struct sockaddr *)IA_SIN(ifa));
-		IF_AFDATA_WUNLOCK(ifp);
-		if (lle == NULL)
-			log(LOG_INFO, "%s: cannot create arp entry for"
-			    " interface address\n", __func__);
-		else {
-			arp_init_llentry(ifp, lle);
-			LLE_RUNLOCK(lle);
-		}
-	}
-
 	ifa->ifa_rtrequest = arp_rtrequest;
 	ifa->ifa_flags |= RTF_CONNECTED;
 
 	/* ARP will handle DAD for this address. */
-	if (in_nullhost(*ip)) {
+	if (in_nullhost(IA_SIN(ifa)->sin_addr)) {
 		if (ia->ia_dad_stop != NULL)	/* safety */
 			ia->ia_dad_stop(ifa);
 		ia->ia_dad_start = NULL;

Index: src/sys/netinet/in.c
diff -u src/sys/netinet/in.c:1.203 src/sys/netinet/in.c:1.203.2.1
--- src/sys/netinet/in.c:1.203	Thu Jun  1 02:45:14 2017
+++ src/sys/netinet/in.c	Fri Jul  7 13:57:27 2017
@@ -1,4 +1,4 @@
-/*	$NetBSD: in.c,v 1.203 2017/06/01 02:45:14 chs Exp $	*/
+/*	$NetBSD: in.c,v 1.203.2.1 2017/07/07 13:57:27 martin Exp $	*/
 
 /*
  * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project.
@@ -91,7 +91,7 @@
  */
 
 #include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: in.c,v 1.203 2017/06/01 02:45:14 chs Exp $");
+__KERNEL_RCSID(0, "$NetBSD: in.c,v 1.203.2.1 2017/07/07 13:57:27 martin Exp $");
 
 #include "arp.h"
 
@@ -1505,6 +1505,7 @@ in_if_down(struct ifnet *ifp)
 {
 
 	in_if_link_down(ifp);
+	lltable_purge_entries(LLTABLE(ifp));
 }
 
 void
@@ -1896,12 +1897,15 @@ in_lltable_match_prefix(const struct soc
 {
 	const struct sockaddr_in *pfx = (const struct sockaddr_in *)prefix;
 	const struct sockaddr_in *msk = (const struct sockaddr_in *)mask;
+	struct in_addr lle_addr;
+
+	lle_addr.s_addr = ntohl(lle->r_l3addr.addr4.s_addr);
 
 	/*
 	 * (flags & LLE_STATIC) means deleting all entries
 	 * including static ARP entries.
 	 */
-	if (IN_ARE_MASKED_ADDR_EQUAL(lle->r_l3addr.addr4, pfx, msk) &&
+	if (IN_ARE_MASKED_ADDR_EQUAL(lle_addr, pfx, msk) &&
 	    ((flags & LLE_STATIC) || !(lle->la_flags & LLE_STATIC)))
 		return (1);
 

Index: src/sys/netinet6/in6.c
diff -u src/sys/netinet6/in6.c:1.245 src/sys/netinet6/in6.c:1.245.2.1
--- src/sys/netinet6/in6.c:1.245	Fri Apr 28 05:56:33 2017
+++ src/sys/netinet6/in6.c	Fri Jul  7 13:57:26 2017
@@ -1,4 +1,4 @@
-/*	$NetBSD: in6.c,v 1.245 2017/04/28 05:56:33 ozaki-r Exp $	*/
+/*	$NetBSD: in6.c,v 1.245.2.1 2017/07/07 13:57:26 martin Exp $	*/
 /*	$KAME: in6.c,v 1.198 2001/07/18 09:12:38 itojun Exp $	*/
 
 /*
@@ -62,7 +62,7 @@
  */
 
 #include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: in6.c,v 1.245 2017/04/28 05:56:33 ozaki-r Exp $");
+__KERNEL_RCSID(0, "$NetBSD: in6.c,v 1.245.2.1 2017/07/07 13:57:26 martin Exp $");
 
 #ifdef _KERNEL_OPT
 #include "opt_inet.h"
@@ -1298,10 +1298,11 @@ in6_update_ifa1(struct ifnet *ifp, struc
 			goto cleanup;
 	}
 
-	/* Add local address to lltable, if necessary (ex. on p2p link). */
-	error = nd6_add_ifa_lle(ia);
-	if (error != 0)
-		goto cleanup;
+	if (nd6_need_cache(ifp)) {
+		/* XXX maybe unnecessary */
+		ia->ia_ifa.ifa_rtrequest = nd6_rtrequest;
+		ia->ia_ifa.ifa_flags |= RTF_CONNECTED;
+	}
 
 	/*
 	 * Perform DAD, if needed.
@@ -2273,6 +2274,7 @@ in6_if_down(struct ifnet *ifp)
 {
 
 	in6_if_link_down(ifp);
+	lltable_purge_entries(LLTABLE6(ifp));
 }
 
 void
@@ -2415,15 +2417,16 @@ static void
 in6_lltable_free_entry(struct lltable *llt, struct llentry *lle)
 {
 	struct ifnet *ifp = llt->llt_ifp;
+	bool locked = false;
 
-	IF_AFDATA_WLOCK_ASSERT(ifp);
 	LLE_WLOCK_ASSERT(lle);
 
 	/* Unlink entry from table */
 	if ((lle->la_flags & LLE_LINKED) != 0) {
-
+		IF_AFDATA_WLOCK_ASSERT(ifp);
 		lltable_unlink_entry(llt, lle);
 		KASSERT((lle->la_flags & LLE_LINKED) == 0);
+		locked = true;
 	}
 	/*
 	 * We need to release the lock here to lle_timer proceeds;
@@ -2433,7 +2436,8 @@ in6_lltable_free_entry(struct lltable *l
 	 */
 	LLE_ADDREF(lle);
 	LLE_WUNLOCK(lle);
-	IF_AFDATA_WUNLOCK(ifp);
+	if (locked)
+		IF_AFDATA_WUNLOCK(ifp);
 
 #ifdef NET_MPSAFE
 	callout_halt(&lle->lle_timer, NULL);
@@ -2449,7 +2453,8 @@ in6_lltable_free_entry(struct lltable *l
 	lltable_drop_entry_queue(lle);
 	LLE_FREE_LOCKED(lle);
 
-	IF_AFDATA_WLOCK(ifp);
+	if (locked)
+		IF_AFDATA_WLOCK(ifp);
 }
 
 static int

Index: src/sys/netinet6/nd6.c
diff -u src/sys/netinet6/nd6.c:1.232 src/sys/netinet6/nd6.c:1.232.2.1
--- src/sys/netinet6/nd6.c:1.232	Thu Jun  1 02:45:14 2017
+++ src/sys/netinet6/nd6.c	Fri Jul  7 13:57:26 2017
@@ -1,4 +1,4 @@
-/*	$NetBSD: nd6.c,v 1.232 2017/06/01 02:45:14 chs Exp $	*/
+/*	$NetBSD: nd6.c,v 1.232.2.1 2017/07/07 13:57:26 martin Exp $	*/
 /*	$KAME: nd6.c,v 1.279 2002/06/08 11:16:51 itojun Exp $	*/
 
 /*
@@ -31,7 +31,7 @@
  */
 
 #include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: nd6.c,v 1.232 2017/06/01 02:45:14 chs Exp $");
+__KERNEL_RCSID(0, "$NetBSD: nd6.c,v 1.232.2.1 2017/07/07 13:57:26 martin Exp $");
 
 #ifdef _KERNEL_OPT
 #include "opt_net_mpsafe.h"
@@ -2298,6 +2298,7 @@ nd6_resolve(struct ifnet *ifp, const str
 	/* Slow path */
 	ln = nd6_lookup(&dst->sin6_addr, ifp, true);
 	if (ln == NULL && nd6_is_addr_neighbor(dst, ifp))  {
+		struct sockaddr_in6 sin6;
 		/*
 		 * Since nd6_is_addr_neighbor() internally calls nd6_lookup(),
 		 * the condition below is not very efficient.  But we believe
@@ -2313,6 +2314,10 @@ nd6_resolve(struct ifnet *ifp, const str
 			m_freem(m);
 			return ENOBUFS;
 		}
+
+		sockaddr_in6_init(&sin6, &ln->r_l3addr.addr6, 0, 0, 0);
+		rt_clonedmsg(sin6tosa(&sin6), ifp, rt);
+
 		created = true;
 	}
 
@@ -2418,61 +2423,6 @@ nd6_need_cache(struct ifnet *ifp)
 	}
 }
 
-/*
- * Add pernament ND6 link-layer record for given
- * interface address.
- *
- * Very similar to IPv4 arp_ifinit(), but:
- * 1) IPv6 DAD is performed in different place
- * 2) It is called by IPv6 protocol stack in contrast to
- * arp_ifinit() which is typically called in SIOCSIFADDR
- * driver ioctl handler.
- *
- */
-int
-nd6_add_ifa_lle(struct in6_ifaddr *ia)
-{
-	struct ifnet *ifp;
-	struct llentry *ln;
-
-	ifp = ia->ia_ifa.ifa_ifp;
-	if (nd6_need_cache(ifp) == 0)
-		return 0;
-	ia->ia_ifa.ifa_rtrequest = nd6_rtrequest;
-	ia->ia_ifa.ifa_flags |= RTF_CONNECTED;
-
-	IF_AFDATA_WLOCK(ifp);
-	ln = lla_create(LLTABLE6(ifp), LLE_IFADDR | LLE_EXCLUSIVE,
-	    sin6tosa(&ia->ia_addr));
-	IF_AFDATA_WUNLOCK(ifp);
-	if (ln == NULL)
-		return ENOBUFS;
-
-	ln->la_expire = 0;  /* for IPv6 this means permanent */
-	ln->ln_state = ND6_LLINFO_REACHABLE;
-
-	LLE_WUNLOCK(ln);
-	return 0;
-}
-
-/*
- * Removes ALL lle records for interface address prefix.
- * XXXME: That's probably not we really want to do, we need
- * to remove address record only and keep other records
- * until we determine if given prefix is really going
- * to be removed.
- */
-void
-nd6_rem_ifa_lle(struct in6_ifaddr *ia)
-{
-	struct sockaddr_in6 mask, addr;
-
-	memcpy(&addr, &ia->ia_addr, sizeof(ia->ia_addr));
-	memcpy(&mask, &ia->ia_prefixmask, sizeof(ia->ia_prefixmask));
-	lltable_prefix_free(AF_INET6, sin6tosa(&addr), sin6tosa(&mask),
-	    LLE_STATIC);
-}
-
 static void 
 clear_llinfo_pqueue(struct llentry *ln)
 {

Index: src/sys/netinet6/nd6.h
diff -u src/sys/netinet6/nd6.h:1.83 src/sys/netinet6/nd6.h:1.83.6.1
--- src/sys/netinet6/nd6.h:1.83	Wed Feb 22 03:02:55 2017
+++ src/sys/netinet6/nd6.h	Fri Jul  7 13:57:26 2017
@@ -1,4 +1,4 @@
-/*	$NetBSD: nd6.h,v 1.83 2017/02/22 03:02:55 ozaki-r Exp $	*/
+/*	$NetBSD: nd6.h,v 1.83.6.1 2017/07/07 13:57:26 martin Exp $	*/
 /*	$KAME: nd6.h,v 1.95 2002/06/08 11:31:06 itojun Exp $	*/
 
 /*
@@ -447,8 +447,6 @@ void nd6_cache_lladdr(struct ifnet *, st
 int nd6_sysctl(int, void *, size_t *, void *, size_t);
 int nd6_need_cache(struct ifnet *);
 void nd6_llinfo_release_pkts(struct llentry *, struct ifnet *);
-int nd6_add_ifa_lle(struct in6_ifaddr *);
-void nd6_rem_ifa_lle(struct in6_ifaddr *);
 
 /* nd6_nbr.c */
 void nd6_na_input(struct mbuf *, int, int);

Index: src/tests/net/net_common.sh
diff -u src/tests/net/net_common.sh:1.18 src/tests/net/net_common.sh:1.18.2.1
--- src/tests/net/net_common.sh:1.18	Fri Jun  2 01:18:51 2017
+++ src/tests/net/net_common.sh	Fri Jul  7 13:57:26 2017
@@ -1,4 +1,4 @@
-#	$NetBSD: net_common.sh,v 1.18 2017/06/02 01:18:51 ozaki-r Exp $
+#	$NetBSD: net_common.sh,v 1.18.2.1 2017/07/07 13:57:26 martin Exp $
 #
 # Copyright (c) 2016 Internet Initiative Japan Inc.
 # All rights reserved.
@@ -157,7 +157,11 @@ start_nc_server()
 	pid=$!
 	echo $pid > $NC_PID
 
-	$DEBUG && rump.netstat -a -f inet
+	if [ $proto = ipv4 ]; then
+		$DEBUG && rump.netstat -a -f inet
+	else
+		$DEBUG && rump.netstat -a -f inet6
+	fi
 
 	export RUMP_SERVER=$backup
 

Index: src/tests/net/arp/t_arp.sh
diff -u src/tests/net/arp/t_arp.sh:1.22 src/tests/net/arp/t_arp.sh:1.22.6.1
--- src/tests/net/arp/t_arp.sh:1.22	Fri Nov 25 08:51:16 2016
+++ src/tests/net/arp/t_arp.sh	Fri Jul  7 13:57:26 2017
@@ -1,4 +1,4 @@
-#	$NetBSD: t_arp.sh,v 1.22 2016/11/25 08:51:16 ozaki-r Exp $
+#	$NetBSD: t_arp.sh,v 1.22.6.1 2017/07/07 13:57:26 martin Exp $
 #
 # Copyright (c) 2015 The NetBSD Foundation, Inc.
 # All rights reserved.
@@ -28,6 +28,8 @@
 SOCKSRC=unix://commsock1
 SOCKDST=unix://commsock2
 IP4SRC=10.0.1.1
+IP4SRC2=10.0.1.5
+IP4NET=10.0.1.0
 IP4DST=10.0.1.2
 IP4DST_PROXYARP1=10.0.1.3
 IP4DST_PROXYARP2=10.0.1.4
@@ -111,16 +113,20 @@ setup_dst_server()
 
 	$DEBUG && rump.ifconfig shmif0
 	$DEBUG && rump.arp -n -a
+	$DEBUG && rump.netstat -nr -f inet
 }
 
 setup_src_server()
 {
-	local keep=$1
+	local keep=${1:-0}
 
 	export RUMP_SERVER=$SOCKSRC
 
 	# Adjust ARP parameters
-	atf_check -s exit:0 -o ignore rump.sysctl -w net.inet.arp.keep=$keep
+	if [ $keep != 0 ]; then
+		atf_check -s exit:0 -o ignore \
+		    rump.sysctl -w net.inet.arp.keep=$keep
+	fi
 
 	# Setup an interface
 	rump_server_add_iface $SOCKSRC shmif0 bus1
@@ -131,8 +137,9 @@ setup_src_server()
 	# Sanity check
 	$DEBUG && rump.ifconfig shmif0
 	$DEBUG && rump.arp -n -a
-	atf_check -s exit:0 -o ignore rump.arp -n $IP4SRC
-	atf_check -s not-exit:0 -e ignore rump.arp -n $IP4DST
+	$DEBUG && rump.netstat -nr -f inet
+	atf_check -s not-exit:0 -e match:'no entry' rump.arp -n $IP4SRC
+	atf_check -s not-exit:0 -e match:'no entry' rump.arp -n $IP4DST
 }
 
 test_cache_expiration()
@@ -153,16 +160,18 @@ test_cache_expiration()
 	atf_check -s exit:0 -o ignore rump.ping -n -w $TIMEOUT -c 1 $IP4DST
 
 	$DEBUG && rump.arp -n -a
-	atf_check -s exit:0 -o ignore rump.arp -n $IP4SRC
+	atf_check -s not-exit:0 -e match:'no entry' rump.arp -n $IP4SRC
 	# Should be cached
 	atf_check -s exit:0 -o ignore rump.arp -n $IP4DST
+	$DEBUG && rump.netstat -nr -f inet
+	atf_check -s exit:0 -o match:"$IP4DST" rump.netstat -nr -f inet
 
 	atf_check -s exit:0 sleep $(($arp_keep + $bonus))
 
 	$DEBUG && rump.arp -n -a
-	atf_check -s exit:0 -o ignore rump.arp -n $IP4SRC
+	atf_check -s not-exit:0 -e match:'no entry' rump.arp -n $IP4SRC
 	# Should be expired
-	atf_check -s not-exit:0 -e ignore rump.arp -n $IP4DST
+	atf_check -s not-exit:0 -e match:'no entry' rump.arp -n $IP4DST
 }
 
 arp_cache_expiration_5s_body()
@@ -179,6 +188,23 @@ arp_cache_expiration_10s_body()
 	rump_server_destroy_ifaces
 }
 
+check_arp_static_entry()
+{
+	local ip=$1
+	local mac=$2
+	local type=$3
+	local flags=
+
+	atf_check -s exit:0 -o match:"$mac" rump.arp -n $ip
+	if [ $type = 'permanent' ]; then
+		atf_check -s exit:0 -o match:'permanent' rump.arp -n $ip
+		check_route $ip "$mac" UHLS shmif0
+	else
+		atf_check -s exit:0 -o not-match:'permanent' rump.arp -n $ip
+		check_route $ip "$mac" UHL shmif0
+	fi
+}
+
 arp_command_body()
 {
 	local arp_keep=5
@@ -192,18 +218,17 @@ arp_command_body()
 
 	export RUMP_SERVER=$SOCKSRC
 
-	# We can delete the entry for the interface's IP address
-	atf_check -s exit:0 -o ignore rump.arp -d $IP4SRC
-
 	# Add and delete a static entry
 	$DEBUG && rump.arp -n -a
 	atf_check -s exit:0 -o ignore rump.arp -s 10.0.1.10 b2:a0:20:00:00:10
 	$DEBUG && rump.arp -n -a
-	atf_check -s exit:0 -o match:'b2:a0:20:00:00:10' rump.arp -n 10.0.1.10
-	atf_check -s exit:0 -o match:'permanent' rump.arp -n 10.0.1.10
+	$DEBUG && rump.netstat -nr -f inet
+	check_arp_static_entry 10.0.1.10 'b2:a0:20:00:00:10' permanent
 	atf_check -s exit:0 -o ignore rump.arp -d 10.0.1.10
 	$DEBUG && rump.arp -n -a
+	$DEBUG && rump.netstat -nr -f inet
 	atf_check -s not-exit:0 -e ignore rump.arp -n 10.0.1.10
+	check_route_no_entry 10.0.1.10
 
 	# Add multiple entries via a file
 	cat - > ./list <<-EOF
@@ -214,18 +239,15 @@ arp_command_body()
 	10.0.1.15 b2:a0:20:00:00:15
 	EOF
 	$DEBUG && rump.arp -n -a
+	$DEBUG && rump.netstat -nr -f inet
 	atf_check -s exit:0 -o ignore rump.arp -f ./list
 	$DEBUG && rump.arp -n -a
-	atf_check -s exit:0 -o match:'b2:a0:20:00:00:11' rump.arp -n 10.0.1.11
-	atf_check -s exit:0 -o match:'permanent' rump.arp -n 10.0.1.11
-	atf_check -s exit:0 -o match:'b2:a0:20:00:00:12' rump.arp -n 10.0.1.12
-	atf_check -s exit:0 -o match:'permanent' rump.arp -n 10.0.1.12
-	atf_check -s exit:0 -o match:'b2:a0:20:00:00:13' rump.arp -n 10.0.1.13
-	atf_check -s exit:0 -o match:'permanent' rump.arp -n 10.0.1.13
-	atf_check -s exit:0 -o match:'b2:a0:20:00:00:14' rump.arp -n 10.0.1.14
-	atf_check -s exit:0 -o match:'permanent' rump.arp -n 10.0.1.14
-	atf_check -s exit:0 -o match:'b2:a0:20:00:00:15' rump.arp -n 10.0.1.15
-	atf_check -s exit:0 -o match:'permanent' rump.arp -n 10.0.1.15
+	$DEBUG && rump.netstat -nr -f inet
+	check_arp_static_entry 10.0.1.11 'b2:a0:20:00:00:11' permanent
+	check_arp_static_entry 10.0.1.12 'b2:a0:20:00:00:12' permanent
+	check_arp_static_entry 10.0.1.13 'b2:a0:20:00:00:13' permanent
+	check_arp_static_entry 10.0.1.14 'b2:a0:20:00:00:14' permanent
+	check_arp_static_entry 10.0.1.15 'b2:a0:20:00:00:15' permanent
 
 	# Test arp -a
 	atf_check -s exit:0 -o match:'10.0.1.11' rump.arp -n -a
@@ -236,6 +258,7 @@ arp_command_body()
 
 	# Flush all entries
 	$DEBUG && rump.arp -n -a
+	$DEBUG && rump.netstat -nr -f inet
 	atf_check -s exit:0 -o ignore rump.arp -d -a
 	atf_check -s not-exit:0 -e ignore rump.arp -n 10.0.1.11
 	atf_check -s not-exit:0 -e ignore rump.arp -n 10.0.1.12
@@ -243,17 +266,23 @@ arp_command_body()
 	atf_check -s not-exit:0 -e ignore rump.arp -n 10.0.1.14
 	atf_check -s not-exit:0 -e ignore rump.arp -n 10.0.1.15
 	atf_check -s not-exit:0 -e ignore rump.arp -n 10.0.1.1
+	check_route_no_entry 10.0.1.11
+	check_route_no_entry 10.0.1.12
+	check_route_no_entry 10.0.1.13
+	check_route_no_entry 10.0.1.14
+	check_route_no_entry 10.0.1.15
 
 	# Test temp option
 	$DEBUG && rump.arp -n -a
 	atf_check -s exit:0 -o ignore rump.arp -s 10.0.1.10 b2:a0:20:00:00:10 temp
 	$DEBUG && rump.arp -n -a
-	atf_check -s exit:0 -o match:'b2:a0:20:00:00:10' rump.arp -n 10.0.1.10
-	atf_check -s exit:0 -o not-match:'permanent' rump.arp -n 10.0.1.10
+	$DEBUG && rump.netstat -nr -f inet
+	check_arp_static_entry 10.0.1.10 'b2:a0:20:00:00:10' temp
 
 	# Hm? the cache doesn't expire...
 	atf_check -s exit:0 sleep $(($arp_keep + $bonus))
 	$DEBUG && rump.arp -n -a
+	$DEBUG && rump.netstat -nr -f inet
 	#atf_check -s not-exit:0 -e ignore rump.arp -n 10.0.1.10
 
 	rump_server_destroy_ifaces
@@ -309,21 +338,23 @@ arp_garp_body()
 
 arp_cache_overwriting_body()
 {
-	local arp_keep=5
 	local bonus=2
 
 	rump_server_start $SOCKSRC
 	rump_server_start $SOCKDST
 
 	setup_dst_server
-	setup_src_server $arp_keep
+	setup_src_server
 
 	export RUMP_SERVER=$SOCKSRC
 
 	# Cannot overwrite a permanent cache
-	atf_check -s not-exit:0 -e match:'File exists' \
-	    rump.arp -s $IP4SRC b2:a0:20:00:00:ff
+	atf_check -s exit:0 rump.arp -s $IP4DST b2:a0:20:00:00:ff
 	$DEBUG && rump.arp -n -a
+	atf_check -s not-exit:0 -e match:'File exists' \
+	    rump.arp -s $IP4DST b2:a0:20:00:00:fe
+	# cleanup
+	atf_check -s exit:0 rump.arp -d $IP4DST
 
 	atf_check -s exit:0 -o ignore rump.ping -n -w $TIMEOUT -c 1 $IP4DST
 	$DEBUG && rump.arp -n -a
@@ -366,7 +397,6 @@ make_pkt_str_garp()
 
 test_proxy_arp()
 {
-	local arp_keep=5
 	local opts= title= flags=
 	local type=$1
 
@@ -374,7 +404,7 @@ test_proxy_arp()
 	rump_server_start $SOCKDST tap
 
 	setup_dst_server
-	setup_src_server $arp_keep
+	setup_src_server
 
 	export RUMP_SERVER=$SOCKDST
 	atf_check -s exit:0 -o ignore rump.sysctl -w net.inet.ip.forwarding=1
@@ -382,11 +412,11 @@ test_proxy_arp()
 
 	if [ "$type" = "pub" ]; then
 		opts="pub"
-		title="permanent published"
 	else
 		opts="pub proxy"
-		title='permanent published \(proxy only\)'
 	fi
+	# Always proxy only since migrating to lltable/llentry
+	title='permanent published \(proxy only\)'
 
 	#
 	# Test#1: First setup an endpoint then create proxy arp entry
@@ -412,26 +442,14 @@ test_proxy_arp()
 
 	# Try to ping
 	export RUMP_SERVER=$SOCKSRC
-	if [ "$type" = "pub" ]; then
-		# XXX fails
-		atf_check -s not-exit:0 -o ignore -e ignore \
-		    rump.ping -n -w 1 -c 1 $IP4DST_PROXYARP1
-	else
-		atf_check -s exit:0 -o ignore \
-		    rump.ping -n -w 1 -c 1 $IP4DST_PROXYARP1
-	fi
+	atf_check -s exit:0 -o ignore rump.ping -n -w 1 -c 1 $IP4DST_PROXYARP1
 
 	extract_new_packets bus1 > ./out
 	$DEBUG && cat ./out
 
 	pkt1=$(make_pkt_str_arprep $IP4DST_PROXYARP1 $macaddr_dst)
 	pkt2=$(make_pkt_str_garp $IP4DST_PROXYARP1 $macaddr_dst)
-	if [ "$type" = "pub" ]; then
-		atf_check -s not-exit:0 -x \
-		    "cat ./out |grep -q -e '$pkt1' -e '$pkt2'"
-	else
-		atf_check -s exit:0 -x "cat ./out |grep -q -e '$pkt1' -e '$pkt2'"
-	fi
+	atf_check -s exit:0 -x "cat ./out |grep -q -e '$pkt1' -e '$pkt2'"
 
 	#
 	# Test#2: Create proxy arp entry then set up an endpoint
@@ -480,14 +498,13 @@ arp_proxy_arp_pubproxy_body()
 
 arp_link_activation_body()
 {
-	local arp_keep=5
 	local bonus=2
 
 	rump_server_start $SOCKSRC
 	rump_server_start $SOCKDST
 
 	setup_dst_server
-	setup_src_server $arp_keep
+	setup_src_server
 
 	# flush old packets
 	extract_new_packets bus1 > ./out
@@ -520,14 +537,13 @@ arp_link_activation_body()
 
 arp_static_body()
 {
-	local arp_keep=5
 	local macaddr_src=
 
 	rump_server_start $SOCKSRC
 	rump_server_start $SOCKDST
 
 	setup_dst_server
-	setup_src_server $arp_keep
+	setup_src_server
 
 	macaddr_src=$(get_macaddr $SOCKSRC shmif0)
 
@@ -598,6 +614,268 @@ arp_static_cleanup()
 	cleanup
 }
 
+atf_test_case arp_rtm cleanup
+arp_rtm_head()
+{
+
+	atf_set "descr" "Tests for routing messages on operations of ARP entries"
+	atf_set "require.progs" "rump_server"
+}
+
+arp_rtm_body()
+{
+	local macaddr_src= macaddr_dst=
+	local file=./tmp
+	local pid= str=
+
+	rump_server_start $SOCKSRC
+	rump_server_start $SOCKDST
+
+	setup_dst_server
+	setup_src_server
+
+	macaddr_src=$(get_macaddr $SOCKSRC shmif0)
+	macaddr_dst=$(get_macaddr $SOCKDST shmif0)
+
+	export RUMP_SERVER=$SOCKSRC
+
+	# Test ping and a resulting routing message (RTM_ADD)
+	rump.route -n monitor -c 1 > $file &
+	pid=$?
+	sleep 1
+	atf_check -s exit:0 -o ignore rump.ping -n -w 1 -c 1 $IP4DST
+	wait $pid
+	$DEBUG && cat $file
+
+	str="RTM_ADD.+<UP,HOST,DONE,LLINFO,CLONED>"
+	atf_check -s exit:0 -o match:"$str" cat $file
+	str="<DST,GATEWAY>"
+	atf_check -s exit:0 -o match:"$str" cat $file
+	str="$IP4DST link#2"
+	atf_check -s exit:0 -o match:"$str" cat $file
+
+	# Test arp -d and resulting routing messages (RTM_DELETE)
+	rump.route -n monitor -c 2 > $file &
+	pid=$?
+	sleep 1
+	atf_check -s exit:0 -o ignore rump.arp -d $IP4DST
+	wait $pid
+	$DEBUG && cat $file
+
+	str="RTM_DELETE.+<HOST,DONE,LLINFO,CLONED>"
+	atf_check -s exit:0 -o match:"$str" grep -A 3 RTM_DELETE $file
+	str="<DST,GATEWAY>"
+	atf_check -s exit:0 -o match:"$str" grep -A 3 RTM_DELETE $file
+	str="$IP4DST $macaddr_dst"
+	atf_check -s exit:0 -o match:"$str" grep -A 3 RTM_DELETE $file
+
+	rump_server_destroy_ifaces
+}
+
+arp_rtm_cleanup()
+{
+
+	$DEBUG && dump
+	cleanup
+}
+
+atf_test_case arp_purge_on_route_change cleanup
+arp_purge_on_route_change_head()
+{
+
+	atf_set "descr" "Tests if ARP entries are removed on route change"
+	atf_set "require.progs" "rump_server"
+}
+
+arp_purge_on_route_change_body()
+{
+
+	rump_server_start $SOCKSRC
+	rump_server_start $SOCKDST
+
+	setup_dst_server
+	setup_src_server
+
+	rump_server_add_iface $SOCKSRC shmif1 bus1
+	export RUMP_SERVER=$SOCKSRC
+	atf_check -s exit:0 rump.ifconfig shmif1 inet $IP4SRC2/24
+	atf_check -s exit:0 rump.ifconfig -w 10
+
+	$DEBUG && rump.netstat -nr -f inet
+	atf_check -s exit:0 -o ignore rump.ping -n -w 1 -c 1 $IP4DST
+	$DEBUG && rump.arp -na
+	atf_check -s exit:0 -o ignore \
+	    rump.route change -net $IP4NET -ifp shmif1
+	$DEBUG && rump.netstat -nr -f inet
+	$DEBUG && rump.arp -na
+	# The entry was already removed on route change
+	atf_check -s not-exit:0 -e match:'no entry' rump.arp -n $IP4DST
+
+	rump_server_destroy_ifaces
+}
+
+arp_purge_on_route_change_cleanup()
+{
+
+	$DEBUG && dump
+	cleanup
+}
+
+atf_test_case arp_purge_on_route_delete cleanup
+arp_purge_on_route_delete_head()
+{
+
+	atf_set "descr" "Tests if ARP entries are removed on route delete"
+	atf_set "require.progs" "rump_server"
+}
+
+arp_purge_on_route_delete_body()
+{
+
+	rump_server_start $SOCKSRC
+	rump_server_start $SOCKDST
+
+	setup_dst_server
+	setup_src_server
+
+	$DEBUG && rump.netstat -nr -f inet
+	atf_check -s exit:0 -o ignore rump.ping -n -w 1 -c 1 $IP4DST
+	$DEBUG && rump.arp -na
+
+	atf_check -s exit:0 -o ignore rump.route delete -net $IP4NET
+	$DEBUG && rump.netstat -nr -f inet
+	$DEBUG && rump.arp -na
+
+	# The entry was already removed on route delete
+	atf_check -s not-exit:0 -e match:'no entry' rump.arp -n $IP4DST
+
+	rump_server_destroy_ifaces
+}
+
+arp_purge_on_route_delete_cleanup()
+{
+
+	$DEBUG && dump
+	cleanup
+}
+
+atf_test_case arp_purge_on_ifdown cleanup
+arp_purge_on_ifdown_head()
+{
+
+	atf_set "descr" "Tests if ARP entries are removed on interface down"
+	atf_set "require.progs" "rump_server"
+}
+
+arp_purge_on_ifdown_body()
+{
+
+	rump_server_start $SOCKSRC
+	rump_server_start $SOCKDST
+
+	setup_dst_server
+	setup_src_server
+
+	$DEBUG && rump.netstat -nr -f inet
+	atf_check -s exit:0 -o ignore rump.ping -n -w 1 -c 1 $IP4DST
+	atf_check -s exit:0 -o match:'shmif0' rump.arp -n $IP4DST
+
+	# Shutdown the interface
+	atf_check -s exit:0 rump.ifconfig shmif0 down
+	$DEBUG && rump.netstat -nr -f inet
+	$DEBUG && rump.arp -na
+
+	atf_check -s not-exit:0 -e match:'no entry' rump.arp -n $IP4DST
+
+	rump_server_destroy_ifaces
+}
+
+arp_purge_on_ifdown_cleanup()
+{
+
+	$DEBUG && dump
+	cleanup
+}
+
+atf_test_case arp_stray_entries cleanup
+arp_stray_entries_head()
+{
+
+	atf_set "descr" "Tests if ARP entries are removed on route change"
+	atf_set "require.progs" "rump_server"
+}
+
+arp_stray_entries_body()
+{
+
+	rump_server_start $SOCKSRC
+	rump_server_start $SOCKDST
+
+	setup_dst_server
+	setup_src_server
+
+	rump_server_add_iface $SOCKSRC shmif1 bus1
+
+	export RUMP_SERVER=$SOCKSRC
+	atf_check -s exit:0 rump.ifconfig shmif1 inet $IP4SRC2/24
+	atf_check -s exit:0 rump.ifconfig -w 10
+
+	$DEBUG && rump.netstat -nr -f inet
+	atf_check -s exit:0 -o ignore rump.ping -n -w 1 -c 1 $IP4DST
+	$DEBUG && rump.arp -na
+	atf_check -s exit:0 -o match:'shmif0' rump.arp -n $IP4DST
+	atf_check -s exit:0 -o not-match:'shmif1' rump.arp -n $IP4DST
+
+	# Clean up
+	atf_check -s exit:0 -o ignore rump.arp -da
+	atf_check -s not-exit:0 -e match:'no entry' rump.arp -n $IP4DST
+
+	# ping from a different source address
+	atf_check -s exit:0 -o ignore \
+	    rump.ping -n -w 1 -c 1 -I $IP4SRC2 $IP4DST
+	$DEBUG && rump.arp -na
+	atf_check -s exit:0 -o match:'shmif0' rump.arp -n $IP4DST
+	# ARP reply goes back via shmif1, so a cache is created on shmif1
+	atf_check -s exit:0 -o match:'shmif1' rump.arp -n $IP4DST
+
+	# Clean up by arp -da
+	atf_check -s exit:0 -o ignore rump.arp -da
+	atf_check -s not-exit:0 -e match:'no entry' rump.arp -n $IP4DST
+
+	# ping from a different source address again
+	atf_check -s exit:0 -o ignore \
+	    rump.ping -n -w 1 -c 1 -I $IP4SRC2 $IP4DST
+	atf_check -s exit:0 -o match:'shmif0' rump.arp -n $IP4DST
+	# ARP reply doen't come
+	atf_check -s exit:0 -o not-match:'shmif1' rump.arp -n $IP4DST
+
+	# Cleanup caches on the destination
+	export RUMP_SERVER=$SOCKDST
+	atf_check -s exit:0 -o ignore rump.arp -da
+	export RUMP_SERVER=$SOCKSRC
+
+	# ping from a different source address again
+	atf_check -s exit:0 -o ignore \
+	    rump.ping -n -w 1 -c 1 -I $IP4SRC2 $IP4DST
+	atf_check -s exit:0 -o match:'shmif0' rump.arp -n $IP4DST
+	# ARP reply goes back via shmif1
+	atf_check -s exit:0 -o match:'shmif1' rump.arp -n $IP4DST
+
+	# Clean up by arp -d <ip>
+	atf_check -s exit:0 -o ignore rump.arp -d $IP4DST
+	# Both entries should be deleted
+	atf_check -s not-exit:0 -e match:'no entry' rump.arp -n $IP4DST
+
+	rump_server_destroy_ifaces
+}
+
+arp_stray_entries_cleanup()
+{
+
+	$DEBUG && dump
+	cleanup
+}
+
 atf_init_test_cases()
 {
 	atf_add_test_case arp_cache_expiration_5s
@@ -609,4 +887,9 @@ atf_init_test_cases()
 	atf_add_test_case arp_proxy_arp_pubproxy
 	atf_add_test_case arp_link_activation
 	atf_add_test_case arp_static
+	atf_add_test_case arp_rtm
+	atf_add_test_case arp_purge_on_route_change
+	atf_add_test_case arp_purge_on_route_delete
+	atf_add_test_case arp_purge_on_ifdown
+	atf_add_test_case arp_stray_entries
 }

Index: src/tests/net/ndp/t_ndp.sh
diff -u src/tests/net/ndp/t_ndp.sh:1.19 src/tests/net/ndp/t_ndp.sh:1.19.2.1
--- src/tests/net/ndp/t_ndp.sh:1.19	Fri May 26 01:14:38 2017
+++ src/tests/net/ndp/t_ndp.sh	Fri Jul  7 13:57:26 2017
@@ -1,4 +1,4 @@
-#	$NetBSD: t_ndp.sh,v 1.19 2017/05/26 01:14:38 ozaki-r Exp $
+#	$NetBSD: t_ndp.sh,v 1.19.2.1 2017/07/07 13:57:26 martin Exp $
 #
 # Copyright (c) 2015 The NetBSD Foundation, Inc.
 # All rights reserved.
@@ -28,7 +28,9 @@
 SOCKSRC=unix://commsock1
 SOCKDST=unix://commsock2
 IP6SRC=fc00::1
+IP6SRC2=fc00::3
 IP6DST=fc00::2
+IP6NET=fc00::0
 
 DEBUG=${DEBUG:-false}
 TIMEOUT=1
@@ -99,8 +101,8 @@ setup_src_server()
 	# Sanity check
 	$DEBUG && rump.ifconfig shmif0
 	$DEBUG && rump.ndp -n -a
-	atf_check -s exit:0 -o ignore rump.ndp -n $IP6SRC
-	atf_check -s not-exit:0 -o ignore -e ignore rump.ndp -n $IP6DST
+	atf_check -s not-exit:0 -o ignore -e match:'no entry' rump.ndp -n $IP6SRC
+	atf_check -s not-exit:0 -o ignore -e match:'no entry' rump.ndp -n $IP6DST
 }
 
 get_timeout()
@@ -126,7 +128,7 @@ ndp_cache_expiration_body()
 	atf_check -s exit:0 -o ignore rump.ping6 -n -X $TIMEOUT -c 1 $IP6DST
 
 	$DEBUG && rump.ndp -n -a
-	atf_check -s exit:0 -o match:'permanent' rump.ndp -n $IP6SRC
+	atf_check -s not-exit:0 -o ignore -e match:'no entry' rump.ndp -n $IP6SRC
 	# Should be cached
 	atf_check -s exit:0 -o not-match:'permanent' rump.ndp -n $IP6DST
 
@@ -135,7 +137,7 @@ ndp_cache_expiration_body()
 	atf_check -s exit:0 sleep $(($timeout + 1))
 
 	$DEBUG && rump.ndp -n -a
-	atf_check -s exit:0 -o match:'permanent' rump.ndp -n $IP6SRC
+	atf_check -s not-exit:0 -o ignore -e match:'no entry' rump.ndp -n $IP6SRC
 	# Expired but remains until GC sweaps it (1 day)
 	atf_check -s exit:0 -o match:"$ONEDAYISH" rump.ndp -n $IP6DST
 
@@ -160,17 +162,16 @@ ndp_commands_body()
 
 	export RUMP_SERVER=$SOCKSRC
 
-	# We can delete the entry for the interface's IP address
-	atf_check -s exit:0 -o match:"$IP6SRC" rump.ndp -d $IP6SRC
-
 	# Add and delete a static entry
 	$DEBUG && rump.ndp -n -a
 	atf_check -s exit:0 -o ignore rump.ndp -s fc00::10 b2:a0:20:00:00:10
 	$DEBUG && rump.ndp -n -a
 	atf_check -s exit:0 -o match:'permanent' rump.ndp -n fc00::10
+	check_route fc00::10 'b2:a0:20:00:00:10' UHLS shmif0
 	atf_check -s exit:0 -o match:'deleted' rump.ndp -d fc00::10
 	$DEBUG && rump.ndp -n -a
 	atf_check -s not-exit:0 -o ignore -e ignore rump.ndp -n fc00::10
+	check_route_no_entry fc00::10
 
 	# Add multiple entries via a file (XXX not implemented)
 	#cat - > ./list <<-EOF
@@ -191,6 +192,9 @@ ndp_commands_body()
 	atf_check -s exit:0 -o not-match:'permanent' rump.ndp -n $IP6DST
 	atf_check -s exit:0 -o match:'permanent' rump.ndp -n fc00::11
 	atf_check -s exit:0 -o match:'permanent' rump.ndp -n fc00::12
+	check_route_flags $IP6DST UHL
+	check_route_flags fc00::11 UHLS
+	check_route_flags fc00::12 UHLS
 
 	# Test ndp -a
 	atf_check -s exit:0 -o match:'fc00::11' rump.ndp -n -a
@@ -204,15 +208,20 @@ ndp_commands_body()
 	atf_check -s exit:0 -o ignore rump.ndp -c
 	atf_check -s not-exit:0 -o ignore -e ignore rump.ndp -n $IP6SRC
 	atf_check -s not-exit:0 -o ignore -e ignore rump.ndp -n $IP6DST
+	#check_route_no_entry $IP6SRC
+	check_route_no_entry $IP6DST
 	# Only the static caches are not deleted
 	atf_check -s exit:0 -o ignore -e ignore rump.ndp -n fc00::11
 	atf_check -s exit:0 -o ignore -e ignore rump.ndp -n fc00::12
+	check_route_flags fc00::11 UHLS
+	check_route_flags fc00::12 UHLS
 
 	$DEBUG && rump.ndp -n -a
 	atf_check -s exit:0 -o ignore rump.ndp -s fc00::10 b2:a0:20:00:00:10 temp
 	rump.ndp -s fc00::10 b2:a0:20:00:00:10 temp
 	$DEBUG && rump.ndp -n -a
 	atf_check -s exit:0 -o not-match:'permanent' rump.ndp -n fc00::10
+	check_route fc00::10 'b2:a0:20:00:00:10' UHL shmif0
 
 	rump_server_destroy_ifaces
 }
@@ -229,8 +238,9 @@ ndp_cache_overwriting_body()
 	export RUMP_SERVER=$SOCKSRC
 
 	# Cannot overwrite a permanent cache
-	atf_check -s not-exit:0 -e ignore rump.ndp -s $IP6SRC b2:a0:20:00:00:ff
+	atf_check -s exit:0 rump.ndp -s $IP6SRC b2:a0:20:00:00:ff
 	$DEBUG && rump.ndp -n -a
+	atf_check -s not-exit:0 -e ignore rump.ndp -s $IP6SRC b2:a0:20:00:00:fe
 
 	atf_check -s exit:0 -o ignore rump.ping6 -n -X $TIMEOUT -c 1 $IP6DST
 	$DEBUG && rump.ndp -n -a
@@ -396,6 +406,275 @@ ndp_link_activation_cleanup()
 	cleanup
 }
 
+atf_test_case ndp_rtm cleanup
+ndp_rtm_head()
+{
+
+	atf_set "descr" "Tests for routing messages on operations of NDP entries"
+	atf_set "require.progs" "rump_server"
+}
+
+ndp_rtm_body()
+{
+	local macaddr_src= macaddr_dst=
+	local file=./tmp
+	local pid= str=
+
+	rump_server_start $SOCKSRC netinet6
+	rump_server_start $SOCKDST netinet6
+
+	setup_dst_server
+	setup_src_server
+
+	macaddr_src=$(get_macaddr $SOCKSRC shmif0)
+	macaddr_dst=$(get_macaddr $SOCKDST shmif0)
+
+	export RUMP_SERVER=$SOCKSRC
+
+	# Test ping and a resulting routing message (RTM_ADD)
+	rump.route -n monitor -c 1 > $file &
+	pid=$?
+	sleep 1
+	atf_check -s exit:0 -o ignore rump.ping6 -n -X 1 -c 1 $IP6DST
+	wait $pid
+	$DEBUG && cat $file
+
+	str="RTM_ADD.+<UP,HOST,DONE,LLINFO,CLONED>"
+	atf_check -s exit:0 -o match:"$str" cat $file
+	str="<DST,GATEWAY>"
+	atf_check -s exit:0 -o match:"$str" cat $file
+	str="$IP6DST link#2"
+	atf_check -s exit:0 -o match:"$str" cat $file
+
+	# Test ndp -d and resulting routing messages (RTM_DELETE)
+	rump.route -n monitor -c 2 > $file &
+	pid=$?
+	sleep 1
+	atf_check -s exit:0 -o ignore rump.ndp -d $IP6DST
+	wait $pid
+	$DEBUG && cat $file
+
+	str="RTM_DELETE.+<HOST,DONE,LLINFO,CLONED>"
+	atf_check -s exit:0 -o match:"$str" grep -A 3 RTM_DELETE $file
+	str="<DST,GATEWAY>"
+	atf_check -s exit:0 -o match:"$str" grep -A 3 RTM_DELETE $file
+	str="$IP6DST $macaddr_dst"
+	atf_check -s exit:0 -o match:"$str" grep -A 3 RTM_DELETE $file
+
+	rump_server_destroy_ifaces
+}
+
+ndp_rtm_cleanup()
+{
+
+	$DEBUG && dump
+	cleanup
+}
+
+atf_test_case ndp_purge_on_route_change cleanup
+ndp_purge_on_route_change_head()
+{
+
+	atf_set "descr" "Tests if NDP entries are removed on route change"
+	atf_set "require.progs" "rump_server"
+}
+
+ndp_purge_on_route_change_body()
+{
+
+	rump_server_start $SOCKSRC netinet6
+	rump_server_start $SOCKDST netinet6
+
+	setup_dst_server
+	setup_src_server
+
+	rump_server_add_iface $SOCKSRC shmif1 bus1
+	export RUMP_SERVER=$SOCKSRC
+	atf_check -s exit:0 rump.ifconfig shmif1 inet6 $IP6SRC2
+	atf_check -s exit:0 rump.ifconfig -w 10
+
+	$DEBUG && rump.netstat -nr -f inet6
+	atf_check -s exit:0 -o ignore rump.ping6 -n -X 1 -c 1 $IP6DST
+	atf_check -s exit:0 -o match:'shmif0' rump.ndp -n $IP6DST
+
+	atf_check -s exit:0 -o ignore \
+	    rump.route change -inet6 -net $IP6NET/64 -ifp shmif1
+	$DEBUG && rump.netstat -nr -f inet6
+	$DEBUG && rump.ndp -na
+	# The entry was already removed on route change
+	atf_check -s not-exit:0 -o ignore -e match:'no entry' \
+	    rump.ndp -n $IP6DST
+
+	rump_server_destroy_ifaces
+}
+
+ndp_purge_on_route_change_cleanup()
+{
+
+	$DEBUG && dump
+	cleanup
+}
+
+atf_test_case ndp_purge_on_route_delete cleanup
+ndp_purge_on_route_delete_head()
+{
+
+	atf_set "descr" "Tests if NDP entries are removed on route delete"
+	atf_set "require.progs" "rump_server"
+}
+
+ndp_purge_on_route_delete_body()
+{
+
+	rump_server_start $SOCKSRC netinet6
+	rump_server_start $SOCKDST netinet6
+
+	setup_dst_server
+	setup_src_server
+
+	$DEBUG && rump.netstat -nr -f inet6
+	atf_check -s exit:0 -o ignore rump.ping6 -n -X 1 -c 1 $IP6DST
+	atf_check -s exit:0 -o match:'shmif0' rump.ndp -n $IP6DST
+
+	atf_check -s exit:0 -o ignore rump.route delete -inet6 -net $IP6NET/64
+	$DEBUG && rump.netstat -nr -f inet6
+	$DEBUG && rump.ndp -na
+
+	# The entry was already removed on route delete
+	atf_check -s not-exit:0 -o ignore -e match:'no entry' \
+	    rump.ndp -n $IP6DST
+
+	rump_server_destroy_ifaces
+}
+
+ndp_purge_on_route_delete_cleanup()
+{
+
+	$DEBUG && dump
+	cleanup
+}
+
+atf_test_case ndp_purge_on_ifdown cleanup
+ndp_purge_on_ifdown_head()
+{
+
+	atf_set "descr" "Tests if NDP entries are removed on interface down"
+	atf_set "require.progs" "rump_server"
+}
+
+ndp_purge_on_ifdown_body()
+{
+
+	rump_server_start $SOCKSRC netinet6
+	rump_server_start $SOCKDST netinet6
+
+	setup_dst_server
+	setup_src_server
+
+	$DEBUG && rump.netstat -nr -f inet6
+	atf_check -s exit:0 -o ignore rump.ping6 -n -X 1 -c 1 $IP6DST
+	atf_check -s exit:0 -o match:'shmif0' rump.ndp -n $IP6DST
+
+	# Shutdown the interface
+	atf_check -s exit:0 rump.ifconfig shmif0 down
+	$DEBUG && rump.netstat -nr -f inet6
+	$DEBUG && rump.ndp -na
+
+	# The entry was already removed on ifconfig down
+	atf_check -s not-exit:0 -o ignore -e match:'no entry' \
+	    rump.ndp -n $IP6DST
+
+	rump_server_destroy_ifaces
+}
+
+ndp_purge_on_ifdown_cleanup()
+{
+
+	$DEBUG && dump
+	cleanup
+}
+
+atf_test_case ndp_stray_entries cleanup
+ndp_stray_entries_head()
+{
+
+	atf_set "descr" "Tests if NDP entries are removed on route change"
+	atf_set "require.progs" "rump_server"
+}
+
+ndp_stray_entries_body()
+{
+
+	rump_server_start $SOCKSRC netinet6
+	rump_server_start $SOCKDST netinet6
+
+	setup_dst_server
+	setup_src_server
+
+	rump_server_add_iface $SOCKSRC shmif1 bus1
+
+	export RUMP_SERVER=$SOCKSRC
+	atf_check -s exit:0 rump.ifconfig shmif1 inet6 $IP6SRC2/64
+	atf_check -s exit:0 rump.ifconfig -w 10
+
+	$DEBUG && rump.netstat -nr -f inet6
+	atf_check -s exit:0 -o ignore rump.ping6 -n -X 1 -c 1 $IP6DST
+	$DEBUG && rump.ndp -na
+	atf_check -s exit:0 -o match:'shmif0' rump.ndp -n $IP6DST
+	atf_check -s exit:0 -o not-match:'shmif1' rump.ndp -n $IP6DST
+
+	# Clean up
+	atf_check -s exit:0 -o ignore rump.ndp -c
+	atf_check -s not-exit:0 -o ignore -e match:'no entry' rump.ndp -n $IP6DST
+
+	# ping from a different source address
+	atf_check -s exit:0 -o ignore \
+	    rump.ping6 -n -X 1 -c 1 -S $IP6SRC2 $IP6DST
+	$DEBUG && rump.ndp -na
+	atf_check -s exit:0 -o match:'shmif0' rump.ndp -n $IP6DST
+	# ARP reply goes back via shmif1, so a cache is created on shmif1
+	atf_check -s exit:0 -o match:'shmif1' rump.ndp -n $IP6DST
+
+	# Clean up by ndp -c
+	atf_check -s exit:0 -o ignore rump.ndp -c
+	atf_check -s not-exit:0 -o ignore -e match:'no entry' rump.ndp -n $IP6DST
+
+	# ping from a different source address again
+	atf_check -s exit:0 -o ignore \
+	    rump.ping6 -n -X 1 -c 1 -S $IP6SRC2 $IP6DST
+	atf_check -s exit:0 -o match:'shmif0' rump.ndp -n $IP6DST
+	# ARP reply doen't come
+	atf_check -s exit:0 -o not-match:'shmif1' rump.ndp -n $IP6DST
+
+	# Cleanup caches on the destination
+	export RUMP_SERVER=$SOCKDST
+	$DEBUG && rump.ndp -na
+	atf_check -s exit:0 -o ignore rump.ndp -c
+	$DEBUG && rump.ndp -na
+	export RUMP_SERVER=$SOCKSRC
+
+	# ping from a different source address again
+	atf_check -s exit:0 -o ignore \
+	    rump.ping6 -n -X 1 -c 1 -S $IP6SRC2 $IP6DST
+	atf_check -s exit:0 -o match:'shmif0' rump.ndp -n $IP6DST
+	# ARP reply goes back via shmif1
+	atf_check -s exit:0 -o match:'shmif1' rump.ndp -n $IP6DST
+
+	# Clean up by ndp -d <ip>
+	atf_check -s exit:0 -o ignore rump.ndp -d $IP6DST
+	# Both entries should be deleted
+	atf_check -s not-exit:0 -o ignore -e match:'no entry' rump.ndp -n $IP6DST
+
+	rump_server_destroy_ifaces
+}
+
+ndp_stray_entries_cleanup()
+{
+
+	$DEBUG && dump
+	cleanup
+}
+
 atf_init_test_cases()
 {
 	atf_add_test_case ndp_cache_expiration
@@ -403,4 +682,9 @@ atf_init_test_cases()
 	atf_add_test_case ndp_cache_overwriting
 	atf_add_test_case ndp_neighborgcthresh
 	atf_add_test_case ndp_link_activation
+	atf_add_test_case ndp_rtm
+	atf_add_test_case ndp_purge_on_route_change
+	atf_add_test_case ndp_purge_on_route_delete
+	atf_add_test_case ndp_purge_on_ifdown
+	atf_add_test_case ndp_stray_entries
 }

Index: src/tests/net/ndp/t_ra.sh
diff -u src/tests/net/ndp/t_ra.sh:1.27 src/tests/net/ndp/t_ra.sh:1.27.2.1
--- src/tests/net/ndp/t_ra.sh:1.27	Fri May 26 01:14:38 2017
+++ src/tests/net/ndp/t_ra.sh	Fri Jul  7 13:57:26 2017
@@ -1,4 +1,4 @@
-#	$NetBSD: t_ra.sh,v 1.27 2017/05/26 01:14:38 ozaki-r Exp $
+#	$NetBSD: t_ra.sh,v 1.27.2.1 2017/07/07 13:57:26 martin Exp $
 #
 # Copyright (c) 2015 Internet Initiative Japan Inc.
 # All rights reserved.
@@ -149,7 +149,7 @@ check_entries()
 	atf_check -s exit:0 \
 	    -o match:"$ll_srv%shmif0 +$mac_srv +shmif0 +$ONEDAYISH S R" \
 	    rump.ndp -n -a
-	atf_check -s exit:0 -o match:$addr_prefix rump.ndp -n -a
+	atf_check -s exit:0 -o not-match:$addr_prefix rump.ndp -n -a
 	atf_check -s exit:0 \
 	    -o match:"$addr_prefix.+<(TENTATIVE,)?AUTOCONF>" \
 	    rump.ifconfig shmif0 inet6
@@ -277,7 +277,7 @@ ra_flush_prefix_entries_body()
 	atf_check -s exit:0 -o empty rump.ndp -p
 	atf_check -s exit:0 -o match:'linkmtu=1300' rump.ndp -n -i shmif0
 	atf_check -s exit:0 -o match:"$ONEDAYISH S R" rump.ndp -n -a
-	atf_check -s exit:0 -o match:'fc00:1:' rump.ndp -n -a
+	atf_check -s exit:0 -o not-match:'fc00:1:' rump.ndp -n -a
 	atf_check -s exit:0 -o not-match:'fc00:1:' rump.ifconfig shmif0 inet6
 	unset RUMP_SERVER
 
@@ -334,8 +334,7 @@ ra_flush_defrouter_entries_body()
 	atf_check -s exit:0 -o empty rump.ndp -r
 	atf_check -s exit:0 -o match:'No advertising router' rump.ndp -p
 	atf_check -s exit:0 -o match:'linkmtu=1300' rump.ndp -n -i shmif0
-	atf_check -s exit:0 -o match:"$ONEDAYISH S R" rump.ndp -n -a
-	atf_check -s exit:0 -o match:'fc00:1:' rump.ndp -n -a
+	atf_check -s exit:0 -o not-match:'fc00:1:' rump.ndp -n -a
 	atf_check -s exit:0 -o match:'fc00:1:' rump.ifconfig shmif0 inet6
 	unset RUMP_SERVER
 
@@ -762,8 +761,7 @@ ra_defrouter_expiration_body()
 	atf_check -s exit:0 -o not-match:'if=shmif0' rump.ndp -r
 	atf_check -s exit:0 -o match:'No advertising router' rump.ndp -p
 	atf_check -s exit:0 -o match:'linkmtu=1300' rump.ndp -n -i shmif0
-	atf_check -s exit:0 -o match:"$ONEDAYISH S R" rump.ndp -n -a
-	atf_check -s exit:0 -o match:'fc00:1:' rump.ndp -n -a
+	atf_check -s exit:0 -o not-match:'fc00:1:' rump.ndp -n -a
 	atf_check -s exit:0 -o match:'fc00:1:' rump.ifconfig shmif0 inet6
 	unset RUMP_SERVER
 
@@ -828,7 +826,7 @@ ra_prefix_expiration_body()
 	atf_check -s exit:0 -o empty rump.ndp -p
 	atf_check -s exit:0 -o match:'linkmtu=1300' rump.ndp -n -i shmif0
 	atf_check -s exit:0 -o match:"$ONEDAYISH S R" rump.ndp -n -a
-	atf_check -s exit:0 -o match:'fc00:1:' rump.ndp -n -a
+	atf_check -s exit:0 -o not-match:'fc00:1:' rump.ndp -n -a
 	atf_check -s exit:0 -o not-match:'fc00:1:' rump.ifconfig shmif0 inet6
 	unset RUMP_SERVER
 

Index: src/tests/net/net/t_ipv6address.sh
diff -u src/tests/net/net/t_ipv6address.sh:1.13 src/tests/net/net/t_ipv6address.sh:1.13.2.1
--- src/tests/net/net/t_ipv6address.sh:1.13	Fri May 26 01:14:38 2017
+++ src/tests/net/net/t_ipv6address.sh	Fri Jul  7 13:57:26 2017
@@ -1,4 +1,4 @@
-#	$NetBSD: t_ipv6address.sh,v 1.13 2017/05/26 01:14:38 ozaki-r Exp $
+#	$NetBSD: t_ipv6address.sh,v 1.13.2.1 2017/07/07 13:57:26 martin Exp $
 #
 # Copyright (c) 2015 Internet Initiative Japan Inc.
 # All rights reserved.
@@ -364,7 +364,7 @@ linklocal_ops_body()
 	    rump.route get -inet6 ${src_if0_lladdr}
 
 	# ndp
-	atf_check -s exit:0 -o match:"${src_if0_lladdr}" \
+	atf_check -s not-exit:0 -o ignore -e match:'no entry' \
 	    rump.ndp -n ${src_if0_lladdr}%shmif0
 
 	# ndp without an interface name (zone index)

Index: src/tests/net/route/t_flags.sh
diff -u src/tests/net/route/t_flags.sh:1.15 src/tests/net/route/t_flags.sh:1.15.6.1
--- src/tests/net/route/t_flags.sh:1.15	Wed Dec 21 02:46:08 2016
+++ src/tests/net/route/t_flags.sh	Fri Jul  7 13:57:26 2017
@@ -1,4 +1,4 @@
-#	$NetBSD: t_flags.sh,v 1.15 2016/12/21 02:46:08 ozaki-r Exp $
+#	$NetBSD: t_flags.sh,v 1.15.6.1 2017/07/07 13:57:26 martin Exp $
 #
 # Copyright (c) 2015 The NetBSD Foundation, Inc.
 # All rights reserved.
@@ -100,7 +100,7 @@ test_connected()
 
 	export RUMP_SERVER=$SOCK_LOCAL
 
-	# Up, Host, LLINFO, local
+	# Up, Host, local
 	check_route_flags 10.0.0.2 UHl
 
 	# Up, Cloning
@@ -148,6 +148,8 @@ test_blackhole()
 
 	# Delete an existing route first
 	atf_check -s exit:0 -o ignore rump.route delete -net 10.0.0.0/24
+	# Should be removed too
+	atf_check -s not-exit:0 -e match:'no entry' rump.arp -n 10.0.0.1
 
 	# Gateway must be lo0
 	atf_check -s exit:0 -o ignore \
@@ -163,6 +165,7 @@ test_blackhole()
 
 	# Shouldn't be created
 	check_route_no_entry 10.0.0.1
+	atf_check -s not-exit:0 -e match:'no entry' rump.arp -n 10.0.0.1
 }
 
 test_reject()
@@ -185,6 +188,7 @@ test_reject()
 
 	# Shouldn't be created
 	check_route_no_entry 10.0.0.1
+	atf_check -s not-exit:0 -e match:'no entry' rump.arp -n 10.0.0.1
 
 	# Gateway is lo0 (RTF_GATEWAY)
 
@@ -204,6 +208,7 @@ test_reject()
 
 	# Shouldn't be created
 	check_route_no_entry 10.0.0.1
+	atf_check -s not-exit:0 -e match:'no entry' rump.arp -n 10.0.0.1
 
 	# Gateway is lo0 (RTF_HOST)
 
@@ -297,6 +302,20 @@ test_announce()
 	# TODO test its behavior
 }
 
+test_llinfo()
+{
+	local peer_macaddr=
+
+	peer_macaddr=$(get_macaddr $SOCK_PEER shmif0)
+
+	export RUMP_SERVER=$SOCK_LOCAL
+
+	atf_check -s exit:0 -o ignore rump.ping -n -w 1 -c 1 10.0.0.1
+
+	# Up, Host, LLINFO
+	check_route 10.0.0.1 $peer_macaddr UHL shmif0
+}
+
 add_test()
 {
 	local name=$1
@@ -331,4 +350,5 @@ atf_init_test_cases()
 	add_test reject          "Tests route flags: reject route"
 	add_test icmp_redirect   "Tests route flags: icmp redirect"
 	add_test announce        "Tests route flags: announce flag"
+	add_test llinfo          "Tests route flags: ARP caches"
 }

Index: src/tests/net/route/t_flags6.sh
diff -u src/tests/net/route/t_flags6.sh:1.12 src/tests/net/route/t_flags6.sh:1.12.6.1
--- src/tests/net/route/t_flags6.sh:1.12	Wed Dec 21 02:46:08 2016
+++ src/tests/net/route/t_flags6.sh	Fri Jul  7 13:57:26 2017
@@ -1,4 +1,4 @@
-#	$NetBSD: t_flags6.sh,v 1.12 2016/12/21 02:46:08 ozaki-r Exp $
+#	$NetBSD: t_flags6.sh,v 1.12.6.1 2017/07/07 13:57:26 martin Exp $
 #
 # Copyright (c) 2016 Internet Initiative Japan Inc.
 # All rights reserved.
@@ -134,6 +134,9 @@ test_blackhole6()
 	# Delete an existing route first
 	atf_check -s exit:0 -o ignore \
 	    rump.route delete -inet6 -net fc00::/64
+	# Should be removed too
+	atf_check -s not-exit:0 -o ignore -e match:'no entry' \
+	    rump.ndp -n $IP6_PEER
 
 	# Gateway must be lo0
 	atf_check -s exit:0 -o ignore \
@@ -149,6 +152,8 @@ test_blackhole6()
 
 	# Shouldn't be created
 	check_route_no_entry $IP6_PEER
+	atf_check -s not-exit:0 -o ignore -e match:'no entry' \
+	    rump.ndp -n $IP6_PEER
 }
 
 test_reject6()
@@ -173,6 +178,8 @@ test_reject6()
 
 	# Shouldn't be created
 	check_route_no_entry $IP6_PEER
+	atf_check -s not-exit:0 -o ignore -e match:'no entry' \
+	    rump.ndp -n $IP6_PEER
 
 	# Gateway is lo0 (RTF_GATEWAY)
 
@@ -193,6 +200,8 @@ test_reject6()
 
 	# Shouldn't be created
 	check_route_no_entry $IP6_PEER
+	atf_check -s not-exit:0 -o ignore -e match:'no entry' \
+	    rump.ndp -n $IP6_PEER
 
 	# Gateway is lo0 (RTF_HOST)
 
@@ -232,6 +241,20 @@ test_announce6()
 	# TODO test its behavior
 }
 
+test_llinfo6()
+{
+	local peer_macaddr=
+
+	peer_macaddr=$(get_macaddr $SOCK_PEER shmif0)
+
+	export RUMP_SERVER=$SOCK_LOCAL
+
+	atf_check -s exit:0 -o ignore rump.ping6 -n -X 1 -c 1 $IP6_PEER
+
+	# Up, Host, LLINFO
+	check_route $IP6_PEER $peer_macaddr UHL shmif0
+}
+
 add_test()
 {
 	local name=$1
@@ -265,4 +288,5 @@ atf_init_test_cases()
 	add_test blackhole6       "Tests route flags: blackhole route"
 	add_test reject6          "Tests route flags: reject route"
 	add_test announce6        "Tests route flags: announce flag"
+	add_test llinfo6          "Tests route flags: announce llinfo"
 }

Index: src/tests/net/route/t_route.sh
diff -u src/tests/net/route/t_route.sh:1.12 src/tests/net/route/t_route.sh:1.12.4.1
--- src/tests/net/route/t_route.sh:1.12	Fri Mar 24 03:47:25 2017
+++ src/tests/net/route/t_route.sh	Fri Jul  7 13:57:26 2017
@@ -1,4 +1,4 @@
-#	$NetBSD: t_route.sh,v 1.12 2017/03/24 03:47:25 ozaki-r Exp $
+#	$NetBSD: t_route.sh,v 1.12.4.1 2017/07/07 13:57:26 martin Exp $
 #
 # Copyright (c) 2016 Internet Initiative Japan Inc.
 # All rights reserved.
@@ -234,7 +234,7 @@ test_route_get()
 destination: 10.0.1.2
  local addr: 10.0.1.2
   interface: lo0
-      flags: <UP,HOST,DONE,LLINFO,LOCAL>
+      flags: <UP,HOST,DONE,LOCAL>
  recvpipe  sendpipe  ssthresh  rtt,msec    rttvar  hopcount      mtu     expire
 	EOF
 	rump.route -n get $IP4SRC > ./output
@@ -309,7 +309,7 @@ test_route_get6()
 destination: fc00:0:0:1::2
  local addr: fc00:0:0:1::2
   interface: lo0
-      flags: <UP,HOST,DONE,LLINFO,LOCAL>
+      flags: <UP,HOST,DONE,LOCAL>
  recvpipe  sendpipe  ssthresh  rtt,msec    rttvar  hopcount      mtu     expire
 	EOF
 	rump.route -n get -inet6 $IP6SRC > ./output

Index: src/usr.sbin/arp/arp.c
diff -u src/usr.sbin/arp/arp.c:1.55 src/usr.sbin/arp/arp.c:1.55.8.1
--- src/usr.sbin/arp/arp.c:1.55	Mon Apr  4 07:37:08 2016
+++ src/usr.sbin/arp/arp.c	Fri Jul  7 13:57:26 2017
@@ -1,4 +1,4 @@
-/*	$NetBSD: arp.c,v 1.55 2016/04/04 07:37:08 ozaki-r Exp $ */
+/*	$NetBSD: arp.c,v 1.55.8.1 2017/07/07 13:57:26 martin Exp $ */
 
 /*
  * Copyright (c) 1984, 1993
@@ -42,7 +42,7 @@ __COPYRIGHT("@(#) Copyright (c) 1984, 19
 #if 0
 static char sccsid[] = "@(#)arp.c	8.3 (Berkeley) 4/28/95";
 #else
-__RCSID("$NetBSD: arp.c,v 1.55 2016/04/04 07:37:08 ozaki-r Exp $");
+__RCSID("$NetBSD: arp.c,v 1.55.8.1 2017/07/07 13:57:26 martin Exp $");
 #endif
 #endif /* not lint */
 
@@ -79,9 +79,9 @@ __RCSID("$NetBSD: arp.c,v 1.55 2016/04/0
 #include "prog_ops.h"
 
 static int is_llinfo(const struct sockaddr_dl *, int);
-static int delete(const char *, const char *);
+static int delete_one(struct rt_msghdr *);
 static void dump(uint32_t);
-static void delete_all(void);
+static void delete(const char *, const char *);
 static void sdl_print(const struct sockaddr_dl *);
 static int getifname(u_int16_t, char *, size_t);
 static int atosdl(const char *s, struct sockaddr_dl *sdl);
@@ -90,13 +90,13 @@ static void get(const char *);
 static int getinetaddr(const char *, struct in_addr *);
 static int getsocket(void);
 static struct rt_msghdr *
-	rtmsg(const int, const int, const struct sockaddr_inarp *,
-	    const struct sockaddr_dl *);
+	rtmsg(const int, const int,  struct rt_msghdr *,
+	    const struct sockaddr_inarp *, const struct sockaddr_dl *);
 static int set(int, char **);
 static void usage(void) __dead;
 
 static int aflag, nflag, vflag;
-static struct sockaddr_in so_mask = { 
+static struct sockaddr_in so_mask = {
 	.sin_len = 8,
 	.sin_addr = {
 		.s_addr = 0xffffffff
@@ -157,11 +157,11 @@ main(int argc, char **argv)
 		break;
 	case 'd':
 		if (aflag && argc == 0)
-			delete_all();
+			delete(NULL, NULL);
 		else {
 			if (aflag || argc < 1 || argc > 2)
 				usage();
-			(void)delete(argv[0], argv[1]);
+			delete(argv[0], argv[1]);
 		}
 		break;
 	case 's':
@@ -206,7 +206,7 @@ file(const char *name)
 		inputstring = line;
 		for (ap = argv; ap < &argv[sizeof(argv) / sizeof(argv[0])] &&
 		    (*ap = stresep(&inputstring, " \t", '\\')) != NULL;) {
-		       if (**ap != '\0')
+			if (**ap != '\0')
 				ap++;
 		}
 		i = ap - argv;
@@ -257,7 +257,7 @@ getlink(const char *name, struct sockadd
 }
 
 /*
- * Set an individual arp entry 
+ * Set an individual arp entry
  */
 static int
 set(int argc, char **argv)
@@ -291,8 +291,8 @@ set(int argc, char **argv)
 			flags |= RTF_ANNOUNCE;
 			doing_proxy = 1;
 			if (argc && strncmp(argv[1], "pro", 3) == 0) {
-			        export_only = 1;
-			        argc--; argv++;
+				export_only = 1;
+				argc--; argv++;
 			}
 		} else if (strncmp(argv[0], "trail", 5) == 0) {
 			warnx("%s: Sending trailers is no longer supported",
@@ -310,7 +310,7 @@ set(int argc, char **argv)
 
 	}
 tryagain:
-	rtm = rtmsg(s, RTM_GET, &sin_m, &sdl_m);
+	rtm = rtmsg(s, RTM_GET, NULL, &sin_m, &sdl_m);
 	if (rtm == NULL) {
 		warn("%s", host);
 		return (1);
@@ -344,7 +344,7 @@ overwrite:
 	sin_m.sin_other = 0;
 	if (doing_proxy && export_only)
 		sin_m.sin_other = SIN_PROXY;
-	rtm = rtmsg(s, RTM_ADD, &sin_m, &sdl_m);
+	rtm = rtmsg(s, RTM_ADD, NULL, &sin_m, &sdl_m);
 	if (vflag)
 		(void)printf("%s (%s) added\n", host, eaddr);
 	return (rtm == NULL) ? 1 : 0;
@@ -387,53 +387,24 @@ is_llinfo(const struct sockaddr_dl *sdl,
 }
 
 /*
- * Delete an arp entry 
+ * Delete an arp entry
  */
 int
-delete(const char *host, const char *info)
+delete_one(struct rt_msghdr *rtm)
 {
 	struct sockaddr_inarp *sina;
 	struct sockaddr_dl *sdl;
-	struct rt_msghdr *rtm;
-	struct sockaddr_inarp sin_m = blank_sin; /* struct copy */
-	struct sockaddr_dl sdl_m = blank_sdl; /* struct copy */
 	int s;
 
 	s = getsocket();
-	if (info && strncmp(info, "pro", 3) == 0)
-		 sin_m.sin_other = SIN_PROXY;
-	if (getinetaddr(host, &sin_m.sin_addr) == -1)
-		return (1);
-tryagain:
-	rtm = rtmsg(s, RTM_GET, &sin_m, &sdl_m);
-	if (rtm == NULL) {
-		warn("%s", host);
-		return (1);
-	}
 	sina = (struct sockaddr_inarp *)(void *)(rtm + 1);
 	sdl = (struct sockaddr_dl *)(void *)(RT_ROUNDUP(sina->sin_len) +
 	    (char *)(void *)sina);
-	if (sina->sin_addr.s_addr == sin_m.sin_addr.s_addr &&
-	    is_llinfo(sdl, rtm->rtm_flags))
-		goto delete;
-	if (sin_m.sin_other & SIN_PROXY) {
-		warnx("delete: can't locate %s", host);
-		return (1);
-	} else {
-		sin_m.sin_other = SIN_PROXY;
-		goto tryagain;
-	}
-delete:
-	if (sdl->sdl_family != AF_LINK) {
-		(void)warnx("cannot locate %s", host);
+	if (sdl->sdl_family != AF_LINK)
 		return (1);
-	}
-	rtm = rtmsg(s, RTM_DELETE, &sin_m, sdl);
+	rtm = rtmsg(s, RTM_DELETE, rtm, sina, sdl);
 	if (rtm == NULL)
 		return (1);
-	if (vflag)
-		(void)printf("%s (%s) deleted\n", host,
-		    inet_ntoa(sin_m.sin_addr));
 	return (0);
 }
 
@@ -519,36 +490,56 @@ dump(uint32_t addr)
  * Delete the entire arp table
  */
 void
-delete_all(void)
+delete(const char *host, const char *info)
 {
 	int mib[6];
 	size_t needed;
-	char addr[sizeof("000.000.000.000\0")];
 	char *lim, *buf, *next;
 	struct rt_msghdr *rtm;
 	struct sockaddr_inarp *sina;
+	struct sockaddr_inarp sin_m = blank_sin; /* struct copy */
+
+	if (host != NULL) {
+		int ret = getinetaddr(host, &sin_m.sin_addr);
+		if (ret == -1)
+			return;
+	}
+	if (info && strncmp(info, "pro", 3) == 0)
+		sin_m.sin_other = SIN_PROXY;
 
+retry:
 	mib[0] = CTL_NET;
 	mib[1] = PF_ROUTE;
 	mib[2] = 0;
 	mib[3] = AF_INET;
 	mib[4] = NET_RT_FLAGS;
-	mib[5] = 0;
+	mib[5] = RTF_LLDATA;
 	if (prog_sysctl(mib, 6, NULL, &needed, NULL, 0) < 0)
 		err(1, "route-sysctl-estimate");
 	if (needed == 0)
 		return;
 	if ((buf = malloc(needed)) == NULL)
 		err(1, "malloc");
-	if (prog_sysctl(mib, 6, buf, &needed, NULL, 0) < 0)
+	if (prog_sysctl(mib, 6, buf, &needed, NULL, 0) < 0) {
+		free(buf);
+		if (errno == ENOBUFS)
+			goto retry;
 		err(1, "actual retrieval of routing table");
+	}
 	lim = buf + needed;
+
 	for (next = buf; next < lim; next += rtm->rtm_msglen) {
+		int ret;
 		rtm = (struct rt_msghdr *)(void *)next;
 		sina = (struct sockaddr_inarp *)(void *)(rtm + 1);
-		(void)snprintf(addr, sizeof(addr), "%s",
-		    inet_ntoa(sina->sin_addr));
-		(void)delete(addr, NULL);
+		if (host != NULL &&
+		    sina->sin_addr.s_addr != sin_m.sin_addr.s_addr)
+			continue;
+		ret = delete_one(rtm);
+		if (vflag && ret == 0) {
+			(void)printf("%s (%s) deleted\n", host,
+			    inet_ntoa(sina->sin_addr));
+		}
 	}
 	free(buf);
 }
@@ -572,13 +563,13 @@ atosdl(const char *ss, struct sockaddr_d
 	int i;
 	unsigned long b;
 	char *endp;
-	char *p; 
+	char *p;
 	char *t, *r;
 
 	p = LLADDR(sdl);
 	endp = ((char *)(void *)sdl) + sdl->sdl_len;
 	i = 0;
-	
+
 	b = strtoul(ss, &t, 16);
 	if (b > 255 || t == ss)
 		return 1;
@@ -605,8 +596,8 @@ usage(void)
 
 	progname = getprogname();
 	(void)fprintf(stderr, "Usage: %s [-n] hostname\n", progname);
-	(void)fprintf(stderr, "	      %s [-nv] -a\n", progname);
-	(void)fprintf(stderr, "	      %s [-v] -d [-a|hostname [pub [proxy]]]\n",
+	(void)fprintf(stderr, "       %s [-nv] -a\n", progname);
+	(void)fprintf(stderr, "       %s [-v] -d [-a|hostname [pub [proxy]]]\n",
 	    progname);
 	(void)fprintf(stderr, "       %s -s hostname ether_addr [temp] [pub [proxy]]\n",
 	    progname);
@@ -615,11 +606,11 @@ usage(void)
 }
 
 static struct rt_msghdr *
-rtmsg(const int s, const int cmd, const struct sockaddr_inarp *sin,
-    const struct sockaddr_dl *sdl)
+rtmsg(const int s, const int cmd, struct rt_msghdr *_rtm,
+    const struct sockaddr_inarp *sin, const struct sockaddr_dl *sdl)
 {
 	static int seq;
-	struct rt_msghdr *rtm;
+	struct rt_msghdr *rtm = _rtm;
 	char *cp;
 	int l;
 	static struct {
@@ -628,16 +619,16 @@ rtmsg(const int s, const int cmd, const 
 	} m_rtmsg;
 	pid_t pid;
 
-	rtm = &m_rtmsg.m_rtm;
-	cp = m_rtmsg.m_space;
 	errno = 0;
-
-	/* XXX depends on rtm is filled by RTM_GET */
-	if (cmd == RTM_DELETE) {
-		rtm->rtm_flags |= RTF_LLDATA;
+	if (rtm != NULL) {
+		memcpy(&m_rtmsg, rtm, rtm->rtm_msglen);
+		rtm = &m_rtmsg.m_rtm;
 		goto doit;
 	}
 	(void)memset(&m_rtmsg, 0, sizeof(m_rtmsg));
+	rtm = &m_rtmsg.m_rtm;
+	cp = m_rtmsg.m_space;
+
 	rtm->rtm_flags = flags;
 	rtm->rtm_version = RTM_VERSION;
 
@@ -660,7 +651,7 @@ rtmsg(const int s, const int cmd, const 
 		break;
 	case RTM_GET:
 		rtm->rtm_flags |= RTF_LLDATA;
-		rtm->rtm_addrs |= RTA_DST | RTA_IFP;
+		rtm->rtm_addrs |= RTA_DST | RTA_GATEWAY;
 	}
 
 #define NEXTADDR(w, s) \
@@ -725,7 +716,7 @@ getifname(u_int16_t ifindex, char *ifnam
 	}
 
 	for (addr = ifaddrs; addr; addr = addr->ifa_next) {
-		if (addr->ifa_addr == NULL || 
+		if (addr->ifa_addr == NULL ||
 		    addr->ifa_addr->sa_family != AF_LINK)
 			continue;
 

Index: src/usr.sbin/ndp/ndp.c
diff -u src/usr.sbin/ndp/ndp.c:1.48 src/usr.sbin/ndp/ndp.c:1.48.6.1
--- src/usr.sbin/ndp/ndp.c:1.48	Mon Sep 19 19:13:14 2016
+++ src/usr.sbin/ndp/ndp.c	Fri Jul  7 13:57:26 2017
@@ -1,4 +1,4 @@
-/*	$NetBSD: ndp.c,v 1.48 2016/09/19 19:13:14 christos Exp $	*/
+/*	$NetBSD: ndp.c,v 1.48.6.1 2017/07/07 13:57:26 martin Exp $	*/
 /*	$KAME: ndp.c,v 1.121 2005/07/13 11:30:13 keiichi Exp $	*/
 
 /*
@@ -122,13 +122,14 @@ static char ifix_buf[IFNAMSIZ];		/* if_i
 static void getsocket(void);
 static int set(int, char **);
 static void get(char *);
-static int delete(char *);
-static void dump(struct in6_addr *, int);
+static int delete(struct rt_msghdr *, char *);
+static void delete_one(char *);
+static void do_foreach(struct in6_addr *, char *, int);
 static struct in6_nbrinfo *getnbrinfo(struct in6_addr *, unsigned int, int);
 static char *ether_str(struct sockaddr_dl *);
 static int ndp_ether_aton(char *, u_char *);
 __dead static void usage(void);
-static int rtmsg(int);
+static int rtmsg(int, struct rt_msghdr *);
 static void ifinfo(char *, int, char **);
 static void rtrlist(void);
 static void plist(void);
@@ -144,6 +145,9 @@ static const char *sec2str(time_t);
 static char *ether_str(struct sockaddr_dl *);
 static void ts_print(const struct timeval *);
 
+#define NDP_F_CLEAR	1
+#define NDP_F_DELETE	2
+
 #ifdef ICMPV6CTL_ND6_DRLIST
 static const char *rtpref_str[] = {
 	"medium",		/* 00 */
@@ -223,14 +227,14 @@ main(int argc, char **argv)
 			usage();
 			/*NOTREACHED*/
 		}
-		dump(0, mode == 'c');
+		do_foreach(0, NULL, mode == 'c' ? NDP_F_CLEAR : 0);
 		break;
 	case 'd':
 		if (argc != 0) {
 			usage();
 			/*NOTREACHED*/
 		}
-		(void)delete(arg);
+		delete_one(arg);
 		break;
 	case 'I':
 #ifdef SIOCSDEFIFACE_IN6	/* XXX: check SIOCGDEFIFACE_IN6 as well? */
@@ -386,7 +390,7 @@ set(int argc, char **argv)
 			flags |= RTF_ANNOUNCE;
 		argv++;
 	}
-	if (rtmsg(RTM_GET) < 0) {
+	if (rtmsg(RTM_GET, NULL) < 0) {
 		errx(1, "RTM_GET(%s) failed", host);
 		/* NOTREACHED */
 	}
@@ -415,7 +419,7 @@ overwrite:
 	}
 	sdl_m.sdl_type = sdl->sdl_type;
 	sdl_m.sdl_index = sdl->sdl_index;
-	return (rtmsg(RTM_ADD));
+	return (rtmsg(RTM_ADD, NULL));
 }
 
 /*
@@ -437,7 +441,7 @@ get(char *host)
 		return;
 	}
 	makeaddr(mysin, res->ai_addr);
-	dump(&mysin->sin6_addr, 0);
+	do_foreach(&mysin->sin6_addr, host, 0);
 	if (found_entry == 0) {
 		(void)getnameinfo((struct sockaddr *)(void *)mysin,
 		    (socklen_t)mysin->sin6_len,
@@ -447,52 +451,44 @@ get(char *host)
 	}
 }
 
-/*
- * Delete a neighbor cache entry
- */
-static int
-delete(char *host)
+static void
+delete_one(char *host)
 {
 	struct sockaddr_in6 *mysin = &sin_m;
-	register struct rt_msghdr *rtm = &m_rtmsg.m_rtm;
-	struct sockaddr_dl *sdl;
 	struct addrinfo hints, *res;
 	int gai_error;
 
-	getsocket();
 	sin_m = blank_sin;
-
-	bzero(&hints, sizeof(hints));
+	(void)memset(&hints, 0, sizeof(hints));
 	hints.ai_family = AF_INET6;
 	gai_error = getaddrinfo(host, NULL, &hints, &res);
 	if (gai_error) {
 		warnx("%s: %s", host, gai_strerror(gai_error));
-		return 1;
+		return;
 	}
 	makeaddr(mysin, res->ai_addr);
-	if (rtmsg(RTM_GET) < 0)
-		errx(1, "RTM_GET(%s) failed", host);
+	do_foreach(&mysin->sin6_addr, host, NDP_F_DELETE);
+}
+
+/*
+ * Delete a neighbor cache entry
+ */
+static int
+delete(struct rt_msghdr *rtm, char *host)
+{
+	struct sockaddr_in6 *mysin = &sin_m;
+	struct sockaddr_dl *sdl;
+
+	getsocket();
 	mysin = (struct sockaddr_in6 *)(void *)(rtm + 1);
 	sdl = (struct sockaddr_dl *)(void *)(RT_ROUNDUP(mysin->sin6_len) +
 	    (char *)(void *)mysin);
-	if (IN6_ARE_ADDR_EQUAL(&mysin->sin6_addr, &sin_m.sin6_addr)) {
-		if (sdl->sdl_family == AF_LINK &&
-		    !(rtm->rtm_flags & RTF_GATEWAY)) {
-			goto delete;
-		}
-		/*
-		 * IPv4 arp command retries with sin_other = SIN_PROXY here.
-		 */
-		warnx("delete: cannot delete non-NDP entry");
-		return 1;
-	}
 
-delete:
 	if (sdl->sdl_family != AF_LINK) {
 		(void)printf("cannot locate %s\n", host);
 		return (1);
 	}
-	if (rtmsg(RTM_DELETE) == 0) {
+	if (rtmsg(RTM_DELETE, rtm) == 0) {
 		struct sockaddr_in6 s6 = *mysin; /* XXX: for safety */
 
 		mysin->sin6_scope_id = 0;
@@ -512,10 +508,13 @@ delete:
 #define W_IF	6
 
 /*
- * Dump the entire neighbor cache
+ * Iterate on neighbor caches and do
+ * - dump all caches,
+ * - clear all caches (NDP_F_CLEAR) or
+ * - remove matched caches (NDP_F_DELETE)
  */
 static void
-dump(struct in6_addr *addr, int cflag)
+do_foreach(struct in6_addr *addr, char *host, int _flags)
 {
 	int mib[6];
 	size_t needed;
@@ -530,6 +529,8 @@ dump(struct in6_addr *addr, int cflag)
 	int ifwidth;
 	char flgbuf[8], *fl;
 	const char *ifname;
+	int cflag = _flags == NDP_F_CLEAR;
+	int dflag = _flags == NDP_F_DELETE;
 
 	/* Print header */
 	if (!tflag && !cflag)
@@ -543,14 +544,18 @@ again:;
 	mib[2] = 0;
 	mib[3] = AF_INET6;
 	mib[4] = NET_RT_FLAGS;
-	mib[5] = 0;
+	mib[5] = RTF_LLDATA;
 	if (prog_sysctl(mib, 6, NULL, &needed, NULL, 0) < 0)
 		err(1, "sysctl(PF_ROUTE estimate)");
 	if (needed > 0) {
 		if ((buf = malloc(needed)) == NULL)
 			err(1, "malloc");
-		if (prog_sysctl(mib, 6, buf, &needed, NULL, 0) < 0)
+		if (prog_sysctl(mib, 6, buf, &needed, NULL, 0) < 0) {
+			free(buf);
+			if (errno == ENOBUFS)
+				goto again;
 			err(1, "sysctl(PF_ROUTE, NET_RT_FLAGS)");
+		}
 		lim = buf + needed;
 	} else
 		buf = lim = NULL;
@@ -587,6 +592,10 @@ again:;
 			found_entry = 1;
 		} else if (IN6_IS_ADDR_MULTICAST(&mysin->sin6_addr))
 			continue;
+		if (dflag) {
+			(void)delete(rtm, host_buf);
+			continue;
+		}
 		if (IN6_IS_ADDR_LINKLOCAL(&mysin->sin6_addr) ||
 		    IN6_IS_ADDR_MC_LINKLOCAL(&mysin->sin6_addr)) {
 			uint16_t scopeid = mysin->sin6_scope_id;
@@ -600,8 +609,13 @@ again:;
 		    host_buf, sizeof(host_buf), NULL, 0,
 		    (nflag ? NI_NUMERICHOST : 0));
 		if (cflag) {
+			/* Restore scopeid */
+			if (IN6_IS_ADDR_LINKLOCAL(&mysin->sin6_addr) ||
+			    IN6_IS_ADDR_MC_LINKLOCAL(&mysin->sin6_addr))
+				inet6_putscopeid(mysin, INET6_IS_ADDR_LINKLOCAL|
+				    INET6_IS_ADDR_MC_LINKLOCAL);
 			if ((rtm->rtm_flags & RTF_STATIC) == 0)
-				(void)delete(host_buf);
+				(void)delete(rtm, host_buf);
 			continue;
 		}
 		(void)gettimeofday(&tim, 0);
@@ -776,19 +790,21 @@ usage(void)
 }
 
 static int
-rtmsg(int cmd)
+rtmsg(int cmd, struct rt_msghdr *_rtm)
 {
 	static int seq;
-	register struct rt_msghdr *rtm = &m_rtmsg.m_rtm;
+	register struct rt_msghdr *rtm = _rtm;
 	register char *cp = m_rtmsg.m_space;
 	register int l;
 
 	errno = 0;
-	if (cmd == RTM_DELETE) {
-		rtm->rtm_flags |= RTF_LLDATA;
+	if (rtm != NULL) {
+		memcpy(&m_rtmsg, rtm, rtm->rtm_msglen);
+		rtm = &m_rtmsg.m_rtm;
 		goto doit;
 	}
 	(void)memset(&m_rtmsg, 0, sizeof(m_rtmsg));
+	rtm = &m_rtmsg.m_rtm;
 	rtm->rtm_flags = flags;
 	rtm->rtm_version = RTM_VERSION;
 
@@ -813,7 +829,7 @@ rtmsg(int cmd)
 		break;
 	case RTM_GET:
 		rtm->rtm_flags |= RTF_LLDATA;
-		rtm->rtm_addrs |= RTA_DST | RTA_IFP;
+		rtm->rtm_addrs |= RTA_DST | RTA_GATEWAY;
 	}
 #define NEXTADDR(w, s) \
 	if (rtm->rtm_addrs & (w)) { \

Reply via email to