1 /*
2 * CDDL HEADER START
3 *
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License, Version 1.0 only
6 * (the "License"). You may not use this file except in compliance
7 * with the License.
8 *
9 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10 * or http://www.opensolaris.org/os/licensing.
11 * See the License for the specific language governing permissions
12 * and limitations under the License.
13 *
14 * When distributing Covered Code, include this CDDL HEADER in each
15 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16 * If applicable, add the following below this CDDL HEADER, with the
17 * fields enclosed by brackets "[]" replaced with your own identifying
18 * information: Portions Copyright [yyyy] [name of copyright owner]
19 *
20 * CDDL HEADER END
21 */
22 /*
23 * Copyright 2004 Sun Microsystems, Inc. All rights reserved.
24 * Use is subject to license terms.
25 */
26 /*
27 * Copyright 2012 Nexenta Systems, Inc. All rights reserved.
28 */
29
30 #include <sys/types.h>
31 #include <sys/cmn_err.h>
32 #include <sys/systm.h>
33 #include <sys/socket.h>
34 #include <sys/sunddi.h>
35 #include <netinet/in.h>
36 #include <inet/led.h>
37
38 static void convert2ascii(char *, const in6_addr_t *);
39 static char *strchr_w(const char *, int);
40 static int str2inet_addr(char *, ipaddr_t *);
41
42 /*
43 * inet_ntop -- Convert an IPv4 or IPv6 address in binary form into
44 * printable form, and return a pointer to that string. Caller should
45 * provide a buffer of correct length to store string into.
46 * Note: this routine is kernel version of inet_ntop. It has similar
47 * format as inet_ntop() defined in rfc2553. But it does not do
48 * error handling operations exactly as rfc2553 defines. This function
49 * is used by kernel inet directory routines only for debugging.
50 * This inet_ntop() function, does not return NULL if third argument
51 * is NULL. The reason is simple that we don't want kernel to panic
52 * as the output of this function is directly fed to ip<n>dbg macro.
53 * Instead it uses a local buffer for destination address for
54 * those calls which purposely pass NULL ptr for the destination
55 * buffer. This function is thread-safe when the caller passes a non-
56 * null buffer with the third argument.
57 */
58 /* ARGSUSED */
59 char *
60 inet_ntop(int af, const void *addr, char *buf, int addrlen)
61 {
62 static char local_buf[INET6_ADDRSTRLEN];
63 static char *err_buf1 = "<badaddr>";
64 static char *err_buf2 = "<badfamily>";
65 in6_addr_t *v6addr;
66 uchar_t *v4addr;
67 char *caddr;
68
69 /*
70 * We don't allow thread unsafe inet_ntop calls, they
71 * must pass a non-null buffer pointer. For DEBUG mode
72 * we use the ASSERT() and for non-debug kernel it will
73 * silently allow it for now. Someday we should remove
74 * the static buffer from this function.
75 */
76
77 ASSERT(buf != NULL);
78 if (buf == NULL)
79 buf = local_buf;
80 buf[0] = '\0';
81
82 /* Let user know politely not to send NULL or unaligned addr */
83 if (addr == NULL || !(OK_32PTR(addr))) {
84 #ifdef DEBUG
85 cmn_err(CE_WARN, "inet_ntop: addr is <null> or unaligned");
86 #endif
87 return (err_buf1);
88 }
89
90
91 #define UC(b) (((int)b) & 0xff)
92 switch (af) {
93 case AF_INET:
94 ASSERT(addrlen >= INET_ADDRSTRLEN);
95 v4addr = (uchar_t *)addr;
96 (void) sprintf(buf, "%03d.%03d.%03d.%03d",
97 UC(v4addr[0]), UC(v4addr[1]), UC(v4addr[2]), UC(v4addr[3]));
98 return (buf);
99
100 case AF_INET6:
101 ASSERT(addrlen >= INET6_ADDRSTRLEN);
102 v6addr = (in6_addr_t *)addr;
103 if (IN6_IS_ADDR_V4MAPPED(v6addr)) {
104 caddr = (char *)addr;
105 (void) sprintf(buf, "::ffff:%d.%d.%d.%d",
106 UC(caddr[12]), UC(caddr[13]),
107 UC(caddr[14]), UC(caddr[15]));
108 } else if (IN6_IS_ADDR_V4COMPAT(v6addr)) {
109 caddr = (char *)addr;
110 (void) sprintf(buf, "::%d.%d.%d.%d",
111 UC(caddr[12]), UC(caddr[13]), UC(caddr[14]),
112 UC(caddr[15]));
113 } else if (IN6_IS_ADDR_UNSPECIFIED(v6addr)) {
114 (void) sprintf(buf, "::");
115 } else {
116 convert2ascii(buf, v6addr);
117 }
118 return (buf);
119
120 default:
121 return (err_buf2);
122 }
123 #undef UC
124 }
125
126 /*
127 *
128 * v6 formats supported
129 * General format xxxx:xxxx:xxxx:xxxx:xxxx:xxxx:xxxx:xxxx
130 * The short hand notation :: is used for COMPAT addr
131 * Other forms : fe80::xxxx:xxxx:xxxx:xxxx
132 */
133 static void
134 convert2ascii(char *buf, const in6_addr_t *addr)
135 {
136 int hexdigits;
137 int head_zero = 0;
138 int tail_zero = 0;
139 /* tempbuf must be big enough to hold ffff:\0 */
140 char tempbuf[6];
141 char *ptr;
142 uint16_t *addr_component;
143 size_t len;
144 boolean_t first = B_FALSE;
145 boolean_t med_zero = B_FALSE;
146 boolean_t end_zero = B_FALSE;
147
148 addr_component = (uint16_t *)addr;
149 ptr = buf;
150
151 /* First count if trailing zeroes higher in number */
152 for (hexdigits = 0; hexdigits < 8; hexdigits++) {
153 if (*addr_component == 0) {
154 if (hexdigits < 4)
155 head_zero++;
156 else
157 tail_zero++;
158 }
159 addr_component++;
160 }
161 addr_component = (uint16_t *)addr;
162 if (tail_zero > head_zero && (head_zero + tail_zero) != 7)
163 end_zero = B_TRUE;
164
165 for (hexdigits = 0; hexdigits < 8; hexdigits++) {
166
167 /* if entry is a 0 */
168
169 if (*addr_component == 0) {
170 if (!first && *(addr_component + 1) == 0) {
171 if (end_zero && (hexdigits < 4)) {
172 *ptr++ = '0';
173 *ptr++ = ':';
174 } else {
175 /*
176 * address starts with 0s ..
177 * stick in leading ':' of pair
178 */
179 if (hexdigits == 0)
180 *ptr++ = ':';
181 /* add another */
182 *ptr++ = ':';
183 first = B_TRUE;
184 med_zero = B_TRUE;
185 }
186 } else if (first && med_zero) {
187 if (hexdigits == 7)
188 *ptr++ = ':';
189 addr_component++;
190 continue;
191 } else {
192 *ptr++ = '0';
193 *ptr++ = ':';
194 }
195 addr_component++;
196 continue;
197 }
198 if (med_zero)
199 med_zero = B_FALSE;
200
201 tempbuf[0] = '\0';
202 (void) sprintf(tempbuf, "%x:", ntohs(*addr_component) & 0xffff);
203 len = strlen(tempbuf);
204 bcopy(tempbuf, ptr, len);
205 ptr = ptr + len;
206 addr_component++;
207 }
208 *--ptr = '\0';
209 }
210
211 /*
212 * search for char c, terminate on trailing white space
213 */
214 static char *
215 strchr_w(const char *sp, int c)
216 {
217 /* skip leading white space */
218 while (*sp && (*sp == ' ' || *sp == '\t')) {
219 sp++;
220 }
221
222 do {
223 if (*sp == (char)c)
224 return ((char *)sp);
225 if (*sp == ' ' || *sp == '\t')
226 return (NULL);
227 } while (*sp++);
228 return (NULL);
229 }
230
231 static int
232 str2inet_addr(char *cp, ipaddr_t *addrp)
233 {
234 char *end;
235 long byte;
236 int i;
237 uint8_t *addr = (uint8_t *)addrp;
238
239 *addrp = 0;
240
241 for (i = 0; i < 4; i++) {
242 if (ddi_strtol(cp, &end, 10, &byte) != 0 || byte < 0 ||
243 byte > 255) {
244 return (0);
245 }
246 addr[i] = (uint8_t)byte;
247 if (i < 3) {
248 if (*end != '.') {
249 return (0);
250 } else {
251 cp = end + 1;
252 }
253 } else {
254 cp = end;
255 }
256 }
257
258 return (1);
259 }
260
261 /*
262 * inet_pton: This function takes string format IPv4 or IPv6 address and
263 * converts it to binary form. The format of this function corresponds to
264 * inet_pton() in the socket library.
265 * It returns 0 for invalid IPv4 and IPv6 address
266 * 1 when successfully converts ascii to binary
267 * -1 when af is not AF_INET or AF_INET6
268 */
269 int
270 m_inet_pton(int af, char *inp, void *outp, int revert)
271 {
272 int i;
273 long byte;
274 char *end;
275
276 switch (af) {
277 case AF_INET:
278 if (str2inet_addr(inp, (ipaddr_t *)outp)) {
279 if (! revert)
280 *(uint32_t *)outp = ntohl(*(uint32_t *)outp);
281 return (1);
282 } else {
283 return (0);
284 }
285 case AF_INET6: {
286 union v6buf_u {
287 uint16_t v6words_u[8];
288 in6_addr_t v6addr_u;
289 } v6buf, *v6outp;
290 uint16_t *dbl_col = NULL;
291 char lastbyte = NULL;
292
293 v6outp = (union v6buf_u *)outp;
294
295 if (strchr_w(inp, '.') != NULL) {
296 /* v4 mapped or v4 compatable */
297 if (strncmp(inp, "::ffff:", 7) == 0) {
298 ipaddr_t ipv4_all_zeroes = 0;
299 /* mapped - first init prefix and then fill */
300 IN6_IPADDR_TO_V4MAPPED(ipv4_all_zeroes,
301 &v6outp->v6addr_u);
302 return (str2inet_addr(inp + 7,
303 &(v6outp->v6addr_u.s6_addr32[3])));
304 } else if (strncmp(inp, "::", 2) == 0) {
305 /* v4 compatable - prefix all zeroes */
306 bzero(&v6outp->v6addr_u, sizeof (in6_addr_t));
307 return (str2inet_addr(inp + 2,
308 &(v6outp->v6addr_u.s6_addr32[3])));
309 }
310 return (0);
311 }
312 for (i = 0; i < 8; i++) {
313 int error;
314 /*
315 * if ddi_strtol() fails it could be because
316 * the string is "::". That is valid and
317 * checked for below so just set the value to
318 * 0 and continue.
319 */
320 if ((error = ddi_strtol(inp, &end, 16, &byte)) != 0) {
321 if (error == ERANGE)
322 return (0);
323 byte = 0;
324 }
325 if (byte < 0 || byte > 0x0ffff) {
326 return (0);
327 }
328 if (revert) {
329 v6buf.v6words_u[i] = htons((uint16_t)byte);
330 } else {
331 v6buf.v6words_u[i] = (uint16_t)byte;
332 }
333 if (*end == NULL || i == 7) {
334 inp = end;
335 break;
336 }
337 if (inp == end) { /* not a number must be */
338 if (*inp == ':' &&
339 ((i == 0 && *(inp + 1) == ':') ||
340 lastbyte == ':')) {
341 if (dbl_col) {
342 return (0);
343 }
344 if (byte != 0)
345 i++;
346 dbl_col = &v6buf.v6words_u[i];
347 if (i == 0)
348 inp++;
349 } else if (*inp == NULL || *inp == ' ' ||
350 *inp == '\t') {
351 break;
352 } else {
353 return (0);
354 }
355 } else {
356 inp = end;
357 }
358 if (*inp != ':') {
359 return (0);
360 }
361 inp++;
362 if (*inp == NULL || *inp == ' ' || *inp == '\t') {
363 break;
364 }
365 lastbyte = *inp;
366 }
367 if (*inp != NULL && *inp != ' ' && *inp != '\t') {
368 return (0);
369 }
370 /*
371 * v6words now contains the bytes we could translate
372 * dbl_col points to the word (should be 0) where
373 * a double colon was found
374 */
375 if (i == 7) {
376 v6outp->v6addr_u = v6buf.v6addr_u;
377 } else {
378 int rem;
379 int word;
380 int next;
381 if (dbl_col == NULL) {
382 return (0);
383 }
384 bzero(&v6outp->v6addr_u, sizeof (in6_addr_t));
385 rem = dbl_col - &v6buf.v6words_u[0];
386 for (next = 0; next < rem; next++) {
387 v6outp->v6words_u[next] = v6buf.v6words_u[next];
388 }
389 next++; /* skip dbl_col 0 */
390 rem = i - rem;
391 word = 8 - rem;
392 while (rem > 0) {
393 v6outp->v6words_u[word] = v6buf.v6words_u[next];
394 word++;
395 rem--;
396 next++;
397 }
398 }
399 return (1); /* Success */
400 }
401 } /* switch */
402 return (-1); /* return -1 for default case */
403 }
404
405
406 int
407 _inet_pton(int af, char *inp, void *outp)
408 {
409 return (m_inet_pton(af, inp, outp, 1));
410 }
411
412 /*
413 * We need this inet_pton to preserve compatibility with old closed binaries.
414 * Earlier, inet_pton returned address in hardware native order,
415 * not in network one. (See http://www.illumos.org/issue/3105).
416 * Having fixed that, we still need to support binaries, that use bad inet_pton
417 * and reverse returned address manually. All new inet_pton calls will be
418 * redirected to _inet_pton with #define in the header file.
419 */
420 int
421 inet_pton(int af, char *inp, void *outp)
422 {
423 return (m_inet_pton(af, inp, outp, 0));
424 }