/*- * 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; }