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