noid-privacy/Modules/DNS/Private/Get-PhysicalAdapters.ps1

150 lines
6.4 KiB
PowerShell

function Get-PhysicalAdapters {
<#
.SYNOPSIS
Get physical network adapters (LAN/WLAN) excluding virtual adapters
.DESCRIPTION
Retrieves physical network adapters using multi-layer filtering to exclude:
- Virtual adapters (Hyper-V, VMware, VirtualBox)
- VPN adapters (TAP, OpenVPN, WireGuard, Cisco, etc.)
- Tunnel adapters (Teredo, 6to4, ISATAP)
- Loopback adapters
Uses Microsoft Best Practice: Get-NetAdapter with -Physical switch
and additional filtering based on InterfaceDescription patterns.
.PARAMETER IncludeDisabled
Include disabled adapters in results
.EXAMPLE
Get-PhysicalAdapters
Returns all active physical network adapters
.EXAMPLE
Get-PhysicalAdapters -IncludeDisabled
Returns all physical adapters including disabled ones
.OUTPUTS
Microsoft.Management.Infrastructure.CimInstance#ROOT/StandardCimv2/MSFT_NetAdapter
.NOTES
Uses Get-NetAdapter -Physical for primary filtering (Microsoft Best Practice)
Additional filtering excludes known virtual adapter patterns
#>
[CmdletBinding()]
param(
[Parameter()]
[switch]$IncludeDisabled
)
try {
Write-Log -Level DEBUG -Message "Retrieving physical network adapters..." -Module $script:ModuleName
# Layer 1: Get physical adapters only (Microsoft Best Practice)
$adapters = Get-NetAdapter -Physical -ErrorAction Stop
# Layer 2: Filter by status if required
if (-not $IncludeDisabled) {
# Allow 'Up' (Connected) and 'Disconnected' (Cable unplugged)
# Only filter out 'Disabled' (Administratively down) or 'Not Present'
$adapters = $adapters | Where-Object { $_.Status -eq "Up" -or $_.Status -eq "Disconnected" }
Write-Log -Level DEBUG -Message "Filtering to active/disconnected adapters (excluding disabled)" -Module $script:ModuleName
}
# Layer 3: Exclude virtual adapter patterns (COMPREHENSIVE!)
# NOTE: We distinguish between HOST-side and GUEST-side virtual adapters:
# - HOST-side (vEthernet, VMware Network Adapter VMnet*) → EXCLUDE
# - GUEST-side (Microsoft Hyper-V Network Adapter in VM) → KEEP!
$virtualPatterns = @(
# Host-side virtualization adapters (NOT guest adapters!)
'*vEthernet*', # Hyper-V HOST virtual switch
'*VMware Network Adapter*', # VMware HOST adapters (VMnet1, VMnet8)
'*VirtualBox Host-Only*', # VirtualBox HOST-only adapter
'*Virtual*Adapter*', # Generic virtual adapters
'*Container*', '*WSL*', '*Docker*',
# Generic VPN protocols
'*VPN*', '*OpenVPN*', '*WireGuard*', '*TAP*',
'*L2TP*', '*IKEv2*', '*RAS*', '*PPTP*',
# Consumer VPN vendors
'*NordVPN*', '*NordLynx*', '*ExpressVPN*', '*ProtonVPN*', '*Mullvad*',
# Enterprise VPN vendors
'*Cisco*', '*Pulse*', '*FortiClient*',
'*Palo Alto*', '*PANGP*', # Palo Alto GlobalProtect (no "VPN" in name!)
'*F5*', '*Checkpoint*', '*Check Point*', '*Sonicwall*', '*Juniper*',
# Tunnel adapters
'*Tunnel*', '*Teredo*', '*6to4*', '*ISATAP*', '*Loopback*'
)
# Layer 4: Check for active Windows VPN connections
$activeVpnConnections = @()
try {
$vpnConns = Get-VpnConnection -ErrorAction SilentlyContinue
$activeVpnConnections = $vpnConns | Where-Object { $_.ConnectionStatus -eq 'Connected' }
}
catch {
Write-Log -Level DEBUG -Message "Get-VpnConnection not available or failed: $_" -Module $script:ModuleName
}
$filteredAdapters = @($adapters | Where-Object {
$description = $_.InterfaceDescription
$name = $_.Name
$skipAdapter = $false
$skipReason = ""
# Check if adapter matches any virtual pattern
foreach ($pattern in $virtualPatterns) {
if ($description -like $pattern -or $name -like $pattern) {
$skipAdapter = $true
$skipReason = "Pattern match: $pattern"
break
}
}
# Check if InterfaceType is Tunnel (131)
if (-not $skipAdapter -and $_.InterfaceType -eq 131) {
$skipAdapter = $true
$skipReason = "InterfaceType = 131 (Tunnel)"
}
# Check MediaType for Tunnel
if (-not $skipAdapter -and $_.MediaType -match "Tunnel") {
$skipAdapter = $true
$skipReason = "MediaType contains 'Tunnel'"
}
# Check for native Windows VPN connection
if (-not $skipAdapter -and $activeVpnConnections) {
$currentAdapterAlias = $_.InterfaceAlias
$matchingVpn = $activeVpnConnections | Where-Object { $_.InterfaceAlias -eq $currentAdapterAlias }
if ($matchingVpn) {
$skipAdapter = $true
$skipReason = "Native Windows VPN active: $($matchingVpn.Name)"
}
}
if ($skipAdapter) {
Write-Log -Level DEBUG -Message "Excluding adapter: $name - $skipReason" -Module $script:ModuleName
}
-not $skipAdapter
}) # Close @( array wrapper
if ($filteredAdapters.Count -eq 0) {
Write-Log -Level WARNING -Message "No physical network adapters found" -Module $script:ModuleName
return @()
}
Write-Log -Level DEBUG -Message "Found $($filteredAdapters.Count) physical network adapter(s)" -Module $script:ModuleName
foreach ($adapter in $filteredAdapters) {
Write-Log -Level DEBUG -Message " - $($adapter.Name) ($($adapter.InterfaceDescription)) [Status: $($adapter.Status)]" -Module $script:ModuleName
}
return $filteredAdapters # Already wrapped as array in line 83
}
catch {
Write-ErrorLog -Message "Failed to retrieve physical network adapters" -Module $script:ModuleName -ErrorRecord $_
return @()
}
}