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

Reply via email to