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