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