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