xref: /openbsd/usr.sbin/ospf6d/util.c (revision 09467b48)
1 /*	$OpenBSD: util.c,v 1.3 2018/12/09 14:56:24 remi 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
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 embeded "
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
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
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
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
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 substract 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 *
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
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