Blog Post

Virtualization
8 MIN READ

Windows NAT (WinNAT) -- Capabilities and limitations

scooley's avatar
scooley
Icon for Microsoft rankMicrosoft
Mar 21, 2019
First published on TECHNET on May 25, 2016
Author: Jason Messer



How many devices (e.g. laptops, smart phones, tablets, DVRs, etc.) do you have at home which connect to the internet? Each of these devices probably has an IP address assigned to it, but did you know that that the public internet actually only sees one IP address for all of these devices? How does this work?

Network Address Translation (NAT) allows users to create a private, internal network which shares a public IP address(es). When a new connection is established (e.g. web browser session to www.bing.com ) the NAT translates the private (source) IP address assigned to your device to the shared public IP address which is routable on the internet and creates a new entry in the NAT flow state table. When a connection comes back into your network, the public (destination) IP address is translated to the private IP address based on the matching flow state entry.

This same NAT technology and concept can also work with host networking using virtual machine and container endpoints running on a single host. IP addresses from the NAT internal subnet (prefix) can be assigned to VMs, containers, or other services running on this host. Similar to how the NAT translates the source IP address of a device, NAT can also translate the source IP address of a VM or container to the IP address of a virtual network adapter (Host vNIC) on the host.

[caption id="attachment_8585" align="alignnone" width="721"] Figure 1: Physical vs Virtual NAT[/caption]



Similarly, the TCP/UDP ports can also be translated (Port Address Translation – PAT) so that traffic received on an external port can be forwarded to a different, internal port. These are known as static mappings.

[caption id="attachment_8595" align="alignnone" width="904"] Figure 2: Static Port Mappings[/caption]

Host Networking with WinNAT to attach VMs and Containers


The first step in creating a host network for VMs and containers is to create an internal Hyper-V virtual switch in the host. This provides Layer-2 (Ethernet) internal connectivity between the endpoints. In order to obtain external connectivity through a NAT (using WinNAT), we add a Host vNIC to the internal vSwitch and assign the default gateway IP address of the NAT to this vNIC. This essentially creates a router so that any network traffic from one of the endpoints that is destined for an IP address outside of the internal network (e.g. bing.com) will go through the NAT translation process.

Note: when the Windows container feature is installed, the docker daemon creates a default NAT network automatically when it starts. To stop this network from being created, make sure the docker daemon (dockerd) is started with the ‘-b “none”’ argument specified.

In addition to address translation, WinNAT also allows users to create static port mappings or forwarding rules so that internal endpoints can be accessed from external clients. Take for example an IIS web server running in a container attached to the default NAT network. The IIS web server will be listening on port 80 and so it requires that any connections coming in on a particular port to the host from an external client will be forwarded or mapped to port 80 on the container. Reference Figure 2 above to see port 8080 on the host being mapped to port 80 on the container.

In order to create a NAT network to connect VMs, please follow these instructions: https://msdn.microsoft.com/en-us/virtualization/hyperv_on_windows/user_guide/setup_nat_network

In order to create a NAT network for containers (or use the default nat network) please follow these instructions:
https://msdn.microsoft.com/en-us/virtualization/windowscontainers/management/container_networking

Key Limitations in WinNAT (Windows NAT implementation)



  • Multiple internal subnet prefixes not supported

  • External / Internal prefixes must not overlap

  • No automatic networking configuration

  • Cannot access externally mapped NAT endpoint IP/ ports directly from host – must use internal IP / ports assigned to endpoints


Multiple Internal Subnet Prefixes


Consider the case where multiple applications / VMs / containers each require access to a private NAT network. WinNAT only allows for one internal subnet prefix to be created on a host which means that if multiple applications or services need NAT connectivity, they will need to coordinate between each other to share this internal NAT network; each application cannot create its own individual NAT network. This may require creating a larger private NAT subnet (e.g. /18) for these endpoints. Moreover, the private IP addresses assigned to the endpoints cannot be re-used so that IP allocation also needs to be coordinated.

[caption id="attachment_8605" align="alignnone" width="494"] Figure 3: Multiple Internal NAT Subnets are not allowed – combined into a larger, shared subnet[/caption]

