1 /* $NetBSD: arp.c,v 1.47 2008/07/21 13:36:57 lukem Exp $ */
4 * Copyright (c) 1984, 1993
5 * The Regents of the University of California. All rights reserved.
7 * This code is derived from software contributed to Berkeley by
8 * Sun Microsystems, Inc.
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
13 * 1. Redistributions of source code must retain the above copyright
14 * notice, this list of conditions and the following disclaimer.
15 * 2. Redistributions in binary form must reproduce the above copyright
16 * notice, this list of conditions and the following disclaimer in the
17 * documentation and/or other materials provided with the distribution.
18 * 3. Neither the name of the University nor the names of its contributors
19 * may be used to endorse or promote products derived from this software
20 * without specific prior written permission.
22 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
23 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
24 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
25 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
26 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
27 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
28 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
29 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
30 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
31 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
35 #include <sys/cdefs.h>
37 __COPYRIGHT("@(#) Copyright (c) 1984, 1993\
38 The Regents of the University of California. All rights reserved.");
43 static char sccsid
[] = "@(#)arp.c 8.3 (Berkeley) 4/28/95";
45 __RCSID("$NetBSD: arp.c,v 1.47 2008/07/21 13:36:57 lukem Exp $");
50 * arp - display, set, and delete arp table entries
53 #include <sys/param.h>
55 #include <sys/socket.h>
56 #include <sys/sysctl.h>
57 #include <sys/ioctl.h>
60 #include <net/if_dl.h>
61 #include <net/if_ether.h>
62 #include <net/if_types.h>
63 #include <net/route.h>
64 #include <netinet/in.h>
65 #include <netinet/if_inarp.h>
66 #include <arpa/inet.h>
79 static int is_llinfo(const struct sockaddr_dl
*, int);
80 static int delete(const char *, const char *);
81 static void dump(uint32_t);
82 static void delete_all(void);
83 static void sdl_print(const struct sockaddr_dl
*);
84 static int getifname(u_int16_t
, char *, size_t);
85 static int atosdl(const char *s
, struct sockaddr_dl
*sdl
);
86 static int file(const char *);
87 static void get(const char *);
88 static int getinetaddr(const char *, struct in_addr
*);
89 static void getsocket(void);
90 static int rtmsg(int);
91 static int set(int, char **);
92 static void usage(void) __dead
;
95 static int aflag
, nflag
, vflag
;
97 static struct ifaddrs
* ifaddrs
= NULL
;
98 static struct sockaddr_in so_mask
= {
104 static struct sockaddr_inarp blank_sin
= {
105 .sin_len
= sizeof(blank_sin
),
106 .sin_family
= AF_INET
108 static struct sockaddr_inarp sin_m
;
109 static struct sockaddr_dl blank_sdl
= {
110 .sdl_len
= sizeof(blank_sdl
),
111 .sdl_family
= AF_LINK
113 static struct sockaddr_dl sdl_m
;
115 static int expire_time
, flags
, export_only
, doing_proxy
, found_entry
;
117 struct rt_msghdr m_rtm
;
122 main(int argc
, char **argv
)
127 setprogname(argv
[0]);
131 while ((ch
= getopt(argc
, argv
, "andsfv")) != -1)
163 if (aflag
&& argc
== 0)
166 if (aflag
|| argc
< 1 || argc
> 2)
168 (void)delete(argv
[0], argv
[1]);
172 if (argc
< 2 || argc
> 5)
174 return (set(argc
, argv
) ? 1 : 0);
178 return (file(argv
[0]));
189 * Process a file to set standard arp entries
192 file(const char *name
)
194 char *line
, *argv
[5];
198 if ((fp
= fopen(name
, "r")) == NULL
)
199 err(1, "cannot open %s", name
);
201 for (; (line
= fparseln(fp
, NULL
, NULL
, NULL
, 0)) != NULL
; free(line
)) {
202 char **ap
, *inputstring
;
205 for (ap
= argv
; ap
< &argv
[sizeof(argv
) / sizeof(argv
[0])] &&
206 (*ap
= stresep(&inputstring
, " \t", '\\')) != NULL
;) {
212 warnx("bad line: %s", line
);
228 s
= socket(PF_ROUTE
, SOCK_RAW
, 0);
234 * Set an individual arp entry
237 set(int argc
, char **argv
)
239 struct sockaddr_inarp
*sina
;
240 struct sockaddr_dl
*sdl
;
241 struct rt_msghdr
*rtm
;
242 char *host
= argv
[0], *eaddr
;
246 rtm
= &(m_rtmsg
.m_rtm
);
252 sdl_m
= blank_sdl
; /* struct copy */
253 sin_m
= blank_sin
; /* struct copy */
254 if (getinetaddr(host
, &sina
->sin_addr
) == -1)
256 if (atosdl(eaddr
, &sdl_m
))
257 warnx("invalid link-level address '%s'", eaddr
);
258 doing_proxy
= flags
= export_only
= expire_time
= 0;
260 if (strncmp(argv
[0], "temp", 4) == 0) {
261 struct timeval timev
;
262 (void)gettimeofday(&timev
, 0);
263 expire_time
= timev
.tv_sec
+ 20 * 60;
265 else if (strncmp(argv
[0], "pub", 3) == 0) {
266 flags
|= RTF_ANNOUNCE
;
267 doing_proxy
= SIN_PROXY
;
268 if (argc
&& strncmp(argv
[1], "pro", 3) == 0) {
272 } else if (strncmp(argv
[0], "trail", 5) == 0) {
273 warnx("%s: Sending trailers is no longer supported",
279 if (rtmsg(RTM_GET
) < 0) {
283 sina
= (struct sockaddr_inarp
*)(void *)(rtm
+ 1);
284 sdl
= (struct sockaddr_dl
*)(void *)(RT_ROUNDUP(sina
->sin_len
) +
285 (char *)(void *)sina
);
286 if (sina
->sin_addr
.s_addr
== sin_m
.sin_addr
.s_addr
) {
287 if (is_llinfo(sdl
, rtm
->rtm_flags
))
289 if (doing_proxy
== 0) {
290 warnx("set: can only proxy for %s", host
);
293 if (sin_m
.sin_other
& SIN_PROXY
) {
294 warnx("set: proxy entry exists for non 802 device");
297 sin_m
.sin_other
= SIN_PROXY
;
302 if (sdl
->sdl_family
!= AF_LINK
) {
303 warnx("cannot intuit interface index and type for %s",
307 sdl_m
.sdl_type
= sdl
->sdl_type
;
308 sdl_m
.sdl_index
= sdl
->sdl_index
;
309 rval
= rtmsg(RTM_ADD
);
311 (void)printf("%s (%s) added\n", host
, eaddr
);
316 * Display an individual arp entry
319 get(const char *host
)
321 struct sockaddr_inarp
*sina
;
324 sin_m
= blank_sin
; /* struct copy */
325 if (getinetaddr(host
, &sina
->sin_addr
) == -1)
327 dump(sina
->sin_addr
.s_addr
);
328 if (found_entry
== 0)
329 errx(1, "%s (%s) -- no entry", host
, inet_ntoa(sina
->sin_addr
));
334 is_llinfo(const struct sockaddr_dl
*sdl
, int rtflags
)
336 if (sdl
->sdl_family
!= AF_LINK
||
337 (rtflags
& (RTF_LLINFO
|RTF_GATEWAY
)) != RTF_LLINFO
)
340 switch (sdl
->sdl_type
) {
354 * Delete an arp entry
357 delete(const char *host
, const char *info
)
359 struct sockaddr_inarp
*sina
;
360 struct rt_msghdr
*rtm
;
361 struct sockaddr_dl
*sdl
;
364 rtm
= &m_rtmsg
.m_rtm
;
367 sin_m
= blank_sin
; /* struct copy */
368 if (info
&& strncmp(info
, "pro", 3) == 0)
369 sina
->sin_other
= SIN_PROXY
;
370 if (getinetaddr(host
, &sina
->sin_addr
) == -1)
373 if (rtmsg(RTM_GET
) < 0) {
377 sina
= (struct sockaddr_inarp
*)(void *)(rtm
+ 1);
378 sdl
= (struct sockaddr_dl
*)(void *)(RT_ROUNDUP(sina
->sin_len
) +
379 (char *)(void *)sina
);
380 if (sina
->sin_addr
.s_addr
== sin_m
.sin_addr
.s_addr
&&
381 is_llinfo(sdl
, rtm
->rtm_flags
))
383 if (sin_m
.sin_other
& SIN_PROXY
) {
384 warnx("delete: can't locate %s", host
);
387 sin_m
.sin_other
= SIN_PROXY
;
391 if (sdl
->sdl_family
!= AF_LINK
) {
392 (void)warnx("cannot locate %s", host
);
395 if (rtmsg(RTM_DELETE
))
398 (void)printf("%s (%s) deleted\n", host
,
399 inet_ntoa(sina
->sin_addr
));
404 * Dump the entire arp table
411 char ifname
[IFNAMSIZ
];
412 char *lim
, *buf
, *next
;
414 struct rt_msghdr
*rtm
;
415 struct sockaddr_inarp
*sina
;
416 struct sockaddr_dl
*sdl
;
423 mib
[4] = NET_RT_FLAGS
;
425 if (sysctl(mib
, 6, NULL
, &needed
, NULL
, 0) < 0)
426 err(1, "route-sysctl-estimate");
429 if ((buf
= malloc(needed
)) == NULL
)
431 if (sysctl(mib
, 6, buf
, &needed
, NULL
, 0) < 0)
432 err(1, "actual retrieval of routing table");
434 for (next
= buf
; next
< lim
; next
+= rtm
->rtm_msglen
) {
435 rtm
= (struct rt_msghdr
*)(void *)next
;
436 sina
= (struct sockaddr_inarp
*)(void *)(rtm
+ 1);
437 sdl
= (struct sockaddr_dl
*)(void *)
438 (RT_ROUNDUP(sina
->sin_len
) + (char *)(void *)sina
);
440 if (addr
!= sina
->sin_addr
.s_addr
)
445 hp
= gethostbyaddr((const char *)(void *)
447 sizeof sina
->sin_addr
, AF_INET
);
451 host
= hp
? hp
->h_name
: "?";
453 (void)printf("%s (%s) at ", host
, inet_ntoa(sina
->sin_addr
));
457 (void)printf("(incomplete)");
459 if (sdl
->sdl_index
) {
460 if (getifname(sdl
->sdl_index
, ifname
, sizeof(ifname
)) == 0)
461 (void)printf(" on %s", ifname
);
464 if (rtm
->rtm_rmx
.rmx_expire
== 0)
465 (void)printf(" permanent");
466 if (sina
->sin_other
& SIN_PROXY
)
467 (void)printf(" published (proxy only)");
468 if (rtm
->rtm_addrs
& RTA_NETMASK
) {
469 sina
= (struct sockaddr_inarp
*)(void *)
470 (RT_ROUNDUP(sdl
->sdl_len
) + (char *)(void *)sdl
);
471 if (sina
->sin_addr
.s_addr
== 0xffffffff)
472 (void)printf(" published");
473 if (sina
->sin_len
!= 8)
474 (void)printf("(weird)");
482 * Delete the entire arp table
489 char addr
[sizeof("000.000.000.000\0")];
490 char *lim
, *buf
, *next
;
491 struct rt_msghdr
*rtm
;
492 struct sockaddr_inarp
*sina
;
498 mib
[4] = NET_RT_FLAGS
;
500 if (sysctl(mib
, 6, NULL
, &needed
, NULL
, 0) < 0)
501 err(1, "route-sysctl-estimate");
504 if ((buf
= malloc(needed
)) == NULL
)
506 if (sysctl(mib
, 6, buf
, &needed
, NULL
, 0) < 0)
507 err(1, "actual retrieval of routing table");
509 for (next
= buf
; next
< lim
; next
+= rtm
->rtm_msglen
) {
510 rtm
= (struct rt_msghdr
*)(void *)next
;
511 sina
= (struct sockaddr_inarp
*)(void *)(rtm
+ 1);
512 (void)snprintf(addr
, sizeof(addr
), "%s",
513 inet_ntoa(sina
->sin_addr
));
514 (void)delete(addr
, NULL
);
520 sdl_print(const struct sockaddr_dl
*sdl
)
522 char hbuf
[NI_MAXHOST
];
524 if (getnameinfo((const struct sockaddr
*)(const void *)sdl
,
525 (socklen_t
)sdl
->sdl_len
,
526 hbuf
, sizeof(hbuf
), NULL
, 0, NI_NUMERICHOST
) != 0)
527 (void)printf("<invalid>");
529 (void)printf("%s", hbuf
);
533 atosdl(const char *ss
, struct sockaddr_dl
*sdl
)
542 endp
= ((char *)(void *)sdl
) + sdl
->sdl_len
;
545 b
= strtoul(ss
, &t
, 16);
546 if (b
> 255 || t
== ss
)
551 while ((p
< endp
) && (*t
++ == ':')) {
552 b
= strtoul(t
, &r
, 16);
553 if (b
> 255 || r
== t
)
567 const char *progname
;
569 progname
= getprogname();
570 (void)fprintf(stderr
, "Usage: %s [-n] hostname\n", progname
);
571 (void)fprintf(stderr
, " %s [-nv] -a\n", progname
);
572 (void)fprintf(stderr
, " %s [-v] -d [-a|hostname [pub [proxy]]]\n",
574 (void)fprintf(stderr
, " %s -s hostname ether_addr [temp] [pub [proxy]]\n",
576 (void)fprintf(stderr
, " %s -f filename\n", progname
);
584 struct rt_msghdr
*rtm
;
588 rtm
= &m_rtmsg
.m_rtm
;
589 cp
= m_rtmsg
.m_space
;
592 if (cmd
== RTM_DELETE
)
594 (void)memset(&m_rtmsg
, 0, sizeof(m_rtmsg
));
595 rtm
->rtm_flags
= flags
;
596 rtm
->rtm_version
= RTM_VERSION
;
600 errx(1, "internal wrong cmd");
603 rtm
->rtm_addrs
|= RTA_GATEWAY
;
604 rtm
->rtm_rmx
.rmx_expire
= expire_time
;
605 rtm
->rtm_inits
= RTV_EXPIRE
;
606 rtm
->rtm_flags
|= (RTF_HOST
| RTF_STATIC
);
610 sin_m
.sin_other
= SIN_PROXY
;
612 rtm
->rtm_addrs
|= RTA_NETMASK
;
613 rtm
->rtm_flags
&= ~RTF_HOST
;
618 rtm
->rtm_addrs
|= RTA_DST
;
621 #define NEXTADDR(w, s) \
622 if (rtm->rtm_addrs & (w)) { \
623 (void)memcpy(cp, &s, \
624 (size_t)((struct sockaddr *)(void *)&s)->sa_len); \
625 RT_ADVANCE(cp, ((struct sockaddr *)(void *)&s)); \
628 NEXTADDR(RTA_DST
, sin_m
);
629 NEXTADDR(RTA_GATEWAY
, sdl_m
);
630 NEXTADDR(RTA_NETMASK
, so_mask
);
632 rtm
->rtm_msglen
= cp
- (char *)(void *)&m_rtmsg
;
635 rtm
->rtm_seq
= ++seq
;
637 if (write(s
, &m_rtmsg
, (size_t)l
) < 0) {
638 if (errno
!= ESRCH
|| cmd
!= RTM_DELETE
) {
639 warn("writing to routing socket");
644 l
= read(s
, &m_rtmsg
, sizeof(m_rtmsg
));
645 } while (l
> 0 && (rtm
->rtm_seq
!= seq
|| rtm
->rtm_pid
!= pid
));
647 warn("read from routing socket");
652 getinetaddr(const char *host
, struct in_addr
*inap
)
656 if (inet_aton(host
, inap
) == 1)
658 if ((hp
= gethostbyname(host
)) == NULL
) {
659 warnx("%s: %s", host
, hstrerror(h_errno
));
662 (void)memcpy(inap
, hp
->h_addr
, sizeof(*inap
));
667 getifname(u_int16_t ifindex
, char *ifname
, size_t l
)
670 struct ifaddrs
*addr
;
671 const struct sockaddr_dl
*sdl
= NULL
;
673 if (ifaddrs
== NULL
) {
674 i
= getifaddrs(&ifaddrs
);
676 err(1, "getifaddrs");
679 for (addr
= ifaddrs
; addr
; addr
= addr
->ifa_next
) {
680 if (addr
->ifa_addr
== NULL
||
681 addr
->ifa_addr
->sa_family
!= AF_LINK
)
684 sdl
= (const struct sockaddr_dl
*)(void *)addr
->ifa_addr
;
685 if (sdl
&& sdl
->sdl_index
== ifindex
) {
686 (void) strlcpy(ifname
, addr
->ifa_name
, l
);