]> git.puffer.fish Git - mirror/frr.git/commitdiff
ldpd: implement RFC 5561 (LDP Capabilities)
authorRenato Westphal <renato@opensourcerouting.org>
Fri, 3 Mar 2017 20:50:22 +0000 (17:50 -0300)
committerRenato Westphal <renato@opensourcerouting.org>
Fri, 3 Mar 2017 20:50:22 +0000 (17:50 -0300)
This patch per-se doesn't introduce any useful functionality, but prepares
the ground for new enhancements to ldpd (i.e. implementation of new RFCs
that make use of LDP capabilities).

Signed-off-by: Renato Westphal <renato@opensourcerouting.org>
ldpd/init.c
ldpd/labelmapping.c
ldpd/ldp.h
ldpd/ldpd.h
ldpd/ldpe.h
ldpd/log.c
ldpd/notification.c
ldpd/packet.c

index 030bff2f51a6da1db2609792cf9f91862d2b7afb..4b3eab270daff7c250389338e9abadf43b28bc96 100644 (file)
@@ -24,6 +24,7 @@
 #include "ldp_debug.h"
 
 static int     gen_init_prms_tlv(struct ibuf *, struct nbr *);
+static int     gen_cap_dynamic_tlv(struct ibuf *);
 
 void
 send_init(struct nbr *nbr)
@@ -34,15 +35,16 @@ send_init(struct nbr *nbr)
 
        debug_msg_send("initialization: lsr-id %s", inet_ntoa(nbr->id));
 
-       size = LDP_HDR_SIZE + LDP_MSG_SIZE + SESS_PRMS_SIZE;
+       size = LDP_HDR_SIZE + LDP_MSG_SIZE + SESS_PRMS_SIZE +
+           CAP_TLV_DYNAMIC_SIZE;
        if ((buf = ibuf_open(size)) == NULL)
                fatal(__func__);
 
        err |= gen_ldp_hdr(buf, size);
        size -= LDP_HDR_SIZE;
        err |= gen_msg_hdr(buf, MSG_TYPE_INIT, size);
-       size -= LDP_MSG_SIZE;
        err |= gen_init_prms_tlv(buf, nbr);
