GCC Code Coverage Report
Directory: ./ Exec Total Coverage
File: usr.sbin/arp/arp.c Lines: 206 418 49.3 %
Date: 2017-11-13 Branches: 99 283 35.0 %

Line Branch Exec Source
1
/*	$OpenBSD: arp.c,v 1.79 2017/04/19 05:36:12 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/time.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 <limits.h>
63
#include <ifaddrs.h>
64
65
void dump(void);
66
int delete(const char *);
67
void search(in_addr_t addr, void (*action)(struct sockaddr_dl *sdl,
68
	struct sockaddr_inarp *sin, struct rt_msghdr *rtm));
69
void print_entry(struct sockaddr_dl *sdl,
70
	struct sockaddr_inarp *sin, struct rt_msghdr *rtm);
71
void nuke_entry(struct sockaddr_dl *sdl,
72
	struct sockaddr_inarp *sin, struct rt_msghdr *rtm);
73
static char *ether_str(struct sockaddr_dl *);
74
int wake(const char *ether_addr, const char *iface);
75
int file(char *);
76
int get(const char *);
77
int getinetaddr(const char *, struct in_addr *);
78
void getsocket(void);
79
int rtget(struct sockaddr_inarp **, struct sockaddr_dl **);
80
int rtmsg(int);
81
int set(int, char **);
82
void usage(void);
83
static char *sec2str(time_t);
84
85
static pid_t pid;
86
static int replace;	/* replace entries when adding */
87
static int nflag;	/* no reverse dns lookups */
88
static int aflag;	/* do it for all entries */
89
static int rtsock = -1;
90
static int rdomain;
91
92
extern int h_errno;
93
94
/* ROUNDUP() is nasty, but it is identical to what's in the kernel. */
95
#define ROUNDUP(a)					\
96
	((a) > 0 ? (1 + (((a) - 1) | (sizeof(long) - 1))) : sizeof(long))
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
144
	const char	*errstr;
110
111
72
	pid = getpid();
112
72
	opterr = 0;
113
72
	rdomain = getrtable();
