xref: /openbsd/usr.sbin/ldpd/labelmapping.c (revision 9246985a)
1 /*	$OpenBSD: labelmapping.c,v 1.68 2017/03/04 00:15:35 renato Exp $ */
2 
3 /*
4  * Copyright (c) 2014, 2015 Renato Westphal <renato@openbsd.org>
5  * Copyright (c) 2009 Michele Marchetto <michele@openbsd.org>
6  *
7  * Permission to use, copy, modify, and distribute this software for any
8  * purpose with or without fee is hereby granted, provided that the above
9  * copyright notice and this permission notice appear in all copies.
10  *
11  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
12  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
13  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
14  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
15  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
16  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
17  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
18  */
19 
20 #include <sys/types.h>
21 #include <sys/socket.h>
22 #include <arpa/inet.h>
23 #include <netmpls/mpls.h>
24 #include <limits.h>
25 #include <stdlib.h>
26 #include <string.h>
27 
28 #include "ldpd.h"
29 #include "ldpe.h"
30 #include "log.h"
31 
32 static void	 enqueue_pdu(struct nbr *, struct ibuf *, uint16_t);
33 static int	 gen_label_tlv(struct ibuf *, uint32_t);
34 static int	 tlv_decode_label(struct nbr *, struct ldp_msg *, char *,
35 		    uint16_t, uint32_t *);
36 static int	 gen_reqid_tlv(struct ibuf *, uint32_t);
37 static void	 log_msg_mapping(int, uint16_t, struct nbr *, struct map *);
38 
39 static void
40 enqueue_pdu(struct nbr *nbr, struct ibuf *buf, uint16_t size)
41 {
42 	struct ldp_hdr		*ldp_hdr;
43 
44 	ldp_hdr = ibuf_seek(buf, 0, sizeof(struct ldp_hdr));
45 	ldp_hdr->length = htons(size);
46 	evbuf_enqueue(&nbr->tcp->wbuf, buf);
47 }
48 
49 /* Generic function that handles all Label Message types */
50 void
51 send_labelmessage(struct nbr *nbr, uint16_t type, struct mapping_head *mh)
52 {
53 	struct ibuf		*buf = NULL;
54 	struct mapping_entry	*me;
55 	uint16_t		 msg_size, size = 0;
56 	int			 first = 1;
57 	int			 err = 0;
58 
59 	/* nothing to send */
60 	if (TAILQ_EMPTY(mh))
61 		return;
62 
63 	while ((me = TAILQ_FIRST(mh)) != NULL) {
64 		/* generate pdu */
65 		if (first) {
66 			if ((buf = ibuf_open(nbr->max_pdu_len +
67 			    LDP_HDR_DEAD_LEN)) == NULL)
68 				fatal(__func__);
69 
70 			/* real size will be set up later */
71 			err |= gen_ldp_hdr(buf, 0);
72 
73 			size = LDP_HDR_PDU_LEN;
74 			first = 0;
75 		}
76 
77 		/* calculate size */
78 		msg_size = LDP_MSG_SIZE;
79 		msg_size += len_fec_tlv(&me->map);
80 		if (me->map.label != NO_LABEL)
81 			msg_size += LABEL_TLV_SIZE;
82 		if (me->map.flags & F_MAP_REQ_ID)
83 			msg_size += REQID_TLV_SIZE;
84 		if (me->map.flags & F_MAP_STATUS)
85 			msg_size += STATUS_SIZE;
86 
87 		/* maximum pdu length exceeded, we need a new ldp pdu */
88 		if (size + msg_size > nbr->max_pdu_len) {
89 			enqueue_pdu(nbr, buf, size);
90 			first = 1;
91 			continue;
92 		}
93 
94 		size += msg_size;
95 
96 		/* append message and tlvs */
97 		err |= gen_msg_hdr(buf, type, msg_size);
98 		err |= gen_fec_tlv(buf, &me->map);
99 		if (me->map.label != NO_LABEL)
100 			err |= gen_label_tlv(buf, me->map.label);
101 		if (me->map.flags & F_MAP_REQ_ID)
102 			err |= gen_reqid_tlv(buf, me->map.requestid);
103 	    	if (me->map.flags & F_MAP_PW_STATUS)
104 			err |= gen_pw_status_tlv(buf, me->map.pw_status);
105 		if (me->map.flags & F_MAP_STATUS)
106 			err |= gen_status_tlv(buf, me->map.st.status_code,
107 			    me->map.st.msg_id, me->map.st.msg_type);
108 		if (err) {
109 			ibuf_free(buf);
110 			mapping_list_clr(mh);
111 			return;
112 		}
113 
114 		log_msg_mapping(1, type, nbr, &me->map);
115 
116 		TAILQ_REMOVE(mh, me, entry);
117 		free(me);
118 	}
119 
120 	enqueue_pdu(nbr, buf, size);
121 
122 	nbr_fsm(nbr, NBR_EVT_PDU_SENT);
123 }
124 
125 /* Generic function that handles all Label Message types */
126 int
127 recv_labelmessage(struct nbr *nbr, char *buf, uint16_t len, uint16_t type)
128 {
129 	struct ldp_msg		 msg;
130 	struct tlv		 ft;
131 	uint32_t		 label = NO_LABEL, reqid = 0;
132 	uint32_t		 pw_status = 0;
133 	uint8_t			 flags = 0;
134 	int			 feclen, lbllen, tlen;
135 	struct mapping_entry	*me;
136 	struct mapping_head	 mh;
137 	struct map		 map;
138 
139 	memcpy(&msg, buf, sizeof(msg));
140 	buf += LDP_MSG_SIZE;
141 	len -= LDP_MSG_SIZE;
142 
143 	/* FEC TLV */
144 	if (len < sizeof(ft)) {
145 		session_shutdown(nbr, S_BAD_TLV_LEN, msg.id, msg.type);
146 		return (-1);
147 	}
148 
149 	memcpy(&ft, buf, sizeof(ft));
150 	if (ntohs(ft.type) != TLV_TYPE_FEC) {
151 		send_notification(nbr->tcp, S_MISS_MSG, msg.id, msg.type);
152 		return (-1);
153 	}
154 	feclen = ntohs(ft.length);
155 	if (feclen > len - TLV_HDR_SIZE) {
156 		session_shutdown(nbr, S_BAD_TLV_LEN, msg.id, msg.type);
157 		return (-1);
158 	}
159 
160 	buf += TLV_HDR_SIZE;	/* just advance to the end of the fec header */
161 	len -= TLV_HDR_SIZE;
162 
163 	TAILQ_INIT(&mh);
164 	do {
165 		memset(&map, 0, sizeof(map));
166 		map.msg_id = msg.id;
167 
168 		if ((tlen = tlv_decode_fec_elm(nbr, &msg, buf, feclen,
169 		    &map)) == -1)
170 			goto err;
171 		if (map.type == MAP_TYPE_PWID &&
172 		    !(map.flags & F_MAP_PW_ID) &&
173 		    type != MSG_TYPE_LABELWITHDRAW &&
174 		    type != MSG_TYPE_LABELRELEASE) {
175 			send_notification(nbr->tcp, S_MISS_MSG, msg.id,
176 			    msg.type);
177 			return (-1);
178 		}
179 
180 		/*
181 		 * The Wildcard FEC Element can be used only in the
182 		 * Label Withdraw and Label Release messages.
183 		 */
184 		if (map.type == MAP_TYPE_WILDCARD) {
185 			switch (type) {
186 			case MSG_TYPE_LABELMAPPING:
187 			case MSG_TYPE_LABELREQUEST:
188 			case MSG_TYPE_LABELABORTREQ:
189 				session_shutdown(nbr, S_UNKNOWN_FEC, msg.id,
190 				    msg.type);
191 				goto err;
192 			default:
193 				break;
194 			}
195 		}
196 
197 		/*
198 		 * RFC 5561 - Section 4:
199 		 * "An LDP implementation that supports the Typed Wildcard
200 		 * FEC Element MUST support its use in Label Request, Label
201 		 * Withdraw, and Label Release messages".
202 		 */
203 		if (map.type == MAP_TYPE_TYPED_WCARD) {
204 			switch (type) {
205 			case MSG_TYPE_LABELMAPPING:
206 			case MSG_TYPE_LABELABORTREQ:
207 				session_shutdown(nbr, S_UNKNOWN_FEC, msg.id,
208 				    msg.type);
209 				goto err;
210 			default:
211 				break;
212 			}
213 		}
214 
215 		/*
216 		 * LDP supports the use of multiple FEC Elements per
217 		 * FEC for the Label Mapping message only.
218 		 */
219 		if (type != MSG_TYPE_LABELMAPPING &&
220 		    tlen != feclen) {
221 			session_shutdown(nbr, S_BAD_TLV_VAL, msg.id, msg.type);
222 			goto err;
223 		}
224 
225 		mapping_list_add(&mh, &map);
226 
227 		buf += tlen;
228 		len -= tlen;
229 		feclen -= tlen;
230 	} while (feclen > 0);
231 
232 	/* Mandatory Label TLV */
233 	if (type == MSG_TYPE_LABELMAPPING) {
234 		lbllen = tlv_decode_label(nbr, &msg, buf, len, &label);
235 		if (lbllen == -1)
236 			goto err;
237 
238 		buf += lbllen;
239 		len -= lbllen;
240 	}
241 
242 	/* Optional Parameters */
243 	while (len > 0) {
244 		struct tlv 	tlv;
245 		uint16_t	tlv_type;
246 		uint16_t	tlv_len;
247 		uint32_t	reqbuf, labelbuf, statusbuf;
248 
249 		if (len < sizeof(tlv)) {
250 			session_shutdown(nbr, S_BAD_TLV_LEN, msg.id, msg.type);
251 			goto err;
252 		}
253 
254 		memcpy(&tlv, buf, TLV_HDR_SIZE);
255 		tlv_type = ntohs(tlv.type);
256 		tlv_len = ntohs(tlv.length);
257 		if (tlv_len + TLV_HDR_SIZE > len) {
258 			session_shutdown(nbr, S_BAD_TLV_LEN, msg.id, msg.type);
259 			goto err;
260 		}
261 		buf += TLV_HDR_SIZE;
262 		len -= TLV_HDR_SIZE;
263 
264 		switch (tlv_type) {
265 		case TLV_TYPE_LABELREQUEST:
266 			switch (type) {
267 			case MSG_TYPE_LABELMAPPING:
268 			case MSG_TYPE_LABELREQUEST:
269 				if (tlv_len != REQID_TLV_LEN) {
270 					session_shutdown(nbr, S_BAD_TLV_LEN,
271 					    msg.id, msg.type);
272 					goto err;
273 				}
274 
275 				flags |= F_MAP_REQ_ID;
276 				memcpy(&reqbuf, buf, sizeof(reqbuf));
277 				reqid = ntohl(reqbuf);
278 				break;
279 			default:
280 				/* ignore */
281 				break;
282 			}
283 			break;
284 		case TLV_TYPE_HOPCOUNT:
285 		case TLV_TYPE_PATHVECTOR:
286 			/* ignore */
287 			break;
288 		case TLV_TYPE_GENERICLABEL:
289 			switch (type) {
290 			case MSG_TYPE_LABELWITHDRAW:
291 			case MSG_TYPE_LABELRELEASE:
292 				if (tlv_len != LABEL_TLV_LEN) {
293 					session_shutdown(nbr, S_BAD_TLV_LEN,
294 					    msg.id, msg.type);
295 					goto err;
296 				}
297 
298 				memcpy(&labelbuf, buf, sizeof(labelbuf));
299 				label = ntohl(labelbuf);
300 				break;
301 			default:
302 				/* ignore */
303 				break;
304 			}
305 			break;
306 		case TLV_TYPE_ATMLABEL:
307 		case TLV_TYPE_FRLABEL:
308 			switch (type) {
309 			case MSG_TYPE_LABELWITHDRAW:
310 			case MSG_TYPE_LABELRELEASE:
311 				/* unsupported */
312 				session_shutdown(nbr, S_BAD_TLV_VAL, msg.id,
313 				    msg.type);
314 				goto err;
315 				break;
316 			default:
317 				/* ignore */
318 				break;
319 			}
320 			break;
321 		case TLV_TYPE_STATUS:
322 			if (tlv_len != STATUS_TLV_LEN) {
323 				session_shutdown(nbr, S_BAD_TLV_LEN, msg.id,
324 				    msg.type);
325 				goto err;
326 			}
327 			/* ignore */
328 			break;
329 		case TLV_TYPE_PW_STATUS:
330 			switch (type) {
331 			case MSG_TYPE_LABELMAPPING:
332 				if (tlv_len != PW_STATUS_TLV_LEN) {
333 					session_shutdown(nbr, S_BAD_TLV_LEN,
334 					    msg.id, msg.type);
335 					goto err;
336 				}
337 
338 				flags |= F_MAP_PW_STATUS;
339 				memcpy(&statusbuf, buf, sizeof(statusbuf));
340 				pw_status = ntohl(statusbuf);
341 				break;
342 			default:
343 				/* ignore */
344 				break;
345 			}
346 			break;
347 		default:
348 			if (!(ntohs(tlv.type) & UNKNOWN_FLAG))
349 				send_notification_rtlvs(nbr, S_UNKNOWN_TLV,
350 				    msg.id, msg.type, tlv_type, tlv_len, buf);
351 			/* ignore unknown tlv */
352 			break;
353 		}
354 		buf += tlv_len;
355 		len -= tlv_len;
356 	}
357 
358 	/* notify lde about the received message. */
359 	while ((me = TAILQ_FIRST(&mh)) != NULL) {
360 		int imsg_type = IMSG_NONE;
361 
362 		me->map.flags |= flags;
363 		switch (me->map.type) {
364 		case MAP_TYPE_PREFIX:
365 			switch (me->map.fec.prefix.af) {
366 			case AF_INET:
367 				if (label == MPLS_LABEL_IPV6NULL) {
368 					session_shutdown(nbr, S_BAD_TLV_VAL,
369 					    msg.id, msg.type);
370 					goto err;
371 				}
372 				if (!nbr->v4_enabled)
373 					goto next;
374 				break;
375 			case AF_INET6:
376 				if (label == MPLS_LABEL_IPV4NULL) {
377 					session_shutdown(nbr, S_BAD_TLV_VAL,
378 					    msg.id, msg.type);
379 					goto err;
380 				}
381 				if (!nbr->v6_enabled)
382 					goto next;
383 				break;
384 			default:
385 				fatalx("recv_labelmessage: unknown af");
386 			}
387 			break;
388 		case MAP_TYPE_PWID:
389 			if (label <= MPLS_LABEL_RESERVED_MAX) {
390 				session_shutdown(nbr, S_BAD_TLV_VAL, msg.id,
391 				    msg.type);
392 				goto err;
393 			}
394 			if (me->map.flags & F_MAP_PW_STATUS)
395 				me->map.pw_status = pw_status;
396 			break;
397 		default:
398 			break;
399 		}
400 		me->map.label = label;
401 		if (me->map.flags & F_MAP_REQ_ID)
402 			me->map.requestid = reqid;
403 
404 		log_msg_mapping(0, type, nbr, &me->map);
405 
406 		switch (type) {
407 		case MSG_TYPE_LABELMAPPING:
408 			imsg_type = IMSG_LABEL_MAPPING;
409 			break;
410 		case MSG_TYPE_LABELREQUEST:
411 			imsg_type = IMSG_LABEL_REQUEST;
412 			break;
413 		case MSG_TYPE_LABELWITHDRAW:
414 			imsg_type = IMSG_LABEL_WITHDRAW;
415 			break;
416 		case MSG_TYPE_LABELRELEASE:
417 			imsg_type = IMSG_LABEL_RELEASE;
418 			break;
419 		case MSG_TYPE_LABELABORTREQ:
420 			imsg_type = IMSG_LABEL_ABORT;
421 			break;
422 		default:
423 			break;
424 		}
425 
426 		ldpe_imsg_compose_lde(imsg_type, nbr->peerid, 0, &me->map,
427 		    sizeof(struct map));
428 
429  next:
430 		TAILQ_REMOVE(&mh, me, entry);
431 		free(me);
432 	}
433 
434 	return (0);
435 
436  err:
437 	mapping_list_clr(&mh);
438 
439 	return (-1);
440 }
441 
442 /* Other TLV related functions */
443 static int
444 gen_label_tlv(struct ibuf *buf, uint32_t label)
445 {
446 	struct label_tlv	lt;
447 
448 	lt.type = htons(TLV_TYPE_GENERICLABEL);
449 	lt.length = htons(LABEL_TLV_LEN);
450 	lt.label = htonl(label);
451 
452 	return (ibuf_add(buf, &lt, sizeof(lt)));
453 }
454 
455 static int
456 tlv_decode_label(struct nbr *nbr, struct ldp_msg *msg, char *buf,
457     uint16_t len, uint32_t *label)
458 {
459 	struct label_tlv lt;
460 
461 	if (len < sizeof(lt)) {
462 		session_shutdown(nbr, S_BAD_TLV_LEN, msg->id, msg->type);
463 		return (-1);
464 	}
465 	memcpy(&lt, buf, sizeof(lt));
466 
467 	if (!(ntohs(lt.type) & TLV_TYPE_GENERICLABEL)) {
468 		send_notification(nbr->tcp, S_MISS_MSG, msg->id, msg->type);
469 		return (-1);
470 	}
471 
472 	switch (htons(lt.type)) {
473 	case TLV_TYPE_GENERICLABEL:
474 		if (ntohs(lt.length) != sizeof(lt) - TLV_HDR_SIZE) {
475 			session_shutdown(nbr, S_BAD_TLV_LEN, msg->id,
476 			    msg->type);
477 			return (-1);
478 		}
479 
480 		*label = ntohl(lt.label);
481 		if (*label > MPLS_LABEL_MAX ||
482 		    (*label <= MPLS_LABEL_RESERVED_MAX &&
483 		     *label != MPLS_LABEL_IPV4NULL &&
484 		     *label != MPLS_LABEL_IPV6NULL &&
485 		     *label != MPLS_LABEL_IMPLNULL)) {
486 			session_shutdown(nbr, S_BAD_TLV_VAL, msg->id,
487 			    msg->type);
488 			return (-1);
489 		}
490 		break;
491 	case TLV_TYPE_ATMLABEL:
492 	case TLV_TYPE_FRLABEL:
493 	default:
494 		/* unsupported */
495 		session_shutdown(nbr, S_BAD_TLV_VAL, msg->id, msg->type);
496 		return (-1);
497 	}
498 
499 	return (sizeof(lt));
500 }
501 
502 static int
503 gen_reqid_tlv(struct ibuf *buf, uint32_t reqid)
504 {
505 	struct reqid_tlv	rt;
506 
507 	rt.type = htons(TLV_TYPE_LABELREQUEST);
508 	rt.length = htons(REQID_TLV_LEN);
509 	rt.reqid = htonl(reqid);
510 
511 	return (ibuf_add(buf, &rt, sizeof(rt)));
512 }
513 
514 int
515 gen_pw_status_tlv(struct ibuf *buf, uint32_t status)
516 {
517 	struct pw_status_tlv	st;
518 
519 	st.type = htons(TLV_TYPE_PW_STATUS);
520 	st.length = htons(PW_STATUS_TLV_LEN);
521 	st.value = htonl(status);
522 
523 	return (ibuf_add(buf, &st, sizeof(st)));
524 }
525 
526 uint16_t
527 len_fec_tlv(struct map *map)
528 {
529 	uint16_t	 len = TLV_HDR_SIZE;
530 
531 	switch (map->type) {
532 	case MAP_TYPE_WILDCARD:
533 		len += FEC_ELM_WCARD_LEN;
534 		break;
535 	case MAP_TYPE_PREFIX:
536 		len += FEC_ELM_PREFIX_MIN_LEN +
537 		    PREFIX_SIZE(map->fec.prefix.prefixlen);
538 		break;
539 	case MAP_TYPE_PWID:
540 		len += FEC_PWID_ELM_MIN_LEN;
541 		if (map->flags & F_MAP_PW_ID)
542 			len += PW_STATUS_TLV_LEN;
543 		if (map->flags & F_MAP_PW_IFMTU)
544 			len += FEC_SUBTLV_IFMTU_SIZE;
545     		if (map->flags & F_MAP_PW_STATUS)
546 			len += PW_STATUS_TLV_SIZE;
547 		break;
548 	case MAP_TYPE_TYPED_WCARD:
549 		len += FEC_ELM_TWCARD_MIN_LEN;
550 		switch (map->fec.twcard.type) {
551 		case MAP_TYPE_PREFIX:
552 		case MAP_TYPE_PWID:
553 			len += sizeof(uint16_t);
554 			break;
555 		default:
556 			fatalx("len_fec_tlv: unexpected fec type");
557 		}
558 		break;
559 	default:
560 		fatalx("len_fec_tlv: unexpected fec type");
561 	}
562 
563 	return (len);
564 }
565 
566 int
567 gen_fec_tlv(struct ibuf *buf, struct map *map)
568 {
569 	struct tlv	ft;
570 	uint16_t	family, len, pw_type, ifmtu;
571 	uint8_t		pw_len = 0, twcard_len;
572 	uint32_t	group_id, pwid;
573 	int		err = 0;
574 
575 	ft.type = htons(TLV_TYPE_FEC);
576 
577 	switch (map->type) {
578 	case MAP_TYPE_WILDCARD:
579 		ft.length = htons(sizeof(uint8_t));
580 		err |= ibuf_add(buf, &ft, sizeof(ft));
581 		err |= ibuf_add(buf, &map->type, sizeof(map->type));
582 		break;
583 	case MAP_TYPE_PREFIX:
584 		len = PREFIX_SIZE(map->fec.prefix.prefixlen);
585 		ft.length = htons(sizeof(map->type) + sizeof(family) +
586 		    sizeof(map->fec.prefix.prefixlen) + len);
587 		err |= ibuf_add(buf, &ft, sizeof(ft));
588 		err |= ibuf_add(buf, &map->type, sizeof(map->type));
589 		switch (map->fec.prefix.af) {
590 		case AF_INET:
591 			family = htons(AF_IPV4);
592 			break;
593 		case AF_INET6:
594 			family = htons(AF_IPV6);
595 			break;
596 		default:
597 			fatalx("gen_fec_tlv: unknown af");
598 			break;
599 		}
600 		err |= ibuf_add(buf, &family, sizeof(family));
601 		err |= ibuf_add(buf, &map->fec.prefix.prefixlen,
602 		    sizeof(map->fec.prefix.prefixlen));
603 		if (len)
604 			err |= ibuf_add(buf, &map->fec.prefix.prefix, len);
605 		break;
606 	case MAP_TYPE_PWID:
607 		if (map->flags & F_MAP_PW_ID)
608 			pw_len += FEC_PWID_SIZE;
609 		if (map->flags & F_MAP_PW_IFMTU)
610 			pw_len += FEC_SUBTLV_IFMTU_SIZE;
611 
612 		len = FEC_PWID_ELM_MIN_LEN + pw_len;
613 
614 		ft.length = htons(len);
615 		err |= ibuf_add(buf, &ft, sizeof(ft));
616 
617 		err |= ibuf_add(buf, &map->type, sizeof(uint8_t));
618 		pw_type = map->fec.pwid.type;
619 		if (map->flags & F_MAP_PW_CWORD)
620 			pw_type |= CONTROL_WORD_FLAG;
621 		pw_type = htons(pw_type);
622 		err |= ibuf_add(buf, &pw_type, sizeof(uint16_t));
623 		err |= ibuf_add(buf, &pw_len, sizeof(uint8_t));
624 		group_id = htonl(map->fec.pwid.group_id);
625 		err |= ibuf_add(buf, &group_id, sizeof(uint32_t));
626 		if (map->flags & F_MAP_PW_ID) {
627 			pwid = htonl(map->fec.pwid.pwid);
628 			err |= ibuf_add(buf, &pwid, sizeof(uint32_t));
629 		}
630 		if (map->flags & F_MAP_PW_IFMTU) {
631 			struct subtlv 	stlv;
632 
633 			stlv.type = SUBTLV_IFMTU;
634 			stlv.length = FEC_SUBTLV_IFMTU_SIZE;
635 			err |= ibuf_add(buf, &stlv, sizeof(uint16_t));
636 
637 			ifmtu = htons(map->fec.pwid.ifmtu);
638 			err |= ibuf_add(buf, &ifmtu, sizeof(uint16_t));
639 		}
640 		break;
641 	case MAP_TYPE_TYPED_WCARD:
642 		len = FEC_ELM_TWCARD_MIN_LEN;
643 		switch (map->fec.twcard.type) {
644 		case MAP_TYPE_PREFIX:
645 		case MAP_TYPE_PWID:
646 			len += sizeof(uint16_t);
647 			break;
648 		default:
649 			fatalx("gen_fec_tlv: unexpected fec type");
650 		}
651 		ft.length = htons(len);
652 		err |= ibuf_add(buf, &ft, sizeof(ft));
653 		err |= ibuf_add(buf, &map->type, sizeof(uint8_t));
654 		err |= ibuf_add(buf, &map->fec.twcard.type, sizeof(uint8_t));
655 
656 		switch (map->fec.twcard.type) {
657 		case MAP_TYPE_PREFIX:
658 			twcard_len = sizeof(uint16_t);
659 			err |= ibuf_add(buf, &twcard_len, sizeof(uint8_t));
660 
661 			switch (map->fec.twcard.u.prefix_af) {
662 			case AF_INET:
663 				family = htons(AF_IPV4);
664 				break;
665 			case AF_INET6:
666 				family = htons(AF_IPV6);
667 				break;
668 			default:
669 				fatalx("gen_fec_tlv: unknown af");
670 				break;
671 			}
672 
673 			err |= ibuf_add(buf, &family, sizeof(uint16_t));
674 			break;
675 		case MAP_TYPE_PWID:
676 			twcard_len = sizeof(uint16_t);
677 			err |= ibuf_add(buf, &twcard_len, sizeof(uint8_t));
678 			pw_type = htons(map->fec.twcard.u.pw_type);
679 			err |= ibuf_add(buf, &pw_type, sizeof(uint16_t));
680 			break;
681 		default:
682 			fatalx("gen_fec_tlv: unexpected fec type");
683 		}
684 		break;
685 	default:
686 		break;
687 	}
688 
689 	return (err);
690 }
691 
692 int
693 tlv_decode_fec_elm(struct nbr *nbr, struct ldp_msg *msg, char *buf,
694     uint16_t len, struct map *map)
695 {
696 	uint16_t	off = 0;
697 	uint8_t		pw_len, twcard_len;
698 
699 	map->type = *buf;
700 	off += sizeof(uint8_t);
701 
702 	switch (map->type) {
703 	case MAP_TYPE_WILDCARD:
704 		if (len == FEC_ELM_WCARD_LEN)
705 			return (off);
706 		else {
707 			session_shutdown(nbr, S_BAD_TLV_VAL, msg->id,
708 			    msg->type);
709 			return (-1);
710 		}
711 		break;
712 	case MAP_TYPE_PREFIX:
713 		if (len < FEC_ELM_PREFIX_MIN_LEN) {
714 			session_shutdown(nbr, S_BAD_TLV_LEN, msg->id,
715 			    msg->type);
716 			return (-1);
717 		}
718 
719 		/* Address Family */
720 		memcpy(&map->fec.prefix.af, buf + off,
721 		    sizeof(map->fec.prefix.af));
722 		off += sizeof(map->fec.prefix.af);
723 		map->fec.prefix.af = ntohs(map->fec.prefix.af);
724 		switch (map->fec.prefix.af) {
725 		case AF_IPV4:
726 			map->fec.prefix.af = AF_INET;
727 			break;
728 		case AF_IPV6:
729 			map->fec.prefix.af = AF_INET6;
730 			break;
731 		default:
732 			send_notification(nbr->tcp, S_UNSUP_ADDR, msg->id,
733 			    msg->type);
734 			return (-1);
735 		}
736 
737 		/* Prefix Length */
738 		map->fec.prefix.prefixlen = buf[off];
739 		off += sizeof(uint8_t);
740 		if (len < off + PREFIX_SIZE(map->fec.prefix.prefixlen)) {
741 			session_shutdown(nbr, S_BAD_TLV_LEN, msg->id,
742 			    msg->type);
743 			return (-1);
744 		}
745 
746 		/* Prefix */
747 		memset(&map->fec.prefix.prefix, 0,
748 		    sizeof(map->fec.prefix.prefix));
749 		memcpy(&map->fec.prefix.prefix, buf + off,
750 		    PREFIX_SIZE(map->fec.prefix.prefixlen));
751 
752 		/* Just in case... */
753 		ldp_applymask(map->fec.prefix.af, &map->fec.prefix.prefix,
754 		    &map->fec.prefix.prefix, map->fec.prefix.prefixlen);
755 
756 		return (off + PREFIX_SIZE(map->fec.prefix.prefixlen));
757 	case MAP_TYPE_PWID:
758 		if (len < FEC_PWID_ELM_MIN_LEN) {
759 			session_shutdown(nbr, S_BAD_TLV_LEN, msg->id,
760 			    msg->type);
761 			return (-1);
762 		}
763 
764 		/* PW type */
765 		memcpy(&map->fec.pwid.type, buf + off, sizeof(uint16_t));
766 		map->fec.pwid.type = ntohs(map->fec.pwid.type);
767 		if (map->fec.pwid.type & CONTROL_WORD_FLAG) {
768 			map->flags |= F_MAP_PW_CWORD;
769 			map->fec.pwid.type &= ~CONTROL_WORD_FLAG;
770 		}
771 		off += sizeof(uint16_t);
772 
773 		/* PW info Length */
774 		pw_len = buf[off];
775 		off += sizeof(uint8_t);
776 
777 		if (len != FEC_PWID_ELM_MIN_LEN + pw_len) {
778 			session_shutdown(nbr, S_BAD_TLV_LEN, msg->id,
779 			    msg->type);
780 			return (-1);
781 		}
782 
783 		/* Group ID */
784 		memcpy(&map->fec.pwid.group_id, buf + off, sizeof(uint32_t));
785 		map->fec.pwid.group_id = ntohl(map->fec.pwid.group_id);
786 		off += sizeof(uint32_t);
787 
788 		/* PW ID */
789 		if (pw_len == 0)
790 			return (off);
791 
792 		if (pw_len < sizeof(uint32_t)) {
793 			session_shutdown(nbr, S_BAD_TLV_LEN, msg->id,
794 			    msg->type);
795 			return (-1);
796 		}
797 
798 		memcpy(&map->fec.pwid.pwid, buf + off, sizeof(uint32_t));
799 		map->fec.pwid.pwid = ntohl(map->fec.pwid.pwid);
800 		map->flags |= F_MAP_PW_ID;
801 		off += sizeof(uint32_t);
802 		pw_len -= sizeof(uint32_t);
803 
804 		/* Optional Interface Parameter Sub-TLVs */
805 		while (pw_len > 0) {
806 			struct subtlv 	stlv;
807 
808 			if (pw_len < sizeof(stlv)) {
809 				session_shutdown(nbr, S_BAD_TLV_LEN, msg->id,
810 				    msg->type);
811 				return (-1);
812 			}
813 
814 			memcpy(&stlv, buf + off, sizeof(stlv));
815 			if (stlv.length > pw_len) {
816 				session_shutdown(nbr, S_BAD_TLV_LEN, msg->id,
817 				    msg->type);
818 				return (-1);
819 			}
820 
821 			switch (stlv.type) {
822 			case SUBTLV_IFMTU:
823 				if (stlv.length != FEC_SUBTLV_IFMTU_SIZE) {
824 					session_shutdown(nbr, S_BAD_TLV_LEN,
825 					    msg->id, msg->type);
826 					return (-1);
827 				}
828 				memcpy(&map->fec.pwid.ifmtu, buf + off +
829 				    SUBTLV_HDR_SIZE, sizeof(uint16_t));
830 				map->fec.pwid.ifmtu = ntohs(map->fec.pwid.ifmtu);
831 				map->flags |= F_MAP_PW_IFMTU;
832 				break;
833 			default:
834 				/* ignore */
835 				break;
836 			}
837 			off += stlv.length;
838 			pw_len -= stlv.length;
839 		}
840 
841 		return (off);
842 	case MAP_TYPE_TYPED_WCARD:
843 		if (len < FEC_ELM_TWCARD_MIN_LEN) {
844 			session_shutdown(nbr, S_BAD_TLV_LEN, msg->id,
845 			    msg->type);
846 			return (-1);
847 		}
848 
849 		memcpy(&map->fec.twcard.type, buf + off, sizeof(uint8_t));
850 		off += sizeof(uint8_t);
851 		memcpy(&twcard_len, buf + off, sizeof(uint8_t));
852 		off += sizeof(uint8_t);
853 		if (len != FEC_ELM_TWCARD_MIN_LEN + twcard_len) {
854 			session_shutdown(nbr, S_BAD_TLV_LEN, msg->id,
855 			    msg->type);
856 			return (-1);
857 		}
858 
859 		switch (map->fec.twcard.type) {
860 		case MAP_TYPE_PREFIX:
861 			if (twcard_len != sizeof(uint16_t)) {
862 				session_shutdown(nbr, S_BAD_TLV_LEN, msg->id,
863 				    msg->type);
864 				return (-1);
865 			}
866 
867 			memcpy(&map->fec.twcard.u.prefix_af, buf + off,
868 			    sizeof(uint16_t));
869 			map->fec.twcard.u.prefix_af =
870 			    ntohs(map->fec.twcard.u.prefix_af);
871 			off += sizeof(uint16_t);
872 
873 			switch (map->fec.twcard.u.prefix_af) {
874 			case AF_IPV4:
875 				map->fec.twcard.u.prefix_af = AF_INET;
876 				break;
877 			case AF_IPV6:
878 				map->fec.twcard.u.prefix_af = AF_INET6;
879 				break;
880 			default:
881 				session_shutdown(nbr, S_BAD_TLV_VAL, msg->id,
882 				    msg->type);
883 				return (-1);
884 			}
885 			break;
886 		case MAP_TYPE_PWID:
887 			if (twcard_len != sizeof(uint16_t)) {
888 				session_shutdown(nbr, S_BAD_TLV_LEN, msg->id,
889 				    msg->type);
890 				return (-1);
891 			}
892 
893 			memcpy(&map->fec.twcard.u.pw_type, buf + off,
894 			    sizeof(uint16_t));
895 			map->fec.twcard.u.pw_type =
896 			    ntohs(map->fec.twcard.u.pw_type);
897 			/* ignore the reserved bit as per RFC 6667 */
898 			map->fec.twcard.u.pw_type &= ~PW_TWCARD_RESERVED_BIT;
899 			off += sizeof(uint16_t);
900 			break;
901 		default:
902 			send_notification(nbr->tcp, S_UNKNOWN_FEC, msg->id,
903 			    msg->type);
904 			return (-1);
905 		}
906 
907 		return (off);
908 	default:
909 		send_notification(nbr->tcp, S_UNKNOWN_FEC, msg->id, msg->type);
910 		break;
911 	}
912 
913 	return (-1);
914 }
915 
916 static void
917 log_msg_mapping(int out, uint16_t msg_type, struct nbr *nbr, struct map *map)
918 {
919 	log_debug("msg-%s: %s: lsr-id %s, fec %s, label %s",
920 	    (out) ? "out" : "in", msg_name(msg_type), inet_ntoa(nbr->id),
921 	    log_map(map), log_label(map->label));
922 }
923