xref: /openbsd-src/usr.sbin/arp/arp.c (revision fba40a1195b51a081e0dd837143227fa0bcead50)
1*fba40a11Sbluhm /*	$OpenBSD: arp.c,v 1.89 2023/04/04 21:18:04 bluhm Exp $ */
2df930be7Sderaadt /*	$NetBSD: arp.c,v 1.12 1995/04/24 13:25:18 cgd Exp $ */
3df930be7Sderaadt 
4df930be7Sderaadt /*
5df930be7Sderaadt  * Copyright (c) 1984, 1993
6df930be7Sderaadt  *	The Regents of the University of California.  All rights reserved.
7df930be7Sderaadt  *
8df930be7Sderaadt  * This code is derived from software contributed to Berkeley by
9df930be7Sderaadt  * Sun Microsystems, Inc.
10df930be7Sderaadt  *
11df930be7Sderaadt  * Redistribution and use in source and binary forms, with or without
12df930be7Sderaadt  * modification, are permitted provided that the following conditions
13df930be7Sderaadt  * are met:
14df930be7Sderaadt  * 1. Redistributions of source code must retain the above copyright
15df930be7Sderaadt  *    notice, this list of conditions and the following disclaimer.
16df930be7Sderaadt  * 2. Redistributions in binary form must reproduce the above copyright
17df930be7Sderaadt  *    notice, this list of conditions and the following disclaimer in the
18df930be7Sderaadt  *    documentation and/or other materials provided with the distribution.
1929295d1cSmillert  * 3. Neither the name of the University nor the names of its contributors
20df930be7Sderaadt  *    may be used to endorse or promote products derived from this software
21df930be7Sderaadt  *    without specific prior written permission.
22df930be7Sderaadt  *
23df930be7Sderaadt  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
24df930be7Sderaadt  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
25df930be7Sderaadt  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
26df930be7Sderaadt  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
27df930be7Sderaadt  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
28df930be7Sderaadt  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
29df930be7Sderaadt  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
30df930be7Sderaadt  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
31df930be7Sderaadt  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
32df930be7Sderaadt  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
33df930be7Sderaadt  * SUCH DAMAGE.
34df930be7Sderaadt  */
35df930be7Sderaadt 
36df930be7Sderaadt /*
37bc878e5dSjasper  * arp - display, set, delete arp table entries and wake up hosts.
38df930be7Sderaadt  */
39df930be7Sderaadt 
40df930be7Sderaadt #include <sys/socket.h>
41df930be7Sderaadt #include <sys/sysctl.h>
42bc878e5dSjasper #include <sys/ioctl.h>
43bc878e5dSjasper #include <net/bpf.h>
44df930be7Sderaadt #include <net/if.h>
45df930be7Sderaadt #include <net/if_dl.h>
46df930be7Sderaadt #include <net/if_types.h>
47df930be7Sderaadt #include <net/route.h>
48df930be7Sderaadt #include <netinet/in.h>
49df930be7Sderaadt #include <netinet/if_ether.h>
50df930be7Sderaadt #include <arpa/inet.h>
51df930be7Sderaadt 
52df930be7Sderaadt #include <netdb.h>
53df930be7Sderaadt #include <errno.h>
54df930be7Sderaadt #include <err.h>
55f4147939Sguenther #include <fcntl.h>
56df930be7Sderaadt #include <stdio.h>
57df930be7Sderaadt #include <stdlib.h>
58df930be7Sderaadt #include <string.h>
591dfeccb7Scheloha #include <time.h>
6070cb125bSderaadt #include <unistd.h>
61b9fc9a72Sderaadt #include <limits.h>
62bc878e5dSjasper #include <ifaddrs.h>
63df930be7Sderaadt 
64e1ee6c49Smpi void dump(void);
65db690f90Smpi int delete(const char *);
6622bb042bSdugsong void search(in_addr_t addr, void (*action)(struct sockaddr_dl *sdl,
6722bb042bSdugsong 	struct sockaddr_inarp *sin, struct rt_msghdr *rtm));
6822bb042bSdugsong void print_entry(struct sockaddr_dl *sdl,
6922bb042bSdugsong 	struct sockaddr_inarp *sin, struct rt_msghdr *rtm);
7022bb042bSdugsong void nuke_entry(struct sockaddr_dl *sdl,
7122bb042bSdugsong 	struct sockaddr_inarp *sin, struct rt_msghdr *rtm);
72e1ee6c49Smpi static char *ether_str(struct sockaddr_dl *);
73bc878e5dSjasper int wake(const char *ether_addr, const char *iface);
74c72b5b24Smillert int file(char *);
75c72b5b24Smillert int get(const char *);
76c72b5b24Smillert void getsocket(void);
77bf1f2a6aSkn int parse_host(const char *, struct in_addr *);
78f3b11628Smpi int rtget(struct sockaddr_inarp **, struct sockaddr_dl **);
79c72b5b24Smillert int rtmsg(int);
80c72b5b24Smillert int set(int, char **);
81c72b5b24Smillert void usage(void);
82e1ee6c49Smpi static char *sec2str(time_t);
83df930be7Sderaadt 
8413ee8a54Sderaadt static pid_t pid;
85f4619fa8Shenning static int replace;	/* replace entries when adding */
8622bb042bSdugsong static int nflag;	/* no reverse dns lookups */
8722bb042bSdugsong static int aflag;	/* do it for all entries */
882f9be637Sbluhm static int rtsock = -1;
89bf44d530Sclaudio static int rdomain;
90df930be7Sderaadt 
91a57ce108Shenric /* ROUNDUP() is nasty, but it is identical to what's in the kernel. */
92a57ce108Shenric #define ROUNDUP(a) \
93a57ce108Shenric 	((a) > 0 ? (1 + (((a) - 1) | (sizeof(long) - 1))) : sizeof(long))
9413c75337Sbluhm #define ADVANCE(x, n) (x += ROUNDUP((n)->sa_len))
95a57ce108Shenric 
96df930be7Sderaadt int
main(int argc,char * argv[])974b6e8b27Sderaadt main(int argc, char *argv[])
98df930be7Sderaadt {
99cbf4cf1bSmpi 	int		 ch, func = 0, error = 0;
100cc0444d0Sclaudio 	const char	*errstr;
101df930be7Sderaadt 
102df930be7Sderaadt 	pid = getpid();
1036f71af19Smillert 	opterr = 0;
104ae71d88dSphessler 	rdomain = getrtable();
105bc878e5dSjasper 	while ((ch = getopt(argc, argv, "andsFfV:W")) != -1) {
1065a27df66Shenning 		switch (ch) {
107df930be7Sderaadt 		case 'a':
10822bb042bSdugsong 			aflag = 1;
10922bb042bSdugsong 			break;
110df930be7Sderaadt 		case 'n':
11122bb042bSdugsong 			nflag = 1;
11222bb042bSdugsong 			break;
11322bb042bSdugsong 		case 'd':
114df930be7Sderaadt 		case 's':
1156c9a643dSkn 		case 'f':
1166c9a643dSkn 		case 'W':
11722bb042bSdugsong 			if (func)
118df930be7Sderaadt 				usage();
1196c9a643dSkn 			func = ch;
12022bb042bSdugsong 			break;
121f4619fa8Shenning 		case 'F':
122f4619fa8Shenning 			replace = 1;
123f4619fa8Shenning 			break;
124cc0444d0Sclaudio 		case 'V':
125cc0444d0Sclaudio 			rdomain = strtonum(optarg, 0, RT_TABLEID_MAX, &errstr);
126cc0444d0Sclaudio 			if (errstr != NULL) {
127cc0444d0Sclaudio 				warn("bad rdomain: %s", errstr);
128cc0444d0Sclaudio 				usage();
129cc0444d0Sclaudio 			}
130cc0444d0Sclaudio 			break;
131df930be7Sderaadt 		default:
132df930be7Sderaadt 			usage();
13322bb042bSdugsong 			break;
134df930be7Sderaadt 		}
13522bb042bSdugsong 	}
13622bb042bSdugsong 	argc -= optind;
13722bb042bSdugsong 	argv += optind;
13822bb042bSdugsong 
13922bb042bSdugsong 	switch (func) {
1406c9a643dSkn 	case 0:
14122bb042bSdugsong 		if (aflag && argc == 0)
142e1ee6c49Smpi 			dump();
14322bb042bSdugsong 		else if (!aflag && argc == 1)
144cbf4cf1bSmpi 			error = get(argv[0]);
145df930be7Sderaadt 		else
146df930be7Sderaadt 			usage();
14722bb042bSdugsong 		break;
1486c9a643dSkn 	case 's':
14922bb042bSdugsong 		if (argc < 2 || argc > 5)
15022bb042bSdugsong 			usage();
151f4619fa8Shenning 		if (replace)
152db690f90Smpi 			delete(argv[0]);
153cbf4cf1bSmpi 		error = set(argc, argv) ? 1 : 0;
15422bb042bSdugsong 		break;
1556c9a643dSkn 	case 'd':
15622bb042bSdugsong 		if (aflag && argc == 0)
15722bb042bSdugsong 			search(0, nuke_entry);
15822bb042bSdugsong 		else if (!aflag && argc == 1)
159db690f90Smpi 			error = delete(argv[0]);
16022bb042bSdugsong 		else
16122bb042bSdugsong 			usage();
16222bb042bSdugsong 		break;
1636c9a643dSkn 	case 'f':
16422bb042bSdugsong 		if (argc != 1)
16522bb042bSdugsong 			usage();
166cbf4cf1bSmpi 		error = file(argv[0]);
16722bb042bSdugsong 		break;
1686c9a643dSkn 	case 'W':
169bc878e5dSjasper 		if (aflag || nflag || replace || rdomain > 0)
170bc878e5dSjasper 			usage();
171bc878e5dSjasper 		if (argc == 1)
172cbf4cf1bSmpi 			error = wake(argv[0], NULL);
173bc878e5dSjasper 		else if (argc == 2)
174cbf4cf1bSmpi 			error = wake(argv[0], argv[1]);
175bc878e5dSjasper 		else
176bc878e5dSjasper 			usage();
177bc878e5dSjasper 		break;
17822bb042bSdugsong 	}
179cbf4cf1bSmpi 	return (error);
180df930be7Sderaadt }
181df930be7Sderaadt 
182df930be7Sderaadt /*
183df930be7Sderaadt  * Process a file to set standard arp entries
184df930be7Sderaadt  */
185df930be7Sderaadt int
file(char * name)1864b6e8b27Sderaadt file(char *name)
187df930be7Sderaadt {
188df930be7Sderaadt 	char	 line[100], arg[5][50], *args[5];
189df930be7Sderaadt 	int	 i, retval;
190df930be7Sderaadt 	FILE	*fp;
191df930be7Sderaadt 
192df930be7Sderaadt 	if ((fp = fopen(name, "r")) == NULL)
193df930be7Sderaadt 		err(1, "cannot open %s", name);
194df930be7Sderaadt 	args[0] = &arg[0][0];
195df930be7Sderaadt 	args[1] = &arg[1][0];
196df930be7Sderaadt 	args[2] = &arg[2][0];
197df930be7Sderaadt 	args[3] = &arg[3][0];
198df930be7Sderaadt 	args[4] = &arg[4][0];
199df930be7Sderaadt 	retval = 0;
200b3c11e64Sray 	while (fgets(line, sizeof(line), fp) != NULL) {
2015a27df66Shenning 		i = sscanf(line, "%49s %49s %49s %49s %49s", arg[0], arg[1],
2025a27df66Shenning 		    arg[2], arg[3], arg[4]);
203df930be7Sderaadt 		if (i < 2) {
204df930be7Sderaadt 			warnx("bad line: %s", line);
205df930be7Sderaadt 			retval = 1;
206df930be7Sderaadt 			continue;
207df930be7Sderaadt 		}
208f4619fa8Shenning 		if (replace)
209db690f90Smpi 			delete(arg[0]);
210df930be7Sderaadt 		if (set(i, args))
211df930be7Sderaadt 			retval = 1;
212df930be7Sderaadt 	}
213df930be7Sderaadt 	fclose(fp);
214df930be7Sderaadt 	return (retval);
215df930be7Sderaadt }
216df930be7Sderaadt 
217df930be7Sderaadt void
getsocket(void)2184b6e8b27Sderaadt getsocket(void)
219df930be7Sderaadt {
220140f2ab4Smikeb 	socklen_t len = sizeof(rdomain);
221140f2ab4Smikeb 
2222f9be637Sbluhm 	if (rtsock >= 0)
223df930be7Sderaadt 		return;
224b47fcd70Skrw 	rtsock = socket(AF_ROUTE, SOCK_RAW, 0);
225df69c215Sderaadt 	if (rtsock == -1)
2262f9be637Sbluhm 		err(1, "routing socket");
227df69c215Sderaadt 	if (setsockopt(rtsock, AF_ROUTE, ROUTE_TABLEFILTER, &rdomain, len) == -1)
228140f2ab4Smikeb 		err(1, "ROUTE_TABLEFILTER");
229e5440999Sderaadt 
2300bd1216cSderaadt 	if (pledge("stdio dns", NULL) == -1)
2310bd1216cSderaadt 		err(1, "pledge");
232df930be7Sderaadt }
233df930be7Sderaadt 
234bf1f2a6aSkn int
parse_host(const char * host,struct in_addr * in)235bf1f2a6aSkn parse_host(const char *host, struct in_addr *in)
236bf1f2a6aSkn {
237bf1f2a6aSkn 	struct addrinfo hints, *res;
238bf1f2a6aSkn 	struct sockaddr_in *sin;
239bf1f2a6aSkn 	int gai_error;
240bf1f2a6aSkn 
241bf1f2a6aSkn 	bzero(&hints, sizeof(hints));
242bf1f2a6aSkn 	hints.ai_family = AF_INET;
243bf1f2a6aSkn 	if (nflag)
244bf1f2a6aSkn 		hints.ai_flags = AI_NUMERICHOST;
245bf1f2a6aSkn 
246bf1f2a6aSkn 	gai_error = getaddrinfo(host, NULL, &hints, &res);
247bf1f2a6aSkn 	if (gai_error) {
248bf1f2a6aSkn 		warnx("%s: %s", host, gai_strerror(gai_error));
249bf1f2a6aSkn 		return 1;
250bf1f2a6aSkn 	}
251bf1f2a6aSkn 
252bf1f2a6aSkn 	sin = (struct sockaddr_in *)res->ai_addr;
253bf1f2a6aSkn 	*in = sin->sin_addr;
254bf1f2a6aSkn 
255bf1f2a6aSkn 	freeaddrinfo(res);
256bf1f2a6aSkn 	return 0;
257bf1f2a6aSkn }
258bf1f2a6aSkn 
259df930be7Sderaadt struct sockaddr_in	so_mask = { 8, 0, 0, { 0xffffffff } };
260df930be7Sderaadt struct sockaddr_inarp	blank_sin = { sizeof(blank_sin), AF_INET }, sin_m;
261df930be7Sderaadt struct sockaddr_dl	blank_sdl = { sizeof(blank_sdl), AF_LINK }, sdl_m;
26213c75337Sbluhm struct sockaddr_dl	ifp_m = { sizeof(ifp_m), AF_LINK };
263a80d0aebSbluhm time_t			expire_time;
264a80d0aebSbluhm int			flags, export_only, doing_proxy, found_entry;
265df930be7Sderaadt struct	{
266df930be7Sderaadt 	struct rt_msghdr	m_rtm;
267df930be7Sderaadt 	char			m_space[512];
268df930be7Sderaadt }	m_rtmsg;
269df930be7Sderaadt 
270df930be7Sderaadt /*
271df930be7Sderaadt  * Set an individual arp entry
272df930be7Sderaadt  */
273df930be7Sderaadt int
set(int argc,char * argv[])2744b6e8b27Sderaadt set(int argc, char *argv[])
275df930be7Sderaadt {
2760ac0d02eSmpech 	struct sockaddr_inarp *sin;
2770ac0d02eSmpech 	struct sockaddr_dl *sdl;
2780ac0d02eSmpech 	struct rt_msghdr *rtm;
279bf1f2a6aSkn 	const char *host = argv[0], *eaddr = argv[1];
280b4af9cc4Sderaadt 	struct ether_addr *ea;
281df930be7Sderaadt 
282df930be7Sderaadt 	sin = &sin_m;
283df930be7Sderaadt 	rtm = &(m_rtmsg.m_rtm);
284df930be7Sderaadt 
285e5440999Sderaadt 	getsocket();
286df930be7Sderaadt 	argc -= 2;
287df930be7Sderaadt 	argv += 2;
288df930be7Sderaadt 	sdl_m = blank_sdl;		/* struct copy */
289df930be7Sderaadt 	sin_m = blank_sin;		/* struct copy */
290bf1f2a6aSkn 	if (parse_host(host, &sin->sin_addr))
291df930be7Sderaadt 		return (1);
292b4af9cc4Sderaadt 	ea = ether_aton(eaddr);
293c1265abaSart 	if (ea == NULL)
29446347781Smpech 		errx(1, "invalid ethernet address: %s", eaddr);
295b4af9cc4Sderaadt 	memcpy(LLADDR(&sdl_m), ea, sizeof(*ea));
296df930be7Sderaadt 	sdl_m.sdl_alen = 6;
297a80d0aebSbluhm 	expire_time = 0;
298a80d0aebSbluhm 	doing_proxy = flags = export_only = 0;
299df930be7Sderaadt 	while (argc-- > 0) {
300df930be7Sderaadt 		if (strncmp(argv[0], "temp", 4) == 0) {
3011dfeccb7Scheloha 			expire_time = time(NULL) + 20 * 60;
302f0c423cdSho 			if (flags & RTF_PERMANENT_ARP) {
303f0c423cdSho 				/* temp or permanent, not both */
304f0c423cdSho 				usage();
305f0c423cdSho 				return (0);
306f0c423cdSho 			}
307ad4c70b3Sderaadt 		} else if (strncmp(argv[0], "pub", 3) == 0) {
308df930be7Sderaadt 			flags |= RTF_ANNOUNCE;
309df930be7Sderaadt 			doing_proxy = SIN_PROXY;
310ad4c70b3Sderaadt 		} else if (strncmp(argv[0], "permanent", 9) == 0) {
311f0c423cdSho 			flags |= RTF_PERMANENT_ARP;
312f0c423cdSho 			if (expire_time != 0) {
313f0c423cdSho 				/* temp or permanent, not both */
314f0c423cdSho 				usage();
315f0c423cdSho 				return (0);
316f0c423cdSho 			}
317a03c2825Stedu 		}
3185a27df66Shenning 
319df930be7Sderaadt 		argv++;
320df930be7Sderaadt 	}
3215a27df66Shenning 
322df930be7Sderaadt tryagain:
323f3b11628Smpi 	if (rtget(&sin, &sdl)) {
324df930be7Sderaadt 		warn("%s", host);
325df930be7Sderaadt 		return (1);
326df930be7Sderaadt 	}
327f3b11628Smpi 
328df930be7Sderaadt 	if (sin->sin_addr.s_addr == sin_m.sin_addr.s_addr) {
329df930be7Sderaadt 		if (sdl->sdl_family == AF_LINK &&
330df930be7Sderaadt 		    (rtm->rtm_flags & RTF_LLINFO) &&
3315a27df66Shenning 		    !(rtm->rtm_flags & RTF_GATEWAY))
3320f3328b9Sderaadt 			switch (sdl->sdl_type) {
3335a27df66Shenning 			case IFT_ETHER:
3345a27df66Shenning 			case IFT_FDDI:
3355a27df66Shenning 			case IFT_ISO88023:
3365a27df66Shenning 			case IFT_ISO88024:
3375a27df66Shenning 			case IFT_ISO88025:
3385a27df66Shenning 			case IFT_CARP:
339df930be7Sderaadt 				goto overwrite;
340df930be7Sderaadt 			}
3415a27df66Shenning 
342df930be7Sderaadt 		if (doing_proxy == 0) {
3435a27df66Shenning 			printf("set: can only proxy for %s\n", host);
344df930be7Sderaadt 			return (1);
345df930be7Sderaadt 		}
346df930be7Sderaadt 		if (sin_m.sin_other & SIN_PROXY) {
3475a27df66Shenning 			printf("set: proxy entry exists for non 802 device\n");
348df930be7Sderaadt 			return (1);
349df930be7Sderaadt 		}
350df930be7Sderaadt 		sin_m.sin_other = SIN_PROXY;
351df930be7Sderaadt 		export_only = 1;
352df930be7Sderaadt 		goto tryagain;
353df930be7Sderaadt 	}
3545a27df66Shenning 
355df930be7Sderaadt overwrite:
356df930be7Sderaadt 	if (sdl->sdl_family != AF_LINK) {
3575a27df66Shenning 		printf("cannot intuit interface index and type for %s\n", host);
358df930be7Sderaadt 		return (1);
359df930be7Sderaadt 	}
360df930be7Sderaadt 	sdl_m.sdl_type = sdl->sdl_type;
361df930be7Sderaadt 	sdl_m.sdl_index = sdl->sdl_index;
362df930be7Sderaadt 	return (rtmsg(RTM_ADD));
363df930be7Sderaadt }
364df930be7Sderaadt 
365e1ee6c49Smpi #define W_ADDR	36
366e1ee6c49Smpi #define W_LL	17
3679dd4e201Smpi #define W_IF	7
368e1ee6c49Smpi 
369df930be7Sderaadt /*
370df930be7Sderaadt  * Display an individual arp entry
371df930be7Sderaadt  */
37222bb042bSdugsong int
get(const char * host)3734b6e8b27Sderaadt get(const char *host)
374df930be7Sderaadt {
375df930be7Sderaadt 	struct sockaddr_inarp *sin;
376df930be7Sderaadt 
377df930be7Sderaadt 	sin = &sin_m;
378df930be7Sderaadt 	sin_m = blank_sin;		/* struct copy */
379bf1f2a6aSkn 	if (parse_host(host, &sin->sin_addr))
380bf1f2a6aSkn 		return (1);
381e1ee6c49Smpi 
3829dd4e201Smpi 	printf("%-*.*s %-*.*s %*.*s %-9.9s %5s\n",
383e1ee6c49Smpi 	    W_ADDR, W_ADDR, "Host", W_LL, W_LL, "Ethernet Address",
384e1ee6c49Smpi 	    W_IF, W_IF, "Netif", "Expire", "Flags");
385e1ee6c49Smpi 
38622bb042bSdugsong 	search(sin->sin_addr.s_addr, print_entry);
387df930be7Sderaadt 	if (found_entry == 0) {
388e1ee6c49Smpi 		printf("%-*.*s no entry\n", W_ADDR, W_ADDR,
389e1ee6c49Smpi 		    inet_ntoa(sin->sin_addr));
39022bb042bSdugsong 		return (1);
391df930be7Sderaadt 	}
39222bb042bSdugsong 	return (0);
393df930be7Sderaadt }
394df930be7Sderaadt 
395df930be7Sderaadt /*
396df930be7Sderaadt  * Delete an arp entry
397df930be7Sderaadt  */
398df930be7Sderaadt int
delete(const char * host)399db690f90Smpi delete(const char *host)
400df930be7Sderaadt {
4010ac0d02eSmpech 	struct sockaddr_inarp *sin;
4020ac0d02eSmpech 	struct rt_msghdr *rtm;
403df930be7Sderaadt 	struct sockaddr_dl *sdl;
404df930be7Sderaadt 
405df930be7Sderaadt 	sin = &sin_m;
406df930be7Sderaadt 	rtm = &m_rtmsg.m_rtm;
407df930be7Sderaadt 
408e5440999Sderaadt 	getsocket();
409df930be7Sderaadt 	sin_m = blank_sin;		/* struct copy */
410bf1f2a6aSkn 	if (parse_host(host, &sin->sin_addr))
411*fba40a11Sbluhm 		return 1;
412df930be7Sderaadt tryagain:
413bb2b993aSsemarie 	if (rtget(&sin, &sdl)) {
414df930be7Sderaadt 		warn("%s", host);
415*fba40a11Sbluhm 		return 1;
416df930be7Sderaadt 	}
41738c8fc57Smpi 	if (sin->sin_addr.s_addr == sin_m.sin_addr.s_addr) {
41838c8fc57Smpi 		if (sdl->sdl_family == AF_LINK && rtm->rtm_flags & RTF_LLINFO) {
419f3b11628Smpi 			if (rtm->rtm_flags & RTF_LOCAL)
420*fba40a11Sbluhm 				return 0;
421*fba40a11Sbluhm 			if ((rtm->rtm_flags & RTF_GATEWAY) == 0)
4220f3328b9Sderaadt 				switch (sdl->sdl_type) {
4235a27df66Shenning 				case IFT_ETHER:
4245a27df66Shenning 				case IFT_FDDI:
4255a27df66Shenning 				case IFT_ISO88023:
4265a27df66Shenning 				case IFT_ISO88024:
4275a27df66Shenning 				case IFT_ISO88025:
4285a27df66Shenning 				case IFT_CARP:
429df930be7Sderaadt 					goto delete;
430df930be7Sderaadt 				}
43138c8fc57Smpi 		}
43238c8fc57Smpi 	}
4335a27df66Shenning 
434df930be7Sderaadt 	if (sin_m.sin_other & SIN_PROXY) {
435*fba40a11Sbluhm 		warnx("delete: cannot locate %s", host);
436*fba40a11Sbluhm 		return 1;
437df930be7Sderaadt 	} else {
438df930be7Sderaadt 		sin_m.sin_other = SIN_PROXY;
439df930be7Sderaadt 		goto tryagain;
440df930be7Sderaadt 	}
441df930be7Sderaadt delete:
442df930be7Sderaadt 	if (sdl->sdl_family != AF_LINK) {
4435a27df66Shenning 		printf("cannot locate %s\n", host);
444*fba40a11Sbluhm 		return 1;
445df930be7Sderaadt 	}
446df930be7Sderaadt 	if (rtmsg(RTM_DELETE))
447*fba40a11Sbluhm 		return 1;
4485a27df66Shenning 	printf("%s (%s) deleted\n", host, inet_ntoa(sin->sin_addr));
449*fba40a11Sbluhm 	return 0;
450df930be7Sderaadt }
451df930be7Sderaadt 
452df930be7Sderaadt /*
45322bb042bSdugsong  * Search the entire arp table, and do some action on matching entries.
454df930be7Sderaadt  */
455df930be7Sderaadt void
search(in_addr_t addr,void (* action)(struct sockaddr_dl * sdl,struct sockaddr_inarp * sin,struct rt_msghdr * rtm))4564b6e8b27Sderaadt search(in_addr_t addr, void (*action)(struct sockaddr_dl *sdl,
4574b6e8b27Sderaadt     struct sockaddr_inarp *sin, struct rt_msghdr *rtm))
458df930be7Sderaadt {
459cc0444d0Sclaudio 	int mib[7];
460df930be7Sderaadt 	size_t needed;
4618ae2d342Sderaadt 	char *lim, *buf = NULL, *next;
462df930be7Sderaadt 	struct rt_msghdr *rtm;
463df930be7Sderaadt 	struct sockaddr_inarp *sin;
464df930be7Sderaadt 	struct sockaddr_dl *sdl;
465df930be7Sderaadt 
466df930be7Sderaadt 	mib[0] = CTL_NET;
467df930be7Sderaadt 	mib[1] = PF_ROUTE;
468df930be7Sderaadt 	mib[2] = 0;
469df930be7Sderaadt 	mib[3] = AF_INET;
470df930be7Sderaadt 	mib[4] = NET_RT_FLAGS;
471df930be7Sderaadt 	mib[5] = RTF_LLINFO;
472cc0444d0Sclaudio 	mib[6] = rdomain;
4738ae2d342Sderaadt 	while (1) {
4748ae2d342Sderaadt 		if (sysctl(mib, 7, NULL, &needed, NULL, 0) == -1)
475df930be7Sderaadt 			err(1, "route-sysctl-estimate");
476afa03bb6Sderaadt 		if (needed == 0)
477afa03bb6Sderaadt 			return;
4788ae2d342Sderaadt 		if ((buf = realloc(buf, needed)) == NULL)
479df930be7Sderaadt 			err(1, "malloc");
4808ae2d342Sderaadt 		if (sysctl(mib, 7, buf, &needed, NULL, 0) == -1) {
4818ae2d342Sderaadt 			if (errno == ENOMEM)
4828ae2d342Sderaadt 				continue;
483df930be7Sderaadt 			err(1, "actual retrieval of routing table");
4848ae2d342Sderaadt 		}
485df930be7Sderaadt 		lim = buf + needed;
4868ae2d342Sderaadt 		break;
4878ae2d342Sderaadt 	}
488df930be7Sderaadt 	for (next = buf; next < lim; next += rtm->rtm_msglen) {
489df930be7Sderaadt 		rtm = (struct rt_msghdr *)next;
49062ae07b0Sclaudio 		if (rtm->rtm_version != RTM_VERSION)
49162ae07b0Sclaudio 			continue;
49292dd616fSclaudio 		sin = (struct sockaddr_inarp *)(next + rtm->rtm_hdrlen);
493df930be7Sderaadt 		sdl = (struct sockaddr_dl *)(sin + 1);
494df930be7Sderaadt 		if (addr) {
495df930be7Sderaadt 			if (addr != sin->sin_addr.s_addr)
496df930be7Sderaadt 				continue;
497df930be7Sderaadt 			found_entry = 1;
498df930be7Sderaadt 		}
49922bb042bSdugsong 		(*action)(sdl, sin, rtm);
50022bb042bSdugsong 	}
50103eb93c6Sderaadt 	free(buf);
50222bb042bSdugsong }
50322bb042bSdugsong 
50422bb042bSdugsong /*
505e1ee6c49Smpi  * Dump the entire ARP table
506e1ee6c49Smpi  */
507e1ee6c49Smpi void
dump(void)508e1ee6c49Smpi dump(void)
509e1ee6c49Smpi {
5109dd4e201Smpi 	printf("%-*.*s %-*.*s %*.*s %-9.9s %5s\n",
511e1ee6c49Smpi 	    W_ADDR, W_ADDR, "Host", W_LL, W_LL, "Ethernet Address",
512e1ee6c49Smpi 	    W_IF, W_IF, "Netif", "Expire", "Flags");
513e1ee6c49Smpi 
514e1ee6c49Smpi 	search(0, print_entry);
515e1ee6c49Smpi }
516e1ee6c49Smpi 
517e1ee6c49Smpi /*
51822bb042bSdugsong  * Display an arp entry
51922bb042bSdugsong  */
52022bb042bSdugsong void
print_entry(struct sockaddr_dl * sdl,struct sockaddr_inarp * sin,struct rt_msghdr * rtm)5214b6e8b27Sderaadt print_entry(struct sockaddr_dl *sdl, struct sockaddr_inarp *sin,
5224b6e8b27Sderaadt     struct rt_msghdr *rtm)
52322bb042bSdugsong {
524e1ee6c49Smpi 	char ifix_buf[IFNAMSIZ], *ifname, *host;
525e1ee6c49Smpi 	struct hostent *hp = NULL;
526e1ee6c49Smpi 	int addrwidth, llwidth, ifwidth ;
5271dfeccb7Scheloha 	time_t now;
528e1ee6c49Smpi 
5291dfeccb7Scheloha 	now = time(NULL);
53022bb042bSdugsong 
531df930be7Sderaadt 	if (nflag == 0)
532df930be7Sderaadt 		hp = gethostbyaddr((caddr_t)&(sin->sin_addr),
53322bb042bSdugsong 		    sizeof(sin->sin_addr), AF_INET);
534df930be7Sderaadt 	if (hp)
535df930be7Sderaadt 		host = hp->h_name;
536df930be7Sderaadt 	else
537e1ee6c49Smpi 		host = inet_ntoa(sin->sin_addr);
538e1ee6c49Smpi 
539e1ee6c49Smpi 	addrwidth = strlen(host);
540e1ee6c49Smpi 	if (addrwidth < W_ADDR)
541e1ee6c49Smpi 		addrwidth = W_ADDR;
542e1ee6c49Smpi 	llwidth = strlen(ether_str(sdl));
543e1ee6c49Smpi 	if (W_ADDR + W_LL - addrwidth > llwidth)
544e1ee6c49Smpi 		llwidth = W_ADDR + W_LL - addrwidth;
545e1ee6c49Smpi 	ifname = if_indextoname(sdl->sdl_index, ifix_buf);
546e1ee6c49Smpi 	if (!ifname)
547e1ee6c49Smpi 		ifname = "?";
548e1ee6c49Smpi 	ifwidth = strlen(ifname);
549e1ee6c49Smpi 	if (W_ADDR + W_LL + W_IF - addrwidth - llwidth > ifwidth)
550e1ee6c49Smpi 		ifwidth = W_ADDR + W_LL + W_IF - addrwidth - llwidth;
551e1ee6c49Smpi 
552e1ee6c49Smpi 	printf("%-*.*s %-*.*s %*.*s", addrwidth, addrwidth, host,
553e1ee6c49Smpi 	    llwidth, llwidth, ether_str(sdl), ifwidth, ifwidth, ifname);
554e1ee6c49Smpi 
555f3b11628Smpi 	if (rtm->rtm_flags & (RTF_PERMANENT_ARP|RTF_LOCAL))
5569dd4e201Smpi 		printf(" %-9.9s", "permanent");
557e1ee6c49Smpi 	else if (rtm->rtm_rmx.rmx_expire == 0)
5589dd4e201Smpi 		printf(" %-9.9s", "static");
5591dfeccb7Scheloha 	else if (rtm->rtm_rmx.rmx_expire > now)
5609dd4e201Smpi 		printf(" %-9.9s",
5611dfeccb7Scheloha 		    sec2str(rtm->rtm_rmx.rmx_expire - now));
562e1ee6c49Smpi 	else
5639dd4e201Smpi 		printf(" %-9.9s", "expired");
564e1ee6c49Smpi 
565cbf4cf1bSmpi 	printf(" %s%s\n",
56648ccdf10Smpi 	    (rtm->rtm_flags & RTF_LOCAL) ? "l" : "",
567e1ee6c49Smpi 	    (rtm->rtm_flags & RTF_ANNOUNCE) ? "p" : "");
568df930be7Sderaadt }
56922bb042bSdugsong 
57022bb042bSdugsong /*
57122bb042bSdugsong  * Nuke an arp entry
57222bb042bSdugsong  */
57322bb042bSdugsong void
nuke_entry(struct sockaddr_dl * sdl,struct sockaddr_inarp * sin,struct rt_msghdr * rtm)5744b6e8b27Sderaadt nuke_entry(struct sockaddr_dl *sdl, struct sockaddr_inarp *sin,
5754b6e8b27Sderaadt     struct rt_msghdr *rtm)
57622bb042bSdugsong {
57722bb042bSdugsong 	char ip[20];
57822bb042bSdugsong 
57922bb042bSdugsong 	strlcpy(ip, inet_ntoa(sin->sin_addr), sizeof(ip));
580db690f90Smpi 	delete(ip);
581df930be7Sderaadt }
582df930be7Sderaadt 
583e1ee6c49Smpi static char *
ether_str(struct sockaddr_dl * sdl)584e1ee6c49Smpi ether_str(struct sockaddr_dl *sdl)
585df930be7Sderaadt {
586e1ee6c49Smpi 	static char hbuf[NI_MAXHOST];
587e1ee6c49Smpi 	u_char *cp;
58883b01f47Sderaadt 
589e1ee6c49Smpi 	if (sdl->sdl_alen) {
590e1ee6c49Smpi 		cp = (u_char *)LLADDR(sdl);
591e8f5ae95Ssthen 		snprintf(hbuf, sizeof(hbuf), "%02x:%02x:%02x:%02x:%02x:%02x",
592743fc5eeSderaadt 		    cp[0], cp[1], cp[2], cp[3], cp[4], cp[5]);
593e1ee6c49Smpi 	} else
594e1ee6c49Smpi 		snprintf(hbuf, sizeof(hbuf), "(incomplete)");
595e1ee6c49Smpi 
596e1ee6c49Smpi 	return(hbuf);
597df930be7Sderaadt }
598df930be7Sderaadt 
599df930be7Sderaadt void
usage(void)6004b6e8b27Sderaadt usage(void)
601df930be7Sderaadt {
602cc0444d0Sclaudio 	fprintf(stderr, "usage: arp [-adn] [-V rdomain] hostname\n");
603cc0444d0Sclaudio 	fprintf(stderr, "       arp [-F] [-f file] [-V rdomain] "
604d2349d47Sderaadt 	    "-s hostname ether_addr\n"
6055a27df66Shenning 	    "           [temp | permanent] [pub]\n");
606bc878e5dSjasper 	fprintf(stderr, "       arp -W ether_addr [iface]\n");
607df930be7Sderaadt 	exit(1);
608df930be7Sderaadt }
609df930be7Sderaadt 
610df930be7Sderaadt int
rtmsg(int cmd)6114b6e8b27Sderaadt rtmsg(int cmd)
612df930be7Sderaadt {
613df930be7Sderaadt 	static int seq;
6140ac0d02eSmpech 	struct rt_msghdr *rtm;
6150ac0d02eSmpech 	char *cp;
6160ac0d02eSmpech 	int l;
617df930be7Sderaadt 
618df930be7Sderaadt 	rtm = &m_rtmsg.m_rtm;
619df930be7Sderaadt 	cp = m_rtmsg.m_space;
620df930be7Sderaadt 	errno = 0;
621df930be7Sderaadt 
622df930be7Sderaadt 	if (cmd == RTM_DELETE)
623df930be7Sderaadt 		goto doit;
6245a27df66Shenning 	memset(&m_rtmsg, 0, sizeof(m_rtmsg));
625df930be7Sderaadt 	rtm->rtm_flags = flags;
626df930be7Sderaadt 	rtm->rtm_version = RTM_VERSION;
62792dd616fSclaudio 	rtm->rtm_hdrlen = sizeof(*rtm);
628cc0444d0Sclaudio 	rtm->rtm_tableid = rdomain;
629df930be7Sderaadt 
630df930be7Sderaadt 	switch (cmd) {
631df930be7Sderaadt 	default:
632df930be7Sderaadt 		errx(1, "internal wrong cmd");
633df930be7Sderaadt 	case RTM_ADD:
634df930be7Sderaadt 		rtm->rtm_addrs |= RTA_GATEWAY;
635df930be7Sderaadt 		rtm->rtm_rmx.rmx_expire = expire_time;
636df930be7Sderaadt 		rtm->rtm_inits = RTV_EXPIRE;
637df930be7Sderaadt 		rtm->rtm_flags |= (RTF_HOST | RTF_STATIC);
638df930be7Sderaadt 		sin_m.sin_other = 0;
639df930be7Sderaadt 		if (doing_proxy) {
640df930be7Sderaadt 			if (export_only)
641df930be7Sderaadt 				sin_m.sin_other = SIN_PROXY;
642df930be7Sderaadt 			else {
643df930be7Sderaadt 				rtm->rtm_addrs |= RTA_NETMASK;
644df930be7Sderaadt 				rtm->rtm_flags &= ~RTF_HOST;
645df930be7Sderaadt 			}
646df930be7Sderaadt 		}
647df930be7Sderaadt 		/* FALLTHROUGH */
648df930be7Sderaadt 	case RTM_GET:
649f3b11628Smpi 		rtm->rtm_addrs |= (RTA_DST | RTA_IFP);
650df930be7Sderaadt 	}
651a57ce108Shenric 
652df930be7Sderaadt #define NEXTADDR(w, s)							\
653df930be7Sderaadt 	if (rtm->rtm_addrs & (w)) {					\
654fd4782b8Sbluhm 		memcpy(cp, &(s), sizeof(s));				\
655fd4782b8Sbluhm 		ADVANCE(cp, (struct sockaddr *)&(s));			\
656a57ce108Shenric 	}
657df930be7Sderaadt 
658df930be7Sderaadt 	NEXTADDR(RTA_DST, sin_m);
659df930be7Sderaadt 	NEXTADDR(RTA_GATEWAY, sdl_m);
660df930be7Sderaadt 	NEXTADDR(RTA_NETMASK, so_mask);
661f3b11628Smpi 	NEXTADDR(RTA_IFP, ifp_m);
662df930be7Sderaadt 
663df930be7Sderaadt 	rtm->rtm_msglen = cp - (char *)&m_rtmsg;
664df930be7Sderaadt doit:
665df930be7Sderaadt 	l = rtm->rtm_msglen;
666df930be7Sderaadt 	rtm->rtm_seq = ++seq;
667df930be7Sderaadt 	rtm->rtm_type = cmd;
668df69c215Sderaadt 	if (write(rtsock, (char *)&m_rtmsg, l) == -1)
669df930be7Sderaadt 		if (errno != ESRCH || cmd != RTM_DELETE) {
670df930be7Sderaadt 			warn("writing to routing socket");
671df930be7Sderaadt 			return (-1);
672df930be7Sderaadt 		}
6735a27df66Shenning 
674df930be7Sderaadt 	do {
6752f9be637Sbluhm 		l = read(rtsock, (char *)&m_rtmsg, sizeof(m_rtmsg));
6766eca9d58Sclaudio 	} while (l > 0 && (rtm->rtm_version != RTM_VERSION ||
6776eca9d58Sclaudio 	    rtm->rtm_seq != seq || rtm->rtm_pid != pid));
6785a27df66Shenning 
679df930be7Sderaadt 	if (l < 0)
680df930be7Sderaadt 		warn("read from routing socket");
681df930be7Sderaadt 	return (0);
682df930be7Sderaadt }
683df930be7Sderaadt 
684df930be7Sderaadt int
rtget(struct sockaddr_inarp ** sinp,struct sockaddr_dl ** sdlp)685f3b11628Smpi rtget(struct sockaddr_inarp **sinp, struct sockaddr_dl **sdlp)
686f3b11628Smpi {
687f3b11628Smpi 	struct rt_msghdr *rtm = &(m_rtmsg.m_rtm);
688f3b11628Smpi 	struct sockaddr_inarp *sin = NULL;
689f3b11628Smpi 	struct sockaddr_dl *sdl = NULL;
690f3b11628Smpi 	struct sockaddr *sa;
691f3b11628Smpi 	char *cp;
692cac6055cSmmcc 	unsigned int i;
693f3b11628Smpi 
694f3b11628Smpi 	if (rtmsg(RTM_GET) < 0)
695f3b11628Smpi 		return (1);
696f3b11628Smpi 
697f3b11628Smpi 	if (rtm->rtm_addrs) {
698f3b11628Smpi 		cp = ((char *)rtm + rtm->rtm_hdrlen);
699f3b11628Smpi 		for (i = 1; i; i <<= 1) {
700f3b11628Smpi 			if (i & rtm->rtm_addrs) {
701f3b11628Smpi 				sa = (struct sockaddr *)cp;
702f3b11628Smpi 				switch (i) {
703f3b11628Smpi 				case RTA_DST:
704f3b11628Smpi 					sin = (struct sockaddr_inarp *)sa;
705f3b11628Smpi 					break;
706f3b11628Smpi 				case RTA_IFP:
707f3b11628Smpi 					sdl = (struct sockaddr_dl *)sa;
708f3b11628Smpi 					break;
709f3b11628Smpi 				default:
710f3b11628Smpi 					break;
711f3b11628Smpi 				}
71213c75337Sbluhm 				ADVANCE(cp, sa);
713f3b11628Smpi 			}
714f3b11628Smpi 		}
715f3b11628Smpi 	}
716f3b11628Smpi 
717f3b11628Smpi 	if (sin == NULL || sdl == NULL)
718f3b11628Smpi 		return (1);
719f3b11628Smpi 
720f3b11628Smpi 	*sinp = sin;
721f3b11628Smpi 	*sdlp = sdl;
722f3b11628Smpi 
723f3b11628Smpi 	return (0);
724f3b11628Smpi }
725f3b11628Smpi 
726e1ee6c49Smpi static char *
sec2str(time_t total)727e1ee6c49Smpi sec2str(time_t total)
728e1ee6c49Smpi {
729e1ee6c49Smpi 	static char result[256];
730e1ee6c49Smpi 	int days, hours, mins, secs;
731e1ee6c49Smpi 	int first = 1;
732e1ee6c49Smpi 	char *p = result;
733e1ee6c49Smpi 	char *ep = &result[sizeof(result)];
734e1ee6c49Smpi 	int n;
735e1ee6c49Smpi 
736e1ee6c49Smpi 	days = total / 3600 / 24;
737e1ee6c49Smpi 	hours = (total / 3600) % 24;
738e1ee6c49Smpi 	mins = (total / 60) % 60;
739e1ee6c49Smpi 	secs = total % 60;
740e1ee6c49Smpi 
741e1ee6c49Smpi 	if (days) {
742e1ee6c49Smpi 		first = 0;
743e1ee6c49Smpi 		n = snprintf(p, ep - p, "%dd", days);
744e1ee6c49Smpi 		if (n < 0 || n >= ep - p)
745e1ee6c49Smpi 			return "?";
746e1ee6c49Smpi 		p += n;
747e1ee6c49Smpi 	}
748e1ee6c49Smpi 	if (!first || hours) {
749e1ee6c49Smpi 		first = 0;
750e1ee6c49Smpi 		n = snprintf(p, ep - p, "%dh", hours);
751e1ee6c49Smpi 		if (n < 0 || n >= ep - p)
752e1ee6c49Smpi 			return "?";
753e1ee6c49Smpi 		p += n;
754e1ee6c49Smpi 	}
755e1ee6c49Smpi 	if (!first || mins) {
756e1ee6c49Smpi 		first = 0;
757e1ee6c49Smpi 		n = snprintf(p, ep - p, "%dm", mins);
758e1ee6c49Smpi 		if (n < 0 || n >= ep - p)
759e1ee6c49Smpi 			return "?";
760e1ee6c49Smpi 		p += n;
761e1ee6c49Smpi 	}
762e1ee6c49Smpi 	snprintf(p, ep - p, "%ds", secs);
763e1ee6c49Smpi 
764e1ee6c49Smpi 	return(result);
765e1ee6c49Smpi }
766e1ee6c49Smpi 
767bc878e5dSjasper /*
768bc878e5dSjasper  * Copyright (c) 2011 Jasper Lievisse Adriaanse <jasper@openbsd.org>
769bc878e5dSjasper  * Copyright (C) 2006,2007,2008,2009 Marc Balmer <mbalmer@openbsd.org>
770bc878e5dSjasper  * Copyright (C) 2000 Eugene M. Kim.  All rights reserved.
771bc878e5dSjasper  *
772bc878e5dSjasper  * Redistribution and use in source and binary forms, with or without
773bc878e5dSjasper  * modification, are permitted provided that the following conditions
774bc878e5dSjasper  * are met:
775bc878e5dSjasper  *
776bc878e5dSjasper  * 1. Redistributions of source code must retain the above copyright
777bc878e5dSjasper  *    notice, this list of conditions and the following disclaimer.
778bc878e5dSjasper  * 2. Author's name may not be used endorse or promote products derived
779bc878e5dSjasper  *    from this software without specific prior written permission.
780bc878e5dSjasper  *
781bc878e5dSjasper  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
782bc878e5dSjasper  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
783bc878e5dSjasper  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
784bc878e5dSjasper  * DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
785bc878e5dSjasper  * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
786bc878e5dSjasper  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
787bc878e5dSjasper  * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
788bc878e5dSjasper  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
789bc878e5dSjasper  * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
790bc878e5dSjasper  * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
791bc878e5dSjasper  * POSSIBILITY OF SUCH DAMAGE.
792bc878e5dSjasper  */
793bc878e5dSjasper 
794bc878e5dSjasper int	do_wakeup(const char *, const char *, int);
795bc878e5dSjasper int	bind_if_to_bpf(const char *, int);
796bc878e5dSjasper int	get_ether(const char *, struct ether_addr *);
797bc878e5dSjasper int	send_frame(int, const struct ether_addr *);
798bc878e5dSjasper 
799bc878e5dSjasper int
wake(const char * ether_addr,const char * iface)800bc878e5dSjasper wake(const char *ether_addr, const char *iface)
801bc878e5dSjasper {
802bc878e5dSjasper 	struct ifaddrs		*ifa, *ifap;
803bc878e5dSjasper 	char			*pname = NULL;
804bc878e5dSjasper 	int			 bpf;
805bc878e5dSjasper 
8067b08a90aSnatano 	if ((bpf = open("/dev/bpf", O_RDWR)) == -1)
8072abf9a0dSnatano 		err(1, "Failed to bind to bpf");
808bc878e5dSjasper 
809bc878e5dSjasper 	if (iface == NULL) {
810bc878e5dSjasper 		if (getifaddrs(&ifa) == -1)
811bc878e5dSjasper 			errx(1, "Could not get interface addresses.");
812bc878e5dSjasper 
813bc878e5dSjasper 		for (ifap = ifa; ifap != NULL; ifap = ifap->ifa_next){
814bc878e5dSjasper 			if (pname && !strcmp(pname, ifap->ifa_name))
815bc878e5dSjasper 				continue;
816bc878e5dSjasper 			pname = ifap->ifa_name;
817bc878e5dSjasper 
818bc878e5dSjasper 			/*
819bc878e5dSjasper 			 * We're only interested in sending the WoL frame on
820bc878e5dSjasper 			 * certain interfaces. So skip the loopback interface,
821bc878e5dSjasper 			 * as well as point-to-point and down interfaces.
822bc878e5dSjasper 			 */
823bc878e5dSjasper 			if ((ifap->ifa_flags & IFF_LOOPBACK) ||
824bc878e5dSjasper 			    (ifap->ifa_flags & IFF_POINTOPOINT) ||
825bc878e5dSjasper 			    (!(ifap->ifa_flags & IFF_UP)) ||
826bc878e5dSjasper 			    (!(ifap->ifa_flags & IFF_BROADCAST)))
827bc878e5dSjasper 				continue;
828bc878e5dSjasper 
829bc878e5dSjasper 			do_wakeup(ether_addr, ifap->ifa_name, bpf);
830bc878e5dSjasper 		}
831bc878e5dSjasper 		freeifaddrs(ifa);
832bc878e5dSjasper 	} else {
833bc878e5dSjasper 		do_wakeup(ether_addr, iface, bpf);
834bc878e5dSjasper 	}
835bc878e5dSjasper 
836bc878e5dSjasper 	(void)close(bpf);
837bc878e5dSjasper 
838bc878e5dSjasper 	return 0;
839bc878e5dSjasper }
840bc878e5dSjasper 
841bc878e5dSjasper int
do_wakeup(const char * eaddr,const char * iface,int bpf)842bc878e5dSjasper do_wakeup(const char *eaddr, const char *iface, int bpf)
843bc878e5dSjasper {
844bc878e5dSjasper 	struct ether_addr	 macaddr;
845bc878e5dSjasper 
846bc878e5dSjasper 	if (get_ether(eaddr, &macaddr) != 0)
847bc878e5dSjasper 		errx(1, "Invalid Ethernet address: %s", eaddr);
848bc878e5dSjasper 	if (bind_if_to_bpf(iface, bpf) != 0)
849bc878e5dSjasper 		errx(1, "Failed to bind %s to bpf.", iface);
850bc878e5dSjasper 	if (send_frame(bpf, &macaddr) != 0)
851bc878e5dSjasper 		errx(1, "Failed to send WoL frame on %s", iface);
852bc878e5dSjasper 	return 0;
853bc878e5dSjasper }
854bc878e5dSjasper 
855bc878e5dSjasper int
bind_if_to_bpf(const char * ifname,int bpf)856bc878e5dSjasper bind_if_to_bpf(const char *ifname, int bpf)
857bc878e5dSjasper {
858bc878e5dSjasper 	struct ifreq ifr;
859bc878e5dSjasper 	u_int dlt;
860bc878e5dSjasper 
861bc878e5dSjasper 	if (strlcpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name)) >=
862bc878e5dSjasper 	    sizeof(ifr.ifr_name))
863bc878e5dSjasper 		return -1;
864bc878e5dSjasper 	if (ioctl(bpf, BIOCSETIF, &ifr) == -1)
865bc878e5dSjasper 		return -1;
866bc878e5dSjasper 	if (ioctl(bpf, BIOCGDLT, &dlt) == -1)
867bc878e5dSjasper 		return -1;
868bc878e5dSjasper 	if (dlt != DLT_EN10MB)
869bc878e5dSjasper 		return -1;
870bc878e5dSjasper 	return 0;
871bc878e5dSjasper }
872bc878e5dSjasper 
873bc878e5dSjasper int
get_ether(const char * text,struct ether_addr * addr)874bc878e5dSjasper get_ether(const char *text, struct ether_addr *addr)
875bc878e5dSjasper {
876bc878e5dSjasper 	struct ether_addr *eaddr;
877bc878e5dSjasper 
878bc878e5dSjasper 	eaddr = ether_aton(text);
879bc878e5dSjasper 
880bc878e5dSjasper 	if (eaddr == NULL) {
881bc878e5dSjasper 		if (ether_hostton(text, addr))
882bc878e5dSjasper 			return -1;
883bc878e5dSjasper 	} else {
884bc878e5dSjasper 		*addr = *eaddr;
885bc878e5dSjasper 		return 0;
886bc878e5dSjasper 	}
887bc878e5dSjasper 
888bc878e5dSjasper 	return 0;
889bc878e5dSjasper }
890bc878e5dSjasper 
891bc878e5dSjasper #define SYNC_LEN 6
892bc878e5dSjasper #define DESTADDR_COUNT 16
893bc878e5dSjasper 
894bc878e5dSjasper int
send_frame(int bpf,const struct ether_addr * addr)895bc878e5dSjasper send_frame(int bpf, const struct ether_addr *addr)
896bc878e5dSjasper {
897bc878e5dSjasper 	struct {
898bc878e5dSjasper 		struct ether_header hdr;
899bc878e5dSjasper 		u_char sync[SYNC_LEN];
900bc878e5dSjasper 		u_char dest[ETHER_ADDR_LEN * DESTADDR_COUNT];
901bc878e5dSjasper 	} __packed pkt;
902bc878e5dSjasper 	u_char *p;
903bc878e5dSjasper 	int i;
904bc878e5dSjasper 
905bc878e5dSjasper 	(void)memset(&pkt, 0, sizeof(pkt));
906bc878e5dSjasper 	(void)memset(&pkt.hdr.ether_dhost, 0xff, sizeof(pkt.hdr.ether_dhost));
907bc878e5dSjasper 	pkt.hdr.ether_type = htons(0);
908bc878e5dSjasper 	(void)memset(pkt.sync, 0xff, SYNC_LEN);
909bc878e5dSjasper 	for (p = pkt.dest, i = 0; i < DESTADDR_COUNT; p += ETHER_ADDR_LEN, i++)
910bc878e5dSjasper 		bcopy(addr->ether_addr_octet, p, ETHER_ADDR_LEN);
911bc878e5dSjasper 	if (write(bpf, &pkt, sizeof(pkt)) != sizeof(pkt))
912bc878e5dSjasper 		return (errno);
913bc878e5dSjasper 	return (0);
914bc878e5dSjasper }
915