441 |
441 |
}
|
442 |
442 |
|
443 |
443 |
/**
|
444 |
|
* Loops through each gateway group and kills states for lower-priority-tier gateways.
|
445 |
|
* Supports the same gateway at different tiers in different gateway groups.
|
|
444 |
* Looks for the highest priority failover tier that is currently online in a
|
|
445 |
* failover gateway group. A failover tier is any tier number greater than the
|
|
446 |
* lowest configured tier number. A failover gateway group must consist of at
|
|
447 |
* least two different tiers.
|
|
448 |
*
|
|
449 |
* @param string $gateway_group Name of an existing failover gateway group
|
|
450 |
*
|
|
451 |
* @return int The tier number - returns 0 if no matches are found
|
|
452 |
* @return false Not a valid failover gateway group
|
|
453 |
*/
|
|
454 |
function get_highest_failover_tier_online(?string $gateway_group): int|false {
|
|
455 |
if (empty($gateway_group)) {
|
|
456 |
return false;
|
|
457 |
}
|
|
458 |
|
|
459 |
foreach (config_get_path('gateways/gateway_group', []) as $group) {
|
|
460 |
// check if this is the gateway group being queried
|
|
461 |
if ($group['name'] != $gateway_group) {
|
|
462 |
continue;
|
|
463 |
}
|
|
464 |
|
|
465 |
// make sure there are expected values and at least two gateways in the gateway group
|
|
466 |
if (!is_array($group['item']) || empty($group['item']) || (count($group['item']) < 2)) {
|
|
467 |
continue;
|
|
468 |
}
|
|
469 |
// build a list of gateways and their tiers
|
|
470 |
$group_gateways = [];
|
|
471 |
foreach ($group['item'] as $item) {
|
|
472 |
$gateway_item = explode("|", $item);
|
|
473 |
// at least a gateway name and tier is expected
|
|
474 |
if (count($gateway_item) < 2) {
|
|
475 |
continue;
|
|
476 |
}
|
|
477 |
|
|
478 |
$group_gateways[$gateway_item[0]] = intval($gateway_item[1]);
|
|
479 |
}
|
|
480 |
|
|
481 |
// make sure it's a failover group by checking for different tiers
|
|
482 |
$group_tiers = array_unique(array_values($group_gateways));
|
|
483 |
if (count($group_tiers) < 2) {
|
|
484 |
break;
|
|
485 |
}
|
|
486 |
|
|
487 |
if (!isset($gateways_states)) {
|
|
488 |
// get a list of all relevant gateway states
|
|
489 |
$gateways_states = return_gateways_status(true);
|
|
490 |
}
|
|
491 |
|
|
492 |
$highest_tier = min($group_tiers);
|
|
493 |
// remove offline gateways
|
|
494 |
foreach (array_keys($group_gateways) as $gateway_name) {
|
|
495 |
if ($gateways_states[$gateway_name]['status'] != 'online') {
|
|
496 |
unset($group_gateways[$gateway_name]);
|
|
497 |
}
|
|
498 |
}
|
|
499 |
|
|
500 |
// no online gateways, or highest online is highest tier
|
|
501 |
$highest_online_tier = min(array_values($group_gateways));
|
|
502 |
if (empty($group_gateways) || ($highest_online_tier == $highest_tier)) {
|
|
503 |
return 0;
|
|
504 |
}
|
|
505 |
|
|
506 |
// get the highest-priority failover tier of online gateways
|
|
507 |
return min(array_values($group_gateways));
|
|
508 |
}
|
|
509 |
|
|
510 |
return false;
|
|
511 |
}
|
|
512 |
|
|
513 |
/**
|
|
514 |
* Loops through each gateway group and kills states for lower-priority gateways.
|
|
515 |
* Supports the same gateway at different tiers in different gateway groups. A
|
|
516 |
* failover gateway group name may be provided to restrict the state removal.
|
|
517 |
*
|
|
518 |
* @param string $gateway_name Gateway group name to remove failover states for
|
446 |
519 |
*/
|
447 |
|
function remove_failover_states(): void {
|
|
520 |
function remove_failover_states(?string $gateway_name = null): void {
|
|
521 |
// get the global default for gateway groups
|
|
522 |
$keep_failover_states = config_path_enabled('system', 'keep_failover_states');
|
|
523 |
|
448 |
524 |
// if killing states from the firewall itself, check for default failover gateway groups
|
449 |
525 |
$remove_states_default = config_get_path('system/remove_failover_states_default');
|
450 |
526 |
if ($remove_states_default) {
|
... | ... | |
462 |
538 |
|
463 |
539 |
foreach (config_get_path('gateways/gateway_group', []) as $group) {
|
464 |
540 |
// skip if states don't need to be killed for this gateway group
|
465 |
|
if (!$remove_states_default && ($group['keep_failover_states'] == 'keep')) {
|
|
541 |
if ((!empty($gateway_name) && ($group['name'] != $gateway_name)) ||
|
|
542 |
($keep_failover_states && ($group['keep_failover_states'] != 'kill'))) {
|
466 |
543 |
continue;
|
467 |
544 |
}
|
468 |
545 |
|
... | ... | |
470 |
547 |
if (!is_array($group['item']) || empty($group['item']) || (count($group['item']) < 2)) {
|
471 |
548 |
continue;
|
472 |
549 |
}
|
473 |
|
// build a list of gateway groups with gateway interfaces and tiers
|
474 |
|
$gateway_groups = [];
|
|
550 |
// build a list gateway interfaces and tiers
|
|
551 |
$gateway_interfaces = [];
|
475 |
552 |
foreach ($group['item'] as $item) {
|
476 |
553 |
$gateway_item = explode("|", $item);
|
477 |
554 |
// at least a gateway name and tier is expected
|
... | ... | |
484 |
561 |
$gateways_states = return_gateways_status(true);
|
485 |
562 |
}
|
486 |
563 |
|
487 |
|
// only get online gateways
|
488 |
|
if ($gateways_states[$gateway_item[0]]['status'] == 'online') {
|
489 |
|
if (!isset($all_gateways)) {
|
490 |
|
// get a list of all relevant gateways
|
491 |
|
$all_gateways = get_gateways();
|
492 |
|
}
|
|
564 |
// only process online gateways
|
|
565 |
if ($gateways_states[$gateway_item[0]]['status'] != 'online') {
|
|
566 |
continue;
|
|
567 |
}
|
493 |
568 |
|
494 |
|
// get the gateway's interface and tier
|
495 |
|
$gateway_groups[$group['name']][$all_gateways[$gateway_item[0]]['interface']] = intval($gateway_item[1]);
|
|
569 |
if (!isset($all_gateways)) {
|
|
570 |
// get a list of all relevant gateways
|
|
571 |
$all_gateways = get_gateways();
|
496 |
572 |
}
|
497 |
|
}
|
498 |
573 |
|
499 |
|
// make sure there are at least two gateways in the gateway group being processed
|
500 |
|
if (empty($gateway_groups[$group['name']]) || (count($gateway_groups[$group['name']]) < 2)) {
|
501 |
|
continue;
|
|
574 |
// get the gateway's interface and tier
|
|
575 |
$gateway_interfaces[$all_gateways[$gateway_item[0]]['interface']] = intval($gateway_item[1]);
|
502 |
576 |
}
|
503 |
577 |
|
504 |
|
// get a list of all assigned interfaces
|
505 |
|
if (!isset($all_interfaces)) {
|
506 |
|
require_once("interfaces_fast.inc");
|
507 |
|
$all_interfaces = convert_real_interface_to_friendly_interface_name_fast();
|
|
578 |
// make sure there are at least two online gateways (interfaces) in this gateway group
|
|
579 |
if (empty($gateway_interfaces) || (count($gateway_interfaces) < 2)) {
|
|
580 |
continue;
|
508 |
581 |
}
|
509 |
582 |
|
510 |
|
// get the interfaces of the default gateway group
|
|
583 |
// build a list of interfaces, used to kill states from the firewall itself
|
511 |
584 |
if ($remove_states_default && array_key_exists($group['name'], $default_gateways)) {
|
512 |
|
$default_gateways[$group['name']] = $gateway_groups[$group['name']];
|
513 |
|
}
|
|
585 |
// get the interfaces of the default gateway group
|
|
586 |
$default_gateways[$group['name']] = $gateway_interfaces;
|
514 |
587 |
|
515 |
|
// build a list of interfaces to kill states on
|
516 |
|
$interfaces_to_kill = $all_interfaces;
|
517 |
|
foreach (array_keys($gateway_groups[$group['name']], min(array_values($gateway_groups[$group['name']]))) as $gateway_interface) {
|
518 |
|
// exclude the interface of the highest tier gateway(s)
|
519 |
|
unset($interfaces_to_kill[$gateway_interface]);
|
520 |
|
if ($remove_states_default) {
|
|
588 |
// for states from the firewall itself (when the default gateway is set to a failover gateway group)
|
|
589 |
foreach (array_keys($gateway_interfaces, min(array_values($gateway_interfaces))) as $gateway_interface) {
|
|
590 |
// exclude the interface of the highest priority gateway(s)
|
521 |
591 |
unset($default_gateways[$group['name']][$gateway_interface]);
|
522 |
592 |
}
|
523 |
|
}
|
524 |
593 |
|
525 |
|
// if all states are to be killed for this gateway group, skip only killing policy routing states
|
526 |
|
if ($remove_states_default && array_key_exists($group['name'], $default_gateways)) {
|
|
594 |
// all states are to be killed for this gateway group, skip killing only policy routing states
|
527 |
595 |
continue;
|
528 |
596 |
}
|
529 |
597 |
|
530 |
|
foreach (array_keys($interfaces_to_kill) as $interface) {
|
531 |
|
// specify the interface to avoid killing states for the higher-tier gateway(s)
|
532 |
|
mwexec_bg('/sbin/pfctl -M -i ' . escapeshellarg($interface) . ' -k label -k ' . escapeshellarg("gw:{$group['name']}") . ' -q');
|
|
598 |
// build a list of tiers to kill policy routing states for
|
|
599 |
$tiers_to_kill = array_unique(array_values($gateway_interfaces));
|
|
600 |
// avoid killing states for the higher-tier gateway(s)
|
|
601 |
$tiers_to_kill = array_diff($tiers_to_kill, [min($tiers_to_kill)]);
|
|
602 |
foreach ($tiers_to_kill as $tier) {
|
|
603 |
mwexec_bg('/sbin/pfctl -M -k label -k ' . escapeshellarg("gw{$tier}:{$group['name']}") . ' -q');
|
533 |
604 |
}
|
534 |
605 |
}
|
535 |
606 |
|
Remove failover states using only the gateway label
Interfaces were previously specified since the inbound state needs to
be killed (due to route-to) for the connection to restart on the
preferred gateway. This interrupts connections already established on
the preferred gateway. To avoid that interruption, the interface can be
omitted by adding the current gateway tier to the gateway label.
States retain the labels from rules that created them even after the
ruleset changes.