gnunet-svn
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[GNUnet-SVN] [gnunet] branch master updated: misc. improvements to new N


From: gnunet
Subject: [GNUnet-SVN] [gnunet] branch master updated: misc. improvements to new NAT service, starting with autoconfiguration logic
Date: Sat, 17 Dec 2016 08:00:45 +0100

This is an automated email from the git hooks/post-receive script.

grothoff pushed a commit to branch master
in repository gnunet.

The following commit(s) were added to refs/heads/master by this push:
     new 545faed  misc. improvements to new NAT service, starting with 
autoconfiguration logic
545faed is described below

commit 545faeda8f7fb7e03bb39602df6142f630157050
Author: Christian Grothoff <address@hidden>
AuthorDate: Sat Dec 17 08:00:29 2016 +0100

    misc. improvements to new NAT service, starting with autoconfiguration logic
---
 src/include/gnunet_nat_service.h  |    2 +-
 src/nat/Makefile.am               |    1 +
 src/nat/gnunet-nat.c              |    2 +-
 src/nat/gnunet-service-nat.c      | 1133 ++++++++++++++++++++++++-------------
 src/nat/gnunet-service-nat_mini.c |  759 +++++++++++++++++++++++++
 src/nat/gnunet-service-nat_mini.h |  127 +++++
 src/nat/nat.h                     |    2 +-
 src/nat/nat_api.c                 |    7 +
 src/util/util.conf                |   10 +
 9 files changed, 1649 insertions(+), 394 deletions(-)

diff --git a/src/include/gnunet_nat_service.h b/src/include/gnunet_nat_service.h
index b66b442..a42d1d7 100644
--- a/src/include/gnunet_nat_service.h
+++ b/src/include/gnunet_nat_service.h
@@ -83,7 +83,7 @@ enum GNUNET_NAT_AddressClass
   /**
    * Addresses useful in the local wired network,
    * i.e. a MAC.  Sensitive, but obvious to people nearby.
-
+   *
    * Useful for broadcasts.
    */
   GNUNET_NAT_AC_LAN = 8,
diff --git a/src/nat/Makefile.am b/src/nat/Makefile.am
index c62a8d2..1dd8e44 100644
--- a/src/nat/Makefile.am
+++ b/src/nat/Makefile.am
@@ -98,6 +98,7 @@ libgnunetnatnew_la_LDFLAGS = \
 gnunet_service_nat_SOURCES = \
  gnunet-service-nat.c \
  gnunet-service-nat_stun.c gnunet-service-nat_stun.h \
+ gnunet-service-nat_mini.c gnunet-service-nat_mini.h \
  gnunet-service-nat_helper.c gnunet-service-nat_helper.h
 gnunet_service_nat_LDADD = \
   $(top_builddir)/src/util/libgnunetutil.la \
