From: Richard C. <ric...@gm...> - 2018-01-02 04:06:04
|
This will be needed in order to support the NetSync Monitor protocol. Signed-off-by: Richard Cochran <ric...@gm...> --- msg.c | 2 ++ msg.h | 1 + 2 files changed, 3 insertions(+) diff --git a/msg.c b/msg.c index 29af416..6a4fc9a 100644 --- a/msg.c +++ b/msg.c @@ -282,6 +282,7 @@ int msg_post_recv(struct ptp_message *m, int cnt) timestamp_post_recv(m, &m->sync.originTimestamp); break; case DELAY_REQ: + suffix = m->delay_req.suffix; break; case PDELAY_REQ: break; @@ -341,6 +342,7 @@ int msg_pre_send(struct ptp_message *m) case SYNC: break; case DELAY_REQ: + suffix = m->delay_req.suffix; break; case PDELAY_REQ: break; diff --git a/msg.h b/msg.h index 12e6ce8..d4f237e 100644 --- a/msg.h +++ b/msg.h @@ -114,6 +114,7 @@ struct sync_msg { struct delay_req_msg { struct ptp_header hdr; struct Timestamp originTimestamp; + uint8_t suffix[0]; } PACKED; struct follow_up_msg { -- 2.11.0 |
From: Richard C. <ric...@gm...> - 2018-01-02 04:06:05
|
As part of the NetSync Monitor protocol, the port will need to have access to the current data set. This patch adds the appropriate function. Signed-off-by: Richard Cochran <ric...@gm...> --- clock.c | 5 +++++ clock.h | 7 +++++++ 2 files changed, 12 insertions(+) diff --git a/clock.c b/clock.c index 41c8f81..61eaa87 100644 --- a/clock.c +++ b/clock.c @@ -764,6 +764,11 @@ struct config *clock_config(struct clock *c) return c->config; } +struct currentDS *clock_current_dataset(struct clock *c) +{ + return &c->cur; +} + static int clock_add_port(struct clock *c, int phc_index, enum timestamp_type timestamping, struct interface *iface) diff --git a/clock.h b/clock.h index 986d363..64c5131 100644 --- a/clock.h +++ b/clock.h @@ -73,6 +73,13 @@ UInteger8 clock_class(struct clock *c); struct config *clock_config(struct clock *c); /** + * Obtains a reference to the current dataset. + * @param c The clock instance. + * @return A pointer to the current dataset, without fail. + */ +struct currentDS *clock_current_dataset(struct clock *c); + +/** * Obtains the required time stamping mode. * @param c The clock instance. * @return The value of required time stamping mode, which is a bit mask -- 2.11.0 |
From: Richard C. <ric...@gm...> - 2018-01-02 04:06:06
|
The NetSync Monitor protocol will require us to report the time stamp of the last synchronization. This patch adds new the method. Signed-off-by: Richard Cochran <ric...@gm...> --- clock.c | 5 +++++ clock.h | 7 +++++++ 2 files changed, 12 insertions(+) diff --git a/clock.c b/clock.c index 61eaa87..08d9d91 100644 --- a/clock.c +++ b/clock.c @@ -1279,6 +1279,11 @@ static void clock_forward_mgmt_msg(struct clock *c, struct port *p, struct ptp_m } } +tmv_t clock_ingress_time(struct clock *c) +{ + return c->ingress_ts; +} + int clock_manage(struct clock *c, struct port *p, struct ptp_message *msg) { int changed = 0, res, answers; diff --git a/clock.h b/clock.h index 64c5131..94d4ad0 100644 --- a/clock.h +++ b/clock.h @@ -157,6 +157,13 @@ struct ClockIdentity clock_identity(struct clock *c); void clock_fda_changed(struct clock *c); /** + * Obtains the time of the latest synchronization. + * @param c The clock instance. + * @return The local time stamp of the last received Sync message. + */ +tmv_t clock_ingress_time(struct clock *c); + +/** * Manage the clock according to a given message. * @param c The clock instance. * @param p The port on which the message arrived. -- 2.11.0 |
From: Richard C. <ric...@gm...> - 2018-01-02 04:06:07
|
This will be needed in order to generate a TLV for the NSM protocol. Signed-off-by: Richard Cochran <ric...@gm...> --- tmv.h | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/tmv.h b/tmv.h index 30b41ee..5d1e12b 100644 --- a/tmv.h +++ b/tmv.h @@ -96,6 +96,21 @@ static inline TimeInterval tmv_to_TimeInterval(tmv_t x) return x << 16; } +static inline struct Timestamp tmv_to_Timestamp(tmv_t x) +{ + struct Timestamp result; + uint64_t sec, nsec; + + sec = x / 1000000000ULL; + nsec = x % 1000000000ULL; + + result.seconds_lsb = sec & 0xFFFFFFFF; + result.seconds_msb = (sec >> 32) & 0xFFFF; + result.nanoseconds = nsec; + + return result; +} + static inline tmv_t timespec_to_tmv(struct timespec ts) { return ts.tv_sec * NS_PER_SEC + ts.tv_nsec; -- 2.11.0 |
From: Richard C. <ric...@gm...> - 2018-01-02 04:06:09
|
The port will need to send unicast Sync messages in order to support the NSM protocol. Besides that, we will need this ability anyhow if we ever want to implement unicast operation. Signed-off-by: Richard Cochran <ric...@gm...> --- port.c | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/port.c b/port.c index b2647d3..4e0da1a 100644 --- a/port.c +++ b/port.c @@ -1313,7 +1313,7 @@ static int port_tx_announce(struct port *p) return err; } -static int port_tx_sync(struct port *p) +static int port_tx_sync(struct port *p, struct address *dst) { struct ptp_message *msg, *fup; int err, pdulen; @@ -1349,6 +1349,10 @@ static int port_tx_sync(struct port *p) if (p->timestamping != TS_ONESTEP) msg->header.flagField[0] |= TWO_STEP; + if (dst) { + msg->address = *dst; + msg->header.flagField[0] |= UNICAST; + } err = port_prepare_and_send(p, msg, event); if (err) { pr_err("port %hu: send sync failed", portnum(p)); @@ -1382,6 +1386,10 @@ static int port_tx_sync(struct port *p) ts_to_timestamp(&msg->hwts.ts, &fup->follow_up.preciseOriginTimestamp); + if (dst) { + fup->address = *dst; + fup->header.flagField[0] |= UNICAST; + } err = port_prepare_and_send(p, fup, 0); if (err) pr_err("port %hu: send follow up failed", portnum(p)); @@ -2321,7 +2329,7 @@ enum fsm_event port_event(struct port *p, int fd_index) case FD_SYNC_TX_TIMER: pr_debug("port %hu: master sync timeout", portnum(p)); port_set_sync_tx_tmo(p); - return port_tx_sync(p) ? EV_FAULT_DETECTED : EV_NONE; + return port_tx_sync(p, NULL) ? EV_FAULT_DETECTED : EV_NONE; case FD_RTNL: pr_debug("port %hu: received link status notification", portnum(p)); -- 2.11.0 |
From: Richard C. <ric...@gm...> - 2018-01-02 04:06:09
|
This patch adds support for packing and unpacking the NSM TLVs. In addition, it introduces macros to make the ntoh/htoh boilerplate easier to read. The idea is to reduce the number of monstrous muti-line assignments like: pds->grandmasterClockQuality.offsetScaledLogVariance = htons(pds->grandmasterClockQuality.offsetScaledLogVariance); Signed-off-by: Richard Cochran <ric...@gm...> --- tlv.c | 144 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ tlv.h | 17 ++++++++ 2 files changed, 161 insertions(+) diff --git a/tlv.c b/tlv.c index a5c2eb3..666890d 100644 --- a/tlv.c +++ b/tlv.c @@ -24,6 +24,11 @@ #include "tlv.h" #include "msg.h" +#define HTONS(x) (x) = htons(x) +#define HTONL(x) (x) = htonl(x) +#define NTOHS(x) (x) = ntohs(x) +#define NTOHL(x) (x) = ntohl(x) + #define TLV_LENGTH_INVALID(tlv, type) \ (tlv->length < sizeof(struct type) - sizeof(struct TLV)) @@ -52,6 +57,24 @@ static uint16_t flip16(uint16_t *p) return v; } +static int64_t host2net64_unaligned(int64_t *p) +{ + int64_t v; + memcpy(&v, p, sizeof(v)); + v = host2net64(v); + memcpy(p, &v, sizeof(v)); + return v; +} + +static int64_t net2host64_unaligned(int64_t *p) +{ + int64_t v; + memcpy(&v, p, sizeof(v)); + v = net2host64(v); + memcpy(p, &v, sizeof(v)); + return v; +} + static int mgt_post_recv(struct management_tlv *m, uint16_t data_len, struct tlv_extra *extra) { @@ -367,6 +390,117 @@ static void mgt_pre_send(struct management_tlv *m, struct tlv_extra *extra) } } +static int nsm_resp_post_recv(struct TLV *tlv) +{ + struct nsm_resp_tlv_head *head; + struct nsm_resp_tlv_foot *foot; + struct timePropertiesDS *tp; + struct PortAddress *paddr; + struct currentDS *cds; + struct parentDS *pds; + unsigned char *ptr; + uint16_t expected; + + if (tlv->length < sizeof(*head) + sizeof(*foot) + - sizeof(head->type) - sizeof(head->length)) { + return -EBADMSG; + } + head = (struct nsm_resp_tlv_head *) tlv; + paddr = &head->parent_addr; + NTOHS(paddr->networkProtocol); + NTOHS(paddr->addressLength); + + switch (paddr->networkProtocol) { + case TRANS_UDP_IPV4: + expected = 4; + break; + case TRANS_UDP_IPV6: + expected = 16; + break; + case TRANS_IEEE_802_3: + expected = 6; + break; + default: + return -EBADMSG; + } + if (paddr->addressLength != expected) { + return -EBADMSG; + } + if (tlv->length != sizeof(*head) + sizeof(*foot) + paddr->addressLength + - sizeof(head->type) - sizeof(head->length)) { + return -EBADMSG; + } + + ptr = (unsigned char *) tlv; + ptr += sizeof(*head) + paddr->addressLength; + foot = (struct nsm_resp_tlv_foot *) ptr; + + pds = &foot->parent; + cds = &foot->current; + tp = &foot->timeprop; + + /* + * At this point the alignment only 2 bytes worst case. + * So we need to be careful with the 64 bit words. + */ + NTOHS(pds->parentPortIdentity.portNumber); + NTOHS(pds->observedParentOffsetScaledLogVariance); + NTOHL(pds->observedParentClockPhaseChangeRate); + NTOHS(pds->grandmasterClockQuality.offsetScaledLogVariance); + + NTOHS(cds->stepsRemoved); + net2host64_unaligned(&cds->offsetFromMaster); + net2host64_unaligned(&cds->meanPathDelay); + + NTOHS(tp->currentUtcOffset); + + NTOHL(foot->lastsync.seconds_lsb); + NTOHS(foot->lastsync.seconds_msb); + NTOHL(foot->lastsync.nanoseconds); + + return 0; +} + +static void nsm_resp_pre_send(struct TLV *tlv) +{ + struct nsm_resp_tlv_head *head; + struct nsm_resp_tlv_foot *foot; + struct timePropertiesDS *tp; + struct PortAddress *paddr; + struct currentDS *cds; + struct parentDS *pds; + unsigned char *ptr; + + head = (struct nsm_resp_tlv_head *) tlv; + paddr = &head->parent_addr; + + ptr = (unsigned char *) tlv; + ptr += sizeof(*head) + paddr->addressLength; + foot = (struct nsm_resp_tlv_foot *) ptr; + + pds = &foot->parent; + cds = &foot->current; + tp = &foot->timeprop; + + NTOHS(paddr->networkProtocol); + NTOHS(paddr->addressLength); + + HTONS(pds->parentPortIdentity.portNumber); + HTONS(pds->observedParentOffsetScaledLogVariance); + HTONL(pds->observedParentClockPhaseChangeRate); + HTONS(pds->grandmasterClockQuality.offsetScaledLogVariance); + + HTONS(cds->stepsRemoved); + host2net64_unaligned(&cds->offsetFromMaster); + host2net64_unaligned(&cds->meanPathDelay); + + HTONS(tp->currentUtcOffset); + + HTONL(foot->lastsync.seconds_lsb); + HTONS(foot->lastsync.seconds_msb); + HTONL(foot->lastsync.nanoseconds); +} + static int org_post_recv(struct organization_tlv *org) { struct follow_up_info_tlv *f; @@ -459,6 +593,11 @@ int tlv_post_recv(struct TLV *tlv, struct tlv_extra *extra) case TLV_AUTHENTICATION_CHALLENGE: case TLV_SECURITY_ASSOCIATION_UPDATE: case TLV_CUM_FREQ_SCALE_FACTOR_OFFSET: + case TLV_PTPMON_REQ: + break; + case TLV_PTPMON_RESP: + result = nsm_resp_post_recv(tlv); + break; default: break; } @@ -497,6 +636,11 @@ void tlv_pre_send(struct TLV *tlv, struct tlv_extra *extra) case TLV_AUTHENTICATION_CHALLENGE: case TLV_SECURITY_ASSOCIATION_UPDATE: case TLV_CUM_FREQ_SCALE_FACTOR_OFFSET: + case TLV_PTPMON_REQ: + break; + case TLV_PTPMON_RESP: + nsm_resp_pre_send(tlv); + break; default: break; } diff --git a/tlv.h b/tlv.h index c345afe..4e58d9c 100644 --- a/tlv.h +++ b/tlv.h @@ -37,6 +37,8 @@ #define TLV_AUTHENTICATION_CHALLENGE 0x2001 #define TLV_SECURITY_ASSOCIATION_UPDATE 0x2002 #define TLV_CUM_FREQ_SCALE_FACTOR_OFFSET 0x2003 +#define TLV_PTPMON_REQ 0x21FE +#define TLV_PTPMON_RESP 0x21FF enum management_action { GET, @@ -132,6 +134,21 @@ struct management_error_status { Octet data[0]; } PACKED; +struct nsm_resp_tlv_head { + Enumeration16 type; + UInteger16 length; + uint8_t port_state; + uint8_t reserved; + struct PortAddress parent_addr; +} PACKED; + +struct nsm_resp_tlv_foot { + struct parentDS parent; + struct currentDS current; + struct timePropertiesDS timeprop; + struct Timestamp lastsync; +} PACKED; + /* Organizationally Unique Identifiers */ #define IEEE_802_1_COMMITTEE 0x00, 0x80, 0xC2 extern uint8_t ieee8021_id[3]; -- 2.11.0 |
From: Richard C. <ric...@gm...> - 2018-01-02 04:06:11
|
When NSM is enabled on a given port, that port always replies to a NSM delay request with a delay response, sync, and follow up, regardless of the current state of the port. Signed-off-by: Richard Cochran <ric...@gm...> --- port.c | 128 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 123 insertions(+), 5 deletions(-) diff --git a/port.c b/port.c index 4e0da1a..4113d3f 100644 --- a/port.c +++ b/port.c @@ -126,6 +126,7 @@ struct port { int freq_est_interval; int hybrid_e2e; int min_neighbor_prop_delay; + int net_sync_monitor; int path_trace_enabled; int rx_timestamp_offset; int tx_timestamp_offset; @@ -184,6 +185,29 @@ static int clear_fault_asap(struct fault_interval *faint) return 0; } +static void extract_address(struct ptp_message *m, 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); + 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); + break; + case TRANS_IEEE_802_3: + len = MAC_LEN; + memcpy(paddr->address, &m->address.sll.sll_addr, len); + break; + default: + return; + } + paddr->addressLength = len; +} + static int msg_current(struct ptp_message *m, struct timespec now) { int64_t t1, t2, tmo; @@ -443,6 +467,61 @@ static int follow_up_info_append(struct port *p, struct ptp_message *m) return sizeof(*fui); } +static int net_sync_resp_append(struct port *p, struct ptp_message *m) +{ + struct timePropertiesDS *tp = clock_time_properties(p->clock); + struct ClockIdentity cid = clock_identity(p->clock), pid; + struct currentDS *cds = clock_current_dataset(p->clock); + struct parent_ds *dad = clock_parent_ds(p->clock); + struct port *best = clock_best_port(p->clock); + struct nsm_resp_tlv_head *head; + struct nsm_resp_tlv_foot *foot; + struct Timestamp last_sync; + struct PortAddress *paddr; + struct ptp_message *tmp; + unsigned char *ptr; + + last_sync = tmv_to_Timestamp(clock_ingress_time(p->clock)); + pid = dad->pds.parentPortIdentity.clockIdentity; + + head = (struct nsm_resp_tlv_head *) m->delay_resp.suffix; + head->type = TLV_PTPMON_RESP; + head->port_state = p->state == PS_GRAND_MASTER ? PS_MASTER : p->state; + + paddr = &head->parent_addr; + + if (best && memcmp(&cid, &pid, sizeof(cid))) { + /* Extract the parent's protocol address. */ + paddr->networkProtocol = transport_type(best->trp); + paddr->addressLength = + transport_protocol_addr(best->trp, paddr->address); + if (best->best) { + tmp = TAILQ_FIRST(&best->best->messages); + extract_address(tmp, paddr); + } + } else { + /* We are our own parent. */ + paddr->networkProtocol = transport_type(p->trp); + paddr->addressLength = + transport_protocol_addr(p->trp, paddr->address); + } + + head->length = sizeof(*head) + sizeof(*foot) + paddr->addressLength + - sizeof(head->type) - sizeof(head->length); + + ptr = (unsigned char *) head; + ptr += sizeof(*head) + paddr->addressLength; + foot = (struct nsm_resp_tlv_foot *) ptr; + + memcpy(&foot->parent, &dad->pds, sizeof(foot->parent)); + memcpy(&foot->current, cds, sizeof(foot->current)); + memcpy(&foot->timeprop, tp, sizeof(foot->timeprop)); + memcpy(&foot->lastsync, &last_sync, sizeof(foot->lastsync)); + + m->tlv_count = 1; + return sizeof(*head) + sizeof(*foot) + paddr->addressLength; +} + static struct follow_up_info_tlv *follow_up_info_extract(struct ptp_message *m) { struct follow_up_info_tlv *f; @@ -660,6 +739,28 @@ static int port_ignore(struct port *p, struct ptp_message *m) return 0; } +static int port_nsm_reply(struct port *p, struct ptp_message *m) +{ + struct TLV *tlv = (struct TLV *) m->delay_req.suffix; + + if (!p->net_sync_monitor) { + return 0; + } + if (!p->hybrid_e2e) { + return 0; + } + if (!(m->header.flagField[0] & UNICAST)) { + return 0; + } + if (!m->tlv_count) { + return 0; + } + if (tlv->type != TLV_PTPMON_REQ) { + return 0; + } + return 1; +} + /* * Test whether a 802.1AS port may transmit a sync message. */ @@ -1642,10 +1743,12 @@ static int process_announce(struct port *p, struct ptp_message *m) static int process_delay_req(struct port *p, struct ptp_message *m) { + int err, nsm, pdulen, saved_seqnum_sync; struct ptp_message *msg; - int err; - if (p->state != PS_MASTER && p->state != PS_GRAND_MASTER) + nsm = port_nsm_reply(p, m); + + if (!nsm && p->state != PS_MASTER && p->state != PS_GRAND_MASTER) return 0; if (p->delayMechanism == DM_P2P) { @@ -1657,11 +1760,14 @@ static int process_delay_req(struct port *p, struct ptp_message *m) if (!msg) return -1; + pdulen = sizeof(struct delay_resp_msg); msg->hwts.type = p->timestamping; - + if (nsm) { + pdulen += net_sync_resp_append(p, msg); + } msg->header.tsmt = DELAY_RESP | p->transportSpecific; msg->header.ver = PTP_VERSION; - msg->header.messageLength = sizeof(struct delay_resp_msg); + msg->header.messageLength = pdulen; msg->header.domainNumber = m->header.domainNumber; msg->header.correction = m->header.correction; msg->header.sourcePortIdentity = p->portIdentity; @@ -1680,8 +1786,17 @@ static int process_delay_req(struct port *p, struct ptp_message *m) } err = port_prepare_and_send(p, msg, 0); - if (err) + if (err) { pr_err("port %hu: send delay response failed", portnum(p)); + goto out; + } + if (nsm) { + saved_seqnum_sync = p->seqnum.sync; + p->seqnum.sync = m->header.sequenceId; + err = port_tx_sync(p, &m->address); + p->seqnum.sync = saved_seqnum_sync; + } +out: msg_put(msg); return err; } @@ -2705,6 +2820,9 @@ struct port *port_open(int phc_index, if (p->hybrid_e2e && p->delayMechanism != DM_E2E) { pr_warning("port %d: hybrid_e2e only works with E2E", number); } + if (p->net_sync_monitor && !p->hybrid_e2e) { + pr_warning("port %d: net_sync_monitor needs hybrid_e2e", number); + } /* Set fault timeouts to a default value */ for (i = 0; i < FT_CNT; i++) { -- 2.11.0 |
From: Richard C. <ric...@gm...> - 2018-01-02 04:06:11
|
Signed-off-by: Richard Cochran <ric...@gm...> --- config.c | 1 + default.cfg | 1 + gPTP.cfg | 1 + port.c | 1 + ptp4l.8 | 12 +++++++++++- 5 files changed, 15 insertions(+), 1 deletion(-) diff --git a/config.c b/config.c index bbaf36e..1407a3a 100644 --- a/config.c +++ b/config.c @@ -204,6 +204,7 @@ struct config_item config_tab[] = { GLOB_ITEM_INT("max_frequency", 900000000, 0, INT_MAX), PORT_ITEM_INT("min_neighbor_prop_delay", -20000000, INT_MIN, -1), PORT_ITEM_INT("neighborPropDelayThresh", 20000000, 0, INT_MAX), + PORT_ITEM_INT("net_sync_monitor", 0, 0, 1), PORT_ITEM_ENU("network_transport", TRANS_UDP_IPV4, nw_trans_enu), GLOB_ITEM_INT("ntpshm_segment", 0, INT_MIN, INT_MAX), GLOB_ITEM_INT("offsetScaledLogVariance", 0xffff, 0, UINT16_MAX), diff --git a/default.cfg b/default.cfg index ebb263a..e76aeae 100644 --- a/default.cfg +++ b/default.cfg @@ -35,6 +35,7 @@ logging_level 6 path_trace_enabled 0 follow_up_info 0 hybrid_e2e 0 +net_sync_monitor 0 tx_timestamp_timeout 1 use_syslog 1 verbose 0 diff --git a/gPTP.cfg b/gPTP.cfg index 142996a..1e7a33e 100644 --- a/gPTP.cfg +++ b/gPTP.cfg @@ -33,6 +33,7 @@ logging_level 6 path_trace_enabled 1 follow_up_info 1 hybrid_e2e 0 +net_sync_monitor 0 tx_timestamp_timeout 1 use_syslog 1 verbose 0 diff --git a/port.c b/port.c index 4113d3f..dcffede 100644 --- a/port.c +++ b/port.c @@ -2802,6 +2802,7 @@ struct port *port_open(int phc_index, p->follow_up_info = config_get_int(cfg, p->name, "follow_up_info"); p->freq_est_interval = config_get_int(cfg, p->name, "freq_est_interval"); p->hybrid_e2e = config_get_int(cfg, p->name, "hybrid_e2e"); + p->net_sync_monitor = config_get_int(cfg, p->name, "net_sync_monitor"); p->path_trace_enabled = config_get_int(cfg, p->name, "path_trace_enabled"); p->rx_timestamp_offset = config_get_int(cfg, p->name, "ingressLatency"); p->tx_timestamp_offset = config_get_int(cfg, p->name, "egressLatency"); diff --git a/ptp4l.8 b/ptp4l.8 index a724151..113e49c 100644 --- a/ptp4l.8 +++ b/ptp4l.8 @@ -1,4 +1,4 @@ -.TH PTP4l 8 "December 2016" "linuxptp" +.TH PTP4l 8 "Novemver 2017" "linuxptp" .SH NAME ptp4l - PTP Boundary/Ordinary Clock @@ -211,6 +211,16 @@ delay requests using unicast delay responses. This option has no effect if the delay_mechanism is set to P2P. The default is 0 (disabled). .TP +.B net_sync_monitor +Enables the NetSync Monitor (NSM) protocol. The NSM protocol allows a +station to measure how well another node is synchronized. The monitor +sends a unicast delay request to the node, which replies +unconditionally with unicast delay response, sync, and follow up +messages. If the monitor is synchronized to the GM, it can use the +time stamps in the message to estimate the node's offset. This option +requires that the 'hybrid_e2e' option be enabled as well. +The default is 0 (disabled). +.TP .B ptp_dst_mac The MAC address to which PTP messages should be sent. Relevant only with L2 transport. The default is 01:1B:19:00:00:00. -- 2.11.0 |
From: Richard C. <ric...@gm...> - 2018-01-02 04:06:12
|
The file, pmc.c, contains utility functions for printing out a port address structure. We will want to call these functions from pmc_common.c, and so this patch moves the utility functions where they belong. Signed-off-by: Richard Cochran <ric...@gm...> --- pmc.c | 48 ------------------------------------------------ util.c | 39 +++++++++++++++++++++++++++++++++++++++ util.h | 15 +++++++++++++++ 3 files changed, 54 insertions(+), 48 deletions(-) diff --git a/pmc.c b/pmc.c index 49282a6..5ebed46 100644 --- a/pmc.c +++ b/pmc.c @@ -127,60 +127,12 @@ static char *text2str(struct PTPText *text) return (char*)(s.text); } -#define MAX_PRINT_BYTES 16 -#define BIN_BUF_SIZE (MAX_PRINT_BYTES * 3 + 1) - -static char *bin2str_impl(Octet *data, int len, char *buf, int buf_len) -{ - int i, offset = 0; - if (len > MAX_PRINT_BYTES) - len = MAX_PRINT_BYTES; - buf[0] = '\0'; - if (!data) - return buf; - if (len) - offset += snprintf(buf, buf_len, "%02hhx", data[0]); - for (i = 1; i < len; i++) { - if (offset >= buf_len) - /* truncated output */ - break; - offset += snprintf(buf + offset, buf_len - offset, ":%02hhx", data[i]); - } - return buf; -} - static char *bin2str(Octet *data, int len) { static char buf[BIN_BUF_SIZE]; return bin2str_impl(data, len, buf, sizeof(buf)); } -static uint16_t align16(uint16_t *p) -{ - uint16_t v; - memcpy(&v, p, sizeof(v)); - return v; -} - -static char *portaddr2str(struct PortAddress *addr) -{ - static char buf[BIN_BUF_SIZE]; - switch(align16(&addr->networkProtocol)) { - case TRANS_UDP_IPV4: - if (align16(&addr->addressLength) == 4 - && inet_ntop(AF_INET, addr->address, buf, sizeof(buf))) - return buf; - break; - case TRANS_UDP_IPV6: - if (align16(&addr->addressLength) == 16 - && inet_ntop(AF_INET6, addr->address, buf, sizeof(buf))) - return buf; - break; - } - bin2str_impl(addr->address, align16(&addr->addressLength), buf, sizeof(buf)); - return buf; -} - static void pmc_show(struct ptp_message *msg, FILE *fp) { int action; diff --git a/util.c b/util.c index 62f2638..2eacafc 100644 --- a/util.c +++ b/util.c @@ -16,6 +16,7 @@ * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ +#include <arpa/inet.h> #include <errno.h> #include <signal.h> #include <stdarg.h> @@ -68,6 +69,25 @@ const char *ev_str[] = { "RS_PASSIVE", }; +char *bin2str_impl(Octet *data, int len, char *buf, int buf_len) +{ + int i, offset = 0; + if (len > MAX_PRINT_BYTES) + len = MAX_PRINT_BYTES; + buf[0] = '\0'; + if (!data) + return buf; + if (len) + offset += snprintf(buf, buf_len, "%02hhx", data[0]); + for (i = 1; i < len; i++) { + if (offset >= buf_len) + /* truncated output */ + break; + offset += snprintf(buf + offset, buf_len - offset, ":%02hhx", data[i]); + } + return buf; +} + char *cid2str(struct ClockIdentity *id) { static char buf[64]; @@ -100,6 +120,25 @@ char *pid2str(struct PortIdentity *id) return buf; } +char *portaddr2str(struct PortAddress *addr) +{ + static char buf[BIN_BUF_SIZE]; + switch (align16(&addr->networkProtocol)) { + case TRANS_UDP_IPV4: + if (align16(&addr->addressLength) == 4 + && inet_ntop(AF_INET, addr->address, buf, sizeof(buf))) + return buf; + break; + case TRANS_UDP_IPV6: + if (align16(&addr->addressLength) == 16 + && inet_ntop(AF_INET6, addr->address, buf, sizeof(buf))) + return buf; + break; + } + bin2str_impl(addr->address, align16(&addr->addressLength), buf, sizeof(buf)); + return buf; +} + int str2mac(const char *s, unsigned char mac[MAC_LEN]) { unsigned char buf[MAC_LEN]; diff --git a/util.h b/util.h index 0c1a357..41fbdb2 100644 --- a/util.h +++ b/util.h @@ -20,11 +20,15 @@ #ifndef HAVE_UTIL_H #define HAVE_UTIL_H +#include <string.h> #include <time.h> #include "ddt.h" #include "ether.h" +#define MAX_PRINT_BYTES 16 +#define BIN_BUF_SIZE (MAX_PRINT_BYTES * 3 + 1) + /** * Table of human readable strings, one for each port state. */ @@ -35,6 +39,15 @@ extern const char *ps_str[]; */ extern const char *ev_str[]; +static inline uint16_t align16(uint16_t *p) +{ + uint16_t v; + memcpy(&v, p, sizeof(v)); + return v; +} + +char *bin2str_impl(Octet *data, int len, char *buf, int buf_len); + /** * Convert a clock identity into a human readable string. * @@ -65,6 +78,8 @@ int count_char(const char *str, char c); */ char *pid2str(struct PortIdentity *id); +char *portaddr2str(struct PortAddress *addr); + /** * Scan a string containing a MAC address and convert it into binary form. * -- 2.11.0 |
From: Richard C. <ric...@gm...> - 2018-01-02 04:06:13
|
As part of the NetSync Monitor protocol, it will be necessary to send and receive PTP event messages and evaluate their time stamps. This patch adds command line flags for choosing the time stamping flavor. Signed-off-by: Richard Cochran <ric...@gm...> --- pmc.c | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/pmc.c b/pmc.c index 5ebed46..a15e800 100644 --- a/pmc.c +++ b/pmc.c @@ -669,6 +669,10 @@ static void usage(char *progname) " -t [hex] transport specific field, default 0x0\n" " -v prints the software version and exits\n" " -z send zero length TLV values with the GET actions\n" + "\n" + " Time Stamping (for NetSync Monitor commands)\n\n" + " -H HARDWARE (default)\n" + " -S SOFTWARE\n" "\n", progname); } @@ -697,7 +701,7 @@ int main(int argc, char *argv[]) /* Process the command line arguments. */ progname = strrchr(argv[0], '/'); progname = progname ? 1+progname : argv[0]; - while (EOF != (c = getopt(argc, argv, "246u""b:d:hi:s:t:vz"))) { + while (EOF != (c = getopt(argc, argv, "246HSu""b:d:hi:s:t:vz"))) { switch (c) { case '2': transport_type = TRANS_IEEE_802_3; @@ -708,6 +712,14 @@ int main(int argc, char *argv[]) case '6': transport_type = TRANS_UDP_IPV6; break; + case 'H': + if (config_set_int(cfg, "time_stamping", TS_HARDWARE)) + goto out; + break; + case 'S': + if (config_set_int(cfg, "time_stamping", TS_SOFTWARE)) + goto out; + break; case 'u': transport_type = TRANS_UDS; break; @@ -852,6 +864,7 @@ int main(int argc, char *argv[]) pmc_destroy(pmc); msg_cleanup(); +out: config_destroy(cfg); return ret; } -- 2.11.0 |
From: Richard C. <ric...@gm...> - 2018-01-02 04:06:14
|
In order to implement the NetSync Monitor protocol, we will need to send and receive PTP event messages. This patch adds a method that allows the pmc program to obtain the event socket. Signed-off-by: Richard Cochran <ric...@gm...> --- pmc_common.c | 5 +++++ pmc_common.h | 1 + 2 files changed, 6 insertions(+) diff --git a/pmc_common.c b/pmc_common.c index 447cf99..8c4f31f 100644 --- a/pmc_common.c +++ b/pmc_common.c @@ -223,6 +223,11 @@ int pmc_get_transport_fd(struct pmc *pmc) return pmc->fdarray.fd[FD_GENERAL]; } +int pmc_get_transport_event_fd(struct pmc *pmc) +{ + return pmc->fdarray.fd[FD_EVENT]; +} + int pmc_send_get_action(struct pmc *pmc, int id) { int datalen, pdulen; diff --git a/pmc_common.h b/pmc_common.h index bb3a1f1..d5ccb29 100644 --- a/pmc_common.h +++ b/pmc_common.h @@ -35,6 +35,7 @@ struct pmc *pmc_create(struct config *cfg, enum transport_type transport_type, void pmc_destroy(struct pmc *pmc); int pmc_get_transport_fd(struct pmc *pmc); +int pmc_get_transport_event_fd(struct pmc *pmc); int pmc_send_get_action(struct pmc *pmc, int id); -- 2.11.0 |
From: Richard C. <ric...@gm...> - 2018-01-02 04:06:15
|
In support of the NSM protocol, the pmc code will want to compute the measured offset from a set of four time stamps from an E2E message exchange. This patch adds the processor in anticipation of the NSM code. Signed-off-by: Richard Cochran <ric...@gm...> --- makefile | 11 ++++++----- pmc_common.c | 10 ++++++++++ 2 files changed, 16 insertions(+), 5 deletions(-) diff --git a/makefile b/makefile index f898336..92fad2b 100644 --- a/makefile +++ b/makefile @@ -46,12 +46,13 @@ all: $(PRG) ptp4l: $(OBJ) -pmc: config.o hash.o msg.o pmc.o pmc_common.o print.o raw.o sk.o tlv.o \ - transport.o udp.o udp6.o uds.o util.o version.o +pmc: config.o filter.o hash.o mave.o mmedian.o msg.o pmc.o pmc_common.o print.o \ + raw.o sk.o tlv.o transport.o tsproc.o udp.o udp6.o uds.o util.o version.o -phc2sys: clockadj.o clockcheck.o config.o hash.o linreg.o msg.o ntpshm.o \ - nullf.o phc.o phc2sys.o pi.o pmc_common.o print.o raw.o servo.o sk.o stats.o \ - sysoff.o tlv.o transport.o udp.o udp6.o uds.o util.o version.o +phc2sys: clockadj.o clockcheck.o config.o filter.o hash.o linreg.o mave.o \ + mmedian.o msg.o ntpshm.o nullf.o phc.o phc2sys.o pi.o pmc_common.o print.o \ + raw.o servo.o sk.o stats.o sysoff.o tlv.o transport.o tsproc.o udp.o udp6.o \ + uds.o util.o version.o hwstamp_ctl: hwstamp_ctl.o version.o diff --git a/pmc_common.c b/pmc_common.c index 8c4f31f..da25c21 100644 --- a/pmc_common.c +++ b/pmc_common.c @@ -58,6 +58,7 @@ struct pmc { struct transport *transport; struct fdarray fdarray; + struct tsproc *tsproc; int zero_length_gets; }; @@ -73,6 +74,12 @@ struct pmc *pmc_create(struct config *cfg, enum transport_type transport_type, if (!pmc) return NULL; + pmc->tsproc = tsproc_create(TSPROC_RAW, FILTER_MOVING_AVERAGE, 10); + if (!pmc->tsproc) { + pr_err("Failed to create time stamp processor"); + goto failed; + } + if (transport_type != TRANS_UDS && generate_clock_identity(&pmc->port_identity.clockIdentity, iface_name)) { @@ -103,6 +110,8 @@ struct pmc *pmc_create(struct config *cfg, enum transport_type transport_type, return pmc; failed: + if (pmc->tsproc) + tsproc_destroy(pmc->tsproc); if (pmc->transport) transport_destroy(pmc->transport); free(pmc); @@ -111,6 +120,7 @@ failed: void pmc_destroy(struct pmc *pmc) { + tsproc_destroy(pmc->tsproc); transport_close(pmc->transport, &pmc->fdarray); transport_destroy(pmc->transport); free(pmc); -- 2.11.0 |
From: Richard C. <ric...@gm...> - 2018-01-02 04:06:17
|
This patch adds a new "NSM" command to the pmc program. The new code handles only one outstanding NSM command at a time. If and when all four event time stamps have arrived, the code prints the instantaneous estimated offset without any averaging or smoothing. Signed-off-by: Richard Cochran <ric...@gm...> --- pmc.c | 28 +++++- pmc_common.c | 324 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++- pmc_common.h | 20 ++++ 3 files changed, 363 insertions(+), 9 deletions(-) diff --git a/pmc.c b/pmc.c index a15e800..c75b590 100644 --- a/pmc.c +++ b/pmc.c @@ -36,6 +36,7 @@ #include "version.h" #define BAD_ACTION -1 +#define NSM_ACTION -2 #define BAD_ID -1 #define AMBIGUOUS_ID -2 #define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0])) @@ -117,8 +118,6 @@ static const char *action_string[] = { "ACKNOWLEDGE", }; -#define IFMT "\n\t\t" - static char *text2str(struct PTPText *text) { static struct static_ptp_text s; @@ -561,6 +560,8 @@ static int parse_action(char *s) return COMMAND; else if (0 == strncasecmp(s, "COMMAND", len)) return COMMAND; + else if (0 == strncasecmp(s, "NSM", len)) + return NSM_ACTION; return BAD_ACTION; } @@ -613,6 +614,11 @@ static void print_help(FILE *fp) fprintf(fp, "\tTARGET [portIdentity]\n"); fprintf(fp, "\tTARGET *\n"); fprintf(fp, "\n"); + fprintf(fp, "\tSend a NetSync Monitor request to a specific port address:\n"); + fprintf(fp, "\n"); + fprintf(fp, "\tNSM 111.222.333.444\n"); + fprintf(fp, "\tNSM aa.bb.cc.dd.ee.ff\n"); + fprintf(fp, "\n"); } static int do_command(char *str) @@ -632,6 +638,9 @@ static int do_command(char *str) return parse_target(id_str); action = parse_action(action_str); + if (action == NSM_ACTION) { + return pmc_nsm_request(pmc, id_str); + } id = parse_id(id_str); if (action == BAD_ACTION || id == BAD_ID) @@ -688,7 +697,7 @@ int main(int argc, char *argv[]) UInteger8 boundary_hops = 1, domain_number = 0, transport_specific = 0; struct ptp_message *msg; struct config *cfg; -#define N_FD 2 +#define N_FD 3 struct pollfd pollfd[N_FD]; handle_term_signals(); @@ -794,6 +803,7 @@ int main(int argc, char *argv[]) pollfd[0].fd = batch_mode ? -1 : STDIN_FILENO; pollfd[1].fd = pmc_get_transport_fd(pmc); + pollfd[2].fd = pmc_get_transport_event_fd(pmc); while (is_running()) { if (batch_mode && !command) { @@ -808,6 +818,7 @@ int main(int argc, char *argv[]) pollfd[0].events = 0; pollfd[1].events = POLLIN | POLLPRI; + pollfd[2].events = POLLIN | POLLPRI; if (!batch_mode && !command) pollfd[0].events |= POLLIN | POLLPRI; @@ -856,7 +867,16 @@ int main(int argc, char *argv[]) if (pollfd[1].revents & (POLLIN|POLLPRI)) { msg = pmc_recv(pmc); if (msg) { - pmc_show(msg, stdout); + if (!pmc_handle_nsm(pmc, msg, stdout)) { + pmc_show(msg, stdout); + } + msg_put(msg); + } + } + if (pollfd[2].revents & (POLLIN|POLLPRI)) { + msg = pmc_recv_event(pmc); + if (msg) { + pmc_handle_nsm(pmc, msg, stdout); msg_put(msg); } } diff --git a/pmc_common.c b/pmc_common.c index da25c21..6366f76 100644 --- a/pmc_common.c +++ b/pmc_common.c @@ -17,9 +17,13 @@ * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ +#include <arpa/inet.h> #include <errno.h> -#include <string.h> +#include <netinet/in.h> +#include <stdint.h> #include <stdlib.h> +#include <string.h> +#include <sys/socket.h> #include "print.h" #include "tlv.h" @@ -60,8 +64,75 @@ struct pmc { struct fdarray fdarray; struct tsproc *tsproc; int zero_length_gets; + + /* NetSync Monitor */ + struct ptp_message *nsm_delay_req; + struct ptp_message *nsm_delay_resp; + struct ptp_message *nsm_sync; + struct ptp_message *nsm_fup; + enum timestamp_type timestamping; + Integer64 asymmetry; }; +static int nsm_complete(struct pmc *pmc) +{ + if (!pmc->nsm_sync) { + return 0; + } + if (one_step(pmc->nsm_sync)) { + return pmc->nsm_delay_resp ? 1 : 0; + } + return (pmc->nsm_delay_resp && pmc->nsm_fup) ? 1 : 0; +} + +static int64_t nsm_compute_offset(struct tsproc *tsp, + struct ptp_message *syn, + struct ptp_message *fup, + struct ptp_message *req, + struct ptp_message *resp) +{ + tmv_t c1, c2, c3, t1, t1c, t2, t3, t4, t4c, offset; + + c1 = correction_to_tmv(syn->header.correction); + c2 = correction_to_tmv(fup->header.correction); + c3 = correction_to_tmv(resp->header.correction); + + t1 = timestamp_to_tmv(fup->ts.pdu); + t2 = timespec_to_tmv(syn->hwts.ts); + t3 = timespec_to_tmv(req->hwts.ts); + t4 = timestamp_to_tmv(resp->ts.pdu); + + t1c = tmv_add(t1, tmv_add(c1, c2)); + t4c = tmv_sub(t4, c3); + + tsproc_reset(tsp, 1); + tsproc_down_ts(tsp, t1c, t2); + tsproc_up_ts(tsp, t3, t4c); + tsproc_update_offset(tsp, &offset, NULL); + + return tmv_to_nanoseconds(offset); +} + +static void nsm_reset(struct pmc *pmc) +{ + if (pmc->nsm_delay_req) { + msg_put(pmc->nsm_delay_req); + } + if (pmc->nsm_delay_resp) { + msg_put(pmc->nsm_delay_resp); + } + if (pmc->nsm_sync) { + msg_put(pmc->nsm_sync); + } + if (pmc->nsm_fup) { + msg_put(pmc->nsm_fup); + } + pmc->nsm_delay_req = NULL; + pmc->nsm_delay_resp = NULL; + pmc->nsm_sync = NULL; + pmc->nsm_fup = NULL; +} + struct pmc *pmc_create(struct config *cfg, enum transport_type transport_type, const char *iface_name, UInteger8 boundary_hops, UInteger8 domain_number, UInteger8 transport_specific, @@ -107,6 +178,10 @@ struct pmc *pmc_create(struct config *cfg, enum transport_type transport_type, } pmc->zero_length_gets = zero_datalen ? 1 : 0; + pmc->timestamping = config_get_int(cfg, NULL, "time_stamping"); + pmc->asymmetry = config_get_int(cfg, iface_name, "delayAsymmetry"); + pmc->asymmetry <<= 16; + return pmc; failed: @@ -120,6 +195,7 @@ failed: void pmc_destroy(struct pmc *pmc) { + nsm_reset(pmc); tsproc_destroy(pmc->tsproc); transport_close(pmc->transport, &pmc->fdarray); transport_destroy(pmc->transport); @@ -238,6 +314,228 @@ int pmc_get_transport_event_fd(struct pmc *pmc) return pmc->fdarray.fd[FD_EVENT]; } +int pmc_handle_nsm(struct pmc *pmc, struct ptp_message *msg, FILE *fp) +{ + struct nsm_resp_tlv_head *head; + struct nsm_resp_tlv_foot *foot; + struct timePropertiesDS *tp; + struct PortAddress *paddr; + struct currentDS cds; + struct parentDS *pds; + struct Timestamp ts; + unsigned char *ptr; + int64_t offset; + + if (!pmc->nsm_delay_req) { + return 0; + } + if (msg->header.sequenceId != + ntohs(pmc->nsm_delay_req->header.sequenceId)) { + return 0; + } + if (!(msg->header.flagField[0] & UNICAST)) { + return 0; + } + + switch (msg_type(msg)) { + case SYNC: + if (!pmc->nsm_sync) { + pmc->nsm_sync = msg; + msg_get(msg); + } + break; + case FOLLOW_UP: + if (!pmc->nsm_fup) { + pmc->nsm_fup = msg; + msg_get(msg); + } + break; + case DELAY_RESP: + if (!pmc->nsm_delay_resp) { + pmc->nsm_delay_resp = msg; + msg_get(msg); + } + break; + case DELAY_REQ: + case PDELAY_REQ: + case PDELAY_RESP: + case PDELAY_RESP_FOLLOW_UP: + case ANNOUNCE: + case SIGNALING: + case MANAGEMENT: + return 0; + } + + if (!nsm_complete(pmc)) { + return 1; + } + + head = (struct nsm_resp_tlv_head *) pmc->nsm_delay_resp->delay_resp.suffix; + paddr = &head->parent_addr; + + ptr = (unsigned char *) head; + ptr += sizeof(*head) + paddr->addressLength; + foot = (struct nsm_resp_tlv_foot *) ptr; + + pds = &foot->parent; + memcpy(&cds, &foot->current, sizeof(cds)); + tp = &foot->timeprop; + memcpy(&ts, &foot->lastsync, sizeof(ts)); + + offset = nsm_compute_offset(pmc->tsproc, pmc->nsm_sync, pmc->nsm_fup, + pmc->nsm_delay_req, pmc->nsm_delay_resp); + + fprintf(fp, "NSM MEASUREMENT COMPLETE" + IFMT "offset %" PRId64 + IFMT "portState %s" + IFMT "parentPortAddress %hu %s\n", + offset, + ps_str[head->port_state], + head->parent_addr.networkProtocol, + portaddr2str(&head->parent_addr)); + fprintf(fp, "\tparentDataset" + IFMT "parentPortIdentity %s" + IFMT "parentStats %hhu" + IFMT "observedParentOffsetScaledLogVariance 0x%04hx" + IFMT "observedParentClockPhaseChangeRate 0x%08x" + IFMT "grandmasterPriority1 %hhu" + IFMT "gm.ClockClass %hhu" + IFMT "gm.ClockAccuracy 0x%02hhx" + IFMT "gm.OffsetScaledLogVariance 0x%04hx" + IFMT "grandmasterPriority2 %hhu" + IFMT "grandmasterIdentity %s\n", + pid2str(&pds->parentPortIdentity), + pds->parentStats, + pds->observedParentOffsetScaledLogVariance, + pds->observedParentClockPhaseChangeRate, + pds->grandmasterPriority1, + pds->grandmasterClockQuality.clockClass, + pds->grandmasterClockQuality.clockAccuracy, + pds->grandmasterClockQuality.offsetScaledLogVariance, + pds->grandmasterPriority2, + cid2str(&pds->grandmasterIdentity)); + fprintf(fp, "\tcurrentDataset" + IFMT "stepsRemoved %hd" + IFMT "offsetFromMaster %.1f" + IFMT "meanPathDelay %.1f\n", + cds.stepsRemoved, cds.offsetFromMaster / 65536.0, + cds.meanPathDelay / 65536.0); + fprintf(fp, "\ttimePropertiesDataset" + IFMT "currentUtcOffset %hd" + IFMT "leap61 %d" + IFMT "leap59 %d" + IFMT "currentUtcOffsetValid %d" + IFMT "ptpTimescale %d" + IFMT "timeTraceable %d" + IFMT "frequencyTraceable %d" + IFMT "timeSource 0x%02hhx\n", + tp->currentUtcOffset, + tp->flags & LEAP_61 ? 1 : 0, + tp->flags & LEAP_59 ? 1 : 0, + tp->flags & UTC_OFF_VALID ? 1 : 0, + tp->flags & PTP_TIMESCALE ? 1 : 0, + tp->flags & TIME_TRACEABLE ? 1 : 0, + tp->flags & FREQ_TRACEABLE ? 1 : 0, + tp->timeSource); + fprintf(fp, "\tlastSyncTimestamp %" PRId64 ".%09u\n", + ((uint64_t)ts.seconds_lsb) | (((uint64_t)ts.seconds_msb) << 32), + ts.nanoseconds); + + fflush(fp); + nsm_reset(pmc); + + return 1; +} + +int pmc_nsm_request(struct pmc *pmc, char *target) +{ + enum transport_type type = transport_type(pmc->transport); + unsigned char mac[MAC_LEN]; + struct in_addr ipv4_addr; + struct ptp_message *msg; + int cnt, err, pdulen; + struct address dst; + struct TLV *tlv; + + memset(&dst, 0, sizeof(dst)); + + switch (type) { + case TRANS_UDS: + case TRANS_UDP_IPV6: + case TRANS_DEVICENET: + case TRANS_CONTROLNET: + case TRANS_PROFINET: + pr_err("sorry, NSM not support with this transport"); + return -1; + case TRANS_UDP_IPV4: + if (!inet_aton(target, &ipv4_addr)) { + pr_err("bad IPv4 address"); + return -1; + } + dst.sin.sin_family = AF_INET; + dst.sin.sin_addr = ipv4_addr; + dst.len = sizeof(dst.sin); + break; + case TRANS_IEEE_802_3: + if (str2mac(target, mac)) { + pr_err("bad Layer-2 address"); + return -1; + } + dst.sll.sll_family = AF_PACKET; + dst.sll.sll_halen = MAC_LEN; + memcpy(&dst.sll.sll_addr, mac, MAC_LEN); + dst.len = sizeof(dst.sll); + break; + } + + msg = msg_allocate(); + if (!msg) { + return -1; + } + pdulen = sizeof(struct delay_req_msg) + sizeof(*tlv); + tlv = (struct TLV *) msg->delay_req.suffix; + tlv->type = TLV_PTPMON_REQ; + tlv->length = 0; + msg->tlv_count = 1; + msg->hwts.type = pmc->timestamping; + + msg->header.tsmt = DELAY_REQ | pmc->transport_specific; + msg->header.ver = PTP_VERSION; + msg->header.messageLength = pdulen; + msg->header.domainNumber = pmc->domain_number; + msg->header.correction = -pmc->asymmetry; + msg->header.sourcePortIdentity = pmc->port_identity; + msg->header.sequenceId = pmc->sequence_id++; + msg->header.control = CTL_DELAY_REQ; + msg->header.logMessageInterval = 0x7f; + + msg->address = dst; + msg->header.flagField[0] |= UNICAST; + + err = msg_pre_send(msg); + if (err) { + pr_err("msg_pre_send failed"); + goto out; + } + cnt = transport_sendto(pmc->transport, &pmc->fdarray, 1, msg); + if (cnt <= 0) { + pr_err("transport_sendto failed"); + err = -1; + goto out; + } + if (msg_sots_missing(msg)) { + pr_err("missing timestamp on transmitted delay request"); + err = -1; + goto out; + } + nsm_reset(pmc); + pmc->nsm_delay_req = msg; + return 0; +out: + msg_put(msg); + return err; +} + int pmc_send_get_action(struct pmc *pmc, int id) { int datalen, pdulen; @@ -301,18 +599,24 @@ int pmc_send_set_action(struct pmc *pmc, int id, void *data, int datasize) return 0; } -struct ptp_message *pmc_recv(struct pmc *pmc) +static struct ptp_message *pmc_recv_internal(struct pmc *pmc, int event) { struct ptp_message *msg; - int cnt, err; + int cnt, err, fd; msg = msg_allocate(); if (!msg) { pr_err("low memory"); return NULL; } - msg->hwts.type = TS_SOFTWARE; - cnt = transport_recv(pmc->transport, pmc_get_transport_fd(pmc), msg); + if (event) { + msg->hwts.type = pmc->timestamping; + fd = pmc_get_transport_event_fd(pmc); + } else { + msg->hwts.type = TS_SOFTWARE; + fd = pmc_get_transport_fd(pmc); + } + cnt = transport_recv(pmc->transport, fd, msg); if (cnt <= 0) { pr_err("recv message failed"); goto failed; @@ -339,6 +643,16 @@ failed: return NULL; } +struct ptp_message *pmc_recv(struct pmc *pmc) +{ + return pmc_recv_internal(pmc, 0); +} + +struct ptp_message *pmc_recv_event(struct pmc *pmc) +{ + return pmc_recv_internal(pmc, 1); +} + int pmc_target(struct pmc *pmc, struct PortIdentity *pid) { pmc->target = *pid; diff --git a/pmc_common.h b/pmc_common.h index d5ccb29..f951d34 100644 --- a/pmc_common.h +++ b/pmc_common.h @@ -25,6 +25,8 @@ #include "msg.h" #include "transport.h" +#define IFMT "\n\t\t" + struct pmc; struct pmc *pmc_create(struct config *cfg, enum transport_type transport_type, @@ -37,11 +39,29 @@ void pmc_destroy(struct pmc *pmc); int pmc_get_transport_fd(struct pmc *pmc); int pmc_get_transport_event_fd(struct pmc *pmc); +/** + * Handle a NetSync Monitor message. + * @param pmc A pointer obtained with pmc_create(). + * @param msg A message which may or may not be a NetSync Monitor message. + * @param fp An open file for logging. + * @return One (1) if the message was handled, zero (0) otherwise. + */ +int pmc_handle_nsm(struct pmc *pmc, struct ptp_message *msg, FILE *fp); + +/** + * Sends a NetSync Monitor delay request to a given node. + * @param pmc A pointer obtained with pmc_create(). + * @param target The address of the node to measure. + * @return Zero on success, non-zero otherwise. + */ +int pmc_nsm_request(struct pmc *pmc, char *target); + int pmc_send_get_action(struct pmc *pmc, int id); int pmc_send_set_action(struct pmc *pmc, int id, void *data, int datasize); struct ptp_message *pmc_recv(struct pmc *pmc); +struct ptp_message *pmc_recv_event(struct pmc *pmc); int pmc_target(struct pmc *pmc, struct PortIdentity *pid); void pmc_target_port(struct pmc *pmc, UInteger16 portNumber); -- 2.11.0 |
From: Miroslav L. <mli...@re...> - 2018-01-08 14:56:38
|
On Mon, Jan 01, 2018 at 08:05:31PM -0800, Richard Cochran wrote: > This series adds support for Meinberg Funkuhren's NetSync Monitor > (NSM) protocol. NSM allows a node with a local time reference (like a > GM using GPS) to measure the offset of a given clock. In a nutshell, > NSM works by having the node to be measured act like a unicast master > clock on demand. Very interesting! It looks a bit like an NTP client/server exchange over PTP. > One the nice features of NSM is that, when used from the GM, it allows > one to empirically determine the end to end asymmetry in the network. You mean asymmetry added by boundary clocks on the path to the grandmaster? > The details of the protocol are described in a brief PDF published by > Meinberg. If anyone would like a copy, please contact me off list. One thing I find odd is that the sync message doesn't have a TLV attached too. Does the specification say anything about that? The asymmetric length of the delay request and sync message will make the delay asymmetric in networks with non-TC switches (using store and forward) and add an error to the measured offset (e.g. 16 nanoseconds per switch on 1Gb network). Are transparent clocks expected to update the correction field in messages with TLVs? > Patches 1-3 fix trivial issues found along the way. > Patches 4-9 add various background support bits. > Patches 10-13 implement NSM in the ptp4l program. On first look these all seem good to me. > Patches 14-18 implement monitor support in the pmc program. This didn't work for me with HW timestamping. I had to modify also pmc_create() to change the timestamping mode in the transport_open() call and set the ts_label in the interface struct. With that, it worked as expected. The offset reported by the NSM command between two machines connected directly and synchronized by ptp4l was very close to zero. Nice! However, is pmc the right place for this functionality? As the exchange does not use any management messages, it would probably make more sense to me if it was in a separate binary, or maybe it could be a special query mode of ptp4l which would just print the offset and exit (with the possibility that it could be later used for normal synchronization of the clock). > The last patch breaks linuxptp-testsuite test 20-pmc. The pmc program > exits immediately with no output at all. When I test pmc outside of > the clknetsim it behaves as expected, but I didn't look into why > clknetsim case fails. Maybe you have an idea? There is a hack which detects that pmc is trying to send a message by checking for POLLOUT in events[1] and nfds == 2. It needs to be updated if the number of descriptors is increased. -- Miroslav Lichvar |
From: Richard C. <ric...@gm...> - 2018-01-09 05:03:01
|
On Mon, Jan 08, 2018 at 03:56:29PM +0100, Miroslav Lichvar wrote: > You mean asymmetry added by boundary clocks on the path to the > grandmaster? Yes, or from any other source at all. > One thing I find odd is that the sync message doesn't have a TLV > attached too. Does the specification say anything about that? No, and I noticed that too. The original 1588 standard makes sure that all event messages have the same length, but this extension does break that. I'll mention that to Meinberg, but I think they have already deployed this as is. > Are transparent clocks expected to update the correction field in > messages with TLVs? Good question. 1588 says: 13.2 General message format requirements All messages shall have a header, body, and suffix. The suffix may have zero length. and: 13.4 Suffix An application layer message is suffixed by a contiguous sequence of zero or more entities of data type TLV ... Nodes should append no TLV entity to event messages. NOTE -- Appending TLV entities to an event message is likely to change the transmission delay suffered by the messages in passing through non-PTP bridges. Notice it only says "should" and not "must not". So I would say that TCs must allow TLVs in any message. > This didn't work for me with HW timestamping. I had to modify also > pmc_create() to change the timestamping mode in the transport_open() > call and set the ts_label in the interface struct. Oops, I only tested with SW time stamping. > However, is pmc the right place for this functionality? As the > exchange does not use any management messages, it would probably make > more sense to me if it was in a separate binary, or maybe it could be > a special query mode of ptp4l which would just print the offset and > exit (with the possibility that it could be later used for normal > synchronization of the clock). I put it into pmc because it seemed to be a management function, event though it uses no management messages. If not there, then I prefer a separate binary for this. > There is a hack which detects that pmc is trying to send a message by > checking for POLLOUT in events[1] and nfds == 2. It needs to be > updated if the number of descriptors is increased. (That is even more reason to make NSM its own binary ;) Thanks, Richard |
From: Miroslav L. <mli...@re...> - 2018-01-09 10:09:53
|
On Mon, Jan 08, 2018 at 09:02:52PM -0800, Richard Cochran wrote: > On Mon, Jan 08, 2018 at 03:56:29PM +0100, Miroslav Lichvar wrote: > > You mean asymmetry added by boundary clocks on the path to the > > grandmaster? > > Yes, or from any other source at all. It's not clear to me how would that work. Let's say I have a grandmaster GM, slave S and monitoring node N, and I don't know how much asymmetry is there on any of the paths GM-S, GM-N and S-N. How will measuring offset of GM and S from N help me? My feeling is that the protocol was not intended for accurate measurements, but rather for detecting faults and bugs, where the clocks drift off too much, etc. > 13.4 Suffix > > An application layer message is suffixed by a contiguous sequence > of zero or more entities of data type TLV ... Nodes should append > no TLV entity to event messages. > > NOTE -- Appending TLV entities to an event message is likely to > change the transmission delay suffered by the messages in passing > through non-PTP bridges. > > Notice it only says "should" and not "must not". So I would say that > TCs must allow TLVs in any message. Ok. If anyone tests this with a TC, I'd be interested to hear if it worked. Thanks, -- Miroslav Lichvar |
From: Richard C. <ric...@gm...> - 2018-01-09 15:52:20
|
On Tue, Jan 09, 2018 at 11:09:43AM +0100, Miroslav Lichvar wrote: > It's not clear to me how would that work. Let's say I have a > grandmaster GM, slave S and monitoring node N, and I don't know how > much asymmetry is there on any of the paths GM-S, GM-N and S-N. How > will measuring offset of GM and S from N help me? I think I said that this works when N==GM. > My feeling is that the protocol was not intended for accurate > measurements, but rather for detecting faults and bugs, where the > clocks drift off too much, etc. Yes, that appears to be the most important use case. Thanks, Richard |
From: Miroslav L. <mli...@re...> - 2018-01-09 16:21:55
|
On Tue, Jan 09, 2018 at 07:52:11AM -0800, Richard Cochran wrote: > On Tue, Jan 09, 2018 at 11:09:43AM +0100, Miroslav Lichvar wrote: > > It's not clear to me how would that work. Let's say I have a > > grandmaster GM, slave S and monitoring node N, and I don't know how > > much asymmetry is there on any of the paths GM-S, GM-N and S-N. How > > will measuring offset of GM and S from N help me? > > I think I said that this works when N==GM. Hm, does that change anything? If the delay of event messages going from GM/N to S is larger than the delay of messages going from S to GM/N, the clock of S will be running behind GM. The measurements of S done by the GM/N will have the opposite error. If there were no other sources of error (e.g. an error specific to boundary clocks, or an asymmetric delay due to the sync message being shorter than the delay request), the offset of S observed by GM/N should be zero. As I understand it, the only way to determine the asymmetry is to directly compare the clocks with a reference clock, e.g. using a PPS signal. -- Miroslav Lichvar |
From: Richard C. <ric...@gm...> - 2018-01-23 04:06:28
|
On Tue, Jan 09, 2018 at 05:21:39PM +0100, Miroslav Lichvar wrote: > > I think I said that this works when N==GM. > > Hm, does that change anything? If the delay of event messages going > from GM/N to S is larger than the delay of messages going from S to > GM/N, the clock of S will be running behind GM. The measurements of S > done by the GM/N will have the opposite error. Yes, you are right, of course. I wasn't thinking. The Meinberg doc has this statement: One big advantage of this mechanism is, that the communicated offset and delay from the monitored device can be compared to the measured offset and delay values and - assumed that the monitoring system is perfectly synchronized - asymmetries can be detected. So assuming the asymmetry on the monitor's path is known, then you can measure a constant offset on the normal GM path. Thanks, Richard |