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