CMakeLists.txt

cmake_minimum_required(VERSION 3.13)
project(UDPDateTime C)

set(CMAKE_C_STANDARD 90)

add_executable(UDPDateTime main.c)

* main.c

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <time.h>
#include <netdb.h>
#include <netinet/in.h>

#define NTP_TIMESTAMP_DELTA 2208988800ull

/* NTP Packet structure */
typedef struct {
    uint8_t li_vn_mode;      /* Eight bits. li, vn, and mode.
                             li.   Two bits.   Leap indicator.
                             vn.   Three bits. Version number of the protocol.
                             mode. Three bits. Client will pick mode 3 for client. */

    uint8_t stratum;         /* Eight bits. Stratum level of the local clock. */
    uint8_t poll;            /* Eight bits. Maximum interval between successive messages. */
    int8_t precision;        /* Eight bits. Precision of the local clock. */

    uint32_t rootDelay;      /* 32 bits. Total round trip delay time. */
    uint32_t rootDispersion; /* 32 bits. Max error aloud from primary clock source. */
    uint32_t refId;          /* 32 bits. Reference clock identifier. */

    uint32_t refTm_s;        /* 32 bits. Reference time-stamp seconds. */
    uint32_t refTm_f;        /* 32 bits. Reference time-stamp fraction of a second. */

    uint32_t origTm_s;       /* 32 bits. Originate time-stamp seconds. */
    uint32_t origTm_f;       /* 32 bits. Originate time-stamp fraction of a second. */

    uint32_t rxTm_s;         /* 32 bits. Received time-stamp seconds. */
    uint32_t rxTm_f;         /* 32 bits. Received time-stamp fraction of a second. */

    uint32_t txTm_s;         /* 32 bits. Transmit time-stamp seconds. */
    uint32_t txTm_f;         /* 32 bits. Transmit time-stamp fraction of a second. */

} ntp_packet;                /* Total: 384 bits or 48 bytes. */

void error(const char *msg) {
    perror(msg);
    exit(0);
}

int main(int argc, char *argv[]) {
    int sockfd, n;
    struct addrinfo hints, *res, *p;
    ntp_packet packet;

    if (argc < 2) {
        fprintf(stderr, "Usage: %s hostname\n", argv[0]);
        exit(0);
    }

    /* Configure the hints for getaddrinfo */
    memset(&hints, 0, sizeof(hints));
    hints.ai_family = AF_INET6; /* Use IPv6 */
    hints.ai_socktype = SOCK_DGRAM; /* Datagram socket */
    hints.ai_protocol = IPPROTO_UDP; /* UDP protocol */

    /* Resolve the hostname to an address */
    if (getaddrinfo(argv[1], "123", &hints, &res) != 0) {
        error("ERROR resolving hostname");
    }

    /* Create a socket */
    sockfd = socket(AF_INET6, SOCK_DGRAM, IPPROTO_UDP);
    if (sockfd < 0) {
        error("ERROR opening socket");
    }

    /* Zero out the packet buffer */
    memset(&packet, 0, sizeof(ntp_packet));

    /* Initialize values needed for NTP request */
    packet.li_vn_mode = 0x1b; /* LI = 0, VN = 3, Mode = 3 (client) */

    /* Iterate through the results from getaddrinfo and send the packet */
    for (p = res; p != NULL; p = p->ai_next) {
        /* Send the packet to the server */
        n = sendto(sockfd, (char *)&packet, sizeof(ntp_packet), 0, p->ai_addr, p->ai_addrlen);
        if (n < 0) {
            error("ERROR sending to socket");
        }

        /* Receive the packet from the server */
        n = recv(sockfd, (char *)&packet, sizeof(ntp_packet), 0);
        if (n < 0) {
            error("ERROR receiving from socket");
        }

        break; /* Exit after successfully sending and receiving a packet */
    }

    /* Free the address info linked list */
    freeaddrinfo(res);

    /* Close the socket */
    close(sockfd);

    /* Interpret the NTP server's response */
    packet.txTm_s = ntohl(packet.txTm_s); /* Time-stamp seconds. */
    packet.txTm_f = ntohl(packet.txTm_f); /* Time-stamp fraction of a second. */

    /* Convert NTP time to UNIX time */
    time_t txTm = (time_t)(packet.txTm_s - NTP_TIMESTAMP_DELTA);

    /* Print the time */
    printf("Time: %s", ctime(&txTm));

    return 0;
}

发UDP包查询时间服务器NTP_udp

查询 2.pool.ntp.org

nslookup 2.pool.ntp.org

Server:        108.61.10.10
Address:    108.61.10.10#53

Non-authoritative answer:
Name:    2.pool.ntp.org
Address: 162.159.200.1
Name:    2.pool.ntp.org
Address: 162.159.200.123
Name:    2.pool.ntp.org
Address: 133.243.238.243
Name:    2.pool.ntp.org
Address: 45.77.20.103
Name:    2.pool.ntp.org
Address: 2603:1040:404:3::188
Name:    2.pool.ntp.org
Address: 2606:4700:f1::1
Name:    2.pool.ntp.org
Address: 2606:4700:f1::123
Name:    2.pool.ntp.org
Address: 2406:da14:10c4:9aa0:123::

