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