route.c revision 36285
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.42.2.25 1998/05/16 21:19:00 brian Exp $
21 *
22 */
23
24#include <sys/types.h>
25#include <sys/socket.h>
26#include <net/if_types.h>
27#include <net/route.h>
28#include <net/if.h>
29#include <netinet/in.h>
30#include <arpa/inet.h>
31#include <net/if_dl.h>
32#include <netinet/in_systm.h>
33#include <netinet/ip.h>
34#include <sys/un.h>
35
36#include <errno.h>
37#include <stdio.h>
38#include <stdlib.h>
39#include <string.h>
40#include <sys/sysctl.h>
41#include <termios.h>
42
43#include "command.h"
44#include "mbuf.h"
45#include "log.h"
46#include "defs.h"
47#include "iplist.h"
48#include "timer.h"
49#include "throughput.h"
50#include "lqr.h"
51#include "hdlc.h"
52#include "fsm.h"
53#include "lcp.h"
54#include "ccp.h"
55#include "link.h"
56#include "slcompress.h"
57#include "ipcp.h"
58#include "filter.h"
59#include "descriptor.h"
60#include "mp.h"
61#include "bundle.h"
62#include "route.h"
63#include "prompt.h"
64
65static void
66p_sockaddr(struct prompt *prompt, struct sockaddr *phost,
67           struct sockaddr *pmask, int width)
68{
69  char buf[29];
70  struct sockaddr_in *ihost = (struct sockaddr_in *)phost;
71  struct sockaddr_in *mask = (struct sockaddr_in *)pmask;
72  struct sockaddr_dl *dl = (struct sockaddr_dl *)phost;
73
74  switch (phost->sa_family) {
75  case AF_INET:
76    if (!phost)
77      buf[0] = '\0';
78    else if (ihost->sin_addr.s_addr == INADDR_ANY)
79      strcpy(buf, "default");
80    else if (!mask)
81      strcpy(buf, inet_ntoa(ihost->sin_addr));
82    else {
83      u_int msk = ntohl(mask->sin_addr.s_addr);
84      u_int tst;
85      int bits;
86      int len;
87      struct sockaddr_in net;
88
89      for (tst = 1, bits=32; tst; tst <<= 1, bits--)
90        if (msk & tst)
91          break;
92
93      for (tst <<=1; tst; tst <<= 1)
94        if (!(msk & tst))
95          break;
96
97      net.sin_addr.s_addr = ihost->sin_addr.s_addr & mask->sin_addr.s_addr;
98      strcpy(buf, inet_ntoa(net.sin_addr));
99      for (len = strlen(buf); len > 3; buf[len-=2] = '\0')
100        if (strcmp(buf+len-2, ".0"))
101          break;
102
103      if (tst)    /* non-contiguous :-( */
104        sprintf(buf+strlen(buf),"&0x%08x", msk);
105      else
106        sprintf(buf+strlen(buf), "/%d", bits);
107    }
108    break;
109
110  case AF_LINK:
111    if (dl->sdl_nlen)
112      snprintf(buf, sizeof buf, "%.*s", dl->sdl_nlen, dl->sdl_data);
113    else if (dl->sdl_alen) {
114      if (dl->sdl_type == IFT_ETHER) {
115        if (dl->sdl_alen < sizeof buf / 3) {
116          int f;
117          u_char *MAC;
118
119          MAC = (u_char *)dl->sdl_data + dl->sdl_nlen;
120          for (f = 0; f < dl->sdl_alen; f++)
121            sprintf(buf+f*3, "%02x:", MAC[f]);
122          buf[f*3-1] = '\0';
123        } else
124	  strcpy(buf, "??:??:??:??:??:??");
125      } else
126        sprintf(buf, "<IFT type %d>", dl->sdl_type);
127    }  else if (dl->sdl_slen)
128      sprintf(buf, "<slen %d?>", dl->sdl_slen);
129    else
130      sprintf(buf, "link#%d", dl->sdl_index);
131    break;
132
133  default:
134    sprintf(buf, "<AF type %d>", phost->sa_family);
135    break;
136  }
137
138  prompt_Printf(prompt, "%-*s ", width-1, buf);
139}
140
141static struct bits {
142  u_long b_mask;
143  char b_val;
144} bits[] = {
145  { RTF_UP, 'U' },
146  { RTF_GATEWAY, 'G' },
147  { RTF_HOST, 'H' },
148  { RTF_REJECT, 'R' },
149  { RTF_DYNAMIC, 'D' },
150  { RTF_MODIFIED, 'M' },
151  { RTF_DONE, 'd' },
152  { RTF_CLONING, 'C' },
153  { RTF_XRESOLVE, 'X' },
154  { RTF_LLINFO, 'L' },
155  { RTF_STATIC, 'S' },
156  { RTF_PROTO1, '1' },
157  { RTF_PROTO2, '2' },
158  { RTF_BLACKHOLE, 'B' },
159#ifdef RTF_WASCLONED
160  { RTF_WASCLONED, 'W' },
161#endif
162#ifdef RTF_PRCLONING
163  { RTF_PRCLONING, 'c' },
164#endif
165#ifdef RTF_PROTO3
166  { RTF_PROTO3, '3' },
167#endif
168#ifdef RTF_BROADCAST
169  { RTF_BROADCAST, 'b' },
170#endif
171  { 0, '\0' }
172};
173
174#ifndef RTF_WASCLONED
175#define RTF_WASCLONED (0)
176#endif
177
178static void
179p_flags(struct prompt *prompt, u_long f, int max)
180{
181  char name[33], *flags;
182  register struct bits *p = bits;
183
184  if (max > sizeof name - 1)
185    max = sizeof name - 1;
186
187  for (flags = name; p->b_mask && flags - name < max; p++)
188    if (p->b_mask & f)
189      *flags++ = p->b_val;
190  *flags = '\0';
191  prompt_Printf(prompt, "%-*.*s", max, max, name);
192}
193
194const char *
195Index2Nam(int idx)
196{
197  static char **ifs;
198  static int nifs, debug_done;
199
200  if (!nifs) {
201    int mib[6], have, had;
202    size_t needed;
203    char *buf, *ptr, *end;
204    struct sockaddr_dl *dl;
205    struct if_msghdr *ifm;
206
207    mib[0] = CTL_NET;
208    mib[1] = PF_ROUTE;
209    mib[2] = 0;
210    mib[3] = 0;
211    mib[4] = NET_RT_IFLIST;
212    mib[5] = 0;
213
214    if (sysctl(mib, 6, NULL, &needed, NULL, 0) < 0) {
215      log_Printf(LogERROR, "Index2Nam: sysctl: estimate: %s\n", strerror(errno));
216      return "???";
217    }
218    if ((buf = malloc(needed)) == NULL)
219      return "???";
220    if (sysctl(mib, 6, buf, &needed, NULL, 0) < 0) {
221      free(buf);
222      return "???";
223    }
224    end = buf + needed;
225
226    have = 0;
227    for (ptr = buf; ptr < end; ptr += ifm->ifm_msglen) {
228      ifm = (struct if_msghdr *)ptr;
229      dl = (struct sockaddr_dl *)(ifm + 1);
230      if (ifm->ifm_index > 0) {
231        if (ifm->ifm_index > have) {
232          had = have;
233          have = ifm->ifm_index + 5;
234          if (had)
235            ifs = (char **)realloc(ifs, sizeof(char *) * have);
236          else
237            ifs = (char **)malloc(sizeof(char *) * have);
238          if (!ifs) {
239            log_Printf(LogDEBUG, "Index2Nam: %s\n", strerror(errno));
240            nifs = 0;
241            return "???";
242          }
243          memset(ifs + had, '\0', sizeof(char *) * (have - had));
244        }
245        if (ifs[ifm->ifm_index-1] == NULL) {
246          ifs[ifm->ifm_index-1] = (char *)malloc(dl->sdl_nlen+1);
247          memcpy(ifs[ifm->ifm_index-1], dl->sdl_data, dl->sdl_nlen);
248          ifs[ifm->ifm_index-1][dl->sdl_nlen] = '\0';
249          if (nifs < ifm->ifm_index)
250            nifs = ifm->ifm_index;
251        }
252      } else if (log_IsKept(LogDEBUG))
253        log_Printf(LogDEBUG, "Skipping out-of-range interface %d!\n",
254                  ifm->ifm_index);
255    }
256    free(buf);
257  }
258
259  if (log_IsKept(LogDEBUG) && !debug_done) {
260    int f;
261
262    log_Printf(LogDEBUG, "Found the following interfaces:\n");
263    for (f = 0; f < nifs; f++)
264      if (ifs[f] != NULL)
265        log_Printf(LogDEBUG, " Index %d, name \"%s\"\n", f+1, ifs[f]);
266    debug_done = 1;
267  }
268
269  if (idx < 1 || idx > nifs || ifs[idx-1] == NULL)
270    return "???";
271
272  return ifs[idx-1];
273}
274
275int
276route_Show(struct cmdargs const *arg)
277{
278  struct rt_msghdr *rtm;
279  struct sockaddr *sa_dst, *sa_gw, *sa_mask;
280  char *sp, *ep, *cp, *wp;
281  size_t needed;
282  int mib[6];
283
284  mib[0] = CTL_NET;
285  mib[1] = PF_ROUTE;
286  mib[2] = 0;
287  mib[3] = 0;
288  mib[4] = NET_RT_DUMP;
289  mib[5] = 0;
290  if (sysctl(mib, 6, NULL, &needed, NULL, 0) < 0) {
291    log_Printf(LogERROR, "route_Show: sysctl: estimate: %s\n", strerror(errno));
292    return (1);
293  }
294  if (needed < 0)
295    return (1);
296  sp = malloc(needed);
297  if (sp == NULL)
298    return (1);
299  if (sysctl(mib, 6, sp, &needed, NULL, 0) < 0) {
300    log_Printf(LogERROR, "route_Show: sysctl: getroute: %s\n", strerror(errno));
301    free(sp);
302    return (1);
303  }
304  ep = sp + needed;
305
306  prompt_Printf(arg->prompt, "%-20s%-20sFlags  Netif\n",
307                "Destination", "Gateway");
308  for (cp = sp; cp < ep; cp += rtm->rtm_msglen) {
309    rtm = (struct rt_msghdr *) cp;
310    wp = (char *)(rtm+1);
311
312    if (rtm->rtm_addrs & RTA_DST) {
313      sa_dst = (struct sockaddr *)wp;
314      wp += sa_dst->sa_len;
315    } else
316      sa_dst = NULL;
317
318    if (rtm->rtm_addrs & RTA_GATEWAY) {
319      sa_gw = (struct sockaddr *)wp;
320      wp += sa_gw->sa_len;
321    } else
322      sa_gw = NULL;
323
324    if (rtm->rtm_addrs & RTA_NETMASK) {
325      sa_mask = (struct sockaddr *)wp;
326      wp += sa_mask->sa_len;
327    } else
328      sa_mask = NULL;
329
330    p_sockaddr(arg->prompt, sa_dst, sa_mask, 20);
331    p_sockaddr(arg->prompt, sa_gw, NULL, 20);
332
333    p_flags(arg->prompt, rtm->rtm_flags, 6);
334    prompt_Printf(arg->prompt, " %s\n", Index2Nam(rtm->rtm_index));
335  }
336  free(sp);
337  return 0;
338}
339
340/*
341 *  Delete routes associated with our interface
342 */
343void
344route_IfDelete(struct bundle *bundle, int all)
345{
346  struct rt_msghdr *rtm;
347  struct sockaddr *sa;
348  struct in_addr sa_dst, sa_none;
349  int pass;
350  size_t needed;
351  char *sp, *cp, *ep;
352  int mib[6];
353
354  log_Printf(LogDEBUG, "route_IfDelete (%d)\n", bundle->ifp.Index);
355  sa_none.s_addr = INADDR_ANY;
356
357  mib[0] = CTL_NET;
358  mib[1] = PF_ROUTE;
359  mib[2] = 0;
360  mib[3] = 0;
361  mib[4] = NET_RT_DUMP;
362  mib[5] = 0;
363  if (sysctl(mib, 6, NULL, &needed, NULL, 0) < 0) {
364    log_Printf(LogERROR, "route_IfDelete: sysctl: estimate: %s\n",
365	      strerror(errno));
366    return;
367  }
368  if (needed < 0)
369    return;
370
371  sp = malloc(needed);
372  if (sp == NULL)
373    return;
374
375  if (sysctl(mib, 6, sp, &needed, NULL, 0) < 0) {
376    log_Printf(LogERROR, "route_IfDelete: sysctl: getroute: %s\n",
377	      strerror(errno));
378    free(sp);
379    return;
380  }
381  ep = sp + needed;
382
383  for (pass = 0; pass < 2; pass++) {
384    /*
385     * We do 2 passes.  The first deletes all cloned routes.  The second
386     * deletes all non-cloned routes.  This is necessary to avoid
387     * potential errors from trying to delete route X after route Y where
388     * route X was cloned from route Y (and is no longer there 'cos it
389     * may have gone with route Y).
390     */
391    if (RTF_WASCLONED == 0 && pass == 0)
392      /* So we can't tell ! */
393      continue;
394    for (cp = sp; cp < ep; cp += rtm->rtm_msglen) {
395      rtm = (struct rt_msghdr *) cp;
396      sa = (struct sockaddr *) (rtm + 1);
397      log_Printf(LogDEBUG, "route_IfDelete: addrs: %x, Netif: %d (%s),"
398                " flags: %x, dst: %s ?\n", rtm->rtm_addrs, rtm->rtm_index,
399                Index2Nam(rtm->rtm_index), rtm->rtm_flags,
400	        inet_ntoa(((struct sockaddr_in *) sa)->sin_addr));
401      if (rtm->rtm_addrs & RTA_DST && rtm->rtm_addrs & RTA_GATEWAY &&
402	  rtm->rtm_index == bundle->ifp.Index &&
403	  (all || (rtm->rtm_flags & RTF_GATEWAY))) {
404        sa_dst.s_addr = ((struct sockaddr_in *)sa)->sin_addr.s_addr;
405        sa = (struct sockaddr *)((char *)sa + sa->sa_len);
406        if (sa->sa_family == AF_INET || sa->sa_family == AF_LINK) {
407          if ((pass == 0 && (rtm->rtm_flags & RTF_WASCLONED)) ||
408              (pass == 1 && !(rtm->rtm_flags & RTF_WASCLONED))) {
409            log_Printf(LogDEBUG, "route_IfDelete: Remove it (pass %d)\n", pass);
410            bundle_SetRoute(bundle, RTM_DELETE, sa_dst, sa_none, sa_none, 0);
411          } else
412            log_Printf(LogDEBUG, "route_IfDelete: Skip it (pass %d)\n", pass);
413        } else
414          log_Printf(LogDEBUG,
415                    "route_IfDelete: Can't remove routes of %d family !\n",
416                    sa->sa_family);
417      }
418    }
419  }
420  free(sp);
421}
422
423int
424GetIfIndex(char *name)
425{
426  int idx;
427  const char *got;
428
429  idx = 1;
430  while (strcmp(got = Index2Nam(idx), "???"))
431    if (!strcmp(got, name))
432      return idx;
433    else
434      idx++;
435  return -1;
436}
437
438void
439route_Change(struct bundle *bundle, struct sticky_route *r,
440             struct in_addr me, struct in_addr peer)
441{
442  struct in_addr none, del;
443
444  none.s_addr = INADDR_ANY;
445  for (; r; r = r->next) {
446    if ((r->type & ROUTE_DSTMYADDR) && r->dst.s_addr != me.s_addr) {
447      del.s_addr = r->dst.s_addr & r->mask.s_addr;
448      bundle_SetRoute(bundle, RTM_DELETE, del, none, none, 1);
449      r->dst = me;
450      if (r->type & ROUTE_GWHISADDR)
451        r->gw = peer;
452    } else if ((r->type & ROUTE_DSTHISADDR) && r->dst.s_addr != peer.s_addr) {
453      del.s_addr = r->dst.s_addr & r->mask.s_addr;
454      bundle_SetRoute(bundle, RTM_DELETE, del, none, none, 1);
455      r->dst = peer;
456      if (r->type & ROUTE_GWHISADDR)
457        r->gw = peer;
458    } else if ((r->type & ROUTE_GWHISADDR) && r->gw.s_addr != peer.s_addr)
459      r->gw = peer;
460    bundle_SetRoute(bundle, RTM_ADD, r->dst, r->gw, r->mask, 1);
461  }
462}
463
464void
465route_Clean(struct bundle *bundle, struct sticky_route *r)
466{
467  struct in_addr none, del;
468
469  none.s_addr = INADDR_ANY;
470  for (; r; r = r->next) {
471    del.s_addr = r->dst.s_addr & r->mask.s_addr;
472    bundle_SetRoute(bundle, RTM_DELETE, del, none, none, 1);
473  }
474}
475
476void
477route_Add(struct sticky_route **rp, int type, struct in_addr dst,
478          struct in_addr mask, struct in_addr gw)
479{
480  if (type != ROUTE_STATIC) {
481    struct sticky_route *r;
482    int dsttype = type & ROUTE_DSTANY;
483
484    r = NULL;
485    while (*rp) {
486      if ((dsttype && dsttype == ((*rp)->type & ROUTE_DSTANY)) ||
487          (!dsttype && (*rp)->dst.s_addr == dst.s_addr)) {
488        r = *rp;
489        *rp = r->next;
490      } else
491        rp = &(*rp)->next;
492    }
493
494    if (!r)
495      r = (struct sticky_route *)malloc(sizeof(struct sticky_route));
496    r->type = type;
497    r->next = NULL;
498    r->dst = dst;
499    r->mask = mask;
500    r->gw = gw;
501    *rp = r;
502  }
503}
504
505void
506route_Delete(struct sticky_route **rp, int type, struct in_addr dst)
507{
508  struct sticky_route *r;
509  int dsttype = type & ROUTE_DSTANY;
510
511  for (; *rp; rp = &(*rp)->next) {
512    if ((dsttype && dsttype == ((*rp)->type & ROUTE_DSTANY)) ||
513        (!dsttype && dst.s_addr == ((*rp)->dst.s_addr & (*rp)->mask.s_addr))) {
514      r = *rp;
515      *rp = r->next;
516      free(r);
517      break;
518    }
519  }
520}
521
522void
523route_DeleteAll(struct sticky_route **rp)
524{
525  struct sticky_route *r, *rn;
526
527  for (r = *rp; r; r = rn) {
528    rn = r->next;
529    free(r);
530  }
531  *rp = NULL;
532}
533
534void
535route_ShowSticky(struct prompt *p, struct sticky_route *r)
536{
537  int def;
538
539  prompt_Printf(p, "Sticky routes:\n");
540  for (; r; r = r->next) {
541    def = r->dst.s_addr == INADDR_ANY && r->mask.s_addr == INADDR_ANY;
542
543    prompt_Printf(p, " add ");
544    if (r->type & ROUTE_DSTMYADDR)
545      prompt_Printf(p, "MYADDR");
546    else if (r->type & ROUTE_DSTHISADDR)
547      prompt_Printf(p, "HISADDR");
548    else if (!def)
549      prompt_Printf(p, "%s", inet_ntoa(r->dst));
550
551    if (def)
552      prompt_Printf(p, "default ");
553    else
554      prompt_Printf(p, " %s ", inet_ntoa(r->mask));
555
556    if (r->type & ROUTE_GWHISADDR)
557      prompt_Printf(p, "HISADDR\n");
558    else
559      prompt_Printf(p, "%s\n", inet_ntoa(r->gw));
560  }
561}
562