This change adds new UNICAST_MASTER_TABLE_NP management TLV that exposes table with details of each gm: identity, IP address, state, if it's selected as best master, and information from the latest Announce msg that is used in BMCA algorithm: ClockQuality and Priorities.
The inspiration was `chronyc sources` command that provides great insights into the state of NTP client. Here we tried to provide something similar, but for unicast PTP client. Justification: we operate unicast PTPv2 in datacenters, and each client has multiple unicast GMs set up in the config. First, it quickly became clear that we need a way to map the GM identity from PARENT_DATA_SET or TIME_STATUS_NP TLVs to the actual IP. Next we realized the need to be able to inspect the full unicast master table along with GM states and preferably Announce information, to quickly debug potential issues and better understand why the particular GM was selected as best master. The 1588-2019 standard doesn't provide any real means to inspect the active unicast master table. UNICAST_MASTER_TABLE TLV is pretty useless as described in the standard, and with linuxptp it can be effectively replaced by `cat /etc/ptp4l.conf`. Here is the example output that hopefully shows the usefullness of new TLV: we can instantly see that ptp4l coudn't even talk to some of the GMs. ./pmc -u -b 0 'GET UNICAST_MASTER_TABLE_NP' sending: GET UNICAST_MASTER_TABLE_NP 4857dd.fffe.0e91da-1 seq 0 RESPONSE MANAGEMENT MID_UNICAST_MASTER_TABLE_NP actualTableSize 9 BM identity address state clockClass clockQuality offsetScaledLogVariance p1 p2 no b8cef6.fffe.7349d4-1 2401:db00:2515:f001:face:0:2a3:0 HAVE_ANN 6 0x21 0x59e0 128 128 yes b8cef6.fffe.0210e4-1 2401:db00:2515:f001:face:0:3d1:0 HAVE_SYDY 6 0x21 0x59e0 128 128 no b8cef6.fffe.057e20-1 2401:db00:2515:f001:face:0:3fa:0 HAVE_ANN 6 0x21 0x59e0 128 128 no b8cef6.fffe.7349dc-1 2401:db00:2515:f001:face:0:da:0 HAVE_ANN 6 0x21 0x59e0 128 128 no ffffff.ffff.ffffff-65535 2401:db00:2515:f002:face:0:11b:0 WAIT 0 0x00 0x0000 0 0 no b8cef6.fffe.7349c4-1 2401:db00:2515:f002:face:0:1ec:0 HAVE_ANN 6 0x21 0x59e0 128 128 no ffffff.ffff.ffffff-65535 2401:db00:2515:f002:face:0:94:0 WAIT 0 0x00 0x0000 0 0 no ffffff.ffff.ffffff-65535 192.168.0.1 WAIT 0 0x00 0x0000 0 0 no b8cef6.fffe.7349c8-1 2401:db00:2515:f002:face:0:b7:0 HAVE_ANN 6 0x21 0x59e0 128 128 I realize that this is can be controversial (for one, no other PMC command provides table-like output), so I'm happy to hear your suggestion. Signed-off-by: Alexander Bulimov <abuli...@fb.com> --- ddt.h | 10 +++++++++ pmc.c | 40 ++++++++++++++++++++++++++++++++++ pmc_common.c | 1 + port.c | 61 ++++++++++++++++++++++++++++++++++++++++++++++------ tlv.c | 52 ++++++++++++++++++++++++++++++++++++++++++++ tlv.h | 6 ++++++ util.c | 16 ++++++++++++++ util.h | 3 +++ 8 files changed, 182 insertions(+), 7 deletions(-) diff --git a/ddt.h b/ddt.h index ef30f73..7a95344 100644 --- a/ddt.h +++ b/ddt.h @@ -120,4 +120,14 @@ struct PortServiceStats { uint64_t followup_mismatch; }; +struct UnicastMasterEntry { + uint8_t selected; + Enumeration8 portState; + struct PortIdentity portIdentity; + struct ClockQuality clockQuality; + UInteger8 priority1; + UInteger8 priority2; + struct PortAddress address; +} PACKED; + #endif diff --git a/pmc.c b/pmc.c index 2e5e93e..577e479 100644 --- a/pmc.c +++ b/pmc.c @@ -88,6 +88,24 @@ static void pmc_show_rx_sync_timing(struct slave_rx_sync_timing_record *record, SHOW_TIMESTAMP(record->syncEventIngressTimestamp)); } + +static void pmc_show_unicast_master_entry(struct UnicastMasterEntry *entry, + FILE *fp) +{ + fprintf(fp, + IFMT "%s %-24s %-34s %-9s %-10hhu 0x%02hhx 0x%04hx %-3hhu %-3hhu", + entry->selected ? "yes" : "no ", + pid2str(&entry->portIdentity), + portaddr2str(&entry->address), + ustate2str(entry->portState), + entry->clockQuality.clockClass, + entry->clockQuality.clockAccuracy, + entry->clockQuality.offsetScaledLogVariance, + entry->priority1, + entry->priority2 + ); +} + static void pmc_show_signaling(struct ptp_message *msg, FILE *fp) { struct slave_rx_sync_timing_record *sync_record; @@ -149,6 +167,7 @@ static void pmc_show(struct ptp_message *msg, FILE *fp) struct time_status_np *tsn; struct port_stats_np *pcp; struct port_service_stats_np *pssp; + struct unicast_master_table_np *umtn; struct tlv_extra *extra; struct port_ds_np *pnp; struct defaultDS *dds; @@ -157,6 +176,9 @@ static void pmc_show(struct ptp_message *msg, FILE *fp) struct portDS *p; struct TLV *tlv; int action; + uint8_t *buf; + + struct UnicastMasterEntry *ume; if (msg_type(msg) == SIGNALING) { pmc_show_signaling(msg, fp); @@ -522,6 +544,24 @@ static void pmc_show(struct ptp_message *msg, FILE *fp) pssp->stats.sync_mismatch, pssp->stats.followup_mismatch); break; + case MID_UNICAST_MASTER_TABLE_NP: + umtn = (struct unicast_master_table_np *) mgt->data; + fprintf(fp, "MID_UNICAST_MASTER_TABLE_NP " + IFMT "actualTableSize %" PRIu16, + umtn->actualTableSize); + buf = (uint8_t *) umtn->unicast_masters; + // table header + fprintf(fp, + IFMT "%s %-24s %-34s %-9s %s %s %s %s %s", + "BM", "identity", "address", "state", + "clockClass", "clockQuality", "offsetScaledLogVariance", + "p1", "p2"); + for (int i = 0; i < umtn->actualTableSize; i++) { + ume = (struct UnicastMasterEntry *) buf; + pmc_show_unicast_master_entry(ume, fp); + buf += sizeof(*ume) + ume->address.addressLength; + } + break; case MID_LOG_ANNOUNCE_INTERVAL: mtd = (struct management_tlv_datum *) mgt->data; fprintf(fp, "LOG_ANNOUNCE_INTERVAL " diff --git a/pmc_common.c b/pmc_common.c index b691bbb..4ed781f 100644 --- a/pmc_common.c +++ b/pmc_common.c @@ -124,6 +124,7 @@ struct management_id idtab[] = { { "UNICAST_NEGOTIATION_ENABLE", MID_UNICAST_NEGOTIATION_ENABLE, not_supported }, { "UNICAST_MASTER_TABLE", MID_UNICAST_MASTER_TABLE, not_supported }, { "UNICAST_MASTER_MAX_TABLE_SIZE", MID_UNICAST_MASTER_MAX_TABLE_SIZE, not_supported }, + { "UNICAST_MASTER_TABLE_NP", MID_UNICAST_MASTER_TABLE_NP, do_get_action }, { "ACCEPTABLE_MASTER_TABLE_ENABLED", MID_ACCEPTABLE_MASTER_TABLE_ENABLED, not_supported }, { "ALTERNATE_MASTER", MID_ALTERNATE_MASTER, not_supported }, { "TRANSPARENT_CLOCK_PORT_DATA_SET", MID_TRANSPARENT_CLOCK_PORT_DATA_SET, not_supported }, diff --git a/port.c b/port.c index c1d0ac8..e25d164 100644 --- a/port.c +++ b/port.c @@ -110,22 +110,23 @@ static int check_source_identity(struct port *p, struct ptp_message *m) return pid_eq(&master, &m->header.sourcePortIdentity) ? 0 : -1; } -static void extract_address(struct ptp_message *m, struct PortAddress *paddr) +static void address_to_portaddress(struct address *addr, + struct PortAddress *paddr) { int len = 0; switch (paddr->networkProtocol) { case TRANS_UDP_IPV4: - len = sizeof(m->address.sin.sin_addr.s_addr); - memcpy(paddr->address, &m->address.sin.sin_addr.s_addr, len); + len = sizeof(addr->sin.sin_addr.s_addr); + memcpy(paddr->address, &addr->sin.sin_addr.s_addr, len); break; case TRANS_UDP_IPV6: - len = sizeof(m->address.sin6.sin6_addr.s6_addr); - memcpy(paddr->address, &m->address.sin6.sin6_addr.s6_addr, len); + len = sizeof(addr->sin6.sin6_addr.s6_addr); + memcpy(paddr->address, &addr->sin6.sin6_addr.s6_addr, len); break; case TRANS_IEEE_802_3: len = MAC_LEN; - memcpy(paddr->address, &m->address.sll.sll_addr, len); + memcpy(paddr->address, &addr->sll.sll_addr, len); break; default: return; @@ -439,7 +440,7 @@ static int net_sync_resp_append(struct port *p, struct ptp_message *m) transport_protocol_addr(best->trp, paddr->address); if (best->best) { tmp = TAILQ_FIRST(&best->best->messages); - extract_address(tmp, paddr); + address_to_portaddress(&tmp->address, paddr); } } else { /* We are our own parent. */ @@ -799,6 +800,7 @@ static int port_management_fill_response(struct port *target, struct port_properties_np *ppn; struct port_stats_np *psn; struct port_service_stats_np *pssn; + struct unicast_master_table_np *umtn; struct management_tlv *tlv; struct port_ds_np *pdsnp; struct tlv_extra *extra; @@ -807,6 +809,10 @@ static int port_management_fill_response(struct port *target, uint16_t u16; uint8_t *buf; int datalen; + struct unicast_master_address *ucma; + struct UnicastMasterEntry *ume; + struct PortIdentity pid; + struct foreign_clock *fc; extra = tlv_extra_alloc(); if (!extra) { @@ -973,6 +979,47 @@ static int port_management_fill_response(struct port *target, pssn->stats = target->service_stats; datalen = sizeof(*pssn); break; + case MID_UNICAST_MASTER_TABLE_NP: + umtn = (struct unicast_master_table_np *)tlv->data; + buf = tlv->data + sizeof(umtn->actualTableSize); + if (!unicast_client_enabled(target)) { + umtn->actualTableSize = 0; + datalen = buf - tlv->data; + break; + } + + STAILQ_FOREACH(ucma, &target->unicast_master_table->addrs, + list) { + ume = (struct UnicastMasterEntry *) buf; + ume->address.networkProtocol = ucma->type; + address_to_portaddress( + &ucma->address, &ume->address); + ume->portIdentity = ucma->portIdentity; + ume->portState = ucma->state; + pid = clock_parent_identity(target->clock); + if (pid_eq(&ucma->portIdentity, &pid)) { + ume->selected = 1; + } + + /* iterate over foreign masters and search for + * the current identity + */ + LIST_FOREACH(fc, &target->foreign_masters, + list) { + if (pid_eq(&ume->portIdentity, + &fc->dataset.sender)) { + ume->clockQuality = fc->dataset.quality; + ume->priority1 = fc->dataset.priority1; + ume->priority2 = fc->dataset.priority2; + break; + } + } + buf += sizeof(struct UnicastMasterEntry) + + ume->address.addressLength; + umtn->actualTableSize++; + } + datalen = buf - tlv->data; + break; default: /* The caller should *not* respond to this message. */ tlv_extra_recycle(extra); diff --git a/tlv.c b/tlv.c index df516be..f5420f7 100644 --- a/tlv.c +++ b/tlv.c @@ -127,6 +127,8 @@ static int mgt_post_recv(struct management_tlv *m, uint16_t data_len, struct defaultDS *dds; struct parentDS *pds; struct portDS *p; + struct unicast_master_table_np *umtn; + struct UnicastMasterEntry *ume; uint8_t *buf; uint16_t u16; @@ -359,6 +361,34 @@ static int mgt_post_recv(struct management_tlv *m, uint16_t data_len, pssn->stats.followup_mismatch = __le64_to_cpu(pssn->stats.followup_mismatch); extra_len = sizeof(struct port_service_stats_np); + break; + case MID_UNICAST_MASTER_TABLE_NP: + if (data_len < sizeof(struct unicast_master_table_np)) + goto bad_length; + len = sizeof(struct unicast_master_table_np); + umtn = (struct unicast_master_table_np *)m->data; + umtn->actualTableSize = + ntohs(umtn->actualTableSize); + buf = (uint8_t *) umtn->unicast_masters; + for (int i = 0; i < umtn->actualTableSize; i++) { + len += sizeof(struct UnicastMasterEntry); + if (data_len < len) + goto bad_length; + ume = (struct UnicastMasterEntry *) buf; + ume->portIdentity.portNumber = + ntohs(ume->portIdentity.portNumber); + ume->clockQuality.offsetScaledLogVariance = + ntohs(ume->clockQuality.offsetScaledLogVariance); + ume->address.networkProtocol = + ntohs(ume->address.networkProtocol); + ume->address.addressLength = + ntohs(ume->address.addressLength); + len += ume->address.addressLength; + if (data_len < len) + goto bad_length; + buf += sizeof(*ume) + ume->address.addressLength; + } + break; case MID_SAVE_IN_NON_VOLATILE_STORAGE: case MID_RESET_NON_VOLATILE_STORAGE: @@ -396,7 +426,10 @@ static void mgt_pre_send(struct management_tlv *m, struct tlv_extra *extra) struct currentDS *cds; struct parentDS *pds; struct portDS *p; + struct unicast_master_table_np *umtn; + struct UnicastMasterEntry *ume; int i; + uint8_t *buf; switch (m->id) { case MID_CLOCK_DESCRIPTION: @@ -503,6 +536,25 @@ static void mgt_pre_send(struct management_tlv *m, struct tlv_extra *extra) pssn->stats.followup_mismatch = __cpu_to_le64(pssn->stats.followup_mismatch); break; + case MID_UNICAST_MASTER_TABLE_NP: + umtn = (struct unicast_master_table_np *)m->data; + buf = (uint8_t *) umtn->unicast_masters; + for (int i = 0; i < umtn->actualTableSize; i++) { + ume = (struct UnicastMasterEntry *) buf; + // update pointer before the conversion + buf += sizeof(*ume) + ume->address.addressLength; + ume->portIdentity.portNumber = + htons(ume->portIdentity.portNumber); + ume->clockQuality.offsetScaledLogVariance = + htons(ume->clockQuality.offsetScaledLogVariance); + ume->address.networkProtocol = + htons(ume->address.networkProtocol); + ume->address.addressLength = + htons(ume->address.addressLength); + } + umtn->actualTableSize = + htons(umtn->actualTableSize); + break; } } diff --git a/tlv.h b/tlv.h index 408c22c..b2471b7 100644 --- a/tlv.h +++ b/tlv.h @@ -126,6 +126,7 @@ enum management_action { #define MID_PORT_PROPERTIES_NP 0xC004 #define MID_PORT_STATS_NP 0xC005 #define MID_PORT_SERVICE_STATS_NP 0xC007 +#define MID_UNICAST_MASTER_TABLE_NP 0xC008 /* Management error ID values */ #define MID_RESPONSE_TOO_BIG 0x0001 @@ -355,6 +356,11 @@ struct port_service_stats_np { struct PortServiceStats stats; } PACKED; +struct unicast_master_table_np { + uint16_t actualTableSize; + struct UnicastMasterEntry unicast_masters[0]; +} PACKED; + #define PROFILE_ID_LEN 6 struct mgmt_clock_description { diff --git a/util.c b/util.c index 25a906c..a59b559 100644 --- a/util.c +++ b/util.c @@ -191,6 +191,22 @@ char *portaddr2str(struct PortAddress *addr) return buf; } +const char *ustate2str(enum unicast_state ustate) +{ + switch (ustate) { + case UC_WAIT: + return "WAIT"; + case UC_HAVE_ANN: + return "HAVE_ANN"; + case UC_NEED_SYDY: + return "NEED_SYDY"; + case UC_HAVE_SYDY: + return "HAVE_SYDY"; + } + + return "???"; +} + void posix_clock_close(clockid_t clock) { if (clock == CLOCK_REALTIME) { diff --git a/util.h b/util.h index 739c8fd..558a675 100644 --- a/util.h +++ b/util.h @@ -27,6 +27,7 @@ #include "ddt.h" #include "ether.h" #include "transport.h" +#include "unicast_fsm.h" #define MAX_PRINT_BYTES 16 #define BIN_BUF_SIZE (MAX_PRINT_BYTES * 3 + 1) @@ -110,6 +111,8 @@ char *pid2str(struct PortIdentity *id); char *portaddr2str(struct PortAddress *addr); +const char *ustate2str(enum unicast_state ustate); + /** * Closes a dynamic posix clock. * @param clock A clock ID obtained via posix_clock_close(). -- 2.30.2 _______________________________________________ Linuxptp-devel mailing list Linuxptp-devel@lists.sourceforge.net https://lists.sourceforge.net/lists/listinfo/linuxptp-devel