Andrew Boyson / net

Dependents:   oldheating gps motorhome heating

udp/ntp/ntpclient.c

Committer:
andrewboyson
Date:
2019-01-21
Revision:
112:f8694d0b8858
Child:
113:904b40231907

File content as of revision 112:f8694d0b8858:

#include <stdint.h>
#include <string.h>
#include <stdbool.h>

#include      "log.h"
#include      "clk.h"
#include  "mstimer.h"
#include  "clktime.h"
#include   "clkntp.h"
#include   "clkutc.h"
#include   "clkgov.h"
#include    "clktm.h"
#include      "net.h"
#include      "ntp.h"
#include      "dns.h"
#include      "ip4.h"
#include    "ar4.h"
#include    "ar6.h"
#include    "arp.h"
#include    "eth.h"
#include    "nr4.h"
#include    "nr6.h"
#include "ip6addr.h"
#include    "mac.h"
#include "action.h"

#define ONE_BILLION 1000000000ULL
#define ONE_MILLION     1000000LL

char    NtpClientServerName[DNS_MAX_LABEL_LENGTH+1]; 
int32_t NtpClientInitialInterval;                    
int32_t NtpClientNormalInterval;                     
int32_t NtpClientRetryInterval;                      
int32_t NtpClientOffsetMs;                           
int32_t NtpClientMaxDelayMs;                         

bool       NtpClientSendRequestsViaIp4 = false;
uint32_t   NtpClientServerIp4;
char       NtpClientServerIp6[16];
bool       NtpClientTrace   = false;

enum {
    INTERVAL_INITIAL,
    INTERVAL_NORMAL,
    INTERVAL_RETRY
};

static uint64_t startNtpMs = 0;
static int  intervalTypeNtp = INTERVAL_INITIAL;
static bool intervalCompleteNtp()
{
    uint32_t interval;
    switch(intervalTypeNtp)
    {
        case INTERVAL_INITIAL: interval = NtpClientInitialInterval; break;
        case INTERVAL_NORMAL:  interval = NtpClientNormalInterval;  break;
        case INTERVAL_RETRY:   interval = NtpClientRetryInterval;   break;
    }
    return MsTimerHasElapsed(startNtpMs, interval * 1000);
}
static void startIntervalNtp(int type)
{
    startNtpMs = MsTimerCount;
    intervalTypeNtp = type;
}

void NtpClientSetClockTime(uint64_t ori, uint64_t rec, int li)
{
    //Check the received timestamp delay
    int64_t oriTicks = ClkTimeFromNtpTimeStamp(ori);
    int64_t ntpTicks = ClkTimeFromNtpTimeStamp(rec);
    int64_t clkTicks = ClkTimeGet();
    
    int64_t roundTripTicks = clkTicks - oriTicks;
    int64_t delayMs        = roundTripTicks >> CLK_TIME_ONE_MS_ISH_SHIFT;
    int64_t limit          = NtpClientMaxDelayMs;
    if (delayMs > limit)
    {
        LogTimeF("NtpClient error: delay %lld ms is greater than limit %lld ms\r\n", delayMs, limit);
        return; 
    }
    
    if (NtpClientTrace)
    {
        int64_t diffMs = ((int64_t)(ntpTicks - clkTicks)) >> CLK_TIME_ONE_MS_ISH_SHIFT;
        LogTimeF("NtpClient difference (ext-int) is %lld ms\r\n", diffMs);
    }
    
    //Handle the LI
    if (li == 3) 
    {
        LogTimeF("NtpClient error: NTP server is not synchronised (LI = 3)\r\n");
        return; 
    }
    if (li == 1 || li == 2)
    {
        struct tm tm;
        ClkTimeToTmUtc(clkTicks, &tm);
        int year1970 = tm.tm_year - 70; //1900
        int month    = tm.tm_mon;       //0 to 11
        ClkUtcSetNextEpochMonth1970(year1970 * 12 + month + 1); //+1 as new UTC epoch is at the start of next month
        ClkUtcSetNextLeapForward(li == 1);
        ClkUtcSetNextLeapEnable(true);
    }
    if (li == 0)
    {
        ClkUtcSetNextLeapEnable(false);
    }
    
    //Set the clock
    int64_t offsetTime = NtpClientOffsetMs << CLK_TIME_ONE_MS_ISH_SHIFT;
    ClkGovSyncTime(ntpTicks + offsetTime);
    
    //Wait for next time
    startIntervalNtp(INTERVAL_NORMAL);
    ClkGovIsReceivingTime = true;
    
}

void  NtpClientInit()
{
    startIntervalNtp(INTERVAL_INITIAL);
    ClkGovIsReceivingTime = false;
}

static bool resolve4(char* server, uint32_t* pIp)
{
    //Check if have IP, if not, then request it and stop
    Nr4NameToIp(server, pIp);
    if (!*pIp)
    {
        Nr4MakeRequestForIpFromName(server); //The request is only repeated if made after a freeze time - call as often as you want.
        return false;
    }

    //Check if have MAC and, if not, request it and stop
    char mac[6];
    Ar4IpToMac(*pIp, mac);
    if (MacIsEmpty(mac))
    {
        Ar4MakeRequestForMacFromIp(*pIp); //The request is only repeated if made after a freeze time - call as often as you want.
        return false;
    }
    
    return true;
}
static bool resolve6(char* server, char* ip)
{
    //Check if have IP, if not, then request it and stop
    Nr6NameToIp(server, ip);
    if (Ip6AddressIsEmpty(ip))
    {
        Nr6MakeRequestForIpFromName(server); //The request is only repeated if made after a freeze time - call as often as you want.
        return false;
    }

    //Check if have MAC and, if not, request it and stop
    char mac[6];
    Ar6IpToMac(ip, mac);
    if (MacIsEmpty(mac))
    {
        Ar6MakeRequestForMacFromIp(ip); //The request is only repeated if made after a freeze time - call as often as you want.
        return false;
    }
    
    return true;
}
static bool haveIpAndMac(int type)
{
    if      (type == IPV4) return resolve4(NtpClientServerName, &NtpClientServerIp4);
    else if (type == IPV6) return resolve6(NtpClientServerName,  NtpClientServerIp6);
    else                   return false;
}
int NtpClientPollForPacketToSend(int type, void* pPacket, int* pSize)
{
    if (NtpClientServerName[0]) //An empty name means ntp client is not enabled
    {
        if (intervalCompleteNtp()) //Wait for the time out
        {
            bool isMulticast = NtpClientServerName[0] == '*';
            if (isMulticast || haveIpAndMac(type))
            {
                ClkGovIsReceivingTime = false;
                startIntervalNtp(INTERVAL_RETRY);
                NtpHdrWriteRequest(pPacket, pSize);
                
                if (isMulticast) return MULTICAST_NTP;
                else             return   UNICAST_NTP;
            }
        }
    }
    return DO_NOTHING;
}