1/*	$OpenBSD: util.c,v 1.4 2022/12/28 21:30:18 jmc Exp $ */
2
3/*
4 * Copyright (c) 2012 Alexander Bluhm <bluhm@openbsd.org>
5 * Copyright (c) 2004 Esben Norby <norby@openbsd.org>
6 * Copyright (c) 2003, 2004 Henning Brauer <henning@openbsd.org>
7 *
8 * Permission to use, copy, modify, and distribute this software for any
9 * purpose with or without fee is hereby granted, provided that the above
10 * copyright notice and this permission notice appear in all copies.
11 *
12 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
13 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
14 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
15 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
16 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
17 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
18 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
19 */
20
21#include <netinet/in.h>
22#include <string.h>
23
24#include "ospf6d.h"
25#include "log.h"
26
27#define IN6_IS_SCOPE_EMBED(a)   \
28	((IN6_IS_ADDR_LINKLOCAL(a)) ||  \
29	 (IN6_IS_ADDR_MC_LINKLOCAL(a)) || \
30	 (IN6_IS_ADDR_MC_INTFACELOCAL(a)))
31
32void
33embedscope(struct sockaddr_in6 *sin6)
34{
35	u_int16_t	 tmp16;
36
37	if (IN6_IS_SCOPE_EMBED(&sin6->sin6_addr)) {
38		bcopy(&sin6->sin6_addr.s6_addr[2], &tmp16, sizeof(tmp16));
39		if (tmp16 != 0) {
40			log_warnx("embedscope: address %s already has embedded "
41			    "scope %u", log_sockaddr(sin6), ntohs(tmp16));
42		}
43		tmp16 = htons(sin6->sin6_scope_id);
44		bcopy(&tmp16, &sin6->sin6_addr.s6_addr[2], sizeof(tmp16));
45		sin6->sin6_scope_id = 0;
46	}
47}
48
49void
50recoverscope(struct sockaddr_in6 *sin6)
51{
52	u_int16_t	 tmp16;
53
54	if (sin6->sin6_scope_id != 0) {
55		log_warnx("recoverscope: address %s already has scope id %u",
56		    log_sockaddr(sin6), sin6->sin6_scope_id);
57	}
58
59	if (IN6_IS_SCOPE_EMBED(&sin6->sin6_addr)) {
60		bcopy(&sin6->sin6_addr.s6_addr[2], &tmp16, sizeof(tmp16));
61		sin6->sin6_scope_id = ntohs(tmp16);
62		sin6->sin6_addr.s6_addr[2] = 0;
63		sin6->sin6_addr.s6_addr[3] = 0;
64	}
65}
66
67void
68addscope(struct sockaddr_in6 *sin6, u_int32_t id)
69{
70	if (sin6->sin6_scope_id != 0) {
71		log_warnx("addscope: address %s already has scope id %u",
72		    log_sockaddr(sin6), sin6->sin6_scope_id);
73	}
74
75	if (IN6_IS_SCOPE_EMBED(&sin6->sin6_addr)) {
76		sin6->sin6_scope_id = id;
77	}
78}
79
80void
81clearscope(struct in6_addr *in6)
82{
83	if (IN6_IS_SCOPE_EMBED(in6)) {
84		in6->s6_addr[2] = 0;
85		in6->s6_addr[3] = 0;
86	}
87}
88
89#undef IN6_IS_SCOPE_EMBED
90
91u_int8_t
92mask2prefixlen(struct sockaddr_in6 *sa_in6)
93{
94	u_int8_t	*ap, *ep;
95	u_int		 l = 0;
96
97	/*
98	 * sin6_len is the size of the sockaddr so subtract the offset of
99	 * the possibly truncated sin6_addr struct.
100	 */
101	ap = (u_int8_t *)&sa_in6->sin6_addr;
102	ep = (u_int8_t *)sa_in6 + sa_in6->sin6_len;
103	for (; ap < ep; ap++) {
104		/* this "beauty" is adopted from sbin/route/show.c ... */
105		switch (*ap) {
106		case 0xff:
107			l += 8;
108			break;
109		case 0xfe:
110			l += 7;
111			goto done;
112		case 0xfc:
113			l += 6;
114			goto done;
115		case 0xf8:
116			l += 5;
117			goto done;
118		case 0xf0:
119			l += 4;
120			goto done;
121		case 0xe0:
122			l += 3;
123			goto done;
124		case 0xc0:
125			l += 2;
126			goto done;
127		case 0x80:
128			l += 1;
129			goto done;
130		case 0x00:
131			goto done;
132		default:
133			fatalx("non contiguous inet6 netmask");
134		}
135	}
136
137done:
138	if (l > sizeof(struct in6_addr) * 8)
139		fatalx("%s: prefixlen %d out of bound", __func__, l);
140	return (l);
141}
142
143struct in6_addr *
144prefixlen2mask(u_int8_t prefixlen)
145{
146	static struct in6_addr	mask;
147	int			i;
148
149	bzero(&mask, sizeof(mask));
150	for (i = 0; i < prefixlen / 8; i++)
151		mask.s6_addr[i] = 0xff;
152	i = prefixlen % 8;
153	if (i)
154		mask.s6_addr[prefixlen / 8] = 0xff00 >> i;
155
156	return (&mask);
157}
158
159void
160inet6applymask(struct in6_addr *dest, const struct in6_addr *src, int prefixlen)
161{
162	struct in6_addr	mask;
163	int		i;
164
165	bzero(&mask, sizeof(mask));
166	for (i = 0; i < prefixlen / 8; i++)
167		mask.s6_addr[i] = 0xff;
168	i = prefixlen % 8;
169	if (i)
170		mask.s6_addr[prefixlen / 8] = 0xff00 >> i;
171
172	for (i = 0; i < 16; i++)
173		dest->s6_addr[i] = src->s6_addr[i] & mask.s6_addr[i];
174}
175