+       err |= gen_cap_dynamic_tlv(buf);
        if (err) {
                ibuf_free(buf);
                return;
@@ -57,6 +59,7 @@ recv_init(struct nbr *nbr, char *buf, uint16_t len)
        struct ldp_msg          msg;
        struct sess_prms_tlv    sess;
        uint16_t                max_pdu_len;
+       int                     caps_rcvd = 0;
 
        debug_msg_recv("initialization: lsr-id %s", inet_ntoa(nbr->id));
 
@@ -93,6 +96,7 @@ recv_init(struct nbr *nbr, char *buf, uint16_t len)
        /* Optional Parameters */
        while (len > 0) {
                struct tlv      tlv;
+               uint16_t        tlv_type;
                uint16_t        tlv_len;
 
                if (len < sizeof(tlv)) {
@@ -101,6 +105,7 @@ recv_init(struct nbr *nbr, char *buf, uint16_t len)
                }
 
                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);
@@ -109,17 +114,42 @@ recv_init(struct nbr *nbr, char *buf, uint16_t len)
                buf += TLV_HDR_SIZE;
                len -= TLV_HDR_SIZE;
 
-               switch (ntohs(tlv.type)) {
+               /*
+                * RFC 5561 - Section 6:
+                * "The S-bit of a Capability Parameter in an Initialization
+                * message MUST be 1 and SHOULD be ignored on receipt".
+                */
+               switch (tlv_type) {
                case TLV_TYPE_ATMSESSIONPAR:
                        session_shutdown(nbr, S_BAD_TLV_VAL, msg.id, msg.type);
                        return (-1);
                case TLV_TYPE_FRSESSION:
                        session_shutdown(nbr, S_BAD_TLV_VAL, msg.id, msg.type);
                        return (-1);
+               case TLV_TYPE_DYNAMIC_CAP:
+                       if (tlv_len != CAP_TLV_DYNAMIC_LEN) {
+                               session_shutdown(nbr, S_BAD_TLV_LEN, msg.id,
+                                   msg.type);
+                               return (-1);
+                       }
+
+                       if (caps_rcvd & F_CAP_TLV_RCVD_DYNAMIC) {
+                               session_shutdown(nbr, S_BAD_TLV_VAL, msg.id,
+                                   msg.type);
+                               return (-1);
+                       }
+                       caps_rcvd |= F_CAP_TLV_RCVD_DYNAMIC;
+
+                       nbr->flags |= F_NBR_CAP_DYNAMIC;
+
+                       log_debug("%s: lsr-id %s announced the Dynamic "
+                           "Capability Announcement capability", __func__,
+                           inet_ntoa(nbr->id));
+                       break;
                default:
                        if (!(ntohs(tlv.type) & UNKNOWN_FLAG))
-                               send_notification(nbr->tcp, S_UNKNOWN_TLV,
-                                   msg.id, msg.type);
+                               send_notification_rtlvs(nbr, S_UNSSUPORTDCAP,
+                                   msg.id, msg.type, tlv_type, tlv_len, buf);
                        /* ignore unknown tlv */
                        break;
                }
@@ -145,6 +175,104 @@ recv_init(struct nbr *nbr, char *buf, uint16_t len)
        return (0);
 }
 
+void
+send_capability(struct nbr *nbr, uint16_t capability, int enable)
+{
+       struct ibuf             *buf;
+       uint16_t                 size;
+       int                      err = 0;
+
+       log_debug("%s: lsr-id %s", __func__, inet_ntoa(nbr->id));
+
+       size = LDP_HDR_SIZE + LDP_MSG_SIZE + CAP_TLV_DYNAMIC_SIZE;
+       if ((buf = ibuf_open(size)) == NULL)
+               fatal(__func__);
+
+       err |= gen_ldp_hdr(buf, size);
+       size -= LDP_HDR_SIZE;
+       err |= gen_msg_hdr(buf, MSG_TYPE_CAPABILITY, size);
+
+       switch (capability) {
+       case TLV_TYPE_DYNAMIC_CAP:
+               /*
+                * RFC 5561 - Section 9:
+                * "An LDP speaker MUST NOT include the Dynamic Capability
+                * Announcement Parameter in Capability messages sent to
+                * its peers".
+                */
+               /* FALLTHROUGH */
+       default:
+               fatalx("send_capability: unsupported capability");
+       }
+
+       if (err) {
+               ibuf_free(buf);
+               return;
+       }
+
+       evbuf_enqueue(&nbr->tcp->wbuf, buf);
+       nbr_fsm(nbr, NBR_EVT_PDU_SENT);
+}
+
+int
+recv_capability(struct nbr *nbr, char *buf, uint16_t len)
+{
+       struct ldp_msg   msg;
+
+       log_debug("%s: lsr-id %s", __func__, inet_ntoa(nbr->id));
+
+       memcpy(&msg, buf, sizeof(msg));
+       buf += LDP_MSG_SIZE;
+       len -= LDP_MSG_SIZE;
+
+       /* Optional Parameters */
+       while (len > 0) {
+               struct tlv       tlv;
+               uint16_t         tlv_type;
+               uint16_t         tlv_len;
+
+               if (len < sizeof(tlv)) {
+                       session_shutdown(nbr, S_BAD_TLV_LEN, msg.id, msg.type);
+                       return (-1);
+               }
+
+               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);
+                       return (-1);
+               }
+               buf += TLV_HDR_SIZE;
+               len -= TLV_HDR_SIZE;
+
+               switch (tlv_type) {
+               case TLV_TYPE_DYNAMIC_CAP:
+                       /*
+                        * RFC 5561 - Section 9:
+                        * "An LDP speaker that receives a Capability message
+                        * from a peer that includes the Dynamic Capability
+                        * Announcement Parameter SHOULD silently ignore the
+                        * parameter and process any other Capability Parameters
+                        * in the message".
+                        */
+                       /* FALLTHROUGH */
+               default:
+                       if (!(ntohs(tlv.type) & UNKNOWN_FLAG))
+                               send_notification_rtlvs(nbr, S_UNSSUPORTDCAP,
+                                   msg.id, msg.type, tlv_type, tlv_len, buf);
+                       /* ignore unknown tlv */
+                       break;
+               }
+               buf += tlv_len;
+               len -= tlv_len;
+       }
+
+       nbr_fsm(nbr, NBR_EVT_PDU_RCVD);
+
+       return (0);
+}
+
 static int
 gen_init_prms_tlv(struct ibuf *buf, struct nbr *nbr)
 {
@@ -163,3 +291,17 @@ gen_init_prms_tlv(struct ibuf *buf, struct nbr *nbr)
 
        return (ibuf_add(buf, &parms, SESS_PRMS_SIZE));
 }
