diff --git a/src/opnsense/mvc/app/controllers/OPNsense/Interfaces/Api/VipSettingsController.php b/src/opnsense/mvc/app/controllers/OPNsense/Interfaces/Api/VipSettingsController.php index 84ba8edd3..9ee379bdf 100644 --- a/src/opnsense/mvc/app/controllers/OPNsense/Interfaces/Api/VipSettingsController.php +++ b/src/opnsense/mvc/app/controllers/OPNsense/Interfaces/Api/VipSettingsController.php @@ -28,10 +28,11 @@ namespace OPNsense\Interfaces\Api; +use OPNsense\Base\ApiMutableModelControllerBase; +use OPNsense\Base\UserException; use OPNsense\Core\Backend; use OPNsense\Core\Config; -use OPNsense\Base\UserException; -use OPNsense\Base\ApiMutableModelControllerBase; +use OPNsense\Firewall\Util; class VipSettingsController extends ApiMutableModelControllerBase { @@ -149,7 +150,11 @@ class VipSettingsController extends ApiMutableModelControllerBase } } if ($node != null && ($post_subnet != (string)$node->subnet || $post_interface != (string)$node->interface)) { - file_put_contents("/tmp/delete_vip_{$uuid}.todo", (string)$node->subnet . "\n", FILE_APPEND); + $addr = (string)$node->subnet; + if (Util::isLinkLocal($addr)) { + $addr .= "@{$node->interface}"; + } + file_put_contents("/tmp/delete_vip_{$uuid}.todo", $addr . PHP_EOL, FILE_APPEND); } return $this->handleFormValidations($this->setBase('vip', 'vip', $uuid, $this->getVipOverlay())); @@ -184,7 +189,11 @@ class VipSettingsController extends ApiMutableModelControllerBase } $response = $this->delBase("vip", $uuid); if (($response['result'] ?? '') == 'deleted') { - file_put_contents("/tmp/delete_vip_{$uuid}.todo", (string)$node->subnet . "\n", FILE_APPEND); + $addr = (string)$node->subnet; + if (Util::isLinkLocal($addr)) { + $addr .= "@{$node->interface}"; + } + file_put_contents("/tmp/delete_vip_{$uuid}.todo", $addr . PHP_EOL, FILE_APPEND); } return $response; } diff --git a/src/opnsense/scripts/interfaces/reconfigure_vips.php b/src/opnsense/scripts/interfaces/reconfigure_vips.php index 3a49e14b1..34ef97fe7 100755 --- a/src/opnsense/scripts/interfaces/reconfigure_vips.php +++ b/src/opnsense/scripts/interfaces/reconfigure_vips.php @@ -38,7 +38,11 @@ foreach (legacy_interfaces_details() as $ifname => $ifcnf) { foreach (['ipv4', 'ipv6'] as $proto) { if (!empty($ifcnf[$proto])) { foreach ($ifcnf[$proto] as $address) { - $addresses[$address['ipaddr']] = [ + $key = $address['ipaddr']; + if ($proto == 'ipv6' && $address['link-local']) { + $key .= '%' . $ifname; + } + $addresses[$key] = [ 'subnetbits' => $address['subnetbits'], 'if' => $ifname, 'vhid' => $address['vhid'] ?? '', @@ -48,8 +52,8 @@ foreach (legacy_interfaces_details() as $ifname => $ifcnf) { if (!empty($address['vhid'])) { foreach ($ifcnf['carp'] as $vhid) { if ($vhid['vhid'] == $address['vhid']) { - $addresses[$address['ipaddr']]['advbase'] = $vhid['advbase']; - $addresses[$address['ipaddr']]['advskew'] = $vhid['advskew']; + $addresses[$key]['advbase'] = $vhid['advbase']; + $addresses[$key]['advskew'] = $vhid['advskew']; } } } @@ -61,6 +65,12 @@ foreach (legacy_interfaces_details() as $ifname => $ifcnf) { // remove deleted vips foreach (glob("/tmp/delete_vip_*.todo") as $filename) { foreach (array_unique(explode("\n", trim(file_get_contents($filename)))) as $address) { + /* '@' designates an IPv6 link-local scope, but not on network device */ + if (strpos($address, '@') !== false) { + list($address, $interface) = explode('@', $address); + /* translate to what ifconfig will understand */ + $address .= '%' . get_real_interface($interface, 'inet6'); + } if (isset($addresses[$address])) { legacy_interface_deladdress($addresses[$address]['if'], $address, is_ipaddrv6($address) ? 6 : 4); } else { @@ -83,6 +93,9 @@ if (!empty($config['virtualip']['vip'])) { if (!empty($vipent['interface']) && !empty($interfaces[$vipent['interface']])) { $if = $interfaces[$vipent['interface']]; $subnet = $vipent['subnet']; + if (is_linklocal($subnet)) { + $subnet .= '%' . get_real_interface($if, 'inet6'); + } $subnet_bits = $vipent['subnet_bits']; $vhid = $vipent['vhid'] ?? ''; $advbase = !empty($vipent['vhid']) ? $vipent['advbase'] : '';