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