gnunet-svn
[Top][All Lists]
Advanced

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

[GNUnet-SVN] [gnunet] branch master updated (2b99bddcb -> b670eec1c)


From: gnunet
Subject: [GNUnet-SVN] [gnunet] branch master updated (2b99bddcb -> b670eec1c)
Date: Mon, 04 Jun 2018 19:18:51 +0200

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

grothoff pushed a change to branch master
in repository gnunet.

    from 2b99bddcb Niibe writes:
     new 65d6d14a1 xt transport for experiments:
     new c62b46170 disable LAN/loopback for AC
     new 28cee7516 build xt
     new 99c89e00b Merge branch 'h2020' of git+ssh://gnunet.org/gnunet into 
h2020
     new 0e70daa01 track hello passing
     new b6b79a4c4 skip validation logic
     new 6f0c1fd78 skip validation logic
     new 1ac69f278 adding xu transport
     new f5a18b746 disable xu transport, header missing
     new adef29b3e add proximity considerations to datacache
     new bc64df2cf add more logging for core KX to improve diagnostics
     new 2e619a454 more logging
     new 18a1d4ec5 proper datacache expiration by proximity first
     new 54031a536 fix logic
     new 9b9c6dc3c more core KX logging
     new 2a7899937 more crypto KX logging
     new fdf5283f5 more logging
     new 41cbe10b7 add function for getopt uint16_t arguments
     new b670eec1c merge benchmark changes

The 19 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails.  The revisions
listed as "add" were already present in the repository and have only
been added to this reference.


Summary of changes:
 src/core/gnunet-service-core_kx.c                  |  197 +-
 src/datacache/datacache.c                          |    3 +
 src/datacache/plugin_datacache_heap.c              |  143 +-
 src/datacache/plugin_datacache_postgres.c          |    2 +
 src/datacache/plugin_datacache_sqlite.c            |   94 +-
 src/datacache/plugin_datacache_template.c          |    2 +
 src/datacache/test_datacache.c                     |   10 +-
 src/datacache/test_datacache_quota.c               |   10 +-
 src/dht/gnunet-dht-get.c                           |   22 +-
 src/dht/gnunet-dht-monitor.c                       |   27 +-
 src/dht/gnunet-service-dht_datacache.c             |    6 +-
 src/dht/gnunet-service-dht_neighbours.c            |   14 +-
 src/dht/gnunet-service-dht_neighbours.h            |   22 +
 src/gns/gnunet-bcd.c                               |   22 +-
 src/gns/gnunet-gns-proxy.c                         |   16 +-
 src/gns/gnunet-service-gns_resolver.c              |    2 +-
 src/hostlist/gnunet-daemon-hostlist_client.c       |   65 +-
 src/include/gnunet_core_service.h                  |    2 +-
 src/include/gnunet_datacache_lib.h                 |    2 +
 src/include/gnunet_datacache_plugin.h              |    2 +
 src/include/gnunet_getopt_lib.h                    |   17 +
 src/include/gnunet_protocols.h                     |    6 +
 src/peerinfo/peerinfo_api.c                        |    3 +
 src/topology/gnunet-daemon-topology.c              |   31 +-
 src/transport/Makefile.am                          |   31 +
 .../gnunet-service-transport_validation.c          |   13 +-
 ...lugin_transport_tcp.c => plugin_transport_xt.c} |   52 +-
 src/transport/plugin_transport_xu.c                | 2492 ++++++++++++++++++++
 ...lugin_transport_udp.h => plugin_transport_xu.h} |  124 +-
 src/transport/transport.conf.in                    |   37 +
 src/util/getopt_helpers.c                          |   76 +
 31 files changed, 3213 insertions(+), 332 deletions(-)
 copy src/transport/{plugin_transport_tcp.c => plugin_transport_xt.c} (98%)
 create mode 100644 src/transport/plugin_transport_xu.c
 copy src/transport/{plugin_transport_udp.h => plugin_transport_xu.h} (64%)

