xref: /openbsd/usr.sbin/arp/arp.c (revision 4bdff4be)
1 /*	$OpenBSD: arp.c,v 1.89 2023/04/04 21:18:04 bluhm Exp $ */
2 /*	$NetBSD: arp.c,v 1.12 1995/04/24 13:25:18 cgd Exp $ */
3 
4 /*
5  * Copyright (c) 1984, 1993
6  *	The Regents of the University of California.  All rights reserved.
7  *
8  * This code is derived from software contributed to Berkeley by
9  * Sun Microsystems, Inc.
10  *
11  * Redistribution and use in source and binary forms, with or without
12  * modification, are permitted provided that the following conditions
13  * are met:
14  * 1. Redistributions of source code must retain the above copyright
15  *    notice, this list of conditions and the following disclaimer.
16  * 2. Redistributions in binary form must reproduce the above copyright
17  *    notice, this list of conditions and the following disclaimer in the
18  *    documentation and/or other materials provided with the distribution.
19  * 3. Neither the name of the University nor the names of its contributors
20  *    may be used to endorse or promote products derived from this software
21  *    without specific prior written permission.
22  *
23  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
24  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
25  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
26  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
27  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
28  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
29  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
30  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
31  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
32  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
33  * SUCH DAMAGE.
34  */
35 
36 /*
37  * arp - display, set, delete arp table entries and wake up hosts.
38  */
39 
40 #include <sys/socket.h>
41 #include <sys/sysctl.h>
42 #include <sys/ioctl.h>
43 #include <net/bpf.h>
44 #include <net/if.h>
45 #include <net/if_dl.h>
46 #include <net/if_types.h>
47 #include <net/route.h>
48 #include <netinet/in.h>
49 #include <netinet/if_ether.h>
50 #include <arpa/inet.h>
51 
52 #include <netdb.h>
53 #include <errno.h>
54 #include <err.h>
55 #include <fcntl.h>
56 #include <stdio.h>
57 #include <stdlib.h>
58 #include <string.h>
59 #include <time.h>
60 #include <unistd.h>
61 #include <limits.h>
62 #include <ifaddrs.h>
63 
64 void dump(void);
65 int delete(const char *);
66 void search(in_addr_t addr, void (*action)(struct sockaddr_dl *sdl,
67 	struct sockaddr_inarp *sin, struct rt_msghdr *rtm));
68 void print_entry(struct sockaddr_dl *sdl,
69 	struct sockaddr_inarp *sin, struct rt_msghdr *rtm);
70 void nuke_entry(struct sockaddr_dl *sdl,
71 	struct sockaddr_inarp *sin, struct rt_msghdr *rtm);
72 static char *ether_str(struct sockaddr_dl *);
73 int wake(const char *ether_addr, const char *iface);
74 int file(char *);
75 int get(const char *);
76 void getsocket(void);
77 int parse_host(const char *, struct in_addr *);
78 int rtget(struct sockaddr_inarp **, struct sockaddr_dl **);
79 int rtmsg(int);
80 int set(int, char **);
81 void usage(void);
82 static char *sec2str(time_t);
83 
84 static pid_t pid;
85 static int replace;	/* replace entries when adding */
86 static int nflag;	/* no reverse dns lookups */
87 static int aflag;	/* do it for all entries */
88 static int rtsock = -1;
89 static int rdomain;
90 
91 /* ROUNDUP() is nasty, but it is identical to what's in the kernel. */
92 #define ROUNDUP(a) \
93 	((a) > 0 ? (1 + (((a) - 1) | (sizeof(long) - 1))) : sizeof(long))
94 #define ADVANCE(x, n) (x += ROUNDUP((n)->sa_len))
95 
96 int
97 main(int argc, char *argv[])
98 {
99 	int		 ch, func = 0, error = 0;
100 	const char	*errstr;
101 
102 	pid = getpid();
103 	opterr = 0;
104 	rdomain = getrtable();
105 	while ((ch = getopt(argc, argv, "andsFfV:W")) != -1) {
106 		switch (ch) {
107 		case 'a':
108 			aflag = 1;
109 			break;
110 		case 'n':
111 			nflag = 1;
112 			break;
113 		case 'd':
114 		case 's':
115 		case 'f':
116 		case 'W':
117 			if (func)
118 				usage();
119 			func = ch;
120 			break;
121 		case 'F':
122 			replace = 1;
123 			break;
124 		case 'V':
125 			rdomain = strtonum(optarg, 0, RT_TABLEID_MAX, &errstr);
126 			if (errstr != NULL) {
127 				warn("bad rdomain: %s", errstr);
128 				usage();
129 			}
130 			break;
131 		default:
132 			usage();
133 			break;
134 		}
135 	}
136 	argc -= optind;
137 	argv += optind;
138 
139 	switch (func) {
140 	case 0:
141 		if (aflag && argc == 0)
142 			dump();
143 		else if (!aflag && argc == 1)
144 			error = get(argv[0]);
145 		else
146 			usage();
147 		break;
148 	case 's':
149 		if (argc < 2 || argc > 5)
150 			usage();
151 		if (replace)
152 			delete(argv[0]);
153 		error = set(argc, argv) ? 1 : 0;
154 		break;
155 	case 'd':
156 		if (aflag && argc == 0)
157 			search(0, nuke_entry);
158 		else if (!aflag && argc == 1)
159 			error = delete(argv[0]);
160 		else
161 			usage();
162 		break;
163 	case 'f':
164 		if (argc != 1)
165 			usage();
166 		error = file(argv[0]);
167 		break;
168 	case 'W':
169 		if (aflag || nflag || replace || rdomain > 0)
170 			usage();
171 		if (argc == 1)
172 			error = wake(argv[0], NULL);
173 		else if (argc == 2)
174 			error = wake(argv[0], argv[1]);
175 		else
176 			usage();
177 		break;
178 	}
179 	return (error);
180 }
181 
182 /*
183  * Process a file to set standard arp entries
184  */
185 int
186 file(char *name)
187 {
188 	char	 line[100], arg[5][50], *args[5];
189 	int	 i, retval;
190 	FILE	*fp;
191 
192 	if ((fp = fopen(name, "r")) == NULL)
193 		err(1, "cannot open %s", name);
194 	args[0] = &arg[0][0];
195 	args[1] = &arg[1][0];
196 	args[2] = &arg[2][0];
197 	args[3] = &arg[3][0];
198 	args[4] = &arg[4][0];
199 	retval = 0;
200 	while (fgets(line, sizeof(line), fp) != NULL) {
201 		i = sscanf(line, "%49s %49s %49s %49s %49s", arg[0], arg[1],
202 		    arg[2], arg[3], arg[4]);
203 		if (i < 2) {
204 			warnx("bad line: %s", line);
205 			retval = 1;
206 			continue;
207 		}
208 		if (replace)
209 			delete(arg[0]);
210 		if (set(i, args))
211 			retval = 1;
212 	}
213 	fclose(fp);
214 	return (retval);
215 }
216 
217 void
218 getsocket(void)
219 {
220 	socklen_t len = sizeof(rdomain);
221 
222 	if (rtsock >= 0)
223 		return;
224 	rtsock = socket(AF_ROUTE, SOCK_RAW, 0);
225 	if (rtsock == -1)
226 		err(1, "routing socket");
227 	if (setsockopt(rtsock, AF_ROUTE, ROUTE_TABLEFILTER, &rdomain, len) == -1)
228 		err(1, "ROUTE_TABLEFILTER");
229 
230 	if (pledge("stdio dns", NULL) == -1)
231 		err(1, "pledge");
232 }
233 
234 int
235 parse_host(const char *host, struct in_addr *in)
236 {
237 	struct addrinfo hints, *res;
238 	struct sockaddr_in *sin;
239 	int gai_error;
240 
241 	bzero(&hints, sizeof(hints));
242 	hints.ai_family = AF_INET;
243 	if (nflag)
244 		hints.ai_flags = AI_NUMERICHOST;
245 
246 	gai_error = getaddrinfo(host, NULL, &hints, &res);
247 	if (gai_error) {
248 		warnx("%s: %s", host, gai_strerror(gai_error));
249 		return 1;
250 	}
251 
252 	sin = (struct sockaddr_in *)res->ai_addr;
253 	*in = sin->sin_addr;
254 
255 	freeaddrinfo(res);
256 	return 0;
257 }
258 
259 struct sockaddr_in	so_mask = { 8, 0, 0, { 0xffffffff } };
260 struct sockaddr_inarp	blank_sin = { sizeof(blank_sin), AF_INET }, sin_m;
261 struct sockaddr_dl	blank_sdl = { sizeof(blank_sdl), AF_LINK }, sdl_m;
262 struct sockaddr_dl	ifp_m = { sizeof(ifp_m), AF_LINK };
263 time_t			expire_time;
264 int			flags, export_only, doing_proxy, found_entry;
265 struct	{
266 	struct rt_msghdr	m_rtm;
267 	char			m_space[512];
268 }	m_rtmsg;
269 
270 /*
271  * Set an individual arp entry
272  */
273 int
274 set(int argc, char *argv[])
275 {
276 	struct sockaddr_inarp *sin;
277 	struct sockaddr_dl *sdl;
278 	struct rt_msghdr *rtm;
279 	const char *host = argv[0], *eaddr = argv[1];
280 	struct ether_addr *ea;
281 
282 	sin = &sin_m;
283 	rtm = &(m_rtmsg.m_rtm);
284 
285 	getsocket();
286 	argc -= 2;
287 	argv += 2;
288 	sdl_m = blank_sdl;		/* struct copy */
289 	sin_m = blank_sin;		/* struct copy */
290 	if (parse_host(host, &sin->sin_addr))
291 		return (1);
292 	ea = ether_aton(eaddr);
293 	if (ea == NULL)
294 		errx(1, "invalid ethernet address: %s", eaddr);
295 	memcpy(LLADDR(&sdl_m), ea, sizeof(*ea));
296 	sdl_m.sdl_alen = 6;
297 	expire_time = 0;
298 	doing_proxy = flags = export_only = 0;
299 	while (argc-- > 0) {
300 		if (strncmp(argv[0], "temp", 4) == 0) {
301 			expire_time = time(NULL) + 20 * 60;
302 			if (flags & RTF_PERMANENT_ARP) {
303 				/* temp or permanent, not both */
304 				usage();
305 				return (0);
306 			}
307 		} else if (strncmp(argv[0], "pub", 3) == 0) {
308 			flags |= RTF_ANNOUNCE;
309 			doing_proxy = SIN_PROXY;
310 		} else if (strncmp(argv[0], "permanent", 9) == 0) {
311 			flags |= RTF_PERMANENT_ARP;
312 			if (expire_time != 0) {
313 				/* temp or permanent, not both */
314 				usage();
315 				return (0);
316 			}
317 		}
318 
319 		argv++;
320 	}
321 
322 tryagain:
323 	if (rtget(&sin, &sdl)) {
324 		warn("%s", host);
325 		return (1);
326 	}
327 
328 	if (sin->sin_addr.s_addr == sin_m.sin_addr.s_addr) {
329 		if (sdl->sdl_family == AF_LINK &&
330 		    (rtm->rtm_flags & RTF_LLINFO) &&
331 		    !(rtm->rtm_flags & RTF_GATEWAY))
332 			switch (sdl->sdl_type) {
333 			case IFT_ETHER:
334 			case IFT_FDDI:
335 			case IFT_ISO88023:
336 			case IFT_ISO88024:
337 			case IFT_ISO88025:
338 			case IFT_CARP:
339 				goto overwrite;
340 			}
341 
342 		if (doing_proxy == 0) {
343 			printf("set: can only proxy for %s\n", host);
344 			return (1);
345 		}
346 		if (sin_m.sin_other & SIN_PROXY) {
347 			printf("set: proxy entry exists for non 802 device\n");
348 			return (1);
349 		}
350 		sin_m.sin_other = SIN_PROXY;
351 		export_only = 1;
352 		goto tryagain;
353 	}
354 
355 overwrite:
356 	if (sdl->sdl_family != AF_LINK) {
357 		printf("cannot intuit interface index and type for %s\n", host);
358 		return (1);
359 	}
360 	sdl_m.sdl_type = sdl->sdl_type;
361 	sdl_m.sdl_index = sdl->sdl_index;
362 	return (rtmsg(RTM_ADD));
363 }
364 
365 #define W_ADDR	36
366 #define W_LL	17
367 #define W_IF	7
368 
369 /*
370  * Display an individual arp entry
371  */
372 int
373 get(const char *host)
374 {
375 	struct sockaddr_inarp *sin;
376 
377 	sin = &sin_m;
378 	sin_m = blank_sin;		/* struct copy */
379 	if (parse_host(host, &sin->sin_addr))
380 		return (1);
381 
382 	printf("%-*.*s %-*.*s %*.*s %-9.9s %5s\n",
383 	    W_ADDR, W_ADDR, "Host", W_LL, W_LL, "Ethernet Address",
384 	    W_IF, W_IF, "Netif", "Expire", "Flags");
385 
386 	search(sin->sin_addr.s_addr, print_entry);
387 	if (found_entry == 0) {
388 		printf("%-*.*s no entry\n", W_ADDR, W_ADDR,
389 		    inet_ntoa(sin->sin_addr));
390 		return (1);
391 	}
392 	return (0);
393 }
394 
395 /*
396  * Delete an arp entry
397  */
398 int
399 delete(const char *host)
400 {
401 	struct sockaddr_inarp *sin;
402 	struct rt_msghdr *rtm;
403 	struct sockaddr_dl *sdl;
404 
405 	sin = &sin_m;
406 	rtm = &m_rtmsg.m_rtm;
407 
408 	getsocket();
409 	sin_m = blank_sin;		/* struct copy */
410 	if (parse_host(host, &sin->sin_addr))
411 		return 1;
412 tryagain:
413 	if (rtget(&sin, &sdl)) {
414 		warn("%s", host);
415 		return 1;
416 	}
417 	if (sin->sin_addr.s_addr == sin_m.sin_addr.s_addr) {
418 		if (sdl->sdl_family == AF_LINK && rtm->rtm_flags & RTF_LLINFO) {
419 			if (rtm->rtm_flags & RTF_LOCAL)
420 				return 0;
421 			if ((rtm->rtm_flags & RTF_GATEWAY) == 0)
422 				switch (sdl->sdl_type) {
423 				case IFT_ETHER:
424 				case IFT_FDDI:
425 				case IFT_ISO88023:
426 				case IFT_ISO88024:
427 				case IFT_ISO88025:
428 				case IFT_CARP:
429 					goto delete;
430 				}
431 		}
432 	}
433 
434 	if (sin_m.sin_other & SIN_PROXY) {
435 		warnx("delete: cannot locate %s", host);
436 		return 1;
437 	} else {
438 		sin_m.sin_other = SIN_PROXY;
439 		goto tryagain;
440 	}
441 delete:
442 	if (sdl->sdl_family != AF_LINK) {
443 		printf("cannot locate %s\n", host);
444 		return 1;
445 	}
446 	if (rtmsg(RTM_DELETE))
447 		return 1;
448 	printf("%s (%s) deleted\n", host, inet_ntoa(sin->sin_addr));
449 	return 0;
450 }
451 
452 /*
453  * Search the entire arp table, and do some action on matching entries.
454  */
455 void
456 search(in_addr_t addr, void (*action)(struct sockaddr_dl *sdl,
457     struct sockaddr_inarp *sin, struct rt_msghdr *rtm))
458 {
459 	int mib[7];
460 	size_t needed;
461 	char *lim, *buf = NULL, *next;
462 	struct rt_msghdr *rtm;
463 	struct sockaddr_inarp *sin;
464 	struct sockaddr_dl *sdl;
465 
466 	mib[0] = CTL_NET;
467 	mib[1] = PF_ROUTE;
468 	mib[2] = 0;
469 	mib[3] = AF_INET;
470 	mib[4] = NET_RT_FLAGS;
471 	mib[5] = RTF_LLINFO;
472 	mib[6] = rdomain;
473 	while (1) {
474 		if (sysctl(mib, 7, NULL, &needed, NULL, 0) == -1)
475 			err(1, "route-sysctl-estimate");
476 		if (needed == 0)
477 			return;
478 		if ((buf = realloc(buf, needed)) == NULL)
479 			err(1, "malloc");
480 		if (sysctl(mib, 7, buf, &needed, NULL, 0) == -1) {
481 			if (errno == ENOMEM)
482 				continue;
483 			err(1, "actual retrieval of routing table");
484 		}
485 		lim = buf + needed;
486 		break;
487 	}
488 	for (next = buf; next < lim; next += rtm->rtm_msglen) {
489 		rtm = (struct rt_msghdr *)next;
490 		if (rtm->rtm_version != RTM_VERSION)
491 			continue;
492 		sin = (struct sockaddr_inarp *)(next + rtm->rtm_hdrlen);
493 		sdl = (struct sockaddr_dl *)(sin + 1);
494 		if (addr) {
495 			if (addr != sin->sin_addr.s_addr)
496 				continue;
497 			found_entry = 1;
498 		}
499 		(*action)(sdl, sin, rtm);
500 	}
501 	free(buf);
502 }
503 
504 /*
505  * Dump the entire ARP table
506  */
507 void
508 dump(void)
509 {
510 	printf("%-*.*s %-*.*s %*.*s %-9.9s %5s\n",
511 	    W_ADDR, W_ADDR, "Host", W_LL, W_LL, "Ethernet Address",
512 	    W_IF, W_IF, "Netif", "Expire", "Flags");
513 
514 	search(0, print_entry);
515 }
516 
517 /*
518  * Display an arp entry
519  */
520 void
521 print_entry(struct sockaddr_dl *sdl, struct sockaddr_inarp *sin,
522     struct rt_msghdr *rtm)
523 {
524 	char ifix_buf[IFNAMSIZ], *ifname, *host;
525 	struct hostent *hp = NULL;
526 	int addrwidth, llwidth, ifwidth ;
527 	time_t now;
528 
529 	now = time(NULL);
530 
531 	if (nflag == 0)
532 		hp = gethostbyaddr((caddr_t)&(sin->sin_addr),
533 		    sizeof(sin->sin_addr), AF_INET);
534 	if (hp)
535 		host = hp->h_name;
536 	else
537 		host = inet_ntoa(sin->sin_addr);
538 
539 	addrwidth = strlen(host);
540 	if (addrwidth < W_ADDR)
541 		addrwidth = W_ADDR;
542 	llwidth = strlen(ether_str(sdl));
543 	if (W_ADDR + W_LL - addrwidth > llwidth)
544 		llwidth = W_ADDR + W_LL - addrwidth;
545 	ifname = if_indextoname(sdl->sdl_index, ifix_buf);
546 	if (!ifname)
547 		ifname = "?";
548 	ifwidth = strlen(ifname);
549 	if (W_ADDR + W_LL + W_IF - addrwidth - llwidth > ifwidth)
550 		ifwidth = W_ADDR + W_LL + W_IF - addrwidth - llwidth;
551 
552 	printf("%-*.*s %-*.*s %*.*s", addrwidth, addrwidth, host,
553 	    llwidth, llwidth, ether_str(sdl), ifwidth, ifwidth, ifname);
554 
555 	if (rtm->rtm_flags & (RTF_PERMANENT_ARP|RTF_LOCAL))
556 		printf(" %-9.9s", "permanent");
557 	else if (rtm->rtm_rmx.rmx_expire == 0)
558 		printf(" %-9.9s", "static");
559 	else if (rtm->rtm_rmx.rmx_expire > now)
560 		printf(" %-9.9s",
561 		    sec2str(rtm->rtm_rmx.rmx_expire - now));
562 	else
563 		printf(" %-9.9s", "expired");
564 
565 	printf(" %s%s\n",
566 	    (rtm->rtm_flags & RTF_LOCAL) ? "l" : "",
567 	    (rtm->rtm_flags & RTF_ANNOUNCE) ? "p" : "");
568 }
569 
570 /*
571  * Nuke an arp entry
572  */
573 void
574 nuke_entry(struct sockaddr_dl *sdl, struct sockaddr_inarp *sin,
575     struct rt_msghdr *rtm)
576 {
577 	char ip[20];
578 
579 	strlcpy(ip, inet_ntoa(sin->sin_addr), sizeof(ip));
580 	delete(ip);
581 }
582 
583 static char *
584 ether_str(struct sockaddr_dl *sdl)
585 {
586 	static char hbuf[NI_MAXHOST];
587 	u_char *cp;
588 
589 	if (sdl->sdl_alen) {
590 		cp = (u_char *)LLADDR(sdl);
591 		snprintf(hbuf, sizeof(hbuf), "%02x:%02x:%02x:%02x:%02x:%02x",
592 		    cp[0], cp[1], cp[2], cp[3], cp[4], cp[5]);
593 	} else
594 		snprintf(hbuf, sizeof(hbuf), "(incomplete)");
595 
596 	return(hbuf);
597 }
598 
599 void
600 usage(void)
601 {
602 	fprintf(stderr, "usage: arp [-adn] [-V rdomain] hostname\n");
603 	fprintf(stderr, "       arp [-F] [-f file] [-V rdomain] "
604 	    "-s hostname ether_addr\n"
605 	    "           [temp | permanent] [pub]\n");
606 	fprintf(stderr, "       arp -W ether_addr [iface]\n");
607 	exit(1);
608 }
609 
610 int
611 rtmsg(int cmd)
612 {
613 	static int seq;
614 	struct rt_msghdr *rtm;
615 	char *cp;
616 	int l;
617 
618 	rtm = &m_rtmsg.m_rtm;
619 	cp = m_rtmsg.m_space;
620 	errno = 0;
621 
622 	if (cmd == RTM_DELETE)
623 		goto doit;
624 	memset(&m_rtmsg, 0, sizeof(m_rtmsg));
625 	rtm->rtm_flags = flags;
626 	rtm->rtm_version = RTM_VERSION;
627 	rtm->rtm_hdrlen = sizeof(*rtm);
628 	rtm->rtm_tableid = rdomain;
629 
630 	switch (cmd) {
631 	default:
632 		errx(1, "internal wrong cmd");
633 	case RTM_ADD:
634 		rtm->rtm_addrs |= RTA_GATEWAY;
635 		rtm->rtm_rmx.rmx_expire = expire_time;
636 		rtm->rtm_inits = RTV_EXPIRE;
637 		rtm->rtm_flags |= (RTF_HOST | RTF_STATIC);
638 		sin_m.sin_other = 0;
639 		if (doing_proxy) {
640 			if (export_only)
641 				sin_m.sin_other = SIN_PROXY;
642 			else {
643 				rtm->rtm_addrs |= RTA_NETMASK;
644 				rtm->rtm_flags &= ~RTF_HOST;
645 			}
646 		}
647 		/* FALLTHROUGH */
648 	case RTM_GET:
649 		rtm->rtm_addrs |= (RTA_DST | RTA_IFP);
650 	}
651 
652 #define NEXTADDR(w, s)							\
653 	if (rtm->rtm_addrs & (w)) {					\
654 		memcpy(cp, &(s), sizeof(s));				\
655 		ADVANCE(cp, (struct sockaddr *)&(s));			\
656 	}
657 
658 	NEXTADDR(RTA_DST, sin_m);
659 	NEXTADDR(RTA_GATEWAY, sdl_m);
660 	NEXTADDR(RTA_NETMASK, so_mask);
661 	NEXTADDR(RTA_IFP, ifp_m);
662 
663 	rtm->rtm_msglen = cp - (char *)&m_rtmsg;
664 doit:
665 	l = rtm->rtm_msglen;
666 	rtm->rtm_seq = ++seq;
667 	rtm->rtm_type = cmd;
668 	if (write(rtsock, (char *)&m_rtmsg, l) == -1)
669 		if (errno != ESRCH || cmd != RTM_DELETE) {
670 			warn("writing to routing socket");
671 			return (-1);
672 		}
673 
674 	do {
675 		l = read(rtsock, (char *)&m_rtmsg, sizeof(m_rtmsg));
676 	} while (l > 0 && (rtm->rtm_version != RTM_VERSION ||
677 	    rtm->rtm_seq != seq || rtm->rtm_pid != pid));
678 
679 	if (l < 0)
680 		warn("read from routing socket");
681 	return (0);
682 }
683 
684 int
685 rtget(struct sockaddr_inarp **sinp, struct sockaddr_dl **sdlp)
686 {
687 	struct rt_msghdr *rtm = &(m_rtmsg.m_rtm);
688 	struct sockaddr_inarp *sin = NULL;
689 	struct sockaddr_dl *sdl = NULL;
690 	struct sockaddr *sa;
691 	char *cp;
692 	unsigned int i;
693 
694 	if (rtmsg(RTM_GET) < 0)
695 		return (1);
696 
697 	if (rtm->rtm_addrs) {
698 		cp = ((char *)rtm + rtm->rtm_hdrlen);
699 		for (i = 1; i; i <<= 1) {
700 			if (i & rtm->rtm_addrs) {
701 				sa = (struct sockaddr *)cp;
702 				switch (i) {
703 				case RTA_DST:
704 					sin = (struct sockaddr_inarp *)sa;
705 					break;
706 				case RTA_IFP:
707 					sdl = (struct sockaddr_dl *)sa;
708 					break;
709 				default:
710 					break;
711 				}
712 				ADVANCE(cp, sa);
713 			}
714 		}
715 	}
716 
717 	if (sin == NULL || sdl == NULL)
718 		return (1);
719 
720 	*sinp = sin;
721 	*sdlp = sdl;
722 
723 	return (0);
724 }
725 
726 static char *
727 sec2str(time_t total)
728 {
729 	static char result[256];
730 	int days, hours, mins, secs;
731 	int first = 1;
732 	char *p = result;
733 	char *ep = &result[sizeof(result)];
734 	int n;
735 
736 	days = total / 3600 / 24;
737 	hours = (total / 3600) % 24;
738 	mins = (total / 60) % 60;
739 	secs = total % 60;
740 
741 	if (days) {
742 		first = 0;
743 		n = snprintf(p, ep - p, "%dd", days);
744 		if (n < 0 || n >= ep - p)
745 			return "?";
746 		p += n;
747 	}
748 	if (!first || hours) {
749 		first = 0;
750 		n = snprintf(p, ep - p, "%dh", hours);
751 		if (n < 0 || n >= ep - p)
752 			return "?";
753 		p += n;
754 	}
755 	if (!first || mins) {
756 		first = 0;
757 		n = snprintf(p, ep - p, "%dm", mins);
758 		if (n < 0 || n >= ep - p)
759 			return "?";
760 		p += n;
761 	}
762 	snprintf(p, ep - p, "%ds", secs);
763 
764 	return(result);
765 }
766 
767 /*
768  * Copyright (c) 2011 Jasper Lievisse Adriaanse <jasper@openbsd.org>
769  * Copyright (C) 2006,2007,2008,2009 Marc Balmer <mbalmer@openbsd.org>
770  * Copyright (C) 2000 Eugene M. Kim.  All rights reserved.
771  *
772  * Redistribution and use in source and binary forms, with or without
773  * modification, are permitted provided that the following conditions
774  * are met:
775  *
776  * 1. Redistributions of source code must retain the above copyright
777  *    notice, this list of conditions and the following disclaimer.
778  * 2. Author's name may not be used endorse or promote products derived
779  *    from this software without specific prior written permission.
780  *
781  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
782  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
783  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
784  * DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
785  * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
786  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
787  * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
788  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
789  * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
790  * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
791  * POSSIBILITY OF SUCH DAMAGE.
792  */
793 
794 int	do_wakeup(const char *, const char *, int);
795 int	bind_if_to_bpf(const char *, int);
796 int	get_ether(const char *, struct ether_addr *);
797 int	send_frame(int, const struct ether_addr *);
798 
799 int
800 wake(const char *ether_addr, const char *iface)
801 {
802 	struct ifaddrs		*ifa, *ifap;
803 	char			*pname = NULL;
804 	int			 bpf;
805 
806 	if ((bpf = open("/dev/bpf", O_RDWR)) == -1)
807 		err(1, "Failed to bind to bpf");
808 
809 	if (iface == NULL) {
810 		if (getifaddrs(&ifa) == -1)
811 			errx(1, "Could not get interface addresses.");
812 
813 		for (ifap = ifa; ifap != NULL; ifap = ifap->ifa_next){
814 			if (pname && !strcmp(pname, ifap->ifa_name))
815 				continue;
816 			pname = ifap->ifa_name;
817 
818 			/*
819 			 * We're only interested in sending the WoL frame on
820 			 * certain interfaces. So skip the loopback interface,
821 			 * as well as point-to-point and down interfaces.
822 			 */
823 			if ((ifap->ifa_flags & IFF_LOOPBACK) ||
824 			    (ifap->ifa_flags & IFF_POINTOPOINT) ||
825 			    (!(ifap->ifa_flags & IFF_UP)) ||
826 			    (!(ifap->ifa_flags & IFF_BROADCAST)))
827 				continue;
828 
829 			do_wakeup(ether_addr, ifap->ifa_name, bpf);
830 		}
831 		freeifaddrs(ifa);
832 	} else {
833 		do_wakeup(ether_addr, iface, bpf);
834 	}
835 
836 	(void)close(bpf);
837 
838 	return 0;
839 }
840 
841 int
842 do_wakeup(const char *eaddr, const char *iface, int bpf)
843 {
844 	struct ether_addr	 macaddr;
845 
846 	if (get_ether(eaddr, &macaddr) != 0)
847 		errx(1, "Invalid Ethernet address: %s", eaddr);
848 	if (bind_if_to_bpf(iface, bpf) != 0)
849 		errx(1, "Failed to bind %s to bpf.", iface);
850 	if (send_frame(bpf, &macaddr) != 0)
851 		errx(1, "Failed to send WoL frame on %s", iface);
852 	return 0;
853 }
854 
855 int
856 bind_if_to_bpf(const char *ifname, int bpf)
857 {
858 	struct ifreq ifr;
859 	u_int dlt;
860 
861 	if (strlcpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name)) >=
862 	    sizeof(ifr.ifr_name))
863 		return -1;
864 	if (ioctl(bpf, BIOCSETIF, &ifr) == -1)
865 		return -1;
866 	if (ioctl(bpf, BIOCGDLT, &dlt) == -1)
867 		return -1;
868 	if (dlt != DLT_EN10MB)
869 		return -1;
870 	return 0;
871 }
872 
873 int
874 get_ether(const char *text, struct ether_addr *addr)
875 {
876 	struct ether_addr *eaddr;
877 
878 	eaddr = ether_aton(text);
879 
880 	if (eaddr == NULL) {
881 		if (ether_hostton(text, addr))
882 			return -1;
883 	} else {
884 		*addr = *eaddr;
885 		return 0;
886 	}
887 
888 	return 0;
889 }
890 
891 #define SYNC_LEN 6
892 #define DESTADDR_COUNT 16
893 
894 int
895 send_frame(int bpf, const struct ether_addr *addr)
896 {
897 	struct {
898 		struct ether_header hdr;
899 		u_char sync[SYNC_LEN];
900 		u_char dest[ETHER_ADDR_LEN * DESTADDR_COUNT];
901 	} __packed pkt;
902 	u_char *p;
903 	int i;
904 
905 	(void)memset(&pkt, 0, sizeof(pkt));
906 	(void)memset(&pkt.hdr.ether_dhost, 0xff, sizeof(pkt.hdr.ether_dhost));
907 	pkt.hdr.ether_type = htons(0);
908 	(void)memset(pkt.sync, 0xff, SYNC_LEN);
909 	for (p = pkt.dest, i = 0; i < DESTADDR_COUNT; p += ETHER_ADDR_LEN, i++)
910 		bcopy(addr->ether_addr_octet, p, ETHER_ADDR_LEN);
911 	if (write(bpf, &pkt, sizeof(pkt)) != sizeof(pkt))
912 		return (errno);
913 	return (0);
914 }
915