route.c revision 31690
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.30 1997/12/07 04:09:15 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 strcpy(dl.sdl_data, iname); 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 = write(s, &rtmes, nb); 142 if (wb < 0) { 143 LogPrintf(LogTCPIP, "OsSetRoute: Dst = %s\n", inet_ntoa(dst)); 144 LogPrintf(LogTCPIP, "OsSetRoute: Gateway = %s\n", inet_ntoa(gateway)); 145 LogPrintf(LogTCPIP, "OsSetRoute: Mask = %s\n", inet_ntoa(mask)); 146 switch (rtmes.m_rtm.rtm_errno) { 147 case EEXIST: 148 LogPrintf(LogWARN, "Add route failed: %s already exists\n", 149 inet_ntoa(dst)); 150 break; 151 case ESRCH: 152 LogPrintf(LogWARN, "Del route failed: %s: Non-existent\n", 153 inet_ntoa(dst)); 154 break; 155 case 0: 156 LogPrintf(LogWARN, "%s route failed: %s\n", cmdstr, strerror(errno)); 157 break; 158 case ENOBUFS: 159 default: 160 LogPrintf(LogWARN, "%s route failed: %s\n", 161 cmdstr, strerror(rtmes.m_rtm.rtm_errno)); 162 break; 163 } 164 } 165 LogPrintf(LogDEBUG, "wrote %d: cmd = %s, dst = %x, gateway = %x\n", 166 wb, cmdstr, dst.s_addr, gateway.s_addr); 167 close(s); 168} 169 170static void 171p_sockaddr(struct sockaddr *phost, struct sockaddr *pmask, int width) 172{ 173 char buf[29]; 174 struct sockaddr_in *ihost = (struct sockaddr_in *)phost; 175 struct sockaddr_in *mask = (struct sockaddr_in *)pmask; 176 struct sockaddr_dl *dl = (struct sockaddr_dl *)phost; 177 178 switch (phost->sa_family) { 179 case AF_INET: 180 if (!phost) 181 buf[0] = '\0'; 182 else if (ihost->sin_addr.s_addr == INADDR_ANY) 183 strcpy(buf, "default"); 184 else if (!mask) 185 strcpy(buf, inet_ntoa(ihost->sin_addr)); 186 else { 187 u_int msk = ntohl(mask->sin_addr.s_addr); 188 u_int tst; 189 int bits; 190 int len; 191 struct sockaddr_in net; 192 193 for (tst = 1, bits=32; tst; tst <<= 1, bits--) 194 if (msk & tst) 195 break; 196 197 for (tst <<=1; tst; tst <<= 1) 198 if (!(msk & tst)) 199 break; 200 201 net.sin_addr.s_addr = ihost->sin_addr.s_addr & mask->sin_addr.s_addr; 202 sprintf(buf, "%s", inet_ntoa(net.sin_addr)); 203 for (len = strlen(buf); len > 3; buf[len-=2] = '\0') 204 if (strcmp(buf+len-2, ".0")) 205 break; 206 207 if (tst) /* non-contiguous :-( */ 208 sprintf(buf+strlen(buf),"&0x%08x", msk); 209 else 210 sprintf(buf+strlen(buf), "/%d", bits); 211 } 212 break; 213 214 case AF_LINK: 215 if (dl->sdl_nlen) 216 snprintf(buf, sizeof buf, "%.*s", dl->sdl_nlen, dl->sdl_data); 217 else if (dl->sdl_alen) 218 if (dl->sdl_type == IFT_ETHER) 219 if (dl->sdl_alen < sizeof(buf)/3) { 220 int f; 221 u_char *MAC; 222 223 MAC = (u_char *)dl->sdl_data + dl->sdl_nlen; 224 for (f = 0; f < dl->sdl_alen; f++) 225 sprintf(buf+f*3, "%02x:", MAC[f]); 226 buf[f*3-1] = '\0'; 227 } else 228 sprintf(buf, "??:??:??:??:??:??"); 229 else 230 sprintf(buf, "<IFT type %d>", dl->sdl_type); 231 else if (dl->sdl_slen) 232 sprintf(buf, "<slen %d?>", dl->sdl_slen); 233 else 234 sprintf(buf, "link#%d", dl->sdl_index); 235 break; 236 237 default: 238 sprintf(buf, "<AF type %d>", phost->sa_family); 239 break; 240 } 241 242 fprintf(VarTerm, "%-*s ", width-1, buf); 243} 244 245struct bits { 246 u_long b_mask; 247 char b_val; 248} bits[] = { 249 250 { RTF_UP, 'U' }, 251 { RTF_GATEWAY, 'G' }, 252 { RTF_HOST, 'H' }, 253 { RTF_REJECT, 'R' }, 254 { RTF_DYNAMIC, 'D' }, 255 { RTF_MODIFIED, 'M' }, 256 { RTF_DONE, 'd' }, 257 { RTF_CLONING, 'C' }, 258 { RTF_XRESOLVE, 'X' }, 259 { RTF_LLINFO, 'L' }, 260 { RTF_STATIC, 'S' }, 261 { RTF_PROTO1, '1' }, 262 { RTF_PROTO2, '2' }, 263 { RTF_BLACKHOLE, 'B' }, 264#ifdef __FreeBSD__ 265 { RTF_WASCLONED, 'W' }, 266 { RTF_PRCLONING, 'c' }, 267 { RTF_PROTO3, '3' }, 268 { RTF_BROADCAST, 'b' }, 269#endif 270 { 0, '\0' } 271}; 272 273static void 274p_flags(u_long f, const char *format) 275{ 276 if (VarTerm) { 277 char name[33], *flags; 278 register struct bits *p = bits; 279 280 for (flags = name; p->b_mask; p++) 281 if (p->b_mask & f) 282 *flags++ = p->b_val; 283 *flags = '\0'; 284 fprintf(VarTerm, format, name); 285 } 286} 287 288static const char * 289Index2Nam(int idx) 290{ 291 static char ifs[200][6]; /* We could have 256 tun devices ! */ 292 static int nifs, debug_done; 293 294 if (!nifs) { 295 int mib[6], needed, len; 296 char *buf, *ptr, *end; 297 struct sockaddr_dl *dl; 298 struct if_msghdr *ifm; 299 300 mib[0] = CTL_NET; 301 mib[1] = PF_ROUTE; 302 mib[2] = 0; 303 mib[3] = 0; 304 mib[4] = NET_RT_IFLIST; 305 mib[5] = 0; 306 307 if (sysctl(mib, 6, NULL, &needed, NULL, 0) < 0) { 308 LogPrintf(LogERROR, "Index2Nam: sysctl: estimate: %s\n", strerror(errno)); 309 return "???"; 310 } 311 if ((buf = malloc(needed)) == NULL) 312 return "???"; 313 if (sysctl(mib, 6, buf, &needed, NULL, 0) < 0) { 314 free(buf); 315 return "???"; 316 } 317 end = buf + needed; 318 319 for (ptr = buf; ptr < end; ptr += ifm->ifm_msglen) { 320 ifm = (struct if_msghdr *)ptr; 321 dl = (struct sockaddr_dl *)(ifm + 1); 322 if (ifm->ifm_index > 0 && ifm->ifm_index <= sizeof(ifs)/sizeof(ifs[0]) 323 && ifs[ifm->ifm_index-1][0] == '\0') { 324 if ((len = dl->sdl_nlen) > sizeof(ifs[0])-1) 325 len = sizeof(ifs[0])-1; 326 strncpy(ifs[ifm->ifm_index-1], dl->sdl_data, len); 327 ifs[ifm->ifm_index-1][len] = '\0'; 328 if (len && nifs < ifm->ifm_index) 329 nifs = ifm->ifm_index; 330 } else if (LogIsKept(LogDEBUG)) 331 LogPrintf(LogDEBUG, "Skipping out-of-range interface %d!\n", 332 ifm->ifm_index); 333 } 334 free(buf); 335 } 336 337 if (LogIsKept(LogDEBUG) && !debug_done) { 338 int f; 339 340 LogPrintf(LogDEBUG, "Found the following interfaces:\n"); 341 for (f = 0; f < nifs; f++) 342 if (*ifs[f] != '\0') 343 LogPrintf(LogDEBUG, " Index %d, name \"%s\"\n", f+1, ifs[f]); 344 debug_done = 1; 345 } 346 347 if (idx < 1 || idx > nifs || ifs[idx-1][0] == '\0') 348 return "???"; 349 350 return ifs[idx-1]; 351} 352 353int 354ShowRoute(struct cmdargs const *arg) 355{ 356 struct rt_msghdr *rtm; 357 struct sockaddr *sa_dst, *sa_gw, *sa_mask; 358 char *sp, *ep, *cp, *wp; 359 int needed; 360 int mib[6]; 361 362 if (!VarTerm) 363 return 1; 364 365 mib[0] = CTL_NET; 366 mib[1] = PF_ROUTE; 367 mib[2] = 0; 368 mib[3] = 0; 369 mib[4] = NET_RT_DUMP; 370 mib[5] = 0; 371 if (sysctl(mib, 6, NULL, &needed, NULL, 0) < 0) { 372 LogPrintf(LogERROR, "ShowRoute: sysctl: estimate: %s\n", strerror(errno)); 373 return (1); 374 } 375 if (needed < 0) 376 return (1); 377 sp = malloc(needed); 378 if (sp == NULL) 379 return (1); 380 if (sysctl(mib, 6, sp, &needed, NULL, 0) < 0) { 381 LogPrintf(LogERROR, "ShowRoute: sysctl: getroute: %s\n", strerror(errno)); 382 free(sp); 383 return (1); 384 } 385 ep = sp + needed; 386 387 fprintf(VarTerm, "%-20s%-20sFlags Netif\n", "Destination", "Gateway"); 388 for (cp = sp; cp < ep; cp += rtm->rtm_msglen) { 389 rtm = (struct rt_msghdr *) cp; 390 wp = (char *)(rtm+1); 391 392 if (rtm->rtm_addrs & RTA_DST) { 393 sa_dst = (struct sockaddr *)wp; 394 wp += sa_dst->sa_len; 395 } else 396 sa_dst = NULL; 397 398 if (rtm->rtm_addrs & RTA_GATEWAY) { 399 sa_gw = (struct sockaddr *)wp; 400 wp += sa_gw->sa_len; 401 } else 402 sa_gw = NULL; 403 404 if (rtm->rtm_addrs & RTA_NETMASK) { 405 sa_mask = (struct sockaddr *)wp; 406 wp += sa_mask->sa_len; 407 } else 408 sa_mask = NULL; 409 410 p_sockaddr(sa_dst, sa_mask, 20); 411 p_sockaddr(sa_gw, NULL, 20); 412 413 p_flags(rtm->rtm_flags, "%-6.6s "); 414 fprintf(VarTerm, "%s\n", Index2Nam(rtm->rtm_index)); 415 } 416 free(sp); 417 return 0; 418} 419 420/* 421 * Delete routes associated with our interface 422 */ 423void 424DeleteIfRoutes(int all) 425{ 426 struct rt_msghdr *rtm; 427 struct sockaddr *sa; 428 struct in_addr sa_dst, sa_none; 429 int needed; 430 char *sp, *cp, *ep; 431 int mib[6]; 432 433 LogPrintf(LogDEBUG, "DeleteIfRoutes (%d)\n", IfIndex); 434 sa_none.s_addr = INADDR_ANY; 435 436 mib[0] = CTL_NET; 437 mib[1] = PF_ROUTE; 438 mib[2] = 0; 439 mib[3] = 0; 440 mib[4] = NET_RT_DUMP; 441 mib[5] = 0; 442 if (sysctl(mib, 6, NULL, &needed, NULL, 0) < 0) { 443 LogPrintf(LogERROR, "DeleteIfRoutes: sysctl: estimate: %s\n", 444 strerror(errno)); 445 return; 446 } 447 if (needed < 0) 448 return; 449 450 sp = malloc(needed); 451 if (sp == NULL) 452 return; 453 454 if (sysctl(mib, 6, sp, &needed, NULL, 0) < 0) { 455 LogPrintf(LogERROR, "DeleteIfRoutes: sysctl: getroute: %s\n", 456 strerror(errno)); 457 free(sp); 458 return; 459 } 460 ep = sp + needed; 461 462 for (cp = sp; cp < ep; cp += rtm->rtm_msglen) { 463 rtm = (struct rt_msghdr *) cp; 464 sa = (struct sockaddr *) (rtm + 1); 465 LogPrintf(LogDEBUG, "DeleteIfRoutes: addrs: %x, Netif: %d (%s), flags: %x," 466 " dst: %s ?\n", rtm->rtm_addrs, rtm->rtm_index, 467 Index2Nam(rtm->rtm_index), rtm->rtm_flags, 468 inet_ntoa(((struct sockaddr_in *) sa)->sin_addr)); 469 if (rtm->rtm_addrs & RTA_DST && rtm->rtm_addrs & RTA_GATEWAY && 470 rtm->rtm_index == IfIndex && 471 (all || (rtm->rtm_flags & RTF_GATEWAY))) { 472 sa_dst.s_addr = ((struct sockaddr_in *)sa)->sin_addr.s_addr; 473 sa = (struct sockaddr *)((char *)sa + sa->sa_len); 474 if (sa->sa_family == AF_INET || sa->sa_family == AF_LINK) { 475 LogPrintf(LogDEBUG, "DeleteIfRoutes: Remove it\n"); 476 LogPrintf(LogDEBUG, "DeleteIfRoutes: Dst: %s\n", inet_ntoa(sa_dst)); 477 OsSetRoute(RTM_DELETE, sa_dst, sa_none, sa_none); 478 } else 479 LogPrintf(LogDEBUG, 480 "DeleteIfRoutes: Can't remove routes of %d family !\n", 481 sa->sa_family); 482 } 483 } 484 free(sp); 485} 486 487int 488GetIfIndex(char *name) 489{ 490 int idx; 491 const char *got; 492 493 idx = 1; 494 while (strcmp(got = Index2Nam(idx), "???")) 495 if (!strcmp(got, name)) 496 return IfIndex = idx; 497 else 498 idx++; 499 return -1; 500} 501 502struct in_addr 503ChooseHisAddr(const struct in_addr gw) 504{ 505 struct in_addr try; 506 int f; 507 508 for (f = 0; f < DefHisChoice.nItems; f++) { 509 try = iplist_next(&DefHisChoice); 510 LogPrintf(LogDEBUG, "ChooseHisAddr: Check item %d (%s)\n", 511 f, inet_ntoa(try)); 512 if (OsTrySetIpaddress(gw, try) == 0) { 513 LogPrintf(LogIPCP, "ChooseHisAddr: Selected IP address %s\n", 514 inet_ntoa(try)); 515 break; 516 } 517 } 518 519 if (f == DefHisChoice.nItems) { 520 LogPrintf(LogDEBUG, "ChooseHisAddr: All addresses in use !\n"); 521 try.s_addr = INADDR_ANY; 522 } 523 524 return try; 525} 526