得到UTC时间 2024-06-28 02:44:46    Asia/Shanghai 时区  +8h   10点44

发UDP包查询时间服务器NTP_网络协议_02

sudo tcpdump -i eth0 host 2.pool.ntp.org -XX

发UDP包查询时间服务器NTP_网络协议_03

#define NTP_TIMESTAMP_DELTA 2208988800ull  /* 这个数字代表1900年到1970年多少秒 */

var lp = 0;
for (var y = 1900; y < 1970; y++) {
    if ((y%400==0) || (y%4==0 && y%100!=0)) {
        lp++;
    }
}
console.log(lp);  /* 17 */
console.log(((1970-1900)*365 + 17)*86400);  /* 2208988800 */

Here's a breakdown of the relevant portions of the tcpdump output:

Packet 1: Sent from telaviv172.xyz to the NTP server (2.pool.ntp.org)

  • Timestamp: 02:44:46.233051
  • Source: telaviv172.xyz (IPv6 address 2a05:f480:2c00:17ff:5400:4ff:fee9:7f7f) on port 58492
  • Destination: any.time.nl.ntp on the NTP port (default 123)
Packet Breakdown:
  • Ethernet Frame:
  • 0x0000: fe00 04e9 7f7f 5600 04e9 7f7f 86dd
  • IPv6 Header:
  • 0x0010: 6000 0000 0038 1140 2a05 f480 2c00 17ff 5400 04ff fee9 7f7f
  • This part includes the source IPv6 address: 2a05:f480:2c00:17ff:5400:4ff:fee9:7f7f
  • 0x0020: 2001 0678 0008 0000 0000 0000 0000 0000 0123 e47c 007b 0038 61db 1b00
  • This part includes the destination IPv6 address and the payload (NTP data).
  • NTP Payload:
  • 0x0030: 0123 e47c 007b 0038 61db 1b00
  • The payload includes NTP data specific to the request.

Packet 2: Response from the NTP server to telaviv172.xyz

  • Timestamp: 02:44:46.233263
  • Source: any.time.nl.ntp on port 123
  • Destination: telaviv172.xyz (IPv6 address 2a05:f480:2c00:17ff:5400:4ff:fee9:7f7f) on port 58492
Packet Breakdown:
  • Ethernet Frame:
  • 0x0000: 5600 04e9 7f7f fe00 04e9 7f7f 86dd
  • IPv6 Header:
  • 0x0010: 6001 4c84 0038 113b 2001 0678 0008 0000 0000 0000 0000 0000 0123
  • This part includes the destination IPv6 address.
  • 0x0020: 2a05 f480 2c00 17ff 5400 04ff fee9 7f7f
  • This part includes the source IPv6 address.
  • NTP Payload:
  • 0x0030: 007b e47c 0038 e542 1c02 00e7 0000 0f8e 0000 0074 1e14 233d ea28
  • The payload includes NTP data specific to the response.

Relevant IPv6 Address Information:

In the first packet, from 0x0016 to 0x0026 byte positions, the IPv6 address 2a05:f480:2c00:17ff:5400:4ff:fee9:7f7f is located as follows:

  • 0x0016: 2a05 f480 2c00 17ff 5400 04ff fee9 7f7f

This matches the IPv6 address of telaviv172.xyz. This part of the packet shows the address in its full form, confirming it as the source address for this packet.

In summary, the tcpdump output confirms a successful NTP query and response transaction between telaviv172.xyz and the NTP server, with the correct IPv6 addresses included in the packets.

Let's break down the Ethernet frame information in more detail:

Ethernet Frame Breakdown:

The Ethernet frame starts from 0x0000 and includes the following information:

  • Destination MAC Address: The first 6 bytes
  • Source MAC Address: The next 6 bytes
  • Type/Length: The following 2 bytes
Ethernet Frame:
  • 0x0000: fe00 04e9 7f7f 5600 04e9 7f7f 86dd
  1. Destination MAC Address:
  • fe:00:04:e9:7f:7f
  1. Source MAC Address:
  • 56:00:04:e9:7f:7f
  1. Type/Length:
  • 0x86dd

Analysis of 0x86dd

  • 0x86dd is the EtherType field in the Ethernet frame.
  • The value 0x86dd indicates that the payload of the Ethernet frame is an IPv6 packet. This is specified by the EtherType for IPv6, which is 0x86dd.

So, the correct interpretation of the Ethernet frame details is:

  • Destination MAC Address: fe:00:04:e9:7f:7f
  • Source MAC Address: 56:00:04:e9:7f:7f
  • Type: 0x86dd (indicating an IPv6 packet)

This means your original interpretation of the MAC addresses and the following two bytes being the EtherType field for IPv6 is correct.