Add VPN detection by mac-address for Windows

This patch adds VPN detection for windows
based on known MAC addresses.
- Cisco AnyConnect
- GlobalProtect Virtual Ethernet

Bug: webrtc:13097
Change-Id: Ia90ee50be0dc2dcd2e6e9de1493fdd2c5e7d9d3a
Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/230245
Reviewed-by: Harald Alvestrand <hta@webrtc.org>
Commit-Queue: Jonas Oreland <jonaso@webrtc.org>
Cr-Commit-Position: refs/heads/main@{#34997}
diff --git a/rtc_base/network.cc b/rtc_base/network.cc
index b485077..a9352d8 100644
--- a/rtc_base/network.cc
+++ b/rtc_base/network.cc
@@ -40,6 +40,14 @@
 namespace rtc {
 namespace {
 
+// List of MAC addresses of known VPN (for windows).
+constexpr uint8_t kVpns[2][6] = {
+    // Cisco AnyConnect.
+    {0x0, 0x5, 0x9A, 0x3C, 0x7A, 0x0},
+    // GlobalProtect Virtual Ethernet.
+    {0x2, 0x50, 0x41, 0x0, 0x0, 0x1},
+};
+
 const uint32_t kUpdateNetworksMessage = 1;
 const uint32_t kSignalNetworksMessage = 2;
 
@@ -486,6 +494,20 @@
   return nullptr;
 }
 
+bool NetworkManagerBase::IsVpnMacAddress(
+    rtc::ArrayView<const uint8_t> address) {
+  if (address.data() == nullptr && address.size() == 0) {
+    return false;
+  }
+  for (const auto& vpn : kVpns) {
+    if (sizeof(vpn) == address.size() &&
+        memcmp(vpn, address.data(), address.size()) == 0) {
+      return true;
+    }
+  }
+  return false;
+}
+
 BasicNetworkManager::BasicNetworkManager() : BasicNetworkManager(nullptr) {}
 
 BasicNetworkManager::BasicNetworkManager(
@@ -781,6 +803,15 @@
             vpn_underlying_adapter_type = adapter_type;
             adapter_type = ADAPTER_TYPE_VPN;
           }
+          if (adapter_type != ADAPTER_TYPE_VPN &&
+              IsVpnMacAddress(rtc::ArrayView<const uint8_t>(
+                  reinterpret_cast<const uint8_t*>(
+                      adapter_addrs->PhysicalAddress),
+                  adapter_addrs->PhysicalAddressLength))) {
+            vpn_underlying_adapter_type = adapter_type;
+            adapter_type = ADAPTER_TYPE_VPN;
+          }
+
           std::unique_ptr<Network> network(new Network(
               name, description, prefix, prefix_length, adapter_type));
           network->set_underlying_type_for_vpn(vpn_underlying_adapter_type);
diff --git a/rtc_base/network.h b/rtc_base/network.h
index 94350e0..d97b07f 100644
--- a/rtc_base/network.h
+++ b/rtc_base/network.h
@@ -19,6 +19,7 @@
 #include <string>
 #include <vector>
 
+#include "api/array_view.h"
 #include "api/sequence_checker.h"
 #include "rtc_base/ip_address.h"
 #include "rtc_base/mdns_responder_interface.h"
@@ -194,6 +195,10 @@
 
   bool GetDefaultLocalAddress(int family, IPAddress* ipaddr) const override;
 
+  // Check if MAC address in |bytes| is one of the pre-defined
+  // MAC addresses for know VPNs.
+  static bool IsVpnMacAddress(rtc::ArrayView<const uint8_t> address);
+
  protected:
   typedef std::map<std::string, Network*> NetworkMap;
   // Updates `networks_` with the networks listed in `list`. If
diff --git a/rtc_base/network_unittest.cc b/rtc_base/network_unittest.cc
index e7b9982..b4f1f31 100644
--- a/rtc_base/network_unittest.cc
+++ b/rtc_base/network_unittest.cc
@@ -1434,4 +1434,19 @@
 }
 #endif  // defined(WEBRTC_POSIX)
 
+TEST_F(NetworkTest, HardcodedVpn) {
+  const uint8_t cisco[] = {0x0, 0x5, 0x9A, 0x3C, 0x7A, 0x0};
+  const uint8_t global[] = {0x2, 0x50, 0x41, 0x0, 0x0, 0x1};
+  const uint8_t unknown[] = {0x2, 0x50, 0x41, 0x0, 0x0, 0x0};
+  const uint8_t five_bytes[] = {0x2, 0x50, 0x41, 0x0, 0x0};
+  EXPECT_TRUE(NetworkManagerBase::IsVpnMacAddress(cisco));
+  EXPECT_TRUE(NetworkManagerBase::IsVpnMacAddress(global));
+
+  EXPECT_FALSE(NetworkManagerBase::IsVpnMacAddress(
+      rtc::ArrayView<const uint8_t>(cisco, 5)));
+  EXPECT_FALSE(NetworkManagerBase::IsVpnMacAddress(five_bytes));
+  EXPECT_FALSE(NetworkManagerBase::IsVpnMacAddress(unknown));
+  EXPECT_FALSE(NetworkManagerBase::IsVpnMacAddress(nullptr));
+}
+
 }  // namespace rtc