xref: /openbsd-src/usr.sbin/bgpd/util.c (revision e3db1f63b9983ca4cf18b686be12853eccdfd031)
1*e3db1f63Sclaudio /*	$OpenBSD: util.c,v 1.92 2025/01/27 15:22:11 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 {
859ad78aa8Sclaudio 	static char	buf[4][NI_MAXHOST];
869ad78aa8Sclaudio 	static int	bufidx;
872ffcd4e0Sclaudio 
889ad78aa8Sclaudio 	bufidx = (bufidx + 1) % 4;
899ad78aa8Sclaudio 	if (sa == NULL || getnameinfo(sa, len, buf[bufidx], sizeof(buf[0]),
909ad78aa8Sclaudio 	    NULL, 0, NI_NUMERICHOST))
912ffcd4e0Sclaudio 		return ("(unknown)");
922ffcd4e0Sclaudio 	else
939ad78aa8Sclaudio 		return (buf[bufidx]);
942ffcd4e0Sclaudio }
952ffcd4e0Sclaudio 
960c88bf70Sclaudio const char *
9739386878Sclaudio log_as(uint32_t as)
980c88bf70Sclaudio {
9906bcde9cSphessler 	static char	buf[11];	/* "4294967294\0" */
1000c88bf70Sclaudio 
101515e489cSderaadt 	if (snprintf(buf, sizeof(buf), "%u", as) < 0)
1020c88bf70Sclaudio 		return ("?");
10306bcde9cSphessler 
1040c88bf70Sclaudio 	return (buf);
1050c88bf70Sclaudio }
1060c88bf70Sclaudio 
107256b680eSclaudio const char *
10839386878Sclaudio log_rd(uint64_t rd)
109256b680eSclaudio {
110256b680eSclaudio 	static char	buf[32];
111256b680eSclaudio 	struct in_addr	addr;
11239386878Sclaudio 	uint32_t	u32;
11339386878Sclaudio 	uint16_t	u16;
114256b680eSclaudio 
115f4c0eb52Sclaudio 	rd = be64toh(rd);
116256b680eSclaudio 	switch (rd >> 48) {
117bf8e2920Sclaudio 	case EXT_COMMUNITY_TRANS_TWO_AS:
118256b680eSclaudio 		u32 = rd & 0xffffffff;
119256b680eSclaudio 		u16 = (rd >> 32) & 0xffff;
12032ecd2d8Sderaadt 		snprintf(buf, sizeof(buf), "rd %hu:%u", u16, u32);
121256b680eSclaudio 		break;
122bf8e2920Sclaudio 	case EXT_COMMUNITY_TRANS_FOUR_AS:
123256b680eSclaudio 		u32 = (rd >> 16) & 0xffffffff;
124256b680eSclaudio 		u16 = rd & 0xffff;
12532ecd2d8Sderaadt 		snprintf(buf, sizeof(buf), "rd %s:%hu", log_as(u32), u16);
126256b680eSclaudio 		break;
127bf8e2920Sclaudio 	case EXT_COMMUNITY_TRANS_IPV4:
128256b680eSclaudio 		u32 = (rd >> 16) & 0xffffffff;
129256b680eSclaudio 		u16 = rd & 0xffff;
130256b680eSclaudio 		addr.s_addr = htonl(u32);
13132ecd2d8Sderaadt 		snprintf(buf, sizeof(buf), "rd %s:%hu", inet_ntoa(addr), u16);
132256b680eSclaudio 		break;
133256b680eSclaudio 	default:
134ec5cb450Sclaudio 		snprintf(buf, sizeof(buf), "rd #%016llx",
135ec5cb450Sclaudio 		    (unsigned long long)rd);
136ec5cb450Sclaudio 		break;
137256b680eSclaudio 	}
138256b680eSclaudio 	return (buf);
139256b680eSclaudio }
140256b680eSclaudio 
141bf8e2920Sclaudio const struct ext_comm_pairs iana_ext_comms[] = IANA_EXT_COMMUNITIES;
142bf8e2920Sclaudio 
143256b680eSclaudio /* NOTE: this function does not check if the type/subtype combo is
144536f41e5Sclaudio  * actually valid. */
145536f41e5Sclaudio const char *
146f8162053Sclaudio log_ext_subtype(int type, uint8_t subtype)
147536f41e5Sclaudio {
148477ac4f1Sclaudio 	static char etype[16];
149bf8e2920Sclaudio 	const struct ext_comm_pairs *cp;
150536f41e5Sclaudio 
151bf8e2920Sclaudio 	for (cp = iana_ext_comms; cp->subname != NULL; cp++) {
1520e6216fdSclaudio 		if ((type == cp->type || type == -1) && subtype == cp->subtype)
153bf8e2920Sclaudio 			return (cp->subname);
154bf8e2920Sclaudio 	}
155477ac4f1Sclaudio 	if (type == -1)
156477ac4f1Sclaudio 		return ("???");
157477ac4f1Sclaudio 	snprintf(etype, sizeof(etype), "[%hhx:%hhx]", (uint8_t)type, subtype);
158536f41e5Sclaudio 	return (etype);
159536f41e5Sclaudio }
160536f41e5Sclaudio 
1611e590dcfSclaudio const char *
162a78f83ceSderaadt log_reason(const char *communication) {
163a78f83ceSderaadt 	static char buf[(REASON_LEN - 1) * 4 + 1];
1640561b344Sphessler 
1650561b344Sphessler 	strnvis(buf, communication, sizeof(buf), VIS_NL | VIS_OCTAL);
1660561b344Sphessler 
1670561b344Sphessler 	return buf;
1680561b344Sphessler }
1690561b344Sphessler 
1706290e740Sclaudio static const char *
1716290e740Sclaudio log_expires(time_t expires)
1726290e740Sclaudio {
1736290e740Sclaudio 	static char buf[32];
1746290e740Sclaudio 
1756290e740Sclaudio 	buf[0] = '\0';
1766290e740Sclaudio 	if (expires != 0)
1776290e740Sclaudio 		snprintf(buf, sizeof(buf), " expires %lld", (long long)expires);
1786290e740Sclaudio 	return buf;
1796290e740Sclaudio }
1806290e740Sclaudio 
1816290e740Sclaudio const char *
1826290e740Sclaudio log_roa(struct roa *roa)
1836290e740Sclaudio {
1846290e740Sclaudio 	static char buf[256];
1856290e740Sclaudio 	char maxbuf[32];
1862fd9f52dSmiod #if defined(__GNUC__) && __GNUC__ < 4
1872fd9f52dSmiod 	struct bgpd_addr addr = { .aid = roa->aid };
1882fd9f52dSmiod 	addr.v6 = roa->prefix.inet6;
1892fd9f52dSmiod #else
1902fd9f52dSmiod 	struct bgpd_addr addr = { .aid = roa->aid, .v6 = roa->prefix.inet6 };
1912fd9f52dSmiod #endif
1926290e740Sclaudio 
1936290e740Sclaudio 	maxbuf[0] = '\0';
1946290e740Sclaudio 	if (roa->prefixlen != roa->maxlen)
1956290e740Sclaudio 		snprintf(maxbuf, sizeof(maxbuf), " maxlen %u", roa->maxlen);
1966290e740Sclaudio 	snprintf(buf, sizeof(buf), "%s/%u%s source-as %u%s", log_addr(&addr),
1976290e740Sclaudio 	    roa->prefixlen, maxbuf, roa->asnum, log_expires(roa->expires));
1986290e740Sclaudio 	return buf;
1996290e740Sclaudio }
2006290e740Sclaudio 
2016290e740Sclaudio const char *
2026290e740Sclaudio log_aspa(struct aspa_set *aspa)
2036290e740Sclaudio {
2046290e740Sclaudio 	static char errbuf[256];
2056290e740Sclaudio 	static char *buf;
2066290e740Sclaudio 	static size_t len;
2076290e740Sclaudio 	char asbuf[16];
2086290e740Sclaudio 	size_t needed;
2096290e740Sclaudio 	uint32_t i;
2106290e740Sclaudio 
2116290e740Sclaudio 	/* include enough space for header and trailer */
2126290e740Sclaudio 	if ((uint64_t)aspa->num > (SIZE_MAX / sizeof(asbuf) - 72))
2136290e740Sclaudio 		goto fail;
2146290e740Sclaudio 	needed = aspa->num * sizeof(asbuf) + 72;
2156290e740Sclaudio 	if (needed > len) {
2166290e740Sclaudio 		char *nbuf;
2176290e740Sclaudio 
2186290e740Sclaudio 		if ((nbuf = realloc(buf, needed)) == NULL)
2196290e740Sclaudio 			goto fail;
2206290e740Sclaudio 		len = needed;
2216290e740Sclaudio 		buf = nbuf;
2226290e740Sclaudio 	}
2236290e740Sclaudio 
2246290e740Sclaudio 	snprintf(buf, len, "customer-as %s%s provider-as { ",
2256290e740Sclaudio 	    log_as(aspa->as), log_expires(aspa->expires));
2266290e740Sclaudio 
2276290e740Sclaudio 	for (i = 0; i < aspa->num; i++) {
2286290e740Sclaudio 		snprintf(asbuf, sizeof(asbuf), "%s ", log_as(aspa->tas[i]));
2296290e740Sclaudio 		if (strlcat(buf, asbuf, len) >= len)
2306290e740Sclaudio 			goto fail;
2316290e740Sclaudio 	}
2326290e740Sclaudio 	if (strlcat(buf, "}", len) >= len)
2336290e740Sclaudio 		goto fail;
2346290e740Sclaudio 	return buf;
2356290e740Sclaudio 
2366290e740Sclaudio  fail:
2376290e740Sclaudio 	free(buf);
2386290e740Sclaudio 	buf = NULL;
2396290e740Sclaudio 	len = 0;
2406290e740Sclaudio 	snprintf(errbuf, sizeof(errbuf), "customer-as %s%s provider-as { ... }",
2416290e740Sclaudio 	    log_as(aspa->as), log_expires(aspa->expires));
2426290e740Sclaudio 	return errbuf;
2436290e740Sclaudio }
2446290e740Sclaudio 
2450561b344Sphessler const char *
24604349dffSclaudio log_aspath_error(int error)
24704349dffSclaudio {
24804349dffSclaudio 	static char buf[20];
24904349dffSclaudio 
25004349dffSclaudio 	switch (error) {
25104349dffSclaudio 	case AS_ERR_LEN:
2529a8ba155Sjob 		return "inconsistent length";
25304349dffSclaudio 	case AS_ERR_TYPE:
25404349dffSclaudio 		return "unknown segment type";
25504349dffSclaudio 	case AS_ERR_BAD:
25604349dffSclaudio 		return "invalid encoding";
25704349dffSclaudio 	case AS_ERR_SOFT:
25804349dffSclaudio 		return "soft failure";
25904349dffSclaudio 	default:
26004349dffSclaudio 		snprintf(buf, sizeof(buf), "unknown %d", error);
26104349dffSclaudio 		return buf;
26204349dffSclaudio 	}
26304349dffSclaudio }
26404349dffSclaudio 
26504349dffSclaudio const char *
266bd9df44eSclaudio log_rtr_error(enum rtr_error err)
267bd9df44eSclaudio {
268bd9df44eSclaudio 	static char buf[20];
269bd9df44eSclaudio 
270bd9df44eSclaudio 	switch (err) {
271bd9df44eSclaudio 	case NO_ERROR:
272bd9df44eSclaudio 		return "No Error";
273bd9df44eSclaudio 	case CORRUPT_DATA:
274bd9df44eSclaudio 		return "Corrupt Data";
275bd9df44eSclaudio 	case INTERNAL_ERROR:
276bd9df44eSclaudio 		return "Internal Error";
277bd9df44eSclaudio 	case NO_DATA_AVAILABLE:
278bd9df44eSclaudio 		return "No Data Available";
279bd9df44eSclaudio 	case INVALID_REQUEST:
280bd9df44eSclaudio 		return "Invalid Request";
281bd9df44eSclaudio 	case UNSUPP_PROTOCOL_VERS:
282bd9df44eSclaudio 		return "Unsupported Protocol Version";
283bd9df44eSclaudio 	case UNSUPP_PDU_TYPE:
284bd9df44eSclaudio 		return "Unsupported PDU Type";
285bd9df44eSclaudio 	case UNK_REC_WDRAWL:
286f4123069Smbuhl 		return "Withdrawal of Unknown Record";
287bd9df44eSclaudio 	case DUP_REC_RECV:
288bd9df44eSclaudio 		return "Duplicate Announcement Received";
289bd9df44eSclaudio 	case UNEXP_PROTOCOL_VERS:
290bd9df44eSclaudio 		return "Unexpected Protocol Version";
291bd9df44eSclaudio 	default:
292bd9df44eSclaudio 		snprintf(buf, sizeof(buf), "unknown %u", err);
293bd9df44eSclaudio 		return buf;
294bd9df44eSclaudio 	}
295bd9df44eSclaudio }
296bd9df44eSclaudio 
297bd9df44eSclaudio const char *
298c0c94bccSclaudio log_policy(enum role role)
299202e5273Stb {
300202e5273Stb 	switch (role) {
301c0c94bccSclaudio 	case ROLE_PROVIDER:
302202e5273Stb 		return "provider";
303c0c94bccSclaudio 	case ROLE_RS:
304202e5273Stb 		return "rs";
305c0c94bccSclaudio 	case ROLE_RS_CLIENT:
306202e5273Stb 		return "rs-client";
307c0c94bccSclaudio 	case ROLE_CUSTOMER:
308202e5273Stb 		return "customer";
309c0c94bccSclaudio 	case ROLE_PEER:
310202e5273Stb 		return "peer";
311202e5273Stb 	default:
312202e5273Stb 		return "unknown";
313202e5273Stb 	}
314202e5273Stb }
315202e5273Stb 
316beb044e9Sclaudio const char *
317beb044e9Sclaudio log_capability(uint8_t capa)
318beb044e9Sclaudio {
319beb044e9Sclaudio 	static char buf[20];
320beb044e9Sclaudio 
321beb044e9Sclaudio 	switch (capa) {
322beb044e9Sclaudio 	case CAPA_MP:
323beb044e9Sclaudio 		return "Multiprotocol Extensions";
324beb044e9Sclaudio 	case CAPA_REFRESH:
325beb044e9Sclaudio 		return "Route Refresh";
326000b2be6Sclaudio 	case CAPA_EXT_NEXTHOP:
327000b2be6Sclaudio 		return "Extended Nexhop Encoding";
32844f660feSclaudio 	case CAPA_EXT_MSG:
32944f660feSclaudio 		return "Extended Message";
330beb044e9Sclaudio 	case CAPA_ROLE:
331beb044e9Sclaudio 		return "BGP Role";
332beb044e9Sclaudio 	case CAPA_RESTART:
333beb044e9Sclaudio 		return "Graceful Restart";
334beb044e9Sclaudio 	case CAPA_AS4BYTE:
335beb044e9Sclaudio 		return "4-octet AS number";
336beb044e9Sclaudio 	case CAPA_ADD_PATH:
337beb044e9Sclaudio 		return "ADD-PATH";
338beb044e9Sclaudio 	case CAPA_ENHANCED_RR:
339beb044e9Sclaudio 		return "Enhanced Route Refresh";
340beb044e9Sclaudio 	default:
341beb044e9Sclaudio 		snprintf(buf, sizeof(buf), "unknown %u", capa);
342beb044e9Sclaudio 		return buf;
343beb044e9Sclaudio 	}
344beb044e9Sclaudio }
345beb044e9Sclaudio 
34604349dffSclaudio static const char *
34739386878Sclaudio aspath_delim(uint8_t seg_type, int closing)
3481e590dcfSclaudio {
3491e590dcfSclaudio 	static char db[8];
3501e590dcfSclaudio 
3511e590dcfSclaudio 	switch (seg_type) {
3521e590dcfSclaudio 	case AS_SET:
3531e590dcfSclaudio 		if (!closing)
3541e590dcfSclaudio 			return ("{ ");
3551e590dcfSclaudio 		else
3561e590dcfSclaudio 			return (" }");
3571e590dcfSclaudio 	case AS_SEQUENCE:
3581e590dcfSclaudio 		return ("");
3591e590dcfSclaudio 	case AS_CONFED_SEQUENCE:
3601e590dcfSclaudio 		if (!closing)
3611e590dcfSclaudio 			return ("( ");
3621e590dcfSclaudio 		else
3631e590dcfSclaudio 			return (" )");
3641e590dcfSclaudio 	case AS_CONFED_SET:
3651e590dcfSclaudio 		if (!closing)
3661e590dcfSclaudio 			return ("[ ");
3671e590dcfSclaudio 		else
3681e590dcfSclaudio 			return (" ]");
3691e590dcfSclaudio 	default:
3701e590dcfSclaudio 		if (!closing)
3711e590dcfSclaudio 			snprintf(db, sizeof(db), "!%u ", seg_type);
3721e590dcfSclaudio 		else
3731e590dcfSclaudio 			snprintf(db, sizeof(db), " !%u", seg_type);
3741e590dcfSclaudio 		return (db);
3751e590dcfSclaudio 	}
3761e590dcfSclaudio }
3771e590dcfSclaudio 
37804349dffSclaudio static int
37904349dffSclaudio aspath_snprint(char *buf, size_t size, struct ibuf *in)
3802ffcd4e0Sclaudio {
3812ffcd4e0Sclaudio #define UPDATE()						\
3822ffcd4e0Sclaudio 	do {							\
38304349dffSclaudio 		if (r < 0 || (unsigned int)r >= size)		\
3842ffcd4e0Sclaudio 			return (-1);				\
3852ffcd4e0Sclaudio 		size -= r;					\
3862ffcd4e0Sclaudio 		buf += r;					\
3872ffcd4e0Sclaudio 	} while (0)
38804349dffSclaudio 
38904349dffSclaudio 	struct ibuf	data;
39004349dffSclaudio 	uint32_t	as;
39104349dffSclaudio 	int		r, n = 0;
39239386878Sclaudio 	uint8_t		i, seg_type, seg_len;
3932ffcd4e0Sclaudio 
39404349dffSclaudio 	ibuf_from_ibuf(&data, in);
39504349dffSclaudio 	while (ibuf_size(&data) > 0) {
39604349dffSclaudio 		if (ibuf_get_n8(&data, &seg_type) == -1 ||
39704349dffSclaudio 		    ibuf_get_n8(&data, &seg_len) == -1 ||
39804349dffSclaudio 		    seg_len == 0)
39904349dffSclaudio 			return (-1);
4002ffcd4e0Sclaudio 
40104349dffSclaudio 		r = snprintf(buf, size, "%s%s", n++ != 0 ? " " : "",
4021e590dcfSclaudio 		    aspath_delim(seg_type, 0));
4032ffcd4e0Sclaudio 		UPDATE();
4042ffcd4e0Sclaudio 
4052ffcd4e0Sclaudio 		for (i = 0; i < seg_len; i++) {
40604349dffSclaudio 			if (ibuf_get_n32(&data, &as) == -1)
40704349dffSclaudio 				return -1;
40804349dffSclaudio 
40904349dffSclaudio 			r = snprintf(buf, size, "%s", log_as(as));
4102ffcd4e0Sclaudio 			UPDATE();
4112ffcd4e0Sclaudio 			if (i + 1 < seg_len) {
4122ffcd4e0Sclaudio 				r = snprintf(buf, size, " ");
4132ffcd4e0Sclaudio 				UPDATE();
4142ffcd4e0Sclaudio 			}
4152ffcd4e0Sclaudio 		}
4161e590dcfSclaudio 		r = snprintf(buf, size, "%s", aspath_delim(seg_type, 1));
4172ffcd4e0Sclaudio 		UPDATE();
4182ffcd4e0Sclaudio 	}
41955e49665Sclaudio 	/* ensure that we have a valid C-string especially for empty as path */
4202ffcd4e0Sclaudio 	*buf = '\0';
42104349dffSclaudio 	return (0);
4222ffcd4e0Sclaudio #undef UPDATE
4232ffcd4e0Sclaudio }
4242ffcd4e0Sclaudio 
42504349dffSclaudio static ssize_t
42604349dffSclaudio aspath_strsize(struct ibuf *in)
4272ffcd4e0Sclaudio {
42804349dffSclaudio 	struct ibuf	 buf;
42904349dffSclaudio 	ssize_t		 total_size = 0;
43039386878Sclaudio 	uint32_t	 as;
43139386878Sclaudio 	uint8_t		 i, seg_type, seg_len;
4322ffcd4e0Sclaudio 
43304349dffSclaudio 	ibuf_from_ibuf(&buf, in);
43404349dffSclaudio 	while (ibuf_size(&buf) > 0) {
43504349dffSclaudio 		if (ibuf_get_n8(&buf, &seg_type) == -1 ||
43604349dffSclaudio 		    ibuf_get_n8(&buf, &seg_len) == -1 ||
43704349dffSclaudio 		    seg_len == 0)
43804349dffSclaudio 			return (-1);
4392ffcd4e0Sclaudio 
4402ffcd4e0Sclaudio 		if (total_size != 0)
4412ffcd4e0Sclaudio 			total_size += 1;
44204349dffSclaudio 		total_size += strlen(aspath_delim(seg_type, 0));
4432ffcd4e0Sclaudio 
4442ffcd4e0Sclaudio 		for (i = 0; i < seg_len; i++) {
44504349dffSclaudio 			if (ibuf_get_n32(&buf, &as) == -1)
44604349dffSclaudio 				return (-1);
4470c88bf70Sclaudio 
4488db4f5d2Sclaudio 			do {
4498db4f5d2Sclaudio 				total_size++;
4508db4f5d2Sclaudio 			} while ((as = as / 10) != 0);
45104349dffSclaudio 		}
45204349dffSclaudio 		total_size += seg_len - 1;
4532ffcd4e0Sclaudio 
45404349dffSclaudio 		total_size += strlen(aspath_delim(seg_type, 1));
45504349dffSclaudio 	}
45604349dffSclaudio 	return (total_size + 1);
4572ffcd4e0Sclaudio }
4582ffcd4e0Sclaudio 
45904349dffSclaudio int
46004349dffSclaudio aspath_asprint(char **ret, struct ibuf *data)
46104349dffSclaudio {
46204349dffSclaudio 	ssize_t	slen;
46304349dffSclaudio 
46404349dffSclaudio 	if ((slen = aspath_strsize(data)) == -1) {
46504349dffSclaudio 		*ret = NULL;
46604349dffSclaudio 		errno = EINVAL;
46704349dffSclaudio 		return (-1);
4682ffcd4e0Sclaudio 	}
46904349dffSclaudio 
47004349dffSclaudio 	*ret = malloc(slen);
47104349dffSclaudio 	if (*ret == NULL)
47204349dffSclaudio 		return (-1);
47304349dffSclaudio 
47404349dffSclaudio 	if (aspath_snprint(*ret, slen, data) == -1) {
47504349dffSclaudio 		free(*ret);
47604349dffSclaudio 		*ret = NULL;
47704349dffSclaudio 		errno = EINVAL;
47804349dffSclaudio 		return (-1);
47904349dffSclaudio 	}
48004349dffSclaudio 
48104349dffSclaudio 	return (0);
4822ffcd4e0Sclaudio }
4832ffcd4e0Sclaudio 
4842ffcd4e0Sclaudio /*
4852ffcd4e0Sclaudio  * Extract the asnum out of the as segment at the specified position.
4862ffcd4e0Sclaudio  * Direct access is not possible because of non-aligned reads.
487506f72cfSclaudio  * Only works on verified 4-byte AS paths.
4882ffcd4e0Sclaudio  */
48939386878Sclaudio uint32_t
4902ffcd4e0Sclaudio aspath_extract(const void *seg, int pos)
4912ffcd4e0Sclaudio {
4922ffcd4e0Sclaudio 	const u_char	*ptr = seg;
49339386878Sclaudio 	uint32_t	 as;
4942ffcd4e0Sclaudio 
495506f72cfSclaudio 	/* minimal pos check, return 0 since that is an invalid ASN */
496506f72cfSclaudio 	if (pos < 0 || pos >= ptr[1])
497506f72cfSclaudio 		return (0);
49839386878Sclaudio 	ptr += 2 + sizeof(uint32_t) * pos;
49939386878Sclaudio 	memcpy(&as, ptr, sizeof(uint32_t));
5000c88bf70Sclaudio 	return (ntohl(as));
5012ffcd4e0Sclaudio }
50221a825c9Sclaudio 
503de5c2eedSclaudio /*
50429328a94Sclaudio  * Verify that the aspath is correctly encoded.
50529328a94Sclaudio  */
50629328a94Sclaudio int
507*e3db1f63Sclaudio aspath_verify(struct ibuf *in, int as4byte, int permit_set)
50829328a94Sclaudio {
50904349dffSclaudio 	struct ibuf	 buf;
51004349dffSclaudio 	int		 pos, error = 0;
51139386878Sclaudio 	uint8_t		 seg_len, seg_type;
51229328a94Sclaudio 
51304349dffSclaudio 	ibuf_from_ibuf(&buf, in);
51404349dffSclaudio 	if (ibuf_size(&buf) & 1) {
51529328a94Sclaudio 		/* odd length aspath are invalid */
51604349dffSclaudio 		error = AS_ERR_BAD;
51704349dffSclaudio 		goto done;
51804349dffSclaudio 	}
51929328a94Sclaudio 
52004349dffSclaudio 	while (ibuf_size(&buf) > 0) {
52104349dffSclaudio 		if (ibuf_get_n8(&buf, &seg_type) == -1 ||
52204349dffSclaudio 		    ibuf_get_n8(&buf, &seg_len) == -1) {
52304349dffSclaudio 			error = AS_ERR_LEN;
52404349dffSclaudio 			goto done;
52504349dffSclaudio 		}
52629328a94Sclaudio 
52704349dffSclaudio 		if (seg_len == 0) {
528d04df938Sclaudio 			/* empty aspath segments are not allowed */
52904349dffSclaudio 			error = AS_ERR_BAD;
53004349dffSclaudio 			goto done;
53104349dffSclaudio 		}
532d04df938Sclaudio 
53329328a94Sclaudio 		/*
53429328a94Sclaudio 		 * BGP confederations should not show up but consider them
53529328a94Sclaudio 		 * as a soft error which invalidates the path but keeps the
53629328a94Sclaudio 		 * bgp session running.
53729328a94Sclaudio 		 */
53829328a94Sclaudio 		if (seg_type == AS_CONFED_SEQUENCE || seg_type == AS_CONFED_SET)
53929328a94Sclaudio 			error = AS_ERR_SOFT;
540aa528464Sclaudio 		/*
541aa528464Sclaudio 		 * If AS_SET filtering (RFC6472) is on, error out on AS_SET
542aa528464Sclaudio 		 * as well.
543aa528464Sclaudio 		 */
544*e3db1f63Sclaudio 		if (!permit_set && seg_type == AS_SET)
545aa528464Sclaudio 			error = AS_ERR_SOFT;
54629328a94Sclaudio 		if (seg_type != AS_SET && seg_type != AS_SEQUENCE &&
54704349dffSclaudio 		    seg_type != AS_CONFED_SEQUENCE &&
54804349dffSclaudio 		    seg_type != AS_CONFED_SET) {
54904349dffSclaudio 			error = AS_ERR_TYPE;
55004349dffSclaudio 			goto done;
55104349dffSclaudio 		}
55229328a94Sclaudio 
55329328a94Sclaudio 		/* RFC 7607 - AS 0 is considered malformed */
55429328a94Sclaudio 		for (pos = 0; pos < seg_len; pos++) {
55539386878Sclaudio 			uint32_t as;
55629328a94Sclaudio 
55704349dffSclaudio 			if (as4byte) {
55804349dffSclaudio 				if (ibuf_get_n32(&buf, &as) == -1) {
55904349dffSclaudio 					error = AS_ERR_LEN;
56004349dffSclaudio 					goto done;
56104349dffSclaudio 				}
56204349dffSclaudio 			} else {
56304349dffSclaudio 				uint16_t tmp;
56404349dffSclaudio 				if (ibuf_get_n16(&buf, &tmp) == -1) {
56504349dffSclaudio 					error = AS_ERR_LEN;
56604349dffSclaudio 					goto done;
56704349dffSclaudio 				}
56804349dffSclaudio 				as = tmp;
56904349dffSclaudio 			}
57029328a94Sclaudio 			if (as == 0)
57156a9a1b8Sclaudio 				error = AS_ERR_SOFT;
57229328a94Sclaudio 		}
57329328a94Sclaudio 	}
57404349dffSclaudio 
57504349dffSclaudio  done:
57629328a94Sclaudio 	return (error);	/* aspath is valid but probably not loop free */
57729328a94Sclaudio }
57829328a94Sclaudio 
57929328a94Sclaudio /*
58029328a94Sclaudio  * convert a 2 byte aspath to a 4 byte one.
58129328a94Sclaudio  */
58204349dffSclaudio struct ibuf *
58304349dffSclaudio aspath_inflate(struct ibuf *in)
58429328a94Sclaudio {
58504349dffSclaudio 	struct ibuf	*out;
58604349dffSclaudio 	uint16_t	 short_as;
58704349dffSclaudio 	uint8_t		 seg_type, seg_len;
58829328a94Sclaudio 
5894ac10ff8Sclaudio 	/*
5904ac10ff8Sclaudio 	 * Allocate enough space for the worst case.
5914ac10ff8Sclaudio 	 * XXX add 1 byte for the empty ASPATH case since we can't
5924ac10ff8Sclaudio 	 * allocate an ibuf of 0 length.
5934ac10ff8Sclaudio 	 */
5944ac10ff8Sclaudio 	if ((out = ibuf_open(ibuf_size(in) * 2 + 1)) == NULL)
59529328a94Sclaudio 		return (NULL);
59629328a94Sclaudio 
59729328a94Sclaudio 	/* then copy the aspath */
59804349dffSclaudio 	while (ibuf_size(in) > 0) {
59904349dffSclaudio 		if (ibuf_get_n8(in, &seg_type) == -1 ||
60004349dffSclaudio 		    ibuf_get_n8(in, &seg_len) == -1 ||
60104349dffSclaudio 		    seg_len == 0)
60204349dffSclaudio 			goto fail;
60304349dffSclaudio 		if (ibuf_add_n8(out, seg_type) == -1 ||
60404349dffSclaudio 		    ibuf_add_n8(out, seg_len) == -1)
60504349dffSclaudio 			goto fail;
60604349dffSclaudio 
60729328a94Sclaudio 		for (; seg_len > 0; seg_len--) {
60804349dffSclaudio 			if (ibuf_get_n16(in, &short_as) == -1)
60904349dffSclaudio 				goto fail;
61004349dffSclaudio 			if (ibuf_add_n32(out, short_as) == -1)
61104349dffSclaudio 				goto fail;
61229328a94Sclaudio 		}
61329328a94Sclaudio 	}
61429328a94Sclaudio 
61504349dffSclaudio 	return (out);
61604349dffSclaudio 
61704349dffSclaudio fail:
61804349dffSclaudio 	ibuf_free(out);
61904349dffSclaudio 	return (NULL);
62029328a94Sclaudio }
62129328a94Sclaudio 
6225c4d2233Sclaudio static const u_char	addrmask[] = { 0x00, 0x80, 0xc0, 0xe0, 0xf0,
6235c4d2233Sclaudio 			    0xf8, 0xfc, 0xfe, 0xff };
6245c4d2233Sclaudio 
6256d3e8673Sclaudio /* NLRI functions to extract prefixes from the NLRI blobs */
6260f144400Sclaudio int
6270f144400Sclaudio extract_prefix(const u_char *p, int len, void *va, uint8_t pfxlen, uint8_t max)
6286d3e8673Sclaudio {
6296d3e8673Sclaudio 	u_char		*a = va;
6303f0f322fSclaudio 	int		 plen;
6316d3e8673Sclaudio 
6323f0f322fSclaudio 	plen = PREFIX_SIZE(pfxlen) - 1;
6333f0f322fSclaudio 	if (len < plen || max < plen)
6343f0f322fSclaudio 		return -1;
6353f0f322fSclaudio 
6363f0f322fSclaudio 	while (pfxlen > 0) {
6376d3e8673Sclaudio 		if (pfxlen < 8) {
6383f0f322fSclaudio 			*a++ = *p++ & addrmask[pfxlen];
6396d3e8673Sclaudio 			break;
6406d3e8673Sclaudio 		} else {
6413f0f322fSclaudio 			*a++ = *p++;
6426d3e8673Sclaudio 			pfxlen -= 8;
6436d3e8673Sclaudio 		}
6446d3e8673Sclaudio 	}
6456d3e8673Sclaudio 	return (plen);
6466d3e8673Sclaudio }
6476d3e8673Sclaudio 
6485c4d2233Sclaudio static int
6495c4d2233Sclaudio extract_prefix_buf(struct ibuf *buf, void *va, uint8_t pfxlen, uint8_t max)
6506d3e8673Sclaudio {
6515c4d2233Sclaudio 	u_char		*a = va;
6525c4d2233Sclaudio 	unsigned int	 plen;
6535c4d2233Sclaudio 	uint8_t		 tmp;
6545c4d2233Sclaudio 
6555c4d2233Sclaudio 	plen = PREFIX_SIZE(pfxlen) - 1;
6565c4d2233Sclaudio 	if (ibuf_size(buf) < plen || max < plen)
6575c4d2233Sclaudio 		return -1;
6585c4d2233Sclaudio 
6595c4d2233Sclaudio 	while (pfxlen > 0) {
6605c4d2233Sclaudio 		if (ibuf_get_n8(buf, &tmp) == -1)
6615c4d2233Sclaudio 			return -1;
6625c4d2233Sclaudio 
6635c4d2233Sclaudio 		if (pfxlen < 8) {
6645c4d2233Sclaudio 			*a++ = tmp & addrmask[pfxlen];
6655c4d2233Sclaudio 			break;
6665c4d2233Sclaudio 		} else {
6675c4d2233Sclaudio 			*a++ = tmp;
6685c4d2233Sclaudio 			pfxlen -= 8;
6695c4d2233Sclaudio 		}
6705c4d2233Sclaudio 	}
6715c4d2233Sclaudio 	return (0);
6725c4d2233Sclaudio }
6735c4d2233Sclaudio 
6745c4d2233Sclaudio int
6755c4d2233Sclaudio nlri_get_prefix(struct ibuf *buf, struct bgpd_addr *prefix, uint8_t *prefixlen)
6765c4d2233Sclaudio {
67739386878Sclaudio 	uint8_t	 pfxlen;
6786d3e8673Sclaudio 
6795c4d2233Sclaudio 	if (ibuf_get_n8(buf, &pfxlen) == -1)
6806d3e8673Sclaudio 		return (-1);
6815c4d2233Sclaudio 	if (pfxlen > 32)
6825c4d2233Sclaudio 		return (-1);
6836d3e8673Sclaudio 
684eafe309eSclaudio 	memset(prefix, 0, sizeof(struct bgpd_addr));
6856d3e8673Sclaudio 	prefix->aid = AID_INET;
6865c4d2233Sclaudio 
6875c4d2233Sclaudio 	if (extract_prefix_buf(buf, &prefix->v4, pfxlen,
6885c4d2233Sclaudio 	    sizeof(prefix->v4)) == -1)
6895c4d2233Sclaudio 		return (-1);
6905c4d2233Sclaudio 
6916d3e8673Sclaudio 	*prefixlen = pfxlen;
6925c4d2233Sclaudio 	return (0);
6936d3e8673Sclaudio }
6946d3e8673Sclaudio 
6956d3e8673Sclaudio int
6965c4d2233Sclaudio nlri_get_prefix6(struct ibuf *buf, struct bgpd_addr *prefix, uint8_t *prefixlen)
6976d3e8673Sclaudio {
69839386878Sclaudio 	uint8_t	pfxlen;
6996d3e8673Sclaudio 
7005c4d2233Sclaudio 	if (ibuf_get_n8(buf, &pfxlen) == -1)
7016d3e8673Sclaudio 		return (-1);
7025c4d2233Sclaudio 	if (pfxlen > 128)
7035c4d2233Sclaudio 		return (-1);
7046d3e8673Sclaudio 
705eafe309eSclaudio 	memset(prefix, 0, sizeof(struct bgpd_addr));
7066d3e8673Sclaudio 	prefix->aid = AID_INET6;
7075c4d2233Sclaudio 
7085c4d2233Sclaudio 	if (extract_prefix_buf(buf, &prefix->v6, pfxlen,
7095c4d2233Sclaudio 	    sizeof(prefix->v6)) == -1)
7105c4d2233Sclaudio 		return (-1);
7115c4d2233Sclaudio 
7126d3e8673Sclaudio 	*prefixlen = pfxlen;
7135c4d2233Sclaudio 	return (0);
7146d3e8673Sclaudio }
7156d3e8673Sclaudio 
7166d3e8673Sclaudio int
7175c4d2233Sclaudio nlri_get_vpn4(struct ibuf *buf, struct bgpd_addr *prefix,
71839386878Sclaudio     uint8_t *prefixlen, int withdraw)
7196d3e8673Sclaudio {
7205c4d2233Sclaudio 	int		 done = 0;
72139386878Sclaudio 	uint8_t		 pfxlen;
7226d3e8673Sclaudio 
7235c4d2233Sclaudio 	if (ibuf_get_n8(buf, &pfxlen) == -1)
7246d3e8673Sclaudio 		return (-1);
7256d3e8673Sclaudio 
726eafe309eSclaudio 	memset(prefix, 0, sizeof(struct bgpd_addr));
7275c4d2233Sclaudio 	prefix->aid = AID_VPN_IPv4;
7286d3e8673Sclaudio 
7296d3e8673Sclaudio 	/* label stack */
7306d3e8673Sclaudio 	do {
7315c4d2233Sclaudio 		if (prefix->labellen + 3U > sizeof(prefix->labelstack) ||
7325c4d2233Sclaudio 		    pfxlen < 3 * 8)
7336d3e8673Sclaudio 			return (-1);
7346d3e8673Sclaudio 		if (withdraw) {
7356d3e8673Sclaudio 			/* on withdraw ignore the labelstack all together */
7365c4d2233Sclaudio 			if (ibuf_skip(buf, 3) == -1)
7375c4d2233Sclaudio 				return (-1);
7386d3e8673Sclaudio 			pfxlen -= 3 * 8;
7396d3e8673Sclaudio 			break;
7406d3e8673Sclaudio 		}
7415c4d2233Sclaudio 		if (ibuf_get(buf, &prefix->labelstack[prefix->labellen], 3) ==
7425c4d2233Sclaudio 		    -1)
7435c4d2233Sclaudio 			return -1;
7445c4d2233Sclaudio 		if (prefix->labelstack[prefix->labellen + 2] &
7456d3e8673Sclaudio 		    BGP_MPLS_BOS)
7466d3e8673Sclaudio 			done = 1;
7475c4d2233Sclaudio 		prefix->labellen += 3;
7486d3e8673Sclaudio 		pfxlen -= 3 * 8;
7496d3e8673Sclaudio 	} while (!done);
7506d3e8673Sclaudio 
7516d3e8673Sclaudio 	/* RD */
7525c4d2233Sclaudio 	if (pfxlen < sizeof(uint64_t) * 8 ||
7535c4d2233Sclaudio 	    ibuf_get_h64(buf, &prefix->rd) == -1)
7546d3e8673Sclaudio 		return (-1);
75539386878Sclaudio 	pfxlen -= sizeof(uint64_t) * 8;
7566d3e8673Sclaudio 
7576d3e8673Sclaudio 	/* prefix */
7586d3e8673Sclaudio 	if (pfxlen > 32)
7596d3e8673Sclaudio 		return (-1);
7605c4d2233Sclaudio 	if (extract_prefix_buf(buf, &prefix->v4, pfxlen,
7615c4d2233Sclaudio 	    sizeof(prefix->v4)) == -1)
7626d3e8673Sclaudio 		return (-1);
7636d3e8673Sclaudio 
7645c4d2233Sclaudio 	*prefixlen = pfxlen;
7655c4d2233Sclaudio 	return (0);
7666d3e8673Sclaudio }
7676d3e8673Sclaudio 
768290f96faSdenis int
7695c4d2233Sclaudio nlri_get_vpn6(struct ibuf *buf, struct bgpd_addr *prefix,
77039386878Sclaudio     uint8_t *prefixlen, int withdraw)
771290f96faSdenis {
7725c4d2233Sclaudio 	int		done = 0;
77339386878Sclaudio 	uint8_t		pfxlen;
774290f96faSdenis 
7755c4d2233Sclaudio 	if (ibuf_get_n8(buf, &pfxlen) == -1)
776290f96faSdenis 		return (-1);
777290f96faSdenis 
778290f96faSdenis 	memset(prefix, 0, sizeof(struct bgpd_addr));
7795c4d2233Sclaudio 	prefix->aid = AID_VPN_IPv6;
780290f96faSdenis 
781290f96faSdenis 	/* label stack */
782290f96faSdenis 	do {
7835c4d2233Sclaudio 		if (prefix->labellen + 3U > sizeof(prefix->labelstack) ||
7845c4d2233Sclaudio 		    pfxlen < 3 * 8)
785290f96faSdenis 			return (-1);
786290f96faSdenis 		if (withdraw) {
787290f96faSdenis 			/* on withdraw ignore the labelstack all together */
7885c4d2233Sclaudio 			if (ibuf_skip(buf, 3) == -1)
7895c4d2233Sclaudio 				return (-1);
790290f96faSdenis 			pfxlen -= 3 * 8;
791290f96faSdenis 			break;
792290f96faSdenis 		}
793290f96faSdenis 
7945c4d2233Sclaudio 		if (ibuf_get(buf, &prefix->labelstack[prefix->labellen], 3) ==
7955c4d2233Sclaudio 		    -1)
7965c4d2233Sclaudio 			return (-1);
7975c4d2233Sclaudio 		if (prefix->labelstack[prefix->labellen + 2] &
798290f96faSdenis 		    BGP_MPLS_BOS)
799290f96faSdenis 			done = 1;
8005c4d2233Sclaudio 		prefix->labellen += 3;
801290f96faSdenis 		pfxlen -= 3 * 8;
802290f96faSdenis 	} while (!done);
803290f96faSdenis 
804290f96faSdenis 	/* RD */
8055c4d2233Sclaudio 	if (pfxlen < sizeof(uint64_t) * 8 ||
8065c4d2233Sclaudio 	    ibuf_get_h64(buf, &prefix->rd) == -1)
807290f96faSdenis 		return (-1);
80839386878Sclaudio 	pfxlen -= sizeof(uint64_t) * 8;
809290f96faSdenis 
810290f96faSdenis 	/* prefix */
811290f96faSdenis 	if (pfxlen > 128)
812290f96faSdenis 		return (-1);
8135c4d2233Sclaudio 	if (extract_prefix_buf(buf, &prefix->v6, pfxlen,
8145c4d2233Sclaudio 	    sizeof(prefix->v6)) == -1)
815290f96faSdenis 		return (-1);
816290f96faSdenis 
8175c4d2233Sclaudio 	*prefixlen = pfxlen;
8185c4d2233Sclaudio 	return (0);
819290f96faSdenis }
820290f96faSdenis 
821fa3a38bbSclaudio static in_addr_t
822fa3a38bbSclaudio prefixlen2mask(uint8_t prefixlen)
823fa3a38bbSclaudio {
824fa3a38bbSclaudio 	if (prefixlen == 0)
825fa3a38bbSclaudio 		return (0);
826290f96faSdenis 
827fa3a38bbSclaudio 	return (0xffffffff << (32 - prefixlen));
828fa3a38bbSclaudio }
829290f96faSdenis 
83029328a94Sclaudio /*
831de5c2eedSclaudio  * This function will have undefined behaviour if the passed in prefixlen is
832290f96faSdenis  * too large for the respective bgpd_addr address family.
833de5c2eedSclaudio  */
834fafbb788Sclaudio int
835fafbb788Sclaudio prefix_compare(const struct bgpd_addr *a, const struct bgpd_addr *b,
836fafbb788Sclaudio     int prefixlen)
837fafbb788Sclaudio {
838fafbb788Sclaudio 	in_addr_t	mask, aa, ba;
839fafbb788Sclaudio 	int		i;
84039386878Sclaudio 	uint8_t		m;
841fafbb788Sclaudio 
842fafbb788Sclaudio 	if (a->aid != b->aid)
843fafbb788Sclaudio 		return (a->aid - b->aid);
844fafbb788Sclaudio 
845fafbb788Sclaudio 	switch (a->aid) {
8463038d3d1Sclaudio 	case AID_VPN_IPv4:
8473038d3d1Sclaudio 		if (be64toh(a->rd) > be64toh(b->rd))
8483038d3d1Sclaudio 			return (1);
8493038d3d1Sclaudio 		if (be64toh(a->rd) < be64toh(b->rd))
8503038d3d1Sclaudio 			return (-1);
8513038d3d1Sclaudio 		/* FALLTHROUGH */
852fafbb788Sclaudio 	case AID_INET:
8537da59fecSclaudio 		if (prefixlen == 0)
8547da59fecSclaudio 			return (0);
855fafbb788Sclaudio 		if (prefixlen > 32)
856de5c2eedSclaudio 			return (-1);
857fafbb788Sclaudio 		mask = htonl(prefixlen2mask(prefixlen));
858fafbb788Sclaudio 		aa = ntohl(a->v4.s_addr & mask);
859fafbb788Sclaudio 		ba = ntohl(b->v4.s_addr & mask);
8603038d3d1Sclaudio 		if (aa > ba)
8613038d3d1Sclaudio 			return (1);
8623038d3d1Sclaudio 		if (aa < ba)
8633038d3d1Sclaudio 			return (-1);
8643038d3d1Sclaudio 		break;
8653038d3d1Sclaudio 	case AID_VPN_IPv6:
8663038d3d1Sclaudio 		if (be64toh(a->rd) > be64toh(b->rd))
8673038d3d1Sclaudio 			return (1);
8683038d3d1Sclaudio 		if (be64toh(a->rd) < be64toh(b->rd))
8693038d3d1Sclaudio 			return (-1);
8703038d3d1Sclaudio 		/* FALLTHROUGH */
871fafbb788Sclaudio 	case AID_INET6:
8727da59fecSclaudio 		if (prefixlen == 0)
8737da59fecSclaudio 			return (0);
874fafbb788Sclaudio 		if (prefixlen > 128)
875de5c2eedSclaudio 			return (-1);
876fafbb788Sclaudio 		for (i = 0; i < prefixlen / 8; i++)
877fafbb788Sclaudio 			if (a->v6.s6_addr[i] != b->v6.s6_addr[i])
878fafbb788Sclaudio 				return (a->v6.s6_addr[i] - b->v6.s6_addr[i]);
879fafbb788Sclaudio 		i = prefixlen % 8;
880fafbb788Sclaudio 		if (i) {
881fafbb788Sclaudio 			m = 0xff00 >> i;
882fafbb788Sclaudio 			if ((a->v6.s6_addr[prefixlen / 8] & m) !=
883fafbb788Sclaudio 			    (b->v6.s6_addr[prefixlen / 8] & m))
884fafbb788Sclaudio 				return ((a->v6.s6_addr[prefixlen / 8] & m) -
885fafbb788Sclaudio 				    (b->v6.s6_addr[prefixlen / 8] & m));
886fafbb788Sclaudio 		}
8873038d3d1Sclaudio 		break;
8883038d3d1Sclaudio 	default:
8893038d3d1Sclaudio 		return (-1);
8903038d3d1Sclaudio 	}
8913038d3d1Sclaudio 
8923038d3d1Sclaudio 	if (a->aid == AID_VPN_IPv4 || a->aid == AID_VPN_IPv6) {
8933038d3d1Sclaudio 		if (a->labellen > b->labellen)
8943038d3d1Sclaudio 			return (1);
8953038d3d1Sclaudio 		if (a->labellen < b->labellen)
8963038d3d1Sclaudio 			return (-1);
8973038d3d1Sclaudio 		return (memcmp(a->labelstack, b->labelstack, a->labellen));
8983038d3d1Sclaudio 	}
899fafbb788Sclaudio 	return (0);
9003038d3d1Sclaudio 
901fafbb788Sclaudio }
902fafbb788Sclaudio 
90321a825c9Sclaudio void
9042b5c88feSclaudio inet4applymask(struct in_addr *dest, const struct in_addr *src, int prefixlen)
9052b5c88feSclaudio {
9062b5c88feSclaudio 	struct in_addr mask;
9072b5c88feSclaudio 
9082b5c88feSclaudio 	mask.s_addr = htonl(prefixlen2mask(prefixlen));
9092b5c88feSclaudio 	dest->s_addr = src->s_addr & mask.s_addr;
9102b5c88feSclaudio }
9112b5c88feSclaudio 
9122b5c88feSclaudio void
91321a825c9Sclaudio inet6applymask(struct in6_addr *dest, const struct in6_addr *src, int prefixlen)
91421a825c9Sclaudio {
91521a825c9Sclaudio 	struct in6_addr	mask;
91621a825c9Sclaudio 	int		i;
91721a825c9Sclaudio 
918eafe309eSclaudio 	memset(&mask, 0, sizeof(mask));
91921a825c9Sclaudio 	for (i = 0; i < prefixlen / 8; i++)
92021a825c9Sclaudio 		mask.s6_addr[i] = 0xff;
92121a825c9Sclaudio 	i = prefixlen % 8;
92221a825c9Sclaudio 	if (i)
92321a825c9Sclaudio 		mask.s6_addr[prefixlen / 8] = 0xff00 >> i;
92421a825c9Sclaudio 
92521a825c9Sclaudio 	for (i = 0; i < 16; i++)
92621a825c9Sclaudio 		dest->s6_addr[i] = src->s6_addr[i] & mask.s6_addr[i];
92721a825c9Sclaudio }
928d6c2e4e8Sclaudio 
92913bcf54fSclaudio void
93013bcf54fSclaudio applymask(struct bgpd_addr *dest, const struct bgpd_addr *src, int prefixlen)
93113bcf54fSclaudio {
93213bcf54fSclaudio 	*dest = *src;
93313bcf54fSclaudio 	switch (src->aid) {
93413bcf54fSclaudio 	case AID_INET:
93513bcf54fSclaudio 	case AID_VPN_IPv4:
93613bcf54fSclaudio 		inet4applymask(&dest->v4, &src->v4, prefixlen);
93713bcf54fSclaudio 		break;
93813bcf54fSclaudio 	case AID_INET6:
93913bcf54fSclaudio 	case AID_VPN_IPv6:
94013bcf54fSclaudio 		inet6applymask(&dest->v6, &src->v6, prefixlen);
94113bcf54fSclaudio 		break;
94213bcf54fSclaudio 	}
94313bcf54fSclaudio }
94413bcf54fSclaudio 
945d6c2e4e8Sclaudio /* address family translation functions */
946d6c2e4e8Sclaudio const struct aid aid_vals[AID_MAX] = AID_VALS;
947d6c2e4e8Sclaudio 
94886729c90Sclaudio const char *
94939386878Sclaudio aid2str(uint8_t aid)
95086729c90Sclaudio {
95186729c90Sclaudio 	if (aid < AID_MAX)
95286729c90Sclaudio 		return (aid_vals[aid].name);
95386729c90Sclaudio 	return ("unknown AID");
95486729c90Sclaudio }
95586729c90Sclaudio 
956d6c2e4e8Sclaudio int
95739386878Sclaudio aid2afi(uint8_t aid, uint16_t *afi, uint8_t *safi)
958d6c2e4e8Sclaudio {
959110c1584Sclaudio 	if (aid != AID_UNSPEC && aid < AID_MAX) {
960d6c2e4e8Sclaudio 		*afi = aid_vals[aid].afi;
961d6c2e4e8Sclaudio 		*safi = aid_vals[aid].safi;
962d6c2e4e8Sclaudio 		return (0);
963d6c2e4e8Sclaudio 	}
964d6c2e4e8Sclaudio 	return (-1);
965d6c2e4e8Sclaudio }
966d6c2e4e8Sclaudio 
967d6c2e4e8Sclaudio int
96839386878Sclaudio afi2aid(uint16_t afi, uint8_t safi, uint8_t *aid)
969d6c2e4e8Sclaudio {
97039386878Sclaudio 	uint8_t i;
971d6c2e4e8Sclaudio 
972110c1584Sclaudio 	for (i = AID_MIN; i < AID_MAX; i++)
973d6c2e4e8Sclaudio 		if (aid_vals[i].afi == afi && aid_vals[i].safi == safi) {
974d6c2e4e8Sclaudio 			*aid = i;
975d6c2e4e8Sclaudio 			return (0);
976d6c2e4e8Sclaudio 		}
977d6c2e4e8Sclaudio 
978d6c2e4e8Sclaudio 	return (-1);
979d6c2e4e8Sclaudio }
980d6c2e4e8Sclaudio 
981d6c2e4e8Sclaudio sa_family_t
98239386878Sclaudio aid2af(uint8_t aid)
983d6c2e4e8Sclaudio {
984d6c2e4e8Sclaudio 	if (aid < AID_MAX)
985d6c2e4e8Sclaudio 		return (aid_vals[aid].af);
986d6c2e4e8Sclaudio 	return (AF_UNSPEC);
987d6c2e4e8Sclaudio }
988d6c2e4e8Sclaudio 
989d6c2e4e8Sclaudio int
99039386878Sclaudio af2aid(sa_family_t af, uint8_t safi, uint8_t *aid)
991d6c2e4e8Sclaudio {
99239386878Sclaudio 	uint8_t i;
993d6c2e4e8Sclaudio 
994d6c2e4e8Sclaudio 	if (safi == 0) /* default to unicast subclass */
995d6c2e4e8Sclaudio 		safi = SAFI_UNICAST;
996d6c2e4e8Sclaudio 
997110c1584Sclaudio 	for (i = AID_UNSPEC; i < AID_MAX; i++)
998d6c2e4e8Sclaudio 		if (aid_vals[i].af == af && aid_vals[i].safi == safi) {
999d6c2e4e8Sclaudio 			*aid = i;
1000d6c2e4e8Sclaudio 			return (0);
1001d6c2e4e8Sclaudio 		}
1002d6c2e4e8Sclaudio 
1003d6c2e4e8Sclaudio 	return (-1);
1004d6c2e4e8Sclaudio }
1005d6c2e4e8Sclaudio 
100645350f87Sclaudio /*
100745350f87Sclaudio  * Convert a struct bgpd_addr into a struct sockaddr. For VPN addresses
100845350f87Sclaudio  * the included label stack is ignored and needs to be handled by the caller.
100945350f87Sclaudio  */
1010d6c2e4e8Sclaudio struct sockaddr *
101139386878Sclaudio addr2sa(const struct bgpd_addr *addr, uint16_t port, socklen_t *len)
1012d6c2e4e8Sclaudio {
1013d6c2e4e8Sclaudio 	static struct sockaddr_storage	 ss;
1014d6c2e4e8Sclaudio 	struct sockaddr_in		*sa_in = (struct sockaddr_in *)&ss;
1015d6c2e4e8Sclaudio 	struct sockaddr_in6		*sa_in6 = (struct sockaddr_in6 *)&ss;
1016d6c2e4e8Sclaudio 
10174886db4cSclaudio 	if (addr == NULL || addr->aid == AID_UNSPEC)
10184886db4cSclaudio 		return (NULL);
101945350f87Sclaudio 
1020eafe309eSclaudio 	memset(&ss, 0, sizeof(ss));
1021d6c2e4e8Sclaudio 	switch (addr->aid) {
1022d6c2e4e8Sclaudio 	case AID_INET:
10233038d3d1Sclaudio 	case AID_VPN_IPv4:
1024d6c2e4e8Sclaudio 		sa_in->sin_family = AF_INET;
1025d6c2e4e8Sclaudio 		sa_in->sin_addr.s_addr = addr->v4.s_addr;
1026d6c2e4e8Sclaudio 		sa_in->sin_port = htons(port);
1027255fe563Sclaudio 		*len = sizeof(struct sockaddr_in);
1028d6c2e4e8Sclaudio 		break;
1029d6c2e4e8Sclaudio 	case AID_INET6:
103045350f87Sclaudio 	case AID_VPN_IPv6:
103145350f87Sclaudio 		sa_in6->sin6_family = AF_INET6;
10323038d3d1Sclaudio 		memcpy(&sa_in6->sin6_addr, &addr->v6,
103345350f87Sclaudio 		    sizeof(sa_in6->sin6_addr));
103445350f87Sclaudio 		sa_in6->sin6_port = htons(port);
103545350f87Sclaudio 		sa_in6->sin6_scope_id = addr->scope_id;
103645350f87Sclaudio 		*len = sizeof(struct sockaddr_in6);
103745350f87Sclaudio 		break;
103841c1c374Sclaudio 	case AID_FLOWSPECv4:
103941c1c374Sclaudio 	case AID_FLOWSPECv6:
104041c1c374Sclaudio 		return (NULL);
1041d6c2e4e8Sclaudio 	}
1042d6c2e4e8Sclaudio 
1043d6c2e4e8Sclaudio 	return ((struct sockaddr *)&ss);
1044d6c2e4e8Sclaudio }
1045d6c2e4e8Sclaudio 
1046d6c2e4e8Sclaudio void
104739386878Sclaudio sa2addr(struct sockaddr *sa, struct bgpd_addr *addr, uint16_t *port)
1048d6c2e4e8Sclaudio {
1049d6c2e4e8Sclaudio 	struct sockaddr_in		*sa_in = (struct sockaddr_in *)sa;
1050d6c2e4e8Sclaudio 	struct sockaddr_in6		*sa_in6 = (struct sockaddr_in6 *)sa;
1051d6c2e4e8Sclaudio 
1052eafe309eSclaudio 	memset(addr, 0, sizeof(*addr));
1053d6c2e4e8Sclaudio 	switch (sa->sa_family) {
1054d6c2e4e8Sclaudio 	case AF_INET:
1055d6c2e4e8Sclaudio 		addr->aid = AID_INET;
1056d6c2e4e8Sclaudio 		memcpy(&addr->v4, &sa_in->sin_addr, sizeof(addr->v4));
1057a27d9e33Sclaudio 		if (port)
1058a27d9e33Sclaudio 			*port = ntohs(sa_in->sin_port);
1059d6c2e4e8Sclaudio 		break;
1060d6c2e4e8Sclaudio 	case AF_INET6:
1061d6c2e4e8Sclaudio 		addr->aid = AID_INET6;
1062be6ced5eSclaudio #ifdef __KAME__
1063be6ced5eSclaudio 		/*
1064be6ced5eSclaudio 		 * XXX thanks, KAME, for this ugliness...
1065be6ced5eSclaudio 		 * adopted from route/show.c
1066be6ced5eSclaudio 		 */
10675177244fSclaudio 		if ((IN6_IS_ADDR_LINKLOCAL(&sa_in6->sin6_addr) ||
10685177244fSclaudio 		    IN6_IS_ADDR_MC_LINKLOCAL(&sa_in6->sin6_addr) ||
10694ff2dba3Sclaudio 		    IN6_IS_ADDR_MC_NODELOCAL(&sa_in6->sin6_addr)) &&
10705177244fSclaudio 		    sa_in6->sin6_scope_id == 0) {
1071be6ced5eSclaudio 			uint16_t tmp16;
1072be6ced5eSclaudio 			memcpy(&tmp16, &sa_in6->sin6_addr.s6_addr[2],
1073be6ced5eSclaudio 			    sizeof(tmp16));
1074be6ced5eSclaudio 			sa_in6->sin6_scope_id = ntohs(tmp16);
1075be6ced5eSclaudio 			sa_in6->sin6_addr.s6_addr[2] = 0;
1076be6ced5eSclaudio 			sa_in6->sin6_addr.s6_addr[3] = 0;
1077be6ced5eSclaudio 		}
1078be6ced5eSclaudio #endif
10795177244fSclaudio 		memcpy(&addr->v6, &sa_in6->sin6_addr, sizeof(addr->v6));
1080d6c2e4e8Sclaudio 		addr->scope_id = sa_in6->sin6_scope_id; /* I hate v6 */
1081a27d9e33Sclaudio 		if (port)
1082a27d9e33Sclaudio 			*port = ntohs(sa_in6->sin6_port);
1083d6c2e4e8Sclaudio 		break;
1084d6c2e4e8Sclaudio 	}
1085d6c2e4e8Sclaudio }
10866e8089a5Sclaudio 
10876e8089a5Sclaudio const char *
1088bc757ddaSclaudio get_baudrate(unsigned long long baudrate, char *unit)
10896e8089a5Sclaudio {
10906e8089a5Sclaudio 	static char bbuf[16];
10913eaf1285Sclaudio 	const unsigned long long kilo = 1000;
10923eaf1285Sclaudio 	const unsigned long long mega = 1000ULL * kilo;
10933eaf1285Sclaudio 	const unsigned long long giga = 1000ULL * mega;
10946e8089a5Sclaudio 
10953eaf1285Sclaudio 	if (baudrate > giga)
10966e8089a5Sclaudio 		snprintf(bbuf, sizeof(bbuf), "%llu G%s",
10973eaf1285Sclaudio 		    baudrate / giga, unit);
10983eaf1285Sclaudio 	else if (baudrate > mega)
10996e8089a5Sclaudio 		snprintf(bbuf, sizeof(bbuf), "%llu M%s",
11003eaf1285Sclaudio 		    baudrate / mega, unit);
11013eaf1285Sclaudio 	else if (baudrate > kilo)
11026e8089a5Sclaudio 		snprintf(bbuf, sizeof(bbuf), "%llu K%s",
11033eaf1285Sclaudio 		    baudrate / kilo, unit);
11046e8089a5Sclaudio 	else
11056e8089a5Sclaudio 		snprintf(bbuf, sizeof(bbuf), "%llu %s",
11066e8089a5Sclaudio 		    baudrate, unit);
11076e8089a5Sclaudio 
11086e8089a5Sclaudio 	return (bbuf);
11096e8089a5Sclaudio }
1110