Stratum 1 NTP Server on FreeBSD 14+

Discuss Wi-Fi setups, cybersecurity, and network troubleshooting.
Post Reply
User avatar
ccb056
Site Administrator
Posts: 981
Joined: January 14th, 2004, 11:36 pm
Location: Texas
Contact:

Stratum 1 NTP Server on FreeBSD 14+

Post by ccb056 »

Building a precision Stratum 1 NTP server on FreeBSD 14+ using the Garmin GPS 18x LVC requires careful attention to serial port configuration, PPS timing, and NTP driver setup. This tutorial provides a complete, step-by-step process for creating a microsecond-accurate time server that serves as an authoritative time source for your network. The Garmin GPS 18x LVC with its hardware PPS output can achieve timing accuracy within 1 microsecond when properly configured.

This comprehensive guide covers every aspect from initial FreeBSD installation through final verification, with detailed explanations for each command and troubleshooting steps. The configuration uses the base FreeBSD NTP daemon which includes full GPS and PPS support in FreeBSD 14+, now condensed to 9 essential steps.

Prerequisites and initial setup

Before beginning the detailed configuration, ensure you have a fresh minimal FreeBSD 14+ installation with root SSH access and GPS hardware properly connected:
  • Garmin GPS 18x LVC on COM1 (primary)
  • u-blox LEA-M8T on COM4 (optional secondary)
Step 1: Verify system requirements

Command:

Code: Select all

freebsd-version && uname -a
Verification: Confirms FreeBSD 14.0-RELEASE or later

Code: Select all

# Expected output should show version 14.x or higher
FreeBSD 14.1-RELEASE FreeBSD 14.1-RELEASE...
Explanation: This verification ensures you're running a supported FreeBSD version with the modern UART drivers and PPS API support required for precision GPS timing.

Why needed: FreeBSD 14+ includes enhanced UART drivers with improved PPS capture capabilities and updated security features that earlier versions lack.

Without this: Older FreeBSD versions may have timing drift issues, missing PPS API features, or incompatible serial port handling that degrades NTP accuracy.

Step 2: Verify base NTP is available

Command:

Code: Select all

which ntpd && ntpd --version
ls -la /etc/ntp.conf
Verification: Confirm base ntpd exists

Code: Select all

# Expected output:
/usr/sbin/ntpd
ntpd 4.2.8p15-a (1)
Explanation: Verifies that the base system's ntpd is present. FreeBSD 14+ includes GPS driver support (driver 20) and PPS capabilities in the base system NTP.

Why needed: Confirms the base NTP daemon is available with the necessary reference clock drivers for GPS operation.

Without this: You need to ensure the base system includes NTP support, which should be present in a standard FreeBSD 14+ installation.

Hardware and serial port configuration

Step 3: Verify GPS hardware detection

Command:

Code: Select all

dmesg | grep -E "(uart|serial)" && ls -la /dev/cua*
Verification: Should show UART detection and cuau0 device

Code: Select all

# Expected output:
uart0: <16550 or compatible> port 0x3f8-0x3ff irq 4 flags 0x10 on acpi0
crw-rw----  1 uucp  dialer  0,92 Jan 19 10:30 /dev/cuau0
Explanation: Confirms the system has detected the hardware serial port (COM1) and created the appropriate device files for communication.

Why needed: Without proper UART detection, the GPS cannot communicate with the system, making time synchronization impossible.

Without this: The GPS hardware would be inaccessible, and NTP driver 20 would fail to initialize, preventing Stratum 1 operation.

Step 4: Create GPS device symlink

Command:

Code: Select all

# For Garmin GPS 18x LVC on COM1
ln -sf /dev/cuau0 /dev/gps0
echo 'link cuau0 gps0' >> /etc/devfs.conf

# For u-blox LEA-M8T on COM4
ln -sf /dev/cuau3 /dev/gps3
echo 'link cuau3 gps3' >> /etc/devfs.conf
Verification: Confirm symlinks exist and point correctly

Code: Select all

ls -la /dev/gps*
# Expected output:
# lrwxr-xr-x  1 root  wheel  9 Jan 19 10:35 /dev/gps0 -> /dev/cuau0
# lrwxr-xr-x  1 root  wheel  9 Jan 19 10:35 /dev/gps3 -> /dev/cuau3
Explanation: Creates permanent symlinks that NTP driver 20 expects for GPS device access. The devfs.conf entries ensure the symlinks persist across reboots. Unit 0 uses /dev/gps0, unit 3 uses /dev/gps3.