114
584
	while ((ch = getopt(argc, argv, "andsFfV:W")) != -1) {
115


220
		switch (ch) {
116
		case 'a':
117
24
			aflag = 1;
118
24
			break;
119
		case 'n':
120
72
			nflag = 1;
121
72
			break;
122
		case 'd':
123
			if (func)
124
				usage();
125
			func = F_DELETE;
126
			break;
127
		case 's':
128
48
			if (func)
129
				usage();
130
			func = F_SET;
131
48
			break;
132
		case 'F':
133
4
			replace = 1;
134
4
			break;
135
		case 'f':
136
			if (func)
137
				usage();
138
			func = F_FILESET;
139
			break;
140
		case 'V':
141
72
			rdomain = strtonum(optarg, 0, RT_TABLEID_MAX, &errstr);
142
72
			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
72
	argc -= optind;
158
72
	argv += optind;
159
160
72
	if (!func)
161
24
		func = F_GET;
162
163

144
	switch (func) {
164
	case F_GET:
165
24
		if (aflag && argc == 0)
166
24
			dump();
167
		else if (!aflag && argc == 1)
168
			error = get(argv[0]);
169
		else
170
			usage();
171
		break;
172
	case F_SET:
173
48
		if (argc < 2 || argc > 5)
174
			usage();
175
48
		if (replace)
176
4
			delete(argv[0]);
177
48
		error = set(argc, argv) ? 1 : 0;
178
48
		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
72
	return (error);
204
72
}
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
104
	if (rtsock >= 0)
247
4
		return;
248
48
	rtsock = socket(PF_ROUTE, SOCK_RAW, 0);
249
48
	if (rtsock < 0)
250
		err(1, "routing socket");
251
48
	if (setsockopt(rtsock, PF_ROUTE, ROUTE_TABLEFILTER, &rdomain, len) < 0)
252
		err(1, "ROUTE_TABLEFILTER");
253
254
48
	if (pledge("stdio dns flock rpath cpath wpath", NULL) == -1)
255
		err(1, "pledge");
256
100
}
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
96
	struct sockaddr_inarp *sin;
276
48
	struct sockaddr_dl *sdl;
277
	struct rt_msghdr *rtm;
278
48
	char *eaddr = argv[1], *host = argv[0];
279
	struct ether_addr *ea;
280
281
48
	sin = &sin_m;
282
	rtm = &(m_rtmsg.m_rtm);
283
284
48
	getsocket();
285
48
	argc -= 2;
286
48
	argv += 2;
287
48
	sdl_m = blank_sdl;		/* struct copy */
288
48
	sin_m = blank_sin;		/* struct copy */
289
48
	if (getinetaddr(host, &sin->sin_addr) == -1)
290
		return (1);
291
48
	ea = ether_aton(eaddr);
292
48
	if (ea == NULL)
293
		errx(1, "invalid ethernet address: %s", eaddr);
294
48
	memcpy(LLADDR(&sdl_m), ea, sizeof(*ea));
295
48
	sdl_m.sdl_alen = 6;
296
48
	expire_time = 0;
297
48
	doing_proxy = flags = export_only = 0;
298
184
	while (argc-- > 0) {
299
20
		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

20
		} else if (strncmp(argv[0], "pub", 3) == 0) {
310
16
			flags |= RTF_ANNOUNCE;
311
16
			doing_proxy = SIN_PROXY;
312
20
		} else if (strncmp(argv[0], "permanent", 9) == 0) {
313
4
			flags |= RTF_PERMANENT_ARP;
314
4
			if (expire_time != 0) {
315
				/* temp or permanent, not both */
316
				usage();
317
				return (0);
318
			}
319
		}
320
321
20
		argv++;
322
	}
323
324
tryagain:
325
48
	if (rtget(&sin, &sdl)) {
326
		warn("%s", host);
327
		return (1);
328
	}
329
330
48
	if (sin->sin_addr.s_addr == sin_m.sin_addr.s_addr) {
331

40
		if (sdl->sdl_family == AF_LINK &&
332
20
		    (rtm->rtm_flags & RTF_LLINFO) &&
333
20
		    !(rtm->rtm_flags & RTF_GATEWAY))
334

20
			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
48
	if (sdl->sdl_family != AF_LINK) {
359
		printf("cannot intuit interface index and type for %s\n", host);
360
		return (1);
361
	}
362
48
	sdl_m.sdl_type = sdl->sdl_type;
363
48
	sdl_m.sdl_index = sdl->sdl_index;
364
48
	return (rtmsg(RTM_ADD));
365
48
}
366
367
#define W_ADDR	36
368
#define W_LL	17
369
#define W_IF	7
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 %-9.9s %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)
402
{
403
8
	struct sockaddr_inarp *sin;
404
	struct rt_msghdr *rtm;
405
4
	struct sockaddr_dl *sdl;
406
407
4
	sin = &sin_m;
408
	rtm = &m_rtmsg.m_rtm;
409
410
4
	getsocket();
411
4
	sin_m = blank_sin;		/* struct copy */
412
8
	if (getinetaddr(host, &sin->sin_addr) == -1)
413
		return (1);
414
tryagain:
415
4
	if (rtget(&sin, &sdl)) {
416
		warn("%s", host);
417
		return (1);
418
	}
419
4
	if (sin->sin_addr.s_addr == sin_m.sin_addr.s_addr) {
420

8
		if (sdl->sdl_family == AF_LINK && rtm->rtm_flags & RTF_LLINFO) {
421
4
			if (rtm->rtm_flags & RTF_LOCAL)
422
				return (0);
423
4
		    	if (!(rtm->rtm_flags & RTF_GATEWAY))
424

4
				switch (sdl->sdl_type) {
425
				case IFT_ETHER:
426
				case IFT_FDDI:
427
				case IFT_ISO88023:
428
				case IFT_ISO88024:
429
				case IFT_ISO88025:
430
				case IFT_CARP:
431
					goto delete;
432
				}
433
		}
434
	}
435
436
	if (sin_m.sin_other & SIN_PROXY) {
437
		warnx("delete: can't locate %s", host);
438
		return (1);
439
	} else {
440
		sin_m.sin_other = SIN_PROXY;
441
		goto tryagain;
442
	}
443
delete:
444
4
	if (sdl->sdl_family != AF_LINK) {
445
		printf("cannot locate %s\n", host);
446
		return (1);
447
	}
448
4
	if (rtmsg(RTM_DELETE))
449
		return (1);
450
4
	printf("%s (%s) deleted\n", host, inet_ntoa(sin->sin_addr));
451
4
	return (0);
452
4
}
453
454
/*
455
 * Search the entire arp table, and do some action on matching entries.
456
 */
