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