Lastly, individual external host ports can only be mapped to one internal endpoint. A user cannot create two static port mappings with external port 80 and have traffic directed to two different internal endpoints. These static port mappings must be coordinated between applications and services requiring NAT. WinNAT does not support dynamic port mappings (i.e. allowing WinNAT to automatically choose an external – or ephemeral – host port to be used by the mapping).

Note: Dynamic port mappings are supported through docker run with the -p | -P options since IP address management (IPAM) is handled by the Host Network Service (HNS) for containers.

Overlapping External and Intern IP Prefixes


A NAT network may be created on either a client or server host. When the NAT is first created, it must be ensured that the internal IP prefix defined does not overlap with the external IP addresses assigned to the host.

Example – This is not allowed :

Internal, Private IP Subnet: 172.16.0.0/12
IP Address assigned to the Host: 172.17.0.4


If a user is roaming on a laptop and connects to a different physical network such that the container host’s IP address is now within the private NAT network, the internal IP prefix of the NAT will need to be modified so that it does not overlap.

Automatic Network Configuration


WinNAT itself does not dynamically assign IP addresses, routes, DNS servers, or other network information to an endpoint. For container endpoints, since HNS manages IPAM, HNS will assign IP networking information from the NAT network to container endpoints. However, if a user is creating a VM and connecting a VM Network Adapter to a NAT network, the admin must assign the IP configuration manually inside the VM.

Accessing internal endpoints directly from the Host


Internal endpoints assigned to VMs or containers cannot be accessed using the external IPs / ports referenced in NAT static port mappings directly from the NAT host. From the NAT host, these internal endpoints must be addressed directly by their internal IP and ports. For instance, assume a container endpoint has IP 172.16.1.100 and is running a web server which is listening on port 80. Moreover, assume a port mapping has been created through docker to forward traffic from the host’s IP address (10.10.50.20) received on TCP port 8080 to the container endpoint. In this case, a user on the container host cannot directly access the web server using the externally mapped ports. e.g. A user operating on the container host cannot access the container web server indirectly on http://10.10.50.20:8080 . Instead, the user must directly access the container web server on http://172.16.1.100:80 .

The one caveat to this limitation is that the internal endpoint can be accessed using the external IP/port from a separate, VM/container endpoint running on the same NAT host: this is called hair-pinning. E.g. A user operating on container A can access a web server running in Container B using the internal IP and port of http://10.10.50.20:8080 .

Configuration Example: Attaching VMs and Containers to a NAT network


If you need to attach multiple VMs and containers to a single NAT, you will need to ensure that the NAT internal subnet prefix is large enough to encompass the IP ranges being assigned by different applications or services (e.g. Docker for Windows and Windows Container – HNS). This will require either application-level assignment of IPs and network configuration or manual configuration which must be done by an admin and guaranteed not to re-use existing IP assignments on the same host.

The solution below will allow both Docker for Windows (Linux VM running Linux containers) and Windows Containers to share the same WinNAT instance using separate internal vSwitches. Connectivity between both Linux and Windows containers will work.

Example



  • User has connected VMs to a NAT network through an internal vSwitch named “VMNAT” and now wants to install Windows Container feature with docker engine


    1. PS C:\> Get-NetNat “VMNAT”| Remove-NetNat (this will remove the NAT but keep the internal vSwitch).

    2. Install Windows Container Feature

    3. DO NOT START Docker Service (daemon)

    4. Edit the arguments passed to the docker daemon (dockerd) by adding --fixed-cidr=<container prefix> parameter. This tells docker to create a default nat network with the IP subnet <container prefix> (e.g. 192.168.1.0/24) so that HNS can allocate IPs from this prefix.


    5. PS C:\> Start-Service Docker; Stop-Service Docker


    6. PS C:\> Get-NetNat | Remove-NetNAT (again, this will remove the NAT but keep the internal vSwitch)


    7. PS C:\> New-NetNat -Name SharedNAT -InternalIPInterfaceAddressPrefix <shared prefix>


    8. PS C:\> Start-Service docker




Docker/HNS will assign IPs to Windows containers from the <container prefix>

