util.c revision 1.11
1/*	$OpenBSD: util.c,v 1.11 2022/06/29 04:49:51 anton 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 * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project.
20 * All rights reserved.
21 *
22 * Redistribution and use in source and binary forms, with or without
23 * modification, are permitted provided that the following conditions
24 * are met:
25 * 1. Redistributions of source code must retain the above copyright
26 *    notice, this list of conditions and the following disclaimer.
27 * 2. Redistributions in binary form must reproduce the above copyright
28 *    notice, this list of conditions and the following disclaimer in the
29 *    documentation and/or other materials provided with the distribution.
30 * 3. Neither the name of the project nor the names of its contributors
31 *    may be used to endorse or promote products derived from this software
32 *    without specific prior written permission.
33 *
34 * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
35 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
36 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
37 * ARE DISCLAIMED.  IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
38 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
39 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
40 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
41 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
42 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
43 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
44 * SUCH DAMAGE.
45 */
46
47#include "srp_compat.h"
48
49#include <sys/socket.h>
50#include <sys/domain.h>
51#include <sys/queue.h>
52#include <sys/srp.h>
53
54#include <net/rtable.h>
55#define _KERNEL
56#include <net/route.h>
57#undef _KERNEL
58
59#include <netinet/in.h>
60#include <arpa/inet.h>
61
62#include <assert.h>
63#include <err.h>
64#include <stddef.h>
65#include <stdio.h>
66#include <stdlib.h>
67#include <string.h>
68
69#include "util.h"
70
71struct sockaddr *rt_plen2mask(struct rtentry *, struct sockaddr_in6 *);
72
73struct domain inetdomain = {
74  .dom_family		= AF_INET,
75  .dom_name		= "inet",
76  .dom_init		= NULL,
77  .dom_externalize	= NULL,
78  .dom_dispose		= NULL,
79  .dom_protosw		= NULL,
80  .dom_protoswNPROTOSW	= NULL,
81  .dom_rtoffset		= offsetof(struct sockaddr_in, sin_addr),
82  .dom_maxplen		= 32,
83  .dom_ifattach		= NULL,
84  .dom_ifdetach		= NULL,
85};
86
87struct domain inet6domain = {
88  .dom_family		= AF_INET6,
89  .dom_name		= "inet6",
90  .dom_init		= NULL,
91  .dom_externalize	= NULL,
92  .dom_dispose		= NULL,
93  .dom_protosw		= NULL,
94  .dom_protoswNPROTOSW	= NULL,
95  .dom_rtoffset		= offsetof(struct sockaddr_in6, sin6_addr),
96  .dom_maxplen		= 128,
97  .dom_ifattach		= NULL,
98  .dom_ifdetach		= NULL,
99};
100
101struct domain *domains[] = { &inetdomain, &inet6domain, NULL };
102
103/*
104 * Insert a route from a string containing a destination: "192.168.1/24"
105 */
106void
107route_insert(unsigned int rid, sa_family_t af, char *string)
108{
109	struct sockaddr_storage	 ss, ms;
110	struct sockaddr		*ndst, *dst = (struct sockaddr *)&ss;
111	struct sockaddr		*mask = (struct sockaddr *)&ms;
112	struct rtentry		*rt, *nrt;
113	char			 ip[INET6_ADDRSTRLEN];
114	int			 plen, error;
115
116	rt = calloc(1, sizeof(*rt));
117	if (rt == NULL)
118		errx(1, "out of memory");
119
120	plen = inet_net_ptosa(af, string, dst, mask);
121	if (plen == -1)
122		err(1, "wrong line: %s", string);
123
124	/* Normalize sockaddr a la rtrequest1(9) */
125	ndst = malloc(dst->sa_len);
126	if (ndst == NULL)
127		errx(1, "out of memory");
128	rt_maskedcopy(dst, ndst, mask);
129
130	if ((error = rtable_insert(rid, ndst, mask, NULL, 0, rt)) != 0) {
131		inet_net_satop(af, ndst, plen, ip, sizeof(ip));
132		errx(1, "can't add route: %s, %s\n", ip, strerror(error));
133	}
134	nrt = rtable_lookup(rid, dst, mask, NULL, RTP_ANY);
135	if (nrt != rt) {
136		inet_net_satop(af, rt_key(rt), plen, ip, sizeof(ip));
137		errx(1, "added route not found: %s\n", ip);
138	}
139}
140
141/*
142 * Delete a route from a string containing a destination: "192.168.1/24"
143 */
144void
145route_delete(unsigned int rid, sa_family_t af, char *string)
146{
147	struct sockaddr_storage	 ss, ms;
148	struct sockaddr		*dst = (struct sockaddr *)&ss;
149	struct sockaddr		*mask = (struct sockaddr *)&ms;
150	struct rtentry		*rt, *nrt;
151	char			 ip[INET6_ADDRSTRLEN];
152	int			 plen, error;
153
154	plen = inet_net_ptosa(af, string, dst, mask);
155	if (plen == -1)
156		err(1, "wrong line: %s", string);
157
158	rt = rtable_lookup(0, dst, mask, NULL, RTP_ANY);
159	if (rt == NULL) {
160		inet_net_satop(af, dst, plen, ip, sizeof(ip));
161		errx(1, "can't find route: %s\n", ip);
162	}
163
164	assert(memcmp(rt_key(rt), dst, dst->sa_len) == 0);
165	assert(rt_plen(rt) == rtable_satoplen(af, mask));
166
167	if ((error = rtable_delete(0, dst, mask, rt)) != 0) {
168		inet_net_satop(af, dst, plen, ip, sizeof(ip));
169		errx(1, "can't rm route: %s, %s\n", ip, strerror(error));
170	}
171
172	nrt = rtable_lookup(0, dst, mask, NULL, RTP_ANY);
173	if (nrt != NULL) {
174		char ip0[INET6_ADDRSTRLEN];
175		inet_net_satop(af, rt_key(nrt), plen, ip, sizeof(ip));
176		inet_net_satop(af, rt_key(rt), plen, ip0, sizeof(ip0));
177		errx(1, "found: %s after deleting: %s", ip, ip0);
178	}
179
180	free(rt_key(rt));
181	free(rt);
182}
183
184/*
185 * Lookup a route from a string containing a destination: "192.168.1/24"
186 */
187void
188route_lookup(unsigned int rid, sa_family_t af, char *string)
189{
190	struct sockaddr_storage	 ss, ms;
191	struct sockaddr		*dst = (struct sockaddr *)&ss;
192	struct sockaddr		*mask = (struct sockaddr *)&ms;
193	struct rtentry		*rt;
194	char			 ip[INET6_ADDRSTRLEN];
195	int			 plen;
196
197	plen = inet_net_ptosa(af, string, dst, mask);
198	if (plen == -1)
199		err(1, "wrong line: %s", string);
200
201	rt = rtable_lookup(0, dst, mask, NULL, RTP_ANY);
202	if (rt == NULL) {
203		inet_net_satop(af, dst, plen, ip, sizeof(ip));
204		errx(1, "%s not found\n", ip);
205	}
206	assert(memcmp(rt_key(rt), dst, dst->sa_len) == 0);
207	assert(rt_plen(rt) == rtable_satoplen(af, mask));
208}
209
210int
211do_from_file(unsigned int rid, sa_family_t af, char *filename,
212    void (*func)(unsigned int, sa_family_t, char *))
213{
214	FILE			*fp;
215	char			*buf;
216	size_t			 len;
217	int			 lines = 0;
218
219	if ((fp = fopen(filename, "r")) == NULL)
220		errx(1, "No such file: %s\n", filename);
221
222	while ((buf = fgetln(fp, &len)) != NULL) {
223		if (buf[len - 1] == '\n')
224			buf[len - 1] = '\0';
225
226		(*func)(rid, af, buf);
227		lines++;
228	}
229	fclose(fp);
230
231	return (lines);
232}
233
234int
235rtentry_dump(struct rtentry *rt, void *w, unsigned int rid)
236{
237	char			 dest[INET6_ADDRSTRLEN];
238	sa_family_t		 af = rt_key(rt)->sa_family;
239
240	inet_net_satop(af, rt_key(rt), rt_plen(rt), dest, sizeof(dest));
241	printf("%s\n", dest);
242
243	return (0);
244}
245
246int
247rtentry_delete(struct rtentry *rt, void *w, unsigned int rid)
248{
249	char			 dest[INET6_ADDRSTRLEN];
250	sa_family_t		 af = rt_key(rt)->sa_family;
251	struct sockaddr_in6	 sa_mask;
252	struct sockaddr		*mask = rt_plen2mask(rt, &sa_mask);
253	int			 error;
254
255	assert(rt_plen(rt) == rtable_satoplen(af, mask));
256
257	if ((error = rtable_delete(0, rt_key(rt), mask, rt)) != 0) {
258		inet_net_satop(af, rt_key(rt), rt_plen(rt), dest, sizeof(dest));
259		errx(1, "can't rm route: %s, %s\n", dest, strerror(error));
260	}
261
262	return (0);
263}
264
265void
266rt_maskedcopy(struct sockaddr *src, struct sockaddr *dst,
267    struct sockaddr *netmask)
268{
269	uint8_t	*cp1 = (uint8_t *)src;
270	uint8_t	*cp2 = (uint8_t *)dst;
271	uint8_t	*cp3 = (uint8_t *)netmask;
272	uint8_t	*cplim = cp2 + *cp3;
273	uint8_t	*cplim2 = cp2 + *cp1;
274
275	*cp2++ = *cp1++; *cp2++ = *cp1++; /* copies sa_len & sa_family */
276	cp3 += 2;
277	if (cplim > cplim2)
278		cplim = cplim2;
279	while (cp2 < cplim)
280		*cp2++ = *cp1++ & *cp3++;
281	if (cp2 < cplim2)
282		memset(cp2, 0, (unsigned int)(cplim2 - cp2));
283}
284
285void
286rtref(struct rtentry *rt)
287{
288	rt->rt_refcnt.r_refs++;
289}
290
291void
292rtfree(struct rtentry *rt)
293{
294	assert(--(rt->rt_refcnt.r_refs) >= 0);
295}
296
297void
298in_prefixlen2mask(struct in_addr *maskp, int plen)
299{
300	if (plen == 0)
301		maskp->s_addr = 0;
302	else
303		maskp->s_addr = htonl(0xffffffff << (32 - plen));
304}
305
306void
307in6_prefixlen2mask(struct in6_addr *maskp, int len)
308{
309	uint8_t maskarray[8] = {0x80, 0xc0, 0xe0, 0xf0, 0xf8, 0xfc, 0xfe, 0xff};
310	int bytelen, bitlen, i;
311
312	assert(0 <= len && len <= 128);
313
314	memset(maskp, 0, sizeof(*maskp));
315	bytelen = len / 8;
316	bitlen = len % 8;
317	for (i = 0; i < bytelen; i++)
318		maskp->s6_addr[i] = 0xff;
319	/* len == 128 is ok because bitlen == 0 then */
320	if (bitlen)
321		maskp->s6_addr[bytelen] = maskarray[bitlen - 1];
322}
323
324struct sockaddr *
325rt_plentosa(sa_family_t af, int plen, struct sockaddr_in6 *sa_mask)
326{
327	struct sockaddr_in	*sin = (struct sockaddr_in *)sa_mask;
328	struct sockaddr_in6	*sin6 = (struct sockaddr_in6 *)sa_mask;
329
330	assert(plen >= 0 || plen == -1);
331
332	if (plen == -1)
333		return (NULL);
334
335	memset(sa_mask, 0, sizeof(*sa_mask));
336
337	switch (af) {
338	case AF_INET:
339		sin->sin_family = AF_INET;
340		sin->sin_len = sizeof(struct sockaddr_in);
341		in_prefixlen2mask(&sin->sin_addr, plen);
342		break;
343	case AF_INET6:
344		sin6->sin6_family = AF_INET6;
345		sin6->sin6_len = sizeof(struct sockaddr_in6);
346		in6_prefixlen2mask(&sin6->sin6_addr, plen);
347		break;
348	default:
349		return (NULL);
350	}
351
352	return ((struct sockaddr *)sa_mask);
353}
354
355struct sockaddr *
356rt_plen2mask(struct rtentry *rt, struct sockaddr_in6 *sa_mask)
357{
358	return (rt_plentosa(rt_key(rt)->sa_family, rt_plen(rt), sa_mask));
359}
360
361
362int
363inet_net_ptosa(sa_family_t af, const char *buf, struct sockaddr *sa,
364    struct sockaddr *ma)
365{
366	struct sockaddr_in *sin = (struct sockaddr_in *)sa;
367	struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)sa;
368	int i, plen;
369
370	switch (af) {
371	case AF_INET:
372		memset(sin, 0, sizeof(*sin));
373		sin->sin_family = af;
374		sin->sin_len = sizeof(*sin);
375		plen = inet_net_pton(af, buf, &sin->sin_addr,
376		    sizeof(sin->sin_addr));
377		if (plen == -1 || ma == NULL)
378			break;
379
380		sin = (struct sockaddr_in *)ma;
381		memset(sin, 0, sizeof(*sin));
382		sin->sin_len = sizeof(*sin);
383		sin->sin_family = 0;
384		in_prefixlen2mask(&sin->sin_addr, plen);
385		break;
386	case AF_INET6:
387		memset(sin6, 0, sizeof(*sin6));
388		sin6->sin6_family = af;
389		sin6->sin6_len = sizeof(*sin6);
390		plen = inet_net_pton(af, buf, &sin6->sin6_addr,
391		    sizeof(sin6->sin6_addr));
392		if (plen == -1 || ma == NULL)
393			break;
394
395		sin6 = (struct sockaddr_in6 *)ma;
396		memset(sin6, 0, sizeof(*sin6));
397		sin6->sin6_len = sizeof(*sin6);
398		sin6->sin6_family = 0;
399		for (i = 0; i < plen / 8; i++)
400			sin6->sin6_addr.s6_addr[i] = 0xff;
401		i = plen % 8;
402		if (i)
403			sin6->sin6_addr.s6_addr[plen / 8] = 0xff00 >> i;
404		break;
405	default:
406		plen = -1;
407	}
408
409	return (plen);
410}
411
412/*
413 * Only compare the address fields, we cannot use memcmp(3) because
414 * the radix tree abuses the first fields of the mask sockaddr for
415 * a different purpose.
416 */
417int
418maskcmp(sa_family_t af, struct sockaddr *sa1, struct sockaddr *sa2)
419{
420	struct sockaddr_in *sin1, *sin2;
421	struct sockaddr_in6 *sin61, *sin62;
422	int len;
423
424	switch (af) {
425	case AF_INET:
426		sin1 = (struct sockaddr_in *)sa1;
427		sin2 = (struct sockaddr_in *)sa2;
428		len = sizeof(sin1->sin_addr);
429		return memcmp(&sin1->sin_addr, &sin2->sin_addr, len);
430	case AF_INET6:
431		sin61 = (struct sockaddr_in6 *)sa1;
432		sin62 = (struct sockaddr_in6 *)sa2;
433		len = sizeof(sin61->sin6_addr);
434		return memcmp(&sin61->sin6_addr, &sin62->sin6_addr, len);
435	default:
436		return (-1);
437	}
438}
439
440char *
441inet_net_satop(sa_family_t af, struct sockaddr *sa, int plen, char *buf,
442    size_t len)
443{
444	struct sockaddr_in *sin = (struct sockaddr_in *)sa;
445	struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)sa;
446
447	switch (af) {
448	case AF_INET:
449		return inet_net_ntop(af, &sin->sin_addr, plen, buf, len);
450	case AF_INET6:
451		return inet_net_ntop(af, &sin6->sin6_addr, plen, buf, len);
452	default:
453		return (NULL);
454	}
455}
456
457/* Give some jitter to hash, to avoid synchronization between routers. */
458static uint32_t		rt_hashjitter;
459
460/*
461 * Originated from bridge_hash() in if_bridge.c
462 */
463#define mix(a, b, c) do {						\
464	a -= b; a -= c; a ^= (c >> 13);					\
465	b -= c; b -= a; b ^= (a << 8);					\
466	c -= a; c -= b; c ^= (b >> 13);					\
467	a -= b; a -= c; a ^= (c >> 12);					\
468	b -= c; b -= a; b ^= (a << 16);					\
469	c -= a; c -= b; c ^= (b >> 5);					\
470	a -= b; a -= c; a ^= (c >> 3);					\
471	b -= c; b -= a; b ^= (a << 10);					\
472	c -= a; c -= b; c ^= (b >> 15);					\
473} while (0)
474
475int
476rt_hash(struct rtentry *rt, struct sockaddr *dst, uint32_t *src)
477{
478	uint32_t a, b, c;
479
480	while (rt_hashjitter == 0)
481		rt_hashjitter = arc4random();
482
483	if (src == NULL)
484		return (-1);
485
486	a = b = 0x9e3779b9;
487	c = rt_hashjitter;
488
489	switch (dst->sa_family) {
490	case AF_INET:
491	    {
492		struct sockaddr_in *sin;
493
494		sin = satosin(dst);
495		a += sin->sin_addr.s_addr;
496		b += (src != NULL) ? src[0] : 0;
497		mix(a, b, c);
498		break;
499	    }
500#ifdef INET6
501	case AF_INET6:
502	    {
503		struct sockaddr_in6 *sin6;
504
505		sin6 = satosin6(dst);
506		a += sin6->sin6_addr.s6_addr32[0];
507		b += sin6->sin6_addr.s6_addr32[2];
508		c += (src != NULL) ? src[0] : 0;
509		mix(a, b, c);
510		a += sin6->sin6_addr.s6_addr32[1];
511		b += sin6->sin6_addr.s6_addr32[3];
512		c += (src != NULL) ? src[1] : 0;
513		mix(a, b, c);
514		a += sin6->sin6_addr.s6_addr32[2];
515		b += sin6->sin6_addr.s6_addr32[1];
516		c += (src != NULL) ? src[2] : 0;
517		mix(a, b, c);
518		a += sin6->sin6_addr.s6_addr32[3];
519		b += sin6->sin6_addr.s6_addr32[0];
520		c += (src != NULL) ? src[3] : 0;
521		mix(a, b, c);
522		break;
523	    }
524#endif /* INET6 */
525	}
526
527	return (c & 0xffff);
528}
529
530void
531rt_timer_init(void)
532{
533}
534