route.c revision 32425
1/*
2 *	      PPP Routing related Module
3 *
4 *	    Written by Toshiharu OHNO (tony-o@iij.ad.jp)
5 *
6 *   Copyright (C) 1994, Internet Initiative Japan, Inc. All rights reserverd.
7 *
8 * Redistribution and use in source and binary forms are permitted
9 * provided that the above copyright notice and this paragraph are
10 * duplicated in all such forms and that any documentation,
11 * advertising materials, and other materials related to such
12 * distribution and use acknowledge that the software was developed
13 * by the Internet Initiative Japan, Inc.  The name of the
14 * IIJ may not be used to endorse or promote products derived
15 * from this software without specific prior written permission.
16 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
17 * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
18 * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
19 *
20 * $Id: route.c,v 1.39 1998/01/11 02:59:22 brian Exp $
21 *
22 */
23
24#include <sys/param.h>
25#include <sys/time.h>
26#include <sys/socket.h>
27#include <net/if_types.h>
28#include <net/route.h>
29#include <net/if.h>
30#include <netinet/in_systm.h>
31#include <netinet/in.h>
32#include <arpa/inet.h>
33#include <net/if_dl.h>
34
35#include <errno.h>
36#include <machine/endian.h>
37#include <stdio.h>
38#include <stdlib.h>
39#include <string.h>
40#include <sys/ioctl.h>
41#include <sys/sysctl.h>
42#include <unistd.h>
43
44#include "command.h"
45#include "mbuf.h"
46#include "log.h"
47#include "loadalias.h"
48#include "defs.h"
49#include "vars.h"
50#include "id.h"
51#include "os.h"
52#include "ipcp.h"
53#include "iplist.h"
54#include "route.h"
55
56static int IfIndex;
57static const char *Index2Nam(int);
58
59struct rtmsg {
60  struct rt_msghdr m_rtm;
61  char m_space[64];
62};
63
64static int seqno;
65
66void
67OsSetRoute(int cmd,
68	   struct in_addr dst,
69	   struct in_addr gateway,
70	   struct in_addr mask,
71	   int bang)
72{
73  struct rtmsg rtmes;
74  int s, nb, wb;
75  char *cp;
76  const char *cmdstr;
77  struct sockaddr_in rtdata;
78
79  if (bang)
80    cmdstr = (cmd == RTM_ADD ? "Add!" : "Delete!");
81  else
82    cmdstr = (cmd == RTM_ADD ? "Add" : "Delete");
83  s = ID0socket(PF_ROUTE, SOCK_RAW, 0);
84  if (s < 0) {
85    LogPrintf(LogERROR, "OsSetRoute: socket(): %s\n", strerror(errno));
86    return;
87  }
88  memset(&rtmes, '\0', sizeof rtmes);
89  rtmes.m_rtm.rtm_version = RTM_VERSION;
90  rtmes.m_rtm.rtm_type = cmd;
91  rtmes.m_rtm.rtm_addrs = RTA_DST;
92  rtmes.m_rtm.rtm_seq = ++seqno;
93  rtmes.m_rtm.rtm_pid = getpid();
94  rtmes.m_rtm.rtm_flags = RTF_UP | RTF_GATEWAY | RTF_STATIC;
95
96  memset(&rtdata, '\0', sizeof rtdata);
97  rtdata.sin_len = 16;
98  rtdata.sin_family = AF_INET;
99  rtdata.sin_port = 0;
100  rtdata.sin_addr = dst;
101
102  cp = rtmes.m_space;
103  memcpy(cp, &rtdata, 16);
104  cp += 16;
105  if (cmd == RTM_ADD)
106    if (gateway.s_addr == INADDR_ANY) {
107      /* Add a route through the interface */
108      struct sockaddr_dl dl;
109      const char *iname;
110      int ilen;
111
112      iname = Index2Nam(IfIndex);
113      ilen = strlen(iname);
114      dl.sdl_len = sizeof dl - sizeof dl.sdl_data + ilen;
115      dl.sdl_family = AF_LINK;
116      dl.sdl_index = IfIndex;
117      dl.sdl_type = 0;
118      dl.sdl_nlen = ilen;
119      dl.sdl_alen = 0;
120      dl.sdl_slen = 0;
121      strncpy(dl.sdl_data, iname, sizeof dl.sdl_data);
122
123      memcpy(cp, &dl, dl.sdl_len);
124      cp += dl.sdl_len;
125      rtmes.m_rtm.rtm_addrs |= RTA_GATEWAY;
126    } else {
127      rtdata.sin_addr = gateway;
128      memcpy(cp, &rtdata, 16);
129      cp += 16;
130      rtmes.m_rtm.rtm_addrs |= RTA_GATEWAY;
131    }
132
133  if (dst.s_addr == INADDR_ANY)
134    mask.s_addr = INADDR_ANY;
135
136  if (cmd == RTM_ADD || dst.s_addr == INADDR_ANY) {
137    rtdata.sin_addr = mask;
138    memcpy(cp, &rtdata, 16);
139    cp += 16;
140    rtmes.m_rtm.rtm_addrs |= RTA_NETMASK;
141  }
142
143  nb = cp - (char *) &rtmes;
144  rtmes.m_rtm.rtm_msglen = nb;
145  wb = ID0write(s, &rtmes, nb);
146  if (wb < 0) {
147    LogPrintf(LogTCPIP, "OsSetRoute failure:\n");
148    LogPrintf(LogTCPIP, "OsSetRoute:  Cmd = %s\n", cmd);
149    LogPrintf(LogTCPIP, "OsSetRoute:  Dst = %s\n", inet_ntoa(dst));
150    LogPrintf(LogTCPIP, "OsSetRoute:  Gateway = %s\n", inet_ntoa(gateway));
151    LogPrintf(LogTCPIP, "OsSetRoute:  Mask = %s\n", inet_ntoa(mask));
152failed:
153    if (cmd == RTM_ADD && (rtmes.m_rtm.rtm_errno == EEXIST ||
154                           (rtmes.m_rtm.rtm_errno == 0 && errno == EEXIST)))
155      if (!bang)
156        LogPrintf(LogWARN, "Add route failed: %s already exists\n",
157                  inet_ntoa(dst));
158      else {
159        rtmes.m_rtm.rtm_type = cmd = RTM_CHANGE;
160        if ((wb = ID0write(s, &rtmes, nb)) < 0)
161          goto failed;
162      }
163    else if (cmd == RTM_DELETE &&
164             (rtmes.m_rtm.rtm_errno == ESRCH ||
165              (rtmes.m_rtm.rtm_errno == 0 && errno == ESRCH))) {
166      if (!bang)
167        LogPrintf(LogWARN, "Del route failed: %s: Non-existent\n",
168                  inet_ntoa(dst));
169    } else if (rtmes.m_rtm.rtm_errno == 0)
170      LogPrintf(LogWARN, "%s route failed: %s: errno: %s\n", cmdstr,
171                inet_ntoa(dst), strerror(errno));
172    else
173      LogPrintf(LogWARN, "%s route failed: %s: %s\n",
174		cmdstr, inet_ntoa(dst), strerror(rtmes.m_rtm.rtm_errno));
175  }
176  LogPrintf(LogDEBUG, "wrote %d: cmd = %s, dst = %x, gateway = %x\n",
177            wb, cmdstr, dst.s_addr, gateway.s_addr);
178  close(s);
179}
180
181static void
182p_sockaddr(struct sockaddr *phost, struct sockaddr *pmask, int width)
183{
184  char buf[29];
185  struct sockaddr_in *ihost = (struct sockaddr_in *)phost;
186  struct sockaddr_in *mask = (struct sockaddr_in *)pmask;
187  struct sockaddr_dl *dl = (struct sockaddr_dl *)phost;
188
189  switch (phost->sa_family) {
190  case AF_INET:
191    if (!phost)
192      buf[0] = '\0';
193    else if (ihost->sin_addr.s_addr == INADDR_ANY)
194      strcpy(buf, "default");
195    else if (!mask)
196      strcpy(buf, inet_ntoa(ihost->sin_addr));
197    else {
198      u_int msk = ntohl(mask->sin_addr.s_addr);
199      u_int tst;
200      int bits;
201      int len;
202      struct sockaddr_in net;
203
204      for (tst = 1, bits=32; tst; tst <<= 1, bits--)
205        if (msk & tst)
206          break;
207
208      for (tst <<=1; tst; tst <<= 1)
209        if (!(msk & tst))
210          break;
211
212      net.sin_addr.s_addr = ihost->sin_addr.s_addr & mask->sin_addr.s_addr;
213      strcpy(buf, inet_ntoa(net.sin_addr));
214      for (len = strlen(buf); len > 3; buf[len-=2] = '\0')
215        if (strcmp(buf+len-2, ".0"))
216          break;
217
218      if (tst)    /* non-contiguous :-( */
219        sprintf(buf+strlen(buf),"&0x%08x", msk);
220      else
221        sprintf(buf+strlen(buf), "/%d", bits);
222    }
223    break;
224
225  case AF_LINK:
226    if (dl->sdl_nlen)
227      snprintf(buf, sizeof buf, "%.*s", dl->sdl_nlen, dl->sdl_data);
228    else if (dl->sdl_alen)
229      if (dl->sdl_type == IFT_ETHER)
230        if (dl->sdl_alen < sizeof buf / 3) {
231          int f;
232          u_char *MAC;
233
234          MAC = (u_char *)dl->sdl_data + dl->sdl_nlen;
235          for (f = 0; f < dl->sdl_alen; f++)
236            sprintf(buf+f*3, "%02x:", MAC[f]);
237          buf[f*3-1] = '\0';
238        } else
239	  strcpy(buf, "??:??:??:??:??:??");
240      else
241        sprintf(buf, "<IFT type %d>", dl->sdl_type);
242    else if (dl->sdl_slen)
243      sprintf(buf, "<slen %d?>", dl->sdl_slen);
244    else
245      sprintf(buf, "link#%d", dl->sdl_index);
246    break;
247
248  default:
249    sprintf(buf, "<AF type %d>", phost->sa_family);
250    break;
251  }
252
253  fprintf(VarTerm, "%-*s ", width-1, buf);
254}
255
256struct bits {
257  u_long b_mask;
258  char b_val;
259} bits[] = {
260  { RTF_UP, 'U' },
261  { RTF_GATEWAY, 'G' },
262  { RTF_HOST, 'H' },
263  { RTF_REJECT, 'R' },
264  { RTF_DYNAMIC, 'D' },
265  { RTF_MODIFIED, 'M' },
266  { RTF_DONE, 'd' },
267  { RTF_CLONING, 'C' },
268  { RTF_XRESOLVE, 'X' },
269  { RTF_LLINFO, 'L' },
270  { RTF_STATIC, 'S' },
271  { RTF_PROTO1, '1' },
272  { RTF_PROTO2, '2' },
273  { RTF_BLACKHOLE, 'B' },
274#ifdef RTF_WASCLONED
275  { RTF_WASCLONED, 'W' },
276#endif
277#ifdef RTF_PRCLONING
278  { RTF_PRCLONING, 'c' },
279#endif
280#ifdef RTF_PROTO3
281  { RTF_PROTO3, '3' },
282#endif
283#ifdef RTF_BROADCAST
284  { RTF_BROADCAST, 'b' },
285#endif
286  { 0, '\0' }
287};
288
289#ifndef RTF_WASCLONED
290#define RTF_WASCLONED (0)
291#endif
292
293static void
294p_flags(u_long f, int max)
295{
296  if (VarTerm) {
297    char name[33], *flags;
298    register struct bits *p = bits;
299
300    if (max > sizeof name - 1)
301      max = sizeof name - 1;
302
303    for (flags = name; p->b_mask && flags - name < max; p++)
304      if (p->b_mask & f)
305	*flags++ = p->b_val;
306    *flags = '\0';
307    fprintf(VarTerm, "%-*.*s", max, max, name);
308  }
309}
310
311static const char *
312Index2Nam(int idx)
313{
314  static char **ifs;
315  static int nifs, debug_done;
316
317  if (!nifs) {
318    int mib[6], have, had;
319    size_t needed;
320    char *buf, *ptr, *end;
321    struct sockaddr_dl *dl;
322    struct if_msghdr *ifm;
323
324    mib[0] = CTL_NET;
325    mib[1] = PF_ROUTE;
326    mib[2] = 0;
327    mib[3] = 0;
328    mib[4] = NET_RT_IFLIST;
329    mib[5] = 0;
330
331    if (sysctl(mib, 6, NULL, &needed, NULL, 0) < 0) {
332      LogPrintf(LogERROR, "Index2Nam: sysctl: estimate: %s\n", strerror(errno));
333      return "???";
334    }
335    if ((buf = malloc(needed)) == NULL)
336      return "???";
337    if (sysctl(mib, 6, buf, &needed, NULL, 0) < 0) {
338      free(buf);
339      return "???";
340    }
341    end = buf + needed;
342
343    have = 0;
344    for (ptr = buf; ptr < end; ptr += ifm->ifm_msglen) {
345      ifm = (struct if_msghdr *)ptr;
346      dl = (struct sockaddr_dl *)(ifm + 1);
347      if (ifm->ifm_index > 0) {
348        if (ifm->ifm_index > have) {
349          had = have;
350          have = ifm->ifm_index + 5;
351          if (had)
352            ifs = (char **)realloc(ifs, sizeof(char *) * have);
353          else
354            ifs = (char **)malloc(sizeof(char *) * have);
355          if (!ifs) {
356            LogPrintf(LogDEBUG, "Index2Nam: %s\n", strerror(errno));
357            nifs = 0;
358            return "???";
359          }
360          memset(ifs + had, '\0', sizeof(char *) * (have - had));
361        }
362        if (ifs[ifm->ifm_index-1] == NULL) {
363          ifs[ifm->ifm_index-1] = (char *)malloc(dl->sdl_nlen+1);
364          memcpy(ifs[ifm->ifm_index-1], dl->sdl_data, dl->sdl_nlen);
365          ifs[ifm->ifm_index-1][dl->sdl_nlen] = '\0';
366          if (nifs < ifm->ifm_index)
367            nifs = ifm->ifm_index;
368        }
369      } else if (LogIsKept(LogDEBUG))
370        LogPrintf(LogDEBUG, "Skipping out-of-range interface %d!\n",
371                  ifm->ifm_index);
372    }
373    free(buf);
374  }
375
376  if (LogIsKept(LogDEBUG) && !debug_done) {
377    int f;
378
379    LogPrintf(LogDEBUG, "Found the following interfaces:\n");
380    for (f = 0; f < nifs; f++)
381      if (ifs[f] != NULL)
382        LogPrintf(LogDEBUG, " Index %d, name \"%s\"\n", f+1, ifs[f]);
383    debug_done = 1;
384  }
385
386  if (idx < 1 || idx > nifs || ifs[idx-1] == NULL)
387    return "???";
388
389  return ifs[idx-1];
390}
391
392int
393ShowRoute(struct cmdargs const *arg)
394{
395  struct rt_msghdr *rtm;
396  struct sockaddr *sa_dst, *sa_gw, *sa_mask;
397  char *sp, *ep, *cp, *wp;
398  size_t needed;
399  int mib[6];
400
401  if (!VarTerm)
402    return 1;
403
404  mib[0] = CTL_NET;
405  mib[1] = PF_ROUTE;
406  mib[2] = 0;
407  mib[3] = 0;
408  mib[4] = NET_RT_DUMP;
409  mib[5] = 0;
410  if (sysctl(mib, 6, NULL, &needed, NULL, 0) < 0) {
411    LogPrintf(LogERROR, "ShowRoute: sysctl: estimate: %s\n", strerror(errno));
412    return (1);
413  }
414  if (needed < 0)
415    return (1);
416  sp = malloc(needed);
417  if (sp == NULL)
418    return (1);
419  if (sysctl(mib, 6, sp, &needed, NULL, 0) < 0) {
420    LogPrintf(LogERROR, "ShowRoute: sysctl: getroute: %s\n", strerror(errno));
421    free(sp);
422    return (1);
423  }
424  ep = sp + needed;
425
426  fprintf(VarTerm, "%-20s%-20sFlags  Netif\n", "Destination", "Gateway");
427  for (cp = sp; cp < ep; cp += rtm->rtm_msglen) {
428    rtm = (struct rt_msghdr *) cp;
429    wp = (char *)(rtm+1);
430
431    if (rtm->rtm_addrs & RTA_DST) {
432      sa_dst = (struct sockaddr *)wp;
433      wp += sa_dst->sa_len;
434    } else
435      sa_dst = NULL;
436
437    if (rtm->rtm_addrs & RTA_GATEWAY) {
438      sa_gw = (struct sockaddr *)wp;
439      wp += sa_gw->sa_len;
440    } else
441      sa_gw = NULL;
442
443    if (rtm->rtm_addrs & RTA_NETMASK) {
444      sa_mask = (struct sockaddr *)wp;
445      wp += sa_mask->sa_len;
446    } else
447      sa_mask = NULL;
448
449    p_sockaddr(sa_dst, sa_mask, 20);
450    p_sockaddr(sa_gw, NULL, 20);
451
452    p_flags(rtm->rtm_flags, 6);
453    fprintf(VarTerm, " %s\n", Index2Nam(rtm->rtm_index));
454  }
455  free(sp);
456  return 0;
457}
458
459/*
460 *  Delete routes associated with our interface
461 */
462void
463DeleteIfRoutes(int all)
464{
465  struct rt_msghdr *rtm;
466  struct sockaddr *sa;
467  struct in_addr sa_dst, sa_none;
468  int pass;
469  size_t needed;
470  char *sp, *cp, *ep;
471  int mib[6];
472
473  LogPrintf(LogDEBUG, "DeleteIfRoutes (%d)\n", IfIndex);
474  sa_none.s_addr = INADDR_ANY;
475
476  mib[0] = CTL_NET;
477  mib[1] = PF_ROUTE;
478  mib[2] = 0;
479  mib[3] = 0;
480  mib[4] = NET_RT_DUMP;
481  mib[5] = 0;
482  if (sysctl(mib, 6, NULL, &needed, NULL, 0) < 0) {
483    LogPrintf(LogERROR, "DeleteIfRoutes: sysctl: estimate: %s\n",
484	      strerror(errno));
485    return;
486  }
487  if (needed < 0)
488    return;
489
490  sp = malloc(needed);
491  if (sp == NULL)
492    return;
493
494  if (sysctl(mib, 6, sp, &needed, NULL, 0) < 0) {
495    LogPrintf(LogERROR, "DeleteIfRoutes: sysctl: getroute: %s\n",
496	      strerror(errno));
497    free(sp);
498    return;
499  }
500  ep = sp + needed;
501
502  for (pass = 0; pass < 2; pass++) {
503    /*
504     * We do 2 passes.  The first deletes all cloned routes.  The second
505     * deletes all non-cloned routes.  This is necessary to avoid
506     * potential errors from trying to delete route X after route Y where
507     * route X was cloned from route Y (which is no longer there).
508     */
509    if (RTF_WASCLONED == 0 && pass == 0)
510      /* So we can't tell ! */
511      continue;
512    for (cp = sp; cp < ep; cp += rtm->rtm_msglen) {
513      rtm = (struct rt_msghdr *) cp;
514      sa = (struct sockaddr *) (rtm + 1);
515      LogPrintf(LogDEBUG, "DeleteIfRoutes: addrs: %x, Netif: %d (%s),"
516                " flags: %x, dst: %s ?\n", rtm->rtm_addrs, rtm->rtm_index,
517                Index2Nam(rtm->rtm_index), rtm->rtm_flags,
518	        inet_ntoa(((struct sockaddr_in *) sa)->sin_addr));
519      if (rtm->rtm_addrs & RTA_DST && rtm->rtm_addrs & RTA_GATEWAY &&
520	  rtm->rtm_index == IfIndex &&
521	  (all || (rtm->rtm_flags & RTF_GATEWAY))) {
522        sa_dst.s_addr = ((struct sockaddr_in *)sa)->sin_addr.s_addr;
523        sa = (struct sockaddr *)((char *)sa + sa->sa_len);
524        if (sa->sa_family == AF_INET || sa->sa_family == AF_LINK) {
525          if ((pass == 0 && (rtm->rtm_flags & RTF_WASCLONED)) ||
526              (pass == 1 && !(rtm->rtm_flags & RTF_WASCLONED))) {
527            LogPrintf(LogDEBUG, "DeleteIfRoutes: Remove it (pass %d)\n", pass);
528            OsSetRoute(RTM_DELETE, sa_dst, sa_none, sa_none, 0);
529          } else
530            LogPrintf(LogDEBUG, "DeleteIfRoutes: Skip it (pass %d)\n", pass);
531        } else
532          LogPrintf(LogDEBUG,
533                    "DeleteIfRoutes: Can't remove routes of %d family !\n",
534                    sa->sa_family);
535      }
536    }
537  }
538  free(sp);
539}
540
541int
542GetIfIndex(char *name)
543{
544  int idx;
545  const char *got;
546
547  idx = 1;
548  while (strcmp(got = Index2Nam(idx), "???"))
549    if (!strcmp(got, name))
550      return IfIndex = idx;
551    else
552      idx++;
553  return -1;
554}
555
556struct in_addr
557ChooseHisAddr(const struct in_addr gw)
558{
559  struct in_addr try;
560  int f;
561
562  for (f = 0; f < DefHisChoice.nItems; f++) {
563    try = iplist_next(&DefHisChoice);
564    LogPrintf(LogDEBUG, "ChooseHisAddr: Check item %d (%s)\n",
565              f, inet_ntoa(try));
566    if (OsTrySetIpaddress(gw, try) == 0) {
567      LogPrintf(LogIPCP, "ChooseHisAddr: Selected IP address %s\n",
568                inet_ntoa(try));
569      break;
570    }
571  }
572
573  if (f == DefHisChoice.nItems) {
574    LogPrintf(LogDEBUG, "ChooseHisAddr: All addresses in use !\n");
575    try.s_addr = INADDR_ANY;
576  }
577
578  return try;
579}
580