diff --git a/src/nat/gnunet-nat.c b/src/nat/gnunet-nat.c
index 1092115..7d10167 100644
--- a/src/nat/gnunet-nat.c
+++ b/src/nat/gnunet-nat.c
@@ -611,7 +611,7 @@ main (int argc,
      gettext_noop ("which external IP and port should be used to test"),
      GNUNET_YES, &GNUNET_GETOPT_set_string, &extern_addr },
     {'i', "in", "ADDRESS",
-     gettext_noop ("which IP and port are we locally using to listen to for 
connection reversals"),
+     gettext_noop ("which IP and port are we locally using to bind/listen to"),
      GNUNET_YES, &GNUNET_GETOPT_set_string, &local_addr },
     {'r', "remote", "ADDRESS",
      gettext_noop ("which remote IP and port should be asked for connection 
reversal"),
diff --git a/src/nat/gnunet-service-nat.c b/src/nat/gnunet-service-nat.c
index 4ad6c8d..af1219d 100644
--- a/src/nat/gnunet-service-nat.c
+++ b/src/nat/gnunet-service-nat.c
@@ -40,6 +40,7 @@
 #include "gnunet_statistics_service.h"
 #include "gnunet_nat_service.h"
 #include "gnunet-service-nat_stun.h"
+#include "gnunet-service-nat_mini.h"
 #include "gnunet-service-nat_helper.h"
 #include "nat.h"
 #include <gcrypt.h>
@@ -51,6 +52,11 @@
  */
 #define SCAN_FREQ GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 15)
 
+/**
+ * How long do we wait until we forcefully terminate autoconfiguration?
+ */
+#define AUTOCONFIG_TIMEOUT GNUNET_TIME_relative_multiply 
(GNUNET_TIME_UNIT_SECONDS, 5)
+
 
 /**
  * Internal data structure we track for each of our clients.
@@ -187,6 +193,75 @@ struct StunExternalIP
 
 
 /**
+ * Context for autoconfiguration operations.
+ */
+struct AutoconfigContext
+{
+  /**
+   * Kept in a DLL.
+   */
+  struct AutoconfigContext *prev;
+
+  /**
+   * Kept in a DLL.
+   */
+  struct AutoconfigContext *next;
+
+  /**
+   * Which client asked the question.
+   */
+  struct ClientHandle *ch;
+
+  /**
+   * Configuration we are creating.
+   */ 
+  struct GNUNET_CONFIGURATION_Handle *c;
+
+  /**
+   * Timeout task to force termination.
+   */
+  struct GNUNET_SCHEDULER_Task *timeout_task;
+
+  /**
+   * What type of system are we on?
+   */
+  char *system_type;
+
+  /**
+   * Handle to activity to probe for our external IP.
+   */
+  struct GNUNET_NAT_ExternalHandle *probe_external;
+
+  /**
+   * #GNUNET_YES if upnpc should be used,
+   * #GNUNET_NO if upnpc should not be used,
+   * #GNUNET_SYSERR if we should simply not change the option.
+   */
+  int enable_upnpc;
+
+  /**
+   * Status code to return to the client.
+   */
+  enum GNUNET_NAT_StatusCode status_code;
+
+  /**
+   * NAT type to return to the client.
+   */
+  enum GNUNET_NAT_Type type;
+};
+
+
+/**
+ * DLL of our autoconfiguration operations.
+ */
+static struct AutoconfigContext *ac_head;
+
+/**
+ * DLL of our autoconfiguration operations.
+ */
+static struct AutoconfigContext *ac_tail;
+
+/**
  * Timeout to use when STUN data is considered stale.
  */
 static struct GNUNET_TIME_Relative stun_stale_timeout;
@@ -236,6 +311,12 @@ static struct StunExternalIP *se_head;
  */ 
 static struct StunExternalIP *se_tail;
 
+/**
+ * Is UPnP enabled? #GNUNET_YES if enabled, #GNUNET_NO if disabled,
+ * #GNUNET_SYSERR if configuration enabled but binary is unavailable.
+ */
+static int enable_upnp;
+
 
 /**
  * Free the DLL starting at #lal_head.
@@ -322,7 +403,9 @@ match_ipv4 (const char *network,
            uint8_t bits)
 {
   struct in_addr net;
-  
+
+  if (0 == ip->s_addr)
+    return GNUNET_YES;
   if (0 == bits)
     return GNUNET_YES;
   GNUNET_assert (1 == inet_pton (AF_INET,
@@ -355,6 +438,10 @@ match_ipv6 (const char *network,
                                 network,
                                 &net));
   memset (&mask, 0, sizeof (mask));
+  if (0 == memcmp (&mask,
+                  ip,
+                  sizeof (mask)))
+    return GNUNET_YES;
   off = 0;
   while (bits > 8)
   {
@@ -411,145 +498,444 @@ is_nat_v6 (const struct in6_addr *ip)
 
 
 /**
- * Handler for #GNUNET_MESSAGE_TYPE_NAT_REGISTER message from client.
- * We remember the client for updates upon future NAT events.
- *
- * @param cls client who sent the message
- * @param message the message received
+ * Closure for #ifc_proc.
  */
-static void
-handle_register (void *cls,
-                const struct GNUNET_NAT_RegisterMessage *message)
+struct IfcProcContext
 {
-  struct ClientHandle *ch = cls;
-  const char *off;
-  size_t left;
 
-  if ( (0 != ch->proto) ||
-       (NULL != ch->addrs) )
-  {
-    /* double registration not allowed */
-    GNUNET_break (0);
-    GNUNET_SERVICE_client_drop (ch->client);
-    return;
-  }
-  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
-             "Received REGISTER message from client\n");
-  ch->flags = message->flags;
-  ch->proto = message->proto;
-  ch->adv_port = ntohs (message->adv_port);
-  ch->num_addrs = ntohs (message->adv_port);
-  ch->addrs = GNUNET_new_array (ch->num_addrs,
-                               struct sockaddr *);
-  left = ntohs (message->header.size) - sizeof (*message);
-  off = (const char *) &message[1];
-  for (unsigned int i=0;i<ch->num_addrs;i++)
-  {
-    size_t alen;
-    const struct sockaddr *sa = (const struct sockaddr *) off;
+  /** 
+   * Head of DLL of local addresses.
+   */
+  struct LocalAddressList *lal_head;
 
-    if (sizeof (sa_family_t) > left)
-    {
-      GNUNET_break (0);
-      GNUNET_SERVICE_client_drop (ch->client);
-      return;
-    }
-    switch (sa->sa_family)
-    {
-    case AF_INET:
-      {
-       const struct sockaddr_in *s4 = (const struct sockaddr_in *) sa;
-       
-       alen = sizeof (struct sockaddr_in);
-       if (is_nat_v4 (&s4->sin_addr))
-         ch->natted_address = GNUNET_YES;
-      }
-      break;
-    case AF_INET6:
-      {
-       const struct sockaddr_in6 *s6 = (const struct sockaddr_in6 *) sa;
-       
-       alen = sizeof (struct sockaddr_in6);
-       if (is_nat_v6 (&s6->sin6_addr))
-         ch->natted_address = GNUNET_YES;
-      }
-      break;
-#if AF_UNIX
-    case AF_UNIX:
-      alen = sizeof (struct sockaddr_un);
-      break;
-#endif
-    default:
-      GNUNET_break (0);
-      GNUNET_SERVICE_client_drop (ch->client);
-      return;      
-    }
-    GNUNET_assert (alen <= left);
-    ch->addrs[i] = GNUNET_malloc (alen);
-    GNUNET_memcpy (ch->addrs[i],
-                  sa,
-                  alen);    
-    off += alen;
-  }  
-  GNUNET_SERVICE_client_continue (ch->client);
-}
+  /**
+   * Tail of DLL of local addresses.
+   */
+  struct LocalAddressList *lal_tail;
+
+};
 
 
 /**
- * Check validity of #GNUNET_MESSAGE_TYPE_NAT_HANDLE_STUN message from
- * client.
+ * Callback function invoked for each interface found.  Adds them
+ * to our new address list.
  *
- * @param cls client who sent the message
- * @param message the message received
- * @return #GNUNET_OK if message is well-formed
+ * @param cls a `struct IfcProcContext *`
+ * @param name name of the interface (can be NULL for unknown)
+ * @param isDefault is this presumably the default interface
+ * @param addr address of this interface (can be NULL for unknown or 
unassigned)
+ * @param broadcast_addr the broadcast address (can be NULL for unknown or 
unassigned)
+ * @param netmask the network mask (can be NULL for unknown or unassigned)
+ * @param addrlen length of the address
+ * @return #GNUNET_OK to continue iteration, #GNUNET_SYSERR to abort
  */
 static int
-check_stun (void *cls,
-           const struct GNUNET_NAT_HandleStunMessage *message)
+ifc_proc (void *cls,
+         const char *name,
+         int isDefault,
+         const struct sockaddr *addr,
+         const struct sockaddr *broadcast_addr,
+         const struct sockaddr *netmask,
+         socklen_t addrlen)
 {
-  size_t sa_len = ntohs (message->sender_addr_size);
-  size_t expect = sa_len + ntohs (message->payload_size);
-  
-  if (ntohs (message->header.size) - sizeof (*message) != expect)
+  struct IfcProcContext *ifc_ctx = cls;
+  struct LocalAddressList *lal;
+  size_t alen;
+  const struct in_addr *ip4;
+  const struct in6_addr *ip6;
+  enum GNUNET_NAT_AddressClass ac;
+
+  switch (addr->sa_family)
   {
+  case AF_INET:
+    alen = sizeof (struct sockaddr_in);
+    ip4 = &((const struct sockaddr_in *) addr)->sin_addr;
+    if (match_ipv4 ("127.0.0.0", ip4, 8))
+      ac = GNUNET_NAT_AC_LOOPBACK;
+    else if (is_nat_v4 (ip4))
+      ac = GNUNET_NAT_AC_LAN;
+    else
+      ac = GNUNET_NAT_AC_GLOBAL;
+    break;
+  case AF_INET6:
+    alen = sizeof (struct sockaddr_in6);
+    ip6 = &((const struct sockaddr_in6 *) addr)->sin6_addr;
+    if (match_ipv6 ("::1", ip6, 128))
+      ac = GNUNET_NAT_AC_LOOPBACK;
+    else if (is_nat_v6 (ip6))
+      ac = GNUNET_NAT_AC_LAN;
+    else
+      ac = GNUNET_NAT_AC_GLOBAL;
+    if ( (ip6->s6_addr[11] == 0xFF) &&
+        (ip6->s6_addr[12] == 0xFE) )
+    {
+      /* contains a MAC, be extra careful! */
+      ac |= GNUNET_NAT_AC_PRIVATE;
+    }
+    break;
+#if AF_UNIX
+  case AF_UNIX:
     GNUNET_break (0);
-    return GNUNET_SYSERR;
-  }
-  if (sa_len < sizeof (sa_family_t))
-  {
+    return GNUNET_OK;
+#endif
+  default:
     GNUNET_break (0);
-    return GNUNET_SYSERR;
+    return GNUNET_OK;
   }
+  lal = GNUNET_malloc (sizeof (*lal));
+  lal->af = addr->sa_family;
+  lal->ac = ac;
+  GNUNET_memcpy (&lal->addr,
+                addr,
+                alen);
+  GNUNET_CONTAINER_DLL_insert (ifc_ctx->lal_head,
+                              ifc_ctx->lal_tail,
+                              lal);
   return GNUNET_OK;
 }
 
 
 /**
- * Notify all clients about our external IP address
- * as reported by the STUN server.
+ * Notify client about a change in the list of addresses this peer
+ * has.
  *
- * @param ip the external IP
+ * @param delta the entry in the list that changed
+ * @param ch client to contact
  * @param add #GNUNET_YES to add, #GNUNET_NO to remove
+ * @param addr the address that changed
+ * @param addr_len number of bytes in @a addr
  */
 static void
-notify_clients_stun_change (const struct sockaddr_in *ip,
-                           int add)
+notify_client (struct LocalAddressList *delta,
+              struct ClientHandle *ch,
+              int add,
+              const void *addr,
+              size_t addr_len)
 {
-  for (struct ClientHandle *ch = ch_head;
-       NULL != ch;
-       ch = ch->next)
-  {
-    struct sockaddr_in v4;
-    struct GNUNET_NAT_AddressChangeNotificationMessage *msg;
-    struct GNUNET_MQ_Envelope *env;
-    
-    if (! ch->natted_address)
-      continue;
-    v4 = *ip;
-    v4.sin_port = htons (ch->adv_port);
-    env = GNUNET_MQ_msg_extra (msg,
-                              sizeof (v4),
-                              GNUNET_MESSAGE_TYPE_NAT_ADDRESS_CHANGE);
+  struct GNUNET_MQ_Envelope *env;
+  struct GNUNET_NAT_AddressChangeNotificationMessage *msg;
+  
+  env = GNUNET_MQ_msg_extra (msg,
+                            addr_len,
+                            GNUNET_MESSAGE_TYPE_NAT_ADDRESS_CHANGE);
+  msg->add_remove = htonl (add);
+  msg->addr_class = htonl (delta->ac);
+  GNUNET_memcpy (&msg[1],
+                addr,
+                addr_len);
+  GNUNET_MQ_send (ch->mq,
+                 env);
+}                     
+
+
+/**
+ * Check if we should bother to notify this client about this
+ * address change, and if so, do it.
+ *
+ * @param delta the entry in the list that changed
+ * @param ch client to check
+ * @param add #GNUNET_YES to add, #GNUNET_NO to remove
+ */
+static void
+check_notify_client (struct LocalAddressList *delta,
+                    struct ClientHandle *ch,
+                    int add)
+{
+  size_t alen;
+  struct sockaddr_in v4;
+  struct sockaddr_in6 v6;
+  
+  if (0 == (ch->flags & GNUNET_NAT_RF_ADDRESSES))
+    return;
+  switch (delta->af)
+  {
+  case AF_INET:
+    alen = sizeof (struct sockaddr_in);
+    GNUNET_memcpy (&v4,
+                  &delta->addr,
+                  alen);
+    for (unsigned int i=0;i<ch->num_addrs;i++)
+    {
+      const struct sockaddr_in *c4;
+      
+      if (AF_INET != ch->addrs[i]->sa_family)
+       return; /* IPv4 not relevant */
+      c4 = (const struct sockaddr_in *) ch->addrs[i];
+      v4.sin_port = c4->sin_port;
+      notify_client (delta,
+                    ch,
+                    add,
+                    &v4,
+                    alen);
+    }
+    break;
+  case AF_INET6:
+    alen = sizeof (struct sockaddr_in6);
+    GNUNET_memcpy (&v6,
+                  &delta->addr,
+                  alen);
+    for (unsigned int i=0;i<ch->num_addrs;i++)
+    {
+      const struct sockaddr_in6 *c6;
+      
+      if (AF_INET6 != ch->addrs[i]->sa_family)
+       return; /* IPv4 not relevant */
+      c6 = (const struct sockaddr_in6 *) ch->addrs[i];
+      v6.sin6_port = c6->sin6_port;
+      notify_client (delta,
+                    ch,
+                    add,
+                    &v6,
+                    alen);
+    }
+    break;
+  default:
+    GNUNET_break (0);
+    return;
+  }
+}
+
+
+/**
+ * Notify all clients about a change in the list
+ * of addresses this peer has.
+ *
+ * @param delta the entry in the list that changed
+ * @param add #GNUNET_YES to add, #GNUNET_NO to remove
+ */
+static void
+notify_clients (struct LocalAddressList *delta,
+               int add)
+{
+  for (struct ClientHandle *ch = ch_head;
+       NULL != ch;
+       ch = ch->next)
+    check_notify_client (delta,
+                        ch,
+                        add);
+}
+
+
+/**
+ * Task we run periodically to scan for network interfaces.
+ *
+ * @param cls NULL
+ */ 
+static void
+run_scan (void *cls)
+{
+  struct IfcProcContext ifc_ctx;
+  int found;
+  
+  scan_task = GNUNET_SCHEDULER_add_delayed (SCAN_FREQ,
+                                           &run_scan,
+                                           NULL);
+  memset (&ifc_ctx,
+         0,
+         sizeof (ifc_ctx));
+  GNUNET_OS_network_interfaces_list (&ifc_proc,
+                                    &ifc_ctx);
+  /* remove addresses that disappeared */
+  for (struct LocalAddressList *lal = lal_head;
+       NULL != lal;
+       lal = lal->next)
+  {
+    found = GNUNET_NO;
+    for (struct LocalAddressList *pos = ifc_ctx.lal_head;
+        NULL != pos;
+        pos = pos->next)
+    {
+      if ( (pos->af == lal->af) &&
+          (0 == memcmp (&lal->addr,
+                        &pos->addr,
+                        (AF_INET == lal->af)
+                        ? sizeof (struct sockaddr_in)
+                        : sizeof (struct sockaddr_in6))) )
+       found = GNUNET_YES;
+    }
+    if (GNUNET_NO == found)
+      notify_clients (lal,
+                     GNUNET_NO);
+  }
+
+  /* add addresses that appeared */
+  for (struct LocalAddressList *pos = ifc_ctx.lal_head;
+       NULL != pos;
+       pos = pos->next)
+  {
+    found = GNUNET_NO;
+    for (struct LocalAddressList *lal = lal_head;
+        NULL != lal;
+        lal = lal->next)
+    {
+      if ( (pos->af == lal->af) &&
+          (0 == memcmp (&lal->addr,
+                        &pos->addr,
+                        (AF_INET == lal->af)
+                        ? sizeof (struct sockaddr_in)
+                        : sizeof (struct sockaddr_in6))) )
+       found = GNUNET_YES;
+    }
+    if (GNUNET_NO == found)
+      notify_clients (pos,
+                     GNUNET_YES);
+  }
+
+  destroy_lal ();
+  lal_head = ifc_ctx.lal_head;
+  lal_tail = ifc_ctx.lal_tail;
+}
+
+
+/**
+ * Handler for #GNUNET_MESSAGE_TYPE_NAT_REGISTER message from client.
+ * We remember the client for updates upon future NAT events.
+ *
+ * @param cls client who sent the message
+ * @param message the message received
+ */
+static void
+handle_register (void *cls,
+                const struct GNUNET_NAT_RegisterMessage *message)
+{
+  struct ClientHandle *ch = cls;
+  const char *off;
+  size_t left;
+
+  if ( (0 != ch->proto) ||
+       (NULL != ch->addrs) )
+  {
+    /* double registration not allowed */
+    GNUNET_break (0);
+    GNUNET_SERVICE_client_drop (ch->client);
+    return;
+  }
+  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+             "Received REGISTER message from client\n");
+  ch->flags = message->flags;
+  ch->proto = message->proto;
+  ch->adv_port = ntohs (message->adv_port);
+  ch->num_addrs = ntohs (message->num_addrs);
+  ch->addrs = GNUNET_new_array (ch->num_addrs,
+                               struct sockaddr *);
+  left = ntohs (message->header.size) - sizeof (*message);
+  off = (const char *) &message[1];
+  for (unsigned int i=0;i<ch->num_addrs;i++)
+  {
+    size_t alen;
+    const struct sockaddr *sa = (const struct sockaddr *) off;
+
+    if (sizeof (sa_family_t) > left)
+    {
+      GNUNET_break (0);
+      GNUNET_SERVICE_client_drop (ch->client);
+      return;
+    }
+    switch (sa->sa_family)
+    {
+    case AF_INET:
+      {
+       const struct sockaddr_in *s4 = (const struct sockaddr_in *) sa;
+       
+       alen = sizeof (struct sockaddr_in);
+       if (is_nat_v4 (&s4->sin_addr))
+         ch->natted_address = GNUNET_YES;
+      }
+      break;
+    case AF_INET6:
+      {
+       const struct sockaddr_in6 *s6 = (const struct sockaddr_in6 *) sa;
+       
+       alen = sizeof (struct sockaddr_in6);
+       if (is_nat_v6 (&s6->sin6_addr))
+         ch->natted_address = GNUNET_YES;
+      }
+      break;
+#if AF_UNIX
+    case AF_UNIX:
+      alen = sizeof (struct sockaddr_un);
+      break;
+#endif
+    default:
+      GNUNET_break (0);
+      GNUNET_SERVICE_client_drop (ch->client);
+      return;      
+    }
+    GNUNET_assert (alen <= left);
+    ch->addrs[i] = GNUNET_malloc (alen);
+    GNUNET_memcpy (ch->addrs[i],
+                  sa,
+                  alen);    
+    off += alen;
+  }
+  /* Actually send IP address list to client */
+  for (struct LocalAddressList *lal = lal_head;
+       NULL != lal;
+       lal = lal->next)
+  {
+    check_notify_client (lal,
+                        ch,
+                        GNUNET_YES);
+  }
+  GNUNET_SERVICE_client_continue (ch->client);
+}
+
+
+/**
+ * Check validity of #GNUNET_MESSAGE_TYPE_NAT_HANDLE_STUN message from
+ * client.
+ *
+ * @param cls client who sent the message
+ * @param message the message received
+ * @return #GNUNET_OK if message is well-formed
+ */
+static int
+check_stun (void *cls,
+           const struct GNUNET_NAT_HandleStunMessage *message)
+{
+  size_t sa_len = ntohs (message->sender_addr_size);
+  size_t expect = sa_len + ntohs (message->payload_size);
+  
+  if (ntohs (message->header.size) - sizeof (*message) != expect)
+  {
+    GNUNET_break (0);
+    return GNUNET_SYSERR;
+  }
+  if (sa_len < sizeof (sa_family_t))
+  {
+    GNUNET_break (0);
+    return GNUNET_SYSERR;
+  }
+  return GNUNET_OK;
+}
+
+
+/**
+ * Notify all clients about our external IP address
+ * as reported by the STUN server.
+ *
+ * @param ip the external IP
+ * @param add #GNUNET_YES to add, #GNUNET_NO to remove
+ */
+static void
+notify_clients_stun_change (const struct sockaddr_in *ip,
+                           int add)
+{
+  for (struct ClientHandle *ch = ch_head;
+       NULL != ch;
+       ch = ch->next)
+  {
+    struct sockaddr_in v4;
+    struct GNUNET_NAT_AddressChangeNotificationMessage *msg;
+    struct GNUNET_MQ_Envelope *env;
+    
+    if (! ch->natted_address)
+      continue;
+    v4 = *ip;
+    v4.sin_port = htons (ch->adv_port);
+    env = GNUNET_MQ_msg_extra (msg,
+                              sizeof (v4),
+                              GNUNET_MESSAGE_TYPE_NAT_ADDRESS_CHANGE);
     msg->add_remove = htonl ((int32_t) add);
     msg->addr_class = htonl (GNUNET_NAT_AC_GLOBAL_EXTERN |
                             GNUNET_NAT_AC_GLOBAL);
@@ -826,344 +1212,289 @@ check_autoconfig_request (void *cls,
 
 
 /**
- * Handler for #GNUNET_MESSAGE_TYPE_NAT_REQUEST_AUTO_CFG message from
- * client.
+ * Stop all pending activities with respect to the @a ac
  *
- * @param cls client who sent the message
- * @param message the message received
+ * @param ac autoconfiguration to terminate activities for
  */
 static void
-handle_autoconfig_request (void *cls,
-                          const struct GNUNET_NAT_AutoconfigRequestMessage 
*message)
+terminate_ac_activities (struct AutoconfigContext *ac)
 {
-  struct ClientHandle *ch = cls;
-  size_t left = ntohs (message->header.size) - sizeof (*message);
-  struct GNUNET_CONFIGURATION_Handle *c;
-
-  c = GNUNET_CONFIGURATION_create ();
-  if (GNUNET_OK !=
-      GNUNET_CONFIGURATION_deserialize (c,
-                                       (const char *) &message[1],
-                                       left,
-                                       GNUNET_NO))
+  if (NULL != ac->probe_external)
   {
-    GNUNET_break (0);
-    GNUNET_SERVICE_client_drop (ch->client);
-    return;
+    GNUNET_NAT_mini_get_external_ipv4_cancel_ (ac->probe_external);
+    ac->probe_external = NULL;
   }
-  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
-             "Received REQUEST_AUTO_CONFIG message from client\n");
-  // FIXME: actually handle request...
-  GNUNET_CONFIGURATION_destroy (c);
-  GNUNET_SERVICE_client_continue (ch->client);
-}
-
-
-/**
- * Task run during shutdown.
- *
- * @param cls unused
- */
-static void
-shutdown_task (void *cls)
-{
-  struct StunExternalIP *se;
-
-  while (NULL != (se = se_head))
-  {
-    GNUNET_CONTAINER_DLL_remove (se_head,
-                                se_tail,
-                                se);
-    GNUNET_SCHEDULER_cancel (se->timeout_task);
-    GNUNET_free (se);
-  }
-  if (NULL != scan_task)
-  {
-    GNUNET_SCHEDULER_cancel (scan_task);
-    scan_task = NULL;
-  }
-  if (NULL != stats)
+  if (NULL != ac->timeout_task)
   {
-    GNUNET_STATISTICS_destroy (stats, GNUNET_NO);
-    stats = NULL;
+    GNUNET_SCHEDULER_cancel (ac->timeout_task);
+    ac->timeout_task = NULL;
   }
-  destroy_lal ();
 }
 
 
 /**
- * Closure for #ifc_proc.
+ * Finish handling the autoconfiguration request and send
+ * the response to the client.
+ *
+ * @param cls the `struct AutoconfigContext` to conclude
  */
-struct IfcProcContext
+static void
+conclude_autoconfig_request (void *cls)
 {
+  struct AutoconfigContext *ac = cls;
+  struct ClientHandle *ch = ac->ch;
+  struct GNUNET_NAT_AutoconfigResultMessage *arm;
+  struct GNUNET_MQ_Envelope *env;
+  size_t c_size;
+  char *buf;
+
+  ac->timeout_task = NULL;
+  terminate_ac_activities (ac);
+
+  /* Send back response */
+  buf = GNUNET_CONFIGURATION_serialize (ac->c,
+                                       &c_size);
+  env = GNUNET_MQ_msg_extra (arm,
+                            c_size,
+                            GNUNET_MESSAGE_TYPE_NAT_AUTO_CFG_RESULT);
+  arm->status_code = htonl ((uint32_t) ac->status_code);
+  arm->type = htonl ((uint32_t) ac->type);
+  GNUNET_memcpy (&arm[1],
+                buf,
+                c_size);
+  GNUNET_free (buf);
+  GNUNET_MQ_send (ch->mq,
+                 env);
 
-  /** 
-   * Head of DLL of local addresses.
-   */
-  struct LocalAddressList *lal_head;
-
-  /**
-   * Tail of DLL of local addresses.
-   */
-  struct LocalAddressList *lal_tail;
-
-};
+  /* clean up */
+  GNUNET_free (ac->system_type);
+  GNUNET_CONFIGURATION_destroy (ac->c);
+  GNUNET_CONTAINER_DLL_remove (ac_head,
+                              ac_tail,
+                              ac);
+  GNUNET_free (ac);
+  GNUNET_SERVICE_client_continue (ch->client);
+}
 
 
 /**
- * Callback function invoked for each interface found.  Adds them
- * to our new address list.
+ * Check if all autoconfiguration operations have concluded,
+ * and if they have, send the result back to the client.
  *
- * @param cls a `struct IfcProcContext *`
- * @param name name of the interface (can be NULL for unknown)
- * @param isDefault is this presumably the default interface
- * @param addr address of this interface (can be NULL for unknown or 
unassigned)
- * @param broadcast_addr the broadcast address (can be NULL for unknown or 
unassigned)
- * @param netmask the network mask (can be NULL for unknown or unassigned)
- * @param addrlen length of the address
- * @return #GNUNET_OK to continue iteration, #GNUNET_SYSERR to abort
+ * @param ac autoconfiguation context to check
  */
-static int
-ifc_proc (void *cls,
-         const char *name,
-         int isDefault,
-         const struct sockaddr *addr,
-         const struct sockaddr *broadcast_addr,
-         const struct sockaddr *netmask,
-         socklen_t addrlen)
+static void
+check_autoconfig_finished (struct AutoconfigContext *ac)
 {
-  struct IfcProcContext *ifc_ctx = cls;
-  struct LocalAddressList *lal;
-  size_t alen;
-  const struct in_addr *ip4;
-  const struct in6_addr *ip6;
-  enum GNUNET_NAT_AddressClass ac;
+  if (NULL != ac->probe_external)
+    return;
+  GNUNET_SCHEDULER_cancel (ac->timeout_task);
+  ac->timeout_task
+    = GNUNET_SCHEDULER_add_now (&conclude_autoconfig_request,
+                               ac);
+}
 
-  switch (addr->sa_family)
+
+/**
+ * Update ENABLE_UPNPC configuration option.
+ *
+ * @param ac autoconfiguration to update
+ */
+static void
+update_enable_upnpc_option (struct AutoconfigContext *ac)
+{
+  switch (ac->enable_upnpc)
   {
-  case AF_INET:
-    alen = sizeof (struct sockaddr_in);
-    ip4 = &((const struct sockaddr_in *) addr)->sin_addr;
-    if (match_ipv4 ("127.0.0.0", ip4, 8))
-      ac = GNUNET_NAT_AC_LOOPBACK;
-    else if (is_nat_v4 (ip4))
-      ac = GNUNET_NAT_AC_LAN;
-    else
-      ac = GNUNET_NAT_AC_GLOBAL;
+  case GNUNET_YES:
+    GNUNET_CONFIGURATION_set_value_string (ac->c,
+                                          "NAT",
+                                          "ENABLE_UPNPC",
+                                          "YES");
     break;
-  case AF_INET6:
-    alen = sizeof (struct sockaddr_in6);
-    ip6 = &((const struct sockaddr_in6 *) addr)->sin6_addr;
-    if (match_ipv6 ("::1", ip6, 128))
-      ac = GNUNET_NAT_AC_LOOPBACK;
-    else if (is_nat_v6 (ip6))
-      ac = GNUNET_NAT_AC_LAN;
-    else
-      ac = GNUNET_NAT_AC_GLOBAL;
-    if ( (ip6->s6_addr[11] == 0xFF) &&
-        (ip6->s6_addr[12] == 0xFE) )
-    {
-      /* contains a MAC, be extra careful! */
-      ac |= GNUNET_NAT_AC_PRIVATE;
-    }
+  case GNUNET_NO:
+    GNUNET_CONFIGURATION_set_value_string (ac->c,
+                                          "NAT",
+                                          "ENABLE_UPNPC",
+                                          "NO");
+    break;
+  case GNUNET_SYSERR:
+    /* We are unsure, do not change option */
     break;
-#if AF_UNIX
-  case AF_UNIX:
-    GNUNET_break (0);
-    return GNUNET_OK;
-#endif
-  default:
-    GNUNET_break (0);
-    return GNUNET_OK;
   }
-  lal = GNUNET_malloc (sizeof (*lal));
-  lal->af = addr->sa_family;
-  lal->ac = ac;
-  GNUNET_memcpy (&lal->addr,
-                addr,
-                alen);
-  GNUNET_CONTAINER_DLL_insert (ifc_ctx->lal_head,
-                              ifc_ctx->lal_tail,
-                              lal);
-  return GNUNET_OK;
 }
 
 
 /**
- * Notify client about a change in the list
- * of addresses this peer has.
+ * Handle result from external IP address probe during
+ * autoconfiguration.
  *
- * @param delta the entry in the list that changed
- * @param ch client to contact
- * @param add #GNUNET_YES to add, #GNUNET_NO to remove
- * @param addr the address that changed
- * @param addr_len number of bytes in @a addr
+ * @param cls our `struct AutoconfigContext`
+ * @param addr the address, NULL on errors
+ * @param result #GNUNET_NAT_ERROR_SUCCESS on success, otherwise the specific 
error code
  */
 static void
-notify_client (struct LocalAddressList *delta,
-              struct ClientHandle *ch,
-              int add,
-              const void *addr,
-              size_t addr_len)
+auto_external_result_cb (void *cls,
+                        const struct in_addr *addr,
+                        enum GNUNET_NAT_StatusCode result)
 {
-  struct GNUNET_MQ_Envelope *env;
-  struct GNUNET_NAT_AddressChangeNotificationMessage *msg;
+  struct AutoconfigContext *ac = cls;
 
-  env = GNUNET_MQ_msg_extra (msg,
-                            addr_len,
-                            GNUNET_MESSAGE_TYPE_NAT_ADDRESS_CHANGE);
-  msg->add_remove = htonl (add);
-  msg->addr_class = htonl (delta->ac);
-  GNUNET_memcpy (&msg[1],
-                addr,
-                addr_len);
-  GNUNET_MQ_send (ch->mq,
-                 env);
-}                     
+  ac->probe_external = NULL;
+  switch (result)
+  {
+  case GNUNET_NAT_ERROR_SUCCESS:
+    ac->enable_upnpc = GNUNET_YES;
+    break;
+  case GNUNET_NAT_ERROR_EXTERNAL_IP_UTILITY_OUTPUT_INVALID:
+  case GNUNET_NAT_ERROR_EXTERNAL_IP_ADDRESS_INVALID:
+  case GNUNET_NAT_ERROR_IPC_FAILURE:
+    ac->enable_upnpc = GNUNET_NO; /* did not work */
+    break;
+  default:
+    GNUNET_break (0); /* unexpected */
+    ac->enable_upnpc = GNUNET_SYSERR;
+    break;    
+  }
+  update_enable_upnpc_option (ac);
+  check_autoconfig_finished (ac);
+}
 
 
 /**
- * Notify all clients about a change in the list
- * of addresses this peer has.
+ * Handler for #GNUNET_MESSAGE_TYPE_NAT_REQUEST_AUTO_CFG message from
+ * client.
  *
- * @param delta the entry in the list that changed
- * @param add #GNUNET_YES to add, #GNUNET_NO to remove
+ * @param cls client who sent the message
+ * @param message the message received
  */
 static void
-notify_clients (struct LocalAddressList *delta,
-               int add)
+handle_autoconfig_request (void *cls,
+                          const struct GNUNET_NAT_AutoconfigRequestMessage 
*message)
 {
-  for (struct ClientHandle *ch = ch_head;
-       NULL != ch;
-       ch = ch->next)
+  struct ClientHandle *ch = cls;
+  size_t left = ntohs (message->header.size) - sizeof (*message);
+  struct LocalAddressList *lal;
+  struct AutoconfigContext *ac;
+
+  ac = GNUNET_new (struct AutoconfigContext);
+  ac->c = GNUNET_CONFIGURATION_create ();
+  if (GNUNET_OK !=
+      GNUNET_CONFIGURATION_deserialize (ac->c,
+                                       (const char *) &message[1],
+                                       left,
+                                       GNUNET_NO))
   {
-    size_t alen;
-    struct sockaddr_in v4;
-    struct sockaddr_in6 v6;
-    
-    if (0 == (ch->flags & GNUNET_NAT_RF_ADDRESSES))
-      continue;
-    switch (delta->af)
+    GNUNET_break (0);
+    GNUNET_SERVICE_client_drop (ch->client);
+    GNUNET_CONFIGURATION_destroy (ac->c);
+    GNUNET_free (ac);
+    return;
+  }
+  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+             "Received REQUEST_AUTO_CONFIG message from client\n");
+
+  if (GNUNET_OK !=
+      GNUNET_CONFIGURATION_get_value_string (ac->c,
+                                            "PEER",
+                                            "SYSTEM_TYPE",
+                                            &ac->system_type))
+    ac->system_type = "UNKNOWN";
+
+  GNUNET_CONTAINER_DLL_insert (ac_head,
+                              ac_tail,
+                              ac);
+  ac->timeout_task
+    = GNUNET_SCHEDULER_add_delayed (AUTOCONFIG_TIMEOUT,
+                                   &conclude_autoconfig_request,
+                                   ac);
+  ac->enable_upnpc = GNUNET_SYSERR; /* undecided */
+  
+  /* Probe for upnpc */
+  if (GNUNET_SYSERR ==
+      GNUNET_OS_check_helper_binary ("upnpc",
+                                    GNUNET_NO,
+                                    NULL))
+  {
+    GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+               _("UPnP client `upnpc` command not found, disabling UPnP\n"));
+    ac->enable_upnpc = GNUNET_NO;
+  }
+  else
+  {
+    for (lal = lal_head; NULL != lal; lal = lal->next)
+      if (GNUNET_NAT_AC_LAN == (lal->ac & GNUNET_NAT_AC_LAN))
+       /* we are behind NAT, useful to try upnpc */
+       ac->enable_upnpc = GNUNET_YES;
+  }
+  if (GNUNET_YES == ac->enable_upnpc)
+  {
+    /* If we are a mobile device, always leave it on as the network
+       may change to one that supports UPnP anytime.  If we are
+       stationary, check if our network actually supports UPnP, and if
+       not, disable it. */
+    if ( (0 == strcasecmp (ac->system_type,
+                          "INFRASTRUCTURE")) ||
+        (0 == strcasecmp (ac->system_type,
+                          "DESKTOP")) )
     {
-    case AF_INET:
-      alen = sizeof (struct sockaddr_in);
-      GNUNET_memcpy (&v4,
-                    &delta->addr,
-                    alen);
-      for (unsigned int i=0;i<ch->num_addrs;i++)
-      {
-       const struct sockaddr_in *c4;
-       
-       if (AF_INET != ch->addrs[i]->sa_family)
-         continue; /* IPv4 not relevant */
-       c4 = (const struct sockaddr_in *) ch->addrs[i];
-       v4.sin_port = c4->sin_port;
-       notify_client (delta,
-                      ch,
-                      add,
-                      &v4,
-                      alen);
-      }
-      break;
-    case AF_INET6:
-      alen = sizeof (struct sockaddr_in6);
-      GNUNET_memcpy (&v6,
-                    &delta->addr,
-                    alen);
-      for (unsigned int i=0;i<ch->num_addrs;i++)
-      {
-       const struct sockaddr_in6 *c6;
-       
-       if (AF_INET6 != ch->addrs[i]->sa_family)
-         continue; /* IPv4 not relevant */
-       c6 = (const struct sockaddr_in6 *) ch->addrs[i];
-       v6.sin6_port = c6->sin6_port;
-       notify_client (delta,
-                      ch,
-                      add,
-                      &v6,
-                      alen);
-      }
-      break;
-    default:
-      GNUNET_break (0);
-      continue;
+      /* Check if upnpc gives us an external IP */
+      ac->probe_external
+       = GNUNET_NAT_mini_get_external_ipv4_ (&auto_external_result_cb,
+                                             ac);
     }
   }
