3384 lines
122 KiB
C
3384 lines
122 KiB
C
/*-
|
|
* Copyright (c) 2016-2017 Wind River Systems, Inc,
|
|
* Copyright (c) 2012-2013 Wojciech Owczarek,
|
|
* Copyright (c) 2011-2012 George V. Neville-Neil,
|
|
* Steven Kreuzer,
|
|
* Martin Burnicki,
|
|
* Jan Breuer,
|
|
* Gael Mace,
|
|
* Alexandre Van Kempen,
|
|
* Inaqui Delgado,
|
|
* Rick Ratzel,
|
|
* National Instruments.
|
|
* Copyright (c) 2009-2010 George V. Neville-Neil,
|
|
* Steven Kreuzer,
|
|
* Martin Burnicki,
|
|
* Jan Breuer,
|
|
* Gael Mace,
|
|
* Alexandre Van Kempen
|
|
*
|
|
* Copyright (c) 2005-2008 Kendall Correll, Aidan Williams
|
|
*
|
|
* All Rights Reserved
|
|
*
|
|
* Redistribution and use in source and binary forms, with or without
|
|
* modification, are permitted provided that the following conditions are
|
|
* met:
|
|
* 1. Redistributions of source code must retain the above copyright notice,
|
|
* this list of conditions and the following disclaimer.
|
|
* 2. Redistributions in binary form must reproduce the above copyright
|
|
* notice, this list of conditions and the following disclaimer in the
|
|
* documentation and/or other materials provided with the distribution.
|
|
*
|
|
* THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS OR
|
|
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
|
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
|
* DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE
|
|
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
|
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
|
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
|
|
* BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
|
|
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
|
|
* OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
|
|
* IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
*/
|
|
|
|
/*
|
|
modification history
|
|
--------------------
|
|
25jan17,xxy Add support for TSN Clocks and Timers (F6548)
|
|
10feb17,rjq clean build warnings (V7NET-1243)
|
|
15aug16,xxy Add PTP features.(F6696)
|
|
13jun16,xxy Add PTP one-step hardware Tx timeStamp
|
|
06may16,wjf clean static issue
|
|
02feb16,h_l Init version.
|
|
*/
|
|
|
|
/**
|
|
* @file protocol.c
|
|
* @date Wed Jun 23 09:40:39 2010
|
|
*
|
|
* @brief The code that handles the IEEE-1588 protocol and state machine
|
|
*
|
|
*
|
|
*/
|
|
|
|
#include "ptpd.h"
|
|
|
|
Boolean doInit(RunTimeOpts *, PtpClock *);
|
|
void doState(RunTimeOpts *, PtpClock *);
|
|
|
|
void handle(RunTimeOpts *, PtpClock *);
|
|
static void handleAnnounce(MsgHeader *, ssize_t, Boolean, RunTimeOpts *, PtpClock *);
|
|
static void handleSync(const MsgHeader *, ssize_t, TimeInternal *, Boolean, RunTimeOpts *, PtpClock *);
|
|
static void handleFollowUp(const MsgHeader *, ssize_t, Boolean, RunTimeOpts *, PtpClock *);
|
|
static void handlePDelayReq(MsgHeader *, ssize_t, const TimeInternal *, Boolean, RunTimeOpts *, PtpClock *);
|
|
void handleDelayReq(const MsgHeader *, ssize_t, const TimeInternal *, Boolean, RunTimeOpts *, PtpClock *);
|
|
static void handlePDelayResp(const MsgHeader *, TimeInternal *, ssize_t, Boolean, RunTimeOpts *, PtpClock *);
|
|
static void handleDelayResp(const MsgHeader *, ssize_t, RunTimeOpts *, PtpClock *);
|
|
static void handlePDelayRespFollowUp(const MsgHeader *, ssize_t, Boolean, RunTimeOpts *, PtpClock *);
|
|
static void handleManagement(MsgHeader *, Boolean, RunTimeOpts *, PtpClock *);
|
|
static void handleSignaling(PtpClock *);
|
|
static void updateDatasets(PtpClock *ptpClock, RunTimeOpts *rtOpts);
|
|
|
|
static void issueAnnounce(RunTimeOpts *, PtpClock *);
|
|
static void issueSync(RunTimeOpts *, PtpClock *);
|
|
static void issueFollowup(const TimeInternal *, RunTimeOpts *, PtpClock *, const UInteger16);
|
|
static void issuePDelayReq(RunTimeOpts *, PtpClock *);
|
|
static void issueDelayReq(RunTimeOpts *, PtpClock *);
|
|
static void issuePDelayResp(const TimeInternal *, MsgHeader *, RunTimeOpts *, PtpClock *);
|
|
static void issueDelayResp(const TimeInternal *, MsgHeader *, RunTimeOpts *, PtpClock *);
|
|
static void issuePDelayRespFollowUp(const TimeInternal *, MsgHeader *, RunTimeOpts *, PtpClock *, const UInteger16);
|
|
#if 0
|
|
static void issueManagement(MsgHeader *, MsgManagement *, RunTimeOpts *, PtpClock *);
|
|
#endif
|
|
static void issueManagementRespOrAck(MsgManagement *, RunTimeOpts *, PtpClock *);
|
|
static void issueManagementErrorStatus(MsgManagement *, RunTimeOpts *, PtpClock *);
|
|
void processMessage(RunTimeOpts *rtOpts, PtpClock *ptpClock, TimeInternal *timeStamp, ssize_t length);
|
|
static void processSyncFromSelf(const TimeInternal *tint, RunTimeOpts *rtOpts, PtpClock *ptpClock, const UInteger16 sequenceId);
|
|
static void processDelayReqFromSelf(const TimeInternal *tint, RunTimeOpts *rtOpts, PtpClock *ptpClock);
|
|
static void processPDelayReqFromSelf(const TimeInternal *tint, RunTimeOpts *rtOpts, PtpClock *ptpClock);
|
|
static void processPDelayRespFromSelf(const TimeInternal *tint, RunTimeOpts *rtOpts, PtpClock *ptpClock, const UInteger16 sequenceId);
|
|
|
|
extern double pow(double x, double y);
|
|
|
|
void addForeign(Octet *, MsgHeader *, PtpClock *);
|
|
|
|
/* loop forever. doState() has a switch for the actions and events to be
|
|
checked for 'port_state'. the actions and events may or may not change
|
|
'port_state' by calling toState(), but once they are done we loop around
|
|
again and perform the actions required for the new 'port_state'. */
|
|
void
|
|
protocol(RunTimeOpts *rtOpts, PtpClock *ptpClock)
|
|
{
|
|
DBG("event POWERUP\n");
|
|
|
|
toState(PTP_INITIALIZING, rtOpts, ptpClock);
|
|
|
|
DBG("Debug Initializing...\n");
|
|
|
|
for (;;)
|
|
{
|
|
/* 20110701: this main loop was rewritten to be more clear */
|
|
if (!rtOpts->runningFlag)
|
|
{
|
|
break;
|
|
}
|
|
|
|
if (ptpClock->portState == PTP_INITIALIZING)
|
|
{
|
|
if (!doInit(rtOpts, ptpClock))
|
|
{
|
|
return;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
doState(rtOpts, ptpClock);
|
|
}
|
|
|
|
if (ptpClock->message_activity)
|
|
{
|
|
DBGV("activity\n");
|
|
}
|
|
|
|
/* Configuration has changed */
|
|
if (rtOpts->restartSubsystems > 0)
|
|
{
|
|
DBG("RestartSubsystems: %d\n", rtOpts->restartSubsystems);
|
|
|
|
/* So far, PTP_INITIALIZING is required for both network and protocol restart */
|
|
if ((rtOpts->restartSubsystems & PTPD_RESTART_PROTOCOL) ||
|
|
(rtOpts->restartSubsystems & PTPD_RESTART_NETWORK))
|
|
{
|
|
if (rtOpts->restartSubsystems & PTPD_RESTART_PROTOCOL)
|
|
{
|
|
INFO("Applying protocol configuration: going into PTP_INITIALIZING\n");
|
|
}
|
|
|
|
if (rtOpts->restartSubsystems & PTPD_RESTART_NETWORK)
|
|
{
|
|
NOTIFY("Applying network configuration: going into PTP_INITIALIZING\n");
|
|
}
|
|
|
|
/* Those two parameters have to be passed to ptpClock before re-init */
|
|
ptpClock->clockQuality.clockClass = rtOpts->clockQuality.clockClass;
|
|
ptpClock->slaveOnly = rtOpts->slaveOnly;
|
|
|
|
/* Move back to primary interface only during configuration changes. */
|
|
ptpClock->runningBackupInterface = FALSE;
|
|
|
|
toState(PTP_INITIALIZING, rtOpts, ptpClock);
|
|
}
|
|
else
|
|
{
|
|
/* Nothing happens here for now - SIGHUP handler does this anyway */
|
|
if (rtOpts->restartSubsystems & PTPD_UPDATE_DATASETS)
|
|
{
|
|
NOTIFY("Applying PTP engine configuration: updating datasets\n");
|
|
updateDatasets(ptpClock, rtOpts);
|
|
}
|
|
}
|
|
|
|
/* Nothing happens here for now - SIGHUP handler does this anyway */
|
|
if (rtOpts->restartSubsystems & PTPD_RESTART_LOGGING)
|
|
{
|
|
NOTIFY("Applying logging configuration: restarting logging\n");
|
|
}
|
|
|
|
if (rtOpts->restartSubsystems & PTPD_RESTART_ACLS)
|
|
{
|
|
NOTIFY("Applying access control list configuration\n");
|
|
/* re-compile ACLs */
|
|
freeIpv4AccessList(&ptpClock->netPath.timingAcl);
|
|
freeIpv4AccessList(&ptpClock->netPath.managementAcl);
|
|
|
|
if (rtOpts->timingAclEnabled)
|
|
{
|
|
ptpClock->netPath.timingAcl = createIpv4AccessList(rtOpts->timingAclPermitText,
|
|
rtOpts->timingAclDenyText, rtOpts->timingAclOrder);
|
|
}
|
|
|
|
if (rtOpts->managementAclEnabled)
|
|
{
|
|
ptpClock->netPath.managementAcl = createIpv4AccessList(rtOpts->managementAclPermitText,
|
|
rtOpts->managementAclDenyText, rtOpts->managementAclOrder);
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Update PI servo parameters */
|
|
setupPIservo(&ptpClock->servo, rtOpts);
|
|
|
|
/* Config changes don't require subsystem restarts - acknowledge it */
|
|
if (rtOpts->restartSubsystems == PTPD_RESTART_NONE)
|
|
{
|
|
NOTIFY("Applying configuration\n");
|
|
}
|
|
|
|
if (rtOpts->restartSubsystems != (UInteger32)0xffffffff)
|
|
{
|
|
rtOpts->restartSubsystems = 0;
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
}
|
|
|
|
|
|
/* perform actions required when leaving 'port_state' and entering 'state' */
|
|
void
|
|
toState(UInteger8 state, RunTimeOpts *rtOpts, PtpClock *ptpClock)
|
|
{
|
|
ptpClock->message_activity = TRUE;
|
|
|
|
/* leaving state tasks */
|
|
switch (ptpClock->portState)
|
|
{
|
|
case PTP_MASTER:
|
|
timerStop(SYNC_INTERVAL_TIMER, ptpClock->itimer);
|
|
timerStop(ANNOUNCE_INTERVAL_TIMER, ptpClock->itimer);
|
|
timerStop(PDELAYREQ_INTERVAL_TIMER, ptpClock->itimer);
|
|
timerStop(MASTER_NETREFRESH_TIMER, ptpClock->itimer);
|
|
break;
|
|
|
|
case PTP_SLAVE:
|
|
timerStop(ANNOUNCE_RECEIPT_TIMER, ptpClock->itimer);
|
|
|
|
if (ptpClock->delayMechanism == E2E)
|
|
{
|
|
timerStop(DELAYREQ_INTERVAL_TIMER, ptpClock->itimer);
|
|
}
|
|
else if (ptpClock->delayMechanism == P2P)
|
|
{
|
|
timerStop(PDELAYREQ_INTERVAL_TIMER, ptpClock->itimer);
|
|
}
|
|
|
|
/* If statistics are enabled, drift should have been saved already - otherwise save it*/
|
|
/* save observed drift value, don't inform user */
|
|
saveDrift(ptpClock, rtOpts, TRUE);
|
|
|
|
|
|
ptpClock->panicMode = FALSE;
|
|
ptpClock->panicOver = FALSE;
|
|
timerStop(PANIC_MODE_TIMER, ptpClock->itimer);
|
|
initClock(rtOpts, ptpClock);
|
|
break;
|
|
|
|
case PTP_PASSIVE:
|
|
timerStop(PDELAYREQ_INTERVAL_TIMER, ptpClock->itimer);
|
|
timerStop(ANNOUNCE_RECEIPT_TIMER, ptpClock->itimer);
|
|
break;
|
|
|
|
case PTP_LISTENING:
|
|
timerStop(ANNOUNCE_RECEIPT_TIMER, ptpClock->itimer);
|
|
|
|
/* we're leaving LISTENING - reset counter */
|
|
if (state != PTP_LISTENING)
|
|
{
|
|
ptpClock->listenCount = 0;
|
|
}
|
|
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
|
|
/* entering state tasks */
|
|
|
|
ptpClock->counters.stateTransitions++;
|
|
|
|
DBG("state %s\n", portState_getName(state));
|
|
|
|
|
|
/*
|
|
* No need of PRE_MASTER state because of only ordinary clock
|
|
* implementation.
|
|
*/
|
|
|
|
switch (state)
|
|
{
|
|
case PTP_INITIALIZING:
|
|
ptpClock->portState = PTP_INITIALIZING;
|
|
break;
|
|
|
|
case PTP_FAULTY:
|
|
ptpClock->portState = PTP_FAULTY;
|
|
break;
|
|
|
|
case PTP_DISABLED:
|
|
ptpClock->portState = PTP_DISABLED;
|
|
break;
|
|
|
|
case PTP_LISTENING:
|
|
/* in Listening mode, make sure we don't send anything. Instead we just expect/wait for announces (started below) */
|
|
timerStop(SYNC_INTERVAL_TIMER, ptpClock->itimer);
|
|
timerStop(ANNOUNCE_INTERVAL_TIMER, ptpClock->itimer);
|
|
timerStop(PDELAYREQ_INTERVAL_TIMER, ptpClock->itimer);
|
|
timerStop(DELAYREQ_INTERVAL_TIMER, ptpClock->itimer);
|
|
|
|
/* This is (re) started on clock updates only */
|
|
timerStop(CLOCK_UPDATE_TIMER, ptpClock->itimer);
|
|
|
|
/* reset PIservo */
|
|
resetPIservo(&ptpClock->servo);
|
|
/*
|
|
* Count how many _unique_ timeouts happen to us.
|
|
* If we were already in Listen mode, then do not count this as a seperate reset, but stil do a new IGMP refresh
|
|
*/
|
|
if (ptpClock->portState != PTP_LISTENING)
|
|
{
|
|
ptpClock->resetCount++;
|
|
}
|
|
else
|
|
{
|
|
ptpClock->listenCount++;
|
|
|
|
if ( ptpClock->listenCount >= rtOpts->maxListen )
|
|
{
|
|
WARNING("Still in LISTENING after %d restarts - will do a full network reset\n",
|
|
rtOpts->maxListen);
|
|
toState(PTP_FAULTY, rtOpts, ptpClock);
|
|
ptpClock->listenCount = 0;
|
|
break;
|
|
}
|
|
}
|
|
|
|
/* Revert to the original DelayReq interval, and ignore the one for the last master */
|
|
ptpClock->logMinDelayReqInterval = (Integer8)rtOpts->initial_delayreq;
|
|
|
|
/* force a IGMP refresh per reset */
|
|
if (rtOpts->ip_mode != IPMODE_UNICAST && rtOpts->do_IGMP_refresh && rtOpts->transport != IEEE_802_3)
|
|
{
|
|
/* if multicast refresh failed, restart network - helps recover after driver reloads and such */
|
|
if (!netRefreshIGMP(&ptpClock->netPath, rtOpts, ptpClock))
|
|
{
|
|
WARNING("Error while refreshing multicast - will do a full network reset\n");
|
|
toState(PTP_FAULTY, rtOpts, ptpClock);
|
|
break;
|
|
}
|
|
}
|
|
|
|
timerStart(ANNOUNCE_RECEIPT_TIMER,
|
|
(float)((ptpClock->announceReceiptTimeout) *
|
|
(pow(2, ptpClock->logAnnounceInterval))),
|
|
ptpClock->itimer);
|
|
ptpClock->announceTimeouts = 0;
|
|
|
|
/* We've entered LISTENING state from SLAVE - start NTP failover actions */
|
|
|
|
/*
|
|
* Log status update only once - condition must be checked before we write the new state,
|
|
* but the new state must be eritten before the log message...
|
|
*/
|
|
if (ptpClock->portState != state)
|
|
{
|
|
ptpClock->portState = PTP_LISTENING;
|
|
displayStatus(rtOpts, ptpClock, "Now in state: ");
|
|
}
|
|
else
|
|
{
|
|
ptpClock->portState = PTP_LISTENING;
|
|
}
|
|
|
|
break;
|
|
|
|
case PTP_MASTER:
|
|
timerStart(SYNC_INTERVAL_TIMER,
|
|
(float)pow(2, ptpClock->logSyncInterval), ptpClock->itimer);
|
|
DBG("SYNC INTERVAL TIMER : %f \n",
|
|
pow(2, ptpClock->logSyncInterval));
|
|
timerStart(ANNOUNCE_INTERVAL_TIMER,
|
|
(float)pow(2, ptpClock->logAnnounceInterval),
|
|
ptpClock->itimer);
|
|
timerStart(PDELAYREQ_INTERVAL_TIMER,
|
|
(float)pow(2, ptpClock->logMinPdelayReqInterval),
|
|
ptpClock->itimer);
|
|
|
|
if ( rtOpts->do_IGMP_refresh &&
|
|
rtOpts->transport == UDP_IPV4 &&
|
|
rtOpts->ip_mode != IPMODE_UNICAST &&
|
|
rtOpts->masterRefreshInterval > 9 )
|
|
timerStart(MASTER_NETREFRESH_TIMER,
|
|
rtOpts->masterRefreshInterval,
|
|
ptpClock->itimer);
|
|
|
|
ptpClock->portState = PTP_MASTER;
|
|
displayStatus(rtOpts, ptpClock, "Now in state: ");
|
|
break;
|
|
|
|
case PTP_PASSIVE:
|
|
timerStart(PDELAYREQ_INTERVAL_TIMER,
|
|
(float)pow(2, ptpClock->logMinPdelayReqInterval),
|
|
ptpClock->itimer);
|
|
timerStart(ANNOUNCE_RECEIPT_TIMER,
|
|
(float)((ptpClock->announceReceiptTimeout) *
|
|
(pow(2, ptpClock->logAnnounceInterval))),
|
|
ptpClock->itimer);
|
|
ptpClock->portState = PTP_PASSIVE;
|
|
p1(ptpClock, rtOpts);
|
|
displayStatus(rtOpts, ptpClock, "Now in state: ");
|
|
break;
|
|
|
|
case PTP_UNCALIBRATED:
|
|
ptpClock->portState = PTP_UNCALIBRATED;
|
|
break;
|
|
|
|
case PTP_SLAVE:
|
|
if (rtOpts->clockUpdateTimeout > 0)
|
|
timerStart(CLOCK_UPDATE_TIMER,
|
|
(float)rtOpts->clockUpdateTimeout, ptpClock->itimer);
|
|
|
|
initClock(rtOpts, ptpClock);
|
|
/*
|
|
* restore the observed drift value using the selected method,
|
|
* reset on failure or when -F 0 (default) is used, don't inform user
|
|
*/
|
|
restoreDrift(ptpClock, rtOpts, TRUE);
|
|
|
|
ptpClock->waitingForFollow = FALSE;
|
|
ptpClock->waitingForDelayResp = FALSE;
|
|
/* FIXME: clear these vars inside initclock */
|
|
clearTime(&ptpClock->delay_req_send_time);
|
|
clearTime(&ptpClock->delay_req_receive_time);
|
|
clearTime(&ptpClock->pdelay_req_send_time);
|
|
clearTime(&ptpClock->pdelay_req_receive_time);
|
|
clearTime(&ptpClock->pdelay_resp_send_time);
|
|
clearTime(&ptpClock->pdelay_resp_receive_time);
|
|
|
|
timerStart(OPERATOR_MESSAGES_TIMER,
|
|
OPERATOR_MESSAGES_INTERVAL,
|
|
ptpClock->itimer);
|
|
|
|
timerStart(ANNOUNCE_RECEIPT_TIMER,
|
|
(float)((ptpClock->announceReceiptTimeout) *
|
|
(pow(2, ptpClock->logAnnounceInterval))),
|
|
ptpClock->itimer);
|
|
|
|
/*
|
|
* Previously, this state transition would start the
|
|
* delayreq timer immediately. However, if this was
|
|
* faster than the first received sync, then the servo
|
|
* would drop the delayResp Now, we only start the
|
|
* timer after we receive the first sync (in
|
|
* handle_sync())
|
|
*/
|
|
|
|
ptpClock->waiting_for_first_sync = TRUE;
|
|
ptpClock->waiting_for_first_delayresp = TRUE;
|
|
ptpClock->announceTimeouts = 0;
|
|
ptpClock->portState = PTP_SLAVE;
|
|
displayStatus(rtOpts, ptpClock, "Now in state: ");
|
|
ptpClock->followUpGap = 0;
|
|
|
|
|
|
#ifdef HAVE_SYS_TIMEX_H
|
|
|
|
/*
|
|
* leap second pending in kernel but no leap second
|
|
* info from GM - withdraw kernel leap second
|
|
* if the flags have disappeared but we're past
|
|
* leap second event, do nothing - kernel flags
|
|
* will be unset in handleAnnounce()
|
|
*/
|
|
if ((!ptpClock->timePropertiesDS.leap59 && !ptpClock->timePropertiesDS.leap61) &&
|
|
!ptpClock->leapSecondInProgress &&
|
|
(checkTimexFlags(STA_INS) || checkTimexFlags(STA_DEL)))
|
|
{
|
|
WARNING("Leap second pending in kernel but not on "
|
|
"GM: aborting kernel leap second\n");
|
|
unsetTimexFlags(STA_INS | STA_DEL, TRUE);
|
|
}
|
|
|
|
#endif /* HAVE_SYS_TIMEX_H */
|
|
break;
|
|
|
|
default:
|
|
DBG("to unrecognized state\n");
|
|
break;
|
|
}
|
|
|
|
if (rtOpts->logStatistics)
|
|
{
|
|
logStatistics(rtOpts, ptpClock);
|
|
}
|
|
}
|
|
|
|
|
|
Boolean
|
|
doInit(RunTimeOpts *rtOpts, PtpClock *ptpClock)
|
|
{
|
|
char filterMask[200];
|
|
|
|
DBG("manufacturerIdentity: %s\n", MANUFACTURER_ID);
|
|
DBG("manufacturerOUI: %02hhx:%02hhx:%02hhx \n",
|
|
MANUFACTURER_ID_OUI0,
|
|
MANUFACTURER_ID_OUI1,
|
|
MANUFACTURER_ID_OUI2);
|
|
/* initialize networking */
|
|
(void)netShutdown(rtOpts, &ptpClock->netPath);
|
|
|
|
if (rtOpts->backupIfaceEnabled &&
|
|
ptpClock->runningBackupInterface)
|
|
{
|
|
rtOpts->ifaceName = rtOpts->backupIfaceName;
|
|
}
|
|
else
|
|
{
|
|
rtOpts->ifaceName = rtOpts->primaryIfaceName;
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!netInit(&ptpClock->netPath, rtOpts, ptpClock))
|
|
{
|
|
NOTICE("Failed to initialize network\n");
|
|
toState(PTP_FAULTY, rtOpts, ptpClock);
|
|
return FALSE;
|
|
}
|
|
|
|
strncpy(filterMask, FILTER_MASK, 199);
|
|
|
|
|
|
/* initialize other stuff */
|
|
initData(rtOpts, ptpClock);
|
|
initTimer();
|
|
initClock(rtOpts, ptpClock);
|
|
setupPIservo(&ptpClock->servo, rtOpts);
|
|
#ifdef HAVE_SYS_TIMEX_H
|
|
|
|
/* restore observed drift and inform user */
|
|
if (ptpClock->clockQuality.clockClass > 127)
|
|
{
|
|
restoreDrift(ptpClock, rtOpts, FALSE);
|
|
}
|
|
|
|
#endif /* HAVE_SYS_TIMEX_H */
|
|
m1(rtOpts, ptpClock );
|
|
msgPackHeader(ptpClock->msgObuf, ptpClock);
|
|
|
|
toState(PTP_LISTENING, rtOpts, ptpClock);
|
|
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
/* handle actions and events for 'port_state' */
|
|
void
|
|
doState(RunTimeOpts *rtOpts, PtpClock *ptpClock)
|
|
{
|
|
UInteger8 state;
|
|
|
|
ptpClock->message_activity = FALSE;
|
|
|
|
/* Process record_update (BMC algorithm) before everything else */
|
|
switch (ptpClock->portState)
|
|
{
|
|
case PTP_LISTENING:
|
|
case PTP_PASSIVE:
|
|
case PTP_SLAVE:
|
|
case PTP_MASTER:
|
|
|
|
/*State decision Event*/
|
|
|
|
/* If we received a valid Announce message
|
|
* and can use it (record_update),
|
|
* or we received a SET management message that
|
|
* changed an attribute in ptpClock,
|
|
* then run the BMC algorithm
|
|
*/
|
|
if (ptpClock->record_update)
|
|
{
|
|
DBG2("event STATE_DECISION_EVENT\n");
|
|
ptpClock->record_update = FALSE;
|
|
state = bmc(ptpClock->foreign, rtOpts, ptpClock);
|
|
|
|
if (state != ptpClock->portState)
|
|
{
|
|
toState(state, rtOpts, ptpClock);
|
|
}
|
|
}
|
|
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
|
|
switch (ptpClock->portState)
|
|
{
|
|
case PTP_FAULTY:
|
|
/* imaginary troubleshooting */
|
|
DBG("event FAULT_CLEARED\n");
|
|
toState(PTP_INITIALIZING, rtOpts, ptpClock);
|
|
return;
|
|
|
|
case PTP_LISTENING:
|
|
case PTP_UNCALIBRATED:
|
|
case PTP_SLAVE:
|
|
|
|
/* passive mode behaves like the SLAVE state, in order to wait for the announce timeout of the current active master */
|
|
case PTP_PASSIVE:
|
|
handle(rtOpts, ptpClock);
|
|
|
|
/*
|
|
* handle SLAVE timers:
|
|
* - No Announce message was received
|
|
* - Time to send new delayReq (miss of delayResp is not monitored explicitelly)
|
|
*/
|
|
if (timerExpired(ANNOUNCE_RECEIPT_TIMER, ptpClock->itimer))
|
|
{
|
|
DBG("event ANNOUNCE_RECEIPT_TIMEOUT_EXPIRES\n");
|
|
|
|
if (!ptpClock->slaveOnly &&
|
|
ptpClock->clockQuality.clockClass != SLAVE_ONLY_CLOCK_CLASS)
|
|
{
|
|
ptpClock->number_foreign_records = 0;
|
|
ptpClock->foreign_record_i = 0;
|
|
m1(rtOpts, ptpClock);
|
|
toState(PTP_MASTER, rtOpts, ptpClock);
|
|
|
|
}
|
|
else if (ptpClock->portState != PTP_LISTENING)
|
|
{
|
|
|
|
if (ptpClock->announceTimeouts < rtOpts->announceTimeoutGracePeriod)
|
|
{
|
|
/*
|
|
* Don't reset yet - just disqualify current GM.
|
|
* If another live master exists, it will be selected,
|
|
* otherwise, timer will cycle and we will reset.
|
|
* Also don't clear the FMR just yet.
|
|
*/
|
|
if (ptpClock->grandmasterClockQuality.clockClass != 255 &&
|
|
ptpClock->grandmasterPriority1 != 255 &&
|
|
ptpClock->grandmasterPriority2 != 255)
|
|
{
|
|
ptpClock->grandmasterClockQuality.clockClass = 255;
|
|
ptpClock->grandmasterPriority1 = 255;
|
|
ptpClock->grandmasterPriority2 = 255;
|
|
ptpClock->foreign[ptpClock->foreign_record_best].announce.grandmasterPriority1 = 255;
|
|
ptpClock->foreign[ptpClock->foreign_record_best].announce.grandmasterPriority2 = 255;
|
|
ptpClock->foreign[ptpClock->foreign_record_best].announce.grandmasterClockQuality.clockClass = 255;
|
|
WARNING("GM announce timeout, disqualified current best GM\n");
|
|
ptpClock->counters.announceTimeouts++;
|
|
}
|
|
|
|
if (rtOpts->ip_mode != IPMODE_UNICAST && rtOpts->do_IGMP_refresh && rtOpts->transport != IEEE_802_3)
|
|
{
|
|
/* if multicast refresh failed, restart network - helps recover after driver reloads and such */
|
|
if (!netRefreshIGMP(&ptpClock->netPath, rtOpts, ptpClock))
|
|
{
|
|
WARNING("Error while refreshing multicast - will do a full network reset\n");
|
|
toState(PTP_FAULTY, rtOpts, ptpClock);
|
|
break;
|
|
}
|
|
}
|
|
|
|
/*
|
|
timerStart(ANNOUNCE_RECEIPT_TIMER,
|
|
(ptpClock->announceReceiptTimeout) *
|
|
(pow(2,ptpClock->logAnnounceInterval)),
|
|
ptpClock->itimer);
|
|
*/
|
|
if (rtOpts->announceTimeoutGracePeriod > 0)
|
|
{
|
|
ptpClock->announceTimeouts++;
|
|
}
|
|
|
|
INFO("Waiting for new master, %d of %d attempts\n", ptpClock->announceTimeouts, rtOpts->announceTimeoutGracePeriod);
|
|
}
|
|
else
|
|
{
|
|
WARNING("No active masters present. Resetting port.\n");
|
|
ptpClock->number_foreign_records = 0;
|
|
ptpClock->foreign_record_i = 0;
|
|
|
|
/* if flipping between primary and backup interface, a full nework re-init is required */
|
|
if (rtOpts->backupIfaceEnabled)
|
|
{
|
|
ptpClock->runningBackupInterface = !ptpClock->runningBackupInterface;
|
|
toState(PTP_INITIALIZING, rtOpts, ptpClock);
|
|
NOTICE("Now switching to %s interface\n", ptpClock->runningBackupInterface ?
|
|
"backup" : "primary");
|
|
}
|
|
else
|
|
{
|
|
|
|
toState(PTP_LISTENING, rtOpts, ptpClock);
|
|
}
|
|
|
|
}
|
|
}
|
|
else
|
|
{
|
|
|
|
/* if flipping between primary and backup interface, a full nework re-init is required */
|
|
if (rtOpts->backupIfaceEnabled)
|
|
{
|
|
ptpClock->runningBackupInterface = !ptpClock->runningBackupInterface;
|
|
toState(PTP_INITIALIZING, rtOpts, ptpClock);
|
|
NOTICE("Now switching to %s interface\n", ptpClock->runningBackupInterface ?
|
|
"backup" : "primary");
|
|
|
|
}
|
|
else
|
|
{
|
|
/*
|
|
* Force a reset when getting a timeout in state listening, that will lead to an IGMP reset
|
|
* previously this was not the case when we were already in LISTENING mode
|
|
*/
|
|
toState(PTP_LISTENING, rtOpts, ptpClock);
|
|
}
|
|
}
|
|
|
|
|
|
}
|
|
|
|
/* Reset the slave if clock update timeout configured */
|
|
if ( (rtOpts->clockUpdateTimeout > 0) &&
|
|
timerExpired(CLOCK_UPDATE_TIMER, ptpClock->itimer))
|
|
{
|
|
WARNING("No clock updates in %d seconds - resetting slave\n",
|
|
rtOpts->clockUpdateTimeout);
|
|
toState(PTP_LISTENING, rtOpts, ptpClock);
|
|
}
|
|
|
|
|
|
if (timerExpired(OPERATOR_MESSAGES_TIMER, ptpClock->itimer))
|
|
{
|
|
reset_operator_messages(rtOpts, ptpClock);
|
|
}
|
|
|
|
|
|
if (ptpClock->delayMechanism == E2E)
|
|
{
|
|
if (timerExpired(DELAYREQ_INTERVAL_TIMER,
|
|
ptpClock->itimer))
|
|
{
|
|
DBG2("event DELAYREQ_INTERVAL_TIMEOUT_EXPIRES\n");
|
|
issueDelayReq(rtOpts, ptpClock);
|
|
}
|
|
}
|
|
else if (ptpClock->delayMechanism == P2P)
|
|
{
|
|
if (timerExpired(PDELAYREQ_INTERVAL_TIMER,
|
|
ptpClock->itimer))
|
|
{
|
|
DBGV("event PDELAYREQ_INTERVAL_TIMEOUT_EXPIRES\n");
|
|
issuePDelayReq(rtOpts, ptpClock);
|
|
}
|
|
|
|
/* FIXME: Path delay should also rearm its timer with the value received from the Master */
|
|
}
|
|
|
|
if (ptpClock->timePropertiesDS.leap59 || ptpClock->timePropertiesDS.leap61)
|
|
{
|
|
DBGV("seconds to midnight: %.3f\n", secondsToMidnight(rtOpts));
|
|
}
|
|
|
|
/* leap second period is over */
|
|
if (timerExpired(LEAP_SECOND_PAUSE_TIMER, ptpClock->itimer) &&
|
|
ptpClock->leapSecondInProgress)
|
|
{
|
|
/*
|
|
* do not unpause offset calculation just
|
|
* yet, just indicate and it will be
|
|
* unpaused in handleAnnounce()
|
|
*/
|
|
ptpClock->leapSecondPending = FALSE;
|
|
timerStop(LEAP_SECOND_PAUSE_TIMER, ptpClock->itimer);
|
|
}
|
|
|
|
/* check if leap second is near and if we should pause updates */
|
|
if ( ptpClock->leapSecondPending &&
|
|
!ptpClock->leapSecondInProgress &&
|
|
(secondsToMidnight(rtOpts) <=
|
|
getPauseAfterMidnight(ptpClock->logAnnounceInterval)))
|
|
{
|
|
WARNING("Leap second event imminent - pausing "
|
|
"clock and offset updates\n");
|
|
ptpClock->leapSecondInProgress = TRUE;
|
|
#ifdef HAVE_SYS_TIMEX_H
|
|
|
|
if (!checkTimexFlags(ptpClock->timePropertiesDS.leap61 ?
|
|
STA_INS : STA_DEL))
|
|
{
|
|
WARNING("Kernel leap second flags have "
|
|
"been unset - attempting to set "
|
|
"again");
|
|
setTimexFlags(ptpClock->timePropertiesDS.leap61 ?
|
|
STA_INS : STA_DEL, FALSE);
|
|
}
|
|
|
|
#endif /* HAVE_SYS_TIMEX_H */
|
|
/*
|
|
* start pause timer from now until [pause] after
|
|
* midnight, plus an extra second if inserting
|
|
* a leap second
|
|
*/
|
|
timerStart(LEAP_SECOND_PAUSE_TIMER,
|
|
(float)(secondsToMidnight(rtOpts) +
|
|
(float)ptpClock->timePropertiesDS.leap61 +
|
|
getPauseAfterMidnight(ptpClock->logAnnounceInterval)),
|
|
ptpClock->itimer);
|
|
}
|
|
|
|
break;
|
|
|
|
case PTP_MASTER:
|
|
|
|
/*
|
|
* handle SLAVE timers:
|
|
* - Time to send new Announce
|
|
* - Time to send new PathDelay
|
|
* - Time to send new Sync (last order - so that follow-up always follows sync
|
|
* in two-step mode: improves interoperability
|
|
* (DelayResp has no timer - as these are sent and retransmitted by the slaves)
|
|
*/
|
|
|
|
if (timerExpired(ANNOUNCE_INTERVAL_TIMER, ptpClock->itimer))
|
|
{
|
|
DBGV("event ANNOUNCE_INTERVAL_TIMEOUT_EXPIRES\n");
|
|
issueAnnounce(rtOpts, ptpClock);
|
|
}
|
|
|
|
if (ptpClock->delayMechanism == P2P)
|
|
{
|
|
if (timerExpired(PDELAYREQ_INTERVAL_TIMER,
|
|
ptpClock->itimer))
|
|
{
|
|
DBGV("event PDELAYREQ_INTERVAL_TIMEOUT_EXPIRES\n");
|
|
issuePDelayReq(rtOpts, ptpClock);
|
|
}
|
|
}
|
|
|
|
if (rtOpts->do_IGMP_refresh &&
|
|
rtOpts->transport == UDP_IPV4 &&
|
|
rtOpts->ip_mode != IPMODE_UNICAST &&
|
|
rtOpts->masterRefreshInterval > 9 &&
|
|
timerExpired(MASTER_NETREFRESH_TIMER, ptpClock->itimer))
|
|
{
|
|
DBGV("Master state periodic IGMP refresh - next in %d seconds...\n",
|
|
rtOpts->masterRefreshInterval);
|
|
|
|
/* if multicast refresh failed, restart network - helps recover after driver reloads and such */
|
|
if (!netRefreshIGMP(&ptpClock->netPath, rtOpts, ptpClock))
|
|
{
|
|
WARNING("Error while refreshing multicast - will do a full network reset\n");
|
|
toState(PTP_FAULTY, rtOpts, ptpClock);
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (timerExpired(SYNC_INTERVAL_TIMER, ptpClock->itimer))
|
|
{
|
|
DBGV("event SYNC_INTERVAL_TIMEOUT_EXPIRES\n");
|
|
issueSync(rtOpts, ptpClock);
|
|
}
|
|
|
|
/* TODO: why is handle() below expiretimer, while in slave is the opposite */
|
|
handle(rtOpts, ptpClock);
|
|
|
|
if (ptpClock->slaveOnly || ptpClock->clockQuality.clockClass == SLAVE_ONLY_CLOCK_CLASS)
|
|
{
|
|
toState(PTP_LISTENING, rtOpts, ptpClock);
|
|
}
|
|
|
|
break;
|
|
|
|
case PTP_DISABLED:
|
|
handle(rtOpts, ptpClock);
|
|
break;
|
|
|
|
default:
|
|
DBG("(doState) do unrecognized state\n");
|
|
break;
|
|
}
|
|
|
|
|
|
if (rtOpts->enablePanicMode && timerExpired(PANIC_MODE_TIMER, ptpClock->itimer))
|
|
{
|
|
|
|
DBG("Panic check\n");
|
|
|
|
if (--ptpClock->panicModeTimeLeft <= 0)
|
|
{
|
|
ptpClock->panicMode = FALSE;
|
|
ptpClock->panicOver = TRUE;
|
|
DBG("panic over!\n");
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
static Boolean
|
|
isFromCurrentParent(const PtpClock *ptpClock, const MsgHeader *header)
|
|
{
|
|
|
|
return (!memcmp(
|
|
ptpClock->parentPortIdentity.clockIdentity,
|
|
header->sourcePortIdentity.clockIdentity,
|
|
CLOCK_IDENTITY_LENGTH) &&
|
|
(ptpClock->parentPortIdentity.portNumber ==
|
|
header->sourcePortIdentity.portNumber));
|
|
|
|
}
|
|
|
|
void
|
|
processMessage(RunTimeOpts *rtOpts, PtpClock *ptpClock, TimeInternal *timeStamp, ssize_t length)
|
|
{
|
|
|
|
Boolean isFromSelf;
|
|
|
|
/*
|
|
* make sure we use the TAI to UTC offset specified, if the
|
|
* master is sending the UTC_VALID bit
|
|
*
|
|
* On the slave, all timestamps that we handle here have been
|
|
* collected by our local clock (loopback+kernel-level
|
|
* timestamp) This includes delayReq just send, and delayResp,
|
|
* when it arrives.
|
|
*
|
|
* these are then adjusted to the same timebase of the Master
|
|
* (+35 leap seconds, as of July 2012)
|
|
*
|
|
* wowczarek: added compatibility flag to always respect the
|
|
* announced UTC offset, preventing clock jumps with some GMs
|
|
*/
|
|
DBGV("__UTC_offset: %d %d \n", ptpClock->timePropertiesDS.currentUtcOffsetValid, ptpClock->timePropertiesDS.currentUtcOffset);
|
|
|
|
if (respectUtcOffset(rtOpts, ptpClock) == TRUE)
|
|
{
|
|
timeStamp->seconds += ptpClock->timePropertiesDS.currentUtcOffset;
|
|
}
|
|
|
|
ptpClock->message_activity = TRUE;
|
|
|
|
if (length < HEADER_LENGTH)
|
|
{
|
|
DBG("Error: message shorter than header length\n");
|
|
ptpClock->counters.messageFormatErrors++;
|
|
return;
|
|
}
|
|
|
|
msgUnpackHeader(ptpClock->msgIbuf, &ptpClock->msgTmpHeader);
|
|
|
|
/* packet is not from self, and is from a non-zero source address - check ACLs */
|
|
if (ptpClock->netPath.lastRecvAddr &&
|
|
(ptpClock->netPath.lastRecvAddr != ptpClock->netPath.interfaceAddr.s_addr))
|
|
{
|
|
#ifdef PTPD_DBG
|
|
struct in_addr in;
|
|
in.s_addr = ptpClock->netPath.lastRecvAddr;
|
|
#endif
|
|
|
|
if (ptpClock->msgTmpHeader.messageType == MANAGEMENT)
|
|
{
|
|
if (rtOpts->managementAclEnabled)
|
|
{
|
|
if (!matchIpv4AccessList(
|
|
ptpClock->netPath.managementAcl,
|
|
ntohl(ptpClock->netPath.lastRecvAddr)))
|
|
{
|
|
DBG("ACL dropped management message from %s\n", inet_ntoa(in));
|
|
ptpClock->counters.aclManagementDiscardedMessages++;
|
|
return;
|
|
}
|
|
else
|
|
{
|
|
DBG("ACL Accepted management message from %s\n", inet_ntoa(in));
|
|
}
|
|
}
|
|
}
|
|
else if (rtOpts->timingAclEnabled)
|
|
{
|
|
if (!matchIpv4AccessList(ptpClock->netPath.timingAcl,
|
|
ntohl(ptpClock->netPath.lastRecvAddr)))
|
|
{
|
|
DBG("ACL dropped timing message from %s\n", inet_ntoa(in));
|
|
ptpClock->counters.aclTimingDiscardedMessages++;
|
|
return;
|
|
}
|
|
else
|
|
{
|
|
DBG("ACL accepted timing message from %s\n", inet_ntoa(in));
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
if (ptpClock->msgTmpHeader.versionPTP != ptpClock->versionNumber)
|
|
{
|
|
DBG("ignore version %d message\n", ptpClock->msgTmpHeader.versionPTP);
|
|
ptpClock->counters.discardedMessages++;
|
|
ptpClock->counters.versionMismatchErrors++;
|
|
return;
|
|
}
|
|
|
|
if (ptpClock->msgTmpHeader.domainNumber != ptpClock->domainNumber)
|
|
{
|
|
DBG("ignore message from domainNumber %d\n", ptpClock->msgTmpHeader.domainNumber);
|
|
ptpClock->counters.discardedMessages++;
|
|
ptpClock->counters.domainMismatchErrors++;
|
|
return;
|
|
}
|
|
|
|
|
|
if (rtOpts->transport != IEEE_802_3)
|
|
{
|
|
|
|
/* received a UNICAST message */
|
|
if ((ptpClock->msgTmpHeader.flagField0 & PTP_UNICAST) == PTP_UNICAST)
|
|
{
|
|
/* in multicast mode, accept only management unicast messages, in hybrid mode accept only unicast delay messages */
|
|
if ((rtOpts->ip_mode == IPMODE_MULTICAST && ptpClock->msgTmpHeader.messageType != MANAGEMENT) ||
|
|
(rtOpts->ip_mode == IPMODE_HYBRID && ptpClock->msgTmpHeader.messageType != DELAY_REQ &&
|
|
ptpClock->msgTmpHeader.messageType != DELAY_RESP))
|
|
{
|
|
DBG("ignord unicast message in multicast mode%d\n");
|
|
ptpClock->counters.discardedMessages++;
|
|
return;
|
|
}
|
|
|
|
/* received a MULTICAST message */
|
|
}
|
|
else
|
|
{
|
|
/* in unicast mode, accept only management multicast messages */
|
|
if (rtOpts->ip_mode == IPMODE_UNICAST && ptpClock->msgTmpHeader.messageType != MANAGEMENT)
|
|
{
|
|
DBG("ignord multicast message in unicast mode%d\n");
|
|
ptpClock->counters.discardedMessages++;
|
|
return;
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
/*Spec 9.5.2.2*/
|
|
isFromSelf = (ptpClock->portIdentity.portNumber == ptpClock->msgTmpHeader.sourcePortIdentity.portNumber
|
|
&& !memcmp(ptpClock->msgTmpHeader.sourcePortIdentity.clockIdentity, ptpClock->portIdentity.clockIdentity, CLOCK_IDENTITY_LENGTH));
|
|
|
|
/*
|
|
* subtract the inbound latency adjustment if it is not a loop
|
|
* back and the time stamp seems reasonable
|
|
*/
|
|
if (!isFromSelf && timeStamp->seconds > 0)
|
|
{
|
|
subTime(timeStamp, timeStamp, &rtOpts->inboundLatency);
|
|
}
|
|
|
|
|
|
#ifdef PTPD_DBG
|
|
/* easy display of received messages */
|
|
char *st;
|
|
|
|
switch (ptpClock->msgTmpHeader.messageType)
|
|
{
|
|
case ANNOUNCE:
|
|
st = "Announce";
|
|
break;
|
|
|
|
case SYNC:
|
|
st = "Sync";
|
|
break;
|
|
|
|
case FOLLOW_UP:
|
|
st = "FollowUp";
|
|
break;
|
|
|
|
case DELAY_REQ:
|
|
st = "DelayReq";
|
|
break;
|
|
|
|
case DELAY_RESP:
|
|
st = "DelayResp";
|
|
break;
|
|
|
|
case PDELAY_REQ:
|
|
st = "PDelayReq";
|
|
break;
|
|
|
|
case PDELAY_RESP:
|
|
st = "PDelayResp";
|
|
break;
|
|
|
|
case PDELAY_RESP_FOLLOW_UP:
|
|
st = "PDelayRespFollowUp";
|
|
break;
|
|
|
|
case MANAGEMENT:
|
|
st = "Management";
|
|
break;
|
|
|
|
default:
|
|
st = "Unk";
|
|
break;
|
|
}
|
|
|
|
DBG(" ==> %s received, sequence %d\n", st, ptpClock->msgTmpHeader.sequenceId);
|
|
#endif
|
|
|
|
|
|
/*
|
|
* on the table below, note that only the event messsages are passed the local time,
|
|
* (collected by us by loopback+kernel TS, and adjusted with UTC seconds
|
|
*
|
|
* (SYNC / DELAY_REQ / PDELAY_REQ / PDELAY_RESP)
|
|
*/
|
|
switch (ptpClock->msgTmpHeader.messageType)
|
|
{
|
|
case ANNOUNCE:
|
|
handleAnnounce(&ptpClock->msgTmpHeader,
|
|
length, isFromSelf, rtOpts, ptpClock);
|
|
|
|
break;
|
|
|
|
case SYNC:
|
|
handleSync(&ptpClock->msgTmpHeader,
|
|
length, timeStamp, isFromSelf, rtOpts, ptpClock);
|
|
break;
|
|
|
|
case FOLLOW_UP:
|
|
handleFollowUp(&ptpClock->msgTmpHeader,
|
|
length, isFromSelf, rtOpts, ptpClock);
|
|
break;
|
|
|
|
case DELAY_REQ:
|
|
handleDelayReq(&ptpClock->msgTmpHeader,
|
|
length, timeStamp, isFromSelf, rtOpts, ptpClock);
|
|
break;
|
|
|
|
case PDELAY_REQ:
|
|
handlePDelayReq(&ptpClock->msgTmpHeader,
|
|
length, timeStamp, isFromSelf, rtOpts, ptpClock);
|
|
break;
|
|
|
|
case DELAY_RESP:
|
|
handleDelayResp(&ptpClock->msgTmpHeader,
|
|
length, rtOpts, ptpClock);
|
|
break;
|
|
|
|
case PDELAY_RESP:
|
|
handlePDelayResp(&ptpClock->msgTmpHeader,
|
|
timeStamp, length, isFromSelf, rtOpts, ptpClock);
|
|
break;
|
|
|
|
case PDELAY_RESP_FOLLOW_UP:
|
|
handlePDelayRespFollowUp(&ptpClock->msgTmpHeader,
|
|
length, isFromSelf, rtOpts, ptpClock);
|
|
break;
|
|
|
|
case MANAGEMENT:
|
|
handleManagement(&ptpClock->msgTmpHeader,
|
|
isFromSelf, rtOpts, ptpClock);
|
|
break;
|
|
|
|
case SIGNALING:
|
|
handleSignaling(ptpClock);
|
|
break;
|
|
|
|
default:
|
|
DBG("handle: unrecognized message\n");
|
|
ptpClock->counters.discardedMessages++;
|
|
ptpClock->counters.unknownMessages++;
|
|
break;
|
|
}
|
|
|
|
if (rtOpts->displayPackets)
|
|
{
|
|
msgDump(ptpClock);
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
/* check and handle received messages */
|
|
void
|
|
handle(RunTimeOpts *rtOpts, PtpClock *ptpClock)
|
|
{
|
|
int ret;
|
|
ssize_t length = -1;
|
|
|
|
TimeInternal timeStamp = { 0, 0 };
|
|
fd_set readfds;
|
|
|
|
|
|
FD_ZERO(&readfds);
|
|
|
|
if (!ptpClock->message_activity)
|
|
{
|
|
|
|
ret = netSelect(rtOpts, NULL, &ptpClock->netPath, &readfds);
|
|
|
|
if (ret < 0)
|
|
{
|
|
PERROR("failed to poll sockets");
|
|
ptpClock->counters.messageRecvErrors++;
|
|
toState(PTP_FAULTY, rtOpts, ptpClock);
|
|
return;
|
|
}
|
|
else if (!ret)
|
|
{
|
|
/* DBGV("handle: nothing\n"); */
|
|
return;
|
|
}
|
|
|
|
/* else length > 0 */
|
|
}
|
|
|
|
DBGV("handle: something\n");
|
|
|
|
if (FD_ISSET(ptpClock->netPath.eventSock, &readfds))
|
|
{
|
|
length = netRecvEvent(ptpClock->msgIbuf, &timeStamp,
|
|
&ptpClock->netPath, 0);
|
|
|
|
if (length < 0)
|
|
{
|
|
PERROR("failed to receive on the event socket");
|
|
toState(PTP_FAULTY, rtOpts, ptpClock);
|
|
ptpClock->counters.messageRecvErrors++;
|
|
return;
|
|
}
|
|
|
|
processMessage(rtOpts, ptpClock, &timeStamp, length);
|
|
}
|
|
|
|
if ((ptpClock->netPath.generalSock >= 0) && FD_ISSET(ptpClock->netPath.generalSock, &readfds))
|
|
{
|
|
length = netRecvGeneral(ptpClock->msgIbuf, &ptpClock->netPath);
|
|
|
|
if (length < 0)
|
|
{
|
|
PERROR("failed to receive on the general socket");
|
|
toState(PTP_FAULTY, rtOpts, ptpClock);
|
|
ptpClock->counters.messageRecvErrors++;
|
|
return;
|
|
}
|
|
|
|
processMessage(rtOpts, ptpClock, &timeStamp, length);
|
|
}
|
|
|
|
}
|
|
|
|
/*spec 9.5.3*/
|
|
static void
|
|
handleAnnounce(MsgHeader *header, ssize_t length,
|
|
Boolean isFromSelf, RunTimeOpts *rtOpts, PtpClock *ptpClock)
|
|
{
|
|
|
|
DBGV("HandleAnnounce : Announce message received : \n");
|
|
|
|
if (length < ANNOUNCE_LENGTH)
|
|
{
|
|
DBG("Error: Announce message too short\n");
|
|
ptpClock->counters.messageFormatErrors++;
|
|
return;
|
|
}
|
|
|
|
/* DBGV(" >> HandleAnnounce : %d \n", ptpClock->portState); */
|
|
switch (ptpClock->portState)
|
|
{
|
|
case PTP_INITIALIZING:
|
|
case PTP_FAULTY:
|
|
case PTP_DISABLED:
|
|
DBG("Handleannounce : disregard\n");
|
|
ptpClock->counters.discardedMessages++;
|
|
return;
|
|
|
|
case PTP_UNCALIBRATED:
|
|
case PTP_SLAVE:
|
|
if (isFromSelf)
|
|
{
|
|
DBGV("HandleAnnounce : Ignore message from self \n");
|
|
return;
|
|
}
|
|
|
|
if (rtOpts->requireUtcValid && !IS_SET(header->flagField1, UTCV))
|
|
{
|
|
ptpClock->counters.ignoredAnnounce++;
|
|
return;
|
|
}
|
|
|
|
/*
|
|
* Valid announce message is received : BMC algorithm
|
|
* will be executed
|
|
*/
|
|
ptpClock->counters.announceMessagesReceived++;
|
|
ptpClock->record_update = TRUE;
|
|
|
|
switch (isFromCurrentParent(ptpClock, header))
|
|
{
|
|
case TRUE:
|
|
msgUnpackAnnounce(ptpClock->msgIbuf,
|
|
&ptpClock->msgTmp.announce);
|
|
|
|
/* update datasets (file bmc.c) */
|
|
s1(header, &ptpClock->msgTmp.announce, ptpClock, rtOpts);
|
|
|
|
/* update current master in the fmr as well */
|
|
memcpy(&ptpClock->foreign[ptpClock->foreign_record_best].header,
|
|
header, sizeof(MsgHeader));
|
|
memcpy(&ptpClock->foreign[ptpClock->foreign_record_best].announce,
|
|
&ptpClock->msgTmp.announce, sizeof(MsgAnnounce));
|
|
|
|
if (ptpClock->leapSecondInProgress)
|
|
{
|
|
/*
|
|
* if leap second period is over
|
|
* (pending == FALSE, inProgress ==
|
|
* TRUE), unpause offset calculation
|
|
* (received first announce after leap
|
|
* second)
|
|
*/
|
|
if (!ptpClock->leapSecondPending)
|
|
{
|
|
WARNING("Leap second event over - "
|
|
"resuming clock and offset updates\n");
|
|
ptpClock->leapSecondInProgress = FALSE;
|
|
ptpClock->timePropertiesDS.leap59 = FALSE;
|
|
ptpClock->timePropertiesDS.leap61 = FALSE;
|
|
#ifdef HAVE_SYS_TIMEX_H
|
|
unsetTimexFlags(STA_INS | STA_DEL, TRUE);
|
|
#endif /* HAVE_SYS_TIMEX_H */
|
|
}
|
|
}
|
|
|
|
DBG2("___ Announce: received Announce from current Master, so reset the Announce timer\n");
|
|
/*Reset Timer handling Announce receipt timeout*/
|
|
timerStart(ANNOUNCE_RECEIPT_TIMER,
|
|
(float)((ptpClock->announceReceiptTimeout) *
|
|
(pow(2, ptpClock->logAnnounceInterval))),
|
|
ptpClock->itimer);
|
|
|
|
|
|
if (rtOpts->announceTimeoutGracePeriod &&
|
|
ptpClock->announceTimeouts > 0)
|
|
{
|
|
NOTICE("Received Announce message from master - cancelling timeout\n");
|
|
ptpClock->announceTimeouts = 0;
|
|
|
|
}
|
|
|
|
/* remember IP address of our master for hybrid mode
|
|
todo: add this to bmc(), to cover the very first packet */
|
|
ptpClock->masterAddr = ptpClock->netPath.lastRecvAddr;
|
|
break;
|
|
|
|
case FALSE:
|
|
/* addForeign takes care of AnnounceUnpacking */
|
|
|
|
/* the actual decision to change masters is
|
|
* only done in doState() / record_update ==
|
|
* TRUE / bmc()
|
|
*/
|
|
|
|
/*
|
|
* wowczarek: do not restart timer here:
|
|
* the slave will sit idle if current parent
|
|
* is not announcing, but another GM is
|
|
*/
|
|
addForeign(ptpClock->msgIbuf, header, ptpClock);
|
|
break;
|
|
|
|
default:
|
|
DBG("HandleAnnounce : (isFromCurrentParent)"
|
|
"strange value ! \n");
|
|
return;
|
|
|
|
} /* switch on (isFromCurrentParrent) */
|
|
|
|
break;
|
|
|
|
/*
|
|
* Passive case: previously, this was handled in the default, just like the master case.
|
|
* This the announce would call addForeign(), but NOT reset the timer, so after 12s it would expire and we would come alive periodically
|
|
*
|
|
* This code is now merged with the slave case to reset the timer, and call addForeign() if it's a third master
|
|
*
|
|
*/
|
|
case PTP_PASSIVE:
|
|
if (isFromSelf)
|
|
{
|
|
DBGV("HandleAnnounce : Ignore message from self \n");
|
|
return;
|
|
}
|
|
|
|
if (rtOpts->requireUtcValid && !IS_SET(header->flagField1, UTCV))
|
|
{
|
|
ptpClock->counters.ignoredAnnounce++;
|
|
return;
|
|
}
|
|
|
|
/*
|
|
* Valid announce message is received : BMC algorithm
|
|
* will be executed
|
|
*/
|
|
ptpClock->counters.announceMessagesReceived++;
|
|
ptpClock->record_update = TRUE;
|
|
|
|
if (isFromCurrentParent(ptpClock, header))
|
|
{
|
|
msgUnpackAnnounce(ptpClock->msgIbuf,
|
|
&ptpClock->msgTmp.announce);
|
|
|
|
/* TODO: not in spec
|
|
* datasets should not be updated by another master
|
|
* this is the reason why we are PASSIVE and not SLAVE
|
|
* this should be p1(ptpClock, rtOpts);
|
|
*/
|
|
/* update datasets (file bmc.c) */
|
|
s1(header, &ptpClock->msgTmp.announce, ptpClock, rtOpts);
|
|
|
|
ptpClock->masterAddr = ptpClock->netPath.lastRecvAddr;
|
|
|
|
DBG("___ Announce: received Announce from current Master, so reset the Announce timer\n\n");
|
|
/*Reset Timer handling Announce receipt timeout*/
|
|
timerStart(ANNOUNCE_RECEIPT_TIMER,
|
|
(float)((ptpClock->announceReceiptTimeout) *
|
|
(pow(2, ptpClock->logAnnounceInterval))),
|
|
ptpClock->itimer);
|
|
}
|
|
else
|
|
{
|
|
/*addForeign takes care of AnnounceUnpacking*/
|
|
/* the actual decision to change masters is only done in doState() / record_update == TRUE / bmc() */
|
|
/* the original code always called: addforeign(new master) + timerstart(announce) */
|
|
|
|
DBG("___ Announce: received Announce from another master, will add to the list, as it might be better\n\n");
|
|
DBGV("this is to be decided immediatly by bmc())\n\n");
|
|
addForeign(ptpClock->msgIbuf, header, ptpClock);
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
case PTP_MASTER:
|
|
case PTP_LISTENING: /* listening mode still causes timeouts in order to send IGMP refreshes */
|
|
default :
|
|
if (isFromSelf)
|
|
{
|
|
DBGV("HandleAnnounce : Ignore message from self \n");
|
|
return;
|
|
}
|
|
|
|
if (rtOpts->requireUtcValid && !IS_SET(header->flagField1, UTCV))
|
|
{
|
|
ptpClock->counters.ignoredAnnounce++;
|
|
return;
|
|
}
|
|
|
|
ptpClock->counters.announceMessagesReceived++;
|
|
DBGV("Announce message from another foreign master\n");
|
|
|
|
addForeign(ptpClock->msgIbuf, header, ptpClock);
|
|
ptpClock->record_update = TRUE; /* run BMC() as soon as possible */
|
|
break;
|
|
|
|
} /* switch on (port_state) */
|
|
|
|
}
|
|
|
|
|
|
static void
|
|
handleSync(const MsgHeader *header, ssize_t length,
|
|
TimeInternal *tint, Boolean isFromSelf,
|
|
RunTimeOpts *rtOpts, PtpClock *ptpClock)
|
|
{
|
|
TimeInternal OriginTimestamp;
|
|
TimeInternal correctionField;
|
|
|
|
DBGV("Sync message received : \n");
|
|
|
|
if (length < SYNC_LENGTH)
|
|
{
|
|
DBG("Error: Sync message too short\n");
|
|
ptpClock->counters.messageFormatErrors++;
|
|
return;
|
|
}
|
|
|
|
switch (ptpClock->portState)
|
|
{
|
|
case PTP_INITIALIZING:
|
|
case PTP_FAULTY:
|
|
case PTP_DISABLED:
|
|
DBGV("HandleSync : disregard\n");
|
|
ptpClock->counters.discardedMessages++;
|
|
return;
|
|
|
|
case PTP_UNCALIBRATED:
|
|
case PTP_SLAVE:
|
|
if (isFromSelf)
|
|
{
|
|
DBGV("HandleSync: Ignore message from self \n");
|
|
return;
|
|
}
|
|
|
|
if (isFromCurrentParent(ptpClock, header))
|
|
{
|
|
ptpClock->counters.syncMessagesReceived++;
|
|
|
|
/* We only start our own delayReq timer after receiving the first sync */
|
|
if (ptpClock->waiting_for_first_sync)
|
|
{
|
|
ptpClock->waiting_for_first_sync = FALSE;
|
|
|
|
if (rtOpts->debug_level > PTP_DEBUG_LEVEL_1)
|
|
{
|
|
NOTICE("Received first Sync from Master\n");
|
|
}
|
|
|
|
if (ptpClock->delayMechanism == E2E)
|
|
timerStart(DELAYREQ_INTERVAL_TIMER,
|
|
(float)pow(2, ptpClock->logMinDelayReqInterval),
|
|
ptpClock->itimer);
|
|
else if (ptpClock->delayMechanism == P2P)
|
|
timerStart(PDELAYREQ_INTERVAL_TIMER,
|
|
(float)pow(2, ptpClock->logMinPdelayReqInterval),
|
|
ptpClock->itimer);
|
|
}
|
|
else if ( rtOpts->syncSequenceChecking &&
|
|
( (((UInteger16)(ptpClock->recvSyncSequenceId + 32768)) >
|
|
(header->sequenceId + 32767)) ||
|
|
(((UInteger16)(ptpClock->recvSyncSequenceId + 1)) >
|
|
(header->sequenceId)) ) )
|
|
{
|
|
DBG("HandleSync : sequence mismatch - "
|
|
"received: %d, expected %d or greater\n",
|
|
header->sequenceId,
|
|
(UInteger16)(ptpClock->recvSyncSequenceId + 1)
|
|
);
|
|
ptpClock->counters.discardedMessages++;
|
|
ptpClock->counters.sequenceMismatchErrors++;
|
|
break;
|
|
}
|
|
|
|
ptpClock->logSyncInterval = header->logMessageInterval;
|
|
|
|
ptpClock->sync_receive_time.seconds = tint->seconds;
|
|
ptpClock->sync_receive_time.nanoseconds = tint->nanoseconds;
|
|
|
|
if ((header->flagField0 & PTP_TWO_STEP) == PTP_TWO_STEP)
|
|
{
|
|
DBG2("HandleSync: waiting for follow-up \n");
|
|
ptpClock->twoStepFlag = TRUE;
|
|
|
|
if (ptpClock->waitingForFollow)
|
|
{
|
|
ptpClock->followUpGap++;
|
|
|
|
if (ptpClock->followUpGap < MAX_FOLLOWUP_GAP)
|
|
{
|
|
DBG("Received Sync while waiting for follow-up - "
|
|
"will wait for another %d messages\n",
|
|
MAX_FOLLOWUP_GAP - ptpClock->followUpGap);
|
|
break;
|
|
}
|
|
|
|
DBG("No follow-up for %d sync - waiting for new follow-up\n",
|
|
ptpClock->followUpGap);
|
|
}
|
|
|
|
ptpClock->waitingForFollow = TRUE;
|
|
/*Save correctionField of Sync message*/
|
|
integer64_to_internalTime(
|
|
header->correctionField,
|
|
&correctionField);
|
|
ptpClock->lastSyncCorrectionField.seconds =
|
|
correctionField.seconds;
|
|
ptpClock->lastSyncCorrectionField.nanoseconds =
|
|
correctionField.nanoseconds;
|
|
ptpClock->recvSyncSequenceId =
|
|
header->sequenceId;
|
|
break;
|
|
}
|
|
else
|
|
{
|
|
ptpClock->recvSyncSequenceId =
|
|
header->sequenceId;
|
|
msgUnpackSync(ptpClock->msgIbuf,
|
|
&ptpClock->msgTmp.sync);
|
|
integer64_to_internalTime(
|
|
ptpClock->msgTmpHeader.correctionField,
|
|
&correctionField);
|
|
DBG("PTP_one_STEP\n");
|
|
timeInternal_display(&correctionField);
|
|
ptpClock->waitingForFollow = FALSE;
|
|
toInternalTime(&OriginTimestamp,
|
|
&ptpClock->msgTmp.sync.originTimestamp);
|
|
updateOffset(&OriginTimestamp,
|
|
&ptpClock->sync_receive_time,
|
|
&ptpClock->ofm_filt, rtOpts,
|
|
ptpClock, &correctionField);
|
|
updateClock(rtOpts, ptpClock);
|
|
ptpClock->twoStepFlag = FALSE;
|
|
break;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
DBG("HandleSync: Sync message received from "
|
|
"another Master not our own \n");
|
|
ptpClock->counters.discardedMessages++;
|
|
}
|
|
|
|
break;
|
|
|
|
case PTP_MASTER:
|
|
default :
|
|
if (!isFromSelf)
|
|
{
|
|
DBGV("HandleSync: Sync message received from "
|
|
"another Master \n");
|
|
/* we are the master, but another is sending */
|
|
ptpClock->counters.discardedMessages++;
|
|
break;
|
|
}
|
|
|
|
if (ptpClock->twoStepFlag)
|
|
{
|
|
DBGV("HandleSync: going to send followup message\n");
|
|
processSyncFromSelf(tint, rtOpts, ptpClock, header->sequenceId);
|
|
break;
|
|
}
|
|
else
|
|
{
|
|
DBGV("HandleSync: Sync message received from self\n");
|
|
}
|
|
}
|
|
}
|
|
|
|
static void
|
|
processSyncFromSelf(const TimeInternal *tint, RunTimeOpts *rtOpts, PtpClock *ptpClock, const UInteger16 sequenceId)
|
|
{
|
|
TimeInternal timestamp;
|
|
/*Add latency*/
|
|
addTime(×tamp, tint, &rtOpts->outboundLatency);
|
|
/* Issue follow-up CORRESPONDING TO THIS SYNC */
|
|
issueFollowup(×tamp, rtOpts, ptpClock, sequenceId);
|
|
}
|
|
|
|
|
|
static void
|
|
handleFollowUp(const MsgHeader *header, ssize_t length,
|
|
Boolean isFromSelf, RunTimeOpts *rtOpts, PtpClock *ptpClock)
|
|
{
|
|
DBGV("Handlefollowup : Follow up message received \n");
|
|
|
|
TimeInternal preciseOriginTimestamp;
|
|
TimeInternal correctionField;
|
|
|
|
if (length < FOLLOW_UP_LENGTH)
|
|
{
|
|
DBG("Error: Follow up message too short\n");
|
|
ptpClock->counters.messageFormatErrors++;
|
|
return;
|
|
}
|
|
|
|
if (isFromSelf)
|
|
{
|
|
DBGV("Handlefollowup : Ignore message from self \n");
|
|
return;
|
|
}
|
|
|
|
switch (ptpClock->portState)
|
|
{
|
|
case PTP_INITIALIZING:
|
|
case PTP_FAULTY:
|
|
case PTP_DISABLED:
|
|
case PTP_LISTENING:
|
|
DBGV("Handfollowup : disregard\n");
|
|
ptpClock->counters.discardedMessages++;
|
|
return;
|
|
|
|
case PTP_UNCALIBRATED:
|
|
case PTP_SLAVE:
|
|
if (isFromCurrentParent(ptpClock, header))
|
|
{
|
|
ptpClock->counters.followUpMessagesReceived++;
|
|
ptpClock->logSyncInterval = header->logMessageInterval;
|
|
|
|
if (ptpClock->waitingForFollow)
|
|
{
|
|
if (ptpClock->recvSyncSequenceId ==
|
|
header->sequenceId)
|
|
{
|
|
ptpClock->followUpGap = 0;
|
|
msgUnpackFollowUp(ptpClock->msgIbuf,
|
|
&ptpClock->msgTmp.follow);
|
|
ptpClock->waitingForFollow = FALSE;
|
|
toInternalTime(&preciseOriginTimestamp,
|
|
&ptpClock->msgTmp.follow.preciseOriginTimestamp);
|
|
integer64_to_internalTime(ptpClock->msgTmpHeader.correctionField,
|
|
&correctionField);
|
|
addTime(&correctionField, &correctionField,
|
|
&ptpClock->lastSyncCorrectionField);
|
|
|
|
if (rtOpts->debug_level > PTP_DEBUG_LEVEL_3)
|
|
{
|
|
/*NOTICE("sync_snd %lld, %09d sync_receive %d, %09lld\n",
|
|
preciseOriginTimestamp.seconds,
|
|
preciseOriginTimestamp.nanoseconds,
|
|
ptpClock->sync_receive_time.seconds,
|
|
ptpClock->sync_receive_time.nanoseconds);*/
|
|
}
|
|
|
|
/*
|
|
send_time = preciseOriginTimestamp (received inside followup)
|
|
recv_time = sync_receive_time (received as CMSG in handleEvent)
|
|
*/
|
|
updateOffset(&preciseOriginTimestamp,
|
|
&ptpClock->sync_receive_time, &ptpClock->ofm_filt,
|
|
rtOpts, ptpClock,
|
|
&correctionField);
|
|
updateClock(rtOpts, ptpClock);
|
|
break;
|
|
}
|
|
else
|
|
{
|
|
DBG("HandleFollowUp : sequence mismatch - "
|
|
"last Sync: %d, this FollowUp: %d\n",
|
|
ptpClock->recvSyncSequenceId,
|
|
header->sequenceId);
|
|
ptpClock->counters.sequenceMismatchErrors++;
|
|
ptpClock->counters.discardedMessages++;
|
|
}
|
|
|
|
}
|
|
else
|
|
{
|
|
DBG2("Ignored followup, Slave was not waiting a follow up "
|
|
"message \n");
|
|
ptpClock->counters.discardedMessages++;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
DBG2("Ignored, Follow up message is not from current parent \n");
|
|
ptpClock->counters.discardedMessages++;
|
|
}
|
|
|
|
case PTP_MASTER:
|
|
case PTP_PASSIVE:
|
|
if (isFromCurrentParent(ptpClock, header))
|
|
{
|
|
DBGV("Ignored, Follow up message received from current master \n");
|
|
}
|
|
else
|
|
{
|
|
/* follow-ups and syncs are expected to be seen from parent, but not from others */
|
|
DBGV("Follow up message received from foreign master!\n");
|
|
ptpClock->counters.discardedMessages++;
|
|
}
|
|
|
|
break;
|
|
|
|
default:
|
|
DBG("do unrecognized state1\n");
|
|
ptpClock->counters.discardedMessages++;
|
|
break;
|
|
} /* Switch on (port_state) */
|
|
|
|
}
|
|
|
|
void
|
|
handleDelayReq(const MsgHeader *header, ssize_t length,
|
|
const TimeInternal *tint, Boolean isFromSelf,
|
|
RunTimeOpts *rtOpts, PtpClock *ptpClock)
|
|
{
|
|
|
|
if (ptpClock->delayMechanism == E2E)
|
|
{
|
|
DBGV("delayReq message received : \n");
|
|
|
|
if (length < DELAY_REQ_LENGTH)
|
|
{
|
|
DBG("Error: DelayReq message too short\n");
|
|
ptpClock->counters.messageFormatErrors++;
|
|
return;
|
|
}
|
|
|
|
switch (ptpClock->portState)
|
|
{
|
|
case PTP_INITIALIZING:
|
|
case PTP_FAULTY:
|
|
case PTP_DISABLED:
|
|
case PTP_UNCALIBRATED:
|
|
case PTP_LISTENING:
|
|
case PTP_PASSIVE:
|
|
DBGV("HandledelayReq : disregard\n");
|
|
ptpClock->counters.discardedMessages++;
|
|
return;
|
|
|
|
case PTP_SLAVE:
|
|
if (isFromSelf)
|
|
{
|
|
DBG("==> Handle DelayReq (%d)\n",
|
|
header->sequenceId);
|
|
|
|
if ( ((UInteger16)(header->sequenceId + 1)) !=
|
|
ptpClock->sentDelayReqSequenceId)
|
|
{
|
|
DBG("HandledelayReq : sequence mismatch - "
|
|
"last DelayReq sent: %d, received: %d\n",
|
|
ptpClock->sentDelayReqSequenceId,
|
|
header->sequenceId
|
|
);
|
|
ptpClock->counters.discardedMessages++;
|
|
ptpClock->counters.sequenceMismatchErrors++;
|
|
break;
|
|
}
|
|
|
|
/*
|
|
* Get sending timestamp from IP stack
|
|
* with SO_TIMESTAMP
|
|
*/
|
|
|
|
/*
|
|
* Make sure we process the REQ
|
|
* _before_ the RESP. While we could
|
|
* do this by any order, (because
|
|
* it's implicitly indexed by
|
|
* (ptpClock->sentDelayReqSequenceId
|
|
* - 1), this is now made explicit
|
|
*/
|
|
|
|
processDelayReqFromSelf(tint, rtOpts, ptpClock);
|
|
|
|
break;
|
|
}
|
|
else
|
|
{
|
|
DBG2("HandledelayReq : disregard delayreq from other client\n");
|
|
ptpClock->counters.discardedMessages++;
|
|
}
|
|
|
|
break;
|
|
|
|
case PTP_MASTER:
|
|
msgUnpackHeader(ptpClock->msgIbuf,
|
|
&ptpClock->delayReqHeader);
|
|
ptpClock->counters.delayReqMessagesReceived++;
|
|
|
|
/* remember IP address of this client for hybrid mode */
|
|
ptpClock->LastSlaveAddr = ptpClock->netPath.lastRecvAddr;
|
|
|
|
issueDelayResp(tint, &ptpClock->delayReqHeader,
|
|
rtOpts, ptpClock);
|
|
break;
|
|
|
|
default:
|
|
DBG("do unrecognized state2\n");
|
|
ptpClock->counters.discardedMessages++;
|
|
break;
|
|
}
|
|
}
|
|
else if (ptpClock->delayMechanism == P2P) /* (Peer to Peer mode) */
|
|
{
|
|
DBG("Delay messages are ignored in Peer to Peer mode\n");
|
|
ptpClock->counters.discardedMessages++;
|
|
ptpClock->counters.delayModeMismatchErrors++;
|
|
/* no delay mechanism */
|
|
}
|
|
else
|
|
{
|
|
DBG("DelayReq ignored - we are in DELAY_DISABLED mode");
|
|
ptpClock->counters.discardedMessages++;
|
|
}
|
|
}
|
|
|
|
|
|
static void
|
|
processDelayReqFromSelf(const TimeInternal *tint, RunTimeOpts *rtOpts, PtpClock *ptpClock)
|
|
{
|
|
ptpClock->waitingForDelayResp = TRUE;
|
|
|
|
ptpClock->delay_req_send_time.seconds = tint->seconds;
|
|
ptpClock->delay_req_send_time.nanoseconds = tint->nanoseconds;
|
|
|
|
/*Add latency*/
|
|
addTime(&ptpClock->delay_req_send_time,
|
|
&ptpClock->delay_req_send_time,
|
|
&rtOpts->outboundLatency);
|
|
|
|
DBGV("processDelayReqFromSelf: %s %d\n",
|
|
dump_TimeInternal(&ptpClock->delay_req_send_time),
|
|
rtOpts->outboundLatency);
|
|
|
|
}
|
|
|
|
static void
|
|
handleDelayResp(const MsgHeader *header, ssize_t length,
|
|
RunTimeOpts *rtOpts, PtpClock *ptpClock)
|
|
{
|
|
if (ptpClock->delayMechanism == E2E)
|
|
{
|
|
TimeInternal requestReceiptTimestamp;
|
|
TimeInternal correctionField;
|
|
|
|
DBGV("delayResp message received : \n");
|
|
|
|
if (length < DELAY_RESP_LENGTH)
|
|
{
|
|
DBG("Error: DelayResp message too short\n");
|
|
ptpClock->counters.messageFormatErrors++;
|
|
return;
|
|
}
|
|
|
|
switch (ptpClock->portState)
|
|
{
|
|
case PTP_INITIALIZING:
|
|
case PTP_FAULTY:
|
|
case PTP_DISABLED:
|
|
case PTP_UNCALIBRATED:
|
|
case PTP_LISTENING:
|
|
DBGV("HandledelayResp : disregard\n");
|
|
ptpClock->counters.discardedMessages++;
|
|
return;
|
|
|
|
case PTP_SLAVE:
|
|
msgUnpackDelayResp(ptpClock->msgIbuf,
|
|
&ptpClock->msgTmp.resp);
|
|
|
|
if ((memcmp(ptpClock->portIdentity.clockIdentity,
|
|
ptpClock->msgTmp.resp.requestingPortIdentity.clockIdentity,
|
|
CLOCK_IDENTITY_LENGTH) == 0) &&
|
|
(ptpClock->portIdentity.portNumber ==
|
|
ptpClock->msgTmp.resp.requestingPortIdentity.portNumber)
|
|
&& isFromCurrentParent(ptpClock, header))
|
|
{
|
|
DBG("==> Handle DelayResp (%d)\n",
|
|
header->sequenceId);
|
|
|
|
if (!ptpClock->waitingForDelayResp)
|
|
{
|
|
DBGV("Ignored DelayResp - wasn't waiting for one\n");
|
|
ptpClock->counters.discardedMessages++;
|
|
break;
|
|
}
|
|
|
|
if (ptpClock->sentDelayReqSequenceId !=
|
|
((UInteger16)(header->sequenceId + 1)))
|
|
{
|
|
DBG("HandledelayResp : sequence mismatch - "
|
|
"last DelayReq sent: %d, delayResp received: %d\n",
|
|
ptpClock->sentDelayReqSequenceId,
|
|
header->sequenceId
|
|
);
|
|
ptpClock->counters.discardedMessages++;
|
|
ptpClock->counters.sequenceMismatchErrors++;
|
|
break;
|
|
}
|
|
|
|
ptpClock->counters.delayRespMessagesReceived++;
|
|
ptpClock->waitingForDelayResp = FALSE;
|
|
|
|
toInternalTime(&requestReceiptTimestamp,
|
|
&ptpClock->msgTmp.resp.receiveTimestamp);
|
|
ptpClock->delay_req_receive_time.seconds =
|
|
requestReceiptTimestamp.seconds;
|
|
ptpClock->delay_req_receive_time.nanoseconds =
|
|
requestReceiptTimestamp.nanoseconds;
|
|
|
|
integer64_to_internalTime(
|
|
header->correctionField,
|
|
&correctionField);
|
|
/*
|
|
send_time = delay_req_send_time (received as CMSG in handleEvent)
|
|
recv_time = requestReceiptTimestamp (received inside delayResp)
|
|
*/
|
|
|
|
updateDelay(&ptpClock->owd_filt,
|
|
rtOpts, ptpClock, &correctionField);
|
|
|
|
if (ptpClock->waiting_for_first_delayresp)
|
|
{
|
|
if (rtOpts->debug_level > PTP_DEBUG_LEVEL_1)
|
|
{
|
|
NOTICE("Received first Delay Response from Master\n");
|
|
}
|
|
}
|
|
|
|
if (rtOpts->ignore_delayreq_interval_master == 0)
|
|
{
|
|
DBGV("current delay_req: %d new delay req: %d \n",
|
|
ptpClock->logMinDelayReqInterval,
|
|
header->logMessageInterval);
|
|
|
|
if (ptpClock->logMinDelayReqInterval != header->logMessageInterval)
|
|
{
|
|
|
|
if (header->logMessageInterval == UNICAST_MESSAGEINTERVAL &&
|
|
rtOpts->autoDelayReqInterval)
|
|
{
|
|
if (ptpClock->waiting_for_first_delayresp)
|
|
{
|
|
NOTICE("Received %d Delay Interval from master (unicast) - overriding with %d\n",
|
|
header->logMessageInterval, rtOpts->subsequent_delayreq);
|
|
}
|
|
|
|
ptpClock->logMinDelayReqInterval = (Integer8)rtOpts->subsequent_delayreq;
|
|
|
|
}
|
|
else
|
|
{
|
|
/* Accept new DelayReq value from the Master */
|
|
|
|
NOTICE("Received new Delay Request interval %d from Master (was: %d)\n",
|
|
header->logMessageInterval, ptpClock->logMinDelayReqInterval );
|
|
|
|
/* collect new value indicated from the Master */
|
|
ptpClock->logMinDelayReqInterval = header->logMessageInterval;
|
|
}
|
|
|
|
/* FIXME: the actual rearming of this timer with the new value only happens later in doState()/issueDelayReq() */
|
|
}
|
|
}
|
|
else
|
|
{
|
|
|
|
if (ptpClock->logMinDelayReqInterval != rtOpts->subsequent_delayreq)
|
|
{
|
|
INFO("New Delay Request interval applied: %d (was: %d)\n",
|
|
rtOpts->subsequent_delayreq, ptpClock->logMinDelayReqInterval);
|
|
}
|
|
|
|
ptpClock->logMinDelayReqInterval = (Integer8)rtOpts->subsequent_delayreq;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
DBG("HandledelayResp : delayResp doesn't match with the delayReq. \n");
|
|
ptpClock->counters.discardedMessages++;
|
|
break;
|
|
}
|
|
|
|
ptpClock->waiting_for_first_delayresp = FALSE;
|
|
}
|
|
}
|
|
else if (ptpClock->delayMechanism == P2P) /* (Peer to Peer mode) */
|
|
{
|
|
DBG("Delay messages are disregarded in Peer to Peer mode \n");
|
|
ptpClock->counters.discardedMessages++;
|
|
ptpClock->counters.delayModeMismatchErrors++;
|
|
/* no delay mechanism */
|
|
}
|
|
else
|
|
{
|
|
DBG("DelayResp ignored - we are in DELAY_DISABLED mode");
|
|
ptpClock->counters.discardedMessages++;
|
|
}
|
|
|
|
}
|
|
|
|
|
|
static void
|
|
handlePDelayReq(MsgHeader *header, ssize_t length,
|
|
const TimeInternal *tint, Boolean isFromSelf,
|
|
RunTimeOpts *rtOpts, PtpClock *ptpClock)
|
|
{
|
|
if (ptpClock->delayMechanism == P2P)
|
|
{
|
|
DBGV("PdelayReq message received : \n");
|
|
|
|
if (length < PDELAY_REQ_LENGTH)
|
|
{
|
|
DBG("Error: PDelayReq message too short\n");
|
|
ptpClock->counters.messageFormatErrors++;
|
|
return;
|
|
}
|
|
|
|
switch (ptpClock->portState )
|
|
{
|
|
case PTP_INITIALIZING:
|
|
case PTP_FAULTY:
|
|
case PTP_DISABLED:
|
|
case PTP_UNCALIBRATED:
|
|
DBGV("HandlePdelayReq : disregard\n");
|
|
ptpClock->counters.discardedMessages++;
|
|
return;
|
|
|
|
case PTP_LISTENING:
|
|
if (rtOpts->ptp_profile == PTP_802_1AS_PROFILE)
|
|
{
|
|
if (isFromSelf)
|
|
{
|
|
processPDelayReqFromSelf(tint, rtOpts, ptpClock);
|
|
break;
|
|
}
|
|
else
|
|
{
|
|
ptpClock->counters.pdelayReqMessagesReceived++;
|
|
msgUnpackHeader(ptpClock->msgIbuf,
|
|
&ptpClock->PdelayReqHeader);
|
|
issuePDelayResp(tint, header, rtOpts,
|
|
ptpClock);
|
|
break;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
DBGV("HandlePdelayReq : disregard\n");
|
|
ptpClock->counters.discardedMessages++;
|
|
return;
|
|
}
|
|
|
|
case PTP_SLAVE:
|
|
case PTP_MASTER:
|
|
case PTP_PASSIVE:
|
|
|
|
if (isFromSelf)
|
|
{
|
|
processPDelayReqFromSelf(tint, rtOpts, ptpClock);
|
|
break;
|
|
}
|
|
else
|
|
{
|
|
ptpClock->counters.pdelayReqMessagesReceived++;
|
|
msgUnpackHeader(ptpClock->msgIbuf,
|
|
&ptpClock->PdelayReqHeader);
|
|
issuePDelayResp(tint, header, rtOpts,
|
|
ptpClock);
|
|
break;
|
|
}
|
|
|
|
default:
|
|
DBG("do unrecognized state3\n");
|
|
ptpClock->counters.discardedMessages++;
|
|
break;
|
|
}
|
|
}
|
|
else if (ptpClock->delayMechanism == E2E) /* (End to End mode..) */
|
|
{
|
|
DBG("Peer Delay messages are disregarded in End to End "
|
|
"mode \n");
|
|
ptpClock->counters.discardedMessages++;
|
|
ptpClock->counters.delayModeMismatchErrors++;
|
|
/* no delay mechanism */
|
|
}
|
|
else
|
|
{
|
|
DBG("PDelayReq ignored - we are in DELAY_DISABLED mode");
|
|
ptpClock->counters.discardedMessages++;
|
|
}
|
|
}
|
|
|
|
static void
|
|
processPDelayReqFromSelf(const TimeInternal *tint, RunTimeOpts *rtOpts, PtpClock *ptpClock)
|
|
{
|
|
/*
|
|
* Get sending timestamp from IP stack
|
|
* with SO_TIMESTAMP
|
|
*/
|
|
ptpClock->pdelay_req_send_time.seconds = tint->seconds;
|
|
ptpClock->pdelay_req_send_time.nanoseconds = tint->nanoseconds;
|
|
|
|
/*Add latency*/
|
|
addTime(&ptpClock->pdelay_req_send_time,
|
|
&ptpClock->pdelay_req_send_time,
|
|
&rtOpts->outboundLatency);
|
|
}
|
|
|
|
static void
|
|
handlePDelayResp(const MsgHeader *header, TimeInternal *tint,
|
|
ssize_t length, Boolean isFromSelf,
|
|
RunTimeOpts *rtOpts, PtpClock *ptpClock)
|
|
{
|
|
if (ptpClock->delayMechanism == P2P)
|
|
{
|
|
/* Boolean isFromCurrentParent = FALSE; NOTE: This is never used in this function */
|
|
TimeInternal requestReceiptTimestamp;
|
|
TimeInternal correctionField;
|
|
|
|
DBG("PdelayResp message received : \n");
|
|
|
|
if (length < PDELAY_RESP_LENGTH)
|
|
{
|
|
DBG("Error: PDelayResp message too short\n");
|
|
ptpClock->counters.messageFormatErrors++;
|
|
return;
|
|
}
|
|
|
|
switch (ptpClock->portState )
|
|
{
|
|
case PTP_INITIALIZING:
|
|
case PTP_FAULTY:
|
|
case PTP_DISABLED:
|
|
case PTP_UNCALIBRATED:
|
|
case PTP_LISTENING:
|
|
DBGV("HandlePdelayResp : disregard\n");
|
|
ptpClock->counters.discardedMessages++;
|
|
return;
|
|
|
|
case PTP_SLAVE:
|
|
case PTP_MASTER:
|
|
if (ptpClock->twoStepFlag && isFromSelf)
|
|
{
|
|
processPDelayRespFromSelf(tint, rtOpts, ptpClock, header->sequenceId);
|
|
break;
|
|
}
|
|
|
|
msgUnpackPDelayResp(ptpClock->msgIbuf,
|
|
&ptpClock->msgTmp.presp);
|
|
|
|
#if 0 /* NOTE: This is never used in this function. Should it? */
|
|
/*
|
|
* wowczarek: shouldn't, P2P vs. E2E. Parent can be somewhere else,
|
|
* peer can sit on an adjacent link - see next evaluation:
|
|
* requestingPortIdentity vs. parentPortIdentity
|
|
*/
|
|
isFromCurrentParent = !memcmp(ptpClock->parentPortIdentity.clockIdentity,
|
|
header->sourcePortIdentity.clockIdentity, CLOCK_IDENTITY_LENGTH) &&
|
|
(ptpClock->parentPortIdentity.portNumber ==
|
|
header->sourcePortIdentity.portNumber);
|
|
#endif
|
|
|
|
if (ptpClock->sentPDelayReqSequenceId !=
|
|
((UInteger16)(header->sequenceId + 1)))
|
|
{
|
|
DBG("PDelayResp: sequence mismatch - sent: %d, received: %d\n",
|
|
ptpClock->sentPDelayReqSequenceId,
|
|
header->sequenceId);
|
|
ptpClock->counters.discardedMessages++;
|
|
ptpClock->counters.sequenceMismatchErrors++;
|
|
break;
|
|
}
|
|
|
|
if ((!memcmp(ptpClock->portIdentity.clockIdentity, ptpClock->msgTmp.presp.requestingPortIdentity.clockIdentity, CLOCK_IDENTITY_LENGTH))
|
|
&& ( ptpClock->portIdentity.portNumber == ptpClock->msgTmp.presp.requestingPortIdentity.portNumber))
|
|
{
|
|
ptpClock->counters.pdelayRespMessagesReceived++;
|
|
|
|
/* Two Step Clock */
|
|
if ((header->flagField0 & PTP_TWO_STEP) == PTP_TWO_STEP)
|
|
{
|
|
/*Store t4 (Fig 35)*/
|
|
ptpClock->pdelay_resp_receive_time.seconds = tint->seconds;
|
|
ptpClock->pdelay_resp_receive_time.nanoseconds = tint->nanoseconds;
|
|
/*store t2 (Fig 35)*/
|
|
toInternalTime(&requestReceiptTimestamp,
|
|
&ptpClock->msgTmp.presp.requestReceiptTimestamp);
|
|
ptpClock->pdelay_req_receive_time.seconds = requestReceiptTimestamp.seconds;
|
|
ptpClock->pdelay_req_receive_time.nanoseconds = requestReceiptTimestamp.nanoseconds;
|
|
|
|
integer64_to_internalTime(header->correctionField, &correctionField);
|
|
ptpClock->lastPdelayRespCorrectionField.seconds = correctionField.seconds;
|
|
ptpClock->lastPdelayRespCorrectionField.nanoseconds = correctionField.nanoseconds;
|
|
}
|
|
else
|
|
{
|
|
/* One step Clock */
|
|
/*Store t4 (Fig 35)*/
|
|
ptpClock->pdelay_resp_receive_time.seconds = tint->seconds;
|
|
ptpClock->pdelay_resp_receive_time.nanoseconds = tint->nanoseconds;
|
|
|
|
integer64_to_internalTime(header->correctionField, &correctionField);
|
|
updatePeerDelay (&ptpClock->owd_filt, rtOpts, ptpClock, &correctionField, FALSE);
|
|
}
|
|
|
|
ptpClock->recvPDelayRespSequenceId = header->sequenceId;
|
|
break;
|
|
}
|
|
else
|
|
{
|
|
DBG("HandlePdelayResp : Pdelayresp doesn't "
|
|
"match with the PdelayReq. \n");
|
|
ptpClock->counters.discardedMessages++;
|
|
break;
|
|
}
|
|
|
|
break; /* XXX added by gnn for safety */
|
|
|
|
default:
|
|
DBG("do unrecognized state4\n");
|
|
ptpClock->counters.discardedMessages++;
|
|
break;
|
|
}
|
|
}
|
|
else if (ptpClock->delayMechanism == E2E) /* (End to End mode..) */
|
|
{
|
|
DBG("Peer Delay messages are disregarded in End to End "
|
|
"mode \n");
|
|
ptpClock->counters.discardedMessages++;
|
|
ptpClock->counters.delayModeMismatchErrors++;
|
|
/* no delay mechanism */
|
|
}
|
|
else
|
|
{
|
|
DBG("PDelayResp ignored - we are in DELAY_DISABLED mode");
|
|
ptpClock->counters.discardedMessages++;
|
|
}
|
|
}
|
|
|
|
static void
|
|
processPDelayRespFromSelf(const TimeInternal *tint, RunTimeOpts *rtOpts, PtpClock *ptpClock, UInteger16 sequenceId)
|
|
{
|
|
TimeInternal timestamp;
|
|
|
|
addTime(×tamp, tint, &rtOpts->outboundLatency);
|
|
|
|
issuePDelayRespFollowUp(×tamp, &ptpClock->PdelayReqHeader,
|
|
rtOpts, ptpClock, sequenceId);
|
|
}
|
|
|
|
static void
|
|
handlePDelayRespFollowUp(const MsgHeader *header, ssize_t length,
|
|
Boolean isFromSelf, RunTimeOpts *rtOpts,
|
|
PtpClock *ptpClock)
|
|
{
|
|
|
|
if (ptpClock->delayMechanism == P2P)
|
|
{
|
|
TimeInternal responseOriginTimestamp;
|
|
TimeInternal correctionField;
|
|
|
|
DBG("PdelayRespfollowup message received : \n");
|
|
|
|
if (length < PDELAY_RESP_FOLLOW_UP_LENGTH)
|
|
{
|
|
DBG("Error: PDelayRespfollowup message too short\n");
|
|
ptpClock->counters.messageFormatErrors++;
|
|
return;
|
|
}
|
|
|
|
switch (ptpClock->portState)
|
|
{
|
|
case PTP_INITIALIZING:
|
|
case PTP_FAULTY:
|
|
case PTP_DISABLED:
|
|
case PTP_UNCALIBRATED:
|
|
DBGV("HandlePdelayResp : disregard\n");
|
|
ptpClock->counters.discardedMessages++;
|
|
return;
|
|
|
|
case PTP_SLAVE:
|
|
case PTP_MASTER:
|
|
if (isFromSelf)
|
|
{
|
|
DBGV("HandlePdelayRespFollowUp : Ignore message from self \n");
|
|
return;
|
|
}
|
|
|
|
if (( ((UInteger16)(header->sequenceId + 1)) ==
|
|
ptpClock->sentPDelayReqSequenceId) && (header->sequenceId == ptpClock->recvPDelayRespSequenceId))
|
|
{
|
|
msgUnpackPDelayRespFollowUp(
|
|
ptpClock->msgIbuf,
|
|
&ptpClock->msgTmp.prespfollow);
|
|
ptpClock->counters.pdelayRespFollowUpMessagesReceived++;
|
|
toInternalTime(
|
|
&responseOriginTimestamp,
|
|
&ptpClock->msgTmp.prespfollow.responseOriginTimestamp);
|
|
ptpClock->pdelay_resp_send_time.seconds =
|
|
responseOriginTimestamp.seconds;
|
|
ptpClock->pdelay_resp_send_time.nanoseconds =
|
|
responseOriginTimestamp.nanoseconds;
|
|
integer64_to_internalTime(
|
|
ptpClock->msgTmpHeader.correctionField,
|
|
&correctionField);
|
|
addTime(&correctionField, &correctionField,
|
|
&ptpClock->lastPdelayRespCorrectionField);
|
|
updatePeerDelay (&ptpClock->owd_filt,
|
|
rtOpts, ptpClock,
|
|
&correctionField, TRUE);
|
|
break;
|
|
}
|
|
else
|
|
{
|
|
DBG("PdelayRespFollowup: sequence mismatch - Received: %d "
|
|
"PdelayReq sent: %d, PdelayResp received: %d\n",
|
|
header->sequenceId, ptpClock->sentPDelayReqSequenceId,
|
|
ptpClock->recvPDelayRespSequenceId);
|
|
ptpClock->counters.discardedMessages++;
|
|
ptpClock->counters.sequenceMismatchErrors++;
|
|
break;
|
|
}
|
|
|
|
default:
|
|
DBGV("Disregard PdelayRespFollowUp message \n");
|
|
ptpClock->counters.discardedMessages++;
|
|
}
|
|
}
|
|
else if (ptpClock->delayMechanism == E2E) /* (End to End mode..) */
|
|
{
|
|
DBG("Peer Delay messages are disregarded in End to End "
|
|
"mode \n");
|
|
ptpClock->counters.discardedMessages++;
|
|
ptpClock->counters.delayModeMismatchErrors++;
|
|
/* no delay mechanism */
|
|
}
|
|
else
|
|
{
|
|
DBG("PDelayRespFollowUp ignored - we are in DELAY_DISABLED mode");
|
|
ptpClock->counters.discardedMessages++;
|
|
}
|
|
}
|
|
|
|
/* Only accept the management message if it satisfies 15.3.1 Table 36 */
|
|
int
|
|
acceptManagementMessage(PortIdentity thisPort, PortIdentity targetPort)
|
|
{
|
|
ClockIdentity allOnesClkIdentity;
|
|
UInteger16 allOnesPortNumber = 0xFFFF;
|
|
memset(allOnesClkIdentity, 0xFF, sizeof(allOnesClkIdentity));
|
|
|
|
return ((memcmp(targetPort.clockIdentity, thisPort.clockIdentity, CLOCK_IDENTITY_LENGTH) == 0) ||
|
|
(memcmp(targetPort.clockIdentity, allOnesClkIdentity, CLOCK_IDENTITY_LENGTH) == 0))
|
|
&&
|
|
((targetPort.portNumber == thisPort.portNumber) ||
|
|
(targetPort.portNumber == allOnesPortNumber));
|
|
}
|
|
|
|
|
|
static void
|
|
handleManagement(MsgHeader *header,
|
|
Boolean isFromSelf, RunTimeOpts *rtOpts, PtpClock *ptpClock)
|
|
{
|
|
DBGV("Management message received : \n");
|
|
|
|
if (!rtOpts->managementEnabled)
|
|
{
|
|
DBGV("Dropping management message - management message support disabled");
|
|
ptpClock->counters.discardedMessages++;
|
|
return;
|
|
}
|
|
|
|
if (isFromSelf)
|
|
{
|
|
DBGV("handleManagement: Ignore message from self \n");
|
|
return;
|
|
}
|
|
|
|
msgUnpackManagement(ptpClock->msgIbuf, &ptpClock->msgTmp.manage, header, ptpClock);
|
|
|
|
if (ptpClock->msgTmp.manage.tlv == NULL)
|
|
{
|
|
DBGV("handleManagement: TLV is empty\n");
|
|
ptpClock->counters.messageFormatErrors++;
|
|
return;
|
|
}
|
|
|
|
if (!acceptManagementMessage(ptpClock->portIdentity, ptpClock->msgTmp.manage.targetPortIdentity))
|
|
{
|
|
DBGV("handleManagement: The management message was not accepted");
|
|
ptpClock->counters.discardedMessages++;
|
|
/* cleanup msgTmp managementTLV */
|
|
freeManagementTLV(&ptpClock->msgTmp.manage);
|
|
return;
|
|
}
|
|
|
|
if (!rtOpts->managementSetEnable &&
|
|
(ptpClock->msgTmp.manage.actionField == SET ||
|
|
ptpClock->msgTmp.manage.actionField == COMMAND))
|
|
{
|
|
DBGV("Dropping SET/COMMAND management message - read-only mode enabled");
|
|
ptpClock->counters.discardedMessages++;
|
|
/* cleanup msgTmp managementTLV */
|
|
freeManagementTLV(&ptpClock->msgTmp.manage);
|
|
return;
|
|
}
|
|
|
|
/* is this an error status management TLV? */
|
|
if (ptpClock->msgTmp.manage.tlv->tlvType == TLV_MANAGEMENT_ERROR_STATUS)
|
|
{
|
|
DBGV("handleManagement: Error Status TLV\n");
|
|
unpackMMErrorStatus(ptpClock->msgIbuf, &ptpClock->msgTmp.manage, ptpClock);
|
|
handleMMErrorStatus(&ptpClock->msgTmp.manage);
|
|
ptpClock->counters.managementMessagesReceived++;
|
|
|
|
/* cleanup msgTmp managementTLV */
|
|
if (ptpClock->msgTmp.manage.tlv)
|
|
{
|
|
DBGV("cleanup ptpClock->msgTmp.manage message \n");
|
|
|
|
if (ptpClock->msgTmp.manage.tlv->dataField)
|
|
{
|
|
freeMMErrorStatusTLV(ptpClock->msgTmp.manage.tlv);
|
|
free(ptpClock->msgTmp.manage.tlv->dataField);
|
|
}
|
|
|
|
free(ptpClock->msgTmp.manage.tlv);
|
|
}
|
|
|
|
return;
|
|
}
|
|
else if (ptpClock->msgTmp.manage.tlv->tlvType != TLV_MANAGEMENT)
|
|
{
|
|
/* do nothing, implemention specific handling */
|
|
DBGV("handleManagement: Currently unsupported management TLV type\n");
|
|
ptpClock->counters.discardedMessages++;
|
|
/* cleanup msgTmp managementTLV */
|
|
freeManagementTLV(&ptpClock->msgTmp.manage);
|
|
return;
|
|
}
|
|
|
|
switch (ptpClock->msgTmp.manage.tlv->managementId)
|
|
{
|
|
case MM_NULL_MANAGEMENT:
|
|
DBGV("handleManagement: Null Management\n");
|
|
handleMMNullManagement(&ptpClock->msgTmp.manage, &ptpClock->outgoingManageTmp, ptpClock);
|
|
ptpClock->counters.managementMessagesReceived++;
|
|
break;
|
|
|
|
case MM_CLOCK_DESCRIPTION:
|
|
DBGV("handleManagement: Clock Description\n");
|
|
unpackMMClockDescription(ptpClock->msgIbuf, &ptpClock->msgTmp.manage, ptpClock);
|
|
handleMMClockDescription(&ptpClock->msgTmp.manage, &ptpClock->outgoingManageTmp, ptpClock);
|
|
ptpClock->counters.managementMessagesReceived++;
|
|
break;
|
|
|
|
case MM_USER_DESCRIPTION:
|
|
DBGV("handleManagement: User Description\n");
|
|
unpackMMUserDescription(ptpClock->msgIbuf, &ptpClock->msgTmp.manage, ptpClock);
|
|
handleMMUserDescription(&ptpClock->msgTmp.manage, &ptpClock->outgoingManageTmp, ptpClock);
|
|
ptpClock->counters.managementMessagesReceived++;
|
|
break;
|
|
|
|
case MM_SAVE_IN_NON_VOLATILE_STORAGE:
|
|
DBGV("handleManagement: Save In Non-Volatile Storage\n");
|
|
handleMMSaveInNonVolatileStorage(&ptpClock->msgTmp.manage, &ptpClock->outgoingManageTmp, ptpClock);
|
|
ptpClock->counters.managementMessagesReceived++;
|
|
break;
|
|
|
|
case MM_RESET_NON_VOLATILE_STORAGE:
|
|
DBGV("handleManagement: Reset Non-Volatile Storage\n");
|
|
handleMMResetNonVolatileStorage(&ptpClock->msgTmp.manage, &ptpClock->outgoingManageTmp, ptpClock);
|
|
ptpClock->counters.managementMessagesReceived++;
|
|
break;
|
|
|
|
case MM_INITIALIZE:
|
|
DBGV("handleManagement: Initialize\n");
|
|
unpackMMInitialize(ptpClock->msgIbuf, &ptpClock->msgTmp.manage, ptpClock);
|
|
handleMMInitialize(&ptpClock->msgTmp.manage, &ptpClock->outgoingManageTmp, ptpClock);
|
|
ptpClock->counters.managementMessagesReceived++;
|
|
break;
|
|
|
|
case MM_DEFAULT_DATA_SET:
|
|
DBGV("handleManagement: Default Data Set\n");
|
|
unpackMMDefaultDataSet(ptpClock->msgIbuf, &ptpClock->msgTmp.manage, ptpClock);
|
|
handleMMDefaultDataSet(&ptpClock->msgTmp.manage, &ptpClock->outgoingManageTmp, ptpClock);
|
|
ptpClock->counters.managementMessagesReceived++;
|
|
break;
|
|
|
|
case MM_CURRENT_DATA_SET:
|
|
DBGV("handleManagement: Current Data Set\n");
|
|
unpackMMCurrentDataSet(ptpClock->msgIbuf, &ptpClock->msgTmp.manage, ptpClock);
|
|
handleMMCurrentDataSet(&ptpClock->msgTmp.manage, &ptpClock->outgoingManageTmp, ptpClock);
|
|
ptpClock->counters.managementMessagesReceived++;
|
|
break;
|
|
|
|
case MM_PARENT_DATA_SET:
|
|
DBGV("handleManagement: Parent Data Set\n");
|
|
unpackMMParentDataSet(ptpClock->msgIbuf, &ptpClock->msgTmp.manage, ptpClock);
|
|
handleMMParentDataSet(&ptpClock->msgTmp.manage, &ptpClock->outgoingManageTmp, ptpClock);
|
|
ptpClock->counters.managementMessagesReceived++;
|
|
break;
|
|
|
|
case MM_TIME_PROPERTIES_DATA_SET:
|
|
DBGV("handleManagement: TimeProperties Data Set\n");
|
|
unpackMMTimePropertiesDataSet(ptpClock->msgIbuf, &ptpClock->msgTmp.manage, ptpClock);
|
|
handleMMTimePropertiesDataSet(&ptpClock->msgTmp.manage, &ptpClock->outgoingManageTmp, ptpClock);
|
|
ptpClock->counters.managementMessagesReceived++;
|
|
break;
|
|
|
|
case MM_PORT_DATA_SET:
|
|
DBGV("handleManagement: Port Data Set\n");
|
|
unpackMMPortDataSet(ptpClock->msgIbuf, &ptpClock->msgTmp.manage, ptpClock);
|
|
handleMMPortDataSet(&ptpClock->msgTmp.manage, &ptpClock->outgoingManageTmp, ptpClock);
|
|
ptpClock->counters.managementMessagesReceived++;
|
|
break;
|
|
|
|
case MM_PRIORITY1:
|
|
DBGV("handleManagement: Priority1\n");
|
|
unpackMMPriority1(ptpClock->msgIbuf, &ptpClock->msgTmp.manage, ptpClock);
|
|
handleMMPriority1(&ptpClock->msgTmp.manage, &ptpClock->outgoingManageTmp, ptpClock);
|
|
ptpClock->counters.managementMessagesReceived++;
|
|
break;
|
|
|
|
case MM_PRIORITY2:
|
|
DBGV("handleManagement: Priority2\n");
|
|
unpackMMPriority2(ptpClock->msgIbuf, &ptpClock->msgTmp.manage, ptpClock);
|
|
handleMMPriority2(&ptpClock->msgTmp.manage, &ptpClock->outgoingManageTmp, ptpClock);
|
|
ptpClock->counters.managementMessagesReceived++;
|
|
break;
|
|
|
|
case MM_DOMAIN:
|
|
DBGV("handleManagement: Domain\n");
|
|
unpackMMDomain(ptpClock->msgIbuf, &ptpClock->msgTmp.manage, ptpClock);
|
|
handleMMDomain(&ptpClock->msgTmp.manage, &ptpClock->outgoingManageTmp, ptpClock);
|
|
ptpClock->counters.managementMessagesReceived++;
|
|
break;
|
|
|
|
case MM_SLAVE_ONLY:
|
|
DBGV("handleManagement: Slave Only\n");
|
|
unpackMMSlaveOnly(ptpClock->msgIbuf, &ptpClock->msgTmp.manage, ptpClock);
|
|
handleMMSlaveOnly(&ptpClock->msgTmp.manage, &ptpClock->outgoingManageTmp, ptpClock);
|
|
ptpClock->counters.managementMessagesReceived++;
|
|
break;
|
|
|
|
case MM_LOG_ANNOUNCE_INTERVAL:
|
|
DBGV("handleManagement: Log Announce Interval\n");
|
|
unpackMMLogAnnounceInterval(ptpClock->msgIbuf, &ptpClock->msgTmp.manage, ptpClock);
|
|
handleMMLogAnnounceInterval(&ptpClock->msgTmp.manage, &ptpClock->outgoingManageTmp, ptpClock);
|
|
ptpClock->counters.managementMessagesReceived++;
|
|
break;
|
|
|
|
case MM_ANNOUNCE_RECEIPT_TIMEOUT:
|
|
DBGV("handleManagement: Announce Receipt Timeout\n");
|
|
unpackMMAnnounceReceiptTimeout(ptpClock->msgIbuf, &ptpClock->msgTmp.manage, ptpClock);
|
|
handleMMAnnounceReceiptTimeout(&ptpClock->msgTmp.manage, &ptpClock->outgoingManageTmp, ptpClock);
|
|
ptpClock->counters.managementMessagesReceived++;
|
|
break;
|
|
|
|
case MM_LOG_SYNC_INTERVAL:
|
|
DBGV("handleManagement: Log Sync Interval\n");
|
|
unpackMMLogSyncInterval(ptpClock->msgIbuf, &ptpClock->msgTmp.manage, ptpClock);
|
|
handleMMLogSyncInterval(&ptpClock->msgTmp.manage, &ptpClock->outgoingManageTmp, ptpClock);
|
|
ptpClock->counters.managementMessagesReceived++;
|
|
break;
|
|
|
|
case MM_VERSION_NUMBER:
|
|
DBGV("handleManagement: Version Number\n");
|
|
unpackMMVersionNumber(ptpClock->msgIbuf, &ptpClock->msgTmp.manage, ptpClock);
|
|
handleMMVersionNumber(&ptpClock->msgTmp.manage, &ptpClock->outgoingManageTmp, ptpClock);
|
|
ptpClock->counters.managementMessagesReceived++;
|
|
break;
|
|
|
|
case MM_ENABLE_PORT:
|
|
DBGV("handleManagement: Enable Port\n");
|
|
handleMMEnablePort(&ptpClock->msgTmp.manage, &ptpClock->outgoingManageTmp, ptpClock);
|
|
ptpClock->counters.managementMessagesReceived++;
|
|
break;
|
|
|
|
case MM_DISABLE_PORT:
|
|
DBGV("handleManagement: Disable Port\n");
|
|
handleMMDisablePort(&ptpClock->msgTmp.manage, &ptpClock->outgoingManageTmp, ptpClock);
|
|
ptpClock->counters.managementMessagesReceived++;
|
|
break;
|
|
|
|
case MM_TIME:
|
|
DBGV("handleManagement: Time\n");
|
|
unpackMMTime(ptpClock->msgIbuf, &ptpClock->msgTmp.manage, ptpClock);
|
|
handleMMTime(&ptpClock->msgTmp.manage, &ptpClock->outgoingManageTmp, ptpClock, rtOpts);
|
|
ptpClock->counters.managementMessagesReceived++;
|
|
break;
|
|
|
|
case MM_CLOCK_ACCURACY:
|
|
DBGV("handleManagement: Clock Accuracy\n");
|
|
unpackMMClockAccuracy(ptpClock->msgIbuf, &ptpClock->msgTmp.manage, ptpClock);
|
|
handleMMClockAccuracy(&ptpClock->msgTmp.manage, &ptpClock->outgoingManageTmp, ptpClock);
|
|
ptpClock->counters.managementMessagesReceived++;
|
|
break;
|
|
|
|
case MM_UTC_PROPERTIES:
|
|
DBGV("handleManagement: Utc Properties\n");
|
|
unpackMMUtcProperties(ptpClock->msgIbuf, &ptpClock->msgTmp.manage, ptpClock);
|
|
handleMMUtcProperties(&ptpClock->msgTmp.manage, &ptpClock->outgoingManageTmp, ptpClock);
|
|
ptpClock->counters.managementMessagesReceived++;
|
|
break;
|
|
|
|
case MM_TRACEABILITY_PROPERTIES:
|
|
DBGV("handleManagement: Traceability Properties\n");
|
|
unpackMMTraceabilityProperties(ptpClock->msgIbuf, &ptpClock->msgTmp.manage, ptpClock);
|
|
handleMMTraceabilityProperties(&ptpClock->msgTmp.manage, &ptpClock->outgoingManageTmp, ptpClock);
|
|
ptpClock->counters.managementMessagesReceived++;
|
|
break;
|
|
|
|
case MM_DELAY_MECHANISM:
|
|
DBGV("handleManagement: Delay Mechanism\n");
|
|
unpackMMDelayMechanism(ptpClock->msgIbuf, &ptpClock->msgTmp.manage, ptpClock);
|
|
handleMMDelayMechanism(&ptpClock->msgTmp.manage, &ptpClock->outgoingManageTmp, ptpClock);
|
|
ptpClock->counters.managementMessagesReceived++;
|
|
break;
|
|
|
|
case MM_LOG_MIN_PDELAY_REQ_INTERVAL:
|
|
DBGV("handleManagement: Log Min Pdelay Req Interval\n");
|
|
unpackMMLogMinPdelayReqInterval(ptpClock->msgIbuf, &ptpClock->msgTmp.manage, ptpClock);
|
|
handleMMLogMinPdelayReqInterval(&ptpClock->msgTmp.manage, &ptpClock->outgoingManageTmp, ptpClock);
|
|
ptpClock->counters.managementMessagesReceived++;
|
|
break;
|
|
|
|
case MM_FAULT_LOG:
|
|
case MM_FAULT_LOG_RESET:
|
|
case MM_TIMESCALE_PROPERTIES:
|
|
case MM_UNICAST_NEGOTIATION_ENABLE:
|
|
case MM_PATH_TRACE_LIST:
|
|
case MM_PATH_TRACE_ENABLE:
|
|
case MM_GRANDMASTER_CLUSTER_TABLE:
|
|
case MM_UNICAST_MASTER_TABLE:
|
|
case MM_UNICAST_MASTER_MAX_TABLE_SIZE:
|
|
case MM_ACCEPTABLE_MASTER_TABLE:
|
|
case MM_ACCEPTABLE_MASTER_TABLE_ENABLED:
|
|
case MM_ACCEPTABLE_MASTER_MAX_TABLE_SIZE:
|
|
case MM_ALTERNATE_MASTER:
|
|
case MM_ALTERNATE_TIME_OFFSET_ENABLE:
|
|
case MM_ALTERNATE_TIME_OFFSET_NAME:
|
|
case MM_ALTERNATE_TIME_OFFSET_MAX_KEY:
|
|
case MM_ALTERNATE_TIME_OFFSET_PROPERTIES:
|
|
case MM_TRANSPARENT_CLOCK_DEFAULT_DATA_SET:
|
|
case MM_TRANSPARENT_CLOCK_PORT_DATA_SET:
|
|
case MM_PRIMARY_DOMAIN:
|
|
DBGV("handleManagement: Currently unsupported managementTLV %d\n",
|
|
ptpClock->msgTmp.manage.tlv->managementId);
|
|
handleErrorManagementMessage(&ptpClock->msgTmp.manage, &ptpClock->outgoingManageTmp,
|
|
ptpClock, ptpClock->msgTmp.manage.tlv->managementId,
|
|
NOT_SUPPORTED);
|
|
ptpClock->counters.discardedMessages++;
|
|
break;
|
|
|
|
default:
|
|
DBGV("handleManagement: Unknown managementTLV %d\n",
|
|
ptpClock->msgTmp.manage.tlv->managementId);
|
|
handleErrorManagementMessage(&ptpClock->msgTmp.manage, &ptpClock->outgoingManageTmp,
|
|
ptpClock, ptpClock->msgTmp.manage.tlv->managementId,
|
|
NO_SUCH_ID);
|
|
ptpClock->counters.discardedMessages++;
|
|
}
|
|
|
|
/* If the management message we received was unicast, we also reply with unicast */
|
|
if ((header->flagField0 & PTP_UNICAST) == PTP_UNICAST)
|
|
{
|
|
ptpClock->LastSlaveAddr = ptpClock->netPath.lastRecvAddr;
|
|
}
|
|
else
|
|
{
|
|
ptpClock->LastSlaveAddr = 0;
|
|
}
|
|
|
|
/* send management message response or acknowledge */
|
|
if (ptpClock->outgoingManageTmp.tlv->tlvType == TLV_MANAGEMENT)
|
|
{
|
|
if (ptpClock->outgoingManageTmp.actionField == RESPONSE ||
|
|
ptpClock->outgoingManageTmp.actionField == ACKNOWLEDGE)
|
|
{
|
|
issueManagementRespOrAck(&ptpClock->outgoingManageTmp, rtOpts, ptpClock);
|
|
}
|
|
}
|
|
else if (ptpClock->outgoingManageTmp.tlv->tlvType == TLV_MANAGEMENT_ERROR_STATUS)
|
|
{
|
|
issueManagementErrorStatus(&ptpClock->outgoingManageTmp, rtOpts, ptpClock);
|
|
}
|
|
|
|
/* cleanup msgTmp managementTLV */
|
|
freeManagementTLV(&ptpClock->msgTmp.manage);
|
|
/* cleanup outgoing managementTLV */
|
|
freeManagementTLV(&ptpClock->outgoingManageTmp);
|
|
|
|
}
|
|
|
|
static void
|
|
handleSignaling(PtpClock *ptpClock)
|
|
{
|
|
|
|
ptpClock->counters.signalingMessagesReceived++;
|
|
|
|
}
|
|
|
|
/*Pack and send on general multicast ip adress an Announce message*/
|
|
static void
|
|
issueAnnounce(RunTimeOpts *rtOpts, PtpClock *ptpClock)
|
|
{
|
|
UInteger16 length;
|
|
|
|
if (rtOpts->ptp_profile == PTP_802_1AS_PROFILE )
|
|
{
|
|
length = ANNOUNCE_LENGTH_8021AS;
|
|
}
|
|
else
|
|
{
|
|
length = ANNOUNCE_LENGTH;
|
|
}
|
|
|
|
msgPackAnnounce(ptpClock->msgObuf, ptpClock, rtOpts);
|
|
|
|
|
|
if (!netSendGeneral(ptpClock->msgObuf, length,
|
|
&ptpClock->netPath, rtOpts, 0))
|
|
{
|
|
toState(PTP_FAULTY, rtOpts, ptpClock);
|
|
ptpClock->counters.messageSendErrors++;
|
|
|
|
DBGV("Announce message can't be sent -> FAULTY state \n");
|
|
}
|
|
else
|
|
{
|
|
DBGV("Announce MSG sent ! \n");
|
|
ptpClock->sentAnnounceSequenceId++;
|
|
ptpClock->counters.announceMessagesSent++;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
/*Pack and send on event multicast ip adress a Sync message*/
|
|
static void
|
|
issueSync(RunTimeOpts *rtOpts, PtpClock *ptpClock)
|
|
{
|
|
Timestamp originTimestamp;
|
|
TimeInternal internalTime;
|
|
|
|
getTime(rtOpts, &internalTime);
|
|
|
|
if (respectUtcOffset(rtOpts, ptpClock) == TRUE)
|
|
{
|
|
internalTime.seconds += ptpClock->timePropertiesDS.currentUtcOffset;
|
|
}
|
|
|
|
fromInternalTime(&internalTime, &originTimestamp);
|
|
|
|
if (rtOpts->debug_level > PTP_DEBUG_LEVEL_3)
|
|
NOTICE("issueSync originTimestamp %ds %09ldns\n",
|
|
internalTime.seconds, internalTime.nanoseconds);
|
|
|
|
msgPackSync(ptpClock->msgObuf, &originTimestamp, ptpClock, rtOpts);
|
|
|
|
if (!netSendEvent(ptpClock->msgObuf, SYNC_LENGTH, ptpClock,
|
|
rtOpts, 0, &internalTime))
|
|
{
|
|
toState(PTP_FAULTY, rtOpts, ptpClock);
|
|
ptpClock->counters.messageSendErrors++;
|
|
DBGV("Sync message can't be sent -> FAULTY state \n");
|
|
}
|
|
else
|
|
{
|
|
DBGV("Sync MSG sent ! \n");
|
|
|
|
if (ptpClock->twoStepFlag == TWO_STEP_FLAG)
|
|
{
|
|
if (!ptpClock->netPath.txTimestampFailure)
|
|
{
|
|
if (respectUtcOffset(rtOpts, ptpClock) == TRUE)
|
|
{
|
|
internalTime.seconds += ptpClock->timePropertiesDS.currentUtcOffset;
|
|
}
|
|
|
|
processSyncFromSelf(&internalTime, rtOpts, ptpClock, ptpClock->sentSyncSequenceId);
|
|
}
|
|
}
|
|
|
|
ptpClock->sentSyncSequenceId++;
|
|
ptpClock->counters.syncMessagesSent++;
|
|
}
|
|
}
|
|
|
|
|
|
/*Pack and send on general multicast ip adress a FollowUp message*/
|
|
static void
|
|
issueFollowup(const TimeInternal *tint, RunTimeOpts *rtOpts, PtpClock *ptpClock, UInteger16 sequenceId)
|
|
{
|
|
Timestamp preciseOriginTimestamp;
|
|
UInteger16 length;
|
|
|
|
if (rtOpts->ptp_profile == PTP_802_1AS_PROFILE )
|
|
{
|
|
length = FOLLOW_UP_LENGTH_8021AS;
|
|
}
|
|
else
|
|
{
|
|
length = FOLLOW_UP_LENGTH;
|
|
}
|
|
|
|
fromInternalTime(tint, &preciseOriginTimestamp);
|
|
|
|
if (rtOpts->debug_level > PTP_DEBUG_LEVEL_3)
|
|
NOTICE("issueFollowup preciseOriginTimestamp %ds %09ldns\n",
|
|
tint->seconds, tint->nanoseconds);
|
|
|
|
msgPackFollowUp(ptpClock->msgObuf, &preciseOriginTimestamp, ptpClock, sequenceId, rtOpts);
|
|
|
|
if (!netSendGeneral(ptpClock->msgObuf, length,
|
|
&ptpClock->netPath, rtOpts, 0))
|
|
{
|
|
toState(PTP_FAULTY, rtOpts, ptpClock);
|
|
ptpClock->counters.messageSendErrors++;
|
|
DBGV("FollowUp message can't be sent -> FAULTY state \n");
|
|
}
|
|
else
|
|
{
|
|
DBGV("FollowUp MSG sent ! \n");
|
|
ptpClock->counters.followUpMessagesSent++;
|
|
}
|
|
}
|
|
|
|
|
|
/*Pack and send on event multicast ip adress a DelayReq message*/
|
|
static void
|
|
issueDelayReq(RunTimeOpts *rtOpts, PtpClock *ptpClock)
|
|
{
|
|
Timestamp originTimestamp;
|
|
TimeInternal internalTime;
|
|
|
|
DBG("==> Issue DelayReq (%d)\n", ptpClock->sentDelayReqSequenceId );
|
|
|
|
/*
|
|
* call GTOD. This time is later replaced in handleDelayReq,
|
|
* to get the actual send timestamp from the OS
|
|
*/
|
|
getTime(rtOpts, &internalTime);
|
|
|
|
if (respectUtcOffset(rtOpts, ptpClock) == TRUE)
|
|
{
|
|
internalTime.seconds += ptpClock->timePropertiesDS.currentUtcOffset;
|
|
}
|
|
|
|
fromInternalTime(&internalTime, &originTimestamp);
|
|
|
|
/* uses current sentDelayReqSequenceId */
|
|
msgPackDelayReq(ptpClock->msgObuf, &originTimestamp, ptpClock);
|
|
|
|
Integer32 dst = 0;
|
|
|
|
/* in hybrid mode, or unicast mode when no master specified,
|
|
send delayReq to current master */
|
|
if (rtOpts->ip_mode == IPMODE_HYBRID ||
|
|
(rtOpts->ip_mode == IPMODE_UNICAST && !ptpClock->netPath.unicastAddr))
|
|
{
|
|
dst = ptpClock->masterAddr;
|
|
}
|
|
|
|
|
|
if (!netSendEvent(ptpClock->msgObuf, DELAY_REQ_LENGTH,
|
|
ptpClock, rtOpts, dst, &internalTime))
|
|
{
|
|
toState(PTP_FAULTY, rtOpts, ptpClock);
|
|
ptpClock->counters.messageSendErrors++;
|
|
DBGV("delayReq message can't be sent -> FAULTY state \n");
|
|
}
|
|
else
|
|
{
|
|
DBGV("DelayReq MSG sent ! \n");
|
|
|
|
|
|
if (!ptpClock->netPath.txTimestampFailure)
|
|
{
|
|
if (respectUtcOffset(rtOpts, ptpClock) == TRUE)
|
|
{
|
|
internalTime.seconds += ptpClock->timePropertiesDS.currentUtcOffset;
|
|
}
|
|
|
|
processDelayReqFromSelf(&internalTime, rtOpts, ptpClock);
|
|
}
|
|
|
|
|
|
ptpClock->sentDelayReqSequenceId++;
|
|
ptpClock->counters.delayReqMessagesSent++;
|
|
|
|
/* From now on, we will only accept delayreq and
|
|
* delayresp of (sentDelayReqSequenceId - 1) */
|
|
|
|
/* Explicitelly re-arm timer for sending the next delayReq */
|
|
timerStart_random(DELAYREQ_INTERVAL_TIMER,
|
|
(float)pow(2, ptpClock->logMinDelayReqInterval),
|
|
ptpClock->itimer);
|
|
#if 0 /* PCAP ONLY */
|
|
msgUnpackHeader(ptpClock->msgObuf, &ourDelayReq);
|
|
handleDelayReq(&ourDelayReq, DELAY_REQ_LENGTH, &internalTime,
|
|
TRUE, rtOpts, ptpClock);
|
|
#endif
|
|
}
|
|
}
|
|
|
|
/*Pack and send on event multicast ip adress a PDelayReq message*/
|
|
static void
|
|
issuePDelayReq(RunTimeOpts *rtOpts, PtpClock *ptpClock)
|
|
{
|
|
Timestamp originTimestamp;
|
|
TimeInternal internalTime;
|
|
getTime(rtOpts, &internalTime);
|
|
|
|
if (respectUtcOffset(rtOpts, ptpClock) == TRUE)
|
|
{
|
|
internalTime.seconds += ptpClock->timePropertiesDS.currentUtcOffset;
|
|
}
|
|
|
|
fromInternalTime(&internalTime, &originTimestamp);
|
|
|
|
msgPackPDelayReq(ptpClock->msgObuf, &originTimestamp, ptpClock, rtOpts);
|
|
|
|
if (!netSendPeerEvent(ptpClock->msgObuf, PDELAY_REQ_LENGTH,
|
|
ptpClock, rtOpts, &internalTime))
|
|
{
|
|
toState(PTP_FAULTY, rtOpts, ptpClock);
|
|
ptpClock->counters.messageSendErrors++;
|
|
DBGV("PdelayReq message can't be sent -> FAULTY state \n");
|
|
}
|
|
else
|
|
{
|
|
DBGV("PDelayReq MSG sent ! \n");
|
|
|
|
|
|
if (!ptpClock->netPath.txTimestampFailure)
|
|
{
|
|
if (respectUtcOffset(rtOpts, ptpClock) == TRUE)
|
|
{
|
|
internalTime.seconds += ptpClock->timePropertiesDS.currentUtcOffset;
|
|
}
|
|
|
|
processPDelayReqFromSelf(&internalTime, rtOpts, ptpClock);
|
|
}
|
|
|
|
|
|
ptpClock->sentPDelayReqSequenceId++;
|
|
ptpClock->counters.pdelayReqMessagesSent++;
|
|
}
|
|
}
|
|
|
|
/*Pack and send on event multicast ip adress a PDelayResp message*/
|
|
static void
|
|
issuePDelayResp(const TimeInternal *tint, MsgHeader *header, RunTimeOpts *rtOpts,
|
|
PtpClock *ptpClock)
|
|
{
|
|
Timestamp requestReceiptTimestamp;
|
|
TimeInternal internalTime;
|
|
|
|
fromInternalTime(tint, &requestReceiptTimestamp);
|
|
msgPackPDelayResp(ptpClock->msgObuf, header,
|
|
&requestReceiptTimestamp, ptpClock);
|
|
|
|
if (!netSendPeerEvent(ptpClock->msgObuf, PDELAY_RESP_LENGTH,
|
|
ptpClock, rtOpts, &internalTime))
|
|
{
|
|
toState(PTP_FAULTY, rtOpts, ptpClock);
|
|
ptpClock->counters.messageSendErrors++;
|
|
DBGV("PdelayResp message can't be sent -> FAULTY state \n");
|
|
}
|
|
else
|
|
{
|
|
DBGV("PDelayResp MSG sent ! \n");
|
|
|
|
if (ptpClock->twoStepFlag == TWO_STEP_FLAG)
|
|
{
|
|
|
|
if (!ptpClock->netPath.txTimestampFailure)
|
|
{
|
|
if (respectUtcOffset(rtOpts, ptpClock) == TRUE)
|
|
{
|
|
internalTime.seconds += ptpClock->timePropertiesDS.currentUtcOffset;
|
|
}
|
|
|
|
processPDelayRespFromSelf(&internalTime, rtOpts, ptpClock, header->sequenceId);
|
|
}
|
|
}
|
|
|
|
ptpClock->counters.pdelayRespMessagesSent++;
|
|
}
|
|
}
|
|
|
|
|
|
/*Pack and send on event multicast ip adress a DelayResp message*/
|
|
static void
|
|
issueDelayResp(const TimeInternal *tint, MsgHeader *header, RunTimeOpts *rtOpts, PtpClock *ptpClock)
|
|
{
|
|
Timestamp requestReceiptTimestamp;
|
|
|
|
fromInternalTime(tint, &requestReceiptTimestamp);
|
|
msgPackDelayResp(ptpClock->msgObuf, header, &requestReceiptTimestamp,
|
|
ptpClock, rtOpts);
|
|
|
|
Integer32 dst = 0;
|
|
|
|
/* hybrid mode: if request was unicast, reply unicast - if not, not */
|
|
if ( (rtOpts->ip_mode == IPMODE_HYBRID) &&
|
|
(header->flagField0 & PTP_UNICAST) == PTP_UNICAST)
|
|
{
|
|
dst = ptpClock->LastSlaveAddr;
|
|
}
|
|
|
|
if (!netSendGeneral(ptpClock->msgObuf, DELAY_RESP_LENGTH,
|
|
&ptpClock->netPath, rtOpts, dst))
|
|
{
|
|
toState(PTP_FAULTY, rtOpts, ptpClock);
|
|
ptpClock->counters.messageSendErrors++;
|
|
DBGV("delayResp message can't be sent -> FAULTY state \n");
|
|
}
|
|
else
|
|
{
|
|
DBGV("PDelayResp MSG sent ! \n");
|
|
ptpClock->counters.delayRespMessagesSent++;
|
|
}
|
|
}
|
|
|
|
|
|
static void
|
|
issuePDelayRespFollowUp(const TimeInternal *tint, MsgHeader *header,
|
|
RunTimeOpts *rtOpts, PtpClock *ptpClock, const UInteger16 sequenceId)
|
|
{
|
|
Timestamp responseOriginTimestamp;
|
|
fromInternalTime(tint, &responseOriginTimestamp);
|
|
|
|
msgPackPDelayRespFollowUp(ptpClock->msgObuf, header,
|
|
&responseOriginTimestamp, ptpClock, sequenceId);
|
|
|
|
if (!netSendPeerGeneral(ptpClock->msgObuf,
|
|
PDELAY_RESP_FOLLOW_UP_LENGTH,
|
|
&ptpClock->netPath, rtOpts))
|
|
{
|
|
toState(PTP_FAULTY, rtOpts, ptpClock);
|
|
ptpClock->counters.messageSendErrors++;
|
|
DBGV("PdelayRespFollowUp message can't be sent -> FAULTY state \n");
|
|
}
|
|
else
|
|
{
|
|
DBGV("PDelayRespFollowUp MSG sent ! \n");
|
|
ptpClock->counters.pdelayRespFollowUpMessagesSent++;
|
|
}
|
|
}
|
|
|
|
#if 0
|
|
static void
|
|
issueManagement(MsgHeader *header, MsgManagement *manage, RunTimeOpts *rtOpts,
|
|
PtpClock *ptpClock)
|
|
{
|
|
|
|
ptpClock->counters.managementMessagesSent++;
|
|
|
|
}
|
|
#endif
|
|
|
|
static void
|
|
issueManagementRespOrAck(MsgManagement *outgoing, RunTimeOpts *rtOpts,
|
|
PtpClock *ptpClock)
|
|
{
|
|
Integer32 dst = 0;
|
|
|
|
/* pack ManagementTLV */
|
|
msgPackManagementTLV( ptpClock->msgObuf, outgoing, ptpClock);
|
|
|
|
/* set header messageLength, the outgoing->tlv->lengthField is now valid */
|
|
outgoing->header.messageLength = (UInteger16)(MANAGEMENT_LENGTH +
|
|
TL_LENGTH +
|
|
outgoing->tlv->lengthField);
|
|
|
|
msgPackManagement( ptpClock->msgObuf, outgoing, ptpClock);
|
|
|
|
/* If LastSlaveAddr was populated, we reply via Unicast */
|
|
if (ptpClock->LastSlaveAddr > 0)
|
|
{
|
|
dst = ptpClock->LastSlaveAddr;
|
|
}
|
|
|
|
if (!netSendGeneral(ptpClock->msgObuf, outgoing->header.messageLength,
|
|
&ptpClock->netPath, rtOpts, dst))
|
|
{
|
|
DBGV("Management response/acknowledge can't be sent -> FAULTY state \n");
|
|
ptpClock->counters.messageSendErrors++;
|
|
toState(PTP_FAULTY, rtOpts, ptpClock);
|
|
}
|
|
else
|
|
{
|
|
DBGV("Management response/acknowledge msg sent \n");
|
|
ptpClock->counters.managementMessagesSent++;
|
|
}
|
|
}
|
|
|
|
static void
|
|
issueManagementErrorStatus(MsgManagement *outgoing, RunTimeOpts *rtOpts, PtpClock *ptpClock)
|
|
{
|
|
Integer32 dst = 0;
|
|
|
|
/* pack ManagementErrorStatusTLV */
|
|
msgPackManagementErrorStatusTLV( ptpClock->msgObuf, outgoing, ptpClock);
|
|
|
|
/* set header messageLength, the outgoing->tlv->lengthField is now valid */
|
|
outgoing->header.messageLength = (UInteger16)(MANAGEMENT_LENGTH +
|
|
TL_LENGTH +
|
|
outgoing->tlv->lengthField);
|
|
|
|
msgPackManagement( ptpClock->msgObuf, outgoing, ptpClock);
|
|
|
|
if (ptpClock->LastSlaveAddr > 0)
|
|
{
|
|
dst = ptpClock->LastSlaveAddr;
|
|
}
|
|
|
|
if (!netSendGeneral(ptpClock->msgObuf, outgoing->header.messageLength,
|
|
&ptpClock->netPath, rtOpts, dst))
|
|
{
|
|
DBGV("Management error status can't be sent -> FAULTY state \n");
|
|
ptpClock->counters.messageSendErrors++;
|
|
toState(PTP_FAULTY, rtOpts, ptpClock);
|
|
}
|
|
else
|
|
{
|
|
DBGV("Management error status msg sent \n");
|
|
ptpClock->counters.managementMessagesSent++;
|
|
}
|
|
|
|
}
|
|
|
|
void
|
|
addForeign(Octet *buf, MsgHeader *header, PtpClock *ptpClock)
|
|
{
|
|
int i, j;
|
|
Boolean found = FALSE;
|
|
|
|
j = ptpClock->foreign_record_best;
|
|
|
|
/*Check if Foreign master is already known*/
|
|
for (i = 0; i < ptpClock->number_foreign_records; i++)
|
|
{
|
|
if (!memcmp(header->sourcePortIdentity.clockIdentity,
|
|
ptpClock->foreign[j].foreignMasterPortIdentity.clockIdentity,
|
|
CLOCK_IDENTITY_LENGTH) &&
|
|
(header->sourcePortIdentity.portNumber ==
|
|
ptpClock->foreign[j].foreignMasterPortIdentity.portNumber))
|
|
{
|
|
/*Foreign Master is already in Foreignmaster data set*/
|
|
ptpClock->foreign[j].foreignMasterAnnounceMessages++;
|
|
found = TRUE;
|
|
DBGV("addForeign : AnnounceMessage incremented \n");
|
|
msgUnpackHeader(buf, &ptpClock->foreign[j].header);
|
|
msgUnpackAnnounce(buf, &ptpClock->foreign[j].announce);
|
|
break;
|
|
}
|
|
|
|
j = (j + 1) % ptpClock->number_foreign_records;
|
|
}
|
|
|
|
/*New Foreign Master*/
|
|
if (!found)
|
|
{
|
|
if (ptpClock->number_foreign_records <
|
|
ptpClock->max_foreign_records)
|
|
{
|
|
ptpClock->number_foreign_records++;
|
|
}
|
|
|
|
/* Preserve best master record from overwriting (sf FR #22) - use next slot*/
|
|
if (ptpClock->foreign_record_i == ptpClock->foreign_record_best)
|
|
{
|
|
ptpClock->foreign_record_i++;
|
|
ptpClock->foreign_record_i =
|
|
(Integer16)(ptpClock->foreign_record_i % ptpClock->number_foreign_records);
|
|
}
|
|
|
|
j = ptpClock->foreign_record_i;
|
|
|
|
/*Copy new foreign master data set from Announce message*/
|
|
copyClockIdentity(ptpClock->foreign[j].foreignMasterPortIdentity.clockIdentity,
|
|
header->sourcePortIdentity.clockIdentity);
|
|
ptpClock->foreign[j].foreignMasterPortIdentity.portNumber =
|
|
header->sourcePortIdentity.portNumber;
|
|
ptpClock->foreign[j].foreignMasterAnnounceMessages = 0;
|
|
|
|
/*
|
|
* header and announce field of each Foreign Master are
|
|
* usefull to run Best Master Clock Algorithm
|
|
*/
|
|
msgUnpackHeader(buf, &ptpClock->foreign[j].header);
|
|
msgUnpackAnnounce(buf, &ptpClock->foreign[j].announce);
|
|
DBGV("New foreign Master added \n");
|
|
|
|
ptpClock->foreign_record_i =
|
|
(Integer16)((ptpClock->foreign_record_i + 1) %
|
|
ptpClock->max_foreign_records);
|
|
}
|
|
}
|
|
|
|
/* Update dataset fields which are safe to change without going into INITIALIZING */
|
|
static void
|
|
updateDatasets(PtpClock *ptpClock, RunTimeOpts *rtOpts)
|
|
{
|
|
|
|
switch (ptpClock->portState)
|
|
{
|
|
|
|
/* We are master so update both the port and the parent dataset */
|
|
case PTP_MASTER:
|
|
ptpClock->numberPorts = NUMBER_PORTS;
|
|
ptpClock->delayMechanism = rtOpts->delayMechanism;
|
|
ptpClock->versionNumber = VERSION_PTP;
|
|
|
|
ptpClock->clockQuality.clockAccuracy =
|
|
rtOpts->clockQuality.clockAccuracy;
|
|
ptpClock->clockQuality.clockClass = rtOpts->clockQuality.clockClass;
|
|
ptpClock->clockQuality.offsetScaledLogVariance =
|
|
rtOpts->clockQuality.offsetScaledLogVariance;
|
|
ptpClock->priority1 = rtOpts->priority1;
|
|
ptpClock->priority2 = rtOpts->priority2;
|
|
|
|
ptpClock->grandmasterClockQuality.clockAccuracy =
|
|
ptpClock->clockQuality.clockAccuracy;
|
|
ptpClock->grandmasterClockQuality.clockClass =
|
|
ptpClock->clockQuality.clockClass;
|
|
ptpClock->grandmasterClockQuality.offsetScaledLogVariance =
|
|
ptpClock->clockQuality.offsetScaledLogVariance;
|
|
ptpClock->clockQuality.clockAccuracy =
|
|
ptpClock->grandmasterPriority1 = ptpClock->priority1;
|
|
ptpClock->grandmasterPriority2 = ptpClock->priority2;
|
|
ptpClock->timePropertiesDS.currentUtcOffsetValid = rtOpts->timeProperties.currentUtcOffsetValid;
|
|
ptpClock->timePropertiesDS.currentUtcOffset = rtOpts->timeProperties.currentUtcOffset;
|
|
ptpClock->timePropertiesDS.timeTraceable = rtOpts->timeProperties.timeTraceable;
|
|
ptpClock->timePropertiesDS.frequencyTraceable = rtOpts->timeProperties.frequencyTraceable;
|
|
ptpClock->timePropertiesDS.ptpTimescale = rtOpts->timeProperties.ptpTimescale;
|
|
ptpClock->timePropertiesDS.timeSource = rtOpts->timeProperties.timeSource;
|
|
ptpClock->logAnnounceInterval = rtOpts->announceInterval;
|
|
ptpClock->announceReceiptTimeout = rtOpts->announceReceiptTimeout;
|
|
ptpClock->logSyncInterval = rtOpts->syncInterval;
|
|
ptpClock->logMinPdelayReqInterval = rtOpts->logMinPdelayReqInterval;
|
|
ptpClock->logMinDelayReqInterval = (Integer8)rtOpts->initial_delayreq;
|
|
break;
|
|
|
|
/*
|
|
* we are not master so update the port dataset only - parent will be updated
|
|
* by m1() if we go master - basically update the fields affecting BMC only
|
|
*/
|
|
case PTP_SLAVE:
|
|
if (ptpClock->logMinDelayReqInterval == UNICAST_MESSAGEINTERVAL &&
|
|
rtOpts->autoDelayReqInterval)
|
|
{
|
|
NOTICE("Running at %d Delay Interval (unicast) - overriding with %d\n",
|
|
ptpClock->logMinDelayReqInterval, rtOpts->subsequent_delayreq);
|
|
ptpClock->logMinDelayReqInterval = (Integer8)rtOpts->subsequent_delayreq;
|
|
}
|
|
|
|
/* this is an intentional fall through to the next case */
|
|
case PTP_PASSIVE:
|
|
ptpClock->numberPorts = NUMBER_PORTS;
|
|
ptpClock->delayMechanism = rtOpts->delayMechanism;
|
|
ptpClock->versionNumber = VERSION_PTP;
|
|
ptpClock->clockQuality.clockAccuracy =
|
|
rtOpts->clockQuality.clockAccuracy;
|
|
ptpClock->clockQuality.clockClass = rtOpts->clockQuality.clockClass;
|
|
ptpClock->clockQuality.offsetScaledLogVariance =
|
|
rtOpts->clockQuality.offsetScaledLogVariance;
|
|
ptpClock->priority1 = rtOpts->priority1;
|
|
ptpClock->priority2 = rtOpts->priority2;
|
|
break;
|
|
|
|
/* In all other states the datasets will be updated when going into an operational state */
|
|
default:
|
|
break;
|
|
}
|
|
|
|
}
|
|
|
|
void
|
|
clearCounters(PtpClock *ptpClock)
|
|
{
|
|
|
|
/* TODO: print port info */
|
|
DBG("Port counters cleared\n");
|
|
memset(&ptpClock->counters, 0, sizeof(ptpClock->counters));
|
|
|
|
}
|
|
|
|
Boolean
|
|
respectUtcOffset(RunTimeOpts *rtOpts, PtpClock *ptpClock)
|
|
{
|
|
if (ptpClock->timePropertiesDS.currentUtcOffsetValid || rtOpts->alwaysRespectUtcOffset)
|
|
{
|
|
return TRUE;
|
|
}
|
|
|
|
return FALSE;
|
|
}
|