1 /*	$OpenBSD: rde_prefix.c,v 1.57 2025/02/04 18:16:56 denis Exp $ */
2 
3 /*
4  * Copyright (c) 2003, 2004 Claudio Jeker <claudio@openbsd.org>
5  *
6  * Permission to use, copy, modify, and distribute this software for any
7  * purpose with or without fee is hereby granted, provided that the above
8  * copyright notice and this permission notice appear in all copies.
9  *
10  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17  */
18 
19 #include <sys/types.h>
20 #include <sys/queue.h>
21 
22 #include <endian.h>
23 #include <errno.h>
24 #include <stdint.h>
25 #include <stdlib.h>
26 #include <string.h>
27 
28 #include "bgpd.h"
29 #include "rde.h"
30 #include "log.h"
31 
32 /*
33  * Prefix Table functions:
34  * pt_add:    create new prefix and link it into the prefix table
35  * pt_remove: Checks if there is no bgp prefix linked to the prefix,
36  *            unlinks from the prefix table and frees the pt_entry.
37  * pt_get:    get a prefix/prefixlen entry. While pt_lookup searches for the
38  *            best matching prefix pt_get only finds the prefix/prefixlen
39  *            entry. The speed of pt_get is important for the bgp updates.
40  * pt_getaddr: convert the address into a struct bgpd_addr.
41  * pt_lookup: lookup a IP in the prefix table. Mainly for "show ip bgp".
42  * pt_empty:  returns true if there is no bgp prefix linked to the pt_entry.
43  * pt_init:   initialize prefix table.
44  * pt_alloc: allocate a AF specific pt_entry. Internal function.
45  * pt_free:   free a pt_entry. Internal function.
46  */
47 
48 /* internal prototypes */
49 static struct pt_entry	*pt_alloc(struct pt_entry *, int len);
50 static void		 pt_free(struct pt_entry *);
51 
52 struct pt_entry4 {
53 	RB_ENTRY(pt_entry)		pt_e;
54 	uint8_t				aid;
55 	uint8_t				prefixlen;
56 	uint16_t			len;
57 	uint32_t			refcnt;
58 	struct in_addr			prefix4;
59 };
60 
61 struct pt_entry6 {
62 	RB_ENTRY(pt_entry)		pt_e;
63 	uint8_t				aid;
64 	uint8_t				prefixlen;
65 	uint16_t			len;
66 	uint32_t			refcnt;
67 	struct in6_addr			prefix6;
68 };
69 
70 struct pt_entry_vpn4 {
71 	RB_ENTRY(pt_entry)		pt_e;
72 	uint8_t				aid;
73 	uint8_t				prefixlen;
74 	uint16_t			len;
75 	uint32_t			refcnt;
76 	uint64_t			rd;
77 	struct in_addr			prefix4;
78 	uint8_t				labelstack[21];
79 	uint8_t				labellen;
80 	uint8_t				pad1;
81 	uint8_t				pad2;
82 };
83 
84 struct pt_entry_vpn6 {
85 	RB_ENTRY(pt_entry)		pt_e;
86 	uint8_t				aid;
87 	uint8_t				prefixlen;
88 	uint16_t			len;
89 	uint32_t			refcnt;
90 	uint64_t			rd;
91 	struct in6_addr			prefix6;
92 	uint8_t				labelstack[21];
93 	uint8_t				labellen;
94 	uint8_t				pad1;
95 	uint8_t				pad2;
96 };
97 
98 struct pt_entry_evpn {
99 	RB_ENTRY(pt_entry)		pt_e;
100 	uint8_t				aid;
101 	uint8_t				prefixlen;
102 	uint16_t			len;
103 	uint32_t			refcnt;
104 	uint64_t			rd;
105 	uint32_t			ethtag;
106 	uint8_t				esi[ESI_ADDR_LEN];
107 	uint8_t				mac[ETHER_ADDR_LEN];
108 	uint8_t				labelstack[6];
109 	uint8_t				labellen;
110 	uint8_t				type;
111 	uint8_t				vpnaid;
112 	union {
113 		struct in_addr	prefix4;
114 		struct in6_addr	prefix6;
115 	};
116 };
117 
118 struct pt_entry_flow {
119 	RB_ENTRY(pt_entry)		pt_e;
120 	uint8_t				aid;
121 	uint8_t				prefixlen;	/* unused ??? */
122 	uint16_t			len;
123 	uint32_t			refcnt;
124 	uint64_t			rd;
125 	uint8_t				flow[1];	/* NLRI */
126 };
127 
128 #define PT_FLOW_SIZE		(offsetof(struct pt_entry_flow, flow))
129 
130 RB_HEAD(pt_tree, pt_entry);
131 RB_PROTOTYPE(pt_tree, pt_entry, pt_e, pt_prefix_cmp);
132 RB_GENERATE(pt_tree, pt_entry, pt_e, pt_prefix_cmp);
133 
134 struct pt_tree	pttable;
135 
136 void
pt_init(void)137 pt_init(void)
138 {
139 	RB_INIT(&pttable);
140 }
141 
142 void
pt_shutdown(void)143 pt_shutdown(void)
144 {
145 	if (!RB_EMPTY(&pttable))
146 		log_debug("pt_shutdown: tree is not empty.");
147 }
148 
149 void
pt_getaddr(struct pt_entry * pte,struct bgpd_addr * addr)150 pt_getaddr(struct pt_entry *pte, struct bgpd_addr *addr)
151 {
152 	struct pt_entry_flow	*pflow;
153 	struct pt_entry_evpn	*evpn;
154 
155 	memset(addr, 0, sizeof(struct bgpd_addr));
156 	addr->aid = pte->aid;
157 	switch (addr->aid) {
158 	case AID_INET:
159 		addr->v4 = ((struct pt_entry4 *)pte)->prefix4;
160 		break;
161 	case AID_INET6:
162 		addr->v6 = ((struct pt_entry6 *)pte)->prefix6;
163 		/* XXX scope_id ??? */
164 		break;
165 	case AID_VPN_IPv4:
166 		addr->v4 = ((struct pt_entry_vpn4 *)pte)->prefix4;
167 		addr->rd = ((struct pt_entry_vpn4 *)pte)->rd;
168 		addr->labellen = ((struct pt_entry_vpn4 *)pte)->labellen;
169 		memcpy(addr->labelstack,
170 		    ((struct pt_entry_vpn4 *)pte)->labelstack,
171 		    addr->labellen);
172 		break;
173 	case AID_VPN_IPv6:
174 		addr->v6 = ((struct pt_entry_vpn6 *)pte)->prefix6;
175 		addr->rd = ((struct pt_entry_vpn6 *)pte)->rd;
176 		addr->labellen = ((struct pt_entry_vpn6 *)pte)->labellen;
177 		memcpy(addr->labelstack,
178 		    ((struct pt_entry_vpn6 *)pte)->labelstack,
179 		    addr->labellen);
180 		break;
181 	case AID_EVPN:
182 		evpn = (struct pt_entry_evpn *)pte;
183 		addr->evpn.type = evpn->type;
184 		addr->rd = evpn->rd;
185 		addr->evpn.ethtag = evpn->ethtag;
186 		addr->labellen = evpn->labellen;
187 		addr->evpn.aid = evpn->vpnaid;
188 		memcpy(addr->labelstack, evpn->labelstack, addr->labellen);
189 		memcpy(addr->evpn.esi, evpn->esi, sizeof(evpn->esi));
190 		memcpy(addr->evpn.mac, evpn->mac, sizeof(evpn->mac));
191 		switch (evpn->vpnaid) {
192 		case AID_INET:
193 			addr->evpn.v4 = evpn->prefix4;
194 			break;
195 		case AID_INET6:
196 			addr->evpn.v6 = evpn->prefix6;
197 			break;
198 		}
199 		break;
200 	case AID_FLOWSPECv4:
201 	case AID_FLOWSPECv6:
202 		pflow = (struct pt_entry_flow *)pte;
203 		flowspec_get_addr(pflow->flow, pflow->len - PT_FLOW_SIZE,
204 		    FLOWSPEC_TYPE_DEST, addr->aid == AID_FLOWSPECv6,
205 		    addr, &pflow->prefixlen, NULL);
206 		break;
207 	default:
208 		fatalx("pt_getaddr: unknown af");
209 	}
210 }
211 
212 int
pt_getflowspec(struct pt_entry * pte,uint8_t ** flow)213 pt_getflowspec(struct pt_entry *pte, uint8_t **flow)
214 {
215 	struct pt_entry_flow	*pflow;
216 
217 	switch (pte->aid) {
218 	case AID_FLOWSPECv4:
219 	case AID_FLOWSPECv6:
220 		pflow = (struct pt_entry_flow *)pte;
221 		*flow = pflow->flow;
222 		return pflow->len - PT_FLOW_SIZE;
223 	default:
224 		fatalx("pt_getflowspec: unknown af");
225 	}
226 }
227 
228 struct pt_entry *
pt_fill(struct bgpd_addr * prefix,int prefixlen)229 pt_fill(struct bgpd_addr *prefix, int prefixlen)
230 {
231 	static struct pt_entry4		pte4;
232 	static struct pt_entry6		pte6;
233 	static struct pt_entry_vpn4	pte_vpn4;
234 	static struct pt_entry_vpn6	pte_vpn6;
235 	static struct pt_entry_evpn	pte_evpn;
236 
237 	switch (prefix->aid) {
238 	case AID_INET:
239 		memset(&pte4, 0, sizeof(pte4));
240 		pte4.len = sizeof(pte4);
241 		pte4.refcnt = UINT32_MAX;
242 		pte4.aid = prefix->aid;
243 		if (prefixlen > 32)
244 			fatalx("pt_fill: bad IPv4 prefixlen");
245 		inet4applymask(&pte4.prefix4, &prefix->v4, prefixlen);
246 		pte4.prefixlen = prefixlen;
247 		return ((struct pt_entry *)&pte4);
248 	case AID_INET6:
249 		memset(&pte6, 0, sizeof(pte6));
250 		pte6.len = sizeof(pte6);
251 		pte6.refcnt = UINT32_MAX;
252 		pte6.aid = prefix->aid;
253 		if (prefixlen > 128)
254 			fatalx("pt_fill: bad IPv6 prefixlen");
255 		inet6applymask(&pte6.prefix6, &prefix->v6, prefixlen);
256 		pte6.prefixlen = prefixlen;
257 		return ((struct pt_entry *)&pte6);
258 	case AID_VPN_IPv4:
259 		memset(&pte_vpn4, 0, sizeof(pte_vpn4));
260 		pte_vpn4.len = sizeof(pte_vpn4);
261 		pte_vpn4.refcnt = UINT32_MAX;
262 		pte_vpn4.aid = prefix->aid;
263 		if (prefixlen > 32)
264 			fatalx("pt_fill: bad IPv4 prefixlen");
265 		inet4applymask(&pte_vpn4.prefix4, &prefix->v4, prefixlen);
266 		pte_vpn4.prefixlen = prefixlen;
267 		pte_vpn4.rd = prefix->rd;
268 		pte_vpn4.labellen = prefix->labellen;
269 		memcpy(pte_vpn4.labelstack, prefix->labelstack,
270 		    prefix->labellen);
271 		return ((struct pt_entry *)&pte_vpn4);
272 	case AID_VPN_IPv6:
273 		memset(&pte_vpn6, 0, sizeof(pte_vpn6));
274 		pte_vpn6.len = sizeof(pte_vpn6);
275 		pte_vpn6.refcnt = UINT32_MAX;
276 		pte_vpn6.aid = prefix->aid;
277 		if (prefixlen > 128)
278 			fatalx("pt_fill: bad IPv6 prefixlen");
279 		inet6applymask(&pte_vpn6.prefix6, &prefix->v6, prefixlen);
280 		pte_vpn6.prefixlen = prefixlen;
281 		pte_vpn6.rd = prefix->rd;
282 		pte_vpn6.labellen = prefix->labellen;
283 		memcpy(pte_vpn6.labelstack, prefix->labelstack,
284 		    prefix->labellen);
285 		return ((struct pt_entry *)&pte_vpn6);
286 	case AID_EVPN:
287 		memset(&pte_evpn, 0, sizeof(pte_evpn));
288 		pte_evpn.len = sizeof(pte_evpn);
289 		pte_evpn.refcnt = UINT32_MAX;
290 		switch (prefix->evpn.aid) {
291 		case AID_UNSPEC:
292 			/* See rfc7432 section 7.2 */
293 			break;
294 		case AID_INET:
295 			pte_evpn.prefix4 = prefix->evpn.v4;
296 			break;
297 		case AID_INET6:
298 			pte_evpn.prefix6 = prefix->evpn.v6;
299 			break;
300 		default:
301 			fatalx("pt_fill: bad EVPN prefixlen");
302 		}
303 		pte_evpn.aid = prefix->aid;
304 		pte_evpn.vpnaid = prefix->evpn.aid;
305 		pte_evpn.prefixlen = prefixlen;
306 		pte_evpn.type = prefix->evpn.type;
307 		pte_evpn.rd = prefix->rd;
308 		pte_evpn.ethtag = prefix->evpn.ethtag;
309 		pte_evpn.labellen = prefix->labellen;
310 		memcpy(pte_evpn.labelstack, prefix->labelstack,
311 		    pte_evpn.labellen);
312 		memcpy(pte_evpn.esi, prefix->evpn.esi,
313 		    sizeof(prefix->evpn.esi));
314 		memcpy(pte_evpn.mac, prefix->evpn.mac,
315 		    sizeof(prefix->evpn.mac));
316 		return ((struct pt_entry *)&pte_evpn);
317 	default:
318 		fatalx("pt_fill: unknown af");
319 	}
320 }
321 
322 struct pt_entry *
pt_get(struct bgpd_addr * prefix,int prefixlen)323 pt_get(struct bgpd_addr *prefix, int prefixlen)
324 {
325 	struct pt_entry	*pte;
326 
327 	pte = pt_fill(prefix, prefixlen);
328 	return RB_FIND(pt_tree, &pttable, pte);
329 }
330 
331 struct pt_entry *
pt_add(struct bgpd_addr * prefix,int prefixlen)332 pt_add(struct bgpd_addr *prefix, int prefixlen)
333 {
334 	struct pt_entry		*p = NULL;
335 
336 	p = pt_fill(prefix, prefixlen);
337 	p = pt_alloc(p, p->len);
338 
339 	if (RB_INSERT(pt_tree, &pttable, p) != NULL)
340 		fatalx("pt_add: insert failed");
341 
342 	return (p);
343 }
344 
345 struct pt_entry *
pt_get_flow(struct flowspec * f)346 pt_get_flow(struct flowspec *f)
347 {
348 	struct pt_entry *needle;
349 	union {
350 		struct pt_entry_flow	flow;
351 		uint8_t			buf[4096];
352 	} x;
353 
354 	needle = (struct pt_entry *)&x.flow;
355 
356 	memset(needle, 0, PT_FLOW_SIZE);
357 	needle->aid = f->aid;
358 	needle->len = f->len + PT_FLOW_SIZE;
359 	memcpy(((struct pt_entry_flow *)needle)->flow, f->data, f->len);
360 
361 	return RB_FIND(pt_tree, &pttable, (struct pt_entry *)needle);
362 }
363 
364 struct pt_entry *
pt_add_flow(struct flowspec * f)365 pt_add_flow(struct flowspec *f)
366 {
367 	struct pt_entry *p;
368 	int len = f->len + PT_FLOW_SIZE;
369 
370 	p = malloc(len);
371 	if (p == NULL)
372 		fatal(__func__);
373 	rdemem.pt_cnt[f->aid]++;
374 	rdemem.pt_size[f->aid] += len;
375 	memset(p, 0, PT_FLOW_SIZE);
376 
377 	p->len = len;
378 	p->aid = f->aid;
379 	memcpy(((struct pt_entry_flow *)p)->flow, f->data, f->len);
380 
381 	if (RB_INSERT(pt_tree, &pttable, p) != NULL)
382 		fatalx("pt_add: insert failed");
383 
384 	return (p);
385 }
386 
387 void
pt_remove(struct pt_entry * pte)388 pt_remove(struct pt_entry *pte)
389 {
390 	if (pte->refcnt != 0)
391 		fatalx("pt_remove: entry still holds references");
392 
393 	if (RB_REMOVE(pt_tree, &pttable, pte) == NULL)
394 		log_warnx("pt_remove: remove failed.");
395 	pt_free(pte);
396 }
397 
398 struct pt_entry *
pt_lookup(struct bgpd_addr * addr)399 pt_lookup(struct bgpd_addr *addr)
400 {
401 	struct pt_entry	*p;
402 	int		 i;
403 
404 	switch (addr->aid) {
405 	case AID_INET:
406 	case AID_VPN_IPv4:
407 		i = 32;
408 		break;
409 	case AID_INET6:
410 	case AID_VPN_IPv6:
411 		i = 128;
412 		break;
413 	default:
414 		fatalx("pt_lookup: unknown af");
415 	}
416 	for (; i >= 0; i--) {
417 		p = pt_get(addr, i);
418 		if (p != NULL)
419 			return (p);
420 	}
421 	return (NULL);
422 }
423 
424 int
pt_prefix_cmp(const struct pt_entry * a,const struct pt_entry * b)425 pt_prefix_cmp(const struct pt_entry *a, const struct pt_entry *b)
426 {
427 	const struct pt_entry4		*a4, *b4;
428 	const struct pt_entry6		*a6, *b6;
429 	const struct pt_entry_vpn4	*va4, *vb4;
430 	const struct pt_entry_vpn6	*va6, *vb6;
431 	const struct pt_entry_flow	*af, *bf;
432 	const struct pt_entry_evpn	*ea, *eb;
433 	int				 i;
434 
435 	if (a->aid > b->aid)
436 		return (1);
437 	if (a->aid < b->aid)
438 		return (-1);
439 
440 	switch (a->aid) {
441 	case AID_INET:
442 		a4 = (const struct pt_entry4 *)a;
443 		b4 = (const struct pt_entry4 *)b;
444 		if (ntohl(a4->prefix4.s_addr) > ntohl(b4->prefix4.s_addr))
445 			return (1);
446 		if (ntohl(a4->prefix4.s_addr) < ntohl(b4->prefix4.s_addr))
447 			return (-1);
448 		if (a4->prefixlen > b4->prefixlen)
449 			return (1);
450 		if (a4->prefixlen < b4->prefixlen)
451 			return (-1);
452 		return (0);
453 	case AID_INET6:
454 		a6 = (const struct pt_entry6 *)a;
455 		b6 = (const struct pt_entry6 *)b;
456 
457 		i = memcmp(&a6->prefix6, &b6->prefix6, sizeof(struct in6_addr));
458 		if (i > 0)
459 			return (1);
460 		if (i < 0)
461 			return (-1);
462 		if (a6->prefixlen < b6->prefixlen)
463 			return (-1);
464 		if (a6->prefixlen > b6->prefixlen)
465 			return (1);
466 		return (0);
467 	case AID_VPN_IPv4:
468 		va4 = (const struct pt_entry_vpn4 *)a;
469 		vb4 = (const struct pt_entry_vpn4 *)b;
470 		if (be64toh(va4->rd) > be64toh(vb4->rd))
471 			return (1);
472 		if (be64toh(va4->rd) < be64toh(vb4->rd))
473 			return (-1);
474 		if (ntohl(va4->prefix4.s_addr) > ntohl(vb4->prefix4.s_addr))
475 			return (1);
476 		if (ntohl(va4->prefix4.s_addr) < ntohl(vb4->prefix4.s_addr))
477 			return (-1);
478 		if (va4->prefixlen > vb4->prefixlen)
479 			return (1);
480 		if (va4->prefixlen < vb4->prefixlen)
481 			return (-1);
482 		return (0);
483 	case AID_VPN_IPv6:
484 		va6 = (const struct pt_entry_vpn6 *)a;
485 		vb6 = (const struct pt_entry_vpn6 *)b;
486 		if (be64toh(va6->rd) > be64toh(vb6->rd))
487 			return (1);
488 		if (be64toh(va6->rd) < be64toh(vb6->rd))
489 			return (-1);
490 		i = memcmp(&va6->prefix6, &vb6->prefix6,
491 		    sizeof(struct in6_addr));
492 		if (i > 0)
493 			return (1);
494 		if (i < 0)
495 			return (-1);
496 		if (va6->prefixlen > vb6->prefixlen)
497 			return (1);
498 		if (va6->prefixlen < vb6->prefixlen)
499 			return (-1);
500 		return (0);
501 	case AID_EVPN:
502 		/* XXXX Need different comparator for different types */
503 		ea = (const struct pt_entry_evpn *)a;
504 		eb = (const struct pt_entry_evpn *)b;
505 		if (ea->ethtag > eb->ethtag)
506 			return (1);
507 		if (ea->ethtag < eb->ethtag)
508 			return (-1);
509 		/* MAC length is always 48 */
510 		i = memcmp(&ea->mac, &eb->mac, sizeof(ea->mac));
511 		if (i > 0)
512 			return (1);
513 		if (i < 0)
514 			return (-1);
515 		if (ea->prefixlen > eb->prefixlen)
516 			return (1);
517 		if (ea->prefixlen < eb->prefixlen)
518 			return (-1);
519 		switch (ea->vpnaid) {
520 		case AID_UNSPEC:
521 			break;
522 		case AID_INET:
523 			i = memcmp(&ea->prefix4, &eb->prefix4,
524 			    sizeof(struct in_addr));
525 			if (i > 0)
526 				return (1);
527 			if (i < 0)
528 				return (-1);
529 			break;
530 		case AID_INET6:
531 			i = memcmp(&ea->prefix6, &eb->prefix6,
532 			    sizeof(struct in6_addr));
533 			if (i > 0)
534 				return (1);
535 			if (i < 0)
536 				return (-1);
537 			break;
538 		default:
539 			fatalx("pt_prefix_cmp: unknown evpn af %d", ea->vpnaid);
540 		}
541 		return (0);
542 	case AID_FLOWSPECv4:
543 	case AID_FLOWSPECv6:
544 		af = (const struct pt_entry_flow *)a;
545 		bf = (const struct pt_entry_flow *)b;
546 		return flowspec_cmp(af->flow, af->len - PT_FLOW_SIZE,
547 		    bf->flow, bf->len - PT_FLOW_SIZE,
548 		    a->aid == AID_FLOWSPECv6);
549 	default:
550 		fatalx("pt_prefix_cmp: unknown af %d", a->aid);
551 	}
552 	return (-1);
553 }
554 
555 /*
556  * Returns a pt_entry cloned from the one passed in.
557  * Function may not return on failure.
558  */
559 static struct pt_entry *
pt_alloc(struct pt_entry * op,int len)560 pt_alloc(struct pt_entry *op, int len)
561 {
562 	struct pt_entry		*p;
563 
564 	p = malloc(len);
565 	if (p == NULL)
566 		fatal("pt_alloc");
567 	rdemem.pt_cnt[op->aid]++;
568 	rdemem.pt_size[op->aid] += len;
569 	memcpy(p, op, len);
570 	p->refcnt = 0;
571 
572 	return (p);
573 }
574 
575 static void
pt_free(struct pt_entry * pte)576 pt_free(struct pt_entry *pte)
577 {
578 	rdemem.pt_cnt[pte->aid]--;
579 	rdemem.pt_size[pte->aid] -= pte->len;
580 	free(pte);
581 }
582 
583 /* dump a prefix into specified buffer */
584 int
pt_writebuf(struct ibuf * buf,struct pt_entry * pte,int withdraw,int add_path,uint32_t pathid)585 pt_writebuf(struct ibuf *buf, struct pt_entry *pte, int withdraw,
586     int add_path, uint32_t pathid)
587 {
588 	struct pt_entry_vpn4	*pvpn4 = (struct pt_entry_vpn4 *)pte;
589 	struct pt_entry_vpn6	*pvpn6 = (struct pt_entry_vpn6 *)pte;
590 	struct pt_entry_flow	*pflow = (struct pt_entry_flow *)pte;
591 	struct pt_entry_evpn	*pevpn = (struct pt_entry_evpn *)pte;
592 	struct ibuf		*tmp;
593 	int			 flowlen, psize;
594 	uint16_t		 plen;
595 
596 	if ((tmp = ibuf_dynamic(32, UINT16_MAX)) == NULL)
597 		goto fail;
598 
599 	if (add_path) {
600 		if (ibuf_add_n32(tmp, pathid) == -1)
601 			goto fail;
602 	}
603 
604 	switch (pte->aid) {
605 	case AID_INET:
606 	case AID_INET6:
607 		plen = pte->prefixlen;
608 		if (ibuf_add_n8(tmp, plen) == -1)
609 			goto fail;
610 		if (ibuf_add(tmp, pte->data, PREFIX_SIZE(plen) - 1) == -1)
611 			goto fail;
612 		break;
613 	case AID_VPN_IPv4:
614 		plen = pvpn4->prefixlen;
615 		psize = PREFIX_SIZE(plen) - 1;
616 		plen += sizeof(pvpn4->rd) * 8;
617 		if (withdraw) {
618 			/* withdraw have one compat label as placeholder */
619 			plen += 3 * 8;
620 		} else {
621 			plen += pvpn4->labellen * 8;
622 		}
623 
624 		if (ibuf_add_n8(tmp, plen) == -1)
625 			goto fail;
626 		if (withdraw) {
627 			/* magic compatibility label as per rfc8277 */
628 			if (ibuf_add_n8(tmp, 0x80) == -1 ||
629 			    ibuf_add_zero(tmp, 2) == -1)
630 				goto fail;
631 		} else {
632 			if (ibuf_add(tmp, &pvpn4->labelstack,
633 			    pvpn4->labellen) == -1)
634 				goto fail;
635 		}
636 		if (ibuf_add(tmp, &pvpn4->rd, sizeof(pvpn4->rd)) == -1 ||
637 		    ibuf_add(tmp, &pvpn4->prefix4, psize) == -1)
638 			goto fail;
639 		break;
640 	case AID_VPN_IPv6:
641 		plen = pvpn6->prefixlen;
642 		psize = PREFIX_SIZE(plen) - 1;
643 		plen += sizeof(pvpn6->rd) * 8;
644 		if (withdraw) {
645 			/* withdraw have one compat label as placeholder */
646 			plen += 3 * 8;
647 		} else {
648 			plen += pvpn6->labellen * 8;
649 		}
650 
651 		if (ibuf_add_n8(tmp, plen) == -1)
652 			goto fail;
653 		if (withdraw) {
654 			/* magic compatibility label as per rfc8277 */
655 			if (ibuf_add_n8(tmp, 0x80) == -1 ||
656 			    ibuf_add_zero(tmp, 2) == -1)
657 				goto fail;
658 		} else {
659 			if (ibuf_add(tmp, &pvpn6->labelstack,
660 			    pvpn6->labellen) == -1)
661 				goto fail;
662 		}
663 		if (ibuf_add(tmp, &pvpn6->rd, sizeof(pvpn6->rd)) == -1 ||
664 		    ibuf_add(tmp, &pvpn6->prefix6, psize) == -1)
665 			goto fail;
666 		break;
667 	case AID_EVPN:
668 		if (ibuf_add_n8(tmp, pevpn->type) == -1)
669 			goto fail;
670 		switch (pevpn->type) {
671 		case EVPN_ROUTE_TYPE_2:
672 			plen = sizeof(pevpn->rd) * 8;
673 			plen += sizeof(pevpn->esi) * 8;
674 			plen += sizeof(pevpn->ethtag) * 8;
675 			plen += 8;	/* MAC length */
676 			plen += sizeof(pevpn->mac) * 8;
677 			plen += 8;	/* IP length */
678 			plen += pevpn->prefixlen;
679 			plen += pevpn->labellen * 8;
680 			if (ibuf_add_n8(tmp, PREFIX_SIZE(plen) - 1) == -1)
681 				goto fail;
682 			if (ibuf_add_h64(tmp, pevpn->rd) == -1 ||
683 			    ibuf_add(tmp, pevpn->esi,
684 			    sizeof(pevpn->esi)) == -1 ||
685 			    ibuf_add_h32(tmp, pevpn->ethtag) == -1)
686 				goto fail;
687 			if (ibuf_add_n8(tmp, sizeof(pevpn->mac) * 8) == -1 ||
688 			    ibuf_add(tmp, pevpn->mac, sizeof(pevpn->mac)) == -1)
689 				goto fail;
690 			if (ibuf_add_n8(tmp, pevpn->prefixlen) == -1)
691 				goto fail;
692 			switch(pevpn->vpnaid) {
693 			case AID_UNSPEC:
694 				/* See rfc7432 section 7.2 */
695 				break;
696 			case AID_INET:
697 				if (ibuf_add(tmp, &pevpn->prefix4,
698 				    sizeof(pevpn->prefix4)) == -1)
699 					goto fail;
700 				break;
701 			case AID_INET6:
702 				if (ibuf_add(tmp, &pevpn->prefix6,
703 				sizeof(pevpn->prefix6)) == -1)
704 					goto fail;
705 				break;
706 			default:
707 				goto fail;
708 			}
709 			if (ibuf_add(tmp, pevpn->labelstack,
710 			    pevpn->labellen) == -1)
711 			       goto fail;
712 			break;
713 		case EVPN_ROUTE_TYPE_3:
714 			plen = sizeof(pevpn->rd) * 8;
715 			plen += sizeof(pevpn->ethtag) * 8;
716 			plen += 8;	/* IP length */
717 			plen += pevpn->prefixlen;
718 			if (ibuf_add_n8(tmp, PREFIX_SIZE(plen) - 1) == -1)
719 				goto fail;
720 			if (ibuf_add_h64(tmp, pevpn->rd) == -1 ||
721 			    ibuf_add_h32(tmp, pevpn->ethtag) == -1)
722 			       goto fail;
723 			if (ibuf_add_n8(tmp, pevpn->prefixlen) == -1)
724 				goto fail;
725 			switch(pevpn->vpnaid) {
726 			case AID_INET:
727 				if (ibuf_add(tmp, &pevpn->prefix4,
728 				    sizeof(pevpn->prefix4)) == -1)
729 					goto fail;
730 				break;
731 			case AID_INET6:
732 				if (ibuf_add(tmp, &pevpn->prefix6,
733 				    sizeof(pevpn->prefix6)) == -1)
734 					goto fail;
735 				break;
736 			default:
737 				goto fail;
738 			}
739 		}
740 		break;
741 	case AID_FLOWSPECv4:
742 	case AID_FLOWSPECv6:
743 		flowlen = pflow->len - PT_FLOW_SIZE;
744 		if (flowlen < FLOWSPEC_LEN_LIMIT) {
745 			if (ibuf_add_n8(tmp, flowlen) == -1)
746 				goto fail;
747 		} else {
748 			if (ibuf_add_n8(tmp, 0xf0 | (flowlen >> 8)) == -1 ||
749 			    ibuf_add_n8(tmp, flowlen) == -1)
750 				goto fail;
751 		}
752 		if (ibuf_add(tmp, &pflow->flow, flowlen) == -1)
753 			goto fail;
754 		break;
755 	default:
756 		fatalx("%s: unknown aid %d", __func__, pte->aid);
757 	}
758 
759 	/* keep 2 bytes reserved in the withdraw case for IPv4 encoding */
760 	if (withdraw && ibuf_left(buf) < ibuf_size(tmp) + 2)
761 		goto fail;
762 	if (ibuf_add_ibuf(buf, tmp) == -1)
763 		goto fail;
764 	ibuf_free(tmp);
765 	return 0;
766 
767  fail:
768 	ibuf_free(tmp);
769 	return -1;
770 }
771