1 /* $OpenBSD: rde_update.c,v 1.123 2020/01/24 05:44:05 claudio Exp $ */ 2 3 /* 4 * Copyright (c) 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 #include <sys/types.h> 19 #include <sys/queue.h> 20 #include <sys/tree.h> 21 22 #include <limits.h> 23 #include <stdlib.h> 24 #include <string.h> 25 #include <siphash.h> 26 27 #include "bgpd.h" 28 #include "rde.h" 29 #include "log.h" 30 31 static struct community comm_no_advertise = { 32 .flags = COMMUNITY_TYPE_BASIC, 33 .data1 = COMMUNITY_WELLKNOWN, 34 .data2 = COMMUNITY_NO_ADVERTISE 35 }; 36 static struct community comm_no_export = { 37 .flags = COMMUNITY_TYPE_BASIC, 38 .data1 = COMMUNITY_WELLKNOWN, 39 .data2 = COMMUNITY_NO_EXPORT 40 }; 41 static struct community comm_no_expsubconfed = { 42 .flags = COMMUNITY_TYPE_BASIC, 43 .data1 = COMMUNITY_WELLKNOWN, 44 .data2 = COMMUNITY_NO_EXPSUBCONFED 45 }; 46 47 static int 48 up_test_update(struct rde_peer *peer, struct prefix *p) 49 { 50 struct bgpd_addr addr; 51 struct rde_aspath *asp; 52 struct rde_community *comm; 53 struct rde_peer *prefp; 54 struct attr *attr; 55 56 if (p == NULL) 57 /* no prefix available */ 58 return (0); 59 60 prefp = prefix_peer(p); 61 asp = prefix_aspath(p); 62 comm = prefix_communities(p); 63 64 if (peer == prefp) 65 /* Do not send routes back to sender */ 66 return (0); 67 68 if (asp == NULL || asp->flags & F_ATTR_PARSE_ERR) 69 fatalx("try to send out a botched path"); 70 if (asp->flags & F_ATTR_LOOP) 71 fatalx("try to send out a looped path"); 72 73 pt_getaddr(p->pt, &addr); 74 if (peer->capa.mp[addr.aid] == 0) 75 return (-1); 76 77 if (!prefp->conf.ebgp && !peer->conf.ebgp) { 78 /* 79 * route reflector redistribution rules: 80 * 1. if announce is set -> announce 81 * 2. old non-client, new non-client -> no 82 * 3. old client, new non-client -> yes 83 * 4. old non-client, new client -> yes 84 * 5. old client, new client -> yes 85 */ 86 if (prefp->conf.reflector_client == 0 && 87 peer->conf.reflector_client == 0 && 88 (asp->flags & F_PREFIX_ANNOUNCED) == 0) 89 /* Do not redistribute updates to ibgp peers */ 90 return (0); 91 } 92 93 /* export type handling */ 94 if (peer->conf.export_type == EXPORT_NONE || 95 peer->conf.export_type == EXPORT_DEFAULT_ROUTE) { 96 /* 97 * no need to withdraw old prefix as this will be 98 * filtered out as well. 99 */ 100 return (-1); 101 } 102 103 /* well known communities */ 104 if (community_match(comm, &comm_no_advertise, NULL)) 105 return (0); 106 if (peer->conf.ebgp) { 107 if (community_match(comm, &comm_no_export, NULL)) 108 return (0); 109 if (community_match(comm, &comm_no_expsubconfed, NULL)) 110 return (0); 111 } 112 113 /* 114 * Don't send messages back to originator 115 * this is not specified in the RFC but seems logical. 116 */ 117 if ((attr = attr_optget(asp, ATTR_ORIGINATOR_ID)) != NULL) { 118 if (memcmp(attr->data, &peer->remote_bgpid, 119 sizeof(peer->remote_bgpid)) == 0) { 120 /* would cause loop don't send */ 121 return (-1); 122 } 123 } 124 125 return (1); 126 } 127 128 void 129 up_generate_updates(struct filter_head *rules, struct rde_peer *peer, 130 struct prefix *new, struct prefix *old) 131 { 132 struct filterstate state; 133 struct bgpd_addr addr; 134 135 if (peer->state != PEER_UP) 136 return; 137 138 if (new == NULL) { 139 withdraw: 140 if (old == NULL) 141 /* no prefix to withdraw */ 142 return; 143 144 /* withdraw prefix */ 145 pt_getaddr(old->pt, &addr); 146 if (prefix_adjout_withdraw(peer, &addr, 147 old->pt->prefixlen) == 1) { 148 peer->prefix_out_cnt--; 149 peer->up_wcnt++; 150 } 151 } else { 152 switch (up_test_update(peer, new)) { 153 case 1: 154 break; 155 case 0: 156 goto withdraw; 157 case -1: 158 return; 159 } 160 161 rde_filterstate_prep(&state, prefix_aspath(new), 162 prefix_communities(new), prefix_nexthop(new), 163 prefix_nhflags(new)); 164 pt_getaddr(new->pt, &addr); 165 if (rde_filter(rules, peer, prefix_peer(new), &addr, 166 new->pt->prefixlen, prefix_vstate(new), &state) == 167 ACTION_DENY) { 168 rde_filterstate_clean(&state); 169 goto withdraw; 170 } 171 172 /* only send update if path changed */ 173 if (prefix_adjout_update(peer, &state, &addr, 174 new->pt->prefixlen, prefix_vstate(new)) == 1) { 175 peer->prefix_out_cnt++; 176 peer->up_nlricnt++; 177 } 178 179 rde_filterstate_clean(&state); 180 181 /* max prefix checker outbound */ 182 if (peer->conf.max_out_prefix && 183 peer->prefix_out_cnt > peer->conf.max_out_prefix) { 184 log_peer_warnx(&peer->conf, 185 "outbound prefix limit reached (>%u/%u)", 186 peer->prefix_out_cnt, peer->conf.max_out_prefix); 187 rde_update_err(peer, ERR_CEASE, 188 ERR_CEASE_MAX_SENT_PREFIX, NULL, 0); 189 } 190 } 191 } 192 193 struct rib_entry *rib_add(struct rib *, struct bgpd_addr *, int); 194 void rib_remove(struct rib_entry *); 195 int rib_empty(struct rib_entry *); 196 197 /* send a default route to the specified peer */ 198 void 199 up_generate_default(struct filter_head *rules, struct rde_peer *peer, 200 u_int8_t aid) 201 { 202 extern struct rde_peer *peerself; 203 struct filterstate state; 204 struct rde_aspath *asp; 205 struct bgpd_addr addr; 206 207 if (peer->capa.mp[aid] == 0) 208 return; 209 210 rde_filterstate_prep(&state, NULL, NULL, NULL, 0); 211 asp = &state.aspath; 212 asp->aspath = aspath_get(NULL, 0); 213 asp->origin = ORIGIN_IGP; 214 /* the other default values are OK, nexthop is once again NULL */ 215 216 /* 217 * XXX apply default overrides. Not yet possible, mainly a parse.y 218 * problem. 219 */ 220 /* rde_apply_set(asp, peerself, peerself, set, af); */ 221 222 bzero(&addr, sizeof(addr)); 223 addr.aid = aid; 224 /* outbound filter as usual */ 225 if (rde_filter(rules, peer, peerself, &addr, 0, ROA_NOTFOUND, 226 &state) == ACTION_DENY) { 227 rde_filterstate_clean(&state); 228 return; 229 } 230 231 if (prefix_adjout_update(peer, &state, &addr, 0, ROA_NOTFOUND) == 1) { 232 peer->prefix_out_cnt++; 233 peer->up_nlricnt++; 234 } 235 236 /* no longer needed */ 237 rde_filterstate_clean(&state); 238 239 /* max prefix checker outbound */ 240 if (peer->conf.max_out_prefix && 241 peer->prefix_out_cnt > peer->conf.max_out_prefix) { 242 log_peer_warnx(&peer->conf, 243 "outbound prefix limit reached (>%u/%u)", 244 peer->prefix_out_cnt, peer->conf.max_out_prefix); 245 rde_update_err(peer, ERR_CEASE, 246 ERR_CEASE_MAX_SENT_PREFIX, NULL, 0); 247 } 248 } 249 250 /* only for IPv4 */ 251 static struct bgpd_addr * 252 up_get_nexthop(struct rde_peer *peer, struct filterstate *state, u_int8_t aid) 253 { 254 struct bgpd_addr *peer_local; 255 256 switch (aid) { 257 case AID_INET: 258 case AID_VPN_IPv4: 259 peer_local = &peer->local_v4_addr; 260 break; 261 case AID_INET6: 262 case AID_VPN_IPv6: 263 peer_local = &peer->local_v6_addr; 264 break; 265 default: 266 fatalx("%s, bad AID %s", __func__, aid2str(aid)); 267 } 268 269 if (state->nhflags & NEXTHOP_SELF) { 270 /* 271 * Forcing the nexthop to self is always possible 272 * and has precedence over other flags. 273 */ 274 return (peer_local); 275 } else if (!peer->conf.ebgp) { 276 /* 277 * in the ibgp case the nexthop is normally not 278 * modified unless it points at the peer itself. 279 */ 280 if (state->nexthop == NULL) { 281 /* announced networks without explicit nexthop set */ 282 return (peer_local); 283 } 284 /* 285 * per RFC: if remote peer address is equal to the nexthop set 286 * the nexthop to our local address. This reduces the risk of 287 * routing loops. This overrides NEXTHOP_NOMODIFY. 288 */ 289 if (memcmp(&state->nexthop->exit_nexthop, 290 &peer->remote_addr, sizeof(peer->remote_addr)) == 0) { 291 return (peer_local); 292 } 293 return (&state->nexthop->exit_nexthop); 294 } else if (peer->conf.distance == 1) { 295 /* 296 * In the ebgp directly connected case never send 297 * out a nexthop that is outside of the connected 298 * network of the peer. No matter what flags are 299 * set. This follows section 5.1.3 of RFC 4271. 300 * So just check if the nexthop is in the same net 301 * is enough here. 302 */ 303 if (state->nexthop != NULL && 304 state->nexthop->flags & NEXTHOP_CONNECTED && 305 prefix_compare(&peer->remote_addr, 306 &state->nexthop->nexthop_net, 307 state->nexthop->nexthop_netlen) == 0) { 308 /* nexthop and peer are in the same net */ 309 return (&state->nexthop->exit_nexthop); 310 } 311 return (peer_local); 312 } else { 313 /* 314 * For ebgp multihop make it possible to overrule 315 * the sent nexthop by setting NEXTHOP_NOMODIFY. 316 * Similar to the ibgp case there is no same net check 317 * needed but still ensure that the nexthop is not 318 * pointing to the peer itself. 319 */ 320 if (state->nhflags & NEXTHOP_NOMODIFY && 321 state->nexthop != NULL && 322 memcmp(&state->nexthop->exit_nexthop, 323 &peer->remote_addr, sizeof(peer->remote_addr)) != 0) { 324 /* no modify flag set and nexthop not peer addr */ 325 return (&state->nexthop->exit_nexthop); 326 } 327 return (peer_local); 328 } 329 } 330 331 static int 332 up_generate_attr(u_char *buf, int len, struct rde_peer *peer, 333 struct filterstate *state, u_int8_t aid) 334 { 335 struct rde_aspath *asp = &state->aspath; 336 struct rde_community *comm = &state->communities; 337 struct attr *oa = NULL, *newaggr = NULL; 338 u_char *pdata; 339 u_int32_t tmp32; 340 in_addr_t nexthop; 341 int flags, r, neednewpath = 0; 342 u_int16_t wlen = 0, plen; 343 u_int8_t oalen = 0, type; 344 345 if (asp->others_len > 0) 346 oa = asp->others[oalen++]; 347 348 /* dump attributes in ascending order */ 349 for (type = ATTR_ORIGIN; type < 255; type++) { 350 r = 0; 351 352 while (oa && oa->type < type) { 353 if (oalen < asp->others_len) 354 oa = asp->others[oalen++]; 355 else 356 oa = NULL; 357 } 358 359 switch (type) { 360 /* 361 * Attributes stored in rde_aspath 362 */ 363 case ATTR_ORIGIN: 364 if ((r = attr_write(buf + wlen, len, ATTR_WELL_KNOWN, 365 ATTR_ORIGIN, &asp->origin, 1)) == -1) 366 return (-1); 367 break; 368 case ATTR_ASPATH: 369 if (!peer->conf.ebgp || 370 peer->conf.flags & PEERFLAG_TRANS_AS) 371 pdata = aspath_prepend(asp->aspath, 372 peer->conf.local_as, 0, &plen); 373 else 374 pdata = aspath_prepend(asp->aspath, 375 peer->conf.local_as, 1, &plen); 376 377 if (!rde_as4byte(peer)) 378 pdata = aspath_deflate(pdata, &plen, 379 &neednewpath); 380 381 if ((r = attr_write(buf + wlen, len, ATTR_WELL_KNOWN, 382 ATTR_ASPATH, pdata, plen)) == -1) 383 return (-1); 384 free(pdata); 385 break; 386 case ATTR_NEXTHOP: 387 switch (aid) { 388 case AID_INET: 389 nexthop = 390 up_get_nexthop(peer, state, aid)->v4.s_addr; 391 if ((r = attr_write(buf + wlen, len, 392 ATTR_WELL_KNOWN, ATTR_NEXTHOP, &nexthop, 393 4)) == -1) 394 return (-1); 395 break; 396 default: 397 break; 398 } 399 break; 400 case ATTR_MED: 401 /* 402 * The old MED from other peers MUST not be announced 403 * to others unless the MED is originating from us or 404 * the peer is an IBGP one. Only exception are routers 405 * with "transparent-as yes" set. 406 */ 407 if (asp->flags & F_ATTR_MED && (!peer->conf.ebgp || 408 asp->flags & F_ATTR_MED_ANNOUNCE || 409 peer->conf.flags & PEERFLAG_TRANS_AS)) { 410 tmp32 = htonl(asp->med); 411 if ((r = attr_write(buf + wlen, len, 412 ATTR_OPTIONAL, ATTR_MED, &tmp32, 4)) == -1) 413 return (-1); 414 } 415 break; 416 case ATTR_LOCALPREF: 417 if (!peer->conf.ebgp) { 418 /* local preference, only valid for ibgp */ 419 tmp32 = htonl(asp->lpref); 420 if ((r = attr_write(buf + wlen, len, 421 ATTR_WELL_KNOWN, ATTR_LOCALPREF, &tmp32, 422 4)) == -1) 423 return (-1); 424 } 425 break; 426 /* 427 * Communities are stored in struct rde_community 428 */ 429 case ATTR_COMMUNITIES: 430 if ((r = community_write(comm, buf + wlen, len)) == -1) 431 return (-1); 432 break; 433 case ATTR_EXT_COMMUNITIES: 434 if ((r = community_ext_write(comm, peer->conf.ebgp, 435 buf + wlen, len)) == -1) 436 return (-1); 437 break; 438 case ATTR_LARGE_COMMUNITIES: 439 if ((r = community_large_write(comm, buf + wlen, 440 len)) == -1) 441 return (-1); 442 break; 443 /* 444 * NEW to OLD conversion when sending stuff to a 2byte AS peer 445 */ 446 case ATTR_AS4_PATH: 447 if (neednewpath) { 448 if (!peer->conf.ebgp || 449 peer->conf.flags & PEERFLAG_TRANS_AS) 450 pdata = aspath_prepend(asp->aspath, 451 peer->conf.local_as, 0, &plen); 452 else 453 pdata = aspath_prepend(asp->aspath, 454 peer->conf.local_as, 1, &plen); 455 flags = ATTR_OPTIONAL|ATTR_TRANSITIVE; 456 if (!(asp->flags & F_PREFIX_ANNOUNCED)) 457 flags |= ATTR_PARTIAL; 458 if (plen == 0) 459 r = 0; 460 else if ((r = attr_write(buf + wlen, len, flags, 461 ATTR_AS4_PATH, pdata, plen)) == -1) 462 return (-1); 463 free(pdata); 464 } 465 break; 466 case ATTR_AS4_AGGREGATOR: 467 if (newaggr) { 468 flags = ATTR_OPTIONAL|ATTR_TRANSITIVE; 469 if (!(asp->flags & F_PREFIX_ANNOUNCED)) 470 flags |= ATTR_PARTIAL; 471 if ((r = attr_write(buf + wlen, len, flags, 472 ATTR_AS4_AGGREGATOR, newaggr->data, 473 newaggr->len)) == -1) 474 return (-1); 475 } 476 break; 477 /* 478 * multiprotocol attributes are handled elsewhere 479 */ 480 case ATTR_MP_REACH_NLRI: 481 case ATTR_MP_UNREACH_NLRI: 482 break; 483 /* 484 * dump all other path attributes. Following rules apply: 485 * 1. well-known attrs: ATTR_ATOMIC_AGGREGATE and 486 * ATTR_AGGREGATOR pass unmodified (enforce flags 487 * to correct values). Actually ATTR_AGGREGATOR may be 488 * deflated for OLD 2-byte peers. 489 * 2. non-transitive attrs: don't re-announce to ebgp peers 490 * 3. transitive known attrs: announce unmodified 491 * 4. transitive unknown attrs: set partial bit and re-announce 492 */ 493 case ATTR_ATOMIC_AGGREGATE: 494 if (oa == NULL || oa->type != type) 495 break; 496 if ((r = attr_write(buf + wlen, len, 497 ATTR_WELL_KNOWN, ATTR_ATOMIC_AGGREGATE, 498 NULL, 0)) == -1) 499 return (-1); 500 break; 501 case ATTR_AGGREGATOR: 502 if (oa == NULL || oa->type != type) 503 break; 504 if (!rde_as4byte(peer)) { 505 /* need to deflate the aggregator */ 506 u_int8_t t[6]; 507 u_int16_t tas; 508 509 if ((!(oa->flags & ATTR_TRANSITIVE)) && 510 peer->conf.ebgp) { 511 r = 0; 512 break; 513 } 514 515 memcpy(&tmp32, oa->data, sizeof(tmp32)); 516 if (ntohl(tmp32) > USHRT_MAX) { 517 tas = htons(AS_TRANS); 518 newaggr = oa; 519 } else 520 tas = htons(ntohl(tmp32)); 521 522 memcpy(t, &tas, sizeof(tas)); 523 memcpy(t + sizeof(tas), 524 oa->data + sizeof(tmp32), 525 oa->len - sizeof(tmp32)); 526 if ((r = attr_write(buf + wlen, len, 527 oa->flags, oa->type, &t, sizeof(t))) == -1) 528 return (-1); 529 break; 530 } 531 /* FALLTHROUGH */ 532 case ATTR_ORIGINATOR_ID: 533 case ATTR_CLUSTER_LIST: 534 if (oa == NULL || oa->type != type) 535 break; 536 if ((!(oa->flags & ATTR_TRANSITIVE)) && 537 peer->conf.ebgp) { 538 r = 0; 539 break; 540 } 541 if ((r = attr_write(buf + wlen, len, 542 oa->flags, oa->type, oa->data, oa->len)) == -1) 543 return (-1); 544 break; 545 default: 546 if (oa == NULL && type >= ATTR_FIRST_UNKNOWN) 547 /* there is no attribute left to dump */ 548 goto done; 549 550 if (oa == NULL || oa->type != type) 551 break; 552 /* unknown attribute */ 553 if (!(oa->flags & ATTR_TRANSITIVE)) { 554 /* 555 * RFC 1771: 556 * Unrecognized non-transitive optional 557 * attributes must be quietly ignored and 558 * not passed along to other BGP peers. 559 */ 560 break; 561 } 562 if ((r = attr_write(buf + wlen, len, 563 oa->flags | ATTR_PARTIAL, oa->type, 564 oa->data, oa->len)) == -1) 565 return (-1); 566 } 567 wlen += r; 568 len -= r; 569 } 570 done: 571 return (wlen); 572 } 573 574 /* 575 * Check if the pending element is a EoR marker. If so remove it from the 576 * tree and return 1. 577 */ 578 int 579 up_is_eor(struct rde_peer *peer, u_int8_t aid) 580 { 581 struct prefix *p; 582 583 p = RB_MIN(prefix_tree, &peer->updates[aid]); 584 if (p != NULL && p->eor) { 585 /* 586 * Need to remove eor from update tree because 587 * prefix_adjout_destroy() can't handle that. 588 */ 589 RB_REMOVE(prefix_tree, &peer->updates[aid], p); 590 p->flags &= ~PREFIX_FLAG_MASK; 591 prefix_adjout_destroy(p); 592 return 1; 593 } 594 return 0; 595 } 596 597 /* minimal buffer size > withdraw len + attr len + attr hdr + afi/safi */ 598 #define MIN_UPDATE_LEN 16 599 600 /* 601 * Write prefixes to buffer until either there is no more space or 602 * the next prefix has no longer the same ASPATH attributes. 603 */ 604 static int 605 up_dump_prefix(u_char *buf, int len, struct prefix_tree *prefix_head, 606 struct rde_peer *peer, int withdraw) 607 { 608 struct prefix *p, *np; 609 struct bgpd_addr addr; 610 int r, wpos = 0, done = 0; 611 612 RB_FOREACH_SAFE(p, prefix_tree, prefix_head, np) { 613 pt_getaddr(p->pt, &addr); 614 if ((r = prefix_write(buf + wpos, len - wpos, 615 &addr, p->pt->prefixlen, withdraw)) == -1) 616 break; 617 wpos += r; 618 619 /* make sure we only dump prefixes which belong together */ 620 if (np == NULL || 621 np->aspath != p->aspath || 622 np->communities != p->communities || 623 np->nexthop != p->nexthop || 624 np->nhflags != p->nhflags || 625 np->eor) 626 done = 1; 627 628 /* prefix sent, remove from list and clear flag */ 629 RB_REMOVE(prefix_tree, prefix_head, p); 630 p->flags &= ~PREFIX_FLAG_MASK; 631 632 if (withdraw) { 633 /* prefix no longer needed, remove it */ 634 prefix_adjout_destroy(p); 635 peer->up_wcnt--; 636 peer->prefix_sent_withdraw++; 637 } else { 638 /* prefix still in Adj-RIB-Out, keep it */ 639 peer->up_nlricnt--; 640 peer->prefix_sent_update++; 641 } 642 if (done) 643 break; 644 } 645 return (wpos); 646 } 647 648 int 649 up_dump_withdraws(u_char *buf, int len, struct rde_peer *peer, u_int8_t aid) 650 { 651 u_int16_t wpos, wd_len; 652 int r; 653 654 if (len < MIN_UPDATE_LEN) 655 return (-1); 656 657 /* reserve space for the length field */ 658 wpos = 2; 659 r = up_dump_prefix(buf + wpos, len - wpos, &peer->withdraws[aid], 660 peer, 1); 661 wd_len = htons(r); 662 memcpy(buf, &wd_len, 2); 663 664 return (wpos + r); 665 } 666 667 int 668 up_dump_mp_unreach(u_char *buf, int len, struct rde_peer *peer, u_int8_t aid) 669 { 670 u_char *attrbuf; 671 int wpos, r; 672 u_int16_t attr_len, tmp; 673 674 if (len < MIN_UPDATE_LEN || RB_EMPTY(&peer->withdraws[aid])) 675 return (-1); 676 677 /* reserve space for withdraw len, attr len */ 678 wpos = 2 + 2; 679 attrbuf = buf + wpos; 680 681 /* attribute header, defaulting to extended length one */ 682 attrbuf[0] = ATTR_OPTIONAL | ATTR_EXTLEN; 683 attrbuf[1] = ATTR_MP_UNREACH_NLRI; 684 wpos += 4; 685 686 /* afi & safi */ 687 if (aid2afi(aid, &tmp, buf + wpos + 2)) 688 fatalx("up_dump_mp_unreach: bad AID"); 689 tmp = htons(tmp); 690 memcpy(buf + wpos, &tmp, sizeof(u_int16_t)); 691 wpos += 3; 692 693 r = up_dump_prefix(buf + wpos, len - wpos, &peer->withdraws[aid], 694 peer, 1); 695 if (r == 0) 696 return (-1); 697 wpos += r; 698 attr_len = r + 3; /* prefixes + afi & safi */ 699 700 /* attribute length */ 701 attr_len = htons(attr_len); 702 memcpy(attrbuf + 2, &attr_len, sizeof(attr_len)); 703 704 /* write length fields */ 705 bzero(buf, sizeof(u_int16_t)); /* withdrawn routes len */ 706 attr_len = htons(wpos - 4); 707 memcpy(buf + 2, &attr_len, sizeof(attr_len)); 708 709 return (wpos); 710 } 711 712 int 713 up_dump_attrnlri(u_char *buf, int len, struct rde_peer *peer) 714 { 715 struct filterstate state; 716 struct prefix *p; 717 int r, wpos; 718 u_int16_t attr_len; 719 720 if (len < 2) 721 fatalx("up_dump_attrnlri: buffer way too small"); 722 if (len < MIN_UPDATE_LEN) 723 goto done; 724 725 p = RB_MIN(prefix_tree, &peer->updates[AID_INET]); 726 if (p == NULL) 727 goto done; 728 729 rde_filterstate_prep(&state, prefix_aspath(p), prefix_communities(p), 730 prefix_nexthop(p), prefix_nhflags(p)); 731 732 r = up_generate_attr(buf + 2, len - 2, peer, &state, AID_INET); 733 rde_filterstate_clean(&state); 734 if (r == -1) { 735 /* 736 * either no packet or not enough space. 737 * The length field needs to be set to zero else it would be 738 * an invalid bgp update. 739 */ 740 done: 741 bzero(buf, 2); 742 return (2); 743 } 744 745 /* first dump the 2-byte path attribute length */ 746 attr_len = htons(r); 747 memcpy(buf, &attr_len, 2); 748 wpos = 2; 749 /* then skip over the already dumped path attributes themselves */ 750 wpos += r; 751 752 /* last but not least dump the nlri */ 753 r = up_dump_prefix(buf + wpos, len - wpos, &peer->updates[AID_INET], 754 peer, 0); 755 wpos += r; 756 757 return (wpos); 758 } 759 760 static int 761 up_generate_mp_reach(u_char *buf, int len, struct rde_peer *peer, 762 struct filterstate *state, u_int8_t aid) 763 { 764 struct bgpd_addr *nexthop; 765 u_char *attrbuf; 766 int r, wpos, attrlen; 767 u_int16_t tmp; 768 769 if (len < 4) 770 return (-1); 771 /* attribute header, defaulting to extended length one */ 772 buf[0] = ATTR_OPTIONAL | ATTR_EXTLEN; 773 buf[1] = ATTR_MP_REACH_NLRI; 774 wpos = 4; 775 attrbuf = buf + wpos; 776 777 switch (aid) { 778 case AID_INET6: 779 attrlen = 21; /* AFI + SAFI + NH LEN + NH + Reserved */ 780 if (len < wpos + attrlen) 781 return (-1); 782 wpos += attrlen; 783 if (aid2afi(aid, &tmp, &attrbuf[2])) 784 fatalx("up_generate_mp_reach: bad AID"); 785 tmp = htons(tmp); 786 memcpy(attrbuf, &tmp, sizeof(tmp)); 787 attrbuf[3] = sizeof(struct in6_addr); 788 attrbuf[20] = 0; /* Reserved must be 0 */ 789 790 /* write nexthop */ 791 attrbuf += 4; 792 nexthop = up_get_nexthop(peer, state, aid); 793 memcpy(attrbuf, &nexthop->v6, sizeof(struct in6_addr)); 794 break; 795 case AID_VPN_IPv4: 796 attrlen = 17; /* AFI + SAFI + NH LEN + NH + Reserved */ 797 if (len < wpos + attrlen) 798 return (-1); 799 wpos += attrlen; 800 if (aid2afi(aid, &tmp, &attrbuf[2])) 801 fatalx("up_generate_mp_reachi: bad AID"); 802 tmp = htons(tmp); 803 memcpy(attrbuf, &tmp, sizeof(tmp)); 804 attrbuf[3] = sizeof(u_int64_t) + sizeof(struct in_addr); 805 bzero(attrbuf + 4, sizeof(u_int64_t)); 806 attrbuf[16] = 0; /* Reserved must be 0 */ 807 808 /* write nexthop */ 809 attrbuf += 12; 810 nexthop = up_get_nexthop(peer, state, aid); 811 memcpy(attrbuf, &nexthop->v4, sizeof(struct in_addr)); 812 break; 813 case AID_VPN_IPv6: 814 attrlen = 29; /* AFI + SAFI + NH LEN + NH + Reserved */ 815 if (len < wpos + attrlen) 816 return (-1); 817 wpos += attrlen; 818 if (aid2afi(aid, &tmp, &attrbuf[2])) 819 fatalx("up_generate_mp_reachi: bad AID"); 820 tmp = htons(tmp); 821 memcpy(attrbuf, &tmp, sizeof(tmp)); 822 attrbuf[3] = sizeof(u_int64_t) + sizeof(struct in6_addr); 823 bzero(attrbuf + 4, sizeof(u_int64_t)); 824 attrbuf[28] = 0; /* Reserved must be 0 */ 825 826 /* write nexthop */ 827 attrbuf += 12; 828 nexthop = up_get_nexthop(peer, state, aid); 829 memcpy(attrbuf, &nexthop->v6, sizeof(struct in6_addr)); 830 break; 831 default: 832 fatalx("up_generate_mp_reach: unknown AID"); 833 } 834 835 r = up_dump_prefix(buf + wpos, len - wpos, &peer->updates[aid], 836 peer, 0); 837 if (r == 0) { 838 /* no prefixes written ... */ 839 return (-1); 840 } 841 attrlen += r; 842 wpos += r; 843 /* update attribute length field */ 844 tmp = htons(attrlen); 845 memcpy(buf + 2, &tmp, sizeof(tmp)); 846 847 return (wpos); 848 } 849 850 int 851 up_dump_mp_reach(u_char *buf, int len, struct rde_peer *peer, u_int8_t aid) 852 { 853 struct filterstate state; 854 struct prefix *p; 855 int r, wpos; 856 u_int16_t attr_len; 857 858 if (len < MIN_UPDATE_LEN) 859 return 0; 860 861 /* get starting point */ 862 p = RB_MIN(prefix_tree, &peer->updates[aid]); 863 if (p == NULL) 864 return 0; 865 866 wpos = 4; /* reserve space for length fields */ 867 868 rde_filterstate_prep(&state, prefix_aspath(p), prefix_communities(p), 869 prefix_nexthop(p), prefix_nhflags(p)); 870 871 /* write regular path attributes */ 872 r = up_generate_attr(buf + wpos, len - wpos, peer, &state, aid); 873 if (r == -1) { 874 rde_filterstate_clean(&state); 875 return 0; 876 } 877 wpos += r; 878 879 /* write mp attribute */ 880 r = up_generate_mp_reach(buf + wpos, len - wpos, peer, &state, aid); 881 rde_filterstate_clean(&state); 882 if (r == -1) 883 return 0; 884 wpos += r; 885 886 /* write length fields */ 887 bzero(buf, sizeof(u_int16_t)); /* withdrawn routes len */ 888 attr_len = htons(wpos - 4); 889 memcpy(buf + 2, &attr_len, sizeof(attr_len)); 890 891 return (wpos); 892 } 893