util.c revision 1.5
1/*	$OpenBSD: util.c,v 1.5 2016/03/24 07:03:30 mpi Exp $ */
2
3/*
4 * Copyright (c) 2015 Martin Pieuchot
5 *
6 * Permission to use, copy, modify, and distribute this software for any
7 * purpose with or without fee is hereby granted, provided that the above
8 * copyright notice and this permission notice appear in all copies.
9 *
10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17 */
18
19#include "srp_compat.h"
20
21#include <sys/socket.h>
22#include <sys/domain.h>
23#include <sys/queue.h>
24#include <sys/srp.h>
25
26#include <net/rtable.h>
27#include <net/route.h>
28
29#include <netinet/in.h>
30#include <arpa/inet.h>
31
32#include <assert.h>
33#include <err.h>
34#include <stddef.h>
35#include <stdio.h>
36#include <stdlib.h>
37#include <string.h>
38
39#include "util.h"
40
41struct sockaddr *rt_plen2mask(struct rtentry *, struct sockaddr_in6 *);
42
43struct domain inetdomain = {
44	AF_INET, "inet", NULL, NULL, NULL, NULL, NULL,
45	sizeof(struct sockaddr_in), offsetof(struct sockaddr_in, sin_addr),
46	32,
47};
48
49struct domain inet6domain = {
50	AF_INET6, "inet6", NULL, NULL, NULL, NULL, NULL,
51	sizeof(struct sockaddr_in6), offsetof(struct sockaddr_in6, sin6_addr),
52	128,
53};
54
55struct domain *domains[] = { &inetdomain, &inet6domain, NULL };
56
57/*
58 * Insert a route from a string containing a destination: "192.168.1/24"
59 */
60void
61route_insert(unsigned int rid, sa_family_t af, char *string)
62{
63	struct sockaddr_storage	 ss, ms;
64	struct sockaddr		*ndst, *dst = (struct sockaddr *)&ss;
65	struct sockaddr		*mask = (struct sockaddr *)&ms;
66	struct rtentry		*rt, *nrt;
67	char			 ip[INET6_ADDRSTRLEN];
68	int			 plen, error;
69
70	rt = calloc(1, sizeof(*rt));
71	if (rt == NULL)
72		errx(1, "out of memory");
73
74	plen = inet_net_ptosa(af, string, dst, mask);
75	if (plen == -1)
76		err(1, "wrong line: %s", string);
77
78	/* Normalize sockaddr a la rtrequest1(9) */
79	ndst = malloc(dst->sa_len);
80	if (ndst == NULL)
81		errx(1, "out of memory");
82	rt_maskedcopy(dst, ndst, mask);
83
84	if ((error = rtable_insert(rid, ndst, mask, NULL, 0, rt)) != 0) {
85		inet_net_satop(af, rt_key(rt), plen, ip, sizeof(ip));
86		errx(1, "can't add route: %s, %s\n", ip, strerror(error));
87	}
88	nrt = rtable_lookup(rid, dst, mask, NULL, RTP_ANY);
89	if (nrt != rt) {
90		inet_net_satop(af, rt_key(rt), plen, ip, sizeof(ip));
91		errx(1, "added route not found: %s\n", ip);
92	}
93}
94
95/*
96 * Delete a route from a string containing a destination: "192.168.1/24"
97 */
98void
99route_delete(unsigned int rid, sa_family_t af, char *string)
100{
101	struct sockaddr_storage	 ss, ms;
102	struct sockaddr		*dst = (struct sockaddr *)&ss;
103	struct sockaddr		*mask = (struct sockaddr *)&ms;
104	struct rtentry		*rt, *nrt;
105	char			 ip[INET6_ADDRSTRLEN];
106	int			 plen, error;
107
108	plen = inet_net_ptosa(af, string, dst, mask);
109	if (plen == -1)
110		err(1, "wrong line: %s", string);
111
112	rt = rtable_lookup(0, dst, mask, NULL, RTP_ANY);
113	if (rt == NULL) {
114		inet_net_satop(af, dst, plen, ip, sizeof(ip));
115		errx(1, "can't find route: %s\n", ip);
116	}
117
118	assert(memcmp(rt_key(rt), dst, dst->sa_len) == 0);
119	assert(rt_plen(rt) == rtable_satoplen(af, mask));
120
121	if ((error = rtable_delete(0, dst, mask, rt)) != 0) {
122		inet_net_satop(af, dst, plen, ip, sizeof(ip));
123		errx(1, "can't rm route: %s, %s\n", ip, strerror(error));
124	}
125
126	nrt = rtable_lookup(0, dst, mask, NULL, RTP_ANY);
127	if (nrt != NULL) {
128		char ip0[INET6_ADDRSTRLEN];
129		inet_net_satop(af, rt_key(nrt), plen, ip, sizeof(ip));
130		inet_net_satop(af, rt_key(rt), plen, ip0, sizeof(ip0));
131		errx(1, "found: %s after deleting: %s", ip, ip0);
132	}
133
134	free(rt_key(rt));
135	free(rt);
136}
137
138/*
139 * Lookup a route from a string containing a destination: "192.168.1/24"
140 */
141void
142route_lookup(unsigned int rid, sa_family_t af, char *string)
143{
144	struct sockaddr_storage	 ss, ms;
145	struct sockaddr		*dst = (struct sockaddr *)&ss;
146	struct sockaddr		*mask = (struct sockaddr *)&ms;
147	struct rtentry		*rt;
148	char			 ip[INET6_ADDRSTRLEN];
149	int			 plen;
150
151	plen = inet_net_ptosa(af, string, dst, mask);
152	if (plen == -1)
153		err(1, "wrong line: %s", string);
154
155	rt = rtable_lookup(0, dst, mask, NULL, RTP_ANY);
156	if (rt == NULL) {
157		inet_net_satop(af, dst, plen, ip, sizeof(ip));
158		errx(1, "%s not found\n", ip);
159	}
160	assert(memcmp(rt_key(rt), dst, dst->sa_len) == 0);
161	assert(rt_plen(rt) == rtable_satoplen(af, mask));
162}
163
164int
165do_from_file(unsigned int rid, sa_family_t af, char *filename,
166    void (*func)(unsigned int, sa_family_t, char *))
167{
168	FILE			*fp;
169	char			*buf;
170	size_t			 len;
171	int			 lines = 0;
172
173	if ((fp = fopen(filename, "r")) == NULL)
174		errx(1, "No such file: %s\n", filename);
175
176	while ((buf = fgetln(fp, &len)) != NULL) {
177		if (buf[len - 1] == '\n')
178			buf[len - 1] = '\0';
179
180		(*func)(rid, af, buf);
181		lines++;
182	}
183	fclose(fp);
184
185	return (lines);
186}
187
188int
189rtentry_dump(struct rtentry *rt, void *w, unsigned int rid)
190{
191	char			 dest[INET6_ADDRSTRLEN];
192	sa_family_t		 af = rt_key(rt)->sa_family;
193
194	inet_net_satop(af, rt_key(rt), rt_plen(rt), dest, sizeof(dest));
195	printf("%s\n", dest);
196
197	return (0);
198}
199
200int
201rtentry_delete(struct rtentry *rt, void *w, unsigned int rid)
202{
203	char			 dest[INET6_ADDRSTRLEN];
204	sa_family_t		 af = rt_key(rt)->sa_family;
205	struct sockaddr_in6	 sa_mask;
206	struct sockaddr		*mask = rt_plen2mask(rt, &sa_mask);
207	int			 error;
208
209	assert(rt_plen(rt) == rtable_satoplen(af, mask));
210
211	if ((error = rtable_delete(0, rt_key(rt), mask, rt)) != 0) {
212		inet_net_satop(af, rt_key(rt), rt_plen(rt), dest, sizeof(dest));
213		errx(1, "can't rm route: %s, %s\n", dest, strerror(error));
214	}
215
216	return (0);
217}
218
219void
220rt_maskedcopy(struct sockaddr *src, struct sockaddr *dst,
221    struct sockaddr *netmask)
222{
223	uint8_t	*cp1 = (uint8_t *)src;
224	uint8_t	*cp2 = (uint8_t *)dst;
225	uint8_t	*cp3 = (uint8_t *)netmask;
226	uint8_t	*cplim = cp2 + *cp3;
227	uint8_t	*cplim2 = cp2 + *cp1;
228
229	*cp2++ = *cp1++; *cp2++ = *cp1++; /* copies sa_len & sa_family */
230	cp3 += 2;
231	if (cplim > cplim2)
232		cplim = cplim2;
233	while (cp2 < cplim)
234		*cp2++ = *cp1++ & *cp3++;
235	if (cp2 < cplim2)
236		memset(cp2, 0, (unsigned int)(cplim2 - cp2));
237}
238
239void
240in_prefixlen2mask(struct in_addr *maskp, int plen)
241{
242	if (plen == 0)
243		maskp->s_addr = 0;
244	else
245		maskp->s_addr = htonl(0xffffffff << (32 - plen));
246}
247
248void
249in6_prefixlen2mask(struct in6_addr *maskp, int len)
250{
251	uint8_t maskarray[8] = {0x80, 0xc0, 0xe0, 0xf0, 0xf8, 0xfc, 0xfe, 0xff};
252	int bytelen, bitlen, i;
253
254	assert(0 <= len && len <= 128);
255
256	memset(maskp, 0, sizeof(*maskp));
257	bytelen = len / 8;
258	bitlen = len % 8;
259	for (i = 0; i < bytelen; i++)
260		maskp->s6_addr[i] = 0xff;
261	/* len == 128 is ok because bitlen == 0 then */
262	if (bitlen)
263		maskp->s6_addr[bytelen] = maskarray[bitlen - 1];
264}
265
266struct sockaddr *
267rt_plentosa(sa_family_t af, int plen, struct sockaddr_in6 *sa_mask)
268{
269	struct sockaddr_in	*sin = (struct sockaddr_in *)sa_mask;
270	struct sockaddr_in6	*sin6 = (struct sockaddr_in6 *)sa_mask;
271
272	assert(plen >= 0 || plen == -1);
273
274	if (plen == -1)
275		return (NULL);
276
277	memset(sa_mask, 0, sizeof(*sa_mask));
278
279	switch (af) {
280	case AF_INET:
281		sin->sin_family = AF_INET;
282		sin->sin_len = sizeof(struct sockaddr_in);
283		in_prefixlen2mask(&sin->sin_addr, plen);
284		break;
285	case AF_INET6:
286		sin6->sin6_family = AF_INET6;
287		sin6->sin6_len = sizeof(struct sockaddr_in6);
288		in6_prefixlen2mask(&sin6->sin6_addr, plen);
289		break;
290	default:
291		return (NULL);
292	}
293
294	return ((struct sockaddr *)sa_mask);
295}
296
297struct sockaddr *
298rt_plen2mask(struct rtentry *rt, struct sockaddr_in6 *sa_mask)
299{
300#ifndef ART
301	return (rt_mask(rt));
302#else
303	return (rt_plentosa(rt_key(rt)->sa_family, rt_plen(rt), sa_mask));
304#endif /* ART */
305}
306
307
308int
309inet_net_ptosa(sa_family_t af, const char *buf, struct sockaddr *sa,
310    struct sockaddr *ma)
311{
312	struct sockaddr_in *sin = (struct sockaddr_in *)sa;
313	struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)sa;
314	int i, plen;
315
316	switch (af) {
317	case AF_INET:
318		memset(sin, 0, sizeof(*sin));
319		sin->sin_family = af;
320		sin->sin_len = sizeof(*sin);
321		plen = inet_net_pton(af, buf, &sin->sin_addr,
322		    sizeof(sin->sin_addr));
323		if (plen == -1 || ma == NULL)
324			break;
325
326		sin = (struct sockaddr_in *)ma;
327		memset(sin, 0, sizeof(*sin));
328		sin->sin_len = sizeof(*sin);
329		sin->sin_family = 0;
330		in_prefixlen2mask(&sin->sin_addr, plen);
331		break;
332	case AF_INET6:
333		memset(sin6, 0, sizeof(*sin6));
334		sin6->sin6_family = af;
335		sin6->sin6_len = sizeof(*sin6);
336		plen = inet_net_pton(af, buf, &sin6->sin6_addr,
337		    sizeof(sin6->sin6_addr));
338		if (plen == -1 || ma == NULL)
339			break;
340
341		sin6 = (struct sockaddr_in6 *)ma;
342		memset(sin6, 0, sizeof(*sin6));
343		sin6->sin6_len = sizeof(*sin6);
344		sin6->sin6_family = 0;
345		for (i = 0; i < plen / 8; i++)
346			sin6->sin6_addr.s6_addr[i] = 0xff;
347		i = plen % 8;
348		if (i)
349			sin6->sin6_addr.s6_addr[plen / 8] = 0xff00 >> i;
350		break;
351	default:
352		plen = -1;
353	}
354
355	return (plen);
356}
357
358/*
359 * Only compare the address fields, we cannot use memcmp(3) because
360 * the radix tree abuses the first fields of the mask sockaddr for
361 * a different purpose.
362 */
363int
364maskcmp(sa_family_t af, struct sockaddr *sa1, struct sockaddr *sa2)
365{
366	struct sockaddr_in *sin1, *sin2;
367	struct sockaddr_in6 *sin61, *sin62;
368	int len;
369
370	switch (af) {
371	case AF_INET:
372		sin1 = (struct sockaddr_in *)sa1;
373		sin2 = (struct sockaddr_in *)sa2;
374		len = sizeof(sin1->sin_addr);
375		return memcmp(&sin1->sin_addr, &sin2->sin_addr, len);
376	case AF_INET6:
377		sin61 = (struct sockaddr_in6 *)sa1;
378		sin62 = (struct sockaddr_in6 *)sa2;
379		len = sizeof(sin61->sin6_addr);
380		return memcmp(&sin61->sin6_addr, &sin62->sin6_addr, len);
381	default:
382		return (-1);
383	}
384}
385
386char *
387inet_net_satop(sa_family_t af, struct sockaddr *sa, int plen, char *buf,
388    size_t len)
389{
390	struct sockaddr_in *sin = (struct sockaddr_in *)sa;
391	struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)sa;
392
393	switch (af) {
394	case AF_INET:
395		return inet_net_ntop(af, &sin->sin_addr, plen, buf, len);
396	case AF_INET6:
397		return inet_net_ntop(af, &sin6->sin6_addr, plen, buf, len);
398	default:
399		return (NULL);
400	}
401}
402