1 /* 2 * Copyright 2006, 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 * Oliver Tappe, zooey@hirschkaefer.de 8 */ 9 10 11 #include <SupportDefs.h> 12 13 #include <arpa/inet.h> 14 #include <net/if.h> 15 #include <net/if_dl.h> 16 #include <net/if_types.h> 17 #include <netinet/in.h> 18 #include <sys/socket.h> 19 #include <sys/sockio.h> 20 21 #include <errno.h> 22 #include <stdio.h> 23 #include <stdlib.h> 24 #include <string.h> 25 #include <unistd.h> 26 27 28 extern const char* __progname; 29 const char* kProgramName = __progname; 30 31 32 struct address_family { 33 int family; 34 const char* name; 35 const char* identifiers[4]; 36 bool (*parse_address)(const char* string, sockaddr* _address); 37 void (*print_address)(sockaddr* address); 38 }; 39 40 // AF_INET family 41 static bool inet_parse_address(const char* string, sockaddr* address); 42 static void inet_print_address(sockaddr* address); 43 44 static const address_family kFamilies[] = { 45 { 46 AF_INET, 47 "inet", 48 {"AF_INET", "inet", "ipv4", NULL}, 49 inet_parse_address, 50 inet_print_address 51 }, 52 { -1, NULL, {NULL}, NULL, NULL } 53 }; 54 55 56 static bool 57 inet_parse_address(const char* string, sockaddr* _address) 58 { 59 in_addr inetAddress; 60 61 if (inet_aton(string, &inetAddress) != 1) 62 return false; 63 64 sockaddr_in& address = *(sockaddr_in *)_address; 65 address.sin_family = AF_INET; 66 address.sin_len = sizeof(struct sockaddr_in); 67 address.sin_port = 0; 68 address.sin_addr = inetAddress; 69 memset(&address.sin_zero[0], 0, sizeof(address.sin_zero)); 70 71 return true; 72 } 73 74 75 static void 76 inet_print_address(sockaddr* _address) 77 { 78 sockaddr_in& address = *(sockaddr_in *)_address; 79 80 if (address.sin_family != AF_INET) 81 return; 82 83 printf("%s", inet_ntoa(address.sin_addr)); 84 } 85 86 87 // #pragma mark - 88 89 90 void 91 usage(int status) 92 { 93 printf("usage: %s [<interface> [<address family>] [<address> [<mask>]] [<option/flags>...]]\n" 94 "\t%s --delete interface [...]\n\n" 95 "Where <option> can be the following:\n" 96 " netmask <addr> - networking subnet mask\n" 97 " broadcast <addr> - set broadcast address\n" 98 " peer <addr> - ppp-peer address\n" 99 " mtu <bytes> - maximal transfer unit\n" 100 " metric <number> - metric number to use (defaults to 0)\n" 101 "And <flags> can be: up, down, [-]promisc, [-]allmulti, [-]bcast, loopback\n\n" 102 "Example:\n" 103 "\t%s loop 127.0.0.1 255.0.0.0 up\n", 104 kProgramName, kProgramName, kProgramName); 105 106 exit(status); 107 } 108 109 110 bool 111 prepare_request(struct ifreq& request, const char* name) 112 { 113 if (strlen(name) > IF_NAMESIZE) { 114 fprintf(stderr, "%s: interface name \"%s\" is too long.\n", kProgramName, name); 115 return false; 116 } 117 118 strcpy(request.ifr_name, name); 119 return true; 120 } 121 122 123 bool 124 get_address_family(const char* argument, int32& familyIndex) 125 { 126 for (int32 i = 0; kFamilies[i].family >= 0; i++) { 127 for (int32 j = 0; kFamilies[i].identifiers[j]; j++) { 128 if (!strcmp(argument, kFamilies[i].identifiers[j])) { 129 // found a match 130 familyIndex = i; 131 return true; 132 } 133 } 134 } 135 136 // defaults to AF_INET 137 familyIndex = 0; 138 return false; 139 } 140 141 142 bool 143 parse_address(int32 familyIndex, const char* argument, struct sockaddr& address) 144 { 145 if (argument == NULL) 146 return false; 147 148 return kFamilies[familyIndex].parse_address(argument, &address); 149 } 150 151 152 // #pragma mark - 153 154 155 void 156 list_interface(int socket, const char* name) 157 { 158 ifreq request; 159 if (!prepare_request(request, name)) 160 return; 161 162 if (ioctl(socket, SIOCGIFINDEX, &request, sizeof(request)) < 0) { 163 fprintf(stderr, "%s: Interface \"%s\" does not exist.\n", kProgramName, name); 164 return; 165 } 166 167 printf("%s", name); 168 size_t length = strlen(name); 169 if (length < 8) 170 putchar('\t'); 171 else 172 printf("\n\t"); 173 174 // get link level interface for this interface 175 176 int linkSocket = ::socket(AF_LINK, SOCK_DGRAM, 0); 177 if (linkSocket < 0) { 178 printf("No link level: %s\n", strerror(errno)); 179 } else { 180 const char *type = "unknown"; 181 char address[256]; 182 strcpy(address, "none"); 183 184 if (ioctl(socket, SIOCGIFPARAM, &request, sizeof(struct ifreq)) == 0) { 185 prepare_request(request, request.ifr_parameter.device); 186 if (ioctl(linkSocket, SIOCGIFADDR, &request, sizeof(struct ifreq)) == 0) { 187 sockaddr_dl &link = *(sockaddr_dl *)&request.ifr_addr; 188 189 switch (link.sdl_type) { 190 case IFT_ETHER: 191 { 192 type = "Ethernet"; 193 194 if (link.sdl_alen > 0) { 195 uint8 *mac = (uint8 *)LLADDR(&link); 196 sprintf(address, "%02x:%02x:%02x:%02x:%02x:%02x", 197 mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]); 198 } else 199 strcpy(address, "not available"); 200 break; 201 } 202 case IFT_LOOP: 203 type = "Local Loopback"; 204 break; 205 case IFT_MODEM: 206 type = "Modem"; 207 break; 208 } 209 } 210 } 211 212 printf("Hardware Type: %s, Address: %s\n", type, address); 213 close(linkSocket); 214 } 215 216 uint32 flags = 0; 217 if (ioctl(socket, SIOCGIFFLAGS, &request, sizeof(struct ifreq)) == 0) 218 flags = request.ifr_flags; 219 220 for (int32 i = 0; kFamilies[i].family >= 0; i++) { 221 int familySocket = ::socket(kFamilies[i].family, SOCK_DGRAM, 0); 222 if (familySocket < 0) 223 continue; 224 225 if (ioctl(familySocket, SIOCGIFADDR, &request, sizeof(struct ifreq)) == 0) { 226 printf("\t%s addr: ", kFamilies[i].name); 227 kFamilies[i].print_address(&request.ifr_addr); 228 229 if ((flags & IFF_BROADCAST) != 0 230 && ioctl(familySocket, SIOCGIFBRDADDR, &request, sizeof(struct ifreq)) == 0 231 && request.ifr_broadaddr.sa_family == kFamilies[i].family) { 232 printf(", Bcast: "); 233 kFamilies[i].print_address(&request.ifr_broadaddr); 234 } 235 if (ioctl(familySocket, SIOCGIFNETMASK, &request, sizeof(struct ifreq)) == 0 236 && request.ifr_mask.sa_family == kFamilies[i].family) { 237 printf(", Mask: "); 238 kFamilies[i].print_address(&request.ifr_mask); 239 } 240 putchar('\n'); 241 } 242 243 close(familySocket); 244 } 245 246 // Print MTU, metric, flags 247 248 printf("\tMTU: "); 249 if (ioctl(socket, SIOCGIFMTU, &request, sizeof(struct ifreq)) == 0) 250 printf("%d", request.ifr_mtu); 251 else 252 printf("-"); 253 254 printf(", Metric: "); 255 if (ioctl(socket, SIOCGIFMETRIC, &request, sizeof(struct ifreq)) == 0) 256 printf("%d", request.ifr_metric); 257 else 258 printf("-"); 259 260 if (flags != 0) { 261 const struct { 262 int value; 263 const char *name; 264 } kFlags[] = { 265 {IFF_UP, "up"}, 266 {IFF_NOARP, "noarp"}, 267 {IFF_BROADCAST, "broadcast"}, 268 {IFF_LOOPBACK, "loopback"}, 269 {IFF_PROMISC, "promiscuous"}, 270 {IFF_ALLMULTI, "allmulti"}, 271 {IFF_AUTOUP, "autoup"}, 272 }; 273 bool first = true; 274 275 for (uint32 i = 0; i < sizeof(kFlags) / sizeof(kFlags[0]); i++) { 276 if ((flags & kFlags[i].value) != 0) { 277 if (first) { 278 printf(","); 279 first = false; 280 } 281 putchar(' '); 282 printf(kFlags[i].name); 283 } 284 } 285 } 286 287 putchar('\n'); 288 289 // Print statistics 290 291 if (ioctl(socket, SIOCGIFSTATS, &request, sizeof(struct ifreq)) == 0) { 292 printf("\tReceive: %ld packets, %ld errors, %Ld bytes, %ld mcasts, %ld dropped\n", 293 request.ifr_stats.receive.packets, request.ifr_stats.receive.errors, 294 request.ifr_stats.receive.bytes, request.ifr_stats.receive.multicast_packets, 295 request.ifr_stats.receive.dropped); 296 printf("\tTransmit: %ld packets, %ld errors, %Ld bytes, %ld mcasts, %ld dropped\n", 297 request.ifr_stats.send.packets, request.ifr_stats.send.errors, 298 request.ifr_stats.send.bytes, request.ifr_stats.send.multicast_packets, 299 request.ifr_stats.send.dropped); 300 printf("\tCollisions: %ld\n", request.ifr_stats.collisions); 301 } 302 303 putchar('\n'); 304 } 305 306 307 void 308 list_interfaces(int socket, const char* name) 309 { 310 if (name != NULL) { 311 list_interface(socket, name); 312 return; 313 } 314 315 // get a list of all interfaces 316 317 ifconf config; 318 config.ifc_len = sizeof(config.ifc_value); 319 if (ioctl(socket, SIOCGIFCOUNT, &config, sizeof(struct ifconf)) < 0) 320 return; 321 322 uint32 count = (uint32)config.ifc_value; 323 if (count == 0) { 324 fprintf(stderr, "%s: There are no interfaces yet!\n", kProgramName); 325 return; 326 } 327 328 void *buffer = malloc(count * sizeof(struct ifreq)); 329 if (buffer == NULL) { 330 fprintf(stderr, "%s: Out of memory.\n", kProgramName); 331 return; 332 } 333 334 config.ifc_len = count * sizeof(struct ifreq); 335 config.ifc_buf = buffer; 336 if (ioctl(socket, SIOCGIFCONF, &config, sizeof(struct ifconf)) < 0) 337 return; 338 339 ifreq *interface = (ifreq *)buffer; 340 341 for (uint32 i = 0; i < count; i++) { 342 list_interface(socket, interface->ifr_name); 343 344 interface = (ifreq *)((addr_t)interface + IF_NAMESIZE + interface->ifr_addr.sa_len); 345 } 346 347 free(buffer); 348 } 349 350 351 void 352 delete_interface(int socket, const char* name) 353 { 354 ifreq request; 355 if (!prepare_request(request, name)) 356 return; 357 358 if (ioctl(socket, SIOCDIFADDR, &request, sizeof(request)) < 0) { 359 fprintf(stderr, "%s: Could not delete interface %s: %s\n", 360 kProgramName, name, strerror(errno)); 361 } 362 } 363 364 365 void 366 configure_interface(int socket, const char* name, char* const* args, 367 int32 argCount) 368 { 369 ifreq request; 370 if (!prepare_request(request, name)) 371 return; 372 373 uint32 index = 0; 374 if (ioctl(socket, SIOCGIFINDEX, &request, sizeof(request)) >= 0) 375 index = request.ifr_index; 376 377 bool hasAddress = false, hasMask = false, hasPeer = false, hasBroadcast = false; 378 struct sockaddr address, mask, peer, broadcast; 379 int mtu = -1, metric = -1, addFlags = 0, removeFlags = 0, currentFlags = 0; 380 381 // try to parse address family 382 383 int32 familyIndex; 384 int32 i = 0; 385 if (get_address_family(args[i], familyIndex)) 386 i++; 387 388 if (kFamilies[familyIndex].family != AF_INET) { 389 close(socket); 390 391 // replace socket with one of the correct address family 392 socket = ::socket(kFamilies[familyIndex].family, SOCK_DGRAM, 0); 393 if (socket < 0) { 394 fprintf(stderr, "%s: Address family \"%s\" is not available.\n", 395 kProgramName, kFamilies[familyIndex].name); 396 exit(1); 397 } 398 } 399 400 if (index == 0) { 401 // the interface does not exist yet, we have to add it first 402 request.ifr_parameter.base_name[0] = '\0'; 403 request.ifr_parameter.device[0] = '\0'; 404 request.ifr_parameter.sub_type = 0; 405 // the default device is okay for us 406 407 if (ioctl(socket, SIOCAIFADDR, &request, sizeof(request)) < 0) { 408 fprintf(stderr, "%s: Could not add interface: %s\n", kProgramName, 409 strerror(errno)); 410 exit(1); 411 } 412 } 413 414 // try to parse address 415 416 if (parse_address(familyIndex, args[i], address)) { 417 hasAddress = true; 418 i++; 419 420 if (parse_address(familyIndex, args[i], mask)) { 421 hasMask = true; 422 i++; 423 } 424 } 425 426 // parse parameters and flags 427 428 while (i < argCount) { 429 if (!strcmp(args[i], "peer")) { 430 if (!parse_address(familyIndex, args[i + 1], peer)) { 431 fprintf(stderr, "%s: Option 'peer' needs valid address parameter\n", 432 kProgramName); 433 exit(1); 434 } 435 hasPeer = true; 436 i++; 437 } else if (!strcmp(args[i], "nm") || !strcmp(args[i], "netmask")) { 438 if (hasMask) { 439 fprintf(stderr, "%s: Netmask is specified twice\n", 440 kProgramName); 441 exit(1); 442 } 443 if (!parse_address(familyIndex, args[i + 1], mask)) { 444 fprintf(stderr, "%s: Option 'netmask' needs valid address parameter\n", 445 kProgramName); 446 exit(1); 447 } 448 hasMask = true; 449 i++; 450 } else if (!strcmp(args[i], "bc") || !strcmp(args[i], "broadcast")) { 451 if (hasBroadcast) { 452 fprintf(stderr, "%s: broadcast address is specified twice\n", 453 kProgramName); 454 exit(1); 455 } 456 if (!parse_address(familyIndex, args[i + 1], broadcast)) { 457 fprintf(stderr, "%s: Option 'broadcast' needs valid address parameter\n", 458 kProgramName); 459 exit(1); 460 } 461 hasBroadcast = true; 462 addFlags |= IFF_BROADCAST; 463 i++; 464 } else if (!strcmp(args[i], "mtu")) { 465 mtu = args[i + 1] ? strtol(args[i + 1], NULL, 0) : 0; 466 if (mtu <= 500) { 467 fprintf(stderr, "%s: Option 'mtu' expected valid max transfer unit size\n", 468 kProgramName); 469 exit(1); 470 } 471 i++; 472 } else if (!strcmp(args[i], "metric")) { 473 if (i + 1 >= argCount) { 474 fprintf(stderr, "%s: Option 'metric' exptected parameter\n", 475 kProgramName); 476 exit(1); 477 } 478 metric = strtol(args[i + 1], NULL, 0); 479 i++; 480 } else if (!strcmp(args[i], "up") || !strcmp(args[i], "-down")) { 481 addFlags |= IFF_UP; 482 } else if (!strcmp(args[i], "down") || !strcmp(args[i], "-up")) { 483 removeFlags |= IFF_UP; 484 } else if (!strcmp(args[i], "bcast")) { 485 addFlags |= IFF_BROADCAST; 486 } else if (!strcmp(args[i], "-bcast")) { 487 removeFlags |= IFF_BROADCAST; 488 } else if (!strcmp(args[i], "promisc")) { 489 addFlags |= IFF_PROMISC; 490 } else if (!strcmp(args[i], "-promisc")) { 491 removeFlags |= IFF_PROMISC; 492 } else if (!strcmp(args[i], "allmulti")) { 493 addFlags |= IFF_ALLMULTI; 494 } else if (!strcmp(args[i], "-allmulti")) { 495 removeFlags |= IFF_ALLMULTI; 496 } else if (!strcmp(args[i], "loopback")) { 497 addFlags |= IFF_LOOPBACK; 498 } else 499 usage(1); 500 501 i++; 502 } 503 504 if ((addFlags & removeFlags) != 0) { 505 fprintf(stderr, "%s: Contradicting flags specified\n", kProgramName); 506 exit(1); 507 } 508 509 // set address/mask/broadcast/peer 510 511 if (hasAddress) { 512 memcpy(&request.ifr_addr, &address, address.sa_len); 513 514 if (ioctl(socket, SIOCSIFADDR, &request, sizeof(struct ifreq)) < 0) { 515 fprintf(stderr, "%s: Setting address failed: %s\n", kProgramName, strerror(errno)); 516 exit(1); 517 } 518 } 519 520 if (ioctl(socket, SIOCGIFFLAGS, &request, sizeof(struct ifreq)) < 0) { 521 fprintf(stderr, "%s: Getting flags failed: %s\n", kProgramName, strerror(errno)); 522 exit(1); 523 } 524 currentFlags = request.ifr_flags; 525 526 if (!hasMask && hasAddress && kFamilies[familyIndex].family == AF_INET 527 && ioctl(socket, SIOCGIFNETMASK, &request, sizeof(struct ifreq)) == 0 528 && request.ifr_mask.sa_family == AF_UNSPEC) { 529 // generate standard netmask if it doesn't have one yet 530 sockaddr_in *netmask = (sockaddr_in *)&mask; 531 netmask->sin_len = sizeof(sockaddr_in); 532 netmask->sin_family = AF_INET; 533 534 // choose default netmask depending on the class of the address 535 in_addr_t net = ((sockaddr_in *)&address)->sin_addr.s_addr; 536 if (IN_CLASSA(net) 537 || (ntohl(net) >> IN_CLASSA_NSHIFT) == IN_LOOPBACKNET) { 538 // class A, or loopback 539 netmask->sin_addr.s_addr = IN_CLASSA_NET; 540 } if (IN_CLASSB(net)) { 541 // class B 542 netmask->sin_addr.s_addr = IN_CLASSB_NET; 543 } else { 544 // class C and rest 545 netmask->sin_addr.s_addr = IN_CLASSC_NET; 546 } 547 548 hasMask = true; 549 } 550 if (hasMask) { 551 memcpy(&request.ifr_mask, &mask, mask.sa_len); 552 553 if (ioctl(socket, SIOCSIFNETMASK, &request, sizeof(struct ifreq)) < 0) { 554 fprintf(stderr, "%s: Setting subnet mask failed: %s\n", kProgramName, strerror(errno)); 555 exit(1); 556 } 557 } 558 559 if (!hasBroadcast && hasAddress && (currentFlags & IFF_BROADCAST) 560 && kFamilies[familyIndex].family == AF_INET 561 && ioctl(socket, SIOCGIFBRDADDR, &request, sizeof(struct ifreq)) == 0 562 && request.ifr_mask.sa_family == AF_UNSPEC) { 563 // generate standard broadcast address if it doesn't have one yet 564 sockaddr_in *broadcastAddr = (sockaddr_in *)&broadcast; 565 uint32 maskValue = ((sockaddr_in *)&mask)->sin_addr.s_addr; 566 uint32 broadcastValue = ((sockaddr_in *)&address)->sin_addr.s_addr; 567 broadcastValue = (broadcastValue & maskValue) | ~maskValue; 568 broadcastAddr->sin_len = sizeof(sockaddr_in); 569 broadcastAddr->sin_family = AF_INET; 570 broadcastAddr->sin_addr.s_addr = broadcastValue; 571 hasBroadcast = true; 572 } 573 if (hasBroadcast) { 574 memcpy(&request.ifr_broadaddr, &broadcast, broadcast.sa_len); 575 576 if (ioctl(socket, SIOCSIFBRDADDR, &request, sizeof(struct ifreq)) < 0) { 577 fprintf(stderr, "%s: Setting broadcast address failed: %s\n", kProgramName, strerror(errno)); 578 exit(1); 579 } 580 } 581 582 if (hasPeer) { 583 memcpy(&request.ifr_dstaddr, &peer, peer.sa_len); 584 585 if (ioctl(socket, SIOCSIFDSTADDR, &request, sizeof(struct ifreq)) < 0) { 586 fprintf(stderr, "%s: Setting peer address failed: %s\n", kProgramName, strerror(errno)); 587 exit(1); 588 } 589 } 590 591 // set flags 592 593 if (addFlags || removeFlags) { 594 request.ifr_flags = (currentFlags & ~removeFlags) | addFlags; 595 if (ioctl(socket, SIOCSIFFLAGS, &request, sizeof(struct ifreq)) < 0) 596 fprintf(stderr, "%s: Setting flags failed: %s\n", kProgramName, strerror(errno)); 597 } 598 599 // set options 600 601 if (mtu != -1) { 602 request.ifr_mtu = mtu; 603 if (ioctl(socket, SIOCSIFMTU, &request, sizeof(struct ifreq)) < 0) 604 fprintf(stderr, "%s: Setting MTU failed: %s\n", kProgramName, strerror(errno)); 605 } 606 607 if (metric != -1) { 608 request.ifr_metric = metric; 609 if (ioctl(socket, SIOCSIFMETRIC, &request, sizeof(struct ifreq)) < 0) 610 fprintf(stderr, "%s: Setting metric failed: %s\n", kProgramName, strerror(errno)); 611 } 612 } 613 614 615 // #pragma mark - 616 617 618 int 619 main(int argc, char** argv) 620 { 621 if (argc > 1 && (!strcmp(argv[1], "--help") || !strcmp(argv[1], "-h"))) 622 usage(0); 623 624 bool deleteInterfaces = false; 625 626 if (argc > 1 627 && (!strcmp(argv[1], "--delete") 628 || !strcmp(argv[1], "--del") 629 || !strcmp(argv[1], "-d"))) { 630 // delete interfaces 631 if (argc < 3) 632 usage(1); 633 634 deleteInterfaces = true; 635 } 636 637 // we need a socket to talk to the networking stack 638 int socket = ::socket(AF_INET, SOCK_DGRAM, 0); 639 if (socket < 0) { 640 fprintf(stderr, "%s: The networking stack doesn't seem to be available.\n", 641 kProgramName); 642 return 1; 643 } 644 645 if (deleteInterfaces) { 646 for (int i = 2; i < argc; i++) { 647 delete_interface(socket, argv[i]); 648 } 649 return 0; 650 } else if (argc > 1 && !strcmp(argv[1], "-a")) { 651 // accept -a option for those that are used to it from other platforms 652 if (argc > 2) 653 usage(1); 654 655 list_interfaces(socket, NULL); 656 return 0; 657 } 658 659 const char* name = argv[1]; 660 if (argc > 2) { 661 // add or configure an interface 662 663 configure_interface(socket, name, argv + 2, argc - 2); 664 return 0; 665 } 666 667 // list interfaces 668 669 list_interfaces(socket, name); 670 return 0; 671 } 672 673