Why needed: NTP driver 20 looks for /dev/gpsN where N is the unit number. With two GPS units configured (127.127.20.0 and 127.127.20.3), we need both symlinks.

Without this: NTP would fail to initialize the GPS reference clocks, logging "GPS_NMEA: cannot open GPS device" errors.

GPS device configuration and testing

Step 5: Test GPS communication with timeout

Command:

Code: Select all

# Test Garmin GPS on COM1
echo "Testing Garmin GPS 18x LVC on COM1:"
timeout 5 cu -l /dev/cuau0 -s 4800
timeout 5 cu -l /dev/gps0 -s 4800

# Test u-blox GPS on COM4
echo "Testing u-blox LEA-M8T on COM4:"
timeout 5 cu -l /dev/cuau3 -s 4800
timeout 5 cu -l /dev/gps3 -s 4800
Verification: Should display NMEA sentences from each GPS

Code: Select all

# Expected output (example):
Connected
$GPRMC,123456.00,A,40yy.xxxx,N,07yyy.xxxx,W,0.0,0.0,190125,0.0,W*CS
$GPGGA,123456.00,40yy.xxxx,N,07yyy.xxxx,W,1,08,1.2,45.6,M,46.8,M,,*CS
# Exit with ~. (tilde dot) or wait for timeout
Explanation: Tests GPS communication through both physical devices and symlinks at 4800 baud. This verifies both GPS units are functioning and accessible via their respective paths.

Why needed: Confirms both GPS receivers are outputting valid NMEA data and that all device paths are properly configured.

Without this: You would proceed with NTP configuration without knowing if either GPS is functional, leading to debugging difficulties later.

NTP server configuration

Step 6: Create comprehensive ntp.conf configuration

Command:

Code: Select all

cat > /etc/ntp.conf << 'EOF'
# Stratum 1 NTP Server Configuration with GPS 18x LVC
# Panic threshold - 0 allows any time correction
tinker panic 0

# Orphan mode stratum and maximum candidate NTP peers
# See https://www.ntp.org/documentation/4.2.8-series/orphan/
tos orphan 12 maxclock 10

# GPS NMEA driver (driver 20) with PPS
# See https://www.ntp.org/documentation/4.2.8-series/assoc/
# See https://www.ntp.org/documentation/drivers/driver20/
# Mode 1: Process $GPRMC sentences only
# maxpoll 4 for PPS drivers (16s interval)

# Unit 0 - Garmin GPS 18x LVC (127.127.20.0)
# See: https://support.ntp.org/Support/ConfiguringNMEARefclocks#Garmin_GPS18x_LVC
server 127.127.20.0 mode 1 minpoll 3 maxpoll 4
fudge 127.127.20.0 time2 0.600 flag1 1 flag2 0 flag3 1

# Unit 3 - u-blox LEA-M8T (127.127.20.3) 
# See: https://support.ntp.org/Support/ConfiguringNMEARefclocks#A_181BLOX_NEO_45M8T
# flag2 1 corrects PPS polarity when using 3.3V TTL-to-RS232 converters
server 127.127.20.3 mode 1 minpoll 3 maxpoll 4
fudge 127.127.20.3 time2 0.200 flag1 1 flag2 1 flag3 1

# Local peer for redundancy (IPv4 only)
# See https://www.ntp.org/documentation/4.2.8-series/assoc/
# For typical Internet/WAN paths: Allan intercept ~2048s → poll 6 (64s)
# For fast LANs with modern computers: Allan intercept ~512s → poll 4 (16s)
#
# See https://www.ntp.org/documentation/4.2.8-series/discipline/
# For symmetric mode peers set minpoll and maxpoll to the same value
#
# LAN peer example (fast local network):
peer -4 192.168.11.1 minpoll 4 maxpoll 4

# NIST time servers (IPv4 only)
# Full list available at: https://tf.nist.gov/tf-cgi/servers.cgi
server -4 utcnist3.colorado.edu iburst
server -4 time-b-g.nist.gov iburst
server -4 time-d-g.nist.gov iburst
server -4 time-a-wwv.nist.gov iburst
server -4 time-c-wwv.nist.gov iburst
server -4 time-e-wwv.nist.gov iburst
server -4 time-a-b.nist.gov iburst
server -4 time-c-b.nist.gov iburst
server -4 time-e-b.nist.gov iburst

