Skip to content

File syn_ntp_server.c

File List > net > syn_ntp_server.c

Go to the documentation of this file

#include "syn_ntp_server.h"

#include <string.h>

static void write_u32_be(uint8_t *buf, uint32_t val)
{
    buf[0] = (uint8_t)((val >> 24) & 0xFFU);
    buf[1] = (uint8_t)((val >> 16) & 0xFFU);
    buf[2] = (uint8_t)((val >> 8) & 0xFFU);
    buf[3] = (uint8_t)(val & 0xFFU);
}

SYN_Status syn_ntp_server_init(SYN_NTPServer *server, uint8_t stratum,
                               uint32_t (*get_epoch_sec_cb)(void))
{
    if (server == NULL) {
        return SYN_INVALID_PARAM;
    }
    if ((stratum == 0U) || (stratum > 15U)) {
        return SYN_INVALID_PARAM;
    }

    memset(server, 0, sizeof(*server));
    server->stratum = stratum;
    server->get_epoch_sec_cb = get_epoch_sec_cb;

    return SYN_OK;
}

SYN_Status syn_ntp_server_process_packet(SYN_NTPServer *server, const uint8_t request_pkt[48],
                                         uint8_t response_pkt[48])
{
    if ((server == NULL) || (request_pkt == NULL) || (response_pkt == NULL)) {
        return SYN_INVALID_PARAM;
    }

    /* Extract Leap Indicator (LI), Version Number (VN), Mode from client packet */
    uint8_t mode = request_pkt[0] & 0x07U;
    if ((mode != 3U) && (mode != 1U)) {
        /* Not a client request (mode 3) or symmetric active (mode 1) */
        return SYN_ERROR;
    }

    memset(response_pkt, 0, 48);

    /* LI = 0 (no warning), VN = 4 (NTPv4), Mode = 4 (Server response) => 0x24 */
    response_pkt[0] = 0x24U;
    response_pkt[1] = server->stratum;
    response_pkt[2] = 6U;           /* Poll interval (64 sec) */
    response_pkt[3] = (uint8_t)-20; /* Precision ~1 microsecond (2^-20 s) */

    /* Root Delay (0) & Root Dispersion (0) */
    write_u32_be(&response_pkt[4], 0);
    write_u32_be(&response_pkt[8], 0);

    /* Reference ID: "LOCL" for local clock */
    response_pkt[12] = 'L';
    response_pkt[13] = 'O';
    response_pkt[14] = 'C';
    response_pkt[15] = 'L';

    uint32_t now_utc = 0;
    if (server->get_epoch_sec_cb != NULL) {
        now_utc = server->get_epoch_sec_cb();
    }
    uint32_t ntp_sec = now_utc + SYN_NTP_EPOCH_OFFSET;

    /* Reference Timestamp (bytes 16..23) */
    write_u32_be(&response_pkt[16], ntp_sec);
    write_u32_be(&response_pkt[20], 0);

    /* Originate Timestamp (bytes 24..31) <- Copy client transmit timestamp from bytes 40..47 */
    memcpy(&response_pkt[24], &request_pkt[40], 8);

    /* Receive Timestamp (bytes 32..39) */
    write_u32_be(&response_pkt[32], ntp_sec);
    write_u32_be(&response_pkt[36], 0);

    /* Transmit Timestamp (bytes 40..47) */
    write_u32_be(&response_pkt[40], ntp_sec);
    write_u32_be(&response_pkt[44], 0);

    return SYN_OK;
}