666c6e3c2d335de8fc8b14e30be8b1fde0216569
[dragonfly.git] / usr.sbin / ppp / route.c
1 /*-
2  * Copyright (c) 1996 - 2001 Brian Somers <brian@Awfulhak.org>
3  *          based on work by Toshiharu OHNO <tony-o@iij.ad.jp>
4  *                           Internet Initiative Japan, Inc (IIJ)
5  * All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  * 1. Redistributions of source code must retain the above copyright
11  *    notice, this list of conditions and the following disclaimer.
12  * 2. Redistributions in binary form must reproduce the above copyright
13  *    notice, this list of conditions and the following disclaimer in the
14  *    documentation and/or other materials provided with the distribution.
15  *
16  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
17  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
20  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26  * SUCH DAMAGE.
27  *
28  * $FreeBSD: src/usr.sbin/ppp/route.c,v 1.60.2.8 2003/04/05 10:39:05 ume Exp $
29  * $DragonFly: src/usr.sbin/ppp/route.c,v 1.3 2004/03/27 01:39:13 cpressey Exp $
30  */
31
32 #include <sys/param.h>
33 #include <sys/socket.h>
34 #include <net/if_types.h>
35 #include <net/route.h>
36 #include <net/if.h>
37 #include <netinet/in.h>
38 #include <arpa/inet.h>
39 #include <net/if_dl.h>
40 #include <netinet/in_systm.h>
41 #include <netinet/ip.h>
42 #include <sys/un.h>
43
44 #include <errno.h>
45 #include <stdarg.h>
46 #include <stdio.h>
47 #include <stdlib.h>
48 #include <string.h>
49 #include <sys/sysctl.h>
50 #include <termios.h>
51 #include <unistd.h>
52
53 #include "layer.h"
54 #include "defs.h"
55 #include "command.h"
56 #include "mbuf.h"
57 #include "log.h"
58 #include "iplist.h"
59 #include "timer.h"
60 #include "throughput.h"
61 #include "lqr.h"
62 #include "hdlc.h"
63 #include "fsm.h"
64 #include "lcp.h"
65 #include "ccp.h"
66 #include "link.h"
67 #include "slcompress.h"
68 #include "ncpaddr.h"
69 #include "ipcp.h"
70 #include "filter.h"
71 #include "descriptor.h"
72 #include "mp.h"
73 #ifndef NORADIUS
74 #include "radius.h"
75 #endif
76 #include "ipv6cp.h"
77 #include "ncp.h"
78 #include "bundle.h"
79 #include "route.h"
80 #include "prompt.h"
81 #include "iface.h"
82 #include "id.h"
83
84
85 static void
86 p_sockaddr(struct prompt *prompt, struct sockaddr *phost,
87            struct sockaddr *pmask, int width)
88 {
89   struct ncprange range;
90   char buf[29];
91   struct sockaddr_dl *dl = (struct sockaddr_dl *)phost;
92
93   if (log_IsKept(LogDEBUG)) {
94     char tmp[50];
95
96     log_Printf(LogDEBUG, "Found the following sockaddr:\n");
97     log_Printf(LogDEBUG, "  Family %d, len %d\n",
98                (int)phost->sa_family, (int)phost->sa_len);
99     inet_ntop(phost->sa_family, phost->sa_data, tmp, sizeof tmp);
100     log_Printf(LogDEBUG, "  Addr %s\n", tmp);
101     if (pmask) {
102       inet_ntop(pmask->sa_family, pmask->sa_data, tmp, sizeof tmp);
103       log_Printf(LogDEBUG, "  Mask %s\n", tmp);
104     }
105   }
106
107   switch (phost->sa_family) {
108   case AF_INET:
109 #ifndef NOINET6
110   case AF_INET6:
111 #endif
112     ncprange_setsa(&range, phost, pmask);
113     if (ncprange_isdefault(&range))
114       prompt_Printf(prompt, "%-*s ", width - 1, "default");
115     else
116       prompt_Printf(prompt, "%-*s ", width - 1, ncprange_ntoa(&range));
117     return;
118
119   case AF_LINK:
120     if (dl->sdl_nlen)
121       snprintf(buf, sizeof buf, "%.*s", dl->sdl_nlen, dl->sdl_data);
122     else if (dl->sdl_alen) {
123       if (dl->sdl_type == IFT_ETHER) {
124         if (dl->sdl_alen < sizeof buf / 3) {
125           int f;
126           u_char *MAC;
127
128           MAC = (u_char *)dl->sdl_data + dl->sdl_nlen;
129           for (f = 0; f < dl->sdl_alen; f++)
130             sprintf(buf+f*3, "%02x:", MAC[f]);
131           buf[f*3-1] = '\0';
132         } else
133           strcpy(buf, "??:??:??:??:??:??");
134       } else
135         sprintf(buf, "<IFT type %d>", dl->sdl_type);
136     }  else if (dl->sdl_slen)
137       sprintf(buf, "<slen %d?>", dl->sdl_slen);
138     else
139       sprintf(buf, "link#%d", dl->sdl_index);
140     break;
141
142   default:
143     sprintf(buf, "<AF type %d>", phost->sa_family);
144     break;
145   }
146
147   prompt_Printf(prompt, "%-*s ", width-1, buf);
148 }
149
150 static struct bits {
151   u_int32_t b_mask;
152   char b_val;
153 } bits[] = {
154   { RTF_UP, 'U' },
155   { RTF_GATEWAY, 'G' },
156   { RTF_HOST, 'H' },
157   { RTF_REJECT, 'R' },
158   { RTF_DYNAMIC, 'D' },
159   { RTF_MODIFIED, 'M' },
160   { RTF_DONE, 'd' },
161   { RTF_CLONING, 'C' },
162   { RTF_XRESOLVE, 'X' },
163   { RTF_LLINFO, 'L' },
164   { RTF_STATIC, 'S' },
165   { RTF_PROTO1, '1' },
166   { RTF_PROTO2, '2' },
167   { RTF_BLACKHOLE, 'B' },
168 #ifdef RTF_WASCLONED
169   { RTF_WASCLONED, 'W' },
170 #endif
171 #ifdef RTF_PRCLONING
172   { RTF_PRCLONING, 'c' },
173 #endif
174 #ifdef RTF_PROTO3
175   { RTF_PROTO3, '3' },
176 #endif
177 #ifdef RTF_BROADCAST
178   { RTF_BROADCAST, 'b' },
179 #endif
180   { 0, '\0' }
181 };
182
183 #ifndef RTF_WASCLONED
184 #define RTF_WASCLONED (0)
185 #endif
186
187 static void
188 p_flags(struct prompt *prompt, u_int32_t f, unsigned max)
189 {
190   char name[33], *flags;
191   struct bits *p = bits;
192
193   if (max > sizeof name - 1)
194     max = sizeof name - 1;
195
196   for (flags = name; p->b_mask && flags - name < (int)max; p++)
197     if (p->b_mask & f)
198       *flags++ = p->b_val;
199   *flags = '\0';
200   prompt_Printf(prompt, "%-*.*s", max, max, name);
201 }
202
203 static int route_nifs = -1;
204
205 const char *
206 Index2Nam(int idx)
207 {
208   /*
209    * XXX: Maybe we should select() on the routing socket so that we can
210    *      notice interfaces that come & go (PCCARD support).
211    *      Or we could even support a signal that resets these so that
212    *      the PCCARD insert/remove events can signal ppp.
213    */
214   static char **ifs;            /* Figure these out once */
215   static int debug_done;        /* Debug once */
216
217   if (idx > route_nifs || (idx > 0 && ifs[idx-1] == NULL)) {
218     int mib[6], have, had;
219     size_t needed;
220     char *buf, *ptr, *end;
221     struct sockaddr_dl *dl;
222     struct if_msghdr *ifm;
223
224     if (ifs) {
225       free(ifs);
226       ifs = NULL;
227       route_nifs = 0;
228     }
229     debug_done = 0;
230
231     mib[0] = CTL_NET;
232     mib[1] = PF_ROUTE;
233     mib[2] = 0;
234     mib[3] = 0;
235     mib[4] = NET_RT_IFLIST;
236     mib[5] = 0;
237
238     if (sysctl(mib, 6, NULL, &needed, NULL, 0) < 0) {
239       log_Printf(LogERROR, "Index2Nam: sysctl: estimate: %s\n",
240                  strerror(errno));
241       return NumStr(idx, NULL, 0);
242     }
243     if ((buf = malloc(needed)) == NULL)
244       return NumStr(idx, NULL, 0);
245     if (sysctl(mib, 6, buf, &needed, NULL, 0) < 0) {
246       free(buf);
247       return NumStr(idx, NULL, 0);
248     }
249     end = buf + needed;
250
251     have = 0;
252     for (ptr = buf; ptr < end; ptr += ifm->ifm_msglen) {
253       ifm = (struct if_msghdr *)ptr;
254       if (ifm->ifm_type != RTM_IFINFO)
255         continue;
256       dl = (struct sockaddr_dl *)(ifm + 1);
257       if (ifm->ifm_index > 0) {
258         if (ifm->ifm_index > have) {
259           char **newifs;
260
261           had = have;
262           have = ifm->ifm_index + 5;
263           if (had)
264             newifs = (char **)realloc(ifs, sizeof(char *) * have);
265           else
266             newifs = (char **)malloc(sizeof(char *) * have);
267           if (!newifs) {
268             log_Printf(LogDEBUG, "Index2Nam: %s\n", strerror(errno));
269             route_nifs = 0;
270             if (ifs) {
271               free(ifs);
272               ifs = NULL;
273             }
274             free(buf);
275             return NumStr(idx, NULL, 0);
276           }
277           ifs = newifs;
278           memset(ifs + had, '\0', sizeof(char *) * (have - had));
279         }
280         if (ifs[ifm->ifm_index-1] == NULL) {
281           ifs[ifm->ifm_index-1] = (char *)malloc(dl->sdl_nlen+1);
282           memcpy(ifs[ifm->ifm_index-1], dl->sdl_data, dl->sdl_nlen);
283           ifs[ifm->ifm_index-1][dl->sdl_nlen] = '\0';
284           if (route_nifs < ifm->ifm_index)
285             route_nifs = ifm->ifm_index;
286         }
287       } else if (log_IsKept(LogDEBUG))
288         log_Printf(LogDEBUG, "Skipping out-of-range interface %d!\n",
289                   ifm->ifm_index);
290     }
291     free(buf);
292   }
293
294   if (log_IsKept(LogDEBUG) && !debug_done) {
295     int f;
296
297     log_Printf(LogDEBUG, "Found the following interfaces:\n");
298     for (f = 0; f < route_nifs; f++)
299       if (ifs[f] != NULL)
300         log_Printf(LogDEBUG, " Index %d, name \"%s\"\n", f+1, ifs[f]);
301     debug_done = 1;
302   }
303
304   if (idx < 1 || idx > route_nifs || ifs[idx-1] == NULL)
305     return NumStr(idx, NULL, 0);
306
307   return ifs[idx-1];
308 }
309
310 void
311 route_ParseHdr(struct rt_msghdr *rtm, struct sockaddr *sa[RTAX_MAX])
312 {
313   char *wp;
314   int rtax;
315
316   wp = (char *)(rtm + 1);
317
318   for (rtax = 0; rtax < RTAX_MAX; rtax++)
319     if (rtm->rtm_addrs & (1 << rtax)) {
320       sa[rtax] = (struct sockaddr *)wp;
321       wp += ROUNDUP(sa[rtax]->sa_len);
322       if (sa[rtax]->sa_family == 0)
323         sa[rtax] = NULL;        /* ??? */
324     } else
325       sa[rtax] = NULL;
326 }
327
328 int
329 route_Show(struct cmdargs const *arg)
330 {
331   struct rt_msghdr *rtm;
332   struct sockaddr *sa[RTAX_MAX];
333   char *sp, *ep, *cp;
334   size_t needed;
335   int mib[6];
336
337   mib[0] = CTL_NET;
338   mib[1] = PF_ROUTE;
339   mib[2] = 0;
340   mib[3] = 0;
341   mib[4] = NET_RT_DUMP;
342   mib[5] = 0;
343   if (sysctl(mib, 6, NULL, &needed, NULL, 0) < 0) {
344     log_Printf(LogERROR, "route_Show: sysctl: estimate: %s\n", strerror(errno));
345     return (1);
346   }
347   sp = malloc(needed);
348   if (sp == NULL)
349     return (1);
350   if (sysctl(mib, 6, sp, &needed, NULL, 0) < 0) {
351     log_Printf(LogERROR, "route_Show: sysctl: getroute: %s\n", strerror(errno));
352     free(sp);
353     return (1);
354   }
355   ep = sp + needed;
356
357   prompt_Printf(arg->prompt, "%-20s%-20sFlags  Netif\n",
358                 "Destination", "Gateway");
359   for (cp = sp; cp < ep; cp += rtm->rtm_msglen) {
360     rtm = (struct rt_msghdr *)cp;
361
362     route_ParseHdr(rtm, sa);
363
364     if (sa[RTAX_DST] && sa[RTAX_GATEWAY]) {
365       p_sockaddr(arg->prompt, sa[RTAX_DST], sa[RTAX_NETMASK], 20);
366       p_sockaddr(arg->prompt, sa[RTAX_GATEWAY], NULL, 20);
367
368       p_flags(arg->prompt, rtm->rtm_flags, 6);
369       prompt_Printf(arg->prompt, " %s\n", Index2Nam(rtm->rtm_index));
370     } else
371       prompt_Printf(arg->prompt, "<can't parse routing entry>\n");
372   }
373   free(sp);
374   return 0;
375 }
376
377 /*
378  *  Delete routes associated with our interface
379  */
380 void
381 route_IfDelete(struct bundle *bundle, int all)
382 {
383   struct rt_msghdr *rtm;
384   struct sockaddr *sa[RTAX_MAX];
385   struct ncprange range;
386   int pass;
387   size_t needed;
388   char *sp, *cp, *ep;
389   int mib[6];
390
391   log_Printf(LogDEBUG, "route_IfDelete (%d)\n", bundle->iface->index);
392
393   mib[0] = CTL_NET;
394   mib[1] = PF_ROUTE;
395   mib[2] = 0;
396   mib[3] = 0;
397   mib[4] = NET_RT_DUMP;
398   mib[5] = 0;
399   if (sysctl(mib, 6, NULL, &needed, NULL, 0) < 0) {
400     log_Printf(LogERROR, "route_IfDelete: sysctl: estimate: %s\n",
401               strerror(errno));
402     return;
403   }
404
405   sp = malloc(needed);
406   if (sp == NULL)
407     return;
408
409   if (sysctl(mib, 6, sp, &needed, NULL, 0) < 0) {
410     log_Printf(LogERROR, "route_IfDelete: sysctl: getroute: %s\n",
411               strerror(errno));
412     free(sp);
413     return;
414   }
415   ep = sp + needed;
416
417   for (pass = 0; pass < 2; pass++) {
418     /*
419      * We do 2 passes.  The first deletes all cloned routes.  The second
420      * deletes all non-cloned routes.  This is done to avoid
421      * potential errors from trying to delete route X after route Y where
422      * route X was cloned from route Y (and is no longer there 'cos it
423      * may have gone with route Y).
424      */
425     if (RTF_WASCLONED == 0 && pass == 0)
426       /* So we can't tell ! */
427       continue;
428     for (cp = sp; cp < ep; cp += rtm->rtm_msglen) {
429       rtm = (struct rt_msghdr *)cp;
430       route_ParseHdr(rtm, sa);
431       if (rtm->rtm_index == bundle->iface->index &&
432           sa[RTAX_DST] && sa[RTAX_GATEWAY] &&
433           (sa[RTAX_DST]->sa_family == AF_INET
434 #ifndef NOINET6
435            || sa[RTAX_DST]->sa_family == AF_INET6
436 #endif
437            ) &&
438           (all || (rtm->rtm_flags & RTF_GATEWAY))) {
439         if (log_IsKept(LogDEBUG)) {
440           char gwstr[41];
441           struct ncpaddr gw;
442           ncprange_setsa(&range, sa[RTAX_DST], sa[RTAX_NETMASK]);
443           ncpaddr_setsa(&gw, sa[RTAX_GATEWAY]);
444           snprintf(gwstr, sizeof gwstr, "%s", ncpaddr_ntoa(&gw));
445           log_Printf(LogDEBUG, "Found %s %s\n", ncprange_ntoa(&range), gwstr);
446         }
447         if (sa[RTAX_GATEWAY]->sa_family == AF_INET ||
448 #ifndef NOINET6
449             sa[RTAX_GATEWAY]->sa_family == AF_INET6 ||
450 #endif
451             sa[RTAX_GATEWAY]->sa_family == AF_LINK) {
452           if ((pass == 0 && (rtm->rtm_flags & RTF_WASCLONED)) ||
453               (pass == 1 && !(rtm->rtm_flags & RTF_WASCLONED))) {
454             ncprange_setsa(&range, sa[RTAX_DST], sa[RTAX_NETMASK]);
455             rt_Set(bundle, RTM_DELETE, &range, NULL, 0, 0);
456           } else
457             log_Printf(LogDEBUG, "route_IfDelete: Skip it (pass %d)\n", pass);
458         } else
459           log_Printf(LogDEBUG,
460                     "route_IfDelete: Can't remove routes for family %d\n",
461                     sa[RTAX_GATEWAY]->sa_family);
462       }
463     }
464   }
465   free(sp);
466 }
467
468
469 /*
470  *  Update the MTU on all routes for the given interface
471  */
472 void
473 route_UpdateMTU(struct bundle *bundle)
474 {
475   struct rt_msghdr *rtm;
476   struct sockaddr *sa[RTAX_MAX];
477   struct ncprange dst;
478   size_t needed;
479   char *sp, *cp, *ep;
480   int mib[6];
481
482   log_Printf(LogDEBUG, "route_UpdateMTU (%d)\n", bundle->iface->index);
483
484   mib[0] = CTL_NET;
485   mib[1] = PF_ROUTE;
486   mib[2] = 0;
487   mib[3] = 0;
488   mib[4] = NET_RT_DUMP;
489   mib[5] = 0;
490   if (sysctl(mib, 6, NULL, &needed, NULL, 0) < 0) {
491     log_Printf(LogERROR, "route_IfDelete: sysctl: estimate: %s\n",
492               strerror(errno));
493     return;
494   }
495
496   sp = malloc(needed);
497   if (sp == NULL)
498     return;
499
500   if (sysctl(mib, 6, sp, &needed, NULL, 0) < 0) {
501     log_Printf(LogERROR, "route_IfDelete: sysctl: getroute: %s\n",
502               strerror(errno));
503     free(sp);
504     return;
505   }
506   ep = sp + needed;
507
508   for (cp = sp; cp < ep; cp += rtm->rtm_msglen) {
509     rtm = (struct rt_msghdr *)cp;
510     route_ParseHdr(rtm, sa);
511     if (sa[RTAX_DST] && (sa[RTAX_DST]->sa_family == AF_INET
512 #ifndef NOINET6
513                          || sa[RTAX_DST]->sa_family == AF_INET6
514 #endif
515                         ) &&
516         sa[RTAX_GATEWAY] && rtm->rtm_index == bundle->iface->index) {
517       if (log_IsKept(LogTCPIP)) {
518         ncprange_setsa(&dst, sa[RTAX_DST], sa[RTAX_NETMASK]);
519         log_Printf(LogTCPIP, "route_UpdateMTU: Netif: %d (%s), dst %s,"
520                    " mtu %lu\n", rtm->rtm_index, Index2Nam(rtm->rtm_index),
521                    ncprange_ntoa(&dst), bundle->iface->mtu);
522       }
523       rt_Update(bundle, sa[RTAX_DST], sa[RTAX_GATEWAY], sa[RTAX_NETMASK]);
524     }
525   }
526
527   free(sp);
528 }
529
530 int
531 GetIfIndex(char *name)
532 {
533   int idx;
534
535   idx = 1;
536   while (route_nifs == -1 || idx < route_nifs)
537     if (strcmp(Index2Nam(idx), name) == 0)
538       return idx;
539     else
540       idx++;
541   return -1;
542 }
543
544 void
545 route_Change(struct bundle *bundle, struct sticky_route *r,
546              const struct ncpaddr *me, const struct ncpaddr *peer)
547 {
548   struct ncpaddr dst;
549
550   for (; r; r = r->next) {
551     ncprange_getaddr(&r->dst, &dst);
552     if (ncpaddr_family(me) == AF_INET) {
553       if ((r->type & ROUTE_DSTMYADDR) && !ncpaddr_equal(&dst, me)) {
554         rt_Set(bundle, RTM_DELETE, &r->dst, NULL, 1, 0);
555         ncprange_sethost(&r->dst, me);
556         if (r->type & ROUTE_GWHISADDR)
557           ncpaddr_copy(&r->gw, peer);
558       } else if ((r->type & ROUTE_DSTHISADDR) && !ncpaddr_equal(&dst, peer)) {
559         rt_Set(bundle, RTM_DELETE, &r->dst, NULL, 1, 0);
560         ncprange_sethost(&r->dst, peer);
561         if (r->type & ROUTE_GWHISADDR)
562           ncpaddr_copy(&r->gw, peer);
563       } else if ((r->type & ROUTE_DSTDNS0) && !ncpaddr_equal(&dst, peer)) {
564         if (bundle->ncp.ipcp.ns.dns[0].s_addr == INADDR_NONE)
565           continue;
566         rt_Set(bundle, RTM_DELETE, &r->dst, NULL, 1, 0);
567         if (r->type & ROUTE_GWHISADDR)
568           ncpaddr_copy(&r->gw, peer);
569       } else if ((r->type & ROUTE_DSTDNS1) && !ncpaddr_equal(&dst, peer)) {
570         if (bundle->ncp.ipcp.ns.dns[1].s_addr == INADDR_NONE)
571           continue;
572         rt_Set(bundle, RTM_DELETE, &r->dst, NULL, 1, 0);
573         if (r->type & ROUTE_GWHISADDR)
574           ncpaddr_copy(&r->gw, peer);
575       } else if ((r->type & ROUTE_GWHISADDR) && !ncpaddr_equal(&r->gw, peer))
576         ncpaddr_copy(&r->gw, peer);
577 #ifndef NOINET6
578     } else if (ncpaddr_family(me) == AF_INET6) {
579       if ((r->type & ROUTE_DSTMYADDR6) && !ncpaddr_equal(&dst, me)) {
580         rt_Set(bundle, RTM_DELETE, &r->dst, NULL, 1, 0);
581         ncprange_sethost(&r->dst, me);
582         if (r->type & ROUTE_GWHISADDR)
583           ncpaddr_copy(&r->gw, peer);
584       } else if ((r->type & ROUTE_DSTHISADDR6) && !ncpaddr_equal(&dst, peer)) {
585         rt_Set(bundle, RTM_DELETE, &r->dst, NULL, 1, 0);
586         ncprange_sethost(&r->dst, peer);
587         if (r->type & ROUTE_GWHISADDR)
588           ncpaddr_copy(&r->gw, peer);
589       } else if ((r->type & ROUTE_GWHISADDR6) && !ncpaddr_equal(&r->gw, peer))
590         ncpaddr_copy(&r->gw, peer);
591 #endif
592     }
593     rt_Set(bundle, RTM_ADD, &r->dst, &r->gw, 1, 0);
594   }
595 }
596
597 void
598 route_Add(struct sticky_route **rp, int type, const struct ncprange *dst,
599           const struct ncpaddr *gw)
600 {
601   struct sticky_route *r;
602   int dsttype = type & ROUTE_DSTANY;
603
604   r = NULL;
605   while (*rp) {
606     if ((dsttype && dsttype == ((*rp)->type & ROUTE_DSTANY)) ||
607         (!dsttype && ncprange_equal(&(*rp)->dst, dst))) {
608       /* Oops, we already have this route - unlink it */
609       free(r);                  /* impossible really  */
610       r = *rp;
611       *rp = r->next;
612     } else
613       rp = &(*rp)->next;
614   }
615
616   if (!r)
617     r = (struct sticky_route *)malloc(sizeof(struct sticky_route));
618   r->type = type;
619   r->next = NULL;
620   ncprange_copy(&r->dst, dst);
621   ncpaddr_copy(&r->gw, gw);
622   *rp = r;
623 }
624
625 void
626 route_Delete(struct sticky_route **rp, int type, const struct ncprange *dst)
627 {
628   struct sticky_route *r;
629   int dsttype = type & ROUTE_DSTANY;
630
631   for (; *rp; rp = &(*rp)->next) {
632     if ((dsttype && dsttype == ((*rp)->type & ROUTE_DSTANY)) ||
633         (!dsttype && ncprange_equal(dst, &(*rp)->dst))) {
634       r = *rp;
635       *rp = r->next;
636       free(r);
637       break;
638     }
639   }
640 }
641
642 void
643 route_DeleteAll(struct sticky_route **rp)
644 {
645   struct sticky_route *r, *rn;
646
647   for (r = *rp; r; r = rn) {
648     rn = r->next;
649     free(r);
650   }
651   *rp = NULL;
652 }
653
654 void
655 route_ShowSticky(struct prompt *p, struct sticky_route *r, const char *tag,
656                  int indent)
657 {
658   int tlen = strlen(tag);
659
660   if (tlen + 2 > indent)
661     prompt_Printf(p, "%s:\n%*s", tag, indent, "");
662   else
663     prompt_Printf(p, "%s:%*s", tag, indent - tlen - 1, "");
664
665   for (; r; r = r->next) {
666     prompt_Printf(p, "%*sadd ", tlen ? 0 : indent, "");
667     tlen = 0;
668     if (r->type & ROUTE_DSTMYADDR)
669       prompt_Printf(p, "MYADDR");
670     else if (r->type & ROUTE_DSTMYADDR6)
671       prompt_Printf(p, "MYADDR6");
672     else if (r->type & ROUTE_DSTHISADDR)
673       prompt_Printf(p, "HISADDR");
674     else if (r->type & ROUTE_DSTHISADDR6)
675       prompt_Printf(p, "HISADDR6");
676     else if (r->type & ROUTE_DSTDNS0)
677       prompt_Printf(p, "DNS0");
678     else if (r->type & ROUTE_DSTDNS1)
679       prompt_Printf(p, "DNS1");
680     else if (ncprange_isdefault(&r->dst))
681       prompt_Printf(p, "default");
682     else
683       prompt_Printf(p, "%s", ncprange_ntoa(&r->dst));
684
685     if (r->type & ROUTE_GWHISADDR)
686       prompt_Printf(p, " HISADDR\n");
687     else if (r->type & ROUTE_GWHISADDR6)
688       prompt_Printf(p, " HISADDR6\n");
689     else
690       prompt_Printf(p, " %s\n", ncpaddr_ntoa(&r->gw));
691   }
692 }
693
694 struct rtmsg {
695   struct rt_msghdr m_rtm;
696   char m_space[256];
697 };
698
699 static size_t
700 memcpy_roundup(char *cp, const void *data, size_t len)
701 {
702   size_t padlen;
703
704   padlen = ROUNDUP(len);
705   memcpy(cp, data, len);
706   if (padlen > len)
707     memset(cp + len, '\0', padlen - len);
708
709   return padlen;
710 }
711
712 #if defined(__KAME__) && !defined(NOINET6)
713 static void
714 add_scope(struct sockaddr *sa, int ifindex)
715 {
716   struct sockaddr_in6 *sa6;
717
718   if (sa->sa_family != AF_INET6)
719     return;
720   sa6 = (struct sockaddr_in6 *)sa;
721   if (!IN6_IS_ADDR_LINKLOCAL(&sa6->sin6_addr) &&
722       !IN6_IS_ADDR_MC_LINKLOCAL(&sa6->sin6_addr))
723     return;
724   if (*(u_int16_t *)&sa6->sin6_addr.s6_addr[2] != 0)
725     return;
726   *(u_int16_t *)&sa6->sin6_addr.s6_addr[2] = htons(ifindex);
727 }
728 #endif
729
730 int
731 rt_Set(struct bundle *bundle, int cmd, const struct ncprange *dst,
732        const struct ncpaddr *gw, int bang, int quiet)
733 {
734   struct rtmsg rtmes;
735   int s, nb, wb;
736   char *cp;
737   const char *cmdstr;
738   struct sockaddr_storage sadst, samask, sagw;
739   int result = 1;
740
741   if (bang)
742     cmdstr = (cmd == RTM_ADD ? "Add!" : "Delete!");
743   else
744     cmdstr = (cmd == RTM_ADD ? "Add" : "Delete");
745   s = ID0socket(PF_ROUTE, SOCK_RAW, 0);
746   if (s < 0) {
747     log_Printf(LogERROR, "rt_Set: socket(): %s\n", strerror(errno));
748     return result;
749   }
750   memset(&rtmes, '\0', sizeof rtmes);
751   rtmes.m_rtm.rtm_version = RTM_VERSION;
752   rtmes.m_rtm.rtm_type = cmd;
753   rtmes.m_rtm.rtm_addrs = RTA_DST;
754   rtmes.m_rtm.rtm_seq = ++bundle->routing_seq;
755   rtmes.m_rtm.rtm_pid = getpid();
756   rtmes.m_rtm.rtm_flags = RTF_UP | RTF_GATEWAY | RTF_STATIC;
757
758   if (cmd == RTM_ADD) {
759     if (bundle->ncp.cfg.sendpipe > 0) {
760       rtmes.m_rtm.rtm_rmx.rmx_sendpipe = bundle->ncp.cfg.sendpipe;
761       rtmes.m_rtm.rtm_inits |= RTV_SPIPE;
762     }
763     if (bundle->ncp.cfg.recvpipe > 0) {
764       rtmes.m_rtm.rtm_rmx.rmx_recvpipe = bundle->ncp.cfg.recvpipe;
765       rtmes.m_rtm.rtm_inits |= RTV_RPIPE;
766     }
767   }
768
769   ncprange_getsa(dst, &sadst, &samask);
770 #if defined(__KAME__) && !defined(NOINET6)
771   add_scope((struct sockaddr *)&sadst, bundle->iface->index);
772 #endif
773
774   cp = rtmes.m_space;
775   cp += memcpy_roundup(cp, &sadst, sadst.ss_len);
776   if (cmd == RTM_ADD) {
777     if (gw == NULL) {
778       log_Printf(LogERROR, "rt_Set: Program error\n");
779       close(s);
780       return result;
781     }
782     ncpaddr_getsa(gw, &sagw);
783 #if defined(__KAME__) && !defined(NOINET6)
784     add_scope((struct sockaddr *)&sagw, bundle->iface->index);
785 #endif
786     if (ncpaddr_isdefault(gw)) {
787       if (!quiet)
788         log_Printf(LogERROR, "rt_Set: Cannot add a route with"
789                    " gateway 0.0.0.0\n");
790       close(s);
791       return result;
792     } else {
793       cp += memcpy_roundup(cp, &sagw, sagw.ss_len);
794       rtmes.m_rtm.rtm_addrs |= RTA_GATEWAY;
795     }
796   }
797
798   if (!ncprange_ishost(dst)) {
799     cp += memcpy_roundup(cp, &samask, samask.ss_len);
800     rtmes.m_rtm.rtm_addrs |= RTA_NETMASK;
801   }
802
803   nb = cp - (char *)&rtmes;
804   rtmes.m_rtm.rtm_msglen = nb;
805   wb = ID0write(s, &rtmes, nb);
806   if (wb < 0) {
807     log_Printf(LogTCPIP, "rt_Set failure:\n");
808     log_Printf(LogTCPIP, "rt_Set:  Cmd = %s\n", cmdstr);
809     log_Printf(LogTCPIP, "rt_Set:  Dst = %s\n", ncprange_ntoa(dst));
810     if (gw != NULL)
811       log_Printf(LogTCPIP, "rt_Set:  Gateway = %s\n", ncpaddr_ntoa(gw));
812 failed:
813     if (cmd == RTM_ADD && (rtmes.m_rtm.rtm_errno == EEXIST ||
814                            (rtmes.m_rtm.rtm_errno == 0 && errno == EEXIST))) {
815       if (!bang) {
816         log_Printf(LogWARN, "Add route failed: %s already exists\n",
817                    ncprange_ntoa(dst));
818         result = 0;     /* Don't add to our dynamic list */
819       } else {
820         rtmes.m_rtm.rtm_type = cmd = RTM_CHANGE;
821         if ((wb = ID0write(s, &rtmes, nb)) < 0)
822           goto failed;
823       }
824     } else if (cmd == RTM_DELETE &&
825              (rtmes.m_rtm.rtm_errno == ESRCH ||
826               (rtmes.m_rtm.rtm_errno == 0 && errno == ESRCH))) {
827       if (!bang)
828         log_Printf(LogWARN, "Del route failed: %s: Non-existent\n",
829                   ncprange_ntoa(dst));
830     } else if (rtmes.m_rtm.rtm_errno == 0) {
831       if (!quiet || errno != ENETUNREACH)
832         log_Printf(LogWARN, "%s route failed: %s: errno: %s\n", cmdstr,
833                    ncprange_ntoa(dst), strerror(errno));
834     } else
835       log_Printf(LogWARN, "%s route failed: %s: %s\n",
836                  cmdstr, ncprange_ntoa(dst), strerror(rtmes.m_rtm.rtm_errno));
837   }
838
839   if (log_IsKept(LogDEBUG)) {
840     char gwstr[40];
841
842     if (gw)
843       snprintf(gwstr, sizeof gwstr, "%s", ncpaddr_ntoa(gw));
844     else
845       snprintf(gwstr, sizeof gwstr, "<none>");
846     log_Printf(LogDEBUG, "wrote %d: cmd = %s, dst = %s, gateway = %s\n",
847                wb, cmdstr, ncprange_ntoa(dst), gwstr);
848   }
849   close(s);
850
851   return result;
852 }
853
854 void
855 rt_Update(struct bundle *bundle, const struct sockaddr *dst,
856           const struct sockaddr *gw, const struct sockaddr *mask)
857 {
858   struct ncprange ncpdst;
859   struct rtmsg rtmes;
860   char *p;
861   int s, wb;
862
863   s = ID0socket(PF_ROUTE, SOCK_RAW, 0);
864   if (s < 0) {
865     log_Printf(LogERROR, "rt_Update: socket(): %s\n", strerror(errno));
866     return;
867   }
868
869   memset(&rtmes, '\0', sizeof rtmes);
870   rtmes.m_rtm.rtm_version = RTM_VERSION;
871   rtmes.m_rtm.rtm_type = RTM_CHANGE;
872   rtmes.m_rtm.rtm_addrs = 0;
873   rtmes.m_rtm.rtm_seq = ++bundle->routing_seq;
874   rtmes.m_rtm.rtm_pid = getpid();
875   rtmes.m_rtm.rtm_flags = RTF_UP | RTF_STATIC;
876
877   if (bundle->ncp.cfg.sendpipe > 0) {
878     rtmes.m_rtm.rtm_rmx.rmx_sendpipe = bundle->ncp.cfg.sendpipe;
879     rtmes.m_rtm.rtm_inits |= RTV_SPIPE;
880   }
881
882   if (bundle->ncp.cfg.recvpipe > 0) {
883     rtmes.m_rtm.rtm_rmx.rmx_recvpipe = bundle->ncp.cfg.recvpipe;
884     rtmes.m_rtm.rtm_inits |= RTV_RPIPE;
885   }
886
887   rtmes.m_rtm.rtm_rmx.rmx_mtu = bundle->iface->mtu;
888   rtmes.m_rtm.rtm_inits |= RTV_MTU;
889   p = rtmes.m_space;
890
891   if (dst) {
892     rtmes.m_rtm.rtm_addrs |= RTA_DST;
893     p += memcpy_roundup(p, dst, dst->sa_len);
894   }
895
896   rtmes.m_rtm.rtm_addrs |= RTA_GATEWAY;
897   p += memcpy_roundup(p, gw, gw->sa_len);
898   if (mask) {
899     rtmes.m_rtm.rtm_addrs |= RTA_NETMASK;
900     p += memcpy_roundup(p, mask, mask->sa_len);
901   }
902
903   rtmes.m_rtm.rtm_msglen = p - (char *)&rtmes;
904
905   wb = ID0write(s, &rtmes, rtmes.m_rtm.rtm_msglen);
906   if (wb < 0) {
907     ncprange_setsa(&ncpdst, dst, mask);
908
909     log_Printf(LogTCPIP, "rt_Update failure:\n");
910     log_Printf(LogTCPIP, "rt_Update:  Dst = %s\n", ncprange_ntoa(&ncpdst));
911
912     if (rtmes.m_rtm.rtm_errno == 0)
913       log_Printf(LogWARN, "%s: Change route failed: errno: %s\n",
914                  ncprange_ntoa(&ncpdst), strerror(errno));
915     else
916       log_Printf(LogWARN, "%s: Change route failed: %s\n",
917                  ncprange_ntoa(&ncpdst), strerror(rtmes.m_rtm.rtm_errno));
918   }
919   close(s);
920 }