Project

General

Profile

« Previous | Next » 

Revision bedd340c

Added by Marcos M over 1 year ago

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.

View differences:

src/etc/inc/filter.inc
3432 3432
	/* if user has selected a custom gateway, lets work with it */
3433 3433
	else if ($rule['gateway'] <> "" && $type == "pass") {
3434 3434
		if (isset($GatewaysList[$rule['gateway']])) {
3435
			/* Check if the rule uses a failover gateway group that is currently
3436
			 * in a failover state. If so, add the current tier to use for
3437
			 * state killing on gateway recovery */
3438
			$gwlabel_prefix = get_highest_failover_tier_online($rule['gateway']);
3439
			$gwlabel_prefix = empty($gwlabel_prefix) ? 'gw:' : "gw{$gwlabel_prefix}:";
3435 3440
			/* Add the load balanced gateways */
3436 3441
			$aline['route'] = " \$GW{$rule['gateway']} ";
3437
			$aline['gwlabel'] = "label \"gw:{$rule['gateway']}\"";
3442
			$aline['gwlabel'] = "label \"{$gwlabel_prefix}{$rule['gateway']}\"";
3438 3443
		} else if (config_path_enabled('system','skip_rules_gw_down')) {
3439 3444
			return "# rule " . $rule['descr'] . " disabled because gateway " . $rule['gateway'] . " is down ";
3440 3445
		} else {
src/etc/inc/gwlb.inc
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

  
src/usr/local/www/status_gateway_groups.php
39 39

  
40 40
if ($_POST['act'] == 'killgw') {
41 41
	if (!empty($_POST['gwname'])) {
42
		mwexec("/sbin/pfctl -k label -k " . escapeshellarg("gw:{$_POST['gwname']}"));
42
		remove_failover_states($_POST['gwname']);
43 43
	} elseif (!empty($_POST['gwip']) && is_ipaddr($_POST['gwip'])) {
44 44
		list($ipaddr, $scope) = explode('%', $_POST['gwip']);
45 45
		mwexec("/sbin/pfctl -k gateway -k " . escapeshellarg($ipaddr));
src/usr/local/www/system_gateway_groups_edit.php
314 314
	]
315 315
))->setHelp('%2$sKeep states on gateway recovery%3$s: states for this gateway ' .
316 316
	'group are unaffected.%1$s%2$sKill states on gateway recovery%3$s: kill  ' .
317
	'policy routing states for lower-priority gateways.',
317
	'policy routing states for lower-priority gateways.%1$sNote: gateway ' .
318
	'priority changes may not affect states created before those changes.',
318 319
	'<br/>', '<strong>', '</strong>');
319 320

  
320 321
$section->addInput(new Form_Select(

Also available in: Unified diff