457
void
458
search(in_addr_t addr, void (*action)(struct sockaddr_dl *sdl,
459
    struct sockaddr_inarp *sin, struct rt_msghdr *rtm))
460
{
461
48
	int mib[7];
462
24
	size_t needed;
463
	char *lim, *buf = NULL, *next;
464
	struct rt_msghdr *rtm;
465
	struct sockaddr_inarp *sin;
466
	struct sockaddr_dl *sdl;
467
468
24
	mib[0] = CTL_NET;
469
24
	mib[1] = PF_ROUTE;
470
24
	mib[2] = 0;
471
24
	mib[3] = AF_INET;
472
24
	mib[4] = NET_RT_FLAGS;
473
24
	mib[5] = RTF_LLINFO;
474
24
	mib[6] = rdomain;
475
24
	while (1) {
476
24
		if (sysctl(mib, 7, NULL, &needed, NULL, 0) == -1)
477
			err(1, "route-sysctl-estimate");
478
24
		if (needed == 0)
479
			return;
480
24
		if ((buf = realloc(buf, needed)) == NULL)
481
			err(1, "malloc");
482
24
		if (sysctl(mib, 7, buf, &needed, NULL, 0) == -1) {
483
			if (errno == ENOMEM)
484
				continue;
485
			err(1, "actual retrieval of routing table");
486
		}
487
24
		lim = buf + needed;
488
		break;
489
	}
490
168
	for (next = buf; next < lim; next += rtm->rtm_msglen) {
491
60
		rtm = (struct rt_msghdr *)next;
492
60
		if (rtm->rtm_version != RTM_VERSION)
493
			continue;
494
60
		sin = (struct sockaddr_inarp *)(next + rtm->rtm_hdrlen);
495
60
		sdl = (struct sockaddr_dl *)(sin + 1);
496
60
		if (addr) {
497
			if (addr != sin->sin_addr.s_addr)
498
				continue;
499
			found_entry = 1;
500
		}
501
60
		(*action)(sdl, sin, rtm);
502
60
	}
503
24
	free(buf);
504
48
}
505
506
/*
507
 * Dump the entire ARP table
508
 */
509
void
510
dump(void)
511
{
512
48
	printf("%-*.*s %-*.*s %*.*s %-9.9s %5s\n",
513
	    W_ADDR, W_ADDR, "Host", W_LL, W_LL, "Ethernet Address",
514
	    W_IF, W_IF, "Netif", "Expire", "Flags");
515
516
24
	search(0, print_entry);
517
24
}
518
519
/*
520
 * Display an arp entry
521
 */
522
void
523
print_entry(struct sockaddr_dl *sdl, struct sockaddr_inarp *sin,
524
    struct rt_msghdr *rtm)
