source: rtems-libbsd/freebsd/usr.sbin/arp/arp.c @ 246b61e

55-freebsd-126-freebsd-12
Last change on this file since 246b61e was 246b61e, checked in by Sebastian Huber <sebastian.huber@…>, on 01/10/17 at 06:58:50

ARP(8): Import from FreeBSD

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