#include "mpls.h"
-static void enqueue_pdu(struct nbr *, struct ibuf *, uint16_t);
+static void enqueue_pdu(struct nbr *, uint16_t, struct ibuf *, uint16_t);
static int gen_label_tlv(struct ibuf *, uint32_t);
-static int tlv_decode_label(struct nbr *, struct ldp_msg *, char *,
- uint16_t, 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, struct ibuf *buf, uint16_t size)
+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->length = htons(size - LDP_HDR_DEAD_LEN);
evbuf_enqueue(&nbr->tcp->wbuf, buf);
}
/* real size will be set up later */
err |= gen_ldp_hdr(buf, 0);
- size = LDP_HDR_PDU_LEN;
+ size = LDP_HDR_SIZE;
first = 0;
}
/* calculate size */
- msg_size = LDP_MSG_SIZE + TLV_HDR_SIZE;
- switch (me->map.type) {
- case MAP_TYPE_WILDCARD:
- msg_size += FEC_ELM_WCARD_LEN;
- break;
- case MAP_TYPE_PREFIX:
- msg_size += FEC_ELM_PREFIX_MIN_LEN +
- PREFIX_SIZE(me->map.fec.prefix.prefixlen);
- break;
- case MAP_TYPE_PWID:
- msg_size += FEC_PWID_ELM_MIN_LEN;
- if (me->map.flags & F_MAP_PW_ID)
- msg_size += PW_STATUS_TLV_LEN;
- if (me->map.flags & F_MAP_PW_IFMTU)
- msg_size += FEC_SUBTLV_IFMTU_SIZE;
- if (me->map.flags & F_MAP_PW_STATUS)
- msg_size += PW_STATUS_TLV_SIZE;
- break;
- }
+ msg_size = LDP_MSG_SIZE;
+ msg_size += len_fec_tlv(&me->map);
if (me->map.label != NO_LABEL)
msg_size += LABEL_TLV_SIZE;
if (me->map.flags & F_MAP_REQ_ID)
/* maximum pdu length exceeded, we need a new ldp pdu */
if (size + msg_size > nbr->max_pdu_len) {
- enqueue_pdu(nbr, buf, size);
+ enqueue_pdu(nbr, type, buf, size);
first = 1;
continue;
}
return;
}
- debug_msg_send("%s: lsr-id %s fec %s label %s", msg_name(type),
- inet_ntoa(nbr->id), log_map(&me->map),
- log_label(me->map.label));
+ 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, buf, size);
+ enqueue_pdu(nbr, type, buf, size);
nbr_fsm(nbr, NBR_EVT_PDU_SENT);
}
uint32_t label = NO_LABEL, reqid = 0;
uint32_t pw_status = 0;
uint8_t flags = 0;
- int feclen, lbllen, tlen;
+ int feclen, tlen;
+ uint16_t current_tlv = 1;
struct mapping_entry *me;
struct mapping_head mh;
struct map map;
memcpy(&ft, buf, sizeof(ft));
if (ntohs(ft.type) != TLV_TYPE_FEC) {
- send_notification_nbr(nbr, S_MISS_MSG, msg.id, msg.type);
+ send_notification(nbr->tcp, S_MISS_MSG, msg.id, msg.type);
return (-1);
}
feclen = ntohs(ft.length);
!(map.flags & F_MAP_PW_ID) &&
type != MSG_TYPE_LABELWITHDRAW &&
type != MSG_TYPE_LABELRELEASE) {
- send_notification_nbr(nbr, S_MISS_MSG, msg.id,
+ send_notification(nbr->tcp, S_MISS_MSG, msg.id,
msg.type);
- return (-1);
+ goto err;
}
/*
}
}
+ /*
+ * 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.
feclen -= tlen;
} while (feclen > 0);
- /* Mandatory Label TLV */
- if (type == MSG_TYPE_LABELMAPPING) {
- lbllen = tlv_decode_label(nbr, &msg, buf, len, &label);
- if (lbllen == -1)
- goto err;
-
- buf += lbllen;
- len -= lbllen;
- }
-
/* Optional Parameters */
while (len > 0) {
struct tlv tlv;
+ uint16_t tlv_type;
uint16_t tlv_len;
uint32_t reqbuf, labelbuf, statusbuf;
}
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);
buf += TLV_HDR_SIZE;
len -= TLV_HDR_SIZE;
- switch (ntohs(tlv.type)) {
+ /*
+ * 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 &&
+ !(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:
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) {
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 */
case TLV_TYPE_ATMLABEL:
case TLV_TYPE_FRLABEL:
switch (type) {
+ case MSG_TYPE_LABELMAPPING:
case MSG_TYPE_LABELWITHDRAW:
case MSG_TYPE_LABELRELEASE:
/* unsupported */
break;
default:
if (!(ntohs(tlv.type) & UNKNOWN_FLAG))
- send_notification_nbr(nbr, S_UNKNOWN_TLV,
- msg.id, msg.type);
+ 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. */
case MAP_TYPE_PREFIX:
switch (me->map.fec.prefix.af) {
case AF_INET:
- if (label == MPLS_LABEL_IPV6NULL) {
+ if (label == MPLS_LABEL_IPV6_EXPLICIT_NULL) {
session_shutdown(nbr, S_BAD_TLV_VAL,
msg.id, msg.type);
goto err;
goto next;
break;
case AF_INET6:
- if (label == MPLS_LABEL_IPV4NULL) {
+ if (label == MPLS_LABEL_IPV4_EXPLICIT_NULL) {
session_shutdown(nbr, S_BAD_TLV_VAL,
msg.id, msg.type);
goto err;
if (me->map.flags & F_MAP_REQ_ID)
me->map.requestid = reqid;
- debug_msg_recv("%s: lsr-id %s fec %s label %s", msg_name(type),
- inet_ntoa(nbr->id), log_map(&me->map),
- log_label(me->map.label));
+ log_msg_mapping(0, type, nbr, &me->map);
switch (type) {
case MSG_TYPE_LABELMAPPING:
ldpe_imsg_compose_lde(imsg_type, nbr->peerid, 0, &me->map,
sizeof(struct map));
-next:
+ next:
TAILQ_REMOVE(&mh, me, entry);
+ assert(me != TAILQ_FIRST(&mh));
free(me);
}
return (0);
-err:
+ err:
mapping_list_clr(&mh);
return (-1);
return (ibuf_add(buf, <, sizeof(lt)));
}
-static int
-tlv_decode_label(struct nbr *nbr, struct ldp_msg *msg, char *buf,
- uint16_t len, uint32_t *label)
-{
- struct label_tlv lt;
-
- if (len < sizeof(lt)) {
- session_shutdown(nbr, S_BAD_TLV_LEN, msg->id, msg->type);
- return (-1);
- }
- memcpy(<, buf, sizeof(lt));
-
- if (!(ntohs(lt.type) & TLV_TYPE_GENERICLABEL)) {
- send_notification_nbr(nbr, S_MISS_MSG, msg->id, msg->type);
- return (-1);
- }
-
- switch (htons(lt.type)) {
- case TLV_TYPE_GENERICLABEL:
- if (ntohs(lt.length) != sizeof(lt) - TLV_HDR_SIZE) {
- session_shutdown(nbr, S_BAD_TLV_LEN, msg->id,
- msg->type);
- return (-1);
- }
-
- *label = ntohl(lt.label);
- if (*label > MPLS_LABEL_MAX ||
- (*label <= MPLS_LABEL_RESERVED_MAX &&
- *label != MPLS_LABEL_IPV4NULL &&
- *label != MPLS_LABEL_IPV6NULL &&
- *label != MPLS_LABEL_IMPLNULL)) {
- session_shutdown(nbr, S_BAD_TLV_VAL, msg->id,
- msg->type);
- return (-1);
- }
- break;
- case TLV_TYPE_ATMLABEL:
- case TLV_TYPE_FRLABEL:
- default:
- /* unsupported */
- session_shutdown(nbr, S_BAD_TLV_VAL, msg->id, msg->type);
- return (-1);
- }
-
- return (sizeof(lt));
-}
-
static int
gen_reqid_tlv(struct ibuf *buf, uint32_t reqid)
{
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 (map->flags & F_MAP_PW_ID)
+ len += PW_STATUS_TLV_LEN;
+ if (map->flags & F_MAP_PW_IFMTU)
+ len += FEC_SUBTLV_IFMTU_SIZE;
+ if (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;
+ uint8_t pw_len = 0, twcard_len;
uint32_t group_id, pwid;
int err = 0;
break;
case MAP_TYPE_PWID:
if (map->flags & F_MAP_PW_ID)
- pw_len += PW_STATUS_TLV_LEN;
+ pw_len += FEC_PWID_SIZE;
if (map->flags & F_MAP_PW_IFMTU)
pw_len += FEC_SUBTLV_IFMTU_SIZE;
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);
+ err |= ibuf_add(buf, &ft, sizeof(ft));
+ err |= ibuf_add(buf, &map->type, sizeof(uint8_t));
+ 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);
+ 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;
+ }
+
+ err |= ibuf_add(buf, &family, sizeof(uint16_t));
+ break;
+ case MAP_TYPE_PWID:
+ twcard_len = sizeof(uint16_t);
+ err |= ibuf_add(buf, &twcard_len, sizeof(uint8_t));
+ pw_type = htons(map->fec.twcard.u.pw_type);
+ err |= ibuf_add(buf, &pw_type, sizeof(uint16_t));
+ break;
+ default:
+ fatalx("gen_fec_tlv: unexpected fec type");
+ }
+ break;
default:
break;
}
uint16_t len, struct map *map)
{
uint16_t off = 0;
- uint8_t pw_len;
+ uint8_t pw_len, twcard_len;
map->type = *buf;
off += sizeof(uint8_t);
map->fec.prefix.af = AF_INET6;
break;
default:
- send_notification_nbr(nbr, S_UNSUP_ADDR, msg->id,
+ send_notification(nbr->tcp, S_UNSUP_ADDR, msg->id,
msg->type);
return (-1);
}
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(nbr, S_UNKNOWN_FEC, msg->id, msg->type);
+ 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 %s, fec %s, label %s", msg_name(msg_type),
+ inet_ntoa(nbr->id), log_map(map), log_label(map->label));
+}