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