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