xref: /dragonfly/contrib/dhcpcd/src/sa.c (revision 6a6d63c5317abf314a78f8c8300ef73c2bc0c39e)
1 /* SPDX-License-Identifier: BSD-2-Clause */
2 /*
3  * Socket Address handling for dhcpcd
4  * Copyright (c) 2015-2023 Roy Marples <roy@marples.name>
5  * All rights reserved
6 
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  * 1. Redistributions of source code must retain the above copyright
11  *    notice, this list of conditions and the following disclaimer.
12  * 2. Redistributions in binary form must reproduce the above copyright
13  *    notice, this list of conditions and the following disclaimer in the
14  *    documentation and/or other materials provided with the distribution.
15  *
16  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
17  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
20  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26  * SUCH DAMAGE.
27  */
28 
29 #include <sys/socket.h>
30 #include <sys/types.h>
31 
32 #include <arpa/inet.h>
33 #ifdef AF_LINK
34 #include <net/if_dl.h>
35 #elif defined(AF_PACKET)
36 #include <linux/if_packet.h>
37 #endif
38 
39 #include <assert.h>
40 #include <errno.h>
41 #include <stdbool.h>
42 #include <stddef.h>
43 #include <stdio.h>
44 #include <stdint.h>
45 #include <string.h>
46 
47 #include "config.h"
48 #include "common.h"
49 #include "sa.h"
50 
51 #ifndef NDEBUG
52 static bool sa_inprefix;
53 #endif
54 
55 socklen_t
sa_addroffset(const struct sockaddr * sa)56 sa_addroffset(const struct sockaddr *sa)
57 {
58 
59           assert(sa != NULL);
60           switch(sa->sa_family) {
61 #ifdef INET
62           case AF_INET:
63                     return offsetof(struct sockaddr_in, sin_addr) +
64                            offsetof(struct in_addr, s_addr);
65 #endif /* INET */
66 #ifdef INET6
67           case AF_INET6:
68                     return offsetof(struct sockaddr_in6, sin6_addr) +
69                            offsetof(struct in6_addr, s6_addr);
70 #endif /* INET6 */
71           default:
72                     errno = EAFNOSUPPORT;
73                     return 0;
74           }
75 }
76 
77 socklen_t
sa_addrlen(const struct sockaddr * sa)78 sa_addrlen(const struct sockaddr *sa)
79 {
80 #define membersize(type, member) sizeof(((type *)0)->member)
81           assert(sa != NULL);
82           switch(sa->sa_family) {
83 #ifdef INET
84           case AF_INET:
85                     return membersize(struct in_addr, s_addr);
86 #endif /* INET */
87 #ifdef INET6
88           case AF_INET6:
89                     return membersize(struct in6_addr, s6_addr);
90 #endif /* INET6 */
91           default:
92                     errno = EAFNOSUPPORT;
93                     return 0;
94           }
95 }
96 
97 #ifndef HAVE_SA_LEN
98 socklen_t
sa_len(const struct sockaddr * sa)99 sa_len(const struct sockaddr *sa)
100 {
101 
102           switch (sa->sa_family) {
103 #ifdef AF_LINK
104           case AF_LINK:
105                     return sizeof(struct sockaddr_dl);
106 #endif
107 #ifdef AF_PACKET
108           case AF_PACKET:
109                     return sizeof(struct sockaddr_ll);
110 #endif
111           case AF_INET:
112                     return sizeof(struct sockaddr_in);
113           case AF_INET6:
114                     return sizeof(struct sockaddr_in6);
115           default:
116                     return sizeof(struct sockaddr);
117           }
118 }
119 #endif
120 
121 bool
sa_is_unspecified(const struct sockaddr * sa)122 sa_is_unspecified(const struct sockaddr *sa)
123 {
124 
125           assert(sa != NULL);
126           switch(sa->sa_family) {
127           case AF_UNSPEC:
128                     return true;
129 #ifdef INET
130           case AF_INET:
131                     return satocsin(sa)->sin_addr.s_addr == INADDR_ANY;
132 #endif /* INET */
133 #ifdef INET6
134           case AF_INET6:
135                     return IN6_IS_ADDR_UNSPECIFIED(&satocsin6(sa)->sin6_addr);
136 #endif /* INET6 */
137           default:
138                     errno = EAFNOSUPPORT;
139                     return false;
140           }
141 }
142 
143 #ifdef INET6
144 #ifndef IN6MASK128
145 #define IN6MASK128 {{{ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, \
146                            0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }}}
147 #endif
148 static const struct in6_addr in6allones = IN6MASK128;
149 #endif
150 
151 bool
sa_is_allones(const struct sockaddr * sa)152 sa_is_allones(const struct sockaddr *sa)
153 {
154 
155           assert(sa != NULL);
156           switch(sa->sa_family) {
157           case AF_UNSPEC:
158                     return false;
159 #ifdef INET
160           case AF_INET:
161           {
162                     const struct sockaddr_in *sin;
163 
164                     sin = satocsin(sa);
165                     return sin->sin_addr.s_addr == INADDR_BROADCAST;
166           }
167 #endif /* INET */
168 #ifdef INET6
169           case AF_INET6:
170           {
171                     const struct sockaddr_in6 *sin6;
172 
173                     sin6 = satocsin6(sa);
174                     return IN6_ARE_ADDR_EQUAL(&sin6->sin6_addr, &in6allones);
175           }
176 #endif /* INET6 */
177           default:
178                     errno = EAFNOSUPPORT;
179                     return false;
180           }
181 }
182 
183 bool
sa_is_loopback(const struct sockaddr * sa)184 sa_is_loopback(const struct sockaddr *sa)
185 {
186 
187           assert(sa != NULL);
188           switch(sa->sa_family) {
189           case AF_UNSPEC:
190                     return false;
191 #ifdef INET
192           case AF_INET:
193           {
194                     const struct sockaddr_in *sin;
195 
196                     sin = satocsin(sa);
197                     return sin->sin_addr.s_addr == htonl(INADDR_LOOPBACK);
198           }
199 #endif /* INET */
200 #ifdef INET6
201           case AF_INET6:
202           {
203                     const struct sockaddr_in6 *sin6;
204 
205                     sin6 = satocsin6(sa);
206                     return IN6_IS_ADDR_LOOPBACK(&sin6->sin6_addr);
207           }
208 #endif /* INET6 */
209           default:
210                     errno = EAFNOSUPPORT;
211                     return false;
212           }
213 }
214 
215 int
sa_toprefix(const struct sockaddr * sa)216 sa_toprefix(const struct sockaddr *sa)
217 {
218           int prefix;
219 
220           assert(sa != NULL);
221           switch(sa->sa_family) {
222 #ifdef INET
223           case AF_INET:
224           {
225                     const struct sockaddr_in *sin;
226                     uint32_t mask;
227 
228                     sin = satocsin(sa);
229                     if (sin->sin_addr.s_addr == INADDR_ANY) {
230                               prefix = 0;
231                               break;
232                     }
233                     mask = ntohl(sin->sin_addr.s_addr);
234                     prefix = 33 - ffs((int)mask); /* 33 - (1 .. 32) -> 32 .. 1 */
235                     if (prefix < 32) {            /* more than 1 bit in mask */
236                               /* check for non-contig netmask */
237                               if ((mask^(((1U << prefix)-1) << (32 - prefix))) != 0) {
238                                         errno = EINVAL;
239                                         return -1;          /* noncontig, no pfxlen */
240                               }
241                     }
242                     break;
243           }
244 #endif
245 #ifdef INET6
246           case AF_INET6:
247           {
248                     const struct sockaddr_in6 *sin6;
249                     int x, y;
250                     const uint8_t *lim, *p;
251 
252                     sin6 = satocsin6(sa);
253                     p = (const uint8_t *)sin6->sin6_addr.s6_addr;
254                     lim = p + sizeof(sin6->sin6_addr.s6_addr);
255                     for (x = 0; p < lim; x++, p++) {
256                               if (*p != 0xff)
257                                         break;
258                     }
259                     y = 0;
260                     if (p < lim) {
261                               for (y = 0; y < NBBY; y++) {
262                                         if ((*p & (0x80 >> y)) == 0)
263                                                   break;
264                               }
265                     }
266 
267                     /*
268                      * when the limit pointer is given, do a stricter check on the
269                      * remaining bits.
270                      */
271                     if (p < lim) {
272                               if (y != 0 && (*p & (0x00ff >> y)) != 0)
273                                         return 0;
274                               for (p = p + 1; p < lim; p++)
275                                         if (*p != 0)
276                                                   return 0;
277                     }
278 
279                     prefix = x * NBBY + y;
280                     break;
281           }
282 #endif
283           default:
284                     errno = EAFNOSUPPORT;
285                     return -1;
286           }
287 
288 #ifndef NDEBUG
289           /* Ensure the calculation is correct */
290           if (!sa_inprefix) {
291                     union sa_ss ss = { .sa = { .sa_family = sa->sa_family } };
292 
293                     sa_inprefix = true;
294                     sa_fromprefix(&ss.sa, prefix);
295                     assert(sa_cmp(sa, &ss.sa) == 0);
296                     sa_inprefix = false;
297           }
298 #endif
299 
300           return prefix;
301 }
302 
303 static void
ipbytes_fromprefix(uint8_t * ap,int prefix,int max_prefix)304 ipbytes_fromprefix(uint8_t *ap, int prefix, int max_prefix)
305 {
306           int bytes, bits, i;
307 
308           bytes = prefix / NBBY;
309           bits = prefix % NBBY;
310 
311           for (i = 0; i < bytes; i++)
312                     *ap++ = 0xff;
313           if (bits) {
314                     uint8_t a;
315 
316                     a = 0xff;
317                     a  = (uint8_t)(a << (8 - bits));
318                     *ap++ = a;
319           }
320           bytes = (max_prefix - prefix) / NBBY;
321           for (i = 0; i < bytes; i++)
322                     *ap++ = 0x00;
323 }
324 
325 void
in6_addr_fromprefix(struct in6_addr * addr,int prefix)326 in6_addr_fromprefix(struct in6_addr *addr, int prefix)
327 {
328           ipbytes_fromprefix((uint8_t *)addr, prefix, 128);
329 }
330 
331 int
sa_fromprefix(struct sockaddr * sa,int prefix)332 sa_fromprefix(struct sockaddr *sa, int prefix)
333 {
334           uint8_t *ap;
335           int max_prefix;
336 
337           switch (sa->sa_family) {
338 #ifdef INET
339           case AF_INET:
340                     max_prefix = 32;
341 #ifdef HAVE_SA_LEN
342                     sa->sa_len = sizeof(struct sockaddr_in);
343 #endif
344                     break;
345 #endif
346 #ifdef INET6
347           case AF_INET6:
348                     max_prefix = 128;
349 #ifdef HAVE_SA_LEN
350                     sa->sa_len = sizeof(struct sockaddr_in6);
351 #endif
352                     break;
353 #endif
354           default:
355                     errno = EAFNOSUPPORT;
356                     return -1;
357           }
358 
359           ap = (uint8_t *)sa + sa_addroffset(sa);
360           ipbytes_fromprefix(ap, prefix, max_prefix);
361 
362 #ifndef NDEBUG
363           /* Ensure the calculation is correct */
364           if (!sa_inprefix) {
365                     sa_inprefix = true;
366                     assert(sa_toprefix(sa) == prefix);
367                     sa_inprefix = false;
368           }
369 #endif
370           return 0;
371 }
372 
373 /* inet_ntop, but for sockaddr. */
374 const char *
sa_addrtop(const struct sockaddr * sa,char * buf,socklen_t len)375 sa_addrtop(const struct sockaddr *sa, char *buf, socklen_t len)
376 {
377           const void *addr;
378 
379           assert(buf != NULL);
380           assert(len > 0);
381 
382           if (sa->sa_family == 0) {
383                     *buf = '\0';
384                     return NULL;
385           }
386 
387 #ifdef AF_LINK
388 #ifndef CLLADDR
389 #define CLLADDR(sdl) (const void *)((sdl)->sdl_data + (sdl)->sdl_nlen)
390 #endif
391           if (sa->sa_family == AF_LINK) {
392                     const struct sockaddr_dl *sdl;
393 
394                     sdl = (const void *)sa;
395                     if (sdl->sdl_alen == 0) {
396                               if (snprintf(buf, len, "link#%d", sdl->sdl_index) == -1)
397                                         return NULL;
398                               return buf;
399                     }
400                     return hwaddr_ntoa(CLLADDR(sdl), sdl->sdl_alen, buf, len);
401           }
402 #elif defined(AF_PACKET)
403           if (sa->sa_family == AF_PACKET) {
404                     const struct sockaddr_ll *sll;
405 
406                     sll = (const void *)sa;
407                     return hwaddr_ntoa(sll->sll_addr, sll->sll_halen, buf, len);
408           }
409 #endif
410           addr = (const char *)sa + sa_addroffset(sa);
411           return inet_ntop(sa->sa_family, addr, buf, len);
412 }
413 
414 int
sa_cmp(const struct sockaddr * sa1,const struct sockaddr * sa2)415 sa_cmp(const struct sockaddr *sa1, const struct sockaddr *sa2)
416 {
417           socklen_t offset, len;
418 
419           assert(sa1 != NULL);
420           assert(sa2 != NULL);
421 
422           /* Treat AF_UNSPEC as the unspecified address. */
423           if ((sa1->sa_family == AF_UNSPEC || sa2->sa_family == AF_UNSPEC) &&
424               sa_is_unspecified(sa1) && sa_is_unspecified(sa2))
425                     return 0;
426 
427           if (sa1->sa_family != sa2->sa_family)
428                     return sa1->sa_family - sa2->sa_family;
429 
430 #ifdef HAVE_SA_LEN
431           len = MIN(sa1->sa_len, sa2->sa_len);
432 #endif
433 
434           switch (sa1->sa_family) {
435 #ifdef INET
436           case AF_INET:
437                     offset = offsetof(struct sockaddr_in, sin_addr);
438 #ifdef HAVE_SA_LEN
439                     len -= offset;
440                     len = MIN(len, sizeof(struct in_addr));
441 #else
442                     len = sizeof(struct in_addr);
443 #endif
444                     break;
445 #endif
446 #ifdef INET6
447           case AF_INET6:
448                     offset = offsetof(struct sockaddr_in6, sin6_addr);
449 #ifdef HAVE_SA_LEN
450                     len -= offset;
451                     len = MIN(len, sizeof(struct in6_addr));
452 #else
453                     len = sizeof(struct in6_addr);
454 #endif
455                     break;
456 #endif
457           default:
458                     offset = 0;
459 #ifndef HAVE_SA_LEN
460                     len = sizeof(struct sockaddr);
461 #endif
462                     break;
463           }
464 
465           return memcmp((const char *)sa1 + offset,
466               (const char *)sa2 + offset,
467               len);
468 }
469 
470 #ifdef INET
471 void
sa_in_init(struct sockaddr * sa,const struct in_addr * addr)472 sa_in_init(struct sockaddr *sa, const struct in_addr *addr)
473 {
474           struct sockaddr_in *sin;
475 
476           assert(sa != NULL);
477           assert(addr != NULL);
478           sin = satosin(sa);
479           sin->sin_family = AF_INET;
480 #ifdef HAVE_SA_LEN
481           sin->sin_len = sizeof(*sin);
482 #endif
483           sin->sin_addr.s_addr = addr->s_addr;
484 }
485 #endif
486 
487 #ifdef INET6
488 void
sa_in6_init(struct sockaddr * sa,const struct in6_addr * addr)489 sa_in6_init(struct sockaddr *sa, const struct in6_addr *addr)
490 {
491           struct sockaddr_in6 *sin6;
492 
493           assert(sa != NULL);
494           assert(addr != NULL);
495           sin6 = satosin6(sa);
496           sin6->sin6_family = AF_INET6;
497 #ifdef HAVE_SA_LEN
498           sin6->sin6_len = sizeof(*sin6);
499 #endif
500           memcpy(&sin6->sin6_addr.s6_addr, &addr->s6_addr,
501               sizeof(sin6->sin6_addr.s6_addr));
502 }
503 #endif
504