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