ip6addrctl.c revision 121748
1/* $KAME: ip6addrctl.c,v 1.1 2001/12/27 12:45:24 jinmei 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: head/usr.sbin/ip6addrctl/ip6addrctl.c 121748 2003-10-30 17:46:40Z ume $ 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 55static char *configfile; 56 57struct policyqueue { 58 TAILQ_ENTRY(policyqueue) pc_entry; 59 struct in6_addrpolicy pc_policy; 60}; 61TAILQ_HEAD(policyhead, policyqueue); 62struct policyhead policyhead; 63 64static void usage __P((void)); 65static void get_policy __P((void)); 66static void dump_policy __P((void)); 67static int mask2plen __P((struct sockaddr_in6 *)); 68static int parse_prefix __P((const char *, struct in6_addrpolicy *)); 69static void make_policy_fromfile __P((char *)); 70static void plen2mask __P((struct sockaddr_in6 *, int)); 71static void set_policy __P((void)); 72static void add_policy __P((char *, char *, char *)); 73static void delete_policy __P((char *)); 74static void flush_policy __P(()); 75 76int 77main(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[0], "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 109static void 110get_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 ((buf = malloc(l)) == NULL) { 122 errx(1, "malloc failed"); 123 /* NOTREACHED */ 124 } 125 if (sysctl(mib, sizeof(mib) / sizeof(mib[0]), buf, &l, NULL, 0) < 0) { 126 err(1, "sysctl(IPV6CTL_ADDRCTLPOLICY)"); 127 /* NOTREACHED */ 128 } 129 130 ep = (struct in6_addrpolicy *)(buf + l); 131 for (pol = (struct in6_addrpolicy *)buf; pol + 1 <= ep; pol++) { 132 struct policyqueue *new; 133 134 if ((new = malloc(sizeof(*new))) == NULL) 135 errx(1, "malloc failed\n"); 136 new->pc_policy = *pol; 137 TAILQ_INSERT_TAIL(&policyhead, new, pc_entry); 138 } 139 140 free(buf); 141} 142 143static void 144dump_policy() 145{ 146 size_t addrlen; 147 char addrbuf[NI_MAXHOST]; 148 struct in6_addrpolicy *pol; 149 struct policyqueue *ent; 150 int plen, first = 1; 151 152 for (ent = TAILQ_FIRST(&policyhead); ent; 153 ent = TAILQ_NEXT(ent, pc_entry)) { 154 pol = &ent->pc_policy; 155 if (first) { 156 printf("%-30s %5s %5s %8s\n", 157 "Prefix", "Prec", "Label", "Use"); 158 first = 0; 159 } 160 161 if ((getnameinfo((struct sockaddr *)&pol->addr, 162 sizeof(pol->addr), addrbuf, sizeof(addrbuf), 163 NULL, 0, NI_NUMERICHOST))) { 164 warnx("getnameinfo for prefix address failed"); 165 continue; 166 } 167 if ((plen = mask2plen(&pol->addrmask)) < 0) { 168 warnx("invalid address mask"); 169 continue; 170 } 171 addrlen = strlen(addrbuf); 172 if (addrlen + sizeof("/128") < sizeof(addrbuf)) { 173 snprintf(&addrbuf[addrlen], 174 sizeof(addrbuf) - addrlen - 1, 175 "/%d", plen); 176 printf("%-30s", addrbuf); 177 } else /* XXX */ 178 printf("%s/%d", addrbuf, plen); 179 printf(" %5d %5d %8llu\n", pol->preced, pol->label, 180 (unsigned long long)pol->use); 181 } 182} 183 184#define SKIP_WHITE(p, emptyok) \ 185 do { \ 186 while((*(p) == ' ' || *(p) == '\t')) \ 187 (p)++; \ 188 if ((*(p) == '\0' || (*(p) == '\n')) && !(emptyok)) \ 189 goto bad; \ 190 } while (0); 191#define SKIP_WORD(p) \ 192 do { \ 193 while(*(p) != ' ' && *(p) != '\t') \ 194 (p)++; \ 195 if (*(p) == '\0' || *(p) == '\n') \ 196 goto bad; \ 197 } while (0); 198 199static void 200make_policy_fromfile(conf) 201 char *conf; 202{ 203 char line[_POSIX2_LINE_MAX], *cp; 204 char *addrstr; 205 FILE *fp; 206 int count = 0; 207 struct in6_addrpolicy pol0; 208 struct policyqueue *new; 209 210 if ((fp = fopen(conf, "r")) == NULL) 211 err(1, "fopen: %s", conf); 212 213 while(fgets(line, sizeof(line), fp)) { 214 count++; 215 cp = line; 216 217 memset(&pol0, 0, sizeof(pol0)); 218 219 /* get prefix */ 220 SKIP_WHITE(cp, 1); 221 if (*cp == '\n') /* empty line */ 222 continue; 223 if (*cp == '#') 224 continue; 225 addrstr = cp; 226 if (parse_prefix((const char *)addrstr, &pol0)) 227 goto bad; 228 229 /* get precedence value */ 230 SKIP_WORD(cp); 231 SKIP_WHITE(cp, 0); 232 pol0.preced = atoi(cp); 233 234 /* get label */ 235 SKIP_WORD(cp); 236 SKIP_WHITE(cp, 0); 237 pol0.label = atoi(cp); 238 239 /* parse succeeded. make a control buffer entry. */ 240 if ((new = malloc(sizeof(*new))) == NULL) 241 errx(1, "malloc failed\n"); 242 memset(new, 0, sizeof(*new)); 243 new->pc_policy = pol0; 244 TAILQ_INSERT_TAIL(&policyhead, new, pc_entry); 245 } 246 247 fclose(fp); 248 return; 249 250 bad: 251 errx(1, "parse failed at line %d", count); 252 /* NOTREACHED */ 253} 254 255static int 256parse_prefix(prefix0, pol) 257 const char *prefix0; 258 struct in6_addrpolicy *pol; 259{ 260 int e = 0, plen; 261 char *prefix, *plenstr; 262 struct addrinfo hints, *res; 263 264 if ((prefix = strdup(prefix0)) == NULL) 265 errx(1, "strdup failed"); 266 267 if ((plenstr = strchr(prefix, '/')) == NULL) { 268 e = -1; 269 goto end; 270 } 271 *plenstr = '\0'; 272 273 memset(&hints, 0, sizeof(hints)); 274 hints.ai_flags = AI_NUMERICHOST; 275 hints.ai_family = AF_INET6; 276 277 if ((e = getaddrinfo(prefix, NULL, &hints, &res)) != 0) { 278 warnx("getaddrinfo failed for %s: %s", prefix, 279 gai_strerror(e)); 280 goto end; 281 } 282 memcpy(&pol->addr, res->ai_addr, res->ai_addrlen); 283 freeaddrinfo(res); 284 plen = atoi(plenstr + 1); 285 if (plen < 0 || plen > 128) { 286 warnx("invalid prefix length: %d", plen); 287 e = -1; 288 goto end; 289 } 290 plen2mask(&pol->addrmask, plen); 291 292 end: 293 free(prefix); 294 return(e); 295} 296 297static void 298plen2mask(mask, plen) 299 struct sockaddr_in6 *mask; 300 int plen; 301{ 302 u_char *cp = (char *)&mask->sin6_addr; 303 304 memset(mask, 0, sizeof(*mask)); 305 mask->sin6_family = AF_INET6; /* just in case */ 306 mask->sin6_len = sizeof(*mask); 307 308 for(; plen >= 8; plen -= 8) 309 *cp++ = 0xff; 310 if (plen > 0) 311 *cp = (0xff << (8 - plen)); 312} 313 314static void 315set_policy() 316{ 317 struct policyqueue *ent; 318 int s; 319 320 if ((s = socket(AF_INET6, SOCK_DGRAM, IPPROTO_UDP)) < 0) 321 err(1, "socket(UDP)"); 322 323 for (ent = TAILQ_FIRST(&policyhead); ent; 324 ent = TAILQ_NEXT(ent, pc_entry)) { 325 if (ioctl(s, SIOCAADDRCTL_POLICY, &ent->pc_policy)) 326 warn("ioctl(SIOCAADDRCTL_POLICY)"); 327 } 328 329 close(s); 330} 331 332static int 333mask2plen(mask) 334 struct sockaddr_in6 *mask; 335{ 336 int masklen, final = 0; 337 u_char *p, *lim; 338 339 masklen = 0; 340 lim = (u_char *)(mask + 1); 341 for (p = (u_char *)(&mask->sin6_addr); p < lim; p++) { 342 if (final && *p) { 343 goto bad; 344 } 345 346 switch (*p & 0xff) { 347 case 0xff: 348 masklen += 8; 349 break; 350 case 0xfe: 351 masklen += 7; 352 final++; 353 break; 354 case 0xfc: 355 masklen += 6; 356 final++; 357 break; 358 case 0xf8: 359 masklen += 5; 360 final++; 361 break; 362 case 0xf0: 363 masklen += 4; 364 final++; 365 break; 366 case 0xe0: 367 masklen += 3; 368 final++; 369 break; 370 case 0xc0: 371 masklen += 2; 372 final++; 373 break; 374 case 0x80: 375 masklen += 1; 376 final++; 377 break; 378 case 0x00: 379 final++; 380 break; 381 default: 382 goto bad; 383 break; 384 } 385 } 386 return(masklen); 387 388 bad: 389 return(-1); 390} 391 392static void 393add_policy(prefix, prec, label) 394 char *prefix, *prec, *label; 395{ 396 struct in6_addrpolicy p; 397 int s; 398 399 memset(&p, 0, sizeof(p)); 400 401 if (parse_prefix((const char *)prefix, &p)) 402 errx(1, "bad prefix: %s", prefix); 403 p.preced = atoi(prec); 404 p.label = atoi(label); 405 406 if ((s = socket(AF_INET6, SOCK_DGRAM, IPPROTO_UDP)) < 0) 407 err(1, "socket(UDP)"); 408 if (ioctl(s, SIOCAADDRCTL_POLICY, &p)) 409 err(1, "ioctl(SIOCAADDRCTL_POLICY)"); 410 411 close(s); 412} 413 414static void 415delete_policy(prefix) 416 char *prefix; 417{ 418 struct in6_addrpolicy p; 419 int s; 420 421 memset(&p, 0, sizeof(p)); 422 423 if (parse_prefix((const char *)prefix, &p)) 424 errx(1, "bad prefix: %s", prefix); 425 426 if ((s = socket(AF_INET6, SOCK_DGRAM, IPPROTO_UDP)) < 0) 427 err(1, "socket(UDP)"); 428 if (ioctl(s, SIOCDADDRCTL_POLICY, &p)) 429 err(1, "ioctl(SIOCDADDRCTL_POLICY)"); 430 431 close(s); 432} 433 434static void 435flush_policy() 436{ 437 struct policyqueue *ent; 438 int s; 439 440 if ((s = socket(AF_INET6, SOCK_DGRAM, IPPROTO_UDP)) < 0) 441 err(1, "socket(UDP)"); 442 443 for (ent = TAILQ_FIRST(&policyhead); ent; 444 ent = TAILQ_NEXT(ent, pc_entry)) { 445 if (ioctl(s, SIOCDADDRCTL_POLICY, &ent->pc_policy)) 446 warn("ioctl(SIOCDADDRCTL_POLICY)"); 447 } 448 449 close(s); 450} 451 452static void 453usage() 454{ 455 fprintf(stderr, "usage: ip6addrctl [show]\n"); 456 fprintf(stderr, " ip6addrctl add " 457 "<prefix> <precedence> <label>\n"); 458 fprintf(stderr, " ip6addrctl delete <prefix>\n"); 459 fprintf(stderr, " ip6addrctl flush\n"); 460 fprintf(stderr, " ip6addrctl install <configfile>\n"); 461 462 exit(1); 463} 464