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