xref: /openbsd/usr.sbin/ldpd/labelmapping.c (revision c6d1c462)
1 /*	$OpenBSD: labelmapping.c,v 1.50 2016/06/11 02:01:46 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_UNKNOWN_FEC, 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 		uint16_t	tlv_len;
236 		uint32_t	reqbuf, labelbuf, statusbuf;
237 
238 		if (len < sizeof(tlv)) {
239 			session_shutdown(nbr, S_BAD_TLV_LEN, lm.msgid,
240 			    lm.type);
241 			goto err;
242 		}
243 
244 		memcpy(&tlv, buf, TLV_HDR_LEN);
245 		buf += TLV_HDR_LEN;
246 		len -= TLV_HDR_LEN;
247 		tlv_len = ntohs(tlv.length);
248 
249 		switch (ntohs(tlv.type)) {
250 		case TLV_TYPE_LABELREQUEST:
251 			switch (type) {
252 			case MSG_TYPE_LABELMAPPING:
253 			case MSG_TYPE_LABELREQUEST:
254 				if (tlv_len != 4) {
255 					session_shutdown(nbr, S_BAD_TLV_LEN,
256 					    lm.msgid, lm.type);
257 					goto err;
258 				}
259 
260 				flags |= F_MAP_REQ_ID;
261 				memcpy(&reqbuf, buf, sizeof(reqbuf));
262 				reqid = ntohl(reqbuf);
263 				break;
264 			default:
265 				/* ignore */
266 				break;
267 			}
268 			break;
269 		case TLV_TYPE_HOPCOUNT:
270 		case TLV_TYPE_PATHVECTOR:
271 			/* TODO just ignore for now */
272 			break;
273 		case TLV_TYPE_GENERICLABEL:
274 			switch (type) {
275 			case MSG_TYPE_LABELWITHDRAW:
276 			case MSG_TYPE_LABELRELEASE:
277 				if (tlv_len != 4) {
278 					session_shutdown(nbr, S_BAD_TLV_LEN,
279 					    lm.msgid, lm.type);
280 					goto err;
281 				}
282 
283 				memcpy(&labelbuf, buf, sizeof(labelbuf));
284 				label = ntohl(labelbuf);
285 				break;
286 			default:
287 				/* ignore */
288 				break;
289 			}
290 			break;
291 		case TLV_TYPE_ATMLABEL:
292 		case TLV_TYPE_FRLABEL:
293 			switch (type) {
294 			case MSG_TYPE_LABELWITHDRAW:
295 			case MSG_TYPE_LABELRELEASE:
296 				/* unsupported */
297 				session_shutdown(nbr, S_BAD_TLV_VAL, lm.msgid,
298 				    lm.type);
299 				goto err;
300 				break;
301 			default:
302 				/* ignore */
303 				break;
304 			}
305 			break;
306 		case TLV_TYPE_PW_STATUS:
307 			switch (type) {
308 			case MSG_TYPE_LABELMAPPING:
309 				if (tlv_len != 4) {
310 					session_shutdown(nbr, S_BAD_TLV_LEN,
311 					    lm.msgid, lm.type);
312 					goto err;
313 				}
314 
315 				flags |= F_MAP_PW_STATUS;
316 				memcpy(&statusbuf, buf, sizeof(statusbuf));
317 				pw_status = ntohl(statusbuf);
318 				break;
319 			default:
320 				/* ignore */
321 				break;
322 			}
323 			break;
324 		default:
325 			if (!(ntohs(tlv.type) & UNKNOWN_FLAG)) {
326 				send_notification_nbr(nbr, S_UNKNOWN_TLV,
327 				    lm.msgid, lm.type);
328 			}
329 			/* ignore unknown tlv */
330 			break;
331 		}
332 		buf += tlv_len;
333 		len -= tlv_len;
334 	}
335 
336 	/* notify lde about the received message. */
337 	while ((me = TAILQ_FIRST(&mh)) != NULL) {
338 		int imsg_type = IMSG_NONE;
339 
340 		me->map.flags |= flags;
341 		switch (me->map.type) {
342 		case MAP_TYPE_PREFIX:
343 			switch (me->map.fec.prefix.af) {
344 			case AF_IPV4:
345 				if (label == MPLS_LABEL_IPV6NULL) {
346 					session_shutdown(nbr, S_BAD_TLV_VAL,
347 					    lm.msgid, lm.type);
348 					goto err;
349 				}
350 				if (!nbr->v4_enabled)
351 					goto next;
352 				break;
353 			case AF_IPV6:
354 				if (label == MPLS_LABEL_IPV4NULL) {
355 					session_shutdown(nbr, S_BAD_TLV_VAL,
356 					    lm.msgid, lm.type);
357 					goto err;
358 				}
359 				if (!nbr->v6_enabled)
360 					goto next;
361 				break;
362 			default:
363 				fatalx("recv_labelmessage: unknown af");
364 			}
365 			break;
366 		case MAP_TYPE_PWID:
367 			if (label <= MPLS_LABEL_RESERVED_MAX) {
368 				session_shutdown(nbr, S_BAD_TLV_VAL, lm.msgid,
369 				    lm.type);
370 				goto err;
371 			}
372 			if (me->map.flags & F_MAP_PW_STATUS)
373 				me->map.pw_status = pw_status;
374 			break;
375 		default:
376 			break;
377 		}
378 		me->map.label = label;
379 		if (me->map.flags & F_MAP_REQ_ID)
380 			me->map.requestid = reqid;
381 
382 		switch (type) {
383 		case MSG_TYPE_LABELMAPPING:
384 			log_debug("label mapping from nbr %s, FEC %s, "
385 			    "label %u", inet_ntoa(nbr->id),
386 			    log_map(&me->map), me->map.label);
387 			imsg_type = IMSG_LABEL_MAPPING;
388 			break;
389 		case MSG_TYPE_LABELREQUEST:
390 			log_debug("label request from nbr %s, FEC %s",
391 			    inet_ntoa(nbr->id), log_map(&me->map));
392 			imsg_type = IMSG_LABEL_REQUEST;
393 			break;
394 		case MSG_TYPE_LABELWITHDRAW:
395 			log_debug("label withdraw from nbr %s, FEC %s",
396 			    inet_ntoa(nbr->id), log_map(&me->map));
397 			imsg_type = IMSG_LABEL_WITHDRAW;
398 			break;
399 		case MSG_TYPE_LABELRELEASE:
400 			log_debug("label release from nbr %s, FEC %s",
401 			    inet_ntoa(nbr->id), log_map(&me->map));
402 			imsg_type = IMSG_LABEL_RELEASE;
403 			break;
404 		case MSG_TYPE_LABELABORTREQ:
405 			log_debug("label abort from nbr %s, FEC %s",
406 			    inet_ntoa(nbr->id), log_map(&me->map));
407 			imsg_type = IMSG_LABEL_ABORT;
408 			break;
409 		default:
410 			break;
411 		}
412 
413 		ldpe_imsg_compose_lde(imsg_type, nbr->peerid, 0, &me->map,
414 		    sizeof(struct map));
415 
416 next:
417 		TAILQ_REMOVE(&mh, me, entry);
418 		free(me);
419 	}
420 
421 	return (0);
422 
423 err:
424 	mapping_list_clr(&mh);
425 
426 	return (-1);
427 }
428 
429 /* Other TLV related functions */
430 static void
431 gen_label_tlv(struct ibuf *buf, uint32_t label)
432 {
433 	struct label_tlv	lt;
434 
435 	lt.type = htons(TLV_TYPE_GENERICLABEL);
436 	lt.length = htons(sizeof(label));
437 	lt.label = htonl(label);
438 
439 	ibuf_add(buf, &lt, sizeof(lt));
440 }
441 
442 static int
443 tlv_decode_label(struct nbr *nbr, struct ldp_msg *lm, char *buf,
444     uint16_t len, uint32_t *label)
445 {
446 	struct label_tlv lt;
447 
448 	if (len < sizeof(lt)) {
449 		session_shutdown(nbr, S_BAD_TLV_LEN, lm->msgid, lm->type);
450 		return (-1);
451 	}
452 	memcpy(&lt, buf, sizeof(lt));
453 
454 	if (!(ntohs(lt.type) & TLV_TYPE_GENERICLABEL)) {
455 		send_notification_nbr(nbr, S_MISS_MSG, lm->msgid, lm->type);
456 		return (-1);
457 	}
458 
459 	switch (htons(lt.type)) {
460 	case TLV_TYPE_GENERICLABEL:
461 		if (ntohs(lt.length) != sizeof(lt) - TLV_HDR_LEN) {
462 			session_shutdown(nbr, S_BAD_TLV_LEN, lm->msgid,
463 			    lm->type);
464 			return (-1);
465 		}
466 
467 		*label = ntohl(lt.label);
468 		if (*label > MPLS_LABEL_MAX ||
469 		    (*label <= MPLS_LABEL_RESERVED_MAX &&
470 		     *label != MPLS_LABEL_IPV4NULL &&
471 		     *label != MPLS_LABEL_IPV6NULL &&
472 		     *label != MPLS_LABEL_IMPLNULL)) {
473 			session_shutdown(nbr, S_BAD_TLV_VAL, lm->msgid,
474 			    lm->type);
475 			return (-1);
476 		}
477 		break;
478 	case TLV_TYPE_ATMLABEL:
479 	case TLV_TYPE_FRLABEL:
480 	default:
481 		/* unsupported */
482 		session_shutdown(nbr, S_BAD_TLV_VAL, lm->msgid, lm->type);
483 		return (-1);
484 	}
485 
486 	return (sizeof(lt));
487 }
488 
489 static void
490 gen_reqid_tlv(struct ibuf *buf, uint32_t reqid)
491 {
492 	struct reqid_tlv	rt;
493 
494 	rt.type = htons(TLV_TYPE_LABELREQUEST);
495 	rt.length = htons(sizeof(reqid));
496 	rt.reqid = htonl(reqid);
497 
498 	ibuf_add(buf, &rt, sizeof(rt));
499 }
500 
501 void
502 gen_pw_status_tlv(struct ibuf *buf, uint32_t status)
503 {
504 	struct pw_status_tlv	st;
505 
506 	st.type = htons(TLV_TYPE_PW_STATUS);
507 	st.length = htons(sizeof(status));
508 	st.value = htonl(status);
509 
510 	ibuf_add(buf, &st, sizeof(st));
511 }
512 
513 void
514 gen_fec_tlv(struct ibuf *buf, struct map *map)
515 {
516 	struct tlv	ft;
517 	uint16_t	family, len, pw_type, ifmtu;
518 	uint8_t		pw_len = 0;
519 	uint32_t	group_id, pwid;
520 
521 	ft.type = htons(TLV_TYPE_FEC);
522 
523 	switch (map->type) {
524 	case MAP_TYPE_WILDCARD:
525 		ft.length = htons(sizeof(uint8_t));
526 		ibuf_add(buf, &ft, sizeof(ft));
527 		ibuf_add(buf, &map->type, sizeof(map->type));
528 		break;
529 	case MAP_TYPE_PREFIX:
530 		len = PREFIX_SIZE(map->fec.prefix.prefixlen);
531 		ft.length = htons(sizeof(map->type) + sizeof(family) +
532 		    sizeof(map->fec.prefix.prefixlen) + len);
533 		ibuf_add(buf, &ft, sizeof(ft));
534 
535 		ibuf_add(buf, &map->type, sizeof(map->type));
536 		family = htons(map->fec.prefix.af);
537 		ibuf_add(buf, &family, sizeof(family));
538 		ibuf_add(buf, &map->fec.prefix.prefixlen,
539 		    sizeof(map->fec.prefix.prefixlen));
540 		if (len)
541 			ibuf_add(buf, &map->fec.prefix.prefix, len);
542 		break;
543 	case MAP_TYPE_PWID:
544 		if (map->flags & F_MAP_PW_ID)
545 			pw_len += sizeof(uint32_t);
546 		if (map->flags & F_MAP_PW_IFMTU)
547 			pw_len += FEC_SUBTLV_IFMTU_LEN;
548 
549 		len = FEC_PWID_ELM_MIN_LEN + pw_len;
550 
551 		ft.length = htons(len);
552 		ibuf_add(buf, &ft, sizeof(ft));
553 
554 		ibuf_add(buf, &map->type, sizeof(uint8_t));
555 		pw_type = map->fec.pwid.type;
556 		if (map->flags & F_MAP_PW_CWORD)
557 			pw_type |= CONTROL_WORD_FLAG;
558 		pw_type = htons(pw_type);
559 		ibuf_add(buf, &pw_type, sizeof(uint16_t));
560 		ibuf_add(buf, &pw_len, sizeof(uint8_t));
561 		group_id = htonl(map->fec.pwid.group_id);
562 		ibuf_add(buf, &group_id, sizeof(uint32_t));
563 		if (map->flags & F_MAP_PW_ID) {
564 			pwid = htonl(map->fec.pwid.pwid);
565 			ibuf_add(buf, &pwid, sizeof(uint32_t));
566 		}
567 		if (map->flags & F_MAP_PW_IFMTU) {
568 			struct subtlv 	stlv;
569 
570 			stlv.type = SUBTLV_IFMTU;
571 			stlv.length = FEC_SUBTLV_IFMTU_LEN;
572 			ibuf_add(buf, &stlv, sizeof(uint16_t));
573 
574 			ifmtu = htons(map->fec.pwid.ifmtu);
575 			ibuf_add(buf, &ifmtu, sizeof(uint16_t));
576 		}
577 		break;
578 	default:
579 		break;
580 	}
581 }
582 
583 int
584 tlv_decode_fec_elm(struct nbr *nbr, struct ldp_msg *lm, char *buf,
585     uint16_t len, struct map *map)
586 {
587 	uint16_t	off = 0;
588 	uint8_t		pw_len;
589 
590 	map->type = *buf;
591 	off += sizeof(uint8_t);
592 
593 	switch (map->type) {
594 	case MAP_TYPE_WILDCARD:
595 		if (len == FEC_ELM_WCARD_LEN)
596 			return (off);
597 		else {
598 			session_shutdown(nbr, S_BAD_TLV_VAL, lm->msgid,
599 			    lm->type);
600 			return (-1);
601 		}
602 		break;
603 	case MAP_TYPE_PREFIX:
604 		if (len < FEC_ELM_PREFIX_MIN_LEN) {
605 			session_shutdown(nbr, S_BAD_TLV_LEN, lm->msgid,
606 			    lm->type);
607 			return (-1);
608 		}
609 
610 		/* Address Family */
611 		memcpy(&map->fec.prefix.af, buf + off,
612 		    sizeof(map->fec.prefix.af));
613 		map->fec.prefix.af = ntohs(map->fec.prefix.af);
614 		off += sizeof(map->fec.prefix.af);
615 		if (map->fec.prefix.af != AF_IPV4 &&
616 		    map->fec.prefix.af != AF_IPV6) {
617 			send_notification_nbr(nbr, S_UNSUP_ADDR, lm->msgid,
618 			    lm->type);
619 			return (-1);
620 		}
621 
622 		/* Prefix Length */
623 		map->fec.prefix.prefixlen = buf[off];
624 		off += sizeof(uint8_t);
625 		if (len < off + PREFIX_SIZE(map->fec.prefix.prefixlen)) {
626 			session_shutdown(nbr, S_BAD_TLV_LEN, lm->msgid,
627 			    lm->type);
628 			return (-1);
629 		}
630 
631 		/* Prefix */
632 		memset(&map->fec.prefix.prefix, 0,
633 		    sizeof(map->fec.prefix.prefix));
634 		memcpy(&map->fec.prefix.prefix, buf + off,
635 		    PREFIX_SIZE(map->fec.prefix.prefixlen));
636 
637 		return (off + PREFIX_SIZE(map->fec.prefix.prefixlen));
638 	case MAP_TYPE_PWID:
639 		if (len < FEC_PWID_ELM_MIN_LEN) {
640 			session_shutdown(nbr, S_BAD_TLV_LEN, lm->msgid,
641 			    lm->type);
642 			return (-1);
643 		}
644 
645 		/* PW type */
646 		memcpy(&map->fec.pwid.type, buf + off, sizeof(uint16_t));
647 		map->fec.pwid.type = ntohs(map->fec.pwid.type);
648 		if (map->fec.pwid.type & CONTROL_WORD_FLAG) {
649 			map->flags |= F_MAP_PW_CWORD;
650 			map->fec.pwid.type &= ~CONTROL_WORD_FLAG;
651 		}
652 		off += sizeof(uint16_t);
653 
654 		/* PW info Length */
655 		pw_len = buf[off];
656 		off += sizeof(uint8_t);
657 
658 		if (len != FEC_PWID_ELM_MIN_LEN + pw_len) {
659 			session_shutdown(nbr, S_BAD_TLV_LEN, lm->msgid,
660 			    lm->type);
661 			return (-1);
662 		}
663 
664 		/* Group ID */
665 		memcpy(&map->fec.pwid.group_id, buf + off, sizeof(uint32_t));
666 		map->fec.pwid.group_id = ntohl(map->fec.pwid.group_id);
667 		off += sizeof(uint32_t);
668 
669 		/* PW ID */
670 		if (pw_len == 0)
671 			return (off);
672 
673 		if (pw_len < sizeof(uint32_t)) {
674 			session_shutdown(nbr, S_BAD_TLV_LEN, lm->msgid,
675 			    lm->type);
676 			return (-1);
677 		}
678 
679 		memcpy(&map->fec.pwid.pwid, buf + off, sizeof(uint32_t));
680 		map->fec.pwid.pwid = ntohl(map->fec.pwid.pwid);
681 		map->flags |= F_MAP_PW_ID;
682 		off += sizeof(uint32_t);
683 		pw_len -= sizeof(uint32_t);
684 
685 		/* Optional Interface Parameter Sub-TLVs */
686 		while (pw_len > 0) {
687 			struct subtlv 	stlv;
688 
689 			if (pw_len < sizeof(stlv)) {
690 				session_shutdown(nbr, S_BAD_TLV_LEN,
691 				    lm->msgid, lm->type);
692 				return (-1);
693 			}
694 
695 			memcpy(&stlv, buf + off, sizeof(stlv));
696 			off += SUBTLV_HDR_LEN;
697 			pw_len -= SUBTLV_HDR_LEN;
698 
699 			switch (stlv.type) {
700 			case SUBTLV_IFMTU:
701 				if (stlv.length != FEC_SUBTLV_IFMTU_LEN) {
702 					session_shutdown(nbr, S_BAD_TLV_LEN,
703 					    lm->msgid, lm->type);
704 					return (-1);
705 				}
706 				memcpy(&map->fec.pwid.ifmtu, buf + off,
707 				    sizeof(uint16_t));
708 				map->fec.pwid.ifmtu = ntohs(map->fec.pwid.ifmtu);
709 				map->flags |= F_MAP_PW_IFMTU;
710 				break;
711 			default:
712 				/* ignore */
713 				break;
714 			}
715 			off += stlv.length - SUBTLV_HDR_LEN;
716 			pw_len -= stlv.length - SUBTLV_HDR_LEN;
717 		}
718 
719 		return (off);
720 	default:
721 		send_notification_nbr(nbr, S_UNKNOWN_FEC, lm->msgid, lm->type);
722 		break;
723 	}
724 
725 	return (-1);
726 }
727