525
{
526
120
	char ifix_buf[IFNAMSIZ], *ifname, *host;
527
	struct hostent *hp = NULL;
528
	int addrwidth, llwidth, ifwidth ;
529
60
	struct timeval now;
530
531
60
	gettimeofday(&now, 0);
532
533
60
	if (nflag == 0)
534
		hp = gethostbyaddr((caddr_t)&(sin->sin_addr),
535
		    sizeof(sin->sin_addr), AF_INET);
536
60
	if (hp)
537
		host = hp->h_name;
538
	else
539
60
		host = inet_ntoa(sin->sin_addr);
540
541
60
	addrwidth = strlen(host);
542
60
	if (addrwidth < W_ADDR)
543
		addrwidth = W_ADDR;
544
60
	llwidth = strlen(ether_str(sdl));
545
60
	if (W_ADDR + W_LL - addrwidth > llwidth)
546
		llwidth = W_ADDR + W_LL - addrwidth;
547
60
	ifname = if_indextoname(sdl->sdl_index, ifix_buf);
548
60
	if (!ifname)
549
		ifname = "?";
550
60
	ifwidth = strlen(ifname);
551
60
	if (W_ADDR + W_LL + W_IF - addrwidth - llwidth > ifwidth)
552
		ifwidth = W_ADDR + W_LL + W_IF - addrwidth - llwidth;
553
554
60
	printf("%-*.*s %-*.*s %*.*s", addrwidth, addrwidth, host,
555
60
	    llwidth, llwidth, ether_str(sdl), ifwidth, ifwidth, ifname);
556
557
60
	if (rtm->rtm_flags & (RTF_PERMANENT_ARP|RTF_LOCAL))
558
28
		printf(" %-9.9s", "permanent");
559
32
	else if (rtm->rtm_rmx.rmx_expire == 0)
560
32
		printf(" %-9.9s", "static");
561
	else if (rtm->rtm_rmx.rmx_expire > now.tv_sec)
562
		printf(" %-9.9s",
563
		    sec2str(rtm->rtm_rmx.rmx_expire - now.tv_sec));
564
	else
565
		printf(" %-9.9s", "expired");
566
567
60
	printf(" %s%s\n",
568
60
	    (rtm->rtm_flags & RTF_LOCAL) ? "l" : "",
569
60
	    (rtm->rtm_flags & RTF_ANNOUNCE) ? "p" : "");
570
60
}
571
572
/*
573
 * Nuke an arp entry
574
 */
575
void
576
nuke_entry(struct sockaddr_dl *sdl, struct sockaddr_inarp *sin,
577
    struct rt_msghdr *rtm)
