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 2006 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 <libintl.h> 30 #include <locale.h> 31 #include <unistd.h> 32 #include <stdlib.h> 33 #include <stdio.h> 34 #include <errno.h> 35 #include <string.h> 36 #include <ctype.h> 37 #include <sys/param.h> 38 #include <sys/types.h> 39 #include <stropts.h> 40 #include <sys/conf.h> 41 #include <sys/socket.h> 42 #include <sys/sockio.h> 43 #include <netinet/in.h> 44 #include <arpa/inet.h> 45 #include <inet/ip.h> 46 #include <inet/ip6_asp.h> 47 48 /* 49 * The size of the table we initially use to retrieve the kernel's policy 50 * table. If this value is too small, we use the value returned from the 51 * SIOCGIP6ADDRPOLICY ioctl. 52 */ 53 #define KERN_POLICY_SIZE 32 54 #define IPV6DAS_MAXLINELEN 1024 55 #define IPV6DAS_MAXENTRIES 512 56 57 typedef enum { 58 IPV6DAS_PRINTPOLICY, 59 IPV6DAS_SETPOLICY, 60 IPV6DAS_SETDEFAULT 61 } ipv6das_cmd_t; 62 63 static char *myname; /* Copied from argv[0] */ 64 65 static int parseconf(const char *, ip6_asp_t **); 66 static int setpolicy(int, ip6_asp_t *, int); 67 static int printpolicy(int); 68 static int ip_mask_to_plen_v6(const in6_addr_t *); 69 static in6_addr_t *ip_plen_to_mask_v6(int, in6_addr_t *); 70 static int strioctl(int, int, void *, int); 71 static void usage(void); 72 73 int 74 main(int argc, char **argv) 75 { 76 int opt, status, sock, count; 77 char *conf_filename; 78 ipv6das_cmd_t ipv6das_cmd = IPV6DAS_PRINTPOLICY; 79 ip6_asp_t *policy_table; 80 81 myname = *argv; 82 83 (void) setlocale(LC_ALL, ""); 84 85 #if !defined(TEXT_DOMAIN) /* Should be defined by cc -D */ 86 #define TEXT_DOMAIN "SYS_TEST" 87 #endif 88 89 (void) textdomain(TEXT_DOMAIN); 90 91 while ((opt = getopt(argc, argv, "df:")) != EOF) 92 switch (opt) { 93 case 'd': 94 ipv6das_cmd = IPV6DAS_SETDEFAULT; 95 break; 96 case 'f': 97 conf_filename = optarg; 98 ipv6das_cmd = IPV6DAS_SETPOLICY; 99 break; 100 default: 101 usage(); 102 return (EXIT_FAILURE); 103 } 104 if (argc > optind) { 105 /* shouldn't be any extra args */ 106 usage(); 107 return (EXIT_FAILURE); 108 } 109 110 /* Open a socket that we can use to send ioctls down to IP. */ 111 if ((sock = socket(PF_INET6, SOCK_DGRAM, 0)) == -1) { 112 perror("socket"); 113 return (EXIT_FAILURE); 114 } 115 116 switch (ipv6das_cmd) { 117 case IPV6DAS_SETPOLICY: 118 if ((count = parseconf(conf_filename, &policy_table)) <= 0) 119 return (EXIT_FAILURE); 120 status = setpolicy(sock, policy_table, count); 121 free(policy_table); 122 break; 123 case IPV6DAS_SETDEFAULT: 124 status = setpolicy(sock, NULL, 0); 125 break; 126 case IPV6DAS_PRINTPOLICY: 127 default: 128 status = printpolicy(sock); 129 break; 130 } 131 132 (void) close(sock); 133 return (status); 134 } 135 136 /* 137 * parseconf(filename, new_policy) 138 * 139 * Parses the file identified by filename, filling in new_policy 140 * with the address selection policy table specified in filename. 141 * Returns -1 on failure, or the number of table entries found 142 * on success. 143 */ 144 static int 145 parseconf(const char *filename, ip6_asp_t **new_policy) 146 { 147 FILE *fp; 148 char line[IPV6DAS_MAXLINELEN]; 149 char *cp, *end; 150 char *prefixstr; 151 uint_t lineno = 0, entryindex = 0; 152 int plen, precedence; 153 char *label; 154 size_t labellen; 155 int retval; 156 ip6_asp_t tmp_policy[IPV6DAS_MAXENTRIES]; 157 boolean_t have_default = B_FALSE; 158 in6_addr_t prefix, mask; 159 boolean_t comment_found = B_FALSE, end_of_line = B_FALSE; 160 161 if ((fp = fopen(filename, "r")) == NULL) { 162 perror(filename); 163 return (-1); 164 } 165 166 while (fgets(line, sizeof (line), fp) != NULL) { 167 if (entryindex == IPV6DAS_MAXENTRIES) { 168 (void) fprintf(stderr, 169 gettext("%s: too many entries\n"), filename); 170 retval = -1; 171 goto end_parse; 172 } 173 174 lineno++; 175 cp = line; 176 177 /* Skip leading whitespace */ 178 while (isspace(*cp)) 179 cp++; 180 181 /* Is this a comment or blank line? */ 182 if (*cp == '#' || *cp == '\0') 183 continue; 184 185 /* 186 * Anything else must be of the form: 187 * <IPv6-addr>/<plen> <precedence> <label> 188 */ 189 prefixstr = cp; 190 if ((cp = strchr(cp, '/')) == NULL) { 191 (void) fprintf(stderr, 192 gettext("%s: invalid prefix on line %d: %s\n"), 193 filename, lineno, prefixstr); 194 continue; 195 } 196 *cp = '\0'; 197 if (inet_pton(AF_INET6, prefixstr, &prefix) != 1) { 198 (void) fprintf(stderr, 199 gettext("%s: invalid prefix on line %d: %s\n"), 200 filename, lineno, prefixstr); 201 continue; 202 } 203 cp++; 204 205 errno = 0; 206 plen = strtol(cp, &end, 10); 207 if (cp == end || errno != 0) { 208 (void) fprintf(stderr, 209 gettext("%s: invalid prefix length on line %d\n"), 210 filename, lineno); 211 continue; 212 } 213 if (ip_plen_to_mask_v6(plen, &mask) == NULL) { 214 (void) fprintf(stderr, 215 gettext("%s: invalid prefix length on line %d:" 216 " %d\n"), filename, lineno, plen); 217 continue; 218 } 219 cp = end; 220 221 errno = 0; 222 precedence = strtol(cp, &end, 10); 223 if (cp == end || precedence < 0 || errno != 0) { 224 (void) fprintf(stderr, 225 gettext("%s: invalid precedence on line %d\n"), 226 filename, lineno); 227 continue; 228 } 229 cp = end; 230 231 while (isspace(*cp)) 232 cp++; 233 label = cp; 234 /* 235 * NULL terminate the label string. The label string is 236 * composed of non-blank characters, and can optionally be 237 * followed by a comment. 238 */ 239 while (*cp != '\0' && !isspace(*cp) && *cp != '#') 240 cp++; 241 if (*cp == '#') 242 comment_found = B_TRUE; 243 else if (*cp == '\0' || *cp == '\n') 244 end_of_line = B_TRUE; 245 *cp = '\0'; 246 247 labellen = cp - label; 248 if (labellen == 0) { 249 (void) fprintf(stderr, 250 gettext("%s: missing label on line %d\n"), 251 filename, lineno); 252 continue; 253 } 254 if (labellen >= IP6_ASP_MAXLABELSIZE) { 255 (void) fprintf(stderr, 256 gettext("%s: label too long on line %d, labels " 257 "have a %d character limit.\n"), filename, lineno, 258 IP6_ASP_MAXLABELSIZE - 1); 259 continue; 260 } 261 262 tmp_policy[entryindex].ip6_asp_prefix = prefix; 263 tmp_policy[entryindex].ip6_asp_mask = mask; 264 tmp_policy[entryindex].ip6_asp_precedence = precedence; 265 /* 266 * We're specifically using strncpy() to copy the label 267 * to take advantage of the fact that strncpy will add 268 * NULL characters to the target string up to the given 269 * length, so don't change the call to strncpy() with 270 * out also taking into account this requirement. The 271 * labels are stored in the kernel in that way in order 272 * to make comparisons more efficient: all 16 bytes of 273 * the labels are compared to each other; random bytes 274 * after the NULL terminator would yield incorrect 275 * comparisons. 276 */ 277 (void) strncpy(tmp_policy[entryindex].ip6_asp_label, label, 278 IP6_ASP_MAXLABELSIZE); 279 280 /* 281 * Anything else on the line should be a comment; print 282 * a warning if that's not the case. 283 */ 284 if (!comment_found && !end_of_line) { 285 cp++; 286 while (*cp != '\0' && isspace(*cp) && *cp != '#') 287 cp++; 288 if (*cp != '\0' && *cp != '#') { 289 (void) fprintf(stderr, 290 gettext("%s: characters following label " 291 "on line %d will be ignored\n"), 292 filename, lineno); 293 } 294 } 295 296 if (IN6_IS_ADDR_UNSPECIFIED(&prefix) && plen == 0) 297 have_default = B_TRUE; 298 299 comment_found = B_FALSE; 300 end_of_line = B_FALSE; 301 entryindex++; 302 } 303 304 if (!have_default) { 305 (void) fprintf(stderr, 306 gettext("%s: config doesn't contain a default entry.\n"), 307 filename); 308 retval = -1; 309 goto end_parse; 310 } 311 312 /* Allocate the caller's array. */ 313 if ((*new_policy = malloc(entryindex * sizeof (ip6_asp_t))) == NULL) { 314 perror("malloc"); 315 retval = -1; 316 goto end_parse; 317 } 318 319 (void) memcpy(*new_policy, tmp_policy, entryindex * sizeof (ip6_asp_t)); 320 retval = entryindex; 321 322 end_parse: 323 (void) fclose(fp); 324 return (retval); 325 } 326 327 /* 328 * setpolicy(sock, new_policy, count) 329 * 330 * Sends an SIOCSIP6ADDRPOLICY ioctl to the kernel to set the address 331 * selection policy table pointed to by new_policy. count should be 332 * the number of entries in the table; sock should be an open INET6 333 * socket. Returns EXIT_FAILURE or EXIT_SUCCESS. 334 */ 335 static int 336 setpolicy(int sock, ip6_asp_t *new_policy, int count) 337 { 338 if (strioctl(sock, SIOCSIP6ADDRPOLICY, new_policy, 339 count * sizeof (ip6_asp_t)) < 0) { 340 perror("SIOCSIP6ADDRPOLICY"); 341 return (EXIT_FAILURE); 342 } 343 return (EXIT_SUCCESS); 344 } 345 346 /* 347 * printpolicy(sock) 348 * 349 * Queries the kernel for the current address selection policy using 350 * the open socket sock, and prints the result. Returns EXIT_FAILURE 351 * if the table cannot be obtained, or EXIT_SUCCESS if the table is 352 * obtained and printed successfully. 353 */ 354 static int 355 printpolicy(int sock) 356 { 357 ip6_asp_t policy[KERN_POLICY_SIZE]; 358 ip6_asp_t *policy_ptr = policy; 359 int count, policy_index; 360 char prefixstr[INET6_ADDRSTRLEN + sizeof ("/128")]; 361 362 if ((count = strioctl(sock, SIOCGIP6ADDRPOLICY, policy_ptr, 363 KERN_POLICY_SIZE * sizeof (ip6_asp_t))) < 0) { 364 perror("SIOCGIP6ADDRPOLICY"); 365 return (EXIT_FAILURE); 366 } 367 if (count > KERN_POLICY_SIZE) { 368 policy_ptr = malloc(count * sizeof (ip6_asp_t)); 369 if (policy_ptr == NULL) { 370 perror("malloc"); 371 return (EXIT_FAILURE); 372 } 373 if ((count = strioctl(sock, SIOCGIP6ADDRPOLICY, policy_ptr, 374 count * sizeof (ip6_asp_t))) < 0) { 375 perror("SIOCGIP6ADDRPOLICY"); 376 return (EXIT_FAILURE); 377 } 378 } 379 380 if (count == 0) { 381 /* 382 * There should always at least be a default entry in the 383 * policy table, so the minimum acceptable value of 384 * policy_count is 1. 385 */ 386 (void) fprintf(stderr, gettext("%s: ERROR: " 387 "IPv6 address selection policy is empty.\n"), myname); 388 return (EXIT_FAILURE); 389 } 390 391 /* 392 * The format printed here must also be parsable by parseconf(), 393 * since we expect users to be able to redirect this output to 394 * a usable configuration file if need be. 395 */ 396 (void) printf("# Prefix " 397 " Precedence Label\n"); 398 for (policy_index = 0; policy_index < count; policy_index++) { 399 (void) snprintf(prefixstr, sizeof (prefixstr), "%s/%d", 400 inet_ntop(AF_INET6, 401 &policy_ptr[policy_index].ip6_asp_prefix, prefixstr, 402 sizeof (prefixstr)), 403 ip_mask_to_plen_v6(&policy_ptr[policy_index].ip6_asp_mask)); 404 (void) printf("%-45s %10d %s\n", prefixstr, 405 policy_ptr[policy_index].ip6_asp_precedence, 406 policy_ptr[policy_index].ip6_asp_label); 407 } 408 409 if (policy_ptr != policy) 410 free(policy_ptr); 411 return (EXIT_SUCCESS); 412 } 413 414 /* 415 * ip_mask_to_plen_v6(v6mask) 416 * 417 * This function takes a mask and returns number of bits set in the 418 * mask (the represented prefix length). Assumes a contigious mask. 419 */ 420 int 421 ip_mask_to_plen_v6(const in6_addr_t *v6mask) 422 { 423 uint8_t bits; 424 uint32_t mask; 425 int i; 426 427 if (v6mask->_S6_un._S6_u32[3] == 0xffffffff) /* check for all ones */ 428 return (IPV6_ABITS); 429 430 /* Find number of words with 32 ones */ 431 bits = 0; 432 for (i = 0; i < 4; i++) { 433 if (v6mask->_S6_un._S6_u32[i] == 0xffffffff) { 434 bits += 32; 435 continue; 436 } 437 break; 438 } 439 440 /* 441 * Find number of bits in the last word by searching 442 * for the first one from the right 443 */ 444 mask = ntohl(v6mask->_S6_un._S6_u32[i]); 445 if (mask == 0) 446 return (bits); 447 448 return (bits + 32 - (ffs(mask) - 1)); 449 } 450 451 /* 452 * ip_plen_to_mask_v6(plen, bitmask) 453 * 454 * Convert a prefix length to the mask for that prefix. 455 * Returns the argument bitmask. 456 */ 457 in6_addr_t * 458 ip_plen_to_mask_v6(int plen, in6_addr_t *bitmask) 459 { 460 uint32_t *ptr; 461 462 if (plen > IPV6_ABITS || plen < 0) 463 return (NULL); 464 465 (void) memset(bitmask, 0, sizeof (in6_addr_t)); 466 if (plen == 0) 467 return (bitmask); 468 469 ptr = (uint32_t *)bitmask; 470 while (plen > 32) { 471 *ptr++ = 0xffffffffU; 472 plen -= 32; 473 } 474 *ptr = htonl(0xffffffffU << (32 - plen)); 475 return (bitmask); 476 } 477 478 /* 479 * strioctl(fd, cmd, ptr, ilen) 480 * 481 * Passes an I_STR ioctl to fd. The ioctl type is specified by cmd, and 482 * any date to be sent down is specified by a pointer to the buffer (ptr) 483 * and the buffer size (ilen). Returns the return value from the ioctl() 484 * call. 485 */ 486 static int 487 strioctl(int fd, int cmd, void *ptr, int ilen) 488 { 489 struct strioctl str; 490 int retv; 491 492 str.ic_cmd = cmd; 493 str.ic_timout = 0; 494 str.ic_len = ilen; 495 str.ic_dp = ptr; 496 497 while ((retv = ioctl(fd, I_STR, &str)) == -1) { 498 if (errno != EINTR) 499 break; 500 } 501 return (retv); 502 } 503 504 static void 505 usage(void) 506 { 507 (void) fprintf(stderr, gettext( 508 "Usage: %s\n" 509 " %s -f <filename>\n" 510 " %s -d\n"), myname, myname, myname); 511 } 512