1 /*
2 * Copyright (c) 2017 Linaro Limited
3 * Copyright (c) 2019 Intel Corporation
4 *
5 * SPDX-License-Identifier: Apache-2.0
6 */
7
8 #include <zephyr/logging/log.h>
9 LOG_MODULE_REGISTER(net_sntp, CONFIG_SNTP_LOG_LEVEL);
10
11 #include <zephyr/net/sntp.h>
12 #include "sntp_pkt.h"
13
14 #define SNTP_LI_MAX 3
15 #define SNTP_VERSION_NUMBER 3
16 #define SNTP_MODE_CLIENT 3
17 #define SNTP_MODE_SERVER 4
18 #define SNTP_STRATUM_KOD 0 /* kiss-o'-death */
19 #define OFFSET_1970_JAN_1 2208988800
20
sntp_pkt_dump(struct sntp_pkt * pkt)21 static void sntp_pkt_dump(struct sntp_pkt *pkt)
22 {
23 if (!pkt) {
24 return;
25 }
26
27 NET_DBG("li %x", SNTP_GET_LI(pkt->lvm));
28 NET_DBG("vn %x", SNTP_GET_VN(pkt->lvm));
29 NET_DBG("mode %x", SNTP_GET_MODE(pkt->lvm));
30 NET_DBG("stratum: %x", pkt->stratum);
31 NET_DBG("poll: %x", pkt->poll);
32 NET_DBG("precision: %x", pkt->precision);
33 NET_DBG("root_delay: %x", pkt->root_delay);
34 NET_DBG("root_dispersion: %x", pkt->root_dispersion);
35 NET_DBG("ref_id: %x", pkt->ref_id);
36 NET_DBG("ref_tm_s: %x", pkt->ref_tm_s);
37 NET_DBG("ref_tm_f: %x", pkt->ref_tm_f);
38 NET_DBG("orig_tm_s: %x", pkt->orig_tm_s);
39 NET_DBG("orig_tm_f: %x", pkt->orig_tm_f);
40 NET_DBG("rx_tm_s: %x", pkt->rx_tm_s);
41 NET_DBG("rx_tm_f: %x", pkt->rx_tm_f);
42 NET_DBG("tx_tm_s: %x", pkt->tx_tm_s);
43 NET_DBG("tx_tm_f: %x", pkt->tx_tm_f);
44 }
45
parse_response(uint8_t * data,uint16_t len,uint32_t orig_ts,struct sntp_time * time)46 static int32_t parse_response(uint8_t *data, uint16_t len, uint32_t orig_ts,
47 struct sntp_time *time)
48 {
49 struct sntp_pkt *pkt = (struct sntp_pkt *)data;
50 uint32_t ts;
51
52 sntp_pkt_dump(pkt);
53
54 if (ntohl(pkt->orig_tm_s) != orig_ts) {
55 NET_DBG("Mismatch originate timestamp: %d, expect: %d",
56 ntohl(pkt->orig_tm_s), orig_ts);
57 return -EINVAL;
58 }
59
60 if (SNTP_GET_MODE(pkt->lvm) != SNTP_MODE_SERVER) {
61 /* For unicast and manycast, server should return 4.
62 * For broadcast (which is not supported now), server should
63 * return 5.
64 */
65 NET_DBG("Unexpected mode: %d", SNTP_GET_MODE(pkt->lvm));
66 return -EINVAL;
67 }
68
69 if (pkt->stratum == SNTP_STRATUM_KOD) {
70 NET_DBG("kiss-o'-death stratum");
71 return -EBUSY;
72 }
73
74 if (ntohl(pkt->tx_tm_s) == 0 && ntohl(pkt->tx_tm_f) == 0) {
75 NET_DBG("zero transmit timestamp");
76 return -EINVAL;
77 }
78
79 time->fraction = ntohl(pkt->tx_tm_f);
80 ts = ntohl(pkt->tx_tm_s);
81
82 /* Check if most significant bit is set */
83 if (ts & 0x80000000) {
84 /* UTC time is reckoned from 0h 0m 0s UTC
85 * on 1 January 1900.
86 */
87 if (ts >= OFFSET_1970_JAN_1) {
88 time->seconds = ts - OFFSET_1970_JAN_1;
89 } else {
90 return -EINVAL;
91 }
92 } else {
93 /* UTC time is reckoned from 6h 28m 16s UTC
94 * on 7 February 2036.
95 */
96 time->seconds = ts + 0x100000000ULL - OFFSET_1970_JAN_1;
97 }
98
99 return 0;
100 }
101
sntp_recv_response(struct sntp_ctx * sntp,uint32_t timeout,struct sntp_time * time)102 static int sntp_recv_response(struct sntp_ctx *sntp, uint32_t timeout,
103 struct sntp_time *time)
104 {
105 struct sntp_pkt buf = { 0 };
106 int status;
107 int rcvd;
108
109 status = zsock_poll(sntp->sock.fds, sntp->sock.nfds, timeout);
110 if (status < 0) {
111 NET_ERR("Error in poll:%d", errno);
112 return -errno;
113 }
114
115 if (status == 0) {
116 return -ETIMEDOUT;
117 }
118
119 rcvd = zsock_recv(sntp->sock.fd, (uint8_t *)&buf, sizeof(buf), 0);
120 if (rcvd < 0) {
121 return -errno;
122 }
123
124 if (rcvd != sizeof(struct sntp_pkt)) {
125 return -EMSGSIZE;
126 }
127
128 status = parse_response((uint8_t *)&buf, sizeof(buf),
129 sntp->expected_orig_ts,
130 time);
131 return status;
132 }
133
get_uptime_in_sec(void)134 static uint32_t get_uptime_in_sec(void)
135 {
136 uint64_t time;
137
138 time = k_uptime_get_32();
139
140 return time / MSEC_PER_SEC;
141 }
142
sntp_init(struct sntp_ctx * ctx,struct sockaddr * addr,socklen_t addr_len)143 int sntp_init(struct sntp_ctx *ctx, struct sockaddr *addr, socklen_t addr_len)
144 {
145 int ret;
146
147 if (!ctx || !addr) {
148 return -EFAULT;
149 }
150
151 memset(ctx, 0, sizeof(struct sntp_ctx));
152
153 ctx->sock.fd = zsock_socket(addr->sa_family, SOCK_DGRAM, IPPROTO_UDP);
154 if (ctx->sock.fd < 0) {
155 NET_ERR("Failed to create UDP socket %d", errno);
156 return -errno;
157 }
158
159 ret = zsock_connect(ctx->sock.fd, addr, addr_len);
160 if (ret < 0) {
161 (void)zsock_close(ctx->sock.fd);
162 NET_ERR("Cannot connect to UDP remote : %d", errno);
163 return -errno;
164 }
165
166 ctx->sock.fds[ctx->sock.nfds].fd = ctx->sock.fd;
167 ctx->sock.fds[ctx->sock.nfds].events = ZSOCK_POLLIN;
168 ctx->sock.nfds++;
169
170 return 0;
171 }
172
sntp_query(struct sntp_ctx * ctx,uint32_t timeout,struct sntp_time * time)173 int sntp_query(struct sntp_ctx *ctx, uint32_t timeout, struct sntp_time *time)
174 {
175 struct sntp_pkt tx_pkt = { 0 };
176 int ret = 0;
177
178 if (!ctx || !time) {
179 return -EFAULT;
180 }
181
182 /* prepare request pkt */
183 SNTP_SET_LI(tx_pkt.lvm, 0);
184 SNTP_SET_VN(tx_pkt.lvm, SNTP_VERSION_NUMBER);
185 SNTP_SET_MODE(tx_pkt.lvm, SNTP_MODE_CLIENT);
186 ctx->expected_orig_ts = get_uptime_in_sec() + OFFSET_1970_JAN_1;
187 tx_pkt.tx_tm_s = htonl(ctx->expected_orig_ts);
188
189 ret = zsock_send(ctx->sock.fd, (uint8_t *)&tx_pkt, sizeof(tx_pkt), 0);
190 if (ret < 0) {
191 NET_ERR("Failed to send over UDP socket %d", ret);
192 return ret;
193 }
194
195 return sntp_recv_response(ctx, timeout, time);
196 }
197
sntp_close(struct sntp_ctx * ctx)198 void sntp_close(struct sntp_ctx *ctx)
199 {
200 if (ctx) {
201 (void)zsock_close(ctx->sock.fd);
202 }
203 }
204