diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-09 13:16:35 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-09 13:16:35 +0000 |
commit | e2bbf175a2184bd76f6c54ccf8456babeb1a46fc (patch) | |
tree | f0b76550d6e6f500ada964a3a4ee933a45e5a6f1 /ldpd/labelmapping.c | |
parent | Initial commit. (diff) | |
download | frr-e2bbf175a2184bd76f6c54ccf8456babeb1a46fc.tar.xz frr-e2bbf175a2184bd76f6c54ccf8456babeb1a46fc.zip |
Adding upstream version 9.1.upstream/9.1
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'ldpd/labelmapping.c')
-rw-r--r-- | ldpd/labelmapping.c | 866 |
1 files changed, 866 insertions, 0 deletions
diff --git a/ldpd/labelmapping.c b/ldpd/labelmapping.c new file mode 100644 index 0000000..3c5a5d9 --- /dev/null +++ b/ldpd/labelmapping.c @@ -0,0 +1,866 @@ +// SPDX-License-Identifier: ISC +/* $OpenBSD$ */ + +/* + * Copyright (c) 2014, 2015 Renato Westphal <renato@openbsd.org> + * Copyright (c) 2009 Michele Marchetto <michele@openbsd.org> + */ + +#include <zebra.h> + +#include "ldpd.h" +#include "ldpe.h" +#include "log.h" +#include "ldp_debug.h" + +#include "mpls.h" + +static void enqueue_pdu(struct nbr *, uint16_t, struct ibuf *, uint16_t); +static int gen_label_tlv(struct ibuf *, uint32_t); +static int gen_reqid_tlv(struct ibuf *, uint32_t); +static void log_msg_mapping(int, uint16_t, struct nbr *, struct map *); + +static void +enqueue_pdu(struct nbr *nbr, uint16_t type, struct ibuf *buf, uint16_t size) +{ + struct ldp_hdr *ldp_hdr; + + ldp_hdr = ibuf_seek(buf, 0, sizeof(struct ldp_hdr)); + ldp_hdr->length = htons(size - LDP_HDR_DEAD_LEN); + evbuf_enqueue(&nbr->tcp->wbuf, buf); +} + +/* Generic function that handles all Label Message types */ +void +send_labelmessage(struct nbr *nbr, uint16_t type, struct mapping_head *mh) +{ + struct ibuf *buf = NULL; + struct mapping_entry *me; + uint16_t msg_size, size = 0; + int first = 1; + int err = 0; + + /* nothing to send */ + if (TAILQ_EMPTY(mh)) + return; + + while ((me = TAILQ_FIRST(mh)) != NULL) { + /* generate pdu */ + if (first) { + if ((buf = ibuf_open(nbr->max_pdu_len + LDP_HDR_DEAD_LEN)) == NULL) + fatal(__func__); + + /* real size will be set up later */ + SET_FLAG(err, gen_ldp_hdr(buf, 0)); + + size = LDP_HDR_SIZE; + first = 0; + } + + /* calculate size */ + msg_size = LDP_MSG_SIZE; + msg_size += len_fec_tlv(&me->map); + if (me->map.label != NO_LABEL) + msg_size += LABEL_TLV_SIZE; + if (CHECK_FLAG(me->map.flags, F_MAP_REQ_ID)) + msg_size += REQID_TLV_SIZE; + if (CHECK_FLAG(me->map.flags, F_MAP_STATUS)) + msg_size += STATUS_SIZE; + + /* maximum pdu length exceeded, we need a new ldp pdu */ + if (size + msg_size > nbr->max_pdu_len) { + enqueue_pdu(nbr, type, buf, size); + first = 1; + continue; + } + + size += msg_size; + + /* append message and tlvs */ + SET_FLAG(err, gen_msg_hdr(buf, type, msg_size)); + SET_FLAG(err, gen_fec_tlv(buf, &me->map)); + if (me->map.label != NO_LABEL) + SET_FLAG(err, gen_label_tlv(buf, me->map.label)); + if (CHECK_FLAG(me->map.flags, F_MAP_REQ_ID)) + SET_FLAG(err, gen_reqid_tlv(buf, me->map.requestid)); + if (CHECK_FLAG(me->map.flags, F_MAP_PW_STATUS)) + SET_FLAG(err, gen_pw_status_tlv(buf, me->map.pw_status)); + if (CHECK_FLAG(me->map.flags, F_MAP_STATUS)) + SET_FLAG(err, gen_status_tlv(buf, me->map.st.status_code, + me->map.st.msg_id, me->map.st.msg_type)); + if (err) { + ibuf_free(buf); + mapping_list_clr(mh); + return; + } + + log_msg_mapping(1, type, nbr, &me->map); + + /* no errors - update per neighbor message counters */ + switch (type) { + case MSG_TYPE_LABELMAPPING: + nbr->stats.labelmap_sent++; + break; + case MSG_TYPE_LABELREQUEST: + nbr->stats.labelreq_sent++; + break; + case MSG_TYPE_LABELWITHDRAW: + nbr->stats.labelwdraw_sent++; + break; + case MSG_TYPE_LABELRELEASE: + nbr->stats.labelrel_sent++; + break; + case MSG_TYPE_LABELABORTREQ: + nbr->stats.labelabreq_sent++; + break; + default: + break; + } + + TAILQ_REMOVE(mh, me, entry); + assert(me != TAILQ_FIRST(mh)); + free(me); + } + + enqueue_pdu(nbr, type, buf, size); + + nbr_fsm(nbr, NBR_EVT_PDU_SENT); +} + +/* Generic function that handles all Label Message types */ +int +recv_labelmessage(struct nbr *nbr, char *buf, uint16_t len, uint16_t type) +{ + struct ldp_msg msg; + struct tlv ft; + uint32_t label = NO_LABEL, reqid = 0; + uint32_t pw_status = 0; + uint8_t flags = 0; + int feclen, tlen; + uint16_t current_tlv = 1; + struct mapping_entry *me; + struct mapping_head mh; + struct map map; + + memcpy(&msg, buf, sizeof(msg)); + buf += LDP_MSG_SIZE; + len -= LDP_MSG_SIZE; + + /* FEC TLV */ + if (len < sizeof(ft)) { + session_shutdown(nbr, S_BAD_TLV_LEN, msg.id, msg.type); + return (-1); + } + + memcpy(&ft, buf, sizeof(ft)); + if (ntohs(ft.type) != TLV_TYPE_FEC) { + send_notification(nbr->tcp, S_MISS_MSG, msg.id, msg.type); + return (-1); + } + feclen = ntohs(ft.length); + if (feclen > len - TLV_HDR_SIZE) { + session_shutdown(nbr, S_BAD_TLV_LEN, msg.id, msg.type); + return (-1); + } + + buf += TLV_HDR_SIZE; /* just advance to the end of the fec header */ + len -= TLV_HDR_SIZE; + + TAILQ_INIT(&mh); + do { + memset(&map, 0, sizeof(map)); + map.msg_id = msg.id; + + if ((tlen = tlv_decode_fec_elm(nbr, &msg, buf, feclen, &map)) == -1) + goto err; + if (map.type == MAP_TYPE_PWID && + !CHECK_FLAG(map.flags, F_MAP_PW_ID) && + type != MSG_TYPE_LABELWITHDRAW && + type != MSG_TYPE_LABELRELEASE) { + send_notification(nbr->tcp, S_MISS_MSG, msg.id, msg.type); + goto err; + } + + /* + * The Wildcard FEC Element can be used only in the + * Label Withdraw and Label Release messages. + */ + if (map.type == MAP_TYPE_WILDCARD) { + switch (type) { + case MSG_TYPE_LABELMAPPING: + case MSG_TYPE_LABELREQUEST: + case MSG_TYPE_LABELABORTREQ: + session_shutdown(nbr, S_UNKNOWN_FEC, msg.id, msg.type); + goto err; + default: + break; + } + } + + /* + * RFC 5561 - Section 4: + * "An LDP implementation that supports the Typed Wildcard + * FEC Element MUST support its use in Label Request, Label + * Withdraw, and Label Release messages". + */ + if (map.type == MAP_TYPE_TYPED_WCARD) { + switch (type) { + case MSG_TYPE_LABELMAPPING: + case MSG_TYPE_LABELABORTREQ: + session_shutdown(nbr, S_UNKNOWN_FEC, msg.id, msg.type); + goto err; + default: + break; + } + } + + /* + * LDP supports the use of multiple FEC Elements per + * FEC for the Label Mapping message only. + */ + if (type != MSG_TYPE_LABELMAPPING && tlen != feclen) { + session_shutdown(nbr, S_BAD_TLV_VAL, msg.id, msg.type); + goto err; + } + + mapping_list_add(&mh, &map); + + buf += tlen; + len -= tlen; + feclen -= tlen; + } while (feclen > 0); + + /* Optional Parameters */ + while (len > 0) { + struct tlv tlv; + uint16_t tlv_type; + uint16_t tlv_len; + uint32_t reqbuf, labelbuf, statusbuf; + + if (len < sizeof(tlv)) { + session_shutdown(nbr, S_BAD_TLV_LEN, msg.id, msg.type); + goto err; + } + + memcpy(&tlv, buf, TLV_HDR_SIZE); + tlv_type = ntohs(tlv.type); + tlv_len = ntohs(tlv.length); + if (tlv_len + TLV_HDR_SIZE > len) { + session_shutdown(nbr, S_BAD_TLV_LEN, msg.id, msg.type); + goto err; + } + buf += TLV_HDR_SIZE; + len -= TLV_HDR_SIZE; + + /* + * For Label Mapping messages the Label TLV is mandatory and + * should appear right after the FEC TLV. + */ + if (current_tlv == 1 && + type == MSG_TYPE_LABELMAPPING && + !CHECK_FLAG(tlv_type, TLV_TYPE_GENERICLABEL)) { + send_notification(nbr->tcp, S_MISS_MSG, msg.id, msg.type); + goto err; + } + + switch (tlv_type) { + case TLV_TYPE_LABELREQUEST: + switch (type) { + case MSG_TYPE_LABELMAPPING: + case MSG_TYPE_LABELREQUEST: + if (tlv_len != REQID_TLV_LEN) { + session_shutdown(nbr, S_BAD_TLV_LEN, msg.id, msg.type); + goto err; + } + + SET_FLAG(flags, F_MAP_REQ_ID); + memcpy(&reqbuf, buf, sizeof(reqbuf)); + reqid = ntohl(reqbuf); + break; + default: + /* ignore */ + break; + } + break; + case TLV_TYPE_HOPCOUNT: + case TLV_TYPE_PATHVECTOR: + /* ignore */ + break; + case TLV_TYPE_GENERICLABEL: + switch (type) { + case MSG_TYPE_LABELMAPPING: + case MSG_TYPE_LABELWITHDRAW: + case MSG_TYPE_LABELRELEASE: + if (tlv_len != LABEL_TLV_LEN) { + session_shutdown(nbr, S_BAD_TLV_LEN, msg.id, msg.type); + goto err; + } + + memcpy(&labelbuf, buf, sizeof(labelbuf)); + label = ntohl(labelbuf); + /* do not accept invalid labels */ + if (label > MPLS_LABEL_MAX || + (label <= MPLS_LABEL_RESERVED_MAX && + label != MPLS_LABEL_IPV4_EXPLICIT_NULL && + label != MPLS_LABEL_IPV6_EXPLICIT_NULL && + label != MPLS_LABEL_IMPLICIT_NULL)) { + session_shutdown(nbr, S_BAD_TLV_VAL, msg.id, msg.type); + goto err; + } + break; + default: + /* ignore */ + break; + } + break; + case TLV_TYPE_ATMLABEL: + case TLV_TYPE_FRLABEL: + switch (type) { + case MSG_TYPE_LABELMAPPING: + case MSG_TYPE_LABELWITHDRAW: + case MSG_TYPE_LABELRELEASE: + /* unsupported */ + session_shutdown(nbr, S_BAD_TLV_VAL, msg.id, msg.type); + goto err; + break; + default: + /* ignore */ + break; + } + break; + case TLV_TYPE_STATUS: + if (tlv_len != STATUS_TLV_LEN) { + session_shutdown(nbr, S_BAD_TLV_LEN, msg.id, msg.type); + goto err; + } + /* ignore */ + break; + case TLV_TYPE_PW_STATUS: + switch (type) { + case MSG_TYPE_LABELMAPPING: + if (tlv_len != PW_STATUS_TLV_LEN) { + session_shutdown(nbr, S_BAD_TLV_LEN, msg.id, msg.type); + goto err; + } + + SET_FLAG(flags, F_MAP_PW_STATUS); + memcpy(&statusbuf, buf, sizeof(statusbuf)); + pw_status = ntohl(statusbuf); + break; + default: + /* ignore */ + break; + } + break; + default: + if (!CHECK_FLAG(ntohs(tlv.type), UNKNOWN_FLAG)) + send_notification_rtlvs(nbr, S_UNKNOWN_TLV, + msg.id, msg.type, tlv_type, tlv_len, buf); + /* ignore unknown tlv */ + break; + } + buf += tlv_len; + len -= tlv_len; + current_tlv++; + } + + /* notify lde about the received message. */ + while ((me = TAILQ_FIRST(&mh)) != NULL) { + int imsg_type = IMSG_NONE; + + SET_FLAG(me->map.flags, flags); + switch (me->map.type) { + case MAP_TYPE_PREFIX: + switch (me->map.fec.prefix.af) { + case AF_INET: + if (label == MPLS_LABEL_IPV6_EXPLICIT_NULL) { + session_shutdown(nbr, S_BAD_TLV_VAL, msg.id, msg.type); + goto err; + } + if (!nbr->v4_enabled) + goto next; + break; + case AF_INET6: + if (label == MPLS_LABEL_IPV4_EXPLICIT_NULL) { + session_shutdown(nbr, S_BAD_TLV_VAL, msg.id, msg.type); + goto err; + } + if (!nbr->v6_enabled) + goto next; + break; + default: + fatalx("recv_labelmessage: unknown af"); + } + break; + case MAP_TYPE_PWID: + if (label <= MPLS_LABEL_RESERVED_MAX) { + session_shutdown(nbr, S_BAD_TLV_VAL, msg.id, msg.type); + goto err; + } + if (CHECK_FLAG(me->map.flags, F_MAP_PW_STATUS)) + me->map.pw_status = pw_status; + break; + default: + break; + } + me->map.label = label; + if (CHECK_FLAG(me->map.flags, F_MAP_REQ_ID)) + me->map.requestid = reqid; + + log_msg_mapping(0, type, nbr, &me->map); + + switch (type) { + case MSG_TYPE_LABELMAPPING: + imsg_type = IMSG_LABEL_MAPPING; + break; + case MSG_TYPE_LABELREQUEST: + imsg_type = IMSG_LABEL_REQUEST; + break; + case MSG_TYPE_LABELWITHDRAW: + imsg_type = IMSG_LABEL_WITHDRAW; + break; + case MSG_TYPE_LABELRELEASE: + imsg_type = IMSG_LABEL_RELEASE; + break; + case MSG_TYPE_LABELABORTREQ: + imsg_type = IMSG_LABEL_ABORT; + break; + default: + break; + } + + ldpe_imsg_compose_lde(imsg_type, nbr->peerid, 0, &me->map, + sizeof(struct map)); + + next: + TAILQ_REMOVE(&mh, me, entry); + assert(me != TAILQ_FIRST(&mh)); + free(me); + } + + return (0); + + err: + mapping_list_clr(&mh); + + return (-1); +} + +/* Other TLV related functions */ +static int +gen_label_tlv(struct ibuf *buf, uint32_t label) +{ + struct label_tlv lt; + + lt.type = htons(TLV_TYPE_GENERICLABEL); + lt.length = htons(LABEL_TLV_LEN); + lt.label = htonl(label); + + return (ibuf_add(buf, <, sizeof(lt))); +} + +static int +gen_reqid_tlv(struct ibuf *buf, uint32_t reqid) +{ + struct reqid_tlv rt; + + rt.type = htons(TLV_TYPE_LABELREQUEST); + rt.length = htons(REQID_TLV_LEN); + rt.reqid = htonl(reqid); + + return (ibuf_add(buf, &rt, sizeof(rt))); +} + +int +gen_pw_status_tlv(struct ibuf *buf, uint32_t status) +{ + struct pw_status_tlv st; + + st.type = htons(TLV_TYPE_PW_STATUS); + st.length = htons(PW_STATUS_TLV_LEN); + st.value = htonl(status); + + return (ibuf_add(buf, &st, sizeof(st))); +} + +uint16_t +len_fec_tlv(struct map *map) +{ + uint16_t len = TLV_HDR_SIZE; + + switch (map->type) { + case MAP_TYPE_WILDCARD: + len += FEC_ELM_WCARD_LEN; + break; + case MAP_TYPE_PREFIX: + len += FEC_ELM_PREFIX_MIN_LEN + + PREFIX_SIZE(map->fec.prefix.prefixlen); + break; + case MAP_TYPE_PWID: + len += FEC_PWID_ELM_MIN_LEN; + if (CHECK_FLAG(map->flags, F_MAP_PW_ID)) + len += PW_STATUS_TLV_LEN; + if (CHECK_FLAG(map->flags, F_MAP_PW_IFMTU)) + len += FEC_SUBTLV_IFMTU_SIZE; + if (CHECK_FLAG(map->flags, F_MAP_PW_STATUS)) + len += PW_STATUS_TLV_SIZE; + break; + case MAP_TYPE_TYPED_WCARD: + len += FEC_ELM_TWCARD_MIN_LEN; + switch (map->fec.twcard.type) { + case MAP_TYPE_PREFIX: + case MAP_TYPE_PWID: + len += sizeof(uint16_t); + break; + default: + fatalx("len_fec_tlv: unexpected fec type"); + } + break; + default: + fatalx("len_fec_tlv: unexpected fec type"); + } + + return (len); +} + +int +gen_fec_tlv(struct ibuf *buf, struct map *map) +{ + struct tlv ft; + uint16_t family, len, pw_type, ifmtu; + uint8_t pw_len = 0, twcard_len; + uint32_t group_id, pwid; + int err = 0; + + ft.type = htons(TLV_TYPE_FEC); + + switch (map->type) { + case MAP_TYPE_WILDCARD: + ft.length = htons(sizeof(uint8_t)); + SET_FLAG(err, ibuf_add(buf, &ft, sizeof(ft))); + SET_FLAG(err, ibuf_add(buf, &map->type, sizeof(map->type))); + break; + case MAP_TYPE_PREFIX: + len = PREFIX_SIZE(map->fec.prefix.prefixlen); + ft.length = htons(sizeof(map->type) + sizeof(family) + + sizeof(map->fec.prefix.prefixlen) + len); + SET_FLAG(err, ibuf_add(buf, &ft, sizeof(ft))); + SET_FLAG(err, ibuf_add(buf, &map->type, sizeof(map->type))); + switch (map->fec.prefix.af) { + case AF_INET: + family = htons(AF_IPV4); + break; + case AF_INET6: + family = htons(AF_IPV6); + break; + default: + fatalx("gen_fec_tlv: unknown af"); + break; + } + SET_FLAG(err, ibuf_add(buf, &family, sizeof(family))); + SET_FLAG(err, ibuf_add(buf, &map->fec.prefix.prefixlen, + sizeof(map->fec.prefix.prefixlen))); + if (len) + SET_FLAG(err, ibuf_add(buf, &map->fec.prefix.prefix, len)); + break; + case MAP_TYPE_PWID: + if (CHECK_FLAG(map->flags, F_MAP_PW_ID)) + pw_len += FEC_PWID_SIZE; + if (CHECK_FLAG(map->flags, F_MAP_PW_IFMTU)) + pw_len += FEC_SUBTLV_IFMTU_SIZE; + + len = FEC_PWID_ELM_MIN_LEN + pw_len; + + ft.length = htons(len); + SET_FLAG(err, ibuf_add(buf, &ft, sizeof(ft))); + + SET_FLAG(err, ibuf_add(buf, &map->type, sizeof(uint8_t))); + pw_type = map->fec.pwid.type; + if (CHECK_FLAG(map->flags, F_MAP_PW_CWORD)) + SET_FLAG(pw_type, CONTROL_WORD_FLAG); + pw_type = htons(pw_type); + SET_FLAG(err, ibuf_add(buf, &pw_type, sizeof(uint16_t))); + SET_FLAG(err, ibuf_add(buf, &pw_len, sizeof(uint8_t))); + group_id = htonl(map->fec.pwid.group_id); + SET_FLAG(err, ibuf_add(buf, &group_id, sizeof(uint32_t))); + if (CHECK_FLAG(map->flags, F_MAP_PW_ID)) { + pwid = htonl(map->fec.pwid.pwid); + SET_FLAG(err, ibuf_add(buf, &pwid, sizeof(uint32_t))); + } + if (CHECK_FLAG(map->flags, F_MAP_PW_IFMTU)) { + struct subtlv stlv; + + stlv.type = SUBTLV_IFMTU; + stlv.length = FEC_SUBTLV_IFMTU_SIZE; + SET_FLAG(err, ibuf_add(buf, &stlv, sizeof(uint16_t))); + + ifmtu = htons(map->fec.pwid.ifmtu); + SET_FLAG(err, ibuf_add(buf, &ifmtu, sizeof(uint16_t))); + } + break; + case MAP_TYPE_TYPED_WCARD: + len = FEC_ELM_TWCARD_MIN_LEN; + switch (map->fec.twcard.type) { + case MAP_TYPE_PREFIX: + case MAP_TYPE_PWID: + len += sizeof(uint16_t); + break; + default: + fatalx("gen_fec_tlv: unexpected fec type"); + } + ft.length = htons(len); + SET_FLAG(err, ibuf_add(buf, &ft, sizeof(ft))); + SET_FLAG(err, ibuf_add(buf, &map->type, sizeof(uint8_t))); + SET_FLAG(err, ibuf_add(buf, &map->fec.twcard.type, sizeof(uint8_t))); + + switch (map->fec.twcard.type) { + case MAP_TYPE_PREFIX: + twcard_len = sizeof(uint16_t); + SET_FLAG(err, ibuf_add(buf, &twcard_len, sizeof(uint8_t))); + + switch (map->fec.twcard.u.prefix_af) { + case AF_INET: + family = htons(AF_IPV4); + break; + case AF_INET6: + family = htons(AF_IPV6); + break; + default: + fatalx("gen_fec_tlv: unknown af"); + break; + } + + SET_FLAG(err, ibuf_add(buf, &family, sizeof(uint16_t))); + break; + case MAP_TYPE_PWID: + twcard_len = sizeof(uint16_t); + SET_FLAG(err, ibuf_add(buf, &twcard_len, sizeof(uint8_t))); + pw_type = htons(map->fec.twcard.u.pw_type); + SET_FLAG(err, ibuf_add(buf, &pw_type, sizeof(uint16_t))); + break; + default: + fatalx("gen_fec_tlv: unexpected fec type"); + } + break; + default: + break; + } + + return (err); +} + +int +tlv_decode_fec_elm(struct nbr *nbr, struct ldp_msg *msg, char *buf, + uint16_t len, struct map *map) +{ + uint16_t off = 0; + uint8_t pw_len, twcard_len; + + map->type = *buf; + off += sizeof(uint8_t); + + switch (map->type) { + case MAP_TYPE_WILDCARD: + if (len == FEC_ELM_WCARD_LEN) + return (off); + else { + session_shutdown(nbr, S_BAD_TLV_VAL, msg->id, msg->type); + return (-1); + } + break; + case MAP_TYPE_PREFIX: + if (len < FEC_ELM_PREFIX_MIN_LEN) { + session_shutdown(nbr, S_BAD_TLV_LEN, msg->id, msg->type); + return (-1); + } + + /* Address Family */ + memcpy(&map->fec.prefix.af, buf + off, sizeof(map->fec.prefix.af)); + off += sizeof(map->fec.prefix.af); + map->fec.prefix.af = ntohs(map->fec.prefix.af); + switch (map->fec.prefix.af) { + case AF_IPV4: + map->fec.prefix.af = AF_INET; + break; + case AF_IPV6: + map->fec.prefix.af = AF_INET6; + break; + default: + send_notification(nbr->tcp, S_UNSUP_ADDR, msg->id, msg->type); + return (-1); + } + + /* Prefix Length */ + map->fec.prefix.prefixlen = buf[off]; + off += sizeof(uint8_t); + if ((map->fec.prefix.af == AF_IPV4 + && map->fec.prefix.prefixlen > IPV4_MAX_BITLEN) + || (map->fec.prefix.af == AF_IPV6 + && map->fec.prefix.prefixlen > IPV6_MAX_BITLEN)) { + session_shutdown(nbr, S_BAD_TLV_VAL, msg->id, msg->type); + return (-1); + } + if (len < off + PREFIX_SIZE(map->fec.prefix.prefixlen)) { + session_shutdown(nbr, S_BAD_TLV_LEN, msg->id, msg->type); + return (-1); + } + + /* Prefix */ + memset(&map->fec.prefix.prefix, 0, sizeof(map->fec.prefix.prefix)); + memcpy(&map->fec.prefix.prefix, buf + off, + PREFIX_SIZE(map->fec.prefix.prefixlen)); + + /* Just in case... */ + ldp_applymask(map->fec.prefix.af, &map->fec.prefix.prefix, + &map->fec.prefix.prefix, map->fec.prefix.prefixlen); + + return (off + PREFIX_SIZE(map->fec.prefix.prefixlen)); + case MAP_TYPE_PWID: + if (len < FEC_PWID_ELM_MIN_LEN) { + session_shutdown(nbr, S_BAD_TLV_LEN, msg->id, msg->type); + return (-1); + } + + /* PW type */ + memcpy(&map->fec.pwid.type, buf + off, sizeof(uint16_t)); + map->fec.pwid.type = ntohs(map->fec.pwid.type); + if (CHECK_FLAG(map->fec.pwid.type, CONTROL_WORD_FLAG)) { + SET_FLAG(map->flags, F_MAP_PW_CWORD); + UNSET_FLAG(map->fec.pwid.type, CONTROL_WORD_FLAG); + } + off += sizeof(uint16_t); + + /* PW info Length */ + pw_len = buf[off]; + off += sizeof(uint8_t); + + if (len != FEC_PWID_ELM_MIN_LEN + pw_len) { + session_shutdown(nbr, S_BAD_TLV_LEN, msg->id, msg->type); + return (-1); + } + + /* Group ID */ + memcpy(&map->fec.pwid.group_id, buf + off, sizeof(uint32_t)); + map->fec.pwid.group_id = ntohl(map->fec.pwid.group_id); + off += sizeof(uint32_t); + + /* PW ID */ + if (pw_len == 0) + return (off); + + if (pw_len < sizeof(uint32_t)) { + session_shutdown(nbr, S_BAD_TLV_LEN, msg->id, msg->type); + return (-1); + } + + memcpy(&map->fec.pwid.pwid, buf + off, sizeof(uint32_t)); + map->fec.pwid.pwid = ntohl(map->fec.pwid.pwid); + SET_FLAG(map->flags, F_MAP_PW_ID); + off += sizeof(uint32_t); + pw_len -= sizeof(uint32_t); + + /* Optional Interface Parameter Sub-TLVs */ + while (pw_len > 0) { + struct subtlv stlv; + + if (pw_len < sizeof(stlv)) { + session_shutdown(nbr, S_BAD_TLV_LEN, msg->id, msg->type); + return (-1); + } + + memcpy(&stlv, buf + off, sizeof(stlv)); + if (stlv.length > pw_len) { + session_shutdown(nbr, S_BAD_TLV_LEN, msg->id, msg->type); + return (-1); + } + + switch (stlv.type) { + case SUBTLV_IFMTU: + if (stlv.length != FEC_SUBTLV_IFMTU_SIZE) { + session_shutdown(nbr, S_BAD_TLV_LEN, msg->id, msg->type); + return (-1); + } + memcpy(&map->fec.pwid.ifmtu, buf + off + + SUBTLV_HDR_SIZE, sizeof(uint16_t)); + map->fec.pwid.ifmtu = ntohs(map->fec.pwid.ifmtu); + SET_FLAG(map->flags, F_MAP_PW_IFMTU); + break; + default: + /* ignore */ + break; + } + off += stlv.length; + pw_len -= stlv.length; + } + + return (off); + case MAP_TYPE_TYPED_WCARD: + if (len < FEC_ELM_TWCARD_MIN_LEN) { + session_shutdown(nbr, S_BAD_TLV_LEN, msg->id, msg->type); + return (-1); + } + + memcpy(&map->fec.twcard.type, buf + off, sizeof(uint8_t)); + off += sizeof(uint8_t); + memcpy(&twcard_len, buf + off, sizeof(uint8_t)); + off += sizeof(uint8_t); + if (len != FEC_ELM_TWCARD_MIN_LEN + twcard_len) { + session_shutdown(nbr, S_BAD_TLV_LEN, msg->id, msg->type); + return (-1); + } + + switch (map->fec.twcard.type) { + case MAP_TYPE_PREFIX: + if (twcard_len != sizeof(uint16_t)) { + session_shutdown(nbr, S_BAD_TLV_LEN, msg->id, msg->type); + return (-1); + } + + memcpy(&map->fec.twcard.u.prefix_af, buf + off, sizeof(uint16_t)); + map->fec.twcard.u.prefix_af = ntohs(map->fec.twcard.u.prefix_af); + off += sizeof(uint16_t); + + switch (map->fec.twcard.u.prefix_af) { + case AF_IPV4: + map->fec.twcard.u.prefix_af = AF_INET; + break; + case AF_IPV6: + map->fec.twcard.u.prefix_af = AF_INET6; + break; + default: + session_shutdown(nbr, S_BAD_TLV_VAL, msg->id, msg->type); + return (-1); + } + break; + case MAP_TYPE_PWID: + if (twcard_len != sizeof(uint16_t)) { + session_shutdown(nbr, S_BAD_TLV_LEN, msg->id, msg->type); + return (-1); + } + + memcpy(&map->fec.twcard.u.pw_type, buf + off, sizeof(uint16_t)); + map->fec.twcard.u.pw_type = ntohs(map->fec.twcard.u.pw_type); + /* ignore the reserved bit as per RFC 6667 */ + map->fec.twcard.u.pw_type &= ~PW_TWCARD_RESERVED_BIT; + off += sizeof(uint16_t); + break; + default: + send_notification(nbr->tcp, S_UNKNOWN_FEC, msg->id, msg->type); + return (-1); + } + + return (off); + default: + send_notification(nbr->tcp, S_UNKNOWN_FEC, msg->id, msg->type); + break; + } + + return (-1); +} + +static void +log_msg_mapping(int out, uint16_t msg_type, struct nbr *nbr, struct map *map) +{ + debug_msg(out, "%s: lsr-id %pI4, fec %s, label %s", msg_name(msg_type), + &nbr->id, log_map(map), log_label(map->label)); +} |