GCC Code Coverage Report
Directory: ./ Exec Total Coverage
File: usr.sbin/arp/arp.c Lines: 0 407 0.0 %
Date: 2016-12-06 Branches: 0 279 0.0 %

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