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