From cbd4f266df8778fef9b1b7efbf9bd16147534f8b Mon Sep 17 00:00:00 2001 From: Ad Schellevis Date: Sat, 1 Jun 2024 16:28:19 +0200 Subject: [PATCH] Interfaces: Virtual IPs: Settings - add peer/peer6 options (available as of FreeBSD 14.x) closes https://github.com/opnsense/core/issues/7486 Currently it doesn't seem to be possible to reliably set both peer and peer6, but since in the kernel these are organized as two fields on the same vhid we prevent both being set with a validation. If we would only offer a "peer" field matching the ip protocol, we might have difficulties in the future when both can be provided at the same time. This commit parses the ifconfig output and offers the result in the interfaces/overview page (tooltip like freq settings). --- src/etc/inc/interfaces.inc | 20 ++++++++++++++-- src/etc/inc/interfaces.lib.inc | 9 +++++++- .../Interfaces/Api/OverviewController.php | 2 ++ .../OPNsense/Interfaces/forms/dialogVip.xml | 22 ++++++++++++++++++ .../app/library/OPNsense/Firewall/Rule.php | 2 +- .../app/models/OPNsense/Interfaces/Vip.php | 19 ++++++++++++++- .../app/models/OPNsense/Interfaces/Vip.xml | 8 +++++++ .../views/OPNsense/Interface/overview.volt | 14 +++++++---- .../scripts/interfaces/reconfigure_vips.php | 23 ++++++++++++++++--- 9 files changed, 106 insertions(+), 13 deletions(-) diff --git a/src/etc/inc/interfaces.inc b/src/etc/inc/interfaces.inc index 9f9e0ef23..0644a2298 100644 --- a/src/etc/inc/interfaces.inc +++ b/src/etc/inc/interfaces.inc @@ -1432,8 +1432,8 @@ function interface_carp_configure($vip) $realif = get_real_interface($vip['interface']); - $vip_password = $vip['password']; - $vip_password = escapeshellarg(addslashes(str_replace(" ", "", $vip_password))); + $vip_password = escapeshellarg(addslashes(str_replace(" ", "", $vip['password']))); + $password = ''; if ($vip['password'] != "") { $password = " pass {$vip_password}"; } @@ -1444,6 +1444,16 @@ function interface_carp_configure($vip) } $advskew = "advskew " . escapeshellarg($vip['advskew']); + if (empty($vip['peer']) || $vip['peer'] == '224.0.0.18') { + $peercfg = ' mcast '; + } else { + $peercfg = ' peer ' . escapeshellarg($vip['peer']) . ' '; + } + if (empty($vip['peer6']) || $vip['peer6'] == 'ff02::12') { + $peercfg .= ' mcast6 '; + } else { + $peercfg .= ' peer6 ' . escapeshellarg($vip['peer6']) . ' '; + } mwexec("/sbin/ifconfig {$realif} vhid " . escapeshellarg($vip['vhid']) . " {$advskew} {$advbase} {$password}"); @@ -1453,6 +1463,12 @@ function interface_carp_configure($vip) } elseif (is_ipaddrv6($vip['subnet'])) { mwexec("/sbin/ifconfig {$realif} inet6 " . escapeshellarg($vip['subnet']) . " prefixlen " . escapeshellarg($vip['subnet_bits']) . " alias vhid " . escapeshellarg($vip['vhid'])); } + /** + * XXX this is pretty flacky. + * If we configure peer[6] during setup, values won't stick, they appear to be flushed when + * the initial address is set. + */ + mwexec("/sbin/ifconfig {$realif} vhid " . escapeshellarg($vip['vhid']) . " {$peercfg}"); } function _interfaces_wlan_clone($realif, $wlcfg) diff --git a/src/etc/inc/interfaces.lib.inc b/src/etc/inc/interfaces.lib.inc index ade43e044..fda67e9f9 100644 --- a/src/etc/inc/interfaces.lib.inc +++ b/src/etc/inc/interfaces.lib.inc @@ -448,8 +448,15 @@ function legacy_interfaces_details($intf = null) "status" => $line_parts[1], "vhid" => $line_parts[3], "advbase" => $line_parts[5], - "advskew" => $line_parts[7] + "advskew" => $line_parts[7], + "peer" => null, + "peer6" => null, ); + if (isset($ifconfig_data[$lineid + 1]) && strpos($ifconfig_data[$lineid + 1], "peer6") !== false) { + preg_match("/\Wpeer (.*)\Wpeer6 (.*)$/", $ifconfig_data[$lineid + 1], $matches); + $result[$current_interface]["carp"][$line_parts[3]]['peer'] = $matches[1]; + $result[$current_interface]["carp"][$line_parts[3]]['peer6'] = $matches[2]; + } } elseif (strpos($line, "\tvxlan") !== false) { if (empty($result[$current_interface]["vxlan"])) { $result[$current_interface]["vxlan"] = []; diff --git a/src/opnsense/mvc/app/controllers/OPNsense/Interfaces/Api/OverviewController.php b/src/opnsense/mvc/app/controllers/OPNsense/Interfaces/Api/OverviewController.php index 87ee8ad93..a0796160e 100644 --- a/src/opnsense/mvc/app/controllers/OPNsense/Interfaces/Api/OverviewController.php +++ b/src/opnsense/mvc/app/controllers/OPNsense/Interfaces/Api/OverviewController.php @@ -209,6 +209,8 @@ class OverviewController extends ApiControllerBase $entry['status'] = $carp['status']; $entry['advbase'] = $carp['advbase']; $entry['advskew'] = $carp['advskew']; + $entry['peer'] = $carp['peer']; + $entry['peer6'] = $carp['peer6']; } } } diff --git a/src/opnsense/mvc/app/controllers/OPNsense/Interfaces/forms/dialogVip.xml b/src/opnsense/mvc/app/controllers/OPNsense/Interfaces/forms/dialogVip.xml index 7b4990ef1..a771e5b97 100644 --- a/src/opnsense/mvc/app/controllers/OPNsense/Interfaces/forms/dialogVip.xml +++ b/src/opnsense/mvc/app/controllers/OPNsense/Interfaces/forms/dialogVip.xml @@ -23,6 +23,28 @@ For some interface types a gateway is required to configure an IP Alias (ppp/pppoe/tun), leave this field empty for all other interface types. true + + vip.peer + + text + + 224.0.0.18 + + Destination address to use when announcing, defaults to multicast, + but can be configured as unicast address when multicast can not be used (for example with cloud providers) + + + + vip.peer6 + + text + + ff02::12 + + Destination address to use when announcing, defaults to multicast, + but can be configured as unicast address when multicast can not be used (for example with cloud providers) + + vip.noexpand diff --git a/src/opnsense/mvc/app/library/OPNsense/Firewall/Rule.php b/src/opnsense/mvc/app/library/OPNsense/Firewall/Rule.php index 167027693..d8b418792 100644 --- a/src/opnsense/mvc/app/library/OPNsense/Firewall/Rule.php +++ b/src/opnsense/mvc/app/library/OPNsense/Firewall/Rule.php @@ -1,7 +1,7 @@ getFlatNodes() as $key => $node) { $tagName = $node->getInternalXMLTagName(); $parentNode = $node->getParentNode(); @@ -160,6 +160,23 @@ class Vip extends BaseModel ) ); } + /* XXX: ideally we shouldn't need the validations below, but when using the same vhid for + * ipv4 and ipv6 one will always flip back to multicast */ + if (strpos($subnet, ':') === false && !empty((string)$node->peer6)) { + $messages->appendMessage( + new Message( + gettext('An (unicast) address can only be configured for the same protocol family.'), + $key . ".peer6" + ) + ); + } elseif (strpos($subnet, ':') !== false && !empty((string)$node->peer)) { + $messages->appendMessage( + new Message( + gettext('An (unicast) address can only be configured for the same protocol family.'), + $key . ".peer" + ) + ); + } } elseif ( (string)$node->mode == 'ipalias' && !empty((string)$node->vhid) && ( diff --git a/src/opnsense/mvc/app/models/OPNsense/Interfaces/Vip.xml b/src/opnsense/mvc/app/models/OPNsense/Interfaces/Vip.xml index 9269f703c..0f59e7b07 100644 --- a/src/opnsense/mvc/app/models/OPNsense/Interfaces/Vip.xml +++ b/src/opnsense/mvc/app/models/OPNsense/Interfaces/Vip.xml @@ -54,6 +54,14 @@ 254 Invalid skew value, acceptable values are 0 to 255. + + N + ipv4 + + + N + ipv6 + diff --git a/src/opnsense/mvc/app/views/OPNsense/Interface/overview.volt b/src/opnsense/mvc/app/views/OPNsense/Interface/overview.volt index b36d8143b..fb946fa0d 100644 --- a/src/opnsense/mvc/app/views/OPNsense/Interface/overview.volt +++ b/src/opnsense/mvc/app/views/OPNsense/Interface/overview.volt @@ -74,11 +74,15 @@ obj.forEach(function (ip) { $span = $('
').text(ip['ipaddr'] + ' '); if ('vhid' in ip) { - $carp = $('').text('vhid ' + ip['vhid']); - $carp.attr('class', 'bootgrid-tooltip badge badge-pill'); + $carp = $('').text('vhid ' + ip['vhid']); + $carp.attr('class', 'badge badge-pill'); $carp.css('background-color', ip['status'] == 'MASTER' ? 'green' : 'primary'); $carp.attr('data-toggle', 'tooltip'); - $carp.attr('title', ip['status'] + ' (freq. ' + ip['advbase'] + '/' + ip['advskew'] + ')'); + let title_text = ip['status'] + ' (freq. ' + ip['advbase'] + '/' + ip['advskew'] + ')'; + if (ip['peer']) { + title_text = title_text + '
' + ip['peer'] + '
' + ip['peer6']; + } + $carp.attr('title', title_text); $span.append($carp); } $elements.append($span); @@ -207,7 +211,7 @@ } } ).on("loaded.rs.jquery.bootgrid", function (e) { - $('[data-toggle="tooltip"]').tooltip(); + $('[data-toggle="tooltip"]').tooltip({html:true}); /* attach event handler to reload buttons */ $('.interface-reload').each(function () { @@ -275,7 +279,7 @@ } $table.append($table_body); - $('[data-toggle="tooltip"]').tooltip(); + $('[data-toggle="tooltip"]').tooltip({html:true}); BootstrapDialog.show({ title: data['description']['value'], message: $table.prop('outerHTML'), diff --git a/src/opnsense/scripts/interfaces/reconfigure_vips.php b/src/opnsense/scripts/interfaces/reconfigure_vips.php index 34ef97fe7..52d802a76 100755 --- a/src/opnsense/scripts/interfaces/reconfigure_vips.php +++ b/src/opnsense/scripts/interfaces/reconfigure_vips.php @@ -2,7 +2,7 @@ $ifcnf) { 'if' => $ifname, 'vhid' => $address['vhid'] ?? '', 'advbase' => '', - 'advskew' => '' + 'advskew' => '', + 'peer' => '', + 'peer6' => '', ]; if (!empty($address['vhid'])) { foreach ($ifcnf['carp'] as $vhid) { if ($vhid['vhid'] == $address['vhid']) { $addresses[$key]['advbase'] = $vhid['advbase']; $addresses[$key]['advskew'] = $vhid['advskew']; + $addresses[$key]['peer'] = $vhid['peer']; + $addresses[$key]['peer6'] = $vhid['peer6']; } } } @@ -100,6 +104,8 @@ if (!empty($config['virtualip']['vip'])) { $vhid = $vipent['vhid'] ?? ''; $advbase = !empty($vipent['vhid']) ? $vipent['advbase'] : ''; $advskew = !empty($vipent['vhid']) ? $vipent['advskew'] : ''; + $peer = !empty($vipent['peer']) ? $vipent['peer'] : '224.0.0.18'; + $peer6 = !empty($vipent['peer6']) ? $vipent['peer6'] : 'ff02::12'; if ($vipent['mode'] == 'proxyarp') { $anyproxyarp = true; } @@ -108,13 +114,24 @@ if (!empty($config['virtualip']['vip'])) { legacy_interface_deladdress($addresses[$subnet]['if'], $subnet, is_ipaddrv6($subnet) ? 6 : 4); } continue; + } elseif ( + $vipent['mode'] == 'ipalias' && + isset($addresses[$subnet]) && + $addresses[$subnet]['subnetbits'] == $subnet_bits && + $addresses[$subnet]['if'] == $if && + $addresses[$subnet]['vhid'] == $vhid + ) { + // configured and found equal + continue; } elseif ( isset($addresses[$subnet]) && $addresses[$subnet]['subnetbits'] == $subnet_bits && $addresses[$subnet]['if'] == $if && $addresses[$subnet]['vhid'] == $vhid && $addresses[$subnet]['advbase'] == $advbase && - $addresses[$subnet]['advskew'] == $advskew + $addresses[$subnet]['advskew'] == $advskew && + $addresses[$subnet]['peer'] == $peer && + $addresses[$subnet]['peer6'] == $peer6 ) { // configured and found equal continue;