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