route.c revision 78189
152400Sbillf/*-
252400Sbillf * Copyright (c) 1996 - 2001 Brian Somers <brian@Awfulhak.org>
352400Sbillf *          based on work by Toshiharu OHNO <tony-o@iij.ad.jp>
452400Sbillf *                           Internet Initiative Japan, Inc (IIJ)
552400Sbillf * All rights reserved.
652400Sbillf *
752400Sbillf * Redistribution and use in source and binary forms, with or without
873651Sdougb * modification, are permitted provided that the following conditions
973651Sdougb * are met:
1052400Sbillf * 1. Redistributions of source code must retain the above copyright
1152495Sbillf *    notice, this list of conditions and the following disclaimer.
1252400Sbillf * 2. Redistributions in binary form must reproduce the above copyright
1368507Sdougb *    notice, this list of conditions and the following disclaimer in the
1452400Sbillf *    documentation and/or other materials provided with the distribution.
1552400Sbillf *
1652533Sbillf * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
1752400Sbillf * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
1867949Sdougb * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
1967949Sdougb * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
2052400Sbillf * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
2152400Sbillf * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2252400Sbillf * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2352400Sbillf * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2452400Sbillf * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2552400Sbillf * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
2652400Sbillf * SUCH DAMAGE.
2767949Sdougb *
2852400Sbillf * $FreeBSD: head/usr.sbin/ppp/route.c 78189 2001-06-13 21:52:19Z brian $
2952400Sbillf */
3052400Sbillf
3152400Sbillf#include <sys/param.h>
3252400Sbillf#include <sys/socket.h>
3367949Sdougb#include <net/if_types.h>
3452400Sbillf#include <net/route.h>
3552400Sbillf#include <net/if.h>
3652400Sbillf#include <netinet/in.h>
3752400Sbillf#include <arpa/inet.h>
3852400Sbillf#include <net/if_dl.h>
3952400Sbillf#include <netinet/in_systm.h>
4052400Sbillf#include <netinet/ip.h>
4167859Sdougb#include <sys/un.h>
4267949Sdougb#include <netdb.h>
4352400Sbillf
4452400Sbillf#include <errno.h>
4558910Salfred#include <stdio.h>
4658910Salfred#include <stdlib.h>
4758910Salfred#include <string.h>
4867850Sdougb#include <sys/sysctl.h>
4967850Sdougb#include <termios.h>
5067850Sdougb#include <unistd.h>
5167850Sdougb
5267850Sdougb#include "layer.h"
5367850Sdougb#include "defs.h"
5467850Sdougb#include "command.h"
5567850Sdougb#include "mbuf.h"
5667850Sdougb#include "log.h"
5767850Sdougb#include "iplist.h"
5867850Sdougb#include "timer.h"
5967850Sdougb#include "throughput.h"
6067949Sdougb#include "lqr.h"
6167850Sdougb#include "hdlc.h"
6267850Sdougb#include "fsm.h"
6367850Sdougb#include "lcp.h"
6467850Sdougb#include "ccp.h"
6567850Sdougb#include "link.h"
6667850Sdougb#include "slcompress.h"
6767850Sdougb#include "ipcp.h"
6867850Sdougb#include "filter.h"
6967859Sdougb#include "descriptor.h"
7067859Sdougb#include "mp.h"
7158910Salfred#ifndef NORADIUS
7267850Sdougb#include "radius.h"
7367850Sdougb#endif
7467850Sdougb#include "bundle.h"
7567850Sdougb#include "route.h"
7667850Sdougb#include "prompt.h"
7767850Sdougb#include "iface.h"
7867859Sdougb#include "id.h"
7967850Sdougb
8067859Sdougb
8167850Sdougbstatic void
8267850Sdougbp_sockaddr(struct prompt *prompt, struct sockaddr *phost,
8367850Sdougb           struct sockaddr *pmask, int width)
8467850Sdougb{
8567859Sdougb  char buf[29];
8667850Sdougb  struct sockaddr_in *ihost4 = (struct sockaddr_in *)phost;
8767850Sdougb  struct sockaddr_in *mask4 = (struct sockaddr_in *)pmask;
8867850Sdougb  struct sockaddr_dl *dl = (struct sockaddr_dl *)phost;
8967850Sdougb
9067850Sdougb  if (log_IsKept(LogDEBUG)) {
9167850Sdougb    char tmp[50];
9267850Sdougb
9367850Sdougb    log_Printf(LogDEBUG, "Found the following sockaddr:\n");
9467850Sdougb    log_Printf(LogDEBUG, "  Family %d, len %d\n",
9567850Sdougb               (int)phost->sa_family, (int)phost->sa_len);
9667850Sdougb    inet_ntop(phost->sa_family, phost->sa_data, tmp, sizeof tmp);
9767850Sdougb    log_Printf(LogDEBUG, "  Addr %s\n", tmp);
9867850Sdougb    if (pmask) {
9967850Sdougb      inet_ntop(pmask->sa_family, pmask->sa_data, tmp, sizeof tmp);
10058910Salfred      log_Printf(LogDEBUG, "  Mask %s\n", tmp);
10158910Salfred    }
10258910Salfred  }
10358910Salfred
10458910Salfred  switch (phost->sa_family) {
10558910Salfred  case AF_INET:
10667850Sdougb    if (!phost)
10758910Salfred      buf[0] = '\0';
10877323Sdougb    else if (ihost4->sin_addr.s_addr == INADDR_ANY)
10977323Sdougb      strcpy(buf, "default");
11067949Sdougb    else if (!pmask)
11167850Sdougb      strcpy(buf, inet_ntoa(ihost4->sin_addr));
11267850Sdougb    else {
11367949Sdougb      u_int32_t msk = ntohl(mask4->sin_addr.s_addr);
11467850Sdougb      u_int32_t tst;
11567850Sdougb      int bits;
11667949Sdougb      int len;
11767850Sdougb      struct sockaddr_in net;
11867850Sdougb
11967850Sdougb      for (tst = 1, bits = 32; tst; tst <<= 1, bits--)
12067850Sdougb        if (msk & tst)
12167949Sdougb          break;
12267850Sdougb
12367949Sdougb      for (tst <<= 1; tst; tst <<= 1)
12467949Sdougb        if (!(msk & tst))
12567949Sdougb          break;
12667949Sdougb
12767949Sdougb      net.sin_addr.s_addr = ihost4->sin_addr.s_addr & mask4->sin_addr.s_addr;
12868507Sdougb      strcpy(buf, inet_ntoa(net.sin_addr));
12967949Sdougb      for (len = strlen(buf); len > 3; buf[len -= 2] = '\0')
13067949Sdougb        if (strcmp(buf + len - 2, ".0"))
13167949Sdougb          break;
13267949Sdougb
13367949Sdougb      if (tst)    /* non-contiguous :-( */
13467949Sdougb        sprintf(buf + strlen(buf),"&0x%08lx", (u_long)msk);
13567949Sdougb      else
13667949Sdougb        sprintf(buf + strlen(buf), "/%d", bits);
13767949Sdougb    }
13867949Sdougb    break;
13967949Sdougb
14067949Sdougb  case AF_LINK:
14167850Sdougb    if (dl->sdl_nlen)
14267859Sdougb      snprintf(buf, sizeof buf, "%.*s", dl->sdl_nlen, dl->sdl_data);
14367850Sdougb    else if (dl->sdl_alen) {
14467850Sdougb      if (dl->sdl_type == IFT_ETHER) {
14567850Sdougb        if (dl->sdl_alen < sizeof buf / 3) {
14667850Sdougb          int f;
14777326Sdougb          u_char *MAC;
14877326Sdougb
14967850Sdougb          MAC = (u_char *)dl->sdl_data + dl->sdl_nlen;
15067850Sdougb          for (f = 0; f < dl->sdl_alen; f++)
15167850Sdougb            sprintf(buf+f*3, "%02x:", MAC[f]);
15267850Sdougb          buf[f*3-1] = '\0';
15367850Sdougb        } else
15467859Sdougb          strcpy(buf, "??:??:??:??:??:??");
15567859Sdougb      } else
15667859Sdougb        sprintf(buf, "<IFT type %d>", dl->sdl_type);
15767850Sdougb    }  else if (dl->sdl_slen)
15867850Sdougb      sprintf(buf, "<slen %d?>", dl->sdl_slen);
15967850Sdougb    else
16067850Sdougb      sprintf(buf, "link#%d", dl->sdl_index);
16167850Sdougb    break;
16267850Sdougb
16367850Sdougb#ifndef NOINET6
16467850Sdougb  case AF_INET6:
16567850Sdougb    if (!phost)
16667850Sdougb      buf[0] = '\0';
16767850Sdougb    else {
16867850Sdougb      const u_char masks[] = { 0x00, 0x80, 0xc0, 0xe0, 0xf0, 0xf8, 0xfc, 0xfe };
16967850Sdougb      struct sockaddr_in6 *ihost6 = (struct sockaddr_in6 *)phost;
17067850Sdougb      struct sockaddr_in6 *mask6 = (struct sockaddr_in6 *)pmask;
17167850Sdougb      int masklen, len;
17267850Sdougb      const u_char *c;
17367850Sdougb
17467850Sdougb      /* XXX: ?????!?!?!!!!!  This is horrible ! */
17567850Sdougb      if (IN6_IS_ADDR_LINKLOCAL(&ihost6->sin6_addr) ||
17667850Sdougb          IN6_IS_ADDR_MC_LINKLOCAL(&ihost6->sin6_addr)) {
17767850Sdougb        ihost6->sin6_scope_id =
17867850Sdougb          ntohs(*(u_short *)&ihost6->sin6_addr.s6_addr[2]);
17967850Sdougb        *(u_short *)&ihost6->sin6_addr.s6_addr[2] = 0;
18067850Sdougb      }
18167850Sdougb
18267850Sdougb      if (mask6) {
18367850Sdougb        const u_char *p, *end;
18467850Sdougb
18567850Sdougb        p = (const u_char *)&mask6->sin6_addr;
18667850Sdougb        end = p + 16;
18767850Sdougb        for (masklen = 0, end = p + 16; p < end && *p == 0xff; p++)
18867850Sdougb          masklen += 8;
18967850Sdougb
19067850Sdougb        if (p < end) {
19167850Sdougb          for (c = masks; c < masks + sizeof masks; c++)
19267850Sdougb            if (*c == *p) {
19367850Sdougb              masklen += c - masks;
19467850Sdougb              break;
19567850Sdougb            }
19667850Sdougb        }
19767850Sdougb      } else
19867850Sdougb        masklen = 128;
19967850Sdougb
20067859Sdougb      if (masklen == 0 && IN6_IS_ADDR_UNSPECIFIED(&ihost6->sin6_addr))
20167850Sdougb        snprintf(buf, sizeof buf, "default");
20267850Sdougb      else {
20367850Sdougb        getnameinfo(phost, ihost6->sin6_len, buf, sizeof buf,
20467850Sdougb                    NULL, 0, NI_WITHSCOPEID | NI_NUMERICHOST);
20567850Sdougb        if (mask6 && (len = strlen(buf)) < sizeof buf - 1)
20667850Sdougb          snprintf(buf + len, sizeof buf - len, "/%d", masklen);
20767850Sdougb      }
20867850Sdougb    }
20958910Salfred    break;
21058910Salfred#endif
21152400Sbillf
21252400Sbillf  default:
21352400Sbillf    sprintf(buf, "<AF type %d>", phost->sa_family);
21452400Sbillf    break;
21573651Sdougb  }
21673651Sdougb
21773651Sdougb  prompt_Printf(prompt, "%-*s ", width-1, buf);
21873651Sdougb}
21973651Sdougb
22073651Sdougbstatic struct bits {
22152400Sbillf  u_int32_t b_mask;
22252400Sbillf  char b_val;
22367949Sdougb} bits[] = {
22452400Sbillf  { RTF_UP, 'U' },
22552400Sbillf  { RTF_GATEWAY, 'G' },
22652400Sbillf  { RTF_HOST, 'H' },
22752400Sbillf  { RTF_REJECT, 'R' },
22852400Sbillf  { RTF_DYNAMIC, 'D' },
22967949Sdougb  { RTF_MODIFIED, 'M' },
23052400Sbillf  { RTF_DONE, 'd' },
23152400Sbillf  { RTF_CLONING, 'C' },
23252400Sbillf  { RTF_XRESOLVE, 'X' },
23352400Sbillf  { RTF_LLINFO, 'L' },
23452400Sbillf  { RTF_STATIC, 'S' },
23552400Sbillf  { RTF_PROTO1, '1' },
23652400Sbillf  { RTF_PROTO2, '2' },
23752400Sbillf  { RTF_BLACKHOLE, 'B' },
23852400Sbillf#ifdef RTF_WASCLONED
23952400Sbillf  { RTF_WASCLONED, 'W' },
24052400Sbillf#endif
24152400Sbillf#ifdef RTF_PRCLONING
24252400Sbillf  { RTF_PRCLONING, 'c' },
24352400Sbillf#endif
24452400Sbillf#ifdef RTF_PROTO3
24552400Sbillf  { RTF_PROTO3, '3' },
24652400Sbillf#endif
24752400Sbillf#ifdef RTF_BROADCAST
24852400Sbillf  { RTF_BROADCAST, 'b' },
24952400Sbillf#endif
25052400Sbillf  { 0, '\0' }
25152400Sbillf};
25252400Sbillf
25352400Sbillf#ifndef RTF_WASCLONED
25467949Sdougb#define RTF_WASCLONED (0)
25567949Sdougb#endif
25667949Sdougb
25752400Sbillfstatic void
25852400Sbillfp_flags(struct prompt *prompt, u_int32_t f, int max)
25952400Sbillf{
26052400Sbillf  char name[33], *flags;
26152400Sbillf  register struct bits *p = bits;
26252400Sbillf
26352400Sbillf  if (max > sizeof name - 1)
26452400Sbillf    max = sizeof name - 1;
26552400Sbillf
26652400Sbillf  for (flags = name; p->b_mask && flags - name < max; p++)
26752400Sbillf    if (p->b_mask & f)
26852400Sbillf      *flags++ = p->b_val;
26952400Sbillf  *flags = '\0';
27052400Sbillf  prompt_Printf(prompt, "%-*.*s", max, max, name);
27152400Sbillf}
27267949Sdougb
27367949Sdougbconst char *
27467949SdougbIndex2Nam(int idx)
27552400Sbillf{
27652400Sbillf  /*
27752400Sbillf   * XXX: Maybe we should select() on the routing socket so that we can
27852400Sbillf   *      notice interfaces that come & go (PCCARD support).
27952400Sbillf   *      Or we could even support a signal that resets these so that
28052400Sbillf   *      the PCCARD insert/remove events can signal ppp.
28152400Sbillf   */
28252400Sbillf  static char **ifs;		/* Figure these out once */
28352400Sbillf  static int nifs, debug_done;	/* Figure out how many once, and debug once */
28452400Sbillf
28552400Sbillf  if (idx > nifs || (idx > 0 && ifs[idx-1] == NULL)) {
28652400Sbillf    int mib[6], have, had;
28752400Sbillf    size_t needed;
28864467Sbrian    char *buf, *ptr, *end;
28952400Sbillf    struct sockaddr_dl *dl;
29064467Sbrian    struct if_msghdr *ifm;
29167859Sdougb
29252400Sbillf    if (ifs) {
29352400Sbillf      free(ifs);
29464467Sbrian      ifs = NULL;
29564467Sbrian      nifs = 0;
29652400Sbillf    }
29752400Sbillf    debug_done = 0;
29852400Sbillf
29952400Sbillf    mib[0] = CTL_NET;
30052400Sbillf    mib[1] = PF_ROUTE;
30167859Sdougb    mib[2] = 0;
30267859Sdougb    mib[3] = 0;
30367859Sdougb    mib[4] = NET_RT_IFLIST;
30452400Sbillf    mib[5] = 0;
30558910Salfred
30652400Sbillf    if (sysctl(mib, 6, NULL, &needed, NULL, 0) < 0) {
30752400Sbillf      log_Printf(LogERROR, "Index2Nam: sysctl: estimate: %s\n",
30858910Salfred                 strerror(errno));
30964467Sbrian      return NumStr(idx, NULL, 0);
31064467Sbrian    }
31164467Sbrian    if ((buf = malloc(needed)) == NULL)
31258910Salfred      return NumStr(idx, NULL, 0);
31364467Sbrian    if (sysctl(mib, 6, buf, &needed, NULL, 0) < 0) {
31464467Sbrian      free(buf);
31564467Sbrian      return NumStr(idx, NULL, 0);
31664467Sbrian    }
31764467Sbrian    end = buf + needed;
31864467Sbrian
31958910Salfred    have = 0;
32052400Sbillf    for (ptr = buf; ptr < end; ptr += ifm->ifm_msglen) {
32160420Sbsd      ifm = (struct if_msghdr *)ptr;
32252400Sbillf      if (ifm->ifm_type != RTM_IFINFO)
32352400Sbillf        continue;
32458910Salfred      dl = (struct sockaddr_dl *)(ifm + 1);
32558910Salfred      if (ifm->ifm_index > 0) {
32658910Salfred        if (ifm->ifm_index > have) {
32752400Sbillf          char **newifs;
32852400Sbillf
32958910Salfred          had = have;
33052400Sbillf          have = ifm->ifm_index + 5;
33152400Sbillf          if (had)
33252400Sbillf            newifs = (char **)realloc(ifs, sizeof(char *) * have);
33352400Sbillf          else
33452400Sbillf            newifs = (char **)malloc(sizeof(char *) * have);
33552400Sbillf          if (!newifs) {
33652400Sbillf            log_Printf(LogDEBUG, "Index2Nam: %s\n", strerror(errno));
33752400Sbillf            nifs = 0;
33852400Sbillf            if (ifs) {
33952400Sbillf              free(ifs);
34052400Sbillf              ifs = NULL;
34152400Sbillf            }
34252400Sbillf            free(buf);
34352400Sbillf            return NumStr(idx, NULL, 0);
34452400Sbillf          }
34552400Sbillf          ifs = newifs;
34652400Sbillf          memset(ifs + had, '\0', sizeof(char *) * (have - had));
34752400Sbillf        }
34852400Sbillf        if (ifs[ifm->ifm_index-1] == NULL) {
34952400Sbillf          ifs[ifm->ifm_index-1] = (char *)malloc(dl->sdl_nlen+1);
35052400Sbillf          memcpy(ifs[ifm->ifm_index-1], dl->sdl_data, dl->sdl_nlen);
35152400Sbillf          ifs[ifm->ifm_index-1][dl->sdl_nlen] = '\0';
35273651Sdougb          if (nifs < ifm->ifm_index)
35373651Sdougb            nifs = ifm->ifm_index;
35473651Sdougb        }
35573651Sdougb      } else if (log_IsKept(LogDEBUG))
35652400Sbillf        log_Printf(LogDEBUG, "Skipping out-of-range interface %d!\n",
35752400Sbillf                  ifm->ifm_index);
35852400Sbillf    }
35952400Sbillf    free(buf);
36052400Sbillf  }
36152400Sbillf
36252400Sbillf  if (log_IsKept(LogDEBUG) && !debug_done) {
36352400Sbillf    int f;
36452400Sbillf
36567859Sdougb    log_Printf(LogDEBUG, "Found the following interfaces:\n");
36652400Sbillf    for (f = 0; f < nifs; f++)
36752400Sbillf      if (ifs[f] != NULL)
36852400Sbillf        log_Printf(LogDEBUG, " Index %d, name \"%s\"\n", f+1, ifs[f]);
36952400Sbillf    debug_done = 1;
37052400Sbillf  }
37152400Sbillf
37252400Sbillf  if (idx < 1 || idx > nifs || ifs[idx-1] == NULL)
37352400Sbillf    return NumStr(idx, NULL, 0);
37452400Sbillf
37552400Sbillf  return ifs[idx-1];
37667859Sdougb}
37767859Sdougb
37867859Sdougbvoid
37967859Sdougbroute_ParseHdr(struct rt_msghdr *rtm, struct sockaddr *sa[RTAX_MAX])
38067859Sdougb{
38167859Sdougb  char *wp;
38267859Sdougb  int rtax;
38367859Sdougb
38467859Sdougb  wp = (char *)(rtm + 1);
38567859Sdougb
38652400Sbillf  for (rtax = 0; rtax < RTAX_MAX; rtax++)
38767859Sdougb    if (rtm->rtm_addrs & (1 << rtax)) {
38867859Sdougb      sa[rtax] = (struct sockaddr *)wp;
38967859Sdougb      wp += ROUNDUP(sa[rtax]->sa_len);
39067859Sdougb    } else
39167859Sdougb      sa[rtax] = NULL;
39267859Sdougb}
39367859Sdougb
39467859Sdougbint
39567859Sdougbroute_Show(struct cmdargs const *arg)
39667859Sdougb{
39767859Sdougb  struct rt_msghdr *rtm;
39867859Sdougb  struct sockaddr *sa[RTAX_MAX];
39967859Sdougb  char *sp, *ep, *cp;
40067859Sdougb  size_t needed;
40167859Sdougb  int mib[6];
40267859Sdougb
40367859Sdougb  mib[0] = CTL_NET;
40467859Sdougb  mib[1] = PF_ROUTE;
40567859Sdougb  mib[2] = 0;
40667859Sdougb  mib[3] = 0;
40752400Sbillf  mib[4] = NET_RT_DUMP;
40877323Sdougb  mib[5] = 0;
40977323Sdougb  if (sysctl(mib, 6, NULL, &needed, NULL, 0) < 0) {
41052400Sbillf    log_Printf(LogERROR, "route_Show: sysctl: estimate: %s\n", strerror(errno));
41152400Sbillf    return (1);
41252400Sbillf  }
41352400Sbillf  sp = malloc(needed);
41452400Sbillf  if (sp == NULL)
41552400Sbillf    return (1);
41652400Sbillf  if (sysctl(mib, 6, sp, &needed, NULL, 0) < 0) {
41752400Sbillf    log_Printf(LogERROR, "route_Show: sysctl: getroute: %s\n", strerror(errno));
41852400Sbillf    free(sp);
41952400Sbillf    return (1);
42052400Sbillf  }
42152400Sbillf  ep = sp + needed;
42252400Sbillf
42352400Sbillf  prompt_Printf(arg->prompt, "%-20s%-20sFlags  Netif\n",
42452400Sbillf                "Destination", "Gateway");
42552400Sbillf  for (cp = sp; cp < ep; cp += rtm->rtm_msglen) {
42652400Sbillf    rtm = (struct rt_msghdr *)cp;
42752400Sbillf
42852400Sbillf    route_ParseHdr(rtm, sa);
42952400Sbillf
43052400Sbillf    if (sa[RTAX_DST] && sa[RTAX_GATEWAY]) {
43152400Sbillf      p_sockaddr(arg->prompt, sa[RTAX_DST], sa[RTAX_NETMASK], 20);
43252400Sbillf      p_sockaddr(arg->prompt, sa[RTAX_GATEWAY], NULL, 20);
43352400Sbillf
43452400Sbillf      p_flags(arg->prompt, rtm->rtm_flags, 6);
43552400Sbillf      prompt_Printf(arg->prompt, " %s\n", Index2Nam(rtm->rtm_index));
43652400Sbillf    } else
43752400Sbillf      prompt_Printf(arg->prompt, "<can't parse routing entry>\n");
43852400Sbillf  }
43952400Sbillf  free(sp);
44052400Sbillf  return 0;
44152400Sbillf}
44252400Sbillf
44352400Sbillf/*
44468507Sdougb *  Delete routes associated with our interface
44568507Sdougb */
44668507Sdougbvoid
44768507Sdougbroute_IfDelete(struct bundle *bundle, int all)
44868507Sdougb{
44968507Sdougb  struct rt_msghdr *rtm;
45052400Sbillf  struct sockaddr *sa[RTAX_MAX];
45174992Sasmodai  struct sockaddr_in **in;
45252400Sbillf  struct in_addr sa_none;
45377323Sdougb  int pass;
45477323Sdougb  size_t needed;
45552400Sbillf  char *sp, *cp, *ep;
45652400Sbillf  int mib[6];
45752400Sbillf
45877323Sdougb  log_Printf(LogDEBUG, "route_IfDelete (%d)\n", bundle->iface->index);
45977323Sdougb  sa_none.s_addr = INADDR_ANY;
46077323Sdougb  in = (struct sockaddr_in **)sa;
46167949Sdougb
46267949Sdougb  mib[0] = CTL_NET;
46367949Sdougb  mib[1] = PF_ROUTE;
46467949Sdougb  mib[2] = 0;
46567949Sdougb  mib[3] = 0;
46667949Sdougb  mib[4] = NET_RT_DUMP;
46767949Sdougb  mib[5] = 0;
46867949Sdougb  if (sysctl(mib, 6, NULL, &needed, NULL, 0) < 0) {
46967949Sdougb    log_Printf(LogERROR, "route_IfDelete: sysctl: estimate: %s\n",
47067949Sdougb              strerror(errno));
47167949Sdougb    return;
47267949Sdougb  }
47367949Sdougb
47467949Sdougb  sp = malloc(needed);
47567949Sdougb  if (sp == NULL)
47667949Sdougb    return;
47767949Sdougb
47867949Sdougb  if (sysctl(mib, 6, sp, &needed, NULL, 0) < 0) {
47967949Sdougb    log_Printf(LogERROR, "route_IfDelete: sysctl: getroute: %s\n",
48067949Sdougb              strerror(errno));
48167949Sdougb    free(sp);
48252400Sbillf    return;
48352400Sbillf  }
48452400Sbillf  ep = sp + needed;
48552400Sbillf
48652400Sbillf  for (pass = 0; pass < 2; pass++) {
48752400Sbillf    /*
48852534Sbillf     * We do 2 passes.  The first deletes all cloned routes.  The second
48952534Sbillf     * deletes all non-cloned routes.  This is done to avoid
49052534Sbillf     * potential errors from trying to delete route X after route Y where
49152534Sbillf     * route X was cloned from route Y (and is no longer there 'cos it
49252534Sbillf     * may have gone with route Y).
49352534Sbillf     */
49452534Sbillf    if (RTF_WASCLONED == 0 && pass == 0)
49565115Sben      /* So we can't tell ! */
49668507Sdougb      continue;
49765115Sben    for (cp = sp; cp < ep; cp += rtm->rtm_msglen) {
49865115Sben      rtm = (struct rt_msghdr *)cp;
49965115Sben      route_ParseHdr(rtm, sa);
50052400Sbillf      if (sa[RTAX_DST] && sa[RTAX_DST]->sa_family == AF_INET) {
50152400Sbillf        log_Printf(LogDEBUG, "route_IfDelete: addrs: %x, Netif: %d (%s),"
50252400Sbillf                   " flags: %x, dst: %s ?\n", rtm->rtm_addrs, rtm->rtm_index,
50352400Sbillf                   Index2Nam(rtm->rtm_index), rtm->rtm_flags,
50452400Sbillf                   inet_ntoa(((struct sockaddr_in *)sa[RTAX_DST])->sin_addr));
50552400Sbillf        if (sa[RTAX_GATEWAY] && rtm->rtm_index == bundle->iface->index &&
50652400Sbillf            (all || (rtm->rtm_flags & RTF_GATEWAY))) {
50752400Sbillf          if (sa[RTAX_GATEWAY]->sa_family == AF_INET ||
50852400Sbillf              sa[RTAX_GATEWAY]->sa_family == AF_LINK) {
50952400Sbillf            if ((pass == 0 && (rtm->rtm_flags & RTF_WASCLONED)) ||
51052400Sbillf                (pass == 1 && !(rtm->rtm_flags & RTF_WASCLONED))) {
51177335Sdougb              log_Printf(LogDEBUG, "route_IfDelete: Remove it (pass %d)\n",
51252400Sbillf                         pass);
51352400Sbillf              rt_Set(bundle, RTM_DELETE, in[RTAX_DST]->sin_addr,
51467859Sdougb                              sa_none, sa_none, 0, 0);
51552400Sbillf            } else
51652400Sbillf              log_Printf(LogDEBUG, "route_IfDelete: Skip it (pass %d)\n", pass);
51767859Sdougb          } else
51852400Sbillf            log_Printf(LogDEBUG,
51967859Sdougb                      "route_IfDelete: Can't remove routes of %d family !\n",
52052400Sbillf                      sa[RTAX_GATEWAY]->sa_family);
52167859Sdougb        }
52252400Sbillf      }
52352400Sbillf    }
52467859Sdougb  }
52567859Sdougb  free(sp);
52667859Sdougb}
52752400Sbillf
52852400Sbillf
52952400Sbillf/*
53052400Sbillf *  Update the MTU on all routes for the given interface
53152400Sbillf */
53252400Sbillfvoid
53352400Sbillfroute_UpdateMTU(struct bundle *bundle)
53452400Sbillf{
53567949Sdougb  struct rt_msghdr *rtm;
53652400Sbillf  struct sockaddr *sa[RTAX_MAX];
53767949Sdougb  struct sockaddr_in **in;
53852400Sbillf  size_t needed;
53967949Sdougb  char *sp, *cp, *ep;
54067949Sdougb  int mib[6];
54167949Sdougb
54267949Sdougb  log_Printf(LogDEBUG, "route_UpdateMTU (%d)\n", bundle->iface->index);
54367949Sdougb  in = (struct sockaddr_in **)sa;
54467949Sdougb
54567949Sdougb  mib[0] = CTL_NET;
54667949Sdougb  mib[1] = PF_ROUTE;
54767949Sdougb  mib[2] = 0;
54852400Sbillf  mib[3] = 0;
54952400Sbillf  mib[4] = NET_RT_DUMP;
55067859Sdougb  mib[5] = 0;
55167859Sdougb  if (sysctl(mib, 6, NULL, &needed, NULL, 0) < 0) {
55267859Sdougb    log_Printf(LogERROR, "route_IfDelete: sysctl: estimate: %s\n",
55352400Sbillf              strerror(errno));
55452400Sbillf    return;
55552400Sbillf  }
55652400Sbillf
55752400Sbillf  sp = malloc(needed);
55852400Sbillf  if (sp == NULL)
55952400Sbillf    return;
56052400Sbillf
56152400Sbillf  if (sysctl(mib, 6, sp, &needed, NULL, 0) < 0) {
56252400Sbillf    log_Printf(LogERROR, "route_IfDelete: sysctl: getroute: %s\n",
56352400Sbillf              strerror(errno));
56452400Sbillf    free(sp);
56552400Sbillf    return;
56652400Sbillf  }
56752400Sbillf  ep = sp + needed;
56852400Sbillf
56952400Sbillf  for (cp = sp; cp < ep; cp += rtm->rtm_msglen) {
57052400Sbillf    rtm = (struct rt_msghdr *)cp;
57152400Sbillf    route_ParseHdr(rtm, sa);
57252400Sbillf    if (sa[RTAX_DST] && sa[RTAX_DST]->sa_family == AF_INET &&
57352400Sbillf        sa[RTAX_GATEWAY] && /* sa[RTAX_NETMASK] && */
57467949Sdougb        rtm->rtm_index == bundle->iface->index &&
57552400Sbillf        (sa[RTAX_GATEWAY]->sa_family == AF_INET ||
57652400Sbillf         sa[RTAX_GATEWAY]->sa_family == AF_LINK)) {
57752400Sbillf      log_Printf(LogTCPIP, "route_UpdateMTU: Netif: %d (%s), dst %s, mtu %d\n",
57852400Sbillf                 rtm->rtm_index, Index2Nam(rtm->rtm_index),
57952400Sbillf                 inet_ntoa(((struct sockaddr_in *)sa[RTAX_DST])->sin_addr),
58052400Sbillf                 bundle->mtu);
58167949Sdougb      rt_Update(bundle, in[RTAX_DST]->sin_addr,
58277323Sdougb                         in[RTAX_GATEWAY]->sin_addr);
58377323Sdougb    }
58467949Sdougb  }
58552400Sbillf
58652400Sbillf  free(sp);
58777323Sdougb}
58877323Sdougb
58952400Sbillfint
59052400SbillfGetIfIndex(char *name)
59152400Sbillf{
59264625Sgshapiro  int idx;
59352400Sbillf  const char *got;
59452400Sbillf
59552400Sbillf  idx = 1;
59652400Sbillf  while (strcmp(got = Index2Nam(idx), "???"))
59752400Sbillf    if (!strcmp(got, name))
59852400Sbillf      return idx;
59967949Sdougb    else
60052400Sbillf      idx++;
60152400Sbillf  return -1;
60252400Sbillf}
60352400Sbillf
60477335Sdougbvoid
60577335Sdougbroute_Change(struct bundle *bundle, struct sticky_route *r,
60652400Sbillf             struct in_addr me, struct in_addr peer, struct in_addr dns[2])
60752400Sbillf{
60867859Sdougb  struct in_addr none, del;
60967859Sdougb
61067859Sdougb  none.s_addr = INADDR_ANY;
61167859Sdougb  for (; r; r = r->next) {
61267859Sdougb    if ((r->type & ROUTE_DSTMYADDR) && r->dst.s_addr != me.s_addr) {
61352400Sbillf      del.s_addr = r->dst.s_addr & r->mask.s_addr;
61452400Sbillf      rt_Set(bundle, RTM_DELETE, del, none, none, 1, 0);
61552400Sbillf      r->dst = me;
61652400Sbillf      if (r->type & ROUTE_GWHISADDR)
61752400Sbillf        r->gw = peer;
61852400Sbillf    } else if ((r->type & ROUTE_DSTHISADDR) && r->dst.s_addr != peer.s_addr) {
61967949Sdougb      del.s_addr = r->dst.s_addr & r->mask.s_addr;
62052400Sbillf      rt_Set(bundle, RTM_DELETE, del, none, none, 1, 0);
62152400Sbillf      r->dst = peer;
62252400Sbillf      if (r->type & ROUTE_GWHISADDR)
62367859Sdougb        r->gw = peer;
62467859Sdougb    } else if ((r->type & ROUTE_DSTDNS0) && r->dst.s_addr != peer.s_addr) {
62577335Sdougb      del.s_addr = r->dst.s_addr & r->mask.s_addr;
62677335Sdougb      rt_Set(bundle, RTM_DELETE, del, none, none, 1, 0);
62777335Sdougb      r->dst = dns[0];
62877335Sdougb      if (r->type & ROUTE_GWHISADDR)
62977335Sdougb        r->gw = peer;
63067859Sdougb    } else if ((r->type & ROUTE_DSTDNS1) && r->dst.s_addr != peer.s_addr) {
63152400Sbillf      del.s_addr = r->dst.s_addr & r->mask.s_addr;
63252400Sbillf      rt_Set(bundle, RTM_DELETE, del, none, none, 1, 0);
63352400Sbillf      r->dst = dns[1];
63452400Sbillf      if (r->type & ROUTE_GWHISADDR)
63552400Sbillf        r->gw = peer;
63652400Sbillf    } else if ((r->type & ROUTE_GWHISADDR) && r->gw.s_addr != peer.s_addr)
63752400Sbillf      r->gw = peer;
63852400Sbillf    rt_Set(bundle, RTM_ADD, r->dst, r->gw, r->mask, 1, 0);
63967949Sdougb  }
64067949Sdougb}
64167949Sdougb
64252400Sbillfvoid
64352400Sbillfroute_Add(struct sticky_route **rp, int type, struct in_addr dst,
64467949Sdougb          struct in_addr mask, struct in_addr gw)
64552400Sbillf{
64652400Sbillf  struct sticky_route *r;
64752400Sbillf  int dsttype = type & ROUTE_DSTANY;
64852400Sbillf
64952400Sbillf  r = NULL;
65052400Sbillf  while (*rp) {
65152400Sbillf    if ((dsttype && dsttype == ((*rp)->type & ROUTE_DSTANY)) ||
65252400Sbillf        (!dsttype && (*rp)->dst.s_addr == dst.s_addr)) {
65352400Sbillf      /* Oops, we already have this route - unlink it */
65452400Sbillf      free(r);			/* impossible really  */
65552400Sbillf      r = *rp;
65652400Sbillf      *rp = r->next;
65767949Sdougb    } else
65852400Sbillf      rp = &(*rp)->next;
65952400Sbillf  }
66052400Sbillf
66152400Sbillf  if (!r)
66252400Sbillf    r = (struct sticky_route *)malloc(sizeof(struct sticky_route));
66352400Sbillf  r->type = type;
66467949Sdougb  r->next = NULL;
66567949Sdougb  r->dst = dst;
66667949Sdougb  r->mask = mask;
66767949Sdougb  r->gw = gw;
66867949Sdougb  *rp = r;
66967949Sdougb}
67052400Sbillf
67152400Sbillfvoid
67252400Sbillfroute_Delete(struct sticky_route **rp, int type, struct in_addr dst)
67352400Sbillf{
67467949Sdougb  struct sticky_route *r;
67567949Sdougb  int dsttype = type & ROUTE_DSTANY;
67667949Sdougb
67752400Sbillf  for (; *rp; rp = &(*rp)->next) {
67867949Sdougb    if ((dsttype && dsttype == ((*rp)->type & ROUTE_DSTANY)) ||
67967949Sdougb        (!dsttype && dst.s_addr == ((*rp)->dst.s_addr & (*rp)->mask.s_addr))) {
68067949Sdougb      r = *rp;
68167949Sdougb      *rp = r->next;
68267949Sdougb      free(r);
68367949Sdougb      break;
68452400Sbillf    }
68552400Sbillf  }
68667949Sdougb}
68752400Sbillf
68852400Sbillfvoid
68967949Sdougbroute_DeleteAll(struct sticky_route **rp)
69067949Sdougb{
69167949Sdougb  struct sticky_route *r, *rn;
69267949Sdougb
69367949Sdougb  for (r = *rp; r; r = rn) {
69477325Sdougb    rn = r->next;
69577325Sdougb    free(r);
69677325Sdougb  }
69777325Sdougb  *rp = NULL;
69877325Sdougb}
69977325Sdougb
70077325Sdougbvoid
70177325Sdougbroute_ShowSticky(struct prompt *p, struct sticky_route *r, const char *tag,
70277325Sdougb                 int indent)
70377325Sdougb{
70477325Sdougb  int def;
70577325Sdougb  int tlen = strlen(tag);
70677325Sdougb
70777325Sdougb  if (tlen + 2 > indent)
70877325Sdougb    prompt_Printf(p, "%s:\n%*s", tag, indent, "");
70977325Sdougb  else
71067949Sdougb    prompt_Printf(p, "%s:%*s", tag, indent - tlen - 1, "");
71167949Sdougb
71267949Sdougb  for (; r; r = r->next) {
71352400Sbillf    def = r->dst.s_addr == INADDR_ANY && r->mask.s_addr == INADDR_ANY;
71452400Sbillf
71552400Sbillf    prompt_Printf(p, "%*sadd ", tlen ? 0 : indent, "");
71652400Sbillf    tlen = 0;
71752400Sbillf    if (r->type & ROUTE_DSTMYADDR)
71852400Sbillf      prompt_Printf(p, "MYADDR");
71952400Sbillf    else if (r->type & ROUTE_DSTHISADDR)
72073651Sdougb      prompt_Printf(p, "HISADDR");
72173651Sdougb    else if (r->type & ROUTE_DSTDNS0)
72252400Sbillf      prompt_Printf(p, "DNS0");
72367949Sdougb    else if (r->type & ROUTE_DSTDNS1)
72473651Sdougb      prompt_Printf(p, "DNS1");
72567949Sdougb    else if (!def)
72667949Sdougb      prompt_Printf(p, "%s", inet_ntoa(r->dst));
72767949Sdougb
72867949Sdougb    if (def)
72952400Sbillf      prompt_Printf(p, "default ");
73052400Sbillf    else
73152400Sbillf      prompt_Printf(p, " %s ", inet_ntoa(r->mask));
73252400Sbillf
73352400Sbillf    if (r->type & ROUTE_GWHISADDR)
73452400Sbillf      prompt_Printf(p, "HISADDR\n");
73552400Sbillf    else
73652400Sbillf      prompt_Printf(p, "%s\n", inet_ntoa(r->gw));
73752400Sbillf  }
73852400Sbillf}
73952400Sbillf
74067949Sdougbstruct rtmsg {
74152400Sbillf  struct rt_msghdr m_rtm;
74252400Sbillf  char m_space[64];
74352400Sbillf};
74477323Sdougb
74577323Sdougbint
74677323Sdougbrt_Set(struct bundle *bundle, int cmd, struct in_addr dst,
74767859Sdougb                struct in_addr gateway, struct in_addr mask, int bang, int ssh)
74852400Sbillf{
74952400Sbillf  struct rtmsg rtmes;
75052400Sbillf  int s, nb, wb;
75158910Salfred  char *cp;
75258910Salfred  const char *cmdstr;
75352400Sbillf  struct sockaddr_in rtdata;
75452400Sbillf  int result = 1;
75552400Sbillf
75652400Sbillf  if (bang)
75752400Sbillf    cmdstr = (cmd == RTM_ADD ? "Add!" : "Delete!");
75852400Sbillf  else
75952400Sbillf    cmdstr = (cmd == RTM_ADD ? "Add" : "Delete");
76052400Sbillf  s = ID0socket(PF_ROUTE, SOCK_RAW, 0);
76152400Sbillf  if (s < 0) {
76252400Sbillf    log_Printf(LogERROR, "rt_Set: socket(): %s\n", strerror(errno));
76352534Sbillf    return result;
76452400Sbillf  }
76552400Sbillf  memset(&rtmes, '\0', sizeof rtmes);
76652400Sbillf  rtmes.m_rtm.rtm_version = RTM_VERSION;
76752400Sbillf  rtmes.m_rtm.rtm_type = cmd;
76852400Sbillf  rtmes.m_rtm.rtm_addrs = RTA_DST;
76952400Sbillf  rtmes.m_rtm.rtm_seq = ++bundle->routing_seq;
77052400Sbillf  rtmes.m_rtm.rtm_pid = getpid();
77177335Sdougb  rtmes.m_rtm.rtm_flags = RTF_UP | RTF_GATEWAY | RTF_STATIC;
77252400Sbillf
77352400Sbillf  if (cmd == RTM_ADD) {
77452400Sbillf    if (bundle->ncp.ipcp.cfg.sendpipe > 0) {
77552400Sbillf      rtmes.m_rtm.rtm_rmx.rmx_sendpipe = bundle->ncp.ipcp.cfg.sendpipe;
77667859Sdougb      rtmes.m_rtm.rtm_inits |= RTV_SPIPE;
77767859Sdougb    }
77867859Sdougb    if (bundle->ncp.ipcp.cfg.recvpipe > 0) {
77952400Sbillf      rtmes.m_rtm.rtm_rmx.rmx_recvpipe = bundle->ncp.ipcp.cfg.recvpipe;
78052400Sbillf      rtmes.m_rtm.rtm_inits |= RTV_RPIPE;
78152400Sbillf    }
78252400Sbillf  }
78352400Sbillf
78452400Sbillf  memset(&rtdata, '\0', sizeof rtdata);
78552400Sbillf  rtdata.sin_len = sizeof rtdata;
78652400Sbillf  rtdata.sin_family = AF_INET;
78752400Sbillf  rtdata.sin_port = 0;
78852400Sbillf  rtdata.sin_addr = dst;
78952400Sbillf
79052400Sbillf  cp = rtmes.m_space;
79152400Sbillf  memcpy(cp, &rtdata, rtdata.sin_len);
79252400Sbillf  cp += rtdata.sin_len;
79352400Sbillf  if (cmd == RTM_ADD) {
79452400Sbillf    if (gateway.s_addr == INADDR_ANY) {
79568153Sdougb      if (!ssh)
79668153Sdougb        log_Printf(LogERROR, "rt_Set: Cannot add a route with"
79768153Sdougb                   " destination 0.0.0.0\n");
79873651Sdougb      close(s);
79973651Sdougb      return result;
80073651Sdougb    } else {
80173651Sdougb      rtdata.sin_addr = gateway;
80277323Sdougb      memcpy(cp, &rtdata, rtdata.sin_len);
80377323Sdougb      cp += rtdata.sin_len;
80473651Sdougb      rtmes.m_rtm.rtm_addrs |= RTA_GATEWAY;
80573651Sdougb    }
80673651Sdougb  }
80773651Sdougb
80868153Sdougb  if (dst.s_addr == INADDR_ANY)
80977323Sdougb    mask.s_addr = INADDR_ANY;
81077323Sdougb
81168153Sdougb  if (cmd == RTM_ADD || dst.s_addr == INADDR_ANY) {
81273651Sdougb    rtdata.sin_addr = mask;
81373651Sdougb    memcpy(cp, &rtdata, rtdata.sin_len);
81468153Sdougb    cp += rtdata.sin_len;
81568153Sdougb    rtmes.m_rtm.rtm_addrs |= RTA_NETMASK;
81668153Sdougb  }
81773651Sdougb
81873651Sdougb  nb = cp - (char *)&rtmes;
81973651Sdougb  rtmes.m_rtm.rtm_msglen = nb;
82073651Sdougb  wb = ID0write(s, &rtmes, nb);
82173651Sdougb  if (wb < 0) {
82277335Sdougb    log_Printf(LogTCPIP, "rt_Set failure:\n");
82373651Sdougb    log_Printf(LogTCPIP, "rt_Set:  Cmd = %s\n", cmdstr);
82473651Sdougb    log_Printf(LogTCPIP, "rt_Set:  Dst = %s\n", inet_ntoa(dst));
82573651Sdougb    log_Printf(LogTCPIP, "rt_Set:  Gateway = %s\n",
82673651Sdougb               inet_ntoa(gateway));
82777335Sdougb    log_Printf(LogTCPIP, "rt_Set:  Mask = %s\n", inet_ntoa(mask));
82877335Sdougbfailed:
82973651Sdougb    if (cmd == RTM_ADD && (rtmes.m_rtm.rtm_errno == EEXIST ||
83073651Sdougb                           (rtmes.m_rtm.rtm_errno == 0 && errno == EEXIST))) {
83177335Sdougb      if (!bang) {
83277335Sdougb        log_Printf(LogWARN, "Add route failed: %s already exists\n",
83377335Sdougb		  dst.s_addr == 0 ? "default" : inet_ntoa(dst));
83477335Sdougb        result = 0;	/* Don't add to our dynamic list */
83577335Sdougb      } else {
83677335Sdougb        rtmes.m_rtm.rtm_type = cmd = RTM_CHANGE;
83773651Sdougb        if ((wb = ID0write(s, &rtmes, nb)) < 0)
83877335Sdougb          goto failed;
83977335Sdougb      }
84077335Sdougb    } else if (cmd == RTM_DELETE &&
84177335Sdougb             (rtmes.m_rtm.rtm_errno == ESRCH ||
84273651Sdougb              (rtmes.m_rtm.rtm_errno == 0 && errno == ESRCH))) {
84373651Sdougb      if (!bang)
84473651Sdougb        log_Printf(LogWARN, "Del route failed: %s: Non-existent\n",
84573651Sdougb                  inet_ntoa(dst));
84673651Sdougb    } else if (rtmes.m_rtm.rtm_errno == 0) {
84773651Sdougb      if (!ssh || errno != ENETUNREACH)
84852400Sbillf        log_Printf(LogWARN, "%s route failed: %s: errno: %s\n", cmdstr,
84952400Sbillf                   inet_ntoa(dst), strerror(errno));
85052400Sbillf    } else
85152400Sbillf      log_Printf(LogWARN, "%s route failed: %s: %s\n",
85277335Sdougb		 cmdstr, inet_ntoa(dst), strerror(rtmes.m_rtm.rtm_errno));
85377335Sdougb  }
85477335Sdougb
85552400Sbillf  log_Printf(LogDEBUG, "wrote %d: cmd = %s, dst = %x, gateway = %x\n",
85652400Sbillf            wb, cmdstr, (unsigned)dst.s_addr, (unsigned)gateway.s_addr);
85752400Sbillf  close(s);
85852400Sbillf
85952400Sbillf  return result;
86052400Sbillf}
86152400Sbillf
86277335Sdougbvoid
86377335Sdougbrt_Update(struct bundle *bundle, struct in_addr dst, struct in_addr gw)
86477335Sdougb{
86577335Sdougb  struct rtmsg rtmes;
86677335Sdougb  int s, wb;
86777335Sdougb  struct sockaddr_in rtdata;
86877335Sdougb
86977335Sdougb  s = ID0socket(PF_ROUTE, SOCK_RAW, 0);
87077335Sdougb  if (s < 0) {
87177335Sdougb    log_Printf(LogERROR, "rt_Update: socket(): %s\n", strerror(errno));
87252400Sbillf    return;
87352400Sbillf  }
87452400Sbillf
87552400Sbillf  memset(&rtmes, '\0', sizeof rtmes);
87652400Sbillf  rtmes.m_rtm.rtm_version = RTM_VERSION;
87752400Sbillf  rtmes.m_rtm.rtm_type = RTM_CHANGE;
87852400Sbillf  rtmes.m_rtm.rtm_addrs = RTA_DST;
87952400Sbillf  rtmes.m_rtm.rtm_seq = ++bundle->routing_seq;
88077335Sdougb  rtmes.m_rtm.rtm_pid = getpid();
88177335Sdougb  rtmes.m_rtm.rtm_flags = RTF_UP | RTF_GATEWAY | RTF_STATIC;
88277335Sdougb
88352400Sbillf  if (bundle->ncp.ipcp.cfg.sendpipe > 0) {
88452400Sbillf    rtmes.m_rtm.rtm_rmx.rmx_sendpipe = bundle->ncp.ipcp.cfg.sendpipe;
88552400Sbillf    rtmes.m_rtm.rtm_inits |= RTV_SPIPE;
88652400Sbillf  }
88752400Sbillf
88852400Sbillf  if (bundle->ncp.ipcp.cfg.recvpipe > 0) {
88952400Sbillf    rtmes.m_rtm.rtm_rmx.rmx_recvpipe = bundle->ncp.ipcp.cfg.recvpipe;
89052400Sbillf    rtmes.m_rtm.rtm_inits |= RTV_RPIPE;
89177335Sdougb  }
89277335Sdougb
89377335Sdougb  rtmes.m_rtm.rtm_rmx.rmx_mtu = bundle->mtu;
89477335Sdougb  rtmes.m_rtm.rtm_inits |= RTV_MTU;
89577335Sdougb
89677335Sdougb  memset(&rtdata, '\0', sizeof rtdata);
89777335Sdougb  rtdata.sin_len = sizeof rtdata;
89877335Sdougb  rtdata.sin_family = AF_INET;
89977335Sdougb  rtdata.sin_port = 0;
90052400Sbillf  rtdata.sin_addr = dst;
90152400Sbillf
90252400Sbillf  memcpy(rtmes.m_space, &rtdata, rtdata.sin_len);
90352400Sbillf  rtmes.m_rtm.rtm_msglen = rtmes.m_space + rtdata.sin_len - (char *)&rtmes;
90452400Sbillf
90567949Sdougb  wb = ID0write(s, &rtmes, rtmes.m_rtm.rtm_msglen);
90667949Sdougb  if (wb < 0) {
90767949Sdougb    log_Printf(LogTCPIP, "rt_Update failure:\n");
90867949Sdougb    log_Printf(LogTCPIP, "rt_Update:  Dst = %s\n", inet_ntoa(dst));
90952400Sbillf    log_Printf(LogTCPIP, "rt_Update:  Gateway = %s\n", inet_ntoa(gw));
91067850Sdougb
911    if (rtmes.m_rtm.rtm_errno == 0)
912      log_Printf(LogWARN, "%s: Change route failed: errno: %s\n",
913                 inet_ntoa(dst), strerror(errno));
914    else
915      log_Printf(LogWARN, "%s: Change route failed: %s\n",
916		 inet_ntoa(dst), strerror(rtmes.m_rtm.rtm_errno));
917  }
918  log_Printf(LogDEBUG, "wrote %d: cmd = Change, dst = %x, gateway = %x\n",
919            wb, (unsigned)dst.s_addr, (unsigned)gw.s_addr);
920  close(s);
921}
922