2.3.2NAT-PMP
2.3.3UPnP
9.0
top
← prev up next →

nat-traversalπŸ”— i

Tony Garnock-Jones <tonygarnockjones@gmail.com>

If you find that this library lacks some feature you need, or you have a suggestion for improving it, please don’t hesitate to get in touch with me!

1IntroductionπŸ”— i

Using this library, you can discover the external IP address of your home router, and can manage port mappings from the public internet to internal TCP and UDP ports.

The library implements

  • NAT-PMP, the Apple/IETF protocol for opening TCP and UDP ports on home routers, and

  • UPnP, in particular the "WANIPConnection" service, the Microsoft et al. protocol for doing the same, plus a whole lot more.

It provides both a low-level interface to each of the two protocols as well as a high-level interface that abstracts away from the details of the particular NAT traversal techniques available.

2How to use the libraryπŸ”— i

2.1The High-Level InterfaceπŸ”— i

The high-level interface to the library lets you automatically manage NAT port mappings by simply changing calls to udp-bind! and tcp-listen to udp-bind!/public and tcp-listen/public , respectively.

Each socket managed by the library is associated with a mapping-change-listener , a background thread that tracks changes to the NAT configuration, keeping a set of "port assignments" up-to-date. A user-supplied callback (on-mapping-change) is called every time the port assignment set changes.

Each port assignment in a set is an address at which the corresponding socket is reachable. A set of port assignments includes both local (internal to the NAT) and public (external to the NAT) addresses.

procedure

