Logo Search packages:      
Sourcecode: freebsd-utils version File versions

route.c

/*
 * Copyright (c) 1983, 1989, 1991, 1993
 *    The Regents of the University of California.  All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 * 4. Neither the name of the University nor the names of its contributors
 *    may be used to endorse or promote products derived from this software
 *    without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 */

#ifndef lint
static const char copyright[] =
"@(#) Copyright (c) 1983, 1989, 1991, 1993\n\
      The Regents of the University of California.  All rights reserved.\n";
#endif /* not lint */

#ifndef lint
#if 0
static char sccsid[] = "@(#)route.c 8.6 (Berkeley) 4/28/95";
#endif
static const char rcsid[] =
  "$FreeBSD: src/sbin/route/route.c,v 1.81.2.3.2.1 2008/11/25 02:59:29 kensmith Exp $";
#endif /* not lint */

#include <sys/param.h>
#include <sys/file.h>
#include <sys/socket.h>
#include <sys/ioctl.h>
#include <sys/sysctl.h>
#include <sys/types.h>

#include <net/if.h>
#include <net/route.h>
#include <net/if_dl.h>
#include <netinet/in.h>
#include <netinet/if_ether.h>
#include <netatalk/at.h>
#include <arpa/inet.h>
#include <netdb.h>

#include <ctype.h>
#include <err.h>
#include <errno.h>
#include <paths.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sysexits.h>
#include <unistd.h>
#include <ifaddrs.h>

struct keytab {
      char  *kt_cp;
      int   kt_i;
} keywords[] = {
#include "keywords.h"
      {0, 0}
};

struct      ortentry route;
union sockunion {
      struct      sockaddr sa;
      struct      sockaddr_in sin;
#ifdef INET6
      struct      sockaddr_in6 sin6;
#endif
      struct      sockaddr_at sat;
      struct      sockaddr_dl sdl;
      struct      sockaddr_inarp sinarp;
      struct      sockaddr_storage ss; /* added to avoid memory overrun */
} so_dst, so_gate, so_mask, so_genmask, so_ifa, so_ifp;

typedef union sockunion *sup;
int   pid, rtm_addrs;
int   s;
int   forcehost, forcenet, doflush, nflag, af, qflag, tflag, keyword();
int   iflag, verbose, aflen = sizeof (struct sockaddr_in);
int   locking, lockrest, debugonly;
struct      rt_metrics rt_metrics;
u_long  rtm_inits;
uid_t uid;
int   atalk_aton(const char *, struct at_addr *);
char  *atalk_ntoa(struct at_addr);
const char  *routename(), *netname();
void  flushroutes(), newroute(), monitor(), sockaddr(), sodump(), bprintf();
void  print_getmsg(), print_rtmsg(), pmsg_common(), pmsg_addrs(), mask_addr();
#ifdef INET6
static int inet6_makenetandmask(struct sockaddr_in6 *, char *);
#endif
int   getaddr(), rtmsg(), x25_makemask();
int   prefixlen();
extern      char *iso_ntoa();

void usage(const char *) __dead2;

void
usage(cp)
      const char *cp;
{
      if (cp)
            warnx("bad keyword: %s", cp);
      (void) fprintf(stderr,
          "usage: route [-dnqtv] command [[modifiers] args]\n");
      exit(EX_USAGE);
      /* NOTREACHED */
}

int
main(argc, argv)
      int argc;
      char **argv;
{
      int ch;

      if (argc < 2)
            usage((char *)NULL);

      while ((ch = getopt(argc, argv, "nqdtv")) != -1)
            switch(ch) {
            case 'n':
                  nflag = 1;
                  break;
            case 'q':
                  qflag = 1;
                  break;
            case 'v':
                  verbose = 1;
                  break;
            case 't':
                  tflag = 1;
                  break;
            case 'd':
                  debugonly = 1;
                  break;
            case '?':
            default:
                  usage((char *)NULL);
            }
      argc -= optind;
      argv += optind;

      pid = getpid();
      uid = geteuid();
      if (tflag)
            s = open(_PATH_DEVNULL, O_WRONLY, 0);
      else
            s = socket(PF_ROUTE, SOCK_RAW, 0);
      if (s < 0)
            err(EX_OSERR, "socket");
      if (*argv)
            switch (keyword(*argv)) {
            case K_GET:
                  uid = 0;
                  /* FALLTHROUGH */

            case K_CHANGE:
            case K_ADD:
            case K_DEL:
            case K_DELETE:
                  newroute(argc, argv);
                  /* NOTREACHED */

            case K_MONITOR:
                  monitor();
                  /* NOTREACHED */

            case K_FLUSH:
                  flushroutes(argc, argv);
                  exit(0);
                  /* NOTREACHED */
            }
      usage(*argv);
      /* NOTREACHED */
}

/*
 * Purge all entries in the routing tables not
 * associated with network interfaces.
 */
void
flushroutes(argc, argv)
      int argc;
      char *argv[];
{
      size_t needed;
      int mib[6], rlen, seqno, count = 0;
      char *buf, *next, *lim;
      struct rt_msghdr *rtm;

      if (uid && !debugonly) {
            errx(EX_NOPERM, "must be root to alter routing table");
      }
      shutdown(s, SHUT_RD); /* Don't want to read back our messages */
      if (argc > 1) {
            argv++;
            if (argc == 2 && **argv == '-')
                switch (keyword(*argv + 1)) {
                  case K_INET:
                        af = AF_INET;
                        break;
#ifdef INET6
                  case K_INET6:
                        af = AF_INET6;
                        break;
#endif
                  case K_ATALK:
                        af = AF_APPLETALK;
                        break;
                  case K_LINK:
                        af = AF_LINK;
                        break;
                  default:
                        goto bad;
            } else
bad:              usage(*argv);
      }
retry:
      mib[0] = CTL_NET;
      mib[1] = PF_ROUTE;
      mib[2] = 0;       /* protocol */
      mib[3] = 0;       /* wildcard address family */
      mib[4] = NET_RT_DUMP;
      mib[5] = 0;       /* no flags */
      if (sysctl(mib, 6, NULL, &needed, NULL, 0) < 0)
            err(EX_OSERR, "route-sysctl-estimate");
      if ((buf = malloc(needed)) == NULL)
            errx(EX_OSERR, "malloc failed");
      if (sysctl(mib, 6, buf, &needed, NULL, 0) < 0) {
            if (errno == ENOMEM && count++ < 10) {
                  warnx("Routing table grew, retrying");  
                  sleep(1);
                  free(buf);
                  goto retry;
            }
            err(EX_OSERR, "route-sysctl-get");
      }
      lim = buf + needed;
      if (verbose)
            (void) printf("Examining routing table from sysctl\n");
      seqno = 0;        /* ??? */
      for (next = buf; next < lim; next += rtm->rtm_msglen) {
            rtm = (struct rt_msghdr *)next;
            if (verbose)
                  print_rtmsg(rtm, rtm->rtm_msglen);
            if ((rtm->rtm_flags & RTF_GATEWAY) == 0)
                  continue;
            if (af) {
                  struct sockaddr *sa = (struct sockaddr *)(rtm + 1);

                  if (sa->sa_family != af)
                        continue;
            }
            if (debugonly)
                  continue;
            rtm->rtm_type = RTM_DELETE;
            rtm->rtm_seq = seqno;
            rlen = write(s, next, rtm->rtm_msglen);
            if (rlen < 0 && errno == EPERM)
                  err(1, "write to routing socket");
            if (rlen < (int)rtm->rtm_msglen) {
                  warn("write to routing socket");
                  (void) printf("got only %d for rlen\n", rlen);
                  free(buf);
                  goto retry;
                  break;
            }
            seqno++;
            if (qflag)
                  continue;
            if (verbose)
                  print_rtmsg(rtm, rlen);
            else {
                  struct sockaddr *sa = (struct sockaddr *)(rtm + 1);
                  (void) printf("%-20.20s ", rtm->rtm_flags & RTF_HOST ?
                      routename(sa) : netname(sa));
                  sa = (struct sockaddr *)(SA_SIZE(sa) + (char *)sa);
                  (void) printf("%-20.20s ", routename(sa));
                  (void) printf("done\n");
            }
      }
}

