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