/** * @file ipaddr6.h * @author Ambroz Bizjak * * @section LICENSE * * 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 author 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 COPYRIGHT HOLDERS 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 AUTHOR 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. * * @section DESCRIPTION * * IPv6 address parsing functions. */ #ifndef BADVPN_MISC_IPADDR6_H #define BADVPN_MISC_IPADDR6_H #include #include #include #include #include #include #include #include #include struct ipv6_addr { uint8_t bytes[16]; }; struct ipv6_ifaddr { struct ipv6_addr addr; int prefix; }; static int ipaddr6_parse_ipv6_addr_bin (const char *name, size_t name_len, struct ipv6_addr *out_addr); static int ipaddr6_parse_ipv6_addr (const char *name, struct ipv6_addr *out_addr); static int ipaddr6_parse_ipv6_prefix_bin (const char *str, size_t str_len, int *out_num); static int ipaddr6_parse_ipv6_prefix (const char *str, int *out_num); static int ipaddr6_parse_ipv6_ifaddr_bin (const char *str, size_t str_len, struct ipv6_ifaddr *out); static int ipaddr6_parse_ipv6_ifaddr (const char *str, struct ipv6_ifaddr *out); static int ipaddr6_ipv6_ifaddr_from_addr_mask (struct ipv6_addr addr, struct ipv6_addr mask, struct ipv6_ifaddr *out); static void ipaddr6_ipv6_mask_from_prefix (int prefix, struct ipv6_addr *out_mask); static int ipaddr6_ipv6_prefix_from_mask (struct ipv6_addr mask, int *out_prefix); static int ipaddr6_ipv6_addrs_in_network (struct ipv6_addr addr1, struct ipv6_addr addr2, int netprefix); #define IPADDR6_PRINT_MAX 44 static void ipaddr6_print_addr (struct ipv6_addr addr, char *out_buf); static void ipaddr6_print_ifaddr (struct ipv6_ifaddr addr, char *out_buf); int ipaddr6_parse_ipv6_addr_bin (const char *name, size_t name_len, struct ipv6_addr *out_addr) { int num_blocks = 0; int compress_pos = -1; uint16_t block = 0; int empty = 1; size_t i = 0; while (i < name_len) { if (name[i] == '.') { goto ipv4_ending; } else if (name[i] == ':') { int is_double = (i + 1 < name_len && name[i + 1] == ':'); if (i > 0) { if (empty || num_blocks == 7) { return 0; } out_addr->bytes[2 * num_blocks + 0] = block >> 8; out_addr->bytes[2 * num_blocks + 1] = block & 0xFF; num_blocks++; block = 0; empty = 1; } else if (!is_double) { return 0; } if (is_double) { if (compress_pos != -1) { return 0; } compress_pos = num_blocks; } i += 1 + is_double; } else { int digit = decode_hex_digit(name[i]); if (digit < 0) { return 0; } if (block > UINT16_MAX / 16) { return 0; } block *= 16; if (digit > UINT16_MAX - block) { return 0; } block += digit; empty = 0; i += 1; } } if (!empty) { out_addr->bytes[2 * num_blocks + 0] = block >> 8; out_addr->bytes[2 * num_blocks + 1] = block & 0xFF; num_blocks++; } else if (num_blocks != compress_pos) { return 0; } ipv4_done: if (compress_pos == -1) { if (num_blocks != 8) { return 0; } compress_pos = 0; } int num_rear = num_blocks - compress_pos; memmove(out_addr->bytes + 2 * (8 - num_rear), out_addr->bytes + 2 * compress_pos, 2 * num_rear); memset(out_addr->bytes + 2 * compress_pos, 0, 2 * (8 - num_rear - compress_pos)); return 1; ipv4_ending: if (empty || (num_blocks == 0 && compress_pos == -1)) { return 0; } while (name[i - 1] != ':') { i--; } uint8_t bytes[4]; int cur_byte = 0; uint8_t byte = 0; empty = 1; while (i < name_len) { if (name[i] == '.') { if (empty || cur_byte == 3) { return 0; } bytes[cur_byte] = byte; cur_byte++; byte = 0; empty = 1; } else { if (!empty && byte == 0) { return 0; } int digit = decode_decimal_digit(name[i]); if (digit < 0) { return 0; } if (byte > UINT8_MAX / 10) { return 0; } byte *= 10; if (digit > UINT8_MAX - byte) { return 0; } byte += digit; empty = 0; } i++; } if (cur_byte != 3 || empty) { return 0; } bytes[cur_byte] = byte; if (8 - num_blocks < 2) { return 0; } memcpy(out_addr->bytes + 2 * num_blocks, bytes, 4); num_blocks += 2; goto ipv4_done; } int ipaddr6_parse_ipv6_addr (const char *name, struct ipv6_addr *out_addr) { return ipaddr6_parse_ipv6_addr_bin(name, strlen(name), out_addr); } int ipaddr6_parse_ipv6_prefix_bin (const char *str, size_t str_len, int *out_num) { uintmax_t d; if (!parse_unsigned_integer_bin(str, str_len, &d)) { return 0; } if (d > 128) { return 0; } *out_num = d; return 1; } int ipaddr6_parse_ipv6_prefix (const char *str, int *out_num) { return ipaddr6_parse_ipv6_prefix_bin(str, strlen(str), out_num); } int ipaddr6_parse_ipv6_ifaddr_bin (const char *str, size_t str_len, struct ipv6_ifaddr *out) { size_t slash_pos; if (!b_find_char_bin(str, str_len, '/', &slash_pos)) { return 0; } return (ipaddr6_parse_ipv6_addr_bin(str, slash_pos, &out->addr) && ipaddr6_parse_ipv6_prefix_bin(str + slash_pos + 1, str_len - slash_pos - 1, &out->prefix)); } int ipaddr6_parse_ipv6_ifaddr (const char *str, struct ipv6_ifaddr *out) { return ipaddr6_parse_ipv6_ifaddr_bin(str, strlen(str), out); } int ipaddr6_ipv6_ifaddr_from_addr_mask (struct ipv6_addr addr, struct ipv6_addr mask, struct ipv6_ifaddr *out) { int prefix; if (!ipaddr6_ipv6_prefix_from_mask(mask, &prefix)) { return 0; } out->addr = addr; out->prefix = prefix; return 1; } void ipaddr6_ipv6_mask_from_prefix (int prefix, struct ipv6_addr *out_mask) { ASSERT(prefix >= 0) ASSERT(prefix <= 128) int quot = prefix / 8; int rem = prefix % 8; if (quot > 0) { memset(out_mask->bytes, UINT8_MAX, quot); } if (16 - quot > 0) { memset(out_mask->bytes + quot, 0, 16 - quot); } for (int i = 0; i < rem; i++) { out_mask->bytes[quot] |= (uint8_t)1 << (8 - i - 1); } } int ipaddr6_ipv6_prefix_from_mask (struct ipv6_addr mask, int *out_prefix) { int prefix = 0; int i = 0; while (i < 16 && mask.bytes[i] == UINT8_MAX) { prefix += 8; i++; } if (i < 16) { uint8_t t = 0; int j; for (j = 0; j <= 8; j++) { if (mask.bytes[i] == t) { break; } if (j < 8) { t |= ((uint8_t)1 << (8 - j - 1)); } } if (!(j <= 8)) { return 0; } prefix += j; i++; while (i < 16) { if (mask.bytes[i] != 0) { return 0; } i++; } } *out_prefix = prefix; return 1; } int ipaddr6_ipv6_addrs_in_network (struct ipv6_addr addr1, struct ipv6_addr addr2, int netprefix) { ASSERT(netprefix >= 0) ASSERT(netprefix <= 128) int quot = netprefix / 8; int rem = netprefix % 8; if (memcmp(addr1.bytes, addr2.bytes, quot)) { return 0; } if (rem == 0) { return 1; } uint8_t t = 0; for (int i = 0; i < rem; i++) { t |= (uint8_t)1 << (8 - i - 1); } return ((addr1.bytes[quot] & t) == (addr2.bytes[quot] & t)); } void ipaddr6_print_addr (struct ipv6_addr addr, char *out_buf) { int largest_start = 0; int largest_len = 0; int current_start = 0; int current_len = 0; for (int i = 0; i < 8; i++) { if (addr.bytes[2 * i] == 0 && addr.bytes[2 * i + 1] == 0) { current_len++; if (current_len > largest_len) { largest_start = current_start; largest_len = current_len; } } else { current_start = i + 1; current_len = 0; } } if (largest_len > 1) { for (int i = 0; i < largest_start; i++) { uint16_t block = ((uint16_t)addr.bytes[2 * i] << 8) | addr.bytes[2 * i + 1]; out_buf += sprintf(out_buf, "%"PRIx16":", block); } if (largest_start == 0) { out_buf += sprintf(out_buf, ":"); } for (int i = largest_start + largest_len; i < 8; i++) { uint16_t block = ((uint16_t)addr.bytes[2 * i] << 8) | addr.bytes[2 * i + 1]; out_buf += sprintf(out_buf, ":%"PRIx16, block); } if (largest_start + largest_len == 8) { out_buf += sprintf(out_buf, ":"); } } else { const char *prefix = ""; for (int i = 0; i < 8; i++) { uint16_t block = ((uint16_t)addr.bytes[2 * i] << 8) | addr.bytes[2 * i + 1]; out_buf += sprintf(out_buf, "%s%"PRIx16, prefix, block); prefix = ":"; } } } void ipaddr6_print_ifaddr (struct ipv6_ifaddr addr, char *out_buf) { ASSERT(addr.prefix >= 0) ASSERT(addr.prefix <= 128) ipaddr6_print_addr(addr.addr, out_buf); sprintf(out_buf + strlen(out_buf), "/%d", addr.prefix); } #endif