const char *
routename(sa)
      struct sockaddr *sa;
{
      char *cp;
      static char line[MAXHOSTNAMELEN + 1];
      struct hostent *hp;
      static char domain[MAXHOSTNAMELEN + 1];
      static int first = 1, n;

      if (first) {
            first = 0;
            if (gethostname(domain, MAXHOSTNAMELEN) == 0 &&
                (cp = strchr(domain, '.'))) {
                  domain[MAXHOSTNAMELEN] = '\0';
                  (void) strcpy(domain, cp + 1);
            } else
                  domain[0] = 0;
      }

      if (sa->sa_len == 0)
            strcpy(line, "default");
      else switch (sa->sa_family) {

      case AF_INET:
          { struct in_addr in;
            in = ((struct sockaddr_in *)sa)->sin_addr;

            cp = 0;
            if (in.s_addr == INADDR_ANY || sa->sa_len < 4)
                  cp = "default";
            if (cp == 0 && !nflag) {
                  hp = gethostbyaddr((char *)&in, sizeof (struct in_addr),
                        AF_INET);
                  if (hp) {
                        if ((cp = strchr(hp->h_name, '.')) &&
                            !strcmp(cp + 1, domain))
                              *cp = 0;
                        cp = hp->h_name;
                  }
            }
            if (cp) {
                  strncpy(line, cp, sizeof(line) - 1);
                  line[sizeof(line) - 1] = '\0';
            } else
                  (void) sprintf(line, "%s", inet_ntoa(in));
            break;
          }

#ifdef INET6
      case AF_INET6:
      {
            struct sockaddr_in6 sin6; /* use static var for safety */
            int niflags = 0;

            memset(&sin6, 0, sizeof(sin6));
            memcpy(&sin6, sa, sa->sa_len);
            sin6.sin6_len = sizeof(struct sockaddr_in6);
            sin6.sin6_family = AF_INET6;
#ifdef __KAME__
            if (sa->sa_len == sizeof(struct sockaddr_in6) &&
                (IN6_IS_ADDR_LINKLOCAL(&sin6.sin6_addr) ||
                 IN6_IS_ADDR_MC_LINKLOCAL(&sin6.sin6_addr)) &&
                sin6.sin6_scope_id == 0) {
                  sin6.sin6_scope_id =
                      ntohs(*(u_int16_t *)&sin6.sin6_addr.s6_addr[2]);
                  sin6.sin6_addr.s6_addr[2] = 0;
                  sin6.sin6_addr.s6_addr[3] = 0;
            }
#endif
            if (nflag)
                  niflags |= NI_NUMERICHOST;
            if (getnameinfo((struct sockaddr *)&sin6, sin6.sin6_len,
                line, sizeof(line), NULL, 0, niflags) != 0)
                  strncpy(line, "invalid", sizeof(line));

            return(line);
      }
#endif

      case AF_APPLETALK:
            (void) snprintf(line, sizeof(line), "atalk %s",
                  atalk_ntoa(((struct sockaddr_at *)sa)->sat_addr));
            break;

      case AF_LINK:
            return (link_ntoa((struct sockaddr_dl *)sa));

      default:
          { u_short *s = (u_short *)sa;
            u_short *slim = s + ((sa->sa_len + 1) >> 1);
            char *cp = line + sprintf(line, "(%d)", sa->sa_family);
            char *cpe = line + sizeof(line);

            while (++s < slim && cp < cpe) /* start with sa->sa_data */
                  if ((n = snprintf(cp, cpe - cp, " %x", *s)) > 0)
                        cp += n;
                  else
                        *cp = '\0';
            break;
          }
      }
      return (line);
}

/*
 * Return the name of the network whose address is given.
 * The address is assumed to be that of a net or subnet, not a host.
 */
const char *
netname(sa)
      struct sockaddr *sa;
{
      char *cp = 0;
      static char line[MAXHOSTNAMELEN + 1];
      struct netent *np = 0;
      u_long net, mask;
      u_long i;
      int n, subnetshift;

