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