+  if (NULL == ac->probe_external)
+    update_enable_upnpc_option (ac);
+
+  /* Finally, check if we are already done */  
+  check_autoconfig_finished (ac);
 }
 
 
 /**
- * Task we run periodically to scan for network interfaces.
+ * Task run during shutdown.
  *
- * @param cls NULL
- */ 
+ * @param cls unused
+ */
 static void
-run_scan (void *cls)
+shutdown_task (void *cls)
 {
-  struct IfcProcContext ifc_ctx;
-  int found;
-  
-  scan_task = GNUNET_SCHEDULER_add_delayed (SCAN_FREQ,
-                                           &run_scan,
-                                           NULL);
-  memset (&ifc_ctx,
-         0,
-         sizeof (ifc_ctx));
-  GNUNET_OS_network_interfaces_list (&ifc_proc,
-                                    &ifc_ctx);
-  for (struct LocalAddressList *lal = lal_head;
-       NULL != lal;
-       lal = lal->next)
+  struct StunExternalIP *se;
+  struct AutoconfigContext *ac;
+
+  while (NULL != (ac = ac_head))
   {
-    found = GNUNET_NO;
-    for (struct LocalAddressList *pos = ifc_ctx.lal_head;
-        NULL != pos;
-        pos = pos->next)
-    {
-      if ( (pos->af == lal->af) &&
-          (0 == memcmp (&lal->addr,
-                        &pos->addr,
-                        (AF_INET == lal->af)
-                        ? sizeof (struct sockaddr_in)
-                        : sizeof (struct sockaddr_in6))) )
-       found = GNUNET_YES;
-    }
-    if (GNUNET_NO == found)
-      notify_clients (lal,
-                     GNUNET_NO);
+    GNUNET_CONTAINER_DLL_remove (ac_head,
+                                ac_tail,
+                                ac);
+    terminate_ac_activities (ac);
+    GNUNET_free (ac);
   }
-
-  for (struct LocalAddressList *pos = ifc_ctx.lal_head;
-       NULL != pos;
-       pos = pos->next)
+  while (NULL != (se = se_head))
   {
-    found = GNUNET_NO;
-    for (struct LocalAddressList *lal = lal_head;
-        NULL != lal;
-        lal = lal->next)
-    {
-      if ( (pos->af == lal->af) &&
-          (0 == memcmp (&lal->addr,
-                        &pos->addr,
-                        (AF_INET == lal->af)
-                        ? sizeof (struct sockaddr_in)
-                        : sizeof (struct sockaddr_in6))) )
-       found = GNUNET_YES;
-    }
-    if (GNUNET_NO == found)
-      notify_clients (pos,
-                     GNUNET_YES);
+    GNUNET_CONTAINER_DLL_remove (se_head,
+                                se_tail,
+                                se);
+    GNUNET_SCHEDULER_cancel (se->timeout_task);
+    GNUNET_free (se);
+  }
+  if (NULL != scan_task)
+  {
+    GNUNET_SCHEDULER_cancel (scan_task);
+    scan_task = NULL;
+  }
+  if (NULL != stats)
+  {
+    GNUNET_STATISTICS_destroy (stats,
+                              GNUNET_NO);
+    stats = NULL;
   }
-
   destroy_lal ();
-  lal_head = ifc_ctx.lal_head;
-  lal_tail = ifc_ctx.lal_tail;
 }
 
 
 /**
- * Handle network size estimate clients.
+ * Setup NAT service.
  *
  * @param cls closure
  * @param c configuration to use
@@ -1181,6 +1512,26 @@ run (void *cls,
                                           "STUN_STALE",
                                           &stun_stale_timeout))
     stun_stale_timeout = GNUNET_TIME_UNIT_HOURS;
+
+  /* Check for UPnP */
+  enable_upnp 
+    = GNUNET_CONFIGURATION_get_value_yesno (cfg,
+                                           "NAT",
+                                           "ENABLE_UPNP");
+  if (GNUNET_YES == enable_upnp)
+  {
+    /* check if it works */
+    if (GNUNET_SYSERR ==
+        GNUNET_OS_check_helper_binary ("upnpc",
+                                      GNUNET_NO,
+                                      NULL))
+    {
+      GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+                 _("UPnP enabled in configuration, but UPnP client `upnpc` 
command not found, disabling UPnP\n"));
+      enable_upnp = GNUNET_SYSERR;
+    }
+  }
+  
   GNUNET_SCHEDULER_add_shutdown (&shutdown_task,
                                 NULL);
   stats = GNUNET_STATISTICS_create ("nat",
diff --git a/src/nat/gnunet-service-nat_mini.c 
b/src/nat/gnunet-service-nat_mini.c
new file mode 100644
index 0000000..658ec72
--- /dev/null
+++ b/src/nat/gnunet-service-nat_mini.c
@@ -0,0 +1,759 @@
+/*
+     This file is part of GNUnet.
+     Copyright (C) 2011-2014, 2016 GNUnet e.V.
+
+     GNUnet is free software; you can redistribute it and/or modify
+     it under the terms of the GNU General Public License as published
+     by the Free Software Foundation; either version 3, or (at your
+     option) any later version.
+
+     GNUnet is distributed in the hope that it will be useful, but
+     WITHOUT ANY WARRANTY; without even the implied warranty of
+     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+     General Public License for more details.
+
+     You should have received a copy of the GNU General Public License
+     along with GNUnet; see the file COPYING.  If not, write to the
+     Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+     Boston, MA 02110-1301, USA.
+*/
+
+/**
+ * @file nat/gnunet-service-nat_mini.c
+ * @brief functions for interaction with miniupnp; tested with miniupnpc 1.5
+ * @author Christian Grothoff
+ */
+#include "platform.h"
+#include "gnunet_util_lib.h"
+#include "gnunet_nat_service.h"
+#include "gnunet-service-nat_mini.h"
+#include "nat.h"
+
+#define LOG(kind,...) GNUNET_log_from (kind, "nat", __VA_ARGS__)
+
+/**
+ * How long do we give upnpc to create a mapping?
+ */
+#define MAP_TIMEOUT GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 
15)
+
+/**
+ * How long do we give upnpc to remove a mapping?
+ */
+#define UNMAP_TIMEOUT GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 
1)
+
+/**
+ * How often do we check for changes in the mapping?
+ */
+#define MAP_REFRESH_FREQ GNUNET_TIME_relative_multiply 
(GNUNET_TIME_UNIT_MINUTES, 5)
+
+
+/* ************************* external-ip calling ************************ */
+
+/**
+ * Opaque handle to cancel "GNUNET_NAT_mini_get_external_ipv4" operation.
+ */
+struct GNUNET_NAT_ExternalHandle
+{
+
+  /**
+   * Function to call with the result.
+   */
+  GNUNET_NAT_IPCallback cb;
+
+  /**
+   * Closure for @e cb.
+   */
+  void *cb_cls;
+
+  /**
+   * Read task.
+   */
+  struct GNUNET_SCHEDULER_Task *task;
+
+  /**
+   * Handle to `external-ip` process.
+   */
+  struct GNUNET_OS_Process *eip;
+
+  /**
+   * Handle to stdout pipe of `external-ip`.
+   */
+  struct GNUNET_DISK_PipeHandle *opipe;
+
+  /**
+   * Read handle of @e opipe.
+   */
+  const struct GNUNET_DISK_FileHandle *r;
+
+  /**
+   * Number of bytes in @e buf that are valid.
+   */
+  size_t off;
+
+  /**
+   * Destination of our read operation (output of 'external-ip').
+   */
+  char buf[17];
+
+  /**
+   * Error code for better debugging and user feedback
+   */
+  enum GNUNET_NAT_StatusCode ret;
+};
+
+
+/**
+ * Read the output of `external-ip` into `buf`.  When complete, parse
+ * the address and call our callback.
+ *
+ * @param cls the `struct GNUNET_NAT_ExternalHandle`
+ */
+static void
+read_external_ipv4 (void *cls)
+{
+  struct GNUNET_NAT_ExternalHandle *eh = cls;
+  ssize_t ret;
+  struct in_addr addr;
+
+  eh->task = NULL;
+  ret = GNUNET_DISK_file_read (eh->r,
+                              &eh->buf[eh->off],
+                               sizeof (eh->buf) - eh->off);
+  if (ret > 0)
+  {
+    /* try to read more */
+    eh->off += ret;
+    eh->task 
+      = GNUNET_SCHEDULER_add_read_file (GNUNET_TIME_UNIT_FOREVER_REL,
+                                       eh->r,
+                                        &read_external_ipv4,
+                                       eh);
+    return;
+  }
+  eh->ret = GNUNET_NAT_ERROR_EXTERNAL_IP_UTILITY_OUTPUT_INVALID;
+  if ( (eh->off > 7) &&
+       (eh->buf[eh->off - 1] == '\n') )
+  {
+    eh->buf[eh->off - 1] = '\0';
+    if (1 == inet_pton (AF_INET,
+                       eh->buf,
+                       &addr))
+    {
+      if (0 != addr.s_addr)
+        eh->ret = GNUNET_NAT_ERROR_EXTERNAL_IP_ADDRESS_INVALID;       /* got 
0.0.0.0 */
+      else
+        eh->ret = GNUNET_NAT_ERROR_SUCCESS;
+    }
+  }
+  eh->cb (eh->cb_cls,
+          (GNUNET_NAT_ERROR_SUCCESS == eh->ret) ? &addr : NULL,
+          eh->ret);
+  GNUNET_NAT_mini_get_external_ipv4_cancel_ (eh);
+}
+
+
+/**
+ * (Asynchronously) signal error invoking `external-ip` to client.
+ *
+ * @param cls the `struct GNUNET_NAT_ExternalHandle` (freed)
+ */
+static void
+signal_external_ip_error (void *cls)
+{
+  struct GNUNET_NAT_ExternalHandle *eh = cls;
+
+  eh->task = NULL;
+  eh->cb (eh->cb_cls,
+          NULL,
+          eh->ret);
+  GNUNET_free (eh);
+}
+
+
+/**
+ * Try to get the external IPv4 address of this peer.
+ *
+ * @param cb function to call with result
+ * @param cb_cls closure for @a cb
+ * @return handle for cancellation (can only be used until @a cb is called), 
never NULL
+ */
+struct GNUNET_NAT_ExternalHandle *
+GNUNET_NAT_mini_get_external_ipv4_ (GNUNET_NAT_IPCallback cb,
+                                   void *cb_cls)
+{
+  struct GNUNET_NAT_ExternalHandle *eh;
+
+  eh = GNUNET_new (struct GNUNET_NAT_ExternalHandle);
+  eh->cb = cb;
+  eh->cb_cls = cb_cls;
+  eh->ret = GNUNET_NAT_ERROR_SUCCESS;
+  if (GNUNET_SYSERR ==
+      GNUNET_OS_check_helper_binary ("external-ip",
+                                    GNUNET_NO,
+                                    NULL))
+  {
+    LOG (GNUNET_ERROR_TYPE_INFO,
+        _("`external-ip' command not found\n"));
+    eh->ret = GNUNET_NAT_ERROR_EXTERNAL_IP_UTILITY_NOT_FOUND;
+    eh->task = GNUNET_SCHEDULER_add_now (&signal_external_ip_error,
+                                         eh);
+    return eh;
+  }
+  LOG (GNUNET_ERROR_TYPE_DEBUG,
+       "Running `external-ip' to determine our external IP\n");
+  eh->opipe = GNUNET_DISK_pipe (GNUNET_YES,
+                               GNUNET_YES,
+                               GNUNET_NO,
+                               GNUNET_YES);
+  if (NULL == eh->opipe)
+  {
+    eh->ret = GNUNET_NAT_ERROR_IPC_FAILURE;
+    eh->task = GNUNET_SCHEDULER_add_now (&signal_external_ip_error,
+                                         eh);
+    return eh;
+  }
+  eh->eip =
+      GNUNET_OS_start_process (GNUNET_NO,
+                              0,
+                              NULL,
+                              eh->opipe,
+                              NULL,
+                               "external-ip",
+                              "external-ip",
+                               NULL);
+  if (NULL == eh->eip)
+  {
+    GNUNET_DISK_pipe_close (eh->opipe);
+    eh->ret = GNUNET_NAT_ERROR_EXTERNAL_IP_UTILITY_FAILED;
+    eh->task = GNUNET_SCHEDULER_add_now (&signal_external_ip_error,
+                                         eh);
+    return eh;
+  }
+  GNUNET_DISK_pipe_close_end (eh->opipe,
+                             GNUNET_DISK_PIPE_END_WRITE);
+  eh->r = GNUNET_DISK_pipe_handle (eh->opipe,
+                                  GNUNET_DISK_PIPE_END_READ);
+  eh->task 
+    = GNUNET_SCHEDULER_add_read_file (GNUNET_TIME_UNIT_FOREVER_REL,
+                                      eh->r,
+                                      &read_external_ipv4,
+                                     eh);
+  return eh;
+}
+
+
+/**
+ * Cancel operation.
+ *
+ * @param eh operation to cancel
+ */
+void
+GNUNET_NAT_mini_get_external_ipv4_cancel_ (struct GNUNET_NAT_ExternalHandle 
*eh)
+{
+  if (NULL != eh->eip)
+  {
+    (void) GNUNET_OS_process_kill (eh->eip,
+                                  SIGKILL);
+    GNUNET_OS_process_destroy (eh->eip);
+  }
+  if (NULL != eh->opipe)
+  {
+    GNUNET_DISK_pipe_close (eh->opipe);
+    eh->opipe = NULL;
+  }
+  if (NULL != eh->task)
+  {
+    GNUNET_SCHEDULER_cancel (eh->task);
+    eh->task = NULL;
+  }
+  GNUNET_free (eh);
+}
+
+
+/* ************************* upnpc calling ************************ */
+
+
+/**
+ * Handle to a mapping created with upnpc.
+ */
+struct GNUNET_NAT_MiniHandle
+{
+
+  /**
+   * Function to call on mapping changes.
+   */
+  GNUNET_NAT_MiniAddressCallback ac;
+
+  /**
+   * Closure for @e ac.
+   */
+  void *ac_cls;
+
+  /**
+   * Command used to install the map.
+   */
+  struct GNUNET_OS_CommandHandle *map_cmd;
+
+  /**
+   * Command used to refresh our map information.
+   */
+  struct GNUNET_OS_CommandHandle *refresh_cmd;
+
+  /**
+   * Command used to remove the mapping.
+   */
+  struct GNUNET_OS_CommandHandle *unmap_cmd;
+
+  /**
+   * Our current external mapping (if we have one).
+   */
+  struct sockaddr_in current_addr;
+
+  /**
+   * We check the mapping periodically to see if it
+   * still works.  This task triggers the check.
+   */
+  struct GNUNET_SCHEDULER_Task *refresh_task;
+
+  /**
+   * Are we mapping TCP or UDP?
+   */
+  int is_tcp;
+
+  /**
+   * Did we succeed with creating a mapping?
+   */
+  int did_map;
+
+  /**
+   * Did we find our mapping during refresh scan?
+   */
+  int found;
+
+  /**
+   * Which port are we mapping?
+   */
+  uint16_t port;
+
+};
+
+
+/**
+ * Run "upnpc -l" to find out if our mapping changed.
+ *
+ * @param cls the `struct GNUNET_NAT_MiniHandle`
+ */
+static void
+do_refresh (void *cls);
+
+
+/**
+ * Process the output from the "upnpc -r" command.
+ *
+ * @param cls the `struct GNUNET_NAT_MiniHandle`
+ * @param line line of output, NULL at the end
+ */
+static void
+process_map_output (void *cls,
+                   const char *line);
+
+
+/**
+ * Run "upnpc -r" to map our internal port.
+ *
+ * @param mini our handle
+ */
+static void
+run_upnpc_r (struct GNUNET_NAT_MiniHandle *mini)
+{
+  char pstr[6];
+
+  GNUNET_snprintf (pstr,
+                   sizeof (pstr),
+                   "%u",
+                   (unsigned int) mini->port);
+  mini->map_cmd 
+    = GNUNET_OS_command_run (&process_map_output,
+                            mini,
+                            MAP_TIMEOUT,
+                            "upnpc",
+                            "upnpc",
+                            "-r",
+                            pstr,
+                            mini->is_tcp ? "tcp" : "udp",
+                            NULL);
+  if (NULL == mini->map_cmd)
+  {
+    mini->ac (mini->ac_cls,
+              GNUNET_SYSERR,
+              NULL,
+             0,
+              GNUNET_NAT_ERROR_UPNPC_FAILED);
+    return;
+  }
+}
+
+
+/**
+ * Process the output from "upnpc -l" to see if our
+ * external mapping changed.  If so, do the notifications.
+ *
+ * @param cls the `struct GNUNET_NAT_MiniHandle`
+ * @param line line of output, NULL at the end
+ */
+static void
+process_refresh_output (void *cls,
+                       const char *line)
+{
+  struct GNUNET_NAT_MiniHandle *mini = cls;
+  char pstr[9];
+  const char *s;
+  unsigned int nport;
+  struct in_addr exip;
+
+  if (NULL == line)
+  {
+    GNUNET_OS_command_stop (mini->refresh_cmd);
+    mini->refresh_cmd = NULL;
+    if (GNUNET_NO == mini->found)
+    {
+      /* mapping disappeared, try to re-create */
+      if (GNUNET_YES == mini->did_map)
+      {
+        mini->ac (mini->ac_cls,
+                  GNUNET_NO,
+                  (const struct sockaddr *) &mini->current_addr,
+                  sizeof (mini->current_addr),
+                  GNUNET_NAT_ERROR_SUCCESS);
+        mini->did_map = GNUNET_NO;
+      }
+      run_upnpc_r (mini);
+    }
+    return;
+  }
+  if (!mini->did_map)
+    return;                     /* never mapped, won't find our mapping anyway 
*/
+
+  /* we're looking for output of the form:
+   * "ExternalIPAddress = 12.134.41.124" */
+
+  s = strstr (line,
+             "ExternalIPAddress = ");
+  if (NULL != s)
+  {
+    s += strlen ("ExternalIPAddress = ");
+    if (1 != inet_pton (AF_INET,
+                       s,
+                       &exip))
+      return;                   /* skip */
+    if (exip.s_addr == mini->current_addr.sin_addr.s_addr)
+      return;                   /* no change */
+    /* update mapping */
+    mini->ac (mini->ac_cls,
+             GNUNET_NO,
+              (const struct sockaddr *) &mini->current_addr,
+              sizeof (mini->current_addr),
+              GNUNET_NAT_ERROR_SUCCESS);
+    mini->current_addr.sin_addr = exip;
+    mini->ac (mini->ac_cls,
+             GNUNET_YES,
+              (const struct sockaddr *) &mini->current_addr,
+              sizeof (mini->current_addr),
+              GNUNET_NAT_ERROR_SUCCESS);
+    return;
+  }
+  /*
+   * we're looking for output of the form:
+   *
+   * "0 TCP  3000->192.168.2.150:3000  'libminiupnpc' ''"
+   * "1 UDP  3001->192.168.2.150:3001  'libminiupnpc' ''"
+   *
+   * the pattern we look for is:
+   *
+   * "%s TCP  PORT->STRING:OURPORT *" or
+   * "%s UDP  PORT->STRING:OURPORT *"
+   */
+  GNUNET_snprintf (pstr,
+                  sizeof (pstr),
+                  ":%u ",
+                  mini->port);
+  if (NULL == (s = strstr (line, "->")))
+    return;                     /* skip */
+  if (NULL == strstr (s, pstr))
+    return;                     /* skip */
+  if (1 !=
+      SSCANF (line,
+              (mini->is_tcp) ? "%*u TCP  %u->%*s:%*u %*s" :
+              "%*u UDP  %u->%*s:%*u %*s", &nport))
+    return;                     /* skip */
+  mini->found = GNUNET_YES;
+  if (nport == ntohs (mini->current_addr.sin_port))
+    return;                     /* no change */
+
+  /* external port changed, update mapping */
+  mini->ac (mini->ac_cls,
+           GNUNET_NO,
+            (const struct sockaddr *) &mini->current_addr,
+            sizeof (mini->current_addr),
+            GNUNET_NAT_ERROR_SUCCESS);
+  mini->current_addr.sin_port = htons ((uint16_t) nport);
+  mini->ac (mini->ac_cls,
+           GNUNET_YES,
+            (const struct sockaddr *) &mini->current_addr,
+            sizeof (mini->current_addr),
+            GNUNET_NAT_ERROR_SUCCESS);
+}
+
+
+/**
+ * Run "upnpc -l" to find out if our mapping changed.
+ *
+ * @param cls the 'struct GNUNET_NAT_MiniHandle'
+ */
+static void
+do_refresh (void *cls)
+{
+  struct GNUNET_NAT_MiniHandle *mini = cls;
+  int ac;
+
+  mini->refresh_task 
+    = GNUNET_SCHEDULER_add_delayed (MAP_REFRESH_FREQ,
+                                   &do_refresh,
+                                   mini);
+  LOG (GNUNET_ERROR_TYPE_DEBUG,
+       "Running `upnpc' to check if our mapping still exists\n");
+  mini->found = GNUNET_NO;
+  ac = GNUNET_NO;
+  if (NULL != mini->map_cmd)
+  {
+    /* took way too long, abort it! */
+    GNUNET_OS_command_stop (mini->map_cmd);
+    mini->map_cmd = NULL;
+    ac = GNUNET_YES;
+  }
+  if (NULL != mini->refresh_cmd)
+  {
+    /* took way too long, abort it! */
+    GNUNET_OS_command_stop (mini->refresh_cmd);
+    mini->refresh_cmd = NULL;
+    ac = GNUNET_YES;
+  }
+  mini->refresh_cmd =
+      GNUNET_OS_command_run (&process_refresh_output,
+                            mini,
+                            MAP_TIMEOUT,
+                             "upnpc",
+                            "upnpc",
+                            "-l",
+                            NULL);
+  if (GNUNET_YES == ac)
+    mini->ac (mini->ac_cls,
+              GNUNET_SYSERR,
+              NULL,
+             0,
+              GNUNET_NAT_ERROR_UPNPC_TIMEOUT);
+}
+
+
+/**
+ * Process the output from the 'upnpc -r' command.
+ *
+ * @param cls the `struct GNUNET_NAT_MiniHandle`
+ * @param line line of output, NULL at the end
+ */
+static void
+process_map_output (void *cls,
+                    const char *line)
+{
+  struct GNUNET_NAT_MiniHandle *mini = cls;
+  const char *ipaddr;
+  char *ipa;
+  const char *pstr;
+  unsigned int port;
+
+  if (NULL == line)
+  {
+    GNUNET_OS_command_stop (mini->map_cmd);
+    mini->map_cmd = NULL;
+    if (GNUNET_YES != mini->did_map)
+      mini->ac (mini->ac_cls,
+                GNUNET_SYSERR,
+                NULL, 0,
+                GNUNET_NAT_ERROR_UPNPC_PORTMAP_FAILED);
+    if (NULL == mini->refresh_task)
+      mini->refresh_task =
+        GNUNET_SCHEDULER_add_delayed (MAP_REFRESH_FREQ,
+                                     &do_refresh,
+                                     mini);
+    return;
+  }
+  /*
+   * The upnpc output we're after looks like this:
+   *
+   * "external 87.123.42.204:3000 TCP is redirected to internal 
192.168.2.150:3000"
+   */
+  if ((NULL == (ipaddr = strstr (line, " "))) ||
+      (NULL == (pstr = strstr (ipaddr, ":"))) ||
+      (1 != SSCANF (pstr + 1, "%u", &port)))
+  {
+    return;                     /* skip line */
+  }
+  ipa = GNUNET_strdup (ipaddr + 1);
+  strstr (ipa, ":")[0] = '\0';
+  if (1 != inet_pton (AF_INET, ipa, &mini->current_addr.sin_addr))
+  {
+    GNUNET_free (ipa);
+    return;                     /* skip line */
+  }
+  GNUNET_free (ipa);
+
+  mini->current_addr.sin_port = htons (port);
+  mini->current_addr.sin_family = AF_INET;
+#if HAVE_SOCKADDR_IN_SIN_LEN
+  mini->current_addr.sin_len = sizeof (struct sockaddr_in);
+#endif
+  mini->did_map = GNUNET_YES;
+  mini->ac (mini->ac_cls, GNUNET_YES,
+            (const struct sockaddr *) &mini->current_addr,
+            sizeof (mini->current_addr),
+            GNUNET_NAT_ERROR_SUCCESS);
+}
+
+
+/**
+ * Start mapping the given port using (mini)upnpc.  This function
+ * should typically not be used directly (it is used within the
+ * general-purpose #GNUNET_NAT_register() code).  However, it can be
+ * used if specifically UPnP-based NAT traversal is to be used or
+ * tested.
+ *
+ * @param port port to map
+ * @param is_tcp #GNUNET_YES to map TCP, #GNUNET_NO for UDP
+ * @param ac function to call with mapping result
+ * @param ac_cls closure for @a ac
+ * @return NULL on error (no 'upnpc' installed)
+ */
+struct GNUNET_NAT_MiniHandle *
+GNUNET_NAT_mini_map_start (uint16_t port,
+                           int is_tcp,
+                           GNUNET_NAT_MiniAddressCallback ac,
+                           void *ac_cls)
+{
+  struct GNUNET_NAT_MiniHandle *ret;
+
+  if (GNUNET_SYSERR ==
+      GNUNET_OS_check_helper_binary ("upnpc",
+                                    GNUNET_NO,
+                                    NULL))
+  {
+    LOG (GNUNET_ERROR_TYPE_INFO,
+        _("`upnpc' command not found\n"));
+    ac (ac_cls,
+        GNUNET_SYSERR,
+        NULL, 0,
+        GNUNET_NAT_ERROR_UPNPC_NOT_FOUND);
+    return NULL;
+  }
+  LOG (GNUNET_ERROR_TYPE_DEBUG,
+       "Running `upnpc' to install mapping\n");
+  ret = GNUNET_new (struct GNUNET_NAT_MiniHandle);
+  ret->ac = ac;
+  ret->ac_cls = ac_cls;
+  ret->is_tcp = is_tcp;
+  ret->port = port;
+  ret->refresh_task =
+    GNUNET_SCHEDULER_add_delayed (MAP_REFRESH_FREQ,
+                                 &do_refresh,
+                                 ret);
+  run_upnpc_r (ret);
+  return ret;
+}
+
+
+/**
+ * Process output from our 'unmap' command.
+ *
+ * @param cls the `struct GNUNET_NAT_MiniHandle`
+ * @param line line of output, NULL at the end
+ */
+static void
+process_unmap_output (void *cls,
+                     const char *line)
+{
+  struct GNUNET_NAT_MiniHandle *mini = cls;
+
+  if (NULL == line)
+  {
+    LOG (GNUNET_ERROR_TYPE_DEBUG,
+         "UPnP unmap done\n");
+    GNUNET_OS_command_stop (mini->unmap_cmd);
+    mini->unmap_cmd = NULL;
+    GNUNET_free (mini);
+    return;
+  }
+  /* we don't really care about the output... */
+}
+
+
+/**
+ * Remove a mapping created with (mini)upnpc.  Calling
+ * this function will give 'upnpc' 1s to remove tha mapping,
+ * so while this function is non-blocking, a task will be
+ * left with the scheduler for up to 1s past this call.
+ *
+ * @param mini the handle
+ */
+void
+GNUNET_NAT_mini_map_stop (struct GNUNET_NAT_MiniHandle *mini)
+{
+  char pstr[6];
+
+  if (NULL != mini->refresh_task)
+  {
+    GNUNET_SCHEDULER_cancel (mini->refresh_task);
+    mini->refresh_task = NULL;
+  }
+  if (NULL != mini->refresh_cmd)
+  {
+    GNUNET_OS_command_stop (mini->refresh_cmd);
+    mini->refresh_cmd = NULL;
+  }
+  if (NULL != mini->map_cmd)
+  {
+    GNUNET_OS_command_stop (mini->map_cmd);
+    mini->map_cmd = NULL;
+  }
+  if (GNUNET_NO == mini->did_map)
+  {
+    GNUNET_free (mini);
+    return;
+  }
+  mini->ac (mini->ac_cls,
+           GNUNET_NO,
+            (const struct sockaddr *) &mini->current_addr,
+            sizeof (mini->current_addr),
+            GNUNET_NAT_ERROR_SUCCESS);
+  /* Note: oddly enough, deletion uses the external port whereas
+   * addition uses the internal port; this rarely matters since they
+   * often are the same, but it might... */
+  GNUNET_snprintf (pstr,
+                   sizeof (pstr),
+                   "%u",
+                   (unsigned int) ntohs (mini->current_addr.sin_port));
+  LOG (GNUNET_ERROR_TYPE_DEBUG,
+       "Unmapping port %u with UPnP\n",
+       ntohs (mini->current_addr.sin_port));
+  mini->unmap_cmd 
+    = GNUNET_OS_command_run (&process_unmap_output,
+                            mini,
+                            UNMAP_TIMEOUT,
+                             "upnpc",
+                            "upnpc",
+                            "-d",
+                            pstr,
+                             mini->is_tcp ? "tcp" : "udp",
+                            NULL);
+}
+
+
+/* end of gnunet-service-nat_mini.c */
diff --git a/src/nat/gnunet-service-nat_mini.h 
b/src/nat/gnunet-service-nat_mini.h
new file mode 100644
index 0000000..2c0dd34
--- /dev/null
+++ b/src/nat/gnunet-service-nat_mini.h
@@ -0,0 +1,127 @@
+/*
+     This file is part of GNUnet.
+     Copyright (C) 2011-2014, 2016 GNUnet e.V.
+
+     GNUnet is free software; you can redistribute it and/or modify
+     it under the terms of the GNU General Public License as published
+     by the Free Software Foundation; either version 3, or (at your
+     option) any later version.
+
+     GNUnet is distributed in the hope that it will be useful, but
+     WITHOUT ANY WARRANTY; without even the implied warranty of
+     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+     General Public License for more details.
+
+     You should have received a copy of the GNU General Public License
+     along with GNUnet; see the file COPYING.  If not, write to the
+     Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+     Boston, MA 02110-1301, USA.
+*/
+
+/**
+ * @file nat/gnunet-service-nat_mini.c
+ * @brief functions for interaction with miniupnp; tested with miniupnpc 1.5
+ * @author Christian Grothoff
+ */
+#ifndef GNUNET_SERVICE_NAT_MINI_H
+#define GNUNET_SERVICE_NAT_MINI_H
+
+
+/**
+ * Signature of a callback that is given an IP address.
+ *
+ * @param cls closure
+ * @param addr the address, NULL on errors
+ * @param result #GNUNET_NAT_ERROR_SUCCESS on success, otherwise the specific 
error code
+ */
+typedef void
+(*GNUNET_NAT_IPCallback) (void *cls,
+                          const struct in_addr *addr,
+                          enum GNUNET_NAT_StatusCode result);
+
+
+/**
+ * Opaque handle to cancel #GNUNET_NAT_mini_get_external_ipv4() operation.
+ */
+struct GNUNET_NAT_ExternalHandle;
+
+
+/**
+ * Try to get the external IPv4 address of this peer.
+ *
+ * @param cb function to call with result
+ * @param cb_cls closure for @a cb
+ * @return handle for cancellation (can only be used until @a cb is called), 
NULL on error
+ */
+struct GNUNET_NAT_ExternalHandle *
+GNUNET_NAT_mini_get_external_ipv4_ (GNUNET_NAT_IPCallback cb,
+                                   void *cb_cls);
+
+
+/**
+ * Cancel operation.
+ *
+ * @param eh operation to cancel
+ */
+void
+GNUNET_NAT_mini_get_external_ipv4_cancel_ (struct GNUNET_NAT_ExternalHandle 
*eh);
+
+
+/**
+ * Handle to a mapping created with upnpc.
+ */
+struct GNUNET_NAT_MiniHandle;
+
+
+/**
+ * Signature of the callback passed to #GNUNET_NAT_register() for
+ * a function to call whenever our set of 'valid' addresses changes.
+ *
+ * @param cls closure
+ * @param add_remove #GNUNET_YES to mean the new public IP address, #GNUNET_NO 
to mean
+ *     the previous (now invalid) one, #GNUNET_SYSERR indicates an error
+ * @param addr either the previous or the new public IP address
+ * @param addrlen actual length of the @a addr
+ * @param result #GNUNET_NAT_ERROR_SUCCESS on success, otherwise the specific 
error code
+ */
+typedef void
+(*GNUNET_NAT_MiniAddressCallback) (void *cls,
+                                   int add_remove,
+                                   const struct sockaddr *addr,
+                                   socklen_t addrlen,
+                                   enum GNUNET_NAT_StatusCode result);
+
+
+/**
+ * Start mapping the given port using (mini)upnpc.  This function
+ * should typically not be used directly (it is used within the
+ * general-purpose #GNUNET_NAT_register() code).  However, it can be
+ * used if specifically UPnP-based NAT traversal is to be used or
+ * tested.
+ *
+ * @param port port to map
+ * @param is_tcp #GNUNET_YES to map TCP, #GNUNET_NO for UDP
+ * @param ac function to call with mapping result
+ * @param ac_cls closure for @a ac
+ * @return NULL on error
+ */
+struct GNUNET_NAT_MiniHandle *
+GNUNET_NAT_mini_map_start (uint16_t port,
+                           int is_tcp,
+                           GNUNET_NAT_MiniAddressCallback ac,
+                           void *ac_cls);
+
+
+/**
+ * Remove a mapping created with (mini)upnpc.  Calling
+ * this function will give 'upnpc' 1s to remove the mapping,
+ * so while this function is non-blocking, a task will be
+ * left with the scheduler for up to 1s past this call.
+ *
+ * @param mini the handle
+ */
+void
+GNUNET_NAT_mini_map_stop (struct GNUNET_NAT_MiniHandle *mini);
+
+
+#endif
diff --git a/src/nat/nat.h b/src/nat/nat.h
index 6df72c0..3356b19 100644
--- a/src/nat/nat.h
+++ b/src/nat/nat.h
@@ -78,7 +78,7 @@ enum GNUNET_NAT_RegisterFlags
 
   /**
    * This client wants to be informed about changes to our
-   * external addresses.
+   * applicable addresses.
    */
   GNUNET_NAT_RF_ADDRESSES = 1,
 