      switch (sa->sa_family) {

      case AF_INET:
          { struct in_addr in;
            in = ((struct sockaddr_in *)sa)->sin_addr;

            i = in.s_addr = ntohl(in.s_addr);
            if (in.s_addr == 0)
                  cp = "default";
            else if (!nflag) {
                  if (IN_CLASSA(i)) {
                        mask = IN_CLASSA_NET;
                        subnetshift = 8;
                  } else if (IN_CLASSB(i)) {
                        mask = IN_CLASSB_NET;
                        subnetshift = 8;
                  } else {
                        mask = IN_CLASSC_NET;
                        subnetshift = 4;
                  }
                  /*
                   * If there are more bits than the standard mask
                   * would suggest, subnets must be in use.
                   * Guess at the subnet mask, assuming reasonable
                   * width subnet fields.
                   */
                  while (in.s_addr &~ mask)
                        mask = (long)mask >> subnetshift;
                  net = in.s_addr & mask;
                  while ((mask & 1) == 0)
                        mask >>= 1, net >>= 1;
                  np = getnetbyaddr(net, AF_INET);
                  if (np)
                        cp = np->n_name;
            }
#define C(x)      (unsigned)((x) & 0xff)
            if (cp)
                  strncpy(line, cp, sizeof(line));
            else if ((in.s_addr & 0xffffff) == 0)
                  (void) sprintf(line, "%u", C(in.s_addr >> 24));
            else if ((in.s_addr & 0xffff) == 0)
                  (void) sprintf(line, "%u.%u", C(in.s_addr >> 24),
                      C(in.s_addr >> 16));
            else if ((in.s_addr & 0xff) == 0)
                  (void) sprintf(line, "%u.%u.%u", C(in.s_addr >> 24),
                      C(in.s_addr >> 16), C(in.s_addr >> 8));
            else
                  (void) sprintf(line, "%u.%u.%u.%u", C(in.s_addr >> 24),
                      C(in.s_addr >> 16), C(in.s_addr >> 8),
                      C(in.s_addr));
#undef C
            break;
          }

#ifdef INET6
      case AF_INET6:
      {
            struct sockaddr_in6 sin6; /* use static var for safety */
            int niflags = 0;

            memset(&sin6, 0, sizeof(sin6));
            memcpy(&sin6, sa, sa->sa_len);
            sin6.sin6_len = sizeof(struct sockaddr_in6);
            sin6.sin6_family = AF_INET6;
#ifdef __KAME__
            if (sa->sa_len == sizeof(struct sockaddr_in6) &&
                (IN6_IS_ADDR_LINKLOCAL(&sin6.sin6_addr) ||
                 IN6_IS_ADDR_MC_LINKLOCAL(&sin6.sin6_addr)) &&
                sin6.sin6_scope_id == 0) {
                  sin6.sin6_scope_id =
                      ntohs(*(u_int16_t *)&sin6.sin6_addr.s6_addr[2]);
                  sin6.sin6_addr.s6_addr[2] = 0;
                  sin6.sin6_addr.s6_addr[3] = 0;
            }
#endif
            if (nflag)
                  niflags |= NI_NUMERICHOST;
            if (getnameinfo((struct sockaddr *)&sin6, sin6.sin6_len,
                line, sizeof(line), NULL, 0, niflags) != 0)
                  strncpy(line, "invalid", sizeof(line));

            return(line);
      }
#endif

      case AF_APPLETALK:
            (void) snprintf(line, sizeof(line), "atalk %s",
                  atalk_ntoa(((struct sockaddr_at *)sa)->sat_addr));
            break;

      case AF_LINK:
            return (link_ntoa((struct sockaddr_dl *)sa));


      default:
          { u_short *s = (u_short *)sa->sa_data;
            u_short *slim = s + ((sa->sa_len + 1)>>1);
            char *cp = line + sprintf(line, "af %d:", sa->sa_family);
            char *cpe = line + sizeof(line);

            while (s < slim && cp < cpe)
                  if ((n = snprintf(cp, cpe - cp, " %x", *s++)) > 0)
                        cp += n;
                  else
                        *cp = '\0';
            break;
          }
      }
      return (line);
}

void
set_metric(value, key)
      char *value;
      int key;
{
      int flag = 0;
      u_long noval, *valp = &noval;

      switch (key) {
#define caseof(x, y, z) case x: valp = &rt_metrics.z; flag = y; break
      caseof(K_MTU, RTV_MTU, rmx_mtu);
      caseof(K_HOPCOUNT, RTV_HOPCOUNT, rmx_hopcount);
      caseof(K_EXPIRE, RTV_EXPIRE, rmx_expire);
      caseof(K_RECVPIPE, RTV_RPIPE, rmx_recvpipe);
      caseof(K_SENDPIPE, RTV_SPIPE, rmx_sendpipe);
      caseof(K_SSTHRESH, RTV_SSTHRESH, rmx_ssthresh);
      caseof(K_RTT, RTV_RTT, rmx_rtt);
      caseof(K_RTTVAR, RTV_RTTVAR, rmx_rttvar);
      }
      rtm_inits |= flag;
      if (lockrest || locking)
            rt_metrics.rmx_locks |= flag;
      if (locking)
            locking = 0;
      *valp = atoi(value);
}