+
+static int
+gen_cap_dynamic_tlv(struct ibuf *buf)
+{
+       struct capability_tlv   cap;
+
+       memset(&cap, 0, sizeof(cap));
+       cap.type = htons(TLV_TYPE_DYNAMIC_CAP);
+       cap.length = htons(CAP_TLV_DYNAMIC_LEN);
+       /* the S-bit is always 1 for the Dynamic Capability Announcement */
+       cap.reserved = STATE_BIT;
+
+       return (ibuf_add(buf, &cap, CAP_TLV_DYNAMIC_SIZE));
+}
index d9f71fdf6b592230ce0f89862e53ab94533622ca..e35f01b136c0e21d5efdc09804d60ab856b726d1 100644 (file)
@@ -238,6 +238,7 @@ recv_labelmessage(struct nbr *nbr, char *buf, uint16_t len, uint16_t type)
        /* Optional Parameters */
        while (len > 0) {
                struct tlv      tlv;
+               uint16_t        tlv_type;
                uint16_t        tlv_len;
                uint32_t        reqbuf, labelbuf, statusbuf;
 
@@ -247,6 +248,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);
@@ -255,7 +257,7 @@ 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)) {
+               switch (tlv_type) {
                case TLV_TYPE_LABELREQUEST:
                        switch (type) {
                        case MSG_TYPE_LABELMAPPING:
@@ -340,8 +342,8 @@ recv_labelmessage(struct nbr *nbr, char *buf, uint16_t len, uint16_t type)
                        break;
                default:
                        if (!(ntohs(tlv.type) & UNKNOWN_FLAG))
-                               send_notification(nbr->tcp, 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;
                }
index 88bfa3a822d4045eeec5101c58581ae0eda97338..75cc2cc15adf49a4942828f13526ab5e4b4d1220 100644 (file)
@@ -63,6 +63,7 @@
 #define MSG_TYPE_HELLO         0x0100
 #define MSG_TYPE_INIT          0x0200
 #define MSG_TYPE_KEEPALIVE     0x0201
+#define MSG_TYPE_CAPABILITY    0x0202 /* RFC 5561 */
 #define MSG_TYPE_ADDR          0x0300
 #define MSG_TYPE_ADDRWITHDRAW  0x0301
 #define MSG_TYPE_LABELMAPPING  0x0400
@@ -95,6 +96,9 @@
 #define TLV_TYPE_PW_STATUS     0x896A
 #define TLV_TYPE_PW_IF_PARAM   0x096B
 #define TLV_TYPE_PW_GROUP_ID   0x096C
+/* RFC 5561 */
+#define TLV_TYPE_RETURNED_TLVS 0x8304
+#define TLV_TYPE_DYNAMIC_CAP   0x8506
 /* RFC 7552 */
 #define TLV_TYPE_DUALSTACK     0x8701
 
@@ -196,6 +200,8 @@ struct hello_prms_opt16_tlv {
 #define S_UNASSIGN_TAI 0x00000029
 #define S_MISCONF_ERR  0x0000002A
 #define S_WITHDRAW_MTHD        0x0000002B
+/* RFC 5561 */
+#define        S_UNSSUPORTDCAP 0x0000002E
 /* RFC 7552 */
 #define        S_TRANS_MISMTCH 0x80000032
 #define        S_DS_NONCMPLNCE 0x80000033
@@ -227,6 +233,18 @@ struct status_tlv {
 #define STATUS_TLV_LEN         10
 #define        STATUS_FATAL            0x80000000
 
+struct capability_tlv {
+       uint16_t        type;
+       uint16_t        length;
+       uint8_t         reserved;
+};
+#define STATE_BIT              0x80
+
+#define F_CAP_TLV_RCVD_DYNAMIC 0x01
+
+#define CAP_TLV_DYNAMIC_SIZE   5
+#define CAP_TLV_DYNAMIC_LEN    1
+
 #define        AF_IPV4                 0x1
 #define        AF_IPV6                 0x2
 
index ff3af19db4729d5ce90b5b0ebd7408036b340e2d..43743cca84a24473d19ec87b8821807bd8901509 100644 (file)
@@ -244,10 +244,16 @@ struct notify_msg {
        uint16_t        msg_type;       /* network byte order */
        uint32_t        pw_status;
        struct map      fec;
+       struct {
+               uint16_t         type;
+               uint16_t         length;
+               char            *data;
+       } rtlvs;
        uint8_t         flags;
 };
 #define F_NOTIF_PW_STATUS      0x01    /* pseudowire status tlv present */
 #define F_NOTIF_FEC            0x02    /* fec tlv present */
+#define F_NOTIF_RETURNED_TLVS  0x04    /* returned tlvs present */
 
 struct if_addr {
        LIST_ENTRY(if_addr)      entry;
index d79c9a82ca8b1697d85942feb3cca7f2c199eb87..ce1b700228607d085c748f45b39ab2f729089b4c 100644 (file)
@@ -111,6 +111,7 @@ struct nbr {
        int                      flags;
 };
 #define F_NBR_GTSM_NEGOTIATED   0x01
+#define F_NBR_CAP_DYNAMIC       0x02
 
 RB_HEAD(nbr_id_head, nbr);
 RB_PROTOTYPE(nbr_id_head, nbr, id_tree, nbr_id_compare)
@@ -159,6 +160,8 @@ void         recv_hello(struct in_addr, struct ldp_msg *, int, union ldpd_addr *,
 /* init.c */
 void    send_init(struct nbr *);
 int     recv_init(struct nbr *, char *, uint16_t);
+void    send_capability(struct nbr *, uint16_t, int);
+int     recv_capability(struct nbr *, char *, uint16_t);
 
 /* keepalive.c */
 void    send_keepalive(struct nbr *);
@@ -167,6 +170,8 @@ int  recv_keepalive(struct nbr *, char *, uint16_t);
 /* notification.c */
 void    send_notification_full(struct tcp_conn *, struct notify_msg *);
 void    send_notification(struct tcp_conn *, uint32_t, uint32_t, uint16_t);
+void    send_notification_rtlvs(struct nbr *, uint32_t, uint32_t, uint16_t,
+           uint16_t, uint16_t, char *);
 int     recv_notification(struct nbr *, char *, uint16_t);
 int     gen_status_tlv(struct ibuf *, uint32_t, uint32_t, uint16_t);
 
index 661fe04b1964d6a29eb436e631976186e9b171c5..a02210efe4e88467148450e591ef01d4c0a0d6be 100644 (file)
@@ -464,6 +464,8 @@ msg_name(uint16_t msg)
                return ("initialization");
        case MSG_TYPE_KEEPALIVE:
                return ("keepalive");
+       case MSG_TYPE_CAPABILITY:
+               return ("capability");
        case MSG_TYPE_ADDR:
                return ("address");
        case MSG_TYPE_ADDRWITHDRAW:
@@ -557,6 +559,8 @@ status_code_name(uint32_t status)
                return ("Generic Misconfiguration Error");
        case S_WITHDRAW_MTHD:
                return ("Label Withdraw PW Status Method");
+       case S_UNSSUPORTDCAP:
+               return ("Unsupported Capability");
        case S_TRANS_MISMTCH:
                return ("Transport Connection Mismatch");
        case S_DS_NONCMPLNCE:
index d573925314b603d892e892e5754716a3e4c7085e..69d4ab8028d50683bc372313aba6fd21b2238f7f 100644 (file)
@@ -24,6 +24,7 @@
 #include "ldpe.h"
 #include "ldp_debug.h"
 
+static int      gen_returned_tlvs(struct ibuf *, uint16_t, uint16_t, char *);
 static void     log_msg_notification(int, struct nbr *, struct notify_msg *);
 
 void
@@ -47,6 +48,8 @@ send_notification_full(struct tcp_conn *tcp, struct notify_msg *nm)
                        break;
                }
        }
+       if (nm->flags & F_NOTIF_RETURNED_TLVS)
+               size += TLV_HDR_SIZE * 2 + nm->rtlvs.length;
 
        if ((buf = ibuf_open(size)) == NULL)
                fatal(__func__);
@@ -60,6 +63,9 @@ send_notification_full(struct tcp_conn *tcp, struct notify_msg *nm)
                err |= gen_pw_status_tlv(buf, nm->pw_status);
        if (nm->flags & F_NOTIF_FEC)
                err |= gen_fec_tlv(buf, &nm->fec);
+       if (nm->flags & F_NOTIF_RETURNED_TLVS)
+               err |= gen_returned_tlvs(buf, nm->rtlvs.type, nm->rtlvs.length,
+                   nm->rtlvs.data);
        if (err) {
                ibuf_free(buf);
                return;
@@ -88,6 +94,27 @@ send_notification(struct tcp_conn *tcp, uint32_t status_code, uint32_t msg_id,
        send_notification_full(tcp, &nm);
 }
 
+void
+send_notification_rtlvs(struct nbr *nbr, uint32_t status_code, uint32_t msg_id,
+    uint16_t msg_type, uint16_t tlv_type, uint16_t tlv_len, char *tlv_data)
+{
+       struct notify_msg        nm;
+
+       memset(&nm, 0, sizeof(nm));
+       nm.status_code = status_code;
+       nm.msg_id = msg_id;
+       nm.msg_type = msg_type;
+       /* do not append the given TLV if it's too big (shouldn't happen) */
+       if (tlv_len < 1024) {
+               nm.rtlvs.type = tlv_type;
+               nm.rtlvs.length = tlv_len;
+               nm.rtlvs.data = tlv_data;
+               nm.flags |= F_NOTIF_RETURNED_TLVS;
+       }
+
+       send_notification_full(nbr->tcp, &nm);
+}
+
 int
 recv_notification(struct nbr *nbr, char *buf, uint16_t len)
 {
@@ -120,6 +147,7 @@ recv_notification(struct nbr *nbr, char *buf, uint16_t len)
        /* Optional Parameters */
        while (len > 0) {
                struct tlv      tlv;
+               uint16_t        tlv_type;
                uint16_t        tlv_len;
 
                if (len < sizeof(tlv)) {
@@ -128,6 +156,7 @@ recv_notification(struct nbr *nbr, char *buf, uint16_t len)
                }
 
                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);
@@ -136,7 +165,7 @@ recv_notification(struct nbr *nbr, char *buf, uint16_t len)
                buf += TLV_HDR_SIZE;
                len -= TLV_HDR_SIZE;
 
-               switch (ntohs(tlv.type)) {
+               switch (tlv_type) {
                case TLV_TYPE_EXTSTATUS:
                case TLV_TYPE_RETURNEDPDU:
                case TLV_TYPE_RETURNEDMSG:
@@ -166,8 +195,8 @@ recv_notification(struct nbr *nbr, char *buf, uint16_t len)
                        break;
                default:
                        if (!(ntohs(tlv.type) & UNKNOWN_FLAG))
-                               send_notification(nbr->tcp, 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;
                }
@@ -229,6 +258,26 @@ gen_status_tlv(struct ibuf *buf, uint32_t status_code, uint32_t msg_id,
        return (ibuf_add(buf, &st, STATUS_SIZE));
 }
 
+static int
+gen_returned_tlvs(struct ibuf *buf, uint16_t type, uint16_t length,
+    char *tlv_data)
+{
+       struct tlv       rtlvs;
+       struct tlv       tlv;
+       int              err;
+
+       rtlvs.type = htons(TLV_TYPE_RETURNED_TLVS);
+       rtlvs.length = htons(length + TLV_HDR_SIZE);
+       tlv.type = htons(type);
+       tlv.length = htons(length);
+
+       err = ibuf_add(buf, &rtlvs, sizeof(rtlvs));
+       err |= ibuf_add(buf, &tlv, sizeof(tlv));
+       err |= ibuf_add(buf, tlv_data, length);
+
+       return (err);
+}
+
 void
 log_msg_notification(int out, struct nbr *nbr, struct notify_msg *nm)
 {
index 653f67b8c27dee12805aa9e819d21ea0fe353bd3..a7be0f6b42e0a578ac03736a62ea14f91297486c 100644 (file)
@@ -519,13 +519,7 @@ session_read(struct thread *thread)
                                        return (0);
                                }
                                break;
-                       case MSG_TYPE_ADDR:
-                       case MSG_TYPE_ADDRWITHDRAW:
-                       case MSG_TYPE_LABELMAPPING:
-                       case MSG_TYPE_LABELREQUEST:
-                       case MSG_TYPE_LABELWITHDRAW:
-                       case MSG_TYPE_LABELRELEASE:
-                       case MSG_TYPE_LABELABORTREQ:
+                       default:
                                if (nbr->state != NBR_STA_OPER) {
                                        session_shutdown(nbr, S_SHUTDOWN,
                                            msg->id, msg->type);
@@ -533,8 +527,6 @@ session_read(struct thread *thread)
                                        return (0);
                                }
                                break;
-                       default:
-                               break;
                        }
 
                        /* switch LDP packet type */
@@ -548,6 +540,9 @@ session_read(struct thread *thread)
                        case MSG_TYPE_KEEPALIVE:
                                ret = recv_keepalive(nbr, pdu, msg_size);
                                break;
+                       case MSG_TYPE_CAPABILITY:
+                               ret = recv_capability(nbr, pdu, msg_size);
+                               break;
                        case MSG_TYPE_ADDR:
                        case MSG_TYPE_ADDRWITHDRAW:
                                ret = recv_address(nbr, pdu, msg_size);