xref: /openbsd/usr.sbin/bgpd/util.c (revision 9ad78aa8)
1*9ad78aa8Sclaudio /*	$OpenBSD: util.c,v 1.86 2024/05/29 10:34:07 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 
35eff7ddafSclaudio char *
ibuf_get_string(struct ibuf * buf,size_t len)36eff7ddafSclaudio ibuf_get_string(struct ibuf *buf, size_t len)
37eff7ddafSclaudio {
38eff7ddafSclaudio 	char *str;
39eff7ddafSclaudio 
40eff7ddafSclaudio 	if (ibuf_size(buf) < len) {
41eff7ddafSclaudio 		errno = EBADMSG;
42eff7ddafSclaudio 		return (NULL);
43eff7ddafSclaudio 	}
44eff7ddafSclaudio 	str = strndup(ibuf_data(buf), len);
45eff7ddafSclaudio 	if (str == NULL)
46eff7ddafSclaudio 		return (NULL);
47eff7ddafSclaudio 	ibuf_skip(buf, len);
48eff7ddafSclaudio 	return (str);
49eff7ddafSclaudio }
50eff7ddafSclaudio 
512ffcd4e0Sclaudio const char *
log_addr(const struct bgpd_addr * addr)522ffcd4e0Sclaudio log_addr(const struct bgpd_addr *addr)
532ffcd4e0Sclaudio {
54290f96faSdenis 	static char	buf[74];
5545350f87Sclaudio 	struct sockaddr *sa;
565624d029Sclaudio 	socklen_t	len;
572ffcd4e0Sclaudio 
5845350f87Sclaudio 	sa = addr2sa(addr, 0, &len);
5915d8de66Sclaudio 	switch (addr->aid) {
6015d8de66Sclaudio 	case AID_INET:
6115d8de66Sclaudio 	case AID_INET6:
6245350f87Sclaudio 		return log_sockaddr(sa, len);
6315d8de66Sclaudio 	case AID_VPN_IPv4:
64290f96faSdenis 	case AID_VPN_IPv6:
653038d3d1Sclaudio 		snprintf(buf, sizeof(buf), "%s %s", log_rd(addr->rd),
6645350f87Sclaudio 		    log_sockaddr(sa, len));
67290f96faSdenis 		return (buf);
6815d8de66Sclaudio 	}
6915d8de66Sclaudio 	return ("???");
702ffcd4e0Sclaudio }
712ffcd4e0Sclaudio 
722ffcd4e0Sclaudio const char *
log_in6addr(const struct in6_addr * addr)732ffcd4e0Sclaudio log_in6addr(const struct in6_addr *addr)
742ffcd4e0Sclaudio {
752ffcd4e0Sclaudio 	struct sockaddr_in6	sa_in6;
762ffcd4e0Sclaudio 
77eafe309eSclaudio 	memset(&sa_in6, 0, sizeof(sa_in6));
782ffcd4e0Sclaudio 	sa_in6.sin6_family = AF_INET6;
792ffcd4e0Sclaudio 	memcpy(&sa_in6.sin6_addr, addr, sizeof(sa_in6.sin6_addr));
802ffcd4e0Sclaudio 
81be6ced5eSclaudio #ifdef __KAME__
822ffcd4e0Sclaudio 	/* XXX thanks, KAME, for this ugliness... adopted from route/show.c */
83bdec2ffaStb 	if ((IN6_IS_ADDR_LINKLOCAL(&sa_in6.sin6_addr) ||
84bdec2ffaStb 	    IN6_IS_ADDR_MC_LINKLOCAL(&sa_in6.sin6_addr) ||
854ff2dba3Sclaudio 	    IN6_IS_ADDR_MC_NODELOCAL(&sa_in6.sin6_addr)) &&
86bdec2ffaStb 	    sa_in6.sin6_scope_id == 0) {
8739386878Sclaudio 		uint16_t tmp16;
882ffcd4e0Sclaudio 		memcpy(&tmp16, &sa_in6.sin6_addr.s6_addr[2], sizeof(tmp16));
892ffcd4e0Sclaudio 		sa_in6.sin6_scope_id = ntohs(tmp16);
902ffcd4e0Sclaudio 		sa_in6.sin6_addr.s6_addr[2] = 0;
912ffcd4e0Sclaudio 		sa_in6.sin6_addr.s6_addr[3] = 0;
922ffcd4e0Sclaudio 	}
93be6ced5eSclaudio #endif
942ffcd4e0Sclaudio 
95255fe563Sclaudio 	return (log_sockaddr((struct sockaddr *)&sa_in6, sizeof(sa_in6)));
962ffcd4e0Sclaudio }
972ffcd4e0Sclaudio 
982ffcd4e0Sclaudio const char *
log_sockaddr(struct sockaddr * sa,socklen_t len)99255fe563Sclaudio log_sockaddr(struct sockaddr *sa, socklen_t len)
1002ffcd4e0Sclaudio {
101*9ad78aa8Sclaudio 	static char	buf[4][NI_MAXHOST];
102*9ad78aa8Sclaudio 	static int	bufidx;
1032ffcd4e0Sclaudio 
104*9ad78aa8Sclaudio 	bufidx = (bufidx + 1) % 4;
105*9ad78aa8Sclaudio 	if (sa == NULL || getnameinfo(sa, len, buf[bufidx], sizeof(buf[0]),
106*9ad78aa8Sclaudio 	    NULL, 0, NI_NUMERICHOST))
1072ffcd4e0Sclaudio 		return ("(unknown)");
1082ffcd4e0Sclaudio 	else
109*9ad78aa8Sclaudio 		return (buf[bufidx]);
1102ffcd4e0Sclaudio }
1112ffcd4e0Sclaudio 
1120c88bf70Sclaudio const char *
log_as(uint32_t as)11339386878Sclaudio log_as(uint32_t as)
1140c88bf70Sclaudio {
11506bcde9cSphessler 	static char	buf[11];	/* "4294967294\0" */
1160c88bf70Sclaudio 
117515e489cSderaadt 	if (snprintf(buf, sizeof(buf), "%u", as) < 0)
1180c88bf70Sclaudio 		return ("?");
11906bcde9cSphessler 
1200c88bf70Sclaudio 	return (buf);
1210c88bf70Sclaudio }
1220c88bf70Sclaudio 
123256b680eSclaudio const char *
log_rd(uint64_t rd)12439386878Sclaudio log_rd(uint64_t rd)
125256b680eSclaudio {
126256b680eSclaudio 	static char	buf[32];
127256b680eSclaudio 	struct in_addr	addr;
12839386878Sclaudio 	uint32_t	u32;
12939386878Sclaudio 	uint16_t	u16;
130256b680eSclaudio 
131f4c0eb52Sclaudio 	rd = be64toh(rd);
132256b680eSclaudio 	switch (rd >> 48) {
133bf8e2920Sclaudio 	case EXT_COMMUNITY_TRANS_TWO_AS:
134256b680eSclaudio 		u32 = rd & 0xffffffff;
135256b680eSclaudio 		u16 = (rd >> 32) & 0xffff;
13632ecd2d8Sderaadt 		snprintf(buf, sizeof(buf), "rd %hu:%u", u16, u32);
137256b680eSclaudio 		break;
138bf8e2920Sclaudio 	case EXT_COMMUNITY_TRANS_FOUR_AS:
139256b680eSclaudio 		u32 = (rd >> 16) & 0xffffffff;
140256b680eSclaudio 		u16 = rd & 0xffff;
14132ecd2d8Sderaadt 		snprintf(buf, sizeof(buf), "rd %s:%hu", log_as(u32), u16);
142256b680eSclaudio 		break;
143bf8e2920Sclaudio 	case EXT_COMMUNITY_TRANS_IPV4:
144256b680eSclaudio 		u32 = (rd >> 16) & 0xffffffff;
145256b680eSclaudio 		u16 = rd & 0xffff;
146256b680eSclaudio 		addr.s_addr = htonl(u32);
14732ecd2d8Sderaadt 		snprintf(buf, sizeof(buf), "rd %s:%hu", inet_ntoa(addr), u16);
148256b680eSclaudio 		break;
149256b680eSclaudio 	default:
150ec5cb450Sclaudio 		snprintf(buf, sizeof(buf), "rd #%016llx",
151ec5cb450Sclaudio 		    (unsigned long long)rd);
152ec5cb450Sclaudio 		break;
153256b680eSclaudio 	}
154256b680eSclaudio 	return (buf);
155256b680eSclaudio }
156256b680eSclaudio 
157bf8e2920Sclaudio const struct ext_comm_pairs iana_ext_comms[] = IANA_EXT_COMMUNITIES;
158bf8e2920Sclaudio 
159256b680eSclaudio /* NOTE: this function does not check if the type/subtype combo is
160536f41e5Sclaudio  * actually valid. */
161536f41e5Sclaudio const char *
log_ext_subtype(int type,uint8_t subtype)162f8162053Sclaudio log_ext_subtype(int type, uint8_t subtype)
163536f41e5Sclaudio {
164536f41e5Sclaudio 	static char etype[6];
165bf8e2920Sclaudio 	const struct ext_comm_pairs *cp;
166536f41e5Sclaudio 
167bf8e2920Sclaudio 	for (cp = iana_ext_comms; cp->subname != NULL; cp++) {
1680e6216fdSclaudio 		if ((type == cp->type || type == -1) && subtype == cp->subtype)
169bf8e2920Sclaudio 			return (cp->subname);
170bf8e2920Sclaudio 	}
171d6340f7aSderaadt 	snprintf(etype, sizeof(etype), "[%u]", subtype);
172536f41e5Sclaudio 	return (etype);
173536f41e5Sclaudio }
174536f41e5Sclaudio 
1751e590dcfSclaudio const char *
log_reason(const char * communication)176a78f83ceSderaadt log_reason(const char *communication) {
177a78f83ceSderaadt 	static char buf[(REASON_LEN - 1) * 4 + 1];
1780561b344Sphessler 
1790561b344Sphessler 	strnvis(buf, communication, sizeof(buf), VIS_NL | VIS_OCTAL);
1800561b344Sphessler 
1810561b344Sphessler 	return buf;
1820561b344Sphessler }
1830561b344Sphessler 
1846290e740Sclaudio static const char *
log_expires(time_t expires)1856290e740Sclaudio log_expires(time_t expires)
1866290e740Sclaudio {
1876290e740Sclaudio 	static char buf[32];
1886290e740Sclaudio 
1896290e740Sclaudio 	buf[0] = '\0';
1906290e740Sclaudio 	if (expires != 0)
1916290e740Sclaudio 		snprintf(buf, sizeof(buf), " expires %lld", (long long)expires);
1926290e740Sclaudio 	return buf;
1936290e740Sclaudio }
1946290e740Sclaudio 
1956290e740Sclaudio const char *
log_roa(struct roa * roa)1966290e740Sclaudio log_roa(struct roa *roa)
1976290e740Sclaudio {
1986290e740Sclaudio 	static char buf[256];
1996290e740Sclaudio 	char maxbuf[32];
2002fd9f52dSmiod #if defined(__GNUC__) && __GNUC__ < 4
2012fd9f52dSmiod 	struct bgpd_addr addr = { .aid = roa->aid };
2022fd9f52dSmiod 	addr.v6 = roa->prefix.inet6;
2032fd9f52dSmiod #else
2042fd9f52dSmiod 	struct bgpd_addr addr = { .aid = roa->aid, .v6 = roa->prefix.inet6 };
2052fd9f52dSmiod #endif
2066290e740Sclaudio 
2076290e740Sclaudio 	maxbuf[0] = '\0';
2086290e740Sclaudio 	if (roa->prefixlen != roa->maxlen)
2096290e740Sclaudio 		snprintf(maxbuf, sizeof(maxbuf), " maxlen %u", roa->maxlen);
2106290e740Sclaudio 	snprintf(buf, sizeof(buf), "%s/%u%s source-as %u%s", log_addr(&addr),
2116290e740Sclaudio 	    roa->prefixlen, maxbuf, roa->asnum, log_expires(roa->expires));
2126290e740Sclaudio 	return buf;
2136290e740Sclaudio }
2146290e740Sclaudio 
2156290e740Sclaudio const char *
log_aspa(struct aspa_set * aspa)2166290e740Sclaudio log_aspa(struct aspa_set *aspa)
2176290e740Sclaudio {
2186290e740Sclaudio 	static char errbuf[256];
2196290e740Sclaudio 	static char *buf;
2206290e740Sclaudio 	static size_t len;
2216290e740Sclaudio 	char asbuf[16];
2226290e740Sclaudio 	size_t needed;
2236290e740Sclaudio 	uint32_t i;
2246290e740Sclaudio 
2256290e740Sclaudio 	/* include enough space for header and trailer */
2266290e740Sclaudio 	if ((uint64_t)aspa->num > (SIZE_MAX / sizeof(asbuf) - 72))
2276290e740Sclaudio 		goto fail;
2286290e740Sclaudio 	needed = aspa->num * sizeof(asbuf) + 72;
2296290e740Sclaudio 	if (needed > len) {
2306290e740Sclaudio 		char *nbuf;
2316290e740Sclaudio 
2326290e740Sclaudio 		if ((nbuf = realloc(buf, needed)) == NULL)
2336290e740Sclaudio 			goto fail;
2346290e740Sclaudio 		len = needed;
2356290e740Sclaudio 		buf = nbuf;
2366290e740Sclaudio 	}
2376290e740Sclaudio 
2386290e740Sclaudio 	snprintf(buf, len, "customer-as %s%s provider-as { ",
2396290e740Sclaudio 	    log_as(aspa->as), log_expires(aspa->expires));
2406290e740Sclaudio 
2416290e740Sclaudio 	for (i = 0; i < aspa->num; i++) {
2426290e740Sclaudio 		snprintf(asbuf, sizeof(asbuf), "%s ", log_as(aspa->tas[i]));
2436290e740Sclaudio 		if (strlcat(buf, asbuf, len) >= len)
2446290e740Sclaudio 			goto fail;
2456290e740Sclaudio 	}
2466290e740Sclaudio 	if (strlcat(buf, "}", len) >= len)
2476290e740Sclaudio 		goto fail;
2486290e740Sclaudio 	return buf;
2496290e740Sclaudio 
2506290e740Sclaudio  fail:
2516290e740Sclaudio 	free(buf);
2526290e740Sclaudio 	buf = NULL;
2536290e740Sclaudio 	len = 0;
2546290e740Sclaudio 	snprintf(errbuf, sizeof(errbuf), "customer-as %s%s provider-as { ... }",
2556290e740Sclaudio 	    log_as(aspa->as), log_expires(aspa->expires));
2566290e740Sclaudio 	return errbuf;
2576290e740Sclaudio }
2586290e740Sclaudio 
2590561b344Sphessler const char *
log_aspath_error(int error)26004349dffSclaudio log_aspath_error(int error)
26104349dffSclaudio {
26204349dffSclaudio 	static char buf[20];
26304349dffSclaudio 
26404349dffSclaudio 	switch (error) {
26504349dffSclaudio 	case AS_ERR_LEN:
26604349dffSclaudio 		return "inconsitent lenght";
26704349dffSclaudio 	case AS_ERR_TYPE:
26804349dffSclaudio 		return "unknown segment type";
26904349dffSclaudio 	case AS_ERR_BAD:
27004349dffSclaudio 		return "invalid encoding";
27104349dffSclaudio 	case AS_ERR_SOFT:
27204349dffSclaudio 		return "soft failure";
27304349dffSclaudio 	default:
27404349dffSclaudio 		snprintf(buf, sizeof(buf), "unknown %d", error);
27504349dffSclaudio 		return buf;
27604349dffSclaudio 	}
27704349dffSclaudio }
27804349dffSclaudio 
27904349dffSclaudio const char *
log_rtr_error(enum rtr_error err)280bd9df44eSclaudio log_rtr_error(enum rtr_error err)
281bd9df44eSclaudio {
282bd9df44eSclaudio 	static char buf[20];
283bd9df44eSclaudio 
284bd9df44eSclaudio 	switch (err) {
285bd9df44eSclaudio 	case NO_ERROR:
286bd9df44eSclaudio 		return "No Error";
287bd9df44eSclaudio 	case CORRUPT_DATA:
288bd9df44eSclaudio 		return "Corrupt Data";
289bd9df44eSclaudio 	case INTERNAL_ERROR:
290bd9df44eSclaudio 		return "Internal Error";
291bd9df44eSclaudio 	case NO_DATA_AVAILABLE:
292bd9df44eSclaudio 		return "No Data Available";
293bd9df44eSclaudio 	case INVALID_REQUEST:
294bd9df44eSclaudio 		return "Invalid Request";
295bd9df44eSclaudio 	case UNSUPP_PROTOCOL_VERS:
296bd9df44eSclaudio 		return "Unsupported Protocol Version";
297bd9df44eSclaudio 	case UNSUPP_PDU_TYPE:
298bd9df44eSclaudio 		return "Unsupported PDU Type";
299bd9df44eSclaudio 	case UNK_REC_WDRAWL:
300f4123069Smbuhl 		return "Withdrawal of Unknown Record";
301bd9df44eSclaudio 	case DUP_REC_RECV:
302bd9df44eSclaudio 		return "Duplicate Announcement Received";
303bd9df44eSclaudio 	case UNEXP_PROTOCOL_VERS:
304bd9df44eSclaudio 		return "Unexpected Protocol Version";
305bd9df44eSclaudio 	default:
306bd9df44eSclaudio 		snprintf(buf, sizeof(buf), "unknown %u", err);
307bd9df44eSclaudio 		return buf;
308bd9df44eSclaudio 	}
309bd9df44eSclaudio }
310bd9df44eSclaudio 
311bd9df44eSclaudio const char *
log_policy(enum role role)312c0c94bccSclaudio log_policy(enum role role)
313202e5273Stb {
314202e5273Stb 	switch (role) {
315c0c94bccSclaudio 	case ROLE_PROVIDER:
316202e5273Stb 		return "provider";
317c0c94bccSclaudio 	case ROLE_RS:
318202e5273Stb 		return "rs";
319c0c94bccSclaudio 	case ROLE_RS_CLIENT:
320202e5273Stb 		return "rs-client";
321c0c94bccSclaudio 	case ROLE_CUSTOMER:
322202e5273Stb 		return "customer";
323c0c94bccSclaudio 	case ROLE_PEER:
324202e5273Stb 		return "peer";
325202e5273Stb 	default:
326202e5273Stb 		return "unknown";
327202e5273Stb 	}
328202e5273Stb }
329202e5273Stb 
330beb044e9Sclaudio const char *
log_capability(uint8_t capa)331beb044e9Sclaudio log_capability(uint8_t capa)
332beb044e9Sclaudio {
333beb044e9Sclaudio 	static char buf[20];
334beb044e9Sclaudio 
335beb044e9Sclaudio 	switch (capa) {
336beb044e9Sclaudio 	case CAPA_MP:
337beb044e9Sclaudio 		return "Multiprotocol Extensions";
338beb044e9Sclaudio 	case CAPA_REFRESH:
339beb044e9Sclaudio 		return "Route Refresh";
340beb044e9Sclaudio 	case CAPA_ROLE:
341beb044e9Sclaudio 		return "BGP Role";
342beb044e9Sclaudio 	case CAPA_RESTART:
343beb044e9Sclaudio 		return "Graceful Restart";
344beb044e9Sclaudio 	case CAPA_AS4BYTE:
345beb044e9Sclaudio 		return "4-octet AS number";
346beb044e9Sclaudio 	case CAPA_ADD_PATH:
347beb044e9Sclaudio 		return "ADD-PATH";
348beb044e9Sclaudio 	case CAPA_ENHANCED_RR:
349beb044e9Sclaudio 		return "Enhanced Route Refresh";
350beb044e9Sclaudio 	default:
351beb044e9Sclaudio 		snprintf(buf, sizeof(buf), "unknown %u", capa);
352beb044e9Sclaudio 		return buf;
353beb044e9Sclaudio 	}
354beb044e9Sclaudio }
355beb044e9Sclaudio 
35604349dffSclaudio static const char *
aspath_delim(uint8_t seg_type,int closing)35739386878Sclaudio aspath_delim(uint8_t seg_type, int closing)
3581e590dcfSclaudio {
3591e590dcfSclaudio 	static char db[8];
3601e590dcfSclaudio 
3611e590dcfSclaudio 	switch (seg_type) {
3621e590dcfSclaudio 	case AS_SET:
3631e590dcfSclaudio 		if (!closing)
3641e590dcfSclaudio 			return ("{ ");
3651e590dcfSclaudio 		else
3661e590dcfSclaudio 			return (" }");
3671e590dcfSclaudio 	case AS_SEQUENCE:
3681e590dcfSclaudio 		return ("");
3691e590dcfSclaudio 	case AS_CONFED_SEQUENCE:
3701e590dcfSclaudio 		if (!closing)
3711e590dcfSclaudio 			return ("( ");
3721e590dcfSclaudio 		else
3731e590dcfSclaudio 			return (" )");
3741e590dcfSclaudio 	case AS_CONFED_SET:
3751e590dcfSclaudio 		if (!closing)
3761e590dcfSclaudio 			return ("[ ");
3771e590dcfSclaudio 		else
3781e590dcfSclaudio 			return (" ]");
3791e590dcfSclaudio 	default:
3801e590dcfSclaudio 		if (!closing)
3811e590dcfSclaudio 			snprintf(db, sizeof(db), "!%u ", seg_type);
3821e590dcfSclaudio 		else
3831e590dcfSclaudio 			snprintf(db, sizeof(db), " !%u", seg_type);
3841e590dcfSclaudio 		return (db);
3851e590dcfSclaudio 	}
3861e590dcfSclaudio }
3871e590dcfSclaudio 
38804349dffSclaudio static int
aspath_snprint(char * buf,size_t size,struct ibuf * in)38904349dffSclaudio aspath_snprint(char *buf, size_t size, struct ibuf *in)
3902ffcd4e0Sclaudio {
3912ffcd4e0Sclaudio #define UPDATE()						\
3922ffcd4e0Sclaudio 	do {							\
39304349dffSclaudio 		if (r < 0 || (unsigned int)r >= size)		\
3942ffcd4e0Sclaudio 			return (-1);				\
3952ffcd4e0Sclaudio 		size -= r;					\
3962ffcd4e0Sclaudio 		buf += r;					\
3972ffcd4e0Sclaudio 	} while (0)
39804349dffSclaudio 
39904349dffSclaudio 	struct ibuf	data;
40004349dffSclaudio 	uint32_t	as;
40104349dffSclaudio 	int		r, n = 0;
40239386878Sclaudio 	uint8_t		i, seg_type, seg_len;
4032ffcd4e0Sclaudio 
40404349dffSclaudio 	ibuf_from_ibuf(&data, in);
40504349dffSclaudio 	while (ibuf_size(&data) > 0) {
40604349dffSclaudio 		if (ibuf_get_n8(&data, &seg_type) == -1 ||
40704349dffSclaudio 		    ibuf_get_n8(&data, &seg_len) == -1 ||
40804349dffSclaudio 		    seg_len == 0)
40904349dffSclaudio 			return (-1);
4102ffcd4e0Sclaudio 
41104349dffSclaudio 		r = snprintf(buf, size, "%s%s", n++ != 0 ? " " : "",
4121e590dcfSclaudio 		    aspath_delim(seg_type, 0));
4132ffcd4e0Sclaudio 		UPDATE();
4142ffcd4e0Sclaudio 
4152ffcd4e0Sclaudio 		for (i = 0; i < seg_len; i++) {
41604349dffSclaudio 			if (ibuf_get_n32(&data, &as) == -1)
41704349dffSclaudio 				return -1;
41804349dffSclaudio 
41904349dffSclaudio 			r = snprintf(buf, size, "%s", log_as(as));
4202ffcd4e0Sclaudio 			UPDATE();
4212ffcd4e0Sclaudio 			if (i + 1 < seg_len) {
4222ffcd4e0Sclaudio 				r = snprintf(buf, size, " ");
4232ffcd4e0Sclaudio 				UPDATE();
4242ffcd4e0Sclaudio 			}
4252ffcd4e0Sclaudio 		}
4261e590dcfSclaudio 		r = snprintf(buf, size, "%s", aspath_delim(seg_type, 1));
4272ffcd4e0Sclaudio 		UPDATE();
4282ffcd4e0Sclaudio 	}
42955e49665Sclaudio 	/* ensure that we have a valid C-string especially for empty as path */
4302ffcd4e0Sclaudio 	*buf = '\0';
43104349dffSclaudio 	return (0);
4322ffcd4e0Sclaudio #undef UPDATE
4332ffcd4e0Sclaudio }
4342ffcd4e0Sclaudio 
43504349dffSclaudio static ssize_t
aspath_strsize(struct ibuf * in)43604349dffSclaudio aspath_strsize(struct ibuf *in)
4372ffcd4e0Sclaudio {
43804349dffSclaudio 	struct ibuf	 buf;
43904349dffSclaudio 	ssize_t		 total_size = 0;
44039386878Sclaudio 	uint32_t	 as;
44139386878Sclaudio 	uint8_t		 i, seg_type, seg_len;
4422ffcd4e0Sclaudio 
44304349dffSclaudio 	ibuf_from_ibuf(&buf, in);
44404349dffSclaudio 	while (ibuf_size(&buf) > 0) {
44504349dffSclaudio 		if (ibuf_get_n8(&buf, &seg_type) == -1 ||
44604349dffSclaudio 		    ibuf_get_n8(&buf, &seg_len) == -1 ||
44704349dffSclaudio 		    seg_len == 0)
44804349dffSclaudio 			return (-1);
4492ffcd4e0Sclaudio 
4502ffcd4e0Sclaudio 		if (total_size != 0)
4512ffcd4e0Sclaudio 			total_size += 1;
45204349dffSclaudio 		total_size += strlen(aspath_delim(seg_type, 0));
4532ffcd4e0Sclaudio 
4542ffcd4e0Sclaudio 		for (i = 0; i < seg_len; i++) {
45504349dffSclaudio 			if (ibuf_get_n32(&buf, &as) == -1)
45604349dffSclaudio 				return (-1);
4570c88bf70Sclaudio 
4588db4f5d2Sclaudio 			do {
4598db4f5d2Sclaudio 				total_size++;
4608db4f5d2Sclaudio 			} while ((as = as / 10) != 0);
46104349dffSclaudio 		}
46204349dffSclaudio 		total_size += seg_len - 1;
4632ffcd4e0Sclaudio 
46404349dffSclaudio 		total_size += strlen(aspath_delim(seg_type, 1));
46504349dffSclaudio 	}
46604349dffSclaudio 	return (total_size + 1);
4672ffcd4e0Sclaudio }
4682ffcd4e0Sclaudio 
46904349dffSclaudio int
aspath_asprint(char ** ret,struct ibuf * data)47004349dffSclaudio aspath_asprint(char **ret, struct ibuf *data)
47104349dffSclaudio {
47204349dffSclaudio 	ssize_t	slen;
47304349dffSclaudio 
47404349dffSclaudio 	if ((slen = aspath_strsize(data)) == -1) {
47504349dffSclaudio 		*ret = NULL;
47604349dffSclaudio 		errno = EINVAL;
47704349dffSclaudio 		return (-1);
4782ffcd4e0Sclaudio 	}
47904349dffSclaudio 
48004349dffSclaudio 	*ret = malloc(slen);
48104349dffSclaudio 	if (*ret == NULL)
48204349dffSclaudio 		return (-1);
48304349dffSclaudio 
48404349dffSclaudio 	if (aspath_snprint(*ret, slen, data) == -1) {
48504349dffSclaudio 		free(*ret);
48604349dffSclaudio 		*ret = NULL;
48704349dffSclaudio 		errno = EINVAL;
48804349dffSclaudio 		return (-1);
48904349dffSclaudio 	}
49004349dffSclaudio 
49104349dffSclaudio 	return (0);
4922ffcd4e0Sclaudio }
4932ffcd4e0Sclaudio 
4942ffcd4e0Sclaudio /*
4952ffcd4e0Sclaudio  * Extract the asnum out of the as segment at the specified position.
4962ffcd4e0Sclaudio  * Direct access is not possible because of non-aligned reads.
497506f72cfSclaudio  * Only works on verified 4-byte AS paths.
4982ffcd4e0Sclaudio  */
49939386878Sclaudio uint32_t
aspath_extract(const void * seg,int pos)5002ffcd4e0Sclaudio aspath_extract(const void *seg, int pos)
5012ffcd4e0Sclaudio {
5022ffcd4e0Sclaudio 	const u_char	*ptr = seg;
50339386878Sclaudio 	uint32_t	 as;
5042ffcd4e0Sclaudio 
505506f72cfSclaudio 	/* minimal pos check, return 0 since that is an invalid ASN */
506506f72cfSclaudio 	if (pos < 0 || pos >= ptr[1])
507506f72cfSclaudio 		return (0);
50839386878Sclaudio 	ptr += 2 + sizeof(uint32_t) * pos;
50939386878Sclaudio 	memcpy(&as, ptr, sizeof(uint32_t));
5100c88bf70Sclaudio 	return (ntohl(as));
5112ffcd4e0Sclaudio }
51221a825c9Sclaudio 
513de5c2eedSclaudio /*
51429328a94Sclaudio  * Verify that the aspath is correctly encoded.
51529328a94Sclaudio  */
51629328a94Sclaudio int
aspath_verify(struct ibuf * in,int as4byte,int noset)51704349dffSclaudio aspath_verify(struct ibuf *in, int as4byte, int noset)
51829328a94Sclaudio {
51904349dffSclaudio 	struct ibuf	 buf;
52004349dffSclaudio 	int		 pos, error = 0;
52139386878Sclaudio 	uint8_t		 seg_len, seg_type;
52229328a94Sclaudio 
52304349dffSclaudio 	ibuf_from_ibuf(&buf, in);
52404349dffSclaudio 	if (ibuf_size(&buf) & 1) {
52529328a94Sclaudio 		/* odd length aspath are invalid */
52604349dffSclaudio 		error = AS_ERR_BAD;
52704349dffSclaudio 		goto done;
52804349dffSclaudio 	}
52929328a94Sclaudio 
53004349dffSclaudio 	while (ibuf_size(&buf) > 0) {
53104349dffSclaudio 		if (ibuf_get_n8(&buf, &seg_type) == -1 ||
53204349dffSclaudio 		    ibuf_get_n8(&buf, &seg_len) == -1) {
53304349dffSclaudio 			error = AS_ERR_LEN;
53404349dffSclaudio 			goto done;
53504349dffSclaudio 		}
53629328a94Sclaudio 
53704349dffSclaudio 		if (seg_len == 0) {
538d04df938Sclaudio 			/* empty aspath segments are not allowed */
53904349dffSclaudio 			error = AS_ERR_BAD;
54004349dffSclaudio 			goto done;
54104349dffSclaudio 		}
542d04df938Sclaudio 
54329328a94Sclaudio 		/*
54429328a94Sclaudio 		 * BGP confederations should not show up but consider them
54529328a94Sclaudio 		 * as a soft error which invalidates the path but keeps the
54629328a94Sclaudio 		 * bgp session running.
54729328a94Sclaudio 		 */
54829328a94Sclaudio 		if (seg_type == AS_CONFED_SEQUENCE || seg_type == AS_CONFED_SET)
54929328a94Sclaudio 			error = AS_ERR_SOFT;
550aa528464Sclaudio 		/*
551aa528464Sclaudio 		 * If AS_SET filtering (RFC6472) is on, error out on AS_SET
552aa528464Sclaudio 		 * as well.
553aa528464Sclaudio 		 */
554aa528464Sclaudio 		if (noset && seg_type == AS_SET)
555aa528464Sclaudio 			error = AS_ERR_SOFT;
55629328a94Sclaudio 		if (seg_type != AS_SET && seg_type != AS_SEQUENCE &&
55704349dffSclaudio 		    seg_type != AS_CONFED_SEQUENCE &&
55804349dffSclaudio 		    seg_type != AS_CONFED_SET) {
55904349dffSclaudio 			error = AS_ERR_TYPE;
56004349dffSclaudio 			goto done;
56104349dffSclaudio 		}
56229328a94Sclaudio 
56329328a94Sclaudio 		/* RFC 7607 - AS 0 is considered malformed */
56429328a94Sclaudio 		for (pos = 0; pos < seg_len; pos++) {
56539386878Sclaudio 			uint32_t as;
56629328a94Sclaudio 
56704349dffSclaudio 			if (as4byte) {
56804349dffSclaudio 				if (ibuf_get_n32(&buf, &as) == -1) {
56904349dffSclaudio 					error = AS_ERR_LEN;
57004349dffSclaudio 					goto done;
57104349dffSclaudio 				}
57204349dffSclaudio 			} else {
57304349dffSclaudio 				uint16_t tmp;
57404349dffSclaudio 				if (ibuf_get_n16(&buf, &tmp) == -1) {
57504349dffSclaudio 					error = AS_ERR_LEN;
57604349dffSclaudio 					goto done;
57704349dffSclaudio 				}
57804349dffSclaudio 				as = tmp;
57904349dffSclaudio 			}
58029328a94Sclaudio 			if (as == 0)
58156a9a1b8Sclaudio 				error = AS_ERR_SOFT;
58229328a94Sclaudio 		}
58329328a94Sclaudio 	}
58404349dffSclaudio 
58504349dffSclaudio  done:
58629328a94Sclaudio 	return (error);	/* aspath is valid but probably not loop free */
58729328a94Sclaudio }
58829328a94Sclaudio 
58929328a94Sclaudio /*
59029328a94Sclaudio  * convert a 2 byte aspath to a 4 byte one.
59129328a94Sclaudio  */
59204349dffSclaudio struct ibuf *
aspath_inflate(struct ibuf * in)59304349dffSclaudio aspath_inflate(struct ibuf *in)
59429328a94Sclaudio {
59504349dffSclaudio 	struct ibuf	*out;
59604349dffSclaudio 	uint16_t	 short_as;
59704349dffSclaudio 	uint8_t		 seg_type, seg_len;
59829328a94Sclaudio 
5994ac10ff8Sclaudio 	/*
6004ac10ff8Sclaudio 	 * Allocate enough space for the worst case.
6014ac10ff8Sclaudio 	 * XXX add 1 byte for the empty ASPATH case since we can't
6024ac10ff8Sclaudio 	 * allocate an ibuf of 0 length.
6034ac10ff8Sclaudio 	 */
6044ac10ff8Sclaudio 	if ((out = ibuf_open(ibuf_size(in) * 2 + 1)) == NULL)
60529328a94Sclaudio 		return (NULL);
60629328a94Sclaudio 
60729328a94Sclaudio 	/* then copy the aspath */
60804349dffSclaudio 	while (ibuf_size(in) > 0) {
60904349dffSclaudio 		if (ibuf_get_n8(in, &seg_type) == -1 ||
61004349dffSclaudio 		    ibuf_get_n8(in, &seg_len) == -1 ||
61104349dffSclaudio 		    seg_len == 0)
61204349dffSclaudio 			goto fail;
61304349dffSclaudio 		if (ibuf_add_n8(out, seg_type) == -1 ||
61404349dffSclaudio 		    ibuf_add_n8(out, seg_len) == -1)
61504349dffSclaudio 			goto fail;
61604349dffSclaudio 
61729328a94Sclaudio 		for (; seg_len > 0; seg_len--) {
61804349dffSclaudio 			if (ibuf_get_n16(in, &short_as) == -1)
61904349dffSclaudio 				goto fail;
62004349dffSclaudio 			if (ibuf_add_n32(out, short_as) == -1)
62104349dffSclaudio 				goto fail;
62229328a94Sclaudio 		}
62329328a94Sclaudio 	}
62429328a94Sclaudio 
62504349dffSclaudio 	return (out);
62604349dffSclaudio 
62704349dffSclaudio fail:
62804349dffSclaudio 	ibuf_free(out);
62904349dffSclaudio 	return (NULL);
63029328a94Sclaudio }
63129328a94Sclaudio 
6325c4d2233Sclaudio static const u_char	addrmask[] = { 0x00, 0x80, 0xc0, 0xe0, 0xf0,
6335c4d2233Sclaudio 			    0xf8, 0xfc, 0xfe, 0xff };
6345c4d2233Sclaudio 
6356d3e8673Sclaudio /* NLRI functions to extract prefixes from the NLRI blobs */
6360f144400Sclaudio int
extract_prefix(const u_char * p,int len,void * va,uint8_t pfxlen,uint8_t max)6370f144400Sclaudio extract_prefix(const u_char *p, int len, void *va, uint8_t pfxlen, uint8_t max)
6386d3e8673Sclaudio {
6396d3e8673Sclaudio 	u_char		*a = va;
6403f0f322fSclaudio 	int		 plen;
6416d3e8673Sclaudio 
6423f0f322fSclaudio 	plen = PREFIX_SIZE(pfxlen) - 1;
6433f0f322fSclaudio 	if (len < plen || max < plen)
6443f0f322fSclaudio 		return -1;
6453f0f322fSclaudio 
6463f0f322fSclaudio 	while (pfxlen > 0) {
6476d3e8673Sclaudio 		if (pfxlen < 8) {
6483f0f322fSclaudio 			*a++ = *p++ & addrmask[pfxlen];
6496d3e8673Sclaudio 			break;
6506d3e8673Sclaudio 		} else {
6513f0f322fSclaudio 			*a++ = *p++;
6526d3e8673Sclaudio 			pfxlen -= 8;
6536d3e8673Sclaudio 		}
6546d3e8673Sclaudio 	}
6556d3e8673Sclaudio 	return (plen);
6566d3e8673Sclaudio }
6576d3e8673Sclaudio 
6585c4d2233Sclaudio static int
extract_prefix_buf(struct ibuf * buf,void * va,uint8_t pfxlen,uint8_t max)6595c4d2233Sclaudio extract_prefix_buf(struct ibuf *buf, void *va, uint8_t pfxlen, uint8_t max)
6606d3e8673Sclaudio {
6615c4d2233Sclaudio 	u_char		*a = va;
6625c4d2233Sclaudio 	unsigned int	 plen;
6635c4d2233Sclaudio 	uint8_t		 tmp;
6645c4d2233Sclaudio 
6655c4d2233Sclaudio 	plen = PREFIX_SIZE(pfxlen) - 1;
6665c4d2233Sclaudio 	if (ibuf_size(buf) < plen || max < plen)
6675c4d2233Sclaudio 		return -1;
6685c4d2233Sclaudio 
6695c4d2233Sclaudio 	while (pfxlen > 0) {
6705c4d2233Sclaudio 		if (ibuf_get_n8(buf, &tmp) == -1)
6715c4d2233Sclaudio 			return -1;
6725c4d2233Sclaudio 
6735c4d2233Sclaudio 		if (pfxlen < 8) {
6745c4d2233Sclaudio 			*a++ = tmp & addrmask[pfxlen];
6755c4d2233Sclaudio 			break;
6765c4d2233Sclaudio 		} else {
6775c4d2233Sclaudio 			*a++ = tmp;
6785c4d2233Sclaudio 			pfxlen -= 8;
6795c4d2233Sclaudio 		}
6805c4d2233Sclaudio 	}
6815c4d2233Sclaudio 	return (0);
6825c4d2233Sclaudio }
6835c4d2233Sclaudio 
6845c4d2233Sclaudio int
nlri_get_prefix(struct ibuf * buf,struct bgpd_addr * prefix,uint8_t * prefixlen)6855c4d2233Sclaudio nlri_get_prefix(struct ibuf *buf, struct bgpd_addr *prefix, uint8_t *prefixlen)
6865c4d2233Sclaudio {
68739386878Sclaudio 	uint8_t	 pfxlen;
6886d3e8673Sclaudio 
6895c4d2233Sclaudio 	if (ibuf_get_n8(buf, &pfxlen) == -1)
6906d3e8673Sclaudio 		return (-1);
6915c4d2233Sclaudio 	if (pfxlen > 32)
6925c4d2233Sclaudio 		return (-1);
6936d3e8673Sclaudio 
694eafe309eSclaudio 	memset(prefix, 0, sizeof(struct bgpd_addr));
6956d3e8673Sclaudio 	prefix->aid = AID_INET;
6965c4d2233Sclaudio 
6975c4d2233Sclaudio 	if (extract_prefix_buf(buf, &prefix->v4, pfxlen,
6985c4d2233Sclaudio 	    sizeof(prefix->v4)) == -1)
6995c4d2233Sclaudio 		return (-1);
7005c4d2233Sclaudio 
7016d3e8673Sclaudio 	*prefixlen = pfxlen;
7025c4d2233Sclaudio 	return (0);
7036d3e8673Sclaudio }
7046d3e8673Sclaudio 
7056d3e8673Sclaudio int
nlri_get_prefix6(struct ibuf * buf,struct bgpd_addr * prefix,uint8_t * prefixlen)7065c4d2233Sclaudio nlri_get_prefix6(struct ibuf *buf, struct bgpd_addr *prefix, uint8_t *prefixlen)
7076d3e8673Sclaudio {
70839386878Sclaudio 	uint8_t	pfxlen;
7096d3e8673Sclaudio 
7105c4d2233Sclaudio 	if (ibuf_get_n8(buf, &pfxlen) == -1)
7116d3e8673Sclaudio 		return (-1);
7125c4d2233Sclaudio 	if (pfxlen > 128)
7135c4d2233Sclaudio 		return (-1);
7146d3e8673Sclaudio 
715eafe309eSclaudio 	memset(prefix, 0, sizeof(struct bgpd_addr));
7166d3e8673Sclaudio 	prefix->aid = AID_INET6;
7175c4d2233Sclaudio 
7185c4d2233Sclaudio 	if (extract_prefix_buf(buf, &prefix->v6, pfxlen,
7195c4d2233Sclaudio 	    sizeof(prefix->v6)) == -1)
7205c4d2233Sclaudio 		return (-1);
7215c4d2233Sclaudio 
7226d3e8673Sclaudio 	*prefixlen = pfxlen;
7235c4d2233Sclaudio 	return (0);
7246d3e8673Sclaudio }
7256d3e8673Sclaudio 
7266d3e8673Sclaudio int
nlri_get_vpn4(struct ibuf * buf,struct bgpd_addr * prefix,uint8_t * prefixlen,int withdraw)7275c4d2233Sclaudio nlri_get_vpn4(struct ibuf *buf, struct bgpd_addr *prefix,
72839386878Sclaudio     uint8_t *prefixlen, int withdraw)
7296d3e8673Sclaudio {
7305c4d2233Sclaudio 	int		 done = 0;
73139386878Sclaudio 	uint8_t		 pfxlen;
7326d3e8673Sclaudio 
7335c4d2233Sclaudio 	if (ibuf_get_n8(buf, &pfxlen) == -1)
7346d3e8673Sclaudio 		return (-1);
7356d3e8673Sclaudio 
736eafe309eSclaudio 	memset(prefix, 0, sizeof(struct bgpd_addr));
7375c4d2233Sclaudio 	prefix->aid = AID_VPN_IPv4;
7386d3e8673Sclaudio 
7396d3e8673Sclaudio 	/* label stack */
7406d3e8673Sclaudio 	do {
7415c4d2233Sclaudio 		if (prefix->labellen + 3U > sizeof(prefix->labelstack) ||
7425c4d2233Sclaudio 		    pfxlen < 3 * 8)
7436d3e8673Sclaudio 			return (-1);
7446d3e8673Sclaudio 		if (withdraw) {
7456d3e8673Sclaudio 			/* on withdraw ignore the labelstack all together */
7465c4d2233Sclaudio 			if (ibuf_skip(buf, 3) == -1)
7475c4d2233Sclaudio 				return (-1);
7486d3e8673Sclaudio 			pfxlen -= 3 * 8;
7496d3e8673Sclaudio 			break;
7506d3e8673Sclaudio 		}
7515c4d2233Sclaudio 		if (ibuf_get(buf, &prefix->labelstack[prefix->labellen], 3) ==
7525c4d2233Sclaudio 		    -1)
7535c4d2233Sclaudio 			return -1;
7545c4d2233Sclaudio 		if (prefix->labelstack[prefix->labellen + 2] &
7556d3e8673Sclaudio 		    BGP_MPLS_BOS)
7566d3e8673Sclaudio 			done = 1;
7575c4d2233Sclaudio 		prefix->labellen += 3;
7586d3e8673Sclaudio 		pfxlen -= 3 * 8;
7596d3e8673Sclaudio 	} while (!done);
7606d3e8673Sclaudio 
7616d3e8673Sclaudio 	/* RD */
7625c4d2233Sclaudio 	if (pfxlen < sizeof(uint64_t) * 8 ||
7635c4d2233Sclaudio 	    ibuf_get_h64(buf, &prefix->rd) == -1)
7646d3e8673Sclaudio 		return (-1);
76539386878Sclaudio 	pfxlen -= sizeof(uint64_t) * 8;
7666d3e8673Sclaudio 
7676d3e8673Sclaudio 	/* prefix */
7686d3e8673Sclaudio 	if (pfxlen > 32)
7696d3e8673Sclaudio 		return (-1);
7705c4d2233Sclaudio 	if (extract_prefix_buf(buf, &prefix->v4, pfxlen,
7715c4d2233Sclaudio 	    sizeof(prefix->v4)) == -1)
7726d3e8673Sclaudio 		return (-1);
7736d3e8673Sclaudio 
7745c4d2233Sclaudio 	*prefixlen = pfxlen;
7755c4d2233Sclaudio 	return (0);
7766d3e8673Sclaudio }
7776d3e8673Sclaudio 
778290f96faSdenis int
nlri_get_vpn6(struct ibuf * buf,struct bgpd_addr * prefix,uint8_t * prefixlen,int withdraw)7795c4d2233Sclaudio nlri_get_vpn6(struct ibuf *buf, struct bgpd_addr *prefix,
78039386878Sclaudio     uint8_t *prefixlen, int withdraw)
781290f96faSdenis {
7825c4d2233Sclaudio 	int		done = 0;
78339386878Sclaudio 	uint8_t		pfxlen;
784290f96faSdenis 
7855c4d2233Sclaudio 	if (ibuf_get_n8(buf, &pfxlen) == -1)
786290f96faSdenis 		return (-1);
787290f96faSdenis 
788290f96faSdenis 	memset(prefix, 0, sizeof(struct bgpd_addr));
7895c4d2233Sclaudio 	prefix->aid = AID_VPN_IPv6;
790290f96faSdenis 
791290f96faSdenis 	/* label stack */
792290f96faSdenis 	do {
7935c4d2233Sclaudio 		if (prefix->labellen + 3U > sizeof(prefix->labelstack) ||
7945c4d2233Sclaudio 		    pfxlen < 3 * 8)
795290f96faSdenis 			return (-1);
796290f96faSdenis 		if (withdraw) {
797290f96faSdenis 			/* on withdraw ignore the labelstack all together */
7985c4d2233Sclaudio 			if (ibuf_skip(buf, 3) == -1)
7995c4d2233Sclaudio 				return (-1);
800290f96faSdenis 			pfxlen -= 3 * 8;
801290f96faSdenis 			break;
802290f96faSdenis 		}
803290f96faSdenis 
8045c4d2233Sclaudio 		if (ibuf_get(buf, &prefix->labelstack[prefix->labellen], 3) ==
8055c4d2233Sclaudio 		    -1)
8065c4d2233Sclaudio 			return (-1);
8075c4d2233Sclaudio 		if (prefix->labelstack[prefix->labellen + 2] &
808290f96faSdenis 		    BGP_MPLS_BOS)
809290f96faSdenis 			done = 1;
8105c4d2233Sclaudio 		prefix->labellen += 3;
811290f96faSdenis 		pfxlen -= 3 * 8;
812290f96faSdenis 	} while (!done);
813290f96faSdenis 
814290f96faSdenis 	/* RD */
8155c4d2233Sclaudio 	if (pfxlen < sizeof(uint64_t) * 8 ||
8165c4d2233Sclaudio 	    ibuf_get_h64(buf, &prefix->rd) == -1)
817290f96faSdenis 		return (-1);
81839386878Sclaudio 	pfxlen -= sizeof(uint64_t) * 8;
819290f96faSdenis 
820290f96faSdenis 	/* prefix */
821290f96faSdenis 	if (pfxlen > 128)
822290f96faSdenis 		return (-1);
8235c4d2233Sclaudio 	if (extract_prefix_buf(buf, &prefix->v6, pfxlen,
8245c4d2233Sclaudio 	    sizeof(prefix->v6)) == -1)
825290f96faSdenis 		return (-1);
826290f96faSdenis 
8275c4d2233Sclaudio 	*prefixlen = pfxlen;
8285c4d2233Sclaudio 	return (0);
829290f96faSdenis }
830290f96faSdenis 
831fa3a38bbSclaudio static in_addr_t
prefixlen2mask(uint8_t prefixlen)832fa3a38bbSclaudio prefixlen2mask(uint8_t prefixlen)
833fa3a38bbSclaudio {
834fa3a38bbSclaudio 	if (prefixlen == 0)
835fa3a38bbSclaudio 		return (0);
836290f96faSdenis 
837fa3a38bbSclaudio 	return (0xffffffff << (32 - prefixlen));
838fa3a38bbSclaudio }
839290f96faSdenis 
84029328a94Sclaudio /*
841de5c2eedSclaudio  * This function will have undefined behaviour if the passed in prefixlen is
842290f96faSdenis  * too large for the respective bgpd_addr address family.
843de5c2eedSclaudio  */
844fafbb788Sclaudio int
prefix_compare(const struct bgpd_addr * a,const struct bgpd_addr * b,int prefixlen)845fafbb788Sclaudio prefix_compare(const struct bgpd_addr *a, const struct bgpd_addr *b,
846fafbb788Sclaudio     int prefixlen)
847fafbb788Sclaudio {
848fafbb788Sclaudio 	in_addr_t	mask, aa, ba;
849fafbb788Sclaudio 	int		i;
85039386878Sclaudio 	uint8_t		m;
851fafbb788Sclaudio 
852fafbb788Sclaudio 	if (a->aid != b->aid)
853fafbb788Sclaudio 		return (a->aid - b->aid);
854fafbb788Sclaudio 
855fafbb788Sclaudio 	switch (a->aid) {
8563038d3d1Sclaudio 	case AID_VPN_IPv4:
8573038d3d1Sclaudio 		if (be64toh(a->rd) > be64toh(b->rd))
8583038d3d1Sclaudio 			return (1);
8593038d3d1Sclaudio 		if (be64toh(a->rd) < be64toh(b->rd))
8603038d3d1Sclaudio 			return (-1);
8613038d3d1Sclaudio 		/* FALLTHROUGH */
862fafbb788Sclaudio 	case AID_INET:
8637da59fecSclaudio 		if (prefixlen == 0)
8647da59fecSclaudio 			return (0);
865fafbb788Sclaudio 		if (prefixlen > 32)
866de5c2eedSclaudio 			return (-1);
867fafbb788Sclaudio 		mask = htonl(prefixlen2mask(prefixlen));
868fafbb788Sclaudio 		aa = ntohl(a->v4.s_addr & mask);
869fafbb788Sclaudio 		ba = ntohl(b->v4.s_addr & mask);
8703038d3d1Sclaudio 		if (aa > ba)
8713038d3d1Sclaudio 			return (1);
8723038d3d1Sclaudio 		if (aa < ba)
8733038d3d1Sclaudio 			return (-1);
8743038d3d1Sclaudio 		break;
8753038d3d1Sclaudio 	case AID_VPN_IPv6:
8763038d3d1Sclaudio 		if (be64toh(a->rd) > be64toh(b->rd))
8773038d3d1Sclaudio 			return (1);
8783038d3d1Sclaudio 		if (be64toh(a->rd) < be64toh(b->rd))
8793038d3d1Sclaudio 			return (-1);
8803038d3d1Sclaudio 		/* FALLTHROUGH */
881fafbb788Sclaudio 	case AID_INET6:
8827da59fecSclaudio 		if (prefixlen == 0)
8837da59fecSclaudio 			return (0);
884fafbb788Sclaudio 		if (prefixlen > 128)
885de5c2eedSclaudio 			return (-1);
886fafbb788Sclaudio 		for (i = 0; i < prefixlen / 8; i++)
887fafbb788Sclaudio 			if (a->v6.s6_addr[i] != b->v6.s6_addr[i])
888fafbb788Sclaudio 				return (a->v6.s6_addr[i] - b->v6.s6_addr[i]);
889fafbb788Sclaudio 		i = prefixlen % 8;
890fafbb788Sclaudio 		if (i) {
891fafbb788Sclaudio 			m = 0xff00 >> i;
892fafbb788Sclaudio 			if ((a->v6.s6_addr[prefixlen / 8] & m) !=
893fafbb788Sclaudio 			    (b->v6.s6_addr[prefixlen / 8] & m))
894fafbb788Sclaudio 				return ((a->v6.s6_addr[prefixlen / 8] & m) -
895fafbb788Sclaudio 				    (b->v6.s6_addr[prefixlen / 8] & m));
896fafbb788Sclaudio 		}
8973038d3d1Sclaudio 		break;
8983038d3d1Sclaudio 	default:
8993038d3d1Sclaudio 		return (-1);
9003038d3d1Sclaudio 	}
9013038d3d1Sclaudio 
9023038d3d1Sclaudio 	if (a->aid == AID_VPN_IPv4 || a->aid == AID_VPN_IPv6) {
9033038d3d1Sclaudio 		if (a->labellen > b->labellen)
9043038d3d1Sclaudio 			return (1);
9053038d3d1Sclaudio 		if (a->labellen < b->labellen)
9063038d3d1Sclaudio 			return (-1);
9073038d3d1Sclaudio 		return (memcmp(a->labelstack, b->labelstack, a->labellen));
9083038d3d1Sclaudio 	}
909fafbb788Sclaudio 	return (0);
9103038d3d1Sclaudio 
911fafbb788Sclaudio }
912fafbb788Sclaudio 
91321a825c9Sclaudio void
inet4applymask(struct in_addr * dest,const struct in_addr * src,int prefixlen)9142b5c88feSclaudio inet4applymask(struct in_addr *dest, const struct in_addr *src, int prefixlen)
9152b5c88feSclaudio {
9162b5c88feSclaudio 	struct in_addr mask;
9172b5c88feSclaudio 
9182b5c88feSclaudio 	mask.s_addr = htonl(prefixlen2mask(prefixlen));
9192b5c88feSclaudio 	dest->s_addr = src->s_addr & mask.s_addr;
9202b5c88feSclaudio }
9212b5c88feSclaudio 
9222b5c88feSclaudio void
inet6applymask(struct in6_addr * dest,const struct in6_addr * src,int prefixlen)92321a825c9Sclaudio inet6applymask(struct in6_addr *dest, const struct in6_addr *src, int prefixlen)
92421a825c9Sclaudio {
92521a825c9Sclaudio 	struct in6_addr	mask;
92621a825c9Sclaudio 	int		i;
92721a825c9Sclaudio 
928eafe309eSclaudio 	memset(&mask, 0, sizeof(mask));
92921a825c9Sclaudio 	for (i = 0; i < prefixlen / 8; i++)
93021a825c9Sclaudio 		mask.s6_addr[i] = 0xff;
93121a825c9Sclaudio 	i = prefixlen % 8;
93221a825c9Sclaudio 	if (i)
93321a825c9Sclaudio 		mask.s6_addr[prefixlen / 8] = 0xff00 >> i;
93421a825c9Sclaudio 
93521a825c9Sclaudio 	for (i = 0; i < 16; i++)
93621a825c9Sclaudio 		dest->s6_addr[i] = src->s6_addr[i] & mask.s6_addr[i];
93721a825c9Sclaudio }
938d6c2e4e8Sclaudio 
93913bcf54fSclaudio void
applymask(struct bgpd_addr * dest,const struct bgpd_addr * src,int prefixlen)94013bcf54fSclaudio applymask(struct bgpd_addr *dest, const struct bgpd_addr *src, int prefixlen)
94113bcf54fSclaudio {
94213bcf54fSclaudio 	*dest = *src;
94313bcf54fSclaudio 	switch (src->aid) {
94413bcf54fSclaudio 	case AID_INET:
94513bcf54fSclaudio 	case AID_VPN_IPv4:
94613bcf54fSclaudio 		inet4applymask(&dest->v4, &src->v4, prefixlen);
94713bcf54fSclaudio 		break;
94813bcf54fSclaudio 	case AID_INET6:
94913bcf54fSclaudio 	case AID_VPN_IPv6:
95013bcf54fSclaudio 		inet6applymask(&dest->v6, &src->v6, prefixlen);
95113bcf54fSclaudio 		break;
95213bcf54fSclaudio 	}
95313bcf54fSclaudio }
95413bcf54fSclaudio 
955d6c2e4e8Sclaudio /* address family translation functions */
956d6c2e4e8Sclaudio const struct aid aid_vals[AID_MAX] = AID_VALS;
957d6c2e4e8Sclaudio 
95886729c90Sclaudio const char *
aid2str(uint8_t aid)95939386878Sclaudio aid2str(uint8_t aid)
96086729c90Sclaudio {
96186729c90Sclaudio 	if (aid < AID_MAX)
96286729c90Sclaudio 		return (aid_vals[aid].name);
96386729c90Sclaudio 	return ("unknown AID");
96486729c90Sclaudio }
96586729c90Sclaudio 
966d6c2e4e8Sclaudio int
aid2afi(uint8_t aid,uint16_t * afi,uint8_t * safi)96739386878Sclaudio aid2afi(uint8_t aid, uint16_t *afi, uint8_t *safi)
968d6c2e4e8Sclaudio {
969110c1584Sclaudio 	if (aid != AID_UNSPEC && aid < AID_MAX) {
970d6c2e4e8Sclaudio 		*afi = aid_vals[aid].afi;
971d6c2e4e8Sclaudio 		*safi = aid_vals[aid].safi;
972d6c2e4e8Sclaudio 		return (0);
973d6c2e4e8Sclaudio 	}
974d6c2e4e8Sclaudio 	return (-1);
975d6c2e4e8Sclaudio }
976d6c2e4e8Sclaudio 
977d6c2e4e8Sclaudio int
afi2aid(uint16_t afi,uint8_t safi,uint8_t * aid)97839386878Sclaudio afi2aid(uint16_t afi, uint8_t safi, uint8_t *aid)
979d6c2e4e8Sclaudio {
98039386878Sclaudio 	uint8_t i;
981d6c2e4e8Sclaudio 
982110c1584Sclaudio 	for (i = AID_MIN; i < AID_MAX; i++)
983d6c2e4e8Sclaudio 		if (aid_vals[i].afi == afi && aid_vals[i].safi == safi) {
984d6c2e4e8Sclaudio 			*aid = i;
985d6c2e4e8Sclaudio 			return (0);
986d6c2e4e8Sclaudio 		}
987d6c2e4e8Sclaudio 
988d6c2e4e8Sclaudio 	return (-1);
989d6c2e4e8Sclaudio }
990d6c2e4e8Sclaudio 
991d6c2e4e8Sclaudio sa_family_t
aid2af(uint8_t aid)99239386878Sclaudio aid2af(uint8_t aid)
993d6c2e4e8Sclaudio {
994d6c2e4e8Sclaudio 	if (aid < AID_MAX)
995d6c2e4e8Sclaudio 		return (aid_vals[aid].af);
996d6c2e4e8Sclaudio 	return (AF_UNSPEC);
997d6c2e4e8Sclaudio }
998d6c2e4e8Sclaudio 
999d6c2e4e8Sclaudio int
af2aid(sa_family_t af,uint8_t safi,uint8_t * aid)100039386878Sclaudio af2aid(sa_family_t af, uint8_t safi, uint8_t *aid)
1001d6c2e4e8Sclaudio {
100239386878Sclaudio 	uint8_t i;
1003d6c2e4e8Sclaudio 
1004d6c2e4e8Sclaudio 	if (safi == 0) /* default to unicast subclass */
1005d6c2e4e8Sclaudio 		safi = SAFI_UNICAST;
1006d6c2e4e8Sclaudio 
1007110c1584Sclaudio 	for (i = AID_UNSPEC; i < AID_MAX; i++)
1008d6c2e4e8Sclaudio 		if (aid_vals[i].af == af && aid_vals[i].safi == safi) {
1009d6c2e4e8Sclaudio 			*aid = i;
1010d6c2e4e8Sclaudio 			return (0);
1011d6c2e4e8Sclaudio 		}
1012d6c2e4e8Sclaudio 
1013d6c2e4e8Sclaudio 	return (-1);
1014d6c2e4e8Sclaudio }
1015d6c2e4e8Sclaudio 
101645350f87Sclaudio /*
101745350f87Sclaudio  * Convert a struct bgpd_addr into a struct sockaddr. For VPN addresses
101845350f87Sclaudio  * the included label stack is ignored and needs to be handled by the caller.
101945350f87Sclaudio  */
1020d6c2e4e8Sclaudio struct sockaddr *
addr2sa(const struct bgpd_addr * addr,uint16_t port,socklen_t * len)102139386878Sclaudio addr2sa(const struct bgpd_addr *addr, uint16_t port, socklen_t *len)
1022d6c2e4e8Sclaudio {
1023d6c2e4e8Sclaudio 	static struct sockaddr_storage	 ss;
1024d6c2e4e8Sclaudio 	struct sockaddr_in		*sa_in = (struct sockaddr_in *)&ss;
1025d6c2e4e8Sclaudio 	struct sockaddr_in6		*sa_in6 = (struct sockaddr_in6 *)&ss;
1026d6c2e4e8Sclaudio 
10274886db4cSclaudio 	if (addr == NULL || addr->aid == AID_UNSPEC)
10284886db4cSclaudio 		return (NULL);
102945350f87Sclaudio 
1030eafe309eSclaudio 	memset(&ss, 0, sizeof(ss));
1031d6c2e4e8Sclaudio 	switch (addr->aid) {
1032d6c2e4e8Sclaudio 	case AID_INET:
10333038d3d1Sclaudio 	case AID_VPN_IPv4:
1034d6c2e4e8Sclaudio 		sa_in->sin_family = AF_INET;
1035d6c2e4e8Sclaudio 		sa_in->sin_addr.s_addr = addr->v4.s_addr;
1036d6c2e4e8Sclaudio 		sa_in->sin_port = htons(port);
1037255fe563Sclaudio 		*len = sizeof(struct sockaddr_in);
1038d6c2e4e8Sclaudio 		break;
1039d6c2e4e8Sclaudio 	case AID_INET6:
104045350f87Sclaudio 	case AID_VPN_IPv6:
104145350f87Sclaudio 		sa_in6->sin6_family = AF_INET6;
10423038d3d1Sclaudio 		memcpy(&sa_in6->sin6_addr, &addr->v6,
104345350f87Sclaudio 		    sizeof(sa_in6->sin6_addr));
104445350f87Sclaudio 		sa_in6->sin6_port = htons(port);
104545350f87Sclaudio 		sa_in6->sin6_scope_id = addr->scope_id;
104645350f87Sclaudio 		*len = sizeof(struct sockaddr_in6);
104745350f87Sclaudio 		break;
104841c1c374Sclaudio 	case AID_FLOWSPECv4:
104941c1c374Sclaudio 	case AID_FLOWSPECv6:
105041c1c374Sclaudio 		return (NULL);
1051d6c2e4e8Sclaudio 	}
1052d6c2e4e8Sclaudio 
1053d6c2e4e8Sclaudio 	return ((struct sockaddr *)&ss);
1054d6c2e4e8Sclaudio }
1055d6c2e4e8Sclaudio 
1056d6c2e4e8Sclaudio void
sa2addr(struct sockaddr * sa,struct bgpd_addr * addr,uint16_t * port)105739386878Sclaudio sa2addr(struct sockaddr *sa, struct bgpd_addr *addr, uint16_t *port)
1058d6c2e4e8Sclaudio {
1059d6c2e4e8Sclaudio 	struct sockaddr_in		*sa_in = (struct sockaddr_in *)sa;
1060d6c2e4e8Sclaudio 	struct sockaddr_in6		*sa_in6 = (struct sockaddr_in6 *)sa;
1061d6c2e4e8Sclaudio 
1062eafe309eSclaudio 	memset(addr, 0, sizeof(*addr));
1063d6c2e4e8Sclaudio 	switch (sa->sa_family) {
1064d6c2e4e8Sclaudio 	case AF_INET:
1065d6c2e4e8Sclaudio 		addr->aid = AID_INET;
1066d6c2e4e8Sclaudio 		memcpy(&addr->v4, &sa_in->sin_addr, sizeof(addr->v4));
1067a27d9e33Sclaudio 		if (port)
1068a27d9e33Sclaudio 			*port = ntohs(sa_in->sin_port);
1069d6c2e4e8Sclaudio 		break;
1070d6c2e4e8Sclaudio 	case AF_INET6:
1071d6c2e4e8Sclaudio 		addr->aid = AID_INET6;
1072be6ced5eSclaudio #ifdef __KAME__
1073be6ced5eSclaudio 		/*
1074be6ced5eSclaudio 		 * XXX thanks, KAME, for this ugliness...
1075be6ced5eSclaudio 		 * adopted from route/show.c
1076be6ced5eSclaudio 		 */
10775177244fSclaudio 		if ((IN6_IS_ADDR_LINKLOCAL(&sa_in6->sin6_addr) ||
10785177244fSclaudio 		    IN6_IS_ADDR_MC_LINKLOCAL(&sa_in6->sin6_addr) ||
10794ff2dba3Sclaudio 		    IN6_IS_ADDR_MC_NODELOCAL(&sa_in6->sin6_addr)) &&
10805177244fSclaudio 		    sa_in6->sin6_scope_id == 0) {
1081be6ced5eSclaudio 			uint16_t tmp16;
1082be6ced5eSclaudio 			memcpy(&tmp16, &sa_in6->sin6_addr.s6_addr[2],
1083be6ced5eSclaudio 			    sizeof(tmp16));
1084be6ced5eSclaudio 			sa_in6->sin6_scope_id = ntohs(tmp16);
1085be6ced5eSclaudio 			sa_in6->sin6_addr.s6_addr[2] = 0;
1086be6ced5eSclaudio 			sa_in6->sin6_addr.s6_addr[3] = 0;
1087be6ced5eSclaudio 		}
1088be6ced5eSclaudio #endif
10895177244fSclaudio 		memcpy(&addr->v6, &sa_in6->sin6_addr, sizeof(addr->v6));
1090d6c2e4e8Sclaudio 		addr->scope_id = sa_in6->sin6_scope_id; /* I hate v6 */
1091a27d9e33Sclaudio 		if (port)
1092a27d9e33Sclaudio 			*port = ntohs(sa_in6->sin6_port);
1093d6c2e4e8Sclaudio 		break;
1094d6c2e4e8Sclaudio 	}
1095d6c2e4e8Sclaudio }
10966e8089a5Sclaudio 
10976e8089a5Sclaudio const char *
get_baudrate(unsigned long long baudrate,char * unit)1098bc757ddaSclaudio get_baudrate(unsigned long long baudrate, char *unit)
10996e8089a5Sclaudio {
11006e8089a5Sclaudio 	static char bbuf[16];
11013eaf1285Sclaudio 	const unsigned long long kilo = 1000;
11023eaf1285Sclaudio 	const unsigned long long mega = 1000ULL * kilo;
11033eaf1285Sclaudio 	const unsigned long long giga = 1000ULL * mega;
11046e8089a5Sclaudio 
11053eaf1285Sclaudio 	if (baudrate > giga)
11066e8089a5Sclaudio 		snprintf(bbuf, sizeof(bbuf), "%llu G%s",
11073eaf1285Sclaudio 		    baudrate / giga, unit);
11083eaf1285Sclaudio 	else if (baudrate > mega)
11096e8089a5Sclaudio 		snprintf(bbuf, sizeof(bbuf), "%llu M%s",
11103eaf1285Sclaudio 		    baudrate / mega, unit);
11113eaf1285Sclaudio 	else if (baudrate > kilo)
11126e8089a5Sclaudio 		snprintf(bbuf, sizeof(bbuf), "%llu K%s",
11133eaf1285Sclaudio 		    baudrate / kilo, unit);
11146e8089a5Sclaudio 	else
11156e8089a5Sclaudio 		snprintf(bbuf, sizeof(bbuf), "%llu %s",
11166e8089a5Sclaudio 		    baudrate, unit);
11176e8089a5Sclaudio 
11186e8089a5Sclaudio 	return (bbuf);
11196e8089a5Sclaudio }
1120