diff --git a/src/nat/nat_api.c b/src/nat/nat_api.c
index 58ed3e6..3fe97ed 100644
--- a/src/nat/nat_api.c
+++ b/src/nat/nat_api.c
@@ -331,6 +331,7 @@ do_connect (void *cls)
                           nh),
     GNUNET_MQ_handler_end ()
   };
+  struct GNUNET_MQ_Envelope *env;
 
   nh->reconnect_task = NULL;
   nh->mq = GNUNET_CLIENT_connecT (nh->cfg,
@@ -339,7 +340,13 @@ do_connect (void *cls)
                                  &mq_error_handler,
                                  nh);
   if (NULL == nh->mq)
+  {
     reconnect (nh);
+    return;
+  }
+  env = GNUNET_MQ_msg_copy (nh->reg);
+  GNUNET_MQ_send (nh->mq,
+                 env);
 }
 
 
diff --git a/src/util/util.conf b/src/util/util.conf
index 6b9c52d..ecc94ea 100644
--- a/src/util/util.conf
+++ b/src/util/util.conf
@@ -48,8 +48,18 @@ GNUNET_USER_RUNTIME_DIR = 
${TMPDIR:-${TMP:-/tmp}}/gnunet-${USERHOME:-${USER:-use
 
 
 [PEER]
+# Where do we store our private key?
 PRIVATE_KEY = $GNUNET_DATA_HOME/private_key.ecc
 
+# What kind of system are we on? Choices are
+# INFRASTRUCTURE (always-on, grid, data center)
+# DESKTOP (sometimes-on, grid, office)
+# NOTEBOOK (sometimes-on, mobile, often limited network,
+#           if on-battery than large battery)
+# MOBILE (sometimes-on, mobile, always limited network,
+#           always battery limited)
+# UNKNOWN (not configured/specified/known)
+SYSTEM_TYPE = UNKNOWN
 
 [TESTING]
 SPEEDUP_INTERVAL = 0 ms

-- 
To stop receiving notification emails like this one, please contact
address@hidden



reply via email to

[Prev in Thread] Current Thread [Next in Thread]