I was like, all, let's do uPNP too... then I was like, meh

So, having worked with mDNS/Zeroconf a bit, I thought I'd take a quick look at uPNP, particularly the IGD device type. IGD here is "Internet Gateway Device", i.e. your router. There are approximately two things you want to be able to access from generic software on a router, your external IP address (so you can share it with people, though this isn't as useful as having a server somewhere that can look at the address on an incoming connection), and a way to map ports to internal hosts (i.e. IMO what 99.9% of people who know what IGD stands for are looking to do with IGD).

So, we've played with mDNS, but for those who weren't playing along, the way it works is this:

  • you join a well known "group" (ip address in the multicast space)
  • you send out a multicast message to the group, the content of this message is a regular dns query for a PTR record
  • anyone in the group who has a PTR record that matches the request responds (to the group) with their PTR records
  • you resolve each PTR record to a SRV record and eventually an A record (the machine address)

For bonus points, the members of the group can return all of the records (PTR, SRV, A and optionally TXT) in a single response to reduce network traffic (and handle broken hardware that expects all of the records at once).

The thing that makes it all work is the PTR record, which is just a specially formatted "domain name" which describes a "class" or "type" of service, rather than a particular service or machine. These look like "_aastra-config._tcp.local." where the _ prefixed values are service-type and service-protocol components. Each machine which provides a service would create an SRV record with "My Cool Service._aastra-config._tcp.local." as the service name, and make the value in their PTR record that service-name. Their SRV record then points to their machine name, and the A name (as is normal) points to their IP address.

Conceptually, uPNP-SD is very similar:

  • you join a well known "group" (ip address in the multicast space)
  • you send out a multicast message to the group, the content of this message is an "http over udp" search query
  • anyone in the group who has a "device" record that matches the request responds (to you, directly) with "http over udp" responses that provide locations (urls)
  • for each url so received, you download an XML device/service description and traverse the description looking for the (sub)-service you actually want
  • once you find the service description, you issue SOAP calls to the url in the service description

The xml file description is pretty straight-forward to parse in Python, use an etree engine, look for the device, iterate over it's services and its sub-devices's services. SOAP is pretty easy to work with too. But wow, the documentation is a PITA to traverse. The key is that the service within IGD that you need is called WanIPConnection:1 (pdf link). If you happen to wade through that document, you'll notice that the spec lets you do everything you could imagine to an IGD (that's just one of 13 services for an IGD version 1). You'll want to search for "PortMapping" in there to see the parts that might be useful.

It was right around here that I went "meh", I have a client that can find all of the IGD-enabled routers on the networks, and can tell me where the IGD WanIPConnection:1 services are on those routers. I even have the API description telling me what to do to create the port-mapping. Thing is, my own router didn't respond, because, of course, it ships by default with uPNP-IGD disabled (because it's a huge security hole to expose such a huge, featureful API when all you really want to allow is port-mapping)... and honestly, it doesn't look like a very nice API (why are all of the parameters prefixed with "New"?)... but I'm almost there, why not finish the experiment? Well, because it wouldn't even work in my own network, and really, my itch stopped itching around then.


Comments are closed.


Pingbacks are closed.