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