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