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 static 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(void); 75 76 int 77 main(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 107 static void 108 get_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 145 static void 146 dump_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 201 static void 202 make_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 256 static int 257 parse_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 296 static void 297 plen2mask(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 311 static void 312 set_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 329 static int 330 mask2plen(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 388 static void 389 add_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 409 static void 410 delete_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 428 static void 429 flush_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 446 static void 447 usage(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