# Enable statistics
enable stats
# Enable automatic calibration (disable after determining time2 values)
# See addendum for calibration procedure
#enable calibrate
# Statistics configuration - see https://www.ntp.org/ntpfaq/ntp-s-trouble/
# Section 8.1.2 explains peerstats and loopstats file formats:
# loopstats: day, second, offset, drift compensation, estimated error, stability, polling interval
# peerstats: day, second, address, status, offset, delay, dispersion, skew (variance)
statistics clockstats loopstats peerstats
statsdir /var/log/ntp
# Logging configuration - see https://www.ntp.org/ntpfaq/ntp-s-config/
# Section 6.1.4.1 for detailed logging options
# Current setting logs all sync, clock, peer, and system events
logconfig =syncall +clockall +peerall +sysall
# For debugging, you could use: logconfig =all

# Drift file location
driftfile /var/db/ntpd.drift

# Access control
restrict default kod limited nomodify notrap
restrict -6 default kod limited nomodify notrap
restrict 192.168.11.0 mask 255.255.255.0

# Leap seconds file
leapfile /var/db/ntpd.leap-seconds.list
EOF
Verification: Check configuration syntax

Code: Select all

# Create statistics directory if it doesn't exist
mkdir -p /var/log/ntp
chown root:wheel /var/log/ntp

# Test configuration syntax by checking if ntpd can parse it
ntpd -n -c /etc/ntp.conf -f /dev/null 2>&1 | head -20
# Look for any error messages in the output
Explanation: This optimized configuration uses GPS unit 1 (127.127.20.1) in mode 0 for better NMEA processing, forces IPv4 for all servers to avoid IPv6 issues, enables detailed logging, and allows unrestricted time corrections with tinker panic 0.

Why needed: This configuration provides better GPS synchronization, improved logging for troubleshooting, and prevents startup failures due to large time corrections.

Without this: The system might experience high root dispersion, IPv6 connectivity issues, or refuse to synchronize if the initial time offset is too large.

Step 7: Configure leap seconds file

Command:

Code: Select all

# Enable automatic leap second updates
echo 'daily_ntpd_leapfile_enable="YES"' >> /etc/periodic.conf

# Download the current leap seconds file
/etc/periodic/daily/480.leapfile-ntpd

# Add leapfile directive to ntp.conf (already included in step 6)
# Verify it's in the configuration
grep leapfile /etc/ntp.conf
Verification: Check leap seconds file is valid

Code: Select all

head -5 /var/db/ntpd.leap-seconds.list
# Should show comments with expiration date
Explanation: Configures automatic leap seconds file updates through FreeBSD's periodic system and adds the leapfile directive to ntp.conf. The leap seconds file is required for proper handling of leap second adjustments.

Why needed: Leap seconds are occasionally added to UTC to keep it synchronized with Earth's rotation. Without this file, ntpd cannot properly handle leap second events.

Without this: The server would not properly handle leap seconds, potentially causing a one-second error during leap second events.

Step 8: Configure NTP service startup

Command:

Code: Select all

sysrc ntpd_enable="YES"
sysrc ntpd_sync_on_start="YES"
Verification: Start the service and check status

Code: Select all

service ntpd start
service ntpd status
# Expected output: ntpd is running as pid xxxxx
Explanation: Configures FreeBSD's service system to enable the base ntpd service with startup synchronization.

Why needed: These settings ensure ntpd starts at boot. The ntpd_sync_on_start allows initial time synchronization even with large offsets.

Without this: The system would not start ntpd automatically at boot.

Service startup and verification

Step 9: Verify GPS reference clock initialization

Command:

Code: Select all

ntpq -p
ntpq -c assoc
Verification: GPS should appear as a peer with valid status

Code: Select all

# Expected output from ntpq -p:
     remote           refid      st t when poll reach   delay   offset  jitter
=============================================================================
*GPS_NMEA(3)     .GPS.            0 l    4   16  377    0.000    0.001   0.002
+192.168.11.1    10.1.1.1         2 s   64  512  377   0.123   -1.234   2.345

# Expected output from ntpq -c assoc:
ind assid status  conf reach auth condition  last_event cnt
===========================================================
  1 22429  971a   yes   yes  none  pps.peer    sys_peer  1
  2 22430  8011   yes    no  none    reject    mobilize  1
# Line 1 with "pps.peer" and "sys_peer" indicates GPS with PPS is the system peer
Explanation: The first command shows all peers including the GPS reference clock. The second shows associations where the GPS (if working with PPS) will show as "pps.peer" in the condition column and "sys_peer" in the last_event column, indicating it's the primary time source.

