Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / Languages / VB

i00 NAT Forwarder

5.00/5 (10 votes)
4 Mar 2013Ms-PL3 min read 40.7K  
Automatic port forwarding for your UPnP enabled router.

Introduction

I wrote this application as I thought there were several things lacking with router port forwards, such as the ability to map port forwards to computer names rather than IP addresses, thus limiting the computer to have a static IP.  There are also other advantages such as portability, ease of use etc.

A summary of advantages are listed below:

  • Allows mapping to computer names as well as IP addresses.
  • Allows computers to have dynamic IP addresses and still have port forwarding (thus allowing easy connectivity to multiple "other" networks without having to change your IP or set it in your router).
  • Circumvents Netgear's restriction on not allowing port redirecting - Netgear routers do not allow you to forward a port on a different external port to the port that it is being hosted on through their interface.
  • Portability - Allows you to take your computer from one network to another, and cupled with a dyndns client have everything "just work" on your new network, imagine being able to move your web server from one network to another without having to change anything!

The only requirement for this app is a UPnP enabled router.

I have tested this on Server 2012, but it should work on anything Vista + ... and may work on previous OS' as well.

I have tested the software on the following brands of router: Netgear, Billion.

Screenshots

Image 1

Method

The UPnP access is done with the NATUPNPLib.dll that comes with Windows, this requires the SSDP Discovery Service to be enabled (NAT Forwarder will ask you to start it if it is not running when it starts).

NAT Forwarder can be installed as a service through the interface and automatically checks that ports are open at the interval specified in the settings page.

The main function of the project is, of course, to automate port forwards through UPnP routers. To do this "NATUPnP 1.0 Type Library" was used. To implement UPnP; first make sure that the "SSDP Discovery Service" is running, to do this click Start then Run, type services.msc and click OK.
Find the "SSDP Discovery Service" in the list and make sure its status is running, if it is stoped right click on it and select start; if it is disabled, right click on it, select properties set the startup type to automatic, click OK, then right click on the service and click start.

In your project add a reference to the COM library: "NATUPnP 1.0 Type Library"

You should now have the ability to manage port forwards from your project. To make things easier the below class can be used to manage port forwards:

VB.NET
'i00 .Net NAT Forwarder
'©i00 Productions All rights reserved
'Created by Kris Bennett
'----------------------------------------------------------------------------------------------------
'All property in this file is and remains the property of i00 Productions, regardless of its usage,
'unless stated otherwise in writing from i00 Productions.
'
'i00 is not and shall not be held accountable for any damages directly or indirectly caused by the
'use or miss-use of this product.

Public Class UPnP
    Implements IDisposable

    'Variables for the COM objects we are going to use
    Private UPnPNAT As NATUPNPLib.UPnPNAT
    Public Mappings As NATUPNPLib.IStaticPortMappingCollection

    'Returns true if UPnP is enabled
    Dim mc_Enabled As Boolean
    Public ReadOnly Property UPnPEnabled() As Boolean
        Get
            Return mc_Enabled
        End Get
    End Property

    'List of protocols that we can port forward to (used in functions)
    Public Enum Protocol
        TCP
        UDP
    End Enum

#Region "Constructors / Destructors"
     Public Sub New()
        'create our COM object to manage port forwards
        UPnPNAT = New NATUPNPLib.UPnPNAT

        'check that mappings can be obtained from the router and load the mapping COM object to our Mappings variable
        Me.GetMappings()
    End Sub

    'check that mappings can be obtained from the router and load the mapping COM object to our Mappings variable
    Private Sub GetMappings()
        Try
            'try to get the static port mappings from the router... using the NATUPNPLib.UPnPNAT COM library... 
            Mappings = UPnPNAT.StaticPortMappingCollection()
            If Mappings Is Nothing Then
                'no static mapping object returned - UPnP is not enabled
                mc_Enabled = False
            Else
                'UPnP is enabled and working
                mc_Enabled = True
            End If
        Catch ex As NotImplementedException
            mc_Enabled = False
        End Try
    End Sub

    Private disposedValue As Boolean = False 'To detect redundant calls

    'Remove the com objects from memory...
    Protected Overridable Sub Dispose(ByVal disposing As Boolean)
        If Not Me.disposedValue Then
            If Mappings IsNot Nothing Then System.Runtime.InteropServices.Marshal.ReleaseComObject(Mappings)
            If UPnPNAT IsNot Nothing Then System.Runtime.InteropServices.Marshal.ReleaseComObject(UPnPNAT)
        End If
        Me.disposedValue = True
    End Sub

    Public Sub Dispose() Implements IDisposable.Dispose
        Dispose(True)
        GC.SuppressFinalize(Me)
    End Sub

