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).
This commit is contained in:
Ad Schellevis 2024-06-01 16:28:19 +02:00
parent 575f9ccaab
commit cbd4f266df
9 changed files with 106 additions and 13 deletions

View File

@ -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)

View File

@ -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"] = [];

View File

@ -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'];
}
}
}

View File

@ -23,6 +23,28 @@
<help>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.</help>
<advanced>true</advanced>
</field>
<field>
<id>vip.peer</id>
<label>Peer (ipv4)</label>
<type>text</type>
<style>mode mode_carp</style>
<hint>224.0.0.18</hint>
<help>
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)
</help>
</field>
<field>
<id>vip.peer6</id>
<label>Peer (ipv6)</label>
<type>text</type>
<style>mode mode_carp</style>
<hint>ff02::12</hint>
<help>
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)
</help>
</field>
<field>
<id>vip.noexpand</id>
<label>Disable Expansion</label>

View File

@ -1,7 +1,7 @@
<?php
/*
* Copyright (C) 2017-2023 Deciso B.V.
* Copyright (C) 2017-2024 Deciso B.V.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without

View File

@ -47,7 +47,7 @@ class Vip extends BaseModel
$vips = [];
// collect changed VIP entries
$vip_fields = ['mode', 'subnet', 'subnet_bits', 'password', 'vhid', 'interface'];
$vip_fields = ['mode', 'subnet', 'subnet_bits', 'password', 'vhid', 'interface', 'peer', 'peer6'];
foreach ($this->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) && (

View File

@ -54,6 +54,14 @@
<MaximumValue>254</MaximumValue>
<ValidationMessage>Invalid skew value, acceptable values are 0 to 255.</ValidationMessage>
</advskew>
<peer type="NetworkField">
<NetMaskAllowed>N</NetMaskAllowed>
<AddressFamily>ipv4</AddressFamily>
</peer>
<peer6 type="NetworkField">
<NetMaskAllowed>N</NetMaskAllowed>
<AddressFamily>ipv6</AddressFamily>
</peer6>
<descr type="DescriptionField"/>
</vip>
</items>

View File

@ -74,11 +74,15 @@
obj.forEach(function (ip) {
$span = $('<span></span><br/>').text(ip['ipaddr'] + ' ');
if ('vhid' in ip) {
$carp = $('<span></span>').text('vhid ' + ip['vhid']);
$carp.attr('class', 'bootgrid-tooltip badge badge-pill');
$carp = $('<span style="cursor: pointer;"></span>').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 + ' <br/> ' + ip['peer'] + ' <br/> ' + 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'),

View File

@ -2,7 +2,7 @@
<?php
/*
* Copyright (C) 2022 Deciso B.V.
* Copyright (C) 2022-2024 Deciso B.V.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@ -47,13 +47,17 @@ foreach (legacy_interfaces_details() as $ifname => $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;