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