diff --git a/src/core/gnunet-service-core_kx.c 
b/src/core/gnunet-service-core_kx.c
index 944d1e692..c29e46997 100644
--- a/src/core/gnunet-service-core_kx.c
+++ b/src/core/gnunet-service-core_kx.c
@@ -448,7 +448,14 @@ derive_auth_key (struct GNUNET_CRYPTO_AuthKey *akey,
 {
   static const char ctx[] = "authentication key";
 
-  GNUNET_CRYPTO_hmac_derive_key (akey, skey,
+  struct GNUNET_HashCode sh;
+  GNUNET_CRYPTO_hash (skey, sizeof (*skey), &sh);
+  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+              "Deriving Auth key from SKEY %s and seed %u\n",
+              GNUNET_h2s (&sh),
+              (unsigned int) seed);
+  GNUNET_CRYPTO_hmac_derive_key (akey,
+                                 skey,
                                  &seed, sizeof (seed),
                                  skey, sizeof (struct 
GNUNET_CRYPTO_SymmetricSessionKey),
                                  ctx, sizeof (ctx),
@@ -472,7 +479,15 @@ derive_iv (struct 
GNUNET_CRYPTO_SymmetricInitializationVector *iv,
 {
   static const char ctx[] = "initialization vector";
 
-  GNUNET_CRYPTO_symmetric_derive_iv (iv, skey,
+  struct GNUNET_HashCode sh;
+  GNUNET_CRYPTO_hash (skey, sizeof (*skey), &sh);
+  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+              "Deriving IV from SKEY %s and seed %u for peer %s\n",
+              GNUNET_h2s (&sh),
+              (unsigned int) seed,
+              GNUNET_i2s (identity));
+  GNUNET_CRYPTO_symmetric_derive_iv (iv,
+                                     skey,
                                      &seed, sizeof (seed),
                                     identity,
                                     sizeof (struct GNUNET_PeerIdentity), ctx,
@@ -498,7 +513,16 @@ derive_pong_iv (struct 
GNUNET_CRYPTO_SymmetricInitializationVector *iv,
 {
   static const char ctx[] = "pong initialization vector";
 
-  GNUNET_CRYPTO_symmetric_derive_iv (iv, skey,
+  struct GNUNET_HashCode sh;
+  GNUNET_CRYPTO_hash (skey, sizeof (*skey), &sh);
+  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+              "Deriving PONG IV from SKEY %s and seed %u/%u for %s\n",
+              GNUNET_h2s (&sh),
+              (unsigned int) seed,
+              (unsigned int) challenge,
+              GNUNET_i2s (identity));
+  GNUNET_CRYPTO_symmetric_derive_iv (iv,
+                                     skey,
                                      &seed, sizeof (seed),
                                     identity,
                                     sizeof (struct GNUNET_PeerIdentity),
@@ -524,6 +548,13 @@ derive_aes_key (const struct GNUNET_PeerIdentity *sender,
 {
   static const char ctx[] = "aes key generation vector";
 
+  struct GNUNET_HashCode sh;
+  GNUNET_CRYPTO_hash (skey, sizeof (*skey), &sh);
+  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+              "Deriving AES Keys for %s to %s from %s\n",
+              GNUNET_i2s (sender),
+              GNUNET_i2s2 (receiver),
+              GNUNET_h2s (key_material));
   GNUNET_CRYPTO_kdf (skey, sizeof (struct GNUNET_CRYPTO_SymmetricSessionKey),
                     ctx, sizeof (ctx),
                     key_material, sizeof (struct GNUNET_HashCode),
@@ -939,15 +970,26 @@ handle_ephemeral_key (void *cls,
   {
     GNUNET_STATISTICS_update (GSC_stats,
                               gettext_noop ("# old ephemeral keys ignored"),
-                             1, GNUNET_NO);
+                             1,
+                              GNUNET_NO);
+    GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
+                "Received expired EPHEMERAL_KEY from %s\n",
+                GNUNET_i2s (&m->origin_identity));
+    return;
+  }
+  if (0 == memcmp (&m->ephemeral_key,
+                   &kx->other_ephemeral_key,
+                   sizeof (m->ephemeral_key)))
+  {
+    GNUNET_STATISTICS_update (GSC_stats,
+                              gettext_noop ("# duplicate ephemeral keys 
ignored"),
+                             1,
+                              GNUNET_NO);
+    GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
+                "Ignoring duplicate EPHEMERAL_KEY from %s\n",
+                GNUNET_i2s (&m->origin_identity));
     return;
   }
-  start_t = GNUNET_TIME_absolute_ntoh (m->creation_time);
-
-  GNUNET_STATISTICS_update (GSC_stats,
-                            gettext_noop ("# ephemeral keys received"),
-                            1, GNUNET_NO);
-
   if (0 !=
       memcmp (&m->origin_identity,
              kx->peer,
@@ -960,9 +1002,6 @@ handle_ephemeral_key (void *cls,
     GNUNET_break_op (0);
     return;
   }
-  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
-              "Core service receives EPHEMERAL_KEY request from `%s'.\n",
-              GNUNET_i2s (kx->peer));
   if ((ntohl (m->purpose.size) !=
        sizeof (struct GNUNET_CRYPTO_EccSignaturePurpose) +
        sizeof (struct GNUNET_TIME_AbsoluteNBO) +
@@ -977,26 +1016,51 @@ handle_ephemeral_key (void *cls,
   {
     /* invalid signature */
     GNUNET_break_op (0);
+    GNUNET_STATISTICS_update (GSC_stats,
+                              gettext_noop ("# EPHEMERAL_KEYs rejected (bad 
signature)"),
+                              1,
+                              GNUNET_NO);
+    GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
+                "Received EPHEMERAL_KEY from %s with bad signature\n",
+                GNUNET_i2s (&m->origin_identity));
     return;
   }
   now = GNUNET_TIME_absolute_get ();
+  start_t = GNUNET_TIME_absolute_ntoh (m->creation_time);
   if ( (end_t.abs_value_us < GNUNET_TIME_absolute_subtract (now, 
REKEY_TOLERANCE).abs_value_us) ||
        (start_t.abs_value_us > GNUNET_TIME_absolute_add (now, 
REKEY_TOLERANCE).abs_value_us) )
   {
     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
-               _("Ephemeral key message from peer `%s' rejected as its 
validity range does not match our system time (%llu not in [%llu,%llu]).\n"),
+               _("EPHERMAL_KEY from peer `%s' rejected as its validity range 
does not match our system time (%llu not in [%llu,%llu]).\n"),
                GNUNET_i2s (kx->peer),
                (unsigned long long) now.abs_value_us,
                 (unsigned long long) start_t.abs_value_us,
                 (unsigned long long) end_t.abs_value_us);
+    GNUNET_STATISTICS_update (GSC_stats,
+                              gettext_noop ("# EPHEMERAL_KEY messages rejected 
due to time"),
+                              1,
+                              GNUNET_NO);
     return;
   }
+  {
+    struct GNUNET_HashCode eh;
+
+    GNUNET_CRYPTO_hash (&m->ephemeral_key,
+                        sizeof (m->ephemeral_key),
+                        &eh);
+    GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+                "Received valid EPHEMERAL_KEY `%s' from `%s' in state %d.\n",
+                GNUNET_h2s (&eh),
+                GNUNET_i2s (kx->peer),
+                kx->status);
+  }
+  GNUNET_STATISTICS_update (GSC_stats,
+                            gettext_noop ("# valid ephemeral keys received"),
+                            1,
+                            GNUNET_NO);
   kx->other_ephemeral_key = m->ephemeral_key;
   kx->foreign_key_expires = end_t;
   derive_session_keys (kx);
-  GNUNET_STATISTICS_update (GSC_stats,
-                            gettext_noop ("# EPHEMERAL_KEY messages 
received"), 1,
-                            GNUNET_NO);
 
   /* check if we still need to send the sender our key */
   sender_status = (enum GNUNET_CORE_KxState) ntohl (m->sender_status);
@@ -1130,8 +1194,9 @@ handle_ping (void *cls,
   {
     if (GNUNET_CORE_KX_STATE_REKEY_SENT != kx->status)
       GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
-                  "Decryption of PING from peer `%s' failed\n",
-                  GNUNET_i2s (kx->peer));
+                  "Decryption of PING from peer `%s' failed, PING for `%s'?\n",
+                  GNUNET_i2s (kx->peer),
+                  GNUNET_i2s2 (&t.target));
     else
       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
                   "Decryption of PING from peer `%s' failed after rekey 
(harmless)\n",
@@ -1328,7 +1393,7 @@ handle_pong (void *cls,
     return;
   }
   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
-              "Received PONG from `%s'\n",
+              "Received valid PONG from `%s'\n",
               GNUNET_i2s (kx->peer));
   /* no need to resend key any longer */
   if (NULL != kx->retry_set_key_task)
@@ -1395,10 +1460,18 @@ send_key (struct GSC_KeyExchangeInfo *kx)
      kx->retry_set_key_task = NULL;
   }
   /* always update sender status in SET KEY message */
-  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
-             "Sending key to `%s' (my status: %d)\n",
-              GNUNET_i2s (kx->peer),
-             kx->status);
+  {
+    struct GNUNET_HashCode hc;
+
+    GNUNET_CRYPTO_hash (&current_ekm.ephemeral_key,
+                        sizeof (current_ekm.ephemeral_key),
+                        &hc);
+    GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+                "Sending EPHERMERAL_KEY %s to `%s' (my status: %d)\n",
+                GNUNET_h2s (&hc),
+                GNUNET_i2s (kx->peer),
+                kx->status);
+  }
   current_ekm.sender_status = htonl ((int32_t) (kx->status));
   env = GNUNET_MQ_msg_copy (&current_ekm.header);
   GNUNET_MQ_send (kx->mq,
@@ -1454,10 +1527,18 @@ GSC_KX_encrypt_and_transmit (struct GSC_KeyExchangeInfo 
*kx,
                              &ph->sequence_number,
                              &em->sequence_number,
                              used - ENCRYPTED_HEADER_SIZE));
-  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
-              "Encrypted %u bytes for %s\n",
-              (unsigned int) (used - ENCRYPTED_HEADER_SIZE),
-              GNUNET_i2s (kx->peer));
+  {
+    struct GNUNET_HashCode hc;
+
+    GNUNET_CRYPTO_hash (&ph->sequence_number,
+                        used - ENCRYPTED_HEADER_SIZE,
+                        &hc);
+    GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+                "Encrypted payload `%s' of %u bytes for %s\n",
+                GNUNET_h2s (&hc),
+                (unsigned int) (used - ENCRYPTED_HEADER_SIZE),
+                GNUNET_i2s (kx->peer));
+  }
   derive_auth_key (&auth_key,
                   &kx->encrypt_key,
                   ph->iv_seed);
@@ -1465,6 +1546,18 @@ GSC_KX_encrypt_and_transmit (struct GSC_KeyExchangeInfo 
*kx,
                       &em->sequence_number,
                       used - ENCRYPTED_HEADER_SIZE,
                       &em->hmac);
+  {
+    struct GNUNET_HashCode hc;
+
+    GNUNET_CRYPTO_hash (&auth_key,
+                        sizeof (auth_key),
+                        &hc);
+    GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+                "For peer %s, used AC %s to create hmac %s\n",
+                GNUNET_i2s (kx->peer),
+                GNUNET_h2s (&hc),
+                GNUNET_h2s2 (&em->hmac));
+  }
   kx->has_excess_bandwidth = GNUNET_NO;
   GNUNET_MQ_send (kx->mq,
                  env);
@@ -1544,6 +1637,19 @@ handle_encrypted (void *cls,
   }
 
   /* validate hash */
+  {
+    struct GNUNET_HashCode hc;
+
+    GNUNET_CRYPTO_hash (&m->sequence_number,
+                        size - ENCRYPTED_HEADER_SIZE,
+                        &hc);
+    GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+                "Received encrypted payload `%s' of %u bytes from %s\n",
+                GNUNET_h2s (&hc),
+                (unsigned int) (size - ENCRYPTED_HEADER_SIZE),
+                GNUNET_i2s (kx->peer));
+  }
+
   derive_auth_key (&auth_key,
                    &kx->decrypt_key,
                    m->iv_seed);
@@ -1551,6 +1657,18 @@ handle_encrypted (void *cls,
                       &m->sequence_number,
                       size - ENCRYPTED_HEADER_SIZE,
                       &ph);
+  {
+    struct GNUNET_HashCode hc;
+
+    GNUNET_CRYPTO_hash (&auth_key,
+                        sizeof (auth_key),
+                        &hc);
+    GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+                "For peer %s, used AC %s to verify hmac %s\n",
+                GNUNET_i2s (kx->peer),
+                GNUNET_h2s (&hc),
+                GNUNET_h2s2 (&m->hmac));
+  }
   if (0 != memcmp (&ph,
                    &m->hmac,
                    sizeof (struct GNUNET_HashCode)))
@@ -1742,11 +1860,19 @@ do_rekey (void *cls)
                                             NULL);
   if (NULL != my_ephemeral_key)
     GNUNET_free (my_ephemeral_key);
-  GNUNET_log (GNUNET_ERROR_TYPE_INFO,
-              "Rekeying\n");
   my_ephemeral_key = GNUNET_CRYPTO_ecdhe_key_create ();
   GNUNET_assert (NULL != my_ephemeral_key);
   sign_ephemeral_key ();
+  {
+    struct GNUNET_HashCode eh;
+
+    GNUNET_CRYPTO_hash (&current_ekm.ephemeral_key,
+                        sizeof (current_ekm.ephemeral_key),
+                        &eh);
+    GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+                "Rekeying to %s\n",
+                GNUNET_h2s (&eh));
+  }
   for (pos = kx_head; NULL != pos; pos = pos->next)
   {
     if (GNUNET_CORE_KX_STATE_UP == pos->status)
@@ -1807,6 +1933,17 @@ GSC_KX_init (struct GNUNET_CRYPTO_EddsaPrivateKey *pk)
     return GNUNET_SYSERR;
   }
   sign_ephemeral_key ();
+  {
+    struct GNUNET_HashCode eh;
+
+    GNUNET_CRYPTO_hash (&current_ekm.ephemeral_key,
+                        sizeof (current_ekm.ephemeral_key),
+                        &eh);
+    GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+                "Starting with ephemeral key %s\n",
+                GNUNET_h2s (&eh));
+  }
+
   nc = GNUNET_notification_context_create (1);
   rekey_task = GNUNET_SCHEDULER_add_delayed (REKEY_FREQUENCY,
                                              &do_rekey,
diff --git a/src/datacache/datacache.c b/src/datacache/datacache.c
index 0646019bd..7c0a975da 100644
--- a/src/datacache/datacache.c
+++ b/src/datacache/datacache.c
@@ -260,6 +260,7 @@ GNUNET_DATACACHE_destroy (struct GNUNET_DATACACHE_Handle *h)
  *
  * @param h handle to the datacache
  * @param key key to store data under
+ * @param xor_distance distance of @a key to our PID
  * @param data_size number of bytes in @a data
  * @param data data to store
  * @param type type of the value
@@ -271,6 +272,7 @@ GNUNET_DATACACHE_destroy (struct GNUNET_DATACACHE_Handle *h)
 int
 GNUNET_DATACACHE_put (struct GNUNET_DATACACHE_Handle *h,
                       const struct GNUNET_HashCode *key,
+                      uint32_t xor_distance,
                       size_t data_size,
                       const char *data,
                       enum GNUNET_BLOCK_Type type,
@@ -282,6 +284,7 @@ GNUNET_DATACACHE_put (struct GNUNET_DATACACHE_Handle *h,
 
   used = h->api->put (h->api->cls,
                       key,
+                      xor_distance,
                      data_size,
                       data,
                      type,
diff --git a/src/datacache/plugin_datacache_heap.c 
b/src/datacache/plugin_datacache_heap.c
index 49e60bca1..ad5e7834d 100644
--- a/src/datacache/plugin_datacache_heap.c
+++ b/src/datacache/plugin_datacache_heap.c
@@ -31,7 +31,7 @@
 
 #define LOG_STRERROR_FILE(kind,op,fn) GNUNET_log_from_strerror_file (kind, 
"datacache-heap", op, fn)
 
-
+#define NUM_HEAPS 24
 
 /**
  * Context for all functions in this plugin.
@@ -49,9 +49,9 @@ struct Plugin
   struct GNUNET_CONTAINER_MultiHashMap *map;
 
   /**
-   * Heap for expirations.
+   * Heaps sorted by distance.
    */
-  struct GNUNET_CONTAINER_Heap *heap;
+  struct GNUNET_CONTAINER_Heap *heaps[NUM_HEAPS];
 
 };
 
@@ -92,6 +92,11 @@ struct Value
   unsigned int path_info_len;
 
   /**
+   * How close is the hash to us? Determines which heap we are in!
+   */
+  uint32_t distance;
+
+  /**
    * Type of the block.
    */
   enum GNUNET_BLOCK_Type type;
@@ -118,11 +123,6 @@ struct PutContext
   const char *data;
 
   /**
-   * Heap from the plugin.
-   */
-  struct GNUNET_CONTAINER_Heap *heap;
-
-  /**
    * Path information.
    */
   const struct GNUNET_PeerIdentity *path_info;
@@ -168,7 +168,9 @@ put_cb (void *cls,
 
   if ( (val->size == put_ctx->size) &&
        (val->type == put_ctx->type) &&
-       (0 == memcmp (&val[1], put_ctx->data, put_ctx->size)) )
+       (0 == memcmp (&val[1],
+                     put_ctx->data,
+                     put_ctx->size)) )
   {
     put_ctx->found = GNUNET_YES;
     val->discard_time = GNUNET_TIME_absolute_max (val->discard_time,
@@ -178,8 +180,8 @@ put_cb (void *cls,
                       val->path_info_len,
                       put_ctx->path_info_len);
     GNUNET_memcpy (val->path_info,
-           put_ctx->path_info,
-           put_ctx->path_info_len * sizeof (struct GNUNET_PeerIdentity));
+                   put_ctx->path_info,
+                   put_ctx->path_info_len * sizeof (struct 
GNUNET_PeerIdentity));
     GNUNET_CONTAINER_heap_update_cost (val->hn,
                                       val->discard_time.abs_value_us);
     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
@@ -199,6 +201,7 @@ put_cb (void *cls,
  *
  * @param cls closure (our `struct Plugin`)
  * @param key key to store data under
+ * @param xor_distance how close is @a key to our PID?
  * @param size number of bytes in @a data
  * @param data data to store
  * @param type type of the value
@@ -210,6 +213,7 @@ put_cb (void *cls,
 static ssize_t
 heap_plugin_put (void *cls,
                  const struct GNUNET_HashCode *key,
+                 uint32_t xor_distance,
                  size_t size,
                 const char *data,
                  enum GNUNET_BLOCK_Type type,
@@ -222,7 +226,6 @@ heap_plugin_put (void *cls,
   struct PutContext put_ctx;
 
   put_ctx.found = GNUNET_NO;
-  put_ctx.heap = plugin->heap;
   put_ctx.data = data;
   put_ctx.size = size;
   put_ctx.path_info = path_info;
@@ -236,22 +239,28 @@ heap_plugin_put (void *cls,
   if (GNUNET_YES == put_ctx.found)
     return 0;
   val = GNUNET_malloc (sizeof (struct Value) + size);
-  GNUNET_memcpy (&val[1], data, size);
+  GNUNET_memcpy (&val[1],
+                 data,
+                 size);
   val->key = *key;
   val->type = type;
   val->discard_time = discard_time;
   val->size = size;
+  if (xor_distance >= NUM_HEAPS)
+    val->distance = NUM_HEAPS - 1;
+  else
+    val->distance = xor_distance;
   GNUNET_array_grow (val->path_info,
                     val->path_info_len,
                     path_info_len);
   GNUNET_memcpy (val->path_info,
-          path_info,
-         path_info_len * sizeof (struct GNUNET_PeerIdentity));
+                 path_info,
+                 path_info_len * sizeof (struct GNUNET_PeerIdentity));
   (void) GNUNET_CONTAINER_multihashmap_put (plugin->map,
                                            &val->key,
                                            val,
                                            
GNUNET_CONTAINER_MULTIHASHMAPOPTION_MULTIPLE);
-  val->hn = GNUNET_CONTAINER_heap_insert (plugin->heap,
+  val->hn = GNUNET_CONTAINER_heap_insert (plugin->heaps[val->distance],
                                          val,
                                          val->discard_time.abs_value_us);
   return size + OVERHEAD;
@@ -369,7 +378,12 @@ heap_plugin_del (void *cls)
   struct Plugin *plugin = cls;
   struct Value *val;
 
-  val = GNUNET_CONTAINER_heap_remove_root (plugin->heap);
+  for (unsigned int i=0;i<NUM_HEAPS;i++)
+  {
+    val = GNUNET_CONTAINER_heap_remove_root (plugin->heaps[i]);
+    if (NULL != val)
+      break;
+  }
   if (NULL == val)
     return GNUNET_SYSERR;
   GNUNET_assert (GNUNET_YES ==
@@ -413,6 +427,53 @@ heap_plugin_get_random (void *cls,
 
 
 /**
+ * Closure for #find_closest().
+ */
+struct GetClosestContext
+{
+  struct Value **values;
+
+  unsigned int num_results;
+
+  const struct GNUNET_HashCode *key;
+};
+
+
+static int
+find_closest (void *cls,
+              const struct GNUNET_HashCode *key,
+              void *value)
+{
+  struct GetClosestContext *gcc = cls;
+  struct Value *val = value;
+  unsigned int j;
+
+  if (1 != GNUNET_CRYPTO_hash_cmp (key,
+                                   gcc->key))
+    return GNUNET_OK; /* useless */
+  j = gcc->num_results;
+  for (unsigned int i=0;i<gcc->num_results;i++)
+  {
+    if (NULL == gcc->values[i])
+    {
+      j = i;
+      break;
+    }
+    if (1 == GNUNET_CRYPTO_hash_cmp (&gcc->values[i]->key,
+                                     key))
+    {
+      j = i;
+      break;
+    }
+  }
+  if (j == gcc->num_results)
+    return GNUNET_OK;
+  gcc->values[j] = val;
+  return GNUNET_OK;
+}
+
+
+/**
  * Iterate over the results that are "close" to a particular key in
  * the datacache.  "close" is defined as numerically larger than @a
  * key (when interpreted as a circular address space), with small
@@ -432,8 +493,30 @@ heap_plugin_get_closest (void *cls,
                          GNUNET_DATACACHE_Iterator iter,
                          void *iter_cls)
 {
-  GNUNET_break (0); // not implemented!
-  return 0;
+  struct Plugin *plugin = cls;
+  struct Value *values[num_results];
+  struct GetClosestContext gcc = {
+    .values = values,
+    .num_results = num_results,
+    .key = key
+  };
+  GNUNET_CONTAINER_multihashmap_iterate (plugin->map,
+                                         &find_closest,
+                                         &gcc);
+  for (unsigned int i=0;i<num_results;i++)
+  {
+    if (NULL == values[i])
+      return i;
+    iter (iter_cls,
+          &values[i]->key,
+          values[i]->size,
+          (void *) &values[i][1],
+          values[i]->type,
+          values[i]->discard_time,
+          values[i]->path_info_len,
+          values[i]->path_info);
+  }
+  return num_results;
 }
 
 
@@ -453,7 +536,8 @@ libgnunet_plugin_datacache_heap_init (void *cls)
   plugin = GNUNET_new (struct Plugin);
   plugin->map = GNUNET_CONTAINER_multihashmap_create (1024,  /* FIXME: base on 
quota! */
                                                      GNUNET_YES);
-  plugin->heap = GNUNET_CONTAINER_heap_create 
(GNUNET_CONTAINER_HEAP_ORDER_MIN);
+  for (unsigned int i=0;i<NUM_HEAPS;i++)
+    plugin->heaps[i] = GNUNET_CONTAINER_heap_create 
(GNUNET_CONTAINER_HEAP_ORDER_MIN);
   plugin->env = env;
   api = GNUNET_new (struct GNUNET_DATACACHE_PluginFunctions);
   api->cls = plugin;
@@ -481,16 +565,19 @@ libgnunet_plugin_datacache_heap_done (void *cls)
   struct Plugin *plugin = api->cls;
   struct Value *val;
 
-  while (NULL != (val = GNUNET_CONTAINER_heap_remove_root (plugin->heap)))
+  for (unsigned int i=0;i<NUM_HEAPS;i++)
   {
-    GNUNET_assert (GNUNET_YES ==
-                  GNUNET_CONTAINER_multihashmap_remove (plugin->map,
-                                                        &val->key,
-                                                        val));
-    GNUNET_free_non_null (val->path_info);
-    GNUNET_free (val);
+    while (NULL != (val = GNUNET_CONTAINER_heap_remove_root 
(plugin->heaps[i])))
+    {
+      GNUNET_assert (GNUNET_YES ==
+                     GNUNET_CONTAINER_multihashmap_remove (plugin->map,
+                                                           &val->key,
+                                                           val));
+      GNUNET_free_non_null (val->path_info);
+      GNUNET_free (val);
+    }
+    GNUNET_CONTAINER_heap_destroy (plugin->heaps[i]);
   }
-  GNUNET_CONTAINER_heap_destroy (plugin->heap);
   GNUNET_CONTAINER_multihashmap_destroy (plugin->map);
   GNUNET_free (plugin);
   GNUNET_free (api);
diff --git a/src/datacache/plugin_datacache_postgres.c 
b/src/datacache/plugin_datacache_postgres.c
index 2c233c4c2..c6ccfb210 100644
--- a/src/datacache/plugin_datacache_postgres.c
+++ b/src/datacache/plugin_datacache_postgres.c
@@ -141,6 +141,7 @@ init_connection (struct Plugin *plugin)
  *
  * @param cls closure (our `struct Plugin`)
  * @param key key to store @a data under
+ * @param am_closest are we the closest peer?
  * @param data_size number of bytes in @a data
  * @param data data to store
  * @param type type of the value
@@ -152,6 +153,7 @@ init_connection (struct Plugin *plugin)
 static ssize_t
 postgres_plugin_put (void *cls,
                      const struct GNUNET_HashCode *key,
+                     int am_closest,
                      size_t data_size,
                      const char *data,
                      enum GNUNET_BLOCK_Type type,
diff --git a/src/datacache/plugin_datacache_sqlite.c 
b/src/datacache/plugin_datacache_sqlite.c
index 15438b29b..ef24199ca 100644
--- a/src/datacache/plugin_datacache_sqlite.c
+++ b/src/datacache/plugin_datacache_sqlite.c
@@ -38,7 +38,7 @@
  * How much overhead do we assume per entry in the
  * datacache?
  */
-#define OVERHEAD (sizeof(struct GNUNET_HashCode) + 32)
+#define OVERHEAD (sizeof(struct GNUNET_HashCode) + 36)
 
 /**
  * Context for all functions in this plugin.
@@ -83,6 +83,11 @@ struct Plugin
   /**
    * Prepared statement for #sqlite_plugin_del.
    */
+  sqlite3_stmt *del_expired_stmt;
+
+  /**
+   * Prepared statement for #sqlite_plugin_del.
+   */
   sqlite3_stmt *del_stmt;
 
   /**
@@ -150,6 +155,7 @@ sq_prepare (sqlite3 *dbh,
  *
  * @param cls closure (our `struct Plugin`)
  * @param key key to store @a data under
+ * @param xor_distance how close is @a key to our PID?
  * @param size number of bytes in @a data
  * @param data data to store
  * @param type type of the value
@@ -161,6 +167,7 @@ sq_prepare (sqlite3 *dbh,
 static ssize_t
 sqlite_plugin_put (void *cls,
                   const struct GNUNET_HashCode *key,
+                   uint32_t xor_distance,
                   size_t size,
                    const char *data,
                   enum GNUNET_BLOCK_Type type,
@@ -174,6 +181,7 @@ sqlite_plugin_put (void *cls,
     GNUNET_SQ_query_param_uint32 (&type32),
     GNUNET_SQ_query_param_absolute_time (&discard_time),
     GNUNET_SQ_query_param_auto_from_type (key),
+    GNUNET_SQ_query_param_uint32 (&xor_distance),
     GNUNET_SQ_query_param_fixed_size (data, size),
     GNUNET_SQ_query_param_fixed_size (path_info,
                                       path_info_len * sizeof (struct 
GNUNET_PeerIdentity)),
@@ -387,6 +395,7 @@ sqlite_plugin_del (void *cls)
   void *data;
   size_t dsize;
   struct GNUNET_HashCode hc;
+  struct GNUNET_TIME_Absolute now;
   struct GNUNET_SQ_ResultSpec rs[] = {
     GNUNET_SQ_result_spec_uint64 (&rowid),
     GNUNET_SQ_result_spec_auto_from_type (&hc),
@@ -398,27 +407,52 @@ sqlite_plugin_del (void *cls)
     GNUNET_SQ_query_param_uint64 (&rowid),
     GNUNET_SQ_query_param_end
   };
+  struct GNUNET_SQ_QueryParam time_params[] = {
+    GNUNET_SQ_query_param_absolute_time (&now),
+    GNUNET_SQ_query_param_end
+  };
 
   LOG (GNUNET_ERROR_TYPE_DEBUG,
        "Processing DEL\n");
-  if (SQLITE_ROW !=
-      sqlite3_step (plugin->del_select_stmt))
+  now = GNUNET_TIME_absolute_get ();
+  if (GNUNET_OK !=
+      GNUNET_SQ_bind (plugin->del_expired_stmt,
+                      time_params))
   {
     LOG_SQLITE (plugin->dbh,
                 GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
-                "sqlite3_step");
+                "sqlite3_bind");
     GNUNET_SQ_reset (plugin->dbh,
-                     plugin->del_select_stmt);
+                     plugin->del_expired_stmt);
     return GNUNET_SYSERR;
   }
-  if (GNUNET_OK !=
-      GNUNET_SQ_extract_result (plugin->del_select_stmt,
-                                rs))
+  if ( (SQLITE_ROW !=
+        sqlite3_step (plugin->del_expired_stmt)) ||
+       (GNUNET_OK !=
+        GNUNET_SQ_extract_result (plugin->del_expired_stmt,
+                                  rs)) )
   {
-    GNUNET_break (0);
     GNUNET_SQ_reset (plugin->dbh,
-                     plugin->del_select_stmt);
-    return GNUNET_SYSERR;
+                     plugin->del_expired_stmt);
+    if (SQLITE_ROW !=
+        sqlite3_step (plugin->del_select_stmt))
+    {
+      LOG_SQLITE (plugin->dbh,
+                  GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
+                  "sqlite3_step");
+      GNUNET_SQ_reset (plugin->dbh,
+                       plugin->del_select_stmt);
+      return GNUNET_SYSERR;
+    }
+    if (GNUNET_OK !=
+        GNUNET_SQ_extract_result (plugin->del_select_stmt,
+                                  rs))
+    {
+      GNUNET_SQ_reset (plugin->dbh,
+                       plugin->del_select_stmt);
+      GNUNET_break (0);
+      return GNUNET_SYSERR;
+    }
   }
   GNUNET_SQ_cleanup_result (rs);
   GNUNET_SQ_reset (plugin->dbh,
@@ -709,13 +743,15 @@ libgnunet_plugin_datacache_sqlite_init (void *cls)
     SQLITE3_EXEC (dbh, "PRAGMA sqlite_temp_store=3");
 
   SQLITE3_EXEC (dbh,
-                "CREATE TABLE ds090 (" "  type INTEGER NOT NULL DEFAULT 0,"
-                "  expire INTEGER NOT NULL DEFAULT 0,"
+                "CREATE TABLE ds091 ("
+                "  type INTEGER NOT NULL DEFAULT 0,"
+                "  expire INTEGER NOT NULL,"
                 "  key BLOB NOT NULL DEFAULT '',"
-                "  value BLOB NOT NULL DEFAULT '',"
+                "  prox INTEGER NOT NULL,"
+                "  value BLOB NOT NULL,"
                "  path BLOB DEFAULT '')");
-  SQLITE3_EXEC (dbh, "CREATE INDEX idx_hashidx ON ds090 (key,type,expire)");
-  SQLITE3_EXEC (dbh, "CREATE INDEX idx_expire ON ds090 (expire)");
+  SQLITE3_EXEC (dbh, "CREATE INDEX idx_hashidx ON ds091 (key,type,expire)");
+  SQLITE3_EXEC (dbh, "CREATE INDEX idx_expire ON ds091 (prox,expire)");
   plugin = GNUNET_new (struct Plugin);
   plugin->env = env;
   plugin->dbh = dbh;
@@ -723,35 +759,42 @@ libgnunet_plugin_datacache_sqlite_init (void *cls)
 
   if ( (SQLITE_OK !=
         sq_prepare (plugin->dbh,
-                    "INSERT INTO ds090 (type, expire, key, value, path) "
-                    "VALUES (?, ?, ?, ?, ?)",
+                    "INSERT INTO ds091 (type, expire, key, prox, value, path) "
+                    "VALUES (?, ?, ?, ?, ?, ?)",
                     &plugin->insert_stmt)) ||
        (SQLITE_OK !=
         sq_prepare (plugin->dbh,
-                    "SELECT count(*) FROM ds090 "
+                    "SELECT count(*) FROM ds091 "
                     "WHERE key=? AND type=? AND expire >= ?",
                     &plugin->get_count_stmt)) ||
        (SQLITE_OK !=
         sq_prepare (plugin->dbh,
-                    "SELECT value,expire,path FROM ds090 "
-                    "WHERE key=? AND type=? AND expire >= ? LIMIT 1 OFFSET ?",
+                    "SELECT value,expire,path FROM ds091"
+                    " WHERE key=? AND type=? AND expire >= ? LIMIT 1 OFFSET ?",
                     &plugin->get_stmt)) ||
        (SQLITE_OK !=
         sq_prepare (plugin->dbh,
-                    "SELECT _ROWID_,key,value FROM ds090 ORDER BY expire ASC 
LIMIT 1",
+                    "SELECT _ROWID_,key,value FROM ds091"
+                    " WHERE expire < ?"
+                    " ORDER BY expire ASC LIMIT 1",
+                    &plugin->del_expired_stmt)) ||
+       (SQLITE_OK !=
+        sq_prepare (plugin->dbh,
+                    "SELECT _ROWID_,key,value FROM ds091"
+                    " ORDER BY prox ASC, expire ASC LIMIT 1",
                     &plugin->del_select_stmt)) ||
        (SQLITE_OK !=
         sq_prepare (plugin->dbh,
-                    "DELETE FROM ds090 WHERE _ROWID_=?",
+                    "DELETE FROM ds091 WHERE _ROWID_=?",
                     &plugin->del_stmt)) ||
        (SQLITE_OK !=
         sq_prepare (plugin->dbh,
-                    "SELECT value,expire,path,key,type FROM ds090 "
+                    "SELECT value,expire,path,key,type FROM ds091 "
                     "ORDER BY key LIMIT 1 OFFSET ?",
                     &plugin->get_random_stmt)) ||
        (SQLITE_OK !=
         sq_prepare (plugin->dbh,
-                    "SELECT value,expire,path,type,key FROM ds090 "
+                    "SELECT value,expire,path,type,key FROM ds091 "
                     "WHERE key>=? AND expire >= ? ORDER BY KEY ASC LIMIT ?",
                     &plugin->get_closest_stmt))
        )
@@ -807,6 +850,7 @@ libgnunet_plugin_datacache_sqlite_done (void *cls)
   sqlite3_finalize (plugin->get_count_stmt);
   sqlite3_finalize (plugin->get_stmt);
   sqlite3_finalize (plugin->del_select_stmt);
+  sqlite3_finalize (plugin->del_expired_stmt);
   sqlite3_finalize (plugin->del_stmt);
   sqlite3_finalize (plugin->get_random_stmt);
   sqlite3_finalize (plugin->get_closest_stmt);
diff --git a/src/datacache/plugin_datacache_template.c 
b/src/datacache/plugin_datacache_template.c
index b9baa64d3..1064d3125 100644
--- a/src/datacache/plugin_datacache_template.c
+++ b/src/datacache/plugin_datacache_template.c
@@ -45,6 +45,7 @@ struct Plugin
  *
  * @param cls closure (our `struct Plugin`)
  * @param key key to store @a data under
+ * @param xor_distance distance of @a key to our PID
  * @param size number of bytes in @a data
  * @param data data to store
  * @param type type of the value
@@ -56,6 +57,7 @@ struct Plugin
 static ssize_t
 template_plugin_put (void *cls,
                      const struct GNUNET_HashCode *key,
+                     uint32_t xor_distance,
                      size_t size,
                      const char *data,
                      enum GNUNET_BLOCK_Type type,
diff --git a/src/datacache/test_datacache.c b/src/datacache/test_datacache.c
index 79e6b6d74..c4d59c3cc 100644
--- a/src/datacache/test_datacache.c
+++ b/src/datacache/test_datacache.c
@@ -87,7 +87,10 @@ run (void *cls, char *const *args, const char *cfgfile,
   {
     GNUNET_CRYPTO_hash (&k, sizeof (struct GNUNET_HashCode), &n);
     ASSERT (GNUNET_OK ==
-            GNUNET_DATACACHE_put (h, &k, sizeof (struct GNUNET_HashCode),
+            GNUNET_DATACACHE_put (h,
+                                  &k,
+                                  GNUNET_YES,
+                                  sizeof (struct GNUNET_HashCode),
                                   (const char *) &n, 1 + i % 16, exp,
                                  0, NULL));
     k = n;
@@ -103,7 +106,10 @@ run (void *cls, char *const *args, const char *cfgfile,
   memset (&k, 42, sizeof (struct GNUNET_HashCode));
   GNUNET_CRYPTO_hash (&k, sizeof (struct GNUNET_HashCode), &n);
   ASSERT (GNUNET_OK ==
-          GNUNET_DATACACHE_put (h, &k, sizeof (struct GNUNET_HashCode),
+          GNUNET_DATACACHE_put (h,
+                                &k,
+                                GNUNET_YES,
+                                sizeof (struct GNUNET_HashCode),
                                 (const char *) &n, 792,
                                 GNUNET_TIME_UNIT_FOREVER_ABS,
                                0, NULL));
diff --git a/src/datacache/test_datacache_quota.c 
b/src/datacache/test_datacache_quota.c
index 78b56ce42..35357a8d2 100644
--- a/src/datacache/test_datacache_quota.c
+++ b/src/datacache/test_datacache_quota.c
@@ -73,7 +73,15 @@ run (void *cls, char *const *args, const char *cfgfile,
     {
       exp.abs_value_us++;
       buf[j] = i;
-      ASSERT (GNUNET_OK == GNUNET_DATACACHE_put (h, &k, j, buf, 1 + i, exp, 0, 
NULL));
+      ASSERT (GNUNET_OK == GNUNET_DATACACHE_put (h,
+                                                 &k,
+                                                 GNUNET_YES,
+                                                 j,
+                                                 buf,
+                                                 1 + i,
+                                                 exp,
+                                                 0,
+                                                 NULL));
       ASSERT (0 < GNUNET_DATACACHE_get (h, &k, 1 + i, NULL, NULL));
     }
     k = n;
diff --git a/src/dht/gnunet-dht-get.c b/src/dht/gnunet-dht-get.c
index 842ec6270..afcd5422c 100644
--- a/src/dht/gnunet-dht-get.c
+++ b/src/dht/gnunet-dht-get.c
@@ -154,7 +154,9 @@ get_result_iterator (void *cls, struct GNUNET_TIME_Absolute 
exp,
                      const void *data)
 {
   FPRINTF (stdout,
-          _("Result %d, type %d:\n%.*s\n"),
+          (GNUNET_BLOCK_TYPE_TEST == type)
+           ? _("Result %d, type %d:\n%.*s\n")
+           : _("Result %d, type %d:\n"),
           result_count,
            type,
            (unsigned int) size,
@@ -196,8 +198,6 @@ run (void *cls, char *const *args, const char *cfgfile,
 {
   struct GNUNET_HashCode key;
 
-
-
   cfg = c;
   if (NULL == query_key)
   {
@@ -215,17 +215,22 @@ run (void *cls, char *const *args, const char *cfgfile,
     query_type = GNUNET_BLOCK_TYPE_TEST;
   GNUNET_CRYPTO_hash (query_key, strlen (query_key), &key);
   if (verbose)
-    FPRINTF (stderr, "%s `%s' \n",  _("Issueing DHT GET with key"), 
GNUNET_h2s_full (&key));
+    FPRINTF (stderr, "%s `%s' \n",
+             _("Issueing DHT GET with key"),
+             GNUNET_h2s_full (&key));
   GNUNET_SCHEDULER_add_shutdown (&cleanup_task, NULL);
   tt = GNUNET_SCHEDULER_add_delayed (timeout_request,
-                                    &timeout_task, NULL);
+                                    &timeout_task,
+                                     NULL);
   get_handle =
       GNUNET_DHT_get_start (dht_handle, query_type, &key, replication,
                             (demultixplex_everywhere) ? 
GNUNET_DHT_RO_DEMULTIPLEX_EVERYWHERE : GNUNET_DHT_RO_NONE,
-                            NULL, 0, &get_result_iterator, NULL);
-
+                            NULL, 0,
+                            &get_result_iterator,
+                            NULL);
 }
 
+
 /**
  * Entry point for gnunet-dht-get
  *
@@ -236,15 +241,12 @@ run (void *cls, char *const *args, const char *cfgfile,
 int
 main (int argc, char *const *argv)
 {
-
   struct GNUNET_GETOPT_CommandLineOption options[] = {
-
     GNUNET_GETOPT_option_string ('k',
                                  "key",
                                  "KEY",
                                  gettext_noop ("the query key"),
                                  &query_key),
-
     GNUNET_GETOPT_option_uint ('r',
                                "replication",
                                "LEVEL",
diff --git a/src/dht/gnunet-dht-monitor.c b/src/dht/gnunet-dht-monitor.c
index b7360bbab..a699b3d17 100644
--- a/src/dht/gnunet-dht-monitor.c
+++ b/src/dht/gnunet-dht-monitor.c
@@ -141,7 +141,8 @@ get_callback (void *cls,
               const struct GNUNET_PeerIdentity *path,
               const struct GNUNET_HashCode * key)
 {
-  FPRINTF (stdout, "GET #%u: type %d, key `%s'\n",
+  FPRINTF (stdout,
+           "GET #%u: type %d, key `%s'\n",
            result_count,
            (int) type,
            GNUNET_h2s_full(key));
@@ -176,8 +177,11 @@ get_resp_callback (void *cls,
                    size_t size)
 {
   FPRINTF (stdout,
-          "RESPONSE #%u: type %d, key `%s', data `%.*s'\n",
+           (GNUNET_BLOCK_TYPE_TEST == type)
+          ? "RESPONSE #%u (%s): type %d, key `%s', data `%.*s'\n"
+           : "RESPONSE #%u (%s): type %d, key `%s'\n",
            result_count,
+           GNUNET_STRINGS_absolute_time_to_string (exp),
            (int) type,
            GNUNET_h2s_full (key),
            (unsigned int) size,
@@ -215,8 +219,11 @@ put_callback (void *cls,
               size_t size)
 {
   FPRINTF (stdout,
-          "PUT %u: type %d, key `%s', data `%.*s'\n",
+           (GNUNET_BLOCK_TYPE_TEST == type)
+          ? "PUT %u (%s): type %d, key `%s', data `%.*s'\n"
+           : "PUT %u (%s): type %d, key `%s'\n",
            result_count,
+           GNUNET_STRINGS_absolute_time_to_string (exp),
            (int) type,
            GNUNET_h2s_full(key),
            (unsigned int) size,
@@ -234,7 +241,9 @@ put_callback (void *cls,
  * @param c configuration
  */
 static void
-run (void *cls, char *const *args, const char *cfgfile,
+run (void *cls,
+     char *const *args,
+     const char *cfgfile,
      const struct GNUNET_CONFIGURATION_Handle *c)
 {
   struct GNUNET_HashCode *key;
@@ -291,30 +300,30 @@ int
 main (int argc, char *const *argv)
 {
   struct GNUNET_GETOPT_CommandLineOption options[] = {
-  
+
     GNUNET_GETOPT_option_string ('k',
                                  "key",
                                  "KEY",
                                  gettext_noop ("the query key"),
                                  &query_key),
-  
+
     GNUNET_GETOPT_option_uint ('t',
                                    "type",
                                    "TYPE",
                                    gettext_noop ("the type of data to look 
for"),
                                    &block_type),
-  
+
     GNUNET_GETOPT_option_relative_time ('T',
                                             "timeout",
                                             "TIMEOUT",
                                             gettext_noop ("how long should the 
monitor command run"),
                                             &timeout_request),
-  
+
     GNUNET_GETOPT_option_flag ('V',
                                   "verbose",
                                   gettext_noop ("be verbose (print progress 
information)"),
                                   &verbose),
-  
+
     GNUNET_GETOPT_OPTION_END
   };
 
diff --git a/src/dht/gnunet-service-dht_datacache.c 
b/src/dht/gnunet-service-dht_datacache.c
index 36047d561..07a666db6 100644
--- a/src/dht/gnunet-service-dht_datacache.c
+++ b/src/dht/gnunet-service-dht_datacache.c
@@ -26,6 +26,7 @@
 #include "platform.h"
 #include "gnunet_datacache_lib.h"
 #include "gnunet-service-dht_datacache.h"
+#include "gnunet-service-dht_neighbours.h"
 #include "gnunet-service-dht_routing.h"
 #include "gnunet-service-dht.h"
 
@@ -79,10 +80,13 @@ GDS_DATACACHE_handle_put (struct GNUNET_TIME_Absolute 
expiration,
   }
   /* Put size is actual data size plus struct overhead plus path length (if 
any) */
   GNUNET_STATISTICS_update (GDS_stats,
-                            gettext_noop ("# ITEMS stored in datacache"), 1,
+                            gettext_noop ("# ITEMS stored in datacache"),
+                            1,
                             GNUNET_NO);
   r = GNUNET_DATACACHE_put (datacache,
                             key,
+                            GNUNET_CRYPTO_hash_matching_bits (key,
+                                                              
&my_identity_hash),
                             data_size,
                             data,
                             type,
diff --git a/src/dht/gnunet-service-dht_neighbours.c 
b/src/dht/gnunet-service-dht_neighbours.c
index 0309bea88..94844374d 100644
--- a/src/dht/gnunet-service-dht_neighbours.c
+++ b/src/dht/gnunet-service-dht_neighbours.c
@@ -404,7 +404,7 @@ static struct GNUNET_PeerIdentity my_identity;
 /**
  * Hash of the identity of this peer.
  */
-static struct GNUNET_HashCode my_identity_hash;
+struct GNUNET_HashCode my_identity_hash;
 
 /**
  * Handle to CORE.
@@ -421,7 +421,7 @@ static struct GNUNET_ATS_ConnectivityHandle *ats_ch;
  * Find the optimal bucket for this key.
  *
  * @param hc the hashcode to compare our identity to
- * @return the proper bucket index, or GNUNET_SYSERR
+ * @return the proper bucket index, or #GNUNET_SYSERR
  *         on error (same hashcode)
  */
 static int
@@ -941,9 +941,9 @@ get_distance (const struct GNUNET_HashCode *target,
  * @return #GNUNET_YES if node location is closest,
  *         #GNUNET_NO otherwise.
  */
-static int
-am_closest_peer (const struct GNUNET_HashCode *key,
-                 const struct GNUNET_CONTAINER_BloomFilter *bloom)
+int
+GDS_am_closest_peer (const struct GNUNET_HashCode *key,
+                     const struct GNUNET_CONTAINER_BloomFilter *bloom)
 {
   int bits;
   int other_bits;
@@ -1803,7 +1803,7 @@ handle_dht_p2p_put (void *cls,
                              payload);
     /* store locally */
     if ((0 != (options & GNUNET_DHT_RO_DEMULTIPLEX_EVERYWHERE)) ||
-        (am_closest_peer (&put->key, bf)))
+        (GDS_am_closest_peer (&put->key, bf)))
       GDS_DATACACHE_handle_put (GNUNET_TIME_absolute_ntoh 
(put->expiration_time),
                                 &put->key,
                                 putlen,
@@ -2122,7 +2122,7 @@ handle_dht_p2p_get (void *cls,
               (unsigned int) ntohl (get->hop_count));
   /* local lookup (this may update the reply_bf) */
   if ((0 != (options & GNUNET_DHT_RO_DEMULTIPLEX_EVERYWHERE)) ||
-      (am_closest_peer (&get->key,
+      (GDS_am_closest_peer (&get->key,
                        peer_bf)))
   {
     if ((0 != (options & GNUNET_DHT_RO_FIND_PEER)))
diff --git a/src/dht/gnunet-service-dht_neighbours.h 
b/src/dht/gnunet-service-dht_neighbours.h
index 34b76ee8a..bf3ed80a2 100644
--- a/src/dht/gnunet-service-dht_neighbours.h
+++ b/src/dht/gnunet-service-dht_neighbours.h
@@ -32,6 +32,12 @@
 #include "gnunet_dht_service.h"
 
 /**
+ * Hash of the identity of this peer.
+ */
+extern struct GNUNET_HashCode my_identity_hash;
+
+
+/**
  * Perform a PUT operation.  Forwards the given request to other
  * peers.   Does not store the data locally.  Does not give the
  * data to local clients.  May do nothing if this is the only
@@ -123,6 +129,22 @@ GDS_NEIGHBOURS_handle_reply (const struct 
GNUNET_PeerIdentity *target,
 
 
 /**
+ * Check whether my identity is closer than any known peers.  If a
+ * non-null bloomfilter is given, check if this is the closest peer
+ * that hasn't already been routed to.
+ *
+ * @param key hash code to check closeness to
+ * @param bloom bloomfilter, exclude these entries from the decision
+ * @return #GNUNET_YES if node location is closest,
+ *         #GNUNET_NO otherwise.
+ */
+int
+GDS_am_closest_peer (const struct GNUNET_HashCode *key,
+                     const struct GNUNET_CONTAINER_BloomFilter *bloom);
+
+
+
+/**
  * Initialize neighbours subsystem.
  *
  * @return #GNUNET_OK on success, #GNUNET_SYSERR on error
diff --git a/src/gns/gnunet-bcd.c b/src/gns/gnunet-bcd.c
index 0746d5c57..d7fd1a812 100644
--- a/src/gns/gnunet-bcd.c
+++ b/src/gns/gnunet-bcd.c
@@ -76,7 +76,7 @@ static char *resfile;
 /**
  * Port number.
  */
-static unsigned int port = 8888;
+static uint16_t port = 8888;
 
 
 struct Entry
@@ -351,7 +351,7 @@ prepare_daemon (struct MHD_Daemon *daemon_handle)
 static int
 server_start ()
 {
-  if ((0 == port) || (port > UINT16_MAX))
+  if (0 == port) 
   {
     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
                 _("Invalid port number %u.  Exiting.\n"),
@@ -362,7 +362,7 @@ server_start ()
               _("Businesscard HTTP server starts on %u\n"),
               port);
   daemon_handle = MHD_start_daemon (MHD_USE_DUAL_STACK | MHD_USE_DEBUG,
-                                    (uint16_t) port,
+                                    port,
                                     NULL /* accept_policy_callback */, NULL,
                                     &access_handler_callback, NULL,
                                     MHD_OPTION_CONNECTION_LIMIT, (unsigned 
int) 512,
@@ -374,7 +374,7 @@ server_start ()
   {
     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
                 _("Could not start businesscard HTTP server on port %u\n"),
-                (unsigned short) port);
+                (unsigned int) port);
     return GNUNET_SYSERR;
   }
   http_task = prepare_daemon (daemon_handle);
@@ -516,17 +516,17 @@ main (int argc, char *const *argv)
 {
   struct GNUNET_GETOPT_CommandLineOption options[] = {
 
-    GNUNET_GETOPT_option_uint ('p',
-                                   "port",
-                                   "PORT",
-                                   gettext_noop ("Run HTTP serve on port PORT 
(default is 8888)"),
-                                   &port),
-
+    GNUNET_GETOPT_option_uint16 ('p',
+                                "port",
+                                "PORT",
+                                gettext_noop ("Run HTTP serve on port PORT 
(default is 8888)"),
+                                &port),
     GNUNET_GETOPT_OPTION_END
   };
   int ret;
 
-  if (GNUNET_OK != GNUNET_STRINGS_get_utf8_args (argc, argv, &argc, &argv))
+  if (GNUNET_OK !=
+      GNUNET_STRINGS_get_utf8_args (argc, argv, &argc, &argv))
     return 2;
   GNUNET_log_setup ("gnunet-bcd", "WARNING", NULL);
   ret =
diff --git a/src/gns/gnunet-gns-proxy.c b/src/gns/gnunet-gns-proxy.c
index 02ebcf0f1..0d7d83b4b 100644
--- a/src/gns/gnunet-gns-proxy.c
+++ b/src/gns/gnunet-gns-proxy.c
@@ -636,7 +636,7 @@ struct Socks5Request
 /**
  * The port the proxy is running on (default 7777)
  */
-static unsigned long long port = GNUNET_GNS_PROXY_PORT;
+static uint16_t port = GNUNET_GNS_PROXY_PORT;
 
 /**
  * The CA file (pem) to use for the proxy CA
@@ -3399,8 +3399,8 @@ run (void *cls,
     return;
   }
   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
-              "Proxy listens on port %llu\n",
-              port);
+              "Proxy listens on port %u\n",
+              (unsigned int) port);
 
   /* start MHD daemon for HTTP */
   hd = GNUNET_new (struct MhdHttpList);
@@ -3437,11 +3437,11 @@ int
 main (int argc, char *const *argv)
 {
   struct GNUNET_GETOPT_CommandLineOption options[] = {
-    GNUNET_GETOPT_option_ulong ('p',
-                                "port",
-                                NULL,
-                                gettext_noop ("listen on specified port 
(default: 7777)"),
-                                &port),
+    GNUNET_GETOPT_option_uint16 ('p',
+                                "port",
+                                NULL,
+                                gettext_noop ("listen on specified port 
(default: 7777)"),
+                                &port),
     GNUNET_GETOPT_option_string ('a',
                                  "authority",
                                  NULL,
diff --git a/src/gns/gnunet-service-gns_resolver.c 
b/src/gns/gnunet-service-gns_resolver.c
index 8b20f2ae3..0d04fc6b9 100644
--- a/src/gns/gnunet-service-gns_resolver.c
+++ b/src/gns/gnunet-service-gns_resolver.c
@@ -60,7 +60,7 @@
 /**
  * DHT replication level
  */
-#define DHT_GNS_REPLICATION_LEVEL 5
+#define DHT_GNS_REPLICATION_LEVEL 10
 
 /**
  * How deep do we allow recursions to go before we abort?
diff --git a/src/hostlist/gnunet-daemon-hostlist_client.c 
b/src/hostlist/gnunet-daemon-hostlist_client.c
index 207cc4a81..2283efe6c 100644
--- a/src/hostlist/gnunet-daemon-hostlist_client.c
+++ b/src/hostlist/gnunet-daemon-hostlist_client.c
@@ -28,6 +28,7 @@
 #include "gnunet_hello_lib.h"
 #include "gnunet_statistics_service.h"
 #include "gnunet_transport_service.h"
+#include "gnunet_peerinfo_service.h"
 #include "gnunet-daemon-hostlist.h"
 #if HAVE_CURL_CURL_H
 #include <curl/curl.h>
@@ -142,14 +143,6 @@ struct Hostlist
 };
 
 
-struct HelloOffer
-{
-  struct HelloOffer *next;
-  struct HelloOffer *prev;
-  struct GNUNET_TRANSPORT_OfferHelloHandle *ohh;
-};
-
-
 /**
  * Our configuration.
  */
@@ -315,24 +308,10 @@ static unsigned int stat_hellos_obtained;
  */
 static unsigned int stat_connection_count;
 
-static struct HelloOffer *ho_head;
-
-static struct HelloOffer *ho_tail;
-
-
 /**
- * Hello offer complete. Clean up.
+ * Handle to peerinfo service.
  */
-static void
-done_offer_hello (void *cls)
-{
-  struct HelloOffer *ho = cls;
-
-  GNUNET_CONTAINER_DLL_remove (ho_head,
-                               ho_tail,
-                               ho);
-  GNUNET_free (ho);
-}
+static struct GNUNET_PEERINFO_Handle *pi;
 
 
 /**
@@ -353,7 +332,6 @@ callback_download (void *ptr,
   static char download_buffer[GNUNET_MAX_MESSAGE_SIZE - 1];
   const char *cbuf = ptr;
   const struct GNUNET_MessageHeader *msg;
-  struct HelloOffer *ho;
   size_t total;
   size_t cpy;
   size_t left;
@@ -413,22 +391,10 @@ callback_download (void *ptr,
                                 ("# valid HELLOs downloaded from hostlist 
servers"),
                                 1, GNUNET_NO);
       stat_hellos_obtained++;
-
-      ho = GNUNET_new (struct HelloOffer);
-      ho->ohh = GNUNET_TRANSPORT_offer_hello (cfg,
-                                              msg,
-                                              &done_offer_hello,
-                                              ho);
-      if (NULL == ho->ohh)
-      {
-        GNUNET_free (ho);
-      }
-      else
-      {
-        GNUNET_CONTAINER_DLL_insert (ho_head,
-                                     ho_tail,
-                                     ho);
-      }
+      (void) GNUNET_PEERINFO_add_peer (pi,
+                                       (const struct GNUNET_HELLO_Message *) 
msg,
+                                       NULL,
+                                       NULL);
     }
     else
     {
@@ -1567,6 +1533,7 @@ GNUNET_HOSTLIST_client_start (const struct 
GNUNET_CONFIGURATION_Handle *c,
   stats = st;
 
   /* Read proxy configuration */
+  pi = GNUNET_PEERINFO_connect (c);
   if (GNUNET_OK ==
       GNUNET_CONFIGURATION_get_value_string (cfg,
                                              "HOSTLIST",
@@ -1723,18 +1690,8 @@ GNUNET_HOSTLIST_client_start (const struct 
GNUNET_CONFIGURATION_Handle *c,
 void
 GNUNET_HOSTLIST_client_stop ()
 {
-  struct HelloOffer *ho;
-
   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
              "Hostlist client shutdown\n");
-  while (NULL != (ho = ho_head))
-  {
-    GNUNET_CONTAINER_DLL_remove (ho_head,
-                                 ho_tail,
-                                 ho);
-    GNUNET_TRANSPORT_offer_hello_cancel (ho->ohh);
-    GNUNET_free (ho);
-  }
   if (NULL != sget)
   {
     GNUNET_STATISTICS_get_cancel (sget);
@@ -1777,7 +1734,11 @@ GNUNET_HOSTLIST_client_stop ()
   proxy_username = NULL;
   GNUNET_free_non_null (proxy_password);
   proxy_password = NULL;
-
+  if (NULL != pi)
+  {
+    GNUNET_PEERINFO_disconnect (pi);
+    pi = NULL;
+  }
   cfg = NULL;
 }
 
diff --git a/src/include/gnunet_core_service.h 
b/src/include/gnunet_core_service.h
index ace223c11..77af465ce 100644
--- a/src/include/gnunet_core_service.h
+++ b/src/include/gnunet_core_service.h
@@ -220,7 +220,7 @@ enum GNUNET_CORE_KxState
   /**
    * No handshake yet.
    */
-  GNUNET_CORE_KX_STATE_DOWN,
+  GNUNET_CORE_KX_STATE_DOWN = 0,
 
   /**
    * We've sent our session key.
diff --git a/src/include/gnunet_datacache_lib.h 
b/src/include/gnunet_datacache_lib.h
index 39a312b17..05ac779d6 100644
--- a/src/include/gnunet_datacache_lib.h
+++ b/src/include/gnunet_datacache_lib.h
@@ -105,6 +105,7 @@ typedef int
  *
  * @param h handle to the datacache
  * @param key key to store data under
+ * @param how close is @a key to our pid?
  * @param data_size number of bytes in @a data
  * @param data data to store
  * @param type type of the value
@@ -116,6 +117,7 @@ typedef int
 int
 GNUNET_DATACACHE_put (struct GNUNET_DATACACHE_Handle *h,
                       const struct GNUNET_HashCode *key,
+                      uint32_t xor_distance,
                       size_t data_size,
                       const char *data,
                       enum GNUNET_BLOCK_Type type,
diff --git a/src/include/gnunet_datacache_plugin.h 
b/src/include/gnunet_datacache_plugin.h
index 166c7bc3b..726108c64 100644
--- a/src/include/gnunet_datacache_plugin.h
+++ b/src/include/gnunet_datacache_plugin.h
@@ -109,6 +109,7 @@ struct GNUNET_DATACACHE_PluginFunctions
    *
    * @param cls closure (internal context for the plugin)
    * @param key key to store the value under
+   * @param xor_distance how close is @a key to our PID?
    * @param size number of bytes in @a data
    * @param data data to store
    * @param type type of the value
@@ -119,6 +120,7 @@ struct GNUNET_DATACACHE_PluginFunctions
    */
   ssize_t (*put) (void *cls,
                   const struct GNUNET_HashCode *key,
+                  uint32_t xor_distance,
                   size_t size,
                  const char *data,
                   enum GNUNET_BLOCK_Type type,
diff --git a/src/include/gnunet_getopt_lib.h b/src/include/gnunet_getopt_lib.h
index e38925f14..88c7825d0 100644
--- a/src/include/gnunet_getopt_lib.h
+++ b/src/include/gnunet_getopt_lib.h
@@ -287,6 +287,23 @@ GNUNET_GETOPT_option_uint (char shortName,
 
 
 /**
+ * Allow user to specify an uint16_t.
+ *
+ * @param shortName short name of the option
+ * @param name long name of the option
+ * @param argumentHelp help text for the option argument
+ * @param description long help text for the option
+ * @param[out] val set to the value specified at the command line
+ */
+struct GNUNET_GETOPT_CommandLineOption
+GNUNET_GETOPT_option_uint16 (char shortName,
+                            const char *name,
+                            const char *argumentHelp,
+                            const char *description,
+                            uint16_t *val);
+
+
+/**
  * Allow user to specify an `unsigned long long`.
  *
  * @param shortName short name of the option
diff --git a/src/include/gnunet_protocols.h b/src/include/gnunet_protocols.h
index c5e45d5c1..7040f2cbf 100644
--- a/src/include/gnunet_protocols.h
+++ b/src/include/gnunet_protocols.h
@@ -252,6 +252,12 @@ extern "C"
 #define GNUNET_MESSAGE_TYPE_DV_BOX 53
 
 
+/**
+ * Experimental message type.
+ */
+#define GNUNET_MESSAGE_TYPE_TRANSPORT_XU_MESSAGE 55
+
+
 
/*******************************************************************************
  * Transport-UDP message types
  
******************************************************************************/
diff --git a/src/peerinfo/peerinfo_api.c b/src/peerinfo/peerinfo_api.c
index 8b47ed444..09ec04bf8 100644
--- a/src/peerinfo/peerinfo_api.c
+++ b/src/peerinfo/peerinfo_api.c
@@ -565,7 +565,10 @@ GNUNET_PEERINFO_add_peer (struct GNUNET_PEERINFO_Handle *h,
   struct GNUNET_PeerIdentity peer;
 
   if (NULL == h->mq)
+  {
+    GNUNET_break (0);
     return NULL;
+  }
   GNUNET_assert (GNUNET_OK ==
                  GNUNET_HELLO_get_id (hello,
                                       &peer));
diff --git a/src/topology/gnunet-daemon-topology.c 
b/src/topology/gnunet-daemon-topology.c
index 4415d0a24..da4f62351 100644
--- a/src/topology/gnunet-daemon-topology.c
+++ b/src/topology/gnunet-daemon-topology.c
@@ -137,6 +137,11 @@ static const struct GNUNET_CONFIGURATION_Handle *cfg;
 static struct GNUNET_CORE_Handle *handle;
 
 /**
+ * Handle to the PEERINFO service.
+ */
+static struct GNUNET_PEERINFO_Handle *pi;
+
+/**
  * Handle to the ATS service.
  */
 static struct GNUNET_ATS_ConnectivityHandle *ats;
@@ -999,16 +1004,6 @@ read_friends_file (const struct 
GNUNET_CONFIGURATION_Handle *cfg)
 
 
 /**
- * Hello offer complete. Clean up.
- */
-static void
-done_offer_hello (void *cls)
-{
-  oh = NULL;
-}
-
-
-/**
  * This function is called whenever an encrypted HELLO message is
  * received.
  *
@@ -1076,12 +1071,10 @@ handle_hello (void *cls,
         (friend_count < minimum_friend_count))
       return;
   }
-  if (NULL != oh)
-    GNUNET_TRANSPORT_offer_hello_cancel (oh);
-  oh = GNUNET_TRANSPORT_offer_hello (cfg,
-                                     &message->header,
-                                     &done_offer_hello,
-                                     NULL);
+  (void) GNUNET_PEERINFO_add_peer (pi,
+                                   message,
+                                   NULL,
+                                   NULL);
 }
 
 
@@ -1125,6 +1118,11 @@ cleaning_task (void *cls)
     GNUNET_ATS_connectivity_done (ats);
     ats = NULL;
   }
+  if (NULL != pi)
+  {
+    GNUNET_PEERINFO_disconnect (pi);
+    pi = NULL;
+  }
   if (NULL != stats)
   {
     GNUNET_STATISTICS_destroy (stats, GNUNET_NO);
@@ -1189,6 +1187,7 @@ run (void *cls,
                                             &blacklist_check,
                                             NULL);
   ats = GNUNET_ATS_connectivity_init (cfg);
+  pi = GNUNET_PEERINFO_connect (cfg);
   handle = GNUNET_CORE_connect (cfg,
                                NULL,
                                &core_init,
diff --git a/src/transport/Makefile.am b/src/transport/Makefile.am
index e8c1f5d4a..8697d0941 100644
--- a/src/transport/Makefile.am
+++ b/src/transport/Makefile.am
@@ -289,6 +289,12 @@ plugin_LTLIBRARIES = \
   $(WLAN_PLUGIN_LA) \
   $(BT_PLUGIN_LA)
 
+if HAVE_EXPERIMENTAL
+plugin_LTLIBRARIES += \
+  libgnunet_plugin_transport_xt.la \
+  libgnunet_plugin_transport_xu.la 
+endif
+
 # Note: real plugins of course need to be added
 # to the plugin_LTLIBRARIES above
 noinst_LTLIBRARIES = \
@@ -306,6 +312,18 @@ libgnunet_plugin_transport_tcp_la_LIBADD = \
 libgnunet_plugin_transport_tcp_la_LDFLAGS = \
  $(GN_PLUGIN_LDFLAGS)
 
+libgnunet_plugin_transport_xt_la_SOURCES = \
+  plugin_transport_xt.c 
+libgnunet_plugin_transport_xt_la_LIBADD = \
+  $(top_builddir)/src/hello/libgnunethello.la \
+  $(top_builddir)/src/statistics/libgnunetstatistics.la \
+  $(top_builddir)/src/peerinfo/libgnunetpeerinfo.la \
+  $(top_builddir)/src/nat/libgnunetnatnew.la \
+  $(top_builddir)/src/util/libgnunetutil.la \
+  $(LTLIBINTL)
+libgnunet_plugin_transport_xt_la_LDFLAGS = \
+ $(GN_PLUGIN_LDFLAGS)
+
 libgnunet_plugin_transport_template_la_SOURCES = \
   plugin_transport_template.c
 libgnunet_plugin_transport_template_la_LIBADD = \
@@ -354,6 +372,19 @@ libgnunet_plugin_transport_udp_la_LIBADD = \
 libgnunet_plugin_transport_udp_la_LDFLAGS = \
  $(GN_PLUGIN_LDFLAGS)
 
+libgnunet_plugin_transport_xu_la_SOURCES = \
+  plugin_transport_xu.c plugin_transport_xu.h
+libgnunet_plugin_transport_udp_la_LIBADD = \
+  $(top_builddir)/src/hello/libgnunethello.la \
+  $(top_builddir)/src/fragmentation/libgnunetfragmentation.la \
+  $(top_builddir)/src/statistics/libgnunetstatistics.la \
+  $(top_builddir)/src/peerinfo/libgnunetpeerinfo.la \
+  $(top_builddir)/src/nat/libgnunetnatnew.la \
+  $(top_builddir)/src/util/libgnunetutil.la \
+  $(LTLIBINTL)
+libgnunet_plugin_transport_udp_la_LDFLAGS = \
+ $(GN_PLUGIN_LDFLAGS)
+
 libgnunet_plugin_transport_unix_la_SOURCES = \
   plugin_transport_unix.c
 libgnunet_plugin_transport_unix_la_LIBADD = \
diff --git a/src/transport/gnunet-service-transport_validation.c 
b/src/transport/gnunet-service-transport_validation.c
index cd5aeb5e2..91acc18ed 100644
--- a/src/transport/gnunet-service-transport_validation.c
+++ b/src/transport/gnunet-service-transport_validation.c
@@ -1623,15 +1623,20 @@ GST_validation_handle_pong (const struct 
GNUNET_PeerIdentity *sender,
                             GNUNET_TRANSPORT_VS_UPDATE);
 
   /* build HELLO to store in PEERINFO */
+  GNUNET_STATISTICS_update (GST_stats,
+                            gettext_noop ("# HELLOs given to peerinfo"),
+                            1,
+                            GNUNET_NO);
   ve->copied = GNUNET_NO;
   hello = GNUNET_HELLO_create (&ve->address->peer.public_key,
                                &add_valid_peer_address,
                               ve,
                                GNUNET_NO);
-  GNUNET_PEERINFO_add_peer (GST_peerinfo,
-                           hello,
-                           NULL,
-                           NULL);
+  GNUNET_break (NULL !=
+                GNUNET_PEERINFO_add_peer (GST_peerinfo,
+                                          hello,
+                                          NULL,
+                                          NULL));
   GNUNET_free (hello);
   return GNUNET_OK;
 }
diff --git a/src/transport/plugin_transport_tcp.c 
b/src/transport/plugin_transport_xt.c
similarity index 98%
copy from src/transport/plugin_transport_tcp.c
copy to src/transport/plugin_transport_xt.c
index 8b00543c3..21ed19da5 100644
--- a/src/transport/plugin_transport_tcp.c
+++ b/src/transport/plugin_transport_xt.c
@@ -18,7 +18,7 @@
   Boston, MA 02110-1301, USA.
  */
 /**
- * @file transport/plugin_transport_tcp.c
+ * @file transport/plugin_transport_xt.c
  * @brief Implementation of the TCP transport service
  * @author Christian Grothoff
  */
@@ -35,9 +35,9 @@
 #include "gnunet_transport_plugin.h"
 #include "transport.h"
 
-#define LOG(kind,...) GNUNET_log_from (kind, "transport-tcp",__VA_ARGS__)
+#define LOG(kind,...) GNUNET_log_from (kind, "transport-xt",__VA_ARGS__)
 
-#define PLUGIN_NAME "tcp"
+#define PLUGIN_NAME "xt"
 
 /**
  * How long until we give up on establishing an NAT connection?
@@ -1450,6 +1450,12 @@ tcp_nat_port_map_callback (void *cls,
   void *arg;
   size_t args;
 
+  if (GNUNET_NAT_AC_LOOPBACK == ac)
+    return;
+  if (GNUNET_NAT_AC_LAN == ac)
+    return;
+  if (GNUNET_NAT_AC_LAN_PRIVATE == ac)
+    return;
   LOG (GNUNET_ERROR_TYPE_INFO,
        "NAT notification to %s address `%s'\n",
        (GNUNET_YES == add_remove) ? "add" : "remove",
@@ -3761,7 +3767,7 @@ tcp_plugin_setup_monitor (void *cls,
  * @return the `struct GNUNET_TRANSPORT_PluginFunctions *` or NULL on error
  */
 void *
-libgnunet_plugin_transport_tcp_init (void *cls)
+libgnunet_plugin_transport_xt_init (void *cls)
 {
   static const struct GNUNET_SERVER_MessageHandler my_handlers[] = {
     { &handle_tcp_welcome, NULL,
@@ -3806,24 +3812,26 @@ libgnunet_plugin_transport_tcp_init (void *cls)
   GNUNET_assert (NULL != env->cfg);
   if (GNUNET_OK !=
       GNUNET_CONFIGURATION_get_value_number (env->cfg,
-                                             "transport-tcp",
+                                             "transport-xt",
                                              "MAX_CONNECTIONS",
                                              &max_connections))
     max_connections = 128;
 
   aport = 0;
   if ((GNUNET_OK !=
-       GNUNET_CONFIGURATION_get_value_number (env->cfg, "transport-tcp",
+       GNUNET_CONFIGURATION_get_value_number (env->cfg,
+                                             "transport-xt",
                                               "PORT", &bport)) ||
       (bport > 65535) ||
       ((GNUNET_OK ==
-        GNUNET_CONFIGURATION_get_value_number (env->cfg, "transport-tcp",
+        GNUNET_CONFIGURATION_get_value_number (env->cfg,
+                                              "transport-xt",
                                                "ADVERTISED-PORT", &aport)) &&
        (aport > 65535) ))
   {
     LOG(GNUNET_ERROR_TYPE_ERROR,
         _("Require valid port number for service `%s' in configuration!\n"),
-        "transport-tcp");
+        "transport-xt");
     return NULL ;
   }
   if (0 == aport)
@@ -3832,7 +3840,7 @@ libgnunet_plugin_transport_tcp_init (void *cls)
     aport = 0;
   if (0 != bport)
   {
-    service = LEGACY_SERVICE_start ("transport-tcp",
+    service = LEGACY_SERVICE_start ("transport-xt",
                                     env->cfg,
                                     LEGACY_SERVICE_OPTION_NONE);
     if (NULL == service)
@@ -3860,7 +3868,7 @@ libgnunet_plugin_transport_tcp_init (void *cls)
   if ( (NULL != service) &&
        (GNUNET_YES ==
         GNUNET_CONFIGURATION_get_value_yesno (env->cfg,
-                                              "transport-tcp",
+                                              "transport-xt",
                                               "TCP_STEALTH")) )
   {
 #ifdef TCP_STEALTH
@@ -3903,7 +3911,7 @@ libgnunet_plugin_transport_tcp_init (void *cls)
   if ( (NULL != service) &&
        (GNUNET_SYSERR !=
         (ret_s =
-         get_server_addresses ("transport-tcp",
+         get_server_addresses ("transport-xt",
                               env->cfg,
                               &addrs,
                               &addrlens))))
@@ -3914,7 +3922,7 @@ libgnunet_plugin_transport_tcp_init (void *cls)
            GNUNET_a2s (addrs[ret], addrlens[ret]));
     plugin->nat
       = GNUNET_NAT_register (env->cfg,
-                            "transport-tcp",
+                            "transport-xt",
                             IPPROTO_TCP,
                              (unsigned int) ret_s,
                              (const struct sockaddr **) addrs,
@@ -3930,7 +3938,7 @@ libgnunet_plugin_transport_tcp_init (void *cls)
   else
   {
     plugin->nat = GNUNET_NAT_register (plugin->env->cfg,
-                                       "transport-tcp",
+                                       "transport-xt",
                                       IPPROTO_TCP,
                                        0,
                                        NULL,
@@ -3964,12 +3972,12 @@ libgnunet_plugin_transport_tcp_init (void *cls)
   {
     if (GNUNET_OK !=
         GNUNET_CONFIGURATION_get_value_time (env->cfg,
-                                             "transport-tcp",
+                                             "transport-xt",
                                              "TIMEOUT",
                                              &idle_timeout))
     {
       GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
-                                 "transport-tcp",
+                                 "transport-xt",
                                  "TIMEOUT");
       goto die;
     }
@@ -3999,19 +4007,19 @@ libgnunet_plugin_transport_tcp_init (void *cls)
                                                                  GNUNET_YES);
   if (0 != bport)
     LOG (GNUNET_ERROR_TYPE_INFO,
-         _("TCP transport listening on port %llu\n"),
+         _("XT transport listening on port %llu\n"),
          bport);
   else
     LOG (GNUNET_ERROR_TYPE_INFO,
-         _("TCP transport not listening on any port (client only)\n"));
+         _("XT transport not listening on any port (client only)\n"));
   if ( (aport != bport) &&
        (0 != bport) )
     LOG (GNUNET_ERROR_TYPE_INFO,
-         _("TCP transport advertises itself as being on port %llu\n"),
+         _("XT transport advertises itself as being on port %llu\n"),
          aport);
   /* Initially set connections to 0 */
   GNUNET_STATISTICS_set (plugin->env->stats,
-                         gettext_noop ("# TCP sessions active"),
+                         gettext_noop ("# XT sessions active"),
                          0,
                          GNUNET_NO);
   return api;
@@ -4035,7 +4043,7 @@ libgnunet_plugin_transport_tcp_init (void *cls)
  * @return NULL
  */
 void *
-libgnunet_plugin_transport_tcp_done (void *cls)
+libgnunet_plugin_transport_xt_done (void *cls)
 {
   struct GNUNET_TRANSPORT_PluginFunctions *api = cls;
   struct Plugin *plugin = api->cls;
@@ -4049,7 +4057,7 @@ libgnunet_plugin_transport_tcp_done (void *cls)
     return NULL ;
   }
   LOG (GNUNET_ERROR_TYPE_DEBUG,
-       "Shutting down TCP plugin\n");
+       "Shutting down XT plugin\n");
 
   /* Removing leftover sessions */
   GNUNET_CONTAINER_multipeermap_iterate (plugin->sessionmap,
@@ -4096,4 +4104,4 @@ libgnunet_plugin_transport_tcp_done (void *cls)
   return NULL;
 }
 
-/* end of plugin_transport_tcp.c */
+/* end of plugin_transport_xt.c */
diff --git a/src/transport/plugin_transport_xu.c 
b/src/transport/plugin_transport_xu.c
new file mode 100644
index 000000000..ea0d3f065
--- /dev/null
+++ b/src/transport/plugin_transport_xu.c
@@ -0,0 +1,2492 @@
+/*
+ This file is part of GNUnet
+ Copyright (C) 2010-2017 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 transport/plugin_transport_xu.c
+ * @brief Implementation of the XU transport protocol
+ * @author Christian Grothoff
+ * @author Nathan Evans
+ * @author Matthias Wachs
+ */
+#include "platform.h"
+#include "plugin_transport_xu.h"
+#include "gnunet_hello_lib.h"
+#include "gnunet_util_lib.h"
+#include "gnunet_fragmentation_lib.h"
+#include "gnunet_nat_service.h"
+#include "gnunet_protocols.h"
+#include "gnunet_resolver_service.h"
+#include "gnunet_signatures.h"
+#include "gnunet_constants.h"
+#include "gnunet_statistics_service.h"
+#include "gnunet_transport_service.h"
+#include "gnunet_transport_plugin.h"
+#include "transport.h"
+
+#define LOG(kind,...) GNUNET_log_from (kind, "transport-xu", __VA_ARGS__)
+
+/**
+ * After how much inactivity should a XU session time out?
+ */
+#define XU_SESSION_TIME_OUT GNUNET_TIME_relative_multiply 
(GNUNET_TIME_UNIT_SECONDS, 60)
+
+
+/**
+ * XU Message-Packet header (after defragmentation).
+ */
+struct XUMessage
+{
+  /**
+   * Message header.
+   */
+  struct GNUNET_MessageHeader header;
+
+  /**
+   * Always zero for now.
+   */
+  uint32_t reserved;
+
+  /**
+   * What is the identity of the sender
+   */
+  struct GNUNET_PeerIdentity sender;
+
+};
+
+
+/**
+ * Closure for #append_port().
+ */
+struct PrettyPrinterContext
+{
+  /**
+   * DLL
+   */
+  struct PrettyPrinterContext *next;
+
+  /**
+   * DLL
+   */
+  struct PrettyPrinterContext *prev;
+
+  /**
+   * Our plugin.
+   */
+  struct Plugin *plugin;
+
+  /**
+   * Resolver handle
+   */
+  struct GNUNET_RESOLVER_RequestHandle *resolver_handle;
+
+  /**
+   * Function to call with the result.
+   */
+  GNUNET_TRANSPORT_AddressStringCallback asc;
+
+  /**
+   * Clsoure for @e asc.
+   */
+  void *asc_cls;
+
+  /**
+   * Timeout task
+   */
+  struct GNUNET_SCHEDULER_Task *timeout_task;
+
+  /**
+   * Is this an IPv6 address?
+   */
+  int ipv6;
+
+  /**
+   * Options
+   */
+  uint32_t options;
+
+  /**
+   * Port to add after the IP address.
+   */
+  uint16_t port;
+
+};
+
+
+/**
+ * Session with another peer.
+ */
+struct GNUNET_ATS_Session
+{
+  /**
+   * Which peer is this session for?
+   */
+  struct GNUNET_PeerIdentity target;
+
+  /**
+   * Tokenizer for inbound messages.
+   */
+  struct GNUNET_MessageStreamTokenizer *mst;
+
+  /**
+   * Plugin this session belongs to.
+   */
+  struct Plugin *plugin;
+
+  /**
+   * Session timeout task
+   */
+  struct GNUNET_SCHEDULER_Task *timeout_task;
+
+  /**
+   * When does this session time out?
+   */
+  struct GNUNET_TIME_Absolute timeout;
+
+  /**
+   * What time did we last transmit?
+   */
+  struct GNUNET_TIME_Absolute last_transmit_time;
+
+  /**
+   * expected delay for ACKs
+   */
+  struct GNUNET_TIME_Relative last_expected_ack_delay;
+
+  /**
+   * desired delay between XU messages
+   */
+  struct GNUNET_TIME_Relative last_expected_msg_delay;
+  
+  /**
+   */
+  struct GNUNET_TIME_Relative flow_delay_for_other_peer;
+  struct GNUNET_TIME_Relative flow_delay_from_other_peer;
+
+  /**
+   * Our own address.
+   */
+  struct GNUNET_HELLO_Address *address;
+
+  /**
+   * Number of bytes waiting for transmission to this peer.
+   */
+  unsigned long long bytes_in_queue;
+
+  /**
+   * Number of messages waiting for transmission to this peer.
+   */
+  unsigned int msgs_in_queue;
+
+  /**
+   * Reference counter to indicate that this session is
+   * currently being used and must not be destroyed;
+   * setting @e in_destroy will destroy it as soon as
+   * possible.
+   */
+  unsigned int rc;
+
+  /**
+   * Network type of the address.
+   */
+  enum GNUNET_ATS_Network_Type scope;
+
+  /**
+   * Is this session about to be destroyed (sometimes we cannot
+   * destroy a session immediately as below us on the stack
+   * there might be code that still uses it; in this case,
+   * @e rc is non-zero).
+   */
+  int in_destroy;
+};
+
+
+
+/**
+ * If a session monitor is attached, notify it about the new
+ * session state.
+ *
+ * @param plugin our plugin
+ * @param session session that changed state
+ * @param state new state of the session
+ */
+static void
+notify_session_monitor (struct Plugin *plugin,
+                        struct GNUNET_ATS_Session *session,
+                        enum GNUNET_TRANSPORT_SessionState state)
+{
+  struct GNUNET_TRANSPORT_SessionInfo info;
+
+  if (NULL == plugin->sic)
+    return;
+  if (GNUNET_YES == session->in_destroy)
+    return; /* already destroyed, just RC>0 left-over actions */
+  memset (&info,
+          0,
+          sizeof (info));
+  info.state = state;
+  info.is_inbound = GNUNET_SYSERR; /* hard to say */
+  info.num_msg_pending = session->msgs_in_queue;
+  info.num_bytes_pending = session->bytes_in_queue;
+  /* info.receive_delay remains zero as this is not supported by XU
+     (cannot selectively not receive from 'some' peer while continuing
+     to receive from others) */
+  info.session_timeout = session->timeout;
+  info.address = session->address;
+  plugin->sic (plugin->sic_cls,
+               session,
+               &info);
+}
+
+
+/**
+ * Return information about the given session to the monitor callback.
+ *
+ * @param cls the `struct Plugin` with the monitor callback (`sic`)
+ * @param peer peer we send information about
+ * @param value our `struct GNUNET_ATS_Session` to send information about
+ * @return #GNUNET_OK (continue to iterate)
+ */
+static int
+send_session_info_iter (void *cls,
+                        const struct GNUNET_PeerIdentity *peer,
+                        void *value)
+{
+  struct Plugin *plugin = cls;
+  struct GNUNET_ATS_Session *session = value;
+
+  (void) peer;
+  notify_session_monitor (plugin,
+                          session,
+                          GNUNET_TRANSPORT_SS_INIT);
+  notify_session_monitor (plugin,
+                          session,
+                          GNUNET_TRANSPORT_SS_UP);
+  return GNUNET_OK;
+}
+
+
+/**
+ * Begin monitoring sessions of a plugin.  There can only
+ * be one active monitor per plugin (i.e. if there are
+ * multiple monitors, the transport service needs to
+ * multiplex the generated events over all of them).
+ *
+ * @param cls closure of the plugin
+ * @param sic callback to invoke, NULL to disable monitor;
+ *            plugin will being by iterating over all active
+ *            sessions immediately and then enter monitor mode
+ * @param sic_cls closure for @a sic
+ */
+static void
+xu_plugin_setup_monitor (void *cls,
+                          GNUNET_TRANSPORT_SessionInfoCallback sic,
+                          void *sic_cls)
+{
+  struct Plugin *plugin = cls;
+
+  plugin->sic = sic;
+  plugin->sic_cls = sic_cls;
+  if (NULL != sic)
+  {
+    GNUNET_CONTAINER_multipeermap_iterate (plugin->sessions,
+                                           &send_session_info_iter,
+                                           plugin);
+    /* signal end of first iteration */
+    sic (sic_cls,
+         NULL,
+         NULL);
+  }
+}
+
+
+/* ****************** Little Helpers ****************** */
+
+
+/**
+ * Function to free last resources associated with a session.
+ *
+ * @param s session to free
+ */
+static void
+free_session (struct GNUNET_ATS_Session *s)
+{
+  if (NULL != s->address)
+  {
+    GNUNET_HELLO_address_free (s->address);
+    s->address = NULL;
+  }
+  if (NULL != s->mst)
+  {
+    GNUNET_MST_destroy (s->mst);
+    s->mst = NULL;
+  }
+  GNUNET_free (s);
+}
+
+
+/**
+ * Function that is called to get the keepalive factor.
+ * #GNUNET_CONSTANTS_IDLE_CONNECTION_TIMEOUT is divided by this number to
+ * calculate the interval between keepalive packets.
+ *
+ * @param cls closure with the `struct Plugin`
+ * @return keepalive factor
+ */
+static unsigned int
+xu_query_keepalive_factor (void *cls)
+{
+  (void) cls;
+  return 15;
+}
+
+
+/**
+ * Function obtain the network type for a session
+ *
+ * @param cls closure (`struct Plugin *`)
+ * @param session the session
+ * @return the network type
+ */
+static enum GNUNET_ATS_Network_Type
+xu_plugin_get_network (void *cls,
+                      struct GNUNET_ATS_Session *session)
+{
+  (void) cls;
+  return session->scope;
+}
+
+
+/**
+ * Function obtain the network type for an address.
+ *
+ * @param cls closure (`struct Plugin *`)
+ * @param address the address
+ * @return the network type
+ */
+static enum GNUNET_ATS_Network_Type
+xu_plugin_get_network_for_address (void *cls,
+                                  const struct GNUNET_HELLO_Address *address)
+{
+  struct Plugin *plugin = cls;
+  size_t addrlen;
+  struct sockaddr_in a4;
+  struct sockaddr_in6 a6;
+  const struct IPv4XuAddress *u4;
+  const struct IPv6XuAddress *u6;
+  const void *sb;
+  size_t sbs;
+
+  addrlen = address->address_length;
+  if (addrlen == sizeof(struct IPv6XuAddress))
+  {
+    GNUNET_assert (NULL != address->address); /* make static analysis happy */
+    u6 = address->address;
+    memset (&a6, 0, sizeof(a6));
+#if HAVE_SOCKADDR_IN_SIN_LEN
+    a6.sin6_len = sizeof (a6);
+#endif
+    a6.sin6_family = AF_INET6;
+    a6.sin6_port = u6->u6_port;
+    GNUNET_memcpy (&a6.sin6_addr, &u6->ipv6_addr, sizeof(struct in6_addr));
+    sb = &a6;
+    sbs = sizeof(a6);
+  }
+  else if (addrlen == sizeof(struct IPv4XuAddress))
+  {
+    GNUNET_assert (NULL != address->address); /* make static analysis happy */
+    u4 = address->address;
+    memset (&a4, 0, sizeof(a4));
+#if HAVE_SOCKADDR_IN_SIN_LEN
+    a4.sin_len = sizeof (a4);
+#endif
+    a4.sin_family = AF_INET;
+    a4.sin_port = u4->u4_port;
+    a4.sin_addr.s_addr = u4->ipv4_addr;
+    sb = &a4;
+    sbs = sizeof(a4);
+  }
+  else
+  {
+    GNUNET_break (0);
+    return GNUNET_ATS_NET_UNSPECIFIED;
+  }
+  return plugin->env->get_address_type (plugin->env->cls,
+                                        sb,
+                                        sbs);
+}
+
+
+/* ******************* Event loop ******************** */
+
+/**
+ * We have been notified that our readset has something to read.  We don't
+ * know which socket needs to be read, so we have to check each one
+ * Then reschedule this function to be called again once more is available.
+ *
+ * @param cls the plugin handle
+ */
+static void
+xu_plugin_select_v4 (void *cls);
+
+
+/**
+ * We have been notified that our readset has something to read.  We don't
+ * know which socket needs to be read, so we have to check each one
+ * Then reschedule this function to be called again once more is available.
+ *
+ * @param cls the plugin handle
+ */
+static void
+xu_plugin_select_v6 (void *cls);
+
+
+/**
+ * (re)schedule IPv4-select tasks for this plugin.
+ *
+ * @param plugin plugin to reschedule
+ */
+static void
+schedule_select_v4 (struct Plugin *plugin)
+{
+  if ( (GNUNET_YES != plugin->enable_ipv4) ||
+       (NULL == plugin->sockv4) )
+    return;
+  if (NULL != plugin->select_task_v4)
+    GNUNET_SCHEDULER_cancel (plugin->select_task_v4);
+  plugin->select_task_v4
+    = GNUNET_SCHEDULER_add_read_net (GNUNET_TIME_UNIT_FOREVER_REL,
+                                    plugin->sockv4,
+                                    &xu_plugin_select_v4,
+                                    plugin);
+}
+
+
+/**
+ * (re)schedule IPv6-select tasks for this plugin.
+ *
+ * @param plugin plugin to reschedule
+ */
+static void
+schedule_select_v6 (struct Plugin *plugin)
+{
+  if ( (GNUNET_YES != plugin->enable_ipv6) ||
+       (NULL == plugin->sockv6) )
+    return;
+  if (NULL != plugin->select_task_v6)
+    GNUNET_SCHEDULER_cancel (plugin->select_task_v6);
+  plugin->select_task_v6
+    = GNUNET_SCHEDULER_add_read_net (GNUNET_TIME_UNIT_FOREVER_REL,
+                                    plugin->sockv6,
+                                    &xu_plugin_select_v6,
+                                    plugin);
+}
+
+
+/* ******************* Address to string and back ***************** */
+
+
+/**
+ * Function called for a quick conversion of the binary address to
+ * a numeric address.  Note that the caller must not free the
+ * address and that the next call to this function is allowed
+ * to override the address again.
+ *
+ * @param cls closure
+ * @param addr binary address (a `union XuAddress`)
+ * @param addrlen length of the @a addr
+ * @return string representing the same address
+ */
+const char *
+xu_address_to_string (void *cls,
+                       const void *addr,
+                       size_t addrlen)
+{
+  static char rbuf[INET6_ADDRSTRLEN + 10];
+  char buf[INET6_ADDRSTRLEN];
+  const void *sb;
+  struct in_addr a4;
+  struct in6_addr a6;
+  const struct IPv4XuAddress *t4;
+  const struct IPv6XuAddress *t6;
+  int af;
+  uint16_t port;
+  uint32_t options;
+
+  (void) cls;
+  if (NULL == addr)
+  {
+    GNUNET_break_op (0);
+    return NULL;
+  }
+
+  if (addrlen == sizeof(struct IPv6XuAddress))
+  {
+    t6 = addr;
+    af = AF_INET6;
+    options = ntohl (t6->options);
+    port = ntohs (t6->u6_port);
+    a6 = t6->ipv6_addr;
+    sb = &a6;
+  }
+  else if (addrlen == sizeof(struct IPv4XuAddress))
+  {
+    t4 = addr;
+    af = AF_INET;
+    options = ntohl (t4->options);
+    port = ntohs (t4->u4_port);
+    a4.s_addr = t4->ipv4_addr;
+    sb = &a4;
+  }
+  else
+  {
+    GNUNET_break_op (0);
+    return NULL;
+  }
+  inet_ntop (af,
+             sb,
+             buf,
+             INET6_ADDRSTRLEN);
+  GNUNET_snprintf (rbuf,
+                   sizeof(rbuf),
+                   (af == AF_INET6)
+                   ? "%s.%u.[%s]:%u"
+                   : "%s.%u.%s:%u",
+                   PLUGIN_NAME,
+                   options,
+                   buf,
+                   port);
+  return rbuf;
+}
+
+
+/**
+ * Function called to convert a string address to a binary address.
+ *
+ * @param cls closure (`struct Plugin *`)
+ * @param addr string address
+ * @param addrlen length of the address
+ * @param buf location to store the buffer
+ * @param added location to store the number of bytes in the buffer.
+ *        If the function returns #GNUNET_SYSERR, its contents are undefined.
+ * @return #GNUNET_OK on success, #GNUNET_SYSERR on failure
+ */
+static int
+xu_string_to_address (void *cls,
+                     const char *addr,
+                     uint16_t addrlen,
+                     void **buf,
+                     size_t *added)
+{
+  struct sockaddr_storage socket_address;
+  char *address;
+  char *plugin;
+  char *optionstr;
+  uint32_t options;
+
+  (void) cls;
+  /* Format tcp.options.address:port */
+  address = NULL;
+  plugin = NULL;
+  optionstr = NULL;
+
+  if ((NULL == addr) || (0 == addrlen))
+  {
+    GNUNET_break (0);
+    return GNUNET_SYSERR;
+  }
+  if ('\0' != addr[addrlen - 1])
+  {
+    GNUNET_break (0);
+    return GNUNET_SYSERR;
+  }
+  if (strlen (addr) + 1 != (size_t) addrlen)
+  {
+    GNUNET_break (0);
+    return GNUNET_SYSERR;
+  }
+  plugin = GNUNET_strdup (addr);
+  optionstr = strchr (plugin, '.');
+  if (NULL == optionstr)
+  {
+    GNUNET_break (0);
+    GNUNET_free (plugin);
+    return GNUNET_SYSERR;
+  }
+  optionstr[0] = '\0';
+  optionstr++;
+  options = atol (optionstr);
+  address = strchr (optionstr, '.');
+  if (NULL == address)
+  {
+    GNUNET_break (0);
+    GNUNET_free (plugin);
+    return GNUNET_SYSERR;
+  }
+  address[0] = '\0';
+  address++;
+
+  if (GNUNET_OK !=
+      GNUNET_STRINGS_to_address_ip (address,
+                                    strlen (address),
+                                    &socket_address))
+  {
+    GNUNET_break (0);
+    GNUNET_free (plugin);
+    return GNUNET_SYSERR;
+  }
+  GNUNET_free(plugin);
+
+  switch (socket_address.ss_family)
+  {
+  case AF_INET:
+    {
+      struct IPv4XuAddress *u4;
+      const struct sockaddr_in *in4 = (const struct sockaddr_in *) 
&socket_address;
+
+      u4 = GNUNET_new (struct IPv4XuAddress);
+      u4->options = htonl (options);
+      u4->ipv4_addr = in4->sin_addr.s_addr;
+      u4->u4_port = in4->sin_port;
+      *buf = u4;
+      *added = sizeof (struct IPv4XuAddress);
+      return GNUNET_OK;
+    }
+  case AF_INET6:
+    {
+      struct IPv6XuAddress *u6;
+      const struct sockaddr_in6 *in6 = (const struct sockaddr_in6 *) 
&socket_address;
+
+      u6 = GNUNET_new (struct IPv6XuAddress);
+      u6->options = htonl (options);
+      u6->ipv6_addr = in6->sin6_addr;
+      u6->u6_port = in6->sin6_port;
+      *buf = u6;
+      *added = sizeof (struct IPv6XuAddress);
+      return GNUNET_OK;
+    }
+  default:
+    GNUNET_break (0);
+    return GNUNET_SYSERR;
+  }
+}
+
+
+/**
+ * Append our port and forward the result.
+ *
+ * @param cls a `struct PrettyPrinterContext *`
+ * @param hostname result from DNS resolver
+ */
+static void
+append_port (void *cls,
+             const char *hostname)
+{
+  struct PrettyPrinterContext *ppc = cls;
+  struct Plugin *plugin = ppc->plugin;
+  char *ret;
+
+  if (NULL == hostname)
+  {
+    /* Final call, done */
+    GNUNET_CONTAINER_DLL_remove (plugin->ppc_dll_head,
+                                 plugin->ppc_dll_tail,
+                                 ppc);
+    ppc->resolver_handle = NULL;
+    ppc->asc (ppc->asc_cls,
+              NULL,
+              GNUNET_OK);
+    GNUNET_free (ppc);
+    return;
+  }
+  if (GNUNET_YES == ppc->ipv6)
+    GNUNET_asprintf (&ret,
+                     "%s.%u.[%s]:%d",
+                     PLUGIN_NAME,
+                     ppc->options,
+                     hostname,
+                     ppc->port);
+  else
+    GNUNET_asprintf (&ret,
+                     "%s.%u.%s:%d",
+                     PLUGIN_NAME,
+                     ppc->options,
+                     hostname,
+                     ppc->port);
+  ppc->asc (ppc->asc_cls,
+            ret,
+            GNUNET_OK);
+  GNUNET_free (ret);
+}
+
+
+/**
+ * Convert the transports address to a nice, human-readable format.
+ *
+ * @param cls closure with the `struct Plugin *`
+ * @param type name of the transport that generated the address
+ * @param addr one of the addresses of the host, NULL for the last address
+ *        the specific address format depends on the transport;
+ *        a `union XuAddress`
+ * @param addrlen length of the address
+ * @param numeric should (IP) addresses be displayed in numeric form?
+ * @param timeout after how long should we give up?
+ * @param asc function to call on each string
+ * @param asc_cls closure for @a asc
+ */
+static void
+xu_plugin_address_pretty_printer (void *cls,
+                                   const char *type,
+                                   const void *addr,
+                                   size_t addrlen,
+                                   int numeric,
+                                   struct GNUNET_TIME_Relative timeout,
+                                   GNUNET_TRANSPORT_AddressStringCallback asc,
+                                   void *asc_cls)
+{
+  struct Plugin *plugin = cls;
+  struct PrettyPrinterContext *ppc;
+  const struct sockaddr *sb;
+  size_t sbs;
+  struct sockaddr_in a4;
+  struct sockaddr_in6 a6;
+  const struct IPv4XuAddress *u4;
+  const struct IPv6XuAddress *u6;
+  uint16_t port;
+  uint32_t options;
+
+  (void) type;
+  if (addrlen == sizeof(struct IPv6XuAddress))
+  {
+    u6 = addr;
+    memset (&a6,
+            0,
+            sizeof (a6));
+    a6.sin6_family = AF_INET6;
+#if HAVE_SOCKADDR_IN_SIN_LEN
+    a6.sin6_len = sizeof (a6);
+#endif
+    a6.sin6_port = u6->u6_port;
+    a6.sin6_addr = u6->ipv6_addr;
+    port = ntohs (u6->u6_port);
+    options = ntohl (u6->options);
+    sb = (const struct sockaddr *) &a6;
+    sbs = sizeof (a6);
+  }
+  else if (addrlen == sizeof (struct IPv4XuAddress))
+  {
+    u4 = addr;
+    memset (&a4,
+            0,
+            sizeof(a4));
+    a4.sin_family = AF_INET;
+#if HAVE_SOCKADDR_IN_SIN_LEN
+    a4.sin_len = sizeof (a4);
+#endif
+    a4.sin_port = u4->u4_port;
+    a4.sin_addr.s_addr = u4->ipv4_addr;
+    port = ntohs (u4->u4_port);
+    options = ntohl (u4->options);
+    sb = (const struct sockaddr *) &a4;
+    sbs = sizeof(a4);
+  }
+  else
+  {
+    /* invalid address */
+    GNUNET_break_op (0);
+    asc (asc_cls,
+         NULL,
+         GNUNET_SYSERR);
+    asc (asc_cls,
+         NULL,
+         GNUNET_OK);
+    return;
+  }
+  ppc = GNUNET_new (struct PrettyPrinterContext);
+  ppc->plugin = plugin;
+  ppc->asc = asc;
+  ppc->asc_cls = asc_cls;
+  ppc->port = port;
+  ppc->options = options;
+  if (addrlen == sizeof (struct IPv6XuAddress))
+    ppc->ipv6 = GNUNET_YES;
+  else
+    ppc->ipv6 = GNUNET_NO;
+  GNUNET_CONTAINER_DLL_insert (plugin->ppc_dll_head,
+                               plugin->ppc_dll_tail,
+                               ppc);
+  ppc->resolver_handle
+    = GNUNET_RESOLVER_hostname_get (sb,
+                                    sbs,
+                                    ! numeric,
+                                    timeout,
+                                    &append_port,
+                                    ppc);
+}
+
+
+/**
+ * Check if the given port is plausible (must be either our listen
+ * port or our advertised port).  If it is neither, we return
+ * #GNUNET_SYSERR.
+ *
+ * @param plugin global variables
+ * @param in_port port number to check
+ * @return #GNUNET_OK if port is either our open or advertised port
+ */
+static int
+check_port (const struct Plugin *plugin,
+            uint16_t in_port)
+{
+  if ( (plugin->port == in_port) ||
+       (plugin->aport == in_port) )
+    return GNUNET_OK;
+  return GNUNET_SYSERR;
+}
+
+
+/**
+ * Function that will be called to check if a binary address for this
+ * plugin is well-formed and corresponds to an address for THIS peer
+ * (as per our configuration).  Naturally, if absolutely necessary,
+ * plugins can be a bit conservative in their answer, but in general
+ * plugins should make sure that the address does not redirect
+ * traffic to a 3rd party that might try to man-in-the-middle our
+ * traffic.
+ *
+ * @param cls closure, should be our handle to the Plugin
+ * @param addr pointer to a `union XuAddress`
+ * @param addrlen length of @a addr
+ * @return #GNUNET_OK if this is a plausible address for this peer
+ *         and transport, #GNUNET_SYSERR if not
+ */
+static int
+xu_plugin_check_address (void *cls,
+                        const void *addr,
+                        size_t addrlen)
+{
+  struct Plugin *plugin = cls;
+  const struct IPv4XuAddress *v4;
+  const struct IPv6XuAddress *v6;
+
+  if (sizeof(struct IPv4XuAddress) == addrlen)
+  {
+    struct sockaddr_in s4;
+
+    v4 = (const struct IPv4XuAddress *) addr;
+    if (GNUNET_OK != check_port (plugin,
+                                 ntohs (v4->u4_port)))
+      return GNUNET_SYSERR;
+    memset (&s4, 0, sizeof (s4));
+    s4.sin_family = AF_INET;
+#if HAVE_SOCKADDR_IN_SIN_LEN
+    s4.sin_len = sizeof (s4);
+#endif
+    s4.sin_port = v4->u4_port;
+    s4.sin_addr.s_addr = v4->ipv4_addr;
+
+    if (GNUNET_OK !=
+       GNUNET_NAT_test_address (plugin->nat,
+                                &s4,
+                                sizeof (struct sockaddr_in)))
+      return GNUNET_SYSERR;
+  }
+  else if (sizeof(struct IPv6XuAddress) == addrlen)
+  {
+    struct sockaddr_in6 s6;
+
+    v6 = (const struct IPv6XuAddress *) addr;
+    if (IN6_IS_ADDR_LINKLOCAL (&v6->ipv6_addr))
+      return GNUNET_OK; /* plausible, if unlikely... */
+    memset (&s6, 0, sizeof (s6));
+    s6.sin6_family = AF_INET6;
+#if HAVE_SOCKADDR_IN_SIN_LEN
+    s6.sin6_len = sizeof (s6);
+#endif
+    s6.sin6_port = v6->u6_port;
+    s6.sin6_addr = v6->ipv6_addr;
+
+    if (GNUNET_OK !=
+       GNUNET_NAT_test_address (plugin->nat,
+                                &s6,
+                                sizeof(struct sockaddr_in6)))
+      return GNUNET_SYSERR;
+  }
+  else
+  {
+    GNUNET_break_op (0);
+    return GNUNET_SYSERR;
+  }
+  return GNUNET_OK;
+}
+
+
+/**
+ * Our external IP address/port mapping has changed.
+ *
+ * @param cls closure, the `struct Plugin`
+ * @param add_remove #GNUNET_YES to mean the new public IP address,
+ *                   #GNUNET_NO to mean the previous (now invalid) one
+ * @param ac address class the address belongs to
+ * @param addr either the previous or the new public IP address
+ * @param addrlen actual length of the @a addr
+ */
+static void
+xu_nat_port_map_callback (void *cls,
+                           int add_remove,
+                          enum GNUNET_NAT_AddressClass ac,
+                           const struct sockaddr *addr,
+                           socklen_t addrlen)
+{
+  struct Plugin *plugin = cls;
+  struct GNUNET_HELLO_Address *address;
+  struct IPv4XuAddress u4;
+  struct IPv6XuAddress u6;
+  void *arg;
+  size_t args;
+
+  if (GNUNET_NAT_AC_LOOPBACK == ac)
+    return;
+  if (GNUNET_NAT_AC_LAN == ac)
+    return;
+  if (GNUNET_NAT_AC_LAN_PRIVATE == ac)
+    return;
+  LOG (GNUNET_ERROR_TYPE_DEBUG,
+       (GNUNET_YES == add_remove)
+       ? "NAT notification to add address `%s'\n"
+       : "NAT notification to remove address `%s'\n",
+       GNUNET_a2s (addr,
+                   addrlen));
+  /* convert 'address' to our internal format */
+  switch (addr->sa_family)
+  {
+  case AF_INET:
+    {
+      const struct sockaddr_in *i4;
+
+      GNUNET_assert (sizeof(struct sockaddr_in) == addrlen);
+      i4 = (const struct sockaddr_in *) addr;
+      if (0 == ntohs (i4->sin_port))
+        return; /* Port = 0 means unmapped, ignore these for XU. */
+      memset (&u4,
+              0,
+              sizeof(u4));
+      u4.options = htonl (plugin->myoptions);
+      u4.ipv4_addr = i4->sin_addr.s_addr;
+      u4.u4_port = i4->sin_port;
+      arg = &u4;
+      args = sizeof (struct IPv4XuAddress);
+      break;
+    }
+  case AF_INET6:
+    {
+      const struct sockaddr_in6 *i6;
+
+      GNUNET_assert (sizeof(struct sockaddr_in6) == addrlen);
+      i6 = (const struct sockaddr_in6 *) addr;
+      if (0 == ntohs (i6->sin6_port))
+        return; /* Port = 0 means unmapped, ignore these for XU. */
+      memset (&u6,
+              0,
+              sizeof(u6));
+      u6.options = htonl (plugin->myoptions);
+      u6.ipv6_addr = i6->sin6_addr;
+      u6.u6_port = i6->sin6_port;
+      arg = &u6;
+      args = sizeof (struct IPv6XuAddress);
+      break;
+    }
+  default:
+    GNUNET_break (0);
+    return;
+  }
+  /* modify our published address list */
+  /* TODO: use 'ac' here in the future... */
+  address = GNUNET_HELLO_address_allocate (plugin->env->my_identity,
+                                           PLUGIN_NAME,
+                                           arg,
+                                           args,
+                                           GNUNET_HELLO_ADDRESS_INFO_NONE);
+  plugin->env->notify_address (plugin->env->cls,
+                               add_remove,
+                               address);
+  GNUNET_HELLO_address_free (address);
+}
+
+
+/* ********************* Finding sessions ******************* */
+
+
+/**
+ * Closure for #session_cmp_it().
+ */
+struct GNUNET_ATS_SessionCompareContext
+{
+  /**
+   * Set to session matching the address.
+   */
+  struct GNUNET_ATS_Session *res;
+
+  /**
+   * Address we are looking for.
+   */
+  const struct GNUNET_HELLO_Address *address;
+};
+
+
+/**
+ * Find a session with a matching address.
+ *
+ * @param cls the `struct GNUNET_ATS_SessionCompareContext *`
+ * @param key peer identity (unused)
+ * @param value the `struct GNUNET_ATS_Session *`
+ * @return #GNUNET_NO if we found the session, #GNUNET_OK if not
+ */
+static int
+session_cmp_it (void *cls,
+                const struct GNUNET_PeerIdentity *key,
+                void *value)
+{
+  struct GNUNET_ATS_SessionCompareContext *cctx = cls;
+  struct GNUNET_ATS_Session *s = value;
+
+  (void) key;
+  if (0 == GNUNET_HELLO_address_cmp (s->address,
+                                     cctx->address))
+  {
+    GNUNET_assert (GNUNET_NO == s->in_destroy);
+    cctx->res = s;
+    return GNUNET_NO;
+  }
+  return GNUNET_OK;
+}
+
+
+/**
+ * Locate an existing session the transport service is using to
+ * send data to another peer.  Performs some basic sanity checks
+ * on the address and then tries to locate a matching session.
+ *
+ * @param cls the plugin
+ * @param address the address we should locate the session by
+ * @return the session if it exists, or NULL if it is not found
+ */
+static struct GNUNET_ATS_Session *
+xu_plugin_lookup_session (void *cls,
+                           const struct GNUNET_HELLO_Address *address)
+{
+  struct Plugin *plugin = cls;
+  const struct IPv6XuAddress *xu_a6;
+  const struct IPv4XuAddress *xu_a4;
+  struct GNUNET_ATS_SessionCompareContext cctx;
+
+  if (NULL == address->address)
+  {
+    GNUNET_break (0);
+    return NULL;
+  }
+  if (sizeof(struct IPv4XuAddress) == address->address_length)
+  {
+    if (NULL == plugin->sockv4)
+      return NULL;
+    xu_a4 = (const struct IPv4XuAddress *) address->address;
+    if (0 == xu_a4->u4_port)
+    {
+      GNUNET_break (0);
+      return NULL;
+    }
+  }
+  else if (sizeof(struct IPv6XuAddress) == address->address_length)
+  {
+    if (NULL == plugin->sockv6)
+      return NULL;
+    xu_a6 = (const struct IPv6XuAddress *) address->address;
+    if (0 == xu_a6->u6_port)
+    {
+      GNUNET_break (0);
+      return NULL;
+    }
+  }
+  else
+  {
+    GNUNET_break (0);
+    return NULL;
+  }
+
+  /* check if session already exists */
+  cctx.address = address;
+  cctx.res = NULL;
+  LOG (GNUNET_ERROR_TYPE_DEBUG,
+       "Looking for existing session for peer `%s' with address `%s'\n",
+       GNUNET_i2s (&address->peer),
+       xu_address_to_string (plugin,
+                              address->address,
+                              address->address_length));
+  GNUNET_CONTAINER_multipeermap_get_multiple (plugin->sessions,
+                                              &address->peer,
+                                              &session_cmp_it,
+                                              &cctx);
+  if (NULL == cctx.res)
+    return NULL;
+  LOG (GNUNET_ERROR_TYPE_DEBUG,
+       "Found existing session %p\n",
+       cctx.res);
+  return cctx.res;
+}
+
+
+/* ********************** Timeout ****************** */
+
+
+/**
+ * Increment session timeout due to activity.
+ *
+ * @param s session to reschedule timeout activity for
+ */
+static void
+reschedule_session_timeout (struct GNUNET_ATS_Session *s)
+{
+  if (GNUNET_YES == s->in_destroy)
+    return;
+  GNUNET_assert (NULL != s->timeout_task);
+  s->timeout = GNUNET_TIME_relative_to_absolute (XU_SESSION_TIME_OUT);
+}
+
+
+
+/**
+ * Function that will be called whenever the transport service wants to
+ * notify the plugin that a session is still active and in use and
+ * therefore the session timeout for this session has to be updated
+ *
+ * @param cls closure with the `struct Plugin`
+ * @param peer which peer was the session for
+ * @param session which session is being updated
+ */
+static void
+xu_plugin_update_session_timeout (void *cls,
+                                   const struct GNUNET_PeerIdentity *peer,
+                                   struct GNUNET_ATS_Session *session)
+{
+  struct Plugin *plugin = cls;
+
+  if (GNUNET_YES !=
+      GNUNET_CONTAINER_multipeermap_contains_value (plugin->sessions,
+                                                    peer,
+                                                    session))
+  {
+    GNUNET_break (0);
+    return;
+  }
+  /* Reschedule session timeout */
+  reschedule_session_timeout (session);
+}
+
+
+/* ************************* Sending ************************ */
+
+
+/**
+ * We failed to transmit a message via XU. Generate
+ * a descriptive error message.
+ *
+ * @param plugin our plugin
+ * @param sa target address we were trying to reach
+ * @param slen number of bytes in @a sa
+ * @param error the errno value returned from the sendto() call
+ */
+static void
+analyze_send_error (struct Plugin *plugin,
+                    const struct sockaddr *sa,
+                    socklen_t slen,
+                    int error)
+{
+  enum GNUNET_ATS_Network_Type type;
+
+  type = plugin->env->get_address_type (plugin->env->cls,
+                                        sa,
+                                        slen);
+  if ( ( (GNUNET_ATS_NET_LAN == type) ||
+         (GNUNET_ATS_NET_WAN == type) ) &&
+       ( (ENETUNREACH == errno) ||
+         (ENETDOWN == errno) ) )
+  {
+    if (slen == sizeof (struct sockaddr_in))
+    {
+      /* IPv4: "Network unreachable" or "Network down"
+       *
+       * This indicates we do not have connectivity
+       */
+      LOG (GNUNET_ERROR_TYPE_WARNING | GNUNET_ERROR_TYPE_BULK,
+           _("XU could not transmit message to `%s': "
+             "Network seems down, please check your network configuration\n"),
+           GNUNET_a2s (sa,
+                       slen));
+    }
+    if (slen == sizeof (struct sockaddr_in6))
+    {
+      /* IPv6: "Network unreachable" or "Network down"
+       *
+       * This indicates that this system is IPv6 enabled, but does not
+       * have a valid global IPv6 address assigned or we do not have
+       * connectivity
+       */
+      LOG (GNUNET_ERROR_TYPE_WARNING | GNUNET_ERROR_TYPE_BULK,
+           _("XU could not transmit IPv6 message! "
+             "Please check your network configuration and disable IPv6 if your 
"
+             "connection does not have a global IPv6 address\n"));
+    }
+  }
+  else
+  {
+    LOG (GNUNET_ERROR_TYPE_WARNING,
+         "XU could not transmit message to `%s': `%s'\n",
+         GNUNET_a2s (sa,
+                     slen),
+         STRERROR (error));
+  }
+}
+
+
+
+
+/**
+ * Function that can be used by the transport service to transmit a
+ * message using the plugin.  Note that in the case of a peer
+ * disconnecting, the continuation MUST be called prior to the
+ * disconnect notification itself.  This function will be called with
+ * this peer's HELLO message to initiate a fresh connection to another
+ * peer.
+ *
+ * @param cls closure
+ * @param s which session must be used
+ * @param msgbuf the message to transmit
+ * @param msgbuf_size number of bytes in @a msgbuf
+ * @param priority how important is the message (most plugins will
+ *                 ignore message priority and just FIFO)
+ * @param to how long to wait at most for the transmission (does not
+ *                require plugins to discard the message after the timeout,
+ *                just advisory for the desired delay; most plugins will ignore
+ *                this as well)
+ * @param cont continuation to call once the message has
+ *        been transmitted (or if the transport is ready
+ *        for the next transmission call; or if the
+ *        peer disconnected...); can be NULL
+ * @param cont_cls closure for @a cont
+ * @return number of bytes used (on the physical network, with overheads);
+ *         -1 on hard errors (i.e. address invalid); 0 is a legal value
+ *         and does NOT mean that the message was not transmitted (DV)
+ */
+static ssize_t
+xu_plugin_send (void *cls,
+               struct GNUNET_ATS_Session *s,
+               const char *msgbuf,
+               size_t msgbuf_size,
+               unsigned int priority,
+               struct GNUNET_TIME_Relative to,
+               GNUNET_TRANSPORT_TransmitContinuation cont,
+               void *cont_cls)
+{
+  struct Plugin *plugin = cls;
+  size_t xumlen = msgbuf_size + sizeof(struct XUMessage);
+  struct XUMessage *xu;
+  char mbuf[xumlen] GNUNET_ALIGN;
+  ssize_t sent;
+  socklen_t slen;
+  const struct sockaddr *a;
+  const struct IPv4XuAddress *u4;
+  struct sockaddr_in a4;
+  const struct IPv6XuAddress *u6;
+  struct sockaddr_in6 a6;
+  struct GNUNET_NETWORK_Handle *sock;
+
+  (void) priority;
+  (void) to;
+  if ( (sizeof(struct IPv6XuAddress) == s->address->address_length) &&
+       (NULL == plugin->sockv6) )
+    return GNUNET_SYSERR;
+  if ( (sizeof(struct IPv4XuAddress) == s->address->address_length) &&
+       (NULL == plugin->sockv4) )
+    return GNUNET_SYSERR;
+  if (xumlen >= GNUNET_MAX_MESSAGE_SIZE)
+  {
+    GNUNET_break (0);
+    return GNUNET_SYSERR;
+  }
+  if (GNUNET_YES !=
+      GNUNET_CONTAINER_multipeermap_contains_value (plugin->sessions,
+                                                    &s->target,
+                                                    s))
+  {
+    GNUNET_break (0);
+    return GNUNET_SYSERR;
+  }
+  LOG (GNUNET_ERROR_TYPE_DEBUG,
+       "XU transmits %u-byte message to `%s' using address `%s'\n",
+       xumlen,
+       GNUNET_i2s (&s->target),
+       xu_address_to_string (plugin,
+                              s->address->address,
+                              s->address->address_length));
+  xu = (struct XUMessage *) mbuf;
+  xu->header.size = htons (xumlen);
+  xu->header.type = htons (GNUNET_MESSAGE_TYPE_TRANSPORT_XU_MESSAGE);
+  xu->reserved = htonl (0);
+  xu->sender = *plugin->env->my_identity;
+  GNUNET_memcpy (&xu[1],
+                msgbuf,
+                msgbuf_size);
+  
+  if (sizeof (struct IPv4XuAddress) == s->address->address_length)
+  {
+    u4 = s->address->address;
+    memset (&a4,
+           0,
+           sizeof(a4));
+    a4.sin_family = AF_INET;
+#if HAVE_SOCKADDR_IN_SIN_LEN
+    a4.sin_len = sizeof (a4);
+#endif
+    a4.sin_port = u4->u4_port;
+    a4.sin_addr.s_addr = u4->ipv4_addr;
+    a = (const struct sockaddr *) &a4;
+    slen = sizeof (a4);
+    sock = plugin->sockv4;
+  }
+  else if (sizeof (struct IPv6XuAddress) == s->address->address_length)
+  {
+    u6 = s->address->address;
+    memset (&a6,
+           0,
+           sizeof(a6));
+    a6.sin6_family = AF_INET6;
+#if HAVE_SOCKADDR_IN_SIN_LEN
+    a6.sin6_len = sizeof (a6);
+#endif
+    a6.sin6_port = u6->u6_port;
+    a6.sin6_addr = u6->ipv6_addr;
+    a = (const struct sockaddr *) &a6;
+    slen = sizeof (a6);
+    sock = plugin->sockv6;
+  }
+  else
+  {
+    GNUNET_break (0);
+    return GNUNET_SYSERR;
+  }
+    
+  sent = GNUNET_NETWORK_socket_sendto (sock,
+                                      mbuf,
+                                      xumlen,
+                                      a,
+                                      slen);
+  s->last_transmit_time
+    = GNUNET_TIME_absolute_max (GNUNET_TIME_absolute_get (),
+                               s->last_transmit_time);
+  
+  if (GNUNET_SYSERR == sent)
+  {
+    /* Failure */
+    analyze_send_error (plugin,
+                       a,
+                       slen,
+                       errno);
+    GNUNET_STATISTICS_update (plugin->env->stats,
+                             "# XU, total, bytes, sent, failure",
+                             sent,
+                             GNUNET_NO);
+    GNUNET_STATISTICS_update (plugin->env->stats,
+                             "# XU, total, messages, sent, failure",
+                             1,
+                             GNUNET_NO);
+    return GNUNET_SYSERR;
+  }
+  /* Success */
+  LOG (GNUNET_ERROR_TYPE_DEBUG,
+       "XU transmitted %u-byte message to  `%s' `%s' (%d: %s)\n",
+       (unsigned int) (msgbuf_size),
+       GNUNET_i2s (&s->target),
+       GNUNET_a2s (a,
+                  slen),
+       (int ) sent,
+       (sent < 0) ? STRERROR (errno) : "ok");
+  GNUNET_STATISTICS_update (plugin->env->stats,
+                           "# XU, total, bytes, sent, success",
+                           sent,
+                           GNUNET_NO);
+  GNUNET_STATISTICS_update (plugin->env->stats,
+                           "# XU, total, messages, sent, success",
+                           1,
+                           GNUNET_NO);
+  cont (cont_cls,
+       &s->target,
+       GNUNET_OK,
+       msgbuf_size,
+       xumlen);
+  notify_session_monitor (s->plugin,
+                          s,
+                          GNUNET_TRANSPORT_SS_UPDATE);
+  return xumlen;
+}
+
+
+/* ********************** Receiving ********************** */
+
+
+/**
+ * Functions with this signature are called whenever we need to close
+ * a session due to a disconnect or failure to establish a connection.
+ *
+ * @param cls closure with the `struct Plugin`
+ * @param s session to close down
+ * @return #GNUNET_OK on success
+ */
+static int
+xu_disconnect_session (void *cls,
+                        struct GNUNET_ATS_Session *s)
+{
+  struct Plugin *plugin = cls;
+
+  GNUNET_assert (GNUNET_YES != s->in_destroy);
+  LOG (GNUNET_ERROR_TYPE_DEBUG,
+       "Session %p to peer `%s' at address %s ended\n",
+       s,
+       GNUNET_i2s (&s->target),
+       xu_address_to_string (plugin,
+                              s->address->address,
+                              s->address->address_length));
+  if (NULL != s->timeout_task)
+  {
+    GNUNET_SCHEDULER_cancel (s->timeout_task);
+    s->timeout_task = NULL;
+  }
+  GNUNET_assert (GNUNET_YES ==
+                 GNUNET_CONTAINER_multipeermap_remove (plugin->sessions,
+                                                       &s->target,
+                                                       s));
+  s->in_destroy = GNUNET_YES;
+  notify_session_monitor (s->plugin,
+                          s,
+                          GNUNET_TRANSPORT_SS_DONE);
+  plugin->env->session_end (plugin->env->cls,
+                            s->address,
+                            s);
+  GNUNET_STATISTICS_set (plugin->env->stats,
+                         "# XU sessions active",
+                         GNUNET_CONTAINER_multipeermap_size (plugin->sessions),
+                         GNUNET_NO);
+  if (0 == s->rc)
+    free_session (s);
+  return GNUNET_OK;
+}
+
+
+/**
+ * Message tokenizer has broken up an incomming message. Pass it on
+ * to the service.
+ *
+ * @param cls the `struct GNUNET_ATS_Session *`
+ * @param hdr the actual message
+ * @return #GNUNET_OK (always)
+ */
+static int
+process_inbound_tokenized_messages (void *cls,
+                                    const struct GNUNET_MessageHeader *hdr)
+{
+  struct GNUNET_ATS_Session *session = cls;
+  struct Plugin *plugin = session->plugin;
+
+  if (GNUNET_YES == session->in_destroy)
+    return GNUNET_OK;
+  reschedule_session_timeout (session);
+  session->flow_delay_for_other_peer
+    = plugin->env->receive (plugin->env->cls,
+                            session->address,
+                            session,
+                            hdr);
+  return GNUNET_OK;
+}
+
+
+/**
+ * Destroy a session, plugin is being unloaded.
+ *
+ * @param cls the `struct Plugin`
+ * @param key hash of public key of target peer
+ * @param value a `struct PeerSession *` to clean up
+ * @return #GNUNET_OK (continue to iterate)
+ */
+static int
+disconnect_and_free_it (void *cls,
+                        const struct GNUNET_PeerIdentity *key,
+                        void *value)
+{
+  struct Plugin *plugin = cls;
+
+  (void) key;
+  xu_disconnect_session (plugin,
+                        value);
+  return GNUNET_OK;
+}
+
+
+/**
+ * Disconnect from a remote node.  Clean up session if we have one for
+ * this peer.
+ *
+ * @param cls closure for this call (should be handle to Plugin)
+ * @param target the peeridentity of the peer to disconnect
+ * @return #GNUNET_OK on success, #GNUNET_SYSERR if the operation failed
+ */
+static void
+xu_disconnect (void *cls,
+                const struct GNUNET_PeerIdentity *target)
+{
+  struct Plugin *plugin = cls;
+
+  LOG (GNUNET_ERROR_TYPE_DEBUG,
+       "Disconnecting from peer `%s'\n",
+       GNUNET_i2s (target));
+  GNUNET_CONTAINER_multipeermap_get_multiple (plugin->sessions,
+                                              target,
+                                              &disconnect_and_free_it,
+                                              plugin);
+}
+
+
+/**
+ * Session was idle, so disconnect it.
+ *
+ * @param cls the `struct GNUNET_ATS_Session` to time out
+ */
+static void
+session_timeout (void *cls)
+{
+  struct GNUNET_ATS_Session *s = cls;
+  struct Plugin *plugin = s->plugin;
+  struct GNUNET_TIME_Relative left;
+
+  s->timeout_task = NULL;
+  left = GNUNET_TIME_absolute_get_remaining (s->timeout);
+  if (left.rel_value_us > 0)
+  {
+    /* not actually our turn yet, but let's at least update
+       the monitor, it may think we're about to die ... */
+    notify_session_monitor (s->plugin,
+                            s,
+                            GNUNET_TRANSPORT_SS_UPDATE);
+    s->timeout_task = GNUNET_SCHEDULER_add_delayed (left,
+                                                    &session_timeout,
+                                                    s);
+    return;
+  }
+  LOG (GNUNET_ERROR_TYPE_DEBUG,
+       "Session %p was idle for %s, disconnecting\n",
+       s,
+       GNUNET_STRINGS_relative_time_to_string (XU_SESSION_TIME_OUT,
+                                               GNUNET_YES));
+  /* call session destroy function */
+  xu_disconnect_session (plugin,
+                          s);
+}
+
+
+/**
+ * Allocate a new session for the given endpoint address.
+ * Note that this function does not inform the service
+ * of the new session, this is the responsibility of the
+ * caller (if needed).
+ *
+ * @param cls the `struct Plugin`
+ * @param address address of the other peer to use
+ * @param network_type network type the address belongs to
+ * @return NULL on error, otherwise session handle
+ */
+static struct GNUNET_ATS_Session *
+xu_plugin_create_session (void *cls,
+                           const struct GNUNET_HELLO_Address *address,
+                           enum GNUNET_ATS_Network_Type network_type)
+{
+  struct Plugin *plugin = cls;
+  struct GNUNET_ATS_Session *s;
+
+  s = GNUNET_new (struct GNUNET_ATS_Session);
+  s->mst = GNUNET_MST_create (&process_inbound_tokenized_messages,
+                              s);
+  s->plugin = plugin;
+  s->address = GNUNET_HELLO_address_copy (address);
+  s->target = address->peer;
+  s->last_transmit_time = GNUNET_TIME_absolute_get ();
+  s->last_expected_ack_delay = GNUNET_TIME_relative_multiply 
(GNUNET_TIME_UNIT_MILLISECONDS,
+                                                              250);
+  s->last_expected_msg_delay = GNUNET_TIME_UNIT_MILLISECONDS;
+  s->flow_delay_from_other_peer = GNUNET_TIME_UNIT_ZERO;
+  s->flow_delay_for_other_peer = GNUNET_TIME_UNIT_ZERO;
+  s->timeout = GNUNET_TIME_relative_to_absolute (XU_SESSION_TIME_OUT);
+  s->timeout_task = GNUNET_SCHEDULER_add_delayed (XU_SESSION_TIME_OUT,
+                                                  &session_timeout,
+                                                  s);
+  s->scope = network_type;
+
+  LOG (GNUNET_ERROR_TYPE_DEBUG,
+       "Creating new session %p for peer `%s' address `%s'\n",
+       s,
+       GNUNET_i2s (&address->peer),
+       xu_address_to_string (plugin,
+                              address->address,
+                              address->address_length));
+  GNUNET_assert (GNUNET_OK ==
+                 GNUNET_CONTAINER_multipeermap_put (plugin->sessions,
+                                                    &s->target,
+                                                    s,
+                                                    
GNUNET_CONTAINER_MULTIHASHMAPOPTION_MULTIPLE));
+  GNUNET_STATISTICS_set (plugin->env->stats,
+                         "# XU sessions active",
+                         GNUNET_CONTAINER_multipeermap_size (plugin->sessions),
+                         GNUNET_NO);
+  notify_session_monitor (plugin,
+                          s,
+                          GNUNET_TRANSPORT_SS_INIT);
+  return s;
+}
+
+
+/**
+ * Creates a new outbound session the transport service will use to
+ * send data to the peer.
+ *
+ * @param cls the `struct Plugin *`
+ * @param address the address
+ * @return the session or NULL of max connections exceeded
+ */
+static struct GNUNET_ATS_Session *
+xu_plugin_get_session (void *cls,
+                        const struct GNUNET_HELLO_Address *address)
+{
+  struct Plugin *plugin = cls;
+  struct GNUNET_ATS_Session *s;
+  enum GNUNET_ATS_Network_Type network_type = GNUNET_ATS_NET_UNSPECIFIED;
+  const struct IPv4XuAddress *xu_v4;
+  const struct IPv6XuAddress *xu_v6;
+
+  if (NULL == address)
+  {
+    GNUNET_break (0);
+    return NULL;
+  }
+  if ( (address->address_length != sizeof(struct IPv4XuAddress)) &&
+       (address->address_length != sizeof(struct IPv6XuAddress)) )
+  {
+    GNUNET_break_op (0);
+    return NULL;
+  }
+  if (NULL != (s = xu_plugin_lookup_session (cls,
+                                              address)))
+    return s;
+
+  /* need to create new session */
+  if (sizeof (struct IPv4XuAddress) == address->address_length)
+  {
+    struct sockaddr_in v4;
+
+    xu_v4 = (const struct IPv4XuAddress *) address->address;
+    memset (&v4, '\0', sizeof (v4));
+    v4.sin_family = AF_INET;
+#if HAVE_SOCKADDR_IN_SIN_LEN
+    v4.sin_len = sizeof (struct sockaddr_in);
+#endif
+    v4.sin_port = xu_v4->u4_port;
+    v4.sin_addr.s_addr = xu_v4->ipv4_addr;
+    network_type = plugin->env->get_address_type (plugin->env->cls,
+                                                  (const struct sockaddr *) 
&v4,
+                                                  sizeof (v4));
+  }
+  if (sizeof (struct IPv6XuAddress) == address->address_length)
+  {
+    struct sockaddr_in6 v6;
+
+    xu_v6 = (const struct IPv6XuAddress *) address->address;
+    memset (&v6, '\0', sizeof (v6));
+    v6.sin6_family = AF_INET6;
+#if HAVE_SOCKADDR_IN_SIN_LEN
+    v6.sin6_len = sizeof (struct sockaddr_in6);
+#endif
+    v6.sin6_port = xu_v6->u6_port;
+    v6.sin6_addr = xu_v6->ipv6_addr;
+    network_type = plugin->env->get_address_type (plugin->env->cls,
+                                                  (const struct sockaddr *) 
&v6,
+                                                  sizeof (v6));
+  }
+  GNUNET_break (GNUNET_ATS_NET_UNSPECIFIED != network_type);
+  return xu_plugin_create_session (cls,
+                                   address,
+                                   network_type);
+}
+
+
+/**
+ * We've received a XU Message.  Process it (pass contents to main service).
+ *
+ * @param plugin plugin context
+ * @param msg the message
+ * @param xu_addr sender address
+ * @param xu_addr_len number of bytes in @a xu_addr
+ * @param network_type network type the address belongs to
+ */
+static void
+process_xu_message (struct Plugin *plugin,
+                     const struct XUMessage *msg,
+                     const union XuAddress *xu_addr,
+                     size_t xu_addr_len,
+                     enum GNUNET_ATS_Network_Type network_type)
+{
+  struct GNUNET_ATS_Session *s;
+  struct GNUNET_HELLO_Address *address;
+
+  GNUNET_break (GNUNET_ATS_NET_UNSPECIFIED != network_type);
+  if (0 != ntohl (msg->reserved))
+  {
+    GNUNET_break_op(0);
+    return;
+  }
+  if (ntohs (msg->header.size)
+      < sizeof(struct GNUNET_MessageHeader) + sizeof(struct XUMessage))
+  {
+    GNUNET_break_op(0);
+    return;
+  }
+
+  address = GNUNET_HELLO_address_allocate (&msg->sender,
+                                           PLUGIN_NAME,
+                                           xu_addr,
+                                           xu_addr_len,
+                                           GNUNET_HELLO_ADDRESS_INFO_NONE);
+  if (NULL ==
+      (s = xu_plugin_lookup_session (plugin,
+                                      address)))
+  {
+    s = xu_plugin_create_session (plugin,
+                                   address,
+                                   network_type);
+    plugin->env->session_start (plugin->env->cls,
+                                address,
+                                s,
+                                s->scope);
+    notify_session_monitor (plugin,
+                            s,
+                            GNUNET_TRANSPORT_SS_UP);
+  }
+  GNUNET_free (address);
+
+  s->rc++;
+  GNUNET_MST_from_buffer (s->mst,
+                          (const char *) &msg[1],
+                          ntohs (msg->header.size) - sizeof(struct XUMessage),
+                          GNUNET_YES,
+                          GNUNET_NO);
+  s->rc--;
+  if ( (0 == s->rc) &&
+       (GNUNET_YES == s->in_destroy) )
+    free_session (s);
+}
+
+
+/**
+ * Read and process a message from the given socket.
+ *
+ * @param plugin the overall plugin
+ * @param rsock socket to read from
+ */
+static void
+xu_select_read (struct Plugin *plugin,
+                 struct GNUNET_NETWORK_Handle *rsock)
+{
+  socklen_t fromlen;
+  struct sockaddr_storage addr;
+  char buf[65536] GNUNET_ALIGN;
+  ssize_t size;
+  const struct GNUNET_MessageHeader *msg;
+  struct IPv4XuAddress v4;
+  struct IPv6XuAddress v6;
+  const struct sockaddr *sa;
+  const struct sockaddr_in *sa4;
+  const struct sockaddr_in6 *sa6;
+  const union XuAddress *int_addr;
+  size_t int_addr_len;
+  enum GNUNET_ATS_Network_Type network_type;
+
+  fromlen = sizeof (addr);
+  memset (&addr,
+          0,
+          sizeof(addr));
+  size = GNUNET_NETWORK_socket_recvfrom (rsock,
+                                         buf,
+                                         sizeof (buf),
+                                         (struct sockaddr *) &addr,
+                                         &fromlen);
+  sa = (const struct sockaddr *) &addr;
+#if MINGW
+  /* On SOCK_DGRAM XU sockets recvfrom might fail with a
+   * WSAECONNRESET error to indicate that previous sendto() (yes, sendto!)
+   * on this socket has failed.
+   * Quote from MSDN:
+   *   WSAECONNRESET - The virtual circuit was reset by the remote side
+   *   executing a hard or abortive close. The application should close
+   *   the socket; it is no longer usable. On a XU-datagram socket this
+   *   error indicates a previous send operation resulted in an ICMP Port
+   *   Unreachable message.
+   */
+  if ( (-1 == size) &&
+       (ECONNRESET == errno) )
+    return;
+#endif
+  if (-1 == size)
+  {
+    LOG (GNUNET_ERROR_TYPE_DEBUG,
+         "XU failed to receive data: %s\n",
+         STRERROR (errno));
+    /* Connection failure or something. Not a protocol violation. */
+    return;
+  }
+
+  /* Check if this is a STUN packet */
+  if (GNUNET_NO !=
+      GNUNET_NAT_stun_handle_packet (plugin->nat,
+                                    (const struct sockaddr *) &addr,
+                                    fromlen,
+                                    buf,
+                                    size))
+    return; /* was STUN, do not process further */
+
+  if (((size_t) size) < sizeof(struct GNUNET_MessageHeader))
+  {
+    LOG (GNUNET_ERROR_TYPE_WARNING,
+         "XU got %u bytes from %s, which is not enough for a GNUnet message 
header\n",
+         (unsigned int ) size,
+         GNUNET_a2s (sa,
+                     fromlen));
+    /* _MAY_ be a connection failure (got partial message) */
+    /* But it _MAY_ also be that the other side uses non-GNUnet protocol. */
+    GNUNET_break_op (0);
+    return;
+  }
+
+  msg = (const struct GNUNET_MessageHeader *) buf;
+  LOG (GNUNET_ERROR_TYPE_DEBUG,
+       "XU received %u-byte message from `%s' type %u\n",
+       (unsigned int) size,
+       GNUNET_a2s (sa,
+                   fromlen),
+       ntohs (msg->type));
+  if (size != ntohs (msg->size))
+  {
+    LOG (GNUNET_ERROR_TYPE_WARNING,
+         "XU malformed message (size %u) header from %s\n",
+         (unsigned int) size,
+         GNUNET_a2s (sa,
+                     fromlen));
+    GNUNET_break_op (0);
+    return;
+  }
+  GNUNET_STATISTICS_update (plugin->env->stats,
+                            "# XU, total bytes received",
+                            size,
+                            GNUNET_NO);
+  network_type = plugin->env->get_address_type (plugin->env->cls,
+                                                sa,
+                                                fromlen);
+  switch (sa->sa_family)
+  {
+  case AF_INET:
+    sa4 = (const struct sockaddr_in *) &addr;
+    v4.options = 0;
+    v4.ipv4_addr = sa4->sin_addr.s_addr;
+    v4.u4_port = sa4->sin_port;
+    int_addr = (union XuAddress *) &v4;
+    int_addr_len = sizeof (v4);
+    break;
+  case AF_INET6:
+    sa6 = (const struct sockaddr_in6 *) &addr;
+    v6.options = 0;
+    v6.ipv6_addr = sa6->sin6_addr;
+    v6.u6_port = sa6->sin6_port;
+    int_addr = (union XuAddress *) &v6;
+    int_addr_len = sizeof (v6);
+    break;
+  default:
+    GNUNET_break (0);
+    return;
+  }
+
+  switch (ntohs (msg->type))
+  {
+  case GNUNET_MESSAGE_TYPE_TRANSPORT_XU_MESSAGE:
+    if (ntohs (msg->size) < sizeof(struct XUMessage))
+    {
+      GNUNET_break_op(0);
+      return;
+    }
+    process_xu_message (plugin,
+                         (const struct XUMessage *) msg,
+                         int_addr,
+                         int_addr_len,
+                         network_type);
+    return;
+  default:
+    GNUNET_break_op(0);
+    return;
+  }
+}
+
+
+/* ***************** Event loop (part 2) *************** */
+
+
+/**
+ * We have been notified that our readset has something to read.  We don't
+ * know which socket needs to be read, so we have to check each one
+ * Then reschedule this function to be called again once more is available.
+ *
+ * @param cls the plugin handle
+ */
+static void
+xu_plugin_select_v4 (void *cls)
+{
+  struct Plugin *plugin = cls;
+  const struct GNUNET_SCHEDULER_TaskContext *tc;
+
+  plugin->select_task_v4 = NULL;
+  if (NULL == plugin->sockv4)
+    return;
+  tc = GNUNET_SCHEDULER_get_task_context ();
+  if ( (0 != (tc->reason & GNUNET_SCHEDULER_REASON_READ_READY)) &&
+       (GNUNET_NETWORK_fdset_isset (tc->read_ready,
+                                   plugin->sockv4)) )
+    xu_select_read (plugin,
+                     plugin->sockv4);
+  schedule_select_v4 (plugin);
+}
+
+
+/**
+ * We have been notified that our readset has something to read.  We don't
+ * know which socket needs to be read, so we have to check each one
+ * Then reschedule this function to be called again once more is available.
+ *
+ * @param cls the plugin handle
+ */
+static void
+xu_plugin_select_v6 (void *cls)
+{
+  struct Plugin *plugin = cls;
+  const struct GNUNET_SCHEDULER_TaskContext *tc;
+
+  plugin->select_task_v6 = NULL;
+  if (NULL == plugin->sockv6)
+    return;
+  tc = GNUNET_SCHEDULER_get_task_context ();
+  if ( (0 != (tc->reason & GNUNET_SCHEDULER_REASON_READ_READY)) &&
+       (GNUNET_NETWORK_fdset_isset (tc->read_ready,
+                                    plugin->sockv6)) )
+    xu_select_read (plugin,
+                     plugin->sockv6);
+  schedule_select_v6 (plugin);
+}
+
+
+/* ******************* Initialization *************** */
+
+
+/**
+ * Setup the XU sockets (for IPv4 and IPv6) for the plugin.
+ *
+ * @param plugin the plugin to initialize
+ * @param bind_v6 IPv6 address to bind to (can be NULL, for 'any')
+ * @param bind_v4 IPv4 address to bind to (can be NULL, for 'any')
+ * @return number of sockets that were successfully bound
+ */
+static unsigned int
+setup_sockets (struct Plugin *plugin,
+               const struct sockaddr_in6 *bind_v6,
+               const struct sockaddr_in *bind_v4)
+{
+  int tries;
+  unsigned int sockets_created = 0;
+  struct sockaddr_in6 server_addrv6;
+  struct sockaddr_in server_addrv4;
+  const struct sockaddr *server_addr;
+  const struct sockaddr *addrs[2];
+  socklen_t addrlens[2];
+  socklen_t addrlen;
+  int eno;
+
+  /* Create IPv6 socket */
+  eno = EINVAL;
+  if (GNUNET_YES == plugin->enable_ipv6)
+  {
+    plugin->sockv6 = GNUNET_NETWORK_socket_create (PF_INET6,
+                                                   SOCK_DGRAM,
+                                                   0);
+    if (NULL == plugin->sockv6)
+    {
+      LOG (GNUNET_ERROR_TYPE_INFO,
+           _("Disabling IPv6 since it is not supported on this system!\n"));
+      plugin->enable_ipv6 = GNUNET_NO;
+    }
+    else
+    {
+      memset (&server_addrv6,
+              0,
+              sizeof(struct sockaddr_in6));
+#if HAVE_SOCKADDR_IN_SIN_LEN
+      server_addrv6.sin6_len = sizeof (struct sockaddr_in6);
+#endif
+      server_addrv6.sin6_family = AF_INET6;
+      if (NULL != bind_v6)
+        server_addrv6.sin6_addr = bind_v6->sin6_addr;
+      else
+        server_addrv6.sin6_addr = in6addr_any;
+
+      if (0 == plugin->port) /* autodetect */
+        server_addrv6.sin6_port
+          = htons (GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_STRONG,
+                                             33537)
+                   + 32000);
+      else
+        server_addrv6.sin6_port = htons (plugin->port);
+      addrlen = sizeof (struct sockaddr_in6);
+      server_addr = (const struct sockaddr *) &server_addrv6;
+
+      tries = 0;
+      while (tries < 10)
+      {
+        LOG(GNUNET_ERROR_TYPE_DEBUG,
+            "Binding to IPv6 `%s'\n",
+            GNUNET_a2s (server_addr,
+                        addrlen));
+        /* binding */
+        if (GNUNET_OK ==
+            GNUNET_NETWORK_socket_bind (plugin->sockv6,
+                                        server_addr,
+                                        addrlen))
+          break;
+        eno = errno;
+        if (0 != plugin->port)
+        {
+          tries = 10; /* fail immediately */
+          break; /* bind failed on specific port */
+        }
+        /* autodetect */
+        server_addrv6.sin6_port
+          = htons (GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_STRONG,
+                                             33537)
+                   + 32000);
+        tries++;
+      }
+      if (tries >= 10)
+      {
+        GNUNET_NETWORK_socket_close (plugin->sockv6);
+        plugin->enable_ipv6 = GNUNET_NO;
+        plugin->sockv6 = NULL;
+      }
+      else
+      {
+        plugin->port = ntohs (server_addrv6.sin6_port);
+      }
+      if (NULL != plugin->sockv6)
+      {
+        LOG (GNUNET_ERROR_TYPE_DEBUG,
+             "IPv6 XU socket created listinging at %s\n",
+             GNUNET_a2s (server_addr,
+                         addrlen));
+        addrs[sockets_created] = server_addr;
+        addrlens[sockets_created] = addrlen;
+        sockets_created++;
+      }
+      else
+      {
+        LOG (GNUNET_ERROR_TYPE_WARNING,
+             _("Failed to bind XU socket to %s: %s\n"),
+             GNUNET_a2s (server_addr,
+                         addrlen),
+             STRERROR (eno));
+      }
+    }
+  }
+
+  /* Create IPv4 socket */
+  eno = EINVAL;
+  plugin->sockv4 = GNUNET_NETWORK_socket_create (PF_INET,
+                                                 SOCK_DGRAM,
+                                                 0);
+  if (NULL == plugin->sockv4)
+  {
+    GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING,
+                         "socket");
+    LOG (GNUNET_ERROR_TYPE_INFO,
+         _("Disabling IPv4 since it is not supported on this system!\n"));
+    plugin->enable_ipv4 = GNUNET_NO;
+  }
+  else
+  {
+    memset (&server_addrv4,
+            0,
+            sizeof(struct sockaddr_in));
+#if HAVE_SOCKADDR_IN_SIN_LEN
+    server_addrv4.sin_len = sizeof (struct sockaddr_in);
+#endif
+    server_addrv4.sin_family = AF_INET;
+    if (NULL != bind_v4)
+      server_addrv4.sin_addr = bind_v4->sin_addr;
+    else
+      server_addrv4.sin_addr.s_addr = INADDR_ANY;
+
+    if (0 == plugin->port)
+      /* autodetect */
+      server_addrv4.sin_port
+        = htons (GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_STRONG,
+                                           33537)
+                 + 32000);
+    else
+      server_addrv4.sin_port = htons (plugin->port);
+
+    addrlen = sizeof (struct sockaddr_in);
+    server_addr = (const struct sockaddr *) &server_addrv4;
+
+    tries = 0;
+    while (tries < 10)
+    {
+      LOG (GNUNET_ERROR_TYPE_DEBUG,
+           "Binding to IPv4 `%s'\n",
+           GNUNET_a2s (server_addr,
+                       addrlen));
+
+      /* binding */
+      if (GNUNET_OK ==
+          GNUNET_NETWORK_socket_bind (plugin->sockv4,
+                                      server_addr,
+                                      addrlen))
+        break;
+      eno = errno;
+      if (0 != plugin->port)
+      {
+        tries = 10; /* fail */
+        break; /* bind failed on specific port */
+      }
+
+      /* autodetect */
+      server_addrv4.sin_port
+        = htons (GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_STRONG,
+                                           33537)
+                 + 32000);
+      tries++;
+    }
+    if (tries >= 10)
+    {
+      GNUNET_NETWORK_socket_close (plugin->sockv4);
+      plugin->enable_ipv4 = GNUNET_NO;
+      plugin->sockv4 = NULL;
+    }
+    else
+    {
+      plugin->port = ntohs (server_addrv4.sin_port);
+    }
+
+    if (NULL != plugin->sockv4)
+    {
+      LOG (GNUNET_ERROR_TYPE_DEBUG,
+           "IPv4 socket created on port %s\n",
+           GNUNET_a2s (server_addr,
+                       addrlen));
+      addrs[sockets_created] = server_addr;
+      addrlens[sockets_created] = addrlen;
+      sockets_created++;
+    }
+    else
+    {
+      LOG (GNUNET_ERROR_TYPE_ERROR,
+           _("Failed to bind XU socket to %s: %s\n"),
+           GNUNET_a2s (server_addr,
+                       addrlen),
+           STRERROR (eno));
+    }
+  }
+
+  if (0 == sockets_created)
+  {
+    LOG (GNUNET_ERROR_TYPE_WARNING,
+         _("Failed to open XU sockets\n"));
+    return 0; /* No sockets created, return */
+  }
+  schedule_select_v4 (plugin);
+  schedule_select_v6 (plugin);
+  plugin->nat = GNUNET_NAT_register (plugin->env->cfg,
+                                    "transport-xu",
+                                    IPPROTO_UDP,
+                                     sockets_created,
+                                     addrs,
+                                     addrlens,
+                                     &xu_nat_port_map_callback,
+                                     NULL,
+                                     plugin);
+  return sockets_created;
+}
+
+
+/**
+ * The exported method. Makes the core api available via a global and
+ * returns the xu transport API.
+ *
+ * @param cls our `struct GNUNET_TRANSPORT_PluginEnvironment`
+ * @return our `struct GNUNET_TRANSPORT_PluginFunctions`
+ */
+void *
+libgnunet_plugin_transport_xu_init (void *cls)
+{
+  struct GNUNET_TRANSPORT_PluginEnvironment *env = cls;
+  struct GNUNET_TRANSPORT_PluginFunctions *api;
+  struct Plugin *p;
+  unsigned long long port;
+  unsigned long long aport;
+  int enable_v6;
+  char *bind4_address;
+  char *bind6_address;
+  struct sockaddr_in server_addrv4;
+  struct sockaddr_in6 server_addrv6;
+  unsigned int res;
+  int have_bind4;
+  int have_bind6;
+
+  if (NULL == env->receive)
+  {
+    /* run in 'stub' mode (i.e. as part of gnunet-peerinfo), don't fully
+     initialze the plugin or the API */
+    api = GNUNET_new (struct GNUNET_TRANSPORT_PluginFunctions);
+    api->cls = NULL;
+    api->address_pretty_printer = &xu_plugin_address_pretty_printer;
+    api->address_to_string = &xu_address_to_string;
+    api->string_to_address = &xu_string_to_address;
+    return api;
+  }
+
+  /* Get port number: port == 0 : autodetect a port,
+   * > 0 : use this port, not given : 2086 default */
+  if (GNUNET_OK !=
+      GNUNET_CONFIGURATION_get_value_number (env->cfg,
+                                             "transport-xu",
+                                             "PORT",
+                                             &port))
+    port = 2086;
+  if (port > 65535)
+  {
+    GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR,
+                               "transport-xu",
+                               "PORT",
+                               _("must be in [0,65535]"));
+    return NULL;
+  }
+  if (GNUNET_OK !=
+      GNUNET_CONFIGURATION_get_value_number (env->cfg,
+                                             "transport-xu",
+                                             "ADVERTISED_PORT",
+                                             &aport))
+    aport = port;
+  if (aport > 65535)
+  {
+    GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR,
+                               "transport-xu",
+                               "ADVERTISED_PORT",
+                               _("must be in [0,65535]"));
+    return NULL;
+  }
+
+  if (GNUNET_YES ==
+      GNUNET_CONFIGURATION_get_value_yesno (env->cfg,
+                                            "nat",
+                                            "DISABLEV6"))
+    enable_v6 = GNUNET_NO;
+  else
+    enable_v6 = GNUNET_YES;
+
+  have_bind4 = GNUNET_NO;
+  memset (&server_addrv4,
+          0,
+          sizeof (server_addrv4));
+  if (GNUNET_YES ==
+      GNUNET_CONFIGURATION_get_value_string (env->cfg,
+                                             "transport-xu",
+                                             "BINDTO",
+                                             &bind4_address))
+  {
+    LOG (GNUNET_ERROR_TYPE_DEBUG,
+         "Binding XU plugin to specific address: `%s'\n",
+         bind4_address);
+    if (1 != inet_pton (AF_INET,
+                        bind4_address,
+                        &server_addrv4.sin_addr))
+    {
+      GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR,
+                                 "transport-xu",
+                                 "BINDTO",
+                                 _("must be valid IPv4 address"));
+      GNUNET_free (bind4_address);
+      return NULL;
+    }
+    have_bind4 = GNUNET_YES;
+  }
+  GNUNET_free_non_null (bind4_address);
+  have_bind6 = GNUNET_NO;
+  memset (&server_addrv6,
+          0,
+          sizeof (server_addrv6));
+  if (GNUNET_YES ==
+      GNUNET_CONFIGURATION_get_value_string (env->cfg,
+                                             "transport-xu",
+                                             "BINDTO6",
+                                             &bind6_address))
+  {
+    LOG (GNUNET_ERROR_TYPE_DEBUG,
+         "Binding xu plugin to specific address: `%s'\n",
+         bind6_address);
+    if (1 != inet_pton (AF_INET6,
+                        bind6_address,
+                        &server_addrv6.sin6_addr))
+    {
+      GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR,
+                                 "transport-xu",
+                                 "BINDTO6",
+                                 _("must be valid IPv6 address"));
+      GNUNET_free (bind6_address);
+      return NULL;
+    }
+    have_bind6 = GNUNET_YES;
+  }
+  GNUNET_free_non_null (bind6_address);
+
+  p = GNUNET_new (struct Plugin);
+  p->port = port;
+  p->aport = aport;
+  p->enable_ipv6 = enable_v6;
+  p->enable_ipv4 = GNUNET_YES; /* default */
+  p->env = env;
+  p->sessions = GNUNET_CONTAINER_multipeermap_create (16,
+                                                      GNUNET_NO);
+  res = setup_sockets (p,
+                       (GNUNET_YES == have_bind6) ? &server_addrv6 : NULL,
+                       (GNUNET_YES == have_bind4) ? &server_addrv4 : NULL);
+  if ( (0 == res) ||
+       ( (NULL == p->sockv4) &&
+         (NULL == p->sockv6) ) )
+  {
+    LOG (GNUNET_ERROR_TYPE_ERROR,
+        _("Failed to create XU network sockets\n"));
+    GNUNET_CONTAINER_multipeermap_destroy (p->sessions);
+    if (NULL != p->nat)
+      GNUNET_NAT_unregister (p->nat);
+    GNUNET_free (p);
+    return NULL;
+  }
+
+  api = GNUNET_new (struct GNUNET_TRANSPORT_PluginFunctions);
+  api->cls = p;
+  api->disconnect_session = &xu_disconnect_session;
+  api->query_keepalive_factor = &xu_query_keepalive_factor;
+  api->disconnect_peer = &xu_disconnect;
+  api->address_pretty_printer = &xu_plugin_address_pretty_printer;
+  api->address_to_string = &xu_address_to_string;
+  api->string_to_address = &xu_string_to_address;
+  api->check_address = &xu_plugin_check_address;
+  api->get_session = &xu_plugin_get_session;
+  api->send = &xu_plugin_send;
+  api->get_network = &xu_plugin_get_network;
+  api->get_network_for_address = &xu_plugin_get_network_for_address;
+  api->update_session_timeout = &xu_plugin_update_session_timeout;
+  api->setup_monitor = &xu_plugin_setup_monitor;
+  return api;
+}
+
+
+/**
+ * The exported method. Makes the core api available via a global and
+ * returns the xu transport API.
+ *
+ * @param cls our `struct GNUNET_TRANSPORT_PluginEnvironment`
+ * @return NULL
+ */
+void *
+libgnunet_plugin_transport_xu_done (void *cls)
+{
+  struct GNUNET_TRANSPORT_PluginFunctions *api = cls;
+  struct Plugin *plugin = api->cls;
+  struct PrettyPrinterContext *cur;
+
+  if (NULL == plugin)
+  {
+    GNUNET_free (api);
+    return NULL;
+  }
+  if (NULL != plugin->select_task_v4)
+  {
+    GNUNET_SCHEDULER_cancel (plugin->select_task_v4);
+    plugin->select_task_v4 = NULL;
+  }
+  if (NULL != plugin->select_task_v6)
+  {
+    GNUNET_SCHEDULER_cancel (plugin->select_task_v6);
+    plugin->select_task_v6 = NULL;
+  }
+  if (NULL != plugin->sockv4)
+  {
+    GNUNET_break (GNUNET_OK ==
+                  GNUNET_NETWORK_socket_close (plugin->sockv4));
+    plugin->sockv4 = NULL;
+  }
+  if (NULL != plugin->sockv6)
+  {
+    GNUNET_break (GNUNET_OK ==
+                  GNUNET_NETWORK_socket_close (plugin->sockv6));
+    plugin->sockv6 = NULL;
+  }
+  if (NULL != plugin->nat)
+  {
+    GNUNET_NAT_unregister (plugin->nat);
+    plugin->nat = NULL;
+  }
+  GNUNET_CONTAINER_multipeermap_destroy (plugin->sessions);
+
+  while (NULL != (cur = plugin->ppc_dll_head))
+  {
+    GNUNET_break (0);
+    GNUNET_CONTAINER_DLL_remove (plugin->ppc_dll_head,
+                                 plugin->ppc_dll_tail,
+                                 cur);
+    GNUNET_RESOLVER_request_cancel (cur->resolver_handle);
+    if (NULL != cur->timeout_task)
+    {
+      GNUNET_SCHEDULER_cancel (cur->timeout_task);
+      cur->timeout_task = NULL;
+    }
+    GNUNET_free (cur);
+  }
+  GNUNET_free (plugin);
+  GNUNET_free (api);
+  return NULL;
+}
+
+/* end of plugin_transport_xu.c */
diff --git a/src/transport/plugin_transport_udp.h 
b/src/transport/plugin_transport_xu.h
similarity index 64%
copy from src/transport/plugin_transport_udp.h
copy to src/transport/plugin_transport_xu.h
index 48c7365c7..1884f92e8 100644
--- a/src/transport/plugin_transport_udp.h
+++ b/src/transport/plugin_transport_xu.h
@@ -19,14 +19,14 @@
 */
 
 /**
- * @file transport/plugin_transport_udp.h
- * @brief Implementation of the UDP transport protocol
+ * @file transport/plugin_transport_xu.h
+ * @brief Implementation of the XU transport protocol
  * @author Christian Grothoff
  * @author Nathan Evans
  * @author Matthias Wachs
  */
-#ifndef PLUGIN_TRANSPORT_UDP_H
-#define PLUGIN_TRANSPORT_UDP_H
+#ifndef PLUGIN_TRANSPORT_XU_H
+#define PLUGIN_TRANSPORT_XU_H
 
 #include "platform.h"
 #include "gnunet_hello_lib.h"
@@ -41,26 +41,26 @@
 #include "gnunet_transport_plugin.h"
 #include "transport.h"
 
-#define LOG(kind,...) GNUNET_log_from (kind, "transport-udp", __VA_ARGS__)
+#define LOG(kind,...) GNUNET_log_from (kind, "transport-xu", __VA_ARGS__)
 
-#define PLUGIN_NAME "udp"
+#define PLUGIN_NAME "xu"
 
-#define DEBUG_UDP GNUNET_NO
+#define DEBUG_XU GNUNET_NO
 
-#define DEBUG_UDP_BROADCASTING GNUNET_NO
+#define DEBUG_XU_BROADCASTING GNUNET_NO
 
 /**
  * MTU for fragmentation subsystem.  Should be conservative since
  * all communicating peers MUST work with this MTU.
  */
-#define UDP_MTU 1400
+#define XU_MTU 1400
 
 
 GNUNET_NETWORK_STRUCT_BEGIN
 /**
  * Network format for IPv4 addresses.
  */
-struct IPv4UdpAddress
+struct IPv4XuAddress
 {
   /**
    * Optional options and flags for this address
@@ -82,7 +82,7 @@ struct IPv4UdpAddress
 /**
  * Network format for IPv6 addresses.
  */
-struct IPv6UdpAddress
+struct IPv6XuAddress
 {
   /**
    * Optional options and flags for this address
@@ -102,27 +102,27 @@ struct IPv6UdpAddress
 GNUNET_NETWORK_STRUCT_END
 
 /**
- * Either an IPv4 or IPv6 UDP address.  Note that without a "length",
+ * Either an IPv4 or IPv6 XU address.  Note that without a "length",
  * one cannot tell which one of the two types this address represents.
  */
-union UdpAddress
+union XuAddress
 {
   /**
    * IPv4 case.
    */
-  struct IPv4UdpAddress v4;
+  struct IPv4XuAddress v4;
 
   /**
    * IPv6 case.
    */
-  struct IPv6UdpAddress v6;
+  struct IPv6XuAddress v6;
 };
 
 
 /**
  * Information we track for each message in the queue.
  */
-struct UDP_MessageWrapper;
+struct XU_MessageWrapper;
 
 
 /**
@@ -149,11 +149,6 @@ struct Plugin
   struct GNUNET_CONTAINER_MultiPeerMap *sessions;
 
   /**
-   * Heap with all of our defragmentation activities.
-   */
-  struct GNUNET_CONTAINER_Heap *defrag_ctxs;
-
-  /**
    * ID of select task for IPv4
    */
   struct GNUNET_SCHEDULER_Task *select_task_v4;
@@ -164,11 +159,6 @@ struct Plugin
   struct GNUNET_SCHEDULER_Task *select_task_v6;
 
   /**
-   * Bandwidth tracker to limit global UDP traffic.
-   */
-  struct GNUNET_BANDWIDTH_Tracker tracker;
-
-  /**
    * Address we were told to bind to exclusively (IPv4).
    */
   char *bind4_address;
@@ -199,36 +189,6 @@ struct Plugin
   struct GNUNET_NETWORK_Handle *sockv6;
 
   /**
-   * Head of DLL of broadcast addresses
-   */
-  struct BroadcastAddress *broadcast_tail;
-
-  /**
-   * Tail of DLL of broadcast addresses
-   */
-  struct BroadcastAddress *broadcast_head;
-
-  /**
-   * Head of messages in IPv4 queue.
-   */
-  struct UDP_MessageWrapper *ipv4_queue_head;
-
-  /**
-   * Tail of messages in IPv4 queue.
-   */
-  struct UDP_MessageWrapper *ipv4_queue_tail;
-
-  /**
-   * Head of messages in IPv6 queue.
-   */
-  struct UDP_MessageWrapper *ipv6_queue_head;
-
-  /**
-   * Tail of messages in IPv6 queue.
-   */
-  struct UDP_MessageWrapper *ipv6_queue_tail;
-
-  /**
    * Running pretty printers: head
    */
   struct PrettyPrinterContext *ppc_dll_head;
@@ -279,21 +239,6 @@ struct Plugin
   int enable_ipv4;
 
   /**
-   * Is broadcasting enabled: #GNUNET_YES or #GNUNET_NO
-   */
-  int enable_broadcasting;
-
-  /**
-   * Is receiving broadcasts enabled: #GNUNET_YES or #GNUNET_NO
-   */
-  int enable_broadcasting_receiving;
-
-  /**
-   * Port we broadcasting on.
-   */
-  uint16_t broadcast_port;
-
-  /**
    * Port we listen on.
    */
   uint16_t port;
@@ -313,45 +258,16 @@ struct Plugin
  * to override the address again.
  *
  * @param cls closure
- * @param addr binary address (a `union UdpAddress`)
+ * @param addr binary address (a `union XuAddress`)
  * @param addrlen length of the @a addr
  * @return string representing the same address
  */
 const char *
-udp_address_to_string (void *cls,
+xu_address_to_string (void *cls,
                        const void *addr,
                        size_t addrlen);
 
 
-/**
- * We received a broadcast message.  Process it and all subsequent
- * messages in the same packet.
- *
- * @param plugin the UDP plugin
- * @param buf the buffer with the message(s)
- * @param size number of bytes in @a buf
- * @param udp_addr address of the sender
- * @param udp_addr_len number of bytes in @a udp_addr
- * @param network_type network type of the sender's address
- */
-void
-udp_broadcast_receive (struct Plugin *plugin,
-                       const char *buf,
-                       ssize_t size,
-                       const union UdpAddress *udp_addr,
-                       size_t udp_addr_len,
-                       enum GNUNET_ATS_Network_Type network_type);
-
-
-void
-setup_broadcast (struct Plugin *plugin,
-                 struct sockaddr_in6 *server_addrv6,
-                 struct sockaddr_in *server_addrv4);
-
-
-void
-stop_broadcast (struct Plugin *plugin);
-
-/*#ifndef PLUGIN_TRANSPORT_UDP_H*/
+/*#ifndef PLUGIN_TRANSPORT_XU_H*/
 #endif
-/* end of plugin_transport_udp.h */
+/* end of plugin_transport_xu.h */
diff --git a/src/transport/transport.conf.in b/src/transport/transport.conf.in
index 2c99af000..1f56a2fa7 100644
--- a/src/transport/transport.conf.in
+++ b/src/transport/transport.conf.in
@@ -68,6 +68,38 @@ MAX_CONNECTIONS = 128
 TCP_STEALTH = NO
 
 
+
+[transport-xt]
+# Use 0 to ONLY advertise as a peer behind NAT (no port binding)
+PORT = 2087
+
+# Obsolete option, to be replaced by HOLE_EXTERNAL (soon)
+ADVERTISED_PORT = 2087
+
+# If we have a manually punched NAT, what is the external IP and port?
+# Can use DNS names for DynDNS-based detection of external IP.
+# Can use IPv6 addresses ([fefc::]:PORT).
+# Use "AUTO" for the hostname to automatically detect external IP.
+# Do not set if NAT is not manually punched.
+# HOLE_EXTERNAL = AUTO:2087
+
+TESTING_IGNORE_KEYS = ACCEPT_FROM;
+
+# Maximum number of open TCP connections allowed
+MAX_CONNECTIONS = 128
+
+TIMEOUT = 5 s
+# ACCEPT_FROM =
+# ACCEPT_FROM6 =
+# REJECT_FROM =
+# REJECT_FROM6 =
+# BINDTO =
+MAX_CONNECTIONS = 128
+
+# Enable TCP stealth?
+TCP_STEALTH = NO
+
+
 [transport-udp]
 # Use PORT = 0 to autodetect a port available
 PORT = 2086
@@ -90,6 +122,11 @@ TESTING_IGNORE_KEYS = ACCEPT_FROM;
 # HOLE_EXTERNAL = AUTO:2086
 
 
+[transport-xu]
+# Use PORT = 0 to autodetect a port available
+PORT = 2087
+
+
 [transport-http_client]
 MAX_CONNECTIONS = 128
 TESTING_IGNORE_KEYS = ACCEPT_FROM;
diff --git a/src/util/getopt_helpers.c b/src/util/getopt_helpers.c
index c836c9055..f9341f528 100644
--- a/src/util/getopt_helpers.c
+++ b/src/util/getopt_helpers.c
@@ -793,6 +793,82 @@ GNUNET_GETOPT_option_uint (char shortName,
 }
 
 
+
+/**
+ * Set an option of type 'uint16_t' from the command line.
+ * A pointer to this function should be passed as part of the
+ * 'struct GNUNET_GETOPT_CommandLineOption' array to initialize options
+ * of this type.  It should be followed by a pointer to a value of
+ * type 'uint16_t'.
+ *
+ * @param ctx command line processing context
+ * @param scls additional closure (will point to the 'unsigned int')
+ * @param option name of the option
+ * @param value actual value of the option as a string.
+ * @return #GNUNET_OK if parsing the value worked
+ */
+static int
+set_uint16 (struct GNUNET_GETOPT_CommandLineProcessorContext *ctx,
+           void *scls,
+           const char *option,
+           const char *value)
+{
+  uint16_t *val = scls;
+  unsigned int v;
+  
+  (void) ctx;
+  if (1 != SSCANF (value,
+                   "%u",
+                   v))
+  {
+    FPRINTF (stderr,
+             _("You must pass a number to the `%s' option.\n"),
+             option);
+    return GNUNET_SYSERR;
+  }
+  if (v > UINT16_MAX)
+  {
+    FPRINTF (stderr,
+             _("You must pass a number below %u to the `%s' option.\n"),
+            (unsigned int) UINT16_MAX,
+             option);
+    return GNUNET_SYSERR;
+  }
+  *val = (uint16_t) v;
+  return GNUNET_OK;
+}
+
+
+/**
+ * Allow user to specify an uint16_t.
+ *
+ * @param shortName short name of the option
+ * @param name long name of the option
+ * @param argumentHelp help text for the option argument
+ * @param description long help text for the option
+ * @param[out] val set to the value specified at the command line
+ */
+struct GNUNET_GETOPT_CommandLineOption
+GNUNET_GETOPT_option_uint16 (char shortName,
+                            const char *name,
+                            const char *argumentHelp,
+                            const char *description,
+                            uint16_t *val)
+{
+  struct GNUNET_GETOPT_CommandLineOption clo = {
+    .shortName =  shortName,
+    .name = name,
+    .argumentHelp = argumentHelp,
+    .description = description,
+    .require_argument = 1,
+    .processor = &set_uint16,
+    .scls = (void *) val
+  };
+
+  return clo;
+}
+
+
 /**
  * Closure for #set_base32().
  */

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



reply via email to

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