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