Why needed: This confirms the GPS reference clock is functioning with PPS timing and being used as the primary time source.

Without this: You wouldn't know if the GPS timing is working properly, potentially operating with degraded accuracy or no GPS synchronization at all.

Performance validation and monitoring

Step 10: Final comprehensive status verification

Command:

Code: Select all

echo "=== NTP Status Summary ==="
date
ntpq -p
echo -e "\n=== GPS Reference Clock Details ==="
ntpq -c assoc | grep "pps.peer"
echo -e "\n=== System Variables ==="
ntpq -c "rv" | grep -E "(stratum|precision|rootdelay|rootdisp)"
echo -e "\n=== Recent GPS Statistics ==="
tail -5 /var/db/ntp/clockstats 2>/dev/null || echo "Statistics not yet available"
Verification: Complete system status overview

Code: Select all

# Expected output showing:
# - Current time in sync
# - GPS marked with 'o' (PPS discipline)
# - Stratum 1 operation
# - Low jitter values (<1ms)
# - Recent clockstats entries showing GPS NMEA sentences

# The 'o' before GPS_NMEA(3) indicates PPS discipline is active
# NIST servers marked with + are good candidates, - are excluded
# clockstats show valid GPS fixes (A in GPRMC sentences)
Explanation: Provides a comprehensive status summary showing all aspects of Stratum 1 operation including GPS status, synchronization state, timing precision, and performance statistics. The 'o' prefix on GPS indicates PPS discipline is active.

Why needed: This final verification ensures all components are working correctly and the server is ready for production use as an authoritative time source.

Without this: Subtle configuration issues or performance problems might go undetected, leading to unreliable time service or degraded accuracy over time.

Conclusion and ongoing monitoring

Your FreeBSD 14+ system is now configured as a precision Stratum 1 NTP server using GPS receivers with PPS timing. The configuration supports dual GPS units (Garmin GPS 18x LVC and u-blox LEA-M8T) for redundancy and provides microsecond-level accuracy through hardware PPS signals while maintaining fallback connectivity to NIST time servers.

Key monitoring commands for ongoing maintenance:
  • Code: Select all

    ntpq -p
    - Check peer status and synchronization (look for 'o' prefix on GPS)
  • Code: Select all

    ntpq -c sysinfo
    - Verify stratum level and timing accuracy
  • Code: Select all

    tail /var/log/ntp/clockstats
    - Monitor GPS NMEA sentences
  • Code: Select all

    service ntpd status
    - Confirm service health
Expected performance characteristics:
  • Timing accuracy: <1 millisecond (typically <100 microseconds with PPS)
  • Stratum level: 1 (primary reference)
  • GPS lock indicator: 'A' in GPRMC sentences
  • PPS indicator: 'o' prefix in ntpq output for primary GPS
  • Clock jitter: <0.1 milliseconds
  • Multiple GPS: Both units may show as available peers if configured
The server will automatically maintain precise time synchronization and serve as an authoritative time source for your network, providing the highest level of timing accuracy achievable with GPS technology on FreeBSD systems.

Addendum: Calibrating GPS time2 Offset Values

The

Code: Select all

fudge time2
parameter compensates for the delay between when the GPS calculates the time and when the NMEA sentence is received by NTP. While the default values work well for most installations, you may need to calibrate for optimal accuracy.

Two calibration methods are available:
  1. Automatic calibration using

    Code: Select all

    enable calibrate
    - Best when you have reliable internet servers
  2. Manual calibration using noselect - Best for isolated networks or when internet servers are unreliable
Manual Calibration Method

If you need to fine-tune the

Code: Select all

fudge time2
value for your specific GPS unit, follow this calibration procedure from https://support.ntp.org/Support/ConfiguringGarminRefclocks:

Step 1: Create calibration configuration

Create a temporary ntp.conf for calibration:

Code: Select all

cp /etc/ntp.conf /etc/ntp.conf.calibration
vi /etc/ntp.conf
Modify the GPS server line to:

Code: Select all

# GPS NMEA driver - calibration mode (no PPS, noselect)
server 127.127.20.0 mode 1 minpoll 4 maxpoll 4 noselect
fudge 127.127.20.0 time2 0.000 flag1 0 flag3 1
The key changes:
  • Add

    Code: Select all

    noselect
    to prevent GPS from being selected as primary source
  • Set

    Code: Select all

    flag1 0
    to disable PPS
  • Set

    Code: Select all

    time2 0.000
    for baseline measurement
Step 2: Run calibration

Code: Select all

