xref: /openbsd/usr.sbin/bgpd/util.c (revision f4123069)
1*f4123069Smbuhl /*	$OpenBSD: util.c,v 1.72 2022/11/07 11:33:24 mbuhl 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 
3539386878Sclaudio const char	*aspath_delim(uint8_t, int);
361e590dcfSclaudio 
372ffcd4e0Sclaudio const char *
382ffcd4e0Sclaudio log_addr(const struct bgpd_addr *addr)
392ffcd4e0Sclaudio {
40290f96faSdenis 	static char	buf[74];
4145350f87Sclaudio 	struct sockaddr *sa;
425624d029Sclaudio 	socklen_t	len;
432ffcd4e0Sclaudio 
4445350f87Sclaudio 	sa = addr2sa(addr, 0, &len);
4515d8de66Sclaudio 	switch (addr->aid) {
4615d8de66Sclaudio 	case AID_INET:
4715d8de66Sclaudio 	case AID_INET6:
4845350f87Sclaudio 		return log_sockaddr(sa, len);
4915d8de66Sclaudio 	case AID_VPN_IPv4:
50290f96faSdenis 	case AID_VPN_IPv6:
513038d3d1Sclaudio 		snprintf(buf, sizeof(buf), "%s %s", log_rd(addr->rd),
5245350f87Sclaudio 		    log_sockaddr(sa, len));
53290f96faSdenis 		return (buf);
5415d8de66Sclaudio 	}
5515d8de66Sclaudio 	return ("???");
562ffcd4e0Sclaudio }
572ffcd4e0Sclaudio 
582ffcd4e0Sclaudio const char *
592ffcd4e0Sclaudio log_in6addr(const struct in6_addr *addr)
602ffcd4e0Sclaudio {
612ffcd4e0Sclaudio 	struct sockaddr_in6	sa_in6;
622ffcd4e0Sclaudio 
63eafe309eSclaudio 	memset(&sa_in6, 0, sizeof(sa_in6));
642ffcd4e0Sclaudio 	sa_in6.sin6_family = AF_INET6;
652ffcd4e0Sclaudio 	memcpy(&sa_in6.sin6_addr, addr, sizeof(sa_in6.sin6_addr));
662ffcd4e0Sclaudio 
67be6ced5eSclaudio #ifdef __KAME__
682ffcd4e0Sclaudio 	/* XXX thanks, KAME, for this ugliness... adopted from route/show.c */
69bdec2ffaStb 	if ((IN6_IS_ADDR_LINKLOCAL(&sa_in6.sin6_addr) ||
70bdec2ffaStb 	    IN6_IS_ADDR_MC_LINKLOCAL(&sa_in6.sin6_addr) ||
714ff2dba3Sclaudio 	    IN6_IS_ADDR_MC_NODELOCAL(&sa_in6.sin6_addr)) &&
72bdec2ffaStb 	    sa_in6.sin6_scope_id == 0) {
7339386878Sclaudio 		uint16_t tmp16;
742ffcd4e0Sclaudio 		memcpy(&tmp16, &sa_in6.sin6_addr.s6_addr[2], sizeof(tmp16));
752ffcd4e0Sclaudio 		sa_in6.sin6_scope_id = ntohs(tmp16);
762ffcd4e0Sclaudio 		sa_in6.sin6_addr.s6_addr[2] = 0;
772ffcd4e0Sclaudio 		sa_in6.sin6_addr.s6_addr[3] = 0;
782ffcd4e0Sclaudio 	}
79be6ced5eSclaudio #endif
802ffcd4e0Sclaudio 
81255fe563Sclaudio 	return (log_sockaddr((struct sockaddr *)&sa_in6, sizeof(sa_in6)));
822ffcd4e0Sclaudio }
832ffcd4e0Sclaudio 
842ffcd4e0Sclaudio const char *
85255fe563Sclaudio log_sockaddr(struct sockaddr *sa, socklen_t len)
862ffcd4e0Sclaudio {
872ffcd4e0Sclaudio 	static char	buf[NI_MAXHOST];
882ffcd4e0Sclaudio 
8945350f87Sclaudio 	if (sa == NULL || getnameinfo(sa, len, buf, sizeof(buf), NULL, 0,
902ffcd4e0Sclaudio 	    NI_NUMERICHOST))
912ffcd4e0Sclaudio 		return ("(unknown)");
922ffcd4e0Sclaudio 	else
932ffcd4e0Sclaudio 		return (buf);
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:
134256b680eSclaudio 		return ("rd ?");
135256b680eSclaudio 	}
136256b680eSclaudio 	return (buf);
137256b680eSclaudio }
138256b680eSclaudio 
139bf8e2920Sclaudio const struct ext_comm_pairs iana_ext_comms[] = IANA_EXT_COMMUNITIES;
140bf8e2920Sclaudio 
141256b680eSclaudio /* NOTE: this function does not check if the type/subtype combo is
142536f41e5Sclaudio  * actually valid. */
143536f41e5Sclaudio const char *
144f8162053Sclaudio log_ext_subtype(int type, uint8_t subtype)
145536f41e5Sclaudio {
146536f41e5Sclaudio 	static char etype[6];
147bf8e2920Sclaudio 	const struct ext_comm_pairs *cp;
148536f41e5Sclaudio 
149bf8e2920Sclaudio 	for (cp = iana_ext_comms; cp->subname != NULL; cp++) {
1500e6216fdSclaudio 		if ((type == cp->type || type == -1) && subtype == cp->subtype)
151bf8e2920Sclaudio 			return (cp->subname);
152bf8e2920Sclaudio 	}
153d6340f7aSderaadt 	snprintf(etype, sizeof(etype), "[%u]", subtype);
154536f41e5Sclaudio 	return (etype);
155536f41e5Sclaudio }
156536f41e5Sclaudio 
1571e590dcfSclaudio const char *
158a78f83ceSderaadt log_reason(const char *communication) {
159a78f83ceSderaadt 	static char buf[(REASON_LEN - 1) * 4 + 1];
1600561b344Sphessler 
1610561b344Sphessler 	strnvis(buf, communication, sizeof(buf), VIS_NL | VIS_OCTAL);
1620561b344Sphessler 
1630561b344Sphessler 	return buf;
1640561b344Sphessler }
1650561b344Sphessler 
1660561b344Sphessler const char *
167bd9df44eSclaudio log_rtr_error(enum rtr_error err)
168bd9df44eSclaudio {
169bd9df44eSclaudio 	static char buf[20];
170bd9df44eSclaudio 
171bd9df44eSclaudio 	switch (err) {
172bd9df44eSclaudio 	case NO_ERROR:
173bd9df44eSclaudio 		return "No Error";
174bd9df44eSclaudio 	case CORRUPT_DATA:
175bd9df44eSclaudio 		return "Corrupt Data";
176bd9df44eSclaudio 	case INTERNAL_ERROR:
177bd9df44eSclaudio 		return "Internal Error";
178bd9df44eSclaudio 	case NO_DATA_AVAILABLE:
179bd9df44eSclaudio 		return "No Data Available";
180bd9df44eSclaudio 	case INVALID_REQUEST:
181bd9df44eSclaudio 		return "Invalid Request";
182bd9df44eSclaudio 	case UNSUPP_PROTOCOL_VERS:
183bd9df44eSclaudio 		return "Unsupported Protocol Version";
184bd9df44eSclaudio 	case UNSUPP_PDU_TYPE:
185bd9df44eSclaudio 		return "Unsupported PDU Type";
186bd9df44eSclaudio 	case UNK_REC_WDRAWL:
187*f4123069Smbuhl 		return "Withdrawal of Unknown Record";
188bd9df44eSclaudio 	case DUP_REC_RECV:
189bd9df44eSclaudio 		return "Duplicate Announcement Received";
190bd9df44eSclaudio 	case UNEXP_PROTOCOL_VERS:
191bd9df44eSclaudio 		return "Unexpected Protocol Version";
192bd9df44eSclaudio 	default:
193bd9df44eSclaudio 		snprintf(buf, sizeof(buf), "unknown %u", err);
194bd9df44eSclaudio 		return buf;
195bd9df44eSclaudio 	}
196bd9df44eSclaudio }
197bd9df44eSclaudio 
198bd9df44eSclaudio const char *
199202e5273Stb log_policy(uint8_t role)
200202e5273Stb {
201202e5273Stb 	switch (role) {
202202e5273Stb 	case CAPA_ROLE_PROVIDER:
203202e5273Stb 		return "provider";
204202e5273Stb 	case CAPA_ROLE_RS:
205202e5273Stb 		return "rs";
206202e5273Stb 	case CAPA_ROLE_RS_CLIENT:
207202e5273Stb 		return "rs-client";
208202e5273Stb 	case CAPA_ROLE_CUSTOMER:
209202e5273Stb 		return "customer";
210202e5273Stb 	case CAPA_ROLE_PEER:
211202e5273Stb 		return "peer";
212202e5273Stb 	default:
213202e5273Stb 		return "unknown";
214202e5273Stb 	}
215202e5273Stb }
216202e5273Stb 
217202e5273Stb const char *
21839386878Sclaudio aspath_delim(uint8_t seg_type, int closing)
2191e590dcfSclaudio {
2201e590dcfSclaudio 	static char db[8];
2211e590dcfSclaudio 
2221e590dcfSclaudio 	switch (seg_type) {
2231e590dcfSclaudio 	case AS_SET:
2241e590dcfSclaudio 		if (!closing)
2251e590dcfSclaudio 			return ("{ ");
2261e590dcfSclaudio 		else
2271e590dcfSclaudio 			return (" }");
2281e590dcfSclaudio 	case AS_SEQUENCE:
2291e590dcfSclaudio 		return ("");
2301e590dcfSclaudio 	case AS_CONFED_SEQUENCE:
2311e590dcfSclaudio 		if (!closing)
2321e590dcfSclaudio 			return ("( ");
2331e590dcfSclaudio 		else
2341e590dcfSclaudio 			return (" )");
2351e590dcfSclaudio 	case AS_CONFED_SET:
2361e590dcfSclaudio 		if (!closing)
2371e590dcfSclaudio 			return ("[ ");
2381e590dcfSclaudio 		else
2391e590dcfSclaudio 			return (" ]");
2401e590dcfSclaudio 	default:
2411e590dcfSclaudio 		if (!closing)
2421e590dcfSclaudio 			snprintf(db, sizeof(db), "!%u ", seg_type);
2431e590dcfSclaudio 		else
2441e590dcfSclaudio 			snprintf(db, sizeof(db), " !%u", seg_type);
2451e590dcfSclaudio 		return (db);
2461e590dcfSclaudio 	}
2471e590dcfSclaudio }
2481e590dcfSclaudio 
2492ffcd4e0Sclaudio int
25039386878Sclaudio aspath_snprint(char *buf, size_t size, void *data, uint16_t len)
2512ffcd4e0Sclaudio {
2522ffcd4e0Sclaudio #define UPDATE()				\
2532ffcd4e0Sclaudio 	do {					\
254515e489cSderaadt 		if (r < 0)			\
2552ffcd4e0Sclaudio 			return (-1);		\
2562ffcd4e0Sclaudio 		total_size += r;		\
2572ffcd4e0Sclaudio 		if ((unsigned int)r < size) {	\
2582ffcd4e0Sclaudio 			size -= r;		\
2592ffcd4e0Sclaudio 			buf += r;		\
2602ffcd4e0Sclaudio 		} else {			\
2612ffcd4e0Sclaudio 			buf += size;		\
2622ffcd4e0Sclaudio 			size = 0;		\
2632ffcd4e0Sclaudio 		}				\
2642ffcd4e0Sclaudio 	} while (0)
26539386878Sclaudio 	uint8_t		*seg;
2662ffcd4e0Sclaudio 	int		 r, total_size;
26739386878Sclaudio 	uint16_t	 seg_size;
26839386878Sclaudio 	uint8_t		 i, seg_type, seg_len;
2692ffcd4e0Sclaudio 
2702ffcd4e0Sclaudio 	total_size = 0;
2712ffcd4e0Sclaudio 	seg = data;
2722ffcd4e0Sclaudio 	for (; len > 0; len -= seg_size, seg += seg_size) {
2732ffcd4e0Sclaudio 		seg_type = seg[0];
2742ffcd4e0Sclaudio 		seg_len = seg[1];
27539386878Sclaudio 		seg_size = 2 + sizeof(uint32_t) * seg_len;
2762ffcd4e0Sclaudio 
2771e590dcfSclaudio 		r = snprintf(buf, size, "%s%s",
2781e590dcfSclaudio 		    total_size != 0 ? " " : "",
2791e590dcfSclaudio 		    aspath_delim(seg_type, 0));
2802ffcd4e0Sclaudio 		UPDATE();
2812ffcd4e0Sclaudio 
2822ffcd4e0Sclaudio 		for (i = 0; i < seg_len; i++) {
2830c88bf70Sclaudio 			r = snprintf(buf, size, "%s",
2840c88bf70Sclaudio 			    log_as(aspath_extract(seg, i)));
2852ffcd4e0Sclaudio 			UPDATE();
2862ffcd4e0Sclaudio 			if (i + 1 < seg_len) {
2872ffcd4e0Sclaudio 				r = snprintf(buf, size, " ");
2882ffcd4e0Sclaudio 				UPDATE();
2892ffcd4e0Sclaudio 			}
2902ffcd4e0Sclaudio 		}
2911e590dcfSclaudio 		r = snprintf(buf, size, "%s", aspath_delim(seg_type, 1));
2922ffcd4e0Sclaudio 		UPDATE();
2932ffcd4e0Sclaudio 	}
29455e49665Sclaudio 	/* ensure that we have a valid C-string especially for empty as path */
2952ffcd4e0Sclaudio 	if (size > 0)
2962ffcd4e0Sclaudio 		*buf = '\0';
2972ffcd4e0Sclaudio 
2982ffcd4e0Sclaudio 	return (total_size);
2992ffcd4e0Sclaudio #undef UPDATE
3002ffcd4e0Sclaudio }
3012ffcd4e0Sclaudio 
3022ffcd4e0Sclaudio int
30339386878Sclaudio aspath_asprint(char **ret, void *data, uint16_t len)
3042ffcd4e0Sclaudio {
3052ffcd4e0Sclaudio 	size_t	slen;
3062ffcd4e0Sclaudio 	int	plen;
3072ffcd4e0Sclaudio 
3082ffcd4e0Sclaudio 	slen = aspath_strlen(data, len) + 1;
3092ffcd4e0Sclaudio 	*ret = malloc(slen);
3102ffcd4e0Sclaudio 	if (*ret == NULL)
3112ffcd4e0Sclaudio 		return (-1);
3122ffcd4e0Sclaudio 
3132ffcd4e0Sclaudio 	plen = aspath_snprint(*ret, slen, data, len);
3142ffcd4e0Sclaudio 	if (plen == -1) {
3152ffcd4e0Sclaudio 		free(*ret);
3162ffcd4e0Sclaudio 		*ret = NULL;
3172ffcd4e0Sclaudio 		return (-1);
3182ffcd4e0Sclaudio 	}
3192ffcd4e0Sclaudio 
3202ffcd4e0Sclaudio 	return (0);
3212ffcd4e0Sclaudio }
3222ffcd4e0Sclaudio 
3232ffcd4e0Sclaudio size_t
32439386878Sclaudio aspath_strlen(void *data, uint16_t len)
3252ffcd4e0Sclaudio {
32639386878Sclaudio 	uint8_t		*seg;
3272ffcd4e0Sclaudio 	int		 total_size;
32839386878Sclaudio 	uint32_t	 as;
32939386878Sclaudio 	uint16_t	 seg_size;
33039386878Sclaudio 	uint8_t		 i, seg_type, seg_len;
3312ffcd4e0Sclaudio 
3322ffcd4e0Sclaudio 	total_size = 0;
3332ffcd4e0Sclaudio 	seg = data;
3342ffcd4e0Sclaudio 	for (; len > 0; len -= seg_size, seg += seg_size) {
3352ffcd4e0Sclaudio 		seg_type = seg[0];
3362ffcd4e0Sclaudio 		seg_len = seg[1];
33739386878Sclaudio 		seg_size = 2 + sizeof(uint32_t) * seg_len;
3382ffcd4e0Sclaudio 
3392ffcd4e0Sclaudio 		if (seg_type == AS_SET)
3402ffcd4e0Sclaudio 			if (total_size != 0)
3412ffcd4e0Sclaudio 				total_size += 3;
3422ffcd4e0Sclaudio 			else
3432ffcd4e0Sclaudio 				total_size += 2;
3442ffcd4e0Sclaudio 		else if (total_size != 0)
3452ffcd4e0Sclaudio 			total_size += 1;
3462ffcd4e0Sclaudio 
3472ffcd4e0Sclaudio 		for (i = 0; i < seg_len; i++) {
3482ffcd4e0Sclaudio 			as = aspath_extract(seg, i);
3490c88bf70Sclaudio 
3508db4f5d2Sclaudio 			do {
3518db4f5d2Sclaudio 				total_size++;
3528db4f5d2Sclaudio 			} while ((as = as / 10) != 0);
3532ffcd4e0Sclaudio 
3542ffcd4e0Sclaudio 			if (i + 1 < seg_len)
3552ffcd4e0Sclaudio 				total_size += 1;
3562ffcd4e0Sclaudio 		}
3572ffcd4e0Sclaudio 
3582ffcd4e0Sclaudio 		if (seg_type == AS_SET)
3592ffcd4e0Sclaudio 			total_size += 2;
3602ffcd4e0Sclaudio 	}
3612ffcd4e0Sclaudio 	return (total_size);
3622ffcd4e0Sclaudio }
3632ffcd4e0Sclaudio 
3642ffcd4e0Sclaudio /*
3652ffcd4e0Sclaudio  * Extract the asnum out of the as segment at the specified position.
3662ffcd4e0Sclaudio  * Direct access is not possible because of non-aligned reads.
367506f72cfSclaudio  * Only works on verified 4-byte AS paths.
3682ffcd4e0Sclaudio  */
36939386878Sclaudio uint32_t
3702ffcd4e0Sclaudio aspath_extract(const void *seg, int pos)
3712ffcd4e0Sclaudio {
3722ffcd4e0Sclaudio 	const u_char	*ptr = seg;
37339386878Sclaudio 	uint32_t	 as;
3742ffcd4e0Sclaudio 
375506f72cfSclaudio 	/* minimal pos check, return 0 since that is an invalid ASN */
376506f72cfSclaudio 	if (pos < 0 || pos >= ptr[1])
377506f72cfSclaudio 		return (0);
37839386878Sclaudio 	ptr += 2 + sizeof(uint32_t) * pos;
37939386878Sclaudio 	memcpy(&as, ptr, sizeof(uint32_t));
3800c88bf70Sclaudio 	return (ntohl(as));
3812ffcd4e0Sclaudio }
38221a825c9Sclaudio 
383de5c2eedSclaudio /*
38429328a94Sclaudio  * Verify that the aspath is correctly encoded.
38529328a94Sclaudio  */
38629328a94Sclaudio int
38739386878Sclaudio aspath_verify(void *data, uint16_t len, int as4byte, int noset)
38829328a94Sclaudio {
38939386878Sclaudio 	uint8_t		*seg = data;
39039386878Sclaudio 	uint16_t	 seg_size, as_size = 2;
39139386878Sclaudio 	uint8_t		 seg_len, seg_type;
39229328a94Sclaudio 	int		 error = 0;
39329328a94Sclaudio 
39429328a94Sclaudio 	if (len & 1)
39529328a94Sclaudio 		/* odd length aspath are invalid */
39629328a94Sclaudio 		return (AS_ERR_BAD);
39729328a94Sclaudio 
39829328a94Sclaudio 	if (as4byte)
39929328a94Sclaudio 		as_size = 4;
40029328a94Sclaudio 
40129328a94Sclaudio 	for (; len > 0; len -= seg_size, seg += seg_size) {
40239386878Sclaudio 		const uint8_t	*ptr;
40329328a94Sclaudio 		int		 pos;
40429328a94Sclaudio 
40529328a94Sclaudio 		if (len < 2)	/* header length check */
40629328a94Sclaudio 			return (AS_ERR_BAD);
40729328a94Sclaudio 		seg_type = seg[0];
40829328a94Sclaudio 		seg_len = seg[1];
40929328a94Sclaudio 
410d04df938Sclaudio 		if (seg_len == 0)
411d04df938Sclaudio 			/* empty aspath segments are not allowed */
412d04df938Sclaudio 			return (AS_ERR_BAD);
413d04df938Sclaudio 
41429328a94Sclaudio 		/*
41529328a94Sclaudio 		 * BGP confederations should not show up but consider them
41629328a94Sclaudio 		 * as a soft error which invalidates the path but keeps the
41729328a94Sclaudio 		 * bgp session running.
41829328a94Sclaudio 		 */
41929328a94Sclaudio 		if (seg_type == AS_CONFED_SEQUENCE || seg_type == AS_CONFED_SET)
42029328a94Sclaudio 			error = AS_ERR_SOFT;
421aa528464Sclaudio 		/*
422aa528464Sclaudio 		 * If AS_SET filtering (RFC6472) is on, error out on AS_SET
423aa528464Sclaudio 		 * as well.
424aa528464Sclaudio 		 */
425aa528464Sclaudio 		if (noset && seg_type == AS_SET)
426aa528464Sclaudio 			error = AS_ERR_SOFT;
42729328a94Sclaudio 		if (seg_type != AS_SET && seg_type != AS_SEQUENCE &&
42829328a94Sclaudio 		    seg_type != AS_CONFED_SEQUENCE && seg_type != AS_CONFED_SET)
42929328a94Sclaudio 			return (AS_ERR_TYPE);
43029328a94Sclaudio 
43129328a94Sclaudio 		seg_size = 2 + as_size * seg_len;
43229328a94Sclaudio 
43329328a94Sclaudio 		if (seg_size > len)
43429328a94Sclaudio 			return (AS_ERR_LEN);
43529328a94Sclaudio 
43629328a94Sclaudio 		/* RFC 7607 - AS 0 is considered malformed */
43729328a94Sclaudio 		ptr = seg + 2;
43829328a94Sclaudio 		for (pos = 0; pos < seg_len; pos++) {
43939386878Sclaudio 			uint32_t as;
44029328a94Sclaudio 
44129328a94Sclaudio 			memcpy(&as, ptr, as_size);
44229328a94Sclaudio 			if (as == 0)
44356a9a1b8Sclaudio 				error = AS_ERR_SOFT;
44451491708Sclaudio 			ptr += as_size;
44529328a94Sclaudio 		}
44629328a94Sclaudio 	}
44729328a94Sclaudio 	return (error);	/* aspath is valid but probably not loop free */
44829328a94Sclaudio }
44929328a94Sclaudio 
45029328a94Sclaudio /*
45129328a94Sclaudio  * convert a 2 byte aspath to a 4 byte one.
45229328a94Sclaudio  */
45329328a94Sclaudio u_char *
45439386878Sclaudio aspath_inflate(void *data, uint16_t len, uint16_t *newlen)
45529328a94Sclaudio {
45639386878Sclaudio 	uint8_t		*seg, *nseg, *ndata;
45739386878Sclaudio 	uint16_t	 seg_size, olen, nlen;
45839386878Sclaudio 	uint8_t		 seg_len;
45929328a94Sclaudio 
46029328a94Sclaudio 	/* first calculate the length of the aspath */
46129328a94Sclaudio 	seg = data;
46229328a94Sclaudio 	nlen = 0;
46329328a94Sclaudio 	for (olen = len; olen > 0; olen -= seg_size, seg += seg_size) {
46429328a94Sclaudio 		seg_len = seg[1];
46539386878Sclaudio 		seg_size = 2 + sizeof(uint16_t) * seg_len;
46639386878Sclaudio 		nlen += 2 + sizeof(uint32_t) * seg_len;
46729328a94Sclaudio 
46829328a94Sclaudio 		if (seg_size > olen) {
46929328a94Sclaudio 			errno = ERANGE;
47029328a94Sclaudio 			return (NULL);
47129328a94Sclaudio 		}
47229328a94Sclaudio 	}
47329328a94Sclaudio 
47429328a94Sclaudio 	*newlen = nlen;
47529328a94Sclaudio 	if ((ndata = malloc(nlen)) == NULL)
47629328a94Sclaudio 		return (NULL);
47729328a94Sclaudio 
47829328a94Sclaudio 	/* then copy the aspath */
47929328a94Sclaudio 	seg = data;
48029328a94Sclaudio 	for (nseg = ndata; nseg < ndata + nlen; ) {
48129328a94Sclaudio 		*nseg++ = *seg++;
48229328a94Sclaudio 		*nseg++ = seg_len = *seg++;
48329328a94Sclaudio 		for (; seg_len > 0; seg_len--) {
48429328a94Sclaudio 			*nseg++ = 0;
48529328a94Sclaudio 			*nseg++ = 0;
48629328a94Sclaudio 			*nseg++ = *seg++;
48729328a94Sclaudio 			*nseg++ = *seg++;
48829328a94Sclaudio 		}
48929328a94Sclaudio 	}
49029328a94Sclaudio 
49129328a94Sclaudio 	return (ndata);
49229328a94Sclaudio }
49329328a94Sclaudio 
4946d3e8673Sclaudio /* NLRI functions to extract prefixes from the NLRI blobs */
4956d3e8673Sclaudio static int
49639386878Sclaudio extract_prefix(u_char *p, uint16_t len, void *va,
49739386878Sclaudio     uint8_t pfxlen, uint8_t max)
4986d3e8673Sclaudio {
4996d3e8673Sclaudio 	static u_char	 addrmask[] = {
5006d3e8673Sclaudio 	    0x00, 0x80, 0xc0, 0xe0, 0xf0, 0xf8, 0xfc, 0xfe, 0xff };
5016d3e8673Sclaudio 	u_char		*a = va;
5026d3e8673Sclaudio 	int		 i;
50339386878Sclaudio 	uint16_t	 plen = 0;
5046d3e8673Sclaudio 
5056d3e8673Sclaudio 	for (i = 0; pfxlen && i < max; i++) {
5066d3e8673Sclaudio 		if (len <= plen)
5076d3e8673Sclaudio 			return (-1);
5086d3e8673Sclaudio 		if (pfxlen < 8) {
5096d3e8673Sclaudio 			a[i] = *p++ & addrmask[pfxlen];
5106d3e8673Sclaudio 			plen++;
5116d3e8673Sclaudio 			break;
5126d3e8673Sclaudio 		} else {
5136d3e8673Sclaudio 			a[i] = *p++;
5146d3e8673Sclaudio 			plen++;
5156d3e8673Sclaudio 			pfxlen -= 8;
5166d3e8673Sclaudio 		}
5176d3e8673Sclaudio 	}
5186d3e8673Sclaudio 	return (plen);
5196d3e8673Sclaudio }
5206d3e8673Sclaudio 
5216d3e8673Sclaudio int
52239386878Sclaudio nlri_get_prefix(u_char *p, uint16_t len, struct bgpd_addr *prefix,
52339386878Sclaudio     uint8_t *prefixlen)
5246d3e8673Sclaudio {
5256d3e8673Sclaudio 	int	 plen;
52639386878Sclaudio 	uint8_t	 pfxlen;
5276d3e8673Sclaudio 
5286d3e8673Sclaudio 	if (len < 1)
5296d3e8673Sclaudio 		return (-1);
5306d3e8673Sclaudio 
5316d3e8673Sclaudio 	pfxlen = *p++;
5326d3e8673Sclaudio 	len--;
5336d3e8673Sclaudio 
534eafe309eSclaudio 	memset(prefix, 0, sizeof(struct bgpd_addr));
5356d3e8673Sclaudio 	prefix->aid = AID_INET;
5366d3e8673Sclaudio 	*prefixlen = pfxlen;
5376d3e8673Sclaudio 
5386d3e8673Sclaudio 	if (pfxlen > 32)
5396d3e8673Sclaudio 		return (-1);
5406d3e8673Sclaudio 	if ((plen = extract_prefix(p, len, &prefix->v4, pfxlen,
5416d3e8673Sclaudio 	    sizeof(prefix->v4))) == -1)
5426d3e8673Sclaudio 		return (-1);
5436d3e8673Sclaudio 
5446d3e8673Sclaudio 	return (plen + 1);	/* pfxlen needs to be added */
5456d3e8673Sclaudio }
5466d3e8673Sclaudio 
5476d3e8673Sclaudio int
54839386878Sclaudio nlri_get_prefix6(u_char *p, uint16_t len, struct bgpd_addr *prefix,
54939386878Sclaudio     uint8_t *prefixlen)
5506d3e8673Sclaudio {
5516d3e8673Sclaudio 	int	plen;
55239386878Sclaudio 	uint8_t	pfxlen;
5536d3e8673Sclaudio 
5546d3e8673Sclaudio 	if (len < 1)
5556d3e8673Sclaudio 		return (-1);
5566d3e8673Sclaudio 
5576d3e8673Sclaudio 	pfxlen = *p++;
5586d3e8673Sclaudio 	len--;
5596d3e8673Sclaudio 
560eafe309eSclaudio 	memset(prefix, 0, sizeof(struct bgpd_addr));
5616d3e8673Sclaudio 	prefix->aid = AID_INET6;
5626d3e8673Sclaudio 	*prefixlen = pfxlen;
5636d3e8673Sclaudio 
5646d3e8673Sclaudio 	if (pfxlen > 128)
5656d3e8673Sclaudio 		return (-1);
5666d3e8673Sclaudio 	if ((plen = extract_prefix(p, len, &prefix->v6, pfxlen,
5676d3e8673Sclaudio 	    sizeof(prefix->v6))) == -1)
5686d3e8673Sclaudio 		return (-1);
5696d3e8673Sclaudio 
5706d3e8673Sclaudio 	return (plen + 1);	/* pfxlen needs to be added */
5716d3e8673Sclaudio }
5726d3e8673Sclaudio 
5736d3e8673Sclaudio int
57439386878Sclaudio nlri_get_vpn4(u_char *p, uint16_t len, struct bgpd_addr *prefix,
57539386878Sclaudio     uint8_t *prefixlen, int withdraw)
5766d3e8673Sclaudio {
5776d3e8673Sclaudio 	int		 rv, done = 0;
57839386878Sclaudio 	uint16_t	 plen;
57939386878Sclaudio 	uint8_t		 pfxlen;
5806d3e8673Sclaudio 
5816d3e8673Sclaudio 	if (len < 1)
5826d3e8673Sclaudio 		return (-1);
5836d3e8673Sclaudio 
5846d3e8673Sclaudio 	memcpy(&pfxlen, p, 1);
5856d3e8673Sclaudio 	p += 1;
5866d3e8673Sclaudio 	plen = 1;
5876d3e8673Sclaudio 
588eafe309eSclaudio 	memset(prefix, 0, sizeof(struct bgpd_addr));
5896d3e8673Sclaudio 
5906d3e8673Sclaudio 	/* label stack */
5916d3e8673Sclaudio 	do {
5926d3e8673Sclaudio 		if (len - plen < 3 || pfxlen < 3 * 8)
5936d3e8673Sclaudio 			return (-1);
5943038d3d1Sclaudio 		if (prefix->labellen + 3U >
5953038d3d1Sclaudio 		    sizeof(prefix->labelstack))
5966d3e8673Sclaudio 			return (-1);
5976d3e8673Sclaudio 		if (withdraw) {
5986d3e8673Sclaudio 			/* on withdraw ignore the labelstack all together */
5996d3e8673Sclaudio 			plen += 3;
6006d3e8673Sclaudio 			pfxlen -= 3 * 8;
6016d3e8673Sclaudio 			break;
6026d3e8673Sclaudio 		}
6033038d3d1Sclaudio 		prefix->labelstack[prefix->labellen++] = *p++;
6043038d3d1Sclaudio 		prefix->labelstack[prefix->labellen++] = *p++;
6053038d3d1Sclaudio 		prefix->labelstack[prefix->labellen] = *p++;
6063038d3d1Sclaudio 		if (prefix->labelstack[prefix->labellen] &
6076d3e8673Sclaudio 		    BGP_MPLS_BOS)
6086d3e8673Sclaudio 			done = 1;
6093038d3d1Sclaudio 		prefix->labellen++;
6106d3e8673Sclaudio 		plen += 3;
6116d3e8673Sclaudio 		pfxlen -= 3 * 8;
6126d3e8673Sclaudio 	} while (!done);
6136d3e8673Sclaudio 
6146d3e8673Sclaudio 	/* RD */
61539386878Sclaudio 	if (len - plen < (int)sizeof(uint64_t) ||
61639386878Sclaudio 	    pfxlen < sizeof(uint64_t) * 8)
6176d3e8673Sclaudio 		return (-1);
61839386878Sclaudio 	memcpy(&prefix->rd, p, sizeof(uint64_t));
61939386878Sclaudio 	pfxlen -= sizeof(uint64_t) * 8;
62039386878Sclaudio 	p += sizeof(uint64_t);
62139386878Sclaudio 	plen += sizeof(uint64_t);
6226d3e8673Sclaudio 
6236d3e8673Sclaudio 	/* prefix */
6246d3e8673Sclaudio 	prefix->aid = AID_VPN_IPv4;
6256d3e8673Sclaudio 	*prefixlen = pfxlen;
6266d3e8673Sclaudio 
6276d3e8673Sclaudio 	if (pfxlen > 32)
6286d3e8673Sclaudio 		return (-1);
6293038d3d1Sclaudio 	if ((rv = extract_prefix(p, len, &prefix->v4,
6303038d3d1Sclaudio 	    pfxlen, sizeof(prefix->v4))) == -1)
6316d3e8673Sclaudio 		return (-1);
6326d3e8673Sclaudio 
6336d3e8673Sclaudio 	return (plen + rv);
6346d3e8673Sclaudio }
6356d3e8673Sclaudio 
636290f96faSdenis int
63739386878Sclaudio nlri_get_vpn6(u_char *p, uint16_t len, struct bgpd_addr *prefix,
63839386878Sclaudio     uint8_t *prefixlen, int withdraw)
639290f96faSdenis {
640290f96faSdenis 	int		rv, done = 0;
64139386878Sclaudio 	uint16_t	plen;
64239386878Sclaudio 	uint8_t		pfxlen;
643290f96faSdenis 
644290f96faSdenis 	if (len < 1)
645290f96faSdenis 		return (-1);
646290f96faSdenis 
647290f96faSdenis 	memcpy(&pfxlen, p, 1);
648290f96faSdenis 	p += 1;
649290f96faSdenis 	plen = 1;
650290f96faSdenis 
651290f96faSdenis 	memset(prefix, 0, sizeof(struct bgpd_addr));
652290f96faSdenis 
653290f96faSdenis 	/* label stack */
654290f96faSdenis 	do {
655290f96faSdenis 		if (len - plen < 3 || pfxlen < 3 * 8)
656290f96faSdenis 			return (-1);
6573038d3d1Sclaudio 		if (prefix->labellen + 3U >
6583038d3d1Sclaudio 		    sizeof(prefix->labelstack))
659290f96faSdenis 			return (-1);
660290f96faSdenis 		if (withdraw) {
661290f96faSdenis 			/* on withdraw ignore the labelstack all together */
662290f96faSdenis 			plen += 3;
663290f96faSdenis 			pfxlen -= 3 * 8;
664290f96faSdenis 			break;
665290f96faSdenis 		}
666290f96faSdenis 
6673038d3d1Sclaudio 		prefix->labelstack[prefix->labellen++] = *p++;
6683038d3d1Sclaudio 		prefix->labelstack[prefix->labellen++] = *p++;
6693038d3d1Sclaudio 		prefix->labelstack[prefix->labellen] = *p++;
6703038d3d1Sclaudio 		if (prefix->labelstack[prefix->labellen] &
671290f96faSdenis 		    BGP_MPLS_BOS)
672290f96faSdenis 			done = 1;
6733038d3d1Sclaudio 		prefix->labellen++;
674290f96faSdenis 		plen += 3;
675290f96faSdenis 		pfxlen -= 3 * 8;
676290f96faSdenis 	} while (!done);
677290f96faSdenis 
678290f96faSdenis 	/* RD */
67939386878Sclaudio 	if (len - plen < (int)sizeof(uint64_t) ||
68039386878Sclaudio 	    pfxlen < sizeof(uint64_t) * 8)
681290f96faSdenis 		return (-1);
682290f96faSdenis 
68339386878Sclaudio 	memcpy(&prefix->rd, p, sizeof(uint64_t));
68439386878Sclaudio 	pfxlen -= sizeof(uint64_t) * 8;
68539386878Sclaudio 	p += sizeof(uint64_t);
68639386878Sclaudio 	plen += sizeof(uint64_t);
687290f96faSdenis 
688290f96faSdenis 	/* prefix */
689290f96faSdenis 	prefix->aid = AID_VPN_IPv6;
690290f96faSdenis 	*prefixlen = pfxlen;
691290f96faSdenis 
692290f96faSdenis 	if (pfxlen > 128)
693290f96faSdenis 		return (-1);
694290f96faSdenis 
6953038d3d1Sclaudio 	if ((rv = extract_prefix(p, len, &prefix->v6,
6963038d3d1Sclaudio 	    pfxlen, sizeof(prefix->v6))) == -1)
697290f96faSdenis 		return (-1);
698290f96faSdenis 
699290f96faSdenis 	return (plen + rv);
700290f96faSdenis }
701290f96faSdenis 
702fa3a38bbSclaudio static in_addr_t
703fa3a38bbSclaudio prefixlen2mask(uint8_t prefixlen)
704fa3a38bbSclaudio {
705fa3a38bbSclaudio 	if (prefixlen == 0)
706fa3a38bbSclaudio 		return (0);
707290f96faSdenis 
708fa3a38bbSclaudio 	return (0xffffffff << (32 - prefixlen));
709fa3a38bbSclaudio }
710290f96faSdenis 
71129328a94Sclaudio /*
712de5c2eedSclaudio  * This function will have undefined behaviour if the passed in prefixlen is
713290f96faSdenis  * too large for the respective bgpd_addr address family.
714de5c2eedSclaudio  */
715fafbb788Sclaudio int
716fafbb788Sclaudio prefix_compare(const struct bgpd_addr *a, const struct bgpd_addr *b,
717fafbb788Sclaudio     int prefixlen)
718fafbb788Sclaudio {
719fafbb788Sclaudio 	in_addr_t	mask, aa, ba;
720fafbb788Sclaudio 	int		i;
72139386878Sclaudio 	uint8_t		m;
722fafbb788Sclaudio 
723fafbb788Sclaudio 	if (a->aid != b->aid)
724fafbb788Sclaudio 		return (a->aid - b->aid);
725fafbb788Sclaudio 
726fafbb788Sclaudio 	switch (a->aid) {
7273038d3d1Sclaudio 	case AID_VPN_IPv4:
7283038d3d1Sclaudio 		if (be64toh(a->rd) > be64toh(b->rd))
7293038d3d1Sclaudio 			return (1);
7303038d3d1Sclaudio 		if (be64toh(a->rd) < be64toh(b->rd))
7313038d3d1Sclaudio 			return (-1);
7323038d3d1Sclaudio 		/* FALLTHROUGH */
733fafbb788Sclaudio 	case AID_INET:
7347da59fecSclaudio 		if (prefixlen == 0)
7357da59fecSclaudio 			return (0);
736fafbb788Sclaudio 		if (prefixlen > 32)
737de5c2eedSclaudio 			return (-1);
738fafbb788Sclaudio 		mask = htonl(prefixlen2mask(prefixlen));
739fafbb788Sclaudio 		aa = ntohl(a->v4.s_addr & mask);
740fafbb788Sclaudio 		ba = ntohl(b->v4.s_addr & mask);
7413038d3d1Sclaudio 		if (aa > ba)
7423038d3d1Sclaudio 			return (1);
7433038d3d1Sclaudio 		if (aa < ba)
7443038d3d1Sclaudio 			return (-1);
7453038d3d1Sclaudio 		break;
7463038d3d1Sclaudio 	case AID_VPN_IPv6:
7473038d3d1Sclaudio 		if (be64toh(a->rd) > be64toh(b->rd))
7483038d3d1Sclaudio 			return (1);
7493038d3d1Sclaudio 		if (be64toh(a->rd) < be64toh(b->rd))
7503038d3d1Sclaudio 			return (-1);
7513038d3d1Sclaudio 		/* FALLTHROUGH */
752fafbb788Sclaudio 	case AID_INET6:
7537da59fecSclaudio 		if (prefixlen == 0)
7547da59fecSclaudio 			return (0);
755fafbb788Sclaudio 		if (prefixlen > 128)
756de5c2eedSclaudio 			return (-1);
757fafbb788Sclaudio 		for (i = 0; i < prefixlen / 8; i++)
758fafbb788Sclaudio 			if (a->v6.s6_addr[i] != b->v6.s6_addr[i])
759fafbb788Sclaudio 				return (a->v6.s6_addr[i] - b->v6.s6_addr[i]);
760fafbb788Sclaudio 		i = prefixlen % 8;
761fafbb788Sclaudio 		if (i) {
762fafbb788Sclaudio 			m = 0xff00 >> i;
763fafbb788Sclaudio 			if ((a->v6.s6_addr[prefixlen / 8] & m) !=
764fafbb788Sclaudio 			    (b->v6.s6_addr[prefixlen / 8] & m))
765fafbb788Sclaudio 				return ((a->v6.s6_addr[prefixlen / 8] & m) -
766fafbb788Sclaudio 				    (b->v6.s6_addr[prefixlen / 8] & m));
767fafbb788Sclaudio 		}
7683038d3d1Sclaudio 		break;
7693038d3d1Sclaudio 	default:
7703038d3d1Sclaudio 		return (-1);
7713038d3d1Sclaudio 	}
7723038d3d1Sclaudio 
7733038d3d1Sclaudio 	if (a->aid == AID_VPN_IPv4 || a->aid == AID_VPN_IPv6) {
7743038d3d1Sclaudio 		if (a->labellen > b->labellen)
7753038d3d1Sclaudio 			return (1);
7763038d3d1Sclaudio 		if (a->labellen < b->labellen)
7773038d3d1Sclaudio 			return (-1);
7783038d3d1Sclaudio 		return (memcmp(a->labelstack, b->labelstack, a->labellen));
7793038d3d1Sclaudio 	}
780fafbb788Sclaudio 	return (0);
7813038d3d1Sclaudio 
782fafbb788Sclaudio }
783fafbb788Sclaudio 
78421a825c9Sclaudio void
7852b5c88feSclaudio inet4applymask(struct in_addr *dest, const struct in_addr *src, int prefixlen)
7862b5c88feSclaudio {
7872b5c88feSclaudio 	struct in_addr mask;
7882b5c88feSclaudio 
7892b5c88feSclaudio 	mask.s_addr = htonl(prefixlen2mask(prefixlen));
7902b5c88feSclaudio 	dest->s_addr = src->s_addr & mask.s_addr;
7912b5c88feSclaudio }
7922b5c88feSclaudio 
7932b5c88feSclaudio void
79421a825c9Sclaudio inet6applymask(struct in6_addr *dest, const struct in6_addr *src, int prefixlen)
79521a825c9Sclaudio {
79621a825c9Sclaudio 	struct in6_addr	mask;
79721a825c9Sclaudio 	int		i;
79821a825c9Sclaudio 
799eafe309eSclaudio 	memset(&mask, 0, sizeof(mask));
80021a825c9Sclaudio 	for (i = 0; i < prefixlen / 8; i++)
80121a825c9Sclaudio 		mask.s6_addr[i] = 0xff;
80221a825c9Sclaudio 	i = prefixlen % 8;
80321a825c9Sclaudio 	if (i)
80421a825c9Sclaudio 		mask.s6_addr[prefixlen / 8] = 0xff00 >> i;
80521a825c9Sclaudio 
80621a825c9Sclaudio 	for (i = 0; i < 16; i++)
80721a825c9Sclaudio 		dest->s6_addr[i] = src->s6_addr[i] & mask.s6_addr[i];
80821a825c9Sclaudio }
809d6c2e4e8Sclaudio 
81013bcf54fSclaudio void
81113bcf54fSclaudio applymask(struct bgpd_addr *dest, const struct bgpd_addr *src, int prefixlen)
81213bcf54fSclaudio {
81313bcf54fSclaudio 	*dest = *src;
81413bcf54fSclaudio 	switch (src->aid) {
81513bcf54fSclaudio 	case AID_INET:
81613bcf54fSclaudio 	case AID_VPN_IPv4:
81713bcf54fSclaudio 		inet4applymask(&dest->v4, &src->v4, prefixlen);
81813bcf54fSclaudio 		break;
81913bcf54fSclaudio 	case AID_INET6:
82013bcf54fSclaudio 	case AID_VPN_IPv6:
82113bcf54fSclaudio 		inet6applymask(&dest->v6, &src->v6, prefixlen);
82213bcf54fSclaudio 		break;
82313bcf54fSclaudio 	}
82413bcf54fSclaudio }
82513bcf54fSclaudio 
826d6c2e4e8Sclaudio /* address family translation functions */
827d6c2e4e8Sclaudio const struct aid aid_vals[AID_MAX] = AID_VALS;
828d6c2e4e8Sclaudio 
82986729c90Sclaudio const char *
83039386878Sclaudio aid2str(uint8_t aid)
83186729c90Sclaudio {
83286729c90Sclaudio 	if (aid < AID_MAX)
83386729c90Sclaudio 		return (aid_vals[aid].name);
83486729c90Sclaudio 	return ("unknown AID");
83586729c90Sclaudio }
83686729c90Sclaudio 
837d6c2e4e8Sclaudio int
83839386878Sclaudio aid2afi(uint8_t aid, uint16_t *afi, uint8_t *safi)
839d6c2e4e8Sclaudio {
840d6c2e4e8Sclaudio 	if (aid < AID_MAX) {
841d6c2e4e8Sclaudio 		*afi = aid_vals[aid].afi;
842d6c2e4e8Sclaudio 		*safi = aid_vals[aid].safi;
843d6c2e4e8Sclaudio 		return (0);
844d6c2e4e8Sclaudio 	}
845d6c2e4e8Sclaudio 	return (-1);
846d6c2e4e8Sclaudio }
847d6c2e4e8Sclaudio 
848d6c2e4e8Sclaudio int
84939386878Sclaudio afi2aid(uint16_t afi, uint8_t safi, uint8_t *aid)
850d6c2e4e8Sclaudio {
85139386878Sclaudio 	uint8_t i;
852d6c2e4e8Sclaudio 
853d6c2e4e8Sclaudio 	for (i = 0; i < AID_MAX; i++)
854d6c2e4e8Sclaudio 		if (aid_vals[i].afi == afi && aid_vals[i].safi == safi) {
855d6c2e4e8Sclaudio 			*aid = i;
856d6c2e4e8Sclaudio 			return (0);
857d6c2e4e8Sclaudio 		}
858d6c2e4e8Sclaudio 
859d6c2e4e8Sclaudio 	return (-1);
860d6c2e4e8Sclaudio }
861d6c2e4e8Sclaudio 
862d6c2e4e8Sclaudio sa_family_t
86339386878Sclaudio aid2af(uint8_t aid)
864d6c2e4e8Sclaudio {
865d6c2e4e8Sclaudio 	if (aid < AID_MAX)
866d6c2e4e8Sclaudio 		return (aid_vals[aid].af);
867d6c2e4e8Sclaudio 	return (AF_UNSPEC);
868d6c2e4e8Sclaudio }
869d6c2e4e8Sclaudio 
870d6c2e4e8Sclaudio int
87139386878Sclaudio af2aid(sa_family_t af, uint8_t safi, uint8_t *aid)
872d6c2e4e8Sclaudio {
87339386878Sclaudio 	uint8_t i;
874d6c2e4e8Sclaudio 
875d6c2e4e8Sclaudio 	if (safi == 0) /* default to unicast subclass */
876d6c2e4e8Sclaudio 		safi = SAFI_UNICAST;
877d6c2e4e8Sclaudio 
878d6c2e4e8Sclaudio 	for (i = 0; i < AID_MAX; i++)
879d6c2e4e8Sclaudio 		if (aid_vals[i].af == af && aid_vals[i].safi == safi) {
880d6c2e4e8Sclaudio 			*aid = i;
881d6c2e4e8Sclaudio 			return (0);
882d6c2e4e8Sclaudio 		}
883d6c2e4e8Sclaudio 
884d6c2e4e8Sclaudio 	return (-1);
885d6c2e4e8Sclaudio }
886d6c2e4e8Sclaudio 
88745350f87Sclaudio /*
88845350f87Sclaudio  * Convert a struct bgpd_addr into a struct sockaddr. For VPN addresses
88945350f87Sclaudio  * the included label stack is ignored and needs to be handled by the caller.
89045350f87Sclaudio  */
891d6c2e4e8Sclaudio struct sockaddr *
89239386878Sclaudio addr2sa(const struct bgpd_addr *addr, uint16_t port, socklen_t *len)
893d6c2e4e8Sclaudio {
894d6c2e4e8Sclaudio 	static struct sockaddr_storage	 ss;
895d6c2e4e8Sclaudio 	struct sockaddr_in		*sa_in = (struct sockaddr_in *)&ss;
896d6c2e4e8Sclaudio 	struct sockaddr_in6		*sa_in6 = (struct sockaddr_in6 *)&ss;
897d6c2e4e8Sclaudio 
8984886db4cSclaudio 	if (addr == NULL || addr->aid == AID_UNSPEC)
8994886db4cSclaudio 		return (NULL);
90045350f87Sclaudio 
901eafe309eSclaudio 	memset(&ss, 0, sizeof(ss));
902d6c2e4e8Sclaudio 	switch (addr->aid) {
903d6c2e4e8Sclaudio 	case AID_INET:
9043038d3d1Sclaudio 	case AID_VPN_IPv4:
905d6c2e4e8Sclaudio 		sa_in->sin_family = AF_INET;
906d6c2e4e8Sclaudio 		sa_in->sin_addr.s_addr = addr->v4.s_addr;
907d6c2e4e8Sclaudio 		sa_in->sin_port = htons(port);
908255fe563Sclaudio 		*len = sizeof(struct sockaddr_in);
909d6c2e4e8Sclaudio 		break;
910d6c2e4e8Sclaudio 	case AID_INET6:
91145350f87Sclaudio 	case AID_VPN_IPv6:
91245350f87Sclaudio 		sa_in6->sin6_family = AF_INET6;
9133038d3d1Sclaudio 		memcpy(&sa_in6->sin6_addr, &addr->v6,
91445350f87Sclaudio 		    sizeof(sa_in6->sin6_addr));
91545350f87Sclaudio 		sa_in6->sin6_port = htons(port);
91645350f87Sclaudio 		sa_in6->sin6_scope_id = addr->scope_id;
91745350f87Sclaudio 		*len = sizeof(struct sockaddr_in6);
91845350f87Sclaudio 		break;
919d6c2e4e8Sclaudio 	}
920d6c2e4e8Sclaudio 
921d6c2e4e8Sclaudio 	return ((struct sockaddr *)&ss);
922d6c2e4e8Sclaudio }
923d6c2e4e8Sclaudio 
924d6c2e4e8Sclaudio void
92539386878Sclaudio sa2addr(struct sockaddr *sa, struct bgpd_addr *addr, uint16_t *port)
926d6c2e4e8Sclaudio {
927d6c2e4e8Sclaudio 	struct sockaddr_in		*sa_in = (struct sockaddr_in *)sa;
928d6c2e4e8Sclaudio 	struct sockaddr_in6		*sa_in6 = (struct sockaddr_in6 *)sa;
929d6c2e4e8Sclaudio 
930eafe309eSclaudio 	memset(addr, 0, sizeof(*addr));
931d6c2e4e8Sclaudio 	switch (sa->sa_family) {
932d6c2e4e8Sclaudio 	case AF_INET:
933d6c2e4e8Sclaudio 		addr->aid = AID_INET;
934d6c2e4e8Sclaudio 		memcpy(&addr->v4, &sa_in->sin_addr, sizeof(addr->v4));
935a27d9e33Sclaudio 		if (port)
936a27d9e33Sclaudio 			*port = ntohs(sa_in->sin_port);
937d6c2e4e8Sclaudio 		break;
938d6c2e4e8Sclaudio 	case AF_INET6:
939d6c2e4e8Sclaudio 		addr->aid = AID_INET6;
940be6ced5eSclaudio #ifdef __KAME__
941be6ced5eSclaudio 		/*
942be6ced5eSclaudio 		 * XXX thanks, KAME, for this ugliness...
943be6ced5eSclaudio 		 * adopted from route/show.c
944be6ced5eSclaudio 		 */
9455177244fSclaudio 		if ((IN6_IS_ADDR_LINKLOCAL(&sa_in6->sin6_addr) ||
9465177244fSclaudio 		    IN6_IS_ADDR_MC_LINKLOCAL(&sa_in6->sin6_addr) ||
9474ff2dba3Sclaudio 		    IN6_IS_ADDR_MC_NODELOCAL(&sa_in6->sin6_addr)) &&
9485177244fSclaudio 		    sa_in6->sin6_scope_id == 0) {
949be6ced5eSclaudio 			uint16_t tmp16;
950be6ced5eSclaudio 			memcpy(&tmp16, &sa_in6->sin6_addr.s6_addr[2],
951be6ced5eSclaudio 			    sizeof(tmp16));
952be6ced5eSclaudio 			sa_in6->sin6_scope_id = ntohs(tmp16);
953be6ced5eSclaudio 			sa_in6->sin6_addr.s6_addr[2] = 0;
954be6ced5eSclaudio 			sa_in6->sin6_addr.s6_addr[3] = 0;
955be6ced5eSclaudio 		}
956be6ced5eSclaudio #endif
9575177244fSclaudio 		memcpy(&addr->v6, &sa_in6->sin6_addr, sizeof(addr->v6));
958d6c2e4e8Sclaudio 		addr->scope_id = sa_in6->sin6_scope_id; /* I hate v6 */
959a27d9e33Sclaudio 		if (port)
960a27d9e33Sclaudio 			*port = ntohs(sa_in6->sin6_port);
961d6c2e4e8Sclaudio 		break;
962d6c2e4e8Sclaudio 	}
963d6c2e4e8Sclaudio }
9646e8089a5Sclaudio 
9656e8089a5Sclaudio const char *
966bc757ddaSclaudio get_baudrate(unsigned long long baudrate, char *unit)
9676e8089a5Sclaudio {
9686e8089a5Sclaudio 	static char bbuf[16];
9693eaf1285Sclaudio 	const unsigned long long kilo = 1000;
9703eaf1285Sclaudio 	const unsigned long long mega = 1000ULL * kilo;
9713eaf1285Sclaudio 	const unsigned long long giga = 1000ULL * mega;
9726e8089a5Sclaudio 
9733eaf1285Sclaudio 	if (baudrate > giga)
9746e8089a5Sclaudio 		snprintf(bbuf, sizeof(bbuf), "%llu G%s",
9753eaf1285Sclaudio 		    baudrate / giga, unit);
9763eaf1285Sclaudio 	else if (baudrate > mega)
9776e8089a5Sclaudio 		snprintf(bbuf, sizeof(bbuf), "%llu M%s",
9783eaf1285Sclaudio 		    baudrate / mega, unit);
9793eaf1285Sclaudio 	else if (baudrate > kilo)
9806e8089a5Sclaudio 		snprintf(bbuf, sizeof(bbuf), "%llu K%s",
9813eaf1285Sclaudio 		    baudrate / kilo, unit);
9826e8089a5Sclaudio 	else
9836e8089a5Sclaudio 		snprintf(bbuf, sizeof(bbuf), "%llu %s",
9846e8089a5Sclaudio 		    baudrate, unit);
9856e8089a5Sclaudio 
9866e8089a5Sclaudio 	return (bbuf);
9876e8089a5Sclaudio }
988