route.c revision 31176
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.25 1997/11/11 22:58:13 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 "mbuf.h"
45#include "log.h"
46#include "loadalias.h"
47#include "command.h"
48#include "defs.h"
49#include "vars.h"
50#include "id.h"
51#include "route.h"
52
53static int IfIndex;
54
55struct rtmsg {
56  struct rt_msghdr m_rtm;
57  char m_space[64];
58};
59
60static int seqno;
61
62void
63OsSetRoute(int cmd,
64	   struct in_addr dst,
65	   struct in_addr gateway,
66	   struct in_addr mask)
67{
68  struct rtmsg rtmes;
69  int s, nb, wb;
70  char *cp, *cmdstr;
71  u_long *lp;
72  struct sockaddr_in rtdata;
73
74  cmdstr = (cmd == RTM_ADD ? "Add" : "Delete");
75  s = ID0socket(PF_ROUTE, SOCK_RAW, 0);
76  if (s < 0) {
77    LogPrintf(LogERROR, "OsSetRoute: socket(): %s\n", strerror(errno));
78    return;
79  }
80  memset(&rtmes, '\0', sizeof(rtmes));
81  rtmes.m_rtm.rtm_version = RTM_VERSION;
82  rtmes.m_rtm.rtm_type = cmd;
83  rtmes.m_rtm.rtm_addrs = RTA_DST | RTA_NETMASK;
84  rtmes.m_rtm.rtm_seq = ++seqno;
85  rtmes.m_rtm.rtm_pid = getpid();
86  rtmes.m_rtm.rtm_flags = RTF_UP | RTF_GATEWAY | RTF_STATIC;
87
88  memset(&rtdata, '\0', sizeof(rtdata));
89  rtdata.sin_len = 16;
90  rtdata.sin_family = AF_INET;
91  rtdata.sin_port = 0;
92  rtdata.sin_addr = dst;
93
94  cp = rtmes.m_space;
95  memcpy(cp, &rtdata, 16);
96  cp += 16;
97  if (gateway.s_addr) {
98    rtdata.sin_addr = gateway;
99    memcpy(cp, &rtdata, 16);
100    cp += 16;
101    rtmes.m_rtm.rtm_addrs |= RTA_GATEWAY;
102  }
103  if (dst.s_addr == INADDR_ANY)
104    mask.s_addr = INADDR_ANY;
105
106  lp = (u_long *) cp;
107
108  if (mask.s_addr) {
109    *lp++ = 8;
110    cp += sizeof(int);
111    *lp = mask.s_addr;
112  } else
113    *lp = 0;
114  cp += sizeof(u_long);
115
116  nb = cp - (char *) &rtmes;
117  rtmes.m_rtm.rtm_msglen = nb;
118  wb = write(s, &rtmes, nb);
119  if (wb < 0) {
120    LogPrintf(LogTCPIP, "OsSetRoute: Dst = %s\n", inet_ntoa(dst));
121    LogPrintf(LogTCPIP, "OsSetRoute:  Gateway = %s\n", inet_ntoa(gateway));
122    LogPrintf(LogTCPIP, "OsSetRoute:  Mask = %s\n", inet_ntoa(mask));
123    switch (rtmes.m_rtm.rtm_errno) {
124    case EEXIST:
125      LogPrintf(LogTCPIP, "Add route failed: Already exists\n");
126      break;
127    case ESRCH:
128      LogPrintf(LogTCPIP, "Del route failed: Non-existent\n");
129      break;
130    case 0:
131      LogPrintf(LogTCPIP, "%s route failed: %s\n", cmdstr, strerror(errno));
132      break;
133    case ENOBUFS:
134    default:
135      LogPrintf(LogTCPIP, "%s route failed: %s\n",
136		cmdstr, strerror(rtmes.m_rtm.rtm_errno));
137      break;
138    }
139  }
140  LogPrintf(LogDEBUG, "wrote %d: cmd = %s, dst = %x, gateway = %x\n",
141            wb, cmdstr, dst.s_addr, gateway.s_addr);
142  close(s);
143}
144
145static void
146p_sockaddr(struct sockaddr *phost, struct sockaddr *pmask, int width)
147{
148  char buf[29], *cp;
149  struct sockaddr_in *ihost = (struct sockaddr_in *)phost;
150  struct sockaddr_in *mask = (struct sockaddr_in *)pmask;
151  struct sockaddr_dl *dl = (struct sockaddr_dl *)phost;
152
153  switch (phost->sa_family) {
154  case AF_INET:
155    if (!phost)
156      cp = "";
157    else if (ihost->sin_addr.s_addr == INADDR_ANY)
158      cp = "default";
159    else if (!mask)
160      cp = inet_ntoa(ihost->sin_addr);
161    else {
162      u_int msk = ntohl(mask->sin_addr.s_addr);
163      u_int tst;
164      int bits;
165      int len;
166      struct sockaddr_in net;
167
168      for (tst = 1, bits=32; tst; tst <<= 1, bits--)
169        if (msk & tst)
170          break;
171
172      for (tst <<=1; tst; tst <<= 1)
173        if (!(msk & tst))
174          break;
175
176      net.sin_addr.s_addr = ihost->sin_addr.s_addr & mask->sin_addr.s_addr;
177      sprintf(buf, "%s", inet_ntoa(net.sin_addr));
178      for (len = strlen(buf); len > 3; buf[len-=2] = '\0')
179        if (strcmp(buf+len-2, ".0"))
180          break;
181
182      if (tst)    /* non-contiguous :-( */
183        sprintf(buf+strlen(buf),"&0x%08x", msk);
184      else
185        sprintf(buf+strlen(buf), "/%d", bits);
186      cp = buf;
187    }
188    break;
189
190  case AF_LINK:
191    if (!dl)
192      cp = "";
193    else if (dl->sdl_nlen == 0 && dl->sdl_alen == 0 && dl->sdl_slen == 0) {
194      sprintf(buf, "link#%d", dl->sdl_index);
195      cp = buf;
196    } else if (dl->sdl_type == IFT_ETHER && dl->sdl_alen &&
197               dl->sdl_alen < sizeof(buf)/3) {
198      int f;
199      u_char *MAC;
200
201      MAC = (u_char *)dl->sdl_data + dl->sdl_nlen;
202      for (f = 0; f < dl->sdl_alen; f++)
203        sprintf(buf+f*3, "%02x:", MAC[f]);
204      buf[f*3-1] = '\0';
205      cp = buf;
206    } else
207      cp = "???";
208    break;
209
210  default:
211    cp = "???";
212    break;
213  }
214
215  fprintf(VarTerm, "%-*s ", width-1, cp);
216}
217
218struct bits {
219  u_long b_mask;
220  char b_val;
221} bits[] = {
222
223  { RTF_UP, 'U' },
224  { RTF_GATEWAY, 'G' },
225  { RTF_HOST, 'H' },
226  { RTF_REJECT, 'R' },
227  { RTF_DYNAMIC, 'D' },
228  { RTF_MODIFIED, 'M' },
229  { RTF_DONE, 'd' },
230  { RTF_CLONING, 'C' },
231  { RTF_XRESOLVE, 'X' },
232  { RTF_LLINFO, 'L' },
233  { RTF_STATIC, 'S' },
234  { RTF_PROTO1, '1' },
235  { RTF_PROTO2, '2' },
236  { RTF_BLACKHOLE, 'B' },
237#ifdef __FreeBSD__
238  { RTF_WASCLONED, 'W' },
239  { RTF_PRCLONING, 'c' },
240  { RTF_PROTO3, '3' },
241  { RTF_BROADCAST, 'b' },
242#endif
243  { 0, '\0' }
244};
245
246static void
247p_flags(u_long f, char *format)
248{
249  if (VarTerm) {
250    char name[33], *flags;
251    register struct bits *p = bits;
252
253    for (flags = name; p->b_mask; p++)
254      if (p->b_mask & f)
255	*flags++ = p->b_val;
256    *flags = '\0';
257    fprintf(VarTerm, format, name);
258  }
259}
260
261static char *
262Index2Nam(int idx)
263{
264  static char ifs[50][6];
265  static int nifs;
266
267  if (!nifs) {
268    int mib[6], needed, len;
269    char *buf, *ptr, *end;
270    struct if_msghdr *n;
271    struct sockaddr_dl *dl;
272    struct if_msghdr *ifm;
273
274    mib[0] = CTL_NET;
275    mib[1] = PF_ROUTE;
276    mib[2] = 0;
277    mib[3] = 0;
278    mib[4] = NET_RT_IFLIST;
279    mib[5] = 0;
280
281    if (sysctl(mib, 6, NULL, &needed, NULL, 0) < 0) {
282      LogPrintf(LogERROR, "Index2Nam: sysctl: estimate: %s\n", strerror(errno));
283      return "???";
284    }
285    if ((buf = malloc(needed)) == NULL)
286      return "???";
287    if (sysctl(mib, 6, buf, &needed, NULL, 0) < 0) {
288      free(buf);
289      return "???";
290    }
291    end = buf + needed;
292
293    ptr = buf;
294    while (ptr < end) {
295      ifm = (struct if_msghdr *)ptr;
296
297      if (ifm->ifm_type != RTM_IFINFO) {
298        free(buf);
299        return "???";
300      }
301      dl = (struct sockaddr_dl *)(ifm + 1);
302      ptr += ifm->ifm_msglen;
303      while (ptr < end) {
304        n = (struct if_msghdr *)ptr;
305        if (n->ifm_type != RTM_NEWADDR)
306          break;
307        ptr += n->ifm_msglen;
308      }
309      if ((len = dl->sdl_nlen) > sizeof(ifs[0])-1)
310        len = sizeof(ifs[0])-1;
311      strncpy(ifs[nifs], dl->sdl_data, len);
312      ifs[nifs++][len] = '\0';
313      if (nifs == sizeof(ifs)/sizeof(ifs[0]))
314        break;
315    }
316    free(buf);
317  }
318
319#ifdef __FreeBSD__
320  idx--;	/* We start at 1, not 0 */
321#endif
322  if (idx < 0 || idx >= nifs)
323    return "???";
324  return ifs[idx];
325}
326
327int
328ShowRoute()
329{
330  struct rt_msghdr *rtm;
331  struct sockaddr *sa_dst, *sa_gw, *sa_mask;
332  char *sp, *ep, *cp, *wp;
333  int needed;
334  int mib[6];
335
336  if (!VarTerm)
337    return 1;
338
339  mib[0] = CTL_NET;
340  mib[1] = PF_ROUTE;
341  mib[2] = 0;
342  mib[3] = 0;
343  mib[4] = NET_RT_DUMP;
344  mib[5] = 0;
345  if (sysctl(mib, 6, NULL, &needed, NULL, 0) < 0) {
346    LogPrintf(LogERROR, "ShowRoute: sysctl: estimate: %s\n", strerror(errno));
347    return (1);
348  }
349  if (needed < 0)
350    return (1);
351  sp = malloc(needed);
352  if (sp == NULL)
353    return (1);
354  if (sysctl(mib, 6, sp, &needed, NULL, 0) < 0) {
355    LogPrintf(LogERROR, "ShowRoute: sysctl: getroute: %s\n", strerror(errno));
356    free(sp);
357    return (1);
358  }
359  ep = sp + needed;
360
361  fprintf(VarTerm, "%-20s%-20sFlags  Netif\n", "Destination", "Gateway");
362  for (cp = sp; cp < ep; cp += rtm->rtm_msglen) {
363    rtm = (struct rt_msghdr *) cp;
364    wp = (char *)(rtm+1);
365
366    if (rtm->rtm_addrs & RTA_DST) {
367      sa_dst = (struct sockaddr *)wp;
368      wp += sa_dst->sa_len;
369    } else
370      sa_dst = NULL;
371
372    if (rtm->rtm_addrs & RTA_GATEWAY) {
373      sa_gw = (struct sockaddr *)wp;
374      wp += sa_gw->sa_len;
375    } else
376      sa_gw = NULL;
377
378    if (rtm->rtm_addrs & RTA_NETMASK) {
379      sa_mask = (struct sockaddr *)wp;
380      wp += sa_mask->sa_len;
381    } else
382      sa_mask = NULL;
383
384    p_sockaddr(sa_dst, sa_mask, 20);
385    p_sockaddr(sa_gw, NULL, 20);
386
387    p_flags(rtm->rtm_flags, "%-6.6s ");
388    fprintf(VarTerm, "%s\n", Index2Nam(rtm->rtm_index));
389  }
390  free(sp);
391  return 0;
392}
393
394/*
395 *  Delete routes associated with our interface
396 */
397void
398DeleteIfRoutes(int all)
399{
400  struct rt_msghdr *rtm;
401  struct sockaddr *sa;
402  struct in_addr sa_dst, sa_gw, sa_mask;
403  int needed;
404  char *sp, *cp, *ep;
405  u_char *wp;
406  int mib[6];
407
408  LogPrintf(LogDEBUG, "DeleteIfRoutes (%d)\n", IfIndex);
409
410  mib[0] = CTL_NET;
411  mib[1] = PF_ROUTE;
412  mib[2] = 0;
413  mib[3] = 0;
414  mib[4] = NET_RT_DUMP;
415  mib[5] = 0;
416  if (sysctl(mib, 6, NULL, &needed, NULL, 0) < 0) {
417    LogPrintf(LogERROR, "DeleteIfRoutes: sysctl: estimate: %s\n",
418	      strerror(errno));
419    return;
420  }
421  if (needed < 0)
422    return;
423
424  sp = malloc(needed);
425  if (sp == NULL)
426    return;
427
428  if (sysctl(mib, 6, sp, &needed, NULL, 0) < 0) {
429    LogPrintf(LogERROR, "DeleteIfRoutes: sysctl: getroute: %s\n",
430	      strerror(errno));
431    free(sp);
432    return;
433  }
434  ep = sp + needed;
435
436  for (cp = sp; cp < ep; cp += rtm->rtm_msglen) {
437    rtm = (struct rt_msghdr *) cp;
438    sa = (struct sockaddr *) (rtm + 1);
439    LogPrintf(LogDEBUG, "DeleteIfRoutes: addrs: %x, Netif: %d (%s), flags: %x,"
440	      " dst: %s ?\n", rtm->rtm_addrs, rtm->rtm_index,
441              Index2Nam(rtm->rtm_index), rtm->rtm_flags,
442	      inet_ntoa(((struct sockaddr_in *) sa)->sin_addr));
443    if (rtm->rtm_addrs & RTA_DST && rtm->rtm_addrs & RTA_GATEWAY &&
444	rtm->rtm_index == IfIndex &&
445	(all || (rtm->rtm_flags & RTF_GATEWAY))) {
446      sa_dst.s_addr = ((struct sockaddr_in *)sa)->sin_addr.s_addr;
447      wp = (u_char *) cp + rtm->rtm_msglen;
448      sa = (struct sockaddr *)((char *)sa + sa->sa_len);
449      if (sa->sa_family == AF_INET) {
450        LogPrintf(LogDEBUG, "DeleteIfRoutes: Remove it\n");
451        sa_gw.s_addr = ((struct sockaddr_in *)sa)->sin_addr.s_addr;
452        sa = (struct sockaddr *)((char *)sa + sa->sa_len);
453        if (rtm->rtm_addrs & RTA_NETMASK)
454          sa_mask.s_addr = ((struct sockaddr_in *)sa)->sin_addr.s_addr;
455        else
456          sa_mask.s_addr = 0xffffffff;
457        if (sa_dst.s_addr == INADDR_ANY)
458	  sa_mask.s_addr = INADDR_ANY;
459        LogPrintf(LogDEBUG, "DeleteIfRoutes: Dst: %s\n", inet_ntoa(sa_dst));
460        LogPrintf(LogDEBUG, "DeleteIfRoutes: Gw: %s\n", inet_ntoa(sa_gw));
461        LogPrintf(LogDEBUG, "DeleteIfRoutes: Index: %d\n", rtm->rtm_index);
462        OsSetRoute(RTM_DELETE, sa_dst, sa_gw, sa_mask);
463      } else
464        LogPrintf(LogDEBUG, "DeleteIfRoutes: Can't remove an AF_LINK !\n");
465    }
466  }
467  free(sp);
468}
469
470int
471GetIfIndex(char *name)
472{
473  int idx;
474  char *got;
475
476#ifdef __FreeBSD__
477  idx = 1;	/* We start at 1, not 0 */
478#else
479  idx = 0;
480#endif
481
482  while (strcmp(got = Index2Nam(idx), "???"))
483    if (!strcmp(got, name))
484      return IfIndex = idx;
485    else
486      idx++;
487  return -1;
488}
489