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