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 #include <string.h>
51
52 #if LWIP_IPV4
53 #include "lwip/ip4_addr.h" /* for ip6addr_aton to handle IPv4-mapped addresses */
54 #endif /* LWIP_IPV4 */
55
56 /* used by IP6_ADDR_ANY(6) in ip6_addr.h */
57 const ip_addr_t ip6_addr_any = IPADDR6_INIT(0ul, 0ul, 0ul, 0ul);
58
59 #define lwip_xchar(i) ((char)((i) < 10 ? '0' + (i) : 'A' + (i) - 10))
60 #if 0 // this ip6addr_aton is buggy, fixed in `fixme.c'
61 /**
62 * Check whether "cp" is a valid ascii representation
63 * of an IPv6 address and convert to a binary address.
64 * Returns 1 if the address is valid, 0 if not.
65 *
66 * @param cp IPv6 address in ascii representation (e.g. "FF01::1")
67 * @param addr pointer to which to save the ip address in network order
68 * @return 1 if cp could be converted to addr, 0 on failure
69 */
70 int
71 ip6addr_aton(const char *cp, ip6_addr_t *addr)
72 {
73 u32_t addr_index, zero_blocks, current_block_index, current_block_value;
74 const char *s;
75 #if LWIP_IPV4
76 int check_ipv4_mapped = 0;
77 #endif /* LWIP_IPV4 */
78
79 /* Count the number of colons, to count the number of blocks in a "::" sequence
80 zero_blocks may be 1 even if there are no :: sequences */
81 zero_blocks = 8;
82 for (s = cp; *s != 0; s++) {
83 if (*s == ':') {
84 zero_blocks--;
85 #if LWIP_IPV4
86 } else if (*s == '.') {
87 if ((zero_blocks == 5) ||(zero_blocks == 2)) {
88 check_ipv4_mapped = 1;
89 /* last block could be the start of an IPv4 address */
90 zero_blocks--;
91 } else {
92 /* invalid format */
93 return 0;
94 }
95 break;
96 #endif /* LWIP_IPV4 */
97 } else if (!lwip_isxdigit(*s)) {
98 break;
99 }
100 }
101
102 /* parse each block */
103 addr_index = 0;
104 current_block_index = 0;
105 current_block_value = 0;
106 for (s = cp; *s != 0; s++) {
107 if (*s == ':') {
108 if (addr) {
109 if (current_block_index & 0x1) {
110 addr->addr[addr_index++] |= current_block_value;
111 }
112 else {
113 addr->addr[addr_index] = current_block_value << 16;
114 }
115 }
116 current_block_index++;
117 #if LWIP_IPV4
118 if (check_ipv4_mapped) {
119 if (current_block_index == 6) {
120 ip4_addr_t ip4;
121 int ret = ip4addr_aton(s + 1, &ip4);
122 if (ret) {
123 if (addr) {
124 addr->addr[3] = lwip_htonl(ip4.addr);
125 current_block_index++;
126 goto fix_byte_order_and_return;
127 }
128 return 1;
129 }
130 }
131 }
132 #endif /* LWIP_IPV4 */
133 current_block_value = 0;
134 if (current_block_index > 7) {
135 /* address too long! */
136 return 0;
137 }
138 if (s[1] == ':') {
139 if (s[2] == ':') {
140 /* invalid format: three successive colons */
141 return 0;
142 }
143 s++;
144 /* "::" found, set zeros */
145 while (zero_blocks > 0) {
146 zero_blocks--;
147 if (current_block_index & 0x1) {
148 addr_index++;
149 } else {
150 if (addr) {
151 addr->addr[addr_index] = 0;
152 }
153 }
154 current_block_index++;
155 if (current_block_index > 7) {
156 /* address too long! */
157 return 0;
158 }
159 }
160 }
161 } else if (lwip_isxdigit(*s)) {
162 /* add current digit */
163 current_block_value = (current_block_value << 4) +
164 (lwip_isdigit(*s) ? (u32_t)(*s - '0') :
165 (u32_t)(10 + (lwip_islower(*s) ? *s - 'a' : *s - 'A')));
166 } else {
167 /* unexpected digit, space? CRLF? */
168 break;
169 }
170 }
171
172 if (addr) {
173 if (current_block_index & 0x1) {
174 addr->addr[addr_index++] |= current_block_value;
175 }
176 else {
177 addr->addr[addr_index] = current_block_value << 16;
178 }
179 #if LWIP_IPV4
180 fix_byte_order_and_return:
181 #endif
182 /* convert to network byte order. */
183 for (addr_index = 0; addr_index < 4; addr_index++) {
184 addr->addr[addr_index] = lwip_htonl(addr->addr[addr_index]);
185 }
186
187 ip6_addr_clear_zone(addr);
188 }
189
190 if (current_block_index != 7) {
191 return 0;
192 }
193
194 return 1;
195 }
196 #endif
197 /**
198 * Convert numeric IPv6 address into ASCII representation.
199 * returns ptr to static buffer; not reentrant!
200 *
201 * @param addr ip6 address in network order to convert
202 * @return pointer to a global static (!) buffer that holds the ASCII
203 * representation of addr
204 */
205 char *
ip6addr_ntoa(const ip6_addr_t * addr)206 ip6addr_ntoa(const ip6_addr_t *addr)
207 {
208 static char str[40];
209 return ip6addr_ntoa_r(addr, str, 40);
210 }
211
212 /**
213 * Same as ipaddr_ntoa, but reentrant since a user-supplied buffer is used.
214 *
215 * @param addr ip6 address in network order to convert
216 * @param buf target buffer where the string is stored
217 * @param buflen length of buf
218 * @return either pointer to buf which now holds the ASCII
219 * representation of addr or NULL if buf was too small
220 */
221 char *
ip6addr_ntoa_r(const ip6_addr_t * addr,char * buf,int buflen)222 ip6addr_ntoa_r(const ip6_addr_t *addr, char *buf, int buflen)
223 {
224 u32_t current_block_index, current_block_value, next_block_value;
225 s32_t i;
226 u8_t zero_flag, empty_block_flag;
227
228 #if LWIP_IPV4
229 if (ip6_addr_isipv4mappedipv6(addr)) {
230 /* This is an IPv4 mapped address */
231 ip4_addr_t addr4;
232 char *ret;
233 #define IP4MAPPED_HEADER "::FFFF:"
234 char *buf_ip4 = buf + sizeof(IP4MAPPED_HEADER) - 1;
235 int buflen_ip4 = buflen - sizeof(IP4MAPPED_HEADER) + 1;
236 if (buflen < (int)sizeof(IP4MAPPED_HEADER)) {
237 return NULL;
238 }
239 memcpy(buf, IP4MAPPED_HEADER, sizeof(IP4MAPPED_HEADER));
240 addr4.addr = addr->addr[3];
241 ret = ip4addr_ntoa_r(&addr4, buf_ip4, buflen_ip4);
242 if (ret != buf_ip4) {
243 return NULL;
244 }
245 return buf;
246 }
247 #endif /* LWIP_IPV4 */
248 i = 0;
249 empty_block_flag = 0; /* used to indicate a zero chain for "::' */
250
251 for (current_block_index = 0; current_block_index < 8; current_block_index++) {
252 /* get the current 16-bit block */
253 current_block_value = lwip_htonl(addr->addr[current_block_index >> 1]);
254 if ((current_block_index & 0x1) == 0) {
255 current_block_value = current_block_value >> 16;
256 }
257 current_block_value &= 0xffff;
258
259 /* Check for empty block. */
260 if (current_block_value == 0) {
261 if (current_block_index == 7 && empty_block_flag == 1) {
262 /* special case, we must render a ':' for the last block. */
263 buf[i++] = ':';
264 if (i >= buflen) {
265 return NULL;
266 }
267 break;
268 }
269 if (empty_block_flag == 0) {
270 /* generate empty block "::", but only if more than one contiguous zero block,
271 * according to current formatting suggestions RFC 5952. */
272 next_block_value = lwip_htonl(addr->addr[(current_block_index + 1) >> 1]);
273 if ((current_block_index & 0x1) == 0x01) {
274 next_block_value = next_block_value >> 16;
275 }
276 next_block_value &= 0xffff;
277 if (next_block_value == 0) {
278 empty_block_flag = 1;
279 buf[i++] = ':';
280 if (i >= buflen) {
281 return NULL;
282 }
283 continue; /* move on to next block. */
284 }
285 } else if (empty_block_flag == 1) {
286 /* move on to next block. */
287 continue;
288 }
289 } else if (empty_block_flag == 1) {
290 /* Set this flag value so we don't produce multiple empty blocks. */
291 empty_block_flag = 2;
292 }
293
294 if (current_block_index > 0) {
295 buf[i++] = ':';
296 if (i >= buflen) {
297 return NULL;
298 }
299 }
300
301 if ((current_block_value & 0xf000) == 0) {
302 zero_flag = 1;
303 } else {
304 buf[i++] = lwip_xchar(((current_block_value & 0xf000) >> 12));
305 zero_flag = 0;
306 if (i >= buflen) {
307 return NULL;
308 }
309 }
310
311 if (((current_block_value & 0xf00) == 0) && (zero_flag)) {
312 /* do nothing */
313 } else {
314 buf[i++] = lwip_xchar(((current_block_value & 0xf00) >> 8));
315 zero_flag = 0;
316 if (i >= buflen) {
317 return NULL;
318 }
319 }
320
321 if (((current_block_value & 0xf0) == 0) && (zero_flag)) {
322 /* do nothing */
323 }
324 else {
325 buf[i++] = lwip_xchar(((current_block_value & 0xf0) >> 4));
326 zero_flag = 0;
327 if (i >= buflen) {
328 return NULL;
329 }
330 }
331
332 buf[i++] = lwip_xchar((current_block_value & 0xf));
333 if (i >= buflen) {
334 return NULL;
335 }
336 }
337
338 buf[i] = 0;
339
340 return buf;
341 }
342
343 #endif /* LWIP_IPV6 */
344