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;