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