xref: /openbsd-src/usr.sbin/bgpd/util.c (revision d1df930ffab53da22f3324c32bed7ac5709915e6)
1 /*	$OpenBSD: util.c,v 1.40 2018/09/26 14:38:19 claudio Exp $ */
2 
3 /*
4  * Copyright (c) 2006 Claudio Jeker <claudio@openbsd.org>
5  * Copyright (c) 2003, 2004 Henning Brauer <henning@openbsd.org>
6  *
7  * Permission to use, copy, modify, and distribute this software for any
8  * purpose with or without fee is hereby granted, provided that the above
9  * copyright notice and this permission notice appear in all copies.
10  *
11  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
12  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
13  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
14  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
15  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
16  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
17  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
18  */
19 #include <sys/types.h>
20 #include <sys/socket.h>
21 #include <net/if.h>
22 #include <net/if_media.h>
23 #include <net/if_types.h>
24 #include <netinet/in.h>
25 #include <arpa/inet.h>
26 #include <errno.h>
27 #include <netdb.h>
28 #include <stdlib.h>
29 #include <stdio.h>
30 #include <string.h>
31 #include <vis.h>
32 
33 #include "bgpd.h"
34 #include "rde.h"
35 #include "log.h"
36 
37 const char	*aspath_delim(u_int8_t, int);
38 
39 const char *
40 log_addr(const struct bgpd_addr *addr)
41 {
42 	static char	buf[48];
43 	char		tbuf[16];
44 
45 	switch (addr->aid) {
46 	case AID_INET:
47 	case AID_INET6:
48 		if (inet_ntop(aid2af(addr->aid), &addr->ba, buf,
49 		    sizeof(buf)) == NULL)
50 			return ("?");
51 		return (buf);
52 	case AID_VPN_IPv4:
53 		if (inet_ntop(AF_INET, &addr->vpn4.addr, tbuf,
54 		    sizeof(tbuf)) == NULL)
55 			return ("?");
56 		snprintf(buf, sizeof(buf), "%s %s", log_rd(addr->vpn4.rd),
57 		   tbuf);
58 		return (buf);
59 	}
60 	return ("???");
61 }
62 
63 const char *
64 log_in6addr(const struct in6_addr *addr)
65 {
66 	struct sockaddr_in6	sa_in6;
67 	u_int16_t		tmp16;
68 
69 	bzero(&sa_in6, sizeof(sa_in6));
70 	sa_in6.sin6_len = sizeof(sa_in6);
71 	sa_in6.sin6_family = AF_INET6;
72 	memcpy(&sa_in6.sin6_addr, addr, sizeof(sa_in6.sin6_addr));
73 
74 	/* XXX thanks, KAME, for this ugliness... adopted from route/show.c */
75 	if (IN6_IS_ADDR_LINKLOCAL(&sa_in6.sin6_addr) ||
76 	    IN6_IS_ADDR_MC_LINKLOCAL(&sa_in6.sin6_addr)) {
77 		memcpy(&tmp16, &sa_in6.sin6_addr.s6_addr[2], sizeof(tmp16));
78 		sa_in6.sin6_scope_id = ntohs(tmp16);
79 		sa_in6.sin6_addr.s6_addr[2] = 0;
80 		sa_in6.sin6_addr.s6_addr[3] = 0;
81 	}
82 
83 	return (log_sockaddr((struct sockaddr *)&sa_in6));
84 }
85 
86 const char *
87 log_sockaddr(struct sockaddr *sa)
88 {
89 	static char	buf[NI_MAXHOST];
90 
91 	if (getnameinfo(sa, sa->sa_len, buf, sizeof(buf), NULL, 0,
92 	    NI_NUMERICHOST))
93 		return ("(unknown)");
94 	else
95 		return (buf);
96 }
97 
98 const char *
99 log_as(u_int32_t as)
100 {
101 	static char	buf[11];	/* "4294967294\0" */
102 
103 	if (snprintf(buf, sizeof(buf), "%u", as) == -1)
104 		return ("?");
105 
106 	return (buf);
107 }
108 
109 const char *
110 log_rd(u_int64_t rd)
111 {
112 	static char	buf[32];
113 	struct in_addr	addr;
114 	u_int32_t	u32;
115 	u_int16_t	u16;
116 
117 	rd = betoh64(rd);
118 	switch (rd >> 48) {
119 	case EXT_COMMUNITY_TRANS_TWO_AS:
120 		u32 = rd & 0xffffffff;
121 		u16 = (rd >> 32) & 0xffff;
122 		snprintf(buf, sizeof(buf), "rd %hu:%u", u16, u32);
123 		break;
124 	case EXT_COMMUNITY_TRANS_FOUR_AS:
125 		u32 = (rd >> 16) & 0xffffffff;
126 		u16 = rd & 0xffff;
127 		snprintf(buf, sizeof(buf), "rd %s:%hu", log_as(u32), u16);
128 		break;
129 	case EXT_COMMUNITY_TRANS_IPV4:
130 		u32 = (rd >> 16) & 0xffffffff;
131 		u16 = rd & 0xffff;
132 		addr.s_addr = htonl(u32);
133 		snprintf(buf, sizeof(buf), "rd %s:%hu", inet_ntoa(addr), u16);
134 		break;
135 	default:
136 		return ("rd ?");
137 	}
138 	return (buf);
139 }
140 
141 const struct ext_comm_pairs iana_ext_comms[] = IANA_EXT_COMMUNITIES;
142 
143 /* NOTE: this function does not check if the type/subtype combo is
144  * actually valid. */
145 const char *
146 log_ext_subtype(u_int8_t type, u_int8_t subtype)
147 {
148 	static char etype[6];
149 	const struct ext_comm_pairs *cp;
150 
151 	for (cp = iana_ext_comms; cp->subname != NULL; cp++) {
152 		if (type == cp->type && subtype == cp->subtype)
153 			return (cp->subname);
154 	}
155 	snprintf(etype, sizeof(etype), "[%u]", subtype);
156 	return (etype);
157 }
158 
159 const char *
160 log_shutcomm(const char *communication) {
161 	static char buf[(SHUT_COMM_LEN - 1) * 4 + 1];
162 
163 	strnvis(buf, communication, sizeof(buf), VIS_NL | VIS_OCTAL);
164 
165 	return buf;
166 }
167 
168 const char *
169 aspath_delim(u_int8_t seg_type, int closing)
170 {
171 	static char db[8];
172 
173 	switch (seg_type) {
174 	case AS_SET:
175 		if (!closing)
176 			return ("{ ");
177 		else
178 			return (" }");
179 	case AS_SEQUENCE:
180 		return ("");
181 	case AS_CONFED_SEQUENCE:
182 		if (!closing)
183 			return ("( ");
184 		else
185 			return (" )");
186 	case AS_CONFED_SET:
187 		if (!closing)
188 			return ("[ ");
189 		else
190 			return (" ]");
191 	default:
192 		if (!closing)
193 			snprintf(db, sizeof(db), "!%u ", seg_type);
194 		else
195 			snprintf(db, sizeof(db), " !%u", seg_type);
196 		return (db);
197 	}
198 }
199 
200 int
201 aspath_snprint(char *buf, size_t size, void *data, u_int16_t len)
202 {
203 #define UPDATE()				\
204 	do {					\
205 		if (r == -1)			\
206 			return (-1);		\
207 		total_size += r;		\
208 		if ((unsigned int)r < size) {	\
209 			size -= r;		\
210 			buf += r;		\
211 		} else {			\
212 			buf += size;		\
213 			size = 0;		\
214 		}				\
215 	} while (0)
216 	u_int8_t	*seg;
217 	int		 r, total_size;
218 	u_int16_t	 seg_size;
219 	u_int8_t	 i, seg_type, seg_len;
220 
221 	total_size = 0;
222 	seg = data;
223 	for (; len > 0; len -= seg_size, seg += seg_size) {
224 		seg_type = seg[0];
225 		seg_len = seg[1];
226 		seg_size = 2 + sizeof(u_int32_t) * seg_len;
227 
228 		r = snprintf(buf, size, "%s%s",
229 		    total_size != 0 ? " " : "",
230 		    aspath_delim(seg_type, 0));
231 		UPDATE();
232 
233 		for (i = 0; i < seg_len; i++) {
234 			r = snprintf(buf, size, "%s",
235 			    log_as(aspath_extract(seg, i)));
236 			UPDATE();
237 			if (i + 1 < seg_len) {
238 				r = snprintf(buf, size, " ");
239 				UPDATE();
240 			}
241 		}
242 		r = snprintf(buf, size, "%s", aspath_delim(seg_type, 1));
243 		UPDATE();
244 	}
245 	/* ensure that we have a valid C-string especially for empty as path */
246 	if (size > 0)
247 		*buf = '\0';
248 
249 	return (total_size);
250 #undef UPDATE
251 }
252 
253 int
254 aspath_asprint(char **ret, void *data, u_int16_t len)
255 {
256 	size_t	slen;
257 	int	plen;
258 
259 	slen = aspath_strlen(data, len) + 1;
260 	*ret = malloc(slen);
261 	if (*ret == NULL)
262 		return (-1);
263 
264 	plen = aspath_snprint(*ret, slen, data, len);
265 	if (plen == -1) {
266 		free(*ret);
267 		*ret = NULL;
268 		return (-1);
269 	}
270 
271 	return (0);
272 }
273 
274 size_t
275 aspath_strlen(void *data, u_int16_t len)
276 {
277 	u_int8_t	*seg;
278 	int		 total_size;
279 	u_int32_t	 as;
280 	u_int16_t	 seg_size;
281 	u_int8_t	 i, seg_type, seg_len;
282 
283 	total_size = 0;
284 	seg = data;
285 	for (; len > 0; len -= seg_size, seg += seg_size) {
286 		seg_type = seg[0];
287 		seg_len = seg[1];
288 		seg_size = 2 + sizeof(u_int32_t) * seg_len;
289 
290 		if (seg_type == AS_SET)
291 			if (total_size != 0)
292 				total_size += 3;
293 			else
294 				total_size += 2;
295 		else if (total_size != 0)
296 			total_size += 1;
297 
298 		for (i = 0; i < seg_len; i++) {
299 			as = aspath_extract(seg, i);
300 
301 			do {
302 				total_size++;
303 			} while ((as = as / 10) != 0);
304 
305 			if (i + 1 < seg_len)
306 				total_size += 1;
307 		}
308 
309 		if (seg_type == AS_SET)
310 			total_size += 2;
311 	}
312 	return (total_size);
313 }
314 
315 static int
316 as_compare(struct filter_as *f, u_int32_t as, u_int32_t neighas)
317 {
318 	u_int32_t match;
319 
320 	if (f->flags & AS_FLAG_AS_SET_NAME)	/* should not happen */
321 		return (0);
322 	if (f->flags & AS_FLAG_AS_SET)
323 		return (as_set_match(f->aset, as));
324 
325 	if (f->flags & AS_FLAG_NEIGHBORAS)
326 		match = neighas;
327 	else
328 		match = f->as_min;
329 
330 	switch (f->op) {
331 	case OP_NONE:
332 	case OP_EQ:
333 		if (as == match)
334 			return (1);
335 		break;
336 	case OP_NE:
337 		if (as != match)
338 			return (1);
339 		break;
340 	case OP_RANGE:
341 		if (as >= f->as_min && as <= f->as_max)
342 			return (1);
343 		break;
344 	case OP_XRANGE:
345 		if (as < f->as_min || as > f->as_max)
346 			return (1);
347 		break;
348 	}
349 	return (0);
350 }
351 
352 /* we need to be able to search more than one as */
353 int
354 aspath_match(void *data, u_int16_t len, struct filter_as *f, u_int32_t neighas)
355 {
356 	u_int8_t	*seg;
357 	int		 final;
358 	u_int16_t	 seg_size;
359 	u_int8_t	 i, seg_len;
360 	u_int32_t	 as = 0;
361 
362 	if (f->type == AS_EMPTY) {
363 		if (len == 0)
364 			return (1);
365 		else
366 			return (0);
367 	}
368 
369 	seg = data;
370 
371 	/* just check the leftmost AS */
372 	if (f->type == AS_PEER && len >= 6) {
373 		as = aspath_extract(seg, 0);
374 		if (as_compare(f, as, neighas))
375 			return (1);
376 		else
377 			return (0);
378 	}
379 
380 	for (; len >= 6; len -= seg_size, seg += seg_size) {
381 		seg_len = seg[1];
382 		seg_size = 2 + sizeof(u_int32_t) * seg_len;
383 
384 		final = (len == seg_size);
385 
386 		if (f->type == AS_SOURCE) {
387 			/*
388 			 * Just extract the rightmost AS
389 			 * but if that segment is an AS_SET then the rightmost
390 			 * AS of a previous AS_SEQUENCE segment should be used.
391 			 * Because of that just look at AS_SEQUENCE segments.
392 			 */
393 			if (seg[0] == AS_SEQUENCE)
394 				as = aspath_extract(seg, seg_len - 1);
395 			/* not yet in the final segment */
396 			if (!final)
397 				continue;
398 			if (as_compare(f, as, neighas))
399 				return (1);
400 			else
401 				return (0);
402 		}
403 		/* AS_TRANSIT or AS_ALL */
404 		for (i = 0; i < seg_len; i++) {
405 			/*
406 			 * the source (rightmost) AS is excluded from
407 			 * AS_TRANSIT matches.
408 			 */
409 			if (final && i == seg_len - 1 && f->type == AS_TRANSIT)
410 				return (0);
411 			as = aspath_extract(seg, i);
412 			if (as_compare(f, as, neighas))
413 				return (1);
414 		}
415 	}
416 	return (0);
417 }
418 
419 /*
420  * Extract the asnum out of the as segment at the specified position.
421  * Direct access is not possible because of non-aligned reads.
422  * ATTENTION: no bounds checks are done.
423  */
424 u_int32_t
425 aspath_extract(const void *seg, int pos)
426 {
427 	const u_char	*ptr = seg;
428 	u_int32_t	 as;
429 
430 	ptr += 2 + sizeof(u_int32_t) * pos;
431 	memcpy(&as, ptr, sizeof(u_int32_t));
432 	return (ntohl(as));
433 }
434 
435 /*
436  * Verify that the aspath is correctly encoded.
437  */
438 int
439 aspath_verify(void *data, u_int16_t len, int as4byte)
440 {
441 	u_int8_t	*seg = data;
442 	u_int16_t	 seg_size, as_size = 2;
443 	u_int8_t	 seg_len, seg_type;
444 	int		 error = 0;
445 
446 	if (len & 1)
447 		/* odd length aspath are invalid */
448 		return (AS_ERR_BAD);
449 
450 	if (as4byte)
451 		as_size = 4;
452 
453 	for (; len > 0; len -= seg_size, seg += seg_size) {
454 		const u_int8_t	*ptr;
455 		int		 pos;
456 
457 		if (len < 2)	/* header length check */
458 			return (AS_ERR_BAD);
459 		seg_type = seg[0];
460 		seg_len = seg[1];
461 
462 		if (seg_len == 0)
463 			/* empty aspath segments are not allowed */
464 			return (AS_ERR_BAD);
465 
466 		/*
467 		 * BGP confederations should not show up but consider them
468 		 * as a soft error which invalidates the path but keeps the
469 		 * bgp session running.
470 		 */
471 		if (seg_type == AS_CONFED_SEQUENCE || seg_type == AS_CONFED_SET)
472 			error = AS_ERR_SOFT;
473 		if (seg_type != AS_SET && seg_type != AS_SEQUENCE &&
474 		    seg_type != AS_CONFED_SEQUENCE && seg_type != AS_CONFED_SET)
475 			return (AS_ERR_TYPE);
476 
477 		seg_size = 2 + as_size * seg_len;
478 
479 		if (seg_size > len)
480 			return (AS_ERR_LEN);
481 
482 		/* RFC 7607 - AS 0 is considered malformed */
483 		ptr = seg + 2;
484 		for (pos = 0; pos < seg_len; pos++) {
485 			u_int32_t as;
486 
487 			memcpy(&as, ptr, as_size);
488 			if (as == 0)
489 				error = AS_ERR_SOFT;
490 			ptr += as_size;
491 		}
492 	}
493 	return (error);	/* aspath is valid but probably not loop free */
494 }
495 
496 /*
497  * convert a 2 byte aspath to a 4 byte one.
498  */
499 u_char *
500 aspath_inflate(void *data, u_int16_t len, u_int16_t *newlen)
501 {
502 	u_int8_t	*seg, *nseg, *ndata;
503 	u_int16_t	 seg_size, olen, nlen;
504 	u_int8_t	 seg_len;
505 
506 	/* first calculate the length of the aspath */
507 	seg = data;
508 	nlen = 0;
509 	for (olen = len; olen > 0; olen -= seg_size, seg += seg_size) {
510 		seg_len = seg[1];
511 		seg_size = 2 + sizeof(u_int16_t) * seg_len;
512 		nlen += 2 + sizeof(u_int32_t) * seg_len;
513 
514 		if (seg_size > olen) {
515 			errno = ERANGE;
516 			return (NULL);
517 		}
518 	}
519 
520 	*newlen = nlen;
521 	if ((ndata = malloc(nlen)) == NULL)
522 		return (NULL);
523 
524 	/* then copy the aspath */
525 	seg = data;
526 	for (nseg = ndata; nseg < ndata + nlen; ) {
527 		*nseg++ = *seg++;
528 		*nseg++ = seg_len = *seg++;
529 		for (; seg_len > 0; seg_len--) {
530 			*nseg++ = 0;
531 			*nseg++ = 0;
532 			*nseg++ = *seg++;
533 			*nseg++ = *seg++;
534 		}
535 	}
536 
537 	return (ndata);
538 }
539 
540 /* NLRI functions to extract prefixes from the NLRI blobs */
541 static int
542 extract_prefix(u_char *p, u_int16_t len, void *va,
543     u_int8_t pfxlen, u_int8_t max)
544 {
545 	static u_char addrmask[] = {
546 	    0x00, 0x80, 0xc0, 0xe0, 0xf0, 0xf8, 0xfc, 0xfe, 0xff };
547 	u_char		*a = va;
548 	int		 i;
549 	u_int16_t	 plen = 0;
550 
551 	for (i = 0; pfxlen && i < max; i++) {
552 		if (len <= plen)
553 			return (-1);
554 		if (pfxlen < 8) {
555 			a[i] = *p++ & addrmask[pfxlen];
556 			plen++;
557 			break;
558 		} else {
559 			a[i] = *p++;
560 			plen++;
561 			pfxlen -= 8;
562 		}
563 	}
564 	return (plen);
565 }
566 
567 int
568 nlri_get_prefix(u_char *p, u_int16_t len, struct bgpd_addr *prefix,
569     u_int8_t *prefixlen)
570 {
571 	u_int8_t	 pfxlen;
572 	int		 plen;
573 
574 	if (len < 1)
575 		return (-1);
576 
577 	pfxlen = *p++;
578 	len--;
579 
580 	bzero(prefix, sizeof(struct bgpd_addr));
581 	prefix->aid = AID_INET;
582 	*prefixlen = pfxlen;
583 
584 	if (pfxlen > 32)
585 		return (-1);
586 	if ((plen = extract_prefix(p, len, &prefix->v4, pfxlen,
587 	    sizeof(prefix->v4))) == -1)
588 		return (-1);
589 
590 	return (plen + 1);	/* pfxlen needs to be added */
591 }
592 
593 int
594 nlri_get_prefix6(u_char *p, u_int16_t len, struct bgpd_addr *prefix,
595     u_int8_t *prefixlen)
596 {
597 	int		plen;
598 	u_int8_t	pfxlen;
599 
600 	if (len < 1)
601 		return (-1);
602 
603 	pfxlen = *p++;
604 	len--;
605 
606 	bzero(prefix, sizeof(struct bgpd_addr));
607 	prefix->aid = AID_INET6;
608 	*prefixlen = pfxlen;
609 
610 	if (pfxlen > 128)
611 		return (-1);
612 	if ((plen = extract_prefix(p, len, &prefix->v6, pfxlen,
613 	    sizeof(prefix->v6))) == -1)
614 		return (-1);
615 
616 	return (plen + 1);	/* pfxlen needs to be added */
617 }
618 
619 int
620 nlri_get_vpn4(u_char *p, u_int16_t len, struct bgpd_addr *prefix,
621     u_int8_t *prefixlen, int withdraw)
622 {
623 	int		 rv, done = 0;
624 	u_int8_t	 pfxlen;
625 	u_int16_t	 plen;
626 
627 	if (len < 1)
628 		return (-1);
629 
630 	memcpy(&pfxlen, p, 1);
631 	p += 1;
632 	plen = 1;
633 
634 	bzero(prefix, sizeof(struct bgpd_addr));
635 
636 	/* label stack */
637 	do {
638 		if (len - plen < 3 || pfxlen < 3 * 8)
639 			return (-1);
640 		if (prefix->vpn4.labellen + 3U >
641 		    sizeof(prefix->vpn4.labelstack))
642 			return (-1);
643 		if (withdraw) {
644 			/* on withdraw ignore the labelstack all together */
645 			plen += 3;
646 			pfxlen -= 3 * 8;
647 			break;
648 		}
649 		prefix->vpn4.labelstack[prefix->vpn4.labellen++] = *p++;
650 		prefix->vpn4.labelstack[prefix->vpn4.labellen++] = *p++;
651 		prefix->vpn4.labelstack[prefix->vpn4.labellen] = *p++;
652 		if (prefix->vpn4.labelstack[prefix->vpn4.labellen] &
653 		    BGP_MPLS_BOS)
654 			done = 1;
655 		prefix->vpn4.labellen++;
656 		plen += 3;
657 		pfxlen -= 3 * 8;
658 	} while (!done);
659 
660 	/* RD */
661 	if (len - plen < (int)sizeof(u_int64_t) ||
662 	    pfxlen < sizeof(u_int64_t) * 8)
663 		return (-1);
664 	memcpy(&prefix->vpn4.rd, p, sizeof(u_int64_t));
665 	pfxlen -= sizeof(u_int64_t) * 8;
666 	p += sizeof(u_int64_t);
667 	plen += sizeof(u_int64_t);
668 
669 	/* prefix */
670 	prefix->aid = AID_VPN_IPv4;
671 	*prefixlen = pfxlen;
672 
673 	if (pfxlen > 32)
674 		return (-1);
675 	if ((rv = extract_prefix(p, len, &prefix->vpn4.addr,
676 	    pfxlen, sizeof(prefix->vpn4.addr))) == -1)
677 		return (-1);
678 
679 	return (plen + rv);
680 }
681 
682 /*
683  * This function will have undefined behaviour if the passed in prefixlen is
684  * to large for the respective bgpd_addr address family.
685  */
686 int
687 prefix_compare(const struct bgpd_addr *a, const struct bgpd_addr *b,
688     int prefixlen)
689 {
690 	in_addr_t	mask, aa, ba;
691 	int		i;
692 	u_int8_t	m;
693 
694 	if (a->aid != b->aid)
695 		return (a->aid - b->aid);
696 
697 	switch (a->aid) {
698 	case AID_INET:
699 		if (prefixlen == 0)
700 			return (0);
701 		if (prefixlen > 32)
702 			return (-1);
703 		mask = htonl(prefixlen2mask(prefixlen));
704 		aa = ntohl(a->v4.s_addr & mask);
705 		ba = ntohl(b->v4.s_addr & mask);
706 		if (aa != ba)
707 			return (aa - ba);
708 		return (0);
709 	case AID_INET6:
710 		if (prefixlen == 0)
711 			return (0);
712 		if (prefixlen > 128)
713 			return (-1);
714 		for (i = 0; i < prefixlen / 8; i++)
715 			if (a->v6.s6_addr[i] != b->v6.s6_addr[i])
716 				return (a->v6.s6_addr[i] - b->v6.s6_addr[i]);
717 		i = prefixlen % 8;
718 		if (i) {
719 			m = 0xff00 >> i;
720 			if ((a->v6.s6_addr[prefixlen / 8] & m) !=
721 			    (b->v6.s6_addr[prefixlen / 8] & m))
722 				return ((a->v6.s6_addr[prefixlen / 8] & m) -
723 				    (b->v6.s6_addr[prefixlen / 8] & m));
724 		}
725 		return (0);
726 	case AID_VPN_IPv4:
727 		if (prefixlen > 32)
728 			return (-1);
729 		if (betoh64(a->vpn4.rd) > betoh64(b->vpn4.rd))
730 			return (1);
731 		if (betoh64(a->vpn4.rd) < betoh64(b->vpn4.rd))
732 			return (-1);
733 		mask = htonl(prefixlen2mask(prefixlen));
734 		aa = ntohl(a->vpn4.addr.s_addr & mask);
735 		ba = ntohl(b->vpn4.addr.s_addr & mask);
736 		if (aa != ba)
737 			return (aa - ba);
738 		if (a->vpn4.labellen > b->vpn4.labellen)
739 			return (1);
740 		if (a->vpn4.labellen < b->vpn4.labellen)
741 			return (-1);
742 		return (memcmp(a->vpn4.labelstack, b->vpn4.labelstack,
743 		    a->vpn4.labellen));
744 	}
745 	return (-1);
746 }
747 
748 in_addr_t
749 prefixlen2mask(u_int8_t prefixlen)
750 {
751 	if (prefixlen == 0)
752 		return (0);
753 
754 	return (0xffffffff << (32 - prefixlen));
755 }
756 
757 void
758 inet4applymask(struct in_addr *dest, const struct in_addr *src, int prefixlen)
759 {
760 	struct in_addr mask;
761 
762 	mask.s_addr = htonl(prefixlen2mask(prefixlen));
763 	dest->s_addr = src->s_addr & mask.s_addr;
764 }
765 
766 void
767 inet6applymask(struct in6_addr *dest, const struct in6_addr *src, int prefixlen)
768 {
769 	struct in6_addr	mask;
770 	int		i;
771 
772 	bzero(&mask, sizeof(mask));
773 	for (i = 0; i < prefixlen / 8; i++)
774 		mask.s6_addr[i] = 0xff;
775 	i = prefixlen % 8;
776 	if (i)
777 		mask.s6_addr[prefixlen / 8] = 0xff00 >> i;
778 
779 	for (i = 0; i < 16; i++)
780 		dest->s6_addr[i] = src->s6_addr[i] & mask.s6_addr[i];
781 }
782 
783 /* address family translation functions */
784 const struct aid aid_vals[AID_MAX] = AID_VALS;
785 
786 const char *
787 aid2str(u_int8_t aid)
788 {
789 	if (aid < AID_MAX)
790 		return (aid_vals[aid].name);
791 	return ("unknown AID");
792 }
793 
794 int
795 aid2afi(u_int8_t aid, u_int16_t *afi, u_int8_t *safi)
796 {
797 	if (aid < AID_MAX) {
798 		*afi = aid_vals[aid].afi;
799 		*safi = aid_vals[aid].safi;
800 		return (0);
801 	}
802 	return (-1);
803 }
804 
805 int
806 afi2aid(u_int16_t afi, u_int8_t safi, u_int8_t *aid)
807 {
808 	u_int8_t i;
809 
810 	for (i = 0; i < AID_MAX; i++)
811 		if (aid_vals[i].afi == afi && aid_vals[i].safi == safi) {
812 			*aid = i;
813 			return (0);
814 		}
815 
816 	return (-1);
817 }
818 
819 sa_family_t
820 aid2af(u_int8_t aid)
821 {
822 	if (aid < AID_MAX)
823 		return (aid_vals[aid].af);
824 	return (AF_UNSPEC);
825 }
826 
827 int
828 af2aid(sa_family_t af, u_int8_t safi, u_int8_t *aid)
829 {
830 	u_int8_t i;
831 
832 	if (safi == 0) /* default to unicast subclass */
833 		safi = SAFI_UNICAST;
834 
835 	for (i = 0; i < AID_MAX; i++)
836 		if (aid_vals[i].af == af && aid_vals[i].safi == safi) {
837 			*aid = i;
838 			return (0);
839 		}
840 
841 	return (-1);
842 }
843 
844 struct sockaddr *
845 addr2sa(struct bgpd_addr *addr, u_int16_t port)
846 {
847 	static struct sockaddr_storage	 ss;
848 	struct sockaddr_in		*sa_in = (struct sockaddr_in *)&ss;
849 	struct sockaddr_in6		*sa_in6 = (struct sockaddr_in6 *)&ss;
850 
851 	if (addr->aid == AID_UNSPEC)
852 		return (NULL);
853 
854 	bzero(&ss, sizeof(ss));
855 	switch (addr->aid) {
856 	case AID_INET:
857 		sa_in->sin_family = AF_INET;
858 		sa_in->sin_len = sizeof(struct sockaddr_in);
859 		sa_in->sin_addr.s_addr = addr->v4.s_addr;
860 		sa_in->sin_port = htons(port);
861 		break;
862 	case AID_INET6:
863 		sa_in6->sin6_family = AF_INET6;
864 		sa_in6->sin6_len = sizeof(struct sockaddr_in6);
865 		memcpy(&sa_in6->sin6_addr, &addr->v6,
866 		    sizeof(sa_in6->sin6_addr));
867 		sa_in6->sin6_port = htons(port);
868 		sa_in6->sin6_scope_id = addr->scope_id;
869 		break;
870 	}
871 
872 	return ((struct sockaddr *)&ss);
873 }
874 
875 void
876 sa2addr(struct sockaddr *sa, struct bgpd_addr *addr)
877 {
878 	struct sockaddr_in		*sa_in = (struct sockaddr_in *)sa;
879 	struct sockaddr_in6		*sa_in6 = (struct sockaddr_in6 *)sa;
880 
881 	bzero(addr, sizeof(*addr));
882 	switch (sa->sa_family) {
883 	case AF_INET:
884 		addr->aid = AID_INET;
885 		memcpy(&addr->v4, &sa_in->sin_addr, sizeof(addr->v4));
886 		break;
887 	case AF_INET6:
888 		addr->aid = AID_INET6;
889 		memcpy(&addr->v6, &sa_in6->sin6_addr, sizeof(addr->v6));
890 		addr->scope_id = sa_in6->sin6_scope_id; /* I hate v6 */
891 		break;
892 	}
893 }
894 
895 const struct if_status_description
896 		if_status_descriptions[] = LINK_STATE_DESCRIPTIONS;
897 const struct ifmedia_description
898 		ifm_type_descriptions[] = IFM_TYPE_DESCRIPTIONS;
899 
900 uint64_t
901 ift2ifm(uint8_t if_type)
902 {
903 	switch (if_type) {
904 	case IFT_ETHER:
905 		return (IFM_ETHER);
906 	case IFT_FDDI:
907 		return (IFM_FDDI);
908 	case IFT_CARP:
909 		return (IFM_CARP);
910 	case IFT_IEEE80211:
911 		return (IFM_IEEE80211);
912 	default:
913 		return (0);
914 	}
915 }
916 
917 const char *
918 get_media_descr(uint64_t media_type)
919 {
920 	const struct ifmedia_description	*p;
921 
922 	for (p = ifm_type_descriptions; p->ifmt_string != NULL; p++)
923 		if (media_type == p->ifmt_word)
924 			return (p->ifmt_string);
925 
926 	return ("unknown media");
927 }
928 
929 const char *
930 get_linkstate(uint8_t if_type, int link_state)
931 {
932 	const struct if_status_description *p;
933 	static char buf[8];
934 
935 	for (p = if_status_descriptions; p->ifs_string != NULL; p++) {
936 		if (LINK_STATE_DESC_MATCH(p, if_type, link_state))
937 			return (p->ifs_string);
938 	}
939 	snprintf(buf, sizeof(buf), "[#%d]", link_state);
940 	return (buf);
941 }
942 
943 const char *
944 get_baudrate(u_int64_t baudrate, char *unit)
945 {
946 	static char bbuf[16];
947 
948 	if (baudrate > IF_Gbps(1))
949 		snprintf(bbuf, sizeof(bbuf), "%llu G%s",
950 		    baudrate / IF_Gbps(1), unit);
951 	else if (baudrate > IF_Mbps(1))
952 		snprintf(bbuf, sizeof(bbuf), "%llu M%s",
953 		    baudrate / IF_Mbps(1), unit);
954 	else if (baudrate > IF_Kbps(1))
955 		snprintf(bbuf, sizeof(bbuf), "%llu K%s",
956 		    baudrate / IF_Kbps(1), unit);
957 	else
958 		snprintf(bbuf, sizeof(bbuf), "%llu %s",
959 		    baudrate, unit);
960 
961 	return (bbuf);
962 }
963