xref: /openbsd-src/usr.sbin/bgpd/util.c (revision 6a13ef69787db04ae501a22e92fa10865b44fd7c)
1 /*	$OpenBSD: util.c,v 1.22 2017/01/13 18:59:12 phessler 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 <netinet/in.h>
22 #include <arpa/inet.h>
23 #include <netdb.h>
24 #include <stdlib.h>
25 #include <stdio.h>
26 #include <string.h>
27 #include <vis.h>
28 
29 #include "bgpd.h"
30 #include "rde.h"
31 
32 const char	*aspath_delim(u_int8_t, int);
33 
34 const char *
35 log_addr(const struct bgpd_addr *addr)
36 {
37 	static char	buf[48];
38 	char		tbuf[16];
39 
40 	switch (addr->aid) {
41 	case AID_INET:
42 	case AID_INET6:
43 		if (inet_ntop(aid2af(addr->aid), &addr->ba, buf,
44 		    sizeof(buf)) == NULL)
45 			return ("?");
46 		return (buf);
47 	case AID_VPN_IPv4:
48 		if (inet_ntop(AF_INET, &addr->vpn4.addr, tbuf,
49 		    sizeof(tbuf)) == NULL)
50 			return ("?");
51 		snprintf(buf, sizeof(buf), "%s %s", log_rd(addr->vpn4.rd),
52 		   tbuf);
53 		return (buf);
54 	}
55 	return ("???");
56 }
57 
58 const char *
59 log_in6addr(const struct in6_addr *addr)
60 {
61 	struct sockaddr_in6	sa_in6;
62 	u_int16_t		tmp16;
63 
64 	bzero(&sa_in6, sizeof(sa_in6));
65 	sa_in6.sin6_len = sizeof(sa_in6);
66 	sa_in6.sin6_family = AF_INET6;
67 	memcpy(&sa_in6.sin6_addr, addr, sizeof(sa_in6.sin6_addr));
68 
69 	/* XXX thanks, KAME, for this ugliness... adopted from route/show.c */
70 	if (IN6_IS_ADDR_LINKLOCAL(&sa_in6.sin6_addr) ||
71 	    IN6_IS_ADDR_MC_LINKLOCAL(&sa_in6.sin6_addr)) {
72 		memcpy(&tmp16, &sa_in6.sin6_addr.s6_addr[2], sizeof(tmp16));
73 		sa_in6.sin6_scope_id = ntohs(tmp16);
74 		sa_in6.sin6_addr.s6_addr[2] = 0;
75 		sa_in6.sin6_addr.s6_addr[3] = 0;
76 	}
77 
78 	return (log_sockaddr((struct sockaddr *)&sa_in6));
79 }
80 
81 const char *
82 log_sockaddr(struct sockaddr *sa)
83 {
84 	static char	buf[NI_MAXHOST];
85 
86 	if (getnameinfo(sa, sa->sa_len, buf, sizeof(buf), NULL, 0,
87 	    NI_NUMERICHOST))
88 		return ("(unknown)");
89 	else
90 		return (buf);
91 }
92 
93 const char *
94 log_as(u_int32_t as)
95 {
96 	static char	buf[11];	/* "4294967294\0" */
97 
98 	if (snprintf(buf, sizeof(buf), "%u", as) == -1)
99 		return ("?");
100 
101 	return (buf);
102 }
103 
104 const char *
105 log_rd(u_int64_t rd)
106 {
107 	static char	buf[32];
108 	struct in_addr	addr;
109 	u_int32_t	u32;
110 	u_int16_t	u16;
111 
112 	rd = betoh64(rd);
113 	switch (rd >> 48) {
114 	case EXT_COMMUNITY_TWO_AS:
115 		u32 = rd & 0xffffffff;
116 		u16 = (rd >> 32) & 0xffff;
117 		snprintf(buf, sizeof(buf), "rd %hu:%u", u16, u32);
118 		break;
119 	case EXT_COMMUNITY_FOUR_AS:
120 		u32 = (rd >> 16) & 0xffffffff;
121 		u16 = rd & 0xffff;
122 		snprintf(buf, sizeof(buf), "rd %s:%hu", log_as(u32), u16);
123 		break;
124 	case EXT_COMMUNITY_IPV4:
125 		u32 = (rd >> 16) & 0xffffffff;
126 		u16 = rd & 0xffff;
127 		addr.s_addr = htonl(u32);
128 		snprintf(buf, sizeof(buf), "rd %s:%hu", inet_ntoa(addr), u16);
129 		break;
130 	default:
131 		return ("rd ?");
132 	}
133 	return (buf);
134 }
135 
136 /* NOTE: this function does not check if the type/subtype combo is
137  * actually valid. */
138 const char *
139 log_ext_subtype(u_int8_t subtype)
140 {
141 	static char etype[6];
142 
143 	switch (subtype) {
144 	case EXT_COMMUNITY_ROUTE_TGT:
145 		return ("rt");	/* route target */
146 	case EXT_COMMUNITY_ROUTE_ORIG:
147 		return ("soo");	/* source of origin */
148 	case EXT_COMMUNITY_OSPF_DOM_ID:
149 		return ("odi");	/* ospf domain id */
150 	case EXT_COMMUNITY_OSPF_RTR_TYPE:
151 		return ("ort");	/* ospf route type */
152 	case EXT_COMMUNITY_OSPF_RTR_ID:
153 		return ("ori");	/* ospf router id */
154 	case EXT_COMMUNITY_BGP_COLLECT:
155 		return ("bdc");	/* bgp data collection */
156 	default:
157 		snprintf(etype, sizeof(etype), "[%u]", subtype);
158 		return (etype);
159 	}
160 }
161 
162 const char *
163 log_shutcomm(const char *communication) {
164 	static char buf[(SHUT_COMM_LEN - 1) * 4 + 1];
165 
166 	strnvis(buf, communication, sizeof(buf), VIS_NL | VIS_OCTAL);
167 
168 	return buf;
169 }
170 
171 const char *
172 aspath_delim(u_int8_t seg_type, int closing)
173 {
174 	static char db[8];
175 
176 	switch (seg_type) {
177 	case AS_SET:
178 		if (!closing)
179 			return ("{ ");
180 		else
181 			return (" }");
182 	case AS_SEQUENCE:
183 		return ("");
184 	case AS_CONFED_SEQUENCE:
185 		if (!closing)
186 			return ("( ");
187 		else
188 			return (" )");
189 	case AS_CONFED_SET:
190 		if (!closing)
191 			return ("[ ");
192 		else
193 			return (" ]");
194 	default:
195 		if (!closing)
196 			snprintf(db, sizeof(db), "!%u ", seg_type);
197 		else
198 			snprintf(db, sizeof(db), " !%u", seg_type);
199 		return (db);
200 	}
201 }
202 
203 int
204 aspath_snprint(char *buf, size_t size, void *data, u_int16_t len)
205 {
206 #define UPDATE()				\
207 	do {					\
208 		if (r == -1)			\
209 			return (-1);		\
210 		total_size += r;		\
211 		if ((unsigned int)r < size) {	\
212 			size -= r;		\
213 			buf += r;		\
214 		} else {			\
215 			buf += size;		\
216 			size = 0;		\
217 		}				\
218 	} while (0)
219 	u_int8_t	*seg;
220 	int		 r, total_size;
221 	u_int16_t	 seg_size;
222 	u_int8_t	 i, seg_type, seg_len;
223 
224 	total_size = 0;
225 	seg = data;
226 	for (; len > 0; len -= seg_size, seg += seg_size) {
227 		seg_type = seg[0];
228 		seg_len = seg[1];
229 		seg_size = 2 + sizeof(u_int32_t) * seg_len;
230 
231 		r = snprintf(buf, size, "%s%s",
232 		    total_size != 0 ? " " : "",
233 		    aspath_delim(seg_type, 0));
234 		UPDATE();
235 
236 		for (i = 0; i < seg_len; i++) {
237 			r = snprintf(buf, size, "%s",
238 			    log_as(aspath_extract(seg, i)));
239 			UPDATE();
240 			if (i + 1 < seg_len) {
241 				r = snprintf(buf, size, " ");
242 				UPDATE();
243 			}
244 		}
245 		r = snprintf(buf, size, "%s", aspath_delim(seg_type, 1));
246 		UPDATE();
247 	}
248 	/* ensure that we have a valid C-string especially for empty as path */
249 	if (size > 0)
250 		*buf = '\0';
251 
252 	return (total_size);
253 #undef UPDATE
254 }
255 
256 int
257 aspath_asprint(char **ret, void *data, u_int16_t len)
258 {
259 	size_t	slen;
260 	int	plen;
261 
262 	slen = aspath_strlen(data, len) + 1;
263 	*ret = malloc(slen);
264 	if (*ret == NULL)
265 		return (-1);
266 
267 	plen = aspath_snprint(*ret, slen, data, len);
268 	if (plen == -1) {
269 		free(*ret);
270 		*ret = NULL;
271 		return (-1);
272 	}
273 
274 	return (0);
275 }
276 
277 size_t
278 aspath_strlen(void *data, u_int16_t len)
279 {
280 	u_int8_t	*seg;
281 	int		 total_size;
282 	u_int32_t	 as;
283 	u_int16_t	 seg_size;
284 	u_int8_t	 i, seg_type, seg_len;
285 
286 	total_size = 0;
287 	seg = data;
288 	for (; len > 0; len -= seg_size, seg += seg_size) {
289 		seg_type = seg[0];
290 		seg_len = seg[1];
291 		seg_size = 2 + sizeof(u_int32_t) * seg_len;
292 
293 		if (seg_type == AS_SET)
294 			if (total_size != 0)
295 				total_size += 3;
296 			else
297 				total_size += 2;
298 		else if (total_size != 0)
299 			total_size += 1;
300 
301 		for (i = 0; i < seg_len; i++) {
302 			as = aspath_extract(seg, i);
303 
304 			do {
305 				total_size++;
306 			} while ((as = as / 10) != 0);
307 
308 			if (i + 1 < seg_len)
309 				total_size += 1;
310 		}
311 
312 		if (seg_type == AS_SET)
313 			total_size += 2;
314 	}
315 	return (total_size);
316 }
317 
318 /* we need to be able to search more than one as */
319 int
320 aspath_match(void *data, u_int16_t len, struct filter_as *f, u_int32_t match)
321 {
322 	u_int8_t	*seg;
323 	int		 final;
324 	u_int16_t	 seg_size;
325 	u_int8_t	 i, seg_len;
326 	u_int32_t	 as;
327 
328 	if (f->type == AS_EMPTY) {
329 		if (len == 0)
330 			return (1);
331 		else
332 			return (0);
333 	}
334 
335 	seg = data;
336 	for (; len > 0; len -= seg_size, seg += seg_size) {
337 		seg_len = seg[1];
338 		seg_size = 2 + sizeof(u_int32_t) * seg_len;
339 
340 		final = (len == seg_size);
341 
342 		/* just check the first (leftmost) AS */
343 		if (f->type == AS_PEER) {
344 			as = aspath_extract(seg, 0);
345 			if (as_compare(f->op, as, match, f->as_min, f->as_max))
346 				return (1);
347 			else
348 				return (0);
349 		}
350 		/* just check the final (rightmost) AS */
351 		if (f->type == AS_SOURCE) {
352 			/* not yet in the final segment */
353 			if (!final)
354 				continue;
355 			as = aspath_extract(seg, seg_len - 1);
356 			if (as_compare(f->op, as, match, f->as_min, f->as_max))
357 				return (1);
358 			else
359 				return (0);
360 		}
361 		/* AS_TRANSIT or AS_ALL */
362 		for (i = 0; i < seg_len; i++) {
363 			/*
364 			 * the source (rightmost) AS is excluded from
365 			 * AS_TRANSIT matches.
366 			 */
367 			if (final && i == seg_len - 1 && f->type == AS_TRANSIT)
368 				return (0);
369 			as = aspath_extract(seg, i);
370 			if (as_compare(f->op, as, match, f->as_min, f->as_max))
371 				return (1);
372 		}
373 	}
374 	return (0);
375 }
376 
377 int
378 as_compare(u_int8_t op, u_int32_t as, u_int32_t match, u_int32_t as_min,
379     u_int32_t as_max)
380 {
381 	if ((op == OP_NONE || op == OP_EQ) && as == match)
382 		return (1);
383 	else if (op == OP_NE && as != match)
384 		return (1);
385 	else if (op == OP_RANGE && as >= as_min && as <= as_max)
386 		return (1);
387 	else if (op == OP_XRANGE && as > as_min && as < as_max)
388 		return (1);
389 	return (0);
390 }
391 
392 /*
393  * Extract the asnum out of the as segment at the specified position.
394  * Direct access is not possible because of non-aligned reads.
395  * ATTENTION: no bounds checks are done.
396  */
397 u_int32_t
398 aspath_extract(const void *seg, int pos)
399 {
400 	const u_char	*ptr = seg;
401 	u_int32_t	 as;
402 
403 	ptr += 2 + sizeof(u_int32_t) * pos;
404 	memcpy(&as, ptr, sizeof(u_int32_t));
405 	return (ntohl(as));
406 }
407 
408 int
409 prefix_compare(const struct bgpd_addr *a, const struct bgpd_addr *b,
410     int prefixlen)
411 {
412 	in_addr_t	mask, aa, ba;
413 	int		i;
414 	u_int8_t	m;
415 
416 	if (a->aid != b->aid)
417 		return (a->aid - b->aid);
418 
419 	switch (a->aid) {
420 	case AID_INET:
421 		if (prefixlen == 0)
422 			return (0);
423 		if (prefixlen > 32)
424 			fatalx("prefix_cmp: bad IPv4 prefixlen");
425 		mask = htonl(prefixlen2mask(prefixlen));
426 		aa = ntohl(a->v4.s_addr & mask);
427 		ba = ntohl(b->v4.s_addr & mask);
428 		if (aa != ba)
429 			return (aa - ba);
430 		return (0);
431 	case AID_INET6:
432 		if (prefixlen == 0)
433 			return (0);
434 		if (prefixlen > 128)
435 			fatalx("prefix_cmp: bad IPv6 prefixlen");
436 		for (i = 0; i < prefixlen / 8; i++)
437 			if (a->v6.s6_addr[i] != b->v6.s6_addr[i])
438 				return (a->v6.s6_addr[i] - b->v6.s6_addr[i]);
439 		i = prefixlen % 8;
440 		if (i) {
441 			m = 0xff00 >> i;
442 			if ((a->v6.s6_addr[prefixlen / 8] & m) !=
443 			    (b->v6.s6_addr[prefixlen / 8] & m))
444 				return ((a->v6.s6_addr[prefixlen / 8] & m) -
445 				    (b->v6.s6_addr[prefixlen / 8] & m));
446 		}
447 		return (0);
448 	case AID_VPN_IPv4:
449 		if (prefixlen > 32)
450 			fatalx("prefix_cmp: bad IPv4 VPN prefixlen");
451 		if (betoh64(a->vpn4.rd) > betoh64(b->vpn4.rd))
452 			return (1);
453 		if (betoh64(a->vpn4.rd) < betoh64(b->vpn4.rd))
454 			return (-1);
455 		mask = htonl(prefixlen2mask(prefixlen));
456 		aa = ntohl(a->vpn4.addr.s_addr & mask);
457 		ba = ntohl(b->vpn4.addr.s_addr & mask);
458 		if (aa != ba)
459 			return (aa - ba);
460 		if (a->vpn4.labellen > b->vpn4.labellen)
461 			return (1);
462 		if (a->vpn4.labellen < b->vpn4.labellen)
463 			return (-1);
464 		return (memcmp(a->vpn4.labelstack, b->vpn4.labelstack,
465 		    a->vpn4.labellen));
466 	default:
467 		fatalx("prefix_cmp: unknown af");
468 	}
469 	return (-1);
470 }
471 
472 in_addr_t
473 prefixlen2mask(u_int8_t prefixlen)
474 {
475 	if (prefixlen == 0)
476 		return (0);
477 
478 	return (0xffffffff << (32 - prefixlen));
479 }
480 
481 void
482 inet6applymask(struct in6_addr *dest, const struct in6_addr *src, int prefixlen)
483 {
484 	struct in6_addr	mask;
485 	int		i;
486 
487 	bzero(&mask, sizeof(mask));
488 	for (i = 0; i < prefixlen / 8; i++)
489 		mask.s6_addr[i] = 0xff;
490 	i = prefixlen % 8;
491 	if (i)
492 		mask.s6_addr[prefixlen / 8] = 0xff00 >> i;
493 
494 	for (i = 0; i < 16; i++)
495 		dest->s6_addr[i] = src->s6_addr[i] & mask.s6_addr[i];
496 }
497 
498 /* address family translation functions */
499 const struct aid aid_vals[AID_MAX] = AID_VALS;
500 
501 const char *
502 aid2str(u_int8_t aid)
503 {
504 	if (aid < AID_MAX)
505 		return (aid_vals[aid].name);
506 	return ("unknown AID");
507 }
508 
509 int
510 aid2afi(u_int8_t aid, u_int16_t *afi, u_int8_t *safi)
511 {
512 	if (aid < AID_MAX) {
513 		*afi = aid_vals[aid].afi;
514 		*safi = aid_vals[aid].safi;
515 		return (0);
516 	}
517 	return (-1);
518 }
519 
520 int
521 afi2aid(u_int16_t afi, u_int8_t safi, u_int8_t *aid)
522 {
523 	u_int8_t i;
524 
525 	for (i = 0; i < AID_MAX; i++)
526 		if (aid_vals[i].afi == afi && aid_vals[i].safi == safi) {
527 			*aid = i;
528 			return (0);
529 		}
530 
531 	return (-1);
532 }
533 
534 sa_family_t
535 aid2af(u_int8_t aid)
536 {
537 	if (aid < AID_MAX)
538 		return (aid_vals[aid].af);
539 	return (AF_UNSPEC);
540 }
541 
542 int
543 af2aid(sa_family_t af, u_int8_t safi, u_int8_t *aid)
544 {
545 	u_int8_t i;
546 
547 	if (safi == 0) /* default to unicast subclass */
548 		safi = SAFI_UNICAST;
549 
550 	for (i = 0; i < AID_MAX; i++)
551 		if (aid_vals[i].af == af && aid_vals[i].safi == safi) {
552 			*aid = i;
553 			return (0);
554 		}
555 
556 	return (-1);
557 }
558 
559 struct sockaddr *
560 addr2sa(struct bgpd_addr *addr, u_int16_t port)
561 {
562 	static struct sockaddr_storage	 ss;
563 	struct sockaddr_in		*sa_in = (struct sockaddr_in *)&ss;
564 	struct sockaddr_in6		*sa_in6 = (struct sockaddr_in6 *)&ss;
565 
566 	if (addr->aid == AID_UNSPEC)
567 		return (NULL);
568 
569 	bzero(&ss, sizeof(ss));
570 	switch (addr->aid) {
571 	case AID_INET:
572 		sa_in->sin_family = AF_INET;
573 		sa_in->sin_len = sizeof(struct sockaddr_in);
574 		sa_in->sin_addr.s_addr = addr->v4.s_addr;
575 		sa_in->sin_port = htons(port);
576 		break;
577 	case AID_INET6:
578 		sa_in6->sin6_family = AF_INET6;
579 		sa_in6->sin6_len = sizeof(struct sockaddr_in6);
580 		memcpy(&sa_in6->sin6_addr, &addr->v6,
581 		    sizeof(sa_in6->sin6_addr));
582 		sa_in6->sin6_port = htons(port);
583 		sa_in6->sin6_scope_id = addr->scope_id;
584 		break;
585 	}
586 
587 	return ((struct sockaddr *)&ss);
588 }
589 
590 void
591 sa2addr(struct sockaddr *sa, struct bgpd_addr *addr)
592 {
593 	struct sockaddr_in		*sa_in = (struct sockaddr_in *)sa;
594 	struct sockaddr_in6		*sa_in6 = (struct sockaddr_in6 *)sa;
595 
596 	bzero(addr, sizeof(*addr));
597 	switch (sa->sa_family) {
598 	case AF_INET:
599 		addr->aid = AID_INET;
600 		memcpy(&addr->v4, &sa_in->sin_addr, sizeof(addr->v4));
601 		break;
602 	case AF_INET6:
603 		addr->aid = AID_INET6;
604 		memcpy(&addr->v6, &sa_in6->sin6_addr, sizeof(addr->v6));
605 		addr->scope_id = sa_in6->sin6_scope_id; /* I hate v6 */
606 		break;
607 	}
608 }
609