578
{
579
	char ip[20];
580
581
	strlcpy(ip, inet_ntoa(sin->sin_addr), sizeof(ip));
582
	delete(ip);
583
}
584
585
static char *
586
ether_str(struct sockaddr_dl *sdl)
587
{
588
	static char hbuf[NI_MAXHOST];
589
	u_char *cp;
590
591
240
	if (sdl->sdl_alen) {
592
120
		cp = (u_char *)LLADDR(sdl);
593
120
		snprintf(hbuf, sizeof(hbuf), "%02x:%02x:%02x:%02x:%02x:%02x",
594
120
		    cp[0], cp[1], cp[2], cp[3], cp[4], cp[5]);
595
120
	} else
596
		snprintf(hbuf, sizeof(hbuf), "(incomplete)");
597
598
120
	return(hbuf);
599
}
600
601
void
602
usage(void)
603
{
604
	fprintf(stderr, "usage: arp [-adn] [-V rdomain] hostname\n");
605
	fprintf(stderr, "       arp [-F] [-f file] [-V rdomain] "
606
	    "-s hostname ether_addr\n"
607
	    "           [temp | permanent] [pub]\n");
608
	fprintf(stderr, "       arp -W ether_addr [iface]\n");
609
	exit(1);
610
}
611
612
int
613
rtmsg(int cmd)
614
{
615
	static int seq;
616
	struct rt_msghdr *rtm;
617
	char *cp;
618
	int l;
619
620
	rtm = &m_rtmsg.m_rtm;
621
	cp = m_rtmsg.m_space;
622
208
	errno = 0;
623
624
104
	if (cmd == RTM_DELETE)
625
		goto doit;
626
148
	memset(&m_rtmsg, 0, sizeof(m_rtmsg));
627
148
	rtm->rtm_flags = flags;
628
148
	rtm->rtm_version = RTM_VERSION;
629
148
	rtm->rtm_hdrlen = sizeof(*rtm);
630
148
	rtm->rtm_tableid = rdomain;
631
632
148
	switch (cmd) {
633
	default:
634
		errx(1, "internal wrong cmd");
635
		/*NOTREACHED*/
636
	case RTM_ADD:
637
48
		rtm->rtm_addrs |= RTA_GATEWAY;
638
48
		rtm->rtm_rmx.rmx_expire = expire_time;
639
48
		rtm->rtm_inits = RTV_EXPIRE;
640
48
		rtm->rtm_flags |= (RTF_HOST | RTF_STATIC);
641
48
		sin_m.sin_other = 0;
642
48
		if (doing_proxy) {
643
16
			if (export_only)
644
				sin_m.sin_other = SIN_PROXY;
645
			else {
646
16
				rtm->rtm_addrs |= RTA_NETMASK;
647
16
				rtm->rtm_flags &= ~RTF_HOST;
648
			}
649
		}
650
		/* FALLTHROUGH */
651
	case RTM_GET:
652
100
		rtm->rtm_addrs |= (RTA_DST | RTA_IFP);
653
	}
654
655
#define NEXTADDR(w, s)					\
656
	if (rtm->rtm_addrs & (w)) {			\
657
		memcpy(cp, &s, sizeof(s));		\
658
		cp += ROUNDUP(sizeof(s));		\
659
	}
660
661
200
	NEXTADDR(RTA_DST, sin_m);
662
148
	NEXTADDR(RTA_GATEWAY, sdl_m);
663
116
	NEXTADDR(RTA_NETMASK, so_mask);
664
200
	NEXTADDR(RTA_IFP, ifp_m);
665
666
100
	rtm->rtm_msglen = cp - (char *)&m_rtmsg;
667
doit:
668
104
	l = rtm->rtm_msglen;
669
104
	rtm->rtm_seq = ++seq;
670
104
	rtm->rtm_type = cmd;
671
104
	if (write(rtsock, (char *)&m_rtmsg, l) < 0)
672
8
		if (errno != ESRCH || cmd != RTM_DELETE) {
673
8
			warn("writing to routing socket");
674
8
			return (-1);
675
		}
676
677
96
	do {
678
96
		l = read(rtsock, (char *)&m_rtmsg, sizeof(m_rtmsg));
679

288
	} while (l > 0 && (rtm->rtm_version != RTM_VERSION ||
680
192
	    rtm->rtm_seq != seq || rtm->rtm_pid != pid));
681
682
96
	if (l < 0)
683
		warn("read from routing socket");
684
96
	return (0);
685
104
}
686
687
int
688
rtget(struct sockaddr_inarp **sinp, struct sockaddr_dl **sdlp)
689
{
690
	struct rt_msghdr *rtm = &(m_rtmsg.m_rtm);
691
	struct sockaddr_inarp *sin = NULL;
692
	struct sockaddr_dl *sdl = NULL;
693
	struct sockaddr *sa;
694
	char *cp;
695
	unsigned int i;
696
697
104
	if (rtmsg(RTM_GET) < 0)
698
		return (1);
699
700
52
	if (rtm->rtm_addrs) {
701
52
		cp = ((char *)rtm + rtm->rtm_hdrlen);
702
3432
		for (i = 1; i; i <<= 1) {
703
1664
			if (i & rtm->rtm_addrs) {
704
364
				sa = (struct sockaddr *)cp;
705
364
				switch (i) {
706
				case RTA_DST:
707
52
					sin = (struct sockaddr_inarp *)sa;
708
52
					break;
709
				case RTA_IFP:
710
52
					sdl = (struct sockaddr_dl *)sa;
711
52
					break;
712
				default:
713
					break;
714
				}
715
780
				cp += ROUNDUP(sa->sa_len);
716
260
			}
717
		}
718
	}
719
720
52
	if (sin == NULL || sdl == NULL)
721
		return (1);
722
723
52
	*sinp = sin;
724
52
	*sdlp = sdl;
725
726
52
	return (0);
727
52
}
728
729
int
730
getinetaddr(const char *host, struct in_addr *inap)
731
{
732
	struct hostent *hp;
733
734
104
	if (inet_aton(host, inap) == 1)
735
52
		return (0);
736
	if ((hp = gethostbyname(host)) == NULL) {
737
		warnx("%s: %s", host, hstrerror(h_errno));
738
		return (-1);
739
	}
740
	memcpy(inap, hp->h_addr, sizeof(*inap));
741
	return (0);
742
52
}
743
744
static char *
745
sec2str(time_t total)
746
{
747
	static char result[256];
748
	int days, hours, mins, secs;
749
	int first = 1;
750
	char *p = result;
751
	char *ep = &result[sizeof(result)];
752
	int n;
753
754
	days = total / 3600 / 24;
755
	hours = (total / 3600) % 24;
756
	mins = (total / 60) % 60;
757
	secs = total % 60;
758
759
	if (days) {
760
		first = 0;
761
		n = snprintf(p, ep - p, "%dd", days);
762
		if (n < 0 || n >= ep - p)
763
			return "?";
764
		p += n;
765
	}
766
	if (!first || hours) {
767
		first = 0;
768
		n = snprintf(p, ep - p, "%dh", hours);
769
		if (n < 0 || n >= ep - p)
770
			return "?";
771
		p += n;
772
	}
773
	if (!first || mins) {
774
		first = 0;
775
		n = snprintf(p, ep - p, "%dm", mins);
776
		if (n < 0 || n >= ep - p)
777
			return "?";
778
		p += n;
779
	}
780
	snprintf(p, ep - p, "%ds", secs);
781
782
	return(result);
783
}
784
785
/*
786
 * Copyright (c) 2011 Jasper Lievisse Adriaanse <jasper@openbsd.org>
787
 * Copyright (C) 2006,2007,2008,2009 Marc Balmer <mbalmer@openbsd.org>
788
 * Copyright (C) 2000 Eugene M. Kim.  All rights reserved.
789
 *
790
 * Redistribution and use in source and binary forms, with or without
791
 * modification, are permitted provided that the following conditions
792
 * are met:
793
 *
794
 * 1. Redistributions of source code must retain the above copyright
795
 *    notice, this list of conditions and the following disclaimer.
796
 * 2. Author's name may not be used endorse or promote products derived
797
 *    from this software without specific prior written permission.
798
 *
799
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
800
 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
801
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
802
 * DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
803
 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
804
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
805
 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
806
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
807
 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
808
 * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
809
 * POSSIBILITY OF SUCH DAMAGE.
810
 */
