/* $OpenBSD: util.c,v 1.15 2024/08/23 12:56:26 anton Exp $ */ /* * Copyright (c) 2015 Martin Pieuchot * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ /* * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the project nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #define _KERNEL #include #undef _KERNEL #include "srp_compat.h" #include #include #include #include #include #define _KERNEL #include #undef _KERNEL #include #include #include #include #include #include #include #include #include "util.h" struct sockaddr *rt_plen2mask(struct rtentry *, struct sockaddr_in6 *); struct domain inetdomain = { .dom_family = AF_INET, .dom_name = "inet", .dom_init = NULL, .dom_externalize = NULL, .dom_dispose = NULL, .dom_protosw = NULL, .dom_protoswNPROTOSW = NULL, .dom_rtoffset = offsetof(struct sockaddr_in, sin_addr), .dom_maxplen = 32, }; struct domain inet6domain = { .dom_family = AF_INET6, .dom_name = "inet6", .dom_init = NULL, .dom_externalize = NULL, .dom_dispose = NULL, .dom_protosw = NULL, .dom_protoswNPROTOSW = NULL, .dom_rtoffset = offsetof(struct sockaddr_in6, sin6_addr), .dom_maxplen = 128, }; struct domain *domains[] = { &inetdomain, &inet6domain, NULL }; /* * Insert a route from a string containing a destination: "192.168.1/24" */ void route_insert(unsigned int rid, sa_family_t af, char *string) { struct sockaddr_storage ss, ms; struct sockaddr *ndst, *dst = (struct sockaddr *)&ss; struct sockaddr *mask = (struct sockaddr *)&ms; struct rtentry *rt, *nrt; char ip[INET6_ADDRSTRLEN]; int plen, error; rt = calloc(1, sizeof(*rt)); if (rt == NULL) errx(1, "out of memory"); refcnt_init(&rt->rt_refcnt); plen = inet_net_ptosa(af, string, dst, mask); if (plen == -1) err(1, "wrong line: %s", string); /* Normalize sockaddr a la rtrequest1(9) */ ndst = malloc(dst->sa_len); if (ndst == NULL) errx(1, "out of memory"); rt_maskedcopy(dst, ndst, mask); if ((error = rtable_insert(rid, ndst, mask, NULL, 0, rt)) != 0) { inet_net_satop(af, ndst, plen, ip, sizeof(ip)); errx(1, "can't add route: %s, %s", ip, strerror(error)); } nrt = rtable_lookup(rid, dst, mask, NULL, RTP_ANY); if (nrt != rt) { inet_net_satop(af, rt_key(rt), plen, ip, sizeof(ip)); errx(1, "added route not found: %s", ip); } rtfree(rt); rtfree(nrt); } /* * Delete a route from a string containing a destination: "192.168.1/24" */ void route_delete(unsigned int rid, sa_family_t af, char *string) { struct sockaddr_storage ss, ms; struct sockaddr *dst = (struct sockaddr *)&ss; struct sockaddr *mask = (struct sockaddr *)&ms; struct rtentry *rt, *nrt; char ip[INET6_ADDRSTRLEN]; int plen, error; plen = inet_net_ptosa(af, string, dst, mask); if (plen == -1) err(1, "wrong line: %s", string); rt = rtable_lookup(0, dst, mask, NULL, RTP_ANY); if (rt == NULL) { inet_net_satop(af, dst, plen, ip, sizeof(ip)); errx(1, "can't find route: %s", ip); } assert(memcmp(rt_key(rt), dst, dst->sa_len) == 0); assert(rt_plen(rt) == rtable_satoplen(af, mask)); if ((error = rtable_delete(0, dst, mask, rt)) != 0) { inet_net_satop(af, dst, plen, ip, sizeof(ip)); errx(1, "can't rm route: %s, %s", ip, strerror(error)); } nrt = rtable_lookup(0, dst, mask, NULL, RTP_ANY); if (nrt != NULL) { char ip0[INET6_ADDRSTRLEN]; inet_net_satop(af, rt_key(nrt), plen, ip, sizeof(ip)); inet_net_satop(af, rt_key(rt), plen, ip0, sizeof(ip0)); errx(1, "found: %s after deleting: %s", ip, ip0); } rtfree(rt); assert(refcnt_read(&rt->rt_refcnt) == 0); free(rt_key(rt)); free(rt); } /* * Lookup a route from a string containing a destination: "192.168.1/24" */ void route_lookup(unsigned int rid, sa_family_t af, char *string) { struct sockaddr_storage ss, ms; struct sockaddr *dst = (struct sockaddr *)&ss; struct sockaddr *mask = (struct sockaddr *)&ms; struct rtentry *rt; char ip[INET6_ADDRSTRLEN]; int plen; plen = inet_net_ptosa(af, string, dst, mask); if (plen == -1) err(1, "wrong line: %s", string); rt = rtable_lookup(0, dst, mask, NULL, RTP_ANY); if (rt == NULL) { inet_net_satop(af, dst, plen, ip, sizeof(ip)); errx(1, "%s not found", ip); } assert(memcmp(rt_key(rt), dst, dst->sa_len) == 0); assert(rt_plen(rt) == rtable_satoplen(af, mask)); rtfree(rt); } int do_from_file(unsigned int rid, sa_family_t af, char *filename, void (*func)(unsigned int, sa_family_t, char *)) { FILE *fp; char *buf; size_t len; int lines = 0; if ((fp = fopen(filename, "r")) == NULL) errx(1, "No such file: %s", filename); while ((buf = fgetln(fp, &len)) != NULL) { if (buf[len - 1] == '\n') buf[len - 1] = '\0'; (*func)(rid, af, buf); lines++; } fclose(fp); return (lines); } int rtentry_dump(struct rtentry *rt, void *w, unsigned int rid) { char dest[INET6_ADDRSTRLEN]; sa_family_t af = rt_key(rt)->sa_family; inet_net_satop(af, rt_key(rt), rt_plen(rt), dest, sizeof(dest)); printf("%s\n", dest); return (0); } int rtentry_delete(struct rtentry *rt, void *w, unsigned int rid) { char dest[INET6_ADDRSTRLEN]; sa_family_t af = rt_key(rt)->sa_family; struct sockaddr_in6 sa_mask; struct sockaddr *mask = rt_plen2mask(rt, &sa_mask); int error; assert(rt_plen(rt) == rtable_satoplen(af, mask)); if ((error = rtable_delete(0, rt_key(rt), mask, rt)) != 0) { inet_net_satop(af, rt_key(rt), rt_plen(rt), dest, sizeof(dest)); errx(1, "can't rm route: %s, %s", dest, strerror(error)); } assert(refcnt_read(&rt->rt_refcnt) == 0); return (0); } void rt_maskedcopy(struct sockaddr *src, struct sockaddr *dst, struct sockaddr *netmask) { uint8_t *cp1 = (uint8_t *)src; uint8_t *cp2 = (uint8_t *)dst; uint8_t *cp3 = (uint8_t *)netmask; uint8_t *cplim = cp2 + *cp3; uint8_t *cplim2 = cp2 + *cp1; *cp2++ = *cp1++; *cp2++ = *cp1++; /* copies sa_len & sa_family */ cp3 += 2; if (cplim > cplim2) cplim = cplim2; while (cp2 < cplim) *cp2++ = *cp1++ & *cp3++; if (cp2 < cplim2) memset(cp2, 0, (unsigned int)(cplim2 - cp2)); } void rtref(struct rtentry *rt) { refcnt_take(&rt->rt_refcnt); } void rtfree(struct rtentry *rt) { if (refcnt_rele(&rt->rt_refcnt) == 0) return; } void refcnt_init(struct refcnt *r) { r->r_refs = 1; } void refcnt_take(struct refcnt *r) { u_int refs; refs = ++r->r_refs; assert(refs != 0); } int refcnt_rele(struct refcnt *r) { u_int refs; refs = --r->r_refs; assert(refs != ~0); if (r->r_refs == 0) return (1); return (0); } unsigned int refcnt_read(struct refcnt *r) { return (r->r_refs); } void in_prefixlen2mask(struct in_addr *maskp, int plen) { if (plen == 0) maskp->s_addr = 0; else maskp->s_addr = htonl(0xffffffff << (32 - plen)); } void in6_prefixlen2mask(struct in6_addr *maskp, int len) { uint8_t maskarray[8] = {0x80, 0xc0, 0xe0, 0xf0, 0xf8, 0xfc, 0xfe, 0xff}; int bytelen, bitlen, i; assert(0 <= len && len <= 128); memset(maskp, 0, sizeof(*maskp)); bytelen = len / 8; bitlen = len % 8; for (i = 0; i < bytelen; i++) maskp->s6_addr[i] = 0xff; /* len == 128 is ok because bitlen == 0 then */ if (bitlen) maskp->s6_addr[bytelen] = maskarray[bitlen - 1]; } struct sockaddr * rt_plentosa(sa_family_t af, int plen, struct sockaddr_in6 *sa_mask) { struct sockaddr_in *sin = (struct sockaddr_in *)sa_mask; struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)sa_mask; assert(plen >= 0 || plen == -1); if (plen == -1) return (NULL); memset(sa_mask, 0, sizeof(*sa_mask)); switch (af) { case AF_INET: sin->sin_family = AF_INET; sin->sin_len = sizeof(struct sockaddr_in); in_prefixlen2mask(&sin->sin_addr, plen); break; case AF_INET6: sin6->sin6_family = AF_INET6; sin6->sin6_len = sizeof(struct sockaddr_in6); in6_prefixlen2mask(&sin6->sin6_addr, plen); break; default: return (NULL); } return ((struct sockaddr *)sa_mask); } struct sockaddr * rt_plen2mask(struct rtentry *rt, struct sockaddr_in6 *sa_mask) { return (rt_plentosa(rt_key(rt)->sa_family, rt_plen(rt), sa_mask)); } int inet_net_ptosa(sa_family_t af, const char *buf, struct sockaddr *sa, struct sockaddr *ma) { struct sockaddr_in *sin = (struct sockaddr_in *)sa; struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)sa; int i, plen; switch (af) { case AF_INET: memset(sin, 0, sizeof(*sin)); sin->sin_family = af; sin->sin_len = sizeof(*sin); plen = inet_net_pton(af, buf, &sin->sin_addr, sizeof(sin->sin_addr)); if (plen == -1 || ma == NULL) break; sin = (struct sockaddr_in *)ma; memset(sin, 0, sizeof(*sin)); sin->sin_len = sizeof(*sin); sin->sin_family = 0; in_prefixlen2mask(&sin->sin_addr, plen); break; case AF_INET6: memset(sin6, 0, sizeof(*sin6)); sin6->sin6_family = af; sin6->sin6_len = sizeof(*sin6); plen = inet_net_pton(af, buf, &sin6->sin6_addr, sizeof(sin6->sin6_addr)); if (plen == -1 || ma == NULL) break; sin6 = (struct sockaddr_in6 *)ma; memset(sin6, 0, sizeof(*sin6)); sin6->sin6_len = sizeof(*sin6); sin6->sin6_family = 0; for (i = 0; i < plen / 8; i++) sin6->sin6_addr.s6_addr[i] = 0xff; i = plen % 8; if (i) sin6->sin6_addr.s6_addr[plen / 8] = 0xff00 >> i; break; default: plen = -1; } return (plen); } /* * Only compare the address fields, we cannot use memcmp(3) because * the radix tree abuses the first fields of the mask sockaddr for * a different purpose. */ int maskcmp(sa_family_t af, struct sockaddr *sa1, struct sockaddr *sa2) { struct sockaddr_in *sin1, *sin2; struct sockaddr_in6 *sin61, *sin62; int len; switch (af) { case AF_INET: sin1 = (struct sockaddr_in *)sa1; sin2 = (struct sockaddr_in *)sa2; len = sizeof(sin1->sin_addr); return memcmp(&sin1->sin_addr, &sin2->sin_addr, len); case AF_INET6: sin61 = (struct sockaddr_in6 *)sa1; sin62 = (struct sockaddr_in6 *)sa2; len = sizeof(sin61->sin6_addr); return memcmp(&sin61->sin6_addr, &sin62->sin6_addr, len); default: return (-1); } } char * inet_net_satop(sa_family_t af, struct sockaddr *sa, int plen, char *buf, size_t len) { struct sockaddr_in *sin = (struct sockaddr_in *)sa; struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)sa; switch (af) { case AF_INET: return inet_net_ntop(af, &sin->sin_addr, plen, buf, len); case AF_INET6: return inet_net_ntop(af, &sin6->sin6_addr, plen, buf, len); default: return (NULL); } } /* Give some jitter to hash, to avoid synchronization between routers. */ static uint32_t rt_hashjitter; /* * Originated from bridge_hash() in if_bridge.c */ #define mix(a, b, c) do { \ a -= b; a -= c; a ^= (c >> 13); \ b -= c; b -= a; b ^= (a << 8); \ c -= a; c -= b; c ^= (b >> 13); \ a -= b; a -= c; a ^= (c >> 12); \ b -= c; b -= a; b ^= (a << 16); \ c -= a; c -= b; c ^= (b >> 5); \ a -= b; a -= c; a ^= (c >> 3); \ b -= c; b -= a; b ^= (a << 10); \ c -= a; c -= b; c ^= (b >> 15); \ } while (0) int rt_hash(struct rtentry *rt, const struct sockaddr *dst, uint32_t *src) { uint32_t a, b, c; while (rt_hashjitter == 0) rt_hashjitter = arc4random(); if (src == NULL) return (-1); a = b = 0x9e3779b9; c = rt_hashjitter; switch (dst->sa_family) { case AF_INET: { struct sockaddr_in *sin; sin = satosin(dst); a += sin->sin_addr.s_addr; b += (src != NULL) ? src[0] : 0; mix(a, b, c); break; } #ifdef INET6 case AF_INET6: { struct sockaddr_in6 *sin6; sin6 = satosin6(dst); a += sin6->sin6_addr.s6_addr32[0]; b += sin6->sin6_addr.s6_addr32[2]; c += (src != NULL) ? src[0] : 0; mix(a, b, c); a += sin6->sin6_addr.s6_addr32[1]; b += sin6->sin6_addr.s6_addr32[3]; c += (src != NULL) ? src[1] : 0; mix(a, b, c); a += sin6->sin6_addr.s6_addr32[2]; b += sin6->sin6_addr.s6_addr32[1]; c += (src != NULL) ? src[2] : 0; mix(a, b, c); a += sin6->sin6_addr.s6_addr32[3]; b += sin6->sin6_addr.s6_addr32[0]; c += (src != NULL) ? src[3] : 0; mix(a, b, c); break; } #endif /* INET6 */ } return (c & 0xffff); } void rt_timer_init(void) { }