diff --git a/src/etc/inc/plugins.inc.d/dpinger.inc b/src/etc/inc/plugins.inc.d/dpinger.inc
index 858360898..cc296da6e 100644
--- a/src/etc/inc/plugins.inc.d/dpinger.inc
+++ b/src/etc/inc/plugins.inc.d/dpinger.inc
@@ -311,6 +311,9 @@ function dpinger_status()
'monitor' => !empty($gwitem['monitor']) ? $gwitem['monitor'] : '~',
'gateway' => $gwitem['gateway'] ?? '',
'monitor_killstates' => $gwitem['monitor_killstates'] ?? '0',
+ 'monitor_killstates_priority' => $gwitem['monitor_killstates_priority'] ?? '0',
+ 'priority' => $gwitem['priority'] ?? '255',
+ 'ipprotocol' => $gwitem['ipprotocol'],
'name' => $gwname,
'stddev' => '~',
'delay' => '~',
diff --git a/src/etc/rc.syshook.d/monitor/20-recover b/src/etc/rc.syshook.d/monitor/20-recover
index 87b960206..4a48f2500 100755
--- a/src/etc/rc.syshook.d/monitor/20-recover
+++ b/src/etc/rc.syshook.d/monitor/20-recover
@@ -35,6 +35,8 @@ require_once 'interfaces.inc';
$gwnames = [];
$affected_gateways = !empty($argv[1]) ? explode(',', $argv[1]) : [];
+$metered_found_prios = ['inet' => 256, 'inet6' => 256];
+$metered_gws = ['inet' => [], 'inet6' => []];
foreach (return_gateways_status() as $status) {
if ($status['status'] == 'down') {
/* try to recover monitors stuck in down state ignoring "force_down" */
@@ -42,6 +44,23 @@ foreach (return_gateways_status() as $status) {
if (!empty($status['monitor_killstates']) && in_array($status['name'], $affected_gateways)) {
configdp_run('filter kill gateway_states', [$status['gateway']], true);
}
+ } elseif ($status['status'] != 'force_down') {
+ /* collect "metered" gateways which should be killed depending priority */
+ if (!empty($status['monitor_killstates_priority'])) {
+ $metered_gws[$status['ipprotocol']][] = $status;
+ } elseif (in_array($status['name'], $affected_gateways)) {
+ /* only trigger on "up" events to calculate highest prio */
+ $metered_found_prios[$status['ipprotocol']] = min($status['priority'], $metered_found_prios[$status['ipprotocol']]);
+ }
+ }
+}
+
+foreach ($metered_found_prios as $ipproto => $metered_found_prio) {
+ /* kill states for "metered" gateways */
+ foreach ($metered_gws[$ipproto] as $status) {
+ if ($status['priority'] > $metered_found_prio) {
+ configdp_run('filter kill gateway_states', [$status['gateway']], true);
+ }
}
}
diff --git a/src/opnsense/mvc/app/controllers/OPNsense/Routing/forms/dialogEditGateway.xml b/src/opnsense/mvc/app/controllers/OPNsense/Routing/forms/dialogEditGateway.xml
index e0e9ac622..1d7088b72 100644
--- a/src/opnsense/mvc/app/controllers/OPNsense/Routing/forms/dialogEditGateway.xml
+++ b/src/opnsense/mvc/app/controllers/OPNsense/Routing/forms/dialogEditGateway.xml
@@ -63,6 +63,13 @@
When a monitor down event is triggered, kill all states to this gateway.
+
+ gateway_item.monitor_killstates_priority
+
+ checkbox
+
+ When a monitor up event is triggered for a gateway with a higher priority (lower value) than this, kill all states to this gateway. Most common use for this switch are metered connections like LTE.
+
gateway_item.monitor
diff --git a/src/opnsense/mvc/app/models/OPNsense/Routing/Gateways.xml b/src/opnsense/mvc/app/models/OPNsense/Routing/Gateways.xml
index 21a0b845d..67903e5c7 100644
--- a/src/opnsense/mvc/app/models/OPNsense/Routing/Gateways.xml
+++ b/src/opnsense/mvc/app/models/OPNsense/Routing/Gateways.xml
@@ -56,6 +56,7 @@
+