Admin will assign IPs to VMs from the difference set of the <shared prefix> and <container prefix>



  • User has installed Windows Container feature with docker engine running and now wants to connect VMs to the NAT network


    1. PS C:\> Stop-Service docker


    2. PS C:\> Get-ContainerNetwork | Remove-ContainerNetwork -force


    3. PS C:\> Get-NetNat | Remove-NetNat (this will remove the NAT but keep the internal vSwitch)

    4. Edit the arguments passed to the docker daemon (dockerd) by adding -b “none” option to the end of docker daemon (dockerd) command to tell docker not to create a default NAT network.


    5. PS C:\> New-ContainerNetwork –name nat –Mode NAT –subnetprefix <container prefix> (create a new NAT and internal vSwitch – HNS will allocate IPs to container endpoints attached to this network from the <container prefix>)


    6. PS C:\> Get-Netnat | Remove-NetNAT (again, this will remove the NAT but keep the internal vSwitch)


    7. PS C:\> New-NetNat -Name SharedNAT -InternalIPInterfaceAddressPrefix <shared prefix>


    8. PS C:\> New-VirtualSwitch -Type internal (attach VMs to this new vSwitch)


    9. PS C:\> Start-Service docker




Docker/HNS will assign IPs to Windows containers from the <container prefix>

Admin will assign IPs to VMs from the difference set of the <shared prefix> and <container prefix>

In the end, you should have two internal VM switches and one NetNat shared between them.

Troubleshooting



  1. Make sure you only have one NAT


Get-NetNat

  1. If a NAT already exists, please delete it


Get-NetNat | Remove-NetNat

  1. Make sure you only have one "internal" vmSwitch for the application or feature (e.g. Windows containers). Record the name of the vSwitch for Step 4


Get-VMSwitch

  1. Check to see if there are private IP addresses (e.g. NAT default Gateway IP Address - usually *.1) from the old NAT still assigned to an adapter


Get-NetIPAddress -InterfaceAlias "vEthernet(<name of vSwitch>)"

  1. If an old private IP address is in use, please delete it


Remove-NetIPAddress -InterfaceAlias "vEthernet(<name of vSwitch>)" -IPAddress <IPAddress>

Removing Multiple NATs


We have seen reports of multiple NAT networks created inadvertently. This is due to a bug in recent builds (including Windows Server 2016 Technical Preview 5 and Windows 10 Insider Preview builds). If you see multiple NAT networks, after running docker network ls or Get-ContainerNetwork , please perform the following from an elevated PowerShell:
$KeyPath = "HKLM:\SYSTEM\CurrentControlSet\Services\vmsmp\parameters\SwitchList"
$keys = get-childitem $KeyPath
foreach($key in $keys)
{
if ($key.GetValue("FriendlyName") -eq 'nat')
{
$newKeyPath = $KeyPath+"\"+$key.PSChildName
Remove-Item -Path $newKeyPath -Recurse
}
}
remove-netnat -Confirm:$false
Get-ContainerNetwork | Remove-ContainerNetwork
Restart the Computer



