1123870Sume/* $KAME: ip6addrctl.c,v 1.3 2003/12/16 08:14:28 suz Exp $ */ 2121748Sume 3121748Sume/* 4121748Sume * Copyright (C) 2001 WIDE Project. 5121748Sume * All rights reserved. 6121748Sume * 7121748Sume * Redistribution and use in source and binary forms, with or without 8121748Sume * modification, are permitted provided that the following conditions 9121748Sume * are met: 10121748Sume * 1. Redistributions of source code must retain the above copyright 11121748Sume * notice, this list of conditions and the following disclaimer. 12121748Sume * 2. Redistributions in binary form must reproduce the above copyright 13121748Sume * notice, this list of conditions and the following disclaimer in the 14121748Sume * documentation and/or other materials provided with the distribution. 15121748Sume * 3. Neither the name of the project nor the names of its contributors 16121748Sume * may be used to endorse or promote products derived from this software 17121748Sume * without specific prior written permission. 18121748Sume * 19121748Sume * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND 20121748Sume * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 21121748Sume * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 22121748Sume * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE 23121748Sume * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 24121748Sume * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 25121748Sume * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 26121748Sume * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 27121748Sume * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 28121748Sume * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 29121748Sume * SUCH DAMAGE. 30121748Sume * 31121748Sume * $FreeBSD: releng/11.0/usr.sbin/ip6addrctl/ip6addrctl.c 281143 2015-04-06 09:42:23Z glebius $ 32121748Sume */ 33121748Sume 34121748Sume#include <sys/types.h> 35121748Sume#include <sys/socket.h> 36121748Sume#include <sys/queue.h> 37121748Sume#include <sys/param.h> 38121748Sume#include <sys/ioctl.h> 39121748Sume#include <sys/sysctl.h> 40121748Sume 41121748Sume#include <net/if.h> 42121748Sume 43121748Sume#include <netinet/in.h> 44121748Sume#include <netinet6/in6_var.h> 45121748Sume 46121748Sume#include <stdlib.h> 47121748Sume#include <netdb.h> 48121748Sume#include <stdio.h> 49121748Sume#include <unistd.h> 50121748Sume#include <limits.h> 51121748Sume#include <string.h> 52121748Sume#include <err.h> 53121748Sume 54121748Sumestatic char *configfile; 55121748Sume 56121748Sumestruct policyqueue { 57121748Sume TAILQ_ENTRY(policyqueue) pc_entry; 58121748Sume struct in6_addrpolicy pc_policy; 59121748Sume}; 60121748SumeTAILQ_HEAD(policyhead, policyqueue); 61241737Sedstatic struct policyhead policyhead; 62121748Sume 63173412Skevlostatic void usage(void); 64173412Skevlostatic void get_policy(void); 65173412Skevlostatic void dump_policy(void); 66173412Skevlostatic int mask2plen(struct sockaddr_in6 *); 67173412Skevlostatic int parse_prefix(const char *, struct in6_addrpolicy *); 68173412Skevlostatic void make_policy_fromfile(char *); 69173412Skevlostatic void plen2mask(struct sockaddr_in6 *, int); 70173412Skevlostatic void set_policy(void); 71173412Skevlostatic void add_policy(char *, char *, char *); 72173412Skevlostatic void delete_policy(char *); 73241134Seadlerstatic void flush_policy(void); 74121748Sume 75121748Sumeint 76241134Seadlermain(int argc, char *argv[]) 77121748Sume{ 78121748Sume TAILQ_INIT(&policyhead); 79121748Sume 80121748Sume if (argc == 1 || strcasecmp(argv[1], "show") == 0) { 81121748Sume get_policy(); 82121748Sume dump_policy(); 83121748Sume } else if (strcasecmp(argv[1], "add") == 0) { 84121748Sume if (argc < 5) 85121748Sume usage(); 86121748Sume add_policy(argv[2], argv[3], argv[4]); 87123714Ssuz } else if (strcasecmp(argv[1], "delete") == 0) { 88121748Sume if (argc < 3) 89121748Sume usage(); 90121748Sume delete_policy(argv[2]); 91121748Sume } else if (strcasecmp(argv[1], "flush") == 0) { 92121748Sume get_policy(); 93121748Sume flush_policy(); 94121748Sume } else if (strcasecmp(argv[1], "install") == 0) { 95121748Sume if (argc < 3) 96121748Sume usage(); 97121748Sume configfile = argv[2]; 98121748Sume make_policy_fromfile(configfile); 99121748Sume set_policy(); 100121748Sume } else 101121748Sume usage(); 102121748Sume 103121748Sume exit(0); 104121748Sume} 105121748Sume 106121748Sumestatic void 107241134Seadlerget_policy(void) 108121748Sume{ 109121748Sume int mib[] = { CTL_NET, PF_INET6, IPPROTO_IPV6, IPV6CTL_ADDRCTLPOLICY }; 110121748Sume size_t l; 111241134Seadler struct in6_addrpolicy *buf; 112121748Sume struct in6_addrpolicy *pol, *ep; 113121748Sume 114121748Sume if (sysctl(mib, sizeof(mib) / sizeof(mib[0]), NULL, &l, NULL, 0) < 0) { 115121748Sume err(1, "sysctl(IPV6CTL_ADDRCTLPOLICY)"); 116121748Sume /* NOTREACHED */ 117121748Sume } 118123870Sume if (l == 0) { 119123870Sume printf("no source-address-selection policy is installed\n"); 120123870Sume return; 121123870Sume } 122121748Sume if ((buf = malloc(l)) == NULL) { 123121748Sume errx(1, "malloc failed"); 124121748Sume /* NOTREACHED */ 125121748Sume } 126121748Sume if (sysctl(mib, sizeof(mib) / sizeof(mib[0]), buf, &l, NULL, 0) < 0) { 127121748Sume err(1, "sysctl(IPV6CTL_ADDRCTLPOLICY)"); 128121748Sume /* NOTREACHED */ 129121748Sume } 130121748Sume 131241134Seadler ep = buf + l/sizeof(*buf); 132241134Seadler for (pol = buf; pol + 1 <= ep; pol++) { 133121748Sume struct policyqueue *new; 134121748Sume 135121748Sume if ((new = malloc(sizeof(*new))) == NULL) 136121748Sume errx(1, "malloc failed\n"); 137121748Sume new->pc_policy = *pol; 138121748Sume TAILQ_INSERT_TAIL(&policyhead, new, pc_entry); 139121748Sume } 140121748Sume 141121748Sume free(buf); 142121748Sume} 143121748Sume 144121748Sumestatic void 145241134Seadlerdump_policy(void) 146121748Sume{ 147121748Sume size_t addrlen; 148121748Sume char addrbuf[NI_MAXHOST]; 149121748Sume struct in6_addrpolicy *pol; 150121748Sume struct policyqueue *ent; 151121748Sume int plen, first = 1; 152121748Sume 153121748Sume for (ent = TAILQ_FIRST(&policyhead); ent; 154121748Sume ent = TAILQ_NEXT(ent, pc_entry)) { 155121748Sume pol = &ent->pc_policy; 156121748Sume if (first) { 157121748Sume printf("%-30s %5s %5s %8s\n", 158121748Sume "Prefix", "Prec", "Label", "Use"); 159121748Sume first = 0; 160121748Sume } 161121748Sume 162121748Sume if ((getnameinfo((struct sockaddr *)&pol->addr, 163121748Sume sizeof(pol->addr), addrbuf, sizeof(addrbuf), 164121748Sume NULL, 0, NI_NUMERICHOST))) { 165121748Sume warnx("getnameinfo for prefix address failed"); 166121748Sume continue; 167121748Sume } 168121748Sume if ((plen = mask2plen(&pol->addrmask)) < 0) { 169121748Sume warnx("invalid address mask"); 170121748Sume continue; 171121748Sume } 172121748Sume addrlen = strlen(addrbuf); 173121748Sume if (addrlen + sizeof("/128") < sizeof(addrbuf)) { 174121748Sume snprintf(&addrbuf[addrlen], 175121748Sume sizeof(addrbuf) - addrlen - 1, 176121748Sume "/%d", plen); 177121748Sume printf("%-30s", addrbuf); 178121748Sume } else /* XXX */ 179121748Sume printf("%s/%d", addrbuf, plen); 180121748Sume printf(" %5d %5d %8llu\n", pol->preced, pol->label, 181121748Sume (unsigned long long)pol->use); 182121748Sume } 183121748Sume} 184121748Sume 185121748Sume#define SKIP_WHITE(p, emptyok) \ 186121748Sume do { \ 187121748Sume while((*(p) == ' ' || *(p) == '\t')) \ 188121748Sume (p)++; \ 189121748Sume if ((*(p) == '\0' || (*(p) == '\n')) && !(emptyok)) \ 190121748Sume goto bad; \ 191121748Sume } while (0); 192121748Sume#define SKIP_WORD(p) \ 193121748Sume do { \ 194121748Sume while(*(p) != ' ' && *(p) != '\t') \ 195121748Sume (p)++; \ 196121748Sume if (*(p) == '\0' || *(p) == '\n') \ 197121748Sume goto bad; \ 198121748Sume } while (0); 199121748Sume 200121748Sumestatic void 201241134Seadlermake_policy_fromfile(char *conf) 202121748Sume{ 203121748Sume char line[_POSIX2_LINE_MAX], *cp; 204121748Sume char *addrstr; 205121748Sume FILE *fp; 206121748Sume int count = 0; 207121748Sume struct in6_addrpolicy pol0; 208121748Sume struct policyqueue *new; 209121748Sume 210121748Sume if ((fp = fopen(conf, "r")) == NULL) 211121748Sume err(1, "fopen: %s", conf); 212121748Sume 213121748Sume while(fgets(line, sizeof(line), fp)) { 214121748Sume count++; 215121748Sume cp = line; 216121748Sume 217121748Sume memset(&pol0, 0, sizeof(pol0)); 218121748Sume 219121748Sume /* get prefix */ 220121748Sume SKIP_WHITE(cp, 1); 221121748Sume if (*cp == '\n') /* empty line */ 222121748Sume continue; 223121748Sume if (*cp == '#') 224121748Sume continue; 225121748Sume addrstr = cp; 226121748Sume if (parse_prefix((const char *)addrstr, &pol0)) 227121748Sume goto bad; 228121748Sume 229121748Sume /* get precedence value */ 230121748Sume SKIP_WORD(cp); 231121748Sume SKIP_WHITE(cp, 0); 232121748Sume pol0.preced = atoi(cp); 233121748Sume 234121748Sume /* get label */ 235121748Sume SKIP_WORD(cp); 236121748Sume SKIP_WHITE(cp, 0); 237121748Sume pol0.label = atoi(cp); 238121748Sume 239121748Sume /* parse succeeded. make a control buffer entry. */ 240121748Sume if ((new = malloc(sizeof(*new))) == NULL) 241121748Sume errx(1, "malloc failed\n"); 242121748Sume memset(new, 0, sizeof(*new)); 243121748Sume new->pc_policy = pol0; 244121748Sume TAILQ_INSERT_TAIL(&policyhead, new, pc_entry); 245121748Sume } 246121748Sume 247121748Sume fclose(fp); 248121748Sume return; 249121748Sume 250121748Sume bad: 251121748Sume errx(1, "parse failed at line %d", count); 252121748Sume /* NOTREACHED */ 253121748Sume} 254121748Sume 255121748Sumestatic int 256241134Seadlerparse_prefix(const char *prefix0, struct in6_addrpolicy *pol) 257121748Sume{ 258121748Sume int e = 0, plen; 259121748Sume char *prefix, *plenstr; 260121748Sume struct addrinfo hints, *res; 261121748Sume 262121748Sume if ((prefix = strdup(prefix0)) == NULL) 263121748Sume errx(1, "strdup failed"); 264121748Sume 265121748Sume if ((plenstr = strchr(prefix, '/')) == NULL) { 266121748Sume e = -1; 267121748Sume goto end; 268121748Sume } 269121748Sume *plenstr = '\0'; 270121748Sume 271121748Sume memset(&hints, 0, sizeof(hints)); 272121748Sume hints.ai_flags = AI_NUMERICHOST; 273121748Sume hints.ai_family = AF_INET6; 274121748Sume 275121748Sume if ((e = getaddrinfo(prefix, NULL, &hints, &res)) != 0) { 276121748Sume warnx("getaddrinfo failed for %s: %s", prefix, 277121748Sume gai_strerror(e)); 278121748Sume goto end; 279121748Sume } 280121748Sume memcpy(&pol->addr, res->ai_addr, res->ai_addrlen); 281121748Sume freeaddrinfo(res); 282121748Sume plen = atoi(plenstr + 1); 283121748Sume if (plen < 0 || plen > 128) { 284121748Sume warnx("invalid prefix length: %d", plen); 285121748Sume e = -1; 286121748Sume goto end; 287121748Sume } 288121748Sume plen2mask(&pol->addrmask, plen); 289121748Sume 290121748Sume end: 291121748Sume free(prefix); 292121748Sume return(e); 293121748Sume} 294121748Sume 295121748Sumestatic void 296241134Seadlerplen2mask(struct sockaddr_in6 *mask, int plen) 297121748Sume{ 298241134Seadler u_char *cp = (unsigned char *)&mask->sin6_addr; 299121748Sume 300121748Sume memset(mask, 0, sizeof(*mask)); 301121748Sume mask->sin6_family = AF_INET6; /* just in case */ 302121748Sume mask->sin6_len = sizeof(*mask); 303121748Sume 304121748Sume for(; plen >= 8; plen -= 8) 305121748Sume *cp++ = 0xff; 306121748Sume if (plen > 0) 307121748Sume *cp = (0xff << (8 - plen)); 308121748Sume} 309121748Sume 310121748Sumestatic void 311241134Seadlerset_policy(void) 312121748Sume{ 313121748Sume struct policyqueue *ent; 314121748Sume int s; 315121748Sume 316121748Sume if ((s = socket(AF_INET6, SOCK_DGRAM, IPPROTO_UDP)) < 0) 317121748Sume err(1, "socket(UDP)"); 318121748Sume 319121748Sume for (ent = TAILQ_FIRST(&policyhead); ent; 320121748Sume ent = TAILQ_NEXT(ent, pc_entry)) { 321121748Sume if (ioctl(s, SIOCAADDRCTL_POLICY, &ent->pc_policy)) 322121748Sume warn("ioctl(SIOCAADDRCTL_POLICY)"); 323121748Sume } 324121748Sume 325121748Sume close(s); 326121748Sume} 327121748Sume 328121748Sumestatic int 329241134Seadlermask2plen(struct sockaddr_in6 *mask) 330121748Sume{ 331121748Sume int masklen, final = 0; 332121748Sume u_char *p, *lim; 333121748Sume 334121748Sume masklen = 0; 335121748Sume lim = (u_char *)(mask + 1); 336121748Sume for (p = (u_char *)(&mask->sin6_addr); p < lim; p++) { 337121748Sume if (final && *p) { 338121748Sume goto bad; 339121748Sume } 340121748Sume 341121748Sume switch (*p & 0xff) { 342121748Sume case 0xff: 343121748Sume masklen += 8; 344121748Sume break; 345121748Sume case 0xfe: 346121748Sume masklen += 7; 347121748Sume final++; 348121748Sume break; 349121748Sume case 0xfc: 350121748Sume masklen += 6; 351121748Sume final++; 352121748Sume break; 353121748Sume case 0xf8: 354121748Sume masklen += 5; 355121748Sume final++; 356121748Sume break; 357121748Sume case 0xf0: 358121748Sume masklen += 4; 359121748Sume final++; 360121748Sume break; 361121748Sume case 0xe0: 362121748Sume masklen += 3; 363121748Sume final++; 364121748Sume break; 365121748Sume case 0xc0: 366121748Sume masklen += 2; 367121748Sume final++; 368121748Sume break; 369121748Sume case 0x80: 370121748Sume masklen += 1; 371121748Sume final++; 372121748Sume break; 373121748Sume case 0x00: 374121748Sume final++; 375121748Sume break; 376121748Sume default: 377121748Sume goto bad; 378121748Sume break; 379121748Sume } 380121748Sume } 381121748Sume return(masklen); 382121748Sume 383121748Sume bad: 384121748Sume return(-1); 385121748Sume} 386121748Sume 387121748Sumestatic void 388241134Seadleradd_policy(char *prefix, char *prec, char *label) 389121748Sume{ 390121748Sume struct in6_addrpolicy p; 391121748Sume int s; 392121748Sume 393121748Sume memset(&p, 0, sizeof(p)); 394121748Sume 395121748Sume if (parse_prefix((const char *)prefix, &p)) 396121748Sume errx(1, "bad prefix: %s", prefix); 397121748Sume p.preced = atoi(prec); 398121748Sume p.label = atoi(label); 399121748Sume 400121748Sume if ((s = socket(AF_INET6, SOCK_DGRAM, IPPROTO_UDP)) < 0) 401121748Sume err(1, "socket(UDP)"); 402121748Sume if (ioctl(s, SIOCAADDRCTL_POLICY, &p)) 403121748Sume err(1, "ioctl(SIOCAADDRCTL_POLICY)"); 404121748Sume 405121748Sume close(s); 406121748Sume} 407121748Sume 408121748Sumestatic void 409241134Seadlerdelete_policy(char *prefix) 410121748Sume{ 411121748Sume struct in6_addrpolicy p; 412121748Sume int s; 413121748Sume 414121748Sume memset(&p, 0, sizeof(p)); 415121748Sume 416121748Sume if (parse_prefix((const char *)prefix, &p)) 417121748Sume errx(1, "bad prefix: %s", prefix); 418121748Sume 419121748Sume if ((s = socket(AF_INET6, SOCK_DGRAM, IPPROTO_UDP)) < 0) 420121748Sume err(1, "socket(UDP)"); 421121748Sume if (ioctl(s, SIOCDADDRCTL_POLICY, &p)) 422121748Sume err(1, "ioctl(SIOCDADDRCTL_POLICY)"); 423121748Sume 424121748Sume close(s); 425121748Sume} 426121748Sume 427121748Sumestatic void 428241134Seadlerflush_policy(void) 429121748Sume{ 430121748Sume struct policyqueue *ent; 431121748Sume int s; 432121748Sume 433121748Sume if ((s = socket(AF_INET6, SOCK_DGRAM, IPPROTO_UDP)) < 0) 434121748Sume err(1, "socket(UDP)"); 435121748Sume 436121748Sume for (ent = TAILQ_FIRST(&policyhead); ent; 437121748Sume ent = TAILQ_NEXT(ent, pc_entry)) { 438121748Sume if (ioctl(s, SIOCDADDRCTL_POLICY, &ent->pc_policy)) 439121748Sume warn("ioctl(SIOCDADDRCTL_POLICY)"); 440121748Sume } 441121748Sume 442121748Sume close(s); 443121748Sume} 444121748Sume 445121748Sumestatic void 446241134Seadlerusage(void) 447121748Sume{ 448121748Sume fprintf(stderr, "usage: ip6addrctl [show]\n"); 449121748Sume fprintf(stderr, " ip6addrctl add " 450121748Sume "<prefix> <precedence> <label>\n"); 451121748Sume fprintf(stderr, " ip6addrctl delete <prefix>\n"); 452121748Sume fprintf(stderr, " ip6addrctl flush\n"); 453121748Sume fprintf(stderr, " ip6addrctl install <configfile>\n"); 454121748Sume 455121748Sume exit(1); 456121748Sume} 457