Project

General

Profile

Feature #14483 » interfaces.txt

Guilherme Oliveira, 07/28/2026 11:29 AM

 
1
--- a/etc/inc/interfaces.inc
2
+++ b/etc/inc/interfaces.inc
3
@@ -1,12 +1,71 @@
4
-		// Create IPsec interface
5
-		if (does_interface_exist($ipsecif)) {
6
-			mwexec("/sbin/ifconfig " . escapeshellarg($ipsecif) . " destroy");
7
+		/*
8
+		 * Preserve an existing IPv4 VTI when its runtime configuration already
9
+		 * matches the configured reqid, outer tunnel, and inner addresses.
10
+		 */
11
+		$vti_reconfigure = true;
12
+		$vti_exists = does_interface_exist($ipsecif);
13
+		if ($vti_exists) {
14
+			$ifconfig_output = array();
15
+			$ifconfig_status = 0;
16
+			exec("/sbin/ifconfig -f inet:cidr " . escapeshellarg($ipsecif),
17
+				$ifconfig_output, $ifconfig_status);
18
+			$ifconfig_output = implode("\n", $ifconfig_output);
19
+			$expected_reqid = $ipsec_reqid_base + $reqids[$ipsecif];
20
+			$outer_inet = is_ipaddrv6($left_spec) ? "inet6" : "inet";
21
+
22
+			$vti_matches =
23
+				($ifconfig_status === 0) &&
24
+				preg_match('/^' . preg_quote($ipsecif, '/') .
25
+					':.*<[^>]*\bUP\b[^>]*>/m', $ifconfig_output) &&
26
+				preg_match('/^\s*reqid:\s*' . preg_quote((string)$expected_reqid, '/') .
27
+					'\s*$/m', $ifconfig_output) &&
28
+				preg_match('/^\s*tunnel\s+' . preg_quote($outer_inet, '/') . '\s+' .
29
+					preg_quote($left_spec, '/') . '\s+-->\s+' .
30
+					preg_quote($right_spec, '/') . '\s*$/m', $ifconfig_output);
31
+
32
+			$expected_v4_count = 0;
33
+			foreach ($addrs as $addr) {
34
+				if (is_v6($addr['left'])) {
35
+					$vti_matches = false;
36
+					break;
37
+				}
38
+				$left_addr = explode('/', $addr['left'], 2);
39
+				$right_addr = explode('/', $addr['right'], 2);
40
+				if (empty($left_addr[0]) || empty($left_addr[1]) ||
41
+				    empty($right_addr[0])) {
42
+					$vti_matches = false;
43
+					break;
44
+				}
45
+				$expected_v4_count++;
46
+				if (!preg_match('/^\s*inet\s+' .
47
+					preg_quote($left_addr[0], '/') . '\s+-->\s+' .
48
+					preg_quote($right_addr[0], '/') . '\/' .
49
+					preg_quote($left_addr[1], '/') . '\s*$/m',
50
+					$ifconfig_output)) {
51
+					$vti_matches = false;
52
+					break;
53
+				}
54
+			}
55
+			preg_match_all('/^\s*inet\s+/m', $ifconfig_output, $current_v4);
56
+			if (count($current_v4[0]) !== $expected_v4_count) {
57
+				$vti_matches = false;
58
+			}
59
+			if ($vti_matches) {
60
+				$vti_reconfigure = false;
61
+				log_error(sprintf("IPsec VTI %s unchanged, preserving interface", $ipsecif));
62
+			}
63
 		}
64
-		mwexec("/sbin/ifconfig " . escapeshellarg($ipsecif) . " create reqid " . escapeshellarg($ipsec_reqid_base + $reqids[$ipsecif]));
65
 
66
-		/* Apply the outer tunnel addresses to the interface */
67
-		$inet = is_ipaddrv6($left_spec) ? "inet6" : "inet";
68
-		mwexec("/sbin/ifconfig " . escapeshellarg($ipsecif) . " {$inet} tunnel " . escapeshellarg($left_spec) . " " . escapeshellarg($right_spec) . " up");
69
+		if ($vti_reconfigure) {
70
+			if ($vti_exists) {
71
+				mwexec("/sbin/ifconfig " . escapeshellarg($ipsecif) . " destroy");
72
+			}
73
+			mwexec("/sbin/ifconfig " . escapeshellarg($ipsecif) . " create reqid " . escapeshellarg($ipsec_reqid_base + $reqids[$ipsecif]));
74
+
75
+			/* Apply the outer tunnel addresses to the interface */
76
+			$inet = is_ipaddrv6($left_spec) ? "inet6" : "inet";
77
+			mwexec("/sbin/ifconfig " . escapeshellarg($ipsecif) . " {$inet} tunnel " . escapeshellarg($left_spec) . " " . escapeshellarg($right_spec) . " up");
78
+		}
79
 
80
 		/* Loop through all of the addresses for this interface and apply them as needed */
81
 		foreach ($addrs as $addr) {
82
@@ -21,7 +80,9 @@
83
 				$right = escapeshellarg((explode('/', $addr['right'], 2))[0]);
84
 			}
85
 
86
-			mwexec("/sbin/ifconfig " . escapeshellarg($ipsecif) . " {$inet} " . escapeshellarg($addr['left']) . " " . $right . $addr['alias']);
87
+			if ($vti_reconfigure) {
88
+				mwexec("/sbin/ifconfig " . escapeshellarg($ipsecif) . " {$inet} " . escapeshellarg($addr['left']) . " " . $right . $addr['alias']);
89
+			}
90
 			/* If alias is empty, this is the first address on the interface and should be used as the gateway. */
91
 			if (empty($addr['alias'])) {
92
 				file_put_contents("/tmp/{$ipsecif}_router{$gwtype}", (explode('/', $addr['right'], 2))[0]);
(5-5/6)