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