Skip to content

NTP Running and Testing

Elbasiouny, Mahmoud edited this page May 29, 2026 · 5 revisions

How to build, run, and test the NTP parser with both real captured packets and hand-crafted test inputs.


Building

From the Hammer repository root, build the checkout-local library first:

scons examples

Then build the NTP example:

cd examples/ntp
make

This produces the ntp_parser executable. If ../../build/opt/src is not present, the Makefile falls back to pkg-config libhammer. See Getting Started for prerequisites.

To clean and rebuild:

make clean && make

Input Modes

The parser accepts raw bytes (not hex strings). Two ways to provide input:

1. File Input

Pass a file path as a command-line argument:

./ntp_parser packet/ntp_stream.bin

2. Standard Input (stdin)

Pipe raw bytes through stdin:

cat packet/ntp_stream.bin | ./ntp_parser

Converting Hex Strings to Raw Bytes

If you have a hex string (common when working with protocol specs or packet captures), convert it with xxd:

echo -n "e30003fa000100000001000000000000000000000000000000000000000000000000000000000000ec17ee223f54e6a6" | xxd -r -p | ./ntp_parser

The flags:

  • echo -n - no trailing newline
  • xxd -r -p - reverse hex dump, plain (no line numbers)

Quick Test with the Included Packet

The Makefile includes a convenience target:

make testp

This runs:

cat packet/ntp_stream.bin | ./ntp_parser

Expected output:

Reading from stdin
Packet accepted

Test Cases

Minimal Valid Packet (48 bytes, essential fields only)

The smallest valid NTP packet is exactly 48 bytes: header + data fields, no extensions, no MAC.

echo -n "e30003fa000100000001000000000000000000000000000000000000000000000000000000000000ec17ee223f54e6a6" \
  | xxd -r -p | ./ntp_parser

Breakdown:

Hex Field Value
e3 LI=3, VN=4, Mode=3 Header byte 1
00 Stratum 0
03 Poll 3
fa Precision -6 (signed)
00010000 Root Delay 0x0001, 0x0000
00010000 Root Dispersion 0x0001, 0x0000
00000000 Reference ID 0
0000000000000000 Reference Timestamp 0
0000000000000000 Origin Timestamp 0
0000000000000000 Receive Timestamp 0
ec17ee223f54e6a6 Transmit Timestamp non-zero

Expected: Packet accepted

Invalid Packet (bad version number)

Change the first byte so the version number is out of range (1-4):

# First byte 0x03: LI=0, VN=0, Mode=3 -- VN=0 is invalid (must be 1-4)
echo -n "030003fa000100000001000000000000000000000000000000000000000000000000000000000000ec17ee223f54e6a6" \
  | xxd -r -p | ./ntp_parser

Expected: Packet rejected (version 0 is outside the valid range 1-4)

Packet With Trailing Garbage

Add extra bytes after a valid 48-byte packet:

echo -n "e30003fa000100000001000000000000000000000000000000000000000000000000000000000000ec17ee223f54e6a6DEADBEEF" \
  | xxd -r -p | ./ntp_parser

Expected: Packet rejected - the h_end_p() combinator detects unconsumed bytes.

Truncated Packet

Send fewer than 48 bytes:

echo -n "e30003fa00010000" | xxd -r -p | ./ntp_parser

Expected: Packet rejected - not enough data for the mandatory fields.


Capturing Your Own NTP Packets

To test with real network traffic, follow the steps in packet/steps.md:

1. Capture an NTP packet

sudo tcpdump -i enp2s1 -c 1 port 123 -s 0 -w ntp_packet.pcap

Replace enp2s1 with your network interface (use ip link to find it). Port 123 is the standard NTP port.

2. Extract the UDP payload

tshark -r ntp_packet.pcap -T fields -e udp.payload > ntp_stream.hex

3. Convert hex to binary

xxd -r -p ntp_stream.hex > ntp_stream.bin

4. Run the parser

./ntp_parser ntp_stream.bin

Understanding Parser Output

Output Meaning
Packet accepted The input matched the NTP grammar (exit code 0)
Packet rejected The input did not match (exit code -1)

You can use the exit code in scripts:

./ntp_parser packet/ntp_stream.bin && echo "Valid NTP" || echo "Not valid NTP"

Debugging Tips

  1. Check byte count: An NTP packet must be at least 48 bytes. You need at least 96 hex characters (2 per byte).

  2. Check the first byte: Decode it manually to verify LI, VN, and Mode are in range:

    Byte: 0xE3 = 11100011 binary
    LI   = 11 (binary) = 3  (0-3)
    VN   = 100 (binary) = 4 (1-4)
    Mode = 011 (binary) = 3 (0-7)
    
  3. Use xxd to inspect binary files:

    xxd packet/ntp_stream.bin | head
  4. Use h_pprint for debugging: You can temporarily add h_pprint(stdout, result->ast, 0, 4); after a successful parse to dump the entire parse tree. This is useful for verifying that fields were split correctly.


Back to: NTP Overview · Examples Index

Previous: Hex Input Preprocessing

Clone this wiki locally