void
newroute(argc, argv)
      int argc;
      char **argv;
{
      char *cmd, *dest = "", *gateway = "", *err;
      int ishost = 0, proxy = 0, ret, attempts, oerrno, flags = RTF_STATIC;
      int key;
      struct hostent *hp = 0;

      if (uid) {
            errx(EX_NOPERM, "must be root to alter routing table");
      }
      cmd = argv[0];
      if (*cmd != 'g')
            shutdown(s, SHUT_RD); /* Don't want to read back our messages */
      while (--argc > 0) {
            if (**(++argv)== '-') {
                  switch (key = keyword(1 + *argv)) {
                  case K_LINK:
                        af = AF_LINK;
                        aflen = sizeof(struct sockaddr_dl);
                        break;
                  case K_INET:
                        af = AF_INET;
                        aflen = sizeof(struct sockaddr_in);
                        break;
#ifdef INET6
                  case K_INET6:
                        af = AF_INET6;
                        aflen = sizeof(struct sockaddr_in6);
                        break;
#endif
                  case K_ATALK:
                        af = AF_APPLETALK;
                        aflen = sizeof(struct sockaddr_at);
                        break;
                  case K_SA:
                        af = PF_ROUTE;
                        aflen = sizeof(union sockunion);
                        break;
                  case K_IFACE:
                  case K_INTERFACE:
                        iflag++;
                        break;
                  case K_NOSTATIC:
                        flags &= ~RTF_STATIC;
                        break;
                  case K_LLINFO:
                        flags |= RTF_LLINFO;
                        break;
                  case K_LOCK:
                        locking = 1;
                        break;
                  case K_LOCKREST:
                        lockrest = 1;
                        break;
                  case K_HOST:
                        forcehost++;
                        break;
                  case K_REJECT:
                        flags |= RTF_REJECT;
                        break;
                  case K_BLACKHOLE:
                        flags |= RTF_BLACKHOLE;
                        break;
                  case K_PROTO1:
                        flags |= RTF_PROTO1;
                        break;
                  case K_PROTO2:
                        flags |= RTF_PROTO2;
                        break;
                  case K_PROXY:
                        proxy = 1;
                        break;
                  case K_CLONING:
                        flags |= RTF_CLONING;
                        break;
                  case K_XRESOLVE:
                        flags |= RTF_XRESOLVE;
                        break;
                  case K_STATIC:
                        flags |= RTF_STATIC;
                        break;
                  case K_IFA:
                        if (!--argc)
                              usage((char *)NULL);
                        (void) getaddr(RTA_IFA, *++argv, 0);
                        break;
                  case K_IFP:
                        if (!--argc)
                              usage((char *)NULL);
                        (void) getaddr(RTA_IFP, *++argv, 0);
                        break;
                  case K_GENMASK:
                        if (!--argc)
                              usage((char *)NULL);
                        (void) getaddr(RTA_GENMASK, *++argv, 0);
                        break;
                  case K_GATEWAY:
                        if (!--argc)
                              usage((char *)NULL);
                        (void) getaddr(RTA_GATEWAY, *++argv, 0);
                        break;
                  case K_DST:
                        if (!--argc)
                              usage((char *)NULL);
                        ishost = getaddr(RTA_DST, *++argv, &hp);
                        dest = *argv;
                        break;
                  case K_NETMASK:
                        if (!--argc)
                              usage((char *)NULL);
                        (void) getaddr(RTA_NETMASK, *++argv, 0);
                        /* FALLTHROUGH */
                  case K_NET:
                        forcenet++;
                        break;
                  case K_PREFIXLEN:
                        if (!--argc)
                              usage((char *)NULL);
                        if (prefixlen(*++argv) == -1) {
                              forcenet = 0;
                              ishost = 1;
                        } else {
                              forcenet = 1;
                              ishost = 0;
                        }
                        break;
                  case K_MTU:
                  case K_HOPCOUNT:
                  case K_EXPIRE:
                  case K_RECVPIPE:
                  case K_SENDPIPE:
                  case K_SSTHRESH:
                  case K_RTT:
                  case K_RTTVAR:
                        if (!--argc)
                              usage((char *)NULL);
                        set_metric(*++argv, key);
                        break;
                  default:
                        usage(1+*argv);
                  }
            } else {
                  if ((rtm_addrs & RTA_DST) == 0) {
                        dest = *argv;
                        ishost = getaddr(RTA_DST, *argv, &hp);
                  } else if ((rtm_addrs & RTA_GATEWAY) == 0) {
                        gateway = *argv;
                        (void) getaddr(RTA_GATEWAY, *argv, &hp);
                  } else {
                        (void) getaddr(RTA_NETMASK, *argv, 0);
                        forcenet = 1;
                  }
            }
      }
      if (forcehost) {
            ishost = 1;
#ifdef INET6
            if (af == AF_INET6) {
                  rtm_addrs &= ~RTA_NETMASK;
                  memset((void *)&so_mask, 0, sizeof(so_mask));
            }
#endif 
      }
      if (forcenet)
            ishost = 0;
      flags |= RTF_UP;
      if (ishost)
            flags |= RTF_HOST;
      if (iflag == 0)
            flags |= RTF_GATEWAY;
      if (proxy) {
            so_dst.sinarp.sin_other = SIN_PROXY;
            flags |= RTF_ANNOUNCE;
      }
      for (attempts = 1; ; attempts++) {
            errno = 0;
            if ((ret = rtmsg(*cmd, flags)) == 0)
                  break;
            if (errno != ENETUNREACH && errno != ESRCH)
                  break;
            if (af == AF_INET && *gateway && hp && hp->h_addr_list[1]) {
                  hp->h_addr_list++;
                  memmove(&so_gate.sin.sin_addr, hp->h_addr_list[0],
                      MIN(hp->h_length, sizeof(so_gate.sin.sin_addr)));
            } else
                  break;
      }
      if (*cmd == 'g')
            exit(ret != 0);
      if (!qflag) {
            oerrno = errno;
            (void) printf("%s %s %s", cmd, ishost? "host" : "net", dest);
            if (*gateway) {
                  (void) printf(": gateway %s", gateway);
                  if (attempts > 1 && ret == 0 && af == AF_INET)
                      (void) printf(" (%s)",
                        inet_ntoa(((struct sockaddr_in *)&route.rt_gateway)->sin_addr));
            }
            if (ret == 0) {
                  (void) printf("\n");
            } else {
                  switch (oerrno) {
                  case ESRCH:
                        err = "not in table";
                        break;
                  case EBUSY:
                        err = "entry in use";
                        break;
                  case ENOBUFS:
                        err = "not enough memory";
                        break;
                  case EADDRINUSE:
                        /* handle recursion avoidance in rt_setgate() */
                        err = "gateway uses the same route";
                        break;
                  case EEXIST:
                        err = "route already in table";
                        break;
                  default:
                        err = strerror(oerrno);
                        break;
                  }
                  (void) printf(": %s\n", err);
            }
      }
      exit(ret != 0);
}

void
inet_makenetandmask(net, sin, bits)
      u_long net, bits;
      struct sockaddr_in *sin;
{
      u_long addr, mask = 0;
      char *cp;

      rtm_addrs |= RTA_NETMASK;
      if (net == 0)
            mask = addr = 0;
      else {
            if (net <= 0xff)
                  addr = net << IN_CLASSA_NSHIFT;
            else if (net <= 0xffff)
                  addr = net << IN_CLASSB_NSHIFT;
            else if (net <= 0xffffff)
                  addr = net << IN_CLASSC_NSHIFT;
            else
                  addr = net;

            if (bits != 0)
                  mask = 0xffffffff << (32 - bits);
            else {
                  if (IN_CLASSA(addr))
                        mask = IN_CLASSA_NET;
                  else if (IN_CLASSB(addr))
                        mask = IN_CLASSB_NET;
                  else if (IN_CLASSC(addr))
                        mask = IN_CLASSC_NET;
                  else if (IN_MULTICAST(addr))
                        mask = IN_CLASSD_NET;
                  else
                        mask = 0xffffffff;
            }
      }
      sin->sin_addr.s_addr = htonl(addr);
      sin = &so_mask.sin;
      sin->sin_addr.s_addr = htonl(mask);
      sin->sin_len = 0;
      sin->sin_family = 0;
      cp = (char *)(&sin->sin_addr + 1);
      while (*--cp == 0 && cp > (char *)sin)
            ;
      sin->sin_len = 1 + cp - (char *)sin;
}

#ifdef INET6
/*
 * XXX the function may need more improvement...
 */
static int
inet6_makenetandmask(sin6, plen)
      struct sockaddr_in6 *sin6;
      char *plen;
{
      struct in6_addr in6;

      if (!plen) {
            if (IN6_IS_ADDR_UNSPECIFIED(&sin6->sin6_addr) &&
                sin6->sin6_scope_id == 0) {
                  plen = "0";
            } else if ((sin6->sin6_addr.s6_addr[0] & 0xe0) == 0x20) {
                  /* aggregatable global unicast - RFC2374 */
                  memset(&in6, 0, sizeof(in6));
                  if (!memcmp(&sin6->sin6_addr.s6_addr[8],
                            &in6.s6_addr[8], 8))
                        plen = "64";
            }
      }

      if (!plen || strcmp(plen, "128") == 0)
            return 1;
      rtm_addrs |= RTA_NETMASK;
      (void)prefixlen(plen);
      return 0;
}
#endif

