xref: /openbsd-src/usr.sbin/arp/arp.c (revision 6fb12b7054efc6b436584db6cef9c2f85c0d7e27)
1 /*	$OpenBSD: arp.c,v 1.62 2014/09/07 22:40:30 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/param.h>
41 #include <sys/file.h>
42 #include <sys/socket.h>
43 #include <sys/sysctl.h>
44 #include <sys/ioctl.h>
45 #include <net/bpf.h>
46 #include <net/if.h>
47 #include <net/if_dl.h>
48 #include <net/if_types.h>
49 #include <net/route.h>
50 #include <netinet/in.h>
51 #include <netinet/if_ether.h>
52 #include <arpa/inet.h>
53 
54 #include <netdb.h>
55 #include <errno.h>
56 #include <err.h>
57 #include <stdio.h>
58 #include <stdlib.h>
59 #include <string.h>
60 #include <paths.h>
61 #include <unistd.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 rtmsg(int);
79 int set(int, char **);
80 void usage(void);
81 static char *sec2str(time_t);
82 
83 static pid_t pid;
84 static int replace;	/* replace entries when adding */
85 static int nflag;	/* no reverse dns lookups */
86 static int aflag;	/* do it for all entries */
87 static int s = -1;
88 static int rdomain;
89 
90 extern int h_errno;
91 
92 /* ROUNDUP() is nasty, but it is identical to what's in the kernel. */
93 #define ROUNDUP(a)					\
94 	((a) > 0 ? (1 + (((a) - 1) | (sizeof(long) - 1))) : sizeof(long))
95 
96 /* which function we're supposed to do */
97 #define F_GET		1
98 #define F_SET		2
99 #define F_FILESET	3
100 #define F_DELETE	4
101 #define F_WAKE		5
102 
103 int
104 main(int argc, char *argv[])
105 {
106 	int		 ch, func = 0, rtn;
107 	const char	*errstr;
108 
109 	pid = getpid();
110 	opterr = 0;
111 	rdomain = getrtable();
112 	while ((ch = getopt(argc, argv, "andsFfV:W")) != -1) {
113 		switch (ch) {
114 		case 'a':
115 			aflag = 1;
116 			break;
117 		case 'n':
118 			nflag = 1;
119 			break;
120 		case 'd':
121 			if (func)
122 				usage();
123 			func = F_DELETE;
124 			break;
125 		case 's':
126 			if (func)
127 				usage();
128 			func = F_SET;
129 			break;
130 		case 'F':
131 			replace = 1;
132 			break;
133 		case 'f':
134 			if (func)
135 				usage();
136 			func = F_FILESET;
137 			break;
138 		case 'V':
139 			rdomain = strtonum(optarg, 0, RT_TABLEID_MAX, &errstr);
140 			if (errstr != NULL) {
141 				warn("bad rdomain: %s", errstr);
142 				usage();
143 			}
144 			break;
145 		case 'W':
146 			if (func)
147 				usage();
148 			func = F_WAKE;
149 			break;
150 		default:
151 			usage();
152 			break;
153 		}
154 	}
155 	argc -= optind;
156 	argv += optind;
157 
158 	if (!func)
159 		func = F_GET;
160 	rtn = 0;
161 
162 	switch (func) {
163 	case F_GET:
164 		if (aflag && argc == 0)
165 			dump();
166 		else if (!aflag && argc == 1)
167 			rtn = 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], NULL);
176 		rtn = 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 			rtn = delete(argv[0], argv[1]);
183 		else
184 			usage();
185 		break;
186 	case F_FILESET:
187 		if (argc != 1)
188 			usage();
189 		rtn = file(argv[0]);
190 		break;
191 	case F_WAKE:
192 		if (aflag || nflag || replace || rdomain > 0)
193 			usage();
194 		if (argc == 1)
195 			rtn = wake(argv[0], NULL);
196 		else if (argc == 2)
197 			rtn = wake(argv[0], argv[1]);
198 		else
199 			usage();
200 		break;
201 	}
202 	return (rtn);
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], NULL);
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 (s >= 0)
246 		return;
247 	s = socket(PF_ROUTE, SOCK_RAW, 0);
248 	if (s < 0)
249 		err(1, "socket");
250 	if (setsockopt(s, PF_ROUTE, ROUTE_TABLEFILTER, &rdomain, len) < 0)
251 		err(1, "ROUTE_TABLEFILTER");
252 }
253 
254 struct sockaddr_in	so_mask = { 8, 0, 0, { 0xffffffff } };
255 struct sockaddr_inarp	blank_sin = { sizeof(blank_sin), AF_INET }, sin_m;
256 struct sockaddr_dl	blank_sdl = { sizeof(blank_sdl), AF_LINK }, sdl_m;
257 time_t			expire_time;
258 int			flags, export_only, doing_proxy, found_entry;
259 struct	{
260 	struct rt_msghdr	m_rtm;
261 	char			m_space[512];
262 }	m_rtmsg;
263 
264 /*
265  * Set an individual arp entry
266  */
267 int
268 set(int argc, char *argv[])
269 {
270 	struct sockaddr_inarp *sin;
271 	struct sockaddr_dl *sdl;
272 	struct rt_msghdr *rtm;
273 	char *eaddr = argv[1], *host = argv[0];
274 	struct ether_addr *ea;
275 
276 	sin = &sin_m;
277 	rtm = &(m_rtmsg.m_rtm);
278 
279 	getsocket();
280 	argc -= 2;
281 	argv += 2;
282 	sdl_m = blank_sdl;		/* struct copy */
283 	sin_m = blank_sin;		/* struct copy */
284 	if (getinetaddr(host, &sin->sin_addr) == -1)
285 		return (1);
286 	ea = ether_aton(eaddr);
287 	if (ea == NULL)
288 		errx(1, "invalid ethernet address: %s", eaddr);
289 	memcpy(LLADDR(&sdl_m), ea, sizeof(*ea));
290 	sdl_m.sdl_alen = 6;
291 	expire_time = 0;
292 	doing_proxy = flags = export_only = 0;
293 	while (argc-- > 0) {
294 		if (strncmp(argv[0], "temp", 4) == 0) {
295 			struct timeval now;
296 
297 			gettimeofday(&now, 0);
298 			expire_time = now.tv_sec + 20 * 60;
299 			if (flags & RTF_PERMANENT_ARP) {
300 				/* temp or permanent, not both */
301 				usage();
302 				return (0);
303 			}
304 		} else if (strncmp(argv[0], "pub", 3) == 0) {
305 			flags |= RTF_ANNOUNCE;
306 			doing_proxy = SIN_PROXY;
307 		} else if (strncmp(argv[0], "permanent", 9) == 0) {
308 			flags |= RTF_PERMANENT_ARP;
309 			if (expire_time != 0) {
310 				/* temp or permanent, not both */
311 				usage();
312 				return (0);
313 			}
314 		} else if (strncmp(argv[0], "trail", 5) == 0)
315 			printf("%s: Sending trailers is no longer supported\n",
316 			    host);
317 
318 		argv++;
319 	}
320 
321 tryagain:
322 	if (rtmsg(RTM_GET) < 0) {
323 		warn("%s", host);
324 		return (1);
325 	}
326 	sin = (struct sockaddr_inarp *)((char *)rtm + rtm->rtm_hdrlen);
327 	sdl = (struct sockaddr_dl *)(ROUNDUP(sin->sin_len) + (char *)sin);
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	6
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 (getinetaddr(host, &sin->sin_addr) == -1)
380 		exit(1);
381 
382 	printf("%-*.*s %-*.*s %*.*s %-10.10s %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, const char *info)
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 	if (info && strncmp(info, "pro", 3) )
409 		export_only = 1;
410 	getsocket();
411 	sin_m = blank_sin;		/* struct copy */
412 	if (getinetaddr(host, &sin->sin_addr) == -1)
413 		return (1);
414 tryagain:
415 	if (rtmsg(RTM_GET) < 0) {
416 		warn("%s", host);
417 		return (1);
418 	}
419 	sin = (struct sockaddr_inarp *)((char *)rtm + rtm->rtm_hdrlen);
420 	sdl = (struct sockaddr_dl *)(ROUNDUP(sin->sin_len) + (char *)sin);
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|RTF_BROADCAST))
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 		if (rtm->rtm_flags & RTF_BROADCAST)
497 			continue;
498 		sin = (struct sockaddr_inarp *)(next + rtm->rtm_hdrlen);
499 		sdl = (struct sockaddr_dl *)(sin + 1);
500 		if (addr) {
501 			if (addr != sin->sin_addr.s_addr)
502 				continue;
503 			found_entry = 1;
504 		}
505 		(*action)(sdl, sin, rtm);
506 	}
507 	free(buf);
508 }
509 
510 /*
511  * Dump the entire ARP table
512  */
513 void
514 dump(void)
515 {
516 	printf("%-*.*s %-*.*s %*.*s %-10.10s %5s\n",
517 	    W_ADDR, W_ADDR, "Host", W_LL, W_LL, "Ethernet Address",
518 	    W_IF, W_IF, "Netif", "Expire", "Flags");
519 
520 	search(0, print_entry);
521 }
522 
523 /*
524  * Display an arp entry
525  */
526 void
527 print_entry(struct sockaddr_dl *sdl, struct sockaddr_inarp *sin,
528     struct rt_msghdr *rtm)
529 {
530 	char ifix_buf[IFNAMSIZ], *ifname, *host;
531 	struct hostent *hp = NULL;
532 	int addrwidth, llwidth, ifwidth ;
533 	struct timeval now;
534 
535 	gettimeofday(&now, 0);
536 
537 	if (nflag == 0)
538 		hp = gethostbyaddr((caddr_t)&(sin->sin_addr),
539 		    sizeof(sin->sin_addr), AF_INET);
540 	if (hp)
541 		host = hp->h_name;
542 	else
543 		host = inet_ntoa(sin->sin_addr);
544 
545 	addrwidth = strlen(host);
546 	if (addrwidth < W_ADDR)
547 		addrwidth = W_ADDR;
548 	llwidth = strlen(ether_str(sdl));
549 	if (W_ADDR + W_LL - addrwidth > llwidth)
550 		llwidth = W_ADDR + W_LL - addrwidth;
551 	ifname = if_indextoname(sdl->sdl_index, ifix_buf);
552 	if (!ifname)
553 		ifname = "?";
554 	ifwidth = strlen(ifname);
555 	if (W_ADDR + W_LL + W_IF - addrwidth - llwidth > ifwidth)
556 		ifwidth = W_ADDR + W_LL + W_IF - addrwidth - llwidth;
557 
558 	printf("%-*.*s %-*.*s %*.*s", addrwidth, addrwidth, host,
559 	    llwidth, llwidth, ether_str(sdl), ifwidth, ifwidth, ifname);
560 
561 	if (rtm->rtm_flags & (RTF_PERMANENT_ARP|RTF_LOCAL|RTF_BROADCAST))
562 		printf(" %-10.10s", "permanent");
563 	else if (rtm->rtm_rmx.rmx_expire == 0)
564 		printf(" %-10.10s", "static");
565 	else if (rtm->rtm_rmx.rmx_expire > now.tv_sec)
566 		printf(" %-10.10s",
567 		    sec2str(rtm->rtm_rmx.rmx_expire - now.tv_sec));
568 	else
569 		printf(" %-10.10s", "expired");
570 
571 	printf(" %s%s%s\n",
572 	    (rtm->rtm_flags & RTF_LOCAL) ? "l" : "",
573 	    (sin->sin_other & SIN_PROXY) ? "P" : "",
574 	    (rtm->rtm_flags & RTF_ANNOUNCE) ? "p" : "");
575 }
576 
577 /*
578  * Nuke an arp entry
579  */
580 void
581 nuke_entry(struct sockaddr_dl *sdl, struct sockaddr_inarp *sin,
582     struct rt_msghdr *rtm)
583 {
584 	char ip[20];
585 
586 	strlcpy(ip, inet_ntoa(sin->sin_addr), sizeof(ip));
587 	delete(ip, NULL);
588 }
589 
590 static char *
591 ether_str(struct sockaddr_dl *sdl)
592 {
593 	static char hbuf[NI_MAXHOST];
594 	u_char *cp;
595 
596 	if (sdl->sdl_alen) {
597 		cp = (u_char *)LLADDR(sdl);
598 		snprintf(hbuf, sizeof(hbuf), "%02x:%02x:%02x:%02x:%02x:%02x",
599 		    cp[0], cp[1], cp[2], cp[3], cp[4], cp[5]);
600 	} else
601 		snprintf(hbuf, sizeof(hbuf), "(incomplete)");
602 
603 	return(hbuf);
604 }
605 
606 void
607 usage(void)
608 {
609 	fprintf(stderr, "usage: arp [-adn] [-V rdomain] hostname\n");
610 	fprintf(stderr, "       arp [-F] [-f file] [-V rdomain] "
611 	    "-s hostname ether_addr\n"
612 	    "           [temp | permanent] [pub]\n");
613 	fprintf(stderr, "       arp -W ether_addr [iface]\n");
614 	exit(1);
615 }
616 
617 int
618 rtmsg(int cmd)
619 {
620 	static int seq;
621 	struct rt_msghdr *rtm;
622 	char *cp;
623 	int l;
624 
625 	rtm = &m_rtmsg.m_rtm;
626 	cp = m_rtmsg.m_space;
627 	errno = 0;
628 
629 	if (cmd == RTM_DELETE)
630 		goto doit;
631 	memset(&m_rtmsg, 0, sizeof(m_rtmsg));
632 	rtm->rtm_flags = flags;
633 	rtm->rtm_version = RTM_VERSION;
634 	rtm->rtm_hdrlen = sizeof(*rtm);
635 	rtm->rtm_tableid = rdomain;
636 
637 	switch (cmd) {
638 	default:
639 		errx(1, "internal wrong cmd");
640 		/*NOTREACHED*/
641 	case RTM_ADD:
642 		rtm->rtm_addrs |= RTA_GATEWAY;
643 		rtm->rtm_rmx.rmx_expire = expire_time;
644 		rtm->rtm_inits = RTV_EXPIRE;
645 		rtm->rtm_flags |= (RTF_HOST | RTF_STATIC);
646 		sin_m.sin_other = 0;
647 		if (doing_proxy) {
648 			if (export_only)
649 				sin_m.sin_other = SIN_PROXY;
650 			else {
651 				rtm->rtm_addrs |= RTA_NETMASK;
652 				rtm->rtm_flags &= ~RTF_HOST;
653 			}
654 		}
655 		/* FALLTHROUGH */
656 	case RTM_GET:
657 		rtm->rtm_addrs |= RTA_DST;
658 	}
659 
660 #define NEXTADDR(w, s)					\
661 	if (rtm->rtm_addrs & (w)) {			\
662 		memcpy(cp, &s, sizeof(s));		\
663 		cp += ROUNDUP(sizeof(s));		\
664 	}
665 
666 	NEXTADDR(RTA_DST, sin_m);
667 	NEXTADDR(RTA_GATEWAY, sdl_m);
668 	NEXTADDR(RTA_NETMASK, so_mask);
669 
670 	rtm->rtm_msglen = cp - (char *)&m_rtmsg;
671 doit:
672 	l = rtm->rtm_msglen;
673 	rtm->rtm_seq = ++seq;
674 	rtm->rtm_type = cmd;
675 	if (write(s, (char *)&m_rtmsg, l) < 0)
676 		if (errno != ESRCH || cmd != RTM_DELETE) {
677 			warn("writing to routing socket");
678 			return (-1);
679 		}
680 
681 	do {
682 		l = read(s, (char *)&m_rtmsg, sizeof(m_rtmsg));
683 	} while (l > 0 && (rtm->rtm_version != RTM_VERSION ||
684 	    rtm->rtm_seq != seq || rtm->rtm_pid != pid));
685 
686 	if (l < 0)
687 		warn("read from routing socket");
688 	return (0);
689 }
690 
691 int
692 getinetaddr(const char *host, struct in_addr *inap)
693 {
694 	struct hostent *hp;
695 
696 	if (inet_aton(host, inap) == 1)
697 		return (0);
698 	if ((hp = gethostbyname(host)) == NULL) {
699 		warnx("%s: %s", host, hstrerror(h_errno));
700 		return (-1);
701 	}
702 	memcpy(inap, hp->h_addr, sizeof(*inap));
703 	return (0);
704 }
705 
706 static char *
707 sec2str(time_t total)
708 {
709 	static char result[256];
710 	int days, hours, mins, secs;
711 	int first = 1;
712 	char *p = result;
713 	char *ep = &result[sizeof(result)];
714 	int n;
715 
716 	days = total / 3600 / 24;
717 	hours = (total / 3600) % 24;
718 	mins = (total / 60) % 60;
719 	secs = total % 60;
720 
721 	if (days) {
722 		first = 0;
723 		n = snprintf(p, ep - p, "%dd", days);
724 		if (n < 0 || n >= ep - p)
725 			return "?";
726 		p += n;
727 	}
728 	if (!first || hours) {
729 		first = 0;
730 		n = snprintf(p, ep - p, "%dh", hours);
731 		if (n < 0 || n >= ep - p)
732 			return "?";
733 		p += n;
734 	}
735 	if (!first || mins) {
736 		first = 0;
737 		n = snprintf(p, ep - p, "%dm", mins);
738 		if (n < 0 || n >= ep - p)
739 			return "?";
740 		p += n;
741 	}
742 	snprintf(p, ep - p, "%ds", secs);
743 
744 	return(result);
745 }
746 
747 /*
748  * Copyright (c) 2011 Jasper Lievisse Adriaanse <jasper@openbsd.org>
749  * Copyright (C) 2006,2007,2008,2009 Marc Balmer <mbalmer@openbsd.org>
750  * Copyright (C) 2000 Eugene M. Kim.  All rights reserved.
751  *
752  * Redistribution and use in source and binary forms, with or without
753  * modification, are permitted provided that the following conditions
754  * are met:
755  *
756  * 1. Redistributions of source code must retain the above copyright
757  *    notice, this list of conditions and the following disclaimer.
758  * 2. Author's name may not be used endorse or promote products derived
759  *    from this software without specific prior written permission.
760  *
761  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
762  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
763  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
764  * DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
765  * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
766  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
767  * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
768  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
769  * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
770  * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
771  * POSSIBILITY OF SUCH DAMAGE.
772  */
773 
774 #ifndef BPF_PATH_FORMAT
775 #define BPF_PATH_FORMAT "/dev/bpf%u"
776 #endif
777 
778 int	do_wakeup(const char *, const char *, int);
779 int	get_bpf(void);
780 int	bind_if_to_bpf(const char *, int);
781 int	get_ether(const char *, struct ether_addr *);
782 int	send_frame(int, const struct ether_addr *);
783 
784 int
785 wake(const char *ether_addr, const char *iface)
786 {
787 	struct ifaddrs		*ifa, *ifap;
788 	char			*pname = NULL;
789 	int			 bpf;
790 
791 	bpf = get_bpf();
792 	if (bpf == -1)
793 		errx(1, "Failed to bind to bpf.");
794 
795 	if (iface == NULL) {
796 		if (getifaddrs(&ifa) == -1)
797 			errx(1, "Could not get interface addresses.");
798 
799 		for (ifap = ifa; ifap != NULL; ifap = ifap->ifa_next){
800 			if (pname && !strcmp(pname, ifap->ifa_name))
801 				continue;
802 			pname = ifap->ifa_name;
803 
804 			/*
805 			 * We're only interested in sending the WoL frame on
806 			 * certain interfaces. So skip the loopback interface,
807 			 * as well as point-to-point and down interfaces.
808 			 */
809 			if ((ifap->ifa_flags & IFF_LOOPBACK) ||
810 			    (ifap->ifa_flags & IFF_POINTOPOINT) ||
811 			    (!(ifap->ifa_flags & IFF_UP)) ||
812 			    (!(ifap->ifa_flags & IFF_BROADCAST)))
813 				continue;
814 
815 			do_wakeup(ether_addr, ifap->ifa_name, bpf);
816 		}
817 		freeifaddrs(ifa);
818 	} else {
819 		do_wakeup(ether_addr, iface, bpf);
820 	}
821 
822 	(void)close(bpf);
823 
824 	return 0;
825 }
826 
827 int
828 do_wakeup(const char *eaddr, const char *iface, int bpf)
829 {
830 	struct ether_addr	 macaddr;
831 
832 	if (get_ether(eaddr, &macaddr) != 0)
833 		errx(1, "Invalid Ethernet address: %s", eaddr);
834 	if (bind_if_to_bpf(iface, bpf) != 0)
835 		errx(1, "Failed to bind %s to bpf.", iface);
836 	if (send_frame(bpf, &macaddr) != 0)
837 		errx(1, "Failed to send WoL frame on %s", iface);
838 	return 0;
839 }
840 
841 int
842 get_bpf(void)
843 {
844 	char path[MAXPATHLEN];
845 	int i, fd;
846 
847 	for (i = 0; ; i++) {
848 		if (snprintf(path, sizeof(path), BPF_PATH_FORMAT, i) == -1)
849 			return -1;
850 		fd = open(path, O_RDWR);
851 		if (fd != -1)
852 			return fd;
853 		if (errno == EBUSY)
854 			continue;
855 		break;
856 	}
857 	return -1;
858 }
859 
860 int
861 bind_if_to_bpf(const char *ifname, int bpf)
862 {
863 	struct ifreq ifr;
864 	u_int dlt;
865 
866 	if (strlcpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name)) >=
867 	    sizeof(ifr.ifr_name))
868 		return -1;
869 	if (ioctl(bpf, BIOCSETIF, &ifr) == -1)
870 		return -1;
871 	if (ioctl(bpf, BIOCGDLT, &dlt) == -1)
872 		return -1;
873 	if (dlt != DLT_EN10MB)
874 		return -1;
875 	return 0;
876 }
877 
878 int
879 get_ether(const char *text, struct ether_addr *addr)
880 {
881 	struct ether_addr *eaddr;
882 
883 	eaddr = ether_aton(text);
884 
885 	if (eaddr == NULL) {
886 		if (ether_hostton(text, addr))
887 			return -1;
888 	} else {
889 		*addr = *eaddr;
890 		return 0;
891 	}
892 
893 	return 0;
894 }
895 
896 #define SYNC_LEN 6
897 #define DESTADDR_COUNT 16
898 
899 int
900 send_frame(int bpf, const struct ether_addr *addr)
901 {
902 	struct {
903 		struct ether_header hdr;
904 		u_char sync[SYNC_LEN];
905 		u_char dest[ETHER_ADDR_LEN * DESTADDR_COUNT];
906 	} __packed pkt;
907 	u_char *p;
908 	int i;
909 
910 	(void)memset(&pkt, 0, sizeof(pkt));
911 	(void)memset(&pkt.hdr.ether_dhost, 0xff, sizeof(pkt.hdr.ether_dhost));
912 	pkt.hdr.ether_type = htons(0);
913 	(void)memset(pkt.sync, 0xff, SYNC_LEN);
914 	for (p = pkt.dest, i = 0; i < DESTADDR_COUNT; p += ETHER_ADDR_LEN, i++)
915 		bcopy(addr->ether_addr_octet, p, ETHER_ADDR_LEN);
916 	if (write(bpf, &pkt, sizeof(pkt)) != sizeof(pkt))
917 		return (errno);
918 	return (0);
919 }
920