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