Created
February 28, 2014 14:31
-
-
Save arr2036/9272050 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** IPv6 address manipulation utility | |
* | |
* This utility is free software; you can redistribute it and/or | |
* modify it under the terms of the GNU Lesser General Public | |
* License as published by the Free Software Foundation; either | |
* version 2.1 of the License, or (at your option) any later version. | |
* | |
* This library is distributed in the hope that it will be useful, | |
* but WITHOUT ANY WARRANTY; without even the implied warranty of | |
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
* Lesser General Public License for more details. | |
* | |
* You should have received a copy of the GNU Lesser General Public | |
* License along with this library; if not, write to the Free Software | |
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA | |
* | |
* @note Much of this code was derived from code in the FreeRADIUS project | |
* @note hence using the GPL. | |
*/ | |
#include <stdlib.h> | |
#include <stdio.h> | |
#include <stdbool.h> | |
#include <string.h> | |
#include <ctype.h> | |
#include <unistd.h> | |
/* May be #include <in.h> on some systems */ | |
#include <netinet/in.h> | |
#include <arpa/inet.h> | |
static char *name; //!< Program name | |
/* lets pretend 128bit integers are ANSI C */ | |
#define uint128_t __uint128_t | |
/* These should cover clang and gcc */ | |
#if !defined(LITTLE_ENDIAN) && !defined(BIG_ENDIAN) | |
# if defined(__LITTLE_ENDIAN__) || \ | |
(defined(__BYTE_ORDER__) && (__BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__)) | |
# define LITTLE_ENDIAN 1 | |
# elif defined(__BIG_ENDIAN__) || \ | |
(defined(__BYTE_ORDER__) && (__BYTE_ORDER__ == __ORDER_BIG_ENDIAN__)) | |
# define BIG_ENDIAN 1 | |
# else | |
# error Failed determining endianness of system | |
# endif | |
#endif | |
/* abcd efgh -> dcba hgfe -> hgfe dcba */ | |
#ifdef LITTLE_ENDIAN | |
# define ntohll(x) (((uint64_t)ntohl((uint32_t)(x >> 32))) | (((uint64_t)ntohl(((uint32_t) x)) << 32))) | |
# define ntohlll(x) (((uint128_t)ntohll((uint64_t)(x >> 64))) | (((uint128_t)ntohll(((uint64_t) x)) << 64))) | |
#else | |
# define ntohll(x) x | |
# define ntohlll(x) x | |
#endif | |
#define htonll(x) ntohll(x) | |
#define htonlll(x) htohlll(x) | |
/** Write 128bit unsigned integer to buffer | |
* | |
* @author Alexey Frunze | |
* | |
* @param out where to write result to. | |
* @param outlen size of out. | |
* @param num 128 bit integer. | |
*/ | |
static size_t prints_uint128(char *out, size_t outlen, uint128_t const num) | |
{ | |
char buff[128 / 3 + 1 + 1]; | |
uint64_t n[2]; | |
char *p = buff; | |
int i; | |
memset(buff, '0', sizeof(buff) - 1); | |
buff[sizeof(buff) - 1] = '\0'; | |
memcpy(n, &num, sizeof(n)); | |
for (i = 0; i < 128; i++) { | |
ssize_t j; | |
int carry; | |
carry = (n[1] >= 0x8000000000000000); | |
// Shift n[] left, doubling it | |
n[1] = ((n[1] << 1) & 0xffffffffffffffff) + (n[0] >= 0x8000000000000000); | |
n[0] = ((n[0] << 1) & 0xffffffffffffffff); | |
// Add s[] to itself in decimal, doubling it | |
for (j = sizeof(buff) - 2; j >= 0; j--) { | |
buff[j] += buff[j] - '0' + carry; | |
carry = (buff[j] > '9'); | |
if (carry) { | |
buff[j] -= 10; | |
} | |
} | |
} | |
while ((*p == '0') && (p < &buff[sizeof(buff) - 2])) { | |
p++; | |
} | |
return strlcpy(out, p, outlen); | |
} | |
/** Return binary data as hex | |
* | |
* @param hex Where to write the hex output (must be double inlen). | |
* @param bin to convert. | |
* @param inlen Length of bin data. | |
* @return inlen * 2. | |
*/ | |
static size_t bin_to_hex(char *hex, uint8_t const *bin, size_t inlen) | |
{ | |
static char const *hextab = "0123456789abcdef"; | |
size_t i; | |
for (i = 0; i < inlen; i++) { | |
hex[0] = hextab[((*bin) >> 4) & 0x0f]; | |
hex[1] = hextab[*bin & 0x0f]; | |
hex += 2; | |
bin++; | |
} | |
*hex = '\0'; | |
return inlen * 2; | |
} | |
/** Mask off the host portion of an IPv6 address | |
* | |
* @param[in] ipaddr to mask. | |
* @param[in] prefix (in bits). | |
* @return in6_addr with host bits set to 0. | |
*/ | |
static struct in6_addr ipv6_mask(struct in6_addr const *ipaddr, uint8_t prefix) | |
{ | |
uint64_t const *p = (uint64_t const *) ipaddr; | |
uint64_t ret[2], *o = ret; | |
if (prefix > 128) { | |
prefix = 128; | |
} | |
/* Short circuit */ | |
if (prefix == 128) { | |
return *ipaddr; | |
} | |
if (prefix >= 64) { | |
prefix -= 64; | |
*o++ = 0xffffffffffffffffULL & *p++; | |
} else { | |
ret[1] = 0; | |
} | |
*o = htonll(~((0x0000000000000001ULL << (64 - prefix)) - 1)) & *p; | |
return *(struct in6_addr *) &ret; | |
} | |
/** Parse a 'presentation' format IPv6 address into it's binary form | |
* | |
* @param[out] out_ipaddr Where to write the resulting address. | |
* @param[out] out_prefix Where to write the prefix length. | |
* @param[in] value The value to parse. | |
* @return 0 on success -1 on failure. | |
*/ | |
static int ipv6_parse(struct in6_addr *out_ipaddr, uint8_t *out_prefix, char const *value) | |
{ | |
char const *p, *addr = value; | |
char buffer[INET6_ADDRSTRLEN + 4], *eptr; | |
struct in6_addr tmp; | |
unsigned long prefix; | |
p = strchr(value, '/'); | |
if (p) { | |
if ((p - value) >= sizeof(buffer)) { | |
fprintf(stderr, "Invalid IPv6 prefix string \"%s\"", value); | |
return -1; | |
} | |
memcpy(buffer, value, p - value); | |
buffer[p - value] = '\0'; | |
addr = buffer; | |
} | |
/* inet_pton is the standard POSIX function for parsing IP addresses */ | |
if (inet_pton(AF_INET6, addr, &tmp) <= 0) { | |
fprintf(stderr, "Failed to parse IPv6 address string \"%s\"", value); | |
return -1; | |
} | |
/* ipv6 prefix, set the host bits to 0 */ | |
if (p) { | |
prefix = strtoul(p + 1, &eptr, 10); | |
if ((prefix > 128) || *eptr) { | |
fprintf(stderr, "Failed to parse IPv6 address string prefix \"%s\"", p + 1); | |
return -1; | |
} | |
if (prefix < 128) { | |
*out_ipaddr = ipv6_mask(&tmp, (uint8_t) prefix); | |
*out_prefix = (uint8_t) prefix; | |
return 0; | |
} | |
} | |
/* single ipv6_address with no range */ | |
*out_prefix = 128; | |
*out_ipaddr = tmp; | |
return 0; | |
} | |
static void usage(int status) | |
{ | |
FILE *output = status ? stderr : stdout; | |
fprintf(output, "Usage: %s [options] -- <ipv6addr> [ipv6addr]\n", name); | |
fprintf(output, "options:\n"); | |
fprintf(output, " -w Width of output 8, 16, 32, 64, 128\n"); | |
fprintf(output, " -h This help text\n"); | |
fprintf(output, " -H Hexify output\n"); | |
exit(status); | |
} | |
int main(int argc, char *argv[]) | |
{ | |
int opt; | |
unsigned long width = 128; | |
bool hexify = false; | |
name = argv[0]; | |
while ((opt = getopt(argc, argv, "w:hH")) != EOF) { | |
switch (opt) { | |
case 'w': | |
width = strtoul(optarg, NULL, 10); | |
switch (width) { | |
case 8: | |
case 16: | |
case 32: | |
case 64: | |
case 128: | |
break; | |
default: | |
fprintf(stderr, "Width %lu not supported\n", width); | |
usage(64); | |
} | |
break; | |
case 'H': | |
hexify = true; | |
break; | |
case 'h': | |
usage(0); | |
break; | |
default: | |
usage(64); | |
} | |
} | |
if (optind >= argc) { | |
fprintf(stderr, "At least one ipv6 address must be specified\n"); | |
usage(64); | |
} | |
while (optind < argc) { | |
struct in6_addr addr; | |
uint8_t prefix; | |
uint128_t mask = 0, masked, shifted; | |
int i; | |
char buffer[1024]; | |
if (ipv6_parse(&addr, &prefix, argv[optind]) < 0) { | |
exit(1); | |
} | |
mask = ~mask; | |
mask >>= (128 - width); | |
shifted = *(uint128_t *)&addr; | |
for (i = (128 / width); i > 0; i--) { | |
masked = shifted & mask; | |
if (!hexify) { | |
uint128_t host; | |
switch (width) { | |
case 128: | |
host = ntohlll(masked); | |
break; | |
case 64: | |
host = ntohll(masked); | |
break; | |
case 32: | |
host = ntohl(masked); | |
break; | |
case 16: | |
host = ntohs(masked); | |
break; | |
case 8: | |
host = masked; | |
break; | |
} | |
prints_uint128(buffer, sizeof(buffer), host); | |
} else { | |
bin_to_hex(buffer, (uint8_t *) &masked, width / 8); | |
} | |
shifted >>= width; | |
fprintf(stdout, "%s", buffer); | |
if (i > 1) fputs(" ", stdout); | |
} | |
fputs("\n", stdout); | |
optind++; | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment