1 /* $KAME: ip6addrctl.c,v 1.3 2003/12/16 08:14:28 suz Exp $ */ 2 3 /*- 4 * SPDX-License-Identifier: BSD-3-Clause 5 * 6 * Copyright (C) 2001 WIDE Project. 7 * All rights reserved. 8 * 9 * Redistribution and use in source and binary forms, with or without 10 * modification, are permitted provided that the following conditions 11 * are met: 12 * 1. Redistributions of source code must retain the above copyright 13 * notice, this list of conditions and the following disclaimer. 14 * 2. Redistributions in binary form must reproduce the above copyright 15 * notice, this list of conditions and the following disclaimer in the 16 * documentation and/or other materials provided with the distribution. 17 * 3. Neither the name of the project nor the names of its contributors 18 * may be used to endorse or promote products derived from this software 19 * without specific prior written permission. 20 * 21 * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND 22 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 23 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 24 * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE 25 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 26 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 27 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 28 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 29 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 30 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 31 * SUCH DAMAGE. 32 */ 33 34 #include <sys/types.h> 35 #include <sys/socket.h> 36 #include <sys/queue.h> 37 #include <sys/param.h> 38 #include <sys/ioctl.h> 39 #ifdef JAIL 40 #include <sys/jail.h> 41 #endif 42 #include <sys/sysctl.h> 43 44 #include <net/if.h> 45 46 #include <netinet/in.h> 47 #include <netinet6/in6_var.h> 48 49 #include <stdlib.h> 50 #include <netdb.h> 51 #ifdef JAIL 52 #include <jail.h> 53 #endif 54 #include <stdio.h> 55 #include <unistd.h> 56 #include <limits.h> 57 #include <string.h> 58 #include <err.h> 59 60 #ifdef JAIL 61 static char *jailname; 62 #endif 63 static char *configfile; 64 65 struct policyqueue { 66 TAILQ_ENTRY(policyqueue) pc_entry; 67 struct in6_addrpolicy pc_policy; 68 }; 69 TAILQ_HEAD(policyhead, policyqueue); 70 static struct policyhead policyhead = TAILQ_HEAD_INITIALIZER(policyhead); 71 72 static void usage(void) __dead2; 73 static void attach_jail(void); 74 static void get_policy(void); 75 static void dump_policy(void); 76 static int mask2plen(struct sockaddr_in6 *); 77 static int parse_prefix(const char *, struct in6_addrpolicy *); 78 static void make_policy_fromfile(char *); 79 static void plen2mask(struct sockaddr_in6 *, int); 80 static void set_policy(void); 81 static void add_policy(char *, char *, char *); 82 static void delete_policy(char *); 83 static void flush_policy(void); 84 85 int 86 main(int argc, char *argv[]) 87 { 88 int ch; 89 90 while ((ch = getopt(argc, argv, "j:")) != -1) { 91 switch (ch) { 92 case 'j': 93 #ifdef JAIL 94 if ((jailname = optarg) == NULL) 95 usage(); 96 #else 97 errx(1, "not built with jail support"); 98 #endif 99 break; 100 default: 101 usage(); 102 } 103 } 104 argc -= optind; 105 argv += optind; 106 107 if (argc == 0 || (argc == 1 && strcasecmp(argv[0], "show") == 0)) { 108 attach_jail(); 109 get_policy(); 110 dump_policy(); 111 } else if (strcasecmp(argv[0], "add") == 0) { 112 if (argc != 4) 113 usage(); 114 attach_jail(); 115 add_policy(argv[1], argv[2], argv[3]); 116 } else if (strcasecmp(argv[0], "delete") == 0) { 117 if (argc != 2) 118 usage(); 119 attach_jail(); 120 delete_policy(argv[1]); 121 } else if (strcasecmp(argv[0], "flush") == 0) { 122 if (argc != 1) 123 usage(); 124 attach_jail(); 125 get_policy(); 126 flush_policy(); 127 } else if (strcasecmp(argv[0], "install") == 0) { 128 if (argc != 2) 129 usage(); 130 configfile = argv[1]; 131 make_policy_fromfile(configfile); 132 attach_jail(); 133 set_policy(); 134 } else 135 usage(); 136 137 exit(0); 138 } 139 140 static void 141 attach_jail(void) 142 { 143 #ifdef JAIL 144 int jid; 145 146 if (jailname == NULL) 147 return; 148 149 jid = jail_getid(jailname); 150 if (jid == -1) 151 errx(1, "jail not found"); 152 if (jail_attach(jid) != 0) 153 errx(1, "cannot attach to jail"); 154 #endif 155 } 156 157 static void 158 get_policy(void) 159 { 160 int mib[] = { CTL_NET, PF_INET6, IPPROTO_IPV6, IPV6CTL_ADDRCTLPOLICY }; 161 size_t l; 162 struct in6_addrpolicy *buf; 163 struct in6_addrpolicy *pol, *ep; 164 165 if (sysctl(mib, nitems(mib), NULL, &l, NULL, 0) < 0) { 166 err(1, "sysctl(IPV6CTL_ADDRCTLPOLICY)"); 167 /* NOTREACHED */ 168 } 169 if (l == 0) { 170 printf("no source-address-selection policy is installed\n"); 171 return; 172 } 173 if ((buf = malloc(l)) == NULL) { 174 errx(1, "malloc failed"); 175 /* NOTREACHED */ 176 } 177 if (sysctl(mib, nitems(mib), buf, &l, NULL, 0) < 0) { 178 err(1, "sysctl(IPV6CTL_ADDRCTLPOLICY)"); 179 /* NOTREACHED */ 180 } 181 182 ep = buf + l/sizeof(*buf); 183 for (pol = buf; pol + 1 <= ep; pol++) { 184 struct policyqueue *new; 185 186 if ((new = malloc(sizeof(*new))) == NULL) 187 errx(1, "malloc failed\n"); 188 new->pc_policy = *pol; 189 TAILQ_INSERT_TAIL(&policyhead, new, pc_entry); 190 } 191 192 free(buf); 193 } 194 195 static void 196 dump_policy(void) 197 { 198 size_t addrlen; 199 char addrbuf[NI_MAXHOST]; 200 struct in6_addrpolicy *pol; 201 struct policyqueue *ent; 202 int plen, first = 1; 203 204 for (ent = TAILQ_FIRST(&policyhead); ent; 205 ent = TAILQ_NEXT(ent, pc_entry)) { 206 pol = &ent->pc_policy; 207 if (first) { 208 printf("%-30s %5s %5s %8s\n", 209 "Prefix", "Prec", "Label", "Use"); 210 first = 0; 211 } 212 213 if ((getnameinfo((struct sockaddr *)&pol->addr, 214 sizeof(pol->addr), addrbuf, sizeof(addrbuf), 215 NULL, 0, NI_NUMERICHOST))) { 216 warnx("getnameinfo for prefix address failed"); 217 continue; 218 } 219 if ((plen = mask2plen(&pol->addrmask)) < 0) { 220 warnx("invalid address mask"); 221 continue; 222 } 223 addrlen = strlen(addrbuf); 224 if (addrlen + sizeof("/128") < sizeof(addrbuf)) { 225 snprintf(&addrbuf[addrlen], 226 sizeof(addrbuf) - addrlen - 1, 227 "/%d", plen); 228 printf("%-30s", addrbuf); 229 } else /* XXX */ 230 printf("%s/%d", addrbuf, plen); 231 printf(" %5d %5d %8llu\n", pol->preced, pol->label, 232 (unsigned long long)pol->use); 233 } 234 } 235 236 #define SKIP_WHITE(p, emptyok) \ 237 do { \ 238 while((*(p) == ' ' || *(p) == '\t')) \ 239 (p)++; \ 240 if ((*(p) == '\0' || (*(p) == '\n')) && !(emptyok)) \ 241 goto bad; \ 242 } while (0); 243 #define SKIP_WORD(p) \ 244 do { \ 245 while(*(p) != ' ' && *(p) != '\t') \ 246 (p)++; \ 247 if (*(p) == '\0' || *(p) == '\n') \ 248 goto bad; \ 249 } while (0); 250 251 static void 252 make_policy_fromfile(char *conf) 253 { 254 char line[_POSIX2_LINE_MAX], *cp; 255 char *addrstr; 256 FILE *fp; 257 int count = 0; 258 struct in6_addrpolicy pol0; 259 struct policyqueue *new; 260 261 if ((fp = fopen(conf, "r")) == NULL) 262 err(1, "fopen: %s", conf); 263 264 while(fgets(line, sizeof(line), fp)) { 265 count++; 266 cp = line; 267 268 memset(&pol0, 0, sizeof(pol0)); 269 270 /* get prefix */ 271 SKIP_WHITE(cp, 1); 272 if (*cp == '\n') /* empty line */ 273 continue; 274 if (*cp == '#') 275 continue; 276 addrstr = cp; 277 if (parse_prefix((const char *)addrstr, &pol0)) 278 goto bad; 279 280 /* get precedence value */ 281 SKIP_WORD(cp); 282 SKIP_WHITE(cp, 0); 283 pol0.preced = atoi(cp); 284 285 /* get label */ 286 SKIP_WORD(cp); 287 SKIP_WHITE(cp, 0); 288 pol0.label = atoi(cp); 289 290 /* parse succeeded. make a control buffer entry. */ 291 if ((new = malloc(sizeof(*new))) == NULL) 292 errx(1, "malloc failed\n"); 293 memset(new, 0, sizeof(*new)); 294 new->pc_policy = pol0; 295 TAILQ_INSERT_TAIL(&policyhead, new, pc_entry); 296 } 297 298 fclose(fp); 299 return; 300 301 bad: 302 errx(1, "parse failed at line %d", count); 303 /* NOTREACHED */ 304 } 305 306 static int 307 parse_prefix(const char *prefix0, struct in6_addrpolicy *pol) 308 { 309 int e = 0, plen; 310 char *prefix, *plenstr; 311 struct addrinfo hints, *res; 312 313 if ((prefix = strdup(prefix0)) == NULL) 314 errx(1, "strdup failed"); 315 316 if ((plenstr = strchr(prefix, '/')) == NULL) { 317 e = -1; 318 goto end; 319 } 320 *plenstr = '\0'; 321 322 memset(&hints, 0, sizeof(hints)); 323 hints.ai_flags = AI_NUMERICHOST; 324 hints.ai_family = AF_INET6; 325 326 if ((e = getaddrinfo(prefix, NULL, &hints, &res)) != 0) { 327 warnx("getaddrinfo failed for %s: %s", prefix, 328 gai_strerror(e)); 329 goto end; 330 } 331 memcpy(&pol->addr, res->ai_addr, res->ai_addrlen); 332 freeaddrinfo(res); 333 plen = atoi(plenstr + 1); 334 if (plen < 0 || plen > 128) { 335 warnx("invalid prefix length: %d", plen); 336 e = -1; 337 goto end; 338 } 339 plen2mask(&pol->addrmask, plen); 340 341 end: 342 free(prefix); 343 return(e); 344 } 345 346 static void 347 plen2mask(struct sockaddr_in6 *mask, int plen) 348 { 349 u_char *cp = (unsigned char *)&mask->sin6_addr; 350 351 memset(mask, 0, sizeof(*mask)); 352 mask->sin6_family = AF_INET6; /* just in case */ 353 mask->sin6_len = sizeof(*mask); 354 355 for(; plen >= 8; plen -= 8) 356 *cp++ = 0xff; 357 if (plen > 0) 358 *cp = (0xff << (8 - plen)); 359 } 360 361 static void 362 set_policy(void) 363 { 364 struct policyqueue *ent; 365 int s; 366 367 if ((s = socket(AF_INET6, SOCK_DGRAM, IPPROTO_UDP)) < 0) 368 err(1, "socket(UDP)"); 369 370 for (ent = TAILQ_FIRST(&policyhead); ent; 371 ent = TAILQ_NEXT(ent, pc_entry)) { 372 if (ioctl(s, SIOCAADDRCTL_POLICY, &ent->pc_policy)) 373 warn("ioctl(SIOCAADDRCTL_POLICY)"); 374 } 375 376 close(s); 377 } 378 379 static int 380 mask2plen(struct sockaddr_in6 *mask) 381 { 382 int masklen, final = 0; 383 u_char *p, *lim; 384 385 masklen = 0; 386 lim = (u_char *)(mask + 1); 387 for (p = (u_char *)(&mask->sin6_addr); p < lim; p++) { 388 if (final && *p) { 389 goto bad; 390 } 391 392 switch (*p & 0xff) { 393 case 0xff: 394 masklen += 8; 395 break; 396 case 0xfe: 397 masklen += 7; 398 final++; 399 break; 400 case 0xfc: 401 masklen += 6; 402 final++; 403 break; 404 case 0xf8: 405 masklen += 5; 406 final++; 407 break; 408 case 0xf0: 409 masklen += 4; 410 final++; 411 break; 412 case 0xe0: 413 masklen += 3; 414 final++; 415 break; 416 case 0xc0: 417 masklen += 2; 418 final++; 419 break; 420 case 0x80: 421 masklen += 1; 422 final++; 423 break; 424 case 0x00: 425 final++; 426 break; 427 default: 428 goto bad; 429 break; 430 } 431 } 432 return(masklen); 433 434 bad: 435 return(-1); 436 } 437 438 static void 439 add_policy(char *prefix, char *prec, char *label) 440 { 441 struct in6_addrpolicy p; 442 int s; 443 444 memset(&p, 0, sizeof(p)); 445 446 if (parse_prefix((const char *)prefix, &p)) 447 errx(1, "bad prefix: %s", prefix); 448 p.preced = atoi(prec); 449 p.label = atoi(label); 450 451 if ((s = socket(AF_INET6, SOCK_DGRAM, IPPROTO_UDP)) < 0) 452 err(1, "socket(UDP)"); 453 if (ioctl(s, SIOCAADDRCTL_POLICY, &p)) 454 err(1, "ioctl(SIOCAADDRCTL_POLICY)"); 455 456 close(s); 457 } 458 459 static void 460 delete_policy(char *prefix) 461 { 462 struct in6_addrpolicy p; 463 int s; 464 465 memset(&p, 0, sizeof(p)); 466 467 if (parse_prefix((const char *)prefix, &p)) 468 errx(1, "bad prefix: %s", prefix); 469 470 if ((s = socket(AF_INET6, SOCK_DGRAM, IPPROTO_UDP)) < 0) 471 err(1, "socket(UDP)"); 472 if (ioctl(s, SIOCDADDRCTL_POLICY, &p)) 473 err(1, "ioctl(SIOCDADDRCTL_POLICY)"); 474 475 close(s); 476 } 477 478 static void 479 flush_policy(void) 480 { 481 struct policyqueue *ent; 482 int s; 483 484 if ((s = socket(AF_INET6, SOCK_DGRAM, IPPROTO_UDP)) < 0) 485 err(1, "socket(UDP)"); 486 487 for (ent = TAILQ_FIRST(&policyhead); ent; 488 ent = TAILQ_NEXT(ent, pc_entry)) { 489 if (ioctl(s, SIOCDADDRCTL_POLICY, &ent->pc_policy)) 490 warn("ioctl(SIOCDADDRCTL_POLICY)"); 491 } 492 493 close(s); 494 } 495 496 static void 497 usage(void) 498 { 499 fprintf(stderr, "usage: ip6addrctl [-j jail] [show]\n"); 500 fprintf(stderr, " ip6addrctl [-j jail] add " 501 "<prefix> <precedence> <label>\n"); 502 fprintf(stderr, " ip6addrctl [-j jail] delete <prefix>\n"); 503 fprintf(stderr, " ip6addrctl [-j jail] flush\n"); 504 fprintf(stderr, " ip6addrctl [-j jail] install <configfile>\n"); 505 506 exit(1); 507 } 508