Click here to Skip to main content
15,118,172 members
Articles / Programming Languages / C#
Posted 21 Jul 2008


145 bookmarked

NAT Traversal with UPnP in C#

Rate me:
Please Sign up or sign in to vote.
4.89/5 (42 votes)
14 Jan 2009Public Domain5 min read
NAT traversal with UPnP in C#, without any libraries.


In writing network code, NAT can be a real problem. For most applications, requiring the user to add port forwarding rules to their router is not a good idea, or even acceptable, because often users are not allowed to change the settings of their router or do not know how to do so.

Fortunately, there are ways to automate the process of adding port forwarding rules. UPnP is one of the ways, to my knowledge the most commonly used way.

Another major problem with networking programs is that they appear to have no reliable way of finding the external IP address of the computer it is running on. UPnP partly solves this, but it is, of course, not completely reliable (not all routers support it, and routers that do may have UPnP turned off for security reasons).

If you have ever tried to use existing UPnP libraries, you may have found that most of them simply do not work, or do not cover the NAT aspects of it. This prompted me to write my own, very small, UPnP library that only covers the NAT aspects, and not even fully at that - only UPnP device discovery, basic port forwarding, and retrieving the external IP address. However, for many networking programs, this is all that is needed.

Using the code

In the source code, you will find a public class NAT, containing the following public methods:

  • static bool Discover()
  • static IPAddress GetExternalIP()
  • static void ForwardPort(int port, ProtocolType protocol, string description)
  • static void DeleteForwardingRule(int port, ProtocolType protocol)

Please note that these methods only do limited error checking, and will fail if there is no UPnP device or if the first UPnP device is too picky about the command syntax. Before GetExternalIP or ForwardPort will work, Discover has to be called. Discover can take up to 3 seconds by default (it will time out after that - you can change the time out value), possibly minutes if you set the time out high, so for any GUI application, it is advisable to place the call on a separate thread.

To forward a port the simplest way, code like this will suffice:

UPnP.NAT.ForwardPort(12345, ProtocolType.Tcp, "MyApp (TCP)");

In practice, it would be best to check for exceptions - and the value returned by Discover.


For the people who want to do it themselves, or who are interested, here is what I found out about UPnP in combination with NAT routers.

The first thing to do is find the UPnP device you are interested in. Apparently, the best way to do that is by using SSDP.

Finding the UPnP device using SSDP comes down to broadcasting a single packet over UDP and examining the replies. The packet looks like this (or at least, when it looks like this, it'll work):

"M-SEARCH * HTTP/1.1\r\n" +
"HOST:\r\n" +
"ST:upnp:rootdevice\r\n" +
"MAN:\"ssdp:discover\"\r\n" +

in ASCII. While the Host is, the packet still has to be broadcasted to the standard IPAddress.Broadcast address.

Examining the replies is pretty straightforward. You should only get one, but if you get more anyway, the right one is identifiable. A standard reply from a NAT device looks like this:

HTTP/1.1 200 OK
Server:IGD-HTTP/1.1 UPnP/1.0 UPnP-Device-Host/1.0

The ST should be "upnp:rootdevice"; if it's not, it isn't the right device, and it shouldn't have replied in the first place, but one never knows what happens in a network.

Anyhow, we're interested in the location, since at that location there will be an XML file containing information about the device - we will need this to find out how to send commands to the device.

Sending commands to a UPnP device is made possible through something they called a "service". The XML file describing the device is quite long, so suffice it to know that the XPath to the service we're interested in is:


Since it uses namespaces, you will have to use an XmlNamespaceManager, but that's a whole different subject.

The result of this query should look somewhat like "/serv3.xml", once again an XML file. Note that this is a relative location, so you will need to append it to the base-URL, which is "" in this example.

This file has a double purpose: first, its contents tell you something about what kinds of commands it accepts and how it will reply to them, and second, you will be POST-ing SOAP to it. That's correct, XML again.

Before this, I had never used a HTTP-POST on an XML file, and it greatly surprised me, but apparently, this is the way it should work.

Sending commands then is just using SOAP in the right way. However, this is not very straightforward - if you are using a WebRequest (as am I), then you will not only have to set the Method to "POST", but also add a header, and change the content-type:

  "\"urn:schemas-upnp-org:service:WANIPConnection:1#" + 
  function + "\"");
ContentType = "text/xml; charset=\"utf-8\"";

The variable function here must be the same as the function you are sending in the contents.

That is pretty much all there is to it. With this information, you should be able to implement your own UPnP NAT traversal methods.

Points of interest

While writing the code for this tiny library, I was absolutely stunned by the complete absence of clear information on the subject. Most sites deal only with UPnP for media and such, or explain the lack of security of UPnP NAT-traversal, without going very far into details on how to actually do it. When there is such information, it is generally written in such a way that at least I do not understand half of it.

