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. Existing UNICAST_MASTER_TABLE TLV exposes only a table of masters PortAddresses. There is nothing more - no identities, no state, no information about ClockQuality. ACCEPTABLE_MASTER_TABLE exposes only a table of masters PortIdentities, and the data in ACCEPTABLE_MASTER_TABLE has very different meaning from UNICAST_MASTER_TABLE (as far as I can tell, it's designed to limit the list of GMs the client is allowed to work with). So while it would be easy to implement UNICAST_MASTER_TABLE TLV it will give nothing that simple `cat /etc/ptp4l.conf` can't (a list of configured unicast master IPs). 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 actual_table_size 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 Signed-off-by: Alexander Bulimov <abuli...@fb.com> --- ddt.h | 10 ++++++++ pmc.c | 43 ++++++++++++++++++++++++++++++++-- pmc_common.c | 3 ++- port.c | 66 +++++++++++++++++++++++++++++++++++++++++++++------- tlv.c | 56 ++++++++++++++++++++++++++++++++++++++++++-- tlv.h | 6 +++++ util.c | 16 +++++++++++++ util.h | 3 +++ 8 files changed, 189 insertions(+), 14 deletions(-) diff --git a/ddt.h b/ddt.h index ef30f73..5dc5530 100644 --- a/ddt.h +++ b/ddt.h @@ -120,4 +120,14 @@ struct PortServiceStats { uint64_t followup_mismatch; }; +struct unicast_master_entry { + struct PortIdentity port_identity; + struct ClockQuality clock_quality; + uint8_t selected; + Enumeration8 port_state; + UInteger8 priority1; + UInteger8 priority2; + struct PortAddress address; +} PACKED; + #endif diff --git a/pmc.c b/pmc.c index 2e5e93e..7916153 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 unicast_master_entry *entry, + FILE *fp) +{ + fprintf(fp, + IFMT "%s %-24s %-34s %-9s %-10hhu 0x%02hhx 0x%04hx %-3hhu %-3hhu", + entry->selected ? "yes" : "no ", + pid2str(&entry->port_identity), + portaddr2str(&entry->address), + ustate2str(entry->port_state), + entry->clock_quality.clockClass, + entry->clock_quality.clockAccuracy, + entry->clock_quality.offsetScaledLogVariance, + entry->priority1, + entry->priority2 + ); +} + static void pmc_show_signaling(struct ptp_message *msg, FILE *fp) { struct slave_rx_sync_timing_record *sync_record; @@ -139,16 +157,18 @@ static void pmc_show_signaling(struct ptp_message *msg, FILE *fp) static void pmc_show(struct ptp_message *msg, FILE *fp) { + struct unicast_master_table_np *umtn; struct grandmaster_settings_np *gsn; + struct port_service_stats_np *pssp; struct mgmt_clock_description *cd; - struct subscribe_events_np *sen; struct management_tlv_datum *mtd; + struct unicast_master_entry *ume; + struct subscribe_events_np *sen; struct port_properties_np *ppn; struct timePropertiesDS *tp; struct management_tlv *mgt; struct time_status_np *tsn; struct port_stats_np *pcp; - struct port_service_stats_np *pssp; struct tlv_extra *extra; struct port_ds_np *pnp; struct defaultDS *dds; @@ -156,6 +176,7 @@ static void pmc_show(struct ptp_message *msg, FILE *fp) struct parentDS *pds; struct portDS *p; struct TLV *tlv; + uint8_t *buf; int action; if (msg_type(msg) == SIGNALING) { @@ -522,6 +543,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 "actual_table_size %" PRIu16, + umtn->actual_table_size); + 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->actual_table_size; i++) { + ume = (struct unicast_master_entry *) 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..54bd328 100644 --- a/pmc_common.c +++ b/pmc_common.c @@ -130,9 +130,10 @@ struct management_id idtab[] = { { "DELAY_MECHANISM", MID_DELAY_MECHANISM, do_get_action }, { "LOG_MIN_PDELAY_REQ_INTERVAL", MID_LOG_MIN_PDELAY_REQ_INTERVAL, do_get_action }, { "PORT_DATA_SET_NP", MID_PORT_DATA_SET_NP, do_set_action }, + { "PORT_PROPERTIES_NP", MID_PORT_PROPERTIES_NP, do_get_action }, { "PORT_STATS_NP", MID_PORT_STATS_NP, do_get_action }, { "PORT_SERVICE_STATS_NP", MID_PORT_SERVICE_STATS_NP, do_get_action }, - { "PORT_PROPERTIES_NP", MID_PORT_PROPERTIES_NP, do_get_action }, + { "UNICAST_MASTER_TABLE_NP", MID_UNICAST_MASTER_TABLE_NP, do_get_action }, }; static void do_get_action(struct pmc *pmc, int action, int index, char *str) diff --git a/port.c b/port.c index c1d0ac8..85b6575 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. */ @@ -793,15 +794,20 @@ static const Octet profile_id_p2p[] = {0x00, 0x1B, 0x19, 0x00, 0x02, 0x00}; static int port_management_fill_response(struct port *target, struct ptp_message *rsp, int id) { + struct unicast_master_table_np *umtn; + struct unicast_master_address *ucma; + struct port_service_stats_np *pssn; struct mgmt_clock_description *cd; struct management_tlv_datum *mtd; + struct unicast_master_entry *ume; struct clock_description *desc; struct port_properties_np *ppn; - struct port_stats_np *psn; - struct port_service_stats_np *pssn; struct management_tlv *tlv; + struct port_stats_np *psn; + struct foreign_clock *fc; struct port_ds_np *pdsnp; struct tlv_extra *extra; + struct PortIdentity pid; const char *ts_label; struct portDS *pds; uint16_t u16; @@ -973,6 +979,48 @@ 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->actual_table_size); + if (!unicast_client_enabled(target)) { + umtn->actual_table_size = 0; + datalen = buf - tlv->data; + break; + } + + STAILQ_FOREACH(ucma, &target->unicast_master_table->addrs, + list) { + ume = (struct unicast_master_entry *) buf; + ume->address.networkProtocol = ucma->type; + address_to_portaddress( + &ucma->address, &ume->address); + ume->port_identity = ucma->portIdentity; + ume->port_state = 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->port_identity, + &fc->dataset.sender)) { + ume->clock_quality = + fc->dataset.quality; + ume->priority1 = fc->dataset.priority1; + ume->priority2 = fc->dataset.priority2; + break; + } + } + buf += sizeof(struct unicast_master_entry) + + ume->address.addressLength; + umtn->actual_table_size++; + } + 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..4b74fe7 100644 --- a/tlv.c +++ b/tlv.c @@ -113,14 +113,16 @@ static bool tlv_array_invalid(struct TLV *tlv, size_t base_size, size_t item_siz static int mgt_post_recv(struct management_tlv *m, uint16_t data_len, struct tlv_extra *extra) { + struct unicast_master_table_np *umtn; struct grandmaster_settings_np *gsn; + struct port_service_stats_np *pssn; struct mgmt_clock_description *cd; + struct unicast_master_entry *ume; struct subscribe_events_np *sen; struct port_properties_np *ppn; struct timePropertiesDS *tp; struct time_status_np *tsn; struct port_stats_np *psn; - struct port_service_stats_np *pssn; int extra_len = 0, i, len; struct port_ds_np *pdsnp; struct currentDS *cds; @@ -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->actual_table_size = + ntohs(umtn->actual_table_size); + buf = (uint8_t *) umtn->unicast_masters; + for (int i = 0; i < umtn->actual_table_size; i++) { + len += sizeof(struct unicast_master_entry); + if (data_len < len) + goto bad_length; + ume = (struct unicast_master_entry *) buf; + ume->port_identity.portNumber = + ntohs(ume->port_identity.portNumber); + ume->clock_quality.offsetScaledLogVariance = + ntohs(ume->clock_quality.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: @@ -383,19 +413,22 @@ bad_length: static void mgt_pre_send(struct management_tlv *m, struct tlv_extra *extra) { + struct unicast_master_table_np *umtn; struct grandmaster_settings_np *gsn; + struct port_service_stats_np *pssn; struct mgmt_clock_description *cd; + struct unicast_master_entry *ume; struct subscribe_events_np *sen; struct port_properties_np *ppn; struct timePropertiesDS *tp; struct time_status_np *tsn; struct port_stats_np *psn; - struct port_service_stats_np *pssn; struct port_ds_np *pdsnp; struct defaultDS *dds; struct currentDS *cds; struct parentDS *pds; struct portDS *p; + uint8_t *buf; int i; switch (m->id) { @@ -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->actual_table_size; i++) { + ume = (struct unicast_master_entry *) buf; + // update pointer before the conversion + buf += sizeof(*ume) + ume->address.addressLength; + ume->port_identity.portNumber = + htons(ume->port_identity.portNumber); + ume->clock_quality.offsetScaledLogVariance = + htons(ume->clock_quality.offsetScaledLogVariance); + ume->address.networkProtocol = + htons(ume->address.networkProtocol); + ume->address.addressLength = + htons(ume->address.addressLength); + } + umtn->actual_table_size = + htons(umtn->actual_table_size); + break; } } diff --git a/tlv.h b/tlv.h index 408c22c..915b9fc 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 actual_table_size; + struct unicast_master_entry 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