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