1 /* 2 * Copyright 2006-2013, Haiku, Inc. All Rights Reserved. 3 * Distributed under the terms of the MIT License. 4 * 5 * Authors: 6 * Axel Dörfler, axeld@pinc-software.de 7 * Alexander von Gluck <kallisti5@unixzen.com> 8 */ 9 10 11 #include <arpa/inet.h> 12 #include <net/if.h> 13 #include <net/if_dl.h> 14 #include <net/if_types.h> 15 #include <netinet/in.h> 16 #include <sys/socket.h> 17 #include <sys/sockio.h> 18 19 #include <errno.h> 20 #include <stdio.h> 21 #include <stdlib.h> 22 #include <string.h> 23 #include <unistd.h> 24 25 #include <NetworkAddress.h> 26 27 28 extern const char* __progname; 29 const char* kProgramName = __progname; 30 31 32 enum modes { 33 RTM_LIST = 0, 34 RTM_DELETE, 35 RTM_ADD, 36 RTM_GET, 37 38 // TODO: 39 RTM_CHANGE, 40 RTM_FLUSH, 41 }; 42 43 enum preferredPrefixFormat { 44 PREFIX_PREFER_NETMASK = 0, 45 PREFIX_PREFER_CIDR, 46 }; 47 48 struct address_family { 49 int family; 50 const char* name; 51 const char* identifiers[4]; 52 int maxAddressLength; 53 int preferredPrefixFormat; 54 }; 55 56 static const address_family kFamilies[] = { 57 { 58 AF_INET, 59 "IPv4", 60 {"AF_INET", "inet", "ipv4", NULL}, 61 15, 62 PREFIX_PREFER_NETMASK, 63 }, 64 { 65 AF_INET6, 66 "IPv6", 67 {"AF_INET6", "inet6", "ipv6", NULL}, 68 39, 69 PREFIX_PREFER_CIDR, 70 }, 71 { -1, NULL, {NULL}, -1, -1 } 72 }; 73 74 75 void 76 usage(int status) 77 { 78 printf("usage: %s [command] [<interface>] [<address family>] " 79 "<address|default> [<option/flags>...]\n" 80 "Where <command> can be the one of:\n" 81 " add - add a route for the specified interface\n" 82 " delete - deletes the specified route\n" 83 " list - list with filters [default]\n" 84 "<option> can be the following:\n" 85 " netmask <addr> - networking subnet mask\n" 86 " prefixlen <number> - subnet mask length in bits\n" 87 " gw <addr> - gateway address\n" 88 " mtu <bytes> - maximal transfer unit\n" 89 "And <flags> can be: reject, local, host\n\n" 90 "Example:\n" 91 "\t%s add /dev/net/ipro1000/0 default gw 192.168.0.254\n", 92 kProgramName, kProgramName); 93 94 exit(status); 95 } 96 97 98 bool 99 prepare_request(struct ifreq& request, const char* name) 100 { 101 if (strlen(name) >= IF_NAMESIZE) { 102 fprintf(stderr, "%s: interface name \"%s\" is too long.\n", 103 kProgramName, name); 104 return false; 105 } 106 107 strcpy(request.ifr_name, name); 108 return true; 109 } 110 111 112 bool 113 get_address_family(const char* argument, int32& familyIndex) 114 { 115 for (int32 i = 0; kFamilies[i].family >= 0; i++) { 116 for (int32 j = 0; kFamilies[i].identifiers[j]; j++) { 117 if (!strcmp(argument, kFamilies[i].identifiers[j])) { 118 // found a match 119 familyIndex = i; 120 return true; 121 } 122 } 123 } 124 125 // defaults to AF_INET 126 familyIndex = 0; 127 return false; 128 } 129 130 131 bool 132 parse_address(int32 familyIndex, const char* argument, BNetworkAddress& address) 133 { 134 if (argument == NULL) 135 return false; 136 137 return address.SetTo(kFamilies[familyIndex].family, argument, 138 (uint16)0, B_NO_ADDRESS_RESOLUTION) == B_OK; 139 } 140 141 142 bool 143 prefix_length_to_mask(int32 familyIndex, const char* argument, 144 BNetworkAddress& mask) 145 { 146 if (argument == NULL) 147 return false; 148 149 char* end; 150 uint32 prefixLength = strtoul(argument, &end, 10); 151 if (end == argument) 152 return false; 153 154 return mask.SetToMask(kFamilies[familyIndex].family, prefixLength) == B_OK; 155 } 156 157 158 // #pragma mark - 159 160 161 void 162 list_routes(int socket, const char *interfaceName, route_entry &route) 163 { 164 // get a list of all routes 165 166 ifconf config; 167 config.ifc_len = sizeof(config.ifc_value); 168 if (ioctl(socket, SIOCGRTSIZE, &config, sizeof(struct ifconf)) < 0) 169 return; 170 171 uint32 size = (uint32)config.ifc_value; 172 if (size == 0) 173 return; 174 175 void *buffer = malloc(size); 176 if (buffer == NULL) { 177 fprintf(stderr, "%s: Out of memory.\n", kProgramName); 178 return; 179 } 180 181 config.ifc_len = size; 182 config.ifc_buf = buffer; 183 if (ioctl(socket, SIOCGRTTABLE, &config, sizeof(struct ifconf)) < 0) 184 return; 185 186 ifreq *interface = (ifreq*)buffer; 187 ifreq *end = (ifreq*)((uint8*)buffer + size); 188 189 // find family (we use the family of the first address as this is 190 // called on a socket for a single family) 191 const address_family *family = NULL; 192 for (int32 i = 0; kFamilies[i].family >= 0; i++) { 193 if (interface->ifr_route.destination->sa_family 194 == kFamilies[i].family) { 195 family = &kFamilies[i]; 196 break; 197 } 198 } 199 200 int addressLength = family->maxAddressLength; 201 202 printf("%s routing table:\n", family->name); 203 204 if (family->preferredPrefixFormat == PREFIX_PREFER_NETMASK) { 205 printf("%*s %*s %*s Flags Interface\n", addressLength, "Destination", 206 addressLength, "Netmask", addressLength, "Gateway"); 207 } else { 208 printf("%*s %*s Flags Interface\n", addressLength, "Destination", 209 addressLength, "Gateway"); 210 } 211 212 while (interface < end) { 213 route_entry& route = interface->ifr_route; 214 215 // apply filters 216 if (interfaceName == NULL 217 || !strcmp(interfaceName, interface->ifr_name)) { 218 219 if (family != NULL) { 220 BNetworkAddress destination(*route.destination); 221 printf("%*s", addressLength, destination.ToString().String()); 222 if (route.mask != NULL) { 223 BNetworkAddress mask; 224 mask.SetTo(*route.mask); 225 if (family->preferredPrefixFormat 226 == PREFIX_PREFER_NETMASK) { 227 printf(" %*s ", addressLength, 228 mask.ToString().String()); 229 } else { 230 printf("/%-3zd ", mask.PrefixLength()); 231 } 232 } else { 233 if (family->preferredPrefixFormat 234 == PREFIX_PREFER_NETMASK) { 235 printf(" %*s ", addressLength, "-"); 236 } else 237 printf(" "); 238 } 239 240 if ((route.flags & RTF_GATEWAY) != 0) { 241 BNetworkAddress gateway; 242 if (route.gateway != NULL) 243 gateway.SetTo(*route.gateway); 244 printf("%*s ", addressLength, gateway.ToString().String()); 245 } else 246 printf("%*s ", family->maxAddressLength, "-"); 247 } else 248 printf("unknown family "); 249 250 if (route.flags != 0) { 251 const struct { 252 int value; 253 const char *name; 254 } kFlags[] = { 255 {RTF_DEFAULT, "D"}, 256 {RTF_REJECT, "R"}, 257 {RTF_HOST, "H"}, 258 {RTF_LOCAL, "L"}, 259 {RTF_DYNAMIC, "D"}, 260 {RTF_MODIFIED, "M"}, 261 }; 262 for (uint32 i = 0; i < sizeof(kFlags) / sizeof(kFlags[0]); 263 i++) { 264 if ((route.flags & kFlags[i].value) != 0) { 265 printf(kFlags[i].name); 266 } else 267 putchar('-'); 268 } 269 printf(" "); 270 } else 271 printf("------ "); 272 273 printf("%s", interface->ifr_name); 274 putchar('\n'); 275 } 276 277 int32 addressSize = 0; 278 if (route.destination != NULL) 279 addressSize += route.destination->sa_len; 280 if (route.mask != NULL) 281 addressSize += route.mask->sa_len; 282 if (route.gateway != NULL) 283 addressSize += route.gateway->sa_len; 284 285 interface = (ifreq*)((addr_t)interface + IF_NAMESIZE 286 + sizeof(route_entry) + addressSize); 287 } 288 289 putchar('\n'); 290 free(buffer); 291 } 292 293 294 void 295 delete_route(int socket, const char *interface, route_entry &route) 296 { 297 ifreq request; 298 if (!prepare_request(request, interface)) 299 return; 300 301 request.ifr_route = route; 302 303 if (ioctl(socket, SIOCDELRT, &request, sizeof(request)) < 0) { 304 fprintf(stderr, "%s: Could not delete route for %s: %s\n", 305 kProgramName, interface, strerror(errno)); 306 } 307 } 308 309 310 void 311 add_route(int socket, const char *interface, route_entry &route) 312 { 313 ifreq request; 314 if (!prepare_request(request, interface)) 315 return; 316 317 route.flags |= RTF_STATIC; 318 request.ifr_route = route; 319 320 if (ioctl(socket, SIOCADDRT, &request, sizeof(request)) < 0) { 321 fprintf(stderr, "%s: Could not add route for %s: %s\n", 322 kProgramName, interface, strerror(errno)); 323 } 324 } 325 326 327 void 328 get_route(int socket, route_entry &route) 329 { 330 union { 331 route_entry request; 332 uint8 buffer[512]; 333 }; 334 335 request = route; 336 337 if (ioctl(socket, SIOCGETRT, buffer, sizeof(buffer)) < 0) { 338 fprintf(stderr, "%s: Could not get route: %s\n", 339 kProgramName, strerror(errno)); 340 return; 341 } 342 343 // find family 344 const address_family *family = NULL; 345 for (int32 i = 0; kFamilies[i].family >= 0; i++) { 346 if (route.destination->sa_family == kFamilies[i].family) { 347 family = &kFamilies[i]; 348 break; 349 } 350 } 351 352 if (family != NULL) { 353 BNetworkAddress destination(*request.destination); 354 BNetworkAddress mask(*request.mask); 355 printf("%s", destination.ToString().String()); 356 printf("/%zd ", mask.PrefixLength()); 357 358 BNetworkAddress gateway(*request.gateway); 359 if (request.flags & RTF_GATEWAY) 360 printf("gateway %s ", gateway.ToString().String()); 361 362 BNetworkAddress source(*request.source); 363 printf("source %s\n", source.ToString().String()); 364 } else { 365 printf("unknown family "); 366 } 367 } 368 369 370 // #pragma mark - 371 372 373 int 374 main(int argc, char** argv) 375 { 376 if (argc > 1 && (!strcmp(argv[1], "--help") || !strcmp(argv[1], "-h"))) 377 usage(0); 378 379 int32 mode = RTM_LIST; 380 381 if (argc > 1) { 382 if (!strcmp(argv[1], "delete") 383 || !strcmp(argv[1], "del") 384 || !strcmp(argv[1], "-d")) { 385 // delete route 386 if (argc < 3) 387 usage(1); 388 389 mode = RTM_DELETE; 390 } else if (!strcmp(argv[1], "add") 391 || !strcmp(argv[1], "-a")) { 392 // add route 393 if (argc < 3) 394 usage(1); 395 396 mode = RTM_ADD; 397 } else if (!strcmp(argv[1], "get")) { 398 // get route for destination 399 if (argc < 3) 400 usage(1); 401 402 mode = RTM_GET; 403 } 404 } 405 406 int32 i = 2; 407 int32 interfaceIndex = i; 408 bool familySpecified = false; 409 int32 familyIndex = 0; 410 const char *interface = NULL; 411 BNetworkAddress destination; 412 BNetworkAddress mask; 413 BNetworkAddress gateway; 414 bool defaultRoute = false; 415 416 route_entry route; 417 memset(&route, 0, sizeof(route_entry)); 418 419 while (i < argc && i < 5) { 420 // try to parse address family 421 if (i <= 3 && familySpecified == false 422 && get_address_family(argv[i], familyIndex)) { 423 familySpecified = true; 424 if (i == 2) 425 interfaceIndex = -1; 426 } 427 428 if (!strcmp(argv[i], "default")) { 429 defaultRoute = true; 430 route.flags = RTF_DEFAULT; 431 i++; 432 break; 433 } else if (parse_address(familyIndex, argv[i], destination)) { 434 i++; 435 break; 436 } 437 438 i++; 439 } 440 441 if (!defaultRoute && destination.IsEmpty() && mode != RTM_LIST) 442 usage(1); 443 444 if (i == 3) 445 interfaceIndex = -1; 446 if (interfaceIndex != -1 && interfaceIndex < argc) 447 interface = argv[interfaceIndex]; 448 449 if (i < argc && parse_address(familyIndex, argv[i], mask)) 450 i++; 451 452 // parse options and flags 453 454 while (i < argc) { 455 if (!strcmp(argv[i], "gw") || !strcmp(argv[i], "gateway")) { 456 if (!parse_address(familyIndex, argv[i + 1], gateway)) { 457 fprintf(stderr, "%s: Option 'gw' needs valid address " 458 "parameter\n", kProgramName); 459 exit(1); 460 } 461 i++; 462 } else if (!strcmp(argv[i], "nm") || !strcmp(argv[i], "netmask")) { 463 if (!mask.IsEmpty()) { 464 fprintf(stderr, "%s: Netmask or prefix length is specified " 465 "twice\n", kProgramName); 466 exit(1); 467 } 468 if (!parse_address(familyIndex, argv[i + 1], mask)) { 469 fprintf(stderr, "%s: Option 'netmask' needs valid address " 470 "parameter\n", kProgramName); 471 exit(1); 472 } 473 i++; 474 } else if (!strcmp(argv[i], "plen") || !strcmp(argv[i], "prefixlen") 475 || !strcmp(argv[i], "prefix-length")) { 476 if (!mask.IsEmpty()) { 477 fprintf(stderr, "%s: Netmask or prefix length is specified " 478 "twice\n", kProgramName); 479 exit(1); 480 } 481 if (!prefix_length_to_mask(familyIndex, argv[i + 1], mask)) { 482 fprintf(stderr, "%s: Option 'prefixlen' is invalid for this " 483 "address family\n", kProgramName); 484 exit(1); 485 } 486 i++; 487 } else if (!strcmp(argv[i], "mtu")) { 488 route.mtu = argv[i + 1] ? strtol(argv[i + 1], NULL, 0) : 0; 489 if (route.mtu <= 500) { 490 fprintf(stderr, "%s: Option 'mtu' exptected valid max transfer " 491 "unit size\n", kProgramName); 492 exit(1); 493 } 494 i++; 495 } else if (!strcmp(argv[i], "host")) { 496 route.flags |= RTF_HOST; 497 } else if (!strcmp(argv[i], "local")) { 498 route.flags |= RTF_LOCAL; 499 } else if (!strcmp(argv[i], "reject")) { 500 route.flags |= RTF_REJECT; 501 } else 502 usage(1); 503 504 i++; 505 } 506 507 if (!destination.IsEmpty()) 508 route.destination = (sockaddr*)destination; 509 if (!mask.IsEmpty()) 510 route.mask = (sockaddr*)mask; 511 if (!gateway.IsEmpty()) { 512 route.gateway = (sockaddr*)gateway; 513 route.flags |= RTF_GATEWAY; 514 } 515 516 // we need a socket to talk to the networking stack 517 int socket = ::socket(kFamilies[familyIndex].family, SOCK_DGRAM, 0); 518 if (socket < 0) { 519 fprintf(stderr, "%s: The requested address family is not available.\n", 520 kProgramName); 521 return 1; 522 } 523 524 switch (mode) { 525 case RTM_ADD: 526 if (interface == NULL) { 527 fprintf(stderr, "%s: You need to specify an interface when " 528 "adding a route.\n", kProgramName); 529 usage(1); 530 } 531 532 add_route(socket, interface, route); 533 break; 534 case RTM_DELETE: 535 if (interface == NULL) { 536 fprintf(stderr, "%s: You need to specify an interface when " 537 "removing a route.\n", kProgramName); 538 usage(1); 539 } 540 541 delete_route(socket, interface, route); 542 break; 543 544 case RTM_LIST: 545 if (familySpecified) 546 list_routes(socket, interface, route); 547 else { 548 for (int32 i = 0; kFamilies[i].family >= 0; i++) { 549 int socket = ::socket(kFamilies[i].family, SOCK_DGRAM, 0); 550 if (socket < 0) 551 continue; 552 553 list_routes(socket, interface, route); 554 close(socket); 555 } 556 } 557 break; 558 559 case RTM_GET: 560 get_route(socket, route); 561 break; 562 } 563 564 close(socket); 565 return 0; 566 } 567 568