ntp: refactor NTP_Packet structure for extension fields

This commit is contained in:
Miroslav Lichvar 2019-02-15 16:18:39 +01:00
parent 5a09adebfd
commit d29bef93e9
8 changed files with 58 additions and 66 deletions

5
keys.c
View file

@ -135,8 +135,9 @@ determine_hash_delay(uint32_t key_id)
for (i = 0; i < 10; i++) { for (i = 0; i < 10; i++) {
LCL_ReadRawTime(&before); LCL_ReadRawTime(&before);
KEY_GenerateAuth(key_id, (unsigned char *)&pkt, NTP_NORMAL_PACKET_LENGTH, KEY_GenerateAuth(key_id, (unsigned char *)&pkt, NTP_HEADER_LENGTH,
(unsigned char *)&pkt.auth_data, sizeof (pkt.auth_data)); (unsigned char *)&pkt + NTP_HEADER_LENGTH,
sizeof (pkt) - NTP_HEADER_LENGTH);
LCL_ReadRawTime(&after); LCL_ReadRawTime(&after);
diff = UTI_DiffTimespecsToDouble(&after, &before); diff = UTI_DiffTimespecsToDouble(&after, &before);

31
ntp.h
View file

@ -47,18 +47,18 @@ typedef uint32_t NTP_int32;
/* Maximum stratum number (infinity) */ /* Maximum stratum number (infinity) */
#define NTP_MAX_STRATUM 16 #define NTP_MAX_STRATUM 16
/* The minimum valid length of an extension field */
#define NTP_MIN_EXTENSION_LENGTH 16
/* The maximum assumed length of all extension fields in received
packets (RFC 5905 doesn't specify a limit on length or number of
extension fields in one packet) */
#define NTP_MAX_EXTENSIONS_LENGTH 1024
/* The minimum and maximum supported length of MAC */ /* The minimum and maximum supported length of MAC */
#define NTP_MIN_MAC_LENGTH (4 + 16) #define NTP_MIN_MAC_LENGTH (4 + 16)
#define NTP_MAX_MAC_LENGTH (4 + MAX_HASH_LENGTH) #define NTP_MAX_MAC_LENGTH (4 + MAX_HASH_LENGTH)
/* The minimum valid length of an extension field */
#define NTP_MIN_EF_LENGTH 16
/* The maximum assumed length of all extension fields in an NTP packet,
including a MAC (RFC 5905 doesn't specify a limit on length or number of
extension fields in one packet) */
#define NTP_MAX_EXTENSIONS_LENGTH (1024 + NTP_MAX_MAC_LENGTH)
/* The maximum length of MAC in NTPv4 packets which allows deterministic /* The maximum length of MAC in NTPv4 packets which allows deterministic
parsing of extension fields (RFC 7822) */ parsing of extension fields (RFC 7822) */
#define NTP_MAX_V4_MAC_LENGTH (4 + 20) #define NTP_MAX_V4_MAC_LENGTH (4 + 20)
@ -93,21 +93,10 @@ typedef struct {
NTP_int64 receive_ts; NTP_int64 receive_ts;
NTP_int64 transmit_ts; NTP_int64 transmit_ts;
/* Optional extension fields, we don't send packets with them yet */ uint8_t extensions[NTP_MAX_EXTENSIONS_LENGTH];
/* uint8_t extensions[] */
/* Optional message authentication code (MAC) */
NTP_int32 auth_keyid;
uint8_t auth_data[NTP_MAX_MAC_LENGTH - 4];
} NTP_Packet; } NTP_Packet;
#define NTP_NORMAL_PACKET_LENGTH (int)offsetof(NTP_Packet, auth_keyid) #define NTP_HEADER_LENGTH (int)offsetof(NTP_Packet, extensions)
/* The buffer used to hold a datagram read from the network */
typedef struct {
NTP_Packet ntp_pkt;
uint8_t extensions[NTP_MAX_EXTENSIONS_LENGTH];
} NTP_Receive_Buffer;
/* Macros to work with the lvm field */ /* Macros to work with the lvm field */
#define NTP_LVM_TO_LEAP(lvm) (((lvm) >> 6) & 0x3) #define NTP_LVM_TO_LEAP(lvm) (((lvm) >> 6) & 0x3)

View file

@ -1067,7 +1067,7 @@ transmit_packet(NTP_Mode my_mode, /* The mode this machine wants to be */
if (smooth_time) if (smooth_time)
UTI_AddDoubleToTimespec(&local_transmit, smooth_offset, &local_transmit); UTI_AddDoubleToTimespec(&local_transmit, smooth_offset, &local_transmit);
length = NTP_NORMAL_PACKET_LENGTH; length = NTP_HEADER_LENGTH;
/* Authenticate the packet */ /* Authenticate the packet */
@ -1083,19 +1083,18 @@ transmit_packet(NTP_Mode my_mode, /* The mode this machine wants to be */
if (auth_mode == AUTH_SYMMETRIC) { if (auth_mode == AUTH_SYMMETRIC) {
/* Truncate long MACs in NTPv4 packets to allow deterministic parsing /* Truncate long MACs in NTPv4 packets to allow deterministic parsing
of extension fields (RFC 7822) */ of extension fields (RFC 7822) */
max_auth_len = version == 4 ? max_auth_len = (version == 4 ?
NTP_MAX_V4_MAC_LENGTH - 4 : sizeof (message.auth_data); NTP_MAX_V4_MAC_LENGTH : (sizeof (message) - length)) - 4;
auth_len = KEY_GenerateAuth(key_id, (unsigned char *) &message, auth_len = KEY_GenerateAuth(key_id, (unsigned char *)&message, length,
offsetof(NTP_Packet, auth_keyid), (unsigned char *)&message + length + 4, max_auth_len);
(unsigned char *)&message.auth_data, max_auth_len);
if (!auth_len) { if (!auth_len) {
DEBUG_LOG("Could not generate auth data with key %"PRIu32, key_id); DEBUG_LOG("Could not generate auth data with key %"PRIu32, key_id);
return 0; return 0;
} }
message.auth_keyid = htonl(key_id); *(uint32_t *)((unsigned char *)&message + length) = htonl(key_id);
length += sizeof (message.auth_keyid) + auth_len; length += 4 + auth_len;
} else if (auth_mode == AUTH_MSSNTP) { } else if (auth_mode == AUTH_MSSNTP) {
/* MS-SNTP packets are signed (asynchronously) by ntp_signd */ /* MS-SNTP packets are signed (asynchronously) by ntp_signd */
return NSD_SignAndSendPacket(key_id, &message, where_to, from, length); return NSD_SignAndSendPacket(key_id, &message, where_to, from, length);
@ -1294,7 +1293,7 @@ check_packet_format(NTP_Packet *message, int length)
return 0; return 0;
} }
if (length < NTP_NORMAL_PACKET_LENGTH || (unsigned int)length % 4) { if (length < NTP_HEADER_LENGTH || (unsigned int)length % 4) {
DEBUG_LOG("NTP packet has invalid length %d", length); DEBUG_LOG("NTP packet has invalid length %d", length);
return 0; return 0;
} }
@ -1331,7 +1330,7 @@ check_packet_auth(NTP_Packet *pkt, int length,
/* Go through extension fields and see if there is a valid MAC */ /* Go through extension fields and see if there is a valid MAC */
version = NTP_LVM_TO_VERSION(pkt->lvm); version = NTP_LVM_TO_VERSION(pkt->lvm);
i = NTP_NORMAL_PACKET_LENGTH; i = NTP_HEADER_LENGTH;
data = (void *)pkt; data = (void *)pkt;
while (1) { while (1) {
@ -1355,7 +1354,7 @@ check_packet_auth(NTP_Packet *pkt, int length,
/* If it's an NTPv4 packet with long MAC and no extension fields, /* If it's an NTPv4 packet with long MAC and no extension fields,
rewrite the version in the packet to respond with long MAC too */ rewrite the version in the packet to respond with long MAC too */
if (version == 4 && NTP_NORMAL_PACKET_LENGTH + remainder == length && if (version == 4 && NTP_HEADER_LENGTH + remainder == length &&
remainder > NTP_MAX_V4_MAC_LENGTH) remainder > NTP_MAX_V4_MAC_LENGTH)
pkt->lvm = NTP_LVM(NTP_LVM_TO_LEAP(pkt->lvm), 3, NTP_LVM_TO_MODE(pkt->lvm)); pkt->lvm = NTP_LVM(NTP_LVM_TO_LEAP(pkt->lvm), 3, NTP_LVM_TO_MODE(pkt->lvm));
@ -1365,9 +1364,9 @@ check_packet_auth(NTP_Packet *pkt, int length,
/* Check if this is a valid NTPv4 extension field and skip it. It should /* Check if this is a valid NTPv4 extension field and skip it. It should
have a 16-bit type, 16-bit length, and data padded to 32 bits. */ have a 16-bit type, 16-bit length, and data padded to 32 bits. */
if (version == 4 && remainder >= NTP_MIN_EXTENSION_LENGTH) { if (version == 4 && remainder >= NTP_MIN_EF_LENGTH) {
ext_length = ntohs(*(uint16_t *)(data + i + 2)); ext_length = ntohs(*(uint16_t *)(data + i + 2));
if (ext_length >= NTP_MIN_EXTENSION_LENGTH && if (ext_length >= NTP_MIN_EF_LENGTH &&
ext_length <= remainder && ext_length % 4 == 0) { ext_length <= remainder && ext_length % 4 == 0) {
i += ext_length; i += ext_length;
continue; continue;

View file

@ -391,8 +391,7 @@ process_message(SCK_Message *message, int sock_fd, int event)
UTI_DiffTimespecsToDouble(&sched_ts, &local_ts.ts), local_ts.source); UTI_DiffTimespecsToDouble(&sched_ts, &local_ts.ts), local_ts.source);
/* Just ignore the packet if it's not of a recognized length */ /* Just ignore the packet if it's not of a recognized length */
if (message->length < NTP_NORMAL_PACKET_LENGTH || if (message->length < NTP_HEADER_LENGTH || message->length > sizeof (NTP_Packet)) {
message->length > sizeof (NTP_Receive_Buffer)) {
DEBUG_LOG("Unexpected length"); DEBUG_LOG("Unexpected length");
return; return;
} }

View file

@ -775,7 +775,7 @@ NIO_Linux_ProcessMessage(SCK_Message *message, NTP_Local_Address *local_addr,
return 1; return 1;
} }
if (message->length < NTP_NORMAL_PACKET_LENGTH) if (message->length < NTP_HEADER_LENGTH)
return 1; return 1;
NSR_ProcessTx(&message->remote_addr.ip, local_addr, local_ts, message->data, message->length); NSR_ProcessTx(&message->remote_addr.ip, local_addr, local_ts, message->data, message->length);

View file

@ -323,7 +323,7 @@ NSD_SignAndSendPacket(uint32_t key_id, NTP_Packet *packet, NTP_Remote_Address *r
return 0; return 0;
} }
if (length != NTP_NORMAL_PACKET_LENGTH) { if (length != NTP_HEADER_LENGTH) {
DEBUG_LOG("Invalid packet length"); DEBUG_LOG("Invalid packet length");
return 0; return 0;
} }

View file

@ -59,7 +59,7 @@ struct Message {
struct iovec iov; struct iovec iov;
/* Buffer of sufficient length for all expected messages */ /* Buffer of sufficient length for all expected messages */
union { union {
NTP_Receive_Buffer ntp_msg; NTP_Packet ntp_msg;
CMD_Request cmd_request; CMD_Request cmd_request;
CMD_Reply cmd_reply; CMD_Reply cmd_reply;
} msg_buf; } msg_buf;

View file

@ -31,7 +31,7 @@
#ifdef FEAT_NTP #ifdef FEAT_NTP
static struct timespec current_time; static struct timespec current_time;
static NTP_Receive_Buffer req_buffer, res_buffer; static NTP_Packet req_buffer, res_buffer;
static int req_length, res_length; static int req_length, res_length;
#define NIO_OpenServerSocket(addr) ((addr)->ip_addr.family != IPADDR_UNSPEC ? 100 : 0) #define NIO_OpenServerSocket(addr) ((addr)->ip_addr.family != IPADDR_UNSPEC ? 100 : 0)
@ -101,7 +101,7 @@ send_request(NCR_Instance inst)
local_ts.err = 0.0; local_ts.err = 0.0;
local_ts.source = NTP_TS_KERNEL; local_ts.source = NTP_TS_KERNEL;
NCR_ProcessTxKnown(inst, &local_addr, &local_ts, &req_buffer.ntp_pkt, req_length); NCR_ProcessTxKnown(inst, &local_addr, &local_ts, &req_buffer, req_length);
} }
} }
@ -120,7 +120,7 @@ process_request(NTP_Remote_Address *remote_addr)
res_length = 0; res_length = 0;
NCR_ProcessRxUnknown(remote_addr, &local_addr, &local_ts, NCR_ProcessRxUnknown(remote_addr, &local_addr, &local_ts,
&req_buffer.ntp_pkt, req_length); &req_buffer, req_length);
res_length = req_length; res_length = req_length;
res_buffer = req_buffer; res_buffer = req_buffer;
@ -129,7 +129,7 @@ process_request(NTP_Remote_Address *remote_addr)
if (random() % 2) { if (random() % 2) {
local_ts.ts = current_time; local_ts.ts = current_time;
NCR_ProcessTxUnknown(remote_addr, &local_addr, &local_ts, NCR_ProcessTxUnknown(remote_addr, &local_addr, &local_ts,
&res_buffer.ntp_pkt, res_length); &res_buffer, res_length);
} }
} }
@ -138,11 +138,12 @@ send_response(int interleaved, int authenticated, int allow_update, int valid_ts
{ {
NTP_Packet *req, *res; NTP_Packet *req, *res;
int auth_len = 0; int auth_len = 0;
uint32_t key_id;
req = &req_buffer.ntp_pkt; req = &req_buffer;
res = &res_buffer.ntp_pkt; res = &res_buffer;
TEST_CHECK(req_length >= NTP_NORMAL_PACKET_LENGTH); TEST_CHECK(req_length >= NTP_HEADER_LENGTH);
res->lvm = NTP_LVM(LEAP_Normal, NTP_LVM_TO_VERSION(req->lvm), res->lvm = NTP_LVM(LEAP_Normal, NTP_LVM_TO_VERSION(req->lvm),
NTP_LVM_TO_MODE(req->lvm) == MODE_CLIENT ? MODE_SERVER : MODE_ACTIVE); NTP_LVM_TO_MODE(req->lvm) == MODE_CLIENT ? MODE_SERVER : MODE_ACTIVE);
@ -184,18 +185,20 @@ send_response(int interleaved, int authenticated, int allow_update, int valid_ts
} }
if (authenticated) { if (authenticated) {
res->auth_keyid = req->auth_keyid ? req->auth_keyid : htonl(get_random_key_id()); key_id = ntohl(*(uint32_t *)req->extensions);
auth_len = KEY_GetAuthLength(ntohl(res->auth_keyid)); if (key_id == 0)
key_id = get_random_key_id();
auth_len = KEY_GetAuthLength(key_id);
assert(auth_len); assert(auth_len);
if (NTP_LVM_TO_VERSION(res->lvm) == 4 && random() % 2) if (NTP_LVM_TO_VERSION(res->lvm) == 4 && random() % 2)
auth_len = MIN(auth_len, NTP_MAX_V4_MAC_LENGTH - 4); auth_len = MIN(auth_len, NTP_MAX_V4_MAC_LENGTH - 4);
if (KEY_GenerateAuth(ntohl(res->auth_keyid), (unsigned char *)res, if (KEY_GenerateAuth(key_id, (unsigned char *)res, NTP_HEADER_LENGTH,
NTP_NORMAL_PACKET_LENGTH, res->auth_data, auth_len) != auth_len) res->extensions + 4, auth_len) != auth_len)
assert(0); assert(0);
res_length = NTP_NORMAL_PACKET_LENGTH + 4 + auth_len; res_length = NTP_HEADER_LENGTH + 4 + auth_len;
} else { } else {
res_length = NTP_NORMAL_PACKET_LENGTH; res_length = NTP_HEADER_LENGTH;
} }
if (!valid_auth && authenticated) { if (!valid_auth && authenticated) {
@ -203,27 +206,29 @@ send_response(int interleaved, int authenticated, int allow_update, int valid_ts
switch (random() % 4) { switch (random() % 4) {
case 0: case 0:
res->auth_keyid = htonl(ntohl(res->auth_keyid) + 1); key_id++;
break; break;
case 1: case 1:
res->auth_keyid = htonl(ntohl(res->auth_keyid) ^ 1); key_id ^= 1;
if (KEY_GenerateAuth(ntohl(res->auth_keyid), (unsigned char *)res, if (KEY_GenerateAuth(key_id, (unsigned char *)res, NTP_HEADER_LENGTH,
NTP_NORMAL_PACKET_LENGTH, res->auth_data, auth_len) != auth_len) res->extensions + 4, auth_len) != auth_len)
assert(0); assert(0);
break; break;
case 2: case 2:
res->auth_data[random() % auth_len]++; res->extensions[4 + random() % auth_len]++;
break; break;
case 3: case 3:
res_length = NTP_NORMAL_PACKET_LENGTH + 4 * (random() % ((4 + auth_len) / 4)); res_length = NTP_HEADER_LENGTH + 4 * (random() % ((4 + auth_len) / 4));
if (NTP_LVM_TO_VERSION(res->lvm) == 4 && if (NTP_LVM_TO_VERSION(res->lvm) == 4 &&
res_length == NTP_NORMAL_PACKET_LENGTH + NTP_MAX_V4_MAC_LENGTH) res_length == NTP_HEADER_LENGTH + NTP_MAX_V4_MAC_LENGTH)
res_length -= 4; res_length -= 4;
break; break;
default: default:
assert(0); assert(0);
} }
} }
*(uint32_t *)res->extensions = htonl(key_id);
} }
static void static void
@ -236,7 +241,7 @@ process_response(NCR_Instance inst, int good, int valid, int updated_sync, int u
struct timespec prev_rx_ts, prev_init_rx_ts; struct timespec prev_rx_ts, prev_init_rx_ts;
int prev_open_socket, ret; int prev_open_socket, ret;
res = &res_buffer.ntp_pkt; res = &res_buffer;
local_addr.ip_addr.family = IPADDR_UNSPEC; local_addr.ip_addr.family = IPADDR_UNSPEC;
local_addr.if_index = INVALID_IF_INDEX; local_addr.if_index = INVALID_IF_INDEX;
@ -285,13 +290,12 @@ process_response(NCR_Instance inst, int good, int valid, int updated_sync, int u
} }
static void static void
process_replay(NCR_Instance inst, NTP_Receive_Buffer *packet_queue, process_replay(NCR_Instance inst, NTP_Packet *packet_queue,
int queue_length, int updated_init) int queue_length, int updated_init)
{ {
do { do {
res_buffer = packet_queue[random() % queue_length]; res_buffer = packet_queue[random() % queue_length];
} while (!UTI_CompareNtp64(&res_buffer.ntp_pkt.transmit_ts, } while (!UTI_CompareNtp64(&res_buffer.transmit_ts, &inst->remote_ntp_tx));
&inst->remote_ntp_tx));
process_response(inst, 0, 0, 0, updated_init); process_response(inst, 0, 0, 0, updated_init);
advance_time(1e-6); advance_time(1e-6);
} }
@ -312,7 +316,7 @@ test_unit(void)
CPS_NTP_Source source; CPS_NTP_Source source;
NTP_Remote_Address remote_addr; NTP_Remote_Address remote_addr;
NCR_Instance inst1, inst2; NCR_Instance inst1, inst2;
NTP_Receive_Buffer packet_queue[PACKET_QUEUE_LENGTH]; NTP_Packet packet_queue[PACKET_QUEUE_LENGTH];
CNF_Initialise(0, 0); CNF_Initialise(0, 0);
for (i = 0; i < sizeof conf / sizeof conf[0]; i++) for (i = 0; i < sizeof conf / sizeof conf[0]; i++)