~ Jason Messer
Updated Mar 22, 2019
Version 2.0
  • Tweltin's avatar
    Tweltin
    Copper Contributor

    Hello Sarah,

     

    Thanks for the write-up, this is very useful for a project I'm working on. 

     

    Is there any way to add static mapping rules for specific external IP addresses on the NAT? For instance, if I had a computer with three network cards with these IPs: 10.95.1.135, 10.95.1.136 and 192.168.1.1 and my NAT was set up so the internal prefix is 192.168.1.0/24, could I set up the NAT so that 10.95.1.135 forwards to 192.168.1.135 for ports 1-65535 but 10.95.1.136 forwards to 192.168.1.136 for ports 1-65535?

     

    Basically I'm trying to get pseudo 1:1 NAT working rather than just PAT. I've tried to set this up, but the Add-NetNatStaticMapping cmdlet fails with any other external IP address other than 0.0.0.0. If I try to add an external address and port range, I get an error saying "Element Not Found".

     

    PS C:\WINDOWS\system32> get-netnat
    
    
    Name                             : NATNetwork
    ExternalIPInterfaceAddressPrefix :
    InternalIPInterfaceAddressPrefix : 192.168.1.0/24
    IcmpQueryTimeout                 : 30
    TcpEstablishedConnectionTimeout  : 1800
    TcpTransientConnectionTimeout    : 120
    TcpFilteringBehavior             : AddressDependentFiltering
    UdpFilteringBehavior             : AddressDependentFiltering
    UdpIdleSessionTimeout            : 120
    UdpInboundRefresh                : False
    Store                            : Local
    Active                           : True
    
    
    
    PS C:\WINDOWS\system32> Add-NetNatStaticMapping -NatName NATNetwork -Protocol TCP -ExternalIPAddress 10.95.1.135 -ExternalPort 80 -InternalIPAddress 192.168.1.135 -InternalPort 80
    Add-NetNatStaticMapping : The external IP address 10.95.1.135 and port number 80 for the static mapping does not match
    an existing ExternalAddress' IP address or port range. Use Add-NetNatExternalAddress to add an ExternalAddress.
    At line:1 char:1
    + Add-NetNatStaticMapping -NatName NATNetwork -Protocol TCP -ExternalIP ...
    + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
        + CategoryInfo          : NotSpecified: (MSFT_NetNatStaticMapping:root/StandardCi...atStaticMapping) [Add-NetNatSt
       aticMapping], CimException
        + FullyQualifiedErrorId : Windows System Error 1169,Add-NetNatStaticMapping
    
    PS C:\WINDOWS\system32> Get-NetNatExternalAddress | where-object {$_.NatName -eq "NATNetwork" -and $_.IPAddress -eq "10.95.1.135"}
    
    
    ExternalAddressID : 30
    NatName           : NATNetwork
    IPAddress         : 10.95.1.135
    PortStart         : 6609
    PortEnd           : 6708
    Active            : True
    
    ExternalAddressID : 31
    NatName           : NATNetwork
    IPAddress         : 10.95.1.135
    PortStart         : 62065
    PortEnd           : 62164
    Active            : True
    
    
    
    PS C:\WINDOWS\system32> Add-NetNatExternalAddress -NatName NATNetwork -IPAddress 10.95.1.135 -PortStart 80 -PortEnd 80
    Add-NetNatExternalAddress : Element not found.
    At line:1 char:1
    + Add-NetNatExternalAddress -NatName NATNetwork -IPAddress 10.95.1.135  ...
    + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
        + CategoryInfo          : ObjectNotFound: (MSFT_NetNatExternalAddress:root/StandardCi...ExternalAddress) [Add-NetN
       atExternalAddress], CimException
        + FullyQualifiedErrorId : Windows System Error 1168,Add-NetNatExternalAddress

     

  • delaneyjwh's avatar
    delaneyjwh
    Copper Contributor

    I have a question regarding these instructions for attaching Docker Containers to the NAT network. 
    Regarding setup: I am running a Windows Server 2016 VMware instance inside a Windows 7 host. Docker is running on the Windows Server 2016 instance. 

    I have set up a NAT network following this guide, but I am confused about the instructions for connecting containers to the NAT network. I followed the second use case example (also shown above, option that says "User has installed Windows Container feature with docker engine running and now wants to connect VMs to the NAT network") but I don't know if that's what I am looking for. What further confuses me is that these instructions set up two internal switches, but in the troubleshooting section it mentions that I need only one internal switch. 
    What is the proper way to go about connecting docker containers to use the custom NAT network?

  • sumnone's avatar
    sumnone
    Copper Contributor

    Great article. However, I'm in the same boat as Tweltin here. Seems the features are lacking and I can't understand who would want to map all your external ip addresses (0.0.0.0) to a single internal address and port. In what scenario would that ever be useful, especially when DNS only points to a single external address? I guess it's better than nothing and better than ICS.

     

    It would be more useful if you could nat all the external ports from a single external ip, rather than all the external ips to a single internal ip/port. 

     

    I really ran into a roadblock tonight when one of the requests outbound picks ANY (AFAIK) external ip address and then the recipient address is expecting a specific address (and errors otherwise), although that's probably a different issue altogether. Maybe I could change the DNS entry to the Ip it thinks it is now, but it's broken again when my guest picks a different external Ip to go outbound on to make that request.

     

    I had all of Tweltin's pain when trying to map two single external Ips to two single internal Ips on the same port. The only way it would work is if you nat them on different external ports (and using ALL external Ips for each internal Ip). I ended up with an haproxy load balancer and an extra guest I didn't really want, so I could keep them on the same external port (i.e. only one IP being Natted). This NetNat stuff seems to be half-baked and requiring work around after work around.

Share