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).
---
 init.c         | 152 +++++++++++++++++++++++++++++++++++++++++++++++++++++++--
 labelmapping.c |   8 +--
 ldp.h          |  18 +++++++
 ldpd.8         |  11 +++++
 ldpd.h         |   6 +++
 ldpe.h         |   5 ++
 logmsg.c       |   4 ++
 notification.c |  55 +++++++++++++++++++--
 packet.c       |  13 ++---
 9 files changed, 252 insertions(+), 20 deletions(-)

diff --git a/init.c b/init.c
index 2ba9b37..cc45443 100644
--- a/init.c
+++ b/init.c
@@ -25,6 +25,7 @@
 #include "log.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)
@@ -35,15 +36,16 @@ send_init(struct nbr *nbr)
 
        log_debug("%s: lsr-id %s", __func__, 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;
@@ -58,6 +60,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;
 
        log_debug("%s: lsr-id %s", __func__, inet_ntoa(nbr->id));
 
@@ -94,6 +97,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)) {
@@ -102,6 +106,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);
@@ -110,17 +115,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;
                }
@@ -146,6 +176,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)
 {
@@ -164,3 +292,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));
+}
diff --git a/labelmapping.c b/labelmapping.c
index e19a53f..867d9f9 100644
--- a/labelmapping.c
+++ b/labelmapping.c
@@ -241,6 +241,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;
 
@@ -250,6 +251,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);
@@ -258,7 +260,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:
@@ -343,8 +345,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;
                }
diff --git a/ldp.h b/ldp.h
index 8256b96..f73c8d1 100644
--- a/ldp.h
+++ b/ldp.h
@@ -65,6 +65,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
@@ -97,6 +98,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
 
@@ -198,6 +202,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
@@ -229,6 +235,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
 
diff --git a/ldpd.8 b/ldpd.8
index 918721a..27efbe3 100644
--- a/ldpd.8
+++ b/ldpd.8
@@ -132,6 +132,17 @@ socket used for communication with
 .Re
 .Pp
 .Rs
+.%A B. Thomas
+.%A K. Raza
+.%A S. Aggarwal
+.%A R. Aggarwal
+.%A JL. Le Roux
+.%D July 2009
+.%R RFC 5561
+.%T LDP Capabilities
+.Re
+.Pp
+.Rs
 .%A C. Pignataro
 .%A R. Asati
 .%D August 2012
diff --git a/ldpd.h b/ldpd.h
index 0457fa8..9b9c620 100644
--- a/ldpd.h
+++ b/ldpd.h
@@ -228,10 +228,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;
diff --git a/ldpe.h b/ldpe.h
index 4d757eb..5d279ca 100644
--- a/ldpe.h
+++ b/ldpe.h
@@ -104,6 +104,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)
@@ -152,6 +153,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 *);
@@ -160,6 +163,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);
 
diff --git a/logmsg.c b/logmsg.c
index 2999446..4c279ef 100644
--- a/logmsg.c
+++ b/logmsg.c
@@ -303,6 +303,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:
@@ -396,6 +398,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:
diff --git a/notification.c b/notification.c
index 09c2729..1a64004 100644
--- a/notification.c
+++ b/notification.c
@@ -25,6 +25,7 @@
 #include "log.h"
 #include "ldpe.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
@@ -48,6 +49,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__);
@@ -61,6 +64,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;
@@ -89,6 +95,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)
 {
@@ -121,6 +148,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)) {
@@ -129,6 +157,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);
@@ -137,7 +166,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:
@@ -167,8 +196,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;
                }
@@ -230,6 +259,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)
 {
diff --git a/packet.c b/packet.c
index 797ccac..91d50e3 100644
--- a/packet.c
+++ b/packet.c
@@ -518,13 +518,7 @@ session_read(int fd, short event, void *arg)
                                        return;
                                }
                                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);
@@ -532,8 +526,6 @@ session_read(int fd, short event, void *arg)
                                        return;
                                }
                                break;
-                       default:
-                               break;
                        }
 
                        /* switch LDP packet type */
@@ -547,6 +539,9 @@ session_read(int fd, short event, void *arg)
                        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);
-- 
1.9.1

Reply via email to