From 91093f3344a09112347dc27cda33f14feb68b4aa Mon Sep 17 00:00:00 2001 From: Franco Fichtner Date: Tue, 19 May 2026 18:45:46 +0200 Subject: [PATCH] interfaces: IAID selection and prefix range reservation #7647 --- src/etc/inc/interfaces.inc | 27 ++++----- src/www/interfaces.php | 109 +++++++++++++++++++++++++++++++++++-- 2 files changed, 117 insertions(+), 19 deletions(-) diff --git a/src/etc/inc/interfaces.inc b/src/etc/inc/interfaces.inc index 8919a7da5f..1db4193e38 100644 --- a/src/etc/inc/interfaces.inc +++ b/src/etc/inc/interfaces.inc @@ -2970,7 +2970,7 @@ function interface_dhcpv6_prepare($interface, $wancfg, $cleanup = false) @file_put_contents('/var/etc/dhcp6c.conf', $dhcp6cconf); } -function DHCP6_Config_File_Basic($interface, $wancfg, $wanif, $id = 0) +function DHCP6_Config_File_Basic($interface, $wancfg, $wanif, $default_id) { $dhcp6cconf = ''; @@ -2995,7 +2995,7 @@ function DHCP6_Config_File_Basic($interface, $wancfg, $wanif, $id = 0) } $pd_conf .= " };\n"; - /* XXX $id from dhcp6_assoc_pd */ + $id = strlen($wancfg['dhcp6_assoc_pd'] ?? '') ? $wancfg['dhcp6_assoc_pd'] : $default_id; $assoc_pd[$id] = ($assoc_pd[$id] ?? '') . $pd_conf; } @@ -3010,25 +3010,26 @@ function DHCP6_Config_File_Basic($interface, $wancfg, $wanif, $id = 0) } $pd_conf .= " };\n"; - /* XXX $id from track6_assoc_pd */ + $id = strlen($lancfg['track6_assoc_pd'] ?? '') ? $lancfg['track6_assoc_pd'] : $default_id; $assoc_pd[$id] = ($assoc_pd[$id] ?? '') . $pd_conf; } } - } - ksort($assoc_pd, SORT_NUMERIC); + if (!count($assoc_pd)) { + /* for backwards compatibility always emit a default PD request */ + $assoc_pd[$default_id] = ''; + } - /* for backwards compatibility always emit this PD request */ - if (!isset($assoc_pd[$id])) { - $assoc_pd[$id] = ''; + ksort($assoc_pd, SORT_NUMERIC); } $dhcp6cconf .= "interface {$wanif} {\n"; if (!isset($wancfg['dhcp6prefixonly'])) { /* request non-temporary address */ - $dhcp6cconf .= " send ia-na {$id};\n"; + $dhcp6cconf .= " send ia-na {$default_id};\n"; } + if (is_numeric($wancfg['dhcp6-ia-pd-len'])) { /* request prefix delegation */ foreach (array_keys($assoc_pd) as $id) { @@ -3049,7 +3050,7 @@ function DHCP6_Config_File_Basic($interface, $wancfg, $wanif, $id = 0) $dhcp6cconf .= "};\n"; if (!isset($wancfg['dhcp6prefixonly'])) { - $dhcp6cconf .= "id-assoc na {$id} { };\n"; + $dhcp6cconf .= "id-assoc na {$default_id} { };\n"; } foreach ($assoc_pd as $i => $pd_conf) { @@ -3066,7 +3067,7 @@ function DHCP6_Config_File_Basic($interface, $wancfg, $wanif, $id = 0) return $dhcp6cconf; } -function DHCP6_Config_File_Advanced($interface, $wancfg, $wanif, $id = 0) +function DHCP6_Config_File_Advanced($interface, $wancfg, $wanif, $default_id) { $send_options = ""; @@ -3108,7 +3109,7 @@ function DHCP6_Config_File_Advanced($interface, $wancfg, $wanif, $id = 0) if (is_numeric($wancfg['adv_dhcp6_id_assoc_statement_address_id'])) { $id_assoc_statement_address .= "{$wancfg['adv_dhcp6_id_assoc_statement_address_id']}"; } else { - $id_assoc_statement_address .= $id; + $id_assoc_statement_address .= $default_id; } $id_assoc_statement_address .= " {\n"; @@ -3138,7 +3139,7 @@ function DHCP6_Config_File_Advanced($interface, $wancfg, $wanif, $id = 0) if (is_numeric($wancfg['adv_dhcp6_id_assoc_statement_prefix_id'])) { $id_assoc_statement_prefix .= "{$wancfg['adv_dhcp6_id_assoc_statement_prefix_id']}"; } else { - $id_assoc_statement_prefix .= $id; + $id_assoc_statement_prefix .= $default_id; } $id_assoc_statement_prefix .= " {\n"; diff --git a/src/www/interfaces.php b/src/www/interfaces.php index aaa4539863..68b82b5bfb 100644 --- a/src/www/interfaces.php +++ b/src/www/interfaces.php @@ -248,23 +248,54 @@ function validate_track6_idassoc6(&$pconfig, $if) if ($ipv6_delegation_length >= 0) { $ipv6_num_prefix_ids = pow(2, $ipv6_delegation_length); $track6_prefix_id = intval($pconfig["{$pconfig['type6']}-prefix-id--hex"], 16); + $track6_prefix_range = $pconfig["{$pconfig['type6']}_prefix_range"]; if ($track6_prefix_id < 0 || $track6_prefix_id >= $ipv6_num_prefix_ids) { $input_errors[] = gettext("You specified an IPv6 prefix ID that is out of range."); + } elseif (strlen($track6_prefix_range)) { + if (!ctype_digit($track6_prefix_range)) { + $input_errors[] = gettext("You specified an IPv6 prefix range that is not valid."); + } elseif ($track6_prefix_range < 1 || $track6_prefix_id + $track6_prefix_range > $ipv6_num_prefix_ids) { + $input_errors[] = gettext("You specified an IPv6 prefix range that is out of range."); + } } + $default_id = interface_dhcpv6_id($pconfig["{$pconfig['type6']}-interface"]); + $assoc_pd_ref = !empty($pconfig["{$pconfig['type6']}_assoc_pd"]) && ctype_digit($pconfig["{$pconfig['type6']}_assoc_pd"]) ? + $pconfig["{$pconfig['type6']}_assoc_pd"] : $default_id; foreach (link_interface_to_track6($pconfig["{$pconfig['type6']}-interface"]) as $trackif => $trackcfg) { - if ($trackif != $if && $trackcfg['track6-prefix-id'] == $track6_prefix_id) { + if ($trackif == $if) { + continue; + } + $assoc_pd_link = !empty($trackcfg['track6_assoc_pd']) ? $trackcfg['track6_assoc_pd'] : $default_id; + if ($assoc_pd_ref != $assoc_pd_ref) { + continue; + } + /* the end of non-overlapping intervals needs to specify 0 ... n-1 */ + $track6_range_end = !empty($track6_prefix_range) ? $track6_prefix_range - 1 : 0; + $track6_range_end += $track6_prefix_id; + $range_end_link = !empty($trackcfg['track6_prefix_range']) ? $trackcfg['track6_prefix_range'] - 1 : 0; + $range_end_link += $trackcfg['track6-prefix-id']; + if ($trackcfg['track6-prefix-id'] == $track6_prefix_id) { $input_errors[] = gettext('You specified an IPv6 prefix ID that is already in use.'); break; + } elseif ($trackcfg['track6-prefix-id'] <= $track6_range_end && $track6_prefix_id <= $range_end_link) { + $input_errors[] = gettext('You specified an IPv6 prefix range that is already in use.'); + break; } } if (isset($config['interfaces'][$pconfig["{$pconfig['type6']}-interface"]]['dhcp6-prefix-id'])) { - if ($config['interfaces'][$pconfig["{$pconfig['type6']}-interface"]]['dhcp6-prefix-id'] == $track6_prefix_id) { + $assoc_pd_parent = !empty($config['interfaces'][$pconfig["{$pconfig['type6']}-interface"]]['dhcp6_assoc_pd']) ? + $config['interfaces'][$pconfig["{$pconfig['type6']}-interface"]]['dhcp6_assoc_pd'] : $default_id; + if ($assoc_pd_ref == $assoc_pd_parent && $config['interfaces'][$pconfig["{$pconfig['type6']}-interface"]]['dhcp6-prefix-id'] == $track6_prefix_id) { $input_errors[] = gettext('You specified an IPv6 prefix ID that is already in use.'); } } } } + if (!empty($pconfig["{$pconfig['type6']}_assoc_pd"]) && !ctype_digit($pconfig["{$pconfig['type6']}_assoc_pd"])) { + $input_errors[] = gettext('You must enter a valid number for the IPv6 prefix association identity.'); + } + if (isset($pconfig["{$pconfig['type6']}_ifid--hex"]) && $pconfig["{$pconfig['type6']}_ifid--hex"] != '') { if (!ctype_xdigit($pconfig["{$pconfig['type6']}_ifid--hex"])) { $input_errors[] = gettext('You must enter a valid hexadecimal number for the IPv6 interface ID.'); @@ -298,6 +329,12 @@ function store_track6_idassoc6(&$new_config, &$pconfig) if (isset($pconfig["{$pconfig['type6']}_ifid--hex"]) && ctype_xdigit($pconfig["{$pconfig['type6']}_ifid--hex"])) { $new_config['track6_ifid'] = intval($pconfig["{$pconfig['type6']}_ifid--hex"], 16); } + if (!empty($pconfig["{$pconfig['type6']}_prefix_range"])) { + $new_config['track6_prefix_range'] = $pconfig["{$pconfig['type6']}_prefix_range"]; + } + if (!empty($pconfig["{$pconfig['type6']}_assoc_pd"])) { + $new_config['track6_assoc_pd'] = $pconfig["{$pconfig['type6']}_assoc_pd"]; + } if ($pconfig['type6'] == 'track6') { /* this is not needed in the new world */ $new_config['dhcpd6track6allowoverride'] = !empty($pconfig['dhcpd6track6allowoverride']); @@ -461,6 +498,7 @@ if ($_SERVER['REQUEST_METHOD'] === 'GET') { 'dhcp6_ifid', 'dhcp6_norequest_dns', 'dhcp6_rapid_commit', + 'dhcp6_assoc_pd', 'dhcp6vlanprio', 'dhcphostname', 'dhcprejectfrom', @@ -488,7 +526,9 @@ if ($_SERVER['REQUEST_METHOD'] === 'GET') { 'subnetv6', 'track6-interface', 'track6-prefix-id', + 'track6_assoc_pd', 'track6_ifid', + 'track6_prefix_range', ]; foreach ($std_copy_fieldnames as $fieldname) { $pconfig[$fieldname] = isset($a_interfaces[$if][$fieldname]) ? $a_interfaces[$if][$fieldname] : null; @@ -509,7 +549,7 @@ if ($_SERVER['REQUEST_METHOD'] === 'GET') { $pconfig['dhcpd6track6allowoverride'] = isset($a_interfaces[$if]['dhcpd6track6allowoverride']); $pconfig['dhcp6_request_dns'] = empty($pconfig['dhcp6_norequest_dns']); - foreach(['-interface', '-prefix-id', '-prefix-id--hex', '_ifid', '_ifid--hex'] as $fieldname) { + foreach(['-interface', '-prefix-id', '-prefix-id--hex', '_assoc_pd', '_ifid', '_ifid--hex', '_prefix_range'] as $fieldname) { /* only for form consistency */ $pconfig["idassoc6{$fieldname}"] = $pconfig["track6{$fieldname}"]; } @@ -747,14 +787,21 @@ if ($_SERVER['REQUEST_METHOD'] === 'GET') { $input_errors[] = gettext("You specified an IPv6 prefix ID that is out of range."); } } + $default_id = interface_dhcpv6_id($if); + $assoc_pd_ref = !empty($pconfig['dhcp6_assoc_pd']) && ctype_digit($pconfig['dhcp6_assoc_pd']) ? + $pconfig['dhcp6_assoc_pd'] : $default_id; foreach (link_interface_to_track6($pconfig['track6-interface']) as $trackif => $trackcfg) { - if ($trackcfg['track6-prefix-id'] == $dhcp6_prefix_id) { + $assoc_pd_link = !empty($trackcfg['track6_assoc_pd']) ? $trackcfg['track6_assoc_pd'] : $default_id; + if ($assoc_pd_ref == $assoc_pd_link && $trackcfg['track6-prefix-id'] == $dhcp6_prefix_id) { $input_errors[] = gettext('You specified an IPv6 prefix ID that is already in use.'); break; } } } } + if (!empty($pconfig['dhcp6_assoc_pd']) && !ctype_digit($pconfig['dhcp6_assoc_pd'])) { + $input_errors[] = gettext('You must enter a valid number for the IPv6 prefix association identity.'); + } if (isset($pconfig['dhcp6_ifid--hex']) && $pconfig['dhcp6_ifid--hex'] != '') { if (!ctype_xdigit($pconfig['dhcp6_ifid--hex'])) { $input_errors[] = gettext('You must enter a valid hexadecimal number for the IPv6 interface ID.'); @@ -1123,6 +1170,9 @@ if ($_SERVER['REQUEST_METHOD'] === 'GET') { if (!empty($pconfig['dhcp6_rapid_commit'])) { $new_config['dhcp6_rapid_commit'] = true; } + if (!empty($pconfig['dhcp6_assoc_pd'])) { + $new_config['dhcp6_assoc_pd'] = $pconfig['dhcp6_assoc_pd']; + } $new_config['adv_dhcp6_interface_statement_send_options'] = $pconfig['adv_dhcp6_interface_statement_send_options']; $new_config['adv_dhcp6_interface_statement_request_options'] = $pconfig['adv_dhcp6_interface_statement_request_options']; $new_config['adv_dhcp6_interface_statement_information_only_enable'] = $pconfig['adv_dhcp6_interface_statement_information_only_enable']; @@ -1809,7 +1859,7 @@ include("head.inc"); - + + + + + + + @@ -2607,6 +2666,16 @@ include("head.inc"); + + + + + + + + @@ -2619,6 +2688,15 @@ include("head.inc"); + + + + + + + @@ -2663,6 +2741,16 @@ include("head.inc"); + + + + + + + + @@ -2675,10 +2763,19 @@ include("head.inc"); + + + + + + + - /> + />