Home | History | Annotate | Line # | Download | only in ip6addrctl
      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: head/usr.sbin/ip6addrctl/ip6addrctl.c 281143 2015-04-06 09:42:23Z glebius $
     32  */
     33 #include <sys/cdefs.h>
     34 __RCSID("$NetBSD: ip6addrctl.c,v 1.2 2015/12/27 12:42:52 joerg Exp $");
     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) __dead;
     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, sizeof(mib) / sizeof(mib[0]), 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, sizeof(mib) / sizeof(mib[0]), 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