xref: /openbsd/usr.sbin/bgpd/util.c (revision 110c1584)
1*110c1584Sclaudio /*	$OpenBSD: util.c,v 1.83 2024/03/20 09:35:46 claudio Exp $ */
22ffcd4e0Sclaudio 
32ffcd4e0Sclaudio /*
42ffcd4e0Sclaudio  * Copyright (c) 2006 Claudio Jeker <claudio@openbsd.org>
52ffcd4e0Sclaudio  * Copyright (c) 2003, 2004 Henning Brauer <henning@openbsd.org>
62ffcd4e0Sclaudio  *
72ffcd4e0Sclaudio  * Permission to use, copy, modify, and distribute this software for any
82ffcd4e0Sclaudio  * purpose with or without fee is hereby granted, provided that the above
92ffcd4e0Sclaudio  * copyright notice and this permission notice appear in all copies.
102ffcd4e0Sclaudio  *
112ffcd4e0Sclaudio  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
122ffcd4e0Sclaudio  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
132ffcd4e0Sclaudio  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
142ffcd4e0Sclaudio  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
152ffcd4e0Sclaudio  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
162ffcd4e0Sclaudio  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
172ffcd4e0Sclaudio  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
182ffcd4e0Sclaudio  */
192ffcd4e0Sclaudio #include <sys/types.h>
202ffcd4e0Sclaudio #include <sys/socket.h>
212ffcd4e0Sclaudio #include <netinet/in.h>
222ffcd4e0Sclaudio #include <arpa/inet.h>
2348bae517Sclaudio #include <endian.h>
2429328a94Sclaudio #include <errno.h>
252ffcd4e0Sclaudio #include <netdb.h>
262ffcd4e0Sclaudio #include <stdlib.h>
272ffcd4e0Sclaudio #include <stdio.h>
282ffcd4e0Sclaudio #include <string.h>
290561b344Sphessler #include <vis.h>
302ffcd4e0Sclaudio 
312ffcd4e0Sclaudio #include "bgpd.h"
322ffcd4e0Sclaudio #include "rde.h"
335e3f6f95Sbenno #include "log.h"
342ffcd4e0Sclaudio 
352ffcd4e0Sclaudio const char *
362ffcd4e0Sclaudio log_addr(const struct bgpd_addr *addr)
372ffcd4e0Sclaudio {
38290f96faSdenis 	static char	buf[74];
3945350f87Sclaudio 	struct sockaddr *sa;
405624d029Sclaudio 	socklen_t	len;
412ffcd4e0Sclaudio 
4245350f87Sclaudio 	sa = addr2sa(addr, 0, &len);
4315d8de66Sclaudio 	switch (addr->aid) {
4415d8de66Sclaudio 	case AID_INET:
4515d8de66Sclaudio 	case AID_INET6:
4645350f87Sclaudio 		return log_sockaddr(sa, len);
4715d8de66Sclaudio 	case AID_VPN_IPv4:
48290f96faSdenis 	case AID_VPN_IPv6:
493038d3d1Sclaudio 		snprintf(buf, sizeof(buf), "%s %s", log_rd(addr->rd),
5045350f87Sclaudio 		    log_sockaddr(sa, len));
51290f96faSdenis 		return (buf);
5215d8de66Sclaudio 	}
5315d8de66Sclaudio 	return ("???");
542ffcd4e0Sclaudio }
552ffcd4e0Sclaudio 
562ffcd4e0Sclaudio const char *
572ffcd4e0Sclaudio log_in6addr(const struct in6_addr *addr)
582ffcd4e0Sclaudio {
592ffcd4e0Sclaudio 	struct sockaddr_in6	sa_in6;
602ffcd4e0Sclaudio 
61eafe309eSclaudio 	memset(&sa_in6, 0, sizeof(sa_in6));
622ffcd4e0Sclaudio 	sa_in6.sin6_family = AF_INET6;
632ffcd4e0Sclaudio 	memcpy(&sa_in6.sin6_addr, addr, sizeof(sa_in6.sin6_addr));
642ffcd4e0Sclaudio 
65be6ced5eSclaudio #ifdef __KAME__
662ffcd4e0Sclaudio 	/* XXX thanks, KAME, for this ugliness... adopted from route/show.c */
67bdec2ffaStb 	if ((IN6_IS_ADDR_LINKLOCAL(&sa_in6.sin6_addr) ||
68bdec2ffaStb 	    IN6_IS_ADDR_MC_LINKLOCAL(&sa_in6.sin6_addr) ||
694ff2dba3Sclaudio 	    IN6_IS_ADDR_MC_NODELOCAL(&sa_in6.sin6_addr)) &&
70bdec2ffaStb 	    sa_in6.sin6_scope_id == 0) {
7139386878Sclaudio 		uint16_t tmp16;
722ffcd4e0Sclaudio 		memcpy(&tmp16, &sa_in6.sin6_addr.s6_addr[2], sizeof(tmp16));
732ffcd4e0Sclaudio 		sa_in6.sin6_scope_id = ntohs(tmp16);
742ffcd4e0Sclaudio 		sa_in6.sin6_addr.s6_addr[2] = 0;
752ffcd4e0Sclaudio 		sa_in6.sin6_addr.s6_addr[3] = 0;
762ffcd4e0Sclaudio 	}
77be6ced5eSclaudio #endif
782ffcd4e0Sclaudio 
79255fe563Sclaudio 	return (log_sockaddr((struct sockaddr *)&sa_in6, sizeof(sa_in6)));
802ffcd4e0Sclaudio }
812ffcd4e0Sclaudio 
822ffcd4e0Sclaudio const char *
83255fe563Sclaudio log_sockaddr(struct sockaddr *sa, socklen_t len)
842ffcd4e0Sclaudio {
852ffcd4e0Sclaudio 	static char	buf[NI_MAXHOST];
862ffcd4e0Sclaudio 
8745350f87Sclaudio 	if (sa == NULL || getnameinfo(sa, len, buf, sizeof(buf), NULL, 0,
882ffcd4e0Sclaudio 	    NI_NUMERICHOST))
892ffcd4e0Sclaudio 		return ("(unknown)");
902ffcd4e0Sclaudio 	else
912ffcd4e0Sclaudio 		return (buf);
922ffcd4e0Sclaudio }
932ffcd4e0Sclaudio 
940c88bf70Sclaudio const char *
9539386878Sclaudio log_as(uint32_t as)
960c88bf70Sclaudio {
9706bcde9cSphessler 	static char	buf[11];	/* "4294967294\0" */
980c88bf70Sclaudio 
99515e489cSderaadt 	if (snprintf(buf, sizeof(buf), "%u", as) < 0)
1000c88bf70Sclaudio 		return ("?");
10106bcde9cSphessler 
1020c88bf70Sclaudio 	return (buf);
1030c88bf70Sclaudio }
1040c88bf70Sclaudio 
105256b680eSclaudio const char *
10639386878Sclaudio log_rd(uint64_t rd)
107256b680eSclaudio {
108256b680eSclaudio 	static char	buf[32];
109256b680eSclaudio 	struct in_addr	addr;
11039386878Sclaudio 	uint32_t	u32;
11139386878Sclaudio 	uint16_t	u16;
112256b680eSclaudio 
113f4c0eb52Sclaudio 	rd = be64toh(rd);
114256b680eSclaudio 	switch (rd >> 48) {
115bf8e2920Sclaudio 	case EXT_COMMUNITY_TRANS_TWO_AS:
116256b680eSclaudio 		u32 = rd & 0xffffffff;
117256b680eSclaudio 		u16 = (rd >> 32) & 0xffff;
11832ecd2d8Sderaadt 		snprintf(buf, sizeof(buf), "rd %hu:%u", u16, u32);
119256b680eSclaudio 		break;
120bf8e2920Sclaudio 	case EXT_COMMUNITY_TRANS_FOUR_AS:
121256b680eSclaudio 		u32 = (rd >> 16) & 0xffffffff;
122256b680eSclaudio 		u16 = rd & 0xffff;
12332ecd2d8Sderaadt 		snprintf(buf, sizeof(buf), "rd %s:%hu", log_as(u32), u16);
124256b680eSclaudio 		break;
125bf8e2920Sclaudio 	case EXT_COMMUNITY_TRANS_IPV4:
126256b680eSclaudio 		u32 = (rd >> 16) & 0xffffffff;
127256b680eSclaudio 		u16 = rd & 0xffff;
128256b680eSclaudio 		addr.s_addr = htonl(u32);
12932ecd2d8Sderaadt 		snprintf(buf, sizeof(buf), "rd %s:%hu", inet_ntoa(addr), u16);
130256b680eSclaudio 		break;
131256b680eSclaudio 	default:
132ec5cb450Sclaudio 		snprintf(buf, sizeof(buf), "rd #%016llx",
133ec5cb450Sclaudio 		    (unsigned long long)rd);
134ec5cb450Sclaudio 		break;
135256b680eSclaudio 	}
136256b680eSclaudio 	return (buf);
137256b680eSclaudio }
138256b680eSclaudio 
139bf8e2920Sclaudio const struct ext_comm_pairs iana_ext_comms[] = IANA_EXT_COMMUNITIES;
140bf8e2920Sclaudio 
141256b680eSclaudio /* NOTE: this function does not check if the type/subtype combo is
142536f41e5Sclaudio  * actually valid. */
143536f41e5Sclaudio const char *
144f8162053Sclaudio log_ext_subtype(int type, uint8_t subtype)
145536f41e5Sclaudio {
146536f41e5Sclaudio 	static char etype[6];
147bf8e2920Sclaudio 	const struct ext_comm_pairs *cp;
148536f41e5Sclaudio 
149bf8e2920Sclaudio 	for (cp = iana_ext_comms; cp->subname != NULL; cp++) {
1500e6216fdSclaudio 		if ((type == cp->type || type == -1) && subtype == cp->subtype)
151bf8e2920Sclaudio 			return (cp->subname);
152bf8e2920Sclaudio 	}
153d6340f7aSderaadt 	snprintf(etype, sizeof(etype), "[%u]", subtype);
154536f41e5Sclaudio 	return (etype);
155536f41e5Sclaudio }
156536f41e5Sclaudio 
1571e590dcfSclaudio const char *
158a78f83ceSderaadt log_reason(const char *communication) {
159a78f83ceSderaadt 	static char buf[(REASON_LEN - 1) * 4 + 1];
1600561b344Sphessler 
1610561b344Sphessler 	strnvis(buf, communication, sizeof(buf), VIS_NL | VIS_OCTAL);
1620561b344Sphessler 
1630561b344Sphessler 	return buf;
1640561b344Sphessler }
1650561b344Sphessler 
1666290e740Sclaudio static const char *
1676290e740Sclaudio log_expires(time_t expires)
1686290e740Sclaudio {
1696290e740Sclaudio 	static char buf[32];
1706290e740Sclaudio 
1716290e740Sclaudio 	buf[0] = '\0';
1726290e740Sclaudio 	if (expires != 0)
1736290e740Sclaudio 		snprintf(buf, sizeof(buf), " expires %lld", (long long)expires);
1746290e740Sclaudio 	return buf;
1756290e740Sclaudio }
1766290e740Sclaudio 
1776290e740Sclaudio const char *
1786290e740Sclaudio log_roa(struct roa *roa)
1796290e740Sclaudio {
1806290e740Sclaudio 	static char buf[256];
1816290e740Sclaudio 	char maxbuf[32];
1822fd9f52dSmiod #if defined(__GNUC__) && __GNUC__ < 4
1832fd9f52dSmiod 	struct bgpd_addr addr = { .aid = roa->aid };
1842fd9f52dSmiod 	addr.v6 = roa->prefix.inet6;
1852fd9f52dSmiod #else
1862fd9f52dSmiod 	struct bgpd_addr addr = { .aid = roa->aid, .v6 = roa->prefix.inet6 };
1872fd9f52dSmiod #endif
1886290e740Sclaudio 
1896290e740Sclaudio 	maxbuf[0] = '\0';
1906290e740Sclaudio 	if (roa->prefixlen != roa->maxlen)
1916290e740Sclaudio 		snprintf(maxbuf, sizeof(maxbuf), " maxlen %u", roa->maxlen);
1926290e740Sclaudio 	snprintf(buf, sizeof(buf), "%s/%u%s source-as %u%s", log_addr(&addr),
1936290e740Sclaudio 	    roa->prefixlen, maxbuf, roa->asnum, log_expires(roa->expires));
1946290e740Sclaudio 	return buf;
1956290e740Sclaudio }
1966290e740Sclaudio 
1976290e740Sclaudio const char *
1986290e740Sclaudio log_aspa(struct aspa_set *aspa)
1996290e740Sclaudio {
2006290e740Sclaudio 	static char errbuf[256];
2016290e740Sclaudio 	static char *buf;
2026290e740Sclaudio 	static size_t len;
2036290e740Sclaudio 	char asbuf[16];
2046290e740Sclaudio 	size_t needed;
2056290e740Sclaudio 	uint32_t i;
2066290e740Sclaudio 
2076290e740Sclaudio 	/* include enough space for header and trailer */
2086290e740Sclaudio 	if ((uint64_t)aspa->num > (SIZE_MAX / sizeof(asbuf) - 72))
2096290e740Sclaudio 		goto fail;
2106290e740Sclaudio 	needed = aspa->num * sizeof(asbuf) + 72;
2116290e740Sclaudio 	if (needed > len) {
2126290e740Sclaudio 		char *nbuf;
2136290e740Sclaudio 
2146290e740Sclaudio 		if ((nbuf = realloc(buf, needed)) == NULL)
2156290e740Sclaudio 			goto fail;
2166290e740Sclaudio 		len = needed;
2176290e740Sclaudio 		buf = nbuf;
2186290e740Sclaudio 	}
2196290e740Sclaudio 
2206290e740Sclaudio 	snprintf(buf, len, "customer-as %s%s provider-as { ",
2216290e740Sclaudio 	    log_as(aspa->as), log_expires(aspa->expires));
2226290e740Sclaudio 
2236290e740Sclaudio 	for (i = 0; i < aspa->num; i++) {
2246290e740Sclaudio 		snprintf(asbuf, sizeof(asbuf), "%s ", log_as(aspa->tas[i]));
2256290e740Sclaudio 		if (strlcat(buf, asbuf, len) >= len)
2266290e740Sclaudio 			goto fail;
2276290e740Sclaudio 	}
2286290e740Sclaudio 	if (strlcat(buf, "}", len) >= len)
2296290e740Sclaudio 		goto fail;
2306290e740Sclaudio 	return buf;
2316290e740Sclaudio 
2326290e740Sclaudio  fail:
2336290e740Sclaudio 	free(buf);
2346290e740Sclaudio 	buf = NULL;
2356290e740Sclaudio 	len = 0;
2366290e740Sclaudio 	snprintf(errbuf, sizeof(errbuf), "customer-as %s%s provider-as { ... }",
2376290e740Sclaudio 	    log_as(aspa->as), log_expires(aspa->expires));
2386290e740Sclaudio 	return errbuf;
2396290e740Sclaudio }
2406290e740Sclaudio 
2410561b344Sphessler const char *
24204349dffSclaudio log_aspath_error(int error)
24304349dffSclaudio {
24404349dffSclaudio 	static char buf[20];
24504349dffSclaudio 
24604349dffSclaudio 	switch (error) {
24704349dffSclaudio 	case AS_ERR_LEN:
24804349dffSclaudio 		return "inconsitent lenght";
24904349dffSclaudio 	case AS_ERR_TYPE:
25004349dffSclaudio 		return "unknown segment type";
25104349dffSclaudio 	case AS_ERR_BAD:
25204349dffSclaudio 		return "invalid encoding";
25304349dffSclaudio 	case AS_ERR_SOFT:
25404349dffSclaudio 		return "soft failure";
25504349dffSclaudio 	default:
25604349dffSclaudio 		snprintf(buf, sizeof(buf), "unknown %d", error);
25704349dffSclaudio 		return buf;
25804349dffSclaudio 	}
25904349dffSclaudio }
26004349dffSclaudio 
26104349dffSclaudio const char *
262bd9df44eSclaudio log_rtr_error(enum rtr_error err)
263bd9df44eSclaudio {
264bd9df44eSclaudio 	static char buf[20];
265bd9df44eSclaudio 
266bd9df44eSclaudio 	switch (err) {
267bd9df44eSclaudio 	case NO_ERROR:
268bd9df44eSclaudio 		return "No Error";
269bd9df44eSclaudio 	case CORRUPT_DATA:
270bd9df44eSclaudio 		return "Corrupt Data";
271bd9df44eSclaudio 	case INTERNAL_ERROR:
272bd9df44eSclaudio 		return "Internal Error";
273bd9df44eSclaudio 	case NO_DATA_AVAILABLE:
274bd9df44eSclaudio 		return "No Data Available";
275bd9df44eSclaudio 	case INVALID_REQUEST:
276bd9df44eSclaudio 		return "Invalid Request";
277bd9df44eSclaudio 	case UNSUPP_PROTOCOL_VERS:
278bd9df44eSclaudio 		return "Unsupported Protocol Version";
279bd9df44eSclaudio 	case UNSUPP_PDU_TYPE:
280bd9df44eSclaudio 		return "Unsupported PDU Type";
281bd9df44eSclaudio 	case UNK_REC_WDRAWL:
282f4123069Smbuhl 		return "Withdrawal of Unknown Record";
283bd9df44eSclaudio 	case DUP_REC_RECV:
284bd9df44eSclaudio 		return "Duplicate Announcement Received";
285bd9df44eSclaudio 	case UNEXP_PROTOCOL_VERS:
286bd9df44eSclaudio 		return "Unexpected Protocol Version";
287bd9df44eSclaudio 	default:
288bd9df44eSclaudio 		snprintf(buf, sizeof(buf), "unknown %u", err);
289bd9df44eSclaudio 		return buf;
290bd9df44eSclaudio 	}
291bd9df44eSclaudio }
292bd9df44eSclaudio 
293bd9df44eSclaudio const char *
294c0c94bccSclaudio log_policy(enum role role)
295202e5273Stb {
296202e5273Stb 	switch (role) {
297c0c94bccSclaudio 	case ROLE_PROVIDER:
298202e5273Stb 		return "provider";
299c0c94bccSclaudio 	case ROLE_RS:
300202e5273Stb 		return "rs";
301c0c94bccSclaudio 	case ROLE_RS_CLIENT:
302202e5273Stb 		return "rs-client";
303c0c94bccSclaudio 	case ROLE_CUSTOMER:
304202e5273Stb 		return "customer";
305c0c94bccSclaudio 	case ROLE_PEER:
306202e5273Stb 		return "peer";
307202e5273Stb 	default:
308202e5273Stb 		return "unknown";
309202e5273Stb 	}
310202e5273Stb }
311202e5273Stb 
31204349dffSclaudio static const char *
31339386878Sclaudio aspath_delim(uint8_t seg_type, int closing)
3141e590dcfSclaudio {
3151e590dcfSclaudio 	static char db[8];
3161e590dcfSclaudio 
3171e590dcfSclaudio 	switch (seg_type) {
3181e590dcfSclaudio 	case AS_SET:
3191e590dcfSclaudio 		if (!closing)
3201e590dcfSclaudio 			return ("{ ");
3211e590dcfSclaudio 		else
3221e590dcfSclaudio 			return (" }");
3231e590dcfSclaudio 	case AS_SEQUENCE:
3241e590dcfSclaudio 		return ("");
3251e590dcfSclaudio 	case AS_CONFED_SEQUENCE:
3261e590dcfSclaudio 		if (!closing)
3271e590dcfSclaudio 			return ("( ");
3281e590dcfSclaudio 		else
3291e590dcfSclaudio 			return (" )");
3301e590dcfSclaudio 	case AS_CONFED_SET:
3311e590dcfSclaudio 		if (!closing)
3321e590dcfSclaudio 			return ("[ ");
3331e590dcfSclaudio 		else
3341e590dcfSclaudio 			return (" ]");
3351e590dcfSclaudio 	default:
3361e590dcfSclaudio 		if (!closing)
3371e590dcfSclaudio 			snprintf(db, sizeof(db), "!%u ", seg_type);
3381e590dcfSclaudio 		else
3391e590dcfSclaudio 			snprintf(db, sizeof(db), " !%u", seg_type);
3401e590dcfSclaudio 		return (db);
3411e590dcfSclaudio 	}
3421e590dcfSclaudio }
3431e590dcfSclaudio 
34404349dffSclaudio static int
34504349dffSclaudio aspath_snprint(char *buf, size_t size, struct ibuf *in)
3462ffcd4e0Sclaudio {
3472ffcd4e0Sclaudio #define UPDATE()						\
3482ffcd4e0Sclaudio 	do {							\
34904349dffSclaudio 		if (r < 0 || (unsigned int)r >= size)		\
3502ffcd4e0Sclaudio 			return (-1);				\
3512ffcd4e0Sclaudio 		size -= r;					\
3522ffcd4e0Sclaudio 		buf += r;					\
3532ffcd4e0Sclaudio 	} while (0)
35404349dffSclaudio 
35504349dffSclaudio 	struct ibuf	data;
35604349dffSclaudio 	uint32_t	as;
35704349dffSclaudio 	int		r, n = 0;
35839386878Sclaudio 	uint8_t		i, seg_type, seg_len;
3592ffcd4e0Sclaudio 
36004349dffSclaudio 	ibuf_from_ibuf(&data, in);
36104349dffSclaudio 	while (ibuf_size(&data) > 0) {
36204349dffSclaudio 		if (ibuf_get_n8(&data, &seg_type) == -1 ||
36304349dffSclaudio 		    ibuf_get_n8(&data, &seg_len) == -1 ||
36404349dffSclaudio 		    seg_len == 0)
36504349dffSclaudio 			return (-1);
3662ffcd4e0Sclaudio 
36704349dffSclaudio 		r = snprintf(buf, size, "%s%s", n++ != 0 ? " " : "",
3681e590dcfSclaudio 		    aspath_delim(seg_type, 0));
3692ffcd4e0Sclaudio 		UPDATE();
3702ffcd4e0Sclaudio 
3712ffcd4e0Sclaudio 		for (i = 0; i < seg_len; i++) {
37204349dffSclaudio 			if (ibuf_get_n32(&data, &as) == -1)
37304349dffSclaudio 				return -1;
37404349dffSclaudio 
37504349dffSclaudio 			r = snprintf(buf, size, "%s", log_as(as));
3762ffcd4e0Sclaudio 			UPDATE();
3772ffcd4e0Sclaudio 			if (i + 1 < seg_len) {
3782ffcd4e0Sclaudio 				r = snprintf(buf, size, " ");
3792ffcd4e0Sclaudio 				UPDATE();
3802ffcd4e0Sclaudio 			}
3812ffcd4e0Sclaudio 		}
3821e590dcfSclaudio 		r = snprintf(buf, size, "%s", aspath_delim(seg_type, 1));
3832ffcd4e0Sclaudio 		UPDATE();
3842ffcd4e0Sclaudio 	}
38555e49665Sclaudio 	/* ensure that we have a valid C-string especially for empty as path */
3862ffcd4e0Sclaudio 	*buf = '\0';
38704349dffSclaudio 	return (0);
3882ffcd4e0Sclaudio #undef UPDATE
3892ffcd4e0Sclaudio }
3902ffcd4e0Sclaudio 
39104349dffSclaudio static ssize_t
39204349dffSclaudio aspath_strsize(struct ibuf *in)
3932ffcd4e0Sclaudio {
39404349dffSclaudio 	struct ibuf	 buf;
39504349dffSclaudio 	ssize_t		 total_size = 0;
39639386878Sclaudio 	uint32_t	 as;
39739386878Sclaudio 	uint8_t		 i, seg_type, seg_len;
3982ffcd4e0Sclaudio 
39904349dffSclaudio 	ibuf_from_ibuf(&buf, in);
40004349dffSclaudio 	while (ibuf_size(&buf) > 0) {
40104349dffSclaudio 		if (ibuf_get_n8(&buf, &seg_type) == -1 ||
40204349dffSclaudio 		    ibuf_get_n8(&buf, &seg_len) == -1 ||
40304349dffSclaudio 		    seg_len == 0)
40404349dffSclaudio 			return (-1);
4052ffcd4e0Sclaudio 
4062ffcd4e0Sclaudio 		if (total_size != 0)
4072ffcd4e0Sclaudio 			total_size += 1;
40804349dffSclaudio 		total_size += strlen(aspath_delim(seg_type, 0));
4092ffcd4e0Sclaudio 
4102ffcd4e0Sclaudio 		for (i = 0; i < seg_len; i++) {
41104349dffSclaudio 			if (ibuf_get_n32(&buf, &as) == -1)
41204349dffSclaudio 				return (-1);
4130c88bf70Sclaudio 
4148db4f5d2Sclaudio 			do {
4158db4f5d2Sclaudio 				total_size++;
4168db4f5d2Sclaudio 			} while ((as = as / 10) != 0);
41704349dffSclaudio 		}
41804349dffSclaudio 		total_size += seg_len - 1;
4192ffcd4e0Sclaudio 
42004349dffSclaudio 		total_size += strlen(aspath_delim(seg_type, 1));
42104349dffSclaudio 	}
42204349dffSclaudio 	return (total_size + 1);
4232ffcd4e0Sclaudio }
4242ffcd4e0Sclaudio 
42504349dffSclaudio int
42604349dffSclaudio aspath_asprint(char **ret, struct ibuf *data)
42704349dffSclaudio {
42804349dffSclaudio 	ssize_t	slen;
42904349dffSclaudio 
43004349dffSclaudio 	if ((slen = aspath_strsize(data)) == -1) {
43104349dffSclaudio 		*ret = NULL;
43204349dffSclaudio 		errno = EINVAL;
43304349dffSclaudio 		return (-1);
4342ffcd4e0Sclaudio 	}
43504349dffSclaudio 
43604349dffSclaudio 	*ret = malloc(slen);
43704349dffSclaudio 	if (*ret == NULL)
43804349dffSclaudio 		return (-1);
43904349dffSclaudio 
44004349dffSclaudio 	if (aspath_snprint(*ret, slen, data) == -1) {
44104349dffSclaudio 		free(*ret);
44204349dffSclaudio 		*ret = NULL;
44304349dffSclaudio 		errno = EINVAL;
44404349dffSclaudio 		return (-1);
44504349dffSclaudio 	}
44604349dffSclaudio 
44704349dffSclaudio 	return (0);
4482ffcd4e0Sclaudio }
4492ffcd4e0Sclaudio 
4502ffcd4e0Sclaudio /*
4512ffcd4e0Sclaudio  * Extract the asnum out of the as segment at the specified position.
4522ffcd4e0Sclaudio  * Direct access is not possible because of non-aligned reads.
453506f72cfSclaudio  * Only works on verified 4-byte AS paths.
4542ffcd4e0Sclaudio  */
45539386878Sclaudio uint32_t
4562ffcd4e0Sclaudio aspath_extract(const void *seg, int pos)
4572ffcd4e0Sclaudio {
4582ffcd4e0Sclaudio 	const u_char	*ptr = seg;
45939386878Sclaudio 	uint32_t	 as;
4602ffcd4e0Sclaudio 
461506f72cfSclaudio 	/* minimal pos check, return 0 since that is an invalid ASN */
462506f72cfSclaudio 	if (pos < 0 || pos >= ptr[1])
463506f72cfSclaudio 		return (0);
46439386878Sclaudio 	ptr += 2 + sizeof(uint32_t) * pos;
46539386878Sclaudio 	memcpy(&as, ptr, sizeof(uint32_t));
4660c88bf70Sclaudio 	return (ntohl(as));
4672ffcd4e0Sclaudio }
46821a825c9Sclaudio 
469de5c2eedSclaudio /*
47029328a94Sclaudio  * Verify that the aspath is correctly encoded.
47129328a94Sclaudio  */
47229328a94Sclaudio int
47304349dffSclaudio aspath_verify(struct ibuf *in, int as4byte, int noset)
47429328a94Sclaudio {
47504349dffSclaudio 	struct ibuf	 buf;
47604349dffSclaudio 	int		 pos, error = 0;
47739386878Sclaudio 	uint8_t		 seg_len, seg_type;
47829328a94Sclaudio 
47904349dffSclaudio 	ibuf_from_ibuf(&buf, in);
48004349dffSclaudio 	if (ibuf_size(&buf) & 1) {
48129328a94Sclaudio 		/* odd length aspath are invalid */
48204349dffSclaudio 		error = AS_ERR_BAD;
48304349dffSclaudio 		goto done;
48404349dffSclaudio 	}
48529328a94Sclaudio 
48604349dffSclaudio 	while (ibuf_size(&buf) > 0) {
48704349dffSclaudio 		if (ibuf_get_n8(&buf, &seg_type) == -1 ||
48804349dffSclaudio 		    ibuf_get_n8(&buf, &seg_len) == -1) {
48904349dffSclaudio 			error = AS_ERR_LEN;
49004349dffSclaudio 			goto done;
49104349dffSclaudio 		}
49229328a94Sclaudio 
49304349dffSclaudio 		if (seg_len == 0) {
494d04df938Sclaudio 			/* empty aspath segments are not allowed */
49504349dffSclaudio 			error = AS_ERR_BAD;
49604349dffSclaudio 			goto done;
49704349dffSclaudio 		}
498d04df938Sclaudio 
49929328a94Sclaudio 		/*
50029328a94Sclaudio 		 * BGP confederations should not show up but consider them
50129328a94Sclaudio 		 * as a soft error which invalidates the path but keeps the
50229328a94Sclaudio 		 * bgp session running.
50329328a94Sclaudio 		 */
50429328a94Sclaudio 		if (seg_type == AS_CONFED_SEQUENCE || seg_type == AS_CONFED_SET)
50529328a94Sclaudio 			error = AS_ERR_SOFT;
506aa528464Sclaudio 		/*
507aa528464Sclaudio 		 * If AS_SET filtering (RFC6472) is on, error out on AS_SET
508aa528464Sclaudio 		 * as well.
509aa528464Sclaudio 		 */
510aa528464Sclaudio 		if (noset && seg_type == AS_SET)
511aa528464Sclaudio 			error = AS_ERR_SOFT;
51229328a94Sclaudio 		if (seg_type != AS_SET && seg_type != AS_SEQUENCE &&
51304349dffSclaudio 		    seg_type != AS_CONFED_SEQUENCE &&
51404349dffSclaudio 		    seg_type != AS_CONFED_SET) {
51504349dffSclaudio 			error = AS_ERR_TYPE;
51604349dffSclaudio 			goto done;
51704349dffSclaudio 		}
51829328a94Sclaudio 
51929328a94Sclaudio 		/* RFC 7607 - AS 0 is considered malformed */
52029328a94Sclaudio 		for (pos = 0; pos < seg_len; pos++) {
52139386878Sclaudio 			uint32_t as;
52229328a94Sclaudio 
52304349dffSclaudio 			if (as4byte) {
52404349dffSclaudio 				if (ibuf_get_n32(&buf, &as) == -1) {
52504349dffSclaudio 					error = AS_ERR_LEN;
52604349dffSclaudio 					goto done;
52704349dffSclaudio 				}
52804349dffSclaudio 			} else {
52904349dffSclaudio 				uint16_t tmp;
53004349dffSclaudio 				if (ibuf_get_n16(&buf, &tmp) == -1) {
53104349dffSclaudio 					error = AS_ERR_LEN;
53204349dffSclaudio 					goto done;
53304349dffSclaudio 				}
53404349dffSclaudio 				as = tmp;
53504349dffSclaudio 			}
53629328a94Sclaudio 			if (as == 0)
53756a9a1b8Sclaudio 				error = AS_ERR_SOFT;
53829328a94Sclaudio 		}
53929328a94Sclaudio 	}
54004349dffSclaudio 
54104349dffSclaudio  done:
54229328a94Sclaudio 	return (error);	/* aspath is valid but probably not loop free */
54329328a94Sclaudio }
54429328a94Sclaudio 
54529328a94Sclaudio /*
54629328a94Sclaudio  * convert a 2 byte aspath to a 4 byte one.
54729328a94Sclaudio  */
54804349dffSclaudio struct ibuf *
54904349dffSclaudio aspath_inflate(struct ibuf *in)
55029328a94Sclaudio {
55104349dffSclaudio 	struct ibuf	*out;
55204349dffSclaudio 	uint16_t	 short_as;
55304349dffSclaudio 	uint8_t		 seg_type, seg_len;
55429328a94Sclaudio 
5554ac10ff8Sclaudio 	/*
5564ac10ff8Sclaudio 	 * Allocate enough space for the worst case.
5574ac10ff8Sclaudio 	 * XXX add 1 byte for the empty ASPATH case since we can't
5584ac10ff8Sclaudio 	 * allocate an ibuf of 0 length.
5594ac10ff8Sclaudio 	 */
5604ac10ff8Sclaudio 	if ((out = ibuf_open(ibuf_size(in) * 2 + 1)) == NULL)
56129328a94Sclaudio 		return (NULL);
56229328a94Sclaudio 
56329328a94Sclaudio 	/* then copy the aspath */
56404349dffSclaudio 	while (ibuf_size(in) > 0) {
56504349dffSclaudio 		if (ibuf_get_n8(in, &seg_type) == -1 ||
56604349dffSclaudio 		    ibuf_get_n8(in, &seg_len) == -1 ||
56704349dffSclaudio 		    seg_len == 0)
56804349dffSclaudio 			goto fail;
56904349dffSclaudio 		if (ibuf_add_n8(out, seg_type) == -1 ||
57004349dffSclaudio 		    ibuf_add_n8(out, seg_len) == -1)
57104349dffSclaudio 			goto fail;
57204349dffSclaudio 
57329328a94Sclaudio 		for (; seg_len > 0; seg_len--) {
57404349dffSclaudio 			if (ibuf_get_n16(in, &short_as) == -1)
57504349dffSclaudio 				goto fail;
57604349dffSclaudio 			if (ibuf_add_n32(out, short_as) == -1)
57704349dffSclaudio 				goto fail;
57829328a94Sclaudio 		}
57929328a94Sclaudio 	}
58029328a94Sclaudio 
58104349dffSclaudio 	return (out);
58204349dffSclaudio 
58304349dffSclaudio fail:
58404349dffSclaudio 	ibuf_free(out);
58504349dffSclaudio 	return (NULL);
58629328a94Sclaudio }
58729328a94Sclaudio 
5885c4d2233Sclaudio static const u_char	addrmask[] = { 0x00, 0x80, 0xc0, 0xe0, 0xf0,
5895c4d2233Sclaudio 			    0xf8, 0xfc, 0xfe, 0xff };
5905c4d2233Sclaudio 
5916d3e8673Sclaudio /* NLRI functions to extract prefixes from the NLRI blobs */
5920f144400Sclaudio int
5930f144400Sclaudio extract_prefix(const u_char *p, int len, void *va, uint8_t pfxlen, uint8_t max)
5946d3e8673Sclaudio {
5956d3e8673Sclaudio 	u_char		*a = va;
5963f0f322fSclaudio 	int		 plen;
5976d3e8673Sclaudio 
5983f0f322fSclaudio 	plen = PREFIX_SIZE(pfxlen) - 1;
5993f0f322fSclaudio 	if (len < plen || max < plen)
6003f0f322fSclaudio 		return -1;
6013f0f322fSclaudio 
6023f0f322fSclaudio 	while (pfxlen > 0) {
6036d3e8673Sclaudio 		if (pfxlen < 8) {
6043f0f322fSclaudio 			*a++ = *p++ & addrmask[pfxlen];
6056d3e8673Sclaudio 			break;
6066d3e8673Sclaudio 		} else {
6073f0f322fSclaudio 			*a++ = *p++;
6086d3e8673Sclaudio 			pfxlen -= 8;
6096d3e8673Sclaudio 		}
6106d3e8673Sclaudio 	}
6116d3e8673Sclaudio 	return (plen);
6126d3e8673Sclaudio }
6136d3e8673Sclaudio 
6145c4d2233Sclaudio static int
6155c4d2233Sclaudio extract_prefix_buf(struct ibuf *buf, void *va, uint8_t pfxlen, uint8_t max)
6166d3e8673Sclaudio {
6175c4d2233Sclaudio 	u_char		*a = va;
6185c4d2233Sclaudio 	unsigned int	 plen;
6195c4d2233Sclaudio 	uint8_t		 tmp;
6205c4d2233Sclaudio 
6215c4d2233Sclaudio 	plen = PREFIX_SIZE(pfxlen) - 1;
6225c4d2233Sclaudio 	if (ibuf_size(buf) < plen || max < plen)
6235c4d2233Sclaudio 		return -1;
6245c4d2233Sclaudio 
6255c4d2233Sclaudio 	while (pfxlen > 0) {
6265c4d2233Sclaudio 		if (ibuf_get_n8(buf, &tmp) == -1)
6275c4d2233Sclaudio 			return -1;
6285c4d2233Sclaudio 
6295c4d2233Sclaudio 		if (pfxlen < 8) {
6305c4d2233Sclaudio 			*a++ = tmp & addrmask[pfxlen];
6315c4d2233Sclaudio 			break;
6325c4d2233Sclaudio 		} else {
6335c4d2233Sclaudio 			*a++ = tmp;
6345c4d2233Sclaudio 			pfxlen -= 8;
6355c4d2233Sclaudio 		}
6365c4d2233Sclaudio 	}
6375c4d2233Sclaudio 	return (0);
6385c4d2233Sclaudio }
6395c4d2233Sclaudio 
6405c4d2233Sclaudio int
6415c4d2233Sclaudio nlri_get_prefix(struct ibuf *buf, struct bgpd_addr *prefix, uint8_t *prefixlen)
6425c4d2233Sclaudio {
64339386878Sclaudio 	uint8_t	 pfxlen;
6446d3e8673Sclaudio 
6455c4d2233Sclaudio 	if (ibuf_get_n8(buf, &pfxlen) == -1)
6466d3e8673Sclaudio 		return (-1);
6475c4d2233Sclaudio 	if (pfxlen > 32)
6485c4d2233Sclaudio 		return (-1);
6496d3e8673Sclaudio 
650eafe309eSclaudio 	memset(prefix, 0, sizeof(struct bgpd_addr));
6516d3e8673Sclaudio 	prefix->aid = AID_INET;
6525c4d2233Sclaudio 
6535c4d2233Sclaudio 	if (extract_prefix_buf(buf, &prefix->v4, pfxlen,
6545c4d2233Sclaudio 	    sizeof(prefix->v4)) == -1)
6555c4d2233Sclaudio 		return (-1);
6565c4d2233Sclaudio 
6576d3e8673Sclaudio 	*prefixlen = pfxlen;
6585c4d2233Sclaudio 	return (0);
6596d3e8673Sclaudio }
6606d3e8673Sclaudio 
6616d3e8673Sclaudio int
6625c4d2233Sclaudio nlri_get_prefix6(struct ibuf *buf, struct bgpd_addr *prefix, uint8_t *prefixlen)
6636d3e8673Sclaudio {
66439386878Sclaudio 	uint8_t	pfxlen;
6656d3e8673Sclaudio 
6665c4d2233Sclaudio 	if (ibuf_get_n8(buf, &pfxlen) == -1)
6676d3e8673Sclaudio 		return (-1);
6685c4d2233Sclaudio 	if (pfxlen > 128)
6695c4d2233Sclaudio 		return (-1);
6706d3e8673Sclaudio 
671eafe309eSclaudio 	memset(prefix, 0, sizeof(struct bgpd_addr));
6726d3e8673Sclaudio 	prefix->aid = AID_INET6;
6735c4d2233Sclaudio 
6745c4d2233Sclaudio 	if (extract_prefix_buf(buf, &prefix->v6, pfxlen,
6755c4d2233Sclaudio 	    sizeof(prefix->v6)) == -1)
6765c4d2233Sclaudio 		return (-1);
6775c4d2233Sclaudio 
6786d3e8673Sclaudio 	*prefixlen = pfxlen;
6795c4d2233Sclaudio 	return (0);
6806d3e8673Sclaudio }
6816d3e8673Sclaudio 
6826d3e8673Sclaudio int
6835c4d2233Sclaudio nlri_get_vpn4(struct ibuf *buf, struct bgpd_addr *prefix,
68439386878Sclaudio     uint8_t *prefixlen, int withdraw)
6856d3e8673Sclaudio {
6865c4d2233Sclaudio 	int		 done = 0;
68739386878Sclaudio 	uint8_t		 pfxlen;
6886d3e8673Sclaudio 
6895c4d2233Sclaudio 	if (ibuf_get_n8(buf, &pfxlen) == -1)
6906d3e8673Sclaudio 		return (-1);
6916d3e8673Sclaudio 
692eafe309eSclaudio 	memset(prefix, 0, sizeof(struct bgpd_addr));
6935c4d2233Sclaudio 	prefix->aid = AID_VPN_IPv4;
6946d3e8673Sclaudio 
6956d3e8673Sclaudio 	/* label stack */
6966d3e8673Sclaudio 	do {
6975c4d2233Sclaudio 		if (prefix->labellen + 3U > sizeof(prefix->labelstack) ||
6985c4d2233Sclaudio 		    pfxlen < 3 * 8)
6996d3e8673Sclaudio 			return (-1);
7006d3e8673Sclaudio 		if (withdraw) {
7016d3e8673Sclaudio 			/* on withdraw ignore the labelstack all together */
7025c4d2233Sclaudio 			if (ibuf_skip(buf, 3) == -1)
7035c4d2233Sclaudio 				return (-1);
7046d3e8673Sclaudio 			pfxlen -= 3 * 8;
7056d3e8673Sclaudio 			break;
7066d3e8673Sclaudio 		}
7075c4d2233Sclaudio 		if (ibuf_get(buf, &prefix->labelstack[prefix->labellen], 3) ==
7085c4d2233Sclaudio 		    -1)
7095c4d2233Sclaudio 			return -1;
7105c4d2233Sclaudio 		if (prefix->labelstack[prefix->labellen + 2] &
7116d3e8673Sclaudio 		    BGP_MPLS_BOS)
7126d3e8673Sclaudio 			done = 1;
7135c4d2233Sclaudio 		prefix->labellen += 3;
7146d3e8673Sclaudio 		pfxlen -= 3 * 8;
7156d3e8673Sclaudio 	} while (!done);
7166d3e8673Sclaudio 
7176d3e8673Sclaudio 	/* RD */
7185c4d2233Sclaudio 	if (pfxlen < sizeof(uint64_t) * 8 ||
7195c4d2233Sclaudio 	    ibuf_get_h64(buf, &prefix->rd) == -1)
7206d3e8673Sclaudio 		return (-1);
72139386878Sclaudio 	pfxlen -= sizeof(uint64_t) * 8;
7226d3e8673Sclaudio 
7236d3e8673Sclaudio 	/* prefix */
7246d3e8673Sclaudio 	if (pfxlen > 32)
7256d3e8673Sclaudio 		return (-1);
7265c4d2233Sclaudio 	if (extract_prefix_buf(buf, &prefix->v4, pfxlen,
7275c4d2233Sclaudio 	    sizeof(prefix->v4)) == -1)
7286d3e8673Sclaudio 		return (-1);
7296d3e8673Sclaudio 
7305c4d2233Sclaudio 	*prefixlen = pfxlen;
7315c4d2233Sclaudio 	return (0);
7326d3e8673Sclaudio }
7336d3e8673Sclaudio 
734290f96faSdenis int
7355c4d2233Sclaudio nlri_get_vpn6(struct ibuf *buf, struct bgpd_addr *prefix,
73639386878Sclaudio     uint8_t *prefixlen, int withdraw)
737290f96faSdenis {
7385c4d2233Sclaudio 	int		done = 0;
73939386878Sclaudio 	uint8_t		pfxlen;
740290f96faSdenis 
7415c4d2233Sclaudio 	if (ibuf_get_n8(buf, &pfxlen) == -1)
742290f96faSdenis 		return (-1);
743290f96faSdenis 
744290f96faSdenis 	memset(prefix, 0, sizeof(struct bgpd_addr));
7455c4d2233Sclaudio 	prefix->aid = AID_VPN_IPv6;
746290f96faSdenis 
747290f96faSdenis 	/* label stack */
748290f96faSdenis 	do {
7495c4d2233Sclaudio 		if (prefix->labellen + 3U > sizeof(prefix->labelstack) ||
7505c4d2233Sclaudio 		    pfxlen < 3 * 8)
751290f96faSdenis 			return (-1);
752290f96faSdenis 		if (withdraw) {
753290f96faSdenis 			/* on withdraw ignore the labelstack all together */
7545c4d2233Sclaudio 			if (ibuf_skip(buf, 3) == -1)
7555c4d2233Sclaudio 				return (-1);
756290f96faSdenis 			pfxlen -= 3 * 8;
757290f96faSdenis 			break;
758290f96faSdenis 		}
759290f96faSdenis 
7605c4d2233Sclaudio 		if (ibuf_get(buf, &prefix->labelstack[prefix->labellen], 3) ==
7615c4d2233Sclaudio 		    -1)
7625c4d2233Sclaudio 			return (-1);
7635c4d2233Sclaudio 		if (prefix->labelstack[prefix->labellen + 2] &
764290f96faSdenis 		    BGP_MPLS_BOS)
765290f96faSdenis 			done = 1;
7665c4d2233Sclaudio 		prefix->labellen += 3;
767290f96faSdenis 		pfxlen -= 3 * 8;
768290f96faSdenis 	} while (!done);
769290f96faSdenis 
770290f96faSdenis 	/* RD */
7715c4d2233Sclaudio 	if (pfxlen < sizeof(uint64_t) * 8 ||
7725c4d2233Sclaudio 	    ibuf_get_h64(buf, &prefix->rd) == -1)
773290f96faSdenis 		return (-1);
77439386878Sclaudio 	pfxlen -= sizeof(uint64_t) * 8;
775290f96faSdenis 
776290f96faSdenis 	/* prefix */
777290f96faSdenis 	if (pfxlen > 128)
778290f96faSdenis 		return (-1);
7795c4d2233Sclaudio 	if (extract_prefix_buf(buf, &prefix->v6, pfxlen,
7805c4d2233Sclaudio 	    sizeof(prefix->v6)) == -1)
781290f96faSdenis 		return (-1);
782290f96faSdenis 
7835c4d2233Sclaudio 	*prefixlen = pfxlen;
7845c4d2233Sclaudio 	return (0);
785290f96faSdenis }
786290f96faSdenis 
787fa3a38bbSclaudio static in_addr_t
788fa3a38bbSclaudio prefixlen2mask(uint8_t prefixlen)
789fa3a38bbSclaudio {
790fa3a38bbSclaudio 	if (prefixlen == 0)
791fa3a38bbSclaudio 		return (0);
792290f96faSdenis 
793fa3a38bbSclaudio 	return (0xffffffff << (32 - prefixlen));
794fa3a38bbSclaudio }
795290f96faSdenis 
79629328a94Sclaudio /*
797de5c2eedSclaudio  * This function will have undefined behaviour if the passed in prefixlen is
798290f96faSdenis  * too large for the respective bgpd_addr address family.
799de5c2eedSclaudio  */
800fafbb788Sclaudio int
801fafbb788Sclaudio prefix_compare(const struct bgpd_addr *a, const struct bgpd_addr *b,
802fafbb788Sclaudio     int prefixlen)
803fafbb788Sclaudio {
804fafbb788Sclaudio 	in_addr_t	mask, aa, ba;
805fafbb788Sclaudio 	int		i;
80639386878Sclaudio 	uint8_t		m;
807fafbb788Sclaudio 
808fafbb788Sclaudio 	if (a->aid != b->aid)
809fafbb788Sclaudio 		return (a->aid - b->aid);
810fafbb788Sclaudio 
811fafbb788Sclaudio 	switch (a->aid) {
8123038d3d1Sclaudio 	case AID_VPN_IPv4:
8133038d3d1Sclaudio 		if (be64toh(a->rd) > be64toh(b->rd))
8143038d3d1Sclaudio 			return (1);
8153038d3d1Sclaudio 		if (be64toh(a->rd) < be64toh(b->rd))
8163038d3d1Sclaudio 			return (-1);
8173038d3d1Sclaudio 		/* FALLTHROUGH */
818fafbb788Sclaudio 	case AID_INET:
8197da59fecSclaudio 		if (prefixlen == 0)
8207da59fecSclaudio 			return (0);
821fafbb788Sclaudio 		if (prefixlen > 32)
822de5c2eedSclaudio 			return (-1);
823fafbb788Sclaudio 		mask = htonl(prefixlen2mask(prefixlen));
824fafbb788Sclaudio 		aa = ntohl(a->v4.s_addr & mask);
825fafbb788Sclaudio 		ba = ntohl(b->v4.s_addr & mask);
8263038d3d1Sclaudio 		if (aa > ba)
8273038d3d1Sclaudio 			return (1);
8283038d3d1Sclaudio 		if (aa < ba)
8293038d3d1Sclaudio 			return (-1);
8303038d3d1Sclaudio 		break;
8313038d3d1Sclaudio 	case AID_VPN_IPv6:
8323038d3d1Sclaudio 		if (be64toh(a->rd) > be64toh(b->rd))
8333038d3d1Sclaudio 			return (1);
8343038d3d1Sclaudio 		if (be64toh(a->rd) < be64toh(b->rd))
8353038d3d1Sclaudio 			return (-1);
8363038d3d1Sclaudio 		/* FALLTHROUGH */
837fafbb788Sclaudio 	case AID_INET6:
8387da59fecSclaudio 		if (prefixlen == 0)
8397da59fecSclaudio 			return (0);
840fafbb788Sclaudio 		if (prefixlen > 128)
841de5c2eedSclaudio 			return (-1);
842fafbb788Sclaudio 		for (i = 0; i < prefixlen / 8; i++)
843fafbb788Sclaudio 			if (a->v6.s6_addr[i] != b->v6.s6_addr[i])
844fafbb788Sclaudio 				return (a->v6.s6_addr[i] - b->v6.s6_addr[i]);
845fafbb788Sclaudio 		i = prefixlen % 8;
846fafbb788Sclaudio 		if (i) {
847fafbb788Sclaudio 			m = 0xff00 >> i;
848fafbb788Sclaudio 			if ((a->v6.s6_addr[prefixlen / 8] & m) !=
849fafbb788Sclaudio 			    (b->v6.s6_addr[prefixlen / 8] & m))
850fafbb788Sclaudio 				return ((a->v6.s6_addr[prefixlen / 8] & m) -
851fafbb788Sclaudio 				    (b->v6.s6_addr[prefixlen / 8] & m));
852fafbb788Sclaudio 		}
8533038d3d1Sclaudio 		break;
8543038d3d1Sclaudio 	default:
8553038d3d1Sclaudio 		return (-1);
8563038d3d1Sclaudio 	}
8573038d3d1Sclaudio 
8583038d3d1Sclaudio 	if (a->aid == AID_VPN_IPv4 || a->aid == AID_VPN_IPv6) {
8593038d3d1Sclaudio 		if (a->labellen > b->labellen)
8603038d3d1Sclaudio 			return (1);
8613038d3d1Sclaudio 		if (a->labellen < b->labellen)
8623038d3d1Sclaudio 			return (-1);
8633038d3d1Sclaudio 		return (memcmp(a->labelstack, b->labelstack, a->labellen));
8643038d3d1Sclaudio 	}
865fafbb788Sclaudio 	return (0);
8663038d3d1Sclaudio 
867fafbb788Sclaudio }
868fafbb788Sclaudio 
86921a825c9Sclaudio void
8702b5c88feSclaudio inet4applymask(struct in_addr *dest, const struct in_addr *src, int prefixlen)
8712b5c88feSclaudio {
8722b5c88feSclaudio 	struct in_addr mask;
8732b5c88feSclaudio 
8742b5c88feSclaudio 	mask.s_addr = htonl(prefixlen2mask(prefixlen));
8752b5c88feSclaudio 	dest->s_addr = src->s_addr & mask.s_addr;
8762b5c88feSclaudio }
8772b5c88feSclaudio 
8782b5c88feSclaudio void
87921a825c9Sclaudio inet6applymask(struct in6_addr *dest, const struct in6_addr *src, int prefixlen)
88021a825c9Sclaudio {
88121a825c9Sclaudio 	struct in6_addr	mask;
88221a825c9Sclaudio 	int		i;
88321a825c9Sclaudio 
884eafe309eSclaudio 	memset(&mask, 0, sizeof(mask));
88521a825c9Sclaudio 	for (i = 0; i < prefixlen / 8; i++)
88621a825c9Sclaudio 		mask.s6_addr[i] = 0xff;
88721a825c9Sclaudio 	i = prefixlen % 8;
88821a825c9Sclaudio 	if (i)
88921a825c9Sclaudio 		mask.s6_addr[prefixlen / 8] = 0xff00 >> i;
89021a825c9Sclaudio 
89121a825c9Sclaudio 	for (i = 0; i < 16; i++)
89221a825c9Sclaudio 		dest->s6_addr[i] = src->s6_addr[i] & mask.s6_addr[i];
89321a825c9Sclaudio }
894d6c2e4e8Sclaudio 
89513bcf54fSclaudio void
89613bcf54fSclaudio applymask(struct bgpd_addr *dest, const struct bgpd_addr *src, int prefixlen)
89713bcf54fSclaudio {
89813bcf54fSclaudio 	*dest = *src;
89913bcf54fSclaudio 	switch (src->aid) {
90013bcf54fSclaudio 	case AID_INET:
90113bcf54fSclaudio 	case AID_VPN_IPv4:
90213bcf54fSclaudio 		inet4applymask(&dest->v4, &src->v4, prefixlen);
90313bcf54fSclaudio 		break;
90413bcf54fSclaudio 	case AID_INET6:
90513bcf54fSclaudio 	case AID_VPN_IPv6:
90613bcf54fSclaudio 		inet6applymask(&dest->v6, &src->v6, prefixlen);
90713bcf54fSclaudio 		break;
90813bcf54fSclaudio 	}
90913bcf54fSclaudio }
91013bcf54fSclaudio 
911d6c2e4e8Sclaudio /* address family translation functions */
912d6c2e4e8Sclaudio const struct aid aid_vals[AID_MAX] = AID_VALS;
913d6c2e4e8Sclaudio 
91486729c90Sclaudio const char *
91539386878Sclaudio aid2str(uint8_t aid)
91686729c90Sclaudio {
91786729c90Sclaudio 	if (aid < AID_MAX)
91886729c90Sclaudio 		return (aid_vals[aid].name);
91986729c90Sclaudio 	return ("unknown AID");
92086729c90Sclaudio }
92186729c90Sclaudio 
922d6c2e4e8Sclaudio int
92339386878Sclaudio aid2afi(uint8_t aid, uint16_t *afi, uint8_t *safi)
924d6c2e4e8Sclaudio {
925*110c1584Sclaudio 	if (aid != AID_UNSPEC && aid < AID_MAX) {
926d6c2e4e8Sclaudio 		*afi = aid_vals[aid].afi;
927d6c2e4e8Sclaudio 		*safi = aid_vals[aid].safi;
928d6c2e4e8Sclaudio 		return (0);
929d6c2e4e8Sclaudio 	}
930d6c2e4e8Sclaudio 	return (-1);
931d6c2e4e8Sclaudio }
932d6c2e4e8Sclaudio 
933d6c2e4e8Sclaudio int
93439386878Sclaudio afi2aid(uint16_t afi, uint8_t safi, uint8_t *aid)
935d6c2e4e8Sclaudio {
93639386878Sclaudio 	uint8_t i;
937d6c2e4e8Sclaudio 
938*110c1584Sclaudio 	for (i = AID_MIN; i < AID_MAX; i++)
939d6c2e4e8Sclaudio 		if (aid_vals[i].afi == afi && aid_vals[i].safi == safi) {
940d6c2e4e8Sclaudio 			*aid = i;
941d6c2e4e8Sclaudio 			return (0);
942d6c2e4e8Sclaudio 		}
943d6c2e4e8Sclaudio 
944d6c2e4e8Sclaudio 	return (-1);
945d6c2e4e8Sclaudio }
946d6c2e4e8Sclaudio 
947d6c2e4e8Sclaudio sa_family_t
94839386878Sclaudio aid2af(uint8_t aid)
949d6c2e4e8Sclaudio {
950d6c2e4e8Sclaudio 	if (aid < AID_MAX)
951d6c2e4e8Sclaudio 		return (aid_vals[aid].af);
952d6c2e4e8Sclaudio 	return (AF_UNSPEC);
953d6c2e4e8Sclaudio }
954d6c2e4e8Sclaudio 
955d6c2e4e8Sclaudio int
95639386878Sclaudio af2aid(sa_family_t af, uint8_t safi, uint8_t *aid)
957d6c2e4e8Sclaudio {
95839386878Sclaudio 	uint8_t i;
959d6c2e4e8Sclaudio 
960d6c2e4e8Sclaudio 	if (safi == 0) /* default to unicast subclass */
961d6c2e4e8Sclaudio 		safi = SAFI_UNICAST;
962d6c2e4e8Sclaudio 
963*110c1584Sclaudio 	for (i = AID_UNSPEC; i < AID_MAX; i++)
964d6c2e4e8Sclaudio 		if (aid_vals[i].af == af && aid_vals[i].safi == safi) {
965d6c2e4e8Sclaudio 			*aid = i;
966d6c2e4e8Sclaudio 			return (0);
967d6c2e4e8Sclaudio 		}
968d6c2e4e8Sclaudio 
969d6c2e4e8Sclaudio 	return (-1);
970d6c2e4e8Sclaudio }
971d6c2e4e8Sclaudio 
97245350f87Sclaudio /*
97345350f87Sclaudio  * Convert a struct bgpd_addr into a struct sockaddr. For VPN addresses
97445350f87Sclaudio  * the included label stack is ignored and needs to be handled by the caller.
97545350f87Sclaudio  */
976d6c2e4e8Sclaudio struct sockaddr *
97739386878Sclaudio addr2sa(const struct bgpd_addr *addr, uint16_t port, socklen_t *len)
978d6c2e4e8Sclaudio {
979d6c2e4e8Sclaudio 	static struct sockaddr_storage	 ss;
980d6c2e4e8Sclaudio 	struct sockaddr_in		*sa_in = (struct sockaddr_in *)&ss;
981d6c2e4e8Sclaudio 	struct sockaddr_in6		*sa_in6 = (struct sockaddr_in6 *)&ss;
982d6c2e4e8Sclaudio 
9834886db4cSclaudio 	if (addr == NULL || addr->aid == AID_UNSPEC)
9844886db4cSclaudio 		return (NULL);
98545350f87Sclaudio 
986eafe309eSclaudio 	memset(&ss, 0, sizeof(ss));
987d6c2e4e8Sclaudio 	switch (addr->aid) {
988d6c2e4e8Sclaudio 	case AID_INET:
9893038d3d1Sclaudio 	case AID_VPN_IPv4:
990d6c2e4e8Sclaudio 		sa_in->sin_family = AF_INET;
991d6c2e4e8Sclaudio 		sa_in->sin_addr.s_addr = addr->v4.s_addr;
992d6c2e4e8Sclaudio 		sa_in->sin_port = htons(port);
993255fe563Sclaudio 		*len = sizeof(struct sockaddr_in);
994d6c2e4e8Sclaudio 		break;
995d6c2e4e8Sclaudio 	case AID_INET6:
99645350f87Sclaudio 	case AID_VPN_IPv6:
99745350f87Sclaudio 		sa_in6->sin6_family = AF_INET6;
9983038d3d1Sclaudio 		memcpy(&sa_in6->sin6_addr, &addr->v6,
99945350f87Sclaudio 		    sizeof(sa_in6->sin6_addr));
100045350f87Sclaudio 		sa_in6->sin6_port = htons(port);
100145350f87Sclaudio 		sa_in6->sin6_scope_id = addr->scope_id;
100245350f87Sclaudio 		*len = sizeof(struct sockaddr_in6);
100345350f87Sclaudio 		break;
100441c1c374Sclaudio 	case AID_FLOWSPECv4:
100541c1c374Sclaudio 	case AID_FLOWSPECv6:
100641c1c374Sclaudio 		return (NULL);
1007d6c2e4e8Sclaudio 	}
1008d6c2e4e8Sclaudio 
1009d6c2e4e8Sclaudio 	return ((struct sockaddr *)&ss);
1010d6c2e4e8Sclaudio }
1011d6c2e4e8Sclaudio 
1012d6c2e4e8Sclaudio void
101339386878Sclaudio sa2addr(struct sockaddr *sa, struct bgpd_addr *addr, uint16_t *port)
1014d6c2e4e8Sclaudio {
1015d6c2e4e8Sclaudio 	struct sockaddr_in		*sa_in = (struct sockaddr_in *)sa;
1016d6c2e4e8Sclaudio 	struct sockaddr_in6		*sa_in6 = (struct sockaddr_in6 *)sa;
1017d6c2e4e8Sclaudio 
1018eafe309eSclaudio 	memset(addr, 0, sizeof(*addr));
1019d6c2e4e8Sclaudio 	switch (sa->sa_family) {
1020d6c2e4e8Sclaudio 	case AF_INET:
1021d6c2e4e8Sclaudio 		addr->aid = AID_INET;
1022d6c2e4e8Sclaudio 		memcpy(&addr->v4, &sa_in->sin_addr, sizeof(addr->v4));
1023a27d9e33Sclaudio 		if (port)
1024a27d9e33Sclaudio 			*port = ntohs(sa_in->sin_port);
1025d6c2e4e8Sclaudio 		break;
1026d6c2e4e8Sclaudio 	case AF_INET6:
1027d6c2e4e8Sclaudio 		addr->aid = AID_INET6;
1028be6ced5eSclaudio #ifdef __KAME__
1029be6ced5eSclaudio 		/*
1030be6ced5eSclaudio 		 * XXX thanks, KAME, for this ugliness...
1031be6ced5eSclaudio 		 * adopted from route/show.c
1032be6ced5eSclaudio 		 */
10335177244fSclaudio 		if ((IN6_IS_ADDR_LINKLOCAL(&sa_in6->sin6_addr) ||
10345177244fSclaudio 		    IN6_IS_ADDR_MC_LINKLOCAL(&sa_in6->sin6_addr) ||
10354ff2dba3Sclaudio 		    IN6_IS_ADDR_MC_NODELOCAL(&sa_in6->sin6_addr)) &&
10365177244fSclaudio 		    sa_in6->sin6_scope_id == 0) {
1037be6ced5eSclaudio 			uint16_t tmp16;
1038be6ced5eSclaudio 			memcpy(&tmp16, &sa_in6->sin6_addr.s6_addr[2],
1039be6ced5eSclaudio 			    sizeof(tmp16));
1040be6ced5eSclaudio 			sa_in6->sin6_scope_id = ntohs(tmp16);
1041be6ced5eSclaudio 			sa_in6->sin6_addr.s6_addr[2] = 0;
1042be6ced5eSclaudio 			sa_in6->sin6_addr.s6_addr[3] = 0;
1043be6ced5eSclaudio 		}
1044be6ced5eSclaudio #endif
10455177244fSclaudio 		memcpy(&addr->v6, &sa_in6->sin6_addr, sizeof(addr->v6));
1046d6c2e4e8Sclaudio 		addr->scope_id = sa_in6->sin6_scope_id; /* I hate v6 */
1047a27d9e33Sclaudio 		if (port)
1048a27d9e33Sclaudio 			*port = ntohs(sa_in6->sin6_port);
1049d6c2e4e8Sclaudio 		break;
1050d6c2e4e8Sclaudio 	}
1051d6c2e4e8Sclaudio }
10526e8089a5Sclaudio 
10536e8089a5Sclaudio const char *
1054bc757ddaSclaudio get_baudrate(unsigned long long baudrate, char *unit)
10556e8089a5Sclaudio {
10566e8089a5Sclaudio 	static char bbuf[16];
10573eaf1285Sclaudio 	const unsigned long long kilo = 1000;
10583eaf1285Sclaudio 	const unsigned long long mega = 1000ULL * kilo;
10593eaf1285Sclaudio 	const unsigned long long giga = 1000ULL * mega;
10606e8089a5Sclaudio 
10613eaf1285Sclaudio 	if (baudrate > giga)
10626e8089a5Sclaudio 		snprintf(bbuf, sizeof(bbuf), "%llu G%s",
10633eaf1285Sclaudio 		    baudrate / giga, unit);
10643eaf1285Sclaudio 	else if (baudrate > mega)
10656e8089a5Sclaudio 		snprintf(bbuf, sizeof(bbuf), "%llu M%s",
10663eaf1285Sclaudio 		    baudrate / mega, unit);
10673eaf1285Sclaudio 	else if (baudrate > kilo)
10686e8089a5Sclaudio 		snprintf(bbuf, sizeof(bbuf), "%llu K%s",
10693eaf1285Sclaudio 		    baudrate / kilo, unit);
10706e8089a5Sclaudio 	else
10716e8089a5Sclaudio 		snprintf(bbuf, sizeof(bbuf), "%llu %s",
10726e8089a5Sclaudio 		    baudrate, unit);
10736e8089a5Sclaudio 
10746e8089a5Sclaudio 	return (bbuf);
10756e8089a5Sclaudio }
1076