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
32 void
embedscope(struct sockaddr_in6 * sin6)33 embedscope(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
49 void
recoverscope(struct sockaddr_in6 * sin6)50 recoverscope(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
67 void
addscope(struct sockaddr_in6 * sin6,u_int32_t id)68 addscope(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
80 void
clearscope(struct in6_addr * in6)81 clearscope(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
91 u_int8_t
mask2prefixlen(struct sockaddr_in6 * sa_in6)92 mask2prefixlen(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
137 done:
138 if (l > sizeof(struct in6_addr) * 8)
139 fatalx("%s: prefixlen %d out of bound", __func__, l);
140 return (l);
141 }
142
143 struct in6_addr *
prefixlen2mask(u_int8_t prefixlen)144 prefixlen2mask(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
159 void
inet6applymask(struct in6_addr * dest,const struct in6_addr * src,int prefixlen)160 inet6applymask(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