/*
 * Interpret an argument as a network address of some kind,
 * returning 1 if a host address, 0 if a network address.
 */
int
getaddr(which, s, hpp)
      int which;
      char *s;
      struct hostent **hpp;
{
      sup su;
      struct hostent *hp;
      struct netent *np;
      u_long val;
      char *q;
      int afamily;  /* local copy of af so we can change it */

      if (af == 0) {
            af = AF_INET;
            aflen = sizeof(struct sockaddr_in);
      }
      afamily = af;
      rtm_addrs |= which;
      switch (which) {
      case RTA_DST:
            su = &so_dst;
            break;
      case RTA_GATEWAY:
            su = &so_gate;
            if (iflag) {
                  struct ifaddrs *ifap, *ifa;
                  struct sockaddr_dl *sdl = NULL;

                  if (getifaddrs(&ifap))
                        err(1, "getifaddrs");

                  for (ifa = ifap; ifa; ifa = ifa->ifa_next) {
                        if (ifa->ifa_addr->sa_family != AF_LINK)
                              continue;

                        if (strcmp(s, ifa->ifa_name))
                              continue;

                        sdl = (struct sockaddr_dl *)ifa->ifa_addr;
                  }
                  /* If we found it, then use it */
                  if (sdl) {
                        /*
                         * Copy is safe since we have a
                         * sockaddr_storage member in sockunion{}.
                         * Note that we need to copy before calling
                         * freeifaddrs().
                         */
                        memcpy(&su->sdl, sdl, sdl->sdl_len);
                  }
                  freeifaddrs(ifap);
                  if (sdl)
                        return(1);
            }
            break;
      case RTA_NETMASK:
            su = &so_mask;
            break;
      case RTA_GENMASK:
            su = &so_genmask;
            break;
      case RTA_IFP:
            su = &so_ifp;
            afamily = AF_LINK;
            break;
      case RTA_IFA:
            su = &so_ifa;
            break;
      default:
            usage("internal error");
            /*NOTREACHED*/
      }
      su->sa.sa_len = aflen;
      su->sa.sa_family = afamily; /* cases that don't want it have left already */
      if (strcmp(s, "default") == 0) {
            /*
             * Default is net 0.0.0.0/0 
             */
            switch (which) {
            case RTA_DST:
                  forcenet++;
#if 0
                  bzero(su, sizeof(*su)); /* for readability */
#endif
                  (void) getaddr(RTA_NETMASK, s, 0);
                  break;
#if 0
            case RTA_NETMASK:
            case RTA_GENMASK:
                  bzero(su, sizeof(*su)); /* for readability */
#endif
            }
            return (0);
      }
      switch (afamily) {
#ifdef INET6
      case AF_INET6:
      {
            struct addrinfo hints, *res;
            int ecode;

            q = NULL;
            if (which == RTA_DST && (q = strchr(s, '/')) != NULL)
                  *q = '\0';
            memset(&hints, 0, sizeof(hints));
            hints.ai_family = afamily;    /*AF_INET6*/
            hints.ai_socktype = SOCK_DGRAM;           /*dummy*/
            ecode = getaddrinfo(s, NULL, &hints, &res);
            if (ecode != 0 || res->ai_family != AF_INET6 ||
                res->ai_addrlen != sizeof(su->sin6)) {
                  (void) fprintf(stderr, "%s: %s\n", s,
                      gai_strerror(ecode));
                  exit(1);
            }
            memcpy(&su->sin6, res->ai_addr, sizeof(su->sin6));
#ifdef __KAME__
            if ((IN6_IS_ADDR_LINKLOCAL(&su->sin6.sin6_addr) ||
                 IN6_IS_ADDR_MC_LINKLOCAL(&su->sin6.sin6_addr)) &&
                su->sin6.sin6_scope_id) {
                  *(u_int16_t *)&su->sin6.sin6_addr.s6_addr[2] =
                        htons(su->sin6.sin6_scope_id);
                  su->sin6.sin6_scope_id = 0;
            }
#endif
            freeaddrinfo(res);
            if (q != NULL)
                  *q++ = '/';
            if (which == RTA_DST)
                  return (inet6_makenetandmask(&su->sin6, q));
            return (0);
      }
#endif /* INET6 */

      case AF_APPLETALK:
            if (!atalk_aton(s, &su->sat.sat_addr))
                  errx(EX_NOHOST, "bad address: %s", s);
            rtm_addrs |= RTA_NETMASK;
            return(forcehost || su->sat.sat_addr.s_node != 0);

      case AF_LINK:
            link_addr(s, &su->sdl);
            return (1);


      case PF_ROUTE:
            su->sa.sa_len = sizeof(*su);
            sockaddr(s, &su->sa);
            return (1);

      case AF_INET:
      default:
            break;
      }

      if (hpp == NULL)
            hpp = &hp;
      *hpp = NULL;

      q = strchr(s,'/');
      if (q && which == RTA_DST) {
            *q = '\0';
            if ((val = inet_network(s)) != INADDR_NONE) {
                  inet_makenetandmask(
                        val, &su->sin, strtoul(q+1, 0, 0));
                  return (0);
            }
            *q = '/';
      }
      if ((which != RTA_DST || forcenet == 0) &&
          inet_aton(s, &su->sin.sin_addr)) {
            val = su->sin.sin_addr.s_addr;
            if (which != RTA_DST || forcehost ||
                inet_lnaof(su->sin.sin_addr) != INADDR_ANY)
                  return (1);
            else {
                  val = ntohl(val);
                  goto netdone;
            }
      }
      if (which == RTA_DST && forcehost == 0 &&
          ((val = inet_network(s)) != INADDR_NONE ||
          ((np = getnetbyname(s)) != NULL && (val = np->n_net) != 0))) {
netdone:
            inet_makenetandmask(val, &su->sin, 0);
            return (0);
      }
      hp = gethostbyname(s);
      if (hp) {
            *hpp = hp;
            su->sin.sin_family = hp->h_addrtype;
            memmove((char *)&su->sin.sin_addr, hp->h_addr,
                MIN(hp->h_length, sizeof(su->sin.sin_addr)));
            return (1);
      }
      errx(EX_NOHOST, "bad address: %s", s);
}

