1 /** 2 * @file 3 * 4 * IPv6 addresses. 5 */ 6 7 /* 8 * Copyright (c) 2010 Inico Technologies Ltd. 9 * All rights reserved. 10 * 11 * Redistribution and use in source and binary forms, with or without modification, 12 * are permitted provided that the following conditions are met: 13 * 14 * 1. Redistributions of source code must retain the above copyright notice, 15 * this list of conditions and the following disclaimer. 16 * 2. Redistributions in binary form must reproduce the above copyright notice, 17 * this list of conditions and the following disclaimer in the documentation 18 * and/or other materials provided with the distribution. 19 * 3. The name of the author may not be used to endorse or promote products 20 * derived from this software without specific prior written permission. 21 * 22 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED 23 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 24 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT 25 * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 26 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT 27 * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 28 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 29 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING 30 * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY 31 * OF SUCH DAMAGE. 32 * 33 * This file is part of the lwIP TCP/IP stack. 34 * 35 * Author: Ivan Delamer <delamer@inicotech.com> 36 * 37 * Functions for handling IPv6 addresses. 38 * 39 * Please coordinate changes and requests with Ivan Delamer 40 * <delamer@inicotech.com> 41 */ 42 43 #include "lwip/opt.h" 44 45 #if LWIP_IPV6 /* don't build if not configured for use in lwipopts.h */ 46 47 #include "lwip/ip_addr.h" 48 #include "lwip/def.h" 49 50 /* used by IP6_ADDR_ANY(6) in ip6_addr.h */ 51 const ip_addr_t ip6_addr_any = IPADDR6_INIT(0ul, 0ul, 0ul, 0ul); 52 53 #ifndef isprint 54 #define in_range(c, lo, up) ((u8_t)c >= lo && (u8_t)c <= up) 55 #define isprint(c) in_range(c, 0x20, 0x7f) 56 #define isdigit(c) in_range(c, '0', '9') 57 #define isxdigit(c) (isdigit(c) || in_range(c, 'a', 'f') || in_range(c, 'A', 'F')) 58 #define islower(c) in_range(c, 'a', 'z') 59 #define isspace(c) (c == ' ' || c == '\f' || c == '\n' || c == '\r' || c == '\t' || c == '\v') 60 #define xchar(i) ((i) < 10 ? '0' + (i) : 'A' + (i) - 10) 61 #endif 62 63 /** 64 * Check whether "cp" is a valid ascii representation 65 * of an IPv6 address and convert to a binary address. 66 * Returns 1 if the address is valid, 0 if not. 67 * 68 * @param cp IPv6 address in ascii representation (e.g. "FF01::1") 69 * @param addr pointer to which to save the ip address in network order 70 * @return 1 if cp could be converted to addr, 0 on failure 71 */ 72 int 73 ip6addr_aton(const char *cp, ip6_addr_t *addr) 74 { 75 u32_t addr_index, zero_blocks, current_block_index, current_block_value; 76 const char *s; 77 78 /* Count the number of colons, to count the number of blocks in a "::" sequence 79 zero_blocks may be 1 even if there are no :: sequences */ 80 zero_blocks = 8; 81 for (s = cp; *s != 0; s++) { 82 if (*s == ':') { 83 zero_blocks--; 84 } else if (!isxdigit(*s)) { 85 break; 86 } 87 } 88 89 /* parse each block */ 90 addr_index = 0; 91 current_block_index = 0; 92 current_block_value = 0; 93 for (s = cp; *s != 0; s++) { 94 if (*s == ':') { 95 if (addr) { 96 if (current_block_index & 0x1) { 97 addr->addr[addr_index++] |= current_block_value; 98 } 99 else { 100 addr->addr[addr_index] = current_block_value << 16; 101 } 102 } 103 current_block_index++; 104 current_block_value = 0; 105 if (current_block_index > 7) { 106 /* address too long! */ 107 return 0; 108 } 109 if (s[1] == ':') { 110 if (s[2] == ':') { 111 /* invalid format: three successive colons */ 112 return 0; 113 } 114 s++; 115 /* "::" found, set zeros */ 116 while (zero_blocks > 0) { 117 zero_blocks--; 118 if (current_block_index & 0x1) { 119 addr_index++; 120 } else { 121 if (addr) { 122 addr->addr[addr_index] = 0; 123 } 124 } 125 current_block_index++; 126 if (current_block_index > 7) { 127 /* address too long! */ 128 return 0; 129 } 130 } 131 } 132 } else if (isxdigit(*s)) { 133 /* add current digit */ 134 current_block_value = (current_block_value << 4) + 135 (isdigit(*s) ? (u32_t)(*s - '0') : 136 (u32_t)(10 + (islower(*s) ? *s - 'a' : *s - 'A'))); 137 } else { 138 /* unexpected digit, space? CRLF? */ 139 break; 140 } 141 } 142 143 if (addr) { 144 if (current_block_index & 0x1) { 145 addr->addr[addr_index++] |= current_block_value; 146 } 147 else { 148 addr->addr[addr_index] = current_block_value << 16; 149 } 150 } 151 152 /* convert to network byte order. */ 153 if (addr) { 154 for (addr_index = 0; addr_index < 4; addr_index++) { 155 addr->addr[addr_index] = lwip_htonl(addr->addr[addr_index]); 156 } 157 } 158 159 if (current_block_index != 7) { 160 return 0; 161 } 162 163 ip6_addr_clear_zone(addr); 164 165 return 1; 166 } 167 168 /** 169 * Convert numeric IPv6 address into ASCII representation. 170 * returns ptr to static buffer; not reentrant! 171 * 172 * @param addr ip6 address in network order to convert 173 * @return pointer to a global static (!) buffer that holds the ASCII 174 * representation of addr 175 */ 176 char * 177 ip6addr_ntoa(const ip6_addr_t *addr) 178 { 179 static char str[40]; 180 return ip6addr_ntoa_r(addr, str, 40); 181 } 182 183 /** 184 * Same as ipaddr_ntoa, but reentrant since a user-supplied buffer is used. 185 * 186 * @param addr ip6 address in network order to convert 187 * @param buf target buffer where the string is stored 188 * @param buflen length of buf 189 * @return either pointer to buf which now holds the ASCII 190 * representation of addr or NULL if buf was too small 191 */ 192 char * 193 ip6addr_ntoa_r(const ip6_addr_t *addr, char *buf, int buflen) 194 { 195 u32_t current_block_index, current_block_value, next_block_value; 196 s32_t i; 197 u8_t zero_flag, empty_block_flag; 198 199 i = 0; 200 empty_block_flag = 0; /* used to indicate a zero chain for "::' */ 201 202 for (current_block_index = 0; current_block_index < 8; current_block_index++) { 203 /* get the current 16-bit block */ 204 current_block_value = lwip_htonl(addr->addr[current_block_index >> 1]); 205 if ((current_block_index & 0x1) == 0) { 206 current_block_value = current_block_value >> 16; 207 } 208 current_block_value &= 0xffff; 209 210 /* Check for empty block. */ 211 if (current_block_value == 0) { 212 if (current_block_index == 7 && empty_block_flag == 1) { 213 /* special case, we must render a ':' for the last block. */ 214 buf[i++] = ':'; 215 if (i >= buflen) { 216 return NULL; 217 } 218 break; 219 } 220 if (empty_block_flag == 0) { 221 /* generate empty block "::", but only if more than one contiguous zero block, 222 * according to current formatting suggestions RFC 5952. */ 223 next_block_value = lwip_htonl(addr->addr[(current_block_index + 1) >> 1]); 224 if ((current_block_index & 0x1) == 0x01) { 225 next_block_value = next_block_value >> 16; 226 } 227 next_block_value &= 0xffff; 228 if (next_block_value == 0) { 229 empty_block_flag = 1; 230 buf[i++] = ':'; 231 if (i >= buflen) { 232 return NULL; 233 } 234 continue; /* move on to next block. */ 235 } 236 } else if (empty_block_flag == 1) { 237 /* move on to next block. */ 238 continue; 239 } 240 } else if (empty_block_flag == 1) { 241 /* Set this flag value so we don't produce multiple empty blocks. */ 242 empty_block_flag = 2; 243 } 244 245 if (current_block_index > 0) { 246 buf[i++] = ':'; 247 if (i >= buflen) { 248 return NULL; 249 } 250 } 251 252 if ((current_block_value & 0xf000) == 0) { 253 zero_flag = 1; 254 } else { 255 buf[i++] = xchar(((current_block_value & 0xf000) >> 12)); 256 zero_flag = 0; 257 if (i >= buflen) { 258 return NULL; 259 } 260 } 261 262 if (((current_block_value & 0xf00) == 0) && (zero_flag)) { 263 /* do nothing */ 264 } else { 265 buf[i++] = xchar(((current_block_value & 0xf00) >> 8)); 266 zero_flag = 0; 267 if (i >= buflen) { 268 return NULL; 269 } 270 } 271 272 if (((current_block_value & 0xf0) == 0) && (zero_flag)) { 273 /* do nothing */ 274 } 275 else { 276 buf[i++] = xchar(((current_block_value & 0xf0) >> 4)); 277 zero_flag = 0; 278 if (i >= buflen) { 279 return NULL; 280 } 281 } 282 283 buf[i++] = xchar((current_block_value & 0xf)); 284 if (i >= buflen) { 285 return NULL; 286 } 287 } 288 289 buf[i] = 0; 290 291 return buf; 292 } 293 294 #endif /* LWIP_IPV6 */ 295