xref: /openbsd/usr.sbin/bgpd/util.c (revision 8932bfb7)
1 /*	$OpenBSD: util.c,v 1.13 2010/11/18 12:18:31 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 <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[12];	/* "65000.65000\0" */
96 
97 	if (as <= USHRT_MAX) {
98 		if (snprintf(buf, sizeof(buf), "%u", as) == -1)
99 			return ("?");
100 	} else {
101 		if (snprintf(buf, sizeof(buf), "%u.%u", as >> 16,
102 		    as & 0xffff) == -1)
103 			return ("?");
104 	}
105 	return (buf);
106 }
107 
108 const char *
109 log_rd(u_int64_t rd)
110 {
111 	static char	buf[32];
112 	struct in_addr	addr;
113 	u_int32_t	u32;
114 	u_int16_t	u16;
115 
116 	rd = betoh64(rd);
117 	switch (rd >> 48) {
118 	case EXT_COMMUNITY_TWO_AS:
119 		u32 = rd & 0xffffffff;
120 		u16 = (rd >> 32) & 0xffff;
121 		snprintf(buf, sizeof(buf), "rd %i:%i", u16, u32);
122 		break;
123 	case EXT_COMMUNITY_FOUR_AS:
124 		u32 = (rd >> 16) & 0xffffffff;
125 		u16 = rd & 0xffff;
126 		snprintf(buf, sizeof(buf), "rd %s:%i", log_as(u32), u16);
127 		break;
128 	case EXT_COMMUNITY_IPV4:
129 		u32 = (rd >> 16) & 0xffffffff;
130 		u16 = rd & 0xffff;
131 		addr.s_addr = htonl(u32);
132 		snprintf(buf, sizeof(buf), "rd %s:%i", inet_ntoa(addr), u16);
133 		break;
134 	default:
135 		return ("rd ?");
136 	}
137 	return (buf);
138 }
139 
140 /* NOTE: this function does not check if the type/subtype combo is
141  * actually valid. */
142 const char *
143 log_ext_subtype(u_int8_t subtype)
144 {
145 	static char etype[6];
146 
147 	switch (subtype) {
148 	case EXT_COMMUNITY_ROUTE_TGT:
149 		return ("rt");	/* route target */
150 	case EXT_CUMMUNITY_ROUTE_ORIG:
151 		return ("soo");	/* source of origin */
152 	case EXT_COMMUNITY_OSPF_DOM_ID:
153 		return ("odi");	/* ospf domain id */
154 	case EXT_COMMUNITY_OSPF_RTR_TYPE:
155 		return ("ort");	/* ospf route type */
156 	case EXT_COMMUNITY_OSPF_RTR_ID:
157 		return ("ori");	/* ospf router id */
158 	case EXT_COMMUNITY_BGP_COLLECT:
159 		return ("bdc");	/* bgp data collection */
160 	default:
161 		snprintf(etype, sizeof(etype), "[%u]", subtype);
162 		return (etype);
163 	}
164 }
165 
166 const char *
167 aspath_delim(u_int8_t seg_type, int closing)
168 {
169 	static char db[8];
170 
171 	switch (seg_type) {
172 	case AS_SET:
173 		if (!closing)
174 			return ("{ ");
175 		else
176 			return (" }");
177 	case AS_SEQUENCE:
178 		return ("");
179 	case AS_CONFED_SEQUENCE:
180 		if (!closing)
181 			return ("( ");
182 		else
183 			return (" )");
184 	case AS_CONFED_SET:
185 		if (!closing)
186 			return ("[ ");
187 		else
188 			return (" ]");
189 	default:
190 		if (!closing)
191 			snprintf(db, sizeof(db), "!%u ", seg_type);
192 		else
193 			snprintf(db, sizeof(db), " !%u", seg_type);
194 		return (db);
195 	}
196 }
197 
198 int
199 aspath_snprint(char *buf, size_t size, void *data, u_int16_t len)
200 {
201 #define UPDATE()				\
202 	do {					\
203 		if (r == -1)			\
204 			return (-1);		\
205 		total_size += r;		\
206 		if ((unsigned int)r < size) {	\
207 			size -= r;		\
208 			buf += r;		\
209 		} else {			\
210 			buf += size;		\
211 			size = 0;		\
212 		}				\
213 	} while (0)
214 	u_int8_t	*seg;
215 	int		 r, total_size;
216 	u_int16_t	 seg_size;
217 	u_int8_t	 i, seg_type, seg_len;
218 
219 	total_size = 0;
220 	seg = data;
221 	for (; len > 0; len -= seg_size, seg += seg_size) {
222 		seg_type = seg[0];
223 		seg_len = seg[1];
224 		seg_size = 2 + sizeof(u_int32_t) * seg_len;
225 
226 		r = snprintf(buf, size, "%s%s",
227 		    total_size != 0 ? " " : "",
228 		    aspath_delim(seg_type, 0));
229 		UPDATE();
230 
231 		for (i = 0; i < seg_len; i++) {
232 			r = snprintf(buf, size, "%s",
233 			    log_as(aspath_extract(seg, i)));
234 			UPDATE();
235 			if (i + 1 < seg_len) {
236 				r = snprintf(buf, size, " ");
237 				UPDATE();
238 			}
239 		}
240 		r = snprintf(buf, size, "%s", aspath_delim(seg_type, 1));
241 		UPDATE();
242 	}
243 	/* ensure that we have a valid C-string especially for empty as path */
244 	if (size > 0)
245 		*buf = '\0';
246 
247 	return (total_size);
248 #undef UPDATE
249 }
250 
251 int
252 aspath_asprint(char **ret, void *data, u_int16_t len)
253 {
254 	size_t	slen;
255 	int	plen;
256 
257 	slen = aspath_strlen(data, len) + 1;
258 	*ret = malloc(slen);
259 	if (*ret == NULL)
260 		return (-1);
261 
262 	plen = aspath_snprint(*ret, slen, data, len);
263 	if (plen == -1) {
264 		free(*ret);
265 		*ret = NULL;
266 		return (-1);
267 	}
268 
269 	return (0);
270 }
271 
272 size_t
273 aspath_strlen(void *data, u_int16_t len)
274 {
275 	u_int8_t	*seg;
276 	int		 total_size;
277 	u_int32_t	 as;
278 	u_int16_t	 seg_size;
279 	u_int8_t	 i, seg_type, seg_len;
280 
281 	total_size = 0;
282 	seg = data;
283 	for (; len > 0; len -= seg_size, seg += seg_size) {
284 		seg_type = seg[0];
285 		seg_len = seg[1];
286 		seg_size = 2 + sizeof(u_int32_t) * seg_len;
287 
288 		if (seg_type == AS_SET)
289 			if (total_size != 0)
290 				total_size += 3;
291 			else
292 				total_size += 2;
293 		else if (total_size != 0)
294 			total_size += 1;
295 
296 		for (i = 0; i < seg_len; i++) {
297 			as = aspath_extract(seg, i);
298 			if (as > USHRT_MAX) {
299 				u_int32_t	a = as >> 16;
300 
301 				if (a >= 10000)
302 					total_size += 5;
303 				else if (a >= 1000)
304 					total_size += 4;
305 				else if (a >= 100)
306 					total_size += 3;
307 				else if (a >= 10)
308 					total_size += 2;
309 				else
310 					total_size += 1;
311 				total_size += 1; /* dot between hi & lo */
312 				as &= 0xffff;
313 			}
314 			if (as >= 10000)
315 				total_size += 5;
316 			else if (as >= 1000)
317 				total_size += 4;
318 			else if (as >= 100)
319 				total_size += 3;
320 			else if (as >= 10)
321 				total_size += 2;
322 			else
323 				total_size += 1;
324 
325 			if (i + 1 < seg_len)
326 				total_size += 1;
327 		}
328 
329 		if (seg_type == AS_SET)
330 			total_size += 2;
331 	}
332 	return (total_size);
333 }
334 
335 /*
336  * Extract the asnum out of the as segment at the specified position.
337  * Direct access is not possible because of non-aligned reads.
338  * ATTENTION: no bounds checks are done.
339  */
340 u_int32_t
341 aspath_extract(const void *seg, int pos)
342 {
343 	const u_char	*ptr = seg;
344 	u_int32_t	 as;
345 
346 	ptr += 2 + sizeof(u_int32_t) * pos;
347 	memcpy(&as, ptr, sizeof(u_int32_t));
348 	return (ntohl(as));
349 }
350 
351 in_addr_t
352 prefixlen2mask(u_int8_t prefixlen)
353 {
354 	if (prefixlen == 0)
355 		return (0);
356 
357 	return (0xffffffff << (32 - prefixlen));
358 }
359 
360 void
361 inet6applymask(struct in6_addr *dest, const struct in6_addr *src, int prefixlen)
362 {
363 	struct in6_addr	mask;
364 	int		i;
365 
366 	bzero(&mask, sizeof(mask));
367 	for (i = 0; i < prefixlen / 8; i++)
368 		mask.s6_addr[i] = 0xff;
369 	i = prefixlen % 8;
370 	if (i)
371 		mask.s6_addr[prefixlen / 8] = 0xff00 >> i;
372 
373 	for (i = 0; i < 16; i++)
374 		dest->s6_addr[i] = src->s6_addr[i] & mask.s6_addr[i];
375 }
376 
377 /* address family translation functions */
378 const struct aid aid_vals[AID_MAX] = AID_VALS;
379 
380 const char *
381 aid2str(u_int8_t aid)
382 {
383 	if (aid < AID_MAX)
384 		return (aid_vals[aid].name);
385 	return ("unknown AID");
386 }
387 
388 int
389 aid2afi(u_int8_t aid, u_int16_t *afi, u_int8_t *safi)
390 {
391 	if (aid < AID_MAX) {
392 		*afi = aid_vals[aid].afi;
393 		*safi = aid_vals[aid].safi;
394 		return (0);
395 	}
396 	return (-1);
397 }
398 
399 int
400 afi2aid(u_int16_t afi, u_int8_t safi, u_int8_t *aid)
401 {
402 	u_int8_t i;
403 
404 	for (i = 0; i < AID_MAX; i++)
405 		if (aid_vals[i].afi == afi && aid_vals[i].safi == safi) {
406 			*aid = i;
407 			return (0);
408 		}
409 
410 	return (-1);
411 }
412 
413 sa_family_t
414 aid2af(u_int8_t aid)
415 {
416 	if (aid < AID_MAX)
417 		return (aid_vals[aid].af);
418 	return (AF_UNSPEC);
419 }
420 
421 int
422 af2aid(sa_family_t af, u_int8_t safi, u_int8_t *aid)
423 {
424 	u_int8_t i;
425 
426 	if (safi == 0) /* default to unicast subclass */
427 		safi = SAFI_UNICAST;
428 
429 	for (i = 0; i < AID_MAX; i++)
430 		if (aid_vals[i].af == af && aid_vals[i].safi == safi) {
431 			*aid = i;
432 			return (0);
433 		}
434 
435 	return (-1);
436 }
437 
438 struct sockaddr *
439 addr2sa(struct bgpd_addr *addr, u_int16_t port)
440 {
441 	static struct sockaddr_storage	 ss;
442 	struct sockaddr_in		*sa_in = (struct sockaddr_in *)&ss;
443 	struct sockaddr_in6		*sa_in6 = (struct sockaddr_in6 *)&ss;
444 
445 	if (addr->aid == AID_UNSPEC)
446 		return (NULL);
447 
448 	bzero(&ss, sizeof(ss));
449 	switch (addr->aid) {
450 	case AID_INET:
451 		sa_in->sin_family = AF_INET;
452 		sa_in->sin_len = sizeof(struct sockaddr_in);
453 		sa_in->sin_addr.s_addr = addr->v4.s_addr;
454 		sa_in->sin_port = htons(port);
455 		break;
456 	case AID_INET6:
457 		sa_in6->sin6_family = AF_INET6;
458 		sa_in6->sin6_len = sizeof(struct sockaddr_in6);
459 		memcpy(&sa_in6->sin6_addr, &addr->v6,
460 		    sizeof(sa_in6->sin6_addr));
461 		sa_in6->sin6_port = htons(port);
462 		sa_in6->sin6_scope_id = addr->scope_id;
463 		break;
464 	}
465 
466 	return ((struct sockaddr *)&ss);
467 }
468 
469 void
470 sa2addr(struct sockaddr *sa, struct bgpd_addr *addr)
471 {
472 	struct sockaddr_in		*sa_in = (struct sockaddr_in *)sa;
473 	struct sockaddr_in6		*sa_in6 = (struct sockaddr_in6 *)sa;
474 
475 	bzero(addr, sizeof(*addr));
476 	switch (sa->sa_family) {
477 	case AF_INET:
478 		addr->aid = AID_INET;
479 		memcpy(&addr->v4, &sa_in->sin_addr, sizeof(addr->v4));
480 		break;
481 	case AF_INET6:
482 		addr->aid = AID_INET6;
483 		memcpy(&addr->v6, &sa_in6->sin6_addr, sizeof(addr->v6));
484 		addr->scope_id = sa_in6->sin6_scope_id; /* I hate v6 */
485 		break;
486 	}
487 }
488