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