Перейти к содержанию

SNTP (Simple Network Time Protocol)

Микроконтроллер после сброса знает только, сколько тиков прошло с включения. Чтобы ставить настоящие даты в логах и проверять сертификаты, ему нужен один UDP-запрос к серверу времени — это и есть SNTP.

Обновлено Редакция GAW

SNTP (Simple Network Time Protocol) — упрощённая реализация протокола сетевого времени NTP, предназначенная для клиентов с единственным сервером и для серверов с одними опорными часами. По RFC 5905 (разд. 14) SNTP не реализует алгоритмы отбора и фильтрации серверов, но использует тот же пакет и тот же порт, поэтому клиенты SNTP и серверы NTP полностью совместимы. Простейший клиент отправляет один запрос по UDP и берёт из ответа поле Transmit Timestamp — так работает SNTP-клиент lwIP на ESP32.

Пакет

Заголовок NTP — 12 слов по 32 бита, то есть 48 байт (наш расчёт), в одной UDP-датаграмме на порт 123 (RFC 5905, разд. 7.2, 7.3):

ПолеРазмерЗначения
LI2 бита0 — без предупреждения, 1 — в сутках 61 с, 2 — 59 с, 3 — часы не синхронизированы
VN3 битаверсия, сейчас 4
Mode3 бита3 — клиент, 4 — сервер, 5 — широковещание
Stratum8 бит1 — первичный сервер (например, с GPS), 2–15 — вторичные, 16 — не синхронизирован
Poll, Precisionпо 8 битпоказатели степени двойки для интервала опроса и точности
Root Delay, Root Dispersionпо 32 битазадержка и разброс до первичного источника
Reference ID32 битаидентификатор источника
Reference, Origin, Receive, Transmit Timestampпо 64 битаметки времени

Клиент заполняет только Mode = 3, VN и, при желании, Transmit Timestamp, остальное — нули; сервер отвечает с Mode = 4 (RFC 4330, разд. 5). Ответ с LI = 3 означает, что часы сервера не синхронизированы (RFC 5905, разд. 7.3; в передаваемых пакетах stratum 16 принято заменять нулём), а ответ со stratum 0, нулевой меткой Transmit или Mode ≠ 4 RFC 4330 (разд. 5) велит отбросить — ставить часы по такой метке нельзя.

Метка времени и эпоха 1900 года

64-битная метка — беззнаковое число с фиксированной точкой: 32 бита секунд и 32 бита дроби с разрешением 232 пс (RFC 5905, разд. 6). Отсчёт идёт от 0 ч 1 января 1900 UTC; поле секунд охватывает 136 лет и переполняется в 2036 году. Нулевая метка — «время неизвестно». Перевод в секунды Unix: вычесть 2 208 988 800 (константа JAN_1970 в прил. A RFC 5905). RFC 4330 (разд. 3) задаёт соглашение на случай переполнения: если старший бит установлен — дата в диапазоне 1968–2036 от 1900 года, если сброшен — 2036–2104 от 6 ч 28 мин 16 с UTC 7 февраля 2036; lwIP в ESP-IDF следует ему, а time_t там 64-битный с версии 5.0, так что проблема 2038 года прошивку не затрагивает.

Полный NTP по четырём меткам считает смещение и задержку (RFC 5905, разд. 8):

θ=12[(T2−T1)+(T3−T4)],δ=(T4−T1)−(T3−T2)\theta = \tfrac{1}{2}\big[(T_2 - T_1) + (T_3 - T_4)\big], \qquad \delta = (T_4 - T_1) - (T_3 - T_2)

где T1 — отправка запроса, T2 — приём на сервере, T3 — отправка ответа, T4 — приём ответа. SNTP-клиент может ограничиться T3, но и полный расчёт по RFC 5905 обходится дёшево — стандарт его поощряет.

В прошивке

В ESP-IDF клиент запускается двумя строками: esp_sntp_config_t config = ESP_NETIF_SNTP_DEFAULT_CONFIG("pool.ntp.org"); esp_netif_sntp_init(&config);, а дождаться первой синхронизации позволяет esp_netif_sntp_sync_wait(pdMS_TO_TICKS(10000)). По умолчанию время выставляется скачком (SNTP_SYNC_MODE_IMMED через settimeofday), режим SNTP_SYNC_MODE_SMOOTH подводит часы плавно через adjtime, но при расхождении более 35 мин тоже ставит скачком. Повторный опрос — раз в час (CONFIG_LWIP_SNTP_UPDATE_DELAY), и не чаще раза в 15 с: это требование RFC 4330 (разд. 10) зашито в sntp_set_sync_interval.

Адрес сервера лучше получать по имени через DNS и не прошивать в коде чужой IP (RFC 4330, разд. 10). В глубоком сне ESP32 продолжает отсчёт по таймеру RTC, и время переживает любой сброс, кроме сброса по включению питания, поэтому ходить к серверу после каждого пробуждения не нужно; при долгих отключениях питания спасают часы реального времени с батарейкой. Правильное время нужно не только логам: без него не проверить срок действия сертификата X.509 при подключении по TLS, а записи SenML с абсолютными метками теряют смысл.

Частые вопросы

Чем SNTP отличается от NTP?

Пакет и порт те же — SNTP использует формат NTPv4 и порт UDP 123. Разница в том, что делает клиент с ответом: полный NTP опрашивает несколько серверов и фильтрует их алгоритмами из разд. 9–13 RFC 5905, а SNTP работает с одним сервером и в простейшем случае берёт только поле Transmit Timestamp (разд. 14). Серверы обоих видов полностью совместимы, поэтому ESP32 с SNTP-клиентом lwIP спокойно ходит к pool.ntp.org.

