Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
freebsd
GitHub Repository: freebsd/freebsd-src
Path: blob/main/usr.sbin/arp/arp.c
102602 views
1
/*-
2
* SPDX-License-Identifier: BSD-3-Clause
3
*
4
* Copyright (c) 1984, 1993
5
* The Regents of the University of California. All rights reserved.
6
*
7
* This code is derived from software contributed to Berkeley by
8
* Sun Microsystems, Inc.
9
*
10
* Redistribution and use in source and binary forms, with or without
11
* modification, are permitted provided that the following conditions
12
* are met:
13
* 1. Redistributions of source code must retain the above copyright
14
* notice, this list of conditions and the following disclaimer.
15
* 2. Redistributions in binary form must reproduce the above copyright
16
* notice, this list of conditions and the following disclaimer in the
17
* documentation and/or other materials provided with the distribution.
18
* 3. Neither the name of the University nor the names of its contributors
19
* may be used to endorse or promote products derived from this software
20
* without specific prior written permission.
21
*
22
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
23
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
24
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
25
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
26
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
27
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
28
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
29
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
30
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
31
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
32
* SUCH DAMAGE.
33
*/
34
35
/*
36
* arp - display, set, and delete arp table entries
37
*/
38
39
#include <sys/param.h>
40
#include <sys/file.h>
41
#include <sys/socket.h>
42
#include <sys/sockio.h>
43
#include <sys/sysctl.h>
44
#include <sys/ioctl.h>
45
#include <sys/time.h>
46
47
#include <net/if.h>
48
#include <net/if_dl.h>
49
#include <net/if_types.h>
50
#include <net/route.h>
51
52
#include <netinet/in.h>
53
#include <netinet/if_ether.h>
54
55
#include <arpa/inet.h>
56
57
#include <ctype.h>
58
#include <errno.h>
59
#include <netdb.h>
60
#include <nlist.h>
61
#include <paths.h>
62
#include <stdbool.h>
63
#include <stdio.h>
64
#include <stdlib.h>
65
#include <string.h>
66
#include <strings.h>
67
#include <unistd.h>
68
#include <ifaddrs.h>
69
#include <libxo/xo.h>
70
#include "arp.h"
71
72
typedef void (action_fn)(struct sockaddr_dl *sdl, struct sockaddr_in *s_in,
73
struct rt_msghdr *rtm);
74
static void nuke_entries(uint32_t ifindex, struct in_addr addr);
75
static int print_entries(uint32_t ifindex, struct in_addr addr);
76
77
static int delete(char *host);
78
static void usage(void) __dead2;
79
static int set(int argc, char **argv);
80
static int get(char *host);
81
static int file(char *name);
82
static struct rt_msghdr *rtmsg(int cmd,
83
struct sockaddr_in *dst, struct sockaddr_dl *sdl);
84
static int set_rtsock(struct sockaddr_in *dst, struct sockaddr_dl *sdl_m,
85
char *host);
86
87
struct if_nameindex *ifnameindex;
88
89
struct arp_opts opts = {};
90
91
/* which function we're supposed to do */
92
#define F_GET 1
93
#define F_SET 2
94
#define F_FILESET 3
95
#define F_REPLACE 4
96
#define F_DELETE 5
97
98
#define SETFUNC(f) { if (func) usage(); func = (f); }
99
100
#define ARP_XO_VERSION "1"
101
102
int
103
main(int argc, char *argv[])
104
{
105
int ch, func = 0;
106
int rtn = 0;
107
108
argc = xo_parse_args(argc, argv);
109
if (argc < 0)
110
exit(1);
111
112
while ((ch = getopt(argc, argv, "andfsSi:")) != -1)
113
switch(ch) {
114
case 'a':
115
opts.aflag = true;
116
break;
117
case 'd':
118
SETFUNC(F_DELETE);
119
break;
120
case 'n':
121
opts.nflag = true;
122
break;
123
case 'S':
124
SETFUNC(F_REPLACE);
125
break;
126
case 's':
127
SETFUNC(F_SET);
128
break;
129
case 'f' :
130
SETFUNC(F_FILESET);
131
break;
132
case 'i':
133
opts.rifname = optarg;
134
break;
135
case '?':
136
default:
137
usage();
138
}
139
argc -= optind;
140
argv += optind;
141
142
if (!func)
143
func = F_GET;
144
if (opts.rifname) {
145
if (func != F_GET && func != F_SET && func != F_REPLACE &&
146
!(func == F_DELETE && opts.aflag))
147
xo_errx(1, "-i not applicable to this operation");
148
if ((opts.rifindex = if_nametoindex(opts.rifname)) == 0) {
149
if (errno == ENXIO)
150
xo_errx(1, "interface %s does not exist",
151
opts.rifname);
152
else
153
xo_err(1, "if_nametoindex(%s)", opts.rifname);
154
}
155
}
156
switch (func) {
157
case F_GET:
158
if (opts.aflag) {
159
if (argc != 0)
160
usage();
161
162
xo_set_version(ARP_XO_VERSION);
163
xo_open_container("arp");
164
xo_open_list("arp-cache");
165
166
struct in_addr all_addrs = {};
167
print_entries(opts.rifindex, all_addrs);
168
169
xo_close_list("arp-cache");
170
xo_close_container("arp");
171
if (xo_finish() < 0)
172
xo_err(1, "stdout");
173
} else {
174
if (argc != 1)
175
usage();
176
rtn = get(argv[0]);
177
}
178
break;
179
case F_SET:
180
case F_REPLACE:
181
if (argc < 2 || argc > 6)
182
usage();
183
if (func == F_REPLACE)
184
(void)delete(argv[0]);
185
rtn = set(argc, argv) ? 1 : 0;
186
break;
187
case F_DELETE:
188
if (opts.aflag) {
189
if (argc != 0)
190
usage();
191
struct in_addr all_addrs = {};
192
nuke_entries(0, all_addrs);
193
} else {
194
if (argc != 1)
195
usage();
196
rtn = delete(argv[0]);
197
}
198
break;
199
case F_FILESET:
200
if (argc != 1)
201
usage();
202
rtn = file(argv[0]);
203
break;
204
}
205
206
if (ifnameindex != NULL)
207
if_freenameindex(ifnameindex);
208
209
exit(rtn);
210
}
211
212
/*
213
* Process a file to set standard arp entries
214
*/
215
static int
216
file(char *name)
217
{
218
FILE *fp;
219
int i, retval;
220
char line[100], arg[5][50], *args[5], *p;
221
222
if ((fp = fopen(name, "r")) == NULL)
223
xo_err(1, "cannot open %s", name);
224
args[0] = &arg[0][0];
225
args[1] = &arg[1][0];
226
args[2] = &arg[2][0];
227
args[3] = &arg[3][0];
228
args[4] = &arg[4][0];
229
retval = 0;
230
while(fgets(line, sizeof(line), fp) != NULL) {
231
if ((p = strchr(line, '#')) != NULL)
232
*p = '\0';
233
for (p = line; isblank(*p); p++);
234
if (*p == '\n' || *p == '\0')
235
continue;
236
i = sscanf(p, "%49s %49s %49s %49s %49s", arg[0], arg[1],
237
arg[2], arg[3], arg[4]);
238
if (i < 2) {
239
xo_warnx("bad line: %s", line);
240
retval = 1;
241
continue;
242
}
243
if (set(i, args))
244
retval = 1;
245
}
246
fclose(fp);
247
return (retval);
248
}
249
250
/*
251
* Given a hostname, fills up a (static) struct sockaddr_in with
252
* the address of the host and returns a pointer to the
253
* structure.
254
*/
255
struct sockaddr_in *
256
getaddr(char *host)
257
{
258
struct hostent *hp;
259
static struct sockaddr_in reply;
260
261
bzero(&reply, sizeof(reply));
262
reply.sin_len = sizeof(reply);
263
reply.sin_family = AF_INET;
264
reply.sin_addr.s_addr = inet_addr(host);
265
if (reply.sin_addr.s_addr == INADDR_NONE) {
266
if (!(hp = gethostbyname(host))) {
267
xo_warnx("%s: %s", host, hstrerror(h_errno));
268
return (NULL);
269
}
270
bcopy((char *)hp->h_addr, (char *)&reply.sin_addr,
271
sizeof reply.sin_addr);
272
}
273
return (&reply);
274
}
275
276
/*
277
* Returns true if the type is a valid one for ARP.
278
*/
279
int
280
valid_type(int type)
281
{
282
283
switch (type) {
284
case IFT_ETHER:
285
case IFT_FDDI:
286
case IFT_IEEE1394:
287
case IFT_INFINIBAND:
288
case IFT_ISO88023:
289
case IFT_ISO88024:
290
case IFT_L2VLAN:
291
case IFT_BRIDGE:
292
return (1);
293
default:
294
return (0);
295
}
296
}
297
298
/*
299
* Set an individual arp entry
300
*/
301
static int
302
set(int argc, char **argv)
303
{
304
struct sockaddr_in *dst; /* what are we looking for */
305
struct ether_addr *ea;
306
char *host = argv[0], *eaddr = argv[1];
307
struct sockaddr_dl sdl_m;
308
309
argc -= 2;
310
argv += 2;
311
312
bzero(&sdl_m, sizeof(sdl_m));
313
sdl_m.sdl_len = sizeof(sdl_m);
314
sdl_m.sdl_family = AF_LINK;
315
316
dst = getaddr(host);
317
if (dst == NULL)
318
return (1);
319
while (argc-- > 0) {
320
if (strcmp(argv[0], "temp") == 0) {
321
int max_age;
322
size_t len = sizeof(max_age);
323
324
if (sysctlbyname("net.link.ether.inet.max_age",
325
&max_age, &len, NULL, 0) != 0)
326
xo_err(1, "sysctlbyname");
327
opts.expire_time = max_age;
328
} else if (strcmp(argv[0], "pub") == 0) {
329
opts.flags |= RTF_ANNOUNCE;
330
if (argc && strcmp(argv[1], "only") == 0) {
331
/*
332
* Compatibility: in pre FreeBSD 8 times
333
* the "only" keyword used to mean that
334
* an ARP entry should be announced, but
335
* not installed into routing table.
336
*/
337
argc--; argv++;
338
}
339
} else if (strcmp(argv[0], "blackhole") == 0) {
340
if (opts.flags & RTF_REJECT) {
341
xo_errx(1, "Choose one of blackhole or reject, "
342
"not both.");
343
}
344
opts.flags |= RTF_BLACKHOLE;
345
} else if (strcmp(argv[0], "reject") == 0) {
346
if (opts.flags & RTF_BLACKHOLE) {
347
xo_errx(1, "Choose one of blackhole or reject, "
348
"not both.");
349
}
350
opts.flags |= RTF_REJECT;
351
} else {
352
xo_warnx("Invalid parameter '%s'", argv[0]);
353
usage();
354
}
355
argv++;
356
}
357
ea = (struct ether_addr *)LLADDR(&sdl_m);
358
if ((opts.flags & RTF_ANNOUNCE) && !strcmp(eaddr, "auto")) {
359
uint32_t ifindex;
360
if (!get_ifinfo(dst->sin_addr.s_addr, ea, &ifindex)) {
361
xo_warnx("no interface found for %s",
362
inet_ntoa(dst->sin_addr));
363
return (1);
364
}
365
if (opts.rifindex == 0)
366
opts.rifindex = ifindex;
367
sdl_m.sdl_alen = ETHER_ADDR_LEN;
368
} else {
369
struct ether_addr *ea1 = ether_aton(eaddr);
370
371
if (ea1 == NULL) {
372
xo_warnx("invalid Ethernet address '%s'", eaddr);
373
return (1);
374
} else {
375
*ea = *ea1;
376
sdl_m.sdl_alen = ETHER_ADDR_LEN;
377
}
378
}
379
#ifndef WITHOUT_NETLINK
380
return (set_nl(dst, &sdl_m, host));
381
#else
382
return (set_rtsock(dst, &sdl_m, host));
383
#endif
384
}
385
386
#ifdef WITHOUT_NETLINK
387
static int
388
set_rtsock(struct sockaddr_in *dst, struct sockaddr_dl *sdl_m, char *host)
389
{
390
struct sockaddr_in *addr;
391
struct sockaddr_dl *sdl;
392
struct rt_msghdr *rtm;
393
394
/*
395
* In the case a proxy-arp entry is being added for
396
* a remote end point, the RTF_ANNOUNCE flag in the
397
* RTM_GET command is an indication to the kernel
398
* routing code that the interface associated with
399
* the prefix route covering the local end of the
400
* PPP link should be returned, on which ARP applies.
401
*/
402
rtm = rtmsg(RTM_GET, dst, NULL);
403
if (rtm == NULL) {
404
xo_warn("%s", host);
405
return (1);
406
}
407
addr = (struct sockaddr_in *)(rtm + 1);
408
sdl = (struct sockaddr_dl *)(SA_SIZE(addr) + (char *)addr);
409
410
if ((sdl->sdl_family != AF_LINK) ||
411
(rtm->rtm_flags & RTF_GATEWAY) ||
412
!valid_type(sdl->sdl_type)) {
413
xo_warnx("cannot intuit interface index and type for %s", host);
414
return (1);
415
}
416
sdl_m->sdl_type = sdl->sdl_type;
417
sdl_m->sdl_index = sdl->sdl_index;
418
return (rtmsg(RTM_ADD, dst, sdl_m) == NULL);
419
}
420
#endif
421
422
/*
423
* Display an individual arp entry
424
*/
425
static int
426
get(char *host)
427
{
428
struct sockaddr_in *addr;
429
int found;
430
431
addr = getaddr(host);
432
if (addr == NULL)
433
return (1);
434
435
xo_set_version(ARP_XO_VERSION);
436
xo_open_container("arp");
437
xo_open_list("arp-cache");
438
439
found = print_entries(opts.rifindex, addr->sin_addr);
440
441
if (found == 0) {
442
xo_emit("{d:hostname/%s} ({d:ip-address/%s}) -- no entry",
443
host, inet_ntoa(addr->sin_addr));
444
if (opts.rifname)
445
xo_emit(" on {d:interface/%s}", opts.rifname);
446
xo_emit("\n");
447
}
448
449
xo_close_list("arp-cache");
450
xo_close_container("arp");
451
if (xo_finish() < 0)
452
xo_err(1, "stdout");
453
454
return (found == 0);
455
}
456
457
/*
458
* Delete an arp entry
459
*/
460
#ifdef WITHOUT_NETLINK
461
static int
462
delete_rtsock(char *host)
463
{
464
struct sockaddr_in *addr, *dst;
465
struct rt_msghdr *rtm;
466
struct sockaddr_dl *sdl;
467
468
dst = getaddr(host);
469
if (dst == NULL)
470
return (1);
471
472
/*
473
* Perform a regular entry delete first.
474
*/
475
opts.flags &= ~RTF_ANNOUNCE;
476
477
for (;;) { /* try twice */
478
rtm = rtmsg(RTM_GET, dst, NULL);
479
if (rtm == NULL) {
480
xo_warn("%s", host);
481
return (1);
482
}
483
addr = (struct sockaddr_in *)(rtm + 1);
484
sdl = (struct sockaddr_dl *)(SA_SIZE(addr) + (char *)addr);
485
486
/*
487
* With the new L2/L3 restructure, the route
488
* returned is a prefix route. The important
489
* piece of information from the previous
490
* RTM_GET is the interface index. In the
491
* case of ECMP, the kernel will traverse
492
* the route group for the given entry.
493
*/
494
if (sdl->sdl_family == AF_LINK &&
495
!(rtm->rtm_flags & RTF_GATEWAY) &&
496
valid_type(sdl->sdl_type) ) {
497
addr->sin_addr.s_addr = dst->sin_addr.s_addr;
498
break;
499
}
500
501
/*
502
* Regular entry delete failed, now check if there
503
* is a proxy-arp entry to remove.
504
*/
505
if (opts.flags & RTF_ANNOUNCE) {
506
xo_warnx("delete: cannot locate %s", host);
507
return (1);
508
}
509
510
opts.flags |= RTF_ANNOUNCE;
511
}
512
rtm->rtm_flags |= RTF_LLDATA;
513
if (rtmsg(RTM_DELETE, dst, NULL) != NULL) {
514
printf("%s (%s) deleted\n", host, inet_ntoa(addr->sin_addr));
515
return (0);
516
}
517
return (1);
518
}
519
#endif
520
521
static int
522
delete(char *host)
523
{
524
#ifdef WITHOUT_NETLINK
525
return (delete_rtsock(host));
526
#else
527
return (delete_nl(host));
528
#endif
529
}
530
531
532
/*
533
* Search the arp table and do some action on matching entries
534
*/
535
static int
536
search(u_long addr, action_fn *action)
537
{
538
int mib[6];
539
size_t needed;
540
char *lim, *buf, *next;
541
struct rt_msghdr *rtm;
542
struct sockaddr_in *sin2;
543
struct sockaddr_dl *sdl;
544
int st, found_entry = 0;
545
546
mib[0] = CTL_NET;
547
mib[1] = PF_ROUTE;
548
mib[2] = 0;
549
mib[3] = AF_INET;
550
mib[4] = NET_RT_FLAGS;
551
#ifdef RTF_LLINFO
552
mib[5] = RTF_LLINFO;
553
#else
554
mib[5] = 0;
555
#endif
556
if (sysctl(mib, 6, NULL, &needed, NULL, 0) < 0)
557
xo_err(1, "route-sysctl-estimate");
558
if (needed == 0) /* empty table */
559
return 0;
560
buf = NULL;
561
for (;;) {
562
buf = reallocf(buf, needed);
563
if (buf == NULL)
564
xo_errx(1, "could not reallocate memory");
565
st = sysctl(mib, 6, buf, &needed, NULL, 0);
566
if (st == 0 || errno != ENOMEM)
567
break;
568
needed += needed / 8;
569
}
570
if (st == -1)
571
xo_err(1, "actual retrieval of routing table");
572
lim = buf + needed;
573
for (next = buf; next < lim; next += rtm->rtm_msglen) {
574
rtm = (struct rt_msghdr *)next;
575
sin2 = (struct sockaddr_in *)(rtm + 1);
576
sdl = (struct sockaddr_dl *)((char *)sin2 + SA_SIZE(sin2));
577
if (opts.rifindex &&
578
(opts.rifindex != sdl->sdl_index))
579
continue;
580
if (addr &&
581
(addr != sin2->sin_addr.s_addr))
582
continue;
583
found_entry = 1;
584
(*action)(sdl, sin2, rtm);
585
}
586
free(buf);
587
return (found_entry);
588
}
589
590
/*
591
* Display an arp entry
592
*/
593
594
static void
595
print_entry(struct sockaddr_dl *sdl,
596
struct sockaddr_in *addr, struct rt_msghdr *rtm)
597
{
598
const char *host;
599
struct hostent *hp;
600
struct if_nameindex *p;
601
602
if (ifnameindex == NULL)
603
if ((ifnameindex = if_nameindex()) == NULL)
604
xo_err(1, "cannot retrieve interface names");
605
606
xo_open_instance("arp-cache");
607
608
if (!opts.nflag)
609
hp = gethostbyaddr((caddr_t)&(addr->sin_addr),
610
sizeof addr->sin_addr, AF_INET);
611
else
612
hp = 0;
613
if (hp)
614
host = hp->h_name;
615
else {
616
host = "?";
617
if (h_errno == TRY_AGAIN)
618
opts.nflag = true;
619
}
620
xo_emit("{:hostname/%s} ({:ip-address/%s}) at ", host,
621
inet_ntoa(addr->sin_addr));
622
if (sdl->sdl_alen) {
623
if ((sdl->sdl_type == IFT_ETHER ||
624
sdl->sdl_type == IFT_L2VLAN ||
625
sdl->sdl_type == IFT_BRIDGE) &&
626
sdl->sdl_alen == ETHER_ADDR_LEN)
627
xo_emit("{:mac-address/%s}",
628
ether_ntoa((struct ether_addr *)LLADDR(sdl)));
629
else {
630
int n = sdl->sdl_nlen > 0 ? sdl->sdl_nlen + 1 : 0;
631
632
xo_emit("{:mac-address/%s}", link_ntoa(sdl) + n);
633
}
634
} else
635
xo_emit("{d:/(incomplete)}{en:incomplete/true}");
636
637
for (p = ifnameindex; p && p->if_index && p->if_name; p++) {
638
if (p->if_index == sdl->sdl_index) {
639
xo_emit(" on {:interface/%s}", p->if_name);
640
break;
641
}
642
}
643
644
if (rtm->rtm_rmx.rmx_expire == 0)
645
xo_emit("{d:/ permanent}{en:permanent/true}");
646
else {
647
static struct timespec tp;
648
time_t expire_time = 0;
649
650
if (tp.tv_sec == 0)
651
clock_gettime(CLOCK_MONOTONIC, &tp);
652
if ((expire_time = rtm->rtm_rmx.rmx_expire - tp.tv_sec) > 0)
653
xo_emit(" expires in {:expires/%d} seconds",
654
(int)expire_time);
655
else
656
xo_emit("{d:/ expired}{en:expired/true}");
657
}
658
659
if (rtm->rtm_flags & RTF_ANNOUNCE)
660
xo_emit("{d:/ published}{en:published/true}");
661
662
switch(sdl->sdl_type) {
663
case IFT_ETHER:
664
xo_emit(" [{:type/ethernet}]");
665
break;
666
case IFT_FDDI:
667
xo_emit(" [{:type/fddi}]");
668
break;
669
case IFT_ATM:
670
xo_emit(" [{:type/atm}]");
671
break;
672
case IFT_L2VLAN:
673
xo_emit(" [{:type/vlan}]");
674
break;
675
case IFT_IEEE1394:
676
xo_emit(" [{:type/firewire}]");
677
break;
678
case IFT_BRIDGE:
679
xo_emit(" [{:type/bridge}]");
680
break;
681
case IFT_INFINIBAND:
682
xo_emit(" [{:type/infiniband}]");
683
break;
684
default:
685
break;
686
}
687
688
xo_emit("\n");
689
690
xo_close_instance("arp-cache");
691
}
692
693
static int
694
print_entries(uint32_t ifindex, struct in_addr addr)
695
{
696
#ifndef WITHOUT_NETLINK
697
return (print_entries_nl(ifindex, addr));
698
#else
699
return (search(addr.s_addr, print_entry));
700
#endif
701
}
702
703
704
/*
705
* Nuke an arp entry
706
*/
707
static void
708
nuke_entry(struct sockaddr_dl *sdl __unused,
709
struct sockaddr_in *addr, struct rt_msghdr *rtm)
710
{
711
char ip[20];
712
713
if (rtm->rtm_flags & RTF_PINNED)
714
return;
715
716
snprintf(ip, sizeof(ip), "%s", inet_ntoa(addr->sin_addr));
717
delete(ip);
718
}
719
720
static void
721
nuke_entries(uint32_t ifindex, struct in_addr addr)
722
{
723
search(addr.s_addr, nuke_entry);
724
}
725
726
static void
727
usage(void)
728
{
729
xo_error("%s\n%s\n%s\n%s\n%s\n%s\n%s\n",
730
"usage: arp [-n] [-i interface] hostname",
731
" arp [-n] [-i interface] -a",
732
" arp -d hostname [pub]",
733
" arp -d [-i interface] -a",
734
" arp -s hostname ether_addr [temp] [reject | blackhole] [pub [only]]",
735
" arp -S hostname ether_addr [temp] [reject | blackhole] [pub [only]]",
736
" arp -f filename");
737
exit(1);
738
}
739
740
static struct rt_msghdr *
741
rtmsg(int cmd, struct sockaddr_in *dst, struct sockaddr_dl *sdl)
742
{
743
static int seq;
744
int rlen;
745
int l;
746
static int s = -1;
747
static pid_t pid;
748
749
static struct {
750
struct rt_msghdr m_rtm;
751
char m_space[512];
752
} m_rtmsg;
753
754
struct rt_msghdr *rtm = &m_rtmsg.m_rtm;
755
char *cp = m_rtmsg.m_space;
756
757
if (s < 0) { /* first time: open socket, get pid */
758
s = socket(PF_ROUTE, SOCK_RAW, 0);
759
if (s < 0)
760
xo_err(1, "socket");
761
pid = getpid();
762
}
763
764
errno = 0;
765
/*
766
* XXX RTM_DELETE relies on a previous RTM_GET to fill the buffer
767
* appropriately.
768
*/
769
if (cmd == RTM_DELETE)
770
goto doit;
771
bzero((char *)&m_rtmsg, sizeof(m_rtmsg));
772
rtm->rtm_flags = opts.flags;
773
rtm->rtm_version = RTM_VERSION;
774
775
switch (cmd) {
776
default:
777
xo_errx(1, "internal wrong cmd");
778
case RTM_ADD:
779
rtm->rtm_addrs |= RTA_GATEWAY;
780
if (opts.expire_time != 0) {
781
struct timespec tp;
782
783
clock_gettime(CLOCK_MONOTONIC, &tp);
784
rtm->rtm_rmx.rmx_expire = opts.expire_time + tp.tv_sec;
785
}
786
rtm->rtm_inits = RTV_EXPIRE;
787
rtm->rtm_flags |= (RTF_HOST | RTF_STATIC | RTF_LLDATA);
788
/* FALLTHROUGH */
789
case RTM_GET:
790
rtm->rtm_addrs |= RTA_DST;
791
}
792
#define NEXTADDR(w, s) \
793
do { \
794
if ((s) != NULL && rtm->rtm_addrs & (w)) { \
795
bcopy((s), cp, sizeof(*(s))); \
796
cp += SA_SIZE(s); \
797
} \
798
} while (0)
799
800
NEXTADDR(RTA_DST, dst);
801
NEXTADDR(RTA_GATEWAY, sdl);
802
803
rtm->rtm_msglen = cp - (char *)&m_rtmsg;
804
doit:
805
l = rtm->rtm_msglen;
806
rtm->rtm_seq = ++seq;
807
rtm->rtm_type = cmd;
808
if ((rlen = write(s, (char *)&m_rtmsg, l)) < 0) {
809
if (errno != ESRCH || cmd != RTM_DELETE) {
810
xo_warn("writing to routing socket");
811
return (NULL);
812
}
813
}
814
do {
815
l = read(s, (char *)&m_rtmsg, sizeof(m_rtmsg));
816
} while (l > 0 && (rtm->rtm_type != cmd || rtm->rtm_seq != seq ||
817
rtm->rtm_pid != pid));
818
if (l < 0)
819
xo_warn("read from routing socket");
820
return (rtm);
821
}
822
823
/*
824
* get_ifinfo - get the hardware address and if_index of an interface
825
* on the same subnet as ipaddr.
826
*/
827
int
828
get_ifinfo(in_addr_t ipaddr, struct ether_addr *hwaddr, uint32_t *pifindex)
829
{
830
struct ifaddrs *ifa, *ifd, *ifas = NULL;
831
in_addr_t ina, mask;
832
struct sockaddr_dl *dla;
833
int retval = 0;
834
835
/*
836
* Scan through looking for an interface with an Internet
837
* address on the same subnet as `ipaddr'.
838
*/
839
if (getifaddrs(&ifas) < 0) {
840
xo_warnx("getifaddrs");
841
goto done;
842
}
843
844
for (ifa = ifas; ifa != NULL; ifa = ifa->ifa_next) {
845
if (ifa->ifa_addr == NULL || ifa->ifa_netmask == NULL)
846
continue;
847
if (ifa->ifa_addr->sa_family != AF_INET)
848
continue;
849
/*
850
* Check that the interface is up,
851
* and not point-to-point or loopback.
852
*/
853
if ((ifa->ifa_flags &
854
(IFF_UP|IFF_BROADCAST|IFF_POINTOPOINT|
855
IFF_LOOPBACK|IFF_NOARP)) != (IFF_UP|IFF_BROADCAST))
856
continue;
857
/* Get its netmask and check that it's on the right subnet. */
858
mask = ((struct sockaddr_in *)
859
ifa->ifa_netmask)->sin_addr.s_addr;
860
ina = ((struct sockaddr_in *)
861
ifa->ifa_addr)->sin_addr.s_addr;
862
if ((ipaddr & mask) == (ina & mask))
863
break; /* ok, we got it! */
864
}
865
if (ifa == NULL)
866
goto done;
867
if (pifindex != NULL)
868
*pifindex = if_nametoindex(ifa->ifa_name);
869
if (hwaddr == NULL) {
870
/* ether addr is not required */
871
retval = ETHER_ADDR_LEN;
872
goto done;
873
}
874
/*
875
* Now scan through again looking for a link-level address
876
* for this interface.
877
*/
878
for (ifd = ifas; ifd != NULL; ifd = ifd->ifa_next) {
879
if (ifd->ifa_addr == NULL)
880
continue;
881
if (strcmp(ifa->ifa_name, ifd->ifa_name) == 0 &&
882
ifd->ifa_addr->sa_family == AF_LINK)
883
break;
884
}
885
if (ifd == NULL)
886
goto done;
887
/*
888
* Found the link-level address - copy it out
889
*/
890
dla = (struct sockaddr_dl *)ifd->ifa_addr;
891
memcpy(hwaddr, LLADDR(dla), dla->sdl_alen);
892
printf("using interface %s for proxy with address %s\n", ifa->ifa_name,
893
ether_ntoa(hwaddr));
894
retval = dla->sdl_alen;
895
done:
896
if (ifas != NULL)
897
freeifaddrs(ifas);
898
return (retval);
899
}
900
901