diff --git a/test/unit/ntp_core.c b/test/unit/ntp_core.c index 9ff62cd..ad69e0f 100644 --- a/test/unit/ntp_core.c +++ b/test/unit/ntp_core.c @@ -36,6 +36,7 @@ static int req_length, res_length; #define NIO_CloseServerSocket(fd) assert(fd == 100) #define NIO_OpenClientSocket(addr) ((addr)->ip_addr.family != IPADDR_UNSPEC ? 101 : 0) #define NIO_CloseClientSocket(fd) assert(fd == 101) +#define NIO_IsServerSocket(fd) (fd == 100) #define NIO_SendPacket(msg, to, from, len, process_tx) (memcpy(&req_buffer, msg, len), req_length = len, 1) #define SCH_AddTimeoutByDelay(delay, handler, arg) (1 ? 102 : (handler(arg), 1)) #define SCH_AddTimeoutInClass(delay, separation, randomness, class, handler, arg) \ @@ -43,6 +44,7 @@ static int req_length, res_length; #define SCH_RemoveTimeout(id) assert(!id || id == 102) #define LCL_ReadRawTime(ts) (*ts = current_time) #define LCL_ReadCookedTime(ts, err) do {double *p = err; *ts = current_time; if (p) *p = 0.0;} while (0) +#define LCL_GetSysPrecisionAsLog() (random() % 10 - 30) #define SRC_UpdateReachability(inst, reach) #define SRC_ResetReachability(inst) @@ -55,8 +57,6 @@ add_timeout_in_class(double min_delay, double separation, double randomness, #include -static NCR_Instance inst; - static void advance_time(double x) { @@ -64,7 +64,7 @@ advance_time(double x) } static void -send_request(void) +send_request(NCR_Instance inst) { NTP_Local_Address local_addr; NTP_Local_Timestamp local_ts; @@ -76,16 +76,46 @@ send_request(void) TEST_CHECK(!inst->valid_rx); TEST_CHECK(prev_tx_count + 1 == inst->report.total_tx_count); - advance_time(1e-4); + advance_time(1e-5); + + if (random() % 2) { + local_addr.ip_addr.family = IPADDR_UNSPEC; + local_addr.if_index = INVALID_IF_INDEX; + local_addr.sock_fd = 101; + local_ts.ts = current_time; + local_ts.err = 0.0; + local_ts.source = NTP_TS_KERNEL; + + NCR_ProcessTxKnown(inst, &local_addr, &local_ts, &req_buffer.ntp_pkt, req_length); + } +} + +static void +process_request(NTP_Remote_Address *remote_addr) +{ + NTP_Local_Address local_addr; + NTP_Local_Timestamp local_ts; local_addr.ip_addr.family = IPADDR_UNSPEC; local_addr.if_index = INVALID_IF_INDEX; - local_addr.sock_fd = 101; + local_addr.sock_fd = 100; local_ts.ts = current_time; local_ts.err = 0.0; - local_ts.source = NTP_TS_DAEMON; + local_ts.source = NTP_TS_KERNEL; - NCR_ProcessTxKnown(inst, &local_addr, &local_ts, &req_buffer.ntp_pkt, req_length); + res_length = 0; + NCR_ProcessRxUnknown(remote_addr, &local_addr, &local_ts, + &req_buffer.ntp_pkt, req_length); + res_length = req_length; + res_buffer = req_buffer; + + advance_time(1e-5); + + if (random() % 2) { + local_ts.ts = current_time; + NCR_ProcessTxUnknown(remote_addr, &local_addr, &local_ts, + &res_buffer.ntp_pkt, res_length); + } } static void @@ -164,23 +194,23 @@ send_response(int interleaved, int authenticated, int allow_update, int valid_ts } static void -process_response(int valid, int updated_sync, int updated_init) +process_response(NCR_Instance inst, int good, int valid, int updated_sync, int updated_init) { NTP_Local_Address local_addr; NTP_Local_Timestamp local_ts; NTP_Packet *res; uint32_t prev_rx_count, prev_valid_count; struct timespec prev_rx_ts, prev_init_rx_ts; - int prev_open_socket; + int prev_open_socket, ret; res = &res_buffer.ntp_pkt; local_addr.ip_addr.family = IPADDR_UNSPEC; local_addr.if_index = INVALID_IF_INDEX; - local_addr.sock_fd = NTP_LVM_TO_MODE(res->lvm) == MODE_ACTIVE ? 100 : 101; + local_addr.sock_fd = NTP_LVM_TO_MODE(res->lvm) != MODE_SERVER ? 100 : 101; local_ts.ts = current_time; local_ts.err = 0.0; - local_ts.source = NTP_TS_DAEMON; + local_ts.source = NTP_TS_KERNEL; prev_rx_count = inst->report.total_rx_count; prev_valid_count = inst->report.total_valid_count; @@ -188,7 +218,12 @@ process_response(int valid, int updated_sync, int updated_init) prev_init_rx_ts = inst->init_local_rx.ts; prev_open_socket = inst->local_addr.sock_fd != INVALID_SOCK_FD; - NCR_ProcessRxKnown(inst, &local_addr, &local_ts, res, res_length); + ret = NCR_ProcessRxKnown(inst, &local_addr, &local_ts, res, res_length); + + if (good > 0) + TEST_CHECK(ret); + else if (!good) + TEST_CHECK(!ret); if (prev_open_socket) TEST_CHECK(prev_rx_count + 1 == inst->report.total_rx_count); @@ -205,9 +240,9 @@ process_response(int valid, int updated_sync, int updated_init) else TEST_CHECK(!UTI_CompareTimespecs(&inst->local_rx.ts, &prev_rx_ts)); - if (updated_init) + if (updated_init > 0) TEST_CHECK(UTI_CompareTimespecs(&inst->init_local_rx.ts, &prev_init_rx_ts)); - else + else if (!updated_init) TEST_CHECK(!UTI_CompareTimespecs(&inst->init_local_rx.ts, &prev_init_rx_ts)); if (valid) { @@ -216,17 +251,35 @@ process_response(int valid, int updated_sync, int updated_init) } } +static void +process_replay(NCR_Instance inst, NTP_Receive_Buffer *packet_queue, + int queue_length, int updated_init) +{ + do { + res_buffer = packet_queue[random() % queue_length]; + } while (!UTI_CompareNtp64(&res_buffer.ntp_pkt.transmit_ts, + &inst->remote_ntp_tx)); + process_response(inst, 0, 0, 0, updated_init); + advance_time(1e-6); +} + +#define PACKET_QUEUE_LENGTH 10 + void test_unit(void) { - char source_line[] = "127.0.0.1"; + char source_line[] = "127.0.0.1 maxdelaydevratio 1e6"; char conf[][100] = { + "allow", "port 0", + "local", "keyfile ntp_core.keys" }; - int i, j, interleaved, authenticated, valid, updated, has_updated; + int i, j, k, interleaved, authenticated, valid, updated, has_updated; CPS_NTP_Source source; NTP_Remote_Address remote_addr; + NCR_Instance inst1, inst2; + NTP_Receive_Buffer packet_queue[PACKET_QUEUE_LENGTH]; CNF_Initialise(0, 0); for (i = 0; i < sizeof conf / sizeof conf[0]; i++) @@ -241,8 +294,11 @@ test_unit(void) REF_Initialise(); KEY_Initialise(); + CNF_SetupAccessRestrictions(); + + CPS_ParseNTPSourceAdd(source_line, &source); + for (i = 0; i < 1000; i++) { - CPS_ParseNTPSourceAdd(source_line, &source); if (random() % 2) source.params.interleaved = 1; if (random() % 2) @@ -254,56 +310,125 @@ test_unit(void) TST_GetRandomAddress(&remote_addr.ip_addr, IPADDR_UNSPEC, -1); remote_addr.port = 123; - inst = NCR_GetInstance(&remote_addr, random() % 2 ? NTP_SERVER : NTP_PEER, &source.params); - NCR_StartInstance(inst); + inst1 = NCR_GetInstance(&remote_addr, random() % 2 ? NTP_SERVER : NTP_PEER, &source.params); + NCR_StartInstance(inst1); has_updated = 0; for (j = 0; j < 50; j++) { - DEBUG_LOG("iteration %d, %d", i, j); + DEBUG_LOG("client/peer test iteration %d/%d", i, j); - interleaved = random() % 2 && (inst->mode != MODE_CLIENT || - inst->tx_count < MAX_CLIENT_INTERLEAVED_TX); + interleaved = random() % 2 && (inst1->mode != MODE_CLIENT || + inst1->tx_count < MAX_CLIENT_INTERLEAVED_TX); authenticated = random() % 2; valid = (!interleaved || (source.params.interleaved && has_updated)) && (!source.params.authkey || authenticated); - updated = (valid || inst->mode == MODE_ACTIVE) && + updated = (valid || inst1->mode == MODE_ACTIVE) && (!source.params.authkey || authenticated); has_updated = has_updated || updated; - if (inst->mode == MODE_CLIENT) + if (inst1->mode == MODE_CLIENT) updated = 0; - send_request(); + send_request(inst1); send_response(interleaved, authenticated, 1, 0, 1); DEBUG_LOG("response 1"); - process_response(0, 0, updated); + process_response(inst1, 0, 0, 0, updated); if (source.params.authkey) { send_response(interleaved, authenticated, 1, 1, 0); DEBUG_LOG("response 2"); - process_response(0, 0, 0); + process_response(inst1, 0, 0, 0, 0); } send_response(interleaved, authenticated, 1, 1, 1); DEBUG_LOG("response 3"); - process_response(valid, valid, updated); + process_response(inst1, -1, valid, valid, updated); DEBUG_LOG("response 4"); - process_response(0, 0, 0); + process_response(inst1, 0, 0, 0, 0); advance_time(-1.0); send_response(interleaved, authenticated, 1, 1, 1); DEBUG_LOG("response 5"); - process_response(0, 0, updated && valid); + process_response(inst1, 0, 0, 0, updated && valid); advance_time(1.0); send_response(interleaved, authenticated, 1, 1, 1); DEBUG_LOG("response 6"); - process_response(0, valid && updated, updated); + process_response(inst1, 0, 0, valid && updated, updated); } - NCR_DestroyInstance(inst); + NCR_DestroyInstance(inst1); + + inst1 = NCR_GetInstance(&remote_addr, random() % 2 ? NTP_SERVER : NTP_PEER, &source.params); + NCR_StartInstance(inst1); + + for (j = 0; j < 20; j++) { + DEBUG_LOG("server test iteration %d/%d", i, j); + + send_request(inst1); + process_request(&remote_addr); + process_response(inst1, 1, 1, 1, 0); + advance_time(1 << inst1->local_poll); + } + + NCR_DestroyInstance(inst1); + + inst1 = NCR_GetInstance(&remote_addr, NTP_PEER, &source.params); + NCR_StartInstance(inst1); + inst2 = NCR_GetInstance(&remote_addr, NTP_PEER, &source.params); + NCR_StartInstance(inst2); + + res_length = req_length = 0; + + for (j = 0; j < 20; j++) { + DEBUG_LOG("peer replay test iteration %d/%d", i, j); + + send_request(inst1); + res_buffer = req_buffer; + assert(!res_length || res_length == req_length); + res_length = req_length; + + TEST_CHECK(inst1->valid_timestamps == (j > 0)); + + DEBUG_LOG("response 1->2"); + process_response(inst2, j > source.params.interleaved, j > 0, j > 0, 1); + + packet_queue[(j * 2) % PACKET_QUEUE_LENGTH] = res_buffer; + + for (k = 0; k < j % 4 + 1; k++) { + DEBUG_LOG("replay ?->1 %d", k); + process_replay(inst1, packet_queue, MIN(j * 2 + 1, PACKET_QUEUE_LENGTH), k ? -1 : 1); + DEBUG_LOG("replay ?->2 %d", k); + process_replay(inst2, packet_queue, MIN(j * 2 + 1, PACKET_QUEUE_LENGTH), -1); + } + + advance_time(1 << (source.params.minpoll - 1)); + + send_request(inst2); + res_buffer = req_buffer; + assert(res_length == req_length); + + TEST_CHECK(inst2->valid_timestamps == (j > 0)); + + DEBUG_LOG("response 2->1"); + process_response(inst1, 1, 1, 1, 1); + + packet_queue[(j * 2 + 1) % PACKET_QUEUE_LENGTH] = res_buffer; + + for (k = 0; k < j % 4 + 1; k++) { + DEBUG_LOG("replay ?->1 %d", k); + process_replay(inst1, packet_queue, MIN(j * 2 + 2, PACKET_QUEUE_LENGTH), k ? -1 : 1); + DEBUG_LOG("replay ?->2 %d", k); + process_replay(inst2, packet_queue, MIN(j * 2 + 2, PACKET_QUEUE_LENGTH), -1); + } + + advance_time(1 << (source.params.minpoll - 1)); + } + + NCR_DestroyInstance(inst1); + NCR_DestroyInstance(inst2); } KEY_Finalise();