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