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;