• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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