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