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_10_T: 227 type = "10 MBit, 10BASE-T"; 228 break; 229 case IFM_100_TX: 230 type = "100 MBit, 100BASE-TX"; 231 break; 232 case IFM_1000_T: 233 type = "1 GBit, 1000BASE-T"; 234 break; 235 case IFM_1000_SX: 236 type = "1 GBit, 1000BASE-SX"; 237 break; 238 } 239 break; 240 241 default: 242 show = false; 243 break; 244 } 245 246 if (show) 247 printf("\tMedia Type: %s\n", type); 248 } 249 250 uint32 flags = 0; 251 if (ioctl(socket, SIOCGIFFLAGS, &request, sizeof(struct ifreq)) == 0) 252 flags = request.ifr_flags; 253 254 for (int32 i = 0; kFamilies[i].family >= 0; i++) { 255 int familySocket = ::socket(kFamilies[i].family, SOCK_DGRAM, 0); 256 if (familySocket < 0) 257 continue; 258 259 if (ioctl(familySocket, SIOCGIFADDR, &request, sizeof(struct ifreq)) == 0) { 260 printf("\t%s addr: ", kFamilies[i].name); 261 kFamilies[i].print_address(&request.ifr_addr); 262 263 if ((flags & IFF_BROADCAST) != 0 264 && ioctl(familySocket, SIOCGIFBRDADDR, &request, sizeof(struct ifreq)) == 0 265 && request.ifr_broadaddr.sa_family == kFamilies[i].family) { 266 printf(", Bcast: "); 267 kFamilies[i].print_address(&request.ifr_broadaddr); 268 } 269 if (ioctl(familySocket, SIOCGIFNETMASK, &request, sizeof(struct ifreq)) == 0 270 && request.ifr_mask.sa_family == kFamilies[i].family) { 271 printf(", Mask: "); 272 kFamilies[i].print_address(&request.ifr_mask); 273 } 274 putchar('\n'); 275 } 276 277 close(familySocket); 278 } 279 280 // Print MTU, metric, flags 281 282 printf("\tMTU: "); 283 if (ioctl(socket, SIOCGIFMTU, &request, sizeof(struct ifreq)) == 0) 284 printf("%d", request.ifr_mtu); 285 else 286 printf("-"); 287 288 printf(", Metric: "); 289 if (ioctl(socket, SIOCGIFMETRIC, &request, sizeof(struct ifreq)) == 0) 290 printf("%d", request.ifr_metric); 291 else 292 printf("-"); 293 294 if (flags != 0) { 295 const struct { 296 int value; 297 const char *name; 298 } kFlags[] = { 299 {IFF_UP, "up"}, 300 {IFF_NOARP, "noarp"}, 301 {IFF_BROADCAST, "broadcast"}, 302 {IFF_LOOPBACK, "loopback"}, 303 {IFF_PROMISC, "promiscuous"}, 304 {IFF_ALLMULTI, "allmulti"}, 305 {IFF_AUTOUP, "autoup"}, 306 {IFF_LINK, "link"}, 307 {IFF_AUTO_CONFIGURED, "auto-configured"}, 308 {IFF_CONFIGURING, "configuring"}, 309 }; 310 bool first = true; 311 312 for (uint32 i = 0; i < sizeof(kFlags) / sizeof(kFlags[0]); i++) { 313 if ((flags & kFlags[i].value) != 0) { 314 if (first) { 315 printf(","); 316 first = false; 317 } 318 putchar(' '); 319 printf(kFlags[i].name); 320 } 321 } 322 } 323 324 putchar('\n'); 325 326 // Print statistics 327 328 if (ioctl(socket, SIOCGIFSTATS, &request, sizeof(struct ifreq)) == 0) { 329 printf("\tReceive: %ld packets, %ld errors, %Ld bytes, %ld mcasts, %ld dropped\n", 330 request.ifr_stats.receive.packets, request.ifr_stats.receive.errors, 331 request.ifr_stats.receive.bytes, request.ifr_stats.receive.multicast_packets, 332 request.ifr_stats.receive.dropped); 333 printf("\tTransmit: %ld packets, %ld errors, %Ld bytes, %ld mcasts, %ld dropped\n", 334 request.ifr_stats.send.packets, request.ifr_stats.send.errors, 335 request.ifr_stats.send.bytes, request.ifr_stats.send.multicast_packets, 336 request.ifr_stats.send.dropped); 337 printf("\tCollisions: %ld\n", request.ifr_stats.collisions); 338 } 339 340 putchar('\n'); 341 } 342 343 344 void 345 list_interfaces(int socket, const char* name) 346 { 347 if (name != NULL) { 348 list_interface(socket, name); 349 return; 350 } 351 352 // get a list of all interfaces 353 354 ifconf config; 355 config.ifc_len = sizeof(config.ifc_value); 356 if (ioctl(socket, SIOCGIFCOUNT, &config, sizeof(struct ifconf)) < 0) 357 return; 358 359 uint32 count = (uint32)config.ifc_value; 360 if (count == 0) { 361 fprintf(stderr, "%s: There are no interfaces yet!\n", kProgramName); 362 return; 363 } 364 365 void *buffer = malloc(count * sizeof(struct ifreq)); 366 if (buffer == NULL) { 367 fprintf(stderr, "%s: Out of memory.\n", kProgramName); 368 return; 369 } 370 371 config.ifc_len = count * sizeof(struct ifreq); 372 config.ifc_buf = buffer; 373 if (ioctl(socket, SIOCGIFCONF, &config, sizeof(struct ifconf)) < 0) 374 return; 375 376 ifreq *interface = (ifreq *)buffer; 377 378 for (uint32 i = 0; i < count; i++) { 379 list_interface(socket, interface->ifr_name); 380 381 interface = (ifreq *)((addr_t)interface + IF_NAMESIZE + interface->ifr_addr.sa_len); 382 } 383 384 free(buffer); 385 } 386 387 388 void 389 delete_interface(int socket, const char* name) 390 { 391 ifreq request; 392 if (!prepare_request(request, name)) 393 return; 394 395 if (ioctl(socket, SIOCDIFADDR, &request, sizeof(request)) < 0) { 396 fprintf(stderr, "%s: Could not delete interface %s: %s\n", 397 kProgramName, name, strerror(errno)); 398 } 399 } 400 401 402 void 403 configure_interface(int socket, const char* name, char* const* args, 404 int32 argCount) 405 { 406 ifreq request; 407 if (!prepare_request(request, name)) 408 return; 409 410 uint32 index = 0; 411 if (ioctl(socket, SIOCGIFINDEX, &request, sizeof(request)) >= 0) 412 index = request.ifr_index; 413 414 bool hasAddress = false, hasMask = false, hasPeer = false, hasBroadcast = false; 415 struct sockaddr address, mask, peer, broadcast; 416 int mtu = -1, metric = -1, addFlags = 0, removeFlags = 0, currentFlags = 0; 417 418 // try to parse address family 419 420 int32 familyIndex; 421 int32 i = 0; 422 if (get_address_family(args[i], familyIndex)) 423 i++; 424 425 if (kFamilies[familyIndex].family != AF_INET) { 426 close(socket); 427 428 // replace socket with one of the correct address family 429 socket = ::socket(kFamilies[familyIndex].family, SOCK_DGRAM, 0); 430 if (socket < 0) { 431 fprintf(stderr, "%s: Address family \"%s\" is not available.\n", 432 kProgramName, kFamilies[familyIndex].name); 433 exit(1); 434 } 435 } 436 437 if (index == 0) { 438 // the interface does not exist yet, we have to add it first 439 request.ifr_parameter.base_name[0] = '\0'; 440 request.ifr_parameter.device[0] = '\0'; 441 request.ifr_parameter.sub_type = 0; 442 // the default device is okay for us 443 444 if (ioctl(socket, SIOCAIFADDR, &request, sizeof(request)) < 0) { 445 fprintf(stderr, "%s: Could not add interface: %s\n", kProgramName, 446 strerror(errno)); 447 exit(1); 448 } 449 } 450 451 // try to parse address 452 453 if (parse_address(familyIndex, args[i], address)) { 454 hasAddress = true; 455 i++; 456 457 if (parse_address(familyIndex, args[i], mask)) { 458 hasMask = true; 459 i++; 460 } 461 } 462 463 // parse parameters and flags 464 465 while (i < argCount) { 466 if (!strcmp(args[i], "peer")) { 467 if (!parse_address(familyIndex, args[i + 1], peer)) { 468 fprintf(stderr, "%s: Option 'peer' needs valid address parameter\n", 469 kProgramName); 470 exit(1); 471 } 472 hasPeer = true; 473 i++; 474 } else if (!strcmp(args[i], "nm") || !strcmp(args[i], "netmask")) { 475 if (hasMask) { 476 fprintf(stderr, "%s: Netmask is specified twice\n", 477 kProgramName); 478 exit(1); 479 } 480 if (!parse_address(familyIndex, args[i + 1], mask)) { 481 fprintf(stderr, "%s: Option 'netmask' needs valid address parameter\n", 482 kProgramName); 483 exit(1); 484 } 485 hasMask = true; 486 i++; 487 } else if (!strcmp(args[i], "bc") || !strcmp(args[i], "broadcast")) { 488 if (hasBroadcast) { 489 fprintf(stderr, "%s: broadcast address is specified twice\n", 490 kProgramName); 491 exit(1); 492 } 493 if (!parse_address(familyIndex, args[i + 1], broadcast)) { 494 fprintf(stderr, "%s: Option 'broadcast' needs valid address parameter\n", 495 kProgramName); 496 exit(1); 497 } 498 hasBroadcast = true; 499 addFlags |= IFF_BROADCAST; 500 i++; 501 } else if (!strcmp(args[i], "mtu")) { 502 mtu = args[i + 1] ? strtol(args[i + 1], NULL, 0) : 0; 503 if (mtu <= 500) { 504 fprintf(stderr, "%s: Option 'mtu' expected valid max transfer unit size\n", 505 kProgramName); 506 exit(1); 507 } 508 i++; 509 } else if (!strcmp(args[i], "metric")) { 510 if (i + 1 >= argCount) { 511 fprintf(stderr, "%s: Option 'metric' exptected parameter\n", 512 kProgramName); 513 exit(1); 514 } 515 metric = strtol(args[i + 1], NULL, 0); 516 i++; 517 } else if (!strcmp(args[i], "up") || !strcmp(args[i], "-down")) { 518 addFlags |= IFF_UP; 519 } else if (!strcmp(args[i], "down") || !strcmp(args[i], "-up")) { 520 removeFlags |= IFF_UP; 521 } else if (!strcmp(args[i], "bcast")) { 522 addFlags |= IFF_BROADCAST; 523 } else if (!strcmp(args[i], "-bcast")) { 524 removeFlags |= IFF_BROADCAST; 525 } else if (!strcmp(args[i], "promisc")) { 526 addFlags |= IFF_PROMISC; 527 } else if (!strcmp(args[i], "-promisc")) { 528 removeFlags |= IFF_PROMISC; 529 } else if (!strcmp(args[i], "allmulti")) { 530 addFlags |= IFF_ALLMULTI; 531 } else if (!strcmp(args[i], "-allmulti")) { 532 removeFlags |= IFF_ALLMULTI; 533 } else if (!strcmp(args[i], "loopback")) { 534 addFlags |= IFF_LOOPBACK; 535 } else 536 usage(1); 537 538 i++; 539 } 540 541 if ((addFlags & removeFlags) != 0) { 542 fprintf(stderr, "%s: Contradicting flags specified\n", kProgramName); 543 exit(1); 544 } 545 546 // set address/mask/broadcast/peer 547 548 if (hasAddress) { 549 memcpy(&request.ifr_addr, &address, address.sa_len); 550 551 if (ioctl(socket, SIOCSIFADDR, &request, sizeof(struct ifreq)) < 0) { 552 fprintf(stderr, "%s: Setting address failed: %s\n", kProgramName, strerror(errno)); 553 exit(1); 554 } 555 } 556 557 if (ioctl(socket, SIOCGIFFLAGS, &request, sizeof(struct ifreq)) < 0) { 558 fprintf(stderr, "%s: Getting flags failed: %s\n", kProgramName, strerror(errno)); 559 exit(1); 560 } 561 currentFlags = request.ifr_flags; 562 563 if (!hasMask && hasAddress && kFamilies[familyIndex].family == AF_INET 564 && ioctl(socket, SIOCGIFNETMASK, &request, sizeof(struct ifreq)) == 0 565 && request.ifr_mask.sa_family == AF_UNSPEC) { 566 // generate standard netmask if it doesn't have one yet 567 sockaddr_in *netmask = (sockaddr_in *)&mask; 568 netmask->sin_len = sizeof(sockaddr_in); 569 netmask->sin_family = AF_INET; 570 571 // choose default netmask depending on the class of the address 572 in_addr_t net = ((sockaddr_in *)&address)->sin_addr.s_addr; 573 if (IN_CLASSA(net) 574 || (ntohl(net) >> IN_CLASSA_NSHIFT) == IN_LOOPBACKNET) { 575 // class A, or loopback 576 netmask->sin_addr.s_addr = IN_CLASSA_NET; 577 } else if (IN_CLASSB(net)) { 578 // class B 579 netmask->sin_addr.s_addr = IN_CLASSB_NET; 580 } else { 581 // class C and rest 582 netmask->sin_addr.s_addr = IN_CLASSC_NET; 583 } 584 585 hasMask = true; 586 } 587 if (hasMask) { 588 memcpy(&request.ifr_mask, &mask, mask.sa_len); 589 590 if (ioctl(socket, SIOCSIFNETMASK, &request, sizeof(struct ifreq)) < 0) { 591 fprintf(stderr, "%s: Setting subnet mask failed: %s\n", kProgramName, strerror(errno)); 592 exit(1); 593 } 594 } 595 596 if (!hasBroadcast && hasAddress && (currentFlags & IFF_BROADCAST) 597 && kFamilies[familyIndex].family == AF_INET 598 && ioctl(socket, SIOCGIFBRDADDR, &request, sizeof(struct ifreq)) == 0 599 && request.ifr_mask.sa_family == AF_UNSPEC) { 600 // generate standard broadcast address if it doesn't have one yet 601 sockaddr_in *broadcastAddr = (sockaddr_in *)&broadcast; 602 uint32 maskValue = ((sockaddr_in *)&mask)->sin_addr.s_addr; 603 uint32 broadcastValue = ((sockaddr_in *)&address)->sin_addr.s_addr; 604 broadcastValue = (broadcastValue & maskValue) | ~maskValue; 605 broadcastAddr->sin_len = sizeof(sockaddr_in); 606 broadcastAddr->sin_family = AF_INET; 607 broadcastAddr->sin_addr.s_addr = broadcastValue; 608 hasBroadcast = true; 609 } 610 if (hasBroadcast) { 611 memcpy(&request.ifr_broadaddr, &broadcast, broadcast.sa_len); 612 613 if (ioctl(socket, SIOCSIFBRDADDR, &request, sizeof(struct ifreq)) < 0) { 614 fprintf(stderr, "%s: Setting broadcast address failed: %s\n", kProgramName, strerror(errno)); 615 exit(1); 616 } 617 } 618 619 if (hasPeer) { 620 memcpy(&request.ifr_dstaddr, &peer, peer.sa_len); 621 622 if (ioctl(socket, SIOCSIFDSTADDR, &request, sizeof(struct ifreq)) < 0) { 623 fprintf(stderr, "%s: Setting peer address failed: %s\n", kProgramName, strerror(errno)); 624 exit(1); 625 } 626 } 627 628 // set flags 629 630 if (addFlags || removeFlags) { 631 request.ifr_flags = (currentFlags & ~removeFlags) | addFlags; 632 if (ioctl(socket, SIOCSIFFLAGS, &request, sizeof(struct ifreq)) < 0) 633 fprintf(stderr, "%s: Setting flags failed: %s\n", kProgramName, strerror(errno)); 634 } 635 636 // set options 637 638 if (mtu != -1) { 639 request.ifr_mtu = mtu; 640 if (ioctl(socket, SIOCSIFMTU, &request, sizeof(struct ifreq)) < 0) 641 fprintf(stderr, "%s: Setting MTU failed: %s\n", kProgramName, strerror(errno)); 642 } 643 644 if (metric != -1) { 645 request.ifr_metric = metric; 646 if (ioctl(socket, SIOCSIFMETRIC, &request, sizeof(struct ifreq)) < 0) 647 fprintf(stderr, "%s: Setting metric failed: %s\n", kProgramName, strerror(errno)); 648 } 649 } 650 651 652 // #pragma mark - 653 654 655 int 656 main(int argc, char** argv) 657 { 658 if (argc > 1 && (!strcmp(argv[1], "--help") || !strcmp(argv[1], "-h"))) 659 usage(0); 660 661 bool deleteInterfaces = false; 662 663 if (argc > 1 664 && (!strcmp(argv[1], "--delete") 665 || !strcmp(argv[1], "--del") 666 || !strcmp(argv[1], "-d"))) { 667 // delete interfaces 668 if (argc < 3) 669 usage(1); 670 671 deleteInterfaces = true; 672 } 673 674 // we need a socket to talk to the networking stack 675 int socket = ::socket(AF_INET, SOCK_DGRAM, 0); 676 if (socket < 0) { 677 fprintf(stderr, "%s: The networking stack doesn't seem to be available.\n", 678 kProgramName); 679 return 1; 680 } 681 682 if (deleteInterfaces) { 683 for (int i = 2; i < argc; i++) { 684 delete_interface(socket, argv[i]); 685 } 686 return 0; 687 } else if (argc > 1 && !strcmp(argv[1], "-a")) { 688 // accept -a option for those that are used to it from other platforms 689 if (argc > 2) 690 usage(1); 691 692 list_interfaces(socket, NULL); 693 return 0; 694 } 695 696 const char* name = argv[1]; 697 if (argc > 2) { 698 // add or configure an interface 699 700 configure_interface(socket, name, argv + 2, argc - 2); 701 return 0; 702 } 703 704 // list interfaces 705 706 list_interfaces(socket, name); 707 return 0; 708 } 709 710