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;