From 8819fc38a0610e5828b7cc65fbbfc42e6ea1cc6a Mon Sep 17 00:00:00 2001 From: Renato Westphal Date: Fri, 3 Mar 2017 17:50:22 -0300 Subject: [PATCH] ldpd: implement RFC 5561 (LDP Capabilities) 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 --- ldpd/init.c | 152 ++++++++++++++++++++++++++++++++++++++++++-- ldpd/labelmapping.c | 8 ++- ldpd/ldp.h | 18 ++++++ ldpd/ldpd.h | 6 ++ ldpd/ldpe.h | 5 ++ ldpd/log.c | 4 ++ ldpd/notification.c | 55 +++++++++++++++- ldpd/packet.c | 13 ++-- 8 files changed, 241 insertions(+), 20 deletions(-) diff --git a/ldpd/init.c b/ldpd/init.c index 030bff2f5..4b3eab270 100644 --- a/ldpd/init.c +++ b/ldpd/init.c @@ -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)); +} diff --git a/ldpd/labelmapping.c b/ldpd/labelmapping.c index d9f71fdf6..e35f01b13 100644 --- a/ldpd/labelmapping.c +++ b/ldpd/labelmapping.c @@ -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; } diff --git a/ldpd/ldp.h b/ldpd/ldp.h index 88bfa3a82..75cc2cc15 100644 --- a/ldpd/ldp.h +++ b/ldpd/ldp.h @@ -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 diff --git a/ldpd/ldpd.h b/ldpd/ldpd.h index ff3af19db..43743cca8 100644 --- a/ldpd/ldpd.h +++ b/ldpd/ldpd.h @@ -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; diff --git a/ldpd/ldpe.h b/ldpd/ldpe.h index d79c9a82c..ce1b70022 100644 --- a/ldpd/ldpe.h +++ b/ldpd/ldpe.h @@ -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); diff --git a/ldpd/log.c b/ldpd/log.c index 661fe04b1..a02210efe 100644 --- a/ldpd/log.c +++ b/ldpd/log.c @@ -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: diff --git a/ldpd/notification.c b/ldpd/notification.c index d57392531..69d4ab802 100644 --- a/ldpd/notification.c +++ b/ldpd/notification.c @@ -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) { diff --git a/ldpd/packet.c b/ldpd/packet.c index 653f67b8c..a7be0f6b4 100644 --- a/ldpd/packet.c +++ b/ldpd/packet.c @@ -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); -- 2.39.5