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