router advertisements: support IP aliases with and without VHID; closes #5185

Deal with case (2) and (3) in the lightest way possible.  This requires
fixes and features for interface-related code, but it's a good field test.  ;)

The static mode isn't really a good construct and will be moved to a checkbox
in the next commit.
This commit is contained in:
Franco Fichtner 2021-11-18 13:37:50 +01:00
parent 65178b937c
commit a47949faaa
3 changed files with 57 additions and 34 deletions

View File

@ -3971,6 +3971,10 @@ function ip_in_interface_alias_subnet($interface, $ipalias)
function get_interface_ip($interface = 'wan', $ifconfig_details = null)
{
if (is_ipaddrv4($interface)) {
return $interface;
}
if (strstr($interface, '_vip')) {
foreach (config_read_array('virtualip', 'vip') as $vip) {
if ($vip['mode'] == 'carp') {
@ -3988,6 +3992,10 @@ function get_interface_ip($interface = 'wan', $ifconfig_details = null)
function get_interface_ipv6($interface = 'wan', $ifconfig_details = null)
{
if (is_ipaddrv6($interface)) {
return $interface;
}
if (strstr($interface, '_vip')) {
foreach (config_read_array('virtualip', 'vip') as $vip) {
if ($vip['mode'] == 'carp') {
@ -4580,14 +4588,14 @@ function interfaces_addresses($interfaces, $as_subnet = false, $ifconfig_details
}
if ($info['family'] == 'inet' && strpos($vip['subnet'], ':') === false) {
$info['alias'] = $vip['subnet'] == $info['address'];
$info['alias'] = $info['alias'] || $vip['subnet'] == $info['address'];
} elseif ($info['family'] == 'inet6' && strpos($vip['subnet'], ':') !== false) {
/*
* Since we do not know what subnet value was given by user
* uncompress/compress to match correctly compressed system
* value.
*/
$info['alias'] = Net_IPv6::compress(Net_IPv6::uncompress($vip['subnet'])) == $info['address'];
$info['alias'] = $info['alias'] || Net_IPv6::compress(Net_IPv6::uncompress($vip['subnet'])) == $info['address'];
}
if ($info['alias'] && !empty($vip['nobind'])) {

View File

@ -235,10 +235,11 @@ function dhcpd_radvd_configure($verbose = false, $blacklist = array())
continue;
}
/*
* Check if we need to listen on a CARP interface. A CARP setup will try
* to leave the clients configured: RemoveRoute off / DeprecatePrefix off
*/
/* XXX static mode is a little iffy: we can't set it on a non-VHID VIP and should migrate to a checkbox */
$static_mode = !empty($dhcpv6ifconf['rainterface']) && $dhcpv6ifconf['rainterface'] == 'static';
$carp_mode = false;
$src_addr = false;
$ifcfgipv6 = get_interface_ipv6(!empty($dhcpv6ifconf['rainterface']) && $dhcpv6ifconf['rainterface'] != 'static' ? $dhcpv6ifconf['rainterface'] : $dhcpv6if);
if (!is_ipaddrv6($ifcfgipv6) && !isset($config['interfaces'][$dhcpv6if]['dhcpd6track6allowoverride'])) {
continue;
@ -308,22 +309,9 @@ function dhcpd_radvd_configure($verbose = false, $blacklist = array())
$radvdconf .= "\tAdvDefaultLifetime 0;\n";
}
/*
* Only a CARP VIP can inject a link-local address and it
* means we need to force that address into the RA message
* for failover to work.
*/
if (is_linklocal($ifcfgipv6)) {
$radvdconf .= "\tAdvRASrcAddress {\n";
$radvdconf .= "\t\t{$ifcfgipv6};\n";
$radvdconf .= "\t};\n";
/* to avoid wrong MAC being stuck in failover case only */
$radvdconf .= "\tAdvSourceLLAddress off;\n";
}
$stanzas = [];
$stanzas = array();
list ($ifcfgipv6, $networkv6) = interfaces_primary_address6($dhcpv6if, $ifconfig_details);
list ($unused, $networkv6) = interfaces_primary_address6($dhcpv6if, $ifconfig_details);
if (is_subnetv6($networkv6)) {
$stanzas[] = $networkv6;
}
@ -333,7 +321,15 @@ function dhcpd_radvd_configure($verbose = false, $blacklist = array())
continue;
}
if (is_linklocal($vip['subnet']) || !empty($vip['nobind'])) {
if (is_linklocal($vip['subnet'])) {
if ($ifcfgipv6 == $vip['subnet']) {
$carp_mode = !empty($vip['vhid']);
$src_addr = true;
}
continue;
}
if (!empty($vip['nobind'])) {
continue;
}
@ -341,12 +337,24 @@ function dhcpd_radvd_configure($verbose = false, $blacklist = array())
$stanzas[] = "{$subnetv6}/{$vip['subnet_bits']}";
}
if ($src_addr) {
/* inject configured link-local address into the RA message */
$radvdconf .= "\tAdvRASrcAddress {\n";
$radvdconf .= "\t\t{$ifcfgipv6};\n";
$radvdconf .= "\t};\n";
}
if ($carp_mode) {
/* to avoid wrong MAC being stuck during failover */
$radvdconf .= "\tAdvSourceLLAddress off;\n";
}
/* VIPs may duplicate readings from system */
$stanzas = array_unique($stanzas);
foreach ($stanzas as $stanza) {
$radvdconf .= "\tprefix {$stanza} {\n";
$radvdconf .= "\t\tDeprecatePrefix " . (!empty($dhcpv6ifconf['rainterface']) ? "off" : "on") . ";\n";
$radvdconf .= "\t\tDeprecatePrefix " . ($carp_mode || $static_mode ? "off" : "on") . ";\n";
switch ($dhcpv6ifconf['ramode']) {
case 'managed':
$radvdconf .= "\t\tAdvOnLink on;\n";
@ -377,7 +385,7 @@ function dhcpd_radvd_configure($verbose = false, $blacklist = array())
if (!empty($dhcpv6ifconf['raroutes'])) {
foreach (explode(',', $dhcpv6ifconf['raroutes']) as $raroute) {
$radvdconf .= "\troute {$raroute} {\n";
if (!empty($dhcpv6ifconf['rainterface'])) {
if ($carp_mode || $static_mode) {
$radvdconf .= "\t\tRemoveRoute off;\n";
}
if (!empty($dhcpv6ifconf['AdvRouteLifetime'])) {

View File

@ -309,22 +309,29 @@ include("head.inc");
</tr>
<?php
$carplist = get_configured_carp_interface_list();
$carplistif = array();
$friendlyif = convert_friendly_interface_to_friendly_descr($if);
if (count($carplist) > 0) {
foreach ($carplist as $ifname => $vip) {
if ((preg_match("/^{$if}_/", $ifname)) && (is_ipaddrv6($vip)))
$carplistif[$ifname] = $vip;
$aliaslist = get_configured_ip_aliases_list();
$carplistif = [];
$ailiaslistif = [];
foreach ($carplist as $ifname => $vip) {
if ((preg_match("/^{$if}_/", $ifname)) && (is_linklocal($vip))) {
$carplistif[$ifname] = convert_friendly_interface_to_friendly_descr($ifname);
}
}
foreach ($aliaslist as $vip => $ifname) {
if ($ifname == $if && (is_linklocal($vip)))
$aliaslistif[$vip] = get_vip_descr($vip) . ' (' . $vip . ')';
} ?>
<tr>
<td><a id="help_for_rainterface" href="#" class="showhelp"><i class="fa fa-info-circle"></i></a> <?=gettext("RA Interface");?></td>
<td>
<select name="rainterface" id="rainterface">
<option value="" <?= empty($pconfig['rainterface']) ? 'selected="selected"' : '' ?>><?= $friendlyif . " (" . gettext('dynamic') . ")" ?></option>
<option value="static" <?= $pconfig['rainterface'] == 'static' ? 'selected="selected"' : '' ?>><?= $friendlyif . " (" . gettext('static') . ")" ?></option>
<?php foreach ($carplistif as $ifname => $vip): ?>
<option value="<?= html_safe($ifname) ?>" <?= $pconfig['rainterface'] == $ifname ? 'selected="selected"' : '' ?>><?= convert_friendly_interface_to_friendly_descr($ifname) ?></option>
<option value="" <?= empty($pconfig['rainterface']) ? 'selected="selected"' : '' ?>><?= gettext('dynamic') ?></option>
<option value="static" <?= $pconfig['rainterface'] == 'static' ? 'selected="selected"' : '' ?>><?= gettext('static') ?></option>
<?php foreach ($carplistif as $ifname => $descr): ?>
<option value="<?= html_safe($ifname) ?>" <?= $pconfig['rainterface'] == $ifname ? 'selected="selected"' : '' ?>><?= $descr ?></option>
<?php endforeach ?>
<?php foreach ($aliaslistif as $vip => $descr): ?>
<option value="<?= html_safe($vip) ?>" <?= $pconfig['rainterface'] == $vip ? 'selected="selected"' : '' ?>><?= $descr ?></option>
<?php endforeach ?>
</select>
<div class="hidden" data-for="help_for_rainterface">