xref: /openbsd/usr.sbin/ldpd/labelmapping.c (revision 5411bbb6)
1 /*	$OpenBSD: labelmapping.c,v 1.45 2016/05/23 19:11:42 renato Exp $ */
2 
3 /*
4  * Copyright (c) 2009 Michele Marchetto <michele@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/socket.h>
21 #include <arpa/inet.h>
22 #include <netmpls/mpls.h>
23 #include <limits.h>
24 #include <stdlib.h>
25 #include <string.h>
26 
27 #include "ldpd.h"
28 #include "ldpe.h"
29 #include "log.h"
30 
31 static void	 enqueue_pdu(struct nbr *, struct ibuf *, uint16_t);
32 static void	 gen_label_tlv(struct ibuf *, uint32_t);
33 static int	 tlv_decode_label(struct nbr *, struct ldp_msg *, char *,
34 		    uint16_t, uint32_t *);
35 static void	 gen_reqid_tlv(struct ibuf *, uint32_t);
36 
37 static void
38 enqueue_pdu(struct nbr *nbr, struct ibuf *buf, uint16_t size)
39 {
40 	struct ldp_hdr		*ldp_hdr;
41 
42 	ldp_hdr = ibuf_seek(buf, 0, sizeof(struct ldp_hdr));
43 	ldp_hdr->length = htons(size);
44 	evbuf_enqueue(&nbr->tcp->wbuf, buf);
45 }
46 
47 /* Generic function that handles all Label Message types */
48 void
49 send_labelmessage(struct nbr *nbr, uint16_t type, struct mapping_head *mh)
50 {
51 	struct ibuf		*buf = NULL;
52 	struct mapping_entry	*me;
53 	uint16_t		 msg_size, size = 0;
54 	int			 first = 1;
55 
56 	/* nothing to send */
57 	if (TAILQ_EMPTY(mh))
58 		return;
59 
60 	while ((me = TAILQ_FIRST(mh)) != NULL) {
61 		/* generate pdu */
62 		if (first) {
63 			if ((buf = ibuf_open(nbr->max_pdu_len +
64 			    LDP_HDR_DEAD_LEN)) == NULL)
65 				fatal(__func__);
66 
67 			/* real size will be set up later */
68 			gen_ldp_hdr(buf, 0);
69 
70 			size = LDP_HDR_PDU_LEN;
71 			first = 0;
72 		}
73 
74 		/* calculate size */
75 		msg_size = LDP_MSG_SIZE + TLV_HDR_LEN;
76 
77 		switch (me->map.type) {
78 		case MAP_TYPE_WILDCARD:
79 			msg_size += FEC_ELM_WCARD_LEN;
80 			break;
81 		case MAP_TYPE_PREFIX:
82 			msg_size += FEC_ELM_PREFIX_MIN_LEN +
83 			    PREFIX_SIZE(me->map.fec.prefix.prefixlen);
84 			break;
85 		case MAP_TYPE_PWID:
86 			msg_size += FEC_PWID_ELM_MIN_LEN;
87 
88 			if (me->map.flags & F_MAP_PW_ID)
89 				msg_size += sizeof(uint32_t);
90 			if (me->map.flags & F_MAP_PW_IFMTU)
91 				msg_size += FEC_SUBTLV_IFMTU_LEN;
92 	    		if (me->map.flags & F_MAP_PW_STATUS)
93 				msg_size += PW_STATUS_TLV_LEN;
94 			break;
95 		}
96 
97 		if (me->map.label != NO_LABEL)
98 			msg_size += LABEL_TLV_LEN;
99 		if (me->map.flags & F_MAP_REQ_ID)
100 			msg_size += REQID_TLV_LEN;
101 
102 		/* maximum pdu length exceeded, we need a new ldp pdu */
103 		if (size + msg_size > nbr->max_pdu_len) {
104 			enqueue_pdu(nbr, buf, size);
105 			first = 1;
106 			continue;
107 		}
108 
109 		size += msg_size;
110 
111 		/* append message and tlvs */
112 		gen_msg_hdr(buf, type, msg_size);
113 		gen_fec_tlv(buf, &me->map);
114 		if (me->map.label != NO_LABEL)
115 			gen_label_tlv(buf, me->map.label);
116 		if (me->map.flags & F_MAP_REQ_ID)
117 			gen_reqid_tlv(buf, me->map.requestid);
118 	    	if (me->map.flags & F_MAP_PW_STATUS)
119 			gen_pw_status_tlv(buf, me->map.pw_status);
120 
121 		TAILQ_REMOVE(mh, me, entry);
122 		free(me);
123 	}
124 
125 	enqueue_pdu(nbr, buf, size);
126 
127 	nbr_fsm(nbr, NBR_EVT_PDU_SENT);
128 }
129 
130 /* Generic function that handles all Label Message types */
131 int
132 recv_labelmessage(struct nbr *nbr, char *buf, uint16_t len, uint16_t type)
133 {
134 	struct ldp_msg		 lm;
135 	struct tlv		 ft;
136 	uint32_t		 label = NO_LABEL, reqid = 0;
137 	uint32_t		 pw_status = 0;
138 	uint8_t			 flags = 0;
139 	int			 feclen, lbllen, tlen;
140 	struct mapping_entry	*me;
141 	struct mapping_head	 mh;
142 	struct map		 map;
143 
144 	memcpy(&lm, buf, sizeof(lm));
145 	buf += LDP_MSG_SIZE;
146 	len -= LDP_MSG_SIZE;
147 
148 	/* FEC TLV */
149 	if (len < sizeof(ft)) {
150 		session_shutdown(nbr, S_BAD_TLV_LEN, lm.msgid, lm.type);
151 		return (-1);
152 	}
153 
154 	memcpy(&ft, buf, sizeof(ft));
155 	if (ntohs(ft.type) != TLV_TYPE_FEC) {
156 		send_notification_nbr(nbr, S_MISS_MSG, lm.msgid, lm.type);
157 		return (-1);
158 	}
159 	feclen = ntohs(ft.length);
160 
161 	if (feclen > len - TLV_HDR_LEN) {
162 		session_shutdown(nbr, S_BAD_TLV_LEN, lm.msgid, lm.type);
163 		return (-1);
164 	}
165 
166 	buf += TLV_HDR_LEN;	/* just advance to the end of the fec header */
167 	len -= TLV_HDR_LEN;
168 
169 	TAILQ_INIT(&mh);
170 	do {
171 		memset(&map, 0, sizeof(map));
172 		map.messageid = lm.msgid;
173 
174 		if ((tlen = tlv_decode_fec_elm(nbr, &lm, buf, feclen,
175 		    &map)) == -1)
176 			goto err;
177 		if (map.type == MAP_TYPE_PWID &&
178 		    !(map.flags & F_MAP_PW_ID) &&
179 		    type != MSG_TYPE_LABELWITHDRAW &&
180 		    type != MSG_TYPE_LABELRELEASE) {
181 			send_notification_nbr(nbr, S_MISS_MSG, lm.msgid,
182 			    lm.type);
183 			return (-1);
184 		}
185 
186 		/*
187 		 * The Wildcard FEC Element can be used only in the
188 		 * Label Withdraw and Label Release messages.
189 		 */
190 		if (map.type == MAP_TYPE_WILDCARD) {
191 			switch (type) {
192 			case MSG_TYPE_LABELMAPPING:
193 			case MSG_TYPE_LABELREQUEST:
194 			case MSG_TYPE_LABELABORTREQ:
195 				session_shutdown(nbr, S_BAD_TLV_VAL, lm.msgid,
196 				    lm.type);
197 				goto err;
198 			default:
199 				break;
200 			}
201 		}
202 
203 		/*
204 		 * LDP supports the use of multiple FEC Elements per
205 		 * FEC for the Label Mapping message only.
206 		 */
207 		if (type != MSG_TYPE_LABELMAPPING &&
208 		    tlen != feclen) {
209 			session_shutdown(nbr, S_BAD_TLV_VAL, lm.msgid,
210 			    lm.type);
211 			goto err;
212 		}
213 
214 		mapping_list_add(&mh, &map);
215 
216 		buf += tlen;
217 		len -= tlen;
218 		feclen -= tlen;
219 	} while (feclen > 0);
220 
221 	/* Mandatory Label TLV */
222 	if (type == MSG_TYPE_LABELMAPPING) {
223 		lbllen = tlv_decode_label(nbr, &lm, buf, len, &label);
224 		if (lbllen == -1)
225 			goto err;
226 
227 		buf += lbllen;
228 		len -= lbllen;
229 	}
230 
231 	/* Optional Parameters */
232 	while (len > 0) {
233 		struct tlv 	tlv;
234 		uint32_t	reqbuf, labelbuf, statusbuf;
235 
236 		if (len < sizeof(tlv)) {
237 			session_shutdown(nbr, S_BAD_TLV_LEN, lm.msgid,
238 			    lm.type);
239 			goto err;
240 		}
241 
242 		memcpy(&tlv, buf, sizeof(tlv));
243 		if (ntohs(tlv.length) != len - TLV_HDR_LEN) {
244 			session_shutdown(nbr, S_BAD_TLV_LEN, lm.msgid,
245 			    lm.type);
246 			goto err;
247 		}
248 		buf += TLV_HDR_LEN;
249 		len -= TLV_HDR_LEN;
250 
251 		switch (ntohs(tlv.type) & ~UNKNOWN_FLAG) {
252 		case TLV_TYPE_LABELREQUEST:
253 			switch (type) {
254 			case MSG_TYPE_LABELMAPPING:
255 			case MSG_TYPE_LABELREQUEST:
256 				if (ntohs(tlv.length) != 4) {
257 					session_shutdown(nbr, S_BAD_TLV_LEN,
258 					    lm.msgid, lm.type);
259 					goto err;
260 				}
261 
262 				flags |= F_MAP_REQ_ID;
263 				memcpy(&reqbuf, buf, sizeof(reqbuf));
264 				reqid = ntohl(reqbuf);
265 				break;
266 			default:
267 				/* ignore */
268 				break;
269 			}
270 			break;
271 		case TLV_TYPE_HOPCOUNT:
272 		case TLV_TYPE_PATHVECTOR:
273 			/* TODO just ignore for now */
274 			break;
275 		case TLV_TYPE_GENERICLABEL:
276 			switch (type) {
277 			case MSG_TYPE_LABELWITHDRAW:
278 			case MSG_TYPE_LABELRELEASE:
279 				if (ntohs(tlv.length) != 4) {
280 					session_shutdown(nbr, S_BAD_TLV_LEN,
281 					    lm.msgid, lm.type);
282 					goto err;
283 				}
284 
285 				memcpy(&labelbuf, buf, sizeof(labelbuf));
286 				label = ntohl(labelbuf);
287 				break;
288 			default:
289 				/* ignore */
290 				break;
291 			}
292 			break;
293 		case TLV_TYPE_ATMLABEL:
294 		case TLV_TYPE_FRLABEL:
295 			switch (type) {
296 			case MSG_TYPE_LABELWITHDRAW:
297 			case MSG_TYPE_LABELRELEASE:
298 				/* unsupported */
299 				session_shutdown(nbr, S_BAD_TLV_VAL, lm.msgid,
300 				    lm.type);
301 				goto err;
302 				break;
303 			default:
304 				/* ignore */
305 				break;
306 			}
307 			break;
308 		case TLV_TYPE_PW_STATUS:
309 			switch (type) {
310 			case MSG_TYPE_LABELMAPPING:
311 				if (ntohs(tlv.length) != 4) {
312 					session_shutdown(nbr, S_BAD_TLV_LEN,
313 					    lm.msgid, lm.type);
314 					goto err;
315 				}
316 
317 				flags |= F_MAP_PW_STATUS;
318 				memcpy(&statusbuf, buf, sizeof(statusbuf));
319 				pw_status = ntohl(statusbuf);
320 				break;
321 			default:
322 				/* ignore */
323 				break;
324 			}
325 			break;
326 		default:
327 			if (!(ntohs(tlv.type) & UNKNOWN_FLAG)) {
328 				send_notification_nbr(nbr, S_UNKNOWN_TLV,
329 				    lm.msgid, lm.type);
330 			}
331 			/* ignore unknown tlv */
332 			break;
333 		}
334 		buf += ntohs(tlv.length);
335 		len -= ntohs(tlv.length);
336 	}
337 
338 	/* notify lde about the received message. */
339 	while ((me = TAILQ_FIRST(&mh)) != NULL) {
340 		int imsg_type = IMSG_NONE;
341 
342 		me->map.flags |= flags;
343 		switch (me->map.type) {
344 		case MAP_TYPE_PREFIX:
345 			switch (me->map.fec.prefix.af) {
346 			case AF_IPV4:
347 				if (label == MPLS_LABEL_IPV6NULL) {
348 					session_shutdown(nbr, S_BAD_TLV_VAL,
349 					    lm.msgid, lm.type);
350 					goto err;
351 				}
352 				if (!nbr->v4_enabled)
353 					goto next;
354 				break;
355 			case AF_IPV6:
356 				if (label == MPLS_LABEL_IPV4NULL) {
357 					session_shutdown(nbr, S_BAD_TLV_VAL,
358 					    lm.msgid, lm.type);
359 					goto err;
360 				}
361 				if (!nbr->v6_enabled)
362 					goto next;
363 				break;
364 			default:
365 				fatalx("recv_labelmessage: unknown af");
366 			}
367 			break;
368 		case MAP_TYPE_PWID:
369 			if (label <= MPLS_LABEL_RESERVED_MAX) {
370 				session_shutdown(nbr, S_BAD_TLV_VAL, lm.msgid,
371 				    lm.type);
372 				goto err;
373 			}
374 			if (me->map.flags & F_MAP_PW_STATUS)
375 				me->map.pw_status = pw_status;
376 			break;
377 		default:
378 			break;
379 		}
380 		me->map.label = label;
381 		if (me->map.flags & F_MAP_REQ_ID)
382 			me->map.requestid = reqid;
383 
384 		switch (type) {
385 		case MSG_TYPE_LABELMAPPING:
386 			log_debug("label mapping from nbr %s, FEC %s, "
387 			    "label %u", inet_ntoa(nbr->id),
388 			    log_map(&me->map), me->map.label);
389 			imsg_type = IMSG_LABEL_MAPPING;
390 			break;
391 		case MSG_TYPE_LABELREQUEST:
392 			log_debug("label request from nbr %s, FEC %s",
393 			    inet_ntoa(nbr->id), log_map(&me->map));
394 			imsg_type = IMSG_LABEL_REQUEST;
395 			break;
396 		case MSG_TYPE_LABELWITHDRAW:
397 			log_debug("label withdraw from nbr %s, FEC %s",
398 			    inet_ntoa(nbr->id), log_map(&me->map));
399 			imsg_type = IMSG_LABEL_WITHDRAW;
400 			break;
401 		case MSG_TYPE_LABELRELEASE:
402 			log_debug("label release from nbr %s, FEC %s",
403 			    inet_ntoa(nbr->id), log_map(&me->map));
404 			imsg_type = IMSG_LABEL_RELEASE;
405 			break;
406 		case MSG_TYPE_LABELABORTREQ:
407 			log_debug("label abort from nbr %s, FEC %s",
408 			    inet_ntoa(nbr->id), log_map(&me->map));
409 			imsg_type = IMSG_LABEL_ABORT;
410 			break;
411 		default:
412 			break;
413 		}
414 
415 		ldpe_imsg_compose_lde(imsg_type, nbr->peerid, 0, &me->map,
416 		    sizeof(struct map));
417 
418 next:
419 		TAILQ_REMOVE(&mh, me, entry);
420 		free(me);
421 	}
422 
423 	return (0);
424 
425 err:
426 	mapping_list_clr(&mh);
427 
428 	return (-1);
429 }
430 
431 /* Other TLV related functions */
432 static void
433 gen_label_tlv(struct ibuf *buf, uint32_t label)
434 {
435 	struct label_tlv	lt;
436 
437 	lt.type = htons(TLV_TYPE_GENERICLABEL);
438 	lt.length = htons(sizeof(label));
439 	lt.label = htonl(label);
440 
441 	ibuf_add(buf, &lt, sizeof(lt));
442 }
443 
444 static int
445 tlv_decode_label(struct nbr *nbr, struct ldp_msg *lm, char *buf,
446     uint16_t len, uint32_t *label)
447 {
448 	struct label_tlv lt;
449 
450 	if (len < sizeof(lt)) {
451 		session_shutdown(nbr, S_BAD_TLV_LEN, lm->msgid, lm->type);
452 		return (-1);
453 	}
454 	memcpy(&lt, buf, sizeof(lt));
455 
456 	if (!(ntohs(lt.type) & TLV_TYPE_GENERICLABEL)) {
457 		send_notification_nbr(nbr, S_MISS_MSG, lm->msgid, lm->type);
458 		return (-1);
459 	}
460 
461 	switch (htons(lt.type)) {
462 	case TLV_TYPE_GENERICLABEL:
463 		if (ntohs(lt.length) != sizeof(lt) - TLV_HDR_LEN) {
464 			session_shutdown(nbr, S_BAD_TLV_LEN, lm->msgid,
465 			    lm->type);
466 			return (-1);
467 		}
468 
469 		*label = ntohl(lt.label);
470 		if (*label > MPLS_LABEL_MAX ||
471 		    (*label <= MPLS_LABEL_RESERVED_MAX &&
472 		     *label != MPLS_LABEL_IPV4NULL &&
473 		     *label != MPLS_LABEL_IPV6NULL &&
474 		     *label != MPLS_LABEL_IMPLNULL)) {
475 			session_shutdown(nbr, S_BAD_TLV_VAL, lm->msgid,
476 			    lm->type);
477 			return (-1);
478 		}
479 		break;
480 	case TLV_TYPE_ATMLABEL:
481 	case TLV_TYPE_FRLABEL:
482 	default:
483 		/* unsupported */
484 		session_shutdown(nbr, S_BAD_TLV_VAL, lm->msgid, lm->type);
485 		return (-1);
486 	}
487 
488 	return (sizeof(lt));
489 }
490 
491 static void
492 gen_reqid_tlv(struct ibuf *buf, uint32_t reqid)
493 {
494 	struct reqid_tlv	rt;
495 
496 	rt.type = htons(TLV_TYPE_LABELREQUEST);
497 	rt.length = htons(sizeof(reqid));
498 	rt.reqid = htonl(reqid);
499 
500 	ibuf_add(buf, &rt, sizeof(rt));
501 }
502 
503 void
504 gen_pw_status_tlv(struct ibuf *buf, uint32_t status)
505 {
506 	struct pw_status_tlv	st;
507 
508 	st.type = htons(TLV_TYPE_PW_STATUS);
509 	st.length = htons(sizeof(status));
510 	st.value = htonl(status);
511 
512 	ibuf_add(buf, &st, sizeof(st));
513 }
514 
515 void
516 gen_fec_tlv(struct ibuf *buf, struct map *map)
517 {
518 	struct tlv	ft;
519 	uint16_t	family, len, pw_type, ifmtu;
520 	uint8_t		pw_len = 0;
521 	uint32_t	group_id, pwid;
522 
523 	ft.type = htons(TLV_TYPE_FEC);
524 
525 	switch (map->type) {
526 	case MAP_TYPE_WILDCARD:
527 		ft.length = htons(sizeof(uint8_t));
528 		ibuf_add(buf, &ft, sizeof(ft));
529 		ibuf_add(buf, &map->type, sizeof(map->type));
530 		break;
531 	case MAP_TYPE_PREFIX:
532 		len = PREFIX_SIZE(map->fec.prefix.prefixlen);
533 		ft.length = htons(sizeof(map->type) + sizeof(family) +
534 		    sizeof(map->fec.prefix.prefixlen) + len);
535 		ibuf_add(buf, &ft, sizeof(ft));
536 
537 		ibuf_add(buf, &map->type, sizeof(map->type));
538 		family = htons(map->fec.prefix.af);
539 		ibuf_add(buf, &family, sizeof(family));
540 		ibuf_add(buf, &map->fec.prefix.prefixlen,
541 		    sizeof(map->fec.prefix.prefixlen));
542 		if (len)
543 			ibuf_add(buf, &map->fec.prefix.prefix, len);
544 		break;
545 	case MAP_TYPE_PWID:
546 		if (map->flags & F_MAP_PW_ID)
547 			pw_len += sizeof(uint32_t);
548 		if (map->flags & F_MAP_PW_IFMTU)
549 			pw_len += FEC_SUBTLV_IFMTU_LEN;
550 
551 		len = FEC_PWID_ELM_MIN_LEN + pw_len;
552 
553 		ft.length = htons(len);
554 		ibuf_add(buf, &ft, sizeof(ft));
555 
556 		ibuf_add(buf, &map->type, sizeof(uint8_t));
557 		pw_type = map->fec.pwid.type;
558 		if (map->flags & F_MAP_PW_CWORD)
559 			pw_type |= CONTROL_WORD_FLAG;
560 		pw_type = htons(pw_type);
561 		ibuf_add(buf, &pw_type, sizeof(uint16_t));
562 		ibuf_add(buf, &pw_len, sizeof(uint8_t));
563 		group_id = htonl(map->fec.pwid.group_id);
564 		ibuf_add(buf, &group_id, sizeof(uint32_t));
565 		if (map->flags & F_MAP_PW_ID) {
566 			pwid = htonl(map->fec.pwid.pwid);
567 			ibuf_add(buf, &pwid, sizeof(uint32_t));
568 		}
569 		if (map->flags & F_MAP_PW_IFMTU) {
570 			struct subtlv 	stlv;
571 
572 			stlv.type = SUBTLV_IFMTU;
573 			stlv.length = FEC_SUBTLV_IFMTU_LEN;
574 			ibuf_add(buf, &stlv, sizeof(uint16_t));
575 
576 			ifmtu = htons(map->fec.pwid.ifmtu);
577 			ibuf_add(buf, &ifmtu, sizeof(uint16_t));
578 		}
579 		break;
580 	default:
581 		break;
582 	}
583 }
584 
585 int
586 tlv_decode_fec_elm(struct nbr *nbr, struct ldp_msg *lm, char *buf,
587     uint16_t len, struct map *map)
588 {
589 	uint16_t	off = 0;
590 	uint8_t		pw_len;
591 
592 	map->type = *buf;
593 	off += sizeof(uint8_t);
594 
595 	switch (map->type) {
596 	case MAP_TYPE_WILDCARD:
597 		if (len == FEC_ELM_WCARD_LEN)
598 			return (off);
599 		else {
600 			session_shutdown(nbr, S_BAD_TLV_VAL, lm->msgid,
601 			    lm->type);
602 			return (-1);
603 		}
604 		break;
605 	case MAP_TYPE_PREFIX:
606 		if (len < FEC_ELM_PREFIX_MIN_LEN) {
607 			session_shutdown(nbr, S_BAD_TLV_LEN, lm->msgid,
608 			    lm->type);
609 			return (-1);
610 		}
611 
612 		/* Address Family */
613 		memcpy(&map->fec.prefix.af, buf + off,
614 		    sizeof(map->fec.prefix.af));
615 		map->fec.prefix.af = ntohs(map->fec.prefix.af);
616 		off += sizeof(map->fec.prefix.af);
617 		if (map->fec.prefix.af != AF_IPV4 &&
618 		    map->fec.prefix.af != AF_IPV6) {
619 			send_notification_nbr(nbr, S_UNSUP_ADDR, lm->msgid,
620 			    lm->type);
621 			return (-1);
622 		}
623 
624 		/* Prefix Length */
625 		map->fec.prefix.prefixlen = buf[off];
626 		off += sizeof(uint8_t);
627 		if (len < off + PREFIX_SIZE(map->fec.prefix.prefixlen)) {
628 			session_shutdown(nbr, S_BAD_TLV_LEN, lm->msgid,
629 			    lm->type);
630 			return (-1);
631 		}
632 
633 		/* Prefix */
634 		memset(&map->fec.prefix.prefix, 0,
635 		    sizeof(map->fec.prefix.prefix));
636 		memcpy(&map->fec.prefix.prefix, buf + off,
637 		    PREFIX_SIZE(map->fec.prefix.prefixlen));
638 
639 		return (off + PREFIX_SIZE(map->fec.prefix.prefixlen));
640 	case MAP_TYPE_PWID:
641 		if (len < FEC_PWID_ELM_MIN_LEN) {
642 			session_shutdown(nbr, S_BAD_TLV_LEN, lm->msgid,
643 			    lm->type);
644 			return (-1);
645 		}
646 
647 		/* PW type */
648 		memcpy(&map->fec.pwid.type, buf + off, sizeof(uint16_t));
649 		map->fec.pwid.type = ntohs(map->fec.pwid.type);
650 		if (map->fec.pwid.type & CONTROL_WORD_FLAG) {
651 			map->flags |= F_MAP_PW_CWORD;
652 			map->fec.pwid.type &= ~CONTROL_WORD_FLAG;
653 		}
654 		off += sizeof(uint16_t);
655 
656 		/* PW info Length */
657 		pw_len = buf[off];
658 		off += sizeof(uint8_t);
659 
660 		if (len != FEC_PWID_ELM_MIN_LEN + pw_len) {
661 			session_shutdown(nbr, S_BAD_TLV_LEN, lm->msgid,
662 			    lm->type);
663 			return (-1);
664 		}
665 
666 		/* Group ID */
667 		memcpy(&map->fec.pwid.group_id, buf + off, sizeof(uint32_t));
668 		map->fec.pwid.group_id = ntohl(map->fec.pwid.group_id);
669 		off += sizeof(uint32_t);
670 
671 		/* PW ID */
672 		if (pw_len == 0)
673 			return (off);
674 
675 		if (pw_len < sizeof(uint32_t)) {
676 			session_shutdown(nbr, S_BAD_TLV_LEN, lm->msgid,
677 			    lm->type);
678 			return (-1);
679 		}
680 
681 		memcpy(&map->fec.pwid.pwid, buf + off, sizeof(uint32_t));
682 		map->fec.pwid.pwid = ntohl(map->fec.pwid.pwid);
683 		map->flags |= F_MAP_PW_ID;
684 		off += sizeof(uint32_t);
685 		pw_len -= sizeof(uint32_t);
686 
687 		/* Optional Interface Parameter Sub-TLVs */
688 		while (pw_len > 0) {
689 			struct subtlv 	stlv;
690 
691 			if (pw_len < sizeof(stlv)) {
692 				session_shutdown(nbr, S_BAD_TLV_LEN,
693 				    lm->msgid, lm->type);
694 				return (-1);
695 			}
696 
697 			memcpy(&stlv, buf + off, sizeof(stlv));
698 			off += SUBTLV_HDR_LEN;
699 			pw_len -= SUBTLV_HDR_LEN;
700 
701 			switch (stlv.type) {
702 			case SUBTLV_IFMTU:
703 				if (stlv.length != FEC_SUBTLV_IFMTU_LEN) {
704 					session_shutdown(nbr, S_BAD_TLV_LEN,
705 					    lm->msgid, lm->type);
706 					return (-1);
707 				}
708 				memcpy(&map->fec.pwid.ifmtu, buf + off,
709 				    sizeof(uint16_t));
710 				map->fec.pwid.ifmtu = ntohs(map->fec.pwid.ifmtu);
711 				map->flags |= F_MAP_PW_IFMTU;
712 				break;
713 			default:
714 				/* ignore */
715 				break;
716 			}
717 			off += stlv.length - SUBTLV_HDR_LEN;
718 			pw_len -= stlv.length - SUBTLV_HDR_LEN;
719 		}
720 
721 		return (off);
722 	default:
723 		send_notification_nbr(nbr, S_UNKNOWN_FEC, lm->msgid, lm->type);
724 		break;
725 	}
726 
727 	return (-1);
728 }
729