int
prefixlen(s)
      char *s;
{
      int len = atoi(s), q, r;
      int max;
      char *p;

      rtm_addrs |= RTA_NETMASK;     
      switch (af) {
#ifdef INET6
      case AF_INET6:
            max = 128;
            p = (char *)&so_mask.sin6.sin6_addr;
            break;
#endif
      case AF_INET:
            max = 32;
            p = (char *)&so_mask.sin.sin_addr;
            break;
      default:
            (void) fprintf(stderr, "prefixlen not supported in this af\n");
            exit(1);
            /*NOTREACHED*/
      }

      if (len < 0 || max < len) {
            (void) fprintf(stderr, "%s: bad value\n", s);
            exit(1);
      }
      
      q = len >> 3;
      r = len & 7;
      so_mask.sa.sa_family = af;
      so_mask.sa.sa_len = aflen;
      memset((void *)p, 0, max / 8);
      if (q > 0)
            memset((void *)p, 0xff, q);
      if (r > 0)
            *((u_char *)p + q) = (0xff00 >> r) & 0xff;
      if (len == max)
            return -1;
      else
            return len;
}

void
interfaces()
{
      size_t needed;
      int mib[6];
      char *buf, *lim, *next, count = 0;
      struct rt_msghdr *rtm;

retry2:
      mib[0] = CTL_NET;
      mib[1] = PF_ROUTE;
      mib[2] = 0;       /* protocol */
      mib[3] = 0;       /* wildcard address family */
      mib[4] = NET_RT_IFLIST;
      mib[5] = 0;       /* no flags */
      if (sysctl(mib, 6, NULL, &needed, NULL, 0) < 0)
            err(EX_OSERR, "route-sysctl-estimate");
      if ((buf = malloc(needed)) == NULL)
            errx(EX_OSERR, "malloc failed");
      if (sysctl(mib, 6, buf, &needed, NULL, 0) < 0) {
            if (errno == ENOMEM && count++ < 10) {
                  warnx("Routing table grew, retrying");
                  sleep(1);
                  free(buf);
                  goto retry2;
            }
            err(EX_OSERR, "actual retrieval of interface table");
      }
      lim = buf + needed;
      for (next = buf; next < lim; next += rtm->rtm_msglen) {
            rtm = (struct rt_msghdr *)next;
            print_rtmsg(rtm, rtm->rtm_msglen);
      }
}

void
monitor()
{
      int n;
      char msg[2048];

      verbose = 1;
      if (debugonly) {
            interfaces();
            exit(0);
      }
      for(;;) {
            time_t now;
            n = read(s, msg, 2048);
            now = time(NULL);
            (void) printf("\ngot message of size %d on %s", n, ctime(&now));
            print_rtmsg((struct rt_msghdr *)msg, n);
      }
}

struct {
      struct      rt_msghdr m_rtm;
      char  m_space[512];
} m_rtmsg;