In the end, I used WireShark to examine µTorrent's UPnP traffic, and reverse-engineered the process. I hope that this article changes matters for some people so that they will not have to reverse-engineer anything to get UPnP NAT-traversal working.


  • July 18, 2008: Wrote the first version of the UPnP NAT library.
  • July 22, 2008: Wrote the first version of this article.
  • July 22, 2008 (later that day): Updated the source and the article to work on some stricter routers and in cases where the port is not 80 (with thanks to ajiau).
  • July 23, 2008: Updated source to work with some additional routers (thanks to Chris Harper)
  • January 14, 2009: Updated source and article to (hopefully) work with yet some more additional routers, with thanks to everyone who posted feedback in the past half year.


This article, along with any associated source code and files, is licensed under A Public Domain dedication


About the Author

harold aptroot
Netherlands Netherlands
No Biography provided

Comments and Discussions

QuestionDoesnt work Pin
Member 1337260114-Mar-18 7:37
MemberMember 1337260114-Mar-18 7:37 
QuestionThe purpose of this is so that we can write a client UPnP device Pin
Member 130407986-Jul-17 20:05
MemberMember 130407986-Jul-17 20:05 
GeneralRe: The purpose of this is so that we can write a client UPnP device Pin
harold aptroot7-Jul-17 4:09
Memberharold aptroot7-Jul-17 4:09 
QuestionA multi-layer router hierarchy is not supported? Pin
Member 1127749931-Jan-16 4:58
MemberMember 1127749931-Jan-16 4:58 
GeneralRe: A multi-layer router hierarchy is not supported? Pin
harold aptroot31-Jan-16 7:05
Memberharold aptroot31-Jan-16 7:05 
QuestionWorks great!!! Pin
Friedrich26-Mar-15 1:23
MemberFriedrich26-Mar-15 1:23 
QuestionAmazing! :) Pin
Hans77Lindgren21-Feb-14 22:48
MemberHans77Lindgren21-Feb-14 22:48 
SuggestionWindows 8 (x64) + miniupnpd = Internal Server Error 500 Pin
CNefarius9-Dec-13 13:23
MemberCNefarius9-Dec-13 13:23 
GeneralRe: Windows 8 (x64) + miniupnpd = Internal Server Error 500 Pin
harold aptroot9-Dec-13 23:23
Memberharold aptroot9-Dec-13 23:23 
GeneralRe: Windows 8 (x64) + miniupnpd = Internal Server Error 500 Pin
nns200915-Mar-15 4:50
Membernns200915-Mar-15 4:50 
QuestionThis code will not work reliably Pin
codeprojectsucks19-Dec-13 5:27
Membercodeprojectsucks19-Dec-13 5:27 
GeneralRe: This code will not work reliably Pin
harold aptroot9-Dec-13 5:49
Memberharold aptroot9-Dec-13 5:49 
GeneralRe: This code will not work reliably Pin
codeprojectsucks110-Dec-13 6:24
Membercodeprojectsucks110-Dec-13 6:24 
GeneralRe: This code will not work reliably Pin
harold aptroot10-Dec-13 7:41
Memberharold aptroot10-Dec-13 7:41 
QuestionMono.Nat is too complicated and it isn't cross platform. Pin
Cleveland Mark Blakemore25-Jun-13 16:23
MemberCleveland Mark Blakemore25-Jun-13 16:23 
QuestionNot working with Hyper-V virtual switch Pin
sensboston3-Jun-13 9:44
Membersensboston3-Jun-13 9:44 
GeneralRe: Not working with Hyper-V virtual switch Pin
harold aptroot3-Jun-13 23:35
Memberharold aptroot3-Jun-13 23:35 
GeneralRe: Not working with Hyper-V virtual switch Pin
sensboston4-Jun-13 8:56
Membersensboston4-Jun-13 8:56 
QuestionFurther question Pin
Johnson.Gao.12623-May-13 21:14
MemberJohnson.Gao.12623-May-13 21:14 
AnswerRe: Further question Pin
harold aptroot23-May-13 22:38
Memberharold aptroot23-May-13 22:38 
GeneralRe: Further question Pin
Johnson.Gao.12624-May-13 20:05
MemberJohnson.Gao.12624-May-13 20:05 
QuestionMy Vote! Pin
Lokukarawita20-May-13 22:53
MemberLokukarawita20-May-13 22:53 
QuestionNot working somehow, im a noob sry Pin
Yevgeny E.15-Jan-13 10:15
MemberYevgeny E.15-Jan-13 10:15 
GeneralRe: Not working somehow, im a noob sry Pin
harold aptroot15-Jan-13 10:19
Memberharold aptroot15-Jan-13 10:19 
AnswerRe: Not working somehow, im a noob sry Pin
Yevgeny E.15-Jan-13 10:33
MemberYevgeny E.15-Jan-13 10:33 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.