service ntpd restart
# Wait 30-60 minutes for stability
sleep 3600
Step 3: Check NMEA offset

Code: Select all

ntpq -p | grep GPS_NMEA
# Note the offset value (in milliseconds)
Example output:

Code: Select all

 GPS_NMEA(0)     .GPS.            0 l   16   16  377    0.000  -450.123   0.432
Step 4: Calculate and apply fudge time2

The fudge time2 value should be the negated offset in seconds:
  • If offset shows -450.123 ms, use time2 0.450
  • If offset shows +320.456 ms, use time2 -0.320
Update your configuration:

Code: Select all

vi /etc/ntp.conf
Change back to:

Code: Select all

# GPS NMEA driver with calibrated time2
server 127.127.20.0 mode 1 minpoll 4 maxpoll 4
fudge 127.127.20.0 time2 0.450 flag1 1 flag3 1
Step 5: Restart with calibrated value

Code: Select all

service ntpd restart
Verification

After 10-15 minutes, verify the GPS offset is now close to zero:

Code: Select all

ntpq -p | grep GPS_NMEA
# Offset should be within ±10ms
Note: The default time2 values (0.600 for Garmin GPS 18x LVC, 0.200 for u-blox LEA-M8T) work well for most units. Only perform this calibration if you observe consistent large offsets between your GPS and internet time servers. If using dual GPS units, calibrate each unit separately.

Alternative: Automatic Calibration Using enable calibrate

NTP provides an automatic calibration feature that can calculate the time2 offset for you. See https://www.ntp.org/documentation/4.2.8-series/refclock/

Step 1: Create automatic calibration configuration

Code: Select all

cp /etc/ntp.conf /etc/ntp.conf.autocalibration
vi /etc/ntp.conf
Add/modify these lines:

Code: Select all

# Enable automatic calibration
enable calibrate

# GPS NMEA drivers with no initial time correction
server 127.127.20.0 mode 1 minpoll 4 maxpoll 4
fudge 127.127.20.0 time2 0.000 flag1 1 flag2 0 flag3 1

server 127.127.20.3 mode 1 minpoll 4 maxpoll 4
fudge 127.127.20.3 time2 0.000 flag1 1 flag2 1 flag3 1

# Select one highly reliable server as preferred reference
server time.nist.gov iburst prefer

# Additional servers for validation
server 0.pool.ntp.org iburst
server 1.pool.ntp.org iburst
Step 2: Run automatic calibration

Code: Select all

service ntpd restart
# Let it run for 1-2 hours for accurate results
Step 3: Check calibration results

Code: Select all

# Look for calibration messages in logs
grep "refclock_report" /var/log/messages
# Example output:
# ntpd[1234]: refclock_report: 127.127.20.0: time2 0.598
# ntpd[1234]: refclock_report: 127.127.20.3: time2 0.203
Step 4: Apply calibrated values

Update your ntp.conf with the reported time2 values and remove the calibration:

Code: Select all

vi /etc/ntp.conf
Remove

Code: Select all

enable calibrate
and update the fudge lines:

Code: Select all

fudge 127.127.20.0 time2 0.598 flag1 1 flag2 0 flag3 1
fudge 127.127.20.3 time2 0.203 flag1 1 flag2 1 flag3 1
Step 5: Restart with calibrated values

Code: Select all

service ntpd restart
Important: The automatic calibration assumes your preferred peer is accurate. Choose a reliable stratum 1 or 2 server, preferably geographically close to minimize network jitter.

Hardware-Specific Notes

u-blox GPS with TTL-to-RS232 Converters:
If using a u-blox GPS receiver (like NEO-6M/7M/8M or LEA-M8T) with a 3.3V TTL-to-RS232 converter, the converter inverts the PPS signal logic. The falling edge of DCD becomes the active edge instead of the rising edge. To correct this, add

Code: Select all

flag2 1
to your fudge line:

Code: Select all

fudge 127.127.20.3 time2 0.200 flag1 1 flag2 1 flag3 1
Reference: https://support.ntp.org/Support/ConfiguringNMEARefclocks#A_181BLOX_NEO_45M8T

Calibration Summary

Both calibration methods achieve the same goal: determining the correct time2 offset for your GPS hardware. Choose the method that best fits your setup:
  • Automatic calibration: Easier, requires a trusted preferred peer, good for initial setup
  • Manual calibration: More control, works without internet servers, better for isolated networks
The key is that PPS provides microsecond accuracy for the second boundary, while the NMEA time2 offset ensures the correct second is selected.
Post Reply