#End Region

#Region "Router functions"
     'Adds a port forwarding rule to the router...
    Public Sub Add(ByVal InternalIP As String, ByVal InternalPort As Integer, ByVal ExternalPort As Integer, ByVal Protocol As Protocol, ByVal Description As String)
        'check that UPnP is enabled
        If Not UPnPEnabled Then Throw New Exception("Could not find a UPnP enabled router")
        'check that the port isnot already mapped
        If Exists(ExternalPort, Protocol) Then Throw New Exception("Mapping already exists")
        'add the port mapping
        Mappings.Add(ExternalPort, Protocol.ToString(), InternalPort, InternalIP, True, Description)
    End Sub

    'Removes a port mapping rule from the router...
    Public Sub Remove(ByVal ExternalPort As Integer, ByVal Protocol As Protocol)
        'check that UPnP is enabled
        If Not UPnPEnabled Then Throw New Exception("Could not find a UPnP enabled router")
        'check that the port isnot already mapped
        If Not Exists(ExternalPort, Protocol) Then Throw New ArgumentException("Mapping does not exist")
        'remove the port mapping
        Mappings.Remove(ExternalPort, Protocol.ToString)
    End Sub

    'See if the 
    Public Function Exists(ByVal ExternalPort As Integer, ByVal Protocol As Protocol) As Boolean
        'check that UPnP is enabled
        If Not UPnPEnabled Then Throw New Exception("Could not find a UPnP enabled router")
        'go through each port that is mapped in our UPnP enabled router to see if the port has been mapped...
        For Each mapping In Mappings.OfType(Of NATUPNPLib.IStaticPortMapping)()
            'if the external port and protocol are what we are looking for then we are mapped...
            If mapping.ExternalPort.Equals(ExternalPort) AndAlso mapping.Protocol.ToString.Equals(Protocol.ToString) Then Return True
        Next
        'we do not have this mapping rule if we got this far
        Return False
    End Function

    Public Function Exists(ByVal ExternalPort As Integer, ByVal Protocol As Protocol, ByVal InternalIP As String, ByVal InternalPort As Integer, ByVal Description As String) As Boolean
        'check that UPnP is enabled
        If Not UPnPEnabled Then Throw New Exception("Could not find a UPnP enabled router")
        ' Begin checking
        For Each mapping In Mappings.OfType(Of NATUPNPLib.IStaticPortMapping)()
            'if the external port, protocol, IP, internal port, and description are what we are looking for then we are mapped...
            If mapping.ExternalPort.Equals(ExternalPort) AndAlso mapping.Protocol.ToString.Equals(Protocol.ToString) AndAlso mapping.InternalClient.Equals(InternalIP) AndAlso mapping.InternalPort.Equals(InternalPort) AndAlso mapping.Description.Equals(Description) Then Return True
        Next
        'we do not have this mapping rule if we got this far
        Return False
    End Function

#End Region

End Class
We now have an easy to use wrapper for easy port forwards, some examples of how to use this wrapper are listed below:

To check UPnP is enabled:

VB.NET
Using UPnP As New UPnP
    Return UPnP.UPnPEnabled
End Using


To add a port map:
VB.NET
Using UPnP As New UPnP
    UPnP.Add(localIP As String, Port As Integer, ExternalPort As Integer, prot As Protocol, desc As String)
End Using


To remove a port map:
VB.NET
Using UPnP As New UPnP
    UPnP.Remove(Port As Integer, Prot As Protocol)
End Using


To check if a port is mapped:
VB.NET
Using UPnP As New UPnP
    Return UPnP.Exists(Port As Integer, Prot As Protocol)
End Using


To load a list of port mappings from the router:
VB.NET
Using UPnP As New UPnP
    For Each mapping As NATUPNPLib.IStaticPortMapping In UPnP.staticMapping
        Debug.Print "Local IP: " & mapping.InternalClient & vbcrlf & _
            "Local Port: " & mapping.InternalPort & vbcrlf & _
            "External Port: " & mapping.ExternalPort & vbcrlf & _
            "Description: " & mapping.Description & vbcrlf & _
            "Portocol: " & mapping.prot.ToString() & vbcrlf & _
            "----------"
    Next
End Using

Change Log

20130302

Initial Release

License

This article, along with any associated source code and files, is licensed under The Microsoft Public License (Ms-PL)