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