1/*	$OpenBSD: util.c,v 1.12 2022/12/28 21:30:16 jmc Exp $ */
2
3/*
4 * Copyright (c) 2015 Renato Westphal <renato@openbsd.org>
5 * Copyright (c) 2012 Alexander Bluhm <bluhm@openbsd.org>
6 * Copyright (c) 2004 Esben Norby <norby@openbsd.org>
7 * Copyright (c) 2003, 2004 Henning Brauer <henning@openbsd.org>
8 *
9 * Permission to use, copy, modify, and distribute this software for any
10 * purpose with or without fee is hereby granted, provided that the above
11 * copyright notice and this permission notice appear in all copies.
12 *
13 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
14 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
15 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
16 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
17 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
18 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
19 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
20 */
21
22#include <sys/types.h>
23
24#include <string.h>
25
26#include "eigrpd.h"
27#include "log.h"
28
29uint8_t
30mask2prefixlen(in_addr_t ina)
31{
32	if (ina == 0)
33		return (0);
34	else
35		return (33 - ffs(ntohl(ina)));
36}
37
38uint8_t
39mask2prefixlen6(struct sockaddr_in6 *sa_in6)
40{
41	unsigned int l = 0;
42	uint8_t *ap, *ep;
43
44	/*
45	 * sin6_len is the size of the sockaddr so subtract the offset of
46	 * the possibly truncated sin6_addr struct.
47	 */
48	ap = (uint8_t *)&sa_in6->sin6_addr;
49	ep = (uint8_t *)sa_in6 + sa_in6->sin6_len;
50	for (; ap < ep; ap++) {
51		/* this "beauty" is adopted from sbin/route/show.c ... */
52		switch (*ap) {
53		case 0xff:
54			l += 8;
55			break;
56		case 0xfe:
57			l += 7;
58			goto done;
59		case 0xfc:
60			l += 6;
61			goto done;
62		case 0xf8:
63			l += 5;
64			goto done;
65		case 0xf0:
66			l += 4;
67			goto done;
68		case 0xe0:
69			l += 3;
70			goto done;
71		case 0xc0:
72			l += 2;
73			goto done;
74		case 0x80:
75			l += 1;
76			goto done;
77		case 0x00:
78			goto done;
79		default:
80			fatalx("non contiguous inet6 netmask");
81		}
82	}
83
84done:
85	if (l > sizeof(struct in6_addr) * 8)
86		fatalx("inet6 prefixlen out of bound");
87	return (l);
88}
89
90in_addr_t
91prefixlen2mask(uint8_t prefixlen)
92{
93	if (prefixlen == 0)
94		return (0);
95
96	return (htonl(0xffffffff << (32 - prefixlen)));
97}
98
99struct in6_addr *
100prefixlen2mask6(uint8_t prefixlen)
101{
102	static struct in6_addr	mask;
103	int			i;
104
105	memset(&mask, 0, sizeof(mask));
106	for (i = 0; i < prefixlen / 8; i++)
107		mask.s6_addr[i] = 0xff;
108	i = prefixlen % 8;
109	if (i)
110		mask.s6_addr[prefixlen / 8] = 0xff00 >> i;
111
112	return (&mask);
113}
114
115void
116eigrp_applymask(int af, union eigrpd_addr *dest, const union eigrpd_addr *src,
117    int prefixlen)
118{
119	struct in6_addr	mask;
120	int		i;
121
122	switch (af) {
123	case AF_INET:
124		dest->v4.s_addr = src->v4.s_addr & prefixlen2mask(prefixlen);
125		break;
126	case AF_INET6:
127		memset(&mask, 0, sizeof(mask));
128		for (i = 0; i < prefixlen / 8; i++)
129			mask.s6_addr[i] = 0xff;
130		i = prefixlen % 8;
131		if (i)
132			mask.s6_addr[prefixlen / 8] = 0xff00 >> i;
133
134		for (i = 0; i < 16; i++)
135			dest->v6.s6_addr[i] = src->v6.s6_addr[i] &
136			    mask.s6_addr[i];
137		break;
138	default:
139		fatalx("eigrp_applymask: unknown af");
140	}
141}
142
143int
144eigrp_addrcmp(int af, const union eigrpd_addr *a, const union eigrpd_addr *b)
145{
146	switch (af) {
147	case AF_INET:
148		if (a->v4.s_addr == b->v4.s_addr)
149			return (0);
150		return ((ntohl(a->v4.s_addr) > ntohl(b->v4.s_addr)) ? 1 : -1);
151	case AF_INET6:
152		return (!!memcmp(&a->v6, &b->v6, sizeof(struct in6_addr)));
153	default:
154		fatalx("eigrp_addrcmp: unknown af");
155	}
156}
157
158int
159eigrp_addrisset(int af, const union eigrpd_addr *addr)
160{
161	switch (af) {
162	case AF_UNSPEC:
163		return (0);
164	case AF_INET:
165		if (addr->v4.s_addr != INADDR_ANY)
166			return (1);
167		break;
168	case AF_INET6:
169		if (!IN6_IS_ADDR_UNSPECIFIED(&addr->v6))
170			return (1);
171		break;
172	default:
173		fatalx("eigrp_addrisset: unknown af");
174	}
175
176	return (0);
177}
178
179int
180eigrp_prefixcmp(int af, const union eigrpd_addr *a, const union eigrpd_addr *b,
181    uint8_t prefixlen)
182{
183	in_addr_t	mask, aa, ba;
184	int		i;
185	uint8_t		m;
186
187	switch (af) {
188	case AF_INET:
189		if (prefixlen == 0)
190			return (0);
191		if (prefixlen > 32)
192			fatalx("eigrp_prefixcmp: bad IPv4 prefixlen");
193		mask = htonl(prefixlen2mask(prefixlen));
194		aa = htonl(a->v4.s_addr) & mask;
195		ba = htonl(b->v4.s_addr) & mask;
196		return (aa - ba);
197	case AF_INET6:
198		if (prefixlen == 0)
199			return (0);
200		if (prefixlen > 128)
201			fatalx("eigrp_prefixcmp: bad IPv6 prefixlen");
202		for (i = 0; i < prefixlen / 8; i++)
203			if (a->v6.s6_addr[i] != b->v6.s6_addr[i])
204				return (a->v6.s6_addr[i] - b->v6.s6_addr[i]);
205		i = prefixlen % 8;
206		if (i) {
207			m = 0xff00 >> i;
208			if ((a->v6.s6_addr[prefixlen / 8] & m) !=
209			    (b->v6.s6_addr[prefixlen / 8] & m))
210				return ((a->v6.s6_addr[prefixlen / 8] & m) -
211				    (b->v6.s6_addr[prefixlen / 8] & m));
212		}
213		return (0);
214	default:
215		fatalx("eigrp_prefixcmp: unknown af");
216	}
217	return (-1);
218}
219
220int
221bad_addr_v4(struct in_addr addr)
222{
223	uint32_t	 a = ntohl(addr.s_addr);
224
225	if (((a >> IN_CLASSA_NSHIFT) == 0) ||
226	    ((a >> IN_CLASSA_NSHIFT) == IN_LOOPBACKNET) ||
227	    IN_MULTICAST(a))
228		return (1);
229
230	return (0);
231}
232
233int
234bad_addr_v6(struct in6_addr *addr)
235{
236	if (IN6_IS_ADDR_UNSPECIFIED(addr) ||
237	    IN6_IS_ADDR_LOOPBACK(addr) ||
238	    IN6_IS_ADDR_MULTICAST(addr) ||
239	    IN6_IS_ADDR_SITELOCAL(addr) ||
240	    IN6_IS_ADDR_V4MAPPED(addr) ||
241	    IN6_IS_ADDR_V4COMPAT(addr))
242		return (1);
243
244	return (0);
245}
246
247int
248bad_addr(int af, union eigrpd_addr *addr)
249{
250	switch (af) {
251	case AF_INET:
252		return (bad_addr_v4(addr->v4));
253	case AF_INET6:
254		return (bad_addr_v6(&addr->v6));
255	default:
256		fatalx("bad_addr: unknown af");
257	}
258}
259
260void
261embedscope(struct sockaddr_in6 *sin6)
262{
263	uint16_t	 tmp16;
264
265	if (IN6_IS_SCOPE_EMBED(&sin6->sin6_addr)) {
266		memcpy(&tmp16, &sin6->sin6_addr.s6_addr[2], sizeof(tmp16));
267		if (tmp16 != 0) {
268			log_warnx("%s: address %s already has embedded scope %u",
269			    __func__, log_sockaddr(sin6), ntohs(tmp16));
270		}
271		tmp16 = htons(sin6->sin6_scope_id);
272		memcpy(&sin6->sin6_addr.s6_addr[2], &tmp16, sizeof(tmp16));
273		sin6->sin6_scope_id = 0;
274	}
275}
276
277void
278recoverscope(struct sockaddr_in6 *sin6)
279{
280	uint16_t	 tmp16;
281
282	if (sin6->sin6_scope_id != 0)
283		log_warnx("%s: address %s already has scope id %u",
284		    __func__, log_sockaddr(sin6), sin6->sin6_scope_id);
285
286	if (IN6_IS_SCOPE_EMBED(&sin6->sin6_addr)) {
287		memcpy(&tmp16, &sin6->sin6_addr.s6_addr[2], sizeof(tmp16));
288		sin6->sin6_scope_id = ntohs(tmp16);
289		sin6->sin6_addr.s6_addr[2] = 0;
290		sin6->sin6_addr.s6_addr[3] = 0;
291	}
292}
293
294void
295addscope(struct sockaddr_in6 *sin6, uint32_t id)
296{
297	if (sin6->sin6_scope_id != 0)
298		log_warnx("%s: address %s already has scope id %u", __func__,
299		    log_sockaddr(sin6), sin6->sin6_scope_id);
300
301	if (IN6_IS_SCOPE_EMBED(&sin6->sin6_addr))
302		sin6->sin6_scope_id = id;
303}
304
305void
306clearscope(struct in6_addr *in6)
307{
308	if (IN6_IS_SCOPE_EMBED(in6)) {
309		in6->s6_addr[2] = 0;
310		in6->s6_addr[3] = 0;
311	}
312}
313
314void
315sa2addr(struct sockaddr *sa, int *af, union eigrpd_addr *addr)
316{
317	struct sockaddr_in		*sa_in = (struct sockaddr_in *)sa;
318	struct sockaddr_in6		*sa_in6 = (struct sockaddr_in6 *)sa;
319
320	memset(addr, 0, sizeof(*addr));
321	switch (sa->sa_family) {
322	case AF_INET:
323		*af = AF_INET;
324		addr->v4 = sa_in->sin_addr;
325		break;
326	case AF_INET6:
327		*af = AF_INET6;
328		addr->v6 = sa_in6->sin6_addr;
329		break;
330	default:
331		fatalx("sa2addr: unknown af");
332	}
333}
334