Module Name: src
Committed By: ozaki-r
Date: Wed Jun 28 08:17:50 UTC 2017
Modified Files:
src/tests/net/arp: t_arp.sh
src/tests/net/ndp: t_ndp.sh
src/usr.sbin/arp: arp.c
src/usr.sbin/ndp: ndp.c
Log Message:
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
To generate a diff of this commit:
cvs rdiff -u -r1.32 -r1.33 src/tests/net/arp/t_arp.sh
cvs rdiff -u -r1.28 -r1.29 src/tests/net/ndp/t_ndp.sh
cvs rdiff -u -r1.57 -r1.58 src/usr.sbin/arp/arp.c
cvs rdiff -u -r1.49 -r1.50 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/tests/net/arp/t_arp.sh
diff -u src/tests/net/arp/t_arp.sh:1.32 src/tests/net/arp/t_arp.sh:1.33
--- src/tests/net/arp/t_arp.sh:1.32 Wed Jun 28 04:14:53 2017
+++ src/tests/net/arp/t_arp.sh Wed Jun 28 08:17:50 2017
@@ -1,4 +1,4 @@
-# $NetBSD: t_arp.sh,v 1.32 2017/06/28 04:14:53 ozaki-r Exp $
+# $NetBSD: t_arp.sh,v 1.33 2017/06/28 08:17:50 ozaki-r Exp $
#
# Copyright (c) 2015 The NetBSD Foundation, Inc.
# All rights reserved.
@@ -654,7 +654,7 @@ arp_rtm_body()
str="$IP4DST link#2"
atf_check -s exit:0 -o match:"$str" cat $file
- # Test arp -d and resulting routing messages (RTM_GET and RTM_DELETE)
+ # Test arp -d and resulting routing messages (RTM_DELETE)
rump.route -n monitor -c 2 > $file &
pid=$?
sleep 1
@@ -662,13 +662,7 @@ arp_rtm_body()
wait $pid
$DEBUG && cat $file
- str="RTM_GET.+<UP,DONE,LLINFO>"
- atf_check -s exit:0 -o match:"$str" grep -A 3 RTM_GET $file
- str="<DST,GATEWAY>"
- atf_check -s exit:0 -o match:"$str" grep -A 3 RTM_GET $file
- str="$IP4DST $macaddr_dst"
- atf_check -s exit:0 -o match:"$str" grep -A 3 RTM_GET $file
- str="RTM_DELETE.+<DONE,LLINFO>"
+ 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
@@ -803,6 +797,85 @@ arp_purge_on_ifdown_cleanup()
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
@@ -818,4 +891,5 @@ atf_init_test_cases()
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.28 src/tests/net/ndp/t_ndp.sh:1.29
--- src/tests/net/ndp/t_ndp.sh:1.28 Wed Jun 28 04:14:53 2017
+++ src/tests/net/ndp/t_ndp.sh Wed Jun 28 08:17:50 2017
@@ -1,4 +1,4 @@
-# $NetBSD: t_ndp.sh,v 1.28 2017/06/28 04:14:53 ozaki-r Exp $
+# $NetBSD: t_ndp.sh,v 1.29 2017/06/28 08:17:50 ozaki-r Exp $
#
# Copyright (c) 2015 The NetBSD Foundation, Inc.
# All rights reserved.
@@ -446,7 +446,7 @@ ndp_rtm_body()
str="$IP6DST link#2"
atf_check -s exit:0 -o match:"$str" cat $file
- # Test ndp -d and resulting routing messages (RTM_GET and RTM_DELETE)
+ # Test ndp -d and resulting routing messages (RTM_DELETE)
rump.route -n monitor -c 2 > $file &
pid=$?
sleep 1
@@ -454,13 +454,7 @@ ndp_rtm_body()
wait $pid
$DEBUG && cat $file
- str="RTM_GET.+<UP,DONE,LLINFO>"
- atf_check -s exit:0 -o match:"$str" grep -A 3 RTM_GET $file
- str="<DST,GATEWAY>"
- atf_check -s exit:0 -o match:"$str" grep -A 3 RTM_GET $file
- str="$IP6DST $macaddr_dst"
- atf_check -s exit:0 -o match:"$str" grep -A 3 RTM_GET $file
- str="RTM_DELETE.+<DONE,LLINFO>"
+ 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
@@ -600,6 +594,87 @@ ndp_purge_on_ifdown_cleanup()
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
@@ -611,4 +686,5 @@ atf_init_test_cases()
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/usr.sbin/arp/arp.c
diff -u src/usr.sbin/arp/arp.c:1.57 src/usr.sbin/arp/arp.c:1.58
--- src/usr.sbin/arp/arp.c:1.57 Mon Jun 26 03:13:40 2017
+++ src/usr.sbin/arp/arp.c Wed Jun 28 08:17:50 2017
@@ -1,4 +1,4 @@
-/* $NetBSD: arp.c,v 1.57 2017/06/26 03:13:40 ozaki-r Exp $ */
+/* $NetBSD: arp.c,v 1.58 2017/06/28 08:17:50 ozaki-r 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.57 2017/06/26 03:13:40 ozaki-r Exp $");
+__RCSID("$NetBSD: arp.c,v 1.58 2017/06/28 08:17:50 ozaki-r Exp $");
#endif
#endif /* not lint */
@@ -79,9 +79,9 @@ __RCSID("$NetBSD: arp.c,v 1.57 2017/06/2
#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,8 +90,8 @@ 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;
@@ -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':
@@ -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;
@@ -390,50 +390,21 @@ is_llinfo(const struct sockaddr_dl *sdl,
* 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);
}
@@ -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;
Index: src/usr.sbin/ndp/ndp.c
diff -u src/usr.sbin/ndp/ndp.c:1.49 src/usr.sbin/ndp/ndp.c:1.50
--- src/usr.sbin/ndp/ndp.c:1.49 Mon Jun 26 03:13:40 2017
+++ src/usr.sbin/ndp/ndp.c Wed Jun 28 08:17:50 2017
@@ -1,4 +1,4 @@
-/* $NetBSD: ndp.c,v 1.49 2017/06/26 03:13:40 ozaki-r Exp $ */
+/* $NetBSD: ndp.c,v 1.50 2017/06/28 08:17:50 ozaki-r 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;