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