1 |
|
|
/* $OpenBSD: arp.c,v 1.75 2016/05/28 07:00:18 natano Exp $ */ |
2 |
|
|
/* $NetBSD: arp.c,v 1.12 1995/04/24 13:25:18 cgd Exp $ */ |
3 |
|
|
|
4 |
|
|
/* |
5 |
|
|
* Copyright (c) 1984, 1993 |
6 |
|
|
* The Regents of the University of California. All rights reserved. |
7 |
|
|
* |
8 |
|
|
* This code is derived from software contributed to Berkeley by |
9 |
|
|
* Sun Microsystems, Inc. |
10 |
|
|
* |
11 |
|
|
* Redistribution and use in source and binary forms, with or without |
12 |
|
|
* modification, are permitted provided that the following conditions |
13 |
|
|
* are met: |
14 |
|
|
* 1. Redistributions of source code must retain the above copyright |
15 |
|
|
* notice, this list of conditions and the following disclaimer. |
16 |
|
|
* 2. Redistributions in binary form must reproduce the above copyright |
17 |
|
|
* notice, this list of conditions and the following disclaimer in the |
18 |
|
|
* documentation and/or other materials provided with the distribution. |
19 |
|
|
* 3. Neither the name of the University nor the names of its contributors |
20 |
|
|
* may be used to endorse or promote products derived from this software |
21 |
|
|
* without specific prior written permission. |
22 |
|
|
* |
23 |
|
|
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND |
24 |
|
|
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
25 |
|
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE |
26 |
|
|
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE |
27 |
|
|
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL |
28 |
|
|
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS |
29 |
|
|
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) |
30 |
|
|
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT |
31 |
|
|
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY |
32 |
|
|
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF |
33 |
|
|
* SUCH DAMAGE. |
34 |
|
|
*/ |
35 |
|
|
|
36 |
|
|
/* |
37 |
|
|
* arp - display, set, delete arp table entries and wake up hosts. |
38 |
|
|
*/ |
39 |
|
|
|
40 |
|
|
#include <sys/file.h> |
41 |
|
|
#include <sys/socket.h> |
42 |
|
|
#include <sys/sysctl.h> |
43 |
|
|
#include <sys/ioctl.h> |
44 |
|
|
#include <net/bpf.h> |
45 |
|
|
#include <net/if.h> |
46 |
|
|
#include <net/if_dl.h> |
47 |
|
|
#include <net/if_types.h> |
48 |
|
|
#include <net/route.h> |
49 |
|
|
#include <netinet/in.h> |
50 |
|
|
#include <netinet/if_ether.h> |
51 |
|
|
#include <arpa/inet.h> |
52 |
|
|
|
53 |
|
|
#include <netdb.h> |
54 |
|
|
#include <errno.h> |
55 |
|
|
#include <err.h> |
56 |
|
|
#include <stdio.h> |
57 |
|
|
#include <stdlib.h> |
58 |
|
|
#include <string.h> |
59 |
|
|
#include <paths.h> |
60 |
|
|
#include <unistd.h> |
61 |
|
|
#include <limits.h> |
62 |
|
|
#include <ifaddrs.h> |
63 |
|
|
|
64 |
|
|
void dump(void); |
65 |
|
|
int delete(const char *); |
66 |
|
|
void search(in_addr_t addr, void (*action)(struct sockaddr_dl *sdl, |
67 |
|
|
struct sockaddr_inarp *sin, struct rt_msghdr *rtm)); |
68 |
|
|
void print_entry(struct sockaddr_dl *sdl, |
69 |
|
|
struct sockaddr_inarp *sin, struct rt_msghdr *rtm); |
70 |
|
|
void nuke_entry(struct sockaddr_dl *sdl, |
71 |
|
|
struct sockaddr_inarp *sin, struct rt_msghdr *rtm); |
72 |
|
|
static char *ether_str(struct sockaddr_dl *); |
73 |
|
|
int wake(const char *ether_addr, const char *iface); |
74 |
|
|
int file(char *); |
75 |
|
|
int get(const char *); |
76 |
|
|
int getinetaddr(const char *, struct in_addr *); |
77 |
|
|
void getsocket(void); |
78 |
|
|
int rtget(struct sockaddr_inarp **, struct sockaddr_dl **); |
79 |
|
|
int rtmsg(int); |
80 |
|
|
int set(int, char **); |
81 |
|
|
void usage(void); |
82 |
|
|
static char *sec2str(time_t); |
83 |
|
|
|
84 |
|
|
static pid_t pid; |
85 |
|
|
static int replace; /* replace entries when adding */ |
86 |
|
|
static int nflag; /* no reverse dns lookups */ |
87 |
|
|
static int aflag; /* do it for all entries */ |
88 |
|
|
static int s = -1; |
89 |
|
|
static int rdomain; |
90 |
|
|
|
91 |
|
|
extern int h_errno; |
92 |
|
|
|
93 |
|
|
/* ROUNDUP() is nasty, but it is identical to what's in the kernel. */ |
94 |
|
|
#define ROUNDUP(a) \ |
95 |
|
|
((a) > 0 ? (1 + (((a) - 1) | (sizeof(long) - 1))) : sizeof(long)) |
96 |
|
|
|
97 |
|
|
/* which function we're supposed to do */ |
98 |
|
|
#define F_GET 1 |
99 |
|
|
#define F_SET 2 |
100 |
|
|
#define F_FILESET 3 |
101 |
|
|
#define F_DELETE 4 |
102 |
|
|
#define F_WAKE 5 |
103 |
|
|
|
104 |
|
|
int |
105 |
|
|
main(int argc, char *argv[]) |
106 |
|
|
{ |
107 |
|
|
int ch, func = 0, error = 0; |
108 |
|
|
const char *errstr; |
109 |
|
|
|
110 |
|
|
pid = getpid(); |
111 |
|
|
opterr = 0; |
112 |
|
|
rdomain = getrtable(); |
113 |
|
|
while ((ch = getopt(argc, argv, "andsFfV:W")) != -1) { |
114 |
|
|
switch (ch) { |
115 |
|
|
case 'a': |
116 |
|
|
aflag = 1; |
117 |
|
|
break; |
118 |
|
|
case 'n': |
119 |
|
|
nflag = 1; |
120 |
|
|
break; |
121 |
|
|
case 'd': |
122 |
|
|
if (func) |
123 |
|
|
usage(); |
124 |
|
|
func = F_DELETE; |
125 |
|
|
break; |
126 |
|
|
case 's': |
127 |
|
|
if (func) |
128 |
|
|
usage(); |
129 |
|
|
func = F_SET; |
130 |
|
|
break; |
131 |
|
|
case 'F': |
132 |
|
|
replace = 1; |
133 |
|
|
break; |
134 |
|
|
case 'f': |
135 |
|
|
if (func) |
136 |
|
|
usage(); |
137 |
|
|
func = F_FILESET; |
138 |
|
|
break; |
139 |
|
|
case 'V': |
140 |
|
|
rdomain = strtonum(optarg, 0, RT_TABLEID_MAX, &errstr); |
141 |
|
|
if (errstr != NULL) { |
142 |
|
|
warn("bad rdomain: %s", errstr); |
143 |
|
|
usage(); |
144 |
|
|
} |
145 |
|
|
break; |
146 |
|
|
case 'W': |
147 |
|
|
if (func) |
148 |
|
|
usage(); |
149 |
|
|
func = F_WAKE; |
150 |
|
|
break; |
151 |
|
|
default: |
152 |
|
|
usage(); |
153 |
|
|
break; |
154 |
|
|
} |
155 |
|
|
} |
156 |
|
|
argc -= optind; |
157 |
|
|
argv += optind; |
158 |
|
|
|
159 |
|
|
if (!func) |
160 |
|
|
func = F_GET; |
161 |
|
|
|
162 |
|
|
switch (func) { |
163 |
|
|
case F_GET: |
164 |
|
|
if (aflag && argc == 0) |
165 |
|
|
dump(); |
166 |
|
|
else if (!aflag && argc == 1) |
167 |
|
|
error = get(argv[0]); |
168 |
|
|
else |
169 |
|
|
usage(); |
170 |
|
|
break; |
171 |
|
|
case F_SET: |
172 |
|
|
if (argc < 2 || argc > 5) |
173 |
|
|
usage(); |
174 |
|
|
if (replace) |
175 |
|
|
delete(argv[0]); |
176 |
|
|
error = set(argc, argv) ? 1 : 0; |
177 |
|
|
break; |
178 |
|
|
case F_DELETE: |
179 |
|
|
if (aflag && argc == 0) |
180 |
|
|
search(0, nuke_entry); |
181 |
|
|
else if (!aflag && argc == 1) |
182 |
|
|
error = delete(argv[0]); |
183 |
|
|
else |
184 |
|
|
usage(); |
185 |
|
|
break; |
186 |
|
|
case F_FILESET: |
187 |
|
|
if (argc != 1) |
188 |
|
|
usage(); |
189 |
|
|
error = file(argv[0]); |
190 |
|
|
break; |
191 |
|
|
case F_WAKE: |
192 |
|
|
if (aflag || nflag || replace || rdomain > 0) |
193 |
|
|
usage(); |
194 |
|
|
if (argc == 1) |
195 |
|
|
error = wake(argv[0], NULL); |
196 |
|
|
else if (argc == 2) |
197 |
|
|
error = wake(argv[0], argv[1]); |
198 |
|
|
else |
199 |
|
|
usage(); |
200 |
|
|
break; |
201 |
|
|
} |
202 |
|
|
return (error); |
203 |
|
|
} |
204 |
|
|
|
205 |
|
|
/* |
206 |
|
|
* Process a file to set standard arp entries |
207 |
|
|
*/ |
208 |
|
|
int |
209 |
|
|
file(char *name) |
210 |
|
|
{ |
211 |
|
|
char line[100], arg[5][50], *args[5]; |
212 |
|
|
int i, retval; |
213 |
|
|
FILE *fp; |
214 |
|
|
|
215 |
|
|
if ((fp = fopen(name, "r")) == NULL) |
216 |
|
|
err(1, "cannot open %s", name); |
217 |
|
|
args[0] = &arg[0][0]; |
218 |
|
|
args[1] = &arg[1][0]; |
219 |
|
|
args[2] = &arg[2][0]; |
220 |
|
|
args[3] = &arg[3][0]; |
221 |
|
|
args[4] = &arg[4][0]; |
222 |
|
|
retval = 0; |
223 |
|
|
while (fgets(line, sizeof(line), fp) != NULL) { |
224 |
|
|
i = sscanf(line, "%49s %49s %49s %49s %49s", arg[0], arg[1], |
225 |
|
|
arg[2], arg[3], arg[4]); |
226 |
|
|
if (i < 2) { |
227 |
|
|
warnx("bad line: %s", line); |
228 |
|
|
retval = 1; |
229 |
|
|
continue; |
230 |
|
|
} |
231 |
|
|
if (replace) |
232 |
|
|
delete(arg[0]); |
233 |
|
|
if (set(i, args)) |
234 |
|
|
retval = 1; |
235 |
|
|
} |
236 |
|
|
fclose(fp); |
237 |
|
|
return (retval); |
238 |
|
|
} |
239 |
|
|
|
240 |
|
|
void |
241 |
|
|
getsocket(void) |
242 |
|
|
{ |
243 |
|
|
socklen_t len = sizeof(rdomain); |
244 |
|
|
|
245 |
|
|
if (s >= 0) |
246 |
|
|
return; |
247 |
|
|
s = socket(PF_ROUTE, SOCK_RAW, 0); |
248 |
|
|
if (s < 0) |
249 |
|
|
err(1, "socket"); |
250 |
|
|
if (setsockopt(s, PF_ROUTE, ROUTE_TABLEFILTER, &rdomain, len) < 0) |
251 |
|
|
err(1, "ROUTE_TABLEFILTER"); |
252 |
|
|
|
253 |
|
|
if (pledge("stdio dns rpath wpath cpath", NULL) == -1) |
254 |
|
|
err(1, "pledge"); |
255 |
|
|
} |
256 |
|
|
|
257 |
|
|
struct sockaddr_in so_mask = { 8, 0, 0, { 0xffffffff } }; |
258 |
|
|
struct sockaddr_inarp blank_sin = { sizeof(blank_sin), AF_INET }, sin_m; |
259 |
|
|
struct sockaddr_dl blank_sdl = { sizeof(blank_sdl), AF_LINK }, sdl_m; |
260 |
|
|
struct sockaddr_dl ifp_m = { sizeof(&ifp_m), AF_LINK }; |
261 |
|
|
time_t expire_time; |
262 |
|
|
int flags, export_only, doing_proxy, found_entry; |
263 |
|
|
struct { |
264 |
|
|
struct rt_msghdr m_rtm; |
265 |
|
|
char m_space[512]; |
266 |
|
|
} m_rtmsg; |
267 |
|
|
|
268 |
|
|
/* |
269 |
|
|
* Set an individual arp entry |
270 |
|
|
*/ |
271 |
|
|
int |
272 |
|
|
set(int argc, char *argv[]) |
273 |
|
|
{ |
274 |
|
|
struct sockaddr_inarp *sin; |
275 |
|
|
struct sockaddr_dl *sdl; |
276 |
|
|
struct rt_msghdr *rtm; |
277 |
|
|
char *eaddr = argv[1], *host = argv[0]; |
278 |
|
|
struct ether_addr *ea; |
279 |
|
|
|
280 |
|
|
sin = &sin_m; |
281 |
|
|
rtm = &(m_rtmsg.m_rtm); |
282 |
|
|
|
283 |
|
|
getsocket(); |
284 |
|
|
argc -= 2; |
285 |
|
|
argv += 2; |
286 |
|
|
sdl_m = blank_sdl; /* struct copy */ |
287 |
|
|
sin_m = blank_sin; /* struct copy */ |
288 |
|
|
if (getinetaddr(host, &sin->sin_addr) == -1) |
289 |
|
|
return (1); |
290 |
|
|
ea = ether_aton(eaddr); |
291 |
|
|
if (ea == NULL) |
292 |
|
|
errx(1, "invalid ethernet address: %s", eaddr); |
293 |
|
|
memcpy(LLADDR(&sdl_m), ea, sizeof(*ea)); |
294 |
|
|
sdl_m.sdl_alen = 6; |
295 |
|
|
expire_time = 0; |
296 |
|
|
doing_proxy = flags = export_only = 0; |
297 |
|
|
while (argc-- > 0) { |
298 |
|
|
if (strncmp(argv[0], "temp", 4) == 0) { |
299 |
|
|
struct timeval now; |
300 |
|
|
|
301 |
|
|
gettimeofday(&now, 0); |
302 |
|
|
expire_time = now.tv_sec + 20 * 60; |
303 |
|
|
if (flags & RTF_PERMANENT_ARP) { |
304 |
|
|
/* temp or permanent, not both */ |
305 |
|
|
usage(); |
306 |
|
|
return (0); |
307 |
|
|
} |
308 |
|
|
} else if (strncmp(argv[0], "pub", 3) == 0) { |
309 |
|
|
flags |= RTF_ANNOUNCE; |
310 |
|
|
doing_proxy = SIN_PROXY; |
311 |
|
|
} else if (strncmp(argv[0], "permanent", 9) == 0) { |
312 |
|
|
flags |= RTF_PERMANENT_ARP; |
313 |
|
|
if (expire_time != 0) { |
314 |
|
|
/* temp or permanent, not both */ |
315 |
|
|
usage(); |
316 |
|
|
return (0); |
317 |
|
|
} |
318 |
|
|
} |
319 |
|
|
|
320 |
|
|
argv++; |
321 |
|
|
} |
322 |
|
|
|
323 |
|
|
tryagain: |
324 |
|
|
if (rtget(&sin, &sdl)) { |
325 |
|
|
warn("%s", host); |
326 |
|
|
return (1); |
327 |
|
|
} |
328 |
|
|
|
329 |
|
|
if (sin->sin_addr.s_addr == sin_m.sin_addr.s_addr) { |
330 |
|
|
if (sdl->sdl_family == AF_LINK && |
331 |
|
|
(rtm->rtm_flags & RTF_LLINFO) && |
332 |
|
|
!(rtm->rtm_flags & RTF_GATEWAY)) |
333 |
|
|
switch (sdl->sdl_type) { |
334 |
|
|
case IFT_ETHER: |
335 |
|
|
case IFT_FDDI: |
336 |
|
|
case IFT_ISO88023: |
337 |
|
|
case IFT_ISO88024: |
338 |
|
|
case IFT_ISO88025: |
339 |
|
|
case IFT_CARP: |
340 |
|
|
goto overwrite; |
341 |
|
|
} |
342 |
|
|
|
343 |
|
|
if (doing_proxy == 0) { |
344 |
|
|
printf("set: can only proxy for %s\n", host); |
345 |
|
|
return (1); |
346 |
|
|
} |
347 |
|
|
if (sin_m.sin_other & SIN_PROXY) { |
348 |
|
|
printf("set: proxy entry exists for non 802 device\n"); |
349 |
|
|
return (1); |
350 |
|
|
} |
351 |
|
|
sin_m.sin_other = SIN_PROXY; |
352 |
|
|
export_only = 1; |
353 |
|
|
goto tryagain; |
354 |
|
|
} |
355 |
|
|
|
356 |
|
|
overwrite: |
357 |
|
|
if (sdl->sdl_family != AF_LINK) { |
358 |
|
|
printf("cannot intuit interface index and type for %s\n", host); |
359 |
|
|
return (1); |
360 |
|
|
} |
361 |
|
|
sdl_m.sdl_type = sdl->sdl_type; |
362 |
|
|
sdl_m.sdl_index = sdl->sdl_index; |
363 |
|
|
return (rtmsg(RTM_ADD)); |
364 |
|
|
} |
365 |
|
|
|
366 |
|
|
#define W_ADDR 36 |
367 |
|
|
#define W_LL 17 |
368 |
|
|
#define W_IF 6 |
369 |
|
|
|
370 |
|
|
/* |
371 |
|
|
* Display an individual arp entry |
372 |
|
|
*/ |
373 |
|
|
int |
374 |
|
|
get(const char *host) |
375 |
|
|
{ |
376 |
|
|
struct sockaddr_inarp *sin; |
377 |
|
|
|
378 |
|
|
sin = &sin_m; |
379 |
|
|
sin_m = blank_sin; /* struct copy */ |
380 |
|
|
if (getinetaddr(host, &sin->sin_addr) == -1) |
381 |
|
|
exit(1); |
382 |
|
|
|
383 |
|
|
printf("%-*.*s %-*.*s %*.*s %-10.10s %5s\n", |
384 |
|
|
W_ADDR, W_ADDR, "Host", W_LL, W_LL, "Ethernet Address", |
385 |
|
|
W_IF, W_IF, "Netif", "Expire", "Flags"); |
386 |
|
|
|
387 |
|
|
search(sin->sin_addr.s_addr, print_entry); |
388 |
|
|
if (found_entry == 0) { |
389 |
|
|
printf("%-*.*s no entry\n", W_ADDR, W_ADDR, |
390 |
|
|
inet_ntoa(sin->sin_addr)); |
391 |
|
|
return (1); |
392 |
|
|
} |
393 |
|
|
return (0); |
394 |
|
|
} |
395 |
|
|
|
396 |
|
|
/* |
397 |
|
|
* Delete an arp entry |
398 |
|
|
*/ |
399 |
|
|
int |
400 |
|
|
delete(const char *host) |
401 |
|
|
{ |
402 |
|
|
struct sockaddr_inarp *sin; |
403 |
|
|
struct rt_msghdr *rtm; |
404 |
|
|
struct sockaddr_dl *sdl; |
405 |
|
|
|
406 |
|
|
sin = &sin_m; |
407 |
|
|
rtm = &m_rtmsg.m_rtm; |
408 |
|
|
|
409 |
|
|
getsocket(); |
410 |
|
|
sin_m = blank_sin; /* struct copy */ |
411 |
|
|
if (getinetaddr(host, &sin->sin_addr) == -1) |
412 |
|
|
return (1); |
413 |
|
|
tryagain: |
414 |
|
|
if (rtget(&sin, &sdl)) { |
415 |
|
|
warn("%s", host); |
416 |
|
|
return (1); |
417 |
|
|
} |
418 |
|
|
if (sin->sin_addr.s_addr == sin_m.sin_addr.s_addr) { |
419 |
|
|
if (sdl->sdl_family == AF_LINK && rtm->rtm_flags & RTF_LLINFO) { |
420 |
|
|
if (rtm->rtm_flags & RTF_LOCAL) |
421 |
|
|
return (0); |
422 |
|
|
if (!(rtm->rtm_flags & RTF_GATEWAY)) |
423 |
|
|
switch (sdl->sdl_type) { |
424 |
|
|
case IFT_ETHER: |
425 |
|
|
case IFT_FDDI: |
426 |
|
|
case IFT_ISO88023: |
427 |
|
|
case IFT_ISO88024: |
428 |
|
|
case IFT_ISO88025: |
429 |
|
|
case IFT_CARP: |
430 |
|
|
goto delete; |
431 |
|
|
} |
432 |
|
|
} |
433 |
|
|
} |
434 |
|
|
|
435 |
|
|
if (sin_m.sin_other & SIN_PROXY) { |
436 |
|
|
warnx("delete: can't locate %s", host); |
437 |
|
|
return (1); |
438 |
|
|
} else { |
439 |
|
|
sin_m.sin_other = SIN_PROXY; |
440 |
|
|
goto tryagain; |
441 |
|
|
} |
442 |
|
|
delete: |
443 |
|
|
if (sdl->sdl_family != AF_LINK) { |
444 |
|
|
printf("cannot locate %s\n", host); |
445 |
|
|
return (1); |
446 |
|
|
} |
447 |
|
|
if (rtmsg(RTM_DELETE)) |
448 |
|
|
return (1); |
449 |
|
|
printf("%s (%s) deleted\n", host, inet_ntoa(sin->sin_addr)); |
450 |
|
|
return (0); |
451 |
|
|
} |
452 |
|
|
|
453 |
|
|
/* |
454 |
|
|
* Search the entire arp table, and do some action on matching entries. |
455 |
|
|
*/ |
456 |
|
|
void |
457 |
|
|
search(in_addr_t addr, void (*action)(struct sockaddr_dl *sdl, |
458 |
|
|
struct sockaddr_inarp *sin, struct rt_msghdr *rtm)) |
459 |
|
|
{ |
460 |
|
|
int mib[7]; |
461 |
|
|
size_t needed; |
462 |
|
|
char *lim, *buf = NULL, *next; |
463 |
|
|
struct rt_msghdr *rtm; |
464 |
|
|
struct sockaddr_inarp *sin; |
465 |
|
|
struct sockaddr_dl *sdl; |
466 |
|
|
|
467 |
|
|
mib[0] = CTL_NET; |
468 |
|
|
mib[1] = PF_ROUTE; |
469 |
|
|
mib[2] = 0; |
470 |
|
|
mib[3] = AF_INET; |
471 |
|
|
mib[4] = NET_RT_FLAGS; |
472 |
|
|
mib[5] = RTF_LLINFO; |
473 |
|
|
mib[6] = rdomain; |
474 |
|
|
while (1) { |
475 |
|
|
if (sysctl(mib, 7, NULL, &needed, NULL, 0) == -1) |
476 |
|
|
err(1, "route-sysctl-estimate"); |
477 |
|
|
if (needed == 0) |
478 |
|
|
return; |
479 |
|
|
if ((buf = realloc(buf, needed)) == NULL) |
480 |
|
|
err(1, "malloc"); |
481 |
|
|
if (sysctl(mib, 7, buf, &needed, NULL, 0) == -1) { |
482 |
|
|
if (errno == ENOMEM) |
483 |
|
|
continue; |
484 |
|
|
err(1, "actual retrieval of routing table"); |
485 |
|
|
} |
486 |
|
|
lim = buf + needed; |
487 |
|
|
break; |
488 |
|
|
} |
489 |
|
|
for (next = buf; next < lim; next += rtm->rtm_msglen) { |
490 |
|
|
rtm = (struct rt_msghdr *)next; |
491 |
|
|
if (rtm->rtm_version != RTM_VERSION) |
492 |
|
|
continue; |
493 |
|
|
sin = (struct sockaddr_inarp *)(next + rtm->rtm_hdrlen); |
494 |
|
|
sdl = (struct sockaddr_dl *)(sin + 1); |
495 |
|
|
if (addr) { |
496 |
|
|
if (addr != sin->sin_addr.s_addr) |
497 |
|
|
continue; |
498 |
|
|
found_entry = 1; |
499 |
|
|
} |
500 |
|
|
(*action)(sdl, sin, rtm); |
501 |
|
|
} |
502 |
|
|
free(buf); |
503 |
|
|
} |
504 |
|
|
|
505 |
|
|
/* |
506 |
|
|
* Dump the entire ARP table |
507 |
|
|
*/ |
508 |
|
|
void |
509 |
|
|
dump(void) |
510 |
|
|
{ |
511 |
|
|
printf("%-*.*s %-*.*s %*.*s %-10.10s %5s\n", |
512 |
|
|
W_ADDR, W_ADDR, "Host", W_LL, W_LL, "Ethernet Address", |
513 |
|
|
W_IF, W_IF, "Netif", "Expire", "Flags"); |
514 |
|
|
|
515 |
|
|
search(0, print_entry); |
516 |
|
|
} |
517 |
|
|
|
518 |
|
|
/* |
519 |
|
|
* Display an arp entry |
520 |
|
|
*/ |
521 |
|
|
void |
522 |
|
|
print_entry(struct sockaddr_dl *sdl, struct sockaddr_inarp *sin, |
523 |
|
|
struct rt_msghdr *rtm) |
524 |
|
|
{ |
525 |
|
|
char ifix_buf[IFNAMSIZ], *ifname, *host; |
526 |
|
|
struct hostent *hp = NULL; |
527 |
|
|
int addrwidth, llwidth, ifwidth ; |
528 |
|
|
struct timeval now; |
529 |
|
|
|
530 |
|
|
gettimeofday(&now, 0); |
531 |
|
|
|
532 |
|
|
if (nflag == 0) |
533 |
|
|
hp = gethostbyaddr((caddr_t)&(sin->sin_addr), |
534 |
|
|
sizeof(sin->sin_addr), AF_INET); |
535 |
|
|
if (hp) |
536 |
|
|
host = hp->h_name; |
537 |
|
|
else |
538 |
|
|
host = inet_ntoa(sin->sin_addr); |
539 |
|
|
|
540 |
|
|
addrwidth = strlen(host); |
541 |
|
|
if (addrwidth < W_ADDR) |
542 |
|
|
addrwidth = W_ADDR; |
543 |
|
|
llwidth = strlen(ether_str(sdl)); |
544 |
|
|
if (W_ADDR + W_LL - addrwidth > llwidth) |
545 |
|
|
llwidth = W_ADDR + W_LL - addrwidth; |
546 |
|
|
ifname = if_indextoname(sdl->sdl_index, ifix_buf); |
547 |
|
|
if (!ifname) |
548 |
|
|
ifname = "?"; |
549 |
|
|
ifwidth = strlen(ifname); |
550 |
|
|
if (W_ADDR + W_LL + W_IF - addrwidth - llwidth > ifwidth) |
551 |
|
|
ifwidth = W_ADDR + W_LL + W_IF - addrwidth - llwidth; |
552 |
|
|
|
553 |
|
|
printf("%-*.*s %-*.*s %*.*s", addrwidth, addrwidth, host, |
554 |
|
|
llwidth, llwidth, ether_str(sdl), ifwidth, ifwidth, ifname); |
555 |
|
|
|
556 |
|
|
if (rtm->rtm_flags & (RTF_PERMANENT_ARP|RTF_LOCAL)) |
557 |
|
|
printf(" %-10.10s", "permanent"); |
558 |
|
|
else if (rtm->rtm_rmx.rmx_expire == 0) |
559 |
|
|
printf(" %-10.10s", "static"); |
560 |
|
|
else if (rtm->rtm_rmx.rmx_expire > now.tv_sec) |
561 |
|
|
printf(" %-10.10s", |
562 |
|
|
sec2str(rtm->rtm_rmx.rmx_expire - now.tv_sec)); |
563 |
|
|
else |
564 |
|
|
printf(" %-10.10s", "expired"); |
565 |
|
|
|
566 |
|
|
printf(" %s%s\n", |
567 |
|
|
(rtm->rtm_flags & RTF_LOCAL) ? "l" : "", |
568 |
|
|
(rtm->rtm_flags & RTF_ANNOUNCE) ? "p" : ""); |
569 |
|
|
} |
570 |
|
|
|
571 |
|
|
/* |
572 |
|
|
* Nuke an arp entry |
573 |
|
|
*/ |
574 |
|
|
void |
575 |
|
|
nuke_entry(struct sockaddr_dl *sdl, struct sockaddr_inarp *sin, |
576 |
|
|
struct rt_msghdr *rtm) |
577 |
|
|
{ |
578 |
|
|
char ip[20]; |
579 |
|
|
|
580 |
|
|
strlcpy(ip, inet_ntoa(sin->sin_addr), sizeof(ip)); |
581 |
|
|
delete(ip); |
582 |
|
|
} |
583 |
|
|
|
584 |
|
|
static char * |
585 |
|
|
ether_str(struct sockaddr_dl *sdl) |
586 |
|
|
{ |
587 |
|
|
static char hbuf[NI_MAXHOST]; |
588 |
|
|
u_char *cp; |
589 |
|
|
|
590 |
|
|
if (sdl->sdl_alen) { |
591 |
|
|
cp = (u_char *)LLADDR(sdl); |
592 |
|
|
snprintf(hbuf, sizeof(hbuf), "%02x:%02x:%02x:%02x:%02x:%02x", |
593 |
|
|
cp[0], cp[1], cp[2], cp[3], cp[4], cp[5]); |
594 |
|
|
} else |
595 |
|
|
snprintf(hbuf, sizeof(hbuf), "(incomplete)"); |
596 |
|
|
|
597 |
|
|
return(hbuf); |
598 |
|
|
} |
599 |
|
|
|
600 |
|
|
void |
601 |
|
|
usage(void) |
602 |
|
|
{ |
603 |
|
|
fprintf(stderr, "usage: arp [-adn] [-V rdomain] hostname\n"); |
604 |
|
|
fprintf(stderr, " arp [-F] [-f file] [-V rdomain] " |
605 |
|
|
"-s hostname ether_addr\n" |
606 |
|
|
" [temp | permanent] [pub]\n"); |
607 |
|
|
fprintf(stderr, " arp -W ether_addr [iface]\n"); |
608 |
|
|
exit(1); |
609 |
|
|
} |
610 |
|
|
|
611 |
|
|
int |
612 |
|
|
rtmsg(int cmd) |
613 |
|
|
{ |
614 |
|
|
static int seq; |
615 |
|
|
struct rt_msghdr *rtm; |
616 |
|
|
char *cp; |
617 |
|
|
int l; |
618 |
|
|
|
619 |
|
|
rtm = &m_rtmsg.m_rtm; |
620 |
|
|
cp = m_rtmsg.m_space; |
621 |
|
|
errno = 0; |
622 |
|
|
|
623 |
|
|
if (cmd == RTM_DELETE) |
624 |
|
|
goto doit; |
625 |
|
|
memset(&m_rtmsg, 0, sizeof(m_rtmsg)); |
626 |
|
|
rtm->rtm_flags = flags; |
627 |
|
|
rtm->rtm_version = RTM_VERSION; |
628 |
|
|
rtm->rtm_hdrlen = sizeof(*rtm); |
629 |
|
|
rtm->rtm_tableid = rdomain; |
630 |
|
|
|
631 |
|
|
switch (cmd) { |
632 |
|
|
default: |
633 |
|
|
errx(1, "internal wrong cmd"); |
634 |
|
|
/*NOTREACHED*/ |
635 |
|
|
case RTM_ADD: |
636 |
|
|
rtm->rtm_addrs |= RTA_GATEWAY; |
637 |
|
|
rtm->rtm_rmx.rmx_expire = expire_time; |
638 |
|
|
rtm->rtm_inits = RTV_EXPIRE; |
639 |
|
|
rtm->rtm_flags |= (RTF_HOST | RTF_STATIC); |
640 |
|
|
sin_m.sin_other = 0; |
641 |
|
|
if (doing_proxy) { |
642 |
|
|
if (export_only) |
643 |
|
|
sin_m.sin_other = SIN_PROXY; |
644 |
|
|
else { |
645 |
|
|
rtm->rtm_addrs |= RTA_NETMASK; |
646 |
|
|
rtm->rtm_flags &= ~RTF_HOST; |
647 |
|
|
} |
648 |
|
|
} |
649 |
|
|
/* FALLTHROUGH */ |
650 |
|
|
case RTM_GET: |
651 |
|
|
rtm->rtm_addrs |= (RTA_DST | RTA_IFP); |
652 |
|
|
} |
653 |
|
|
|
654 |
|
|
#define NEXTADDR(w, s) \ |
655 |
|
|
if (rtm->rtm_addrs & (w)) { \ |
656 |
|
|
memcpy(cp, &s, sizeof(s)); \ |
657 |
|
|
cp += ROUNDUP(sizeof(s)); \ |
658 |
|
|
} |
659 |
|
|
|
660 |
|
|
NEXTADDR(RTA_DST, sin_m); |
661 |
|
|
NEXTADDR(RTA_GATEWAY, sdl_m); |
662 |
|
|
NEXTADDR(RTA_NETMASK, so_mask); |
663 |
|
|
NEXTADDR(RTA_IFP, ifp_m); |
664 |
|
|
|
665 |
|
|
rtm->rtm_msglen = cp - (char *)&m_rtmsg; |
666 |
|
|
doit: |
667 |
|
|
l = rtm->rtm_msglen; |
668 |
|
|
rtm->rtm_seq = ++seq; |
669 |
|
|
rtm->rtm_type = cmd; |
670 |
|
|
if (write(s, (char *)&m_rtmsg, l) < 0) |
671 |
|
|
if (errno != ESRCH || cmd != RTM_DELETE) { |
672 |
|
|
warn("writing to routing socket"); |
673 |
|
|
return (-1); |
674 |
|
|
} |
675 |
|
|
|
676 |
|
|
do { |
677 |
|
|
l = read(s, (char *)&m_rtmsg, sizeof(m_rtmsg)); |
678 |
|
|
} while (l > 0 && (rtm->rtm_version != RTM_VERSION || |
679 |
|
|
rtm->rtm_seq != seq || rtm->rtm_pid != pid)); |
680 |
|
|
|
681 |
|
|
if (l < 0) |
682 |
|
|
warn("read from routing socket"); |
683 |
|
|
return (0); |
684 |
|
|
} |
685 |
|
|
|
686 |
|
|
int |
687 |
|
|
rtget(struct sockaddr_inarp **sinp, struct sockaddr_dl **sdlp) |
688 |
|
|
{ |
689 |
|
|
struct rt_msghdr *rtm = &(m_rtmsg.m_rtm); |
690 |
|
|
struct sockaddr_inarp *sin = NULL; |
691 |
|
|
struct sockaddr_dl *sdl = NULL; |
692 |
|
|
struct sockaddr *sa; |
693 |
|
|
char *cp; |
694 |
|
|
unsigned int i; |
695 |
|
|
|
696 |
|
|
if (rtmsg(RTM_GET) < 0) |
697 |
|
|
return (1); |
698 |
|
|
|
699 |
|
|
if (rtm->rtm_addrs) { |
700 |
|
|
cp = ((char *)rtm + rtm->rtm_hdrlen); |
701 |
|
|
for (i = 1; i; i <<= 1) { |
702 |
|
|
if (i & rtm->rtm_addrs) { |
703 |
|
|
sa = (struct sockaddr *)cp; |
704 |
|
|
switch (i) { |
705 |
|
|
case RTA_DST: |
706 |
|
|
sin = (struct sockaddr_inarp *)sa; |
707 |
|
|
break; |
708 |
|
|
case RTA_IFP: |
709 |
|
|
sdl = (struct sockaddr_dl *)sa; |
710 |
|
|
break; |
711 |
|
|
default: |
712 |
|
|
break; |
713 |
|
|
} |
714 |
|
|
cp += ROUNDUP(sa->sa_len); |
715 |
|
|
} |
716 |
|
|
} |
717 |
|
|
} |
718 |
|
|
|
719 |
|
|
if (sin == NULL || sdl == NULL) |
720 |
|
|
return (1); |
721 |
|
|
|
722 |
|
|
*sinp = sin; |
723 |
|
|
*sdlp = sdl; |
724 |
|
|
|
725 |
|
|
return (0); |
726 |
|
|
} |
727 |
|
|
|
728 |
|
|
int |
729 |
|
|
getinetaddr(const char *host, struct in_addr *inap) |
730 |
|
|
{ |
731 |
|
|
struct hostent *hp; |
732 |
|
|
|
733 |
|
|
if (inet_aton(host, inap) == 1) |
734 |
|
|
return (0); |
735 |
|
|
if ((hp = gethostbyname(host)) == NULL) { |
736 |
|
|
warnx("%s: %s", host, hstrerror(h_errno)); |
737 |
|
|
return (-1); |
738 |
|
|
} |
739 |
|
|
memcpy(inap, hp->h_addr, sizeof(*inap)); |
740 |
|
|
return (0); |
741 |
|
|
} |
742 |
|
|
|
743 |
|
|
static char * |
744 |
|
|
sec2str(time_t total) |
745 |
|
|
{ |
746 |
|
|
static char result[256]; |
747 |
|
|
int days, hours, mins, secs; |
748 |
|
|
int first = 1; |
749 |
|
|
char *p = result; |
750 |
|
|
char *ep = &result[sizeof(result)]; |
751 |
|
|
int n; |
752 |
|
|
|
753 |
|
|
days = total / 3600 / 24; |
754 |
|
|
hours = (total / 3600) % 24; |
755 |
|
|
mins = (total / 60) % 60; |
756 |
|
|
secs = total % 60; |
757 |
|
|
|
758 |
|
|
if (days) { |
759 |
|
|
first = 0; |
760 |
|
|
n = snprintf(p, ep - p, "%dd", days); |
761 |
|
|
if (n < 0 || n >= ep - p) |
762 |
|
|
return "?"; |
763 |
|
|
p += n; |
764 |
|
|
} |
765 |
|
|
if (!first || hours) { |
766 |
|
|
first = 0; |
767 |
|
|
n = snprintf(p, ep - p, "%dh", hours); |
768 |
|
|
if (n < 0 || n >= ep - p) |
769 |
|
|
return "?"; |
770 |
|
|
p += n; |
771 |
|
|
} |
772 |
|
|
if (!first || mins) { |
773 |
|
|
first = 0; |
774 |
|
|
n = snprintf(p, ep - p, "%dm", mins); |
775 |
|
|
if (n < 0 || n >= ep - p) |
776 |
|
|
return "?"; |
777 |
|
|
p += n; |
778 |
|
|
} |
779 |
|
|
snprintf(p, ep - p, "%ds", secs); |
780 |
|
|
|
781 |
|
|
return(result); |
782 |
|
|
} |
783 |
|
|
|
784 |
|
|
/* |
785 |
|
|
* Copyright (c) 2011 Jasper Lievisse Adriaanse <jasper@openbsd.org> |
786 |
|
|
* Copyright (C) 2006,2007,2008,2009 Marc Balmer <mbalmer@openbsd.org> |
787 |
|
|
* Copyright (C) 2000 Eugene M. Kim. All rights reserved. |
788 |
|
|
* |
789 |
|
|
* Redistribution and use in source and binary forms, with or without |
790 |
|
|
* modification, are permitted provided that the following conditions |
791 |
|
|
* are met: |
792 |
|
|
* |
793 |
|
|
* 1. Redistributions of source code must retain the above copyright |
794 |
|
|
* notice, this list of conditions and the following disclaimer. |
795 |
|
|
* 2. Author's name may not be used endorse or promote products derived |
796 |
|
|
* from this software without specific prior written permission. |
797 |
|
|
* |
798 |
|
|
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR |
799 |
|
|
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED |
800 |
|
|
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE |
801 |
|
|
* DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, |
802 |
|
|
* INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES |
803 |
|
|
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR |
804 |
|
|
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) |
805 |
|
|
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, |
806 |
|
|
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING |
807 |
|
|
* IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE |
808 |
|
|
* POSSIBILITY OF SUCH DAMAGE. |
809 |
|
|
*/ |
810 |
|
|
|
811 |
|
|
int do_wakeup(const char *, const char *, int); |
812 |
|
|
int bind_if_to_bpf(const char *, int); |
813 |
|
|
int get_ether(const char *, struct ether_addr *); |
814 |
|
|
int send_frame(int, const struct ether_addr *); |
815 |
|
|
|
816 |
|
|
int |
817 |
|
|
wake(const char *ether_addr, const char *iface) |
818 |
|
|
{ |
819 |
|
|
struct ifaddrs *ifa, *ifap; |
820 |
|
|
char *pname = NULL; |
821 |
|
|
int bpf; |
822 |
|
|
|
823 |
|
|
if ((bpf = open("/dev/bpf0", O_RDWR)) == -1) |
824 |
|
|
err(1, "Failed to bind to bpf"); |
825 |
|
|
|
826 |
|
|
if (iface == NULL) { |
827 |
|
|
if (getifaddrs(&ifa) == -1) |
828 |
|
|
errx(1, "Could not get interface addresses."); |
829 |
|
|
|
830 |
|
|
for (ifap = ifa; ifap != NULL; ifap = ifap->ifa_next){ |
831 |
|
|
if (pname && !strcmp(pname, ifap->ifa_name)) |
832 |
|
|
continue; |
833 |
|
|
pname = ifap->ifa_name; |
834 |
|
|
|
835 |
|
|
/* |
836 |
|
|
* We're only interested in sending the WoL frame on |
837 |
|
|
* certain interfaces. So skip the loopback interface, |
838 |
|
|
* as well as point-to-point and down interfaces. |
839 |
|
|
*/ |
840 |
|
|
if ((ifap->ifa_flags & IFF_LOOPBACK) || |
841 |
|
|
(ifap->ifa_flags & IFF_POINTOPOINT) || |
842 |
|
|
(!(ifap->ifa_flags & IFF_UP)) || |
843 |
|
|
(!(ifap->ifa_flags & IFF_BROADCAST))) |
844 |
|
|
continue; |
845 |
|
|
|
846 |
|
|
do_wakeup(ether_addr, ifap->ifa_name, bpf); |
847 |
|
|
} |
848 |
|
|
freeifaddrs(ifa); |
849 |
|
|
} else { |
850 |
|
|
do_wakeup(ether_addr, iface, bpf); |
851 |
|
|
} |
852 |
|
|
|
853 |
|
|
(void)close(bpf); |
854 |
|
|
|
855 |
|
|
return 0; |
856 |
|
|
} |
857 |
|
|
|
858 |
|
|
int |
859 |
|
|
do_wakeup(const char *eaddr, const char *iface, int bpf) |
860 |
|
|
{ |
861 |
|
|
struct ether_addr macaddr; |
862 |
|
|
|
863 |
|
|
if (get_ether(eaddr, &macaddr) != 0) |
864 |
|
|
errx(1, "Invalid Ethernet address: %s", eaddr); |
865 |
|
|
if (bind_if_to_bpf(iface, bpf) != 0) |
866 |
|
|
errx(1, "Failed to bind %s to bpf.", iface); |
867 |
|
|
if (send_frame(bpf, &macaddr) != 0) |
868 |
|
|
errx(1, "Failed to send WoL frame on %s", iface); |
869 |
|
|
return 0; |
870 |
|
|
} |
871 |
|
|
|
872 |
|
|
int |
873 |
|
|
bind_if_to_bpf(const char *ifname, int bpf) |
874 |
|
|
{ |
875 |
|
|
struct ifreq ifr; |
876 |
|
|
u_int dlt; |
877 |
|
|
|
878 |
|
|
if (strlcpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name)) >= |
879 |
|
|
sizeof(ifr.ifr_name)) |
880 |
|
|
return -1; |
881 |
|
|
if (ioctl(bpf, BIOCSETIF, &ifr) == -1) |
882 |
|
|
return -1; |
883 |
|
|
if (ioctl(bpf, BIOCGDLT, &dlt) == -1) |
884 |
|
|
return -1; |
885 |
|
|
if (dlt != DLT_EN10MB) |
886 |
|
|
return -1; |
887 |
|
|
return 0; |
888 |
|
|
} |
889 |
|
|
|
890 |
|
|
int |
891 |
|
|
get_ether(const char *text, struct ether_addr *addr) |
892 |
|
|
{ |
893 |
|
|
struct ether_addr *eaddr; |
894 |
|
|
|
895 |
|
|
eaddr = ether_aton(text); |
896 |
|
|
|
897 |
|
|
if (eaddr == NULL) { |
898 |
|
|
if (ether_hostton(text, addr)) |
899 |
|
|
return -1; |
900 |
|
|
} else { |
901 |
|
|
*addr = *eaddr; |
902 |
|
|
return 0; |
903 |
|
|
} |
904 |
|
|
|
905 |
|
|
return 0; |
906 |
|
|
} |
907 |
|
|
|
908 |
|
|
#define SYNC_LEN 6 |
909 |
|
|
#define DESTADDR_COUNT 16 |
910 |
|
|
|
911 |
|
|
int |
912 |
|
|
send_frame(int bpf, const struct ether_addr *addr) |
913 |
|
|
{ |
914 |
|
|
struct { |
915 |
|
|
struct ether_header hdr; |
916 |
|
|
u_char sync[SYNC_LEN]; |
917 |
|
|
u_char dest[ETHER_ADDR_LEN * DESTADDR_COUNT]; |
918 |
|
|
} __packed pkt; |
919 |
|
|
u_char *p; |
920 |
|
|
int i; |
921 |
|
|
|
922 |
|
|
(void)memset(&pkt, 0, sizeof(pkt)); |
923 |
|
|
(void)memset(&pkt.hdr.ether_dhost, 0xff, sizeof(pkt.hdr.ether_dhost)); |
924 |
|
|
pkt.hdr.ether_type = htons(0); |
925 |
|
|
(void)memset(pkt.sync, 0xff, SYNC_LEN); |
926 |
|
|
for (p = pkt.dest, i = 0; i < DESTADDR_COUNT; p += ETHER_ADDR_LEN, i++) |
927 |
|
|
bcopy(addr->ether_addr_octet, p, ETHER_ADDR_LEN); |
928 |
|
|
if (write(bpf, &pkt, sizeof(pkt)) != sizeof(pkt)) |
929 |
|
|
return (errno); |
930 |
|
|
return (0); |
931 |
|
|
} |