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