A stack which works with or without an Mbed os library. Provides IPv4 or IPv6 with a full 1500 byte buffer.

Dependents:   oldheating gps motorhome heating

udp/ntp.cpp

Committer:
andrewboyson
Date:
2017-07-03
Revision:
22:914b970356f0
Parent:
20:23f2b29b48ea
Child:
35:93c39d260a83

File content as of revision 22:914b970356f0:

#include  "mbed.h"
#include   "log.h"
#include   "net.h"
#include   "udp.h"
#include    "ar.h"
#include   "arp.h"
#include    "io.h"
#include   "eth.h"

#define DEBUG false

#define HEADER_SIZE 48

#define CLIENT 3
#define SERVER 4

uint64_t (*NtpGetClockNowFunction)    ();

bool       NtpServerEnable  = false;
uint64_t (*NtpGetClockRefFunction)    ();
int      (*NtpGetClockStratumFunction)();
char *   (*NtpGetClockIdentFunction)  ();

bool       NtpClientRequest = false;
uint32_t   NtpServerIp4 = 0;
void     (*NtpSetClockFunction)       (uint64_t ori, uint64_t rec);

__packed struct header {
    unsigned Mode : 3;
    unsigned VN   : 3;
    unsigned LI   : 2;
    uint8_t  Stratum; 
     int8_t  Poll;
     int8_t  Precision;
    uint32_t RootDelay;
    uint32_t Dispersion;
    char     RefIdentifier[4];
    
    uint64_t RefTimeStamp;
    uint64_t OriTimeStamp;
    uint64_t RecTimeStamp;
    uint64_t TraTimeStamp;
};

static int handleRequest(struct header* pHeader)
{
    if (!NtpServerEnable) return DO_NOTHING;
    
    if (!NtpGetClockRefFunction || !NtpGetClockNowFunction || !NtpGetClockStratumFunction || !NtpGetClockIdentFunction)
    {
        LogTimeF("NtpHandleRequest - NTP server is enabled but has not been plumbed into a clock\r\n");
        return DO_NOTHING;
    }
        
    uint64_t refNtp  = NtpGetClockRefFunction();
    uint64_t nowNtp  = NtpGetClockNowFunction();
    int      stratum = NtpGetClockStratumFunction();
    char*    ident   = NtpGetClockIdentFunction();

    pHeader->Mode       = SERVER;
    pHeader->LI         = 0;
    pHeader->Stratum    = stratum;
    pHeader->Poll       = 0;
    pHeader->Precision  = 0;
    pHeader->RootDelay  = 0;
    pHeader->Dispersion = 0;
    pHeader->RefIdentifier[0] = ident[0]; //For stratum 1 (reference clock), this is a four-octet, left-justified, zero-padded ASCII string.
    pHeader->RefIdentifier[1] = ident[1];
    pHeader->RefIdentifier[2] = ident[2];
    pHeader->RefIdentifier[3] = ident[3];
    
    pHeader->RefTimeStamp = NetToHost64(refNtp);
    pHeader->OriTimeStamp = pHeader->TraTimeStamp;
    pHeader->RecTimeStamp = NetToHost64(nowNtp);
    pHeader->TraTimeStamp = NetToHost64(nowNtp);
    return UNICAST;
}
static int handleReply(struct header* pHeader)
{
    if (!NtpGetClockNowFunction || !NtpSetClockFunction)
    {
        LogTimeF("Ntp reply has been received but NTP has not been plumbed into a clock\r\n");
        return DO_NOTHING;
    }
    if (pHeader->Mode != SERVER)
    {
        LogTimeF("Ntp reply has been received but mode is not SERVER it is %d\r\n", pHeader->Mode);
        return DO_NOTHING;
    }
    if (DEBUG)
    {
        LogTimeF("NTP received reply\r\n");
        LogTimeF("REF %llu\r\n", NetToHost64(pHeader->RefTimeStamp));
        LogTimeF("ORI %llu\r\n", NetToHost64(pHeader->OriTimeStamp));
        LogTimeF("REC %llu\r\n", NetToHost64(pHeader->RecTimeStamp));
        LogTimeF("TRA %llu\r\n", NetToHost64(pHeader->TraTimeStamp));
    }
    uint64_t ori = NetToHost64(pHeader->OriTimeStamp);
    uint64_t rec = NetToHost64(pHeader->RecTimeStamp);
    
    NtpSetClockFunction(ori, rec);
    return DO_NOTHING;
}
int NtpHandlePacketReceived(int* pSize, void * pPacket)
{
    if (*pSize != HEADER_SIZE)
    {
        LogTimeF("\r\nNTP packet wrong size %d\r\n", *pSize);
        return DO_NOTHING;
    }
    struct header* pHeader = (struct header*)pPacket;
    
    switch (pHeader->Mode)
    {
        case CLIENT: return handleRequest(pHeader);            
        case SERVER: return handleReply(pHeader);
        default:
            LogTimeF("\r\nNTP packet unknown mode %d\r\n", pHeader->Mode);
            return DO_NOTHING;
    }
}
static int sendRequest(void* pPacket, int* pSize)
{
    struct header* pHeader = (struct header*)pPacket;
    
    pHeader->Mode       = CLIENT;
    pHeader->VN         = 3;
    pHeader->LI         = 0;
    pHeader->Stratum    = 0;
    pHeader->Poll       = 0;
    pHeader->Precision  = 0;
    pHeader->RootDelay  = 0;
    pHeader->Dispersion = 0;
    pHeader->RefIdentifier[0] = 0;
    pHeader->RefIdentifier[1] = 0;
    pHeader->RefIdentifier[2] = 0;
    pHeader->RefIdentifier[3] = 0;
    
    pHeader->RefTimeStamp = 0;
    pHeader->OriTimeStamp = 0;
    pHeader->RecTimeStamp = 0;
    pHeader->TraTimeStamp = NetToHost64(NtpGetClockNowFunction());
    *pSize = HEADER_SIZE;

    return UNICAST_NTP;
}
static bool arpRequested = true;
static uint32_t elapsed = 0;
static uint32_t started = 0;
int NtpPollForPacketToSend(int type, void* pPacket, int* pSize)
{
    if (!NtpClientRequest) return DO_NOTHING; //Wait until a request for time is made
    if (type != IPV4) return DO_NOTHING;      //Only have an IP4 address at this moment

    if (!NtpGetClockNowFunction || !NtpSetClockFunction || !NtpServerIp4)
    {
        LogTimeF("NtpPollForRequestToSend - A request to send a client message has been made but NTP has not been plumbed into a clock\r\n");
        NtpClientRequest = false;
        return DO_NOTHING;
    }
    
    //Check if have MAC
    char mac[6];
    ArRev4(NtpServerIp4, mac);
    if (mac[0] || mac[1] || mac[2] || mac[3] || mac[4] || mac[5])
    {
        //Yes - send request
        if (DEBUG) LogTimeF("Sending NTP request\r\n");
        NtpClientRequest = false;
        arpRequested = false;
        return sendRequest(pPacket, pSize);
    }
    else
    {
        //No - send RARP
        if (!arpRequested)
        {
            if (DEBUG) LogTimeF("Sending NTP RARP request\r\n");
            ArpAddressToResolve = NtpServerIp4;
            ArpResolveRequestFlag = true;
            arpRequested = true;
            started = elapsed;
        }
        else
        {
            if (elapsed - started > 2)
            {
                if (DEBUG) LogTimeF("Reaped NTP RARP request\r\n");
                NtpClientRequest = false;
                arpRequested = false;
            }
        }
        return DO_NOTHING;
    }
}

void NtpTick()
{
    elapsed++;
}