diff --git a/src/etc/inc/plugins.inc.d/dhcpd.inc b/src/etc/inc/plugins.inc.d/dhcpd.inc index 991c04934..0d2e6fee6 100644 --- a/src/etc/inc/plugins.inc.d/dhcpd.inc +++ b/src/etc/inc/plugins.inc.d/dhcpd.inc @@ -47,16 +47,23 @@ function dhcpd_run() function dhcpd_dhcpv6_enabled() { global $config; + $explicit_off = []; /* handle manually configured DHCP6 server settings first */ foreach (config_read_array('dhcpdv6') as $dhcpv6if => $dhcpv6ifconf) { if (isset($config['interfaces'][$dhcpv6if]['enable']) && isset($dhcpv6ifconf['enable'])) { - return true; + if ($dhcpv6ifconf['enable'] == '-1') { + $explicit_off[] = $dhcpv6if; + } else { + return true; + } } } - /* handle DHCP-PD prefixes and 6RD dynamic interfaces */ - foreach (legacy_config_get_interfaces(array('virtual' => false)) as $ifcfg) { + foreach (legacy_config_get_interfaces(array('virtual' => false)) as $ifnm => $ifcfg) { + if (in_array($ifnm, $explicit_off)) { + continue; + } if (isset($ifcfg['enable']) && !empty($ifcfg['track6-interface']) && !isset($ifcfg['dhcpd6track6allowoverride'])) { return true; } @@ -864,8 +871,12 @@ function dhcpd_dhcp6_configure($verbose = false, $blacklist = []) if (!isset($config['interfaces'][$ifname]['dhcpd6track6allowoverride'])) { /* mock a real server */ - $dhcpdv6cfg[$ifname] = array(); - $dhcpdv6cfg[$ifname]['enable'] = true; + if (!empty($dhcpdv6cfg[$ifname]) && $dhcpdv6cfg[$ifname]['enable'] == '-1') { + /* tracking, but dhcpv6 disabled */ + $dhcpdv6cfg[$ifname] = []; + } else { + $dhcpdv6cfg[$ifname] = ['enable' => true]; + } /* fixed range */ $ifcfgipv6arr[7] = '1000'; diff --git a/src/etc/inc/plugins.inc.d/radvd.inc b/src/etc/inc/plugins.inc.d/radvd.inc index 2de8f8b5a..f241a915d 100644 --- a/src/etc/inc/plugins.inc.d/radvd.inc +++ b/src/etc/inc/plugins.inc.d/radvd.inc @@ -40,16 +40,21 @@ function radvd_configure() function radvd_enabled() { global $config; + $explicit_off = []; /* handle manually configured DHCP6 server settings first */ foreach (config_read_array('dhcpdv6') as $dhcpv6if => $dhcpv6ifconf) { if (isset($config['interfaces'][$dhcpv6if]['enable']) && isset($dhcpv6ifconf['ramode']) && $dhcpv6ifconf['ramode'] != 'disabled') { return true; + } elseif (isset($dhcpv6ifconf['ramode']) && $dhcpv6ifconf['ramode'] == 'disabled') { + $explicit_off[] = $dhcpv6if; } } - /* handle DHCP-PD prefixes and 6RD dynamic interfaces */ - foreach (legacy_config_get_interfaces(array('virtual' => false)) as $ifcfg) { + foreach (legacy_config_get_interfaces(array('virtual' => false)) as $ifnm => $ifcfg) { + if (in_array($ifnm, $explicit_off)) { + continue; + } if (isset($ifcfg['enable']) && !empty($ifcfg['track6-interface']) && !isset($ifcfg['dhcpd6track6allowoverride'])) { return true; } @@ -349,6 +354,9 @@ function radvd_configure_do($verbose = false, $blacklist = []) } elseif (isset($blacklist[$if])) { $radvdconf .= "# Skipping blacklisted interface {$if}\n"; continue; + } elseif (!empty($config['dhcpdv6'][$if]) && !empty($config['dhcpdv6'][$if]['ramode']) && $config['dhcpdv6'][$if]['ramode'] == 'disabled') { + $radvdconf .= "# Skipping explicit disabled interface {$if}\n"; + continue; } $trackif = $config['interfaces'][$if]['track6-interface']; diff --git a/src/opnsense/mvc/app/models/OPNsense/Base/Menu/MenuSystem.php b/src/opnsense/mvc/app/models/OPNsense/Base/Menu/MenuSystem.php index 32ba718fc..7973e95c2 100644 --- a/src/opnsense/mvc/app/models/OPNsense/Base/Menu/MenuSystem.php +++ b/src/opnsense/mvc/app/models/OPNsense/Base/Menu/MenuSystem.php @@ -265,7 +265,7 @@ class MenuSystem ) { $iftargets['dhcp4'][$key] = !empty($node->descr) ? (string)$node->descr : strtoupper($key); } - if (!empty(filter_var($node->ipaddrv6, FILTER_VALIDATE_IP, FILTER_FLAG_IPV6)) || !empty($node->dhcpd6track6allowoverride)) { + if (!empty(filter_var($node->ipaddrv6, FILTER_VALIDATE_IP, FILTER_FLAG_IPV6)) || !empty($node->{'track6-interface'})) { $iftargets['dhcp6'][$key] = !empty($node->descr) ? (string)$node->descr : strtoupper($key); } } diff --git a/src/www/services_dhcpv6.php b/src/www/services_dhcpv6.php index c25a988fd..3ed71f2dd 100644 --- a/src/www/services_dhcpv6.php +++ b/src/www/services_dhcpv6.php @@ -1,7 +1,7 @@ * Copyright (C) 2010 Seth Mos * All rights reserved. @@ -34,6 +34,104 @@ require_once("system.inc"); require_once("interfaces.inc"); require_once("plugins.inc.d/dhcpd.inc"); + +function show_track6_form($if) +{ + global $config; + $service_hook = 'dhcpd6'; + include("head.inc"); + include("fbegin.inc"); + $enable_label = gettext("Enable"); + $range_label = gettext("Range"); + $enable_descr = sprintf(gettext("Enable DHCPv6 server on " . "%s " ."interface"),!empty($config['interfaces'][$if]['descr']) ? htmlspecialchars($config['interfaces'][$if]['descr']) : strtoupper($if)); + $enable_input = 'checked="checked"'; + $save_btn_text = html_safe(gettext('Save')); + /* calculated "fixed" range */ + list ($ifcfgipv6) = interfaces_primary_address6($if, legacy_interfaces_details()); + $range = ['from' => '?', 'to' => '?']; + if (is_ipaddrv6($ifcfgipv6)) { + $ifcfgipv6 = Net_IPv6::getNetmask($ifcfgipv6, 64); + $ifcfgipv6arr = explode(':', $ifcfgipv6); + $ifcfgipv6arr[7] = '1000'; + $range['from'] = Net_IPv6::compress(implode(':', $ifcfgipv6arr)); + $ifcfgipv6arr[7] = '2000'; + $range['to'] = Net_IPv6::compress(implode(':', $ifcfgipv6arr)); + } + + if (!empty($config['dhcpdv6']) && !empty($config['dhcpdv6'][$if]) && isset($config['dhcpdv6'][$if]['enable']) && $config['dhcpdv6'][$if]['enable'] == '-1') { + /* disabled */ + $enable_input = ''; + } + + $range_tr = << + $range_label + {$range['from']} - {$range['to']} + + EOD; + + + echo << +
+
+
+
+
+
+ + + + + + + + + $range_tr + + + + + +
+
+
$enable_label + + $enable_descr +
  + + +
+
+
+
+
+
+
+ + EOD; + include("foot.inc"); +} + +function process_track6_form($if) +{ + $dhcpdv6cfg = &config_read_array('dhcpdv6'); + $this_server = []; + if (isset($dhcpdv6cfg[$if]) && isset($dhcpdv6cfg[$if]['ramode'])) { + /* keep ramode for router advertisements so we can use this field to disable the service when in tracking mode */ + $this_server['ramode'] = $dhcpdv6cfg[$if]['ramode']; + } + if (empty($_POST['enable'])) { + $this_server['enable'] = '-1'; + } + $dhcpdv6cfg[$if] = $this_server; + write_config(); + reconfigure_dhcpd(); + filter_configure(); + header(url_safe('Location: /services_dhcpv6.php?if=%s', array($if))); +} + + function reconfigure_dhcpd() { system_resolver_configure(); @@ -41,17 +139,15 @@ function reconfigure_dhcpd() clear_subsystem_dirty('staticmapsv6'); } +$if = null; +$act = null; if ($_SERVER['REQUEST_METHOD'] === 'GET') { // handle identifiers and action if (!empty($_GET['if']) && !empty($config['interfaces'][$_GET['if']]) && isset($config['interfaces'][$_GET['if']]['enable']) && (is_ipaddr($config['interfaces'][$_GET['if']]['ipaddrv6']) || - !empty($config['interfaces'][$_GET['if']]['dhcpd6track6allowoverride']))) { + !empty($config['interfaces'][$_GET['if']]['track6-interface']))) { $if = $_GET['if']; - } else { - /* if no interface is provided this invoke is invalid */ - header(url_safe('Location: /index.php')); - exit; } } elseif ($_SERVER['REQUEST_METHOD'] === 'POST') { // handle identifiers and actions @@ -60,11 +156,28 @@ if ($_SERVER['REQUEST_METHOD'] === 'GET') { } if (!empty($_POST['act'])) { $act = $_POST['act']; - } else { - $act = null; } } +/** + * XXX: In case of tracking, show different form and only handle on/off options. + * this code injection is intended to keep changes as minimal as possible and avoid regressions on existing isc-dhcp6 installs, + * while showing current state for tracking interfaces. + **/ +if ($if === null) { + /* if no interface is provided this invoke is invalid */ + header(url_safe('Location: /index.php')); + exit; +} elseif (!empty($config['interfaces'][$if]['track6-interface']) && !isset($config['interfaces'][$if]['dhcpd6track6allowoverride'])) { + if ($_SERVER['REQUEST_METHOD'] === 'GET') { + show_track6_form($if); + } elseif ($_SERVER['REQUEST_METHOD'] === 'POST') { + process_track6_form($if); + } + exit; +} +/* default form processing */ + $ifcfgip = $config['interfaces'][$if]['ipaddrv6']; $ifcfgsn = $config['interfaces'][$if]['subnetv6']; diff --git a/src/www/services_router_advertisements.php b/src/www/services_router_advertisements.php index 4ab69c0c5..7c951823d 100644 --- a/src/www/services_router_advertisements.php +++ b/src/www/services_router_advertisements.php @@ -2,7 +2,7 @@ /* * Copyright (C) 2016-2022 Franco Fichtner - * Copyright (C) 2014-2016 Deciso B.V. + * Copyright (C) 2014-2025 Deciso B.V. * Copyright (C) 2003-2004 Manuel Kasper * Copyright (C) 2010 Seth Mos * All rights reserved. @@ -37,6 +37,108 @@ function val_int_in_range($value, $min, $max) { return (((string)(int)$value) == $value) && $value >= $min && $value <= $max; } +function show_track6_form($if) +{ + global $config; + $service_hook = 'radvd'; + include("head.inc"); + include("fbegin.inc"); + + $ra_label = gettext('Router Advertisements'); + $save_btn_text = html_safe(gettext('Save')); + + if (!empty($config['dhcpdv6']) && !empty($config['dhcpdv6'][$if]) && isset($config['dhcpdv6'][$if]['ramode']) && $config['dhcpdv6'][$if]['ramode'] == 'disabled') { + /* disabled */ + $options = "\n"; + $options .= ""; + } else { + $options = "\n"; + $options .= ""; + } + + echo << +
+
+
+
+
+
+ + + + + + + + + + + + + +
+
+
$ra_label + +
  + + +
+
+
+
+
+
+
+ + EOD; + + include("foot.inc"); +} + +function process_track6_form($if) +{ + $dhcpdv6cfg = &config_read_array('dhcpdv6'); + $this_server = []; + if (isset($dhcpdv6cfg[$if]) && isset($dhcpdv6cfg[$if]['enable'])) { + /* keep enable for dhcpv6 so we can use this field to disable the service when in tracking mode */ + $this_server['enable'] = $dhcpdv6cfg[$if]['enable']; + } + if (!empty($_POST['ramode'])) { + $this_server['ramode'] = 'disabled'; + } + $dhcpdv6cfg[$if] = $this_server; + write_config(); + radvd_configure_do(); + header(url_safe('Location: /services_router_advertisements.php?if=%s', array($if))); +} + +$if = null; +if (!empty($_REQUEST['if']) && !empty($config['interfaces'][$_REQUEST['if']])) { + $if = $_REQUEST['if']; +} else { + /* if no interface is provided this invoke is invalid */ + header(url_safe('Location: /index.php')); + exit; +} + +/** + * XXX: In case of tracking, show different form and only handle on/off options. + * this code injection is intended to keep changes as minimal as possible and avoid regressions on existing isc-dhcp6 installs, + * while showing current state for tracking interfaces. + **/ +if (!empty($config['interfaces'][$if]) && !empty($config['interfaces'][$if]['track6-interface']) && !isset($config['interfaces'][$if]['dhcpd6track6allowoverride'])) { + if ($_SERVER['REQUEST_METHOD'] === 'GET') { + show_track6_form($if); + } elseif ($_SERVER['REQUEST_METHOD'] === 'POST') { + process_track6_form($if); + } + exit; +} +/* default form processing */ + + $advanced_options = [ 'AdvDefaultLifetime', 'AdvValidLifetime', @@ -50,14 +152,6 @@ $advanced_options = [ ]; if ($_SERVER['REQUEST_METHOD'] === 'GET') { - if (!empty($_GET['if']) && !empty($config['interfaces'][$_GET['if']])) { - $if = $_GET['if']; - } else { - /* if no interface is provided this invoke is invalid */ - header(url_safe('Location: /index.php')); - exit; - } - $pconfig = array(); $config_copy_fieldsnames = array('ramode', 'rapriority', 'rainterface', 'ramininterval', 'ramaxinterval', 'radomainsearchlist'); $config_copy_fieldsnames = array_merge($advanced_options, $config_copy_fieldsnames); @@ -93,10 +187,6 @@ if ($_SERVER['REQUEST_METHOD'] === 'GET') { $pconfig['raroutes'] = array(); } } elseif ($_SERVER['REQUEST_METHOD'] === 'POST') { - if (!empty($_POST['if']) && !empty($config['interfaces'][$_POST['if']])) { - $if = $_POST['if']; - } - /* input validation */ $input_errors = array(); $pconfig = $_POST;