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