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