diff --git a/ldpd/l2vpn.c b/ldpd/l2vpn.c index b234e3ebe3..4a944fa019 100644 --- a/ldpd/l2vpn.c +++ b/ldpd/l2vpn.c @@ -303,6 +303,7 @@ l2vpn_pw_ok(struct l2vpn_pw *pw, struct fec_nh *fnh) if (fnh->remote_label == NO_LABEL) { log_warnx("%s: pseudowire %s: no remote label", __func__, pw->ifname); + pw->reason = F_PW_NO_REMOTE_LABEL; return (0); } @@ -310,6 +311,7 @@ l2vpn_pw_ok(struct l2vpn_pw *pw, struct fec_nh *fnh) if (pw->l2vpn->mtu != pw->remote_mtu) { log_warnx("%s: pseudowire %s: MTU mismatch detected", __func__, pw->ifname); + pw->reason = F_PW_MTU_MISMATCH; return (0); } @@ -318,9 +320,11 @@ l2vpn_pw_ok(struct l2vpn_pw *pw, struct fec_nh *fnh) pw->remote_status != PW_FORWARDING) { log_warnx("%s: pseudowire %s: remote end is down", __func__, pw->ifname); + pw->reason = F_PW_REMOTE_NOT_FWD; return (0); } + pw->reason = F_PW_NO_ERR; return (1); } @@ -517,10 +521,13 @@ l2vpn_pw_status_update(struct zapi_pw_status *zpw) return (1); } - if (zpw->status == PW_STATUS_UP) + if (zpw->status == PW_STATUS_UP) { local_status = PW_FORWARDING; - else + pw->reason = F_PW_NO_ERR; + } else { local_status = PW_NOT_FORWARDING; + pw->reason = F_PW_LOCAL_NOT_FWD; + } /* local status didn't change */ if (pw->local_status == local_status) @@ -604,6 +611,7 @@ l2vpn_binding_ctl(pid_t pid) pwctl.local_ifmtu = pw->l2vpn->mtu; pwctl.local_cword = (pw->flags & F_PW_CWORD_CONF) ? 1 : 0; + pwctl.reason = pw->reason; } else pwctl.local_label = NO_LABEL; diff --git a/ldpd/lde.c b/ldpd/lde.c index 7d1df158a5..3220278960 100644 --- a/ldpd/lde.c +++ b/ldpd/lde.c @@ -751,7 +751,6 @@ lde_update_label(struct fec_node *fn) return (MPLS_LABEL_IMPLICIT_NULL); return MPLS_LABEL_IPV6_EXPLICIT_NULL; default: - fatalx("lde_update_label: unexpected fec type"); break; } } @@ -1421,8 +1420,10 @@ lde_nbr_del(struct lde_nbr *ln) if (f->u.pwid.lsr_id.s_addr != ln->id.s_addr) continue; pw = (struct l2vpn_pw *) fn->data; - if (pw) + if (pw) { + pw->reason = F_PW_NO_REMOTE_LABEL; l2vpn_pw_reset(pw); + } break; default: break; diff --git a/ldpd/ldp_vty_exec.c b/ldpd/ldp_vty_exec.c index d317da7b20..d74017c7e2 100644 --- a/ldpd/ldp_vty_exec.c +++ b/ldpd/ldp_vty_exec.c @@ -1256,6 +1256,8 @@ show_l2vpn_binding_msg(struct vty *vty, struct imsg *imsg, "GroupID: %u\n", "", pw->local_cword, pw_type_name(pw->type),pw->local_gid); vty_out (vty, "%-8sMTU: %u\n", "",pw->local_ifmtu); + vty_out (vty, "%-8sLast failure: %s\n", "", + pw_error_code(pw->reason)); } else vty_out (vty," Local Label: unassigned\n"); @@ -1309,6 +1311,8 @@ show_l2vpn_binding_msg_json(struct imsg *imsg, struct show_params *params, pw->local_gid); json_object_int_add(json_pw, "localIfMtu", pw->local_ifmtu); + json_object_string_add(json_pw, "lastFailureReason", + pw_error_code(pw->reason)); } else json_object_string_add(json_pw, "localLabel", "unassigned"); diff --git a/ldpd/ldpd.h b/ldpd/ldpd.h index 606fb372bb..c1bcc56c44 100644 --- a/ldpd/ldpd.h +++ b/ldpd/ldpd.h @@ -422,6 +422,7 @@ struct l2vpn_pw { uint32_t local_status; uint32_t remote_status; uint8_t flags; + uint8_t reason; QOBJ_FIELDS }; RB_HEAD(l2vpn_pw_head, l2vpn_pw); @@ -433,6 +434,12 @@ DECLARE_QOBJ_TYPE(l2vpn_pw) #define F_PW_CWORD 0x08 /* control word negotiated */ #define F_PW_STATIC_NBR_ADDR 0x10 /* static neighbor address configured */ +#define F_PW_NO_ERR 0x00 /* no error reported */ +#define F_PW_LOCAL_NOT_FWD 0x01 /* locally can't forward over PW */ +#define F_PW_REMOTE_NOT_FWD 0x02 /* remote end of PW reported fwd error*/ +#define F_PW_NO_REMOTE_LABEL 0x03 /* have not recvd label from peer */ +#define F_PW_MTU_MISMATCH 0x04 /* mtu mismatch between peers */ + struct l2vpn { RB_ENTRY(l2vpn) entry; char name[L2VPN_NAME_LEN]; @@ -662,6 +669,7 @@ struct ctl_pw { uint16_t remote_ifmtu; uint8_t remote_cword; uint32_t status; + uint8_t reason; }; extern struct ldpd_conf *ldpd_conf, *vty_conf; @@ -808,6 +816,7 @@ const char *if_type_name(enum iface_type); const char *msg_name(uint16_t); const char *status_code_name(uint32_t); const char *pw_type_name(uint16_t); +const char *pw_error_code(uint8_t); /* quagga */ extern struct thread_master *master; diff --git a/ldpd/logmsg.c b/ldpd/logmsg.c index 2c9fbf0dae..6427d0e13b 100644 --- a/ldpd/logmsg.c +++ b/ldpd/logmsg.c @@ -485,3 +485,25 @@ pw_type_name(uint16_t pw_type) return (buf); } } + +const char * +pw_error_code(uint8_t status) +{ + static char buf[16]; + + switch (status) { + case F_PW_NO_ERR: + return ("No Error"); + case F_PW_LOCAL_NOT_FWD: + return ("local not forwarding"); + case F_PW_REMOTE_NOT_FWD: + return ("remote not forwarding"); + case F_PW_NO_REMOTE_LABEL: + return ("no remote label"); + case F_PW_MTU_MISMATCH: + return ("mtu mismatch between peers"); + default: + snprintf(buf, sizeof(buf), "[%0x]", status); + return (buf); + } +} diff --git a/zebra/zebra_pw.c b/zebra/zebra_pw.c index c26b7a6157..273843baa2 100644 --- a/zebra/zebra_pw.c +++ b/zebra/zebra_pw.c @@ -24,6 +24,8 @@ #include "thread.h" #include "command.h" #include "vrf.h" +#include "lib/json.h" +#include "printfrr.h" #include "zebra/debug.h" #include "zebra/rib.h" @@ -506,6 +508,155 @@ DEFUN (show_pseudowires, return CMD_SUCCESS; } +static void vty_show_mpls_pseudowire_detail(struct vty *vty) +{ + struct zebra_vrf *zvrf; + struct zebra_pw *pw; + struct route_entry *re; + struct nexthop *nexthop; + + zvrf = vrf_info_lookup(VRF_DEFAULT); + if (!zvrf) + return; + + RB_FOREACH (pw, zebra_pw_head, &zvrf->pseudowires) { + char buf_nbr[INET6_ADDRSTRLEN]; + char buf_nh[100]; + + vty_out(vty, "Interface: %s\n", pw->ifname); + inet_ntop(pw->af, &pw->nexthop, buf_nbr, sizeof(buf_nbr)); + vty_out(vty, " Neighbor: %s\n", + (pw->af != AF_UNSPEC) ? buf_nbr : "-"); + if (pw->local_label != MPLS_NO_LABEL) + vty_out(vty, " Local Label: %u\n", pw->local_label); + else + vty_out(vty, " Local Label: %s\n", "-"); + if (pw->remote_label != MPLS_NO_LABEL) + vty_out(vty, " Remote Label: %u\n", pw->remote_label); + else + vty_out(vty, " Remote Label: %s\n", "-"); + vty_out(vty, " Protocol: %s\n", + zebra_route_string(pw->protocol)); + if (pw->protocol == ZEBRA_ROUTE_LDP) + vty_out(vty, " VC-ID: %u\n", pw->data.ldp.pwid); + vty_out(vty, " Status: %s \n", + (zebra_pw_enabled(pw) && pw->status == PW_STATUS_UP) + ? "Up" + : "Down"); + re = rib_match(family2afi(pw->af), SAFI_UNICAST, pw->vrf_id, + &pw->nexthop, NULL); + if (re) { + for (ALL_NEXTHOPS_PTR(rib_active_nhg(re), nexthop)) { + snprintfrr(buf_nh, sizeof(buf_nh), "%pNHv", + nexthop); + vty_out(vty, " Next Hop: %s\n", buf_nh); + if (nexthop->nh_label) + vty_out(vty, " Next Hop label: %u\n", + nexthop->nh_label->label[0]); + else + vty_out(vty, " Next Hop label: %s\n", + "-"); + } + } + } +} + +static void vty_show_mpls_pseudowire(struct zebra_pw *pw, json_object *json_pws) +{ + struct route_entry *re; + struct nexthop *nexthop; + char buf_nbr[INET6_ADDRSTRLEN]; + char buf_nh[100]; + json_object *json_pw = NULL; + json_object *json_nexthop = NULL; + json_object *json_nexthops = NULL; + + json_nexthops = json_object_new_array(); + json_pw = json_object_new_object(); + + json_object_string_add(json_pw, "interface", pw->ifname); + if (pw->af == AF_UNSPEC) + json_object_string_add(json_pw, "neighbor", "-"); + else { + inet_ntop(pw->af, &pw->nexthop, buf_nbr, sizeof(buf_nbr)); + json_object_string_add(json_pw, "neighbor", buf_nbr); + } + if (pw->local_label != MPLS_NO_LABEL) + json_object_int_add(json_pw, "localLabel", pw->local_label); + else + json_object_string_add(json_pw, "localLabel", "-"); + if (pw->remote_label != MPLS_NO_LABEL) + json_object_int_add(json_pw, "remoteLabel", pw->remote_label); + else + json_object_string_add(json_pw, "remoteLabel", "-"); + json_object_string_add(json_pw, "protocol", + zebra_route_string(pw->protocol)); + if (pw->protocol == ZEBRA_ROUTE_LDP) + json_object_int_add(json_pw, "vcId", pw->data.ldp.pwid); + json_object_string_add( + json_pw, "Status", + (zebra_pw_enabled(pw) && pw->status == PW_STATUS_UP) ? "Up" + : "Down"); + re = rib_match(family2afi(pw->af), SAFI_UNICAST, pw->vrf_id, + &pw->nexthop, NULL); + if (re) { + for (ALL_NEXTHOPS_PTR(rib_active_nhg(re), nexthop)) { + json_nexthop = json_object_new_object(); + snprintfrr(buf_nh, sizeof(buf_nh), "%pNHv", nexthop); + json_object_string_add(json_nexthop, "nexthop", buf_nh); + if (nexthop->nh_label) + json_object_int_add( + json_nexthop, "nhLabel", + nexthop->nh_label->label[0]); + else + json_object_string_add(json_nexthop, "nhLabel", + "-"); + + json_object_array_add(json_nexthops, json_nexthop); + } + json_object_object_add(json_pw, "nexthops", json_nexthops); + } + json_object_array_add(json_pws, json_pw); +} + +static void vty_show_mpls_pseudowire_detail_json(struct vty *vty) +{ + json_object *json = NULL; + json_object *json_pws = NULL; + struct zebra_vrf *zvrf; + struct zebra_pw *pw; + + zvrf = vrf_info_lookup(VRF_DEFAULT); + if (!zvrf) + return; + + json = json_object_new_object(); + json_pws = json_object_new_array(); + RB_FOREACH (pw, zebra_pw_head, &zvrf->pseudowires) { + vty_show_mpls_pseudowire(pw, json_pws); + } + json_object_object_add(json, "pw", json_pws); + vty_out(vty, "%s\n", + json_object_to_json_string_ext(json, JSON_C_TO_STRING_PRETTY)); + json_object_free(json); +} + +DEFUN(show_pseudowires_detail, show_pseudowires_detail_cmd, + "show mpls pseudowires detail [json]$json", + SHOW_STR MPLS_STR + "Pseudowires\n" + "Detailed output\n" JSON_STR) +{ + bool uj = use_json(argc, argv); + + if (uj) + vty_show_mpls_pseudowire_detail_json(vty); + else + vty_show_mpls_pseudowire_detail(vty); + + return CMD_SUCCESS; +} + /* Pseudowire configuration write function. */ static int zebra_pw_config(struct vty *vty) { @@ -568,4 +719,5 @@ void zebra_pw_vty_init(void) install_element(PW_NODE, &pseudowire_control_word_cmd); install_element(VIEW_NODE, &show_pseudowires_cmd); + install_element(VIEW_NODE, &show_pseudowires_detail_cmd); }