From d4afb81972d9666d730445fa81090d711fc0d54f Mon Sep 17 00:00:00 2001 From: Renato Westphal Date: Fri, 3 Mar 2017 17:50:22 -0300 Subject: [PATCH] ldpd: implement RFC 5918 (Typed Wildcard FEC) Signed-off-by: Renato Westphal --- ldpd/init.c | 68 ++++++++++++++++++++++++- ldpd/labelmapping.c | 120 +++++++++++++++++++++++++++++++++++++++++++- ldpd/lde.c | 14 ++++++ ldpd/lde.h | 3 ++ ldpd/lde_lib.c | 56 +++++++++++++++++++++ ldpd/ldp.h | 8 +++ ldpd/ldpd.h | 6 +++ ldpd/ldpe.h | 1 + ldpd/log.c | 17 +++++++ 9 files changed, 290 insertions(+), 3 deletions(-) diff --git a/ldpd/init.c b/ldpd/init.c index 4b3eab270d..13104b4ee6 100644 --- a/ldpd/init.c +++ b/ldpd/init.c @@ -25,6 +25,7 @@ static int gen_init_prms_tlv(struct ibuf *, struct nbr *); static int gen_cap_dynamic_tlv(struct ibuf *); +static int gen_cap_twcard_tlv(struct ibuf *, int); void send_init(struct nbr *nbr) @@ -36,7 +37,7 @@ 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 + - CAP_TLV_DYNAMIC_SIZE; + CAP_TLV_DYNAMIC_SIZE + CAP_TLV_TWCARD_SIZE; if ((buf = ibuf_open(size)) == NULL) fatal(__func__); @@ -45,6 +46,7 @@ send_init(struct nbr *nbr) err |= gen_msg_hdr(buf, MSG_TYPE_INIT, size); err |= gen_init_prms_tlv(buf, nbr); err |= gen_cap_dynamic_tlv(buf); + err |= gen_cap_twcard_tlv(buf, 1); if (err) { ibuf_free(buf); return; @@ -146,6 +148,25 @@ recv_init(struct nbr *nbr, char *buf, uint16_t len) "Capability Announcement capability", __func__, inet_ntoa(nbr->id)); break; + case TLV_TYPE_TWCARD_CAP: + if (tlv_len != CAP_TLV_TWCARD_LEN) { + session_shutdown(nbr, S_BAD_TLV_LEN, msg.id, + msg.type); + return (-1); + } + + if (caps_rcvd & F_CAP_TLV_RCVD_TWCARD) { + session_shutdown(nbr, S_BAD_TLV_VAL, msg.id, + msg.type); + return (-1); + } + caps_rcvd |= F_CAP_TLV_RCVD_TWCARD; + + nbr->flags |= F_NBR_CAP_TWCARD; + + log_debug("%s: lsr-id %s announced the Typed Wildcard " + "FEC capability", __func__, inet_ntoa(nbr->id)); + break; default: if (!(ntohs(tlv.type) & UNKNOWN_FLAG)) send_notification_rtlvs(nbr, S_UNSSUPORTDCAP, @@ -193,6 +214,9 @@ send_capability(struct nbr *nbr, uint16_t capability, int enable) err |= gen_msg_hdr(buf, MSG_TYPE_CAPABILITY, size); switch (capability) { + case TLV_TYPE_TWCARD_CAP: + err |= gen_cap_twcard_tlv(buf, enable); + break; case TLV_TYPE_DYNAMIC_CAP: /* * RFC 5561 - Section 9: @@ -218,6 +242,8 @@ int recv_capability(struct nbr *nbr, char *buf, uint16_t len) { struct ldp_msg msg; + int enable = 0; + int caps_rcvd = 0; log_debug("%s: lsr-id %s", __func__, inet_ntoa(nbr->id)); @@ -230,6 +256,7 @@ recv_capability(struct nbr *nbr, char *buf, uint16_t len) struct tlv tlv; uint16_t tlv_type; uint16_t tlv_len; + uint8_t reserved; if (len < sizeof(tlv)) { session_shutdown(nbr, S_BAD_TLV_LEN, msg.id, msg.type); @@ -247,6 +274,31 @@ recv_capability(struct nbr *nbr, char *buf, uint16_t len) len -= TLV_HDR_SIZE; switch (tlv_type) { + case TLV_TYPE_TWCARD_CAP: + if (tlv_len != CAP_TLV_TWCARD_LEN) { + session_shutdown(nbr, S_BAD_TLV_LEN, msg.id, + msg.type); + return (-1); + } + + if (caps_rcvd & F_CAP_TLV_RCVD_TWCARD) { + session_shutdown(nbr, S_BAD_TLV_VAL, msg.id, + msg.type); + return (-1); + } + caps_rcvd |= F_CAP_TLV_RCVD_TWCARD; + + memcpy(&reserved, buf, sizeof(reserved)); + enable = reserved & STATE_BIT; + if (enable) + nbr->flags |= F_NBR_CAP_TWCARD; + else + nbr->flags &= ~F_NBR_CAP_TWCARD; + + log_debug("%s: lsr-id %s %s the Typed Wildcard FEC " + "capability", __func__, inet_ntoa(nbr->id), + (enable) ? "announced" : "withdrew"); + break; case TLV_TYPE_DYNAMIC_CAP: /* * RFC 5561 - Section 9: @@ -305,3 +357,17 @@ gen_cap_dynamic_tlv(struct ibuf *buf) return (ibuf_add(buf, &cap, CAP_TLV_DYNAMIC_SIZE)); } + +static int +gen_cap_twcard_tlv(struct ibuf *buf, int enable) +{ + struct capability_tlv cap; + + memset(&cap, 0, sizeof(cap)); + cap.type = htons(TLV_TYPE_TWCARD_CAP); + cap.length = htons(CAP_TLV_TWCARD_LEN); + if (enable) + cap.reserved = STATE_BIT; + + return (ibuf_add(buf, &cap, CAP_TLV_TWCARD_SIZE)); +} diff --git a/ldpd/labelmapping.c b/ldpd/labelmapping.c index e35f01b136..4559ae628b 100644 --- a/ldpd/labelmapping.c +++ b/ldpd/labelmapping.c @@ -90,6 +90,16 @@ send_labelmessage(struct nbr *nbr, uint16_t type, struct mapping_head *mh) if (me->map.flags & F_MAP_PW_STATUS) msg_size += PW_STATUS_TLV_SIZE; break; + case MAP_TYPE_TYPED_WCARD: + msg_size += FEC_ELM_TWCARD_MIN_LEN; + switch (me->map.fec.twcard.type) { + case MAP_TYPE_PREFIX: + msg_size += sizeof(uint16_t); + break; + default: + fatalx("send_labelmessage: unexpected fec type"); + } + break; } if (me->map.label != NO_LABEL) msg_size += LABEL_TLV_SIZE; @@ -208,6 +218,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. @@ -524,7 +552,7 @@ 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; @@ -594,6 +622,43 @@ 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: + 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; + default: + fatalx("gen_fec_tlv: unexpected fec type"); + } + break; default: break; } @@ -606,7 +671,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); @@ -750,6 +815,57 @@ 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; + default: + send_notification(nbr->tcp, S_UNKNOWN_FEC, msg->id, + msg->type); + return (-1); + } + return (off); default: send_notification(nbr->tcp, S_UNKNOWN_FEC, msg->id, msg->type); diff --git a/ldpd/lde.c b/ldpd/lde.c index 3041c44bdc..948a4468cd 100644 --- a/ldpd/lde.c +++ b/ldpd/lde.c @@ -1003,6 +1003,20 @@ lde_send_labelwithdraw_wcard(struct lde_nbr *ln, uint32_t label) lde_send_labelwithdraw(ln, NULL, &wcard, NULL); } +void +lde_send_labelwithdraw_twcard_prefix(struct lde_nbr *ln, uint16_t af, + uint32_t label) +{ + struct map wcard; + + memset(&wcard, 0, sizeof(wcard)); + wcard.type = MAP_TYPE_TYPED_WCARD; + wcard.fec.twcard.type = MAP_TYPE_PREFIX; + wcard.fec.twcard.u.prefix_af = af; + wcard.label = label; + lde_send_labelwithdraw(ln, NULL, &wcard, NULL); +} + void lde_send_labelwithdraw_pwid_wcard(struct lde_nbr *ln, uint16_t pw_type, uint32_t group_id) diff --git a/ldpd/lde.h b/ldpd/lde.h index 367ba254db..148e583466 100644 --- a/ldpd/lde.h +++ b/ldpd/lde.h @@ -145,6 +145,8 @@ void lde_send_labelmapping(struct lde_nbr *, struct fec_node *, void lde_send_labelwithdraw(struct lde_nbr *, struct fec_node *, struct map *, struct status_tlv *); void lde_send_labelwithdraw_wcard(struct lde_nbr *, uint32_t); +void lde_send_labelwithdraw_twcard_prefix(struct lde_nbr *, + uint16_t, uint32_t); void lde_send_labelwithdraw_pwid_wcard(struct lde_nbr *, uint16_t, uint32_t); void lde_send_labelrelease(struct lde_nbr *, struct fec_node *, @@ -181,6 +183,7 @@ void lde_kernel_remove(struct fec *, int, union ldpd_addr *, void lde_kernel_update(struct fec *); void lde_check_mapping(struct map *, struct lde_nbr *); void lde_check_request(struct map *, struct lde_nbr *); +void lde_check_request_wcard(struct map *, struct lde_nbr *); void lde_check_release(struct map *, struct lde_nbr *); void lde_check_release_wcard(struct map *, struct lde_nbr *); void lde_check_withdraw(struct map *, struct lde_nbr *); diff --git a/ldpd/lde_lib.c b/ldpd/lde_lib.c index c37a9e9660..71ab05f04d 100644 --- a/ldpd/lde_lib.c +++ b/ldpd/lde_lib.c @@ -555,6 +555,12 @@ lde_check_request(struct map *map, struct lde_nbr *ln) struct fec_node *fn; struct fec_nh *fnh; + /* wildcard label request */ + if (map->type == MAP_TYPE_TYPED_WCARD) { + lde_check_request_wcard(map, ln); + return; + } + /* LRq.1: skip loop detection (not necessary) */ /* LRq.2: is there a next hop for fec? */ @@ -605,6 +611,40 @@ lde_check_request(struct map *map, struct lde_nbr *ln) */ } +void +lde_check_request_wcard(struct map *map, struct lde_nbr *ln) +{ + struct fec *f; + struct fec_node *fn; + struct lde_req *lre; + + RB_FOREACH(f, fec_tree, &ft) { + fn = (struct fec_node *)f; + + /* only a typed wildcard is possible here */ + if (lde_wildcard_apply(map, &fn->fec, NULL) == 0) + continue; + + /* LRq.2: is there a next hop for fec? */ + if (LIST_EMPTY(&fn->nexthops)) + continue; + + /* LRq.6: first check if we have a pending request running */ + lre = (struct lde_req *)fec_find(&ln->recv_req, &fn->fec); + if (lre != NULL) + /* LRq.7: duplicate request */ + continue; + + /* LRq.8: record label request */ + lre = lde_req_add(ln, &fn->fec, 0); + if (lre != NULL) + lre->msg_id = ntohl(map->msg_id); + + /* LRq.9: perform LSR label distribution */ + lde_send_labelmapping(ln, fn, 1); + } +} + void lde_check_release(struct map *map, struct lde_nbr *ln) { @@ -615,6 +655,7 @@ lde_check_release(struct map *map, struct lde_nbr *ln) /* wildcard label release */ if (map->type == MAP_TYPE_WILDCARD || + map->type == MAP_TYPE_TYPED_WCARD || (map->type == MAP_TYPE_PWID && !(map->flags & F_MAP_PW_ID))) { lde_check_release_wcard(map, ln); return; @@ -690,6 +731,7 @@ lde_check_withdraw(struct map *map, struct lde_nbr *ln) /* wildcard label withdraw */ if (map->type == MAP_TYPE_WILDCARD || + map->type == MAP_TYPE_TYPED_WCARD || (map->type == MAP_TYPE_PWID && !(map->flags & F_MAP_PW_ID))) { lde_check_withdraw_wcard(map, ln); return; @@ -793,6 +835,20 @@ lde_wildcard_apply(struct map *wcard, struct fec *fec, struct lde_map *me) case MAP_TYPE_WILDCARD: /* full wildcard */ return (1); + case MAP_TYPE_TYPED_WCARD: + switch (wcard->fec.twcard.type) { + case MAP_TYPE_PREFIX: + if (wcard->fec.twcard.u.prefix_af == AF_INET && + fec->type != FEC_TYPE_IPV4) + return (0); + if (wcard->fec.twcard.u.prefix_af == AF_INET6 && + fec->type != FEC_TYPE_IPV6) + return (0); + return (1); + default: + fatalx("lde_wildcard_apply: unexpected fec type"); + } + break; case MAP_TYPE_PWID: /* RFC4447 pw-id group wildcard */ if (fec->type != FEC_TYPE_PWID) diff --git a/ldpd/ldp.h b/ldpd/ldp.h index 75cc2cc15a..2edf0475c9 100644 --- a/ldpd/ldp.h +++ b/ldpd/ldp.h @@ -99,6 +99,8 @@ /* RFC 5561 */ #define TLV_TYPE_RETURNED_TLVS 0x8304 #define TLV_TYPE_DYNAMIC_CAP 0x8506 +/* RFC 5918 */ +#define TLV_TYPE_TWCARD_CAP 0x850B /* RFC 7552 */ #define TLV_TYPE_DUALSTACK 0x8701 @@ -241,10 +243,14 @@ struct capability_tlv { #define STATE_BIT 0x80 #define F_CAP_TLV_RCVD_DYNAMIC 0x01 +#define F_CAP_TLV_RCVD_TWCARD 0x02 #define CAP_TLV_DYNAMIC_SIZE 5 #define CAP_TLV_DYNAMIC_LEN 1 +#define CAP_TLV_TWCARD_SIZE 5 +#define CAP_TLV_TWCARD_LEN 1 + #define AF_IPV4 0x1 #define AF_IPV6 0x2 @@ -261,9 +267,11 @@ struct address_list_tlv { #define FEC_ELM_PREFIX_MIN_LEN 4 #define FEC_PWID_ELM_MIN_LEN 8 #define FEC_PWID_SIZE 4 +#define FEC_ELM_TWCARD_MIN_LEN 3 #define MAP_TYPE_WILDCARD 0x01 #define MAP_TYPE_PREFIX 0x02 +#define MAP_TYPE_TYPED_WCARD 0x05 #define MAP_TYPE_PWID 0x80 #define MAP_TYPE_GENPWID 0x81 diff --git a/ldpd/ldpd.h b/ldpd/ldpd.h index 43743cca84..36594e54ec 100644 --- a/ldpd/ldpd.h +++ b/ldpd/ldpd.h @@ -220,6 +220,12 @@ struct map { uint32_t group_id; uint16_t ifmtu; } pwid; + struct { + uint8_t type; + union { + uint16_t prefix_af; + } u; + } twcard; } fec; struct { uint32_t status_code; diff --git a/ldpd/ldpe.h b/ldpd/ldpe.h index ce1b700228..24512989de 100644 --- a/ldpd/ldpe.h +++ b/ldpd/ldpe.h @@ -112,6 +112,7 @@ struct nbr { }; #define F_NBR_GTSM_NEGOTIATED 0x01 #define F_NBR_CAP_DYNAMIC 0x02 +#define F_NBR_CAP_TWCARD 0x04 RB_HEAD(nbr_id_head, nbr); RB_PROTOTYPE(nbr_id_head, nbr, id_tree, nbr_id_compare) diff --git a/ldpd/log.c b/ldpd/log.c index a02210efe4..0e85daea29 100644 --- a/ldpd/log.c +++ b/ldpd/log.c @@ -332,6 +332,23 @@ log_map(const struct map *map) pw_type_name(map->fec.pwid.type)) == -1) return ("???"); break; + case MAP_TYPE_TYPED_WCARD: + if (snprintf(buf, sizeof(buf), "typed wildcard") < 0) + return ("???"); + switch (map->fec.twcard.type) { + case MAP_TYPE_PREFIX: + if (snprintf(buf + strlen(buf), sizeof(buf) - + strlen(buf), " (prefix, address-family %s)", + af_name(map->fec.twcard.u.prefix_af)) < 0) + return ("???"); + break; + default: + if (snprintf(buf + strlen(buf), sizeof(buf) - + strlen(buf), " (unknown type)") < 0) + return ("???"); + break; + } + break; default: return ("???"); }