#c #nat #ntp
#c #nat #ntp
Вопрос:
У меня есть самостоятельно реализованный NTP-сервер, работающий на компьютере с Ubuntu.
Клиент NTP — это еще одна машина Ubuntu с установленными утилитами ntpdate и ntpd. Сервер имеет адрес 192.168.2.200, а клиент — 192.168.2.74 . Если я запущу:
ntpdate 192.168.2.200
Или
ntpd -c /root/NTP.conf
С помощью NTP.conf
server 192.168.2.200 minpoll 0 maxpoll 0 prefer
Клиентская ОС будет синхронизироваться с моим сервером.
Следующий шаг — поместить сервер за NAT.
Клиент NTP по-прежнему имеет адрес 192.168.2.74, но теперь сервер находится за компьютером с Windows 10 с адресом 192.168.2.75 .
Я установил переадресацию портов, чтобы пересылать UDP-пакеты, направленные на 192.168.2.75:123, на внутренний адрес 192.168.2.200:123, где расположен сервер. По какой-то причине, даже если клиент (2.74) получит тот же ответ от сервера (2.200 соответствует 2.75), он не будет синхронизироваться.
Это то, что я сделал до сих пор:
Определите пользовательскую структуру для сообщения NTP, как определено в rfc5905:
typedef struct
{
uint8_t hdrhead[4];
uint32_t delay;
uint32_t dispersion;
uint32_t reference_id;
uint64_t reference_ts;
uint64_t origin_ts;
uint64_t receive_ts;
uint64_t transmit_ts;
} ntp_msg5905;
В основной функции у меня есть простой цикл while:
// socket initialization...
while (1)
{
uint8_t recv_msg[NTP_MSGLEN];
while (recvfrom(s, recv_msg, NTP_MSGLEN, 0, (struct sockaddr*) amp;src_addr, amp;src_addrlen) < NTP_MSGLEN)
{
_log_("Arrived wrong NTP request: message too short", stdout);
}
_log_("Arrived NTP request", stdout);
uint64_t recvtime;
timestamp_64(amp;recvtime);
ntp_reply(s, amp;src_addr , src_addrlen, *(ntp_msg5905*)recv_msg, recvtime);
}
функция timestamp_64() просто создает 64-битную временную метку, полученную с помощью gettimeofday(), ничего особенного или связанного с ntp.
Функция ntp_reply является:
void ntp_reply(int clisock, struct sockaddr_in* saddr_p, socklen_t saddrlen, ntp_msg5905 request, uint64_t recvtime)
{
ntp_msg5905 reply;
memset(amp;reply, 0x00, NTP_MSGLEN);
reply.hdrhead[STRATUM] = NTP_STRATUM_PRIMARY_SERVER;
reply.hdrhead[PREC] = NTP_HOST_PRECISION;
reply.delay = NTP_NODELAY;
reply.dispersion = NTP_NODISPERSION;
reply.reference_id = NTP_REF_ID_GPS;
reply.hdrhead[LIVN] = request.hdrhead[LIVN] amp; 0x38 NTP_MODE_SERVER;
reply.hdrhead[POLL] = request.hdrhead[POLL];
reply.receive_ts = recvtime;
reply.origin_ts = request.transmit_ts;
uint64_t* rts = amp;reply.reference_ts;
uint64_t* tts = amp;reply.transmit_ts;
timestamp_64(rts);
timestamp_64(tts);
if (sendto(clisock, (char*) amp;reply, NTP_MSGLEN, 0, (struct sockaddr*) saddr_p, saddrlen) < NTP_MSGLEN)
{
_log_("Cannot send NTP reply", stdout);
}
else
{
_log_("Request reply sent back to client", stdout);
}
}
Глобальный определяет ниже:
/* Global NTP parameters */
#define NTP_UTC 2208988800U
#define NTP_PORT 123
#define NTP_MSGLEN 48
/* NTP header's first 4 bytes */
#define LIVN 0
#define STRATUM 1
#define POLL 2
#define PREC 3
/* NTP header field values */
#define NTP_MODE_SERVER 4
#define NTP_STRATUM_PRIMARY_SERVER 1
#define NTP_HOST_PRECISION -6
#define NTP_NODELAY 0
#define NTP_NODISPERSION 0
#define NTP_REF_ID_GPS *(uint32_t*)("GPS")
Где я ошибаюсь?
Комментарии:
1. Работает ли он без NAT?
2. Да, если я напрямую подключаю клиент и сервер через кабель Ethernet, синхронизация работает
3. Можем ли мы видеть пакеты запросов и ответов, которые видит машина, отправившая запрос? Вы можете использовать какой-нибудь инструмент, который может сбрасывать исходные и целевые IP-адреса и порты. Держу пари, что IP-адрес источника ответа не совпадает с IP-адресом назначения запроса на запрашивающем компьютере. (Потому что NAT неверен. Для этого вам нужен двойной NAT.)