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