diff --git a/ldpd/address.c b/ldpd/address.c index 1c4c116f21..ad23ca690b 100644 --- a/ldpd/address.c +++ b/ldpd/address.c @@ -26,10 +26,14 @@ static void send_address(struct nbr *, int, struct if_addr_head *, unsigned int, int); -static int gen_address_list_tlv(struct ibuf *, uint16_t, int, - struct if_addr_head *, unsigned int); +static int gen_address_list_tlv(struct ibuf *, int, struct if_addr_head *, + unsigned int); +static int gen_mac_list_tlv(struct ibuf *, uint8_t *); static void address_list_add(struct if_addr_head *, struct if_addr *); static void address_list_clr(struct if_addr_head *); +static void log_msg_address(int, uint16_t, struct nbr *, int, + union ldpd_addr *); +static void log_msg_mac_withdrawal(int, struct nbr *, uint8_t *); static void send_address(struct nbr *nbr, int af, struct if_addr_head *addr_list, @@ -83,8 +87,7 @@ send_address(struct nbr *nbr, int af, struct if_addr_head *addr_list, size -= LDP_HDR_SIZE; err |= gen_msg_hdr(buf, msg_type, size); size -= LDP_MSG_SIZE; - err |= gen_address_list_tlv(buf, size, af, addr_list, - tlv_addr_count); + err |= gen_address_list_tlv(buf, af, addr_list, tlv_addr_count); if (err) { address_list_clr(addr_list); ibuf_free(buf); @@ -92,9 +95,7 @@ send_address(struct nbr *nbr, int af, struct if_addr_head *addr_list, } while ((if_addr = LIST_FIRST(addr_list)) != NULL) { - debug_msg_send("%s: lsr-id %s address %s", - msg_name(msg_type), inet_ntoa(nbr->id), - log_addr(af, &if_addr->addr)); + log_msg_address(1, msg_type, nbr, af, &if_addr->addr); LIST_REMOVE(if_addr, entry); free(if_addr); @@ -137,16 +138,63 @@ send_address_all(struct nbr *nbr, int af) send_address(nbr, af, &addr_list, addr_count, 0); } +void +send_mac_withdrawal(struct nbr *nbr, struct map *fec, uint8_t *mac) +{ + struct ibuf *buf; + uint16_t size; + int err; + + size = LDP_HDR_SIZE + LDP_MSG_SIZE + ADDR_LIST_SIZE + len_fec_tlv(fec) + + TLV_HDR_SIZE; + if (mac) + size += ETHER_ADDR_LEN; + + 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_ADDRWITHDRAW, size); + size -= LDP_MSG_SIZE; + err |= gen_address_list_tlv(buf, AF_INET, NULL, 0); + err |= gen_fec_tlv(buf, fec); + err |= gen_mac_list_tlv(buf, mac); + if (err) { + ibuf_free(buf); + return; + } + + log_msg_mac_withdrawal(1, nbr, mac); + + evbuf_enqueue(&nbr->tcp->wbuf, buf); + + nbr_fsm(nbr, NBR_EVT_PDU_SENT); +} + int recv_address(struct nbr *nbr, char *buf, uint16_t len) { struct ldp_msg msg; uint16_t msg_type; - struct address_list_tlv alt; enum imsg_type type; + struct address_list_tlv alt; + uint16_t alt_len; + uint16_t alt_family; struct lde_addr lde_addr; memcpy(&msg, buf, sizeof(msg)); + msg_type = ntohs(msg.type); + switch (msg_type) { + case MSG_TYPE_ADDR: + type = IMSG_ADDRESS_ADD; + break; + case MSG_TYPE_ADDRWITHDRAW: + type = IMSG_ADDRESS_DEL; + break; + default: + fatalx("recv_address: unexpected msg type"); + } buf += LDP_MSG_SIZE; len -= LDP_MSG_SIZE; @@ -155,17 +203,18 @@ recv_address(struct nbr *nbr, char *buf, uint16_t len) session_shutdown(nbr, S_BAD_MSG_LEN, msg.id, msg.type); return (-1); } - memcpy(&alt, buf, sizeof(alt)); - if (ntohs(alt.length) != len - TLV_HDR_SIZE) { + alt_len = ntohs(alt.length); + alt_family = ntohs(alt.family); + if (alt_len > len - TLV_HDR_SIZE) { session_shutdown(nbr, S_BAD_TLV_LEN, msg.id, msg.type); return (-1); } if (ntohs(alt.type) != TLV_TYPE_ADDRLIST) { - session_shutdown(nbr, S_UNKNOWN_TLV, msg.id, msg.type); + send_notification(nbr->tcp, S_MISS_MSG, msg.id, msg.type); return (-1); } - switch (ntohs(alt.family)) { + switch (alt_family) { case AF_IPV4: if (!nbr->v4_enabled) /* just ignore the message */ @@ -177,22 +226,18 @@ recv_address(struct nbr *nbr, char *buf, uint16_t len) return (0); break; default: - send_notification_nbr(nbr, S_UNSUP_ADDR, msg.id, msg.type); + send_notification(nbr->tcp, S_UNSUP_ADDR, msg.id, msg.type); return (-1); } + alt_len -= sizeof(alt.family); buf += sizeof(alt); len -= sizeof(alt); - msg_type = ntohs(msg.type); - if (msg_type == MSG_TYPE_ADDR) - type = IMSG_ADDRESS_ADD; - else - type = IMSG_ADDRESS_DEL; - - while (len > 0) { - switch (ntohs(alt.family)) { + /* Process all received addresses */ + while (alt_len > 0) { + switch (alt_family) { case AF_IPV4: - if (len < sizeof(struct in_addr)) { + if (alt_len < sizeof(struct in_addr)) { session_shutdown(nbr, S_BAD_TLV_LEN, msg.id, msg.type); return (-1); @@ -204,9 +249,10 @@ recv_address(struct nbr *nbr, char *buf, uint16_t len) buf += sizeof(struct in_addr); len -= sizeof(struct in_addr); + alt_len -= sizeof(struct in_addr); break; case AF_IPV6: - if (len < sizeof(struct in6_addr)) { + if (alt_len < sizeof(struct in6_addr)) { session_shutdown(nbr, S_BAD_TLV_LEN, msg.id, msg.type); return (-1); @@ -218,24 +264,57 @@ recv_address(struct nbr *nbr, char *buf, uint16_t len) buf += sizeof(struct in6_addr); len -= sizeof(struct in6_addr); + alt_len -= sizeof(struct in6_addr); break; default: fatalx("recv_address: unknown af"); } - debug_msg_recv("%s: lsr-id %s address %s", msg_name(msg_type), - inet_ntoa(nbr->id), log_addr(lde_addr.af, &lde_addr.addr)); + log_msg_address(0, msg_type, nbr, lde_addr.af, &lde_addr.addr); ldpe_imsg_compose_lde(type, nbr->peerid, 0, &lde_addr, sizeof(lde_addr)); } + /* 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) { + default: + if (!(ntohs(tlv.type) & UNKNOWN_FLAG)) + 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; + } + return (0); } static int -gen_address_list_tlv(struct ibuf *buf, uint16_t size, int af, - struct if_addr_head *addr_list, unsigned int tlv_addr_count) +gen_address_list_tlv(struct ibuf *buf, int af, struct if_addr_head *addr_list, + unsigned int tlv_addr_count) { struct address_list_tlv alt; uint16_t addr_size; @@ -243,8 +322,7 @@ gen_address_list_tlv(struct ibuf *buf, uint16_t size, int af, int err = 0; memset(&alt, 0, sizeof(alt)); - alt.type = TLV_TYPE_ADDRLIST; - alt.length = htons(size - TLV_HDR_SIZE); + alt.type = htons(TLV_TYPE_ADDRLIST); switch (af) { case AF_INET: @@ -258,8 +336,12 @@ gen_address_list_tlv(struct ibuf *buf, uint16_t size, int af, default: fatalx("gen_address_list_tlv: unknown af"); } + alt.length = htons(sizeof(alt.family) + addr_size * tlv_addr_count); err |= ibuf_add(buf, &alt, sizeof(alt)); + if (addr_list == NULL) + return (err); + LIST_FOREACH(if_addr, addr_list, entry) { err |= ibuf_add(buf, &if_addr->addr, addr_size); if (--tlv_addr_count == 0) @@ -269,6 +351,23 @@ gen_address_list_tlv(struct ibuf *buf, uint16_t size, int af, return (err); } +static int +gen_mac_list_tlv(struct ibuf *buf, uint8_t *mac) +{ + struct tlv tlv; + int err; + + memset(&tlv, 0, sizeof(tlv)); + tlv.type = htons(TLV_TYPE_MAC_LIST); + if (mac) + tlv.length = htons(ETHER_ADDR_LEN); + err = ibuf_add(buf, &tlv, sizeof(tlv)); + if (mac) + err |= ibuf_add(buf, mac, ETHER_ADDR_LEN); + + return (err); +} + static void address_list_add(struct if_addr_head *addr_list, struct if_addr *if_addr) { @@ -292,3 +391,21 @@ address_list_clr(struct if_addr_head *addr_list) free(if_addr); } } + +static void +log_msg_address(int out, uint16_t msg_type, struct nbr *nbr, int af, + union ldpd_addr *addr) +{ + debug_msg(out, "%s: lsr-id %s, address %s", msg_name(msg_type), + inet_ntoa(nbr->id), log_addr(af, addr)); +} + +static void +log_msg_mac_withdrawal(int out, struct nbr *nbr, uint8_t *mac) +{ + char buf[ETHER_ADDR_STRLEN]; + + debug_msg(out, "mac withdrawal: lsr-id %s, mac %s", inet_ntoa(nbr->id), + (mac) ? prefix_mac2str((struct ethaddr *)mac, buf, sizeof(buf)) : + "wildcard"); +} diff --git a/ldpd/control.c b/ldpd/control.c index 8a2280be07..0bfe0abc9d 100644 --- a/ldpd/control.c +++ b/ldpd/control.c @@ -148,9 +148,10 @@ control_connbyfd(int fd) { struct ctl_conn *c; - for (c = TAILQ_FIRST(&ctl_conns); c != NULL && c->iev.ibuf.fd != fd; - c = TAILQ_NEXT(c, entry)) - ; /* nothing */ + TAILQ_FOREACH(c, &ctl_conns, entry) { + if (c->iev.ibuf.fd == fd) + break; + } return (c); } @@ -160,9 +161,10 @@ control_connbypid(pid_t pid) { struct ctl_conn *c; - for (c = TAILQ_FIRST(&ctl_conns); c != NULL && c->iev.ibuf.pid != pid; - c = TAILQ_NEXT(c, entry)) - ; /* nothing */ + TAILQ_FOREACH(c, &ctl_conns, entry) { + if (c->iev.ibuf.pid == pid) + break; + } return (c); } diff --git a/ldpd/init.c b/ldpd/init.c index ed6b53c02d..bc3a69edc7 100644 --- a/ldpd/init.c +++ b/ldpd/init.c @@ -24,6 +24,9 @@ #include "ldp_debug.h" 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); +static int gen_cap_unotif_tlv(struct ibuf *, int); void send_init(struct nbr *nbr) @@ -34,15 +37,18 @@ 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 + CAP_TLV_TWCARD_SIZE + CAP_TLV_UNOTIF_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); + err |= gen_cap_twcard_tlv(buf, 1); + err |= gen_cap_unotif_tlv(buf, 1); if (err) { ibuf_free(buf); return; @@ -57,6 +63,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 +100,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 +109,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 +118,81 @@ 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; + 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; + case TLV_TYPE_UNOTIF_CAP: + if (tlv_len != CAP_TLV_UNOTIF_LEN) { + session_shutdown(nbr, S_BAD_TLV_LEN, msg.id, + msg.type); + return (-1); + } + + if (caps_rcvd & F_CAP_TLV_RCVD_UNOTIF) { + session_shutdown(nbr, S_BAD_TLV_VAL, msg.id, + msg.type); + return (-1); + } + caps_rcvd |= F_CAP_TLV_RCVD_UNOTIF; + + nbr->flags |= F_NBR_CAP_UNOTIF; + + log_debug("%s: lsr-id %s announced the Unrecognized " + "Notification capability", __func__, + inet_ntoa(nbr->id)); + break; default: if (!(ntohs(tlv.type) & UNKNOWN_FLAG)) - send_notification_nbr(nbr, 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 +218,164 @@ 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_TWCARD_CAP: + err |= gen_cap_twcard_tlv(buf, enable); + break; + case TLV_TYPE_UNOTIF_CAP: + err |= gen_cap_unotif_tlv(buf, enable); + break; + 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; + int enable = 0; + int caps_rcvd = 0; + + 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; + uint8_t reserved; + + 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_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_UNOTIF_CAP: + if (tlv_len != CAP_TLV_UNOTIF_LEN) { + session_shutdown(nbr, S_BAD_TLV_LEN, msg.id, + msg.type); + return (-1); + } + + if (caps_rcvd & F_CAP_TLV_RCVD_UNOTIF) { + session_shutdown(nbr, S_BAD_TLV_VAL, msg.id, + msg.type); + return (-1); + } + caps_rcvd |= F_CAP_TLV_RCVD_UNOTIF; + + memcpy(&reserved, buf, sizeof(reserved)); + enable = reserved & STATE_BIT; + if (enable) + nbr->flags |= F_NBR_CAP_UNOTIF; + else + nbr->flags &= ~F_NBR_CAP_UNOTIF; + + log_debug("%s: lsr-id %s %s the Unrecognized " + "Notification capability", __func__, + inet_ntoa(nbr->id), (enable) ? "announced" : + "withdrew"); + break; + 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 +394,45 @@ 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)); +} + +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)); +} + +static int +gen_cap_unotif_tlv(struct ibuf *buf, int enable) +{ + struct capability_tlv cap; + + memset(&cap, 0, sizeof(cap)); + cap.type = htons(TLV_TYPE_UNOTIF_CAP); + cap.length = htons(CAP_TLV_UNOTIF_LEN); + if (enable) + cap.reserved = STATE_BIT; + + return (ibuf_add(buf, &cap, CAP_TLV_UNOTIF_SIZE)); +} diff --git a/ldpd/l2vpn.c b/ldpd/l2vpn.c index 792608d425..3f4e21e685 100644 --- a/ldpd/l2vpn.c +++ b/ldpd/l2vpn.c @@ -152,6 +152,33 @@ l2vpn_if_find_name(struct l2vpn *l2vpn, const char *ifname) return (RB_FIND(l2vpn_if_head, &l2vpn->if_tree, &lif)); } +void +l2vpn_if_update(struct l2vpn_if *lif) +{ + struct l2vpn *l2vpn = lif->l2vpn; + struct l2vpn_pw *pw; + struct map fec; + struct nbr *nbr; + + if ((lif->flags & IFF_UP) && (lif->flags & IFF_RUNNING)) + return; + + RB_FOREACH(pw, l2vpn_pw_head, &l2vpn->pw_tree) { + nbr = nbr_find_ldpid(pw->lsr_id.s_addr); + if (nbr == NULL) + continue; + + memset(&fec, 0, sizeof(fec)); + fec.type = MAP_TYPE_PWID; + fec.fec.pwid.type = l2vpn->pw_type; + fec.fec.pwid.group_id = 0; + fec.flags |= F_MAP_PW_ID; + fec.fec.pwid.pwid = pw->pwid; + + send_mac_withdrawal(nbr, &fec, lif->mac); + } +} + static __inline int l2vpn_pw_compare(struct l2vpn_pw *a, struct l2vpn_pw *b) { @@ -330,7 +357,7 @@ l2vpn_pw_negotiate(struct lde_nbr *ln, struct fec_node *fn, struct map *map) st.status_code = S_WRONG_CBIT; st.msg_id = map->msg_id; st.msg_type = htons(MSG_TYPE_LABELMAPPING); - lde_send_labelwithdraw(ln, fn, NO_LABEL, &st); + lde_send_labelwithdraw(ln, fn, NULL, &st); pw->flags &= ~F_PW_CWORD; lde_send_labelmapping(ln, fn, 1); @@ -353,7 +380,7 @@ l2vpn_pw_negotiate(struct lde_nbr *ln, struct fec_node *fn, struct map *map) } void -l2vpn_send_pw_status(uint32_t peerid, uint32_t status, struct fec *fec) +l2vpn_send_pw_status(struct lde_nbr *ln, uint32_t status, struct fec *fec) { struct notify_msg nm; @@ -364,8 +391,27 @@ l2vpn_send_pw_status(uint32_t peerid, uint32_t status, struct fec *fec) lde_fec2map(fec, &nm.fec); nm.flags |= F_NOTIF_FEC; - lde_imsg_compose_ldpe(IMSG_NOTIFICATION_SEND, peerid, 0, - &nm, sizeof(nm)); + lde_imsg_compose_ldpe(IMSG_NOTIFICATION_SEND, ln->peerid, 0, &nm, + sizeof(nm)); +} + +void +l2vpn_send_pw_status_wcard(struct lde_nbr *ln, uint32_t status, + uint16_t pw_type, uint32_t group_id) +{ + struct notify_msg nm; + + memset(&nm, 0, sizeof(nm)); + nm.status_code = S_PW_STATUS; + nm.pw_status = status; + nm.flags |= F_NOTIF_PW_STATUS; + nm.fec.type = MAP_TYPE_PWID; + nm.fec.fec.pwid.type = pw_type; + nm.fec.fec.pwid.group_id = group_id; + nm.flags |= F_NOTIF_FEC; + + lde_imsg_compose_ldpe(IMSG_NOTIFICATION_SEND, ln->peerid, 0, &nm, + sizeof(nm)); } void @@ -376,9 +422,11 @@ l2vpn_recv_pw_status(struct lde_nbr *ln, struct notify_msg *nm) struct fec_nh *fnh; struct l2vpn_pw *pw; - /* TODO group wildcard */ - if (!(nm->fec.flags & F_MAP_PW_ID)) + if (nm->fec.type == MAP_TYPE_TYPED_WCARD || + !(nm->fec.flags & F_MAP_PW_ID)) { + l2vpn_recv_pw_status_wcard(ln, nm); return; + } lde_map2fec(&nm->fec, ln->id, &fec); fn = (struct fec_node *)fec_find(&ft, &fec); @@ -397,7 +445,6 @@ l2vpn_recv_pw_status(struct lde_nbr *ln, struct notify_msg *nm) /* remote status didn't change */ if (pw->remote_status == nm->pw_status) return; - pw->remote_status = nm->pw_status; if (l2vpn_pw_ok(pw, fnh)) @@ -406,6 +453,56 @@ l2vpn_recv_pw_status(struct lde_nbr *ln, struct notify_msg *nm) lde_send_delete_klabel(fn, fnh); } +/* RFC4447 PWid group wildcard */ +void +l2vpn_recv_pw_status_wcard(struct lde_nbr *ln, struct notify_msg *nm) +{ + struct fec *f; + struct fec_node *fn; + struct fec_nh *fnh; + struct l2vpn_pw *pw; + struct map *wcard = &nm->fec; + + RB_FOREACH(f, fec_tree, &ft) { + fn = (struct fec_node *)f; + if (fn->fec.type != FEC_TYPE_PWID) + continue; + + pw = (struct l2vpn_pw *) fn->data; + if (pw == NULL) + continue; + + switch (wcard->type) { + case MAP_TYPE_TYPED_WCARD: + if (wcard->fec.twcard.u.pw_type != PW_TYPE_WILDCARD && + wcard->fec.twcard.u.pw_type != fn->fec.u.pwid.type) + continue; + break; + case MAP_TYPE_PWID: + if (wcard->fec.pwid.type != fn->fec.u.pwid.type) + continue; + if (wcard->fec.pwid.group_id != pw->remote_group) + continue; + break; + } + + fnh = fec_nh_find(fn, AF_INET, (union ldpd_addr *)&ln->id, + 0, 0); + if (fnh == NULL) + continue; + + /* remote status didn't change */ + if (pw->remote_status == nm->pw_status) + continue; + pw->remote_status = nm->pw_status; + + if (l2vpn_pw_ok(pw, fnh)) + lde_send_change_klabel(fn, fnh); + else + lde_send_delete_klabel(fn, fnh); + } +} + void l2vpn_sync_pws(int af, union ldpd_addr *addr) { diff --git a/ldpd/labelmapping.c b/ldpd/labelmapping.c index 62f2a620d2..e8ce7fbdf5 100644 --- a/ldpd/labelmapping.c +++ b/ldpd/labelmapping.c @@ -28,9 +28,8 @@ static void enqueue_pdu(struct nbr *, 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) @@ -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) @@ -124,9 +106,7 @@ 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); TAILQ_REMOVE(mh, me, entry); free(me); @@ -146,7 +126,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 +144,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 +168,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 +190,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 +225,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 +238,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 +247,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 +284,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 +295,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_IPV4NULL && + label != MPLS_LABEL_IPV6NULL && + label != MPLS_LABEL_IMPLNULL)) { + session_shutdown(nbr, S_BAD_TLV_VAL, + msg.id, msg.type); + goto err; + } break; default: /* ignore */ @@ -301,6 +314,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 +355,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. */ @@ -396,9 +411,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: @@ -423,14 +436,14 @@ recv_labelmessage(struct nbr *nbr, char *buf, uint16_t len, uint16_t type) ldpe_imsg_compose_lde(imsg_type, nbr->peerid, 0, &me->map, sizeof(struct map)); -next: + next: TAILQ_REMOVE(&mh, me, entry); free(me); } return (0); -err: + err: mapping_list_clr(&mh); return (-1); @@ -449,53 +462,6 @@ gen_label_tlv(struct ibuf *buf, uint32_t label) return (ibuf_add(buf, <, 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(<, 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 +486,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; @@ -562,7 +568,7 @@ gen_fec_tlv(struct ibuf *buf, struct map *map) break; case MAP_TYPE_PWID: if (map->flags & F_MAP_PW_ID) - pw_len += PW_STATUS_TLV_LEN; + pw_len += FEC_PWID_SIZE; if (map->flags & F_MAP_PW_IFMTU) pw_len += FEC_SUBTLV_IFMTU_SIZE; @@ -595,6 +601,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 +657,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 +692,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 +801,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)); +} diff --git a/ldpd/lde.c b/ldpd/lde.c index 1323ba3d02..08339c720a 100644 --- a/ldpd/lde.c +++ b/ldpd/lde.c @@ -37,7 +37,7 @@ static void lde_shutdown(void); static int lde_dispatch_imsg(struct thread *); static int lde_dispatch_parent(struct thread *); -static __inline int lde_nbr_compare(struct lde_nbr *, +static __inline int lde_nbr_compare(struct lde_nbr *, struct lde_nbr *); static struct lde_nbr *lde_nbr_new(uint32_t, struct lde_nbr *); static void lde_nbr_del(struct lde_nbr *); @@ -205,9 +205,9 @@ lde_dispatch_imsg(struct thread *thread) struct imsgbuf *ibuf = &iev->ibuf; struct imsg imsg; struct lde_nbr *ln; - struct map map; - struct lde_addr lde_addr; - struct notify_msg nm; + struct map *map; + struct lde_addr *lde_addr; + struct notify_msg *nm; ssize_t n; int shut = 0; @@ -240,9 +240,10 @@ lde_dispatch_imsg(struct thread *thread) case IMSG_LABEL_RELEASE: case IMSG_LABEL_WITHDRAW: case IMSG_LABEL_ABORT: - if (imsg.hdr.len - IMSG_HEADER_SIZE != sizeof(map)) + if (imsg.hdr.len - IMSG_HEADER_SIZE != + sizeof(struct map)) fatalx("lde_dispatch_imsg: wrong imsg len"); - memcpy(&map, imsg.data, sizeof(map)); + map = imsg.data; ln = lde_nbr_find(imsg.hdr.peerid); if (ln == NULL) { @@ -253,22 +254,16 @@ lde_dispatch_imsg(struct thread *thread) switch (imsg.hdr.type) { case IMSG_LABEL_MAPPING: - lde_check_mapping(&map, ln); + lde_check_mapping(map, ln); break; case IMSG_LABEL_REQUEST: - lde_check_request(&map, ln); + lde_check_request(map, ln); break; case IMSG_LABEL_RELEASE: - if (map.type == MAP_TYPE_WILDCARD) - lde_check_release_wcard(&map, ln); - else - lde_check_release(&map, ln); + lde_check_release(map, ln); break; case IMSG_LABEL_WITHDRAW: - if (map.type == MAP_TYPE_WILDCARD) - lde_check_withdraw_wcard(&map, ln); - else - lde_check_withdraw(&map, ln); + lde_check_withdraw(map, ln); break; case IMSG_LABEL_ABORT: /* not necessary */ @@ -276,9 +271,10 @@ lde_dispatch_imsg(struct thread *thread) } break; case IMSG_ADDRESS_ADD: - if (imsg.hdr.len - IMSG_HEADER_SIZE != sizeof(lde_addr)) + if (imsg.hdr.len - IMSG_HEADER_SIZE != + sizeof(struct lde_addr)) fatalx("lde_dispatch_imsg: wrong imsg len"); - memcpy(&lde_addr, imsg.data, sizeof(lde_addr)); + lde_addr = imsg.data; ln = lde_nbr_find(imsg.hdr.peerid); if (ln == NULL) { @@ -286,16 +282,17 @@ lde_dispatch_imsg(struct thread *thread) __func__); break; } - if (lde_address_add(ln, &lde_addr) < 0) { + if (lde_address_add(ln, lde_addr) < 0) { log_debug("%s: cannot add address %s, it " "already exists", __func__, - log_addr(lde_addr.af, &lde_addr.addr)); + log_addr(lde_addr->af, &lde_addr->addr)); } break; case IMSG_ADDRESS_DEL: - if (imsg.hdr.len - IMSG_HEADER_SIZE != sizeof(lde_addr)) + if (imsg.hdr.len - IMSG_HEADER_SIZE != + sizeof(struct lde_addr)) fatalx("lde_dispatch_imsg: wrong imsg len"); - memcpy(&lde_addr, imsg.data, sizeof(lde_addr)); + lde_addr = imsg.data; ln = lde_nbr_find(imsg.hdr.peerid); if (ln == NULL) { @@ -303,16 +300,17 @@ lde_dispatch_imsg(struct thread *thread) __func__); break; } - if (lde_address_del(ln, &lde_addr) < 0) { + if (lde_address_del(ln, lde_addr) < 0) { log_debug("%s: cannot delete address %s, it " "does not exist", __func__, - log_addr(lde_addr.af, &lde_addr.addr)); + log_addr(lde_addr->af, &lde_addr->addr)); } break; case IMSG_NOTIFICATION: - if (imsg.hdr.len - IMSG_HEADER_SIZE != sizeof(nm)) + if (imsg.hdr.len - IMSG_HEADER_SIZE != + sizeof(struct notify_msg)) fatalx("lde_dispatch_imsg: wrong imsg len"); - memcpy(&nm, imsg.data, sizeof(nm)); + nm = imsg.data; ln = lde_nbr_find(imsg.hdr.peerid); if (ln == NULL) { @@ -321,10 +319,17 @@ lde_dispatch_imsg(struct thread *thread) break; } - switch (nm.status_code) { + switch (nm->status_code) { case S_PW_STATUS: - l2vpn_recv_pw_status(ln, &nm); + l2vpn_recv_pw_status(ln, nm); break; + case S_ENDOFLIB: + /* + * Do nothing for now. Should be useful in + * the future when we implement LDP-IGP + * Synchronization (RFC 5443) and Graceful + * Restart (RFC 3478). + */ default: break; } @@ -391,7 +396,7 @@ lde_dispatch_parent(struct thread *thread) struct l2vpn_if *nlif; struct l2vpn_pw *npw; struct imsg imsg; - struct kroute kr; + struct kroute *kr; int fd = THREAD_FD(thread); struct imsgev *iev = THREAD_ARG(thread); struct imsgbuf *ibuf = &iev->ibuf; @@ -415,22 +420,23 @@ lde_dispatch_parent(struct thread *thread) switch (imsg.hdr.type) { case IMSG_NETWORK_ADD: case IMSG_NETWORK_UPDATE: - if (imsg.hdr.len != IMSG_HEADER_SIZE + sizeof(kr)) { + if (imsg.hdr.len != IMSG_HEADER_SIZE + + sizeof(struct kroute)) { log_warnx("%s: wrong imsg len", __func__); break; } - memcpy(&kr, imsg.data, sizeof(kr)); + kr = imsg.data; - switch (kr.af) { + switch (kr->af) { case AF_INET: fec.type = FEC_TYPE_IPV4; - fec.u.ipv4.prefix = kr.prefix.v4; - fec.u.ipv4.prefixlen = kr.prefixlen; + fec.u.ipv4.prefix = kr->prefix.v4; + fec.u.ipv4.prefixlen = kr->prefixlen; break; case AF_INET6: fec.type = FEC_TYPE_IPV6; - fec.u.ipv6.prefix = kr.prefix.v6; - fec.u.ipv6.prefixlen = kr.prefixlen; + fec.u.ipv6.prefix = kr->prefix.v6; + fec.u.ipv6.prefixlen = kr->prefixlen; break; default: fatalx("lde_dispatch_parent: unknown af"); @@ -438,9 +444,9 @@ lde_dispatch_parent(struct thread *thread) switch (imsg.hdr.type) { case IMSG_NETWORK_ADD: - lde_kernel_insert(&fec, kr.af, &kr.nexthop, - kr.ifindex, kr.priority, - kr.flags & F_CONNECTED, NULL); + lde_kernel_insert(&fec, kr->af, &kr->nexthop, + kr->ifindex, kr->priority, + kr->flags & F_CONNECTED, NULL); break; case IMSG_NETWORK_UPDATE: lde_kernel_update(&fec); @@ -929,8 +935,8 @@ lde_send_labelmapping(struct lde_nbr *ln, struct fec_node *fn, int single) } void -lde_send_labelwithdraw(struct lde_nbr *ln, struct fec_node *fn, uint32_t label, - struct status_tlv *st) +lde_send_labelwithdraw(struct lde_nbr *ln, struct fec_node *fn, + struct map *wcard, struct status_tlv *st) { struct lde_wdraw *lw; struct map map; @@ -959,11 +965,8 @@ lde_send_labelwithdraw(struct lde_nbr *ln, struct fec_node *fn, uint32_t label, break; } map.label = fn->local_label; - } else { - memset(&map, 0, sizeof(map)); - map.type = MAP_TYPE_WILDCARD; - map.label = label; - } + } else + memcpy(&map, wcard, sizeof(map)); if (st) { map.st.status_code = st->status_code; @@ -984,8 +987,13 @@ lde_send_labelwithdraw(struct lde_nbr *ln, struct fec_node *fn, uint32_t label, lw = lde_wdraw_add(ln, fn); lw->label = map.label; } else { + struct lde_map *me; + RB_FOREACH(f, fec_tree, &ft) { fn = (struct fec_node *)f; + me = (struct lde_map *)fec_find(&ln->sent_map, &fn->fec); + if (lde_wildcard_apply(wcard, &fn->fec, me) == 0) + continue; lw = (struct lde_wdraw *)fec_find(&ln->sent_wdraw, &fn->fec); @@ -997,16 +1005,62 @@ lde_send_labelwithdraw(struct lde_nbr *ln, struct fec_node *fn, uint32_t label, } void -lde_send_labelwithdraw_all(struct fec_node *fn, uint32_t label) +lde_send_labelwithdraw_wcard(struct lde_nbr *ln, uint32_t label) { - struct lde_nbr *ln; + struct map wcard; - RB_FOREACH(ln, nbr_tree, &lde_nbrs) - lde_send_labelwithdraw(ln, fn, label, NULL); + memset(&wcard, 0, sizeof(wcard)); + wcard.type = MAP_TYPE_WILDCARD; + wcard.label = label; + lde_send_labelwithdraw(ln, NULL, &wcard, NULL); } void -lde_send_labelrelease(struct lde_nbr *ln, struct fec_node *fn, uint32_t label) +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_twcard_pwid(struct lde_nbr *ln, uint16_t pw_type, + uint32_t label) +{ + struct map wcard; + + memset(&wcard, 0, sizeof(wcard)); + wcard.type = MAP_TYPE_TYPED_WCARD; + wcard.fec.twcard.type = MAP_TYPE_PWID; + wcard.fec.twcard.u.pw_type = pw_type; + 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) +{ + struct map wcard; + + memset(&wcard, 0, sizeof(wcard)); + wcard.type = MAP_TYPE_PWID; + wcard.fec.pwid.type = pw_type; + wcard.fec.pwid.group_id = group_id; + /* we can not append a Label TLV when using PWid group wildcards. */ + wcard.label = NO_LABEL; + lde_send_labelwithdraw(ln, NULL, &wcard, NULL); +} + +void +lde_send_labelrelease(struct lde_nbr *ln, struct fec_node *fn, + struct map *wcard, uint32_t label) { struct map map; struct l2vpn_pw *pw; @@ -1032,10 +1086,8 @@ lde_send_labelrelease(struct lde_nbr *ln, struct fec_node *fn, uint32_t label) map.flags |= F_MAP_PW_CWORD; break; } - } else { - memset(&map, 0, sizeof(map)); - map.type = MAP_TYPE_WILDCARD; - } + } else + memcpy(&map, wcard, sizeof(map)); map.label = label; lde_imsg_compose_ldpe(IMSG_RELEASE_ADD, ln->peerid, 0, @@ -1044,7 +1096,7 @@ lde_send_labelrelease(struct lde_nbr *ln, struct fec_node *fn, uint32_t label) } void -lde_send_notification(uint32_t peerid, uint32_t status_code, uint32_t msg_id, +lde_send_notification(struct lde_nbr *ln, uint32_t status_code, uint32_t msg_id, uint16_t msg_type) { struct notify_msg nm; @@ -1055,7 +1107,39 @@ lde_send_notification(uint32_t peerid, uint32_t status_code, uint32_t msg_id, nm.msg_id = msg_id; nm.msg_type = msg_type; - lde_imsg_compose_ldpe(IMSG_NOTIFICATION_SEND, peerid, 0, + lde_imsg_compose_ldpe(IMSG_NOTIFICATION_SEND, ln->peerid, 0, + &nm, sizeof(nm)); +} + +void +lde_send_notification_eol_prefix(struct lde_nbr *ln, int af) +{ + struct notify_msg nm; + + memset(&nm, 0, sizeof(nm)); + nm.status_code = S_ENDOFLIB; + nm.fec.type = MAP_TYPE_TYPED_WCARD; + nm.fec.fec.twcard.type = MAP_TYPE_PREFIX; + nm.fec.fec.twcard.u.prefix_af = af; + nm.flags |= F_NOTIF_FEC; + + lde_imsg_compose_ldpe(IMSG_NOTIFICATION_SEND, ln->peerid, 0, + &nm, sizeof(nm)); +} + +void +lde_send_notification_eol_pwid(struct lde_nbr *ln, uint16_t pw_type) +{ + struct notify_msg nm; + + memset(&nm, 0, sizeof(nm)); + nm.status_code = S_ENDOFLIB; + nm.fec.type = MAP_TYPE_TYPED_WCARD; + nm.fec.fec.twcard.type = MAP_TYPE_PWID; + nm.fec.fec.twcard.u.pw_type = pw_type; + nm.flags |= F_NOTIF_FEC; + + lde_imsg_compose_ldpe(IMSG_NOTIFICATION_SEND, ln->peerid, 0, &nm, sizeof(nm)); } @@ -1076,6 +1160,7 @@ lde_nbr_new(uint32_t peerid, struct lde_nbr *new) ln->id = new->id; ln->v4_enabled = new->v4_enabled; ln->v6_enabled = new->v6_enabled; + ln->flags = new->flags; ln->peerid = peerid; fec_init(&ln->recv_map); fec_init(&ln->sent_map); @@ -1352,13 +1437,11 @@ lde_change_egress_label(int af) /* explicitly withdraw all null labels */ RB_FOREACH(ln, nbr_tree, &lde_nbrs) { - lde_send_labelwithdraw(ln, NULL, MPLS_LABEL_IMPLNULL, NULL); + lde_send_labelwithdraw_wcard(ln, MPLS_LABEL_IMPLNULL); if (ln->v4_enabled) - lde_send_labelwithdraw(ln, NULL, MPLS_LABEL_IPV4NULL, - NULL); + lde_send_labelwithdraw_wcard(ln, MPLS_LABEL_IPV4NULL); if (ln->v6_enabled) - lde_send_labelwithdraw(ln, NULL, MPLS_LABEL_IPV6NULL, - NULL); + lde_send_labelwithdraw_wcard(ln, MPLS_LABEL_IPV6NULL); } /* update label of connected routes */ diff --git a/ldpd/lde.h b/ldpd/lde.h index e0e9873d5c..b5bcb42c8b 100644 --- a/ldpd/lde.h +++ b/ldpd/lde.h @@ -90,6 +90,7 @@ struct lde_nbr { struct in_addr id; int v4_enabled; /* announce/process v4 msgs */ int v6_enabled; /* announce/process v6 msgs */ + int flags; /* capabilities */ struct fec_tree recv_req; struct fec_tree sent_req; struct fec_tree recv_map; @@ -143,11 +144,20 @@ void lde_map2fec(struct map *, struct in_addr, struct fec *); void lde_send_labelmapping(struct lde_nbr *, struct fec_node *, int); void lde_send_labelwithdraw(struct lde_nbr *, struct fec_node *, - uint32_t, struct status_tlv *); -void lde_send_labelwithdraw_all(struct fec_node *, uint32_t); -void lde_send_labelrelease(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_twcard_pwid(struct lde_nbr *, uint16_t, uint32_t); -void lde_send_notification(uint32_t, uint32_t, uint32_t, uint16_t); +void lde_send_labelwithdraw_pwid_wcard(struct lde_nbr *, uint16_t, + uint32_t); +void lde_send_labelrelease(struct lde_nbr *, struct fec_node *, + struct map *, uint32_t); +void lde_send_notification(struct lde_nbr *, uint32_t, uint32_t, + uint16_t); +void lde_send_notification_eol_prefix(struct lde_nbr *, int); +void lde_send_notification_eol_pwid(struct lde_nbr *, uint16_t); struct lde_nbr *lde_nbr_find_by_lsrid(struct in_addr); struct lde_nbr *lde_nbr_find_by_addr(int, union ldpd_addr *); struct lde_map *lde_map_add(struct lde_nbr *, struct fec_node *, int); @@ -178,10 +188,13 @@ 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 *); void lde_check_withdraw_wcard(struct map *, struct lde_nbr *); +int lde_wildcard_apply(struct map *, struct fec *, + struct lde_map *); int lde_gc_timer(struct thread *); void lde_gc_start_timer(void); void lde_gc_stop_timer(void); @@ -195,6 +208,7 @@ void l2vpn_exit(struct l2vpn *); struct l2vpn_if *l2vpn_if_new(struct l2vpn *, struct kif *); struct l2vpn_if *l2vpn_if_find(struct l2vpn *, unsigned int); struct l2vpn_if *l2vpn_if_find_name(struct l2vpn *, const char *); +void l2vpn_if_update(struct l2vpn_if *); struct l2vpn_pw *l2vpn_pw_new(struct l2vpn *, struct kif *); struct l2vpn_pw *l2vpn_pw_find(struct l2vpn *, unsigned int); struct l2vpn_pw *l2vpn_pw_find_name(struct l2vpn *, const char *); @@ -204,8 +218,12 @@ void l2vpn_pw_reset(struct l2vpn_pw *); int l2vpn_pw_ok(struct l2vpn_pw *, struct fec_nh *); int l2vpn_pw_negotiate(struct lde_nbr *, struct fec_node *, struct map *); -void l2vpn_send_pw_status(uint32_t, uint32_t, struct fec *); +void l2vpn_send_pw_status(struct lde_nbr *, uint32_t, struct fec *); +void l2vpn_send_pw_status_wcard(struct lde_nbr *, uint32_t, + uint16_t, uint32_t); void l2vpn_recv_pw_status(struct lde_nbr *, struct notify_msg *); +void l2vpn_recv_pw_status_wcard(struct lde_nbr *, + struct notify_msg *); void l2vpn_sync_pws(int, union ldpd_addr *); void l2vpn_pw_ctl(pid_t); void l2vpn_binding_ctl(pid_t); diff --git a/ldpd/lde_lib.c b/ldpd/lde_lib.c index 234d373fbb..4444a1e1ac 100644 --- a/ldpd/lde_lib.c +++ b/ldpd/lde_lib.c @@ -374,7 +374,8 @@ lde_kernel_update(struct fec *fec) } if (LIST_EMPTY(&fn->nexthops)) { - lde_send_labelwithdraw_all(fn, NO_LABEL); + RB_FOREACH(ln, nbr_tree, &lde_nbrs) + lde_send_labelwithdraw(ln, fn, NULL, NULL); fn->local_label = NO_LABEL; fn->data = NULL; } else { @@ -478,7 +479,7 @@ lde_check_mapping(struct map *map, struct lde_nbr *ln) /* LMp.10 */ if (me->map.label != map->label && lre == NULL) { /* LMp.10a */ - lde_send_labelrelease(ln, fn, me->map.label); + lde_send_labelrelease(ln, fn, NULL, me->map.label); /* * Can not use lde_nbr_find_by_addr() because there's @@ -554,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? */ @@ -561,7 +568,7 @@ lde_check_request(struct map *map, struct lde_nbr *ln) fn = (struct fec_node *)fec_find(&ft, &fec); if (fn == NULL || LIST_EMPTY(&fn->nexthops)) { /* LRq.5: send No Route notification */ - lde_send_notification(ln->peerid, S_NO_ROUTE, map->msg_id, + lde_send_notification(ln, S_NO_ROUTE, map->msg_id, htons(MSG_TYPE_LABELREQUEST)); return; } @@ -575,8 +582,8 @@ lde_check_request(struct map *map, struct lde_nbr *ln) continue; /* LRq.4: send Loop Detected notification */ - lde_send_notification(ln->peerid, S_LOOP_DETECTED, - map->msg_id, htons(MSG_TYPE_LABELREQUEST)); + lde_send_notification(ln, S_LOOP_DETECTED, map->msg_id, + htons(MSG_TYPE_LABELREQUEST)); return; default: break; @@ -604,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) { @@ -612,9 +653,13 @@ lde_check_release(struct map *map, struct lde_nbr *ln) struct lde_wdraw *lw; struct lde_map *me; - /* TODO group wildcard */ - if (map->type == MAP_TYPE_PWID && !(map->flags & F_MAP_PW_ID)) + /* 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; + } lde_map2fec(map, ln->id, &fec); fn = (struct fec_node *)fec_find(&ft, &fec); @@ -624,8 +669,7 @@ lde_check_release(struct map *map, struct lde_nbr *ln) /* LRl.3: first check if we have a pending withdraw running */ lw = (struct lde_wdraw *)fec_find(&ln->sent_wdraw, &fn->fec); - if (lw && (map->label == NO_LABEL || - (lw->label != NO_LABEL && map->label == lw->label))) { + if (lw && (map->label == NO_LABEL || map->label == lw->label)) { /* LRl.4: delete record of outstanding label withdraw */ lde_wdraw_del(ln, lw); } @@ -651,17 +695,20 @@ lde_check_release_wcard(struct map *map, struct lde_nbr *ln) RB_FOREACH(f, fec_tree, &ft) { fn = (struct fec_node *)f; + me = (struct lde_map *)fec_find(&ln->sent_map, &fn->fec); + + /* LRl.1: does FEC match a known FEC? */ + if (lde_wildcard_apply(map, &fn->fec, me) == 0) + continue; /* LRl.3: first check if we have a pending withdraw running */ lw = (struct lde_wdraw *)fec_find(&ln->sent_wdraw, &fn->fec); - if (lw && (map->label == NO_LABEL || - (lw->label != NO_LABEL && map->label == lw->label))) { + if (lw && (map->label == NO_LABEL || map->label == lw->label)) { /* LRl.4: delete record of outstanding lbl withdraw */ lde_wdraw_del(ln, lw); } /* LRl.6: check sent map list and remove it if available */ - me = (struct lde_map *)fec_find(&ln->sent_map, &fn->fec); if (me && (map->label == NO_LABEL || map->label == me->map.label)) lde_map_del(ln, me, 1); @@ -682,9 +729,13 @@ lde_check_withdraw(struct map *map, struct lde_nbr *ln) struct lde_map *me; struct l2vpn_pw *pw; - /* TODO group wildcard */ - if (map->type == MAP_TYPE_PWID && !(map->flags & F_MAP_PW_ID)) + /* 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; + } lde_map2fec(map, ln->id, &fec); fn = (struct fec_node *)fec_find(&ft, &fec); @@ -707,12 +758,15 @@ lde_check_withdraw(struct map *map, struct lde_nbr *ln) default: break; } + if (map->label != NO_LABEL && map->label != fnh->remote_label) + continue; + lde_send_delete_klabel(fn, fnh); fnh->remote_label = NO_LABEL; } /* LWd.2: send label release */ - lde_send_labelrelease(ln, fn, map->label); + lde_send_labelrelease(ln, fn, NULL, map->label); /* LWd.3: check previously received label mapping */ me = (struct lde_map *)fec_find(&ln->recv_map, &fn->fec); @@ -730,10 +784,14 @@ lde_check_withdraw_wcard(struct map *map, struct lde_nbr *ln) struct lde_map *me; /* LWd.2: send label release */ - lde_send_labelrelease(ln, NULL, map->label); + lde_send_labelrelease(ln, NULL, map, map->label); RB_FOREACH(f, fec_tree, &ft) { fn = (struct fec_node *)f; + me = (struct lde_map *)fec_find(&ln->recv_map, &fn->fec); + + if (lde_wildcard_apply(map, &fn->fec, me) == 0) + continue; /* LWd.1: remove label from forwarding/switching use */ LIST_FOREACH(fnh, &fn->nexthops, entry) { @@ -751,12 +809,15 @@ lde_check_withdraw_wcard(struct map *map, struct lde_nbr *ln) default: break; } + if (map->label != NO_LABEL && map->label != + fnh->remote_label) + continue; + lde_send_delete_klabel(fn, fnh); fnh->remote_label = NO_LABEL; } /* LWd.3: check previously received label mapping */ - me = (struct lde_map *)fec_find(&ln->recv_map, &fn->fec); if (me && (map->label == NO_LABEL || map->label == me->map.label)) /* @@ -767,6 +828,49 @@ lde_check_withdraw_wcard(struct map *map, struct lde_nbr *ln) } } +int +lde_wildcard_apply(struct map *wcard, struct fec *fec, struct lde_map *me) +{ + switch (wcard->type) { + 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); + case MAP_TYPE_PWID: + if (fec->type != FEC_TYPE_PWID) + return (0); + if (wcard->fec.twcard.u.pw_type != PW_TYPE_WILDCARD && + wcard->fec.twcard.u.pw_type != fec->u.pwid.type) + 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) + return (0); + if (fec->u.pwid.type != wcard->fec.pwid.type) + return (0); + if (me == NULL || (me->map.fec.pwid.group_id != + wcard->fec.pwid.group_id)) + return (0); + return (1); + default: + fatalx("lde_wildcard_apply: unexpected fec type"); + } +} + /* gabage collector timer: timer to remove dead entries from the LIB */ /* ARGSUSED */ diff --git a/ldpd/ldp.h b/ldpd/ldp.h index c421cddc38..c2b64d20c6 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 @@ -92,9 +93,17 @@ #define TLV_TYPE_FRSESSION 0x0502 #define TLV_TYPE_LABELREQUEST 0x0600 /* RFC 4447 */ -#define TLV_TYPE_PW_STATUS 0x096A +#define TLV_TYPE_MAC_LIST 0x8404 +#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 5918 */ +#define TLV_TYPE_TWCARD_CAP 0x850B +/* RFC 5919 */ +#define TLV_TYPE_UNOTIF_CAP 0x8603 /* RFC 7552 */ #define TLV_TYPE_DUALSTACK 0x8701 @@ -196,6 +205,10 @@ 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 5919 */ +#define S_ENDOFLIB 0x0000002F /* RFC 7552 */ #define S_TRANS_MISMTCH 0x80000032 #define S_DS_NONCMPLNCE 0x80000033 @@ -227,6 +240,26 @@ 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 F_CAP_TLV_RCVD_TWCARD 0x02 +#define F_CAP_TLV_RCVD_UNOTIF 0x04 + +#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 CAP_TLV_UNOTIF_SIZE 5 +#define CAP_TLV_UNOTIF_LEN 1 + #define AF_IPV4 0x1 #define AF_IPV6 0x2 @@ -242,17 +275,23 @@ struct address_list_tlv { #define FEC_ELM_WCARD_LEN 1 #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 #define CONTROL_WORD_FLAG 0x8000 #define PW_TYPE_ETHERNET_TAGGED 0x0004 #define PW_TYPE_ETHERNET 0x0005 +#define PW_TYPE_WILDCARD 0x7FFF #define DEFAULT_PW_TYPE PW_TYPE_ETHERNET +#define PW_TWCARD_RESERVED_BIT 0x8000 + /* RFC 4447 Sub-TLV record */ struct subtlv { uint8_t type; diff --git a/ldpd/ldp_debug.h b/ldpd/ldp_debug.h index aa0cd47e7b..f944851b6e 100644 --- a/ldpd/ldp_debug.h +++ b/ldpd/ldp_debug.h @@ -104,6 +104,14 @@ do { \ log_debug("msg[out]: " emsg, __VA_ARGS__); \ } while (0) +#define debug_msg(out, emsg, ...) \ +do { \ + if (out) \ + debug_msg_send(emsg, __VA_ARGS__); \ + else \ + debug_msg_recv(emsg, __VA_ARGS__); \ +} while (0) + #define debug_kalive_recv(emsg, ...) \ do { \ if (LDP_DEBUG(msg, MSG_RECV_ALL)) \ diff --git a/ldpd/ldp_zebra.c b/ldpd/ldp_zebra.c index 12954b91af..c41a0dbd91 100644 --- a/ldpd/ldp_zebra.c +++ b/ldpd/ldp_zebra.c @@ -65,6 +65,8 @@ ifp2kif(struct interface *ifp, struct kif *kif) strlcpy(kif->ifname, ifp->name, sizeof(kif->ifname)); kif->ifindex = ifp->ifindex; kif->flags = ifp->flags; + if (ifp->ll_type == ZEBRA_LLT_ETHER) + memcpy(kif->mac, ifp->hw_addr, ETHER_ADDR_LEN); } static void diff --git a/ldpd/ldpd.h b/ldpd/ldpd.h index ff3af19db4..0a7e1177bc 100644 --- a/ldpd/ldpd.h +++ b/ldpd/ldpd.h @@ -27,6 +27,7 @@ #include "imsg.h" #include "thread.h" #include "qobj.h" +#include "prefix.h" #include "filter.h" #include "ldp.h" @@ -220,6 +221,13 @@ struct map { uint32_t group_id; uint16_t ifmtu; } pwid; + struct { + uint8_t type; + union { + uint16_t prefix_af; + uint16_t pw_type; + } u; + } twcard; } fec; struct { uint32_t status_code; @@ -244,10 +252,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; @@ -337,6 +351,7 @@ struct l2vpn_if { char ifname[IF_NAMESIZE]; unsigned int ifindex; uint16_t flags; + uint8_t mac[ETHER_ADDR_LEN]; QOBJ_FIELDS }; RB_HEAD(l2vpn_if_head, l2vpn_if); @@ -506,6 +521,7 @@ struct kif { char ifname[IF_NAMESIZE]; unsigned short ifindex; int flags; + uint8_t mac[ETHER_ADDR_LEN]; int mtu; }; diff --git a/ldpd/ldpe.c b/ldpd/ldpe.c index 7dcc8fbe16..3bb84e92a9 100644 --- a/ldpd/ldpe.c +++ b/ldpd/ldpe.c @@ -251,8 +251,8 @@ ldpe_dispatch_main(struct thread *thread) struct iface *niface; struct tnbr *ntnbr; struct nbr_params *nnbrp; - static struct l2vpn *nl2vpn; - struct l2vpn_if *nlif; + static struct l2vpn *l2vpn, *nl2vpn; + struct l2vpn_if *lif = NULL, *nlif; struct l2vpn_pw *npw; struct imsg imsg; int fd = THREAD_FD(thread); @@ -292,11 +292,22 @@ ldpe_dispatch_main(struct thread *thread) kif = imsg.data; iface = if_lookup_name(leconf, kif->ifname); - if (!iface) + if (iface) { + if_update_info(iface, kif); + if_update(iface, AF_UNSPEC); break; + } - if_update_info(iface, kif); - if_update(iface, AF_UNSPEC); + RB_FOREACH(l2vpn, l2vpn_head, &leconf->l2vpn_tree) { + lif = l2vpn_if_find_name(l2vpn, kif->ifname); + if (lif) { + lif->flags = kif->flags; + memcpy(lif->mac, kif->mac, + sizeof(lif->mac)); + l2vpn_if_update(lif); + break; + } + } break; case IMSG_NEWADDR: if (imsg.hdr.len != IMSG_HEADER_SIZE + @@ -529,10 +540,10 @@ ldpe_dispatch_lde(struct thread *thread) struct imsgev *iev = THREAD_ARG(thread); struct imsgbuf *ibuf = &iev->ibuf; struct imsg imsg; - struct map map; - struct notify_msg nm; + struct map *map; + struct notify_msg *nm; + struct nbr *nbr; int n, shut = 0; - struct nbr *nbr = NULL; iev->ev_read = NULL; @@ -552,9 +563,10 @@ ldpe_dispatch_lde(struct thread *thread) case IMSG_RELEASE_ADD: case IMSG_REQUEST_ADD: case IMSG_WITHDRAW_ADD: - if (imsg.hdr.len - IMSG_HEADER_SIZE != sizeof(map)) + if (imsg.hdr.len - IMSG_HEADER_SIZE != + sizeof(struct map)) fatalx("invalid size of map request"); - memcpy(&map, imsg.data, sizeof(map)); + map = imsg.data; nbr = nbr_find_peerid(imsg.hdr.peerid); if (nbr == NULL) { @@ -567,16 +579,16 @@ ldpe_dispatch_lde(struct thread *thread) switch (imsg.hdr.type) { case IMSG_MAPPING_ADD: - mapping_list_add(&nbr->mapping_list, &map); + mapping_list_add(&nbr->mapping_list, map); break; case IMSG_RELEASE_ADD: - mapping_list_add(&nbr->release_list, &map); + mapping_list_add(&nbr->release_list, map); break; case IMSG_REQUEST_ADD: - mapping_list_add(&nbr->request_list, &map); + mapping_list_add(&nbr->request_list, map); break; case IMSG_WITHDRAW_ADD: - mapping_list_add(&nbr->withdraw_list, &map); + mapping_list_add(&nbr->withdraw_list, map); break; } break; @@ -613,9 +625,10 @@ ldpe_dispatch_lde(struct thread *thread) } break; case IMSG_NOTIFICATION_SEND: - if (imsg.hdr.len - IMSG_HEADER_SIZE != sizeof(nm)) + if (imsg.hdr.len - IMSG_HEADER_SIZE != + sizeof(struct notify_msg)) fatalx("invalid size of OE request"); - memcpy(&nm, imsg.data, sizeof(nm)); + nm = imsg.data; nbr = nbr_find_peerid(imsg.hdr.peerid); if (nbr == NULL) { @@ -626,7 +639,7 @@ ldpe_dispatch_lde(struct thread *thread) if (nbr->state != NBR_STA_OPER) break; - send_notification_full(nbr->tcp, &nm); + send_notification_full(nbr->tcp, nm); break; case IMSG_CTL_END: case IMSG_CTL_SHOW_LIB: @@ -791,8 +804,7 @@ ldpe_iface_af_ctl(struct ctl_conn *c, int af, unsigned int idx) continue; ictl = if_to_ctl(ia); - imsg_compose_event(&c->iev, - IMSG_CTL_SHOW_INTERFACE, + imsg_compose_event(&c->iev, IMSG_CTL_SHOW_INTERFACE, 0, 0, -1, ictl, sizeof(struct ctl_iface)); } } diff --git a/ldpd/ldpe.h b/ldpd/ldpe.h index 052439df88..22b75eb008 100644 --- a/ldpd/ldpe.h +++ b/ldpd/ldpe.h @@ -111,6 +111,9 @@ struct nbr { int flags; }; #define F_NBR_GTSM_NEGOTIATED 0x01 +#define F_NBR_CAP_DYNAMIC 0x02 +#define F_NBR_CAP_TWCARD 0x04 +#define F_NBR_CAP_UNOTIF 0x08 RB_HEAD(nbr_id_head, nbr); RB_PROTOTYPE(nbr_id_head, nbr, id_tree, nbr_id_compare) @@ -159,6 +162,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 *); @@ -166,15 +171,16 @@ int recv_keepalive(struct nbr *, char *, uint16_t); /* notification.c */ void send_notification_full(struct tcp_conn *, struct notify_msg *); -void send_notification(uint32_t, struct tcp_conn *, uint32_t, - uint16_t); -void send_notification_nbr(struct nbr *, uint32_t, uint32_t, uint16_t); +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); /* address.c */ void send_address_single(struct nbr *, struct if_addr *, int); void send_address_all(struct nbr *, int); +void send_mac_withdrawal(struct nbr *, struct map *, uint8_t *); int recv_address(struct nbr *, char *, uint16_t); /* labelmapping.c */ @@ -182,6 +188,7 @@ int recv_address(struct nbr *, char *, uint16_t); void send_labelmessage(struct nbr *, uint16_t, struct mapping_head *); int recv_labelmessage(struct nbr *, char *, uint16_t, uint16_t); int gen_pw_status_tlv(struct ibuf *, uint32_t); +uint16_t len_fec_tlv(struct map *); int gen_fec_tlv(struct ibuf *, struct map *); int tlv_decode_fec_elm(struct nbr *, struct ldp_msg *, char *, uint16_t, struct map *); diff --git a/ldpd/log.c b/ldpd/log.c index 77efdb4714..b30604db0d 100644 --- a/ldpd/log.c +++ b/ldpd/log.c @@ -313,7 +313,7 @@ log_hello_src(const struct hello_source *src) const char * log_map(const struct map *map) { - static char buf[64]; + static char buf[128]; switch (map->type) { case MAP_TYPE_WILDCARD: @@ -327,11 +327,34 @@ log_map(const struct map *map) return ("???"); break; case MAP_TYPE_PWID: - if (snprintf(buf, sizeof(buf), "pwid %u (%s)", - map->fec.pwid.pwid, + if (snprintf(buf, sizeof(buf), "pw-id %u group-id %u (%s)", + map->fec.pwid.pwid, map->fec.pwid.group_id, 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; + case MAP_TYPE_PWID: + if (snprintf(buf + strlen(buf), sizeof(buf) - + strlen(buf), " (pwid, type %s)", + pw_type_name(map->fec.twcard.u.pw_type)) < 0) + return ("???"); + break; + default: + if (snprintf(buf + strlen(buf), sizeof(buf) - + strlen(buf), " (unknown type)") < 0) + return ("???"); + break; + } + break; default: return ("???"); } @@ -464,6 +487,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 +582,10 @@ 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_ENDOFLIB: + return ("End-of-LIB"); case S_TRANS_MISMTCH: return ("Transport Connection Mismatch"); case S_DS_NONCMPLNCE: @@ -577,6 +606,8 @@ pw_type_name(uint16_t pw_type) return ("Eth Tagged"); case PW_TYPE_ETHERNET: return ("Ethernet"); + case PW_TYPE_WILDCARD: + return ("Wildcard"); default: snprintf(buf, sizeof(buf), "[%0x]", pw_type); return (buf); diff --git a/ldpd/neighbor.c b/ldpd/neighbor.c index d24ceb1229..077d472850 100644 --- a/ldpd/neighbor.c +++ b/ldpd/neighbor.c @@ -744,6 +744,7 @@ nbr_act_session_operational(struct nbr *nbr) lde_nbr.id = nbr->id; lde_nbr.v4_enabled = nbr->v4_enabled; lde_nbr.v6_enabled = nbr->v6_enabled; + lde_nbr.flags = nbr->flags; return (ldpe_imsg_compose_lde(IMSG_NEIGHBOR_UP, nbr->peerid, 0, &lde_nbr, sizeof(lde_nbr))); } diff --git a/ldpd/notification.c b/ldpd/notification.c index d306361d5c..393994ed5f 100644 --- a/ldpd/notification.c +++ b/ldpd/notification.c @@ -24,6 +24,9 @@ #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 send_notification_full(struct tcp_conn *tcp, struct notify_msg *nm) { @@ -35,16 +38,10 @@ send_notification_full(struct tcp_conn *tcp, struct notify_msg *nm) size = LDP_HDR_SIZE + LDP_MSG_SIZE + STATUS_SIZE; if (nm->flags & F_NOTIF_PW_STATUS) size += PW_STATUS_TLV_SIZE; - if (nm->flags & F_NOTIF_FEC) { - size += TLV_HDR_SIZE; - switch (nm->fec.type) { - case MAP_TYPE_PWID: - size += FEC_PWID_ELM_MIN_LEN; - if (nm->fec.flags & F_MAP_PW_ID) - size += sizeof(uint32_t); - break; - } - } + if (nm->flags & F_NOTIF_FEC) + size += len_fec_tlv(&nm->fec); + if (nm->flags & F_NOTIF_RETURNED_TLVS) + size += TLV_HDR_SIZE * 2 + nm->rtlvs.length; if ((buf = ibuf_open(size)) == NULL) fatal(__func__); @@ -58,22 +55,25 @@ 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; } - if (tcp->nbr) - debug_msg_send("notification: lsr-id %s status %s%s", - inet_ntoa(tcp->nbr->id), status_code_name(nm->status_code), - (nm->status_code & STATUS_FATAL) ? " (fatal)" : ""); + if (tcp->nbr) { + log_msg_notification(1, tcp->nbr, nm); + nbr_fsm(tcp->nbr, NBR_EVT_PDU_SENT); + } evbuf_enqueue(&tcp->wbuf, buf); } /* send a notification without optional tlvs */ void -send_notification(uint32_t status_code, struct tcp_conn *tcp, uint32_t msg_id, +send_notification(struct tcp_conn *tcp, uint32_t status_code, uint32_t msg_id, uint16_t msg_type) { struct notify_msg nm; @@ -87,11 +87,24 @@ send_notification(uint32_t status_code, struct tcp_conn *tcp, uint32_t msg_id, } void -send_notification_nbr(struct nbr *nbr, uint32_t status_code, uint32_t msg_id, - uint16_t msg_type) +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) { - send_notification(status_code, nbr->tcp, msg_id, msg_type); - nbr_fsm(nbr, NBR_EVT_PDU_SENT); + 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 @@ -126,6 +139,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)) { @@ -134,6 +148,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); @@ -142,7 +157,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: @@ -172,8 +187,8 @@ recv_notification(struct nbr *nbr, char *buf, uint16_t len) 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; } @@ -181,9 +196,11 @@ recv_notification(struct nbr *nbr, char *buf, uint16_t len) len -= tlv_len; } - if (nm.status_code == S_PW_STATUS) { + /* sanity checks */ + switch (nm.status_code) { + case S_PW_STATUS: if (!(nm.flags & (F_NOTIF_PW_STATUS|F_NOTIF_FEC))) { - send_notification_nbr(nbr, S_MISS_MSG, + send_notification(nbr->tcp, S_MISS_MSG, msg.id, msg.type); return (-1); } @@ -192,15 +209,28 @@ recv_notification(struct nbr *nbr, char *buf, uint16_t len) case MAP_TYPE_PWID: break; default: - send_notification_nbr(nbr, S_BAD_TLV_VAL, + send_notification(nbr->tcp, S_BAD_TLV_VAL, msg.id, msg.type); return (-1); } + break; + case S_ENDOFLIB: + if (!(nm.flags & F_NOTIF_FEC)) { + send_notification(nbr->tcp, S_MISS_MSG, + msg.id, msg.type); + return (-1); + } + if (nm.fec.type != MAP_TYPE_TYPED_WCARD) { + send_notification(nbr->tcp, S_BAD_TLV_VAL, + msg.id, msg.type); + return (-1); + } + break; + default: + break; } - debug_msg_recv("notification: lsr-id %s: %s%s", inet_ntoa(nbr->id), - status_code_name(ntohl(st.status_code)), - (st.status_code & htonl(STATUS_FATAL)) ? " (fatal)" : ""); + log_msg_notification(0, nbr, &nm); if (st.status_code & htonl(STATUS_FATAL)) { if (nbr->state == NBR_STA_OPENSENT) @@ -210,9 +240,16 @@ recv_notification(struct nbr *nbr, char *buf, uint16_t len) return (-1); } - if (nm.status_code == S_PW_STATUS) + /* lde needs to know about a few notification messages */ + switch (nm.status_code) { + case S_PW_STATUS: + case S_ENDOFLIB: ldpe_imsg_compose_lde(IMSG_NOTIFICATION, nbr->peerid, 0, &nm, sizeof(nm)); + break; + default: + break; + } return (0); } @@ -236,3 +273,42 @@ 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) +{ + if (nm->status_code & STATUS_FATAL) { + debug_msg(out, "notification: lsr-id %s, status %s " + "(fatal error)", inet_ntoa(nbr->id), + status_code_name(nm->status_code)); + return; + } + + debug_msg(out, "notification: lsr-id %s, status %s", + inet_ntoa(nbr->id), status_code_name(nm->status_code)); + if (nm->flags & F_NOTIF_FEC) + debug_msg(out, "notification: fec %s", log_map(&nm->fec)); + if (nm->flags & F_NOTIF_PW_STATUS) + debug_msg(out, "notification: pw-status %s", + (nm->pw_status) ? "not forwarding" : "forwarding"); +} diff --git a/ldpd/packet.c b/ldpd/packet.c index b085cac055..a7be0f6b42 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); @@ -564,7 +559,7 @@ session_read(struct thread *thread) log_debug("%s: unknown LDP message from nbr %s", __func__, inet_ntoa(nbr->id)); if (!(ntohs(msg->type) & UNKNOWN_FLAG)) - send_notification_nbr(nbr, + send_notification(nbr->tcp, S_UNKNOWN_MSG, msg->id, msg->type); /* ignore the message */ ret = 0; @@ -632,7 +627,7 @@ session_shutdown(struct nbr *nbr, uint32_t status, uint32_t msg_id, case NBR_STA_OPER: log_debug("%s: lsr-id %s", __func__, inet_ntoa(nbr->id)); - send_notification_nbr(nbr, status, msg_id, msg_type); + send_notification(nbr->tcp, status, msg_id, msg_type); nbr_fsm(nbr, NBR_EVT_CLOSE_SESSION); break; @@ -788,7 +783,7 @@ pending_conn_timeout(struct thread *thread) * notification message reliably. */ tcp = tcp_new(pconn->fd, NULL); - send_notification(S_NO_HELLO, tcp, 0, 0); + send_notification(tcp, S_NO_HELLO, 0, 0); msgbuf_write(&tcp->wbuf.wbuf); pending_conn_del(pconn); diff --git a/ldpd/pfkey.c b/ldpd/pfkey.c index 29f763e6a6..88a778cccc 100644 --- a/ldpd/pfkey.c +++ b/ldpd/pfkey.c @@ -131,7 +131,7 @@ pfkey_send(int sd, uint8_t satype, uint8_t mtype, uint8_t dir, sa.sadb_sa_exttype = SADB_EXT_SA; sa.sadb_sa_len = sizeof(sa) / 8; sa.sadb_sa_replay = 0; - sa.sadb_sa_spi = spi; + sa.sadb_sa_spi = htonl(spi); sa.sadb_sa_state = SADB_SASTATE_MATURE; break; } @@ -280,7 +280,7 @@ pfkey_read(int sd, struct sadb_msg *h) } static int -pfkey_reply(int sd, uint32_t *spip) +pfkey_reply(int sd, uint32_t *spi) { struct sadb_msg hdr, *msg; struct sadb_ext *ext; @@ -317,7 +317,7 @@ pfkey_reply(int sd, uint32_t *spip) } if (hdr.sadb_msg_type == SADB_GETSPI) { - if (spip == NULL) { + if (spi == NULL) { explicit_bzero(data, len); free(data); return (0); @@ -331,7 +331,7 @@ pfkey_reply(int sd, uint32_t *spip) ext->sadb_ext_len * PFKEY2_CHUNK)) { if (ext->sadb_ext_type == SADB_EXT_SA) { sa = (struct sadb_sa *) ext; - *spip = sa->sadb_sa_spi; + *spi = ntohl(sa->sadb_sa_spi); break; } }