nat-traversal
1 Introduction
2 How to use the library
2.1 The High-Level Interface
udp-bind!/  public
tcp-listen/  public
mapping-change-listener
mapping-change-listener
port-assignment
mapping-change-listener-current-mappings
mapping-change-listener-stop!
2.2 Getting information on local gateways and interfaces
gateway-ip-address
interface-ip-addresses
best-interface-ip-address
wildcard-ip-address?
localhost-ip-address?
private-ip-address?
2.3 Low-level interfaces
2.3.1 Records of established mappings
mapping
2.3.2 NAT-PMP
nat-pmp-external-ip-address
nat-pmp-make-persistent-mapping
nat-pmp-map-port!
nat-pmp-unmap-all-ports!
nat-pmp-refresh-mapping!
nat-pmp-delete-mapping!
2.3.3 UPn  P
2.3.4 Managing low-level persistent mappings
stop-persistent-mapping!
current-persistent-mapping
refresh-persistent-mapping!
persistent-mapping
2.3.5 Calling other UPn  P services
default-scan-time
exn:  fail:  upnp
exn:  fail:  upnp?/  code
upnp-service
upnp-service-action
in-upnp-services
upnp-service-type=?
3 References
8.12

nat-traversal🔗ℹ

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!

1 Introduction🔗ℹ

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

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.

2 How to use the library🔗ℹ

 (require nat-traversal) package: nat-traversal

2.1 The High-Level Interface🔗ℹ

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-change on-mapping-change]) 
  mapping-change-listener?
  udp-socket : udp?
  hostname-string : (or/c string? #f)
  port-no : 
(and/c exact-nonnegative-integer?
       (integer-in 0 65535))
  on-mapping-change : 
(-> (set/c port-assignment?)
    any/c)
 = 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-change on-mapping-change]) 
  
tcp-listener? mapping-change-listener?
  port-no : 
(and/c exact-nonnegative-integer?
       (integer-in 0 65535))
  max-allow-wait : exact-nonnegative-integer? = 4
  reuse? : boolean? = #f
  hostname : (or/c string? #f) = #f
  on-mapping-change : 
(-> (set/c port-assignment?)
    any/c)
 = void
Does the work of tcp-listen, and opens and starts managing a TCP port mapping at the local NAT.

procedure

(mapping-change-listener protocol 
  initial-local-address 
  local-port 
  on-mapping-change) 
  mapping-change-listener?
  protocol : (or/c 'tcp 'udp)
  initial-local-address : string?
  local-port : (integer-in 0 65535)
  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.

struct

(struct mapping-change-listener (thread)
    #: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?
  port : exact-nonnegative-integer?
  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.2 Getting information on local gateways and interfaces🔗ℹ

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

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.3 Low-level interfaces🔗ℹ

2.3.1 Records of established mappings🔗ℹ

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.2 NAT-PMP🔗ℹ

(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-interval refresh-interval 
  #:on-mapping on-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)
    any/c)
 = 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.3 UPnP🔗ℹ

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

TODO

2.3.4 Managing low-level persistent mappings🔗ℹ

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-mapping p)  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.5 Calling other UPnP services🔗ℹ

(require nat-traversal/upnp)

Routines for discovering UPnP services and calling service actions.

parameter

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

(default-scan-time seconds)  void?
  seconds : exact-nonnegative-integer?
Defines the number of seconds that an in-upnp-services discovery scan will remain active for.

struct

(struct exn:fail:upnp exn:fail (code description)
    #:transparent)
  code : (or/c exact-nonnegative-integer? #f)
  description : (or/c exact-nonnegative-integer? #f)
Exception thrown upon a UPnP-related SOAP fault or failure.

procedure

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

  code : exact-nonnegative-integer?
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 (type control-url event-url scpd-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 (name args results)
    #: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-time scan-time])

  
(sequenceof (case-> (-> 'descriptor upnp-service?)
                    (-> 'actions (hash/c string? upnp-service-action?))
                    (-> string? #:rest (listof string?) (hash/c string? string?))))
  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:

procedure

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

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

3 References🔗ℹ

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

UPnP is a vast expanse of entangled specification.