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