summaryrefslogtreecommitdiffstats
path: root/test/simulation
diff options
context:
space:
mode:
Diffstat (limited to '')
-rwxr-xr-xtest/simulation/001-defaults13
-rwxr-xr-xtest/simulation/002-largenetwork22
-rwxr-xr-xtest/simulation/003-largefreqoffset19
-rwxr-xr-xtest/simulation/004-largetimeoffset18
-rwxr-xr-xtest/simulation/005-externalstep46
-rwxr-xr-xtest/simulation/006-largejitter21
-rwxr-xr-xtest/simulation/007-largewander20
-rwxr-xr-xtest/simulation/008-ntpera50
-rwxr-xr-xtest/simulation/009-sourceselection40
-rwxr-xr-xtest/simulation/010-multrecv17
-rwxr-xr-xtest/simulation/011-asymjitter18
-rwxr-xr-xtest/simulation/012-daemonts15
-rwxr-xr-xtest/simulation/013-nameserv15
-rwxr-xr-xtest/simulation/101-poll56
-rwxr-xr-xtest/simulation/102-iburst23
-rwxr-xr-xtest/simulation/103-initstepslew63
-rwxr-xr-xtest/simulation/104-driftfile23
-rwxr-xr-xtest/simulation/105-ntpauth96
-rwxr-xr-xtest/simulation/106-refclock143
-rwxr-xr-xtest/simulation/107-allowdeny48
-rwxr-xr-xtest/simulation/108-peer54
-rwxr-xr-xtest/simulation/109-makestep41
-rwxr-xr-xtest/simulation/110-chronyc496
-rwxr-xr-xtest/simulation/111-knownclient17
-rwxr-xr-xtest/simulation/112-port57
-rwxr-xr-xtest/simulation/113-leapsecond61
-rwxr-xr-xtest/simulation/114-presend25
-rwxr-xr-xtest/simulation/115-cmdmontime24
-rwxr-xr-xtest/simulation/116-minsources24
-rwxr-xr-xtest/simulation/117-fallbackdrift24
-rwxr-xr-xtest/simulation/118-maxdelay42
-rwxr-xr-xtest/simulation/119-smoothtime82
-rwxr-xr-xtest/simulation/120-selectoptions89
-rwxr-xr-xtest/simulation/121-orphan26
-rwxr-xr-xtest/simulation/122-xleave91
-rwxr-xr-xtest/simulation/123-mindelay27
-rwxr-xr-xtest/simulation/124-tai45
-rwxr-xr-xtest/simulation/125-packetloss29
-rwxr-xr-xtest/simulation/126-burst45
-rwxr-xr-xtest/simulation/127-filter43
-rwxr-xr-xtest/simulation/128-nocontrol27
-rwxr-xr-xtest/simulation/129-reload109
-rwxr-xr-xtest/simulation/130-quit31
-rwxr-xr-xtest/simulation/131-maxchange20
-rwxr-xr-xtest/simulation/132-logchange21
-rwxr-xr-xtest/simulation/133-hwtimestamp60
-rwxr-xr-xtest/simulation/134-log35
-rwxr-xr-xtest/simulation/135-ratelimit18
-rwxr-xr-xtest/simulation/136-broadcast16
-rwxr-xr-xtest/simulation/137-pool49
-rwxr-xr-xtest/simulation/138-syncloop34
-rwxr-xr-xtest/simulation/139-nts312
-rwxr-xr-xtest/simulation/140-noclientlog21
-rwxr-xr-xtest/simulation/141-copy19
-rwxr-xr-xtest/simulation/142-ptpport41
-rwxr-xr-xtest/simulation/143-manual70
-rwxr-xr-xtest/simulation/144-exp155
-rwxr-xr-xtest/simulation/201-freqaccumulation35
-rwxr-xr-xtest/simulation/202-prefer21
-rw-r--r--test/simulation/README11
-rwxr-xr-xtest/simulation/run90
-rw-r--r--test/simulation/test.common528
62 files changed, 3731 insertions, 0 deletions
diff --git a/test/simulation/001-defaults b/test/simulation/001-defaults
new file mode 100755
index 0000000..b39d95c
--- /dev/null
+++ b/test/simulation/001-defaults
@@ -0,0 +1,13 @@
+#!/usr/bin/env bash
+
+. ./test.common
+
+test_start "default test settings"
+
+run_test || test_fail
+check_chronyd_exit || test_fail
+check_source_selection || test_fail
+check_packet_interval || test_fail
+check_sync || test_fail
+
+test_pass
diff --git a/test/simulation/002-largenetwork b/test/simulation/002-largenetwork
new file mode 100755
index 0000000..a9e0ad8
--- /dev/null
+++ b/test/simulation/002-largenetwork
@@ -0,0 +1,22 @@
+#!/usr/bin/env bash
+
+. ./test.common
+
+test_start "large network"
+
+time_rms_limit=5e-4
+
+server_strata=3
+servers=4
+clients=5
+
+client_start=2000
+min_sync_time=2100
+max_sync_time=2300
+
+run_test || test_fail
+check_chronyd_exit || test_fail
+check_packet_interval || test_fail
+check_sync || test_fail
+
+test_pass
diff --git a/test/simulation/003-largefreqoffset b/test/simulation/003-largefreqoffset
new file mode 100755
index 0000000..9381b1a
--- /dev/null
+++ b/test/simulation/003-largefreqoffset
@@ -0,0 +1,19 @@
+#!/usr/bin/env bash
+
+. ./test.common
+
+test_start "large frequency offset"
+
+max_sync_time=1000
+
+for freq_offset in -5e-2 -5e-3 5e-3 5e-2; do
+ # Adjust offset so it's close to 0 on first clock update
+ time_offset=$(awk "BEGIN {print -($freq_offset * 130)}")
+ run_test || test_fail
+ check_chronyd_exit || test_fail
+ check_source_selection || test_fail
+ check_packet_interval || test_fail
+ check_sync || test_fail
+done
+
+test_pass
diff --git a/test/simulation/004-largetimeoffset b/test/simulation/004-largetimeoffset
new file mode 100755
index 0000000..4aebdd3
--- /dev/null
+++ b/test/simulation/004-largetimeoffset
@@ -0,0 +1,18 @@
+#!/usr/bin/env bash
+
+. ./test.common
+
+test_start "large time offset"
+
+min_sync_time=1300
+max_sync_time=1400
+
+for time_offset in -1e2 1e2; do
+ run_test || test_fail
+ check_chronyd_exit || test_fail
+ check_source_selection || test_fail
+ check_packet_interval || test_fail
+ check_sync || test_fail
+done
+
+test_pass
diff --git a/test/simulation/005-externalstep b/test/simulation/005-externalstep
new file mode 100755
index 0000000..e6fff26
--- /dev/null
+++ b/test/simulation/005-externalstep
@@ -0,0 +1,46 @@
+#!/usr/bin/env bash
+
+. ./test.common
+
+test_start "external time step"
+
+min_sync_time=1500
+max_sync_time=1550
+
+for step in -1e2 1e2; do
+ # Make one step in 150th second
+ client_step="(* $step (equal 0.1 (sum 1.0) 150))"
+ run_test || test_fail
+ check_chronyd_exit || test_fail
+ check_packet_interval || test_fail
+ check_sync || test_fail
+done
+
+min_sync_time=5120
+max_sync_time=6200
+client_conf="makestep 1 -1"
+
+for step in -1e8 -1e5 1e5 1e8; do
+ # Make one step in 5000th second
+ client_step="(* $step (equal 0.1 (sum 1.0) 5000))"
+ run_test || test_fail
+ check_chronyd_exit || test_fail
+ check_packet_interval || test_fail
+ check_sync || test_fail
+done
+
+min_sync_time=$default_min_sync_time
+max_sync_time=$default_max_sync_time
+time_max_limit=2e4
+time_rms_limit=8e3
+
+for step in -1e4 1e4; do
+ # Make a step every 500 seconds
+ client_step="(* $step (equal 0.1 (% (sum 1.0) 500) 0))"
+ run_test || test_fail
+ check_chronyd_exit || test_fail
+ check_packet_interval || test_fail
+ check_sync || test_fail
+done
+
+test_pass
diff --git a/test/simulation/006-largejitter b/test/simulation/006-largejitter
new file mode 100755
index 0000000..36ae5e2
--- /dev/null
+++ b/test/simulation/006-largejitter
@@ -0,0 +1,21 @@
+#!/usr/bin/env bash
+
+. ./test.common
+
+test_start "large jitter"
+
+time_offset=1e0
+jitter=1e-1
+
+time_max_limit=5e-1
+freq_max_limit=2e-1
+time_rms_limit=1e-1
+freq_rms_limit=5e-3
+
+run_test || test_fail
+check_chronyd_exit || test_fail
+check_source_selection || test_fail
+check_packet_interval || test_fail
+check_sync || test_fail
+
+test_pass
diff --git a/test/simulation/007-largewander b/test/simulation/007-largewander
new file mode 100755
index 0000000..af0c599
--- /dev/null
+++ b/test/simulation/007-largewander
@@ -0,0 +1,20 @@
+#!/usr/bin/env bash
+
+. ./test.common
+
+test_start "large wander"
+
+wander=1e-7
+
+time_max_limit=5e-3
+freq_max_limit=5e-3
+time_rms_limit=1e-3
+freq_rms_limit=1e-4
+
+run_test || test_fail
+check_chronyd_exit || test_fail
+check_source_selection || test_fail
+check_packet_interval || test_fail
+check_sync || test_fail
+
+test_pass
diff --git a/test/simulation/008-ntpera b/test/simulation/008-ntpera
new file mode 100755
index 0000000..2eea63b
--- /dev/null
+++ b/test/simulation/008-ntpera
@@ -0,0 +1,50 @@
+#!/usr/bin/env bash
+
+. ./test.common
+test_start "NTP eras"
+
+if check_config_h 'HAVE_LONG_TIME_T 1'; then
+ ntp_start=$(awk "BEGIN {print $(grep NTP_ERA_SPLIT ../../config.h | tr -dc '0-9*+-')}")
+else
+ ntp_start="-2147483648"
+fi
+
+# Set the starting test date to 500 seconds before the second NTP era.
+# This should work with 32-bit time_t and also with 64-bit time_t if the
+# configured NTP interval covers the test interval.
+export CLKNETSIM_START_DATE=$(date -d 'Feb 7 06:19:56 UTC 2036' +'%s')
+
+if awk "BEGIN {exit !($ntp_start <= $CLKNETSIM_START_DATE && \
+ $CLKNETSIM_START_DATE + $limit < $ntp_start + 2^32)}"; then
+ run_test || test_fail
+ check_chronyd_exit || test_fail
+ check_source_selection || test_fail
+ check_packet_interval || test_fail
+ check_sync || test_fail
+fi
+
+# The following tests need 64-bit time_t and ntp_start not before 1970
+check_config_h 'HAVE_LONG_TIME_T 1' || test_skip
+echo "$ntp_start" | grep -q '-' && test_skip
+
+for time_offset in -1e-1 1e-1; do
+ for start_offset in 0 "2^32 - $limit"; do
+ export CLKNETSIM_START_DATE=$(awk "BEGIN {printf \"%.0f\", $ntp_start + $start_offset}")
+ run_test || test_fail
+ check_chronyd_exit || test_fail
+ check_source_selection || test_fail
+ check_packet_interval || test_fail
+ check_sync || test_fail
+ done
+
+ for start_offset in -$limit "2^32"; do
+ export CLKNETSIM_START_DATE=$(awk "BEGIN {printf \"%.0f\", $ntp_start + $start_offset}")
+ run_test || test_fail
+ check_chronyd_exit || test_fail
+ check_source_selection || test_fail
+ check_packet_interval || test_fail
+ check_sync && test_fail
+ done
+done
+
+test_pass
diff --git a/test/simulation/009-sourceselection b/test/simulation/009-sourceselection
new file mode 100755
index 0000000..547c376
--- /dev/null
+++ b/test/simulation/009-sourceselection
@@ -0,0 +1,40 @@
+#!/usr/bin/env bash
+
+. ./test.common
+
+test_start "source selection"
+
+# Falsetickers should be detected if their number is less than half of all
+
+base_delay=1e-3
+servers=5
+
+for falsetickers in 1 2; do
+ run_test || test_fail
+ check_chronyd_exit || test_fail
+ check_source_selection || test_fail
+ check_packet_interval || test_fail
+ check_sync || test_fail
+done
+
+for falsetickers in 3 4; do
+ run_test || test_fail
+ check_chronyd_exit || test_fail
+ check_packet_interval || test_fail
+ # These check are expected to fail
+ check_source_selection && test_fail
+ check_sync && test_fail
+done
+
+# Sources with large asymmetric delay should be excluded
+
+servers=3
+falsetickers=0
+base_delay="(+ 1e-3 (equal 0.1 to 2) (equal 0.1 to 3))"
+
+run_test || test_fail
+check_chronyd_exit || test_fail
+check_source_selection || test_fail
+check_sync || test_fail
+
+test_pass
diff --git a/test/simulation/010-multrecv b/test/simulation/010-multrecv
new file mode 100755
index 0000000..36e7476
--- /dev/null
+++ b/test/simulation/010-multrecv
@@ -0,0 +1,17 @@
+#!/usr/bin/env bash
+
+. ./test.common
+
+export CLKNETSIM_RECV_MULTIPLY=4
+
+test_start "multiple received packets"
+
+limit=50000
+client_server_options="minpoll 6 maxpoll 6"
+
+run_test || test_fail
+check_chronyd_exit || test_fail
+check_source_selection || test_fail
+check_sync || test_fail
+
+test_pass
diff --git a/test/simulation/011-asymjitter b/test/simulation/011-asymjitter
new file mode 100755
index 0000000..9fb5567
--- /dev/null
+++ b/test/simulation/011-asymjitter
@@ -0,0 +1,18 @@
+#!/usr/bin/env bash
+
+. ./test.common
+
+test_start "asymmetric jitter"
+
+jitter=5e-4
+jitter_asymmetry=0.47
+limit=100000
+max_sync_time=2000
+
+run_test || test_fail
+check_chronyd_exit || test_fail
+check_source_selection || test_fail
+check_packet_interval || test_fail
+check_sync || test_fail
+
+test_pass
diff --git a/test/simulation/012-daemonts b/test/simulation/012-daemonts
new file mode 100755
index 0000000..a1b90e3
--- /dev/null
+++ b/test/simulation/012-daemonts
@@ -0,0 +1,15 @@
+#!/usr/bin/env bash
+
+. ./test.common
+
+test_start "daemon timestamping"
+
+export CLKNETSIM_TIMESTAMPING=0
+
+run_test || test_fail
+check_chronyd_exit || test_fail
+check_source_selection || test_fail
+check_packet_interval || test_fail
+check_sync || test_fail
+
+test_pass
diff --git a/test/simulation/013-nameserv b/test/simulation/013-nameserv
new file mode 100755
index 0000000..941026b
--- /dev/null
+++ b/test/simulation/013-nameserv
@@ -0,0 +1,15 @@
+#!/usr/bin/env bash
+
+. ./test.common
+
+test_start "name resolving"
+
+dns=1
+
+run_test || test_fail
+check_chronyd_exit || test_fail
+check_source_selection || test_fail
+check_packet_interval || test_fail
+check_sync || test_fail
+
+test_pass
diff --git a/test/simulation/101-poll b/test/simulation/101-poll
new file mode 100755
index 0000000..1416b22
--- /dev/null
+++ b/test/simulation/101-poll
@@ -0,0 +1,56 @@
+#!/usr/bin/env bash
+
+. ./test.common
+test_start "minpoll/maxpoll options"
+
+wander=0.0
+jitter=1e-6
+
+time_max_limit=1e-5
+freq_max_limit=1e-5
+time_rms_limit=5e-6
+freq_rms_limit=5e-6
+client_conf="makestep 1e-2 1"
+
+for poll in $(seq 1 14); do
+ client_server_options="minpoll $poll maxpoll $poll"
+ limit=$[2**$poll * 10]
+ min_sync_time=$[2**$poll * 2]
+ max_sync_time=$[2**$poll * 21 / 10 + 1]
+ client_max_min_out_interval=$(awk "BEGIN {print 2^$poll * 1.1}")
+ client_min_mean_out_interval=$(awk "BEGIN {print 2^$poll * 0.99}")
+
+ run_test || test_fail
+ check_chronyd_exit || test_fail
+ check_source_selection || test_fail
+ check_packet_interval || test_fail
+ check_sync || test_fail
+done
+
+min_sync_time=$default_min_sync_time
+max_sync_time=$default_max_sync_time
+client_max_min_out_interval=$default_client_max_min_out_interval
+client_min_mean_out_interval=$default_client_min_mean_out_interval
+
+limit=10
+
+for poll in $(seq -7 2 -1); do
+ client_server_options="minpoll $poll maxpoll $poll"
+
+ base_delay=1e-4
+
+ run_test || test_fail
+ check_chronyd_exit || test_fail
+ check_source_selection || test_fail
+ check_file_messages " 2 1 " \
+ $[2**-poll * limit * 9 / 10] $[2**-poll * limit] log.packets || test_fail
+
+ base_delay=2e-2
+
+ run_test || test_fail
+ check_chronyd_exit || test_fail
+ check_source_selection || test_fail
+ check_file_messages " 2 1 " $[limit * 9 / 10] $limit log.packets || test_fail
+done
+
+test_pass
diff --git a/test/simulation/102-iburst b/test/simulation/102-iburst
new file mode 100755
index 0000000..9936572
--- /dev/null
+++ b/test/simulation/102-iburst
@@ -0,0 +1,23 @@
+#!/usr/bin/env bash
+
+. ./test.common
+test_start "iburst option"
+
+freq_offset=1e-4
+
+client_conf="makestep 1e-2 1
+driftfile tmp/drift"
+client_server_options="iburst"
+
+min_sync_time=4
+max_sync_time=6
+
+echo "100 1.0" > tmp/drift
+
+run_test || test_fail
+check_chronyd_exit || test_fail
+check_source_selection || test_fail
+check_packet_interval || test_fail
+check_sync || test_fail
+
+test_pass
diff --git a/test/simulation/103-initstepslew b/test/simulation/103-initstepslew
new file mode 100755
index 0000000..fe47b68
--- /dev/null
+++ b/test/simulation/103-initstepslew
@@ -0,0 +1,63 @@
+#!/usr/bin/env bash
+
+. ./test.common
+test_start "initstepslew directive"
+
+freq_offset=0.0
+wander=0.0
+time_rms_limit=1e-3
+limit=100
+
+client_conf="initstepslew 5 192.168.123.1"
+client_server_conf="#"
+
+min_sync_time=6
+max_sync_time=35
+
+for time_offset in -2.0 -0.2 0.2 2.0; do
+ run_test || test_fail
+ check_chronyd_exit || test_fail
+ check_packet_interval || test_fail
+ check_sync || test_fail
+ check_log_messages "00:00:0.Z System's initial.*slew" 1 1 || test_fail
+done
+
+min_sync_time=5
+max_sync_time=5
+
+for time_offset in -1e8 -1e2 1e2 1e8; do
+ run_test || test_fail
+ check_packet_interval || test_fail
+ check_sync || test_fail
+ check_log_messages "System's initial.*step" 1 1 || test_fail
+done
+
+time_offset=3
+limit=500
+servers=2
+falsetickers=1
+client_conf="initstepslew 5 192.168.123.1 192.168.123.2"
+client_server_conf="server 192.168.123.2"
+
+min_sync_time=360
+max_sync_time=450
+
+run_test || test_fail
+check_chronyd_exit || test_fail
+check_packet_interval || test_fail
+check_sync || test_fail
+check_log_messages "00:03:2.Z No suitable source for initstepslew" 1 1 || test_fail
+
+client_conf="initstepslew 5 192.168.123.1 192.168.123.2"
+
+min_sync_time=1
+max_sync_time=500
+server_conf="deny all"
+
+run_test || test_fail
+check_chronyd_exit || test_fail
+check_packet_interval || test_fail
+check_sync && test_fail
+check_log_messages "00:00:1.Z No suitable source for initstepslew" 1 1 || test_fail
+
+test_pass
diff --git a/test/simulation/104-driftfile b/test/simulation/104-driftfile
new file mode 100755
index 0000000..93d4363
--- /dev/null
+++ b/test/simulation/104-driftfile
@@ -0,0 +1,23 @@
+#!/usr/bin/env bash
+
+. ./test.common
+test_start "driftfile directive"
+
+servers=0
+time_offset=0.0
+wander=0.0
+limit=10
+freq_max_limit=1e-9
+min_sync_time=1
+max_sync_time=1
+client_conf="driftfile tmp/drift"
+
+for freq_offset in -5e-2 -5e-4 -5e-6 5e-6 5e-4 5e-2; do
+ awk "BEGIN {printf \"%.9e 1\", 1e6 - 1 / (1 + $freq_offset) * 1e6}" > tmp/drift
+ run_test || test_fail
+ check_chronyd_exit || test_fail
+ check_packet_interval || test_fail
+ check_sync || test_fail
+done
+
+test_pass
diff --git a/test/simulation/105-ntpauth b/test/simulation/105-ntpauth
new file mode 100755
index 0000000..1f228f5
--- /dev/null
+++ b/test/simulation/105-ntpauth
@@ -0,0 +1,96 @@
+#!/usr/bin/env bash
+
+. ./test.common
+
+test_start "NTP authentication"
+
+server_conf="keyfile tmp/server.keys"
+client_conf="keyfile tmp/client.keys"
+
+cat > tmp/server.keys <<-EOF
+1 MD5 HEX:6B5D3C6A2E4A74775E4F6F3B7A35453E6E5C5F302D783D2979505C663C295A5E
+2 MD5 HEX:6B5D3C6A2E4A74775E4F6F3B7A35453E6E5C5F302D783D2979505C663C295A5E
+3 MD5 HEX:6B5D3C6A2E4A74775E4F6F3B7A35453E6E5C5F302D783D2979505C663C295A5E
+4 MD5 HEX:6B5D3C6A2E4A74775E4F6F3B7A35453E6E5C5F302D783D2979505C663C295A5E
+EOF
+
+cat > tmp/client.keys <<-EOF
+1 k]<j.Jtw^Oo;z5E>n\_0-x=)yP\f<)Z^
+2 ASCII:k]<j.Jtw^Oo;z5E>n\_0-x=)yP\f<)Z^
+3 MD5 ASCII:k]<j.Jtw^Oo;z5E>n\_0-x=)yP\f<)Z^
+4 MD5 HEX:6B5D3C6A2E4A74775E4F6F3B7A35453E6E5C5F302D783D2979505C663C295A5E
+EOF
+
+keys=4
+
+types="MD5"
+check_config_h 'FEAT_SECHASH 1' && types="$types SHA1 SHA256 SHA384 SHA512"
+check_config_h 'HAVE_CMAC 1' && types="$types AES128 AES256"
+
+for type in $types; do
+ keys=$[$keys + 1]
+ case $type in
+ AES128) length=16;;
+ AES256) length=32;;
+ *) length=$[$RANDOM % 32 + 1];;
+ esac
+
+ key=$(echo $keys $type HEX:$(tr -c -d '0-9A-F' < /dev/urandom 2> /dev/null | \
+ head -c $[$length * 2]))
+ echo "$key" >> tmp/server.keys
+ echo "$key" >> tmp/client.keys
+done
+
+for version in 3 4; do
+ for key in $(seq $keys); do
+ client_server_options="version $version key $key"
+ run_test || test_fail
+ check_chronyd_exit || test_fail
+ check_source_selection || test_fail
+ check_packet_interval || test_fail
+ check_sync || test_fail
+ done
+done
+
+server_conf=""
+
+run_test || test_fail
+check_chronyd_exit || test_fail
+# This check must fail as the server doesn't know the key
+check_sync && test_fail
+check_packet_interval || test_fail
+
+server_conf="keyfile tmp/server.keys"
+client_conf=""
+
+run_test || test_fail
+check_chronyd_exit || test_fail
+# This check must fail as the client doesn't know the key
+check_sync && test_fail
+check_packet_interval || test_fail
+
+client_conf="keyfile tmp/client.keys"
+clients=2
+peers=2
+max_sync_time=500
+base_delay="$default_base_delay (* -1 (equal 0.1 from 3) (equal 0.1 to 1))"
+
+for versions in "3 3" "3 4" "4 3" "4 4"; do
+ for key in 1 $keys; do
+ client_lpeer_options="version ${versions% *} key $key"
+ client_rpeer_options="version ${versions#* } key $key"
+ run_test || test_fail
+ check_chronyd_exit || test_fail
+ check_sync || test_fail
+ done
+done
+
+client_lpeer_options="key 1"
+client_rpeer_options="key 2"
+
+run_test || test_fail
+check_chronyd_exit || test_fail
+# This check must fail as the peers are using different keys"
+check_sync && test_fail
+
+test_pass
diff --git a/test/simulation/106-refclock b/test/simulation/106-refclock
new file mode 100755
index 0000000..f09f170
--- /dev/null
+++ b/test/simulation/106-refclock
@@ -0,0 +1,143 @@
+#!/usr/bin/env bash
+
+. ./test.common
+test_start "SHM refclock"
+
+check_config_h 'FEAT_REFCLOCK 1' || test_skip
+check_config_h 'FEAT_PHC 1' || test_skip
+check_config_h 'FEAT_CMDMON 1' || test_skip
+
+export CLKNETSIM_PHC_DELAY=1e-6
+export CLKNETSIM_PHC_JITTER=1e-7
+
+servers=0
+limit=1000
+refclock_jitter=$jitter
+min_sync_time=45
+max_sync_time=70
+chronyc_start=70
+chronyc_conf="tracking"
+
+for refclock in "SHM 0" "PHC /dev/ptp0" "PHC /dev/ptp0:nocrossts"; do
+ client_conf="refclock $refclock stratum 3 delay 1e-3 refid GPS
+logdir tmp
+log refclocks"
+
+ run_test || test_fail
+ check_chronyd_exit || test_fail
+ check_source_selection || test_fail
+ check_sync || test_fail
+ check_chronyc_output "^Reference ID.*47505300 \(GPS\)
+Stratum.*: 4
+.*
+Root delay : 0.001000000 seconds
+.*
+Update interval : 16\.. seconds
+.*$" || test_fail
+
+ if echo "$refclock" | grep -q 'PHC.*nocrossts'; then
+ check_file_messages "20.* GPS.*[0-9] N " 650 750 refclocks.log || test_fail
+ else
+ check_file_messages "20.* GPS.*[0-9] N " 997 1001 refclocks.log || test_fail
+ fi
+ check_file_messages "20.* GPS.*- N " 61 63 refclocks.log || test_fail
+ rm -f tmp/refclocks.log
+done
+
+if check_config_h 'FEAT_PPS 1'; then
+ refclock_offset=0.35
+ refclock_jitter=0.05
+
+ client_conf="
+refclock SHM 0 refid NMEA noselect
+refclock PPS /dev/pps0 lock NMEA
+logdir tmp
+log refclocks"
+
+ run_test || test_fail
+ check_chronyd_exit || test_fail
+ check_source_selection || test_fail
+ check_sync || test_fail
+ check_chronyc_output "^Reference ID.*50505331 \(PPS1\)
+Stratum.*: 1
+.*
+Root delay : 0\.000000001 seconds
+.*$" || test_fail
+
+ check_file_messages "20.* PPS1.*[0-9] N " 620 740 refclocks.log || test_fail
+ check_file_messages "20.* PPS1.*- N " 60 63 refclocks.log || test_fail
+ rm -f tmp/refclocks.log
+
+ client_conf="
+refclock SHM 0 noselect
+refclock PPS /dev/pps0
+local
+logdir tmp
+log refclocks"
+
+ run_test || test_fail
+ check_chronyd_exit || test_fail
+ check_source_selection || test_fail
+ check_sync || test_fail
+ check_chronyc_output "^Reference ID.*50505331 \(PPS1\)
+Stratum.*: 10
+.*
+Root delay : 0\.000000001 seconds
+.*$" || test_fail
+
+ check_file_messages "20.* PPS1.*[0-9] N " 997 1001 refclocks.log || test_fail
+ check_file_messages "20.* PPS1.*- N " 60 63 refclocks.log || test_fail
+ rm -f tmp/refclocks.log
+
+ min_sync_time=100
+ max_sync_time=220
+ chronyc_start=220
+ client_conf="
+refclock SHM 0 refid NMEA offset 0.35 delay 0.1
+refclock PPS /dev/pps0
+logdir tmp
+log refclocks"
+
+ run_test || test_fail
+ check_chronyd_exit || test_fail
+ check_source_selection || test_fail
+ check_sync || test_fail
+ check_chronyc_output "^Reference ID.*50505331 \(PPS1\)
+Stratum.*: 1
+.*
+Root delay : 0\.000000001 seconds
+.*$" || test_fail
+
+ check_file_messages "20.* PPS1.*[0-9] N " 800 940 refclocks.log || test_fail
+ check_file_messages "20.* PPS1.*- N " 50 63 refclocks.log || test_fail
+ rm -f tmp/refclocks.log
+fi
+
+refclock_offset="(+ 0.399 (sum 1e-3))"
+refclock_jitter=1e-6
+servers=1
+freq_offset="(* 1e-4 (sine 1000))"
+base_delay="(* -1.0 (equal 0.1 (min time 5000) 5000))"
+client_server_options="minpoll 4 maxpoll 4 filter 5 minsamples 64"
+client_conf="
+refclock PHC /dev/ptp0 local poll 2
+logdir tmp
+log refclocks tracking"
+chronyc_conf=""
+limit=10000
+max_sync_time=5000
+time_max_limit=1e-3
+time_rms_limit=5e-4
+freq_max_limit=2e-5
+freq_rms_limit=5e-6
+
+run_test || test_fail
+check_chronyd_exit || test_fail
+check_sync || test_fail
+
+check_file_messages "20.* PHC0 .* [0-9] ? " 9999 10001 refclocks.log || test_fail
+check_file_messages "20.* PHC0 .* - ? " 2499 2501 refclocks.log || test_fail
+check_file_messages "20.* PHC0 " 0 0 tracking.log || test_fail
+rm -f tmp/refclocks.log tmp/tracking.log
+
+test_pass
diff --git a/test/simulation/107-allowdeny b/test/simulation/107-allowdeny
new file mode 100755
index 0000000..4665337
--- /dev/null
+++ b/test/simulation/107-allowdeny
@@ -0,0 +1,48 @@
+#!/usr/bin/env bash
+
+. ./test.common
+
+test_start "allow/deny directives"
+
+check_config_h 'FEAT_CMDMON 1' || test_skip
+
+limit=500
+
+# Note that start_client in clknetsim.bash always adds allow to the config
+
+for server_conf in \
+ "deny" \
+ "deny all" \
+ "deny 192.168.0.0/16" \
+ "deny 192.168.123" \
+ "deny 192.168.123.2" \
+ "deny all
+allow 192.168.124.0/24"
+do
+ run_test || test_fail
+ check_chronyd_exit || test_fail
+ check_packet_interval || test_fail
+ # These checks are expected to fail
+ check_source_selection && test_fail
+ check_sync && test_fail
+done
+
+for server_conf in \
+ "deny all
+allow" \
+ "deny all
+allow all" \
+ "deny all
+allow 192.168.123" \
+ "deny all
+allow 192.168.123/24" \
+ "deny 192.168.124.0/24"
+do
+ run_test || test_fail
+ check_chronyd_exit || test_fail
+ check_source_selection || test_fail
+ check_packet_interval || test_fail
+ check_sync || test_fail
+done
+
+test_pass
diff --git a/test/simulation/108-peer b/test/simulation/108-peer
new file mode 100755
index 0000000..906de17
--- /dev/null
+++ b/test/simulation/108-peer
@@ -0,0 +1,54 @@
+#!/usr/bin/env bash
+
+. ./test.common
+
+test_start "NTP peers"
+
+# Allow and drop packets to the server in 1000 second intervals, so only one
+# client has access to it and the other is forced to switch to the peer.
+base_delay=$(cat <<-EOF | tr -d '\n'
+ (+ 1e-4
+ (* -1
+ (equal 0.1 from 2)
+ (equal 0.1 to 1)
+ (equal 0.1 (min (% time 2000) 1000) 1000))
+ (* -1
+ (equal 0.1 from 3)
+ (equal 0.1 to 1)
+ (equal 0.1 (max (% time 2000) 1000) 1000)))
+EOF
+)
+
+clients=2
+peers=2
+max_sync_time=1000
+client_server_options="minpoll 6 maxpoll 6"
+client_peer_options="minpoll 6 maxpoll 6"
+
+run_test || test_fail
+check_chronyd_exit || test_fail
+check_source_selection || test_fail
+check_sync || test_fail
+
+base_delay="(+ 1e-4 (* -1 (equal 0.1 from 3) (equal 0.1 to 1)))"
+client_peer_options=""
+
+while read lminpoll lmaxpoll rminpoll rmaxpoll max_sync_time; do
+ client_lpeer_options="minpoll $lminpoll maxpoll $lmaxpoll"
+ client_rpeer_options="minpoll $rminpoll maxpoll $rmaxpoll"
+ limit=$[$max_sync_time * 10]
+
+ run_test || test_fail
+ check_chronyd_exit || test_fail
+ check_sync || test_fail
+done <<-EOF
+ 3 6 3 6 400
+ 3 3 6 6 450
+ 6 6 3 3 450
+ 3 6 6 6 450
+ 6 6 3 6 450
+ -2 -2 2 2 220
+ 2 2 -2 -2 220
+EOF
+
+test_pass
diff --git a/test/simulation/109-makestep b/test/simulation/109-makestep
new file mode 100755
index 0000000..78d8d59
--- /dev/null
+++ b/test/simulation/109-makestep
@@ -0,0 +1,41 @@
+#!/usr/bin/env bash
+
+. ./test.common
+test_start "makestep directive"
+
+client_conf="makestep 0 -1
+corrtimeratio 1e10"
+
+run_test || test_fail
+check_chronyd_exit || test_fail
+check_source_selection || test_fail
+check_packet_interval || test_fail
+check_sync || test_fail
+
+limit=200
+jitter=1e-5
+client_conf="makestep 2 1"
+
+min_sync_time=130
+max_sync_time=150
+
+for time_offset in -1.0 -0.1 0.1 1.0; do
+ run_test || test_fail
+ check_chronyd_exit || test_fail
+ check_source_selection || test_fail
+ check_packet_interval || test_fail
+ check_sync || test_fail
+done
+
+min_sync_time=120
+max_sync_time=140
+
+for time_offset in -1e8 -1e2 1e2 1e8; do
+ run_test || test_fail
+ check_chronyd_exit || test_fail
+ check_source_selection || test_fail
+ check_packet_interval || test_fail
+ check_sync || test_fail
+done
+
+test_pass
diff --git a/test/simulation/110-chronyc b/test/simulation/110-chronyc
new file mode 100755
index 0000000..b78f0d8
--- /dev/null
+++ b/test/simulation/110-chronyc
@@ -0,0 +1,496 @@
+#!/usr/bin/env bash
+
+. ./test.common
+
+test_start "chronyc"
+
+check_config_h 'FEAT_REFCLOCK 1' || test_skip
+check_config_h 'FEAT_CMDMON 1' || test_skip
+
+refclock_jitter=$jitter
+client_server_conf="
+server node1.net1.clk
+server 192.168.123.2"
+client_conf="
+refclock SHM 0 noselect
+smoothtime 400 0.001 leaponly"
+cmdmon_unix=0
+
+chronyc_conf="activity
+tracking
+sourcename 192.168.123.1
+sourcename 192.168.123.2
+sources
+sourcestats
+manual list
+smoothing
+waitsync
+rtcdata"
+
+run_test || test_fail
+check_chronyd_exit || test_fail
+
+check_chronyc_output "^200 OK
+2 sources online
+0 sources offline
+0 sources doing burst \(return to online\)
+0 sources doing burst \(return to offline\)
+0 sources with unknown address
+Reference ID : C0A87B01 \(192\.168\.123\.1\)
+Stratum : 2
+Ref time \(UTC\) : Fri Jan 01 00:1.:.. 2010
+System time : 0\.0000..... seconds (slow|fast) of NTP time
+Last offset : [+-]0\.000...... seconds
+RMS offset : 0\.000...... seconds
+Frequency : (99|100)\.... ppm fast
+Residual freq : [+-][0-9]\.... ppm
+Skew : [0-9]\.... ppm
+Root delay : 0\.000...... seconds
+Root dispersion : 0\.000...... seconds
+Update interval : [0-9]+\.. seconds
+Leap status : Normal
+node1\.net1\.clk
+192\.168\.123\.2
+MS Name/IP address Stratum Poll Reach LastRx Last sample
+===============================================================================
+#\? SHM0 0 4 377 [0-9]+ [0-9 +-]+[un]s\[[0-9 +-]+[un]s\] \+/-[ 0-9]+[un]s
+\^\* 192\.168\.123\.1 1 [67] 377 [0-9]+ [0-9 +-]+[un]s\[[0-9 +-]+[un]s\] \+/-[ 0-9]+[un]s
+\^\? 192\.168\.123\.2 0 [0-9]+ 0 - \+0ns\[ \+0ns\] \+/- 0ns
+Name/IP Address NP NR Span Frequency Freq Skew Offset Std Dev
+==============================================================================
+SHM0 [0-9 ]+ [0-9 ]+ [0-9 ]+ [ +-][01]\.... [0-9 ]+\.... [0-9 +-]+[un]s [0-9 ]+[un]s
+192\.168\.123\.1 [0-9 ]+ [0-9 ]+ [0-9 ]+ [ +-][01]\.... [0-9 ]+\.... [0-9 +-]+[un]s [0-9 ]+[un]s
+192\.168\.123\.2 0 0 0 \+0\.000 2000\.000 \+0ns 4000ms
+210 n_samples = 0
+# Date Time\(UTC\) Slewed Original Residual
+=======================================================
+Active : Yes \(leap second only\)
+Offset : \+0\.000000000 seconds
+Frequency : \+0\.000000 ppm
+Wander : \+0\.000000 ppm per second
+Last update : [0-9]+\.. seconds ago
+Remaining time : 0\.0 seconds
+try: 1, refid: C0A87B01, correction: 0\.000......, skew: .\....
+513 RTC driver not running$" \
+|| test_fail
+
+chronyc_conf="tracking"
+dns=1
+
+run_test || test_fail
+check_chronyd_exit || test_fail
+
+check_chronyc_output "^Reference ID : C0A87B01 \(node1\.net1\.clk\)" \
+ || test_fail
+
+chronyc_options="-c"
+
+run_test || test_fail
+check_chronyd_exit || test_fail
+
+check_chronyc_output "^C0A87B01,192\.168\.123\.1,2,12623049..\..........,-?0\.0000.....,-?0\.000......,0\.000......,(99|100)\....,-?[0-9]\....,[0-9]\....,0\.000......,0\.000......,[0-9]+\..,Normal$" \
+ || test_fail
+
+chronyc_options=""
+server_strata=0
+chronyc_start=0.5
+client_server_conf=""
+client_conf=""
+server_conf="server 192.168.123.1"
+limit=1
+
+for chronyc_conf in \
+ "accheck 1.2.3.4" \
+ "add peer 10.0.0.0 minpoll 2 maxpoll 6" \
+ "add server 10.0.0.0 minpoll 6 maxpoll 10 iburst burst key 1 certset 2 maxdelay 1e-3 maxdelayratio 10.0 maxdelaydevratio 10.0 maxdelayquant 0.5 mindelay 1e-4 asymmetry 0.5 offset 1e-5 minsamples 6 maxsamples 6 filter 3 offline auto_offline prefer noselect trust require xleave polltarget 20 port 123 presend 7 minstratum 3 version 4 nts ntsport 4460 copy extfield F323" \
+ "add server node1.net1.clk" \
+ "allow 1.2.3.4" \
+ "allow 1.2" \
+ "allow 3.4.5" \
+ "allow 6.7.8/22" \
+ "allow 6.7.8.9/22" \
+ "allow 0/0" \
+ "allow" \
+ "allow all 10/24" \
+ "authdata" \
+ "burst 5/10" \
+ "burst 3/5 255.255.255.0/1.2.3.0" \
+ "burst 1/2 1.2.3.0/24" \
+ "clients" \
+ "clients -k" \
+ "clients -p 100" \
+ "clients -r" \
+ "cmdaccheck 1.2.3.4" \
+ "cmdallow 1.2.3.4" \
+ "cmdallow all 1.2.3.0/24" \
+ "cmddeny 1.2.3.4" \
+ "cmddeny all 1.2.3.0/24" \
+ "cyclelogs" \
+ "delete 10.0.0.0" \
+ "delete ID#0000000001" \
+ "deny 1.2.3.4" \
+ "deny all 1.2.3.0/24" \
+ "dfreq 1.0e-3" \
+ "doffset -1.0" \
+ "dump" \
+ "local stratum 5 distance 1.0 orphan" \
+ "local off" \
+ "makestep 10.0 3" \
+ "makestep" \
+ "manual delete 0" \
+ "manual off" \
+ "manual on" \
+ "manual reset" \
+ "maxdelay 1.2.3.4 1e-2" \
+ "maxdelaydevratio 1.2.3.4 5.0" \
+ "maxdelayratio 1.2.3.4 3.0" \
+ "maxpoll 1.2.3.4 5" \
+ "maxupdateskew 1.2.3.4 10.0" \
+ "minpoll 1.2.3.4 3" \
+ "minstratum 1.2.3.4 1" \
+ "minstratum ID#0000000001 1" \
+ "ntpdata 1.2.3.4" \
+ "offline" \
+ "offline 255.255.255.0/1.2.3.0" \
+ "offline 1.2.3.0/24" \
+ "online" \
+ "online 1.2.3.0/24" \
+ "onoffline" \
+ "polltarget 1.2.3.4 10" \
+ "refresh" \
+ "rekey" \
+ "reload sources" \
+ "reselect" \
+ "reselectdist 1e-3" \
+ "reset sources" \
+ "selectdata" \
+ "settime 16:30" \
+ "settime 16:30:05" \
+ "settime Nov 21, 2015 16:30:05" \
+ "serverstats" \
+ "shutdown" \
+ "smoothtime reset" \
+ "smoothtime activate" \
+ "trimrtc" \
+ "writertc"
+do
+ run_test || test_fail
+ check_chronyd_exit || test_fail
+ check_chronyc_output "501 Not authorised$" || test_fail
+done
+
+cmdmon_unix=1
+
+chronyc_conf="
+authdata
+clients -k -p 2
+clients -r
+clients
+ntpdata
+selectdata
+serverstats"
+
+run_test || test_fail
+check_chronyd_exit || test_fail
+
+check_chronyc_output "^Name/IP address Mode KeyID Type KLen Last Atmp NAK Cook CLen
+=========================================================================
+node1\.net1\.clk - 0 0 0 - 0 0 0 0
+Hostname NTP Drop Int IntL Last NTS-KE Drop Int Last
+===============================================================================
+Hostname NTP Drop Int IntL Last Cmd Drop Int Last
+===============================================================================
+node1\.net1\.clk 1 0 - - 0 0 0 - -
+Hostname NTP Drop Int IntL Last Cmd Drop Int Last
+===============================================================================
+node1\.net1\.clk 0 0 - - 0 0 0 - -
+
+Remote address : 192\.168\.123\.1 \(C0A87B01\)
+Remote port : 123
+Local address : 192\.168\.123\.1 \(C0A87B01\)
+Leap status : Normal
+Version : 4
+Mode : Server
+Stratum : 1
+Poll interval : 6 \(64 seconds\)
+Precision : -23 \(0\.000000119 seconds\)
+Root delay : 0\.000000 seconds
+Root dispersion : 0\.000000 seconds
+Reference ID : 7F7F0101 \(\)
+Reference time : Thu Dec 31 23:59:5[89] 2009
+Offset : [-+]0\.000...... seconds
+Peer delay : 0\.00....... seconds
+Peer dispersion : 0\.00000.... seconds
+Response time : 0\.000000... seconds
+Jitter asymmetry: \+0\.00
+NTP tests : 111 111 1110
+Interleaved : No
+Authenticated : No
+TX timestamping : Kernel
+RX timestamping : Kernel
+Total TX : 1
+Total RX : 1
+Total valid RX : 1
+Total good RX : 0
+S Name/IP Address Auth COpts EOpts Last Score Interval Leap
+=======================================================================
+M node1\.net1\.clk N ----- ----- 0 1\.0 \+0ns \+0ns N
+NTP packets received : 1
+NTP packets dropped : 0
+Command packets received : 12
+Command packets dropped : 0
+Client log records dropped : 0
+NTS-KE connections accepted: 0
+NTS-KE connections dropped : 0
+Authenticated NTP packets : 0
+Interleaved NTP packets : 0
+NTP timestamps held : 0
+NTP timestamp span : 0$" || test_fail
+
+chronyc_conf="
+deny all
+cmdallow all
+allow 1.2.3.4
+allow 1.2.3.0/28
+deny 1.2.3.0/27
+allow 1.2.4.5
+deny all 1.2.4.0/27
+cmddeny 5.6.7.8
+cmdallow all 5.6.7.0/28
+accheck 1.2.3.4
+accheck 1.2.3.5
+accheck 1.2.4.5
+cmdaccheck 5.6.7.8"
+
+run_test || test_fail
+check_chronyd_exit || test_fail
+
+check_chronyc_output "^200 OK
+200 OK
+200 OK
+200 OK
+200 OK
+200 OK
+200 OK
+200 OK
+200 OK
+208 Access allowed
+209 Access denied
+209 Access denied
+208 Access allowed$" || test_fail
+
+if check_config_h 'FEAT_IPV6 1'; then
+ chronyc_conf="
+ deny all
+ cmdallow all
+ allow 2001:db8::1
+ allow 2001:db8::/64
+ deny 2001:db8::/63
+ allow 2001:db8:1::1
+ deny all 2001:db8:1::/63
+ cmddeny 2001:db9::1
+ cmdallow all 2001:db9::/64
+ accheck 2001:db8::1
+ accheck 2001:db8::2
+ accheck 2001:db8:1::1
+ cmdaccheck 2001:db9::1"
+
+ run_test || test_fail
+ check_chronyd_exit || test_fail
+
+ check_chronyc_output "^200 OK
+200 OK
+200 OK
+200 OK
+200 OK
+200 OK
+200 OK
+200 OK
+200 OK
+208 Access allowed
+209 Access denied
+209 Access denied
+208 Access allowed$" || test_fail
+fi
+
+chronyc_conf="
+delete 192.168.123.1
+add server node1.net1.clk minpoll 6 maxpoll 10 iburst
+offline 192.168.123.1
+burst 1/1 192.168.123.1
+online 192.168.123.1
+maxdelay 192.168.123.1 1e-2
+maxdelaydevratio 192.168.123.1 5.0
+maxdelayratio 192.168.123.1 3.0
+maxpoll 192.168.123.1 5
+maxupdateskew 192.168.123.1 10.0
+minpoll 192.168.123.1 3
+minstratum 192.168.123.1 1
+polltarget 192.168.123.1 10
+delete 192.168.123.1"
+
+run_test || test_fail
+check_chronyd_exit || test_fail
+
+check_chronyc_output "^200 OK
+200 OK
+200 OK
+200 OK
+200 OK
+200 OK
+200 OK
+200 OK
+200 OK
+200 OK
+200 OK
+200 OK
+200 OK
+200 OK$" || test_fail
+
+chronyc_conf="
+cyclelogs
+dump
+dfreq 1.0e-3
+doffset -0.01
+local stratum 5 distance 1.0 orphan
+local off
+makestep 10.0 3
+makestep
+manual on
+settime now
+manual delete 0
+manual reset
+manual off
+onoffline
+refresh
+rekey
+reload sources
+reselect
+reselectdist 1e-3
+reset sources
+shutdown"
+
+run_test || test_fail
+check_chronyd_exit || test_fail
+
+check_chronyc_output "^200 OK
+200 OK
+200 OK
+200 OK
+200 OK
+200 OK
+200 OK
+200 OK
+200 OK
+200 OK
+Clock was .\... seconds fast. Frequency change = 0.00ppm, new frequency = 0.00ppm
+200 OK
+200 OK
+200 OK
+200 OK
+200 OK
+200 OK
+200 OK
+200 OK
+200 OK
+200 OK
+200 OK$" || test_fail
+
+server_conf="
+server 192.168.123.1
+noclientlog"
+
+commands=(
+ "add server nosuchnode.net1.clk" "^Invalid host/IP address$"
+ "allow nosuchnode.net1.clk" "^Could not read address$"
+ "allow 192.168.123.0/2 4" "^Could not read address$"
+ "allow 192.168.123.0/2e" "^Could not read address$"
+ "allow 192.168.12e" "^Could not read address$"
+ "allow 192.168123" "^Could not read address$"
+ "allow 192.168.123.2/33" "^507 Bad subnet$"
+ "clients" "Hostname.*519 Client logging is not active in the daemon$"
+ "delete 192.168.123.2" "^503 No such source$"
+ "minpoll 192.168.123.2 5" "^503 No such source$"
+ "ntpdata 192.168.123.2" "^503 No such source$"
+ "settime now" "^505 Facility not enabled in daemon$"
+ "smoothing" "^505 Facility not enabled in daemon$"
+ "smoothtime activate" "^505 Facility not enabled in daemon$"
+ "smoothtime reset" "^505 Facility not enabled in daemon$"
+ "sourcename 192.168.123.2" "^503 No such source$"
+ "trimrtc" "^513 RTC driver not running$"
+ "writertc" "^513 RTC driver not running$"
+)
+
+for i in $(seq 0 $[${#commands[*]} / 2]); do
+ chronyc_conf=${commands[$[i * 2]]}
+ run_test || test_fail
+ check_chronyd_exit || test_fail
+ check_chronyc_output "${commands[$[i * 2 + 1]]}" || test_fail
+done
+
+cmdmon_unix=0
+server_conf="server 192.168.123.1"
+
+chronyc_conf="dns -n
+dns +n
+dns -4
+dns -6
+dns -46
+timeout 200
+retries 1
+keygen
+keygen 10 MD5 128
+keygen 11 MD5 40
+help
+quit
+nosuchcommand"
+
+run_test || test_fail
+
+check_chronyc_output "^1 (MD5|SHA1) HEX:........................................
+10 MD5 HEX:................................
+11 MD5 HEX:....................
+System clock:.*this help
+ *$" || test_fail
+
+chronyc_conf="keygen 10 NOSUCHTYPE 128
+help"
+run_test || test_fail
+check_chronyc_output "^Unknown hash function or cipher NOSUCHTYPE\$" || test_fail
+
+if check_config_h 'FEAT_SECHASH 1'; then
+ for hash in MD5 SHA1 SHA256 SHA384 SHA512; do
+ chronyc_conf="keygen 5 $hash"
+ run_test || test_fail
+ check_chronyc_output "^5 $hash HEX:........................................\$" || test_fail
+ done
+fi
+
+if check_config_h 'HAVE_CMAC 1'; then
+ chronyc_conf="keygen 6 AES128
+keygen 7 AES256"
+ run_test || test_fail
+ check_chronyc_output "^6 AES128 HEX:................................
+7 AES256 HEX:................................................................\$" || test_fail
+fi
+
+# Pass every fourth request
+base_delay=$(cat <<-EOF | tr -d '\n'
+ (+ 1e-4
+ (* -1
+ (equal 0.1 from 2)
+ (equal 0.1 (min (% (sum 1) 4) 1) 1)))
+EOF
+)
+limit=15
+
+chronyc_conf="sources"
+run_test || test_fail
+check_chronyc_output "^506 Cannot talk to daemon$" || test_fail
+
+chronyc_conf="retries 3
+sources"
+run_test || test_fail
+check_chronyc_output "^MS.*0ns$" || test_fail
+
+test_pass
diff --git a/test/simulation/111-knownclient b/test/simulation/111-knownclient
new file mode 100755
index 0000000..92bad54
--- /dev/null
+++ b/test/simulation/111-knownclient
@@ -0,0 +1,17 @@
+#!/usr/bin/env bash
+
+. ./test.common
+
+test_start "reply to client configured as server"
+
+server_conf="server 192.168.123.2 noselect
+acquisitionport 123"
+client_conf="acquisitionport 123"
+
+run_test || test_fail
+check_chronyd_exit || test_fail
+check_source_selection || test_fail
+check_packet_port || test_fail
+check_sync || test_fail
+
+test_pass
diff --git a/test/simulation/112-port b/test/simulation/112-port
new file mode 100755
index 0000000..2f10eed
--- /dev/null
+++ b/test/simulation/112-port
@@ -0,0 +1,57 @@
+#!/usr/bin/env bash
+
+. ./test.common
+
+test_start "port and acquisitionport directives"
+
+check_config_h 'FEAT_CMDMON 1' || test_skip
+
+run_test || test_fail
+check_chronyd_exit || test_fail
+check_source_selection || test_fail
+check_packet_interval || test_fail
+check_sync || test_fail
+# This check is expected to fail
+check_packet_port && test_fail
+
+client_conf="acquisitionport 123"
+run_test || test_fail
+check_chronyd_exit || test_fail
+check_packet_port || test_fail
+check_source_selection || test_fail
+check_packet_interval || test_fail
+check_sync || test_fail
+
+client_conf=""
+for server_conf in \
+ "port 0" \
+ "acquisitionport 123
+port 0"
+do
+ run_test || test_fail
+ check_chronyd_exit || test_fail
+ check_packet_port || test_fail
+ check_packet_interval || test_fail
+ # These checks are expected to fail
+ check_source_selection && test_fail
+ check_sync && test_fail
+done
+
+server_conf="port 124
+acquisitionport 123"
+client_server_options="port 124"
+for client_conf in \
+ "acquisitionport 0" \
+ "acquisitionport 123" \
+ "acquisitionport 124"
+do
+ run_test || test_fail
+ check_chronyd_exit || test_fail
+ check_source_selection || test_fail
+ check_packet_interval || test_fail
+ check_sync || test_fail
+ # This check is expected to fail
+ check_packet_port && test_fail
+done
+
+test_pass
diff --git a/test/simulation/113-leapsecond b/test/simulation/113-leapsecond
new file mode 100755
index 0000000..394440b
--- /dev/null
+++ b/test/simulation/113-leapsecond
@@ -0,0 +1,61 @@
+#!/usr/bin/env bash
+
+. ./test.common
+
+test_start "leap second"
+
+check_config_h 'FEAT_REFCLOCK 1' || test_skip
+
+export CLKNETSIM_START_DATE=$(TZ=UTC date -d 'Dec 30 2008 0:00:00' +'%s')
+
+leap=$[2 * 24 * 3600]
+limit=$[4 * 24 * 3600]
+client_start=$[2 * 3600]
+server_conf="refclock SHM 0 dpoll 10 poll 10
+leapsectz right/UTC"
+refclock_jitter=1e-9
+refclock_offset="(* -1.0 (equal 0.1 (max (sum 1.0) $leap) $leap))"
+
+for leapmode in system step slew; do
+ client_conf="leapsecmode $leapmode"
+ if [ $leapmode = slew ]; then
+ max_sync_time=$[$leap + 12]
+ else
+ max_sync_time=$[$leap]
+ fi
+
+ run_test || test_fail
+ check_chronyd_exit || test_fail
+ check_source_selection || test_fail
+ check_packet_interval || test_fail
+ check_sync || test_fail
+done
+
+client_server_options="trust"
+client_conf="refclock SHM 0 dpoll 10 poll 10 delay 1e-3"
+
+run_test || test_fail
+check_chronyd_exit || test_fail
+check_source_selection || test_fail
+check_packet_interval || test_fail
+check_sync || test_fail
+
+client_server_options=""
+client_conf="leapsecmode system"
+min_sync_time=230000
+max_sync_time=240000
+
+for smoothmode in "" "leaponly"; do
+ server_conf="refclock SHM 0 dpoll 10 poll 10
+ leapsectz right/UTC
+ leapsecmode slew
+ smoothtime 400 0.001 $smoothmode"
+
+ run_test || test_fail
+ check_chronyd_exit || test_fail
+ check_source_selection || test_fail
+ check_packet_interval || test_fail
+ check_sync || test_fail
+done
+
+test_pass
diff --git a/test/simulation/114-presend b/test/simulation/114-presend
new file mode 100755
index 0000000..4fd89f8
--- /dev/null
+++ b/test/simulation/114-presend
@@ -0,0 +1,25 @@
+#!/usr/bin/env bash
+
+. ./test.common
+test_start "presend option"
+
+min_sync_time=136
+max_sync_time=260
+client_server_options="presend 6 maxdelay 16"
+client_conf="maxdistance 10"
+
+run_test || test_fail
+check_chronyd_exit || test_fail
+check_source_selection || test_fail
+check_packet_interval || test_fail
+check_sync || test_fail
+
+base_delay=5
+
+run_test || test_fail
+check_chronyd_exit || test_fail
+check_source_selection || test_fail
+check_packet_interval || test_fail
+check_sync || test_fail
+
+test_pass
diff --git a/test/simulation/115-cmdmontime b/test/simulation/115-cmdmontime
new file mode 100755
index 0000000..525062d
--- /dev/null
+++ b/test/simulation/115-cmdmontime
@@ -0,0 +1,24 @@
+#!/usr/bin/env bash
+
+. ./test.common
+
+test_start "cmdmon timestamps"
+
+# The following tests need 64-bit time_t
+check_config_h 'HAVE_LONG_TIME_T 1' || test_skip
+check_config_h 'FEAT_CMDMON 1' || test_skip
+
+limit=2
+client_server_options="noselect"
+client_conf="local stratum 1"
+chronyc_start="1.5"
+chronyc_conf="tracking"
+
+for year in `seq 1850 100 2300`; do
+ export CLKNETSIM_START_DATE=$(date -d "Jan 01 00:00:05 $year UTC" +'%s')
+ run_test || test_fail
+ check_chronyd_exit || test_fail
+ check_chronyc_output "^.*Ref time \(UTC\).*Jan 01 00:00:0. $year.*$" || test_fail
+done
+
+test_pass
diff --git a/test/simulation/116-minsources b/test/simulation/116-minsources
new file mode 100755
index 0000000..f576423
--- /dev/null
+++ b/test/simulation/116-minsources
@@ -0,0 +1,24 @@
+#!/usr/bin/env bash
+
+. ./test.common
+
+test_start "minsources directive"
+
+client_conf="minsources 3"
+
+run_test || test_fail
+check_chronyd_exit || test_fail
+check_packet_interval || test_fail
+# These check are expected to fail
+check_source_selection && test_fail
+check_sync && test_fail
+
+servers=3
+
+run_test || test_fail
+check_chronyd_exit || test_fail
+check_packet_interval || test_fail
+check_source_selection || test_fail
+check_sync || test_fail
+
+test_pass
diff --git a/test/simulation/117-fallbackdrift b/test/simulation/117-fallbackdrift
new file mode 100755
index 0000000..21f6963
--- /dev/null
+++ b/test/simulation/117-fallbackdrift
@@ -0,0 +1,24 @@
+#!/usr/bin/env bash
+
+. ./test.common
+test_start "fallback drift"
+
+limit=100000
+wander=0.0
+jitter=1e-6
+time_offset=10
+freq_offset="(* 1e-4 (sine 1000))"
+base_delay="(* -1.0 (equal 0.1 (min time 4250) 4250))"
+client_server_options="minpoll 4 maxpoll 4"
+client_conf="fallbackdrift 6 10"
+max_sync_time=4500
+time_max_limit=1e0
+time_rms_limit=1e0
+freq_max_limit=2e-4
+freq_rms_limit=1e-4
+
+run_test || test_fail
+check_chronyd_exit || test_fail
+check_sync || test_fail
+
+test_pass
diff --git a/test/simulation/118-maxdelay b/test/simulation/118-maxdelay
new file mode 100755
index 0000000..117b170
--- /dev/null
+++ b/test/simulation/118-maxdelay
@@ -0,0 +1,42 @@
+#!/usr/bin/env bash
+
+. ./test.common
+test_start "maxdelay options"
+
+max_sync_time=2000
+base_delay=1e-5
+jitter=1e-5
+wander=0.0
+freq_offset="(sum 1e-10)"
+time_rms_limit=2e-4
+
+client_server_options="maxpoll 6 maxdelay 3e-5 maxdelayratio 2.0 maxdelaydevratio 2.0"
+
+run_test || test_fail
+check_chronyd_exit || test_fail
+check_source_selection || test_fail
+check_packet_interval || test_fail
+check_sync || test_fail
+
+for client_server_options in "maxpoll 6 maxdelay 2e-5"; do
+ run_test || test_fail
+ check_chronyd_exit || test_fail
+ check_packet_interval || test_fail
+ check_sync && test_fail
+done
+
+min_sync_time=10
+client_conf="
+logdir tmp
+log rawmeasurements"
+client_server_options="minpoll 2 maxpoll 2 maxdelayquant 0.1"
+
+run_test || test_fail
+check_chronyd_exit || test_fail
+check_packet_interval || test_fail
+check_sync || test_fail
+
+check_file_messages "20.*123\.1.* 111 111 1111" 200 500 measurements.log || test_fail
+check_file_messages "20.*123\.1.* 111 111 1101" 2000 2300 measurements.log || test_fail
+
+test_pass
diff --git a/test/simulation/119-smoothtime b/test/simulation/119-smoothtime
new file mode 100755
index 0000000..7f5114c
--- /dev/null
+++ b/test/simulation/119-smoothtime
@@ -0,0 +1,82 @@
+#!/usr/bin/env bash
+
+. ./test.common
+
+test_start "smoothtime option"
+
+check_config_h 'FEAT_REFCLOCK 1' || test_skip
+
+server_strata=2
+server_conf="smoothtime 400 0.001"
+server_server_options="minpoll 8"
+min_sync_time=600
+max_sync_time=800
+
+run_test || test_fail
+check_chronyd_exit || test_fail
+check_source_selection || test_fail
+check_sync || test_fail
+
+limit=10000
+refclock_jitter=1e-4
+refclock_offset="(* 10.0 (equal 0.1 (max (sum 1.0) 1000) 1000))"
+server_step="(* -10.0 (equal 0.1 (sum 1.0) 1))"
+server_strata=1
+server_conf="refclock SHM 0 dpoll 4 poll 6
+smoothtime 2000 1
+maxjitter 10.0"
+time_offset=-10
+server_server_options=""
+client_server_options="minpoll 6 maxpoll 6"
+client_conf="corrtimeratio 100"
+min_sync_time=8000
+max_sync_time=9000
+
+run_test || test_fail
+check_chronyd_exit || test_fail
+check_source_selection || test_fail
+check_sync || test_fail
+
+client_server_options="minpoll 6 maxpoll 6 xleave maxdelay 1e-1"
+
+run_test || test_fail
+check_chronyd_exit || test_fail
+check_source_selection || test_fail
+check_sync || test_fail
+
+client_server_options="minpoll 6 maxpoll 6"
+min_sync_time=$default_min_sync_time
+max_sync_time=$default_max_sync_time
+time_max_limit=11
+time_rms_limit=11
+freq_max_limit=1e-2
+freq_rms_limit=2e-3
+
+run_test || test_fail
+check_chronyd_exit || test_fail
+check_source_selection || test_fail
+check_sync || test_fail
+
+refclock_jitter=1e-9
+refclock_offset="(* 1e-1 (triangle 1000) (+ -1.0 (pulse 1000 10000)))"
+server_step=""
+server_conf="refclock SHM 0 dpoll 4 poll 6 minsamples 4 maxsamples 4
+smoothtime 1e4 1e-6"
+client_server_options="minpoll 4 maxpoll 4"
+time_offset=0.1
+jitter=1e-6
+wander=0.0
+min_sync_time=30
+max_sync_time=40
+time_max_limit=1e-5
+time_rms_limit=5e-6
+freq_max_limit=1e-6
+freq_rms_limit=1e-7
+
+run_test || test_fail
+check_chronyd_exit || test_fail
+check_source_selection || test_fail
+check_packet_interval || test_fail
+check_sync || test_fail
+
+test_pass
diff --git a/test/simulation/120-selectoptions b/test/simulation/120-selectoptions
new file mode 100755
index 0000000..611815e
--- /dev/null
+++ b/test/simulation/120-selectoptions
@@ -0,0 +1,89 @@
+#!/usr/bin/env bash
+
+. ./test.common
+
+test_start "source selection options"
+
+servers=3
+falsetickers=2
+
+base_delay=0.6
+client_server_conf="
+server 192.168.123.1
+server 192.168.123.2
+server 192.168.123.3 trust"
+
+run_test || test_fail
+check_chronyd_exit || test_fail
+check_source_selection || test_fail
+check_sync || test_fail
+
+client_server_conf="
+server 192.168.123.1
+server 192.168.123.2
+server 192.168.123.3 prefer"
+
+run_test || test_fail
+check_chronyd_exit || test_fail
+check_source_selection || test_fail
+# This check is expected to fail
+check_sync && test_fail
+
+base_delay=1.1
+
+run_test || test_fail
+check_chronyd_exit || test_fail
+check_source_selection || test_fail
+check_sync || test_fail
+
+base_delay=1e-3
+falsetickers=1
+
+client_server_conf="
+server 192.168.123.1
+server 192.168.123.2
+server 192.168.123.3 require"
+
+run_test || test_fail
+check_chronyd_exit || test_fail
+check_source_selection || test_fail
+check_packet_interval || test_fail
+check_sync || test_fail
+
+client_server_conf="
+server 192.168.123.1 require
+server 192.168.123.2
+server 192.168.123.3"
+
+run_test || test_fail
+check_chronyd_exit || test_fail
+check_packet_interval || test_fail
+# These checks are expected to fail
+check_source_selection && test_fail
+check_sync && test_fail
+
+cat > tmp/keys <<-EOF
+1 MD5 HEX:1B81CBF88D4A73F2E8CE59647F6E5C1719B6CAF5
+EOF
+
+server_conf="keyfile tmp/keys"
+client_server_conf="
+server 192.168.123.1 key 1
+server 192.168.123.2
+server 192.168.123.3"
+
+for authselectmode in require prefer mix ignore; do
+ client_conf="keyfile tmp/keys
+ authselectmode $authselectmode"
+ run_test || test_fail
+ check_chronyd_exit || test_fail
+ check_source_selection || test_fail
+ check_packet_interval || test_fail
+ if [ $authselectmode = ignore ]; then
+ check_sync || test_fail
+ else
+ check_sync && test_fail
+ fi
+done
+
+test_pass
diff --git a/test/simulation/121-orphan b/test/simulation/121-orphan
new file mode 100755
index 0000000..7579997
--- /dev/null
+++ b/test/simulation/121-orphan
@@ -0,0 +1,26 @@
+#!/usr/bin/env bash
+
+. ./test.common
+
+test_start "orphan option"
+
+check_config_h 'FEAT_CMDMON 1' || test_skip
+
+server_strata=3
+server_conf="local stratum 5 orphan
+server 192.168.123.1
+server 192.168.123.2
+server 192.168.123.3"
+max_sync_time=900
+client_start=140
+chronyc_start=700
+chronyc_conf="tracking"
+time_rms_limit=5e-4
+
+run_test || test_fail
+check_chronyd_exit || test_fail
+check_source_selection || test_fail
+check_sync || test_fail
+check_chronyc_output "^.*Stratum *: 7.*$" || test_fail
+
+test_pass
diff --git a/test/simulation/122-xleave b/test/simulation/122-xleave
new file mode 100755
index 0000000..c19063a
--- /dev/null
+++ b/test/simulation/122-xleave
@@ -0,0 +1,91 @@
+#!/usr/bin/env bash
+
+. ./test.common
+test_start "interleaved mode"
+
+client_server_options="xleave"
+client_conf="
+logdir tmp
+log rawmeasurements"
+
+server_conf="noclientlog"
+
+run_test || test_fail
+check_chronyd_exit || test_fail
+check_source_selection || test_fail
+check_sync || test_fail
+
+check_file_messages "111 111 .111.* 4I [DKH] [DKH]\$" 0 0 measurements.log || test_fail
+rm -f tmp/measurements.log
+
+server_conf=""
+max_sync_time=270
+
+run_test || test_fail
+check_chronyd_exit || test_fail
+check_source_selection || test_fail
+check_packet_interval || test_fail
+check_sync || test_fail
+
+check_file_messages "111 111 1111.* 4B [DKH] [DKH]\$" 2 2 measurements.log || test_fail
+check_file_messages "111 111 1111.* 4I [DKH] [DKH]\$" 30 200 measurements.log || test_fail
+check_file_messages "111 111 0111.* 4I [DKH] [DKH]\$" 1 1 measurements.log || test_fail
+rm -f tmp/measurements.log
+
+clients=2
+peers=2
+max_sync_time=500
+base_delay="(+ 1e-4 (* -1 (equal 0.1 from 2) (equal 0.1 to 1)))"
+
+client_lpeer_options="xleave minpoll 5 maxpoll 5"
+client_rpeer_options="minpoll 5 maxpoll 5"
+
+run_test || test_fail
+check_chronyd_exit || test_fail
+# These checks are expected to fail
+check_source_selection && test_fail
+check_sync && test_fail
+
+rm -f tmp/measurements.log
+
+for rpoll in 4 5 6; do
+ client_rpeer_options="xleave minpoll $rpoll maxpoll $rpoll"
+
+ run_test || test_fail
+ check_chronyd_exit || test_fail
+ check_source_selection || test_fail
+ check_sync || test_fail
+
+ if [ $rpoll -le 5 ]; then
+ check_file_messages "111 111 1111.* 1B [DKH] [DKH]\$" 0 0 measurements.log || test_fail
+ check_file_messages "111 111 1111.* 1I [DKH] [DKH]\$" 200 310 measurements.log || test_fail
+ else
+ check_file_messages "111 111 1111.* 1B [DKH] [DKH]\$" 125 135 measurements.log || test_fail
+ check_file_messages "111 111 1111.* 1I [DKH] [DKH]\$" 20 30 measurements.log || test_fail
+ fi
+ rm -f tmp/measurements.log
+done
+
+if check_config_h 'FEAT_CMDMON 1'; then
+ # test client timestamp selection and server timestamp correction
+ base_delay="(+ 1.25e-6 (* -1 (equal 0.1 from 5)))"
+ jitter=1e-9
+ wander=1e-12
+ client_lpeer_options="xleave minpoll 5 maxpoll 5 noselect"
+ client_rpeer_options="xleave minpoll 5 maxpoll 5 noselect"
+ chronyc_conf="doffset -0.1"
+ chronyc_start=7200
+
+ run_test || test_fail
+ check_chronyd_exit || test_fail
+ check_source_selection || test_fail
+ check_sync && test_fail
+
+ check_file_messages "\.2 N 2 111 111 .... 5 5 .\... ..\....e-.. 2\....e-06" \
+ 290 310 measurements.log || test_fail
+ check_file_messages "\.2 N 2 111 111 .... 5 5 .\... ..\....e-.. .\....e-0[0123]" \
+ 0 0 measurements.log || test_fail
+ rm -f tmp/measurements.log
+fi
+
+test_pass
diff --git a/test/simulation/123-mindelay b/test/simulation/123-mindelay
new file mode 100755
index 0000000..89a9f33
--- /dev/null
+++ b/test/simulation/123-mindelay
@@ -0,0 +1,27 @@
+#!/usr/bin/env bash
+
+. ./test.common
+
+test_start "mindelay and asymmetry options"
+
+jitter_asymmetry=0.499
+time_rms_limit=1e-6
+time_freq_limit=1e-9
+wander=1e-12
+
+for client_server_options in "mindelay 2e-4 asymmetry 0.499"; do
+ run_test || test_fail
+ check_chronyd_exit || test_fail
+ check_source_selection || test_fail
+ check_packet_interval || test_fail
+ check_sync || test_fail
+done
+
+for client_server_options in "mindelay 1e-4 asymmetry 0.499" "mindelay 2e-4 asymmetry 0.0"; do
+ run_test || test_fail
+ check_chronyd_exit || test_fail
+ check_source_selection || test_fail
+ check_sync && test_fail
+done
+
+test_pass
diff --git a/test/simulation/124-tai b/test/simulation/124-tai
new file mode 100755
index 0000000..97064f7
--- /dev/null
+++ b/test/simulation/124-tai
@@ -0,0 +1,45 @@
+#!/usr/bin/env bash
+
+. ./test.common
+
+test_start "tai option"
+
+check_config_h 'FEAT_REFCLOCK 1' || test_skip
+
+export CLKNETSIM_START_DATE=$(TZ=UTC date -d 'Dec 31 2008 23:50:00' +'%s')
+
+leap=$[10 * 60]
+limit=$[20 * 60]
+min_sync_time=2
+max_sync_time=15
+refclock_jitter=1e-6
+servers=0
+
+refclock_offset="(+ -34 (equal 0.1 (max (sum 1.0) $leap) $leap))"
+client_conf="
+refclock SHM 0 dpoll 0 poll 0 tai
+leapsectz right/UTC
+leapsecmode ignore
+maxchange 1e-3 1 0"
+
+run_test || test_fail
+check_chronyd_exit || test_fail
+check_source_selection || test_fail
+check_sync || test_fail
+
+export CLKNETSIM_START_DATE=$(TZ=UTC date -d 'Jan 01 2009 00:10:00' +'%s')
+
+time_offset=-1000
+refclock_offset="(+ -34)"
+client_conf="
+refclock SHM 0 dpoll 0 poll 0 tai
+leapsectz right/UTC
+makestep 1 1
+maxchange 1e-3 1 0"
+
+run_test || test_fail
+check_chronyd_exit || test_fail
+check_source_selection || test_fail
+check_sync || test_fail
+
+test_pass
diff --git a/test/simulation/125-packetloss b/test/simulation/125-packetloss
new file mode 100755
index 0000000..505e4fa
--- /dev/null
+++ b/test/simulation/125-packetloss
@@ -0,0 +1,29 @@
+#!/usr/bin/env bash
+
+. ./test.common
+
+test_start "packet loss"
+
+# Drop 33% of packets by default and 100% on the 3->1 path
+base_delay=$(cat <<-EOF | tr -d '\n'
+ (+ 1e-4
+ (* -1 (equal 0.33 (uniform) 1.0))
+ (* -1 (equal 0.1 from 3) (equal 0.1 to 1)))
+EOF
+)
+clients=2
+peers=2
+jitter=1e-5
+limit=20000
+max_sync_time=10000
+
+for options in "maxpoll 8" "maxpoll 8 xleave"; do
+ client_server_options=$options
+ client_peer_options=$options
+
+ run_test || test_fail
+ check_chronyd_exit || test_fail
+ check_sync || test_fail
+done
+
+test_pass
diff --git a/test/simulation/126-burst b/test/simulation/126-burst
new file mode 100755
index 0000000..1cb6f9c
--- /dev/null
+++ b/test/simulation/126-burst
@@ -0,0 +1,45 @@
+#!/usr/bin/env bash
+
+. ./test.common
+
+test_start "burst option"
+
+# Pass every fourth packet on the 2->1 path
+base_delay=$(cat <<-EOF | tr -d '\n'
+ (+ 1e-4
+ (* -1
+ (equal 0.1 from 2)
+ (equal 0.1 to 1)
+ (equal 0.1 (min (% (sum 1) 4) 1) 1)))
+EOF
+)
+
+client_server_options="burst polltarget 1"
+min_sync_time=700
+max_sync_time=730
+client_max_min_out_interval=2.2
+client_min_mean_out_interval=150.0
+
+run_test || test_fail
+check_chronyd_exit || test_fail
+check_source_selection || test_fail
+check_packet_interval || test_fail
+check_sync || test_fail
+
+# Add a significant delay to 70% of packets on the 2->1 path after 6th packet
+base_delay=$(cat <<-EOF | tr -d '\n'
+ (+ 1e-4
+ (* 0.15
+ (equal 0.1 from 2)
+ (equal 0.1 to 1)
+ (equal 0.1 (min (sum 1) 7) 7)
+ (equal 0.7 (uniform) 0.0)))
+EOF
+)
+
+run_test || test_fail
+check_chronyd_exit || test_fail
+check_source_selection || test_fail
+check_packet_interval || test_fail
+
+test_pass
diff --git a/test/simulation/127-filter b/test/simulation/127-filter
new file mode 100755
index 0000000..739dd91
--- /dev/null
+++ b/test/simulation/127-filter
@@ -0,0 +1,43 @@
+#!/usr/bin/env bash
+
+. ./test.common
+
+test_start "filter option"
+
+client_server_options="minpoll 4 maxpoll 4 filter 15 maxdelay 3.5e-4"
+min_sync_time=710
+max_sync_time=720
+client_max_min_out_interval=16.1
+client_min_mean_out_interval=15.9
+
+run_test || test_fail
+check_chronyd_exit || test_fail
+check_source_selection || test_fail
+check_packet_interval || test_fail
+check_sync || test_fail
+
+base_delay="(+ 1e-4 (* -1 (equal 0.3 (uniform) 0.0)))"
+client_server_options="minpoll 4 maxpoll 4 filter 3"
+min_sync_time=130
+
+run_test || test_fail
+check_chronyd_exit || test_fail
+check_packet_interval || test_fail
+check_sync || test_fail
+
+limit=10
+client_server_options="minpoll -6 maxpoll -6 filter 1"
+
+base_delay=1e-4
+
+run_test || test_fail
+check_chronyd_exit || test_fail
+check_file_messages " 2 1 " 590 640 log.packets || test_fail
+
+base_delay=2e-2
+
+run_test || test_fail
+check_chronyd_exit || test_fail
+check_file_messages " 2 1 " 9 10 log.packets || test_fail
+
+test_pass
diff --git a/test/simulation/128-nocontrol b/test/simulation/128-nocontrol
new file mode 100755
index 0000000..3f0d18d
--- /dev/null
+++ b/test/simulation/128-nocontrol
@@ -0,0 +1,27 @@
+#!/usr/bin/env bash
+
+. ./test.common
+
+test_start "-x option"
+
+check_config_h 'FEAT_CMDMON 1' || test_skip
+
+wander=0.0
+time_offset=0.0
+freq_offset=0.0
+time_max_limit=1e-6
+freq_max_limit=1e-9
+min_sync_time=0
+max_sync_time=0
+client_chronyd_options="-x"
+chronyc_start=300
+chronyc_conf="tracking"
+
+run_test || test_fail
+check_chronyd_exit || test_fail
+check_source_selection || test_fail
+check_packet_interval || test_fail
+check_sync || test_fail
+check_chronyc_output "^.*Stratum *: 2.*$" || test_fail
+
+test_pass
diff --git a/test/simulation/129-reload b/test/simulation/129-reload
new file mode 100755
index 0000000..56bc3da
--- /dev/null
+++ b/test/simulation/129-reload
@@ -0,0 +1,109 @@
+#!/usr/bin/env bash
+
+. ./test.common
+
+test_start "-r option"
+
+wander=0.0
+limit=100
+min_sync_time=100
+max_sync_time=104
+client_chronyd_options="-r"
+client_conf="dumpdir tmp
+maxupdateskew 10000"
+
+run_test || test_fail
+check_chronyd_exit || test_fail
+
+check_log_messages "Loaded dump file" 0 0 || test_fail
+check_file_messages "." 6 6 192.168.123.1.dat || test_fail
+
+client_start=$limit
+limit=1000
+
+run_test || test_fail
+check_chronyd_exit || test_fail
+check_source_selection || test_fail
+check_packet_interval || test_fail
+check_sync || test_fail
+
+check_log_messages "Loaded dump file" 1 1 || test_fail
+check_file_messages "." 10 30 192.168.123.1.dat || test_fail
+
+rm -f tmp/*.dat
+
+client_start=0
+limit=200
+jitter=1e-6
+client_conf="dumpdir tmp
+maxupdateskew 1e-6
+maxslewrate 1e-6"
+
+run_test || test_fail
+check_chronyd_exit || test_fail
+
+check_log_messages "Loaded dump file" 0 0 || test_fail
+check_file_messages "." 8 8 192.168.123.1.dat || test_fail
+cp tmp/192.168.123.1.dat tmp/backup.dat
+
+client_start=$limit
+limit=1000
+min_sync_time=201
+max_sync_time=203
+client_server_options="offline"
+client_conf="dumpdir tmp"
+
+run_test || test_fail
+check_chronyd_exit || test_fail
+check_source_selection || test_fail
+check_packet_interval || test_fail
+check_sync || test_fail
+
+check_log_messages "Loaded dump file" 1 1 || test_fail
+check_file_messages "." 8 8 192.168.123.1.dat || test_fail
+
+cp -f tmp/backup.dat tmp/192.168.123.1.dat
+
+client_server_options="key 1"
+
+run_test || test_fail
+check_chronyd_exit || test_fail
+check_sync && test_fail
+
+check_log_messages "Could not load dump file" 1 1 || test_fail
+check_log_messages "Loaded dump file" 0 0 || test_fail
+
+client_server_options=""
+
+if check_config_h 'FEAT_REFCLOCK 1'; then
+ refclock_jitter=1e-6
+ servers=0
+ client_start=0
+ limit=40
+ min_sync_time=56
+ max_sync_time=58
+ client_chronyd_options="-r"
+ client_conf="dumpdir tmp
+ refclock SHM 0"
+
+ run_test || test_fail
+ check_chronyd_exit || test_fail
+
+ check_log_messages "Loaded dump file" 0 0 || test_fail
+ check_file_messages "." 6 6 refid:53484d30.dat || test_fail
+
+ client_start=$limit
+ limit=300
+
+ run_test || test_fail
+ check_chronyd_exit || test_fail
+ check_source_selection || test_fail
+ check_sync || test_fail
+
+ check_log_messages "Loaded dump file" 1 1 || test_fail
+ check_file_messages "." 6 23 refid:53484d30.dat || test_fail
+
+ rm -f tmp/*.dat
+fi
+
+test_pass
diff --git a/test/simulation/130-quit b/test/simulation/130-quit
new file mode 100755
index 0000000..da2b8cf
--- /dev/null
+++ b/test/simulation/130-quit
@@ -0,0 +1,31 @@
+#!/usr/bin/env bash
+
+. ./test.common
+
+test_start "-q/-Q option"
+
+wander=0.0
+freq_offset=0.0
+min_sync_time=5
+max_sync_time=10
+client_chronyd_options="-q"
+client_server_options="iburst"
+
+run_test || test_fail
+check_chronyd_exit || test_fail
+check_packet_interval || test_fail
+check_sync || test_fail
+
+min_sync_time=1
+max_sync_time=1
+client_server_options="iburst maxsamples 1"
+
+run_test || test_fail
+check_chronyd_exit || test_fail
+check_sync || test_fail
+
+client_chronyd_options="-Q"
+run_test || test_fail
+check_sync && test_fail
+
+test_pass
diff --git a/test/simulation/131-maxchange b/test/simulation/131-maxchange
new file mode 100755
index 0000000..59cc0c1
--- /dev/null
+++ b/test/simulation/131-maxchange
@@ -0,0 +1,20 @@
+#!/usr/bin/env bash
+
+. ./test.common
+
+test_start "maxchange directive"
+
+time_offset=2
+max_sync_time=5000
+client_conf="maxchange 0.1 1 3"
+client_step="(* $step (equal 0.1 (sum 1.0) 300))"
+
+run_test || test_fail
+check_chronyd_exit && test_fail
+check_source_selection || test_fail
+check_packet_interval || test_fail
+check_sync && test_fail
+check_log_messages "seconds exceeds.*ignored" 3 3 || test_fail
+check_log_messages "seconds exceeds.*exiting" 1 1 || test_fail
+
+test_pass
diff --git a/test/simulation/132-logchange b/test/simulation/132-logchange
new file mode 100755
index 0000000..59ddf7c
--- /dev/null
+++ b/test/simulation/132-logchange
@@ -0,0 +1,21 @@
+#!/usr/bin/env bash
+
+. ./test.common
+
+test_start "logchange directive"
+
+time_offset=2
+min_sync_time=590
+max_sync_time=700
+client_server_options="maxsamples 6"
+client_conf="logchange 0.1"
+client_step="(* $step (equal 0.1 (sum 1.0) 300))"
+
+run_test || test_fail
+check_chronyd_exit || test_fail
+check_source_selection || test_fail
+check_packet_interval || test_fail
+check_sync || test_fail
+check_log_messages "clock wrong by" 4 8 || test_fail
+
+test_pass
diff --git a/test/simulation/133-hwtimestamp b/test/simulation/133-hwtimestamp
new file mode 100755
index 0000000..d3cce6d
--- /dev/null
+++ b/test/simulation/133-hwtimestamp
@@ -0,0 +1,60 @@
+#!/usr/bin/env bash
+
+. ./test.common
+
+test_start "hwtimestamp directive"
+
+check_config_h 'HAVE_LINUX_TIMESTAMPING 1' || test_skip
+
+export CLKNETSIM_TIMESTAMPING=2
+export CLKNETSIM_PHC_DELAY=1e-6
+export CLKNETSIM_PHC_JITTER=1e-7
+export CLKNETSIM_PHC_JITTER_ASYM=0.4
+
+refclock_jitter=1e-8
+refclock_offset=10.0
+min_sync_time=4
+max_sync_time=20
+time_rms_limit=1e-7
+freq_rms_limit=3e-8
+jitter=1e-8
+freq_offset=1e-5
+limit=200
+server_conf="
+clockprecision 1e-9
+hwtimestamp eth0"
+client_server_options="minpoll 0 maxpoll 0 xleave"
+client_chronyd_options="-d"
+
+for client_conf in \
+ "hwtimestamp eth0 nocrossts
+ clockprecision 1e-9" \
+ "hwtimestamp eth0
+ clockprecision 1e-9
+ acquisitionport 123"; do
+ run_test || test_fail
+ check_chronyd_exit || test_fail
+ check_source_selection || test_fail
+ check_sync || test_fail
+
+ if check_config_h 'FEAT_DEBUG 1'; then
+ check_log_messages "Accepted reading" 0 2 || test_fail
+ check_log_messages "Combined .* readings" 190 220 || test_fail
+ check_log_messages "HW clock samples" 190 200 || test_fail
+ check_log_messages "HW clock reset" 0 0 || test_fail
+ check_log_messages "Missing TX timestamp" 1 1 || test_fail
+ check_log_messages "Received message.*tss=KH" 195 200 || test_fail
+ check_log_messages "Received error.*message.*tss=KH" 195 200 || test_fail
+ check_log_messages "Updated RX timestamp.*tss=1" 1 1 || test_fail
+ check_log_messages "Updated RX timestamp.*tss=2" 195 200 || test_fail
+ if echo "$client_conf" | grep -q nocrossts; then
+ check_log_messages "update_tx_timestamp.*Updated" 180 200 || test_fail
+ check_log_messages "update_tx_timestamp.*Unacceptable" 0 10 || test_fail
+ else
+ check_log_messages "update_tx_timestamp.*Updated" 50 140 || test_fail
+ check_log_messages "update_tx_timestamp.*Unacceptable" 50 140 || test_fail
+ fi
+ fi
+done
+
+test_pass
diff --git a/test/simulation/134-log b/test/simulation/134-log
new file mode 100755
index 0000000..ab1ced2
--- /dev/null
+++ b/test/simulation/134-log
@@ -0,0 +1,35 @@
+#!/usr/bin/env bash
+
+. ./test.common
+
+test_start "log directive"
+
+check_config_h 'FEAT_PHC 1' || test_skip
+
+refclock_jitter=$jitter
+client_server_options="maxpoll 6"
+client_conf="refclock PHC /dev/ptp0 dpoll 4 poll 6 noselect
+logbanner 10
+logdir tmp
+log tracking rawmeasurements measurements selection statistics rtc refclocks tempcomp
+tempcomp tmp/tempcomp 64 0.0 0.0 0.0 0.0"
+
+echo 0.0 > tmp/tempcomp
+
+run_test || test_fail
+check_chronyd_exit || test_fail
+check_source_selection || test_fail
+check_packet_interval || test_fail
+check_sync || test_fail
+
+check_file_messages "=============" 31 33 \
+ tracking.log measurements.log tempcomp.log || test_fail
+check_file_messages "20.*192\.168\.123\.1" 150 160 \
+ tracking.log measurements.log statistics.log || test_fail
+check_file_messages "20.*PHC0 * N " 300 320 selection.log || test_fail
+check_file_messages "20.*192\.168\.123\.1 *[M*]" 300 320 selection.log || test_fail
+check_file_messages "20.*PHC0" 150 160 statistics.log || test_fail
+check_file_messages "20.*PHC0" 750 800 refclocks.log || test_fail
+check_file_messages "20.* 0\.0000" 150 160 tempcomp.log || test_fail
+
+test_pass
diff --git a/test/simulation/135-ratelimit b/test/simulation/135-ratelimit
new file mode 100755
index 0000000..86c435d
--- /dev/null
+++ b/test/simulation/135-ratelimit
@@ -0,0 +1,18 @@
+#!/usr/bin/env bash
+
+. ./test.common
+test_start "ratelimit directive"
+
+server_conf="ratelimit interval 6 burst 2 leak 4"
+client_server_options="minpoll 3 maxpoll 3"
+min_sync_time=16
+
+run_test || test_fail
+check_chronyd_exit || test_fail
+check_packet_interval || test_fail
+check_sync || test_fail
+
+check_file_messages " 2 1 " 1200 1300 log.packets || test_fail
+check_file_messages " 1 2 " 180 220 log.packets || test_fail
+
+test_pass
diff --git a/test/simulation/136-broadcast b/test/simulation/136-broadcast
new file mode 100755
index 0000000..1488c53
--- /dev/null
+++ b/test/simulation/136-broadcast
@@ -0,0 +1,16 @@
+#!/usr/bin/env bash
+
+. ./test.common
+
+test_start "broadcast directive"
+
+server_conf="broadcast 64 192.168.123.255"
+client_server_options="offline"
+
+run_test || test_fail
+check_chronyd_exit || test_fail
+check_packet_interval && test_fail
+
+check_file_messages " 1 2 " 150 160 log.packets || test_fail
+
+test_pass
diff --git a/test/simulation/137-pool b/test/simulation/137-pool
new file mode 100755
index 0000000..de8d77d
--- /dev/null
+++ b/test/simulation/137-pool
@@ -0,0 +1,49 @@
+#!/usr/bin/env bash
+
+. ./test.common
+
+test_start "pool directive"
+
+limit=500
+client_conf="logdir tmp
+log measurements"
+
+servers=3
+client_server_conf="pool nodes-1-2-3.net1.clk"
+
+run_test || test_fail
+check_chronyd_exit || test_fail
+check_source_selection || test_fail
+check_packet_interval || test_fail
+check_sync || test_fail
+
+check_file_messages "20.*192.168.123.1" 5 10 measurements.log || test_fail
+check_file_messages "20.*192.168.123.2" 5 10 measurements.log || test_fail
+check_file_messages "20.*192.168.123.3" 5 10 measurements.log || test_fail
+rm -f tmp/measurements.log
+
+servers=6
+client_server_conf="pool nodes-1-2-3-4-5-6.net1.clk minpoll 6 maxpoll 6"
+
+run_test || test_fail
+check_chronyd_exit || test_fail
+check_source_selection || test_fail
+check_packet_interval || test_fail
+check_sync || test_fail
+
+check_file_messages "20.*192.168.123.*" 30 35 measurements.log || test_fail
+rm -f tmp/measurements.log
+
+servers=6
+client_server_conf="pool nodes-1-2-3-4-5-6.net1.clk maxsources 2 minpoll 6 maxpoll 6"
+
+run_test || test_fail
+check_chronyd_exit || test_fail
+check_source_selection || test_fail
+check_packet_interval || test_fail
+check_sync || test_fail
+
+check_file_messages "20.*192.168.123.*" 15 17 measurements.log || test_fail
+rm -f tmp/measurements.log
+
+test_pass
diff --git a/test/simulation/138-syncloop b/test/simulation/138-syncloop
new file mode 100755
index 0000000..2d3999e
--- /dev/null
+++ b/test/simulation/138-syncloop
@@ -0,0 +1,34 @@
+#!/usr/bin/env bash
+
+. ./test.common
+
+test_start "loop prevention"
+
+mkdir tmp/logdir1 tmp/logdir2
+
+server_conf="
+server 192.168.123.1
+server 192.168.123.2
+logdir tmp/logdir1
+log measurements"
+client_server_conf="
+server 192.168.123.1
+server 192.168.123.2
+logdir tmp/logdir2
+log measurements
+allow"
+
+run_test || test_fail
+check_chronyd_exit || test_fail
+check_source_selection || test_fail
+check_sync || test_fail
+
+check_file_messages "20.*123\.1.* 111 111 1110" 30 200 logdir1/measurements.log || test_fail
+check_file_messages "20.*123\.2.* 111 111 1110" 30 200 logdir1/measurements.log || test_fail
+check_file_messages "20.*123\...* 111 111 1111" 0 0 logdir1/measurements.log || test_fail
+check_file_messages "20.*123\.1.* 111 111 1111" 30 200 logdir2/measurements.log || test_fail
+check_file_messages "20.*123\.1.* 111 111 1110" 0 0 logdir2/measurements.log || test_fail
+check_file_messages "20.*123\.2.* 111 111 1110" 30 200 logdir2/measurements.log || test_fail
+check_file_messages "20.*123\.2.* 111 111 1111" 0 0 logdir1/measurements.log || test_fail
+
+test_pass
diff --git a/test/simulation/139-nts b/test/simulation/139-nts
new file mode 100755
index 0000000..6a2112d
--- /dev/null
+++ b/test/simulation/139-nts
@@ -0,0 +1,312 @@
+#!/usr/bin/env bash
+
+. ./test.common
+
+test_start "NTP authentication with NTS"
+
+check_config_h 'FEAT_NTS 1' || test_skip
+certtool --help &> /dev/null || test_skip
+
+export CLKNETSIM_START_DATE=$(date -d 'Jan 1 00:00:00 UTC 2010' +'%s')
+
+for i in 1 2; do
+ cat > tmp/cert$i.cfg <<-EOF
+ cn = "node$i.net1.clk"
+ dns_name = "node$i.net1.clk"
+ ip_address = "192.168.123.$i"
+ serial = 001
+ activation_date = "2010-01-01 00:00:00 UTC"
+ expiration_date = "2010-01-02 00:00:00 UTC"
+ signing_key
+ encryption_key
+ EOF
+
+ certtool --generate-privkey --key-type=ed25519 --outfile tmp/server$i.key &> \
+ tmp/log.certtool$i
+ certtool --generate-self-signed --load-privkey tmp/server$i.key \
+ --template tmp/cert$i.cfg --outfile tmp/server$i.crt &>> tmp/log.certtool$i
+done
+
+max_sync_time=400
+dns=1
+server_conf="
+ntsserverkey tmp/server1.key
+ntsservercert tmp/server1.crt
+ntsprocesses 0
+ntsrotate 66
+ntsdumpdir tmp
+"
+client_server_options="minpoll 6 maxpoll 6 nts"
+client_conf="
+nosystemcert
+ntstrustedcerts /dev/null
+ntstrustedcerts tmp/server1.crt
+ntstrustedcerts /dev/null
+logdir tmp
+log rawmeasurements"
+
+run_test || test_fail
+check_chronyd_exit || test_fail
+check_source_selection || test_fail
+check_sync || test_fail
+
+check_file_messages "20.*123\.1.* 111 111 1111" 75 80 measurements.log || test_fail
+check_file_messages "20.*123\.1.* 111 001 0000" 37 39 measurements.log || test_fail
+check_file_messages " 2 1 .* 4460 " 260 300 log.packets || test_fail
+check_file_messages "." 6 6 ntskeys || test_fail
+rm -f tmp/measurements.log
+
+client_conf+="
+ntsrefresh 120
+ntsdumpdir tmp"
+
+run_test || test_fail
+check_chronyd_exit || test_fail
+check_source_selection || test_fail
+check_sync || test_fail
+
+check_file_messages "20.*123\.1.* 111 111 1111" 99 103 measurements.log || test_fail
+check_file_messages "20.*123\.1.* 111 001 0000" 0 0 measurements.log || test_fail
+check_file_messages " 2 1 .* 4460 " 350 390 log.packets || test_fail
+check_file_messages "." 6 6 ntskeys || test_fail
+check_file_messages "." 12 13 192.168.123.1.nts || test_fail
+rm -f tmp/measurements.log
+
+export CLKNETSIM_START_DATE=$(date -d 'Jan 1 00:00:00 UTC 2010 + 40000 sec' +'%s')
+
+server_conf+="
+ntsrotate 100000"
+client_conf+="
+ntsrefresh 39500"
+
+run_test || test_fail
+check_chronyd_exit || test_fail
+check_source_selection || test_fail
+check_sync || test_fail
+
+check_file_messages "20.*123\.1.* 111 111 1111" 150 160 measurements.log || test_fail
+check_file_messages "20.*123\.1.* 111 001 0000" 0 0 measurements.log || test_fail
+check_file_messages " 2 1 .* 4460 " 6 10 log.packets || test_fail
+check_file_messages "^9\.......e+03 2 1 .* 4460 " 6 10 log.packets || test_fail
+check_file_messages "." 6 6 ntskeys || test_fail
+check_file_messages "." 12 13 192.168.123.1.nts || test_fail
+rm -f tmp/measurements.log
+
+client_conf="
+nosystemcert"
+
+run_test || test_fail
+check_chronyd_exit || test_fail
+check_source_selection && test_fail
+check_sync && test_fail
+
+check_file_messages " 2 1 .* 123 " 0 0 log.packets || test_fail
+check_file_messages " 2 1 .* 4460 " 10 20 log.packets || test_fail
+
+export CLKNETSIM_START_DATE=$(date -d 'Jan 2 00:00:01 UTC 2010' +'%s')
+
+client_conf="
+nosystemcert
+ntstrustedcerts tmp/server1.crt"
+
+run_test || test_fail
+check_chronyd_exit || test_fail
+check_source_selection && test_fail
+check_sync && test_fail
+
+check_file_messages " 2 1 .* 123 " 0 0 log.packets || test_fail
+check_file_messages " 2 1 .* 4460 " 10 20 log.packets || test_fail
+check_log_messages "expired certificate" 4 4 || test_fail
+
+client_conf+="
+nocerttimecheck 1"
+
+run_test || test_fail
+check_chronyd_exit || test_fail
+check_source_selection || test_fail
+check_sync || test_fail
+
+export CLKNETSIM_START_DATE=$(date -d 'Jan 1 00:00:00 UTC 2010' +'%s')
+
+client_conf="
+nosystemcert
+ntstrustedcerts tmp/server1.crt
+ntsrefresh 500"
+
+for dns in 1 0; do
+ server_conf="
+ ntsserverkey tmp/server1.key
+ ntsservercert tmp/server1.crt
+ ntsprocesses 0
+ ntsrotate 0
+ ntsdumpdir tmp"
+
+ if [ $dns != 0 ]; then
+ server_conf+="
+ ntsntpserver node2.net1.clk"
+ client_server_conf="server node1.net1.clk $client_server_options"
+ else
+ server_conf+="
+ ntsntpserver 192.168.123.2"
+ client_server_conf="server 192.168.123.1 $client_server_options"
+ fi
+
+ servers=1
+
+ run_test || test_fail
+ check_chronyd_exit || test_fail
+ check_source_selection && test_fail
+ check_sync && test_fail
+
+ check_file_messages " 2 1 .* 4460 " 50 100 log.packets || test_fail
+ check_file_messages " 2 2 .* 4460 " 0 0 log.packets || test_fail
+ check_log_messages "Source 192.168.123.1 changed to 192.168.123.2" 6 8 || test_fail
+ check_log_messages "Source 192.168.123.2 replaced with 192.168.123.1" 6 8 || test_fail
+
+ servers=2
+
+ run_test || test_fail
+ check_chronyd_exit || test_fail
+ check_source_selection || test_fail
+ check_sync || test_fail
+
+ check_file_messages " 3 1 .* 4460 " 100 150 log.packets || test_fail
+ check_file_messages " 3 2 .* 4460 " 0 0 log.packets || test_fail
+ check_log_messages "Source 192.168.123.1 changed to 192.168.123.2" 1 1 || test_fail
+ check_log_messages "Source 192.168.123.2 replaced with 192.168.123.1" 0 0 || test_fail
+
+ server_conf+="
+ ntsratelimit interval 12 burst 1 leak 4"
+
+ run_test || test_fail
+ check_chronyd_exit || test_fail
+ check_source_selection && test_fail
+
+ check_file_messages " 3 1 .* 4460 1 0 2" 25 50 log.packets || test_fail
+ check_file_messages " 3 2 .* 4460 " 0 0 log.packets || test_fail
+ check_log_messages "Source 192.168.123.1 changed to 192.168.123.2" 2 6 || test_fail
+ check_log_messages "Source 192.168.123.2 replaced with 192.168.123.1" 1 6 || test_fail
+done
+
+servers=2
+server_conf="
+ntsserverkey tmp/server1.key
+ntsservercert tmp/server1.crt
+ntsprocesses 0
+ntsrotate 0
+ntsntpserver node2.net1.clk
+port 11123
+ntsdumpdir tmp"
+client_conf="
+nosystemcert
+ntstrustedcerts tmp/server1.crt
+ntsdumpdir tmp"
+client_server_conf="server 192.168.123.1 $client_server_options"
+
+rm -f tmp/*.nts
+
+run_test || test_fail
+check_chronyd_exit || test_fail
+check_source_selection || test_fail
+check_sync || test_fail
+
+check_log_messages "Could not change" 0 0 || test_fail
+check_file_messages " 3 1 .* 4460 1 0 2" 1 1 log.packets || test_fail
+check_file_messages " 3 2 .* 4460 " 0 0 log.packets || test_fail
+
+for dns in 1 0; do
+ run_test || test_fail
+ check_chronyd_exit || test_fail
+ check_source_selection || test_fail
+ check_sync || test_fail
+
+ check_log_messages "Could not change" 0 0 || test_fail
+ check_file_messages " 3 1 .* 4460 1 0 2" 0 0 log.packets || test_fail
+ check_file_messages " 3 2 .* 4460 " 0 0 log.packets || test_fail
+done
+
+server_conf="
+ntsserverkey tmp/server1.key
+ntsservercert tmp/server1.crt
+ntsprocesses 0
+ntsrotate 0
+ntsdumpdir tmp"
+
+head -n 8 tmp/192.168.123.1.nts > tmp/192.168.123.1.nts_
+mv tmp/192.168.123.1.nts_ tmp/192.168.123.1.nts
+
+run_test || test_fail
+check_chronyd_exit || test_fail
+check_source_selection || test_fail
+check_sync || test_fail
+
+check_log_messages "Could not change" 0 0 || test_fail
+check_file_messages " 3 1 .* 4460 1 0 2" 1 1 log.packets || test_fail
+check_file_messages " 3 2 .* 4460 " 0 0 log.packets || test_fail
+check_file_messages " 3 1 .* 11123 " 0 0 log.packets || test_fail
+check_file_messages " 3 2 .* 123 " 0 0 log.packets || test_fail
+check_file_messages " 3 2 .* 11123 " 3 3 log.packets || test_fail
+
+dns=1
+server_conf="
+ntsserverkey tmp/server1.key
+ntsservercert tmp/server1.crt
+ntsserverkey tmp/server2.key
+ntsservercert tmp/server2.crt
+ntsprocesses 0"
+client_conf="
+nosystemcert
+ntstrustedcerts tmp/server1.crt
+ntstrustedcerts tmp/server2.crt
+minsources 2"
+client_server_conf=""
+
+run_test || test_fail
+check_chronyd_exit || test_fail
+check_source_selection || test_fail
+check_sync || test_fail
+
+client_conf="
+nosystemcert
+ntstrustedcerts tmp/server1.crt
+ntstrustedcerts 1 tmp/server1.crt
+ntstrustedcerts 2 tmp/server2.crt
+ntstrustedcerts 3 tmp/server2.crt"
+client_server_conf="
+server node1.net1.clk $client_server_options certset 0
+server node2.net1.clk $client_server_options certset 2"
+
+run_test || test_fail
+check_chronyd_exit || test_fail
+check_source_selection || test_fail
+check_sync || test_fail
+
+check_file_messages " 3 1 .* 123 " 100 200 log.packets || test_fail
+check_file_messages " 3 2 .* 123 " 100 200 log.packets || test_fail
+
+client_server_conf="
+server node1.net1.clk $client_server_options certset 2
+server node2.net1.clk $client_server_options"
+
+run_test || test_fail
+check_chronyd_exit || test_fail
+check_source_selection && test_fail
+check_sync && test_fail
+
+check_file_messages " 3 1 .* 123 " 0 0 log.packets || test_fail
+check_file_messages " 3 2 .* 123 " 0 0 log.packets || test_fail
+
+client_conf="
+nosystemcert
+ntstrustedcerts tmp/nosuch.crt
+ntstrustedcerts 2 tmp/nosuch.crt"
+
+run_test || test_fail
+check_chronyd_exit || test_fail
+check_source_selection && test_fail
+check_sync && test_fail
+
+check_file_messages " 3 1 .* 123 " 0 0 log.packets || test_fail
+check_file_messages " 3 2 .* 123 " 0 0 log.packets || test_fail
+
+test_pass
diff --git a/test/simulation/140-noclientlog b/test/simulation/140-noclientlog
new file mode 100755
index 0000000..502398f
--- /dev/null
+++ b/test/simulation/140-noclientlog
@@ -0,0 +1,21 @@
+#!/usr/bin/env bash
+
+. ./test.common
+test_start "noclientlog option"
+
+server_conf="noclientlog"
+client_server_options="xleave"
+client_conf="
+logdir tmp
+log rawmeasurements"
+
+run_test || test_fail
+check_chronyd_exit || test_fail
+check_source_selection || test_fail
+check_packet_interval || test_fail
+check_sync || test_fail
+
+check_file_messages "111 111 1111.* 4B " 30 200 measurements.log || test_fail
+check_file_messages "111 111 1111.* 4I " 0 0 measurements.log || test_fail
+
+test_pass
diff --git a/test/simulation/141-copy b/test/simulation/141-copy
new file mode 100755
index 0000000..80e56bc
--- /dev/null
+++ b/test/simulation/141-copy
@@ -0,0 +1,19 @@
+#!/usr/bin/env bash
+
+. ./test.common
+
+test_start "copy option"
+
+check_config_h 'FEAT_CMDMON 1' || test_skip
+
+client_server_options="copy"
+chronyc_conf="tracking"
+
+run_test || test_fail
+check_chronyd_exit || test_fail
+check_source_selection || test_fail
+check_sync || test_fail
+check_chronyc_output "^Reference ID *: 7F7F0101 \(192\.168\.123\.1\)
+Stratum *: 1" || test_fail
+
+test_pass
diff --git a/test/simulation/142-ptpport b/test/simulation/142-ptpport
new file mode 100755
index 0000000..060932c
--- /dev/null
+++ b/test/simulation/142-ptpport
@@ -0,0 +1,41 @@
+#!/usr/bin/env bash
+
+. ./test.common
+
+test_start "PTP port"
+
+# Block communication between 3 and 1
+base_delay="(+ 1e-4 (* -1 (equal 0.1 from 3) (equal 0.1 to 1)))"
+
+cat > tmp/peer.keys <<-EOF
+1 MD5 1234567890
+EOF
+
+clients=2
+peers=2
+max_sync_time=420
+
+server_conf="
+ptpport 319"
+client_conf="
+ptpport 319
+authselectmode ignore
+keyfile tmp/peer.keys"
+client_server_options="minpoll 6 maxpoll 6 port 319"
+client_peer_options="minpoll 6 maxpoll 6 port 319 key 1"
+
+run_test || test_fail
+check_chronyd_exit || test_fail
+check_source_selection || test_fail
+check_sync || test_fail
+
+check_file_messages " 2 1 .* 319 319 1 96 " 150 160 \
+ log.packets || test_fail
+check_file_messages " 1 2 .* 319 319 1 96 " 150 160 \
+ log.packets || test_fail
+check_file_messages " 2 3 .* 319 319 1 116 " 150 160 \
+ log.packets || test_fail
+check_file_messages " 3 2 .* 319 319 1 116 " 150 160 \
+ log.packets || test_fail
+
+test_pass
diff --git a/test/simulation/143-manual b/test/simulation/143-manual
new file mode 100755
index 0000000..618cee6
--- /dev/null
+++ b/test/simulation/143-manual
@@ -0,0 +1,70 @@
+#!/usr/bin/env bash
+
+. ./test.common
+
+export TZ=UTC
+
+test_start "manual input"
+
+check_config_h 'FEAT_CMDMON 1' || test_skip
+
+limit=$[12 * 3600]
+client_server_conf=" "
+client_conf="manual"
+chronyc_conf="timeout 4000000
+settime 1:00:00
+settime 2:00:00
+settime 3:00:00
+settime 4:00:00
+manual delete 2
+settime 6:00:00
+manual list
+settime 8:00:00
+manual reset
+settime 10:00:00
+manual list"
+chronyc_start=1800
+base_delay=1800
+jitter=1e-6
+
+time_max_limit=4e-3
+freq_max_limit=4e-3
+time_rms_limit=2e-3
+freq_rms_limit=2e-5
+min_sync_time=7204
+max_sync_time=7206
+
+run_test || test_fail
+check_chronyd_exit || test_fail
+check_sync || test_fail
+
+check_chronyc_output "^200 OK
+Clock was 0\.4. seconds fast\. Frequency change = 0\.00ppm, new frequency = 0\.00ppm
+200 OK
+Clock was 0\.3. seconds fast\. Frequency change = (99|100)\...ppm, new frequency = (99|100)\...ppm
+200 OK
+Clock was \-?0\.0. seconds fast\. Frequency change = \-?0\.[01].ppm, new frequency = (99|100)\...ppm
+200 OK
+Clock was \-?0\.0. seconds fast\. Frequency change = \-?0\.[01].ppm, new frequency = (99|100)\...ppm
+200 OK
+200 OK
+Clock was \-?0\.0. seconds fast\. Frequency change = \-?0\.[012].ppm, new frequency = (99|100)\...ppm
+210 n_samples = 4
+# Date Time\(UTC\) Slewed Original Residual
+=======================================================
+ 0 2010-01-01 (00:59:59|01:00:00) [- ]0\.00 0\.46 [- ]0\.00
+ 1 2010-01-01 (01:59:59|02:00:00) [- ]0\.00 0\.36 [- ]0\.00
+ 2 2010-01-01 (03:59:59|04:00:00) [- ]0\.00 [- ]0\.00 [- ]0\.00
+ 3 2010-01-01 (05:59:59|06:00:00) [- ]0\.00 [- ]0\.00 [- ]0\.00
+200 OK
+Clock was \-?0\.0. seconds fast\. Frequency change = \-?0\.[012].ppm, new frequency = (99|100)\...ppm
+200 OK
+200 OK
+Clock was \-?0\.0. seconds fast\. Frequency change = \-?0\.00ppm, new frequency = (99|100)\...ppm
+210 n_samples = 1
+# Date Time\(UTC\) Slewed Original Residual
+=======================================================
+ 0 2010-01-01 (09:59:59|10:00:00) [- ]0\.00 [- ]0\.00 [- ]0\.00$" \
+ || test_fail
+
+test_pass
diff --git a/test/simulation/144-exp1 b/test/simulation/144-exp1
new file mode 100755
index 0000000..4a2042d
--- /dev/null
+++ b/test/simulation/144-exp1
@@ -0,0 +1,55 @@
+#!/usr/bin/env bash
+
+. ./test.common
+
+test_start "experimental extension field"
+
+check_config_h 'FEAT_CMDMON 1' || test_skip
+
+primary_time_offset=0.1
+server_strata=4
+min_sync_time=2000
+max_sync_time=2300
+chronyc_conf="doffset 0.1"
+chronyc_options="-h /clknetsim/unix/1:1"
+chronyc_start=2000
+
+for options in "extfield F323" "xleave extfield F323"; do
+ client_server_options="minpoll 6 maxpoll 6 $options"
+ server_server_options="$client_server_options"
+
+ run_test || test_fail
+ check_chronyd_exit || test_fail
+ check_source_selection || test_fail
+ check_sync || test_fail
+done
+
+server_server_options=""
+server_strata=1
+clients=4
+peers=4
+max_sync_time=2400
+# chain of peers and one enabled chronyc
+base_delay=$(cat <<-EOF | tr -d '\n'
+ (+ 1e-4 -1
+ (equal 0.1 from (+ to 1))
+ (equal 0.1 from (+ to -1))
+ (equal 0.1 from 6)
+ (equal 0.1 to 6))
+EOF
+)
+
+for lpoll in 5 6 7; do
+ for options in "minsamples 16 extfield F323" "minsamples 16 xleave extfield F323"; do
+ client_lpeer_options="minpoll $lpoll maxpoll $lpoll $options"
+ client_rpeer_options="minpoll 6 maxpoll 6 $options"
+ client_server_options="$client_rpeer_options"
+
+ run_test || test_fail
+ check_chronyd_exit || test_fail
+ check_source_selection || test_fail
+ check_sync || test_fail
+ done
+done
+
+test_pass
diff --git a/test/simulation/201-freqaccumulation b/test/simulation/201-freqaccumulation
new file mode 100755
index 0000000..6f14246
--- /dev/null
+++ b/test/simulation/201-freqaccumulation
@@ -0,0 +1,35 @@
+#!/usr/bin/env bash
+
+. ./test.common
+
+# Test fix in commit 60d0fa299307076143da94d36deb7b908fa9bdb7
+
+test_start "frequency accumulation"
+
+time_offset=100.0
+jitter=1e-6
+base_delay=1e-6
+wander=0.0
+
+limit=180
+time_max_limit=1e-5
+freq_max_limit=1e-7
+time_rms_limit=1e-5
+freq_rms_limit=1e-7
+min_sync_time=120
+max_sync_time=140
+
+client_server_options="minpoll 6 maxpoll 6"
+client_conf="driftfile tmp/drift
+makestep 1 1"
+
+for freq_offset in -5e-2 -5e-4 5e-4 5e-2; do
+ for drift in -1e+4 -1e+2 1e+2 1e+4; do
+ echo "$drift 100000" > tmp/drift
+ run_test || test_fail
+ check_chronyd_exit || test_fail
+ check_sync || test_fail
+ done
+done
+
+test_pass
diff --git a/test/simulation/202-prefer b/test/simulation/202-prefer
new file mode 100755
index 0000000..207c800
--- /dev/null
+++ b/test/simulation/202-prefer
@@ -0,0 +1,21 @@
+#!/usr/bin/env bash
+
+. ./test.common
+
+# Test fix in commit 4253075a97141edfa62043ab71bd0673587e6629
+
+test_start "prefer option"
+
+servers=3
+client_server_conf="
+server 192.168.123.1
+server 192.168.123.2
+server 192.168.123.3 prefer"
+
+run_test || test_fail
+check_chronyd_exit || test_fail
+check_source_selection || test_fail
+check_packet_interval || test_fail
+check_sync || test_fail
+
+test_pass
diff --git a/test/simulation/README b/test/simulation/README
new file mode 100644
index 0000000..e174500
--- /dev/null
+++ b/test/simulation/README
@@ -0,0 +1,11 @@
+This is a collection of simulation tests using the clknetsim simulator
+(supported on Linux only).
+
+https://github.com/mlichvar/clknetsim
+
+The CLKNETSIM_PATH environment variable should point to the directory where
+clknetsim was downloaded and compiled. If the variable is not set, the tests
+will look for clknetsim in ./clknetsim in the current directory.
+
+The tests are written in bash and they can be run directly. The ./run script
+runs all tests.
diff --git a/test/simulation/run b/test/simulation/run
new file mode 100755
index 0000000..0954438
--- /dev/null
+++ b/test/simulation/run
@@ -0,0 +1,90 @@
+#!/usr/bin/env bash
+
+print_help() {
+ echo "$1 [-a] [-i ITERATIONS] [-m MAXFAILS] [-s SEED] [TEST]..."
+}
+
+run_test() {
+ local result name=$1 seed=$2
+
+ CLKNETSIM_RANDOM_SEED=$seed ./$name
+ result=$?
+
+ if [ $result -ne 0 -a $result -ne 9 ]; then
+ if [ $abort_on_fail -ne 0 ]; then
+ echo 1>&2
+ echo Failed with random seed $seed 1>&2
+ exit 1
+ fi
+ failed_seeds=(${failed_seeds[@]} $seed)
+ fi
+
+ return $result
+}
+
+abort_on_fail=0
+iterations=1
+max_fails=0
+random_seed=${CLKNETSIM_RANDOM_SEED:-$RANDOM}
+
+while getopts ":ai:m:s:" opt; do
+ case $opt in
+ a) abort_on_fail=1;;
+ i) iterations=$OPTARG;;
+ m) max_fails=$OPTARG;;
+ s) random_seed=$OPTARG;;
+ *) print_help "$0"; exit 3;;
+ esac
+done
+
+shift $[$OPTIND - 1]
+
+passed=() failed=() skipped=() failed_seeds=()
+
+[ $# -gt 0 ] && tests=($@) || tests=([0-9]*-*[^_])
+
+for test in "${tests[@]}"; do
+ if [ $iterations -gt 1 ]; then
+ printf "%-30s" "$test"
+ fails=0
+ for i in $(seq 1 $iterations); do
+ run_test $test $[$random_seed + $i - 1] > /dev/null
+ case $? in
+ 0) echo -n ".";;
+ 9) break;;
+ *) echo -n "x"; fails=$[$fails + 1];;
+ esac
+ done
+ if [ $i -lt $iterations ]; then
+ printf "%${iterations}s" ""
+ echo " SKIP"
+ result=9
+ elif [ $fails -gt $max_fails ]; then
+ echo " FAIL"
+ result=1
+ else
+ echo " PASS"
+ result=0
+ fi
+ else
+ printf "%s " "$test"
+ run_test $test $random_seed
+ result=$?
+ echo
+ fi
+
+ case $result in
+ 0) passed=(${passed[@]} $test);;
+ 9) skipped=(${skipped[@]} $test);;
+ *) failed=(${failed[@]} $test);;
+ esac
+done
+
+echo
+echo "SUMMARY:"
+echo " TOTAL $[${#passed[@]} + ${#failed[@]} + ${#skipped[@]}]"
+echo " PASSED ${#passed[@]}"
+echo " FAILED ${#failed[@]} (${failed[@]}) (${failed_seeds[@]})"
+echo " SKIPPED ${#skipped[@]} (${skipped[@]})"
+
+[ ${#failed[@]} -eq 0 ]
diff --git a/test/simulation/test.common b/test/simulation/test.common
new file mode 100644
index 0000000..70bbde1
--- /dev/null
+++ b/test/simulation/test.common
@@ -0,0 +1,528 @@
+# Copyright (C) 2013-2014 Miroslav Lichvar <mlichvar@redhat.com>
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+export LC_ALL=C
+export PATH=../../:$PATH
+export CLKNETSIM_PATH=${CLKNETSIM_PATH:-clknetsim}
+
+if [ ! -x $CLKNETSIM_PATH/clknetsim ]; then
+ echo "SKIP (clknetsim not found)"
+ exit 9
+fi
+
+. $CLKNETSIM_PATH/clknetsim.bash
+
+# Default test testings
+
+default_limit=10000
+default_primary_time_offset=0.0
+default_time_offset=1e-1
+default_freq_offset=1e-4
+default_base_delay=1e-4
+default_jitter=1e-4
+default_jitter_asymmetry=0.0
+default_wander=1e-9
+default_refclock_jitter=""
+default_refclock_offset=0.0
+
+default_update_interval=0
+default_shift_pll=2
+
+default_server_strata=1
+default_servers=1
+default_clients=1
+default_peers=0
+default_falsetickers=0
+default_server_start=0.0
+default_client_start=0.0
+default_chronyc_start=1000.0
+default_server_step=""
+default_client_step=""
+
+default_client_server_conf=""
+default_server_server_options=""
+default_client_server_options=""
+default_server_peer_options=""
+default_server_lpeer_options=""
+default_server_rpeer_options=""
+default_client_peer_options=""
+default_client_lpeer_options=""
+default_client_rpeer_options=""
+default_server_conf=""
+default_client_conf=""
+default_chronyc_conf=""
+default_server_chronyd_options=""
+default_client_chronyd_options=""
+default_chronyc_options=""
+
+default_time_max_limit=1e-3
+default_freq_max_limit=5e-4
+default_time_rms_limit=3e-4
+default_freq_rms_limit=1e-5
+default_min_sync_time=120
+default_max_sync_time=210
+
+default_client_min_mean_out_interval=0.0
+default_client_max_min_out_interval=inf
+
+default_cmdmon_unix=1
+default_dns=0
+
+# Initialize test settings from their defaults
+for defoptname in ${!default_*}; do
+ optname=${defoptname#default_}
+ [ -z "${!optname}" ] && declare "$optname"="${!defoptname}"
+done
+
+test_start() {
+ rm -rf tmp/*
+ echo "Testing $@:"
+
+ check_config_h 'FEAT_NTP 1' || test_skip
+}
+
+test_pass() {
+ echo "PASS"
+ exit 0
+}
+
+test_fail() {
+ echo "FAIL"
+ exit 1
+}
+
+test_skip() {
+ echo "SKIP"
+ exit 9
+}
+
+test_ok() {
+ pad_line
+ echo -e "\tOK"
+ return 0
+}
+
+test_bad() {
+ pad_line
+ echo -e "\tBAD"
+ return 1
+}
+
+test_error() {
+ pad_line
+ echo -e "\tERROR"
+ return 1
+}
+
+msg_length=0
+pad_line() {
+ local line_length=56
+ [ $msg_length -lt $line_length ] && \
+ printf "%$[$line_length - $msg_length]s" ""
+ msg_length=0
+}
+
+# Print aligned message
+test_message() {
+ local level=$1 eol=$2
+ shift 2
+ local msg="$*"
+
+ while [ $level -gt 0 ]; do
+ echo -n " "
+ level=$[$level - 1]
+ msg_length=$[$msg_length + 2]
+ done
+ echo -n "$msg"
+
+ msg_length=$[$msg_length + ${#msg}]
+ if [ $eol -ne 0 ]; then
+ echo
+ msg_length=0
+ fi
+}
+
+get_wander_expr() {
+ local scaled_wander
+
+ scaled_wander=$(awk "BEGIN {print $wander / \
+ sqrt($update_interval < 0 ? 2^-($update_interval) : 1)}")
+
+ echo "(+ $freq_offset (sum (* $scaled_wander (normal))))"
+}
+
+
+get_delay_expr() {
+ local direction=$1 asym
+
+ if [ $jitter_asymmetry == "0.0" ]; then
+ asym=""
+ elif [ $direction = "up" ]; then
+ asym=$(awk "BEGIN {print 1 - 2 * $jitter_asymmetry}")
+ elif [ $direction = "down" ]; then
+ asym=$(awk "BEGIN {print 1 + 2 * $jitter_asymmetry}")
+ fi
+ echo "(+ $base_delay (* $asym $jitter (exponential)))"
+}
+
+get_refclock_expr() {
+ echo "(+ $refclock_offset (* $refclock_jitter (normal)))"
+}
+
+get_chronyd_nodes() {
+ echo $[$servers * $server_strata + $clients]
+}
+
+get_node_name() {
+ local index=$1
+
+ if [ $dns -ne 0 ]; then
+ echo "node$index.net1.clk"
+ else
+ echo "192.168.123.$index"
+ fi
+}
+
+get_chronyd_conf() {
+ local i stratum=$1 peer=$2
+
+ if [ $stratum -eq 1 ]; then
+ echo "local stratum 1"
+ echo "$server_conf"
+ elif [ $stratum -le $server_strata ]; then
+ for i in $(seq 1 $servers); do
+ echo "server $(get_node_name $[$servers * ($stratum - 2) + $i]) $server_server_options"
+ done
+ for i in $(seq 1 $peers); do
+ [ $i -eq $peer -o $i -gt $servers ] && continue
+ echo -n "peer $(get_node_name $[$servers * ($stratum - 1) + $i]) $server_peer_options "
+ [ $i -lt $peer ] && echo "$server_lpeer_options" || echo "$server_rpeer_options"
+ done
+ echo "$server_conf"
+ else
+ echo "deny"
+ if [ -n "$client_server_conf" ]; then
+ echo "$client_server_conf"
+ else
+ for i in $(seq 1 $servers); do
+ echo "server $(get_node_name $[$servers * ($stratum - 2) + $i]) $client_server_options"
+ done
+ fi
+ for i in $(seq 1 $peers); do
+ [ $i -eq $peer -o $i -gt $clients ] && continue
+ echo -n "peer $(get_node_name $[$servers * ($stratum - 1) + $i]) $client_peer_options "
+ [ $i -lt $peer ] && echo "$client_lpeer_options" || echo "$client_rpeer_options"
+ done
+ echo "$client_conf"
+ fi
+}
+
+# Check if chrony was built with specified option in config.h
+check_config_h() {
+ local pattern=$1
+ grep -q "^#define $pattern" ../../config.h
+}
+
+# Check if the clock was well synchronized
+check_sync() {
+ local i sync_time max_time_error max_freq_error ret=0
+ local rms_time_error rms_freq_error
+
+ test_message 2 1 "checking clock sync time, max/rms time/freq error:"
+
+ for i in $(seq 1 $(get_chronyd_nodes)); do
+ [ $i -gt $[$servers * $server_strata] ] || continue
+
+ sync_time=$(find_sync tmp/log.offset tmp/log.freq $i \
+ $time_max_limit $freq_max_limit 1.0)
+ max_time_error=$(get_stat 'Maximum absolute offset' $i)
+ max_freq_error=$(get_stat 'Maximum absolute frequency' $i)
+ rms_time_error=$(get_stat 'RMS offset' $i)
+ rms_freq_error=$(get_stat 'RMS frequency' $i)
+
+ test_message 3 0 "node $i: $sync_time $(printf '%.2e %.2e %.2e %.2e' \
+ $max_time_error $max_freq_error $rms_time_error $rms_freq_error)"
+
+ check_stat $sync_time $min_sync_time $max_sync_time && \
+ check_stat $max_time_error 0.0 $time_max_limit && \
+ check_stat $max_freq_error 0.0 $freq_max_limit && \
+ check_stat $rms_time_error 0.0 $time_rms_limit && \
+ check_stat $rms_freq_error 0.0 $freq_rms_limit && \
+ test_ok || test_bad
+
+ [ $? -eq 0 ] || ret=1
+ done
+
+ return $ret
+}
+
+# Check if chronyd exited properly
+check_chronyd_exit() {
+ local i ret=0
+
+ test_message 2 1 "checking chronyd exit:"
+
+ for i in $(seq 1 $(get_chronyd_nodes)); do
+ test_message 3 0 "node $i:"
+
+ grep -q 'chronyd exiting' tmp/log.$i && \
+ ! grep -q 'Adjustment.*exceeds.*exiting' tmp/log.$i && \
+ ! grep -q 'Assertion.*failed' tmp/log.$i && \
+ test_ok || test_bad
+ [ $? -eq 0 ] || ret=1
+ done
+
+ return $ret
+}
+
+# Check for problems in source selection
+check_source_selection() {
+ local i ret=0
+
+ test_message 2 1 "checking source selection:"
+
+ for i in $(seq $[$servers * $server_strata + 1] $(get_chronyd_nodes)); do
+ test_message 3 0 "node $i:"
+
+ ! grep -q 'no majority\|no selectable sources' tmp/log.$i && \
+ grep -q 'Selected source' tmp/log.$i && \
+ test_ok || test_bad
+ [ $? -eq 0 ] || ret=1
+ done
+
+ return $ret
+}
+
+# Check if incoming and outgoing packet intervals are sane
+check_packet_interval() {
+ local i ret=0 mean_in_interval mean_out_interval min_in_interval min_out_interval
+
+ test_message 2 1 "checking mean/min incoming/outgoing packet interval:"
+
+ for i in $(seq 1 $(get_chronyd_nodes)); do
+ mean_in_interval=$(get_stat 'Mean incoming packet interval' $i)
+ mean_out_interval=$(get_stat 'Mean outgoing packet interval' $i)
+ min_in_interval=$(get_stat 'Minimum incoming packet interval' $i)
+ min_out_interval=$(get_stat 'Minimum outgoing packet interval' $i)
+
+ test_message 3 0 "node $i: $(printf '%.2e %.2e %.2e %.2e' \
+ $mean_in_interval $mean_out_interval $min_in_interval $min_out_interval)"
+
+ # Check that the mean intervals are non-zero and shorter than
+ # limit, incoming is not longer than outgoing for stratum 1
+ # servers, outgoing is not longer than incoming for clients,
+ # and the minimum outgoing interval is not shorter than the NTP
+ # sampling separation or iburst interval for clients
+ nodes=$[$servers * $server_strata + $clients]
+ check_stat $mean_in_interval 0.1 inf && \
+ check_stat $mean_out_interval 0.1 inf && \
+ ([ $i -gt $servers ] || \
+ check_stat $mean_in_interval 0.0 $mean_out_interval 10*$jitter) && \
+ ([ $i -le $[$servers * $server_strata] ] || \
+ check_stat $mean_out_interval $client_min_mean_out_interval \
+ $mean_in_interval 10*$jitter) && \
+ ([ $i -le $[$servers * $server_strata] ] || \
+ check_stat $min_out_interval \
+ $([ $servers -gt 1 ] && echo 0.18 || echo 1.8) \
+ $client_max_min_out_interval) && \
+ test_ok || test_bad
+
+ [ $? -eq 0 ] || ret=1
+ done
+
+ return $ret
+}
+
+# Compare chronyc output with specified pattern
+check_chronyc_output() {
+ local i ret=0 pattern=$1
+
+ test_message 2 1 "checking chronyc output:"
+
+ for i in $(seq $[$(get_chronyd_nodes) + 1] $[$(get_chronyd_nodes) + $clients]); do
+ test_message 3 0 "node $i:"
+
+ [[ "$(cat tmp/log.$i)" =~ $pattern ]] && \
+ test_ok || test_bad
+ [ $? -eq 0 ] || ret=1
+ done
+
+ return $ret
+}
+
+# Check the number of messages matching a pattern in the client logs
+check_log_messages() {
+ local i count ret=0 pattern=$1 min=$2 max=$3
+
+ test_message 2 1 "checking number of messages \"$pattern\":"
+
+ for i in $(seq $[$servers * $server_strata + 1] $(get_chronyd_nodes)); do
+ count=$(grep "$pattern" tmp/log.$i | wc -l)
+ test_message 3 0 "node $i: $count"
+
+ [ "$min" -le "$count" ] && [ "$count" -le "$max" ] && \
+ test_ok || test_bad
+ [ $? -eq 0 ] || ret=1
+ done
+
+ return $ret
+}
+
+# Check the number of messages matching a pattern in a specified file
+check_file_messages() {
+ local i count ret=0 pattern=$1 min=$2 max=$3
+ shift 3
+
+ test_message 2 1 "checking number of messages \"$pattern\":"
+
+ for i; do
+ count=$(grep "$pattern" tmp/$i | wc -l)
+ test_message 3 0 "$i: $count"
+
+ [ "$min" -le "$count" ] && [ "$count" -le "$max" ] && \
+ test_ok || test_bad
+ [ $? -eq 0 ] || ret=1
+ done
+
+ return $ret
+}
+
+# Check if only NTP port (123) was used
+check_packet_port() {
+ local i ret=0 port=123
+
+ test_message 2 1 "checking port numbers in packet log:"
+
+ for i in $(seq 1 $(get_chronyd_nodes)); do
+ test_message 3 0 "node $i:"
+
+ grep -E -q "^([0-9e.+-]+ ){5}$port " tmp/log.packets && \
+ ! grep -E "^[0-9e.+-]+ $i " tmp/log.packets | \
+ grep -E -q -v "^([0-9e.+-]+ ){5}$port " && \
+ test_ok || test_bad
+ [ $? -eq 0 ] || ret=1
+ done
+
+ return $ret
+}
+
+# Print test settings which differ from default value
+print_nondefaults() {
+ local defoptname optname
+
+ test_message 2 1 "non-default settings:"
+ for defoptname in ${!default_*}; do
+ optname=${defoptname#default_}
+ [ "${!defoptname}" = "${!optname}" ] || \
+ test_message 3 1 $optname=${!optname}
+ done
+}
+
+run_simulation() {
+ local nodes=$1
+
+ test_message 2 0 "running simulation:"
+
+ start_server $nodes \
+ -n 2 \
+ -o tmp/log.offset -f tmp/log.freq -p tmp/log.packets \
+ -R $(awk "BEGIN {print $update_interval < 0 ? 2^-($update_interval) : 1}") \
+ -r $(awk "BEGIN {print $max_sync_time * 2^$update_interval}") \
+ -l $(awk "BEGIN {print $limit * 2^$update_interval}") && test_ok || test_error
+}
+
+run_test() {
+ local i j n stratum node nodes step start freq offset conf options
+
+ test_message 1 1 "network with $servers*$server_strata servers and $clients clients:"
+ print_nondefaults
+
+ nodes=$(get_chronyd_nodes)
+ [ -n "$chronyc_conf" ] && nodes=$[$nodes + $clients]
+
+ export CLKNETSIM_UNIX_SUBNET=$[$cmdmon_unix != 0 ? 2 : 0]
+
+ for i in $(seq 1 $nodes); do
+ echo "node${i}_shift_pll = $shift_pll"
+ for j in $(seq 1 $nodes); do
+ echo "node${i}_delay${j} = $(get_delay_expr up)"
+ echo "node${j}_delay${i} = $(get_delay_expr down)"
+ done
+ done > tmp/conf
+
+ node=1
+
+ for stratum in $(seq 1 $[$server_strata + 1]); do
+ [ $stratum -le $server_strata ] && n=$servers || n=$clients
+
+ for i in $(seq 1 $n); do
+ test_message 2 0 "starting node $node:"
+ if [ $stratum -eq 1 ]; then
+ step=$server_step
+ start=$server_start
+ freq=""
+ [ $i -le $falsetickers ] &&
+ offset=$i.0 || offset=$primary_time_offset
+ options=$server_chronyd_options
+ elif [ $stratum -le $server_strata ]; then
+ step=$server_step
+ start=$server_start
+ freq=$(get_wander_expr)
+ offset=0.0
+ options=$server_chronyd_options
+ else
+ step=$client_step
+ start=$client_start
+ freq=$(get_wander_expr)
+ offset=$time_offset
+ options=$client_chronyd_options
+ fi
+
+ conf=$(get_chronyd_conf $stratum $i $n)
+
+ [ -z "$freq" ] || echo "node${node}_freq = $freq" >> tmp/conf
+ [ -z "$step" ] || echo "node${node}_step = $step" >> tmp/conf
+ [ -z "$refclock_jitter" ] || \
+ echo "node${node}_refclock = $(get_refclock_expr)" >> tmp/conf
+ echo "node${node}_offset = $offset" >> tmp/conf
+ echo "node${node}_start = $start" >> tmp/conf
+ start_client $node chronyd "$conf" "" "$options" && \
+ test_ok || test_error
+
+ [ $? -ne 0 ] && return 1
+ node=$[$node + 1]
+ done
+ done
+
+ for i in $(seq 1 $[$nodes - $node + 1]); do
+ test_message 2 0 "starting node $node:"
+
+ options=$([ $dns -eq 0 ] && printf "%s" "-n")
+ if [ $cmdmon_unix -ne 0 ]; then
+ options+=" -h /clknetsim/unix/$[$node - $clients]:1"
+ else
+ options+=" -h $(get_node_name $[$node - $clients])"
+ fi
+
+ echo "node${node}_start = $chronyc_start" >> tmp/conf
+ start_client $node chronyc "$chronyc_conf" "" "$options $chronyc_options" && \
+ test_ok || test_error
+
+ [ $? -ne 0 ] && return 1
+ node=$[$node + 1]
+ done
+
+ run_simulation $nodes
+}