commit 0cfd008330b543a1674787cb031507fb1951a1f9
Author: Viktor G <viktor@netgate.com>
Date:   Sat Nov 13 17:33:51 2021 +0300

    NTP Peer mode. Implements #11496

diff --git a/src/etc/inc/system.inc b/src/etc/inc/system.inc
index 4e62f27f08..91a432f9de 100644
--- a/src/etc/inc/system.inc
+++ b/src/etc/inc/system.inc
@@ -1621,7 +1621,7 @@ global $ntp_poll_min_value, $ntp_poll_max_value;
 global $ntp_poll_min_default_gps, $ntp_poll_max_default_gps;
 global $ntp_poll_min_default_pps, $ntp_poll_max_default_pps;
 global $ntp_poll_min_default, $ntp_poll_max_default;
-global $ntp_auth_halgos;
+global $ntp_auth_halgos, $ntp_server_types;
 $ntp_poll_min_value = 4;
 $ntp_poll_max_value = 17;
 $ntp_poll_min_default_gps = 4;
@@ -1635,6 +1635,11 @@ $ntp_auth_halgos = array(
 	'sha1' => 'SHA1',
 	'sha256' => 'SHA256'
 );
+$ntp_server_types = array(
+	'server' => 'Server',
+	'pool' => 'Pool',
+	'peer' => 'Peer'
+);
 
 function system_ntp_poll_values() {
 	global $ntp_poll_min_value, $ntp_poll_max_value;
@@ -1996,7 +2001,11 @@ function system_ntp_configure() {
 			$ntpcfg .= 'pool ';
 			$have_pools = true;
 		} else {
-			$ntpcfg .= 'server ';
+			if (substr_count($config['ntpd']['ispeer'], $ts)) {
+				$ntpcfg .= 'peer ';
+			} else {
+				$ntpcfg .= 'server ';
+			}
 			if ($config['ntpd']['dnsresolv'] == 'inet') {
 				$ntpcfg .= '-4 ';
 			} elseif ($config['ntpd']['dnsresolv'] == 'inet6') {
@@ -2004,7 +2013,10 @@ function system_ntp_configure() {
 			}
 		}
 
-		$ntpcfg .= "{$ts} iburst";
+		$ntpcfg .= "{$ts}";
+		if (!substr_count($config['ntpd']['ispeer'], $ts)) {
+			$ntpcfg .= " iburst";
+		}
 
 		$ntpcfg .= system_ntp_fixup_poll_value('minpoll', $config['ntpd']['ntpminpoll'], $ntp_poll_min_default);
 		$ntpcfg .= system_ntp_fixup_poll_value('maxpoll', $config['ntpd']['ntpmaxpoll'], $ntp_poll_max_default);
diff --git a/src/usr/local/www/services_ntpd.php b/src/usr/local/www/services_ntpd.php
index fd654e38d4..ba512a5e90 100644
--- a/src/usr/local/www/services_ntpd.php
+++ b/src/usr/local/www/services_ntpd.php
@@ -34,7 +34,7 @@ require_once("guiconfig.inc");
 require_once('rrd.inc');
 require_once("shaper.inc");
 
-global $ntp_poll_min_default, $ntp_poll_max_default;
+global $ntp_poll_min_default, $ntp_poll_max_default, $ntp_server_types;
 $ntp_poll_values = system_ntp_poll_values();
 $auto_pool_suffix = "pool.ntp.org";
 $max_candidate_peers = 25;
@@ -79,7 +79,7 @@ if ($_POST) {
 	}
 
 	for ($i = 0; $i < NUMTIMESERVERS; $i++) {
-		if (isset($pconfig["servselect{$i}"]) && (isset($pconfig["servispool{$i}"]) || 
+		if (isset($pconfig["servselect{$i}"]) && (($pconfig["servistype{$i}"] == 'pool') || 
 		    (substr_compare($pconfig["server{$i}"], $auto_pool_suffix, strlen($pconfig["server{$i}"]) - strlen($auto_pool_suffix), strlen($auto_pool_suffix)) === 0))) {
 			$input_errors[] = gettext("It is not possible to use 'No Select' for pools.");
 		}
@@ -121,6 +121,7 @@ if ($_POST) {
 		unset($config['ntpd']['prefer']);
 		unset($config['ntpd']['noselect']);
 		unset($config['ntpd']['ispool']);
+		unset($config['ntpd']['ispeer']);
 		$timeservers = '';
 
 		for ($i = 0; $i < NUMTIMESERVERS; $i++) {
@@ -133,8 +134,10 @@ if ($_POST) {
 				if (isset($_POST["servselect{$i}"])) {
 					$config['ntpd']['noselect'] .= "{$tserver} ";
 				}
-				if (isset($_POST["servispool{$i}"])) {
+				if ($_POST["servistype{$i}"] == 'pool') {
 					$config['ntpd']['ispool'] .= "{$tserver} ";
+				} elseif ($_POST["servistype{$i}"] == 'peer') {
+					$config['ntpd']['ispeer'] .= "{$tserver} ";
 				}
 			}
 		}
@@ -329,13 +332,20 @@ for ($counter=0; $counter < $maxrows; $counter++) {
 		isset($config['ntpd']['noselect']) && isset($timeservers[$counter]) && substr_count($config['ntpd']['noselect'], $timeservers[$counter])
 	 ))->sethelp('No Select');
 
-	$group->add(new Form_Checkbox(
-		'servispool' . $counter,
-		null,
+	if ((substr_compare($timeservers[$counter], $auto_pool_suffix, strlen($timeservers[$counter]) - strlen($auto_pool_suffix), strlen($auto_pool_suffix)) === 0) || (isset($config['ntpd']['ispool']) && isset($timeservers[$counter]) && substr_count($config['ntpd']['ispool'], $timeservers[$counter]))) {
+		$servertype = 'pool';
+	} elseif (isset($config['ntpd']['ispeer']) && isset($timeservers[$counter]) && substr_count($config['ntpd']['ispeer'], $timeservers[$counter])) {
+		$servertype = 'peer';
+	} else {
+		$servertype = 'server';
+	}
+
+	$group->add(new Form_Select(
+		'servistype' . $counter,
 		null,
-		(substr_compare($timeservers[$counter], $auto_pool_suffix, strlen($timeservers[$counter]) - strlen($auto_pool_suffix), strlen($auto_pool_suffix)) === 0)
-		 || (isset($config['ntpd']['ispool']) && isset($timeservers[$counter]) && substr_count($config['ntpd']['ispool'], $timeservers[$counter]))
-	 ))->sethelp('Is a Pool');
+		$servertype,
+		$ntp_server_types
+	 ))->sethelp('Type')->setWidth(2);
 
 	$group->add(new Form_Button(
 		'deleterow' . $counter,
@@ -363,7 +373,7 @@ $section->addInput(new Form_StaticText(
 	'are configured and they disagree, %2$sneither%3$s will be believed. Options:%1$s' .
 	'%2$sPrefer%3$s - NTP should favor the use of this server more than all others.%1$s' .
 	'%2$sNo Select%3$s - NTP should not use this server for time, but stats for this server will be collected and displayed.%1$s' .
-	'%2$sIs a Pool%3$s - this entry is a pool of NTP servers and not a single address. This is assumed for *.pool.ntp.org.',
+	'%2$sType%3$s - Server, Peer or a Pool of NTP servers and not a single address. This is assumed for *.pool.ntp.org.',
 	'<br />',
 	'<b>',
 	'</b>',
