summaryrefslogtreecommitdiffstats
path: root/ldpd/util.c
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--ldpd/util.c382
1 files changed, 382 insertions, 0 deletions
diff --git a/ldpd/util.c b/ldpd/util.c
new file mode 100644
index 0000000..019d846
--- /dev/null
+++ b/ldpd/util.c
@@ -0,0 +1,382 @@
+/* $OpenBSD$ */
+
+/*
+ * Copyright (c) 2015 Renato Westphal <renato@openbsd.org>
+ * Copyright (c) 2012 Alexander Bluhm <bluhm@openbsd.org>
+ * Copyright (c) 2004 Esben Norby <norby@openbsd.org>
+ * Copyright (c) 2003, 2004 Henning Brauer <henning@openbsd.org>
+ *
+ * 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.
+ */
+
+#include <zebra.h>
+
+#include "ldpd.h"
+#include "log.h"
+
+uint8_t
+mask2prefixlen(in_addr_t ina)
+{
+ if (ina == 0)
+ return (0);
+ else
+ return (33 - ffs(ntohl(ina)));
+}
+
+uint8_t
+mask2prefixlen6(struct sockaddr_in6 *sa_in6)
+{
+ uint8_t l = 0, *ap, *ep;
+
+ /*
+ * sin6_len is the size of the sockaddr so substract the offset of
+ * the possibly truncated sin6_addr struct.
+ */
+ ap = (uint8_t *)&sa_in6->sin6_addr;
+ ep = (uint8_t *)sa_in6 + sockaddr_len((struct sockaddr *)sa_in6);
+ for (; ap < ep; ap++) {
+ /* this "beauty" is adopted from sbin/route/show.c ... */
+ switch (*ap) {
+ case 0xff:
+ l += 8;
+ break;
+ case 0xfe:
+ l += 7;
+ return (l);
+ case 0xfc:
+ l += 6;
+ return (l);
+ case 0xf8:
+ l += 5;
+ return (l);
+ case 0xf0:
+ l += 4;
+ return (l);
+ case 0xe0:
+ l += 3;
+ return (l);
+ case 0xc0:
+ l += 2;
+ return (l);
+ case 0x80:
+ l += 1;
+ return (l);
+ case 0x00:
+ return (l);
+ default:
+ fatalx("non contiguous inet6 netmask");
+ }
+ }
+
+ return (l);
+}
+
+in_addr_t
+prefixlen2mask(uint8_t prefixlen)
+{
+ if (prefixlen == 0)
+ return (0);
+
+ return (htonl(0xffffffff << (32 - prefixlen)));
+}
+
+struct in6_addr *
+prefixlen2mask6(uint8_t prefixlen)
+{
+ static struct in6_addr mask;
+ int i;
+
+ memset(&mask, 0, sizeof(mask));
+ for (i = 0; i < prefixlen / 8; i++)
+ mask.s6_addr[i] = 0xff;
+ i = prefixlen % 8;
+ if (i)
+ mask.s6_addr[prefixlen / 8] = 0xff00 >> i;
+
+ return (&mask);
+}
+
+void
+ldp_applymask(int af, union ldpd_addr *dest, const union ldpd_addr *src,
+ int prefixlen)
+{
+ struct in6_addr mask;
+ int i;
+
+ switch (af) {
+ case AF_INET:
+ dest->v4.s_addr = src->v4.s_addr & prefixlen2mask(prefixlen);
+ break;
+ case AF_INET6:
+ memset(&mask, 0, sizeof(mask));
+ for (i = 0; i < prefixlen / 8; i++)
+ mask.s6_addr[i] = 0xff;
+ i = prefixlen % 8;
+ if (i)
+ mask.s6_addr[prefixlen / 8] = 0xff00 >> i;
+
+ for (i = 0; i < 16; i++)
+ dest->v6.s6_addr[i] = src->v6.s6_addr[i] &
+ mask.s6_addr[i];
+ break;
+ default:
+ fatalx("ldp_applymask: unknown af");
+ }
+}
+
+int
+ldp_addrcmp(int af, const union ldpd_addr *a, const union ldpd_addr *b)
+{
+ switch (af) {
+ case AF_INET:
+ if (a->v4.s_addr == b->v4.s_addr)
+ return (0);
+ return ((ntohl(a->v4.s_addr) > ntohl(b->v4.s_addr)) ? 1 : -1);
+ case AF_INET6:
+ return (memcmp(&a->v6, &b->v6, sizeof(struct in6_addr)));
+ default:
+ fatalx("ldp_addrcmp: unknown af");
+ }
+}
+
+int
+ldp_addrisset(int af, const union ldpd_addr *addr)
+{
+ switch (af) {
+ case AF_UNSPEC:
+ return (0);
+ case AF_INET:
+ if (addr->v4.s_addr != INADDR_ANY)
+ return (1);
+ break;
+ case AF_INET6:
+ if (!IN6_IS_ADDR_UNSPECIFIED(&addr->v6))
+ return (1);
+ break;
+ default:
+ fatalx("ldp_addrisset: unknown af");
+ }
+
+ return (0);
+}
+
+int
+ldp_prefixcmp(int af, const union ldpd_addr *a, const union ldpd_addr *b,
+ uint8_t prefixlen)
+{
+ in_addr_t mask, aa, ba;
+ int i;
+ uint8_t m;
+
+ switch (af) {
+ case AF_INET:
+ if (prefixlen == 0)
+ return (0);
+ if (prefixlen > IPV4_MAX_BITLEN)
+ fatalx("ldp_prefixcmp: bad IPv4 prefixlen");
+ mask = htonl(prefixlen2mask(prefixlen));
+ aa = htonl(a->v4.s_addr) & mask;
+ ba = htonl(b->v4.s_addr) & mask;
+ return (aa - ba);
+ case AF_INET6:
+ if (prefixlen == 0)
+ return (0);
+ if (prefixlen > IPV6_MAX_BITLEN)
+ fatalx("ldp_prefixcmp: bad IPv6 prefixlen");
+ for (i = 0; i < prefixlen / 8; i++)
+ if (a->v6.s6_addr[i] != b->v6.s6_addr[i])
+ return (a->v6.s6_addr[i] - b->v6.s6_addr[i]);
+ i = prefixlen % 8;
+ if (i) {
+ m = 0xff00 >> i;
+ if ((a->v6.s6_addr[prefixlen / 8] & m) !=
+ (b->v6.s6_addr[prefixlen / 8] & m))
+ return ((a->v6.s6_addr[prefixlen / 8] & m) -
+ (b->v6.s6_addr[prefixlen / 8] & m));
+ }
+ return (0);
+ default:
+ fatalx("ldp_prefixcmp: unknown af");
+ }
+ return (-1);
+}
+
+int
+bad_addr_v4(struct in_addr addr)
+{
+ uint32_t a = ntohl(addr.s_addr);
+
+ if (((a >> IN_CLASSA_NSHIFT) == 0) ||
+ ((a >> IN_CLASSA_NSHIFT) == IN_LOOPBACKNET) ||
+ IN_MULTICAST(a) || IN_BADCLASS(a))
+ return (1);
+
+ return (0);
+}
+
+int
+bad_addr_v6(struct in6_addr *addr)
+{
+ if (IN6_IS_ADDR_UNSPECIFIED(addr) ||
+ IN6_IS_ADDR_LOOPBACK(addr) ||
+ IN6_IS_ADDR_MULTICAST(addr) ||
+ IN6_IS_ADDR_SITELOCAL(addr) ||
+ IN6_IS_ADDR_V4MAPPED(addr) ||
+ IN6_IS_ADDR_V4COMPAT(addr))
+ return (1);
+
+ return (0);
+}
+
+int
+bad_addr(int af, union ldpd_addr *addr)
+{
+ switch (af) {
+ case AF_INET:
+ return (bad_addr_v4(addr->v4));
+ case AF_INET6:
+ return (bad_addr_v6(&addr->v6));
+ default:
+ fatalx("bad_addr: unknown af");
+ }
+}
+
+void
+embedscope(struct sockaddr_in6 *sin6)
+{
+ uint16_t tmp16;
+
+ if (IN6_IS_SCOPE_EMBED(&sin6->sin6_addr)) {
+ memcpy(&tmp16, &sin6->sin6_addr.s6_addr[2], sizeof(tmp16));
+ if (tmp16 != 0) {
+ log_warnx("%s: address %s already has embedded scope %u",
+ __func__, log_sockaddr(sin6), ntohs(tmp16));
+ }
+ tmp16 = htons(sin6->sin6_scope_id);
+ memcpy(&sin6->sin6_addr.s6_addr[2], &tmp16, sizeof(tmp16));
+ sin6->sin6_scope_id = 0;
+ }
+}
+
+void
+recoverscope(struct sockaddr_in6 *sin6)
+{
+ uint16_t tmp16;
+
+ if (sin6->sin6_scope_id != 0)
+ log_warnx("%s: address %s already has scope id %u",
+ __func__, log_sockaddr(sin6), sin6->sin6_scope_id);
+
+ if (IN6_IS_SCOPE_EMBED(&sin6->sin6_addr)) {
+ memcpy(&tmp16, &sin6->sin6_addr.s6_addr[2], sizeof(tmp16));
+ sin6->sin6_scope_id = ntohs(tmp16);
+ sin6->sin6_addr.s6_addr[2] = 0;
+ sin6->sin6_addr.s6_addr[3] = 0;
+ }
+}
+
+void
+addscope(struct sockaddr_in6 *sin6, uint32_t id)
+{
+ if (sin6->sin6_scope_id != 0)
+ log_warnx("%s: address %s already has scope id %u", __func__,
+ log_sockaddr(sin6), sin6->sin6_scope_id);
+
+ if (IN6_IS_SCOPE_EMBED(&sin6->sin6_addr))
+ sin6->sin6_scope_id = id;
+}
+
+void
+clearscope(struct in6_addr *in6)
+{
+ if (IN6_IS_SCOPE_EMBED(in6)) {
+ in6->s6_addr[2] = 0;
+ in6->s6_addr[3] = 0;
+ }
+}
+
+void
+addr2sa(int af, const union ldpd_addr *addr, uint16_t port, union sockunion *su)
+{
+ struct sockaddr_in *sa_in = &su->sin;
+ struct sockaddr_in6 *sa_in6 = &su->sin6;
+
+ memset(su, 0, sizeof(*su));
+ switch (af) {
+ case AF_INET:
+ sa_in->sin_family = AF_INET;
+#ifdef HAVE_STRUCT_SOCKADDR_IN_SIN_LEN
+ sa_in->sin_len = sizeof(struct sockaddr_in);
+#endif
+ sa_in->sin_addr = addr->v4;
+ sa_in->sin_port = htons(port);
+ break;
+ case AF_INET6:
+ sa_in6->sin6_family = AF_INET6;
+#ifdef HAVE_STRUCT_SOCKADDR_IN_SIN_LEN
+ sa_in6->sin6_len = sizeof(struct sockaddr_in6);
+#endif
+ sa_in6->sin6_addr = addr->v6;
+ sa_in6->sin6_port = htons(port);
+ break;
+ default:
+ fatalx("addr2sa: unknown af");
+ }
+}
+
+void
+sa2addr(struct sockaddr *sa, int *af, union ldpd_addr *addr, in_port_t *port)
+{
+ struct sockaddr_in *sa_in = (struct sockaddr_in *)sa;
+ struct sockaddr_in6 *sa_in6 = (struct sockaddr_in6 *)sa;
+
+ if (addr)
+ memset(addr, 0, sizeof(*addr));
+ switch (sa->sa_family) {
+ case AF_INET:
+ if (af)
+ *af = AF_INET;
+ if (addr)
+ addr->v4 = sa_in->sin_addr;
+ if (port)
+ *port = sa_in->sin_port;
+ break;
+ case AF_INET6:
+ if (af)
+ *af = AF_INET6;
+ if (addr)
+ addr->v6 = sa_in6->sin6_addr;
+ if (port)
+ *port = sa_in6->sin6_port;
+ break;
+ default:
+ fatalx("sa2addr: unknown af");
+ }
+}
+
+socklen_t
+sockaddr_len(struct sockaddr *sa)
+{
+#ifdef HAVE_STRUCT_SOCKADDR_IN_SIN_LEN
+ return (sa->sa_len);
+#else
+ switch (sa->sa_family) {
+ case AF_INET:
+ return (sizeof(struct sockaddr_in));
+ case AF_INET6:
+ return (sizeof(struct sockaddr_in6));
+ default:
+ fatalx("sockaddr_len: unknown af");
+ }
+#endif
+}