# This is the common part in the header format. # See RFC 5905 for details type NTP_PDU(is_orig: bool) = record { # The first byte of the NTP header contains the leap indicator, # the version and the mode first_byte : uint8; # Modes 1-5 are standard NTP time sync standard_modes : case (mode>=1 && mode<=5) of { true -> std : NTP_std_msg; false -> emp : empty; }; modes_6_7 : case (mode) of { # mode 6 is for control messages (format is different from modes 6-7) 6 -> control : NTP_control_msg; # mode 7 is reserved or private (and implementation dependent). For example used for some commands such as MONLIST 7 -> mode7 : NTP_mode7_msg; default -> unknown : bytestring &restofdata; }; } &let { leap: uint8 = (first_byte & 0xc0)>>6; # First 2 bits of 8-bits value version: uint8 = (first_byte & 0x38)>>3; # Bits 3-5 of 8-bits value mode: uint8 = (first_byte & 0x07); # Bits 6-8 of 8-bits value } &byteorder=bigendian &exportsourcedata; # This is the most common type of message, corresponding to modes 1-5 # This kind of msg are used for normal operation of syncronization # See RFC 5905 for details type NTP_std_msg = record { stratum : uint8; poll : int8; precision : int8; root_delay : NTP_Short_Time; root_dispersion: NTP_Short_Time; reference_id : bytestring &length=4; reference_ts : NTP_Time; origin_ts : NTP_Time; receive_ts : NTP_Time; transmit_ts : NTP_Time; #extensions : Extension_Field[] &until($input.length() == 20); #TODO: this need to be properly parsed mac_fields : case (mac_len) of { 20 -> mac : NTP_MAC; 24 -> mac_ext : NTP_MAC_ext; false -> nil : empty; } &requires(mac_len); } &let { length = sourcedata.length(); mac_len: uint32 = (length - offsetof(mac_fields)); } &byteorder=bigendian &exportsourcedata; # This format is for mode==6, control msg # See RFC 1119 for details type NTP_control_msg = record { second_byte : uint8; sequence : uint16; status : uint16; #TODO: this can be further parsed internally association_id : uint16; offs : uint16; c : uint16; data : bytestring &length=c; mac_fields : case (has_control_mac) of { true -> mac : NTP_CONTROL_MAC; false -> nil : empty; } &requires(has_control_mac); } &let { R: bool = (second_byte & 0x80) > 0; # First bit of 8-bits value E: bool = (second_byte & 0x40) > 0; # Second bit of 8-bits value M: bool = (second_byte & 0x20) > 0; # Third bit of 8-bits value OpCode: uint8 = (second_byte & 0x1F); # Last 5 bits of 8-bits value length = sourcedata.length(); has_control_mac: bool = (length - offsetof(mac_fields)) == 12; } &byteorder=bigendian &exportsourcedata; # As in RFC 5905 type NTP_MAC = record { key_id: uint32; digest: bytestring &length=16; } &length=20; # As in RFC 5906, same as NTP_MAC but with a 160 bit digest type NTP_MAC_ext = record { key_id: uint32; digest: bytestring &length=20; } &length=24; # As in RFC 1119 type NTP_CONTROL_MAC = record { key_id: uint32; crypto_checksum: bytestring &length=8; } &length=12; type Extension_Field = record { field_type: uint16; ext_len : uint16; data : bytestring &length=ext_len-4; }; type NTP_Short_Time = record { seconds: int16; fractions: int16; }; type NTP_Time = record { seconds: uint32; fractions: uint32; };