int
rtmsg(cmd, flags)
      int cmd, flags;
{
      static int seq;
      int rlen;
      char *cp = m_rtmsg.m_space;
      int l;

#define NEXTADDR(w, u) \
      if (rtm_addrs & (w)) {\
          l = SA_SIZE(&(u.sa)); memmove(cp, &(u), l); cp += l;\
          if (verbose) sodump(&(u),#u);\
      }

      errno = 0;
      memset(&m_rtmsg, 0, sizeof(m_rtmsg));
      if (cmd == 'a')
            cmd = RTM_ADD;
      else if (cmd == 'c')
            cmd = RTM_CHANGE;
      else if (cmd == 'g') {
            cmd = RTM_GET;
            if (so_ifp.sa.sa_family == 0) {
                  so_ifp.sa.sa_family = AF_LINK;
                  so_ifp.sa.sa_len = sizeof(struct sockaddr_dl);
                  rtm_addrs |= RTA_IFP;
            }
      } else
            cmd = RTM_DELETE;
#define rtm m_rtmsg.m_rtm
      rtm.rtm_type = cmd;
      rtm.rtm_flags = flags;
      rtm.rtm_version = RTM_VERSION;
      rtm.rtm_seq = ++seq;
      rtm.rtm_addrs = rtm_addrs;
      rtm.rtm_rmx = rt_metrics;
      rtm.rtm_inits = rtm_inits;

      if (rtm_addrs & RTA_NETMASK)
            mask_addr();
      NEXTADDR(RTA_DST, so_dst);
      NEXTADDR(RTA_GATEWAY, so_gate);
      NEXTADDR(RTA_NETMASK, so_mask);
      NEXTADDR(RTA_GENMASK, so_genmask);
      NEXTADDR(RTA_IFP, so_ifp);
      NEXTADDR(RTA_IFA, so_ifa);
      rtm.rtm_msglen = l = cp - (char *)&m_rtmsg;
      if (verbose)
            print_rtmsg(&rtm, l);
      if (debugonly)
            return (0);
      if ((rlen = write(s, (char *)&m_rtmsg, l)) < 0) {
            if (errno == EPERM)
                  err(1, "writing to routing socket");
            warn("writing to routing socket");
            return (-1);
      }
      if (cmd == RTM_GET) {
            do {
                  l = read(s, (char *)&m_rtmsg, sizeof(m_rtmsg));
            } while (l > 0 && (rtm.rtm_seq != seq || rtm.rtm_pid != pid));
            if (l < 0)
                  warn("read from routing socket");
            else
                  print_getmsg(&rtm, l);
      }
#undef rtm
      return (0);
}

void
mask_addr()
{
      int olen = so_mask.sa.sa_len;
      char *cp1 = olen + (char *)&so_mask, *cp2;

      for (so_mask.sa.sa_len = 0; cp1 > (char *)&so_mask; )
            if (*--cp1 != 0) {
                  so_mask.sa.sa_len = 1 + cp1 - (char *)&so_mask;
                  break;
            }
      if ((rtm_addrs & RTA_DST) == 0)
            return;
      switch (so_dst.sa.sa_family) {
      case AF_INET:
#ifdef INET6
      case AF_INET6:
#endif
      case AF_APPLETALK:
      case 0:
            return;
      }
      cp1 = so_mask.sa.sa_len + 1 + (char *)&so_dst;
      cp2 = so_dst.sa.sa_len + 1 + (char *)&so_dst;
      while (cp2 > cp1)
            *--cp2 = 0;
      cp2 = so_mask.sa.sa_len + 1 + (char *)&so_mask;
      while (cp1 > so_dst.sa.sa_data)
            *--cp1 &= *--cp2;
}

char *msgtypes[] = {
      "",
      "RTM_ADD: Add Route",
      "RTM_DELETE: Delete Route",
      "RTM_CHANGE: Change Metrics or flags",
      "RTM_GET: Report Metrics",
      "RTM_LOSING: Kernel Suspects Partitioning",
      "RTM_REDIRECT: Told to use different route",
      "RTM_MISS: Lookup failed on this address",
      "RTM_LOCK: fix specified metrics",
      "RTM_OLDADD: caused by SIOCADDRT",
      "RTM_OLDDEL: caused by SIOCDELRT",
      "RTM_RESOLVE: Route created by cloning",
      "RTM_NEWADDR: address being added to iface",
      "RTM_DELADDR: address being removed from iface",
      "RTM_IFINFO: iface status change",
      "RTM_NEWMADDR: new multicast group membership on iface",
      "RTM_DELMADDR: multicast group membership removed from iface",
      "RTM_IFANNOUNCE: interface arrival/departure",
      0,
};

char metricnames[] =
"\011pksent\010rttvar\7rtt\6ssthresh\5sendpipe\4recvpipe\3expire\2hopcount"
"\1mtu";
char routeflags[] =
"\1UP\2GATEWAY\3HOST\4REJECT\5DYNAMIC\6MODIFIED\7DONE\010MASK_PRESENT"
"\011CLONING\012XRESOLVE\013LLINFO\014STATIC\015BLACKHOLE\016b016"
"\017PROTO2\020PROTO1\021PRCLONING\022WASCLONED\023PROTO3\024CHAINDELETE"
"\025PINNED\026LOCAL\027BROADCAST\030MULTICAST";
char ifnetflags[] =
"\1UP\2BROADCAST\3DEBUG\4LOOPBACK\5PTP\6b6\7RUNNING\010NOARP"
"\011PPROMISC\012ALLMULTI\013OACTIVE\014SIMPLEX\015LINK0\016LINK1"
"\017LINK2\020MULTICAST";
char addrnames[] =
"\1DST\2GATEWAY\3NETMASK\4GENMASK\5IFP\6IFA\7AUTHOR\010BRD";

void
print_rtmsg(rtm, msglen)
      struct rt_msghdr *rtm;
      int msglen;
{
      struct if_msghdr *ifm;
      struct ifa_msghdr *ifam;
#ifdef RTM_NEWMADDR
      struct ifma_msghdr *ifmam;
#endif
      struct if_announcemsghdr *ifan;
      char *state;

      if (verbose == 0)
            return;
      if (rtm->rtm_version != RTM_VERSION) {
            (void) printf("routing message version %d not understood\n",
                rtm->rtm_version);
            return;
      }
      if (msgtypes[rtm->rtm_type] != NULL)
            (void)printf("%s: ", msgtypes[rtm->rtm_type]);
      else
            (void)printf("#%d: ", rtm->rtm_type);
      (void)printf("len %d, ", rtm->rtm_msglen);
      switch (rtm->rtm_type) {
      case RTM_IFINFO:
            ifm = (struct if_msghdr *)rtm;
            (void) printf("if# %d, ", ifm->ifm_index);
            switch (ifm->ifm_data.ifi_link_state) {
            case LINK_STATE_DOWN:
                  state = "down";
                  break;
            case LINK_STATE_UP:
                  state = "up";
                  break;
            default:
                  state = "unknown";
                  break;
            }
            (void) printf("link: %s, flags:", state);
            bprintf(stdout, ifm->ifm_flags, ifnetflags);
            pmsg_addrs((char *)(ifm + 1), ifm->ifm_addrs);
            break;
      case RTM_NEWADDR:
      case RTM_DELADDR:
            ifam = (struct ifa_msghdr *)rtm;
            (void) printf("metric %d, flags:", ifam->ifam_metric);
            bprintf(stdout, ifam->ifam_flags, routeflags);
            pmsg_addrs((char *)(ifam + 1), ifam->ifam_addrs);
            break;
#ifdef RTM_NEWMADDR
      case RTM_NEWMADDR:
      case RTM_DELMADDR:
            ifmam = (struct ifma_msghdr *)rtm;
            pmsg_addrs((char *)(ifmam + 1), ifmam->ifmam_addrs);
            break;
#endif
      case RTM_IFANNOUNCE:
            ifan = (struct if_announcemsghdr *)rtm;
            (void) printf("if# %d, what: ", ifan->ifan_index);
            switch (ifan->ifan_what) {
            case IFAN_ARRIVAL:
                  printf("arrival");
                  break;
            case IFAN_DEPARTURE:
                  printf("departure");
                  break;
            default:
                  printf("#%d", ifan->ifan_what);
                  break;
            }
            printf("\n");
            break;

      default:
            (void) printf("pid: %ld, seq %d, errno %d, flags:",
                  (long)rtm->rtm_pid, rtm->rtm_seq, rtm->rtm_errno);
            bprintf(stdout, rtm->rtm_flags, routeflags);
            pmsg_common(rtm);
      }
}

void
print_getmsg(rtm, msglen)
      struct rt_msghdr *rtm;
      int msglen;
{
      struct sockaddr *dst = NULL, *gate = NULL, *mask = NULL;
      struct sockaddr_dl *ifp = NULL;
      struct sockaddr *sa;
      char *cp;
      int i;

      (void) printf("   route to: %s\n", routename(&so_dst));
      if (rtm->rtm_version != RTM_VERSION) {
            warnx("routing message version %d not understood",
                 rtm->rtm_version);
            return;
      }
      if (rtm->rtm_msglen > msglen) {
            warnx("message length mismatch, in packet %d, returned %d",
                  rtm->rtm_msglen, msglen);
      }
      if (rtm->rtm_errno)  {
            errno = rtm->rtm_errno;
            warn("message indicates error %d", errno);
            return;
      }
      cp = ((char *)(rtm + 1));
      if (rtm->rtm_addrs)
            for (i = 1; i; i <<= 1)
                  if (i & rtm->rtm_addrs) {
                        sa = (struct sockaddr *)cp;
                        switch (i) {
                        case RTA_DST:
                              dst = sa;
                              break;
                        case RTA_GATEWAY:
                              gate = sa;
                              break;
                        case RTA_NETMASK:
                              mask = sa;
                              break;
                        case RTA_IFP:
                              if (sa->sa_family == AF_LINK &&
                                 ((struct sockaddr_dl *)sa)->sdl_nlen)
                                    ifp = (struct sockaddr_dl *)sa;
                              break;
                        }
                        cp += SA_SIZE(sa);
                  }
      if (dst && mask)
            mask->sa_family = dst->sa_family;   /* XXX */
      if (dst)
            (void)printf("destination: %s\n", routename(dst));
      if (mask) {
            int savenflag = nflag;

            nflag = 1;
            (void)printf("       mask: %s\n", routename(mask));
            nflag = savenflag;
      }
      if (gate && rtm->rtm_flags & RTF_GATEWAY)
            (void)printf("    gateway: %s\n", routename(gate));
      if (ifp)
            (void)printf("  interface: %.*s\n",
                ifp->sdl_nlen, ifp->sdl_data);
      (void)printf("      flags: ");
      bprintf(stdout, rtm->rtm_flags, routeflags);

#define lock(f)   ((rtm->rtm_rmx.rmx_locks & __CONCAT(RTV_,f)) ? 'L' : ' ')
#define msec(u)   (((u) + 500) / 1000)          /* usec to msec */

      (void) printf("\n%s\n", "\
 recvpipe  sendpipe  ssthresh  rtt,msec    rttvar  hopcount      mtu     expire");
      printf("%8ld%c ", rtm->rtm_rmx.rmx_recvpipe, lock(RPIPE));
      printf("%8ld%c ", rtm->rtm_rmx.rmx_sendpipe, lock(SPIPE));
      printf("%8ld%c ", rtm->rtm_rmx.rmx_ssthresh, lock(SSTHRESH));
      printf("%8ld%c ", msec(rtm->rtm_rmx.rmx_rtt), lock(RTT));
      printf("%8ld%c ", msec(rtm->rtm_rmx.rmx_rttvar), lock(RTTVAR));
      printf("%8ld%c ", rtm->rtm_rmx.rmx_hopcount, lock(HOPCOUNT));
      printf("%8ld%c ", rtm->rtm_rmx.rmx_mtu, lock(MTU));
      if (rtm->rtm_rmx.rmx_expire)
            rtm->rtm_rmx.rmx_expire -= time(0);
      printf("%8ld%c\n", rtm->rtm_rmx.rmx_expire, lock(EXPIRE));
#undef lock
#undef msec
#define     RTA_IGN     (RTA_DST|RTA_GATEWAY|RTA_NETMASK|RTA_IFP|RTA_IFA|RTA_BRD)
      if (verbose)
            pmsg_common(rtm);
      else if (rtm->rtm_addrs &~ RTA_IGN) {
            (void) printf("sockaddrs: ");
            bprintf(stdout, rtm->rtm_addrs, addrnames);
            putchar('\n');
      }
#undef      RTA_IGN
}

void
pmsg_common(rtm)
      struct rt_msghdr *rtm;
{
      (void) printf("\nlocks: ");
      bprintf(stdout, rtm->rtm_rmx.rmx_locks, metricnames);
      (void) printf(" inits: ");
      bprintf(stdout, rtm->rtm_inits, metricnames);
      pmsg_addrs(((char *)(rtm + 1)), rtm->rtm_addrs);
}

void
pmsg_addrs(cp, addrs)
      char  *cp;
      int   addrs;
{
      struct sockaddr *sa;
      int i;

      if (addrs == 0) {
            (void) putchar('\n');
            return;
      }
      (void) printf("\nsockaddrs: ");
      bprintf(stdout, addrs, addrnames);
      (void) putchar('\n');
      for (i = 1; i; i <<= 1)
            if (i & addrs) {
                  sa = (struct sockaddr *)cp;
                  (void) printf(" %s", routename(sa));
                  cp += SA_SIZE(sa);
            }
      (void) putchar('\n');
      (void) fflush(stdout);
}

void
bprintf(fp, b, s)
      FILE *fp;
      int b;
      u_char *s;
{
      int i;
      int gotsome = 0;

      if (b == 0)
            return;
      while ((i = *s++) != 0) {
            if (b & (1 << (i-1))) {
                  if (gotsome == 0)
                        i = '<';
                  else
                        i = ',';
                  (void) putc(i, fp);
                  gotsome = 1;
                  for (; (i = *s) > 32; s++)
                        (void) putc(i, fp);
            } else
                  while (*s > 32)
                        s++;
      }
      if (gotsome)
            (void) putc('>', fp);
}

int
keyword(cp)
      char *cp;
{
      struct keytab *kt = keywords;

      while (kt->kt_cp && strcmp(kt->kt_cp, cp))
            kt++;
      return kt->kt_i;
}

void
sodump(su, which)
      sup su;
      char *which;
{
      switch (su->sa.sa_family) {
      case AF_LINK:
            (void) printf("%s: link %s; ",
                which, link_ntoa(&su->sdl));
            break;
      case AF_INET:
            (void) printf("%s: inet %s; ",
                which, inet_ntoa(su->sin.sin_addr));
            break;
      case AF_APPLETALK:
            (void) printf("%s: atalk %s; ",
                which, atalk_ntoa(su->sat.sat_addr));
            break;
      }
      (void) fflush(stdout);
}

/* States*/
#define VIRGIN    0
#define GOTONE    1
#define GOTTWO    2
/* Inputs */
#define     DIGIT (4*0)
#define     END   (4*1)
#define DELIM     (4*2)

void
sockaddr(addr, sa)
      char *addr;
      struct sockaddr *sa;
{
      char *cp = (char *)sa;
      int size = sa->sa_len;
      char *cplim = cp + size;
      int byte = 0, state = VIRGIN, new = 0 /* foil gcc */;

      memset(cp, 0, size);
      cp++;
      do {
            if ((*addr >= '0') && (*addr <= '9')) {
                  new = *addr - '0';
            } else if ((*addr >= 'a') && (*addr <= 'f')) {
                  new = *addr - 'a' + 10;
            } else if ((*addr >= 'A') && (*addr <= 'F')) {
                  new = *addr - 'A' + 10;
            } else if (*addr == 0)
                  state |= END;
            else
                  state |= DELIM;
            addr++;
            switch (state /* | INPUT */) {
            case GOTTWO | DIGIT:
                  *cp++ = byte; /*FALLTHROUGH*/
            case VIRGIN | DIGIT:
                  state = GOTONE; byte = new; continue;
            case GOTONE | DIGIT:
                  state = GOTTWO; byte = new + (byte << 4); continue;
            default: /* | DELIM */
                  state = VIRGIN; *cp++ = byte; byte = 0; continue;
            case GOTONE | END:
            case GOTTWO | END:
                  *cp++ = byte; /* FALLTHROUGH */
            case VIRGIN | END:
                  break;
            }
            break;
      } while (cp < cplim);
      sa->sa_len = cp - (char *)sa;
}

int
atalk_aton(const char *text, struct at_addr *addr)
{
      u_int net, node;

      if (sscanf(text, "%u.%u", &net, &node) != 2
          || net > 0xffff || node > 0xff)
            return(0);
      addr->s_net = htons(net);
      addr->s_node = node;
      return(1);
}

char *
atalk_ntoa(struct at_addr at)
{
      static char buf[20];

      (void) snprintf(buf, sizeof(buf), "%u.%u", ntohs(at.s_net), at.s_node);
      return(buf);
}

Generated by  Doxygen 1.6.0   Back to index