]> git.proxmox.com Git - mirror_frr.git/blobdiff - ldpd/labelmapping.c
Merge pull request #3502 from donaldsharp/socket_to_me_baby
[mirror_frr.git] / ldpd / labelmapping.c
index 15861cfd9ac2c37ff32a3fc3db653119810ed68e..944f93331f002a3fb1ef6c6ae3cfaa4a6b27171f 100644 (file)
 
 #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;
 
@@ -71,25 +70,8 @@ send_labelmessage(struct nbr *nbr, uint16_t type, struct mapping_head *mh)
                }
 
                /* 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)
@@ -99,7 +81,7 @@ send_labelmessage(struct nbr *nbr, uint16_t type, struct mapping_head *mh)
 
                /* 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;
                }
@@ -124,15 +106,35 @@ send_labelmessage(struct nbr *nbr, uint16_t type, struct mapping_head *mh)
                        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);
 }
@@ -146,7 +148,8 @@ recv_labelmessage(struct nbr *nbr, char *buf, uint16_t len, uint16_t type)
        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;
@@ -163,7 +166,7 @@ recv_labelmessage(struct nbr *nbr, char *buf, uint16_t len, uint16_t type)
 
        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);
@@ -187,9 +190,9 @@ recv_labelmessage(struct nbr *nbr, char *buf, uint16_t len, uint16_t type)
                    !(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;
                }
 
                /*
@@ -209,6 +212,24 @@ recv_labelmessage(struct nbr *nbr, char *buf, uint16_t len, uint16_t type)
                        }
                }
 
+               /*
+                * 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.
@@ -226,19 +247,10 @@ recv_labelmessage(struct nbr *nbr, char *buf, uint16_t len, uint16_t type)
                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;
 
@@ -248,6 +260,7 @@ recv_labelmessage(struct nbr *nbr, char *buf, uint16_t len, uint16_t type)
                }
 
                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);
@@ -256,7 +269,18 @@ recv_labelmessage(struct nbr *nbr, char *buf, uint16_t len, uint16_t 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:
@@ -282,6 +306,7 @@ recv_labelmessage(struct nbr *nbr, char *buf, uint16_t len, uint16_t type)
                        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) {
@@ -292,6 +317,16 @@ recv_labelmessage(struct nbr *nbr, char *buf, uint16_t len, uint16_t type)
 
                                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 */
@@ -301,6 +336,7 @@ recv_labelmessage(struct nbr *nbr, char *buf, uint16_t len, uint16_t type)
                case TLV_TYPE_ATMLABEL:
                case TLV_TYPE_FRLABEL:
                        switch (type) {
+                       case MSG_TYPE_LABELMAPPING:
                        case MSG_TYPE_LABELWITHDRAW:
                        case MSG_TYPE_LABELRELEASE:
                                /* unsupported */
@@ -341,13 +377,14 @@ recv_labelmessage(struct nbr *nbr, char *buf, uint16_t len, uint16_t type)
                        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. */
@@ -359,7 +396,7 @@ recv_labelmessage(struct nbr *nbr, char *buf, uint16_t len, uint16_t type)
                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;
@@ -368,7 +405,7 @@ recv_labelmessage(struct nbr *nbr, char *buf, uint16_t len, uint16_t type)
                                        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;
@@ -396,9 +433,7 @@ recv_labelmessage(struct nbr *nbr, char *buf, uint16_t len, uint16_t type)
                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:
@@ -425,6 +460,7 @@ recv_labelmessage(struct nbr *nbr, char *buf, uint16_t len, uint16_t type)
 
  next:
                TAILQ_REMOVE(&mh, me, entry);
+               assert(me != TAILQ_FIRST(&mh));
                free(me);
        }
 
@@ -449,53 +485,6 @@ gen_label_tlv(struct ibuf *buf, uint32_t label)
        return (ibuf_add(buf, &lt, 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(&lt, 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)
 {
@@ -520,12 +509,52 @@ gen_pw_status_tlv(struct ibuf *buf, uint32_t 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 (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;
 
@@ -595,6 +624,50 @@ gen_fec_tlv(struct ibuf *buf, struct map *map)
                        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;
        }
@@ -607,7 +680,7 @@ 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;
+       uint8_t         pw_len, twcard_len;
 
        map->type = *buf;
        off += sizeof(uint8_t);
@@ -642,7 +715,7 @@ tlv_decode_fec_elm(struct nbr *nbr, struct ldp_msg *msg, char *buf,
                        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);
                }
@@ -751,11 +824,84 @@ tlv_decode_fec_elm(struct nbr *nbr, struct ldp_msg *msg, char *buf,
                        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));
+}