A Hyper-V related question that shows regularly up in the forums is how to setup virtual switch ports in promiscuous mode so that external traffic can be received / monitored on the host’s root partition or on virtual machines.
Hyper-V 2012 introduced the concept of port monitoring (also called port mirroring) which can be enabled on any virtual machine network adapter or on the host. The interesting part is that there’s quite some official documentation available if you want to setup port monitoring / mirroring between two or more VMs, but you are almost on your own if you want to capture traffic coming from an external network or from the host root partition. PowerShell APIs, which do a great job in setting up port monitoring between VMs, are quite convoluted and obscure when it comes to host monitoring settings.
This blog post has the purpose of explaining how to handle non trivial Hyper-V promiscuous mode requirements and introduces a simple PowerShell module to easily handle port monitoring settings on the host.
How does port monitoring / mirroring work?
The Hyper-V port monitoring functionality is already well explained elsewhere, so I’ll keep the basics to the minimum here. In short, unlike other virtualization solutions like VMWare ESXi where you set an entire virtual switch or group of ports in promiscuous mode, in Hyper-V you need to enable monitoring on each switch port individually, for either VM network adapters (vNICs) or host adapters (NICs).
Furthermore, Hyper-V does not let you simply set a “promiscuous mode” flag on a port, as you need to specify if a given port is supposed to be the source or the destination of the network packets, “mirroring” the traffic, hence the name.
The Hyper-V PowerShell module does a great job in making life easy from this perspective, for example:
Set—VMNetworkAdapter MyVM —PortMirroring Destination |
where -PortMirroring can be Source, Destination or None. If you have multiple adapters on a VM you’d most probably want to choose which adapter to configure:
Get—VMNetworkAdapter MyVM | ? MacAddress —eq ‘xxxxxxxx’ | Set—VMNetworkAdapter MyVM —PortMirroring Destination |
What about external traffic?
There are quite a few scenarios where you want to be able to receive on a VM all the traffic coming from an external network. Some of the most typical use cases include network intrusion detection systems (NIDS), monitoring tools (Wireshark, Message Analyzer, tcpdump, etc) or software defined networking (SDN) routers / switches, like for example Open vSwitch.
The PowerShell cmdlets (Add-VMSwitchExtensionPortFeature, Get-VMSystemSwitchExtensionPortFeature, etc) that can be used to manage port monitoring at the host level are not exactly user friendly and don’t cover all the relevant uses cases when it comes to internal ports. Beside the reference documentation, there are a few forum posts providing some info on how to use them, but not much more at the time of writing. Here’s an example for setting the port monitoring mode of a switch host NIC to source:
$portFeature=Get—VMSystemSwitchExtensionPortFeature —FeatureName «Ethernet Switch Port Security Settings» # None = 0, Destination = 1, Source = 2 $portFeature.SettingData.MonitorMode = 2 Add—VMSwitchExtensionPortFeature —ExternalPort —SwitchName MySwitch —VMSwitchExtensionFeature $portFeature |
With the above example, all traffic passing on the external network NIC of MySwitch will be “mirrored” to any VM whose port monitoring mode has been set to destination.
Internal switches and shared for management NICs
There are two scenarios which are not covered by the previous example: internal switches, used manly for root partition / guest communication and external switches shared for management (i.e. created with New-VMSwitch -ManagementOS $true in PowerShell).
In those cases the above example becomes:
$portFeature=Get—VMSystemSwitchExtensionPortFeature —FeatureName «Ethernet Switch Port Security Settings» # None = 0, Destination = 1, Source = 2 $portFeature.SettingData.MonitorMode = 2 Add—VMSwitchExtensionPortFeature —ManagementOS —VMSwitchExtensionFeature $portFeature |
By specifying -ManagementOS it becomes unfortunately not possible to specify the switch, so the monitoring mode will be set for every internal switch and shared management NIC port on the host!
An easier way to set host NIC monitoring mode in PowerShell
The complications and limitations of the PowerShell cmdlets outlined above led to writing a simplified set of PowerShell commands, where enabling port monitoring on external or internal switches gets as simple as:
Import—Module .\VMSwitchPortMonitorMode.psm1 Set—VMSwitchPortMonitorMode —SwitchName MySwitch —MonitorMode Source |
The module is available here.
How to check the status of port monitoring on a given switch:
PS C:\Dev> Get—VMSwitchPortMonitorMode MySwitch PortType MonitorMode ——— ———— External Source |
Disabling monitoring is also very easy:
Set—VMSwitchPortMonitorMode —SwitchName MySwitch —MonitorMode None |
In case of external switches shared for management, you can set different port monitoring settings for the external and internal NICs by simply adding the -PortType parameter specifying either Host or Internal.
Edit (Dec 5th, 2020): Starting with Windows Server 2019, consider setting the destination VM network adapter in trunk mode, as described below, even if you don’t use VLANs. Many thanks to Gian Maria for bringing this up!
How to monitor VLAN tagged traffic?
Traffic generated on a VM with a vNIC set to tag traffic with a VLAN id cannot be directly monitored on another VM, unless trunking is set on the target. For example, let’s say that we want to monitor the traffic between two VMs (VM1 and VM2) on a third VM (VM3), with packets tagged with VLAN id 100.
VM1 and VM2:
Set—VMNetworkAdapterVlan VM1 —Access —VlanId 100 Set—VMNetworkAdapter VM1 —PortMirroring Source |
Set—VMNetworkAdapterVlan VM2 —Access —VlanId 100 Set—VMNetworkAdapter VM2 —PortMirroring Source |
VM3 (monitoring):
Set—VMNetworkAdapterVlan VM3 —Trunk —AllowedVlanIdList «100,101» —NativeVlanId 0 Set—VMNetworkAdapter VM3 —PortMirroring Destination |
Monitoring traffic on VM3 while pinging VM2 from VM1 will show something like the following tcpdump output, where the packets 802.1Q (VLAN) info is highlighted by the red ellipse:
Adding Filters to nmcap
You can filter the nmcap capture based on protocols, ports, IP addresses, and MAC or Ethernet addresses. The following table shows some common uses with these filters.
Filtering Traffic Commands | Comments |
---|---|
Capture traffic based on specific protocols.
/capture protocol-list
nmcap /network * | adapter-name
filter-protocol /file filename.
cap:size
C:\>nmcap /network * /capture icmp
/file dccap.cap:10mb
C:\>nmcap /network * /capture ldap
/file dccap.cap:10mb
C:\>nmcap /network * /capture !ldap
/file dccap.cap:10mb
C:\>nmcap /network * /capture (ldap
and icmp and dns) /file dccap.
cap:10mb
|
You can add protocols to filter in the nmcap/capture switch. after the
The first example captures only ICMP traffic and ignores all other traffic. The second example captures only LDAP traffic. The third example captures all traffic except for LDAP traffic. Tip The ! character is used as a Boolean NOT character. In other words, if you want to capture LDAP traffic, use ldap as the filter. If you want to capture everything but LDAP, use !ldap (read as NOT ldap).
|
Capture traffic based on a specific port.
/capture tcp|udp.port==port-number
C:\>nmcap /network * /capture
tcp.port==80 /file dccap.cap:10mb
C:\>nmcap /network * /capture
udp.port==53 /file dccap.cap:10mb
C:\>nmcap /network * /capture
(tcp.port==80 and udp.port==53)
/file dccap.cap:10mb
|
You can specify traffic to capture based on the TCP or UDP port used. The first example captures traffic using TCP port 80, the second example captures traffic using UDP port 53, and the third example captures traffic from both TCP port 80 and UDP port 53.
Note The == is two equal symbols put together. |
Capture traffic based on IP addresses.
/capture ipv4.address==IP-address
C:\>nmcap /network * /capture
ipv4.address==192.168.1.5 /file
dccap.cap:10mb
|
You can add a filter for specific IP addresses. The example captures only traffic to or from the system with the IPv4 address of 192.168.1.15. |
Capture traffic based on MAC addresses.
/capture ipv4.address==IP-address
C:\> nmcap /network * /capture
ethernet.address==00-03-ff-62-13-d7
/file dccap.cap:10mb
|
You can also filter based on Ethernet addresses (also called MAC addresses or physical addresses). The example captures only traffic to and from the system with the specified MAC address. |
Enabling Promiscuous Mode in nmcap
By default, Network Monitor and nmcap capture only traffic sent directly to or coming from the local IP address and broadcast traffic. However, you frequently want to be able to capture all traffic that reaches the NIC. To do so, you need to enable promiscuous mode, or P-Mode, with the /disablelocalonly switch.
Enabling Promiscuous Mode with nmcap | Comments |
---|---|
Enable Promiscuous Mode.
/disablelocalonly
C:\>nmcap /network * /capture /file
dc3cap.cap:10mb /disablelocalonly
|
Disables local-only capture, which enables promiscuous mode, or P-Mode. All frames that reach the network cards are captured regardless of their source and destination IP addresses. |
Windows, Windows Server 2008
Mar 12, 2016
What Is The Promiscuous Mode?
By default when a network card receives a packet, it checks whether the packet belongs to itself. If not, the interface card normally drops the packet. But in promiscuous mode, the card doesn’t drop the packet. Instead, it will accept all the packets which flows through the network card.
Some Network Interface Cards (NICs) may not allow network traffic after you create a Network Bridge. This is due to the inability of NIC to automatically enable Promiscuous Mode when creating a Network Bridge. Following are the steps to enable it manually.
Process
1. In the Start Menu search bar type cmd and press SHIFT + CTRL + ENTER to launch with Elevated Privileges.
2. Enter the following command to know the ID of your NIC
netsh bridge show adapter
In this example we see will assume the NIC id is 1.
3. When you know the NIC ID enter the following command to enable the Promiscuous Mode, remember to add the relevant NIC ID,
netsh bridge set adapter 1 forcecompatmode=enable
4. The above step will enable the Promiscuous Mode. Enter the command we used in Step 2, Now the Force Compatibility Mode (Promiscuous Mode) will display “enabled”.
Conclusion
Promiscuous Mode is automatically enabled when Bridging is activated, but some NIC models have some issues which can be solved by enabling it manually.
In this post I describe how to configure a Hyper-V virtual network switch into promiscuous mode. This mode allows you to monitor external traffic, eg. Needed for Microsoft Defender for IoT.
Assuming you already created an dedicated virtual network switch, you have to run these four steps.
- Turn off Allow management operation system to share this network adapter
- Turn off Enable virtual machine queue
- Set port mirroing mode to Destination
- Configure the Ethernet Switch Port Security Settings
My setup:
- Hyper-V Host – HOME-NUC01
- Virtual Network Switch – Span4IoT
- Virtual Machine – MD4IOT
- Open Hyper-V Manager > Select Action > Virtual Switch Manager > (your NIC) > External Network
- clear option Allow management operation system to share this network adapter
- Open Hyper-V Manager > (your VM) > Settings > (your NIC) > Hardware Acceleration
- clear option Enable virtual machine queue
- Open Hyper-V Manager > (your VM) > Settings > (your NIC) > Advanced Features
- Port mirroring > Mirroing mode > Destination
Run the following PowerShell cmdlets on the hyper-v host (not in the virtual machine)
#get VMSwitch Settings Get-VMSwitch | ft Name, SwitchType, NetAdapterInterfaceDescription, AllowManagementOs # # configure the corresponding switch for monitorting $VMSwitch = "Span4IoT" $portFeature=Get-VMSystemSwitchExtensionPortFeature -FeatureName "Ethernet Switch Port Security Settings" $portFeature.SettingData.MonitorMode = 2 Add-VMSwitchExtensionPortFeature -ExternalPort -SwitchName $VMSwitch -VMSwitchExtensionFeature $portFeature