xref: /openbsd/usr.sbin/bgpd/rde_update.c (revision 09467b48)
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