1/*	$KAME: prefix.c,v 1.13 2003/09/02 22:50:17 itojun Exp $	*/
2/*	$FreeBSD$	*/
3
4/*
5 * Copyright (C) 2000 WIDE Project.
6 * All rights reserved.
7 *
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
10 * are met:
11 * 1. Redistributions of source code must retain the above copyright
12 *    notice, this list of conditions and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 *    notice, this list of conditions and the following disclaimer in the
15 *    documentation and/or other materials provided with the distribution.
16 * 3. Neither the name of the project nor the names of its contributors
17 *    may be used to endorse or promote products derived from this software
18 *    without specific prior written permission.
19 *
20 * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
21 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23 * ARE DISCLAIMED.  IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
24 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
26 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
29 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30 * SUCH DAMAGE.
31 */
32
33#include <sys/types.h>
34#include <sys/socket.h>
35#include <netinet/in.h>
36#include <stdio.h>
37#include <netdb.h>
38#include <string.h>
39#include <stddef.h>
40#include <stdlib.h>
41#include <limits.h>
42
43#ifndef offsetof
44#define	offsetof(type, member)	((size_t)(u_long)(&((type *)0)->member))
45#endif
46
47#include "faithd.h"
48#include "prefix.h"
49
50static int prefix_set(const char *, struct prefix *, int);
51static struct config *config_load1(const char *);
52#if 0
53static void config_show1(const struct config *);
54static void config_show(void);
55#endif
56
57struct config *config_list = NULL;
58const int niflags = NI_NUMERICHOST;
59
60static int
61prefix_set(const char *s, struct prefix *prefix, int slash)
62{
63	char *p = NULL, *q, *r;
64	struct addrinfo hints, *res = NULL;
65	int max;
66
67	p = strdup(s);
68	if (!p)
69		goto fail;
70	q = strchr(p, '/');
71	if (q) {
72		if (!slash)
73			goto fail;
74		*q++ = '\0';
75	}
76
77	memset(&hints, 0, sizeof(hints));
78	hints.ai_family = PF_UNSPEC;
79	hints.ai_socktype = SOCK_DGRAM;	/*dummy*/
80	hints.ai_flags = AI_NUMERICHOST;
81	if (getaddrinfo(p, "0", &hints, &res))
82		goto fail;
83	if (res->ai_next || res->ai_addrlen > sizeof(prefix->a))
84		goto fail;
85	memcpy(&prefix->a, res->ai_addr, res->ai_addrlen);
86
87	switch (prefix->a.ss_family) {
88	case AF_INET:
89		max = 32;
90		break;
91	case AF_INET6:
92		max = 128;
93		break;
94	default:
95		max = -1;
96		break;
97	}
98
99	if (q) {
100		r = NULL;
101		prefix->l = (int)strtoul(q, &r, 10);
102		if (!*q || *r)
103			goto fail;
104		if (prefix->l < 0 || prefix->l > max)
105			goto fail;
106	} else
107		prefix->l = max;
108
109	if (p)
110		free(p);
111	if (res)
112		freeaddrinfo(res);
113	return 0;
114
115fail:
116	if (p)
117		free(p);
118	if (res)
119		freeaddrinfo(res);
120	return -1;
121}
122
123const char *
124prefix_string(const struct prefix *prefix)
125{
126	static char buf[NI_MAXHOST + 20];
127	char hbuf[NI_MAXHOST];
128
129	if (getnameinfo((const struct sockaddr *)&prefix->a, prefix->a.ss_len,
130	    hbuf, sizeof(hbuf), NULL, 0, niflags))
131		return NULL;
132	snprintf(buf, sizeof(buf), "%s/%d", hbuf, prefix->l);
133	return buf;
134}
135
136int
137prefix_match(const struct prefix *prefix, const struct sockaddr *sa)
138{
139	struct sockaddr_storage a, b;
140	char *pa, *pb;
141	int off, l;
142
143	if (prefix->a.ss_family != sa->sa_family ||
144	    prefix->a.ss_len != sa->sa_len)
145		return 0;
146
147	if (prefix->a.ss_len > sizeof(a) || sa->sa_len > sizeof(b))
148		return 0;
149
150	switch (prefix->a.ss_family) {
151	case AF_INET:
152		off = offsetof(struct sockaddr_in, sin_addr);
153		break;
154	case AF_INET6:
155		off = offsetof(struct sockaddr_in6, sin6_addr);
156		break;
157	default:
158		if (memcmp(&prefix->a, sa, prefix->a.ss_len) != 0)
159			return 0;
160		else
161			return 1;
162	}
163
164	memcpy(&a, &prefix->a, prefix->a.ss_len);
165	memcpy(&b, sa, sa->sa_len);
166	l = prefix->l / 8 + (prefix->l % 8 ? 1 : 0);
167
168	/* overrun check */
169	if (off + l > a.ss_len)
170		return 0;
171
172	pa = ((char *)&a) + off;
173	pb = ((char *)&b) + off;
174	if (prefix->l % 8) {
175		pa[prefix->l / 8] &= 0xff00 >> (prefix->l % 8);
176		pb[prefix->l / 8] &= 0xff00 >> (prefix->l % 8);
177	}
178	if (memcmp(pa, pb, l) != 0)
179		return 0;
180	else
181		return 1;
182}
183
184/*
185 * prefix/prefixlen permit/deny prefix/prefixlen [srcaddr]
186 * 3ffe::/16 permit 10.0.0.0/8 10.1.1.1
187 */
188static struct config *
189config_load1(const char *line)
190{
191	struct config *conf;
192	char buf[BUFSIZ];
193	char *p;
194	char *token[4];
195	int i;
196
197	if (strlen(line) + 1 > sizeof(buf))
198		return NULL;
199	strlcpy(buf, line, sizeof(buf));
200
201	p = strchr(buf, '\n');
202	if (!p)
203		return NULL;
204	*p = '\0';
205	p = strchr(buf, '#');
206	if (p)
207		*p = '\0';
208	if (strlen(buf) == 0)
209		return NULL;
210
211	p = buf;
212	memset(token, 0, sizeof(token));
213	for (i = 0; i < sizeof(token) / sizeof(token[0]); i++) {
214		token[i] = strtok(p, "\t ");
215		p = NULL;
216		if (token[i] == NULL)
217			break;
218	}
219	/* extra tokens? */
220	if (strtok(p, "\t ") != NULL)
221		return NULL;
222	/* insufficient tokens */
223	switch (i) {
224	case 3:
225	case 4:
226		break;
227	default:
228		return NULL;
229	}
230
231	conf = (struct config *)malloc(sizeof(*conf));
232	if (conf == NULL)
233		return NULL;
234	memset(conf, 0, sizeof(*conf));
235
236	if (strcasecmp(token[1], "permit") == 0)
237		conf->permit = 1;
238	else if (strcasecmp(token[1], "deny") == 0)
239		conf->permit = 0;
240	else {
241		/* invalid keyword is considered as "deny" */
242		conf->permit = 0;
243	}
244
245	if (prefix_set(token[0], &conf->match, 1) < 0)
246		goto fail;
247	if (prefix_set(token[2], &conf->dest, 1) < 0)
248		goto fail;
249	if (token[3]) {
250		if (prefix_set(token[3], &conf->src, 0) < 0)
251			goto fail;
252	}
253
254	return conf;
255
256fail:
257	free(conf);
258	return NULL;
259}
260
261int
262config_load(const char *configfile)
263{
264	FILE *fp;
265	char buf[BUFSIZ];
266	struct config *conf, *p;
267	struct config sentinel;
268
269	config_list = NULL;
270
271	if (!configfile)
272		configfile = _PATH_PREFIX_CONF;
273	fp = fopen(configfile, "r");
274	if (fp == NULL)
275		return -1;
276
277	p = &sentinel;
278	sentinel.next = NULL;
279	while (fgets(buf, sizeof(buf), fp) != NULL) {
280		conf = config_load1(buf);
281		if (conf) {
282			p->next = conf;
283			p = p->next;
284		}
285	}
286	config_list = sentinel.next;
287
288	fclose(fp);
289	return 0;
290}
291
292#if 0
293static void
294config_show1(const struct config *conf)
295{
296	const char *p;
297
298	p = prefix_string(&conf->match);
299	printf("%s", p ? p : "?");
300
301	if (conf->permit)
302		printf(" permit");
303	else
304		printf(" deny");
305
306	p = prefix_string(&conf->dest);
307	printf(" %s", p ? p : "?");
308
309	printf("\n");
310}
311
312static void
313config_show()
314{
315	struct config *conf;
316
317	for (conf = config_list; conf; conf = conf->next)
318		config_show1(conf);
319}
320#endif
321
322const struct config *
323config_match(struct sockaddr *sa1, struct sockaddr *sa2)
324{
325	static struct config conf;
326	const struct config *p;
327
328	if (sa1->sa_len > sizeof(conf.match.a) ||
329	    sa2->sa_len > sizeof(conf.dest.a))
330		return NULL;
331
332	memset(&conf, 0, sizeof(conf));
333	if (!config_list) {
334		conf.permit = 1;
335		memcpy(&conf.match.a, sa1, sa1->sa_len);
336		memcpy(&conf.dest.a, sa2, sa2->sa_len);
337		return &conf;
338	}
339
340	for (p = config_list; p; p = p->next)
341		if (prefix_match(&p->match, sa1) && prefix_match(&p->dest, sa2))
342			return p;
343
344	return NULL;
345}
346