1 /* $OpenBSD: mrt.c,v 1.123 2024/12/16 16:10:10 claudio Exp $ */ 2 3 /* 4 * Copyright (c) 2003, 2004 Claudio Jeker <claudio@openbsd.org> 5 * 6 * Permission to use, copy, modify, and distribute this software for any 7 * purpose with or without fee is hereby granted, provided that the above 8 * copyright notice and this permission notice appear in all copies. 9 * 10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 13 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 14 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 15 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 16 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17 */ 18 19 #include <sys/types.h> 20 #include <sys/queue.h> 21 22 #include <errno.h> 23 #include <fcntl.h> 24 #include <limits.h> 25 #include <stdlib.h> 26 #include <string.h> 27 #include <time.h> 28 #include <unistd.h> 29 30 #include "bgpd.h" 31 #include "rde.h" 32 #include "session.h" 33 34 #include "mrt.h" 35 #include "log.h" 36 37 static int mrt_attr_dump(struct ibuf *, struct rde_aspath *, 38 struct rde_community *, struct bgpd_addr *, int); 39 static int mrt_dump_entry_mp(struct mrt *, struct prefix *, uint16_t, 40 struct rde_peer*); 41 static int mrt_dump_entry(struct mrt *, struct prefix *, uint16_t, 42 struct rde_peer*); 43 static int mrt_dump_entry_v2(struct mrt *, struct rib_entry *, uint32_t); 44 static int mrt_dump_peer(struct ibuf *, struct rde_peer *); 45 static int mrt_dump_hdr_se(struct ibuf **, struct peer *, uint16_t, 46 uint16_t, uint32_t, int); 47 static int mrt_dump_hdr_rde(struct ibuf **, uint16_t type, uint16_t, 48 uint32_t); 49 static int mrt_open(struct mrt *, time_t); 50 51 #define RDEIDX 0 52 #define SEIDX 1 53 #define TYPE2IDX(x) ((x == MRT_TABLE_DUMP || \ 54 x == MRT_TABLE_DUMP_MP || \ 55 x == MRT_TABLE_DUMP_V2) ? RDEIDX : SEIDX \ 56 ) 57 58 static uint8_t 59 mrt_update_msg_guess_aid(struct ibuf *pkg) 60 { 61 struct ibuf buf; 62 uint16_t wlen, alen, len, afi; 63 uint8_t type, flags, aid, safi; 64 65 ibuf_from_ibuf(&buf, pkg); 66 67 if (ibuf_skip(&buf, MSGSIZE_HEADER) == -1 || 68 ibuf_get_n16(&buf, &wlen) == -1) 69 goto bad; 70 71 if (wlen > 0) { 72 /* UPDATE has withdraw routes, therefore IPv4 */ 73 return AID_INET; 74 } 75 76 if (ibuf_get_n16(&buf, &alen) == -1) 77 goto bad; 78 79 if (alen < ibuf_size(&buf)) { 80 /* UPDATE has NLRI prefixes, therefore IPv4 */ 81 return AID_INET; 82 } 83 84 if (wlen == 0 && alen == 0) { 85 /* UPDATE is an IPv4 EoR marker */ 86 return AID_INET; 87 } 88 89 /* bad attribute length */ 90 if (alen > ibuf_size(&buf)) 91 goto bad; 92 93 /* try to extract AFI/SAFI from the MP attributes */ 94 while (ibuf_size(&buf) > 0) { 95 if (ibuf_get_n8(&buf, &flags) == -1 || 96 ibuf_get_n8(&buf, &type) == -1) 97 goto bad; 98 if (flags & ATTR_EXTLEN) { 99 if (ibuf_get_n16(&buf, &len) == -1) 100 goto bad; 101 } else { 102 uint8_t tmp; 103 if (ibuf_get_n8(&buf, &tmp) == -1) 104 goto bad; 105 len = tmp; 106 } 107 if (len > ibuf_size(&buf)) 108 goto bad; 109 110 if (type == ATTR_MP_REACH_NLRI || 111 type == ATTR_MP_UNREACH_NLRI) { 112 if (ibuf_get_n16(&buf, &afi) == -1 || 113 ibuf_get_n8(&buf, &safi) == -1) 114 goto bad; 115 if (afi2aid(afi, safi, &aid) == -1) 116 goto bad; 117 return aid; 118 } 119 if (ibuf_skip(&buf, len) == -1) 120 goto bad; 121 } 122 123 bad: 124 return AID_UNSPEC; 125 } 126 127 static uint16_t 128 mrt_bgp_msg_subtype(struct mrt *mrt, struct ibuf *pkg, struct peer *peer, 129 enum msg_type msgtype, int in) 130 { 131 uint16_t subtype = BGP4MP_MESSAGE; 132 uint8_t aid, mask; 133 134 if (peer->capa.neg.as4byte) 135 subtype = BGP4MP_MESSAGE_AS4; 136 137 if (msgtype != MSG_UPDATE) 138 return subtype; 139 140 /* 141 * RFC8050 adjust types for add-path enabled sessions. 142 * It is necessary to extract the AID from UPDATES to decide 143 * if the add-path types are needed or not. The ADDPATH 144 * subtypes only matter for BGP UPDATES. 145 */ 146 147 mask = in ? CAPA_AP_RECV : CAPA_AP_SEND; 148 /* only guess if add-path could be active */ 149 if (peer->capa.neg.add_path[0] & mask) { 150 aid = mrt_update_msg_guess_aid(pkg); 151 if (aid != AID_UNSPEC && 152 (peer->capa.neg.add_path[aid] & mask)) { 153 if (peer->capa.neg.as4byte) 154 subtype = BGP4MP_MESSAGE_AS4_ADDPATH; 155 else 156 subtype = BGP4MP_MESSAGE_ADDPATH; 157 } 158 } 159 160 return subtype; 161 } 162 163 void 164 mrt_dump_bgp_msg(struct mrt *mrt, struct ibuf *pkg, struct peer *peer, 165 enum msg_type msgtype) 166 { 167 struct ibuf *buf; 168 int in = 0; 169 uint16_t subtype = BGP4MP_MESSAGE; 170 171 /* get the direction of the message to swap address and AS fields */ 172 if (mrt->type == MRT_ALL_IN || mrt->type == MRT_UPDATE_IN) 173 in = 1; 174 175 subtype = mrt_bgp_msg_subtype(mrt, pkg, peer, msgtype, in); 176 177 if (mrt_dump_hdr_se(&buf, peer, MSG_PROTOCOL_BGP4MP_ET, subtype, 178 ibuf_size(pkg), in) == -1) 179 goto fail; 180 181 if (ibuf_add_ibuf(buf, pkg) == -1) 182 goto fail; 183 184 ibuf_close(mrt->wbuf, buf); 185 return; 186 187 fail: 188 log_warn("%s: ibuf error", __func__); 189 ibuf_free(buf); 190 } 191 192 void 193 mrt_dump_state(struct mrt *mrt, uint16_t old_state, uint16_t new_state, 194 struct peer *peer) 195 { 196 struct ibuf *buf; 197 uint16_t subtype = BGP4MP_STATE_CHANGE; 198 199 if (peer->capa.neg.as4byte) 200 subtype = BGP4MP_STATE_CHANGE_AS4; 201 202 if (mrt_dump_hdr_se(&buf, peer, MSG_PROTOCOL_BGP4MP_ET, subtype, 203 2 * sizeof(short), 0) == -1) 204 goto fail; 205 206 if (ibuf_add_n16(buf, old_state) == -1) 207 goto fail; 208 if (ibuf_add_n16(buf, new_state) == -1) 209 goto fail; 210 211 ibuf_close(mrt->wbuf, buf); 212 return; 213 214 fail: 215 log_warn("%s: ibuf error", __func__); 216 ibuf_free(buf); 217 } 218 219 static int 220 mrt_attr_dump(struct ibuf *buf, struct rde_aspath *a, struct rde_community *c, 221 struct bgpd_addr *nexthop, int v2) 222 { 223 struct attr *oa; 224 u_char *pdata; 225 uint32_t tmp; 226 int neednewpath = 0; 227 uint16_t plen, afi; 228 uint8_t l, safi; 229 230 /* origin */ 231 if (attr_writebuf(buf, ATTR_WELL_KNOWN, ATTR_ORIGIN, 232 &a->origin, 1) == -1) 233 return (-1); 234 235 /* aspath */ 236 plen = aspath_length(a->aspath); 237 pdata = aspath_dump(a->aspath); 238 239 if (!v2) 240 pdata = aspath_deflate(pdata, &plen, &neednewpath); 241 if (attr_writebuf(buf, ATTR_WELL_KNOWN, ATTR_ASPATH, pdata, 242 plen) == -1) { 243 if (!v2) 244 free(pdata); 245 return (-1); 246 } 247 if (!v2) 248 free(pdata); 249 250 if (nexthop && nexthop->aid == AID_INET) { 251 /* nexthop, already network byte order */ 252 if (attr_writebuf(buf, ATTR_WELL_KNOWN, ATTR_NEXTHOP, 253 &nexthop->v4.s_addr, 4) == -1) 254 return (-1); 255 } 256 257 /* MED, non transitive */ 258 if (a->med != 0) { 259 tmp = htonl(a->med); 260 if (attr_writebuf(buf, ATTR_OPTIONAL, ATTR_MED, &tmp, 4) == -1) 261 return (-1); 262 } 263 264 /* local preference */ 265 tmp = htonl(a->lpref); 266 if (attr_writebuf(buf, ATTR_WELL_KNOWN, ATTR_LOCALPREF, &tmp, 4) == -1) 267 return (-1); 268 269 /* communities */ 270 if (community_writebuf(c, ATTR_COMMUNITIES, 0, buf) == -1 || 271 community_writebuf(c, ATTR_EXT_COMMUNITIES, 0, buf) == -1 || 272 community_writebuf(c, ATTR_LARGE_COMMUNITIES, 0, buf) == -1) 273 return (-1); 274 275 /* dump all other path attributes without modification */ 276 for (l = 0; l < a->others_len; l++) { 277 if ((oa = a->others[l]) == NULL) 278 break; 279 if (attr_writebuf(buf, oa->flags, oa->type, 280 oa->data, oa->len) == -1) 281 return (-1); 282 } 283 284 if (nexthop && nexthop->aid != AID_INET) { 285 struct ibuf *nhbuf; 286 287 if ((nhbuf = ibuf_dynamic(0, UCHAR_MAX)) == NULL) 288 return (-1); 289 if (!v2) { 290 if (aid2afi(nexthop->aid, &afi, &safi)) 291 goto fail; 292 if (ibuf_add_n16(nhbuf, afi) == -1) 293 goto fail; 294 if (ibuf_add_n8(nhbuf, safi) == -1) 295 goto fail; 296 } 297 switch (nexthop->aid) { 298 case AID_INET6: 299 if (ibuf_add_n8(nhbuf, sizeof(struct in6_addr)) == -1) 300 goto fail; 301 if (ibuf_add(nhbuf, &nexthop->v6, 302 sizeof(struct in6_addr)) == -1) 303 goto fail; 304 break; 305 case AID_VPN_IPv4: 306 if (ibuf_add_n8(nhbuf, sizeof(uint64_t) + 307 sizeof(struct in_addr)) == -1) 308 goto fail; 309 if (ibuf_add_n64(nhbuf, 0) == -1) /* set RD to 0 */ 310 goto fail; 311 if (ibuf_add(nhbuf, &nexthop->v4, 312 sizeof(nexthop->v4)) == -1) 313 goto fail; 314 break; 315 case AID_VPN_IPv6: 316 if (ibuf_add_n8(nhbuf, sizeof(uint64_t) + 317 sizeof(struct in6_addr)) == -1) 318 goto fail; 319 if (ibuf_add_n64(nhbuf, 0) == -1) /* set RD to 0 */ 320 goto fail; 321 if (ibuf_add(nhbuf, &nexthop->v6, 322 sizeof(nexthop->v6)) == -1) 323 goto fail; 324 break; 325 } 326 if (!v2) 327 if (ibuf_add_n8(nhbuf, 0) == -1) 328 goto fail; 329 if (attr_writebuf(buf, ATTR_OPTIONAL, ATTR_MP_REACH_NLRI, 330 ibuf_data(nhbuf), ibuf_size(nhbuf)) == -1) { 331 fail: 332 ibuf_free(nhbuf); 333 return (-1); 334 } 335 ibuf_free(nhbuf); 336 } 337 338 if (neednewpath) { 339 pdata = aspath_prepend(a->aspath, rde_local_as(), 0, &plen); 340 if (plen != 0) 341 if (attr_writebuf(buf, ATTR_OPTIONAL|ATTR_TRANSITIVE, 342 ATTR_AS4_PATH, pdata, plen) == -1) { 343 free(pdata); 344 return (-1); 345 } 346 free(pdata); 347 } 348 349 return (0); 350 } 351 352 static int 353 mrt_dump_entry_mp(struct mrt *mrt, struct prefix *p, uint16_t snum, 354 struct rde_peer *peer) 355 { 356 struct ibuf *buf, *hbuf = NULL, *h2buf = NULL; 357 struct nexthop *n; 358 struct bgpd_addr nexthop, *nh; 359 uint16_t len; 360 uint8_t aid; 361 362 if ((buf = ibuf_dynamic(0, MAX_PKTSIZE)) == NULL) { 363 log_warn("mrt_dump_entry_mp: ibuf_dynamic"); 364 return (-1); 365 } 366 367 if (mrt_attr_dump(buf, prefix_aspath(p), prefix_communities(p), 368 NULL, 0) == -1) 369 goto fail; 370 len = ibuf_size(buf); 371 372 if ((h2buf = ibuf_dynamic(MRT_BGP4MP_IPv4_HEADER_SIZE + 373 MRT_BGP4MP_IPv4_ENTRY_SIZE, MRT_BGP4MP_IPv6_HEADER_SIZE + 374 MRT_BGP4MP_IPv6_ENTRY_SIZE + MRT_BGP4MP_MAX_PREFIXLEN)) == NULL) 375 goto fail; 376 377 if (ibuf_add_n16(h2buf, peer->conf.local_short_as) == -1) 378 goto fail; 379 if (ibuf_add_n16(h2buf, peer->short_as) == -1) 380 goto fail; 381 if (ibuf_add_n16(h2buf, /* ifindex */ 0) == -1) 382 goto fail; 383 384 /* XXX is this for peer self? */ 385 aid = peer->remote_addr.aid == AID_UNSPEC ? p->pt->aid : 386 peer->remote_addr.aid; 387 switch (aid) { 388 case AID_INET: 389 case AID_VPN_IPv4: 390 if (ibuf_add_n16(h2buf, AFI_IPv4) == -1) 391 goto fail; 392 if (ibuf_add(h2buf, &peer->local_v4_addr.v4, 393 sizeof(peer->local_v4_addr.v4)) == -1 || 394 ibuf_add(h2buf, &peer->remote_addr.v4, 395 sizeof(peer->remote_addr.v4)) == -1) 396 goto fail; 397 break; 398 case AID_INET6: 399 case AID_VPN_IPv6: 400 if (ibuf_add_n16(h2buf, AFI_IPv6) == -1) 401 goto fail; 402 if (ibuf_add(h2buf, &peer->local_v6_addr.v6, 403 sizeof(peer->local_v6_addr.v6)) == -1 || 404 ibuf_add(h2buf, &peer->remote_addr.v6, 405 sizeof(peer->remote_addr.v6)) == -1) 406 goto fail; 407 break; 408 default: 409 log_warnx("king bula found new AF %d in %s", aid, __func__); 410 goto fail; 411 } 412 413 if (ibuf_add_n16(h2buf, 0) == -1) /* view */ 414 goto fail; 415 if (ibuf_add_n16(h2buf, 1) == -1) /* status */ 416 goto fail; 417 /* originated timestamp */ 418 if (ibuf_add_n32(h2buf, time(NULL) - (getmonotime() - 419 p->lastchange)) == -1) 420 goto fail; 421 422 n = prefix_nexthop(p); 423 if (n == NULL) { 424 memset(&nexthop, 0, sizeof(struct bgpd_addr)); 425 nexthop.aid = p->pt->aid; 426 nh = &nexthop; 427 } else 428 nh = &n->exit_nexthop; 429 430 switch (p->pt->aid) { 431 case AID_INET: 432 if (ibuf_add_n16(h2buf, AFI_IPv4) == -1) /* afi */ 433 goto fail; 434 if (ibuf_add_n8(h2buf, SAFI_UNICAST) == -1) /* safi */ 435 goto fail; 436 if (ibuf_add_n8(h2buf, 4) == -1) /* nhlen */ 437 goto fail; 438 if (ibuf_add(h2buf, &nh->v4, sizeof(nh->v4)) == -1) 439 goto fail; 440 break; 441 case AID_INET6: 442 if (ibuf_add_n16(h2buf, AFI_IPv6) == -1) /* afi */ 443 goto fail; 444 if (ibuf_add_n8(h2buf, SAFI_UNICAST) == -1) /* safi */ 445 goto fail; 446 if (ibuf_add_n8(h2buf, 16) == -1) /* nhlen */ 447 goto fail; 448 if (ibuf_add(h2buf, &nh->v6, sizeof(nh->v6)) == -1) 449 goto fail; 450 break; 451 case AID_VPN_IPv4: 452 if (ibuf_add_n16(h2buf, AFI_IPv4) == -1) /* afi */ 453 goto fail; 454 if (ibuf_add_n8(h2buf, SAFI_MPLSVPN) == -1) /* safi */ 455 goto fail; 456 if (ibuf_add_n8(h2buf, sizeof(uint64_t) + 457 sizeof(struct in_addr)) == -1) 458 goto fail; 459 if (ibuf_add_n64(h2buf, 0) == -1) /* set RD to 0 */ 460 goto fail; 461 if (ibuf_add(h2buf, &nh->v4, sizeof(nh->v4)) == -1) 462 goto fail; 463 break; 464 case AID_VPN_IPv6: 465 if (ibuf_add_n16(h2buf, AFI_IPv6) == -1) /* afi */ 466 goto fail; 467 if (ibuf_add_n8(h2buf, SAFI_MPLSVPN) == -1) /* safi */ 468 goto fail; 469 if (ibuf_add_n8(h2buf, sizeof(uint64_t) + 470 sizeof(struct in6_addr)) == -1) 471 goto fail; 472 if (ibuf_add_n64(h2buf, 0) == -1) /* set RD to 0 */ 473 goto fail; 474 if (ibuf_add(h2buf, &nh->v6, sizeof(nh->v6)) == -1) 475 goto fail; 476 break; 477 case AID_FLOWSPECv4: 478 case AID_FLOWSPECv6: 479 if (p->pt->aid == AID_FLOWSPECv4) { 480 if (ibuf_add_n16(h2buf, AFI_IPv4) == -1) /* afi */ 481 goto fail; 482 } else { 483 if (ibuf_add_n16(h2buf, AFI_IPv6) == -1) /* afi */ 484 goto fail; 485 } 486 if (ibuf_add_n8(h2buf, SAFI_FLOWSPEC) == -1) /* safi */ 487 goto fail; 488 if (ibuf_add_n8(h2buf, 0) == -1) /* nhlen */ 489 goto fail; 490 break; 491 default: 492 log_warnx("king bula found new AF in %s", __func__); 493 goto fail; 494 } 495 496 if (pt_writebuf(h2buf, p->pt, 0, 0, 0) == -1) 497 goto fail; 498 499 if (ibuf_add_n16(h2buf, len) == -1) 500 goto fail; 501 len += ibuf_size(h2buf); 502 503 if (mrt_dump_hdr_rde(&hbuf, MSG_PROTOCOL_BGP4MP, BGP4MP_ENTRY, 504 len) == -1) 505 goto fail; 506 507 ibuf_close(mrt->wbuf, hbuf); 508 ibuf_close(mrt->wbuf, h2buf); 509 ibuf_close(mrt->wbuf, buf); 510 511 return (len + MRT_HEADER_SIZE); 512 513 fail: 514 log_warn("%s: ibuf error", __func__); 515 ibuf_free(hbuf); 516 ibuf_free(h2buf); 517 ibuf_free(buf); 518 return (-1); 519 } 520 521 static int 522 mrt_dump_entry(struct mrt *mrt, struct prefix *p, uint16_t snum, 523 struct rde_peer *peer) 524 { 525 struct ibuf *buf, *hbuf = NULL; 526 struct nexthop *nexthop; 527 struct bgpd_addr addr, *nh; 528 size_t len; 529 uint16_t subtype; 530 uint8_t dummy; 531 532 if (p->pt->aid != peer->remote_addr.aid && 533 p->pt->aid != AID_INET && p->pt->aid != AID_INET6) 534 /* only able to dump pure IPv4/IPv6 */ 535 return (0); 536 537 if ((buf = ibuf_dynamic(0, MAX_PKTSIZE)) == NULL) { 538 log_warn("mrt_dump_entry: ibuf_dynamic"); 539 return (-1); 540 } 541 542 nexthop = prefix_nexthop(p); 543 if (nexthop == NULL) { 544 memset(&addr, 0, sizeof(struct bgpd_addr)); 545 addr.aid = p->pt->aid; 546 nh = &addr; 547 } else 548 nh = &nexthop->exit_nexthop; 549 if (mrt_attr_dump(buf, prefix_aspath(p), prefix_communities(p), 550 nh, 0) == -1) 551 goto fail; 552 553 len = ibuf_size(buf); 554 aid2afi(p->pt->aid, &subtype, &dummy); 555 if (mrt_dump_hdr_rde(&hbuf, MSG_TABLE_DUMP, subtype, len) == -1) 556 goto fail; 557 558 if (ibuf_add_n16(hbuf, 0) == -1) 559 goto fail; 560 if (ibuf_add_n16(hbuf, snum) == -1) 561 goto fail; 562 563 pt_getaddr(p->pt, &addr); 564 switch (p->pt->aid) { 565 case AID_INET: 566 if (ibuf_add(hbuf, &addr.v4, sizeof(addr.v4)) == -1) 567 goto fail; 568 break; 569 case AID_INET6: 570 if (ibuf_add(hbuf, &addr.v6, sizeof(addr.v6)) == -1) 571 goto fail; 572 break; 573 } 574 if (ibuf_add_n8(hbuf, p->pt->prefixlen) == -1) 575 goto fail; 576 577 if (ibuf_add_n8(hbuf, 1) == -1) /* state */ 578 goto fail; 579 /* originated timestamp */ 580 if (ibuf_add_n32(hbuf, time(NULL) - (getmonotime() - 581 p->lastchange)) == -1) 582 goto fail; 583 switch (p->pt->aid) { 584 case AID_INET: 585 if (ibuf_add(hbuf, &peer->remote_addr.v4, 586 sizeof(peer->remote_addr.v4)) == -1) 587 goto fail; 588 break; 589 case AID_INET6: 590 if (ibuf_add(hbuf, &peer->remote_addr.v6, 591 sizeof(peer->remote_addr.v6)) == -1) 592 goto fail; 593 break; 594 } 595 if (ibuf_add_n16(hbuf, peer->short_as) == -1) 596 goto fail; 597 if (ibuf_add_n16(hbuf, len) == -1) 598 goto fail; 599 600 ibuf_close(mrt->wbuf, hbuf); 601 ibuf_close(mrt->wbuf, buf); 602 603 return (len + MRT_HEADER_SIZE); 604 605 fail: 606 log_warn("%s: ibuf error", __func__); 607 ibuf_free(hbuf); 608 ibuf_free(buf); 609 return (-1); 610 } 611 612 static int 613 mrt_dump_entry_v2_rib(struct rib_entry *re, struct ibuf **nb, struct ibuf **apb, 614 uint16_t *np, uint16_t *app) 615 { 616 struct bgpd_addr addr; 617 struct ibuf *buf = NULL, **bp; 618 struct ibuf *tbuf = NULL; 619 struct prefix *p; 620 int addpath; 621 622 *np = 0; 623 *app = 0; 624 625 TAILQ_FOREACH(p, &re->prefix_h, entry.list.rib) { 626 struct nexthop *nexthop; 627 struct bgpd_addr *nh; 628 629 addpath = peer_has_add_path(prefix_peer(p), re->prefix->aid, 630 CAPA_AP_RECV); 631 632 if (addpath) { 633 bp = apb; 634 *app += 1; 635 } else { 636 bp = nb; 637 *np += 1; 638 } 639 if ((buf = *bp) == NULL) { 640 if ((buf = ibuf_dynamic(0, UINT_MAX)) == NULL) 641 goto fail; 642 *bp = buf; 643 } 644 645 nexthop = prefix_nexthop(p); 646 if (nexthop == NULL) { 647 memset(&addr, 0, sizeof(struct bgpd_addr)); 648 addr.aid = re->prefix->aid; 649 nh = &addr; 650 } else 651 nh = &nexthop->exit_nexthop; 652 653 if (ibuf_add_n16(buf, prefix_peer(p)->mrt_idx) == -1) 654 goto fail; 655 /* originated timestamp */ 656 if (ibuf_add_n32(buf, time(NULL) - (getmonotime() - 657 p->lastchange)) == -1) 658 goto fail; 659 660 /* RFC8050: path-id if add-path is used */ 661 if (addpath) 662 if (ibuf_add_n32(buf, p->path_id) == -1) 663 goto fail; 664 665 if ((tbuf = ibuf_dynamic(0, MAX_PKTSIZE)) == NULL) 666 goto fail; 667 if (mrt_attr_dump(tbuf, prefix_aspath(p), prefix_communities(p), 668 nh, 1) == -1) 669 goto fail; 670 if (ibuf_add_n16(buf, ibuf_size(tbuf)) == -1) 671 goto fail; 672 if (ibuf_add_ibuf(buf, tbuf) == -1) 673 goto fail; 674 ibuf_free(tbuf); 675 tbuf = NULL; 676 } 677 678 return 0; 679 680 fail: 681 ibuf_free(tbuf); 682 return -1; 683 } 684 685 static int 686 mrt_dump_entry_v2(struct mrt *mrt, struct rib_entry *re, uint32_t snum) 687 { 688 struct ibuf *hbuf = NULL, *nbuf = NULL, *apbuf = NULL, *pbuf; 689 size_t hlen, len; 690 uint16_t subtype, apsubtype, nump, apnump, afi; 691 uint8_t safi; 692 693 if ((pbuf = ibuf_dynamic(0, UINT_MAX)) == NULL) { 694 log_warn("%s: ibuf_dynamic", __func__); 695 return -1; 696 } 697 698 switch (re->prefix->aid) { 699 case AID_INET: 700 subtype = MRT_DUMP_V2_RIB_IPV4_UNICAST; 701 apsubtype = MRT_DUMP_V2_RIB_IPV4_UNICAST_ADDPATH; 702 break; 703 case AID_INET6: 704 subtype = MRT_DUMP_V2_RIB_IPV6_UNICAST; 705 apsubtype = MRT_DUMP_V2_RIB_IPV6_UNICAST_ADDPATH; 706 break; 707 default: 708 /* 709 * XXX The RFC defined the format for this type differently 710 * and it is prohibitly expensive to implement that format. 711 * Instead do what gobgp does and encode it like the other 712 * types. 713 */ 714 subtype = MRT_DUMP_V2_RIB_GENERIC; 715 apsubtype = MRT_DUMP_V2_RIB_GENERIC_ADDPATH; 716 aid2afi(re->prefix->aid, &afi, &safi); 717 718 /* first add 3-bytes AFI/SAFI */ 719 if (ibuf_add_n16(pbuf, afi) == -1) 720 goto fail; 721 if (ibuf_add_n8(pbuf, safi) == -1) 722 goto fail; 723 break; 724 } 725 726 if (pt_writebuf(pbuf, re->prefix, 0, 0, 0) == -1) 727 goto fail; 728 729 hlen = sizeof(snum) + sizeof(nump) + ibuf_size(pbuf); 730 731 if (mrt_dump_entry_v2_rib(re, &nbuf, &apbuf, &nump, &apnump)) 732 goto fail; 733 734 if (nump > 0) { 735 len = ibuf_size(nbuf) + hlen; 736 if (mrt_dump_hdr_rde(&hbuf, MSG_TABLE_DUMP_V2, subtype, 737 len) == -1) 738 goto fail; 739 740 if (ibuf_add_n32(hbuf, snum) == -1) 741 goto fail; 742 if (ibuf_add_ibuf(hbuf, pbuf) == -1) 743 goto fail; 744 if (ibuf_add_n16(hbuf, nump) == -1) 745 goto fail; 746 747 ibuf_close(mrt->wbuf, hbuf); 748 ibuf_close(mrt->wbuf, nbuf); 749 hbuf = NULL; 750 nbuf = NULL; 751 } 752 753 if (apnump > 0) { 754 len = ibuf_size(apbuf) + hlen; 755 if (mrt_dump_hdr_rde(&hbuf, MSG_TABLE_DUMP_V2, apsubtype, 756 len) == -1) 757 goto fail; 758 759 if (ibuf_add_n32(hbuf, snum) == -1) 760 goto fail; 761 if (ibuf_add_ibuf(hbuf, pbuf) == -1) 762 goto fail; 763 if (ibuf_add_n16(hbuf, apnump) == -1) 764 goto fail; 765 766 ibuf_close(mrt->wbuf, hbuf); 767 ibuf_close(mrt->wbuf, apbuf); 768 hbuf = NULL; 769 apbuf = NULL; 770 } 771 772 ibuf_free(pbuf); 773 return (0); 774 fail: 775 log_warn("%s: ibuf error", __func__); 776 ibuf_free(apbuf); 777 ibuf_free(nbuf); 778 ibuf_free(hbuf); 779 ibuf_free(pbuf); 780 return (-1); 781 } 782 783 struct cb_arg { 784 struct ibuf *buf; 785 int nump; 786 }; 787 788 static void 789 mrt_dump_v2_hdr_peer(struct rde_peer *peer, void *arg) 790 { 791 struct cb_arg *a = arg; 792 793 if (a->nump == -1) 794 return; 795 peer->mrt_idx = a->nump; 796 if (mrt_dump_peer(a->buf, peer) == -1) { 797 a->nump = -1; 798 return; 799 } 800 a->nump++; 801 } 802 803 int 804 mrt_dump_v2_hdr(struct mrt *mrt, struct bgpd_config *conf) 805 { 806 struct ibuf *buf, *hbuf = NULL; 807 size_t len, off; 808 uint16_t nlen, nump; 809 struct cb_arg arg; 810 811 if ((buf = ibuf_dynamic(0, UINT_MAX)) == NULL) { 812 log_warn("%s: ibuf_dynamic", __func__); 813 return (-1); 814 } 815 816 if (ibuf_add_n32(buf, conf->bgpid) == -1) 817 goto fail; 818 nlen = strlen(mrt->rib); 819 if (nlen > 0) 820 nlen += 1; 821 if (ibuf_add_n16(buf, nlen) == -1) 822 goto fail; 823 if (ibuf_add(buf, mrt->rib, nlen) == -1) 824 goto fail; 825 826 off = ibuf_size(buf); 827 if (ibuf_add_zero(buf, sizeof(nump)) == -1) 828 goto fail; 829 arg.nump = 0; 830 arg.buf = buf; 831 peer_foreach(mrt_dump_v2_hdr_peer, &arg); 832 if (arg.nump == -1) 833 goto fail; 834 835 if (ibuf_set_n16(buf, off, arg.nump) == -1) 836 goto fail; 837 838 len = ibuf_size(buf); 839 if (mrt_dump_hdr_rde(&hbuf, MSG_TABLE_DUMP_V2, 840 MRT_DUMP_V2_PEER_INDEX_TABLE, len) == -1) 841 goto fail; 842 843 ibuf_close(mrt->wbuf, hbuf); 844 ibuf_close(mrt->wbuf, buf); 845 846 return (0); 847 fail: 848 log_warn("%s: ibuf error", __func__); 849 ibuf_free(hbuf); 850 ibuf_free(buf); 851 return (-1); 852 } 853 854 static int 855 mrt_dump_peer(struct ibuf *buf, struct rde_peer *peer) 856 { 857 uint8_t type = 0; 858 859 if (peer->capa.as4byte) 860 type |= MRT_DUMP_V2_PEER_BIT_A; 861 if (peer->remote_addr.aid == AID_INET6) 862 type |= MRT_DUMP_V2_PEER_BIT_I; 863 864 if (ibuf_add_n8(buf, type) == -1) 865 goto fail; 866 if (ibuf_add_n32(buf, peer->remote_bgpid) == -1) 867 goto fail; 868 869 switch (peer->remote_addr.aid) { 870 case AID_INET: 871 if (ibuf_add(buf, &peer->remote_addr.v4, 872 sizeof(peer->remote_addr.v4)) == -1) 873 goto fail; 874 break; 875 case AID_INET6: 876 if (ibuf_add(buf, &peer->remote_addr.v6, 877 sizeof(peer->remote_addr.v6)) == -1) 878 goto fail; 879 break; 880 case AID_UNSPEC: /* XXX special handling for peerself? */ 881 if (ibuf_add_n32(buf, 0) == -1) 882 goto fail; 883 break; 884 default: 885 log_warnx("king bula found new AF in %s", __func__); 886 goto fail; 887 } 888 889 if (peer->capa.as4byte) { 890 if (ibuf_add_n32(buf, peer->conf.remote_as) == -1) 891 goto fail; 892 } else { 893 if (ibuf_add_n16(buf, peer->short_as) == -1) 894 goto fail; 895 } 896 return (0); 897 fail: 898 log_warn("%s: ibuf error", __func__); 899 return (-1); 900 } 901 902 void 903 mrt_dump_upcall(struct rib_entry *re, void *ptr) 904 { 905 struct mrt *mrtbuf = ptr; 906 struct prefix *p; 907 908 if (mrtbuf->type == MRT_TABLE_DUMP_V2) { 909 mrt_dump_entry_v2(mrtbuf, re, mrtbuf->seqnum++); 910 return; 911 } 912 913 /* 914 * dump all prefixes even the inactive ones. That is the way zebra 915 * dumps the table so we do the same. If only the active route should 916 * be dumped p should be set to p = pt->active. 917 */ 918 TAILQ_FOREACH(p, &re->prefix_h, entry.list.rib) { 919 if (mrtbuf->type == MRT_TABLE_DUMP) 920 mrt_dump_entry(mrtbuf, p, mrtbuf->seqnum++, 921 prefix_peer(p)); 922 else 923 mrt_dump_entry_mp(mrtbuf, p, mrtbuf->seqnum++, 924 prefix_peer(p)); 925 } 926 } 927 928 void 929 mrt_done(struct mrt *mrtbuf) 930 { 931 mrtbuf->state = MRT_STATE_REMOVE; 932 } 933 934 static int 935 mrt_dump_hdr_se(struct ibuf ** bp, struct peer *peer, uint16_t type, 936 uint16_t subtype, uint32_t len, int swap) 937 { 938 struct timespec time; 939 940 if ((*bp = ibuf_dynamic(MRT_ET_HEADER_SIZE, MRT_ET_HEADER_SIZE + 941 MRT_BGP4MP_AS4_IPv6_HEADER_SIZE + len)) == NULL) 942 return (-1); 943 944 clock_gettime(CLOCK_REALTIME, &time); 945 946 if (ibuf_add_n32(*bp, time.tv_sec) == -1) 947 goto fail; 948 if (ibuf_add_n16(*bp, type) == -1) 949 goto fail; 950 if (ibuf_add_n16(*bp, subtype) == -1) 951 goto fail; 952 953 switch (peer->local.aid) { 954 case AID_INET: 955 if (subtype == BGP4MP_STATE_CHANGE_AS4 || 956 subtype == BGP4MP_MESSAGE_AS4 || 957 subtype == BGP4MP_MESSAGE_AS4_ADDPATH) 958 len += MRT_BGP4MP_ET_AS4_IPv4_HEADER_SIZE; 959 else 960 len += MRT_BGP4MP_ET_IPv4_HEADER_SIZE; 961 break; 962 case AID_INET6: 963 if (subtype == BGP4MP_STATE_CHANGE_AS4 || 964 subtype == BGP4MP_MESSAGE_AS4 || 965 subtype == BGP4MP_MESSAGE_AS4_ADDPATH) 966 len += MRT_BGP4MP_ET_AS4_IPv6_HEADER_SIZE; 967 else 968 len += MRT_BGP4MP_ET_IPv6_HEADER_SIZE; 969 break; 970 case 0: 971 goto fail; 972 default: 973 log_warnx("king bula found new AF in %s", __func__); 974 goto fail; 975 } 976 977 if (ibuf_add_n32(*bp, len) == -1) 978 goto fail; 979 /* millisecond field use by the _ET format */ 980 if (ibuf_add_n32(*bp, time.tv_nsec / 1000) == -1) 981 goto fail; 982 983 if (subtype == BGP4MP_STATE_CHANGE_AS4 || 984 subtype == BGP4MP_MESSAGE_AS4 || 985 subtype == BGP4MP_MESSAGE_AS4_ADDPATH) { 986 if (!swap) 987 if (ibuf_add_n32(*bp, peer->conf.local_as) == -1) 988 goto fail; 989 if (ibuf_add_n32(*bp, peer->conf.remote_as) == -1) 990 goto fail; 991 if (swap) 992 if (ibuf_add_n32(*bp, peer->conf.local_as) == -1) 993 goto fail; 994 } else { 995 if (!swap) 996 if (ibuf_add_n16(*bp, peer->conf.local_short_as) == -1) 997 goto fail; 998 if (ibuf_add_n16(*bp, peer->short_as) == -1) 999 goto fail; 1000 if (swap) 1001 if (ibuf_add_n16(*bp, peer->conf.local_short_as) == -1) 1002 goto fail; 1003 } 1004 1005 if (ibuf_add_n16(*bp, /* ifindex */ 0) == -1) 1006 goto fail; 1007 1008 switch (peer->local.aid) { 1009 case AID_INET: 1010 if (ibuf_add_n16(*bp, AFI_IPv4) == -1) 1011 goto fail; 1012 if (!swap) 1013 if (ibuf_add(*bp, &peer->local.v4, 1014 sizeof(peer->local.v4)) == -1) 1015 goto fail; 1016 if (ibuf_add(*bp, &peer->remote.v4, 1017 sizeof(peer->remote.v4)) == -1) 1018 goto fail; 1019 if (swap) 1020 if (ibuf_add(*bp, &peer->local.v4, 1021 sizeof(peer->local.v4)) == -1) 1022 goto fail; 1023 break; 1024 case AID_INET6: 1025 if (ibuf_add_n16(*bp, AFI_IPv6) == -1) 1026 goto fail; 1027 if (!swap) 1028 if (ibuf_add(*bp, &peer->local.v6, 1029 sizeof(peer->local.v6)) == -1) 1030 goto fail; 1031 if (ibuf_add(*bp, &peer->remote.v6, 1032 sizeof(peer->remote.v6)) == -1) 1033 goto fail; 1034 if (swap) 1035 if (ibuf_add(*bp, &peer->local.v6, 1036 sizeof(peer->local.v6)) == -1) 1037 goto fail; 1038 break; 1039 } 1040 1041 return (0); 1042 1043 fail: 1044 ibuf_free(*bp); 1045 *bp = NULL; 1046 return (-1); 1047 } 1048 1049 int 1050 mrt_dump_hdr_rde(struct ibuf **bp, uint16_t type, uint16_t subtype, 1051 uint32_t len) 1052 { 1053 struct timespec time; 1054 1055 if ((*bp = ibuf_dynamic(MRT_HEADER_SIZE, MRT_HEADER_SIZE + 1056 MRT_BGP4MP_AS4_IPv6_HEADER_SIZE + MRT_BGP4MP_IPv6_ENTRY_SIZE)) == 1057 NULL) 1058 return (-1); 1059 1060 clock_gettime(CLOCK_REALTIME, &time); 1061 1062 if (ibuf_add_n32(*bp, time.tv_sec) == -1) 1063 goto fail; 1064 if (ibuf_add_n16(*bp, type) == -1) 1065 goto fail; 1066 if (ibuf_add_n16(*bp, subtype) == -1) 1067 goto fail; 1068 1069 switch (type) { 1070 case MSG_TABLE_DUMP: 1071 switch (subtype) { 1072 case AFI_IPv4: 1073 len += MRT_DUMP_HEADER_SIZE; 1074 break; 1075 case AFI_IPv6: 1076 len += MRT_DUMP_HEADER_SIZE_V6; 1077 break; 1078 } 1079 if (ibuf_add_n32(*bp, len) == -1) 1080 goto fail; 1081 break; 1082 case MSG_PROTOCOL_BGP4MP: 1083 case MSG_TABLE_DUMP_V2: 1084 if (ibuf_add_n32(*bp, len) == -1) 1085 goto fail; 1086 break; 1087 default: 1088 log_warnx("mrt_dump_hdr_rde: unsupported type"); 1089 goto fail; 1090 } 1091 return (0); 1092 1093 fail: 1094 ibuf_free(*bp); 1095 *bp = NULL; 1096 return (-1); 1097 } 1098 1099 void 1100 mrt_write(struct mrt *mrt) 1101 { 1102 if (ibuf_write(mrt->fd, mrt->wbuf) == -1) { 1103 log_warn("mrt dump aborted, mrt_write"); 1104 mrt_clean(mrt); 1105 mrt_done(mrt); 1106 } 1107 } 1108 1109 void 1110 mrt_clean(struct mrt *mrt) 1111 { 1112 close(mrt->fd); 1113 msgbuf_free(mrt->wbuf); 1114 mrt->wbuf = NULL; 1115 } 1116 1117 static struct imsgbuf *mrt_imsgbuf[2]; 1118 1119 void 1120 mrt_init(struct imsgbuf *rde, struct imsgbuf *se) 1121 { 1122 mrt_imsgbuf[RDEIDX] = rde; 1123 mrt_imsgbuf[SEIDX] = se; 1124 } 1125 1126 int 1127 mrt_open(struct mrt *mrt, time_t now) 1128 { 1129 enum imsg_type type; 1130 int fd; 1131 1132 if (strftime(MRT2MC(mrt)->file, sizeof(MRT2MC(mrt)->file), 1133 MRT2MC(mrt)->name, localtime(&now)) == 0) { 1134 log_warnx("mrt_open: strftime conversion failed"); 1135 return (-1); 1136 } 1137 1138 fd = open(MRT2MC(mrt)->file, 1139 O_WRONLY|O_NONBLOCK|O_CREAT|O_TRUNC|O_CLOEXEC, 0644); 1140 if (fd == -1) { 1141 log_warn("mrt_open %s", MRT2MC(mrt)->file); 1142 return (1); 1143 } 1144 1145 if (mrt->state == MRT_STATE_OPEN) 1146 type = IMSG_MRT_OPEN; 1147 else 1148 type = IMSG_MRT_REOPEN; 1149 1150 if (imsg_compose(mrt_imsgbuf[TYPE2IDX(mrt->type)], type, 0, 0, fd, 1151 mrt, sizeof(struct mrt)) == -1) 1152 log_warn("mrt_open"); 1153 1154 return (1); 1155 } 1156 1157 time_t 1158 mrt_timeout(struct mrt_head *mrt) 1159 { 1160 struct mrt *m; 1161 time_t now; 1162 time_t timeout = -1; 1163 1164 now = time(NULL); 1165 LIST_FOREACH(m, mrt, entry) { 1166 if (m->state == MRT_STATE_RUNNING && 1167 MRT2MC(m)->ReopenTimerInterval != 0) { 1168 if (MRT2MC(m)->ReopenTimer <= now) { 1169 mrt_open(m, now); 1170 MRT2MC(m)->ReopenTimer = 1171 now + MRT2MC(m)->ReopenTimerInterval; 1172 } 1173 if (timeout == -1 || 1174 MRT2MC(m)->ReopenTimer - now < timeout) 1175 timeout = MRT2MC(m)->ReopenTimer - now; 1176 } 1177 } 1178 return (timeout); 1179 } 1180 1181 void 1182 mrt_reconfigure(struct mrt_head *mrt) 1183 { 1184 struct mrt *m, *xm; 1185 time_t now; 1186 1187 now = time(NULL); 1188 for (m = LIST_FIRST(mrt); m != NULL; m = xm) { 1189 xm = LIST_NEXT(m, entry); 1190 if (m->state == MRT_STATE_OPEN || 1191 m->state == MRT_STATE_REOPEN) { 1192 if (mrt_open(m, now) == -1) 1193 continue; 1194 if (MRT2MC(m)->ReopenTimerInterval != 0) 1195 MRT2MC(m)->ReopenTimer = 1196 now + MRT2MC(m)->ReopenTimerInterval; 1197 m->state = MRT_STATE_RUNNING; 1198 } 1199 if (m->state == MRT_STATE_REMOVE) { 1200 if (imsg_compose(mrt_imsgbuf[TYPE2IDX(m->type)], 1201 IMSG_MRT_CLOSE, 0, 0, -1, m, sizeof(struct mrt)) == 1202 -1) 1203 log_warn("mrt_reconfigure"); 1204 LIST_REMOVE(m, entry); 1205 free(m); 1206 continue; 1207 } 1208 } 1209 } 1210 1211 void 1212 mrt_handler(struct mrt_head *mrt) 1213 { 1214 struct mrt *m; 1215 time_t now; 1216 1217 now = time(NULL); 1218 LIST_FOREACH(m, mrt, entry) { 1219 if (m->state == MRT_STATE_RUNNING && 1220 (MRT2MC(m)->ReopenTimerInterval != 0 || 1221 m->type == MRT_TABLE_DUMP || 1222 m->type == MRT_TABLE_DUMP_MP || 1223 m->type == MRT_TABLE_DUMP_V2)) { 1224 if (mrt_open(m, now) == -1) 1225 continue; 1226 MRT2MC(m)->ReopenTimer = 1227 now + MRT2MC(m)->ReopenTimerInterval; 1228 } 1229 } 1230 } 1231 1232 struct mrt * 1233 mrt_get(struct mrt_head *c, struct mrt *m) 1234 { 1235 struct mrt *t; 1236 1237 LIST_FOREACH(t, c, entry) { 1238 if (t->type != m->type) 1239 continue; 1240 if (strcmp(t->rib, m->rib)) 1241 continue; 1242 if (t->peer_id == m->peer_id && 1243 t->group_id == m->group_id) 1244 return (t); 1245 } 1246 return (NULL); 1247 } 1248 1249 void 1250 mrt_mergeconfig(struct mrt_head *xconf, struct mrt_head *nconf) 1251 { 1252 struct mrt *m, *xm; 1253 1254 /* both lists here are actually struct mrt_conifg nodes */ 1255 LIST_FOREACH(m, nconf, entry) { 1256 if ((xm = mrt_get(xconf, m)) == NULL) { 1257 /* NEW */ 1258 if ((xm = malloc(sizeof(struct mrt_config))) == NULL) 1259 fatal("mrt_mergeconfig"); 1260 memcpy(xm, m, sizeof(struct mrt_config)); 1261 xm->state = MRT_STATE_OPEN; 1262 LIST_INSERT_HEAD(xconf, xm, entry); 1263 } else { 1264 /* MERGE */ 1265 if (strlcpy(MRT2MC(xm)->name, MRT2MC(m)->name, 1266 sizeof(MRT2MC(xm)->name)) >= 1267 sizeof(MRT2MC(xm)->name)) 1268 fatalx("mrt_mergeconfig: strlcpy"); 1269 MRT2MC(xm)->ReopenTimerInterval = 1270 MRT2MC(m)->ReopenTimerInterval; 1271 xm->state = MRT_STATE_REOPEN; 1272 } 1273 } 1274 1275 LIST_FOREACH(xm, xconf, entry) 1276 if (mrt_get(nconf, xm) == NULL) 1277 /* REMOVE */ 1278 xm->state = MRT_STATE_REMOVE; 1279 1280 /* free config */ 1281 while ((m = LIST_FIRST(nconf)) != NULL) { 1282 LIST_REMOVE(m, entry); 1283 free(m); 1284 } 1285 } 1286