Как перевести метку NTP в время Unix?

Старшие 32 бита метки — секунды от 0 ч 1 января 1900 UTC, младшие 32 бита — дробная часть секунды с шагом 232 пс. Вычтите 2 208 988 800 (константа JAN_1970 из прил. A RFC 5905) и получите секунды Unix от 1970 года. В 2036 году 32-битное поле секунд переполнится; RFC 4330 (разд. 3) предлагает трактовать значения со сброшенным старшим битом как даты 2036–2104, и lwIP в ESP-IDF так и делает.

Как часто можно опрашивать сервер времени?

Не чаще одного раза в 15 с — это жёсткое требование RFC 4330 (разд. 10), и функция sntp_set_sync_interval в ESP-IDF не даст задать меньше. Для устройства без атомных часов разумнее интервал в часы: в ESP-IDF период CONFIG_LWIP_SNTP_UPDATE_DELAY по умолчанию равен часу. Если сервер не отвечает, интервал надо увеличивать экспоненциально, а на пакет kiss-o'-death — переключаться на другой сервер.

Источники

  1. D. Mills, J. Martin (Ed.), J. Burbank, W. Kasch. Network Time Protocol Version 4: Protocol and Algorithms Specification. RFC 5905, June 2010 — разд. 1: NTPv4, SNTPv4 — его подмножество, документ заменяет RFC 1305 и RFC 4330; разд. 6: три формата времени — 128-битная дата, 64-битная метка (32 бита секунд на 136 лет и 32 бита дроби с разрешением 232 пс), 32-битный короткий формат (16 + 16 бит); эпоха эры 0 — 0 ч 1 января 1900 UTC, эра 0 длится до 2036 года, когда поле секунд переполняется, нулевая метка означает неизвестное или несинхронизированное время; разд. 7.2, рис. 6: PORT 123, VERSION 4, MINPOLL 4 (16 с), MAXPOLL 17 (36 ч), MAXDIST 1 с, MAXSTRAT 16, TOLERANCE 15e-6; разд. 7.3, рис. 8 и 9–11: пакет — UDP-датаграмма, заголовок из 12 слов по 32 бита плюс необязательные расширения и MAC; LI 2 бита (0 нет предупреждения, 1 — 61 с, 2 — 59 с, 3 — часы не синхронизированы), VN 3 бита = 4, Mode 3 бита (3 клиент, 4 сервер, 5 широковещание), Stratum 8 бит (0 недействителен, 1 первичный сервер с GPS, 2–15 вторичные, 16 не синхронизирован; принято отображать stratum 16 и выше в 0 в передаваемых пакетах), Poll, Precision, Root Delay, Root Dispersion, Reference ID, четыре 64-битные метки Reference, Origin, Receive, Transmit; разд. 8: смещение theta = ½[(T2 − T1) + (T3 − T4)], задержка delta = (T4 − T1) − (T3 − T2), контроль дубликатов и подделок по меткам org и xmt; разд. 14: SNTP не реализует алгоритмы отбора разд. 9 и далее, предназначен для первичных серверов с одними опорными часами и клиентов с одним сервером без зависимых клиентов, простейший клиент берёт только метку Transmit из ответа; прил. A: константа JAN_1970 = 2 208 988 800 — секунды между 1900 и 1970 годами
  2. D. Mills. Simple Network Time Protocol (SNTP) Version 4 for IPv4, IPv6 and OSI. RFC 4330, January 2006 — разд. 3: метка — 64-битное беззнаковое число с фиксированной точкой, старший бит установлен с 1968 года, переполнение в 2036 году на секунде 4 294 967 296; соглашение: бит 0 установлен — диапазон 1968–2036 от 1 января 1900, сброшен — 2036–2104 от 6 ч 28 мин 16 с UTC 7 февраля 2036; часы клиента должны быть в пределах 34 лет от сервера; разд. 5: одноадресный клиент шлёт запрос с mode 3 и ждёт ответ с mode 4, широковещательный ждёт mode 5, в запросе все поля нулевые, кроме Mode, VN и необязательной Transmit Timestamp, время берётся из поля Transmit ответа; ответ отбрасывают, если Stratum или Transmit Timestamp равны 0 либо Mode не 4 (5 для широковещания); разд. 10: интервал опроса не менее 15 с, экспоненциальное увеличение, адреса серверов через DNS, реакция на пакет kiss-o'-death
  3. Espressif. ESP-IDF Programming Guide v6.1, ESP32: System Time — SNTP-клиент lwIP: конфигурация ESP_NETIF_SNTP_DEFAULT_CONFIG("pool.ntp.org") и esp_netif_sntp_init(), ожидание esp_netif_sntp_sync_wait(pdMS_TO_TICKS(10000)); режимы SNTP_SYNC_MODE_IMMED (settimeofday, по умолчанию) и SNTP_SYNC_MODE_SMOOTH (adjtime; при расхождении более 35 мин — сразу settimeofday); период CONFIG_LWIP_SNTP_UPDATE_DELAY по умолчанию один час; sntp_set_sync_interval не ниже 15 с со ссылкой на RFC 4330; метки 64-битные от 1 января 1900, переполнение 2036 года обходится соглашением по старшему биту до 2104 года; time_t 64-битный с ESP-IDF v5.0; esp_sntp_getreachability — регистр достижимости по RFC 5905; таймер RTC ведёт отсчёт времени в режимах сна и сохраняет его при любых сбросах, кроме сброса по включению питания
Страница помогла?

Нашли ошибку? ·