811
812
int	do_wakeup(const char *, const char *, int);
813
int	bind_if_to_bpf(const char *, int);
814
int	get_ether(const char *, struct ether_addr *);
815
int	send_frame(int, const struct ether_addr *);
816
817
int
818
wake(const char *ether_addr, const char *iface)
819
{
820
	struct ifaddrs		*ifa, *ifap;
821
	char			*pname = NULL;
822
	int			 bpf;
823
824
	if ((bpf = open("/dev/bpf", O_RDWR)) == -1)
825
		err(1, "Failed to bind to bpf");
826
827
	if (iface == NULL) {
828
		if (getifaddrs(&ifa) == -1)
829
			errx(1, "Could not get interface addresses.");
830
831
		for (ifap = ifa; ifap != NULL; ifap = ifap->ifa_next){
832
			if (pname && !strcmp(pname, ifap->ifa_name))
833
				continue;
834
			pname = ifap->ifa_name;
835
836
			/*
837
			 * We're only interested in sending the WoL frame on
838
			 * certain interfaces. So skip the loopback interface,
839
			 * as well as point-to-point and down interfaces.
840
			 */
841
			if ((ifap->ifa_flags & IFF_LOOPBACK) ||
842
			    (ifap->ifa_flags & IFF_POINTOPOINT) ||
843
			    (!(ifap->ifa_flags & IFF_UP)) ||
844
			    (!(ifap->ifa_flags & IFF_BROADCAST)))
845
				continue;
846
847
			do_wakeup(ether_addr, ifap->ifa_name, bpf);
848
		}
849
		freeifaddrs(ifa);
850
	} else {
851
		do_wakeup(ether_addr, iface, bpf);
852
	}
853
854
	(void)close(bpf);
855
856
	return 0;
857
}
858
859
int
860
do_wakeup(const char *eaddr, const char *iface, int bpf)
861
{
862
	struct ether_addr	 macaddr;
863
864
	if (get_ether(eaddr, &macaddr) != 0)
865
		errx(1, "Invalid Ethernet address: %s", eaddr);
866
	if (bind_if_to_bpf(iface, bpf) != 0)
867
		errx(1, "Failed to bind %s to bpf.", iface);
868
	if (send_frame(bpf, &macaddr) != 0)
869
		errx(1, "Failed to send WoL frame on %s", iface);
870
	return 0;
871
}
872
873
int
874
bind_if_to_bpf(const char *ifname, int bpf)
875
{
876
	struct ifreq ifr;
877
	u_int dlt;
878
879
	if (strlcpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name)) >=