( udp-bind!/public udp-socket
hostname-string
port-no
[ #:on-mapping-changeon-mapping-change])
udp-socket:udp?
hostname-string:(or/c string? #f)
on-mapping-change : = void
Does the work of udp-bind! , and opens and starts managing a UDP port mapping at the local NAT.

procedure

( tcp-listen/public port-no
[ max-allow-wait
reuse?
hostname
#:on-mapping-changeon-mapping-change])
max-allow-wait:exact-nonnegative-integer? =4
reuse?:boolean? =#f
hostname:(or/c string? #f)=#f
on-mapping-change : = void
Does the work of tcp-listen , and opens and starts managing a TCP port mapping at the local NAT.

procedure

initial-local-address
local-port
on-mapping-change)
protocol:(or/c 'tcp'udp)
initial-local-address:string?
local-port:(integer-in 065535)
on-mapping-change:(-> (set/c port-assignment? )any/c )
Opens and starts managing a TCP or UDP port mapping at the local NAT for the given port. This routine is the workhorse that both udp-bind!/public and tcp-listen/public delegate to.

#:prefab)
thread:thread?
Handle for a mapping change listener. Useful with mapping-change-listener-stop! and so forth.

struct

(struct port-assignment ( protocol
address
port
nat-traversal-technique)
#:prefab)
protocol:(or/c 'udp'tcp)
address:string?
nat-traversal-technique:(or/c 'nat-pmp'upnp#f)
Record of a particular name for a socket. The protocol, address, and port together form a name that can be used by remote peers to contact the socket. The port-assignment-nat-traversal-technique field is 'nat-pmp or 'upnp for a NAT-originated name, or #f for a local interface name (i.e., built from the results of interface-ip-addresses ).

Retrieves the current set of port assignments from the given change listener. May be useful instead of or in addition to using on-mapping-change.

Deletes any active NAT mappings, and stops the background thread involved in a mapping change listener. Call this while or after closing the associated socket.

2.2Getting information on local gateways and interfacesπŸ”— i

This library provides utilities for discovering and classifying interface IP addresses, and for discovering the local default gateway IP.

procedure

( gateway-ip-address )string?

Retrieves a string representation of the current default gateway IP address, for example "10.0.0.1". Currently, this library learns this information by running the system utility netstat and parsing its output.

Retrieves a list of string representations of the IP addresses associated with the system’s currently-active interfaces. This is done by running the system utility ifconfig and parsing its output.

Returns the "best" value from the list returned by interface-ip-addresses , where

  • the first non-private, non-local IP address found is considered best;

  • the first private, non-local IP address found is considered second-best;

  • any other address found is considered third-best;

  • and if no addresses were found at all, "127.0.0.1" is returned.

procedure

( wildcard-ip-address? addr)boolean?

addr:string?
Returns #t if and only if the argument is the wildcard (a.k.a. INADDR_ANY) IP address string; that is, if it is the string "0.0.0.0".

procedure

( localhost-ip-address? addr)boolean?

addr:string?
Returns #t if and only if the argument is a local IP address string; that is, if it begins with the string "127.".

procedure

( private-ip-address? addr)boolean?

addr:string?
Returns #t if and only if the argument is an IP address in one of the RFC 1918 "private" address ranges; that is, 10.x.y.z, 172.16.x.y through 172.31.x.y, or 192.168.x.y.

2.3Low-level interfacesπŸ”— i

2.3.1Records of established mappingsπŸ”— i

struct

(struct mapping ( protocol
internal-address
internal-port
external-port
lifetime)
#:prefab)
protocol:(or/c 'udp'tcp)
internal-address:(or/c string? #f)
internal-port:integer?
external-port:integer?
lifetime:integer?
A record of an established mapping. In cases where the internal address is not known at the time of mapping (e.g. when using NAT-PMP), mapping-internal-address will be #f.

2.3.2NAT-PMPπŸ”— i

(require nat-traversal/nat-pmp)

NAT-PMP depends on being able to learn the IP address of the current default gateway. It does so by calling gateway-ip-address .

Requests made to the gateway using NAT-PMP will eventually time out if the gateway does not support NAT-PMP. When this happens, an exception is raised by the routine making the request.

procedure

( nat-pmp-external-ip-address)string?

Uses the NAT-PMP protocol to ask the current gateway what the current external IP address is.

procedure

( nat-pmp-make-persistent-mapping
protocol
local-port
requested-port
[ #:refresh-intervalrefresh-interval
#:on-mappingon-mapping])
persistent-mapping?
protocol:(or/c 'udp'tcp)
local-port:integer?
requested-port:integer?
refresh-interval:integer? =3600
on-mapping :
(-> (or/c string? #f)
(or/c mapping?#f)
= void
Establishes a persistent mapping, which will refresh itself in the background every #:refresh-interval seconds until told to stop with stop-persistent-mapping!.

Every time the externally mapped port changes (including when the mapping is first established!) the #:on-mapping callback is called with the updated mapping information. Note that the callback is invoked directly from the mapping’s thread - if it raises an exception, it will kill the persistent mapping.

If you can’t or don’t want to use persistent mapping, the following routines let you explicitly manage mappings with the gateway.

procedure

( nat-pmp-map-port! protocol
local-port
requested-port
lifetime-seconds)mapping?
protocol:(or/c 'udp'tcp)
local-port:integer?
requested-port:integer?
lifetime-seconds:integer?
Creates a mapping at the gateway, requesting that requested-port on the external IP address be mapped to local-port on the IP address of the local machine. The mapping will survive on the gateway for lifetime-seconds seconds or until the router is rebooted. If both requested-port and lifetime-seconds are zero, an existing mapping for local-port is deleted (but use nat-pmp-delete-mapping! instead).

procedure

( nat-pmp-unmap-all-ports!protocol)void?

protocol:(or/c 'udp'tcp)
Unmaps all existing mappings for the local IP address and the given protocol.

procedure

( nat-pmp-refresh-mapping!mapping)mapping?

mapping:mapping?
Refreshes a mapping by extracting its fields and calling nat-pmp-map-port!.

procedure

( nat-pmp-delete-mapping!mapping)void?

mapping:mapping?
Deletes a mapping.

2.3.3UPnPπŸ”— i

(require nat-traversal/upnp-ip-gateway)

TODO

2.3.4Managing low-level persistent mappingsπŸ”— i

procedure

( stop-persistent-mapping!p)void?

p:persistent-mapping?
Deletes and stops refreshing an earlier-established persistent mapping. The mapping is deleted at the gateway where it was established.

procedure

( current-persistent-mappingp)mapping?

p:persistent-mapping?
Retrieves the current mapping details from a persistent mapping.

procedure

( refresh-persistent-mapping!p)void?

p:persistent-mapping?
Overrides the internal timers in the persistent mapping, causing it to refresh itself at its gateway right now. Normal refreshing will resume thereafter.

struct

(struct persistent-mapping(thread)
#:prefab)
thread:thread?
Handle for a persistent mapping. Useful with stop-persistent-mapping!, etc.

2.3.5Calling other UPnP servicesπŸ”— i

(require nat-traversal/upnp)

Routines for discovering UPnP services and calling service actions.

parameter

( default-scan-time)exact-nonnegative-integer?

(default-scan-timeseconds)void?
Defines the number of seconds that an in-upnp-services discovery scan will remain active for.

struct

(struct exn:fail:upnpexn:fail (codedescription)
#:transparent)
Exception thrown upon a UPnP-related SOAP fault or failure.

procedure

( exn:fail:upnp?/codecode)(-> any/c boolean? )

Returns a predicate that returns #t if and only if its argument is an exn:fail:upnp? with exn:fail:upnp-code equal to code.

struct

(struct upnp-service(typecontrol-urlevent-urlscpd-url)
#:prefab)
type:string?
control-url:url?
event-url:url?
scpd-url:url?
Describes a UPnP service. The upnp-service-type will be a URI.

struct

(struct upnp-service-action(nameargsresults)
#:prefab)
name:string?
args:(listof string? )
results:(listof string? )
Describes a particular action available on a UPnP service. The upnp-service-action-args are the names of the arguments expected from the caller, and the upnp-service-action-results are the names of the results expected to be returned from the server.

procedure

( in-upnp-services[#:scan-timescan-time])

(sequenceof(case-> (-> 'descriptorupnp-service?)
(-> 'actions(hash/c string? upnp-service-action?))
scan-time:exact-nonnegative-integer? =(default-scan-time)
Produces a sequence of service dispatchers, one for each discovered UPnP service on the local network. Yields each dispatcher as it is discovered. Continues producing values as they arrive until scan-time seconds have elapsed, at which point the sequence ends.

Each of the yielded dispatchers is a procedure taking a variable number of arguments:

  • If the first argument is 'descriptor, the underlying upnp-service is returned.

  • If the first argument is 'actions, a hashtable mapping action name strings to upnp-service-action instances is returned.

  • Otherwise, the first argument must be a string naming an action supported by the service being dispatched to. The number of additional arguments must be equal to the number of arguments expected by the named action, and they must all be strings. The result will be a hashtable mapping result name to string result value.

procedure

( upnp-service-type=?stype)boolean?

s:upnp-service?
type:string?
Returns #t if and only if (upnp-service-types) is equal to type.

3ReferencesπŸ”— i

NAT-PMP is currently defined in an Internet-Draft.

UPnP is a vast expanse of entangled specification.

  • The core discovery mechanism is SSDP.

  • Everything else in UPnP is done with SOAP (!) over HTTP.

  • You can download the 70MB (!) specification zip file from the UPnP forum. Fair warning, it’s not an easy read.

top
← prev up next →

AltStyle γ«γ‚ˆγ£γ¦ε€‰ζ›γ•γ‚ŒγŸγƒšγƒΌγ‚Έ (->γ‚ͺγƒͺγ‚ΈγƒŠγƒ«) /