Author: axeld Date: 2010-07-19 17:24:36 +0200 (Mon, 19 Jul 2010) New Revision: 37600 Changeset: http://dev.haiku-os.org/changeset/37600 Added: haiku/trunk/src/bin/network/ping6/ haiku/trunk/src/bin/network/ping6/Jamfile haiku/trunk/src/bin/network/ping6/ping6.c Modified: haiku/trunk/src/bin/network/Jamfile haiku/trunk/src/bin/network/ifconfig/ifconfig.cpp haiku/trunk/src/bin/network/route/route.cpp haiku/trunk/src/bin/network/tcpdump/Jamfile Log: Another patch by Atis Elsts: * Added ping6 command from FreeBSD. * tcpdump is now built with IPv6 supprt. * added IPv6 support to route, and ifconfig (the family stuff should eventually be factored out). Modified: haiku/trunk/src/bin/network/Jamfile =================================================================== --- haiku/trunk/src/bin/network/Jamfile 2010-07-19 15:03:37 UTC (rev 37599) +++ haiku/trunk/src/bin/network/Jamfile 2010-07-19 15:24:36 UTC (rev 37600) @@ -15,6 +15,7 @@ #SubInclude HAIKU_TOP src bin network pppconfig ; #SubInclude HAIKU_TOP src bin network ppp_up ; SubInclude HAIKU_TOP src bin network ping ; +SubInclude HAIKU_TOP src bin network ping6 ; SubInclude HAIKU_TOP src bin network route ; SubInclude HAIKU_TOP src bin network setwep ; SubInclude HAIKU_TOP src bin network tcpdump ; Modified: haiku/trunk/src/bin/network/ifconfig/ifconfig.cpp =================================================================== --- haiku/trunk/src/bin/network/ifconfig/ifconfig.cpp 2010-07-19 15:03:37 UTC (rev 37599) +++ haiku/trunk/src/bin/network/ifconfig/ifconfig.cpp 2010-07-19 15:24:36 UTC (rev 37600) @@ -1,10 +1,11 @@ /* - * Copyright 2006-2008, Haiku, Inc. All Rights Reserved. + * Copyright 2006-2010, Haiku, Inc. All Rights Reserved. * Distributed under the terms of the MIT License. * * Authors: * Axel Dörfler, axeld@xxxxxxxxxxxxxxxx * Oliver Tappe, zooey@xxxxxxxxxxxxxxx + * Atis Elsts, the.kfx@xxxxxxxxx */ @@ -33,30 +34,82 @@ const char* kProgramName = __progname; +enum preferred_output_format { + PREFER_OUTPUT_MASK, + PREFER_OUTPUT_PREFIX_LENGTH, +}; + + struct address_family { int family; const char* name; const char* identifiers[4]; + preferred_output_format preferred_format; bool (*parse_address)(const char* string, sockaddr* _address); + bool (*prefix_length_to_mask)(uint8 prefixLength, sockaddr* mask); + uint8 (*mask_to_prefix_length)(sockaddr* mask); void (*print_address)(sockaddr* address); }; + +bool initialize_address_families(); + // AF_INET family static bool inet_parse_address(const char* string, sockaddr* address); +static bool inet_prefix_length_to_mask(uint8 prefixLength, sockaddr* mask); +static uint8 inet_mask_to_prefix_length(sockaddr* mask); static void inet_print_address(sockaddr* address); +// AF_INET6 family +static bool inet6_parse_address(const char* string, sockaddr* address); +static bool inet6_prefix_length_to_mask(uint8 prefixLength, sockaddr* mask); +static uint8 inet6_mask_to_prefix_length(sockaddr* mask); +static void inet6_print_address(sockaddr* address); + static const address_family kFamilies[] = { { AF_INET, "inet", {"AF_INET", "inet", "ipv4", NULL}, + PREFER_OUTPUT_MASK, inet_parse_address, + inet_prefix_length_to_mask, + inet_mask_to_prefix_length, inet_print_address }, - { -1, NULL, {NULL}, NULL, NULL } + { + AF_INET6, + "inet6", + {"AF_INET6", "inet6", "ipv6", NULL}, + PREFER_OUTPUT_PREFIX_LENGTH, + inet6_parse_address, + inet6_prefix_length_to_mask, + inet6_mask_to_prefix_length, + inet6_print_address + }, + { -1, NULL, {NULL}, PREFER_OUTPUT_MASK, NULL, NULL, NULL, NULL } }; +static int sAddressFamilySockets[sizeof(kFamilies) / sizeof(kFamilies[0])]; + + +bool +initialize_address_families() +{ + bool ok = false; + for (int32 i = 0; kFamilies[i].family >= 0; i++) { + int fd = socket(kFamilies[i].family, SOCK_DGRAM, 0); + if (fd != -1) { + sAddressFamilySockets[i] = fd; + ok = true; + } + } + return ok; +} + + + static bool inet_parse_address(const char* string, sockaddr* _address) { @@ -65,9 +118,9 @@ if (inet_aton(string, &inetAddress) != 1) return false; - sockaddr_in& address = *(sockaddr_in *)_address; + sockaddr_in& address = *(sockaddr_in*)_address; address.sin_family = AF_INET; - address.sin_len = sizeof(struct sockaddr_in); + address.sin_len = sizeof(sockaddr_in); address.sin_port = 0; address.sin_addr = inetAddress; memset(&address.sin_zero[0], 0, sizeof(address.sin_zero)); @@ -76,10 +129,50 @@ } +static bool +inet_prefix_length_to_mask(uint8 prefixLength, sockaddr* _mask) +{ + if (prefixLength > 32) + return false; + + sockaddr_in& mask = *(sockaddr_in*)_mask; + mask.sin_family = AF_INET; + mask.sin_len = sizeof(sockaddr_in); + mask.sin_port = 0; + memset(&mask.sin_zero[0], 0, sizeof(mask.sin_zero)); + + uint32 hostMask = 0; + for (uint8 i = 32; i > 32 - prefixLength; i--) + hostMask |= 1 << (i - 1); + mask.sin_addr.s_addr = htonl(hostMask); + + return true; +} + + +static uint8 +inet_mask_to_prefix_length(sockaddr* _mask) +{ + sockaddr_in& mask = *(sockaddr_in*)_mask; + if (mask.sin_family != AF_INET) + return (uint8)-1; + + uint8 result = 0; + uint32 hostMask = ntohl(mask.sin_addr.s_addr); + for (uint8 i = 32; i > 0; i--) { + if (hostMask & (1 << (i - 1)) == 0) + break; + result++; + } + + return result; +} + + static void inet_print_address(sockaddr* _address) { - sockaddr_in& address = *(sockaddr_in *)_address; + sockaddr_in& address = *(sockaddr_in*)_address; if (address.sin_family != AF_INET) return; @@ -88,6 +181,90 @@ } +static bool +inet6_parse_address(const char* string, sockaddr* _address) +{ + sockaddr_in6& address = *(sockaddr_in6*)_address; + + if (inet_pton(AF_INET6, string, &address.sin6_addr) != 1) + return false; + + address.sin6_family = AF_INET6; + address.sin6_len = sizeof(sockaddr_in6); + address.sin6_port = 0; + address.sin6_flowinfo = 0; + address.sin6_scope_id = 0; + + return true; +} + + +static bool +inet6_prefix_length_to_mask(uint8 prefixLength, sockaddr* _mask) +{ + if (prefixLength > 128) + return false; + + sockaddr_in6& mask = *(sockaddr_in6*)_mask; + mask.sin6_family = AF_INET6; + mask.sin6_len = sizeof(sockaddr_in6); + mask.sin6_port = 0; + mask.sin6_flowinfo = 0; + mask.sin6_scope_id = 0; + memset(mask.sin6_addr.s6_addr, 0, sizeof(in6_addr)); + + for (uint8 i = 0; i < sizeof(in6_addr); i++, prefixLength -= 8) { + if (prefixLength < 8) { + const uint8 masks[] = { + 0x00, 0x80, 0xc0, 0xe0, + 0xf0, 0xf8, 0xfc, 0xfe + }; + mask.sin6_addr.s6_addr[i] = masks[prefixLength]; + break; + } + + mask.sin6_addr.s6_addr[i] = 0xff; + } + + return true; +} + + +static uint8 +inet6_mask_to_prefix_length(sockaddr* _mask) +{ + sockaddr_in6& mask = *(sockaddr_in6*)_mask; + if (mask.sin6_family != AF_INET6) + return (uint8)~0; + + uint8 result = 0; + for (uint8 i = 0; i < sizeof(in6_addr); i++) { + for (uint8 j = 0; j < 8; j++) { + if (!(mask.sin6_addr.s6_addr[i] & (1 << j))) + return result; + result++; + } + } + + return 128; +} + + +static void +inet6_print_address(sockaddr* _address) +{ + sockaddr_in6& address = *(sockaddr_in6*)_address; + + if (address.sin6_family != AF_INET6) + return; + + char buffer[INET6_ADDRSTRLEN]; + + printf("%s", + inet_ntop(AF_INET6, &address.sin6_addr, buffer, sizeof(buffer))); +} + + // #pragma mark - @@ -181,12 +358,13 @@ "auto-config] [<option/flags>...]]\n" "\t%s --delete interface [...]\n\n" "Where <option> can be the following:\n" - " netmask <addr> - networking subnet mask\n" - " broadcast <addr> - set broadcast address\n" - " peer <addr> - ppp-peer address\n" - " mtu <bytes> - maximal transfer unit\n" - " metric <number> - metric number to use (defaults to 0)\n" - " media <media> - media type to use (defaults to auto)\n", + " netmask <addr> - networking subnet mask\n" + " prefixlen <number> - subnet mask length in bits\n" + " broadcast <addr> - set broadcast address\n" + " peer <addr> - ppp-peer address\n" + " mtu <bytes> - maximal transfer unit\n" + " metric <number> - metric number to use (defaults to 0)\n" + " media <media> - media type to use (defaults to auto)\n", kProgramName, kProgramName); for (int32 i = 0; kMediaTypes[i].type >= 0; i++) { printf("For %s <media> can be one of: ", kMediaTypes[i].pretty); @@ -210,8 +388,8 @@ prepare_request(struct ifreq& request, const char* name) { if (strlen(name) > IF_NAMESIZE) { - fprintf(stderr, "%s: interface name \"%s\" is too long.\n", kProgramName, - name); + fprintf(stderr, "%s: interface name \"%s\" is too long.\n", + kProgramName, name); return false; } @@ -249,22 +427,103 @@ } +bool +prefix_length_to_mask(int32 familyIndex, const char* argument, + struct sockaddr& mask) +{ + if (argument == NULL) + return false; + + char *end; + uint32 prefixLength = strtoul(argument, &end, 10); + if (end == argument) + return false; + + return kFamilies[familyIndex].prefix_length_to_mask( + (uint8)prefixLength, &mask); +} + + // #pragma mark - +int +find_socket(struct ifreq& request, int addressFamily) +{ + int socket = -1; + bool socketExists = false; + bool ok = false; + + for (int32 i = 0; kFamilies[i].family >= 0; i++) { + if (addressFamily != -1 && addressFamily != kFamilies[i].family) + continue; + + socket = sAddressFamilySockets[i]; + if (socket == -1) + continue; + + socketExists = true; + + if (ioctl(socket, SIOCGIFINDEX, &request, sizeof(request)) >= 0) { + ok = true; + break; + } + } + + if (socketExists && !ok) { + fprintf(stderr, "%s: Interface \"%s\" does not exist.\n", + kProgramName, request.ifr_name); + return -1; + } + + return socket; +} + + void -list_interface(int socket, const char* name) +list_interface_address(int socket, const address_family* family, + uint32 flags, ifreq* request) { - ifreq request; - if (!prepare_request(request, name)) + if (ioctl(socket, SIOCGIFADDR, request, sizeof(struct ifreq)) < 0) return; - if (ioctl(socket, SIOCGIFINDEX, &request, sizeof(request)) < 0) { - fprintf(stderr, "%s: Interface \"%s\" does not exist.\n", kProgramName, - name); - return; + printf("\t%s addr: ", family->name); + family->print_address(&request->ifr_addr); + + if ((flags & IFF_BROADCAST) != 0 + && ioctl(socket, SIOCGIFBRDADDR, request, sizeof(struct ifreq)) == 0 + && request->ifr_broadaddr.sa_family == family->family) { + printf(", Bcast: "); + family->print_address(&request->ifr_broadaddr); } + if (ioctl(socket, SIOCGIFNETMASK, request, sizeof(struct ifreq)) == 0 + && request->ifr_mask.sa_family == family->family) { + switch (family->preferred_format) { + case PREFER_OUTPUT_MASK: + printf(", Mask: "); + family->print_address(&request->ifr_mask); + break; + case PREFER_OUTPUT_PREFIX_LENGTH: + printf(", Prefix Length: %u", + family->mask_to_prefix_length(&request->ifr_mask)); + break; + } + } + putchar('\n'); +} + +bool +list_interface(const char* name, int addressFamily) +{ + ifreq request; + if (!prepare_request(request, name)) + return true; + + int socket = find_socket(request, addressFamily); + if (socket == -1) + return false; + printf("%s", name); size_t length = strlen(name); if (length < 8) @@ -286,7 +545,7 @@ prepare_request(request, request.ifr_parameter.device); if (ioctl(linkSocket, SIOCGIFADDR, &request, sizeof(struct ifreq)) == 0) { - sockaddr_dl &link = *(sockaddr_dl *)&request.ifr_addr; + sockaddr_dl &link = *(sockaddr_dl*)&request.ifr_addr; switch (link.sdl_type) { case IFT_ETHER: @@ -294,7 +553,7 @@ type = "Ethernet"; if (link.sdl_alen > 0) { - uint8 *mac = (uint8 *)LLADDR(&link); + uint8 *mac = (uint8*)LLADDR(&link); sprintf(address, "%02x:%02x:%02x:%02x:%02x:%02x", mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]); } else @@ -348,29 +607,11 @@ flags = request.ifr_flags; for (int32 i = 0; kFamilies[i].family >= 0; i++) { - int familySocket = ::socket(kFamilies[i].family, SOCK_DGRAM, 0); - if (familySocket < 0) - continue; - - if (ioctl(familySocket, SIOCGIFADDR, &request, sizeof(struct ifreq)) == 0) { - printf("\t%s addr: ", kFamilies[i].name); - kFamilies[i].print_address(&request.ifr_addr); - - if ((flags & IFF_BROADCAST) != 0 - && ioctl(familySocket, SIOCGIFBRDADDR, &request, sizeof(struct ifreq)) == 0 - && request.ifr_broadaddr.sa_family == kFamilies[i].family) { - printf(", Bcast: "); - kFamilies[i].print_address(&request.ifr_broadaddr); - } - if (ioctl(familySocket, SIOCGIFNETMASK, &request, sizeof(struct ifreq)) == 0 - && request.ifr_mask.sa_family == kFamilies[i].family) { - printf(", Mask: "); - kFamilies[i].print_address(&request.ifr_mask); - } - putchar('\n'); + int familySocket = sAddressFamilySockets[i]; + if (familySocket != -1) { + list_interface_address(familySocket, &kFamilies[i], + flags, &request); } - - close(familySocket); } // Print MTU, metric, flags @@ -434,19 +675,22 @@ } putchar('\n'); + return true; } void -list_interfaces(int socket, const char* name) +list_interfaces(const char* name) { if (name != NULL) { - list_interface(socket, name); + list_interface(name, -1); return; } // get a list of all interfaces + int socket = sAddressFamilySockets[0]; + ifconf config; config.ifc_len = sizeof(config.ifc_value); if (ioctl(socket, SIOCGIFCOUNT, &config, sizeof(struct ifconf)) < 0) @@ -469,12 +713,13 @@ if (ioctl(socket, SIOCGIFCONF, &config, sizeof(struct ifconf)) < 0) return; - ifreq *interface = (ifreq *)buffer; + ifreq* interface = (ifreq*)buffer; for (uint32 i = 0; i < count; i++) { - list_interface(socket, interface->ifr_name); + list_interface(interface->ifr_name, interface->ifr_addr.sa_family); - interface = (ifreq *)((addr_t)interface + IF_NAMESIZE + interface->ifr_addr.sa_len); + interface = (ifreq*)((addr_t)interface + IF_NAMESIZE + + interface->ifr_addr.sa_len); } free(buffer); @@ -482,12 +727,16 @@ void -delete_interface(int socket, const char* name) +delete_interface(const char* name) { ifreq request; if (!prepare_request(request, name)) return; + int socket = find_socket(request, -1); + if (socket == -1) + return; + if (ioctl(socket, SIOCDIFADDR, &request, sizeof(request)) < 0) { fprintf(stderr, "%s: Could not delete interface %s: %s\n", kProgramName, name, strerror(errno)); @@ -496,13 +745,27 @@ void -configure_interface(int socket, const char* name, char* const* args, +configure_interface(const char* name, char* const* args, int32 argCount) { ifreq request; if (!prepare_request(request, name)) return; + // try to parse address family + + int32 familyIndex; + int32 i = 0; + if (get_address_family(args[i], familyIndex)) + i++; + + int socket = sAddressFamilySockets[familyIndex]; + if (socket < 0) { + fprintf(stderr, "%s: Address family \"%s\" is not available.\n", + kProgramName, kFamilies[familyIndex].name); + exit(1); + } + uint32 index = 0; if (ioctl(socket, SIOCGIFINDEX, &request, sizeof(request)) >= 0) index = request.ifr_index; @@ -513,25 +776,6 @@ int mtu = -1, metric = -1, media = -1; int addFlags = 0, currentFlags = 0, removeFlags = 0; - // try to parse address family - - int32 familyIndex; - int32 i = 0; - if (get_address_family(args[i], familyIndex)) - i++; - - if (kFamilies[familyIndex].family != AF_INET) { - close(socket); - - // replace socket with one of the correct address family - socket = ::socket(kFamilies[familyIndex].family, SOCK_DGRAM, 0); - if (socket < 0) { - fprintf(stderr, "%s: Address family \"%s\" is not available.\n", - kProgramName, kFamilies[familyIndex].name); - exit(1); - } - } - if (index == 0) { // the interface does not exist yet, we have to add it first request.ifr_parameter.base_name[0] = '\0'; @@ -571,8 +815,8 @@ i++; } else if (!strcmp(args[i], "nm") || !strcmp(args[i], "netmask")) { if (hasMask) { - fprintf(stderr, "%s: Netmask is specified twice\n", - kProgramName); + fprintf(stderr, "%s: Netmask or prefix length is specified " + "twice\n", kProgramName); exit(1); } if (!parse_address(familyIndex, args[i + 1], mask)) { @@ -582,6 +826,20 @@ } hasMask = true; i++; + } else if (!strcmp(args[i], "prefixlen") + || !strcmp(args[i], "plen")) { + if (hasMask) { + fprintf(stderr, "%s: Netmask or prefix length is specified " + "twice\n", kProgramName); + exit(1); + } + if (!prefix_length_to_mask(familyIndex, args[i + 1], mask)) { + fprintf(stderr, "%s: Option 'prefixlen' is invalid for this " + "address family\n", kProgramName); + exit(1); + } + hasMask = true; + i++; } else if (!strcmp(args[i], "bc") || !strcmp(args[i], "broadcast")) { if (hasBroadcast) { fprintf(stderr, "%s: broadcast address is specified twice\n", @@ -761,6 +1019,7 @@ BMessage message(kMsgConfigureInterface); message.AddString("device", name); BMessage address; + // TODO: this is not working for ipv6 yet address.AddString("family", "inet"); address.AddBool("auto_config", true); message.AddMessage("address", &address); @@ -807,9 +1066,7 @@ deleteInterfaces = true; } - // we need a socket to talk to the networking stack - int socket = ::socket(AF_INET, SOCK_DGRAM, 0); - if (socket < 0) { + if (initialize_address_families() == false) { fprintf(stderr, "%s: The networking stack doesn't seem to be " "available.\n", kProgramName); return 1; @@ -817,7 +1074,7 @@ if (deleteInterfaces) { for (int i = 2; i < argc; i++) { - delete_interface(socket, argv[i]); + delete_interface(argv[i]); } return 0; } else if (argc > 1 && !strcmp(argv[1], "-a")) { @@ -825,7 +1082,7 @@ if (argc > 2) usage(1); - list_interfaces(socket, NULL); + list_interfaces(NULL); return 0; } @@ -833,13 +1090,13 @@ if (argc > 2) { // add or configure an interface - configure_interface(socket, name, argv + 2, argc - 2); + configure_interface(name, argv + 2, argc - 2); return 0; } // list interfaces - list_interfaces(socket, name); + list_interfaces(name); return 0; } Added: haiku/trunk/src/bin/network/ping6/Jamfile =================================================================== --- haiku/trunk/src/bin/network/ping6/Jamfile (rev 0) +++ haiku/trunk/src/bin/network/ping6/Jamfile 2010-07-19 15:24:36 UTC (rev 37600) @@ -0,0 +1,29 @@ +SubDir HAIKU_TOP src bin network ping6 ; + +SetSubDirSupportedPlatforms $(HAIKU_BONE_COMPATIBLE_PLATFORMS) ; + +if ! $(TARGET_PLATFORM_HAIKU_COMPATIBLE) { + UseHeaders [ FDirName $(HAIKU_TOP) headers posix ] : true ; + # We need the public network headers also when not compiling for Haiku. + # Unfortunately we get more than we want, namely all POSIX headers. +} + +BinCommand ping6 : + ping6.c + : $(TARGET_NETWORK_LIBS) $(TARGET_SELECT_UNAME_ETC_LIB) ; + +# Installation -- in the test directory for the time being +HaikuInstall install-networking + : [ FDirName $(HAIKU_TEST_DIR) kits net ] + : ping6 ; + +HaikuInstall install-userland-networking + : [ FDirName $(HAIKU_TEST_DIR) kits net userland ] + : ping6 + : installed-userland-networking +; + +Package haiku-networkingkit-cvs : + ping6 : + boot home Desktop haiku-networkingkit ; + Added: haiku/trunk/src/bin/network/ping6/ping6.c =================================================================== --- haiku/trunk/src/bin/network/ping6/ping6.c (rev 0) +++ haiku/trunk/src/bin/network/ping6/ping6.c 2010-07-19 15:24:36 UTC (rev 37600) @@ -0,0 +1,2712 @@ +/* + * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the project nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +/* + * Copyright (c) 1989, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Mike Muuss. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +/* + * Using the InterNet Control Message Protocol (ICMP) "ECHO" facility, + * measure round-trip-delays and packet loss across network paths. + * + * Author - + * Mike Muuss + * U. S. Army Ballistic Research Laboratory + * December, 1983 + * + * Status - + * Public Domain. Distribution Unlimited. + * Bugs - + * More statistics could always be gathered. + * This program has to run SUID to ROOT to access the ICMP socket. + */ + +/* + * NOTE: + * USE_SIN6_SCOPE_ID assumes that sin6_scope_id has the same semantics + * as IPV6_PKTINFO. Some people object it (sin6_scope_id specifies *link* + * while IPV6_PKTINFO specifies *interface*. Link is defined as collection of + * network attached to 1 or more interfaces) + */ +#define USE_SIN6_SCOPE_ID 1 + +#define USE_RFC2292BIS 1 +#define HAVE_POLL_H 1 +#define CMSG_SENDING_UNSUPPORTED 1 +#undef IPSEC +#undef IPV6_OPTIONS + +#include <sys/param.h> +#include <sys/uio.h> +#include <sys/socket.h> +#include <sys/time.h> + +#include <net/if.h> +#include <net/route.h> + +#include <netinet/in.h> +#include <netinet/ip6.h> +#include <netinet/icmp6.h> +#include <arpa/inet.h> +#include <arpa/nameser.h> +#include <netdb.h> + +#include <ctype.h> +#include <errno.h> +#include <fcntl.h> +#include <math.h> +#include <signal.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#ifdef HAVE_POLL_H +#include <poll.h> +#endif + +#ifdef IPSEC +#include <netipsec/ah.h> +#include <netipsec/ipsec.h> +#endif + +struct tv32 { + u_int32_t tv32_sec; + u_int32_t tv32_usec; +}; + +#define IP6LEN 40 +#define MAXPACKETLEN (IPV6_MAXPACKET - IP6LEN) +#define ICMP6ECHOLEN 8 /* icmp echo header len excluding time */ +#define ICMP6ECHOTMLEN sizeof(struct tv32) +#define ICMP6_NIQLEN (ICMP6ECHOLEN + 8) +# define CONTROLLEN 10240 /* ancillary data buffer size RFC3542 20.1 */ +/* FQDN case, 64 bits of nonce + 32 bits ttl */ +#define ICMP6_NIRLEN (ICMP6ECHOLEN + 12) +#define EXTRA 256 /* for AH and various other headers. weird. */ +#define DEFDATALEN ICMP6ECHOTMLEN +#define MAXDATALEN MAXPACKETLEN - IP6LEN - ICMP6ECHOLEN +#define NROUTES 9 /* number of record route slots */ + +#define A(bit) rcvd_tbl[(bit)>>3] /* identify byte in array */ +#define B(bit) (1 << ((bit) & 0x07)) /* identify bit in byte */ +#define SET(bit) (A(bit) |= B(bit)) +#define CLR(bit) (A(bit) &= (~B(bit))) +#define TST(bit) (A(bit) & B(bit)) + +#define F_FLOOD 0x0001 +#define F_INTERVAL 0x0002 +#define F_PINGFILLED 0x0008 +#define F_QUIET 0x0010 +#define F_RROUTE 0x0020 +#define F_SO_DEBUG 0x0040 +#define F_VERBOSE 0x0100 +#ifdef IPSEC +#ifdef IPSEC_POLICY_IPSEC +#define F_POLICY 0x0400 +#else +#define F_AUTHHDR 0x0200 +#define F_ENCRYPT 0x0400 +#endif /*IPSEC_POLICY_IPSEC*/ +#endif /*IPSEC*/ +#define F_NODEADDR 0x0800 +#define F_FQDN 0x1000 +#define F_INTERFACE 0x2000 +#define F_SRCADDR 0x4000 +#define F_HOSTNAME 0x10000 +#define F_FQDNOLD 0x20000 +#define F_NIGROUP 0x40000 +#define F_SUPTYPES 0x80000 +#define F_NOMINMTU 0x100000 +#define F_ONCE 0x200000 +#define F_AUDIBLE 0x400000 +#define F_MISSED 0x800000 +#define F_NOUSERDATA (F_NODEADDR | F_FQDN | F_FQDNOLD | F_SUPTYPES) +u_int options; + +#define IN6LEN sizeof(struct in6_addr) +#define SA6LEN sizeof(struct sockaddr_in6) +#define DUMMY_PORT 10101 + +#define SIN6(s) ((struct sockaddr_in6 *)(s)) + +/* + * MAX_DUP_CHK is the number of bits in received table, i.e. the maximum + * number of received sequence numbers we can keep track of. Change 128 + * to 8192 for complete accuracy... + */ +#define MAX_DUP_CHK (8 * 8192) +int mx_dup_ck = MAX_DUP_CHK; +char rcvd_tbl[MAX_DUP_CHK / 8]; + +struct addrinfo *res; +struct sockaddr_in6 dst; /* who to ping6 */ +struct sockaddr_in6 src; /* src addr of this packet */ +socklen_t srclen; +int datalen = DEFDATALEN; +int s; /* socket file descriptor */ +u_char outpack[MAXPACKETLEN]; +char BSPACE = '\b'; /* characters written for flood */ +char BBELL = '\a'; /* characters written for AUDIBLE */ +char DOT = '.'; +char *hostname; +int ident; /* process id to identify our packets */ +u_int8_t nonce[8]; /* nonce field for node information */ +int hoplimit = -1; /* hoplimit */ +int pathmtu = 0; /* path MTU for the destination. 0 = unspec. */ + +/* counters */ +long nmissedmax; /* max value of ntransmitted - nreceived - 1 */ +long npackets; /* max packets to transmit */ +long nreceived; /* # of packets we got back */ +long nrepeats; /* number of duplicates */ +long ntransmitted; /* sequence # for outbound packets = #sent */ +struct timeval interval = {1, 0}; /* interval between packets */ + +/* timing */ +int timing; /* flag to do timing */ +double tmin = 999999999.0; /* minimum round trip time */ +double tmax = 0.0; /* maximum round trip time */ +double tsum = 0.0; /* sum of all times, for doing average */ +double tsumsq = 0.0; /* sum of all times squared, for std. dev. */ + +/* for node addresses */ +u_short naflags; + +/* for ancillary data(advanced API) */ +struct msghdr smsghdr; +struct iovec smsgiov; +char *scmsg = 0; + +volatile sig_atomic_t seenalrm; +volatile sig_atomic_t seenint; +#ifdef SIGINFO +volatile sig_atomic_t seeninfo; +#endif + +int main(int, char *[]); +void fill(char *, char *); +int get_hoplim(struct msghdr *); +int get_pathmtu(struct msghdr *); +struct in6_pktinfo *get_rcvpktinfo(struct msghdr *); +void onsignal(int); +void retransmit(void); +void onint(int); +size_t pingerlen(void); +int pinger(void); +const char *pr_addr(struct sockaddr *, int); +void pr_icmph(struct icmp6_hdr *, u_char *); +void pr_iph(struct ip6_hdr *); +void pr_suptypes(struct icmp6_nodeinfo *, size_t); +void pr_nodeaddr(struct icmp6_nodeinfo *, int); +int myechoreply(const struct icmp6_hdr *); +int mynireply(const struct icmp6_nodeinfo *); +char *dnsdecode(const u_char **, const u_char *, const u_char *, + char *, size_t); +void pr_pack(u_char *, int, struct msghdr *); +void pr_exthdrs(struct msghdr *); +void pr_ip6opt(void *, size_t); +void pr_rthdr(void *, size_t); +int pr_bitrange(u_int32_t, int, int); +void pr_retip(struct ip6_hdr *, u_char *); +void summary(void); +void tvsub(struct timeval *, struct timeval *); +int setpolicy(int, char *); +char *nigroup(char *); +void usage(void); + + +static void err(int exitval, char *where) +{ + printf("error: %s: error %d [%s]\n", where, errno, strerror(errno)); + exit(exitval); +} + +static void errx(int exitval, char *fmt_string, ...) +{ + va_list list; + va_start(list, fmt_string); + printf("error: "); + vprintf(fmt_string, list); + printf("\n"); + va_end(list); + exit(exitval); +} + +static void warn(char *where) +{ + printf("warning: %s: error %d [%s]\n", where, errno, strerror(errno)); +} [... truncated: 2928 lines follow ...]