880
	    sizeof(ifr.ifr_name))
881
		return -1;
882
	if (ioctl(bpf, BIOCSETIF, &ifr) == -1)
883
		return -1;
884
	if (ioctl(bpf, BIOCGDLT, &dlt) == -1)
885
		return -1;
886
	if (dlt != DLT_EN10MB)
887
		return -1;
888
	return 0;
889
}
890
891
int
892
get_ether(const char *text, struct ether_addr *addr)
893
{
894
	struct ether_addr *eaddr;
895
896
	eaddr = ether_aton(text);
897
898
	if (eaddr == NULL) {
899
		if (ether_hostton(text, addr))
900
			return -1;
901
	} else {
902
		*addr = *eaddr;
903
		return 0;
904
	}
905
906
	return 0;
907
}
908
909
#define SYNC_LEN 6
910
#define DESTADDR_COUNT 16
911
912
int
913
send_frame(int bpf, const struct ether_addr *addr)
914
{
915
	struct {
916
		struct ether_header hdr;
917
		u_char sync[SYNC_LEN];
918
		u_char dest[ETHER_ADDR_LEN * DESTADDR_COUNT];
919
	} __packed pkt;
920
	u_char *p;
921
	int i;
922
923
	(void)memset(&pkt, 0, sizeof(pkt));
924
	(void)memset(&pkt.hdr.ether_dhost, 0xff, sizeof(pkt.hdr.ether_dhost));
925
	pkt.hdr.ether_type = htons(0);
926
	(void)memset(pkt.sync, 0xff, SYNC_LEN);
927
	for (p = pkt.dest, i = 0; i < DESTADDR_COUNT; p += ETHER_ADDR_LEN, i++)
928
		bcopy(addr->ether_addr_octet, p, ETHER_ADDR_LEN);
929
	if (write(bpf, &pkt, sizeof(pkt)) != sizeof(pkt))
930
		return (errno);
931
	return (0);
932
}