-doxygen
[oweals/gnunet.git] / src / transport / plugin_transport_udp.c
index ad87ea68d8ba61b764d2fcda821a35cf1ad05b96..7f442cff0cfedb7cf39b365e5bd1db3140fc2622 100644 (file)
@@ -1,6 +1,6 @@
 /*
      This file is part of GNUnet
-     (C) 2010, 2011 Christian Grothoff (and other contributing authors)
+     (C) 2010-2013 Christian Grothoff (and other contributing authors)
 
      GNUnet is free software; you can redistribute it and/or modify
      it under the terms of the GNU General Public License as published
@@ -42,6 +42,7 @@
 
 #define LOG(kind,...) GNUNET_log_from (kind, "transport-udp", __VA_ARGS__)
 
+#define PLUGIN_NAME "udp"
 
 /**
  * Number of messages we can defragment in parallel.  We only really
  */
 #define UDP_MAX_SENDER_ADDRESSES_WITH_DEFRAG 128
 
+/**
+ * Running pretty printers: head
+ */
+static struct PrettyPrinterContext *ppc_dll_head;
 
+/**
+ * Running pretty printers: tail
+ */
+static struct PrettyPrinterContext *ppc_dll_tail;
 
 /**
  * Closure for 'append_port'.
  */
 struct PrettyPrinterContext
 {
+  /**
+   * DLL
+   */
+  struct PrettyPrinterContext *next;
+
+  /**
+   * DLL
+   */
+  struct PrettyPrinterContext *prev;
+
+  /**
+   * Timeout task
+   */
+  GNUNET_SCHEDULER_TaskIdentifier timeout_task;
+
+  /**
+   * Resolver handle
+   */
+  struct GNUNET_RESOLVER_RequestHandle *resolver_handle;
+
   /**
    * Function to call with the result.
    */
@@ -83,9 +112,30 @@ struct PrettyPrinterContext
    * Port to add after the IP address.
    */
   uint16_t port;
+
+  /**
+   * IPv6 address
+   */
+
+  int ipv6;
+
+  /**
+   * Options
+   */
+  uint32_t options;
 };
 
 
+enum UDP_MessageType
+{
+  UNDEFINED = 0,
+  MSG_FRAGMENTED = 1,
+  MSG_FRAGMENTED_COMPLETE = 2,
+  MSG_UNFRAGMENTED = 3,
+  MSG_ACK = 4,
+  MSG_BEACON = 5
+};
+
 struct Session
 {
   /**
@@ -93,7 +143,7 @@ struct Session
    */
   struct GNUNET_PeerIdentity target;
 
-  struct FragmentationContext * frag_ctx;
+  struct UDP_FragmentationContext * frag_ctx;
 
   /**
    * Address of the other peer
@@ -118,7 +168,12 @@ struct Session
   /**
    * expected delay for ACKs
    */
-  struct GNUNET_TIME_Relative last_expected_delay;
+  struct GNUNET_TIME_Relative last_expected_ack_delay;
+
+  /**
+   * desired delay between UDP messages
+   */
+  struct GNUNET_TIME_Relative last_expected_msg_delay;
 
   struct GNUNET_ATS_Information ats;
 
@@ -128,6 +183,8 @@ struct Session
   unsigned int rc;
 
   int in_destroy;
+
+  int inbound;
 };
 
 
@@ -135,6 +192,7 @@ struct SessionCompareContext
 {
   struct Session *res;
   const struct GNUNET_HELLO_Address *addr;
+  int inbound;
 };
 
 
@@ -225,15 +283,33 @@ struct DefragContext
 
 
 /**
- * Closure for 'process_inbound_tokenized_messages'
+ * Context to send fragmented messages
  */
-struct FragmentationContext
+struct UDP_FragmentationContext
 {
-  struct FragmentationContext * next;
-  struct FragmentationContext * prev;
+  /**
+   * Next in linked list
+   */
+  struct UDP_FragmentationContext * next;
+
+  /**
+   * Previous in linked list
+   */
+  struct UDP_FragmentationContext * prev;
 
+  /**
+   * The plugin
+   */
   struct Plugin * plugin;
+
+  /**
+   * Handle for GNUNET_FRAGMENT context
+   */
   struct GNUNET_FRAGMENT_Context * frag;
+
+  /**
+   * The session this fragmentation context belongs to
+   */
   struct Session * session;
 
   /**
@@ -246,34 +322,87 @@ struct FragmentationContext
    */
   void *cont_cls;
 
+  /**
+   * Message timeout
+   */
   struct GNUNET_TIME_Absolute timeout;
 
-  size_t bytes_to_send;
+  /**
+   * Payload size of original unfragmented message
+   */
+  size_t payload_size;
+
+  /**
+   * Bytes used to send all fragments on wire including UDP overhead
+   */
+  size_t on_wire_size;
+
+  unsigned int fragments_used;
+
 };
 
 
-struct UDPMessageWrapper
+struct UDP_MessageWrapper
 {
+  /**
+   * Session this message belongs to
+   */
   struct Session *session;
-  struct UDPMessageWrapper *prev;
-  struct UDPMessageWrapper *next;
-  char *udp;
 
   /**
-   * Function to call upon completion of the transmission.
+   * DLL of messages
+   * previous element
    */
-  GNUNET_TRANSPORT_TransmitContinuation cont;
+  struct UDP_MessageWrapper *prev;
 
   /**
-   * Closure for 'cont'.
+   * DLL of messages
+   * previous element
    */
-  void *cont_cls;
+  struct UDP_MessageWrapper *next;
 
-  struct FragmentationContext *frag_ctx;
+  /**
+   * Message type
+   * According to UDP_MessageType
+   */
+  int msg_type;
+
+  /**
+   * Message with size msg_size including UDP specific overhead
+   */
+  char *msg_buf;
 
+  /**
+   * Size of UDP message to send including UDP specific overhead
+   */
   size_t msg_size;
 
+  /**
+   * Payload size of original message
+   */
+  size_t payload_size;
+
+  /**
+   * Message timeout
+   */
   struct GNUNET_TIME_Absolute timeout;
+
+  /**
+   * Function to call upon completion of the transmission.
+   */
+  GNUNET_TRANSPORT_TransmitContinuation cont;
+
+  /**
+   * Closure for 'cont'.
+   */
+  void *cont_cls;
+
+  /**
+   * Fragmentation context
+   * frag_ctx == NULL if transport <= MTU
+   * frag_ctx != NULL if transport > MTU
+   */
+  struct UDP_FragmentationContext *frag_ctx;
 };
 
 
@@ -299,6 +428,12 @@ struct UDP_ACK_Message
 
 };
 
+/**
+ * Address options
+ */
+static uint32_t myoptions;
+
+
 /**
  * Encapsulation of all of the state of the plugin.
  */
@@ -330,23 +465,74 @@ udp_plugin_select_v6 (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc);
 
 
 /**
- * Start session timeout
+ * Cancel timeout
  */
 static void
-start_session_timeout (struct Session *s);
+stop_session_timeout (struct Session *s)
+{
+  GNUNET_assert (NULL != s);
+
+  if (GNUNET_SCHEDULER_NO_TASK != s->timeout_task)
+  {
+    GNUNET_SCHEDULER_cancel (s->timeout_task);
+    s->timeout_task = GNUNET_SCHEDULER_NO_TASK;
+    GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+                "Timeout stopped for session %p canceled\n",
+                s);
+  }
+}
 
-/**
- * Increment session timeout due to activity
- */
-static void
-reschedule_session_timeout (struct Session *s);
 
 /**
- * Cancel timeout
+ * (re)schedule select tasks for this plugin.
+ *
+ * @param plugin plugin to reschedule
  */
 static void
-stop_session_timeout (struct Session *s);
+schedule_select (struct Plugin *plugin)
+{
+  struct GNUNET_TIME_Relative min_delay;
+  struct UDP_MessageWrapper *udpw;
+
+  if ((GNUNET_YES == plugin->enable_ipv4) && (NULL != plugin->sockv4))
+  {
+    /* Find a message ready to send:
+     * Flow delay from other peer is expired or not set (0) */
+    min_delay = GNUNET_TIME_UNIT_FOREVER_REL;
+    for (udpw = plugin->ipv4_queue_head; NULL != udpw; udpw = udpw->next)
+      min_delay = GNUNET_TIME_relative_min (min_delay,
+                                           GNUNET_TIME_absolute_get_remaining (udpw->session->flow_delay_from_other_peer));
+
+    if (plugin->select_task != GNUNET_SCHEDULER_NO_TASK)
+      GNUNET_SCHEDULER_cancel(plugin->select_task);
+
+    /* Schedule with:
+     * - write active set if message is ready
+     * - timeout minimum delay */
+    plugin->select_task =
+      GNUNET_SCHEDULER_add_select (GNUNET_SCHEDULER_PRIORITY_DEFAULT,
+                                  (0 == min_delay.rel_value_us) ? GNUNET_TIME_UNIT_FOREVER_REL : min_delay,
+                                  plugin->rs_v4,
+                                  (0 == min_delay.rel_value_us) ? plugin->ws_v4 : NULL,
+                                  &udp_plugin_select, plugin);
+  }
+  if ((GNUNET_YES == plugin->enable_ipv6) && (NULL != plugin->sockv6))
+  {
+    min_delay = GNUNET_TIME_UNIT_FOREVER_REL;
+    for (udpw = plugin->ipv6_queue_head; NULL != udpw; udpw = udpw->next)
+      min_delay = GNUNET_TIME_relative_min (min_delay,
+                                           GNUNET_TIME_absolute_get_remaining (udpw->session->flow_delay_from_other_peer));
 
+    if (GNUNET_SCHEDULER_NO_TASK != plugin->select_task_v6)
+      GNUNET_SCHEDULER_cancel(plugin->select_task_v6);
+    plugin->select_task_v6 =
+      GNUNET_SCHEDULER_add_select (GNUNET_SCHEDULER_PRIORITY_DEFAULT,
+                                  (0 == min_delay.rel_value_us) ? GNUNET_TIME_UNIT_FOREVER_REL : min_delay,
+                                  plugin->rs_v6,
+                                  (0 == min_delay.rel_value_us) ? plugin->ws_v6 : NULL,
+                                  &udp_plugin_select_v6, plugin);
+  }
+}
 
 
 /**
@@ -372,31 +558,35 @@ udp_address_to_string (void *cls, const void *addr, size_t addrlen)
   const struct IPv6UdpAddress *t6;
   int af;
   uint16_t port;
+  uint32_t options;
 
-  if (addrlen == sizeof (struct IPv6UdpAddress))
+  if ((NULL != addr) && (addrlen == sizeof (struct IPv6UdpAddress)))
   {
     t6 = addr;
     af = AF_INET6;
+    options = ntohl (t6->options);
     port = ntohs (t6->u6_port);
     memcpy (&a6, &t6->ipv6_addr, sizeof (a6));
     sb = &a6;
   }
-  else if (addrlen == sizeof (struct IPv4UdpAddress))
+  else if ((NULL != addr) && (addrlen == sizeof (struct IPv4UdpAddress)))
   {
     t4 = addr;
     af = AF_INET;
+    options = ntohl (t4->options);
     port = ntohs (t4->u4_port);
     memcpy (&a4, &t4->ipv4_addr, sizeof (a4));
     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",
-                   buf, port);
+
+  GNUNET_snprintf (rbuf, sizeof (rbuf),
+                  (af == AF_INET6) ? "%s.%u.[%s]:%u" : "%s.%u.%s:%u",
+                   PLUGIN_NAME, options, buf, port);
   return rbuf;
 }
 
@@ -418,29 +608,63 @@ udp_string_to_address (void *cls, const char *addr, uint16_t addrlen,
     void **buf, size_t *added)
 {
   struct sockaddr_storage socket_address;
-  
-  if ((NULL == addr) || (0 == addrlen))
+  char *address;
+  char *plugin;
+  char *optionstr;
+  uint32_t options;
+
+  /* Format tcp.options.address:port */
+  address = NULL;
+  plugin = NULL;
+  optionstr = NULL;
+
+  if ((NULL == addr) || (addrlen == 0))
   {
     GNUNET_break (0);
     return GNUNET_SYSERR;
   }
-
   if ('\0' != addr[addrlen - 1])
   {
+    GNUNET_break (0);
     return GNUNET_SYSERR;
   }
-
   if (strlen (addr) != addrlen - 1)
   {
+    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 (addr, strlen (addr),
-                                                &socket_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:
@@ -448,6 +672,7 @@ udp_string_to_address (void *cls, const char *addr, uint16_t addrlen,
       struct IPv4UdpAddress *u4;
       struct sockaddr_in *in4 = (struct sockaddr_in *) &socket_address;
       u4 = GNUNET_malloc (sizeof (struct IPv4UdpAddress));
+      u4->options =  htonl (options);
       u4->ipv4_addr = in4->sin_addr.s_addr;
       u4->u4_port = in4->sin_port;
       *buf = u4;
@@ -459,6 +684,7 @@ udp_string_to_address (void *cls, const char *addr, uint16_t addrlen,
       struct IPv6UdpAddress *u6;
       struct sockaddr_in6 *in6 = (struct sockaddr_in6 *) &socket_address;
       u6 = GNUNET_malloc (sizeof (struct IPv6UdpAddress));
+      u6->options =  htonl (options);
       u6->ipv6_addr = in6->sin6_addr;
       u6->u6_port = in6->sin6_port;
       *buf = u6;
@@ -472,6 +698,23 @@ udp_string_to_address (void *cls, const char *addr, uint16_t addrlen,
 }
 
 
+static void
+ppc_cancel_task (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
+{
+       struct PrettyPrinterContext *ppc = cls;
+       /* GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "PPC %p was not removed!\n", ppc); */
+       ppc->timeout_task = GNUNET_SCHEDULER_NO_TASK;
+       if (NULL != ppc->resolver_handle)
+       {
+               GNUNET_RESOLVER_request_cancel (ppc->resolver_handle);
+               ppc->resolver_handle = NULL;
+       }
+
+       GNUNET_CONTAINER_DLL_remove (ppc_dll_head, ppc_dll_tail, ppc);
+       GNUNET_free (ppc);
+}
+
+
 /**
  * Append our port and forward the result.
  *
@@ -482,20 +725,39 @@ static void
 append_port (void *cls, const char *hostname)
 {
   struct PrettyPrinterContext *ppc = cls;
+  struct PrettyPrinterContext *cur;
   char *ret;
-
+       /* GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "PPC callback: %p `%s'\n",ppc, hostname); */
   if (hostname == NULL)
   {
     ppc->asc (ppc->asc_cls, NULL);
+    GNUNET_CONTAINER_DLL_remove (ppc_dll_head, ppc_dll_tail, ppc);
+    GNUNET_SCHEDULER_cancel (ppc->timeout_task);
+    ppc->timeout_task = GNUNET_SCHEDULER_NO_TASK;
+    ppc->resolver_handle = NULL;
+       /* GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "PPC %p was removed!\n", ppc); */
     GNUNET_free (ppc);
     return;
   }
-  GNUNET_asprintf (&ret, "%s:%d", hostname, ppc->port);
+  for (cur = ppc_dll_head; (NULL != cur); cur = cur->next)
+  {
+       if (cur == ppc)
+               break;
+  }
+  if (NULL == cur)
+  {
+       GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Invalid callback for PPC %p \n", 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_free (ret);
 }
 
-
 /**
  * Convert the transports address to a nice, human-readable
  * format.
@@ -526,6 +788,7 @@ udp_plugin_address_pretty_printer (void *cls, const char *type,
   const struct IPv4UdpAddress *u4;
   const struct IPv6UdpAddress *u6;
   uint16_t port;
+  uint32_t options;
 
   if (addrlen == sizeof (struct IPv6UdpAddress))
   {
@@ -538,6 +801,7 @@ udp_plugin_address_pretty_printer (void *cls, const char *type,
     a6.sin6_port = u6->u6_port;
     memcpy (&a6.sin6_addr, &u6->ipv6_addr, sizeof (struct in6_addr));
     port = ntohs (u6->u6_port);
+    options = ntohl (u6->options);
     sb = &a6;
     sbs = sizeof (a6);
   }
@@ -552,15 +816,10 @@ udp_plugin_address_pretty_printer (void *cls, const char *type,
     a4.sin_port = u4->u4_port;
     a4.sin_addr.s_addr = u4->ipv4_addr;
     port = ntohs (u4->u4_port);
+    options = ntohl (u4->options);
     sb = &a4;
     sbs = sizeof (a4);
   }
-  else if (0 == addrlen)
-  {
-    asc (asc_cls, "<inbound connection>");
-    asc (asc_cls, NULL);
-    return;
-  }
   else
   {
     /* invalid address */
@@ -572,22 +831,186 @@ udp_plugin_address_pretty_printer (void *cls, const char *type,
   ppc->asc = asc;
   ppc->asc_cls = asc_cls;
   ppc->port = port;
-  GNUNET_RESOLVER_hostname_get (sb, sbs, !numeric, timeout, &append_port, ppc);
+  ppc->options = options;
+  if (addrlen == sizeof (struct IPv6UdpAddress))
+    ppc->ipv6 = GNUNET_YES;
+  else
+    ppc->ipv6 = GNUNET_NO;
+  ppc->timeout_task = GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_relative_multiply(timeout, 2),
+               &ppc_cancel_task, ppc);
+  GNUNET_CONTAINER_DLL_insert (ppc_dll_head, ppc_dll_tail, ppc);
+       /* GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "PPC %p was created!\n", ppc); */
+  ppc->resolver_handle = GNUNET_RESOLVER_hostname_get (sb, sbs, !numeric, timeout, &append_port, ppc);
+
 }
 
 
 static void
-call_continuation (struct UDPMessageWrapper *udpw, int result)
+call_continuation (struct UDP_MessageWrapper *udpw, int result)
 {
+  size_t overhead;
+
   LOG (GNUNET_ERROR_TYPE_DEBUG,
       "Calling continuation for %u byte message to `%s' with result %s\n",
-      udpw->msg_size, GNUNET_i2s (&udpw->session->target),
+      udpw->payload_size, GNUNET_i2s (&udpw->session->target),
       (GNUNET_OK == result) ? "OK" : "SYSERR");
-  if (NULL != udpw->cont)
-  {
-    udpw->cont (udpw->cont_cls, &udpw->session->target,result);
-  }
 
+  if (udpw->msg_size >= udpw->payload_size)
+    overhead = udpw->msg_size - udpw->payload_size;
+  else
+    overhead = udpw->msg_size;
+
+  switch (result) {
+    case GNUNET_OK:
+      switch (udpw->msg_type) {
+        case MSG_UNFRAGMENTED:
+          if (NULL != udpw->cont)
+          {
+            /* Transport continuation */
+            udpw->cont (udpw->cont_cls, &udpw->session->target, result,
+                      udpw->payload_size, udpw->msg_size);
+          }
+          GNUNET_STATISTICS_update (plugin->env->stats,
+                                    "# UDP, unfragmented msgs, messages, sent, success",
+                                    1, GNUNET_NO);
+          GNUNET_STATISTICS_update (plugin->env->stats,
+                                    "# UDP, unfragmented msgs, bytes payload, sent, success",
+                                    udpw->payload_size, GNUNET_NO);
+          GNUNET_STATISTICS_update (plugin->env->stats,
+                                    "# UDP, unfragmented msgs, bytes overhead, sent, success",
+                                    overhead, GNUNET_NO);
+          GNUNET_STATISTICS_update (plugin->env->stats,
+                                    "# UDP, total, bytes overhead, sent",
+                                    overhead, GNUNET_NO);
+          GNUNET_STATISTICS_update (plugin->env->stats,
+                                    "# UDP, total, bytes payload, sent",
+                                    udpw->payload_size, GNUNET_NO);
+          break;
+        case MSG_FRAGMENTED_COMPLETE:
+          GNUNET_assert (NULL != udpw->frag_ctx);
+          if (udpw->frag_ctx->cont != NULL)
+            udpw->frag_ctx->cont (udpw->frag_ctx->cont_cls, &udpw->session->target, GNUNET_OK,
+                               udpw->frag_ctx->payload_size, udpw->frag_ctx->on_wire_size);
+          GNUNET_STATISTICS_update (plugin->env->stats,
+                                    "# UDP, fragmented msgs, messages, sent, success",
+                                    1, GNUNET_NO);
+          GNUNET_STATISTICS_update (plugin->env->stats,
+                                    "# UDP, fragmented msgs, bytes payload, sent, success",
+                                    udpw->payload_size, GNUNET_NO);
+          GNUNET_STATISTICS_update (plugin->env->stats,
+                                    "# UDP, fragmented msgs, bytes overhead, sent, success",
+                                    overhead, GNUNET_NO);
+          GNUNET_STATISTICS_update (plugin->env->stats,
+                                    "# UDP, total, bytes overhead, sent",
+                                    overhead, GNUNET_NO);
+          GNUNET_STATISTICS_update (plugin->env->stats,
+                                    "# UDP, total, bytes payload, sent",
+                                    udpw->payload_size, GNUNET_NO);
+          GNUNET_STATISTICS_update (plugin->env->stats,
+                                    "# UDP, fragmented msgs, messages, pending",
+                                    -1, GNUNET_NO);
+          break;
+        case MSG_FRAGMENTED:
+          /* Fragmented message: enqueue next fragment */
+          if (NULL != udpw->cont)
+            udpw->cont (udpw->cont_cls, &udpw->session->target, result,
+                      udpw->payload_size, udpw->msg_size);
+          GNUNET_STATISTICS_update (plugin->env->stats,
+                                    "# UDP, fragmented msgs, fragments, sent, success",
+                                    1, GNUNET_NO);
+          GNUNET_STATISTICS_update (plugin->env->stats,
+                                    "# UDP, fragmented msgs, fragments bytes, sent, success",
+                                    udpw->msg_size, GNUNET_NO);
+          break;
+        case MSG_ACK:
+          /* No continuation */
+          GNUNET_STATISTICS_update (plugin->env->stats,
+                                    "# UDP, ACK msgs, messages, sent, success",
+                                    1, GNUNET_NO);
+          GNUNET_STATISTICS_update (plugin->env->stats,
+                                    "# UDP, ACK msgs, bytes overhead, sent, success",
+                                    overhead, GNUNET_NO);
+          GNUNET_STATISTICS_update (plugin->env->stats,
+                                    "# UDP, total, bytes overhead, sent",
+                                    overhead, GNUNET_NO);
+          break;
+        case MSG_BEACON:
+          GNUNET_break (0);
+          break;
+        default:
+          LOG (GNUNET_ERROR_TYPE_ERROR,
+              "ERROR: %u\n", udpw->msg_type);
+          GNUNET_break (0);
+          break;
+      }
+      break;
+    case GNUNET_SYSERR:
+      switch (udpw->msg_type) {
+        case MSG_UNFRAGMENTED:
+          /* Unfragmented message: failed to send */
+          if (NULL != udpw->cont)
+            udpw->cont (udpw->cont_cls, &udpw->session->target, result,
+                      udpw->payload_size, overhead);
+          GNUNET_STATISTICS_update (plugin->env->stats,
+                                  "# UDP, unfragmented msgs, messages, sent, failure",
+                                  1, GNUNET_NO);
+          GNUNET_STATISTICS_update (plugin->env->stats,
+                                    "# UDP, unfragmented msgs, bytes payload, sent, failure",
+                                    udpw->payload_size, GNUNET_NO);
+          GNUNET_STATISTICS_update (plugin->env->stats,
+                                    "# UDP, unfragmented msgs, bytes overhead, sent, failure",
+                                    overhead, GNUNET_NO);
+          break;
+        case MSG_FRAGMENTED_COMPLETE:
+          GNUNET_assert (NULL != udpw->frag_ctx);
+          if (udpw->frag_ctx->cont != NULL)
+            udpw->frag_ctx->cont (udpw->frag_ctx->cont_cls, &udpw->session->target, GNUNET_SYSERR,
+                               udpw->frag_ctx->payload_size, udpw->frag_ctx->on_wire_size);
+          GNUNET_STATISTICS_update (plugin->env->stats,
+                                    "# UDP, fragmented msgs, messages, sent, failure",
+                                    1, GNUNET_NO);
+          GNUNET_STATISTICS_update (plugin->env->stats,
+                                    "# UDP, fragmented msgs, bytes payload, sent, failure",
+                                    udpw->payload_size, GNUNET_NO);
+          GNUNET_STATISTICS_update (plugin->env->stats,
+                                    "# UDP, fragmented msgs, bytes payload, sent, failure",
+                                    overhead, GNUNET_NO);
+          GNUNET_STATISTICS_update (plugin->env->stats,
+                                    "# UDP, fragmented msgs, bytes payload, sent, failure",
+                                    overhead, GNUNET_NO);
+          GNUNET_STATISTICS_update (plugin->env->stats,
+                                    "# UDP, fragmented msgs, messages, pending",
+                                    -1, GNUNET_NO);
+          break;
+        case MSG_FRAGMENTED:
+          GNUNET_assert (NULL != udpw->frag_ctx);
+          /* Fragmented message: failed to send */
+          GNUNET_STATISTICS_update (plugin->env->stats,
+                                    "# UDP, fragmented msgs, fragments, sent, failure",
+                                    1, GNUNET_NO);
+          GNUNET_STATISTICS_update (plugin->env->stats,
+                                    "# UDP, fragmented msgs, fragments bytes, sent, failure",
+                                    udpw->msg_size, GNUNET_NO);
+          break;
+        case MSG_ACK:
+          /* ACK message: failed to send */
+          GNUNET_STATISTICS_update (plugin->env->stats,
+                                    "# UDP, ACK msgs, messages, sent, failure",
+                                    1, GNUNET_NO);
+          break;
+        case MSG_BEACON:
+          /* Beacon message: failed to send */
+          GNUNET_break (0);
+          break;
+        default:
+          GNUNET_break (0);
+          break;
+      }
+      break;
+    default:
+      GNUNET_break (0);
+      break;
+  }
 }
 
 
@@ -635,7 +1058,6 @@ udp_plugin_check_address (void *cls, const void *addr, size_t addrlen)
   if ((addrlen != sizeof (struct IPv4UdpAddress)) &&
       (addrlen != sizeof (struct IPv6UdpAddress)))
   {
-    GNUNET_break_op (0);
     return GNUNET_SYSERR;
   }
   if (addrlen == sizeof (struct IPv4UdpAddress))
@@ -675,9 +1097,9 @@ udp_plugin_check_address (void *cls, const void *addr, size_t addrlen)
 static void
 free_session (struct Session *s)
 {
-  if (s->frag_ctx != NULL)
+  if (NULL != s->frag_ctx)
   {
-    GNUNET_FRAGMENT_context_destroy(s->frag_ctx->frag);
+    GNUNET_FRAGMENT_context_destroy(s->frag_ctx->frag, NULL, NULL);
     GNUNET_free (s->frag_ctx);
     s->frag_ctx = NULL;
   }
@@ -685,6 +1107,95 @@ free_session (struct Session *s)
 }
 
 
+static void
+dequeue (struct Plugin *plugin, struct UDP_MessageWrapper * udpw)
+{
+  if (plugin->bytes_in_buffer < udpw->msg_size)
+      GNUNET_break (0);
+  else
+  {
+    GNUNET_STATISTICS_update (plugin->env->stats,
+                              "# UDP, total, bytes in buffers",
+                              - (long long) udpw->msg_size, GNUNET_NO);
+    plugin->bytes_in_buffer -= udpw->msg_size;
+  }
+  GNUNET_STATISTICS_update (plugin->env->stats,
+                            "# UDP, total, msgs in buffers",
+                            -1, GNUNET_NO);
+  if (udpw->session->addrlen == sizeof (struct sockaddr_in))
+    GNUNET_CONTAINER_DLL_remove (plugin->ipv4_queue_head,
+                                 plugin->ipv4_queue_tail, udpw);
+  if (udpw->session->addrlen == sizeof (struct sockaddr_in6))
+    GNUNET_CONTAINER_DLL_remove (plugin->ipv6_queue_head,
+                                 plugin->ipv6_queue_tail, udpw);
+}
+
+
+static void
+fragmented_message_done (struct UDP_FragmentationContext *fc, int result)
+{
+  struct UDP_MessageWrapper *udpw;
+  struct UDP_MessageWrapper *tmp;
+  struct UDP_MessageWrapper dummy;
+  struct Session *s = fc->session;
+
+  LOG (GNUNET_ERROR_TYPE_DEBUG,
+       "%p : Fragmented message removed with result %s\n",
+       fc,
+       (result == GNUNET_SYSERR) ? "FAIL" : "SUCCESS");
+
+  /* Call continuation for fragmented message */
+  memset (&dummy, 0, sizeof (dummy));
+  dummy.msg_type = MSG_FRAGMENTED_COMPLETE;
+  dummy.msg_size = s->frag_ctx->on_wire_size;
+  dummy.payload_size = s->frag_ctx->payload_size;
+  dummy.frag_ctx = s->frag_ctx;
+  dummy.cont = NULL;
+  dummy.cont_cls = NULL;
+  dummy.session = s;
+
+  call_continuation (&dummy, result);
+
+  /* Remove leftover fragments from queue */
+  if (s->addrlen == sizeof (struct sockaddr_in6))
+  {
+    udpw = plugin->ipv6_queue_head;
+    while (NULL != udpw)
+    {
+      tmp = udpw->next;
+      if ((udpw->frag_ctx != NULL) && (udpw->frag_ctx == s->frag_ctx))
+      {
+        dequeue (plugin, udpw);
+        call_continuation (udpw, GNUNET_SYSERR);
+        GNUNET_free (udpw);
+      }
+      udpw = tmp;
+    }
+  }
+  if (s->addrlen == sizeof (struct sockaddr_in))
+  {
+    udpw = plugin->ipv4_queue_head;
+    while (udpw!= NULL)
+    {
+      tmp = udpw->next;
+      if ((NULL != udpw->frag_ctx) && (udpw->frag_ctx == s->frag_ctx))
+      {
+        dequeue (plugin, udpw);
+        call_continuation (udpw, GNUNET_SYSERR);
+        GNUNET_free (udpw);
+      }
+      udpw = tmp;
+    }
+  }
+
+  /* Destroy fragmentation context */
+  GNUNET_FRAGMENT_context_destroy (fc->frag,
+                                     &s->last_expected_msg_delay,
+                                     &s->last_expected_ack_delay);
+  s->frag_ctx = NULL;
+  GNUNET_free (fc );
+}
+
 /**
  * Functions with this signature are called whenever we need
  * to close a session due to a disconnect or failure to
@@ -695,8 +1206,8 @@ free_session (struct Session *s)
 static void
 disconnect_session (struct Session *s)
 {
-  struct UDPMessageWrapper *udpw;
-  struct UDPMessageWrapper *next;
+  struct UDP_MessageWrapper *udpw;
+  struct UDP_MessageWrapper *next;
 
   GNUNET_assert (GNUNET_YES != s->in_destroy);
   LOG (GNUNET_ERROR_TYPE_DEBUG,
@@ -704,14 +1215,21 @@ disconnect_session (struct Session *s)
          s,
          GNUNET_i2s (&s->target),
          GNUNET_a2s (s->sock_addr, s->addrlen));
-  stop_session_timeout(s);
+  stop_session_timeout (s);
+
+  if (NULL != s->frag_ctx)
+  {
+    /* Remove fragmented message due to disconnect */
+    fragmented_message_done (s->frag_ctx, GNUNET_SYSERR);
+  }
+
   next = plugin->ipv4_queue_head;
   while (NULL != (udpw = next))
   {
     next = udpw->next;
     if (udpw->session == s)
     {
-      GNUNET_CONTAINER_DLL_remove(plugin->ipv4_queue_head, plugin->ipv4_queue_tail, udpw);
+      dequeue (plugin, udpw);
       call_continuation(udpw, GNUNET_SYSERR);
       GNUNET_free (udpw);
     }
@@ -722,11 +1240,10 @@ disconnect_session (struct Session *s)
     next = udpw->next;
     if (udpw->session == s)
     {
-      GNUNET_CONTAINER_DLL_remove(plugin->ipv6_queue_head, plugin->ipv6_queue_tail, udpw);
+      dequeue (plugin, udpw);
       call_continuation(udpw, GNUNET_SYSERR);
       GNUNET_free (udpw);
     }
-    udpw = next;
   }
   plugin->env->session_end (plugin->env->cls, &s->target, s);
 
@@ -734,7 +1251,8 @@ disconnect_session (struct Session *s)
   {
     if (NULL != s->frag_ctx->cont)
     {
-      s->frag_ctx->cont (s->frag_ctx->cont_cls, &s->target, GNUNET_SYSERR);
+      s->frag_ctx->cont (s->frag_ctx->cont_cls, &s->target, GNUNET_SYSERR,
+                         s->frag_ctx->payload_size, s->frag_ctx->on_wire_size);
       LOG (GNUNET_ERROR_TYPE_DEBUG,
           "Calling continuation for fragemented message to `%s' with result SYSERR\n",
           GNUNET_i2s (&s->target));
@@ -742,12 +1260,12 @@ disconnect_session (struct Session *s)
   }
 
   GNUNET_assert (GNUNET_YES ==
-                 GNUNET_CONTAINER_multihashmap_remove (plugin->sessions,
-                                                       &s->target.hashPubKey,
+                 GNUNET_CONTAINER_multipeermap_remove (plugin->sessions,
+                                                       &s->target,
                                                        s));
   GNUNET_STATISTICS_set(plugin->env->stats,
-                        "# UDP sessions active",
-                        GNUNET_CONTAINER_multihashmap_size(plugin->sessions),
+                        "# UDP, sessions active",
+                        GNUNET_CONTAINER_multipeermap_size(plugin->sessions),
                         GNUNET_NO);
   if (s->rc > 0)
     s->in_destroy = GNUNET_YES;
@@ -764,7 +1282,7 @@ disconnect_session (struct Session *s)
  * @return GNUNET_OK (continue to iterate)
  */
 static int
-disconnect_and_free_it (void *cls, const GNUNET_HashCode * key, void *value)
+disconnect_and_free_it (void *cls, const struct GNUNET_PeerIdentity * key, void *value)
 {
   disconnect_session(value);
   return GNUNET_OK;
@@ -788,31 +1306,102 @@ udp_disconnect (void *cls, const struct GNUNET_PeerIdentity *target)
   LOG (GNUNET_ERROR_TYPE_DEBUG,
        "Disconnecting from peer `%s'\n", GNUNET_i2s (target));
   /* Clean up sessions */
-  GNUNET_CONTAINER_multihashmap_get_multiple (plugin->sessions, &target->hashPubKey, &disconnect_and_free_it, plugin);
+  GNUNET_CONTAINER_multipeermap_get_multiple (plugin->sessions, target,
+                                             &disconnect_and_free_it, plugin);
 }
 
 
-static struct Session *
-create_session (struct Plugin *plugin, const struct GNUNET_PeerIdentity *target,
-                const void *addr, size_t addrlen,
-                GNUNET_TRANSPORT_TransmitContinuation cont, void *cont_cls)
+/**
+ * Session was idle, so disconnect it
+ */
+static void
+session_timeout (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
 {
-  struct Session *s;
-  const struct IPv4UdpAddress *t4;
-  const struct IPv6UdpAddress *t6;
-  struct sockaddr_in *v4;
-  struct sockaddr_in6 *v6;
-  size_t len;
+  GNUNET_assert (NULL != cls);
+  struct Session *s = cls;
 
-  switch (addrlen)
-  {
-  case sizeof (struct IPv4UdpAddress):
-    if (NULL == plugin->sockv4)
-    {
-      return NULL;
-    }
-    t4 = addr;
-    s = GNUNET_malloc (sizeof (struct Session) + sizeof (struct sockaddr_in));
+  s->timeout_task = GNUNET_SCHEDULER_NO_TASK;
+  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+              "Session %p was idle for %s, disconnecting\n",
+              s,
+             GNUNET_STRINGS_relative_time_to_string (GNUNET_CONSTANTS_IDLE_CONNECTION_TIMEOUT,
+                                                     GNUNET_YES));
+  /* call session destroy function */
+  disconnect_session (s);
+}
+
+
+/**
+ * Start session timeout
+ */
+static void
+start_session_timeout (struct Session *s)
+{
+  GNUNET_assert (NULL != s);
+  GNUNET_assert (GNUNET_SCHEDULER_NO_TASK == s->timeout_task);
+  s->timeout_task =  GNUNET_SCHEDULER_add_delayed (GNUNET_CONSTANTS_IDLE_CONNECTION_TIMEOUT,
+                                                   &session_timeout,
+                                                   s);
+  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+              "Timeout for session %p set to %s\n",
+              s,
+             GNUNET_STRINGS_relative_time_to_string (GNUNET_CONSTANTS_IDLE_CONNECTION_TIMEOUT,
+                                                     GNUNET_YES));
+}
+
+
+/**
+ * Increment session timeout due to activity
+ */
+static void
+reschedule_session_timeout (struct Session *s)
+{
+  GNUNET_assert (NULL != s);
+  GNUNET_assert (GNUNET_SCHEDULER_NO_TASK != s->timeout_task);
+
+  GNUNET_SCHEDULER_cancel (s->timeout_task);
+  s->timeout_task =  GNUNET_SCHEDULER_add_delayed (GNUNET_CONSTANTS_IDLE_CONNECTION_TIMEOUT,
+                                                   &session_timeout,
+                                                   s);
+  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+              "Timeout rescheduled for session %p set to %s\n",
+              s,
+             GNUNET_STRINGS_relative_time_to_string (GNUNET_CONSTANTS_IDLE_CONNECTION_TIMEOUT,
+                                                     GNUNET_YES));
+}
+
+
+static struct Session *
+create_session (struct Plugin *plugin, const struct GNUNET_PeerIdentity *target,
+                const void *addr, size_t addrlen,
+                GNUNET_TRANSPORT_TransmitContinuation cont, void *cont_cls)
+{
+  struct Session *s;
+  const struct IPv4UdpAddress *t4;
+  const struct IPv6UdpAddress *t6;
+  struct sockaddr_in *v4;
+  struct sockaddr_in6 *v6;
+  size_t len;
+
+  if (NULL == addr)
+  {
+       GNUNET_break (0);
+       return NULL;
+  }
+
+  switch (addrlen)
+  {
+  case sizeof (struct IPv4UdpAddress):
+    if (NULL == plugin->sockv4)
+    {
+      LOG (GNUNET_ERROR_TYPE_DEBUG,
+           "Could not create session for peer `%s' address `%s': IPv4 is not enabled\n",
+           GNUNET_i2s(target),
+           udp_address_to_string(NULL, addr, addrlen));
+      return NULL;
+    }
+    t4 = addr;
+    s = GNUNET_malloc (sizeof (struct Session) + sizeof (struct sockaddr_in));
     len = sizeof (struct sockaddr_in);
     v4 = (struct sockaddr_in *) &s[1];
     v4->sin_family = AF_INET;
@@ -826,11 +1415,14 @@ create_session (struct Plugin *plugin, const struct GNUNET_PeerIdentity *target,
   case sizeof (struct IPv6UdpAddress):
     if (NULL == plugin->sockv6)
     {
+      LOG (GNUNET_ERROR_TYPE_INFO,
+           "Could not create session for peer `%s' address `%s': IPv6 is not enabled\n",
+           GNUNET_i2s(target),
+           udp_address_to_string(NULL, addr, addrlen));
       return NULL;
     }
     t6 = addr;
-    s =
-        GNUNET_malloc (sizeof (struct Session) + sizeof (struct sockaddr_in6));
+    s = GNUNET_malloc (sizeof (struct Session) + sizeof (struct sockaddr_in6));
     len = sizeof (struct sockaddr_in6);
     v6 = (struct sockaddr_in6 *) &s[1];
     v6->sin6_family = AF_INET6;
@@ -843,21 +1435,28 @@ create_session (struct Plugin *plugin, const struct GNUNET_PeerIdentity *target,
     break;
   default:
     /* Must have a valid address to send to */
-    GNUNET_break_op (0);
+    GNUNET_STATISTICS_update (plugin->env->stats,
+                              gettext_noop
+                              ("# requests to create session with invalid address"),
+                              1, GNUNET_NO);
     return NULL;
   }
   s->addrlen = len;
   s->target = *target;
   s->sock_addr = (const struct sockaddr *) &s[1];
-  s->last_expected_delay = GNUNET_TIME_UNIT_SECONDS;
-  start_session_timeout(s);
+  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_ABS;
+  s->flow_delay_for_other_peer = GNUNET_TIME_UNIT_ZERO;
+  s->inbound = GNUNET_NO;
+  start_session_timeout (s);
   return s;
 }
 
 
 static int
 session_cmp_it (void *cls,
-               const GNUNET_HashCode * key,
+               const struct GNUNET_PeerIdentity * key,
                void *value)
 {
   struct SessionCompareContext * cctx = cls;
@@ -866,14 +1465,19 @@ session_cmp_it (void *cls,
 
   socklen_t s_addrlen = s->addrlen;
 
-  LOG (GNUNET_ERROR_TYPE_DEBUG, "Comparing  address %s <-> %s\n",
-      udp_address_to_string (NULL, (void *) address->address, address->address_length),
-      GNUNET_a2s (s->sock_addr, s->addrlen));
+  LOG (GNUNET_ERROR_TYPE_DEBUG,
+       "Comparing address %s <-> %s\n",
+       udp_address_to_string (NULL, (void *) address->address,
+                             address->address_length),
+       GNUNET_a2s (s->sock_addr, s->addrlen));
+  if (s->inbound != cctx->inbound)
+    return GNUNET_YES;
   if ((address->address_length == sizeof (struct IPv4UdpAddress)) &&
       (s_addrlen == sizeof (struct sockaddr_in)))
   {
     struct IPv4UdpAddress * u4 = NULL;
     u4 = (struct IPv4UdpAddress *) address->address;
+    GNUNET_assert (NULL != u4);
     const struct sockaddr_in *s4 = (const struct sockaddr_in *) s->sock_addr;
     if ((0 == memcmp ((const void *) &u4->ipv4_addr,(const void *) &s4->sin_addr, sizeof (struct in_addr))) &&
         (u4->u4_port == s4->sin_port))
@@ -888,6 +1492,7 @@ session_cmp_it (void *cls,
   {
     struct IPv6UdpAddress * u6 = NULL;
     u6 = (struct IPv6UdpAddress *) address->address;
+    GNUNET_assert (NULL != u6);
     const struct sockaddr_in6 *s6 = (const struct sockaddr_in6 *) s->sock_addr;
     if ((0 == memcmp (&u6->ipv6_addr, &s6->sin6_addr, sizeof (struct in6_addr))) &&
         (u6->u6_port == s6->sin6_port))
@@ -900,19 +1505,35 @@ session_cmp_it (void *cls,
 }
 
 
+/**
+ * Function obtain the network type for a session
+ *
+ * @param cls closure ('struct Plugin*')
+ * @param session the session
+ * @return the network type in HBO or GNUNET_SYSERR
+ */
+static enum GNUNET_ATS_Network_Type
+udp_get_network (void *cls,
+                struct Session *session)
+{
+  return ntohl (session->ats.value);
+}
+
+
 /**
  * Creates a new outbound session the transport service will use to send data to the
  * peer
  *
  * @param cls the plugin
  * @param address the address
+ * @param inbound look for inbound session
  * @return the session or NULL of max connections exceeded
  */
 static struct Session *
-udp_plugin_get_session (void *cls,
-                  const struct GNUNET_HELLO_Address *address)
+udp_plugin_lookup_session (void *cls,
+                          const struct GNUNET_HELLO_Address *address,
+                          int inbound)
 {
-  struct Session * s = NULL;
   struct Plugin * plugin = cls;
   struct IPv6UdpAddress * udp_a6;
   struct IPv4UdpAddress * udp_a4;
@@ -925,7 +1546,11 @@ udp_plugin_get_session (void *cls,
       ((address->address_length != sizeof (struct IPv4UdpAddress)) &&
       (address->address_length != sizeof (struct IPv6UdpAddress))))
   {
-    GNUNET_break (0);
+    LOG (GNUNET_ERROR_TYPE_WARNING,
+       _("Trying to create session for address of unexpected length %u (should be %u or %u)\n"),
+       address->address_length,
+       sizeof (struct IPv4UdpAddress),
+       sizeof (struct IPv6UdpAddress));
     return NULL;
   }
 
@@ -951,54 +1576,110 @@ udp_plugin_get_session (void *cls,
   struct SessionCompareContext cctx;
   cctx.addr = address;
   cctx.res = NULL;
+  cctx.inbound = inbound;
   LOG (GNUNET_ERROR_TYPE_DEBUG,
-       "Looking for existing session for peer `%s' `%s' \n", 
-       GNUNET_i2s (&address->peer), 
+       "Looking for existing session for peer `%s' `%s' \n",
+       GNUNET_i2s (&address->peer),
        udp_address_to_string(NULL, address->address, address->address_length));
-  GNUNET_CONTAINER_multihashmap_get_multiple(plugin->sessions, &address->peer.hashPubKey, session_cmp_it, &cctx);
+  GNUNET_CONTAINER_multipeermap_get_multiple(plugin->sessions, &address->peer, session_cmp_it, &cctx);
   if (cctx.res != NULL)
   {
     LOG (GNUNET_ERROR_TYPE_DEBUG, "Found existing session %p\n", cctx.res);
     return cctx.res;
   }
+  return NULL;
+}
+
+
+static struct Session *
+udp_plugin_create_session (void *cls,
+                          const struct GNUNET_HELLO_Address *address,
+                          int inbound)
+{
+  struct Session *s;
 
-  /* otherwise create new */
   s = create_session (plugin,
-      &address->peer,
-      address->address,
-      address->address_length,
-      NULL, NULL);
+                     &address->peer,
+                     address->address,
+                     address->address_length,
+                     NULL, NULL);
+  if (NULL == s)
+    return NULL; /* protocol not supported or address invalid */
+  s->inbound = inbound;
   LOG (GNUNET_ERROR_TYPE_DEBUG,
-       "Creating new session %p for peer `%s' address `%s'\n",
+       "Creating new %s session %p for peer `%s' address `%s'\n",
+       (GNUNET_YES == s->inbound) ? "inbound" : "outbound",
        s,
        GNUNET_i2s(&address->peer),
        udp_address_to_string(NULL,address->address,address->address_length));
   GNUNET_assert (GNUNET_OK ==
-                 GNUNET_CONTAINER_multihashmap_put (plugin->sessions,
-                                                    &s->target.hashPubKey,
+                 GNUNET_CONTAINER_multipeermap_put (plugin->sessions,
+                                                    &s->target,
                                                     s,
                                                     GNUNET_CONTAINER_MULTIHASHMAPOPTION_MULTIPLE));
-
   GNUNET_STATISTICS_set(plugin->env->stats,
-                        "# UDP sessions active",
-                        GNUNET_CONTAINER_multihashmap_size(plugin->sessions),
+                        "# UDP, sessions active",
+                        GNUNET_CONTAINER_multipeermap_size(plugin->sessions),
                         GNUNET_NO);
-
   return s;
 }
 
 
-static void 
-enqueue (struct Plugin *plugin, struct UDPMessageWrapper * udpw)
+/**
+ * Creates a new outbound session the transport service will use to send data to the
+ * peer
+ *
+ * @param cls the plugin
+ * @param address the address
+ * @return the session or NULL of max connections exceeded
+ */
+static struct Session *
+udp_plugin_get_session (void *cls,
+                       const struct GNUNET_HELLO_Address *address)
 {
+  struct Session *s;
+
+  if (NULL == address)
+  {
+    GNUNET_break (0);
+    return NULL;
+  }
+  if ((address->address_length != sizeof (struct IPv4UdpAddress)) &&
+      (address->address_length != sizeof (struct IPv6UdpAddress)))
+    return NULL;
 
+  /* otherwise create new */
+  if (NULL != (s = udp_plugin_lookup_session (cls, address, GNUNET_NO)))
+    return s;
+  return udp_plugin_create_session (cls, address, GNUNET_NO);
+}
+
+
+static void
+enqueue (struct Plugin *plugin, struct UDP_MessageWrapper * udpw)
+{
+  if (plugin->bytes_in_buffer + udpw->msg_size > INT64_MAX)
+      GNUNET_break (0);
+  else
+  {
+    GNUNET_STATISTICS_update (plugin->env->stats,
+                              "# UDP, total, bytes in buffers",
+                              udpw->msg_size, GNUNET_NO);
+    plugin->bytes_in_buffer += udpw->msg_size;
+  }
+  GNUNET_STATISTICS_update (plugin->env->stats,
+                            "# UDP, total, msgs in buffers",
+                            1, GNUNET_NO);
   if (udpw->session->addrlen == sizeof (struct sockaddr_in))
-    GNUNET_CONTAINER_DLL_insert(plugin->ipv4_queue_head, plugin->ipv4_queue_tail, udpw);
+    GNUNET_CONTAINER_DLL_insert (plugin->ipv4_queue_head,
+                                 plugin->ipv4_queue_tail, udpw);
   if (udpw->session->addrlen == sizeof (struct sockaddr_in6))
-    GNUNET_CONTAINER_DLL_insert(plugin->ipv6_queue_head, plugin->ipv6_queue_tail, udpw);
+    GNUNET_CONTAINER_DLL_insert (plugin->ipv6_queue_head,
+                                 plugin->ipv6_queue_tail, udpw);
 }
 
 
+
 /**
  * Fragment message was transmitted via UDP, let fragmentation know
  * to send the next fragment now.
@@ -1006,15 +1687,17 @@ enqueue (struct Plugin *plugin, struct UDPMessageWrapper * udpw)
  * @param cls the 'struct UDPMessageWrapper' of the fragment
  * @param target destination peer (ignored)
  * @param result GNUNET_OK on success (ignored)
+ * @param payload bytes payload sent
+ * @param physical bytes physical sent
  */
 static void
 send_next_fragment (void *cls,
                    const struct GNUNET_PeerIdentity *target,
-                   int result)
+                   int result, size_t payload, size_t physical)
 {
-  struct UDPMessageWrapper *udpw = cls;
+  struct UDP_MessageWrapper *udpw = cls;
 
-  GNUNET_FRAGMENT_context_transmission_done (udpw->frag_ctx->frag);  
+  GNUNET_FRAGMENT_context_transmission_done (udpw->frag_ctx->frag);
 }
 
 
@@ -1030,59 +1713,27 @@ send_next_fragment (void *cls,
 static void
 enqueue_fragment (void *cls, const struct GNUNET_MessageHeader *msg)
 {
-  struct FragmentationContext *frag_ctx = cls;
+  struct UDP_FragmentationContext *frag_ctx = cls;
   struct Plugin *plugin = frag_ctx->plugin;
-  struct UDPMessageWrapper * udpw;
-  struct Session *s;
+  struct UDP_MessageWrapper * udpw;
   size_t msg_len = ntohs (msg->size);
 
-  LOG (GNUNET_ERROR_TYPE_DEBUG, 
-       "Enqueuing fragment with %u bytes %u\n", msg_len , sizeof (struct UDPMessageWrapper));
-  udpw = GNUNET_malloc (sizeof (struct UDPMessageWrapper) + msg_len);
+  LOG (GNUNET_ERROR_TYPE_DEBUG,
+       "Enqueuing fragment with %u bytes\n", msg_len);
+  frag_ctx->fragments_used ++;
+  udpw = GNUNET_malloc (sizeof (struct UDP_MessageWrapper) + msg_len);
   udpw->session = frag_ctx->session;
-  s = udpw->session;
-  udpw->udp = (char *) &udpw[1];
-
+  udpw->msg_buf = (char *) &udpw[1];
   udpw->msg_size = msg_len;
+  udpw->payload_size = msg_len; /*FIXME: minus fragment overhead */
   udpw->cont = &send_next_fragment;
   udpw->cont_cls = udpw;
   udpw->timeout = frag_ctx->timeout;
   udpw->frag_ctx = frag_ctx;
-  memcpy (udpw->udp, msg, msg_len);
+  udpw->msg_type = MSG_FRAGMENTED;
+  memcpy (udpw->msg_buf, msg, msg_len);
   enqueue (plugin, udpw);
-
-  if (s->addrlen == sizeof (struct sockaddr_in))
-  {
-    if (plugin->with_v4_ws == GNUNET_NO)
-    {
-      if (plugin->select_task != GNUNET_SCHEDULER_NO_TASK)
-        GNUNET_SCHEDULER_cancel(plugin->select_task);
-
-      plugin->select_task =
-          GNUNET_SCHEDULER_add_select (GNUNET_SCHEDULER_PRIORITY_DEFAULT,
-                                       GNUNET_TIME_UNIT_FOREVER_REL,
-                                       plugin->rs_v4,
-                                       plugin->ws_v4,
-                                       &udp_plugin_select, plugin);
-      plugin->with_v4_ws = GNUNET_YES;
-    }
-  }
-  else if (s->addrlen == sizeof (struct sockaddr_in6))
-  {
-    if (plugin->with_v6_ws == GNUNET_NO)
-    {
-      if (plugin->select_task_v6 != GNUNET_SCHEDULER_NO_TASK)
-        GNUNET_SCHEDULER_cancel(plugin->select_task_v6);
-
-      plugin->select_task_v6 =
-          GNUNET_SCHEDULER_add_select (GNUNET_SCHEDULER_PRIORITY_DEFAULT,
-                                       GNUNET_TIME_UNIT_FOREVER_REL,
-                                       plugin->rs_v6,
-                                       plugin->ws_v6,
-                                       &udp_plugin_select_v6, plugin);
-      plugin->with_v6_ws = GNUNET_YES;
-    }
-  }
+  schedule_select (plugin);
 }
 
 
@@ -1122,10 +1773,11 @@ udp_plugin_send (void *cls,
                   GNUNET_TRANSPORT_TransmitContinuation cont, void *cont_cls)
 {
   struct Plugin *plugin = cls;
-  size_t mlen = msgbuf_size + sizeof (struct UDPMessage);
-  struct UDPMessageWrapper * udpw;
+  size_t udpmlen = msgbuf_size + sizeof (struct UDPMessage);
+  struct UDP_FragmentationContext * frag_ctx;
+  struct UDP_MessageWrapper * udpw;
   struct UDPMessage *udp;
-  char mbuf[mlen];
+  char mbuf[udpmlen];
   GNUNET_assert (plugin != NULL);
   GNUNET_assert (s != NULL);
 
@@ -1133,105 +1785,90 @@ udp_plugin_send (void *cls,
     return GNUNET_SYSERR;
   if ((s->addrlen == sizeof (struct sockaddr_in)) && (plugin->sockv4 == NULL))
     return GNUNET_SYSERR;
-  if (mlen >= GNUNET_SERVER_MAX_MESSAGE_SIZE)
+  if (udpmlen >= GNUNET_SERVER_MAX_MESSAGE_SIZE)
   {
     GNUNET_break (0);
     return GNUNET_SYSERR;
   }
-  if (GNUNET_YES != GNUNET_CONTAINER_multihashmap_contains_value(plugin->sessions, &s->target.hashPubKey, s))
+  if (GNUNET_YES != GNUNET_CONTAINER_multipeermap_contains_value(plugin->sessions, &s->target, s))
   {
     GNUNET_break (0);
     return GNUNET_SYSERR;
   }
   LOG (GNUNET_ERROR_TYPE_DEBUG,
        "UDP transmits %u-byte message to `%s' using address `%s'\n",
-       mlen,
+       udpmlen,
        GNUNET_i2s (&s->target),
        GNUNET_a2s(s->sock_addr, s->addrlen));
-  
+
+
   /* Message */
   udp = (struct UDPMessage *) mbuf;
-  udp->header.size = htons (mlen);
+  udp->header.size = htons (udpmlen);
   udp->header.type = htons (GNUNET_MESSAGE_TYPE_TRANSPORT_UDP_MESSAGE);
   udp->reserved = htonl (0);
   udp->sender = *plugin->env->my_identity;
 
   reschedule_session_timeout(s);
-  if (mlen <= UDP_MTU)
+  if (udpmlen <= UDP_MTU)
   {
-    udpw = GNUNET_malloc (sizeof (struct UDPMessageWrapper) + mlen);
+    /* unfragmented message */
+    udpw = GNUNET_malloc (sizeof (struct UDP_MessageWrapper) + udpmlen);
     udpw->session = s;
-    udpw->udp = (char *) &udpw[1];
-    udpw->msg_size = mlen;
+    udpw->msg_buf = (char *) &udpw[1];
+    udpw->msg_size = udpmlen; /* message size with UDP overhead */
+    udpw->payload_size = msgbuf_size; /* message size without UDP overhead */
     udpw->timeout = GNUNET_TIME_absolute_add(GNUNET_TIME_absolute_get(), to);
     udpw->cont = cont;
     udpw->cont_cls = cont_cls;
     udpw->frag_ctx = NULL;
-    memcpy (udpw->udp, udp, sizeof (struct UDPMessage));
-    memcpy (&udpw->udp[sizeof (struct UDPMessage)], msgbuf, msgbuf_size);
-
+    udpw->msg_type = MSG_UNFRAGMENTED;
+    memcpy (udpw->msg_buf, udp, sizeof (struct UDPMessage));
+    memcpy (&udpw->msg_buf[sizeof (struct UDPMessage)], msgbuf, msgbuf_size);
     enqueue (plugin, udpw);
+
+    GNUNET_STATISTICS_update (plugin->env->stats,
+                              "# UDP, unfragmented msgs, messages, attempt",
+                              1, GNUNET_NO);
+    GNUNET_STATISTICS_update (plugin->env->stats,
+                              "# UDP, unfragmented msgs, bytes payload, attempt",
+                              udpw->payload_size, GNUNET_NO);
   }
   else
   {
-    LOG (GNUNET_ERROR_TYPE_DEBUG,
-         "UDP has to fragment message \n");
+    /* fragmented message */
     if  (s->frag_ctx != NULL)
       return GNUNET_SYSERR;
     memcpy (&udp[1], msgbuf, msgbuf_size);
-    struct FragmentationContext * frag_ctx = GNUNET_malloc(sizeof (struct FragmentationContext));
-
+    frag_ctx = GNUNET_malloc (sizeof (struct UDP_FragmentationContext));
     frag_ctx->plugin = plugin;
     frag_ctx->session = s;
     frag_ctx->cont = cont;
     frag_ctx->cont_cls = cont_cls;
     frag_ctx->timeout = GNUNET_TIME_absolute_add(GNUNET_TIME_absolute_get(), to);
-    frag_ctx->bytes_to_send = mlen;
+    frag_ctx->payload_size = msgbuf_size; /* unfragmented message size without UDP overhead */
+    frag_ctx->on_wire_size = 0; /* bytes with UDP and fragmentation overhead */
     frag_ctx->frag = GNUNET_FRAGMENT_context_create (plugin->env->stats,
-              UDP_MTU,
-              &plugin->tracker,
-              s->last_expected_delay,
-              &udp->header,
-              &enqueue_fragment,
-              frag_ctx);
-
+                                                    UDP_MTU,
+                                                    &plugin->tracker,
+                                                    s->last_expected_msg_delay,
+                                                    s->last_expected_ack_delay,
+                                                    &udp->header,
+                                                    &enqueue_fragment,
+                                                    frag_ctx);
     s->frag_ctx = frag_ctx;
-  }
-
-  if (s->addrlen == sizeof (struct sockaddr_in))
-  {
-    if (plugin->with_v4_ws == GNUNET_NO)
-    {
-      if (plugin->select_task != GNUNET_SCHEDULER_NO_TASK)
-        GNUNET_SCHEDULER_cancel(plugin->select_task);
-
-      plugin->select_task =
-          GNUNET_SCHEDULER_add_select (GNUNET_SCHEDULER_PRIORITY_DEFAULT,
-                                       GNUNET_TIME_UNIT_FOREVER_REL,
-                                       plugin->rs_v4,
-                                       plugin->ws_v4,
-                                       &udp_plugin_select, plugin);
-      plugin->with_v4_ws = GNUNET_YES;
-    }
-  }
-  else if (s->addrlen == sizeof (struct sockaddr_in6))
-  {
-    if (plugin->with_v6_ws == GNUNET_NO)
-    {
-      if (plugin->select_task_v6 != GNUNET_SCHEDULER_NO_TASK)
-        GNUNET_SCHEDULER_cancel(plugin->select_task_v6);
-
-      plugin->select_task_v6 =
-        GNUNET_SCHEDULER_add_select (GNUNET_SCHEDULER_PRIORITY_DEFAULT,
-                                     GNUNET_TIME_UNIT_FOREVER_REL,
-                                     plugin->rs_v6,
-                                     plugin->ws_v6,
-                                     &udp_plugin_select_v6, plugin);
-      plugin->with_v6_ws = GNUNET_YES;
-    }
-  }
-
-  return mlen;
+    GNUNET_STATISTICS_update (plugin->env->stats,
+                              "# UDP, fragmented msgs, messages, pending",
+                              1, GNUNET_NO);
+    GNUNET_STATISTICS_update (plugin->env->stats,
+                              "# UDP, fragmented msgs, messages, attempt",
+                              1, GNUNET_NO);
+    GNUNET_STATISTICS_update (plugin->env->stats,
+                              "# UDP, fragmented msgs, bytes payload, attempt",
+                              frag_ctx->payload_size, GNUNET_NO);
+  }
+  schedule_select (plugin);
+  return udpmlen;
 }
 
 
@@ -1254,30 +1891,43 @@ udp_nat_port_map_callback (void *cls, int add_remove,
   void *arg;
   size_t args;
 
+  LOG (GNUNET_ERROR_TYPE_INFO,
+       "NAT notification to %s address `%s'\n",
+       (GNUNET_YES == add_remove) ? "add" : "remove",
+       GNUNET_a2s (addr, addrlen));
+
   /* convert 'addr' to our internal format */
   switch (addr->sa_family)
   {
   case AF_INET:
     GNUNET_assert (addrlen == sizeof (struct sockaddr_in));
+    memset (&u4, 0, sizeof (u4));
+    u4.options = htonl(myoptions);
     u4.ipv4_addr = ((struct sockaddr_in *) addr)->sin_addr.s_addr;
     u4.u4_port = ((struct sockaddr_in *) addr)->sin_port;
+    if (0 == ((struct sockaddr_in *) addr)->sin_port)
+       return;
     arg = &u4;
-    args = sizeof (u4);
+    args = sizeof (struct IPv4UdpAddress);
     break;
   case AF_INET6:
     GNUNET_assert (addrlen == sizeof (struct sockaddr_in6));
+    memset (&u4, 0, sizeof (u4));
+    u6.options = htonl(myoptions);
+    if (0 == ((struct sockaddr_in6 *) addr)->sin6_port)
+       return;
     memcpy (&u6.ipv6_addr, &((struct sockaddr_in6 *) addr)->sin6_addr,
             sizeof (struct in6_addr));
     u6.u6_port = ((struct sockaddr_in6 *) addr)->sin6_port;
     arg = &u6;
-    args = sizeof (u6);
+    args = sizeof (struct IPv6UdpAddress);
     break;
   default:
     GNUNET_break (0);
     return;
   }
   /* modify our published address list */
-  plugin->env->notify_address (plugin->env->cls, add_remove, arg, args);
+  plugin->env->notify_address (plugin->env->cls, add_remove, arg, args, "udp");
 }
 
 
@@ -1296,24 +1946,27 @@ process_inbound_tokenized_messages (void *cls, void *client,
 {
   struct Plugin *plugin = cls;
   struct SourceInformation *si = client;
-  struct GNUNET_ATS_Information ats[2];
   struct GNUNET_TIME_Relative delay;
 
   GNUNET_assert (si->session != NULL);
   if (GNUNET_YES == si->session->in_destroy)
     return GNUNET_OK;
   /* setup ATS */
-  ats[0].type = htonl (GNUNET_ATS_QUALITY_NET_DISTANCE);
-  ats[0].value = htonl (1);
-  ats[1] = si->session->ats;
-  GNUNET_break (ntohl(ats[1].value) != GNUNET_ATS_NET_UNSPECIFIED);
+  GNUNET_break (ntohl(si->session->ats.value) != GNUNET_ATS_NET_UNSPECIFIED);
   delay = plugin->env->receive (plugin->env->cls,
                                &si->sender,
                                hdr,
-                               (const struct GNUNET_ATS_Information *) &ats, 2,
-                               NULL,
-                               si->arg,
-                               si->args);
+                               si->session,
+                (GNUNET_YES == si->session->inbound) ? NULL : si->arg,
+                (GNUNET_YES == si->session->inbound) ? 0 : si->args);
+
+  plugin->env->update_address_metrics (plugin->env->cls,
+                                      &si->sender,
+                                        (GNUNET_YES == si->session->inbound) ? NULL : si->arg,
+                                        (GNUNET_YES == si->session->inbound) ? 0 : si->args,
+                                      si->session,
+                                      &si->session->ats, 1);
+
   si->session->flow_delay_for_other_peer = delay;
   reschedule_session_timeout(si->session);
   return GNUNET_OK;
@@ -1357,6 +2010,8 @@ process_udp_message (struct Plugin *plugin, const struct UDPMessage *msg,
   {
   case AF_INET:
     GNUNET_assert (sender_addr_len == sizeof (struct sockaddr_in));
+    memset (&u4, 0, sizeof (u4));
+    u6.options = htonl (0);
     u4.ipv4_addr = ((struct sockaddr_in *) sender_addr)->sin_addr.s_addr;
     u4.u4_port = ((struct sockaddr_in *) sender_addr)->sin_port;
     arg = &u4;
@@ -1364,6 +2019,8 @@ process_udp_message (struct Plugin *plugin, const struct UDPMessage *msg,
     break;
   case AF_INET6:
     GNUNET_assert (sender_addr_len == sizeof (struct sockaddr_in6));
+    memset (&u6, 0, sizeof (u6));
+    u6.options = htonl (0);
     u6.ipv6_addr = ((struct sockaddr_in6 *) sender_addr)->sin6_addr;
     u6.u6_port = ((struct sockaddr_in6 *) sender_addr)->sin6_port;
     arg = &u6;
@@ -1379,7 +2036,12 @@ process_udp_message (struct Plugin *plugin, const struct UDPMessage *msg,
        GNUNET_a2s (sender_addr, sender_addr_len));
 
   struct GNUNET_HELLO_Address * address = GNUNET_HELLO_address_allocate(&msg->sender, "udp", arg, args);
-  s = udp_plugin_get_session(plugin, address);
+  if (NULL == (s = udp_plugin_lookup_session (plugin, address, GNUNET_YES)))
+  {
+    s = udp_plugin_create_session (plugin, address, GNUNET_YES);
+    plugin->env->session_start (NULL, &address->peer, PLUGIN_NAME,
+                  address->address, address->address_length, s, NULL, 0);
+  }
   GNUNET_free (address);
 
   /* iterate over all embedded messages */
@@ -1452,25 +2114,39 @@ fragment_msg_proc (void *cls, const struct GNUNET_MessageHeader *msg)
 
 struct LookupContext
 {
-  const struct sockaddr * addr;
-
   struct Session *res;
 
+  const struct sockaddr * addr;
+
   size_t addrlen;
+
+  int must_have_frag_ctx;
 };
 
 
 static int
-lookup_session_by_addr_it (void *cls, const GNUNET_HashCode * key, void *value)
+lookup_session_by_addr_it (void *cls,
+                          const struct GNUNET_PeerIdentity *key,
+                          void *value)
 {
   struct LookupContext *l_ctx = cls;
   struct Session * s = value;
 
+  if ((GNUNET_YES == l_ctx->must_have_frag_ctx) && (NULL == s->frag_ctx))
+    return GNUNET_YES;
+
+  /*
+  GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Comparing session: have %s %s %p<-> want %s\n",
+      GNUNET_a2s(s->sock_addr, s->addrlen),
+      (GNUNET_YES == s->inbound) ? "inbound" : "outbound",
+      s->frag_ctx,
+      GNUNET_a2s(l_ctx->addr, l_ctx->addrlen));
+   */
   if ((s->addrlen == l_ctx->addrlen) &&
       (0 == memcmp (s->sock_addr, l_ctx->addr, s->addrlen)))
   {
     l_ctx->res = s;
-    return GNUNET_NO;
+    return GNUNET_YES;
   }
   return GNUNET_YES;
 }
@@ -1490,14 +2166,15 @@ ack_proc (void *cls, uint32_t id, const struct GNUNET_MessageHeader *msg)
   size_t msize = sizeof (struct UDP_ACK_Message) + ntohs (msg->size);
   struct UDP_ACK_Message *udp_ack;
   uint32_t delay = 0;
-  struct UDPMessageWrapper *udpw;
+  struct UDP_MessageWrapper *udpw;
   struct Session *s;
-
   struct LookupContext l_ctx;
+
   l_ctx.addr = rc->src_addr;
   l_ctx.addrlen = rc->addr_len;
   l_ctx.res = NULL;
-  GNUNET_CONTAINER_multihashmap_iterate (rc->plugin->sessions,
+  l_ctx.must_have_frag_ctx = GNUNET_NO;
+  GNUNET_CONTAINER_multipeermap_iterate (rc->plugin->sessions,
       &lookup_session_by_addr_it,
       &l_ctx);
   s = l_ctx.res;
@@ -1505,36 +2182,38 @@ ack_proc (void *cls, uint32_t id, const struct GNUNET_MessageHeader *msg)
   if (NULL == s)
     return;
 
-  if (s->flow_delay_for_other_peer.rel_value <= UINT32_MAX)
-    delay = s->flow_delay_for_other_peer.rel_value;
+  if (s->flow_delay_for_other_peer.rel_value_us <= UINT32_MAX)
+    delay = s->flow_delay_for_other_peer.rel_value_us;
 
   LOG (GNUNET_ERROR_TYPE_DEBUG,
-       "Sending ACK to `%s' including delay of %u ms\n",
+       "Sending ACK to `%s' including delay of %s\n",
        GNUNET_a2s (rc->src_addr,
                    (rc->src_addr->sa_family ==
                     AF_INET) ? sizeof (struct sockaddr_in) : sizeof (struct
                                                                      sockaddr_in6)),
-       delay);
-  udpw = GNUNET_malloc (sizeof (struct UDPMessageWrapper) + msize);
+       GNUNET_STRINGS_relative_time_to_string (s->flow_delay_for_other_peer,
+                                              GNUNET_YES));
+  udpw = GNUNET_malloc (sizeof (struct UDP_MessageWrapper) + msize);
   udpw->msg_size = msize;
+  udpw->payload_size = 0;
   udpw->session = s;
   udpw->timeout = GNUNET_TIME_UNIT_FOREVER_ABS;
-  udpw->udp = (char *)&udpw[1];
-  udp_ack = (struct UDP_ACK_Message *) udpw->udp;
+  udpw->msg_buf = (char *)&udpw[1];
+  udpw->msg_type = MSG_ACK;
+  udp_ack = (struct UDP_ACK_Message *) udpw->msg_buf;
   udp_ack->header.size = htons ((uint16_t) msize);
   udp_ack->header.type = htons (GNUNET_MESSAGE_TYPE_TRANSPORT_UDP_ACK);
   udp_ack->delay = htonl (delay);
   udp_ack->sender = *rc->plugin->env->my_identity;
   memcpy (&udp_ack[1], msg, ntohs (msg->size));
-
   enqueue (rc->plugin, udpw);
 }
 
 
-static void 
+static void
 read_process_msg (struct Plugin *plugin,
                  const struct GNUNET_MessageHeader *msg,
-                 const char *addr,
+                 const struct sockaddr *addr,
                  socklen_t fromlen)
 {
   if (ntohs (msg->size) < sizeof (struct UDPMessage))
@@ -1543,14 +2222,14 @@ read_process_msg (struct Plugin *plugin,
     return;
   }
   process_udp_message (plugin, (const struct UDPMessage *) msg,
-                       (const struct sockaddr *) addr, fromlen);
+                       addr, fromlen);
 }
 
 
-static void 
+static void
 read_process_ack (struct Plugin *plugin,
                  const struct GNUNET_MessageHeader *msg,
-                 char *addr,
+                 const struct sockaddr *addr,
                  socklen_t fromlen)
 {
   const struct GNUNET_MessageHeader *ack;
@@ -1569,17 +2248,22 @@ read_process_ack (struct Plugin *plugin,
   l_ctx.addr = (const struct sockaddr *) addr;
   l_ctx.addrlen = fromlen;
   l_ctx.res = NULL;
-  GNUNET_CONTAINER_multihashmap_iterate (plugin->sessions,
-      &lookup_session_by_addr_it,
-      &l_ctx);
+  l_ctx.must_have_frag_ctx = GNUNET_YES;
+  GNUNET_CONTAINER_multipeermap_iterate (plugin->sessions,
+                                        &lookup_session_by_addr_it,
+                                        &l_ctx);
   s = l_ctx.res;
-
-  if ((s == NULL) || (s->frag_ctx == NULL))
+  if ((NULL == s) || (NULL == s->frag_ctx))
+  {
     return;
+  }
+
 
-  flow_delay.rel_value = (uint64_t) ntohl (udp_ack->delay);
-  LOG (GNUNET_ERROR_TYPE_DEBUG, "We received a sending delay of %llu\n",
-       flow_delay.rel_value);
+  flow_delay.rel_value_us = (uint64_t) ntohl (udp_ack->delay);
+  LOG (GNUNET_ERROR_TYPE_DEBUG,
+       "We received a sending delay of %s\n",
+       GNUNET_STRINGS_relative_time_to_string (flow_delay,
+                                              GNUNET_YES));
   s->flow_delay_from_other_peer =
       GNUNET_TIME_relative_to_absolute (flow_delay);
 
@@ -1591,69 +2275,32 @@ read_process_ack (struct Plugin *plugin,
     return;
   }
 
+  if (0 != memcmp (&l_ctx.res->target, &udp_ack->sender, sizeof (struct GNUNET_PeerIdentity)))
+    GNUNET_break (0);
   if (GNUNET_OK != GNUNET_FRAGMENT_process_ack (s->frag_ctx->frag, ack))
   {
     LOG (GNUNET_ERROR_TYPE_DEBUG,
         "UDP processes %u-byte acknowledgement from `%s' at `%s'\n",
         (unsigned int) ntohs (msg->size), GNUNET_i2s (&udp_ack->sender),
-        GNUNET_a2s ((const struct sockaddr *) addr, fromlen));
+        GNUNET_a2s (addr, fromlen));
+    /* Expect more ACKs to arrive */
     return;
   }
 
   LOG (GNUNET_ERROR_TYPE_DEBUG,
-       "FULL MESSAGE ACKed\n",
+       "Message full ACK'ed\n",
        (unsigned int) ntohs (msg->size), GNUNET_i2s (&udp_ack->sender),
-       GNUNET_a2s ((const struct sockaddr *) addr, fromlen));
-  s->last_expected_delay = GNUNET_FRAGMENT_context_destroy (s->frag_ctx->frag);
-
-  struct UDPMessageWrapper * udpw;
-  struct UDPMessageWrapper * tmp;
-  if (s->addrlen == sizeof (struct sockaddr_in6))
-  {
-    udpw = plugin->ipv6_queue_head;
-    while (NULL != udpw)
-    {
-      tmp = udpw->next;
-      if ((udpw->frag_ctx != NULL) && (udpw->frag_ctx == s->frag_ctx))
-      {
-        GNUNET_CONTAINER_DLL_remove(plugin->ipv6_queue_head, plugin->ipv6_queue_tail, udpw);
-        GNUNET_free (udpw);
-      }
-      udpw = tmp;
-    }
-  }
-  if (s->addrlen == sizeof (struct sockaddr_in))
-  {
-    udpw = plugin->ipv4_queue_head;
-    while (udpw!= NULL)
-    {
-      tmp = udpw->next;
-      if ((udpw->frag_ctx != NULL) && (udpw->frag_ctx == s->frag_ctx))
-      {
-        GNUNET_CONTAINER_DLL_remove(plugin->ipv4_queue_head, plugin->ipv4_queue_tail, udpw);
-        GNUNET_free (udpw);
-      }
-      udpw = tmp;
-    }
-  }
+       GNUNET_a2s (addr, fromlen));
 
-  if (s->frag_ctx->cont != NULL)
-  {
-    LOG (GNUNET_ERROR_TYPE_DEBUG,
-        "Calling continuation for fragmented message to `%s' with result %s\n",
-        GNUNET_i2s (&s->target), "OK");
-    s->frag_ctx->cont (s->frag_ctx->cont_cls, &udp_ack->sender, GNUNET_OK);
-  }
-
-  GNUNET_free (s->frag_ctx);
-  s->frag_ctx = NULL;
+  /* Remove fragmented message after successful sending */
+  fragmented_message_done (s->frag_ctx, GNUNET_OK);
 }
 
 
-static void 
+static void
 read_process_fragment (struct Plugin *plugin,
                       const struct GNUNET_MessageHeader *msg,
-                      char *addr,
+                      const struct sockaddr *addr,
                       socklen_t fromlen)
 {
   struct DefragContext *d_ctx;
@@ -1661,12 +2308,12 @@ read_process_fragment (struct Plugin *plugin,
   struct FindReceiveContext frc;
 
   frc.rc = NULL;
-  frc.addr = (const struct sockaddr *) addr;
+  frc.addr = addr;
   frc.addr_len = fromlen;
 
   LOG (GNUNET_ERROR_TYPE_DEBUG, "UDP processes %u-byte fragment from `%s'\n",
        (unsigned int) ntohs (msg->size),
-       GNUNET_a2s ((const struct sockaddr *) addr, fromlen));
+       GNUNET_a2s (addr, fromlen));
   /* Lookup existing receive context for this address */
   GNUNET_CONTAINER_heap_iterate (plugin->defrag_ctxs,
                                  &find_receive_context,
@@ -1689,18 +2336,18 @@ read_process_fragment (struct Plugin *plugin,
     d_ctx->hnode =
         GNUNET_CONTAINER_heap_insert (plugin->defrag_ctxs, d_ctx,
                                       (GNUNET_CONTAINER_HeapCostType)
-                                      now.abs_value);
-    LOG (GNUNET_ERROR_TYPE_DEBUG, 
+                                      now.abs_value_us);
+    LOG (GNUNET_ERROR_TYPE_DEBUG,
         "Created new defragmentation context for %u-byte fragment from `%s'\n",
         (unsigned int) ntohs (msg->size),
-        GNUNET_a2s ((const struct sockaddr *) addr, fromlen));
+        GNUNET_a2s (addr, fromlen));
   }
   else
   {
     LOG (GNUNET_ERROR_TYPE_DEBUG,
         "Found existing defragmentation context for %u-byte fragment from `%s'\n",
         (unsigned int) ntohs (msg->size),
-        GNUNET_a2s ((const struct sockaddr *) addr, fromlen));
+        GNUNET_a2s (addr, fromlen));
   }
 
   if (GNUNET_OK == GNUNET_DEFRAGMENT_process_fragment (d_ctx->defrag, msg))
@@ -1708,7 +2355,7 @@ read_process_fragment (struct Plugin *plugin,
     /* keep this 'rc' from expiring */
     GNUNET_CONTAINER_heap_update_cost (plugin->defrag_ctxs, d_ctx->hnode,
                                        (GNUNET_CONTAINER_HeapCostType)
-                                       now.abs_value);
+                                       now.abs_value_us);
   }
   if (GNUNET_CONTAINER_heap_get_size (plugin->defrag_ctxs) >
       UDP_MAX_SENDER_ADDRESSES_WITH_DEFRAG)
@@ -1732,7 +2379,7 @@ static void
 udp_select_read (struct Plugin *plugin, struct GNUNET_NETWORK_Handle *rsock)
 {
   socklen_t fromlen;
-  char addr[32];
+  struct sockaddr_storage addr;
   char buf[65536] GNUNET_ALIGN;
   ssize_t size;
   const struct GNUNET_MessageHeader *msg;
@@ -1740,25 +2387,45 @@ udp_select_read (struct Plugin *plugin, struct GNUNET_NETWORK_Handle *rsock)
   fromlen = sizeof (addr);
   memset (&addr, 0, sizeof (addr));
   size = GNUNET_NETWORK_socket_recvfrom (rsock, buf, sizeof (buf),
-                                      (struct sockaddr *) &addr, &fromlen);
+                                         (struct sockaddr *) &addr, &fromlen);
 #if MINGW
   /* On SOCK_DGRAM UDP sockets recvfrom might fail with a
-   * WSAECONNRESET error to indicate that previous sendto() (???)
+   * 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 UDP-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) || (size < sizeof (struct GNUNET_MessageHeader)))
+  if (-1 == size)
+  {
+    LOG (GNUNET_ERROR_TYPE_DEBUG,
+        "UDP failed to receive data: %s\n", STRERROR (errno));
+    /* Connection failure or something. Not a protocol violation. */
+    return;
+  }
+  if (size < sizeof (struct GNUNET_MessageHeader))
   {
+    LOG (GNUNET_ERROR_TYPE_WARNING,
+        "UDP got %u bytes, which is not enough for a GNUnet message header\n",
+        (unsigned int) size);
+    /* _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,
-       "UDP received %u-byte message from `%s' type %i\n", (unsigned int) size,
-       GNUNET_a2s ((const struct sockaddr *) addr, fromlen), ntohs (msg->type));
+       "UDP received %u-byte message from `%s' type %u\n",
+       (unsigned int) size,
+       GNUNET_a2s ((const struct sockaddr *) &addr, fromlen),
+       ntohs (msg->type));
 
   if (size != ntohs (msg->size))
   {
@@ -1766,24 +2433,28 @@ udp_select_read (struct Plugin *plugin, struct GNUNET_NETWORK_Handle *rsock)
     return;
   }
 
+  GNUNET_STATISTICS_update (plugin->env->stats,
+                            "# UDP, total, bytes, received",
+                            size, GNUNET_NO);
+
   switch (ntohs (msg->type))
   {
   case GNUNET_MESSAGE_TYPE_TRANSPORT_BROADCAST_BEACON:
-    udp_broadcast_receive (plugin, &buf, size, addr, fromlen);
+    udp_broadcast_receive (plugin, buf, size,
+                           (const struct sockaddr *) &addr, fromlen);
     return;
-
   case GNUNET_MESSAGE_TYPE_TRANSPORT_UDP_MESSAGE:
-    read_process_msg (plugin, msg, addr, fromlen);
+    read_process_msg (plugin, msg,
+                      (const struct sockaddr *) &addr, fromlen);
     return;
-
   case GNUNET_MESSAGE_TYPE_TRANSPORT_UDP_ACK:
-    read_process_ack (plugin, msg, addr, fromlen);
+    read_process_ack (plugin, msg,
+                      (const struct sockaddr *) &addr, fromlen);
     return;
-
   case GNUNET_MESSAGE_TYPE_FRAGMENT:
-    read_process_fragment (plugin, msg, addr, fromlen);
+    read_process_fragment (plugin, msg,
+                           (const struct sockaddr *) &addr, fromlen);
     return;
-
   default:
     GNUNET_break_op (0);
     return;
@@ -1791,141 +2462,223 @@ udp_select_read (struct Plugin *plugin, struct GNUNET_NETWORK_Handle *rsock)
 }
 
 
-static size_t
-udp_select_send (struct Plugin *plugin, struct GNUNET_NETWORK_Handle *sock)
+static struct UDP_MessageWrapper *
+remove_timeout_messages_and_select (struct UDP_MessageWrapper *head,
+                                    struct GNUNET_NETWORK_Handle *sock)
 {
-  ssize_t sent;
-  size_t slen;
-  struct GNUNET_TIME_Absolute max;
-  struct UDPMessageWrapper *udpw = NULL;
-
-  if (sock == plugin->sockv4)
-  {
-    udpw = plugin->ipv4_queue_head;
-  }
-  else if (sock == plugin->sockv6)
-  {
-    udpw = plugin->ipv6_queue_head;
-  }
-  else
-  {
-    GNUNET_break (0);
-    return 0;
-  }
-
-  const struct sockaddr * sa = udpw->session->sock_addr;
-  slen = udpw->session->addrlen;
-
-  max = GNUNET_TIME_absolute_max(udpw->timeout, GNUNET_TIME_absolute_get());
+  struct UDP_MessageWrapper *udpw = NULL;
+  struct GNUNET_TIME_Relative remaining;
 
+  udpw = head;
   while (udpw != NULL)
   {
-    if (max.abs_value != udpw->timeout.abs_value)
+    /* Find messages with timeout */
+    remaining = GNUNET_TIME_absolute_get_remaining (udpw->timeout);
+    if (GNUNET_TIME_UNIT_ZERO.rel_value_us == remaining.rel_value_us)
     {
       /* Message timed out */
-      call_continuation(udpw, GNUNET_SYSERR);
-      if (udpw->frag_ctx != NULL)
-      {
-        LOG (GNUNET_ERROR_TYPE_DEBUG,
-            "Fragmented message for peer `%s' with size %u timed out\n",
-            GNUNET_i2s(&udpw->session->target), udpw->frag_ctx->bytes_to_send);
-        udpw->session->last_expected_delay = GNUNET_FRAGMENT_context_destroy(udpw->frag_ctx->frag);
-        GNUNET_free (udpw->frag_ctx);
-        udpw->session->frag_ctx = NULL;
-      }
-      else
-      {
-        LOG (GNUNET_ERROR_TYPE_DEBUG, 
-            "Message for peer `%s' with size %u timed out\n",
-            GNUNET_i2s(&udpw->session->target), udpw->msg_size);
+      switch (udpw->msg_type) {
+        case MSG_UNFRAGMENTED:
+          GNUNET_STATISTICS_update (plugin->env->stats,
+                                    "# UDP, total, bytes, sent, timeout",
+                                    udpw->msg_size, GNUNET_NO);
+          GNUNET_STATISTICS_update (plugin->env->stats,
+                                    "# UDP, total, messages, sent, timeout",
+                                    1, GNUNET_NO);
+          GNUNET_STATISTICS_update (plugin->env->stats,
+                                    "# UDP, unfragmented msgs, messages, sent, timeout",
+                                    1, GNUNET_NO);
+          GNUNET_STATISTICS_update (plugin->env->stats,
+                                    "# UDP, unfragmented msgs, bytes, sent, timeout",
+                                    udpw->payload_size, GNUNET_NO);
+          /* Not fragmented message */
+          LOG (GNUNET_ERROR_TYPE_DEBUG,
+               "Message for peer `%s' with size %u timed out\n",
+               GNUNET_i2s(&udpw->session->target), udpw->payload_size);
+          call_continuation (udpw, GNUNET_SYSERR);
+          /* Remove message */
+          dequeue (plugin, udpw);
+          GNUNET_free (udpw);
+          break;
+        case MSG_FRAGMENTED:
+          /* Fragmented message */
+          GNUNET_STATISTICS_update (plugin->env->stats,
+                                    "# UDP, total, bytes, sent, timeout",
+                                    udpw->frag_ctx->on_wire_size, GNUNET_NO);
+          GNUNET_STATISTICS_update (plugin->env->stats,
+                                    "# UDP, total, messages, sent, timeout",
+                                    1, GNUNET_NO);
+          call_continuation (udpw, GNUNET_SYSERR);
+          LOG (GNUNET_ERROR_TYPE_DEBUG,
+               "Fragment for message for peer `%s' with size %u timed out\n",
+               GNUNET_i2s(&udpw->session->target), udpw->frag_ctx->payload_size);
+
+
+          GNUNET_STATISTICS_update (plugin->env->stats,
+                                    "# UDP, fragmented msgs, messages, sent, timeout",
+                                    1, GNUNET_NO);
+          GNUNET_STATISTICS_update (plugin->env->stats,
+                                    "# UDP, fragmented msgs, bytes, sent, timeout",
+                                    udpw->frag_ctx->payload_size, GNUNET_NO);
+          /* Remove fragmented message due to timeout */
+          fragmented_message_done (udpw->frag_ctx, GNUNET_SYSERR);
+          break;
+        case MSG_ACK:
+          GNUNET_STATISTICS_update (plugin->env->stats,
+                                    "# UDP, total, bytes, sent, timeout",
+                                    udpw->msg_size, GNUNET_NO);
+          GNUNET_STATISTICS_update (plugin->env->stats,
+                                    "# UDP, total, messages, sent, timeout",
+                                    1, GNUNET_NO);
+          LOG (GNUNET_ERROR_TYPE_DEBUG,
+               "ACK Message for peer `%s' with size %u timed out\n",
+               GNUNET_i2s(&udpw->session->target), udpw->payload_size);
+          call_continuation (udpw, GNUNET_SYSERR);
+          dequeue (plugin, udpw);
+          GNUNET_free (udpw);
+          break;
+        default:
+          break;
       }
-
       if (sock == plugin->sockv4)
-      {
-        GNUNET_CONTAINER_DLL_remove(plugin->ipv4_queue_head, plugin->ipv4_queue_tail, udpw);
-        GNUNET_free (udpw);
         udpw = plugin->ipv4_queue_head;
-      }
       else if (sock == plugin->sockv6)
-      {
-        GNUNET_CONTAINER_DLL_remove(plugin->ipv6_queue_head, plugin->ipv6_queue_tail, udpw);
-        GNUNET_free (udpw);
         udpw = plugin->ipv6_queue_head;
+      else
+      {
+        GNUNET_break (0); /* should never happen */
+        udpw = NULL;
       }
+      GNUNET_STATISTICS_update (plugin->env->stats,
+                                "# messages dismissed due to timeout",
+                                1, GNUNET_NO);
     }
     else
     {
-      struct GNUNET_TIME_Relative delta = GNUNET_TIME_absolute_get_remaining (udpw->session->flow_delay_from_other_peer);
-      if (delta.rel_value == 0)
+      /* Message did not time out, check flow delay */
+      remaining = GNUNET_TIME_absolute_get_remaining (udpw->session->flow_delay_from_other_peer);
+      if (GNUNET_TIME_UNIT_ZERO.rel_value_us == remaining.rel_value_us)
       {
         /* this message is not delayed */
-        LOG (GNUNET_ERROR_TYPE_DEBUG, 
-            "Message for peer `%s' (%u bytes) is not delayed \n",
-            GNUNET_i2s(&udpw->session->target), udpw->msg_size);
-        break;
+        LOG (GNUNET_ERROR_TYPE_DEBUG,
+             "Message for peer `%s' (%u bytes) is not delayed \n",
+             GNUNET_i2s(&udpw->session->target), udpw->payload_size);
+        break; /* Found message to send, break */
       }
       else
       {
-        /* this message is delayed, try next */
+        /* Message is delayed, try next */
         LOG (GNUNET_ERROR_TYPE_DEBUG,
-            "Message for peer `%s' (%u bytes) is delayed for %llu \n",
-            GNUNET_i2s(&udpw->session->target), udpw->msg_size,
-            delta);
+             "Message for peer `%s' (%u bytes) is delayed for %s\n",
+             GNUNET_i2s(&udpw->session->target), udpw->payload_size,
+            GNUNET_STRINGS_relative_time_to_string (remaining,
+                                                    GNUNET_YES));
         udpw = udpw->next;
       }
     }
   }
+  return udpw;
+}
 
-  if (udpw == NULL)
-  {
-    /* No message left */
-    return 0;
-  }
 
-  sent = GNUNET_NETWORK_socket_sendto (sock, udpw->udp, udpw->msg_size, sa, slen);
+static void
+analyze_send_error (struct Plugin *plugin,
+                    const struct sockaddr * sa,
+                    socklen_t slen,
+                    int error)
+{
+  static int network_down_error;
+  struct GNUNET_ATS_Information type;
+
+ type = plugin->env->get_address_type (plugin->env->cls,sa, slen);
+ if (((GNUNET_ATS_NET_LAN == ntohl(type.value)) || (GNUNET_ATS_NET_WAN == ntohl(type.value))) &&
+     ((ENETUNREACH == errno) || (ENETDOWN == errno)))
+ {
+   if ((network_down_error == GNUNET_NO) && (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,
+         _("UDP could not transmit message to `%s': "
+           "Network seems down, please check your network configuration\n"),
+         GNUNET_a2s (sa, slen));
+   }
+   if ((network_down_error == GNUNET_NO) && (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,
+        _("UDP 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,
+      "UDP could not transmit message to `%s': `%s'\n",
+      GNUNET_a2s (sa, slen), STRERROR (error));
+ }
+}
+
+
+static size_t
+udp_select_send (struct Plugin *plugin, struct GNUNET_NETWORK_Handle *sock)
+{
+  const struct sockaddr * sa;
+  ssize_t sent;
+  socklen_t slen;
+
+  struct UDP_MessageWrapper *udpw = NULL;
+
+  /* Find message to send */
+  udpw = remove_timeout_messages_and_select ((sock == plugin->sockv4) ? plugin->ipv4_queue_head : plugin->ipv6_queue_head,
+                                             sock);
+  if (NULL == udpw)
+    return 0; /* No message to send */
+
+  sa = udpw->session->sock_addr;
+  slen = udpw->session->addrlen;
+
+  sent = GNUNET_NETWORK_socket_sendto (sock, udpw->msg_buf, udpw->msg_size, sa, slen);
 
   if (GNUNET_SYSERR == sent)
   {
-    const struct GNUNET_ATS_Information type = plugin->env->get_address_type
-        (plugin->env->cls,sa, slen);
-
-    if ((GNUNET_ATS_NET_WAN == type.value) &&
-        ((ENETUNREACH == errno) || (ENETDOWN == errno)))
-    {
-      /* "Network unreachable" or "Network down" */
-      /*
-       * This indicates that this system is IPv6 enabled, but does not
-       * have a valid global IPv6 address assigned
-       */
-       LOG (GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
-           _("UDP could not message to `%s': `%s'. "
-            "Please check your network configuration and disable IPv6 if your "
-            "connection does not have a global IPv6 address\n"),
-           GNUNET_a2s (sa, slen),
-           STRERROR (errno));
-    }
-    else
-    {
-      LOG (GNUNET_ERROR_TYPE_ERROR,
-         "UDP could not transmit %u-byte message to `%s': `%s'\n",
-         (unsigned int) (udpw->msg_size), GNUNET_a2s (sa, slen),
-         STRERROR (errno));
-    }
+    /* Failure */
+    analyze_send_error (plugin, sa, slen, errno);
     call_continuation(udpw, GNUNET_SYSERR);
+    GNUNET_STATISTICS_update (plugin->env->stats,
+                            "# UDP, total, bytes, sent, failure",
+                            sent, GNUNET_NO);
+    GNUNET_STATISTICS_update (plugin->env->stats,
+                              "# UDP, total, messages, sent, failure",
+                              1, GNUNET_NO);
   }
   else
   {
+    /* Success */
     LOG (GNUNET_ERROR_TYPE_DEBUG,
-         "UDP transmitted %u-byte message to `%s' (%d: %s)\n",
-         (unsigned int) (udpw->msg_size), GNUNET_a2s (sa, slen), (int) sent,
+         "UDP transmitted %u-byte message to  `%s' `%s' (%d: %s)\n",
+         (unsigned int) (udpw->msg_size), GNUNET_i2s(&udpw->session->target) ,GNUNET_a2s (sa, slen), (int) sent,
          (sent < 0) ? STRERROR (errno) : "ok");
-    call_continuation(udpw, GNUNET_OK);
-  }
-
-  if (sock == plugin->sockv4)
-    GNUNET_CONTAINER_DLL_remove(plugin->ipv4_queue_head, plugin->ipv4_queue_tail, udpw);
-  else if (sock == plugin->sockv6)
-    GNUNET_CONTAINER_DLL_remove(plugin->ipv6_queue_head, plugin->ipv6_queue_tail, udpw);
+    GNUNET_STATISTICS_update (plugin->env->stats,
+                              "# UDP, total, bytes, sent, success",
+                              sent, GNUNET_NO);
+    GNUNET_STATISTICS_update (plugin->env->stats,
+                              "# UDP, total, messages, sent, success",
+                              1, GNUNET_NO);
+    if (NULL != udpw->frag_ctx)
+        udpw->frag_ctx->on_wire_size += udpw->msg_size;
+    call_continuation (udpw, GNUNET_OK);
+  }
+  dequeue (plugin, udpw);
   GNUNET_free (udpw);
   udpw = NULL;
 
@@ -1947,38 +2700,18 @@ udp_plugin_select (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
   struct Plugin *plugin = cls;
 
   plugin->select_task = GNUNET_SCHEDULER_NO_TASK;
-  if ((tc->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN) != 0)
+  if (0 != (tc->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN))
     return;
-  plugin->with_v4_ws = GNUNET_NO;
-
-  if ((tc->reason & GNUNET_SCHEDULER_REASON_READ_READY) != 0)
-  {
-    if ((NULL != plugin->sockv4) &&
-      (GNUNET_NETWORK_fdset_isset (tc->read_ready, plugin->sockv4)))
-        udp_select_read (plugin, plugin->sockv4);
-
-  }
-
-  if ((tc->reason & GNUNET_SCHEDULER_REASON_WRITE_READY) != 0)
-  {
-    if ((NULL != plugin->sockv4) && (plugin->ipv4_queue_head != NULL) &&
-      (GNUNET_NETWORK_fdset_isset (tc->write_ready, plugin->sockv4)))
-      {
-        udp_select_send (plugin, plugin->sockv4);
-      }
-  }
-
-  if (plugin->select_task != GNUNET_SCHEDULER_NO_TASK)
-    GNUNET_SCHEDULER_cancel (plugin->select_task);
-  plugin->select_task = GNUNET_SCHEDULER_add_select (GNUNET_SCHEDULER_PRIORITY_DEFAULT,
-                                   GNUNET_TIME_UNIT_FOREVER_REL,
-                                   plugin->rs_v4,
-                                   (plugin->ipv4_queue_head != NULL) ? plugin->ws_v4 : NULL,
-                                   &udp_plugin_select, plugin);
-  if (plugin->ipv4_queue_head != NULL)
-    plugin->with_v4_ws = GNUNET_YES;
-  else
-    plugin->with_v4_ws = GNUNET_NO;
+  if ( (0 != (tc->reason & GNUNET_SCHEDULER_REASON_READ_READY)) &&
+       (NULL != plugin->sockv4) &&
+       (GNUNET_NETWORK_fdset_isset (tc->read_ready, plugin->sockv4)) )
+    udp_select_read (plugin, plugin->sockv4);
+  if ( (0 != (tc->reason & GNUNET_SCHEDULER_REASON_WRITE_READY)) &&
+       (NULL != plugin->sockv4) &&
+       (NULL != plugin->ipv4_queue_head) &&
+       (GNUNET_NETWORK_fdset_isset (tc->write_ready, plugin->sockv4)) )
+    udp_select_send (plugin, plugin->sockv4);
+  schedule_select (plugin);
 }
 
 
@@ -1996,161 +2729,209 @@ udp_plugin_select_v6 (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
   struct Plugin *plugin = cls;
 
   plugin->select_task_v6 = GNUNET_SCHEDULER_NO_TASK;
-  if ((tc->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN) != 0)
+  if (0 != (tc->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN))
     return;
-
-  plugin->with_v6_ws = GNUNET_NO;
-  if ((tc->reason & GNUNET_SCHEDULER_REASON_READ_READY) != 0)
-  {
-    if ((NULL != plugin->sockv6) &&
-      (GNUNET_NETWORK_fdset_isset (tc->read_ready, plugin->sockv6)))
-        udp_select_read (plugin, plugin->sockv6);
-  }
-
-  if ((tc->reason & GNUNET_SCHEDULER_REASON_WRITE_READY) != 0)
-  {
-    if ((NULL != plugin->sockv6) && (plugin->ipv6_queue_head != NULL) &&
-      (GNUNET_NETWORK_fdset_isset (tc->write_ready, plugin->sockv6)))
-      {
-        udp_select_send (plugin, plugin->sockv6);
-      }
-  }
-  if (plugin->select_task_v6 != GNUNET_SCHEDULER_NO_TASK)
-    GNUNET_SCHEDULER_cancel (plugin->select_task_v6);
-  plugin->select_task_v6 = GNUNET_SCHEDULER_add_select (GNUNET_SCHEDULER_PRIORITY_DEFAULT,
-                                   GNUNET_TIME_UNIT_FOREVER_REL,
-                                   plugin->rs_v6,
-                                   (plugin->ipv6_queue_head != NULL) ? plugin->ws_v6 : NULL,
-                                   &udp_plugin_select_v6, plugin);
-  if (plugin->ipv6_queue_head != NULL)
-    plugin->with_v6_ws = GNUNET_YES;
-  else
-    plugin->with_v6_ws = GNUNET_NO;
+  if ( ((tc->reason & GNUNET_SCHEDULER_REASON_READ_READY) != 0) &&
+       (NULL != plugin->sockv6) &&
+       (GNUNET_NETWORK_fdset_isset (tc->read_ready, plugin->sockv6)) )
+    udp_select_read (plugin, plugin->sockv6);
+  if ( (0 != (tc->reason & GNUNET_SCHEDULER_REASON_WRITE_READY)) &&
+       (NULL != plugin->sockv6) && (plugin->ipv6_queue_head != NULL) &&
+       (GNUNET_NETWORK_fdset_isset (tc->write_ready, plugin->sockv6)) )
+    udp_select_send (plugin, plugin->sockv6);
+  schedule_select (plugin);
 }
 
 
+/**
+ *
+ * @return number of sockets that were successfully bound
+ */
 static int
-setup_sockets (struct Plugin *plugin, struct sockaddr_in6 *serverAddrv6, struct sockaddr_in *serverAddrv4)
+setup_sockets (struct Plugin *plugin,
+              const struct sockaddr_in6 *bind_v6,
+              const struct sockaddr_in *bind_v4)
 {
   int tries;
   int sockets_created = 0;
-  struct sockaddr *serverAddr;
+  struct sockaddr_in6 server_addrv6;
+  struct sockaddr_in server_addrv4;
+  struct sockaddr *server_addr;
   struct sockaddr *addrs[2];
   socklen_t addrlens[2];
   socklen_t addrlen;
+  int eno;
 
   /* Create IPv6 socket */
+  eno = EINVAL;
   if (plugin->enable_ipv6 == GNUNET_YES)
   {
     plugin->sockv6 = GNUNET_NETWORK_socket_create (PF_INET6, SOCK_DGRAM, 0);
     if (NULL == plugin->sockv6)
     {
+       GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, "socket");
       LOG (GNUNET_ERROR_TYPE_WARNING, "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
-      serverAddrv6->sin6_len = sizeof (serverAddrv6);
+      server_addrv6.sin6_len = sizeof (struct sockaddr_in6);
 #endif
-      serverAddrv6->sin6_family = AF_INET6;
-      serverAddrv6->sin6_addr = in6addr_any;
-      serverAddrv6->sin6_port = htons (plugin->port);
+      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);
-      serverAddr = (struct sockaddr *) serverAddrv6;
-      LOG (GNUNET_ERROR_TYPE_DEBUG, "Binding to IPv6 port %d\n",
-           ntohs (serverAddrv6->sin6_port));
+      server_addr = (struct sockaddr *) &server_addrv6;
+
       tries = 0;
-      while (GNUNET_NETWORK_socket_bind (plugin->sockv6, serverAddr, addrlen) !=
-             GNUNET_OK)
+      while (tries < 10)
       {
-        serverAddrv6->sin6_port = htons (GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_STRONG, 33537) + 32000);        /* Find a good, non-root port */
-        LOG (GNUNET_ERROR_TYPE_DEBUG,
-             "IPv6 Binding failed, trying new port %d\n",
-             ntohs (serverAddrv6->sin6_port));
-        tries++;
-        if (tries > 10)
-        {
-          GNUNET_NETWORK_socket_close (plugin->sockv6);
-          plugin->sockv6 = NULL;
-          break;
-        }
+               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 */
+                               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;
       }
+
       if (plugin->sockv6 != NULL)
       {
         LOG (GNUNET_ERROR_TYPE_DEBUG,
-             "IPv6 socket created on port %d\n",
-             ntohs (serverAddrv6->sin6_port));
-        addrs[sockets_created] = (struct sockaddr *) serverAddrv6;
+             "IPv6 socket created on port %s\n",
+             GNUNET_a2s (server_addr, addrlen));
+        addrs[sockets_created] = (struct sockaddr *) &server_addrv6;
         addrlens[sockets_created] = sizeof (struct sockaddr_in6);
         sockets_created++;
       }
+      else
+      {
+          LOG (GNUNET_ERROR_TYPE_ERROR,
+               "Failed to bind UDP 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_WARNING, "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
-    serverAddrv4->sin_len = sizeof (serverAddrv4);
+    server_addrv4.sin_len = sizeof (struct sockaddr_in);
 #endif
-    serverAddrv4->sin_family = AF_INET;
-    serverAddrv4->sin_addr.s_addr = INADDR_ANY;
-    serverAddrv4->sin_port = htons (plugin->port);
+    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);
-    serverAddr = (struct sockaddr *) serverAddrv4;
+    server_addr = (struct sockaddr *) &server_addrv4;
 
-    LOG (GNUNET_ERROR_TYPE_DEBUG, "Binding to IPv4 port %d\n",
-         ntohs (serverAddrv4->sin_port));
     tries = 0;
-    while (GNUNET_NETWORK_socket_bind (plugin->sockv4, serverAddr, addrlen) !=
-           GNUNET_OK)
+    while (tries < 10)
     {
-      serverAddrv4->sin_port = htons (GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_STRONG, 33537) + 32000);   /* Find a good, non-root port */
-      LOG (GNUNET_ERROR_TYPE_DEBUG, "IPv4 Binding failed, trying new port %d\n",
-           ntohs (serverAddrv4->sin_port));
-      tries++;
-      if (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)
       {
-        GNUNET_NETWORK_socket_close (plugin->sockv4);
-        plugin->sockv4 = NULL;
-        break;
+               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;
+    }
+
     if (plugin->sockv4 != NULL)
     {
-      addrs[sockets_created] = (struct sockaddr *) serverAddrv4;
+      LOG (GNUNET_ERROR_TYPE_DEBUG,
+               "IPv4 socket created on port %s\n", GNUNET_a2s (server_addr, addrlen));
+      addrs[sockets_created] = (struct sockaddr *) &server_addrv4;
       addrlens[sockets_created] = sizeof (struct sockaddr_in);
       sockets_created++;
     }
+    else
+    {
+      LOG (GNUNET_ERROR_TYPE_ERROR,
+               "Failed to bind UDP socket to %s: %s\n",
+               GNUNET_a2s (server_addr, addrlen), STRERROR (eno));
+    }
   }
 
-  /* Create file descriptors */
-  plugin->rs_v4 = GNUNET_NETWORK_fdset_create ();
-  plugin->ws_v4 = GNUNET_NETWORK_fdset_create ();
-  GNUNET_NETWORK_fdset_zero (plugin->rs_v4);
-  GNUNET_NETWORK_fdset_zero (plugin->ws_v4);
-  if (NULL != plugin->sockv4)
+  if (0 == sockets_created)
   {
-    GNUNET_NETWORK_fdset_set (plugin->rs_v4, plugin->sockv4);
-    GNUNET_NETWORK_fdset_set (plugin->ws_v4, plugin->sockv4);
+               LOG (GNUNET_ERROR_TYPE_WARNING, _("Failed to open UDP sockets\n"));
+               return 0; /* No sockets created, return */
   }
 
-  if (sockets_created == 0)
-    LOG (GNUNET_ERROR_TYPE_WARNING, _("Failed to open UDP sockets\n"));
-
-  plugin->select_task =
-      GNUNET_SCHEDULER_add_select (GNUNET_SCHEDULER_PRIORITY_DEFAULT,
-                                   GNUNET_TIME_UNIT_FOREVER_REL,
-                                   plugin->rs_v4,
-                                   NULL,
-                                   &udp_plugin_select, plugin);
-  plugin->with_v4_ws = GNUNET_NO;
+  /* Create file descriptors */
+  if (plugin->enable_ipv4 == GNUNET_YES)
+  {
+                       plugin->rs_v4 = GNUNET_NETWORK_fdset_create ();
+                       plugin->ws_v4 = GNUNET_NETWORK_fdset_create ();
+                       GNUNET_NETWORK_fdset_zero (plugin->rs_v4);
+                       GNUNET_NETWORK_fdset_zero (plugin->ws_v4);
+                       if (NULL != plugin->sockv4)
+                       {
+                               GNUNET_NETWORK_fdset_set (plugin->rs_v4, plugin->sockv4);
+                               GNUNET_NETWORK_fdset_set (plugin->ws_v4, plugin->sockv4);
+                       }
+  }
 
   if (plugin->enable_ipv6 == GNUNET_YES)
   {
@@ -2163,16 +2944,9 @@ setup_sockets (struct Plugin *plugin, struct sockaddr_in6 *serverAddrv6, struct
       GNUNET_NETWORK_fdset_set (plugin->rs_v6, plugin->sockv6);
       GNUNET_NETWORK_fdset_set (plugin->ws_v6, plugin->sockv6);
     }
-
-    plugin->select_task_v6 =
-        GNUNET_SCHEDULER_add_select (GNUNET_SCHEDULER_PRIORITY_DEFAULT,
-                                     GNUNET_TIME_UNIT_FOREVER_REL,
-                                     plugin->rs_v6,
-                                     NULL,
-                                     &udp_plugin_select_v6, plugin);
-    plugin->with_v6_ws = GNUNET_NO;
   }
 
+  schedule_select (plugin);
   plugin->nat = GNUNET_NAT_register (plugin->env->cfg,
                            GNUNET_NO, plugin->port,
                            sockets_created,
@@ -2182,82 +2956,6 @@ setup_sockets (struct Plugin *plugin, struct sockaddr_in6 *serverAddrv6, struct
   return sockets_created;
 }
 
-/**
- * Session was idle, so disconnect it
- */
-static void
-session_timeout (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
-{
-  GNUNET_assert (NULL != cls);
-  struct Session *s = cls;
-
-  s->timeout_task = GNUNET_SCHEDULER_NO_TASK;
-
-  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Session %p was idle for %llu, disconnecting\n",
-      s, GNUNET_CONSTANTS_IDLE_CONNECTION_TIMEOUT.rel_value);
-
-  /* call session destroy function */
-  disconnect_session(s);
-
-}
-
-/**
- * Start session timeout
- */
-static void
-start_session_timeout (struct Session *s)
-{
-  GNUNET_assert (NULL != s);
-  GNUNET_assert (GNUNET_SCHEDULER_NO_TASK == s->timeout_task);
-
-  s->timeout_task =  GNUNET_SCHEDULER_add_delayed (GNUNET_CONSTANTS_IDLE_CONNECTION_TIMEOUT,
-                                                   &session_timeout,
-                                                   s);
-
-  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Timeout for session %p set to %llu\n",
-      s, GNUNET_CONSTANTS_IDLE_CONNECTION_TIMEOUT.rel_value);
-}
-
-/**
- * Increment session timeout due to activity
- */
-static void
-reschedule_session_timeout (struct Session *s)
-{
-  GNUNET_assert (NULL != s);
-  GNUNET_assert (GNUNET_SCHEDULER_NO_TASK != s->timeout_task);
-
-  GNUNET_SCHEDULER_cancel (s->timeout_task);
-  s->timeout_task =  GNUNET_SCHEDULER_add_delayed (GNUNET_CONSTANTS_IDLE_CONNECTION_TIMEOUT,
-                                                   &session_timeout,
-                                                   s);
-
-  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Timeout rescheduled for session %p set to %llu\n",
-      s, GNUNET_CONSTANTS_IDLE_CONNECTION_TIMEOUT.rel_value);
-}
-
-/**
- * Cancel timeout
- */
-static void
-stop_session_timeout (struct Session *s)
-{
-  GNUNET_assert (NULL != s);
-
-  if (GNUNET_SCHEDULER_NO_TASK != s->timeout_task)
-  {
-    GNUNET_SCHEDULER_cancel (s->timeout_task);
-    s->timeout_task = GNUNET_SCHEDULER_NO_TASK;
-
-    GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Timeout rescheduled for session %p canceled\n",
-      s, GNUNET_CONSTANTS_IDLE_CONNECTION_TIMEOUT.rel_value);
-  }
-  else
-  {
-    GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Timeout for session %p was not active\n",
-      s);
-  }
-}
 
 /**
  * The exported method. Makes the core api available via a global and
@@ -2279,10 +2977,13 @@ libgnunet_plugin_transport_udp_init (void *cls)
   unsigned long long enable_v6;
   char * bind4_address;
   char * bind6_address;
+  char * fancy_interval;
   struct GNUNET_TIME_Relative interval;
-  struct sockaddr_in serverAddrv4;
-  struct sockaddr_in6 serverAddrv6;
+  struct sockaddr_in server_addrv4;
+  struct sockaddr_in6 server_addrv6;
   int res;
+  int have_bind4;
+  int have_bind6;
 
   if (NULL == env->receive)
   {
@@ -2296,9 +2997,11 @@ libgnunet_plugin_transport_udp_init (void *cls)
     return api;
   }
 
-  GNUNET_assertNULL != env->stats);
+  GNUNET_assert (NULL != env->stats);
 
-  /* Get port number */
+  /* 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-udp", "PORT",
                                              &port))
@@ -2319,16 +3022,13 @@ libgnunet_plugin_transport_udp_init (void *cls)
   if ((GNUNET_YES ==
        GNUNET_CONFIGURATION_get_value_yesno (env->cfg, "nat",
                                              "DISABLEV6")))
-  {
     enable_v6 = GNUNET_NO;
-  }
   else
     enable_v6 = GNUNET_YES;
 
   /* Addresses */
-  memset (&serverAddrv6, 0, sizeof (serverAddrv6));
-  memset (&serverAddrv4, 0, sizeof (serverAddrv4));
-
+  have_bind4 = GNUNET_NO;
+  memset (&server_addrv4, 0, sizeof (server_addrv4));
   if (GNUNET_YES ==
       GNUNET_CONFIGURATION_get_value_string (env->cfg, "transport-udp",
                                              "BINDTO", &bind4_address))
@@ -2336,13 +3036,16 @@ libgnunet_plugin_transport_udp_init (void *cls)
     LOG (GNUNET_ERROR_TYPE_DEBUG,
          "Binding udp plugin to specific address: `%s'\n",
          bind4_address);
-    if (1 != inet_pton (AF_INET, bind4_address, &serverAddrv4.sin_addr))
+    if (1 != inet_pton (AF_INET, bind4_address, &server_addrv4.sin_addr))
     {
       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-udp",
                                              "BINDTO6", &bind6_address))
@@ -2351,15 +3054,19 @@ libgnunet_plugin_transport_udp_init (void *cls)
          "Binding udp plugin to specific address: `%s'\n",
          bind6_address);
     if (1 !=
-        inet_pton (AF_INET6, bind6_address, &serverAddrv6.sin6_addr))
+        inet_pton (AF_INET6, bind6_address, &server_addrv6.sin6_addr))
     {
       LOG (GNUNET_ERROR_TYPE_ERROR, _("Invalid IPv6 address: `%s'\n"),
            bind6_address);
-      GNUNET_free_non_null (bind4_address);
       GNUNET_free (bind6_address);
       return NULL;
     }
+    have_bind6 = GNUNET_YES;
   }
+  GNUNET_free_non_null (bind6_address);
+
+  /* Initialize my flags */
+  myoptions = 0;
 
   /* Enable neighbour discovery */
   broadcast = GNUNET_CONFIGURATION_get_value_yesno (env->cfg, "transport-udp",
@@ -2367,11 +3074,19 @@ libgnunet_plugin_transport_udp_init (void *cls)
   if (broadcast == GNUNET_SYSERR)
     broadcast = GNUNET_NO;
 
-  if (GNUNET_SYSERR == GNUNET_CONFIGURATION_get_value_time (env->cfg, "transport-udp",
-                                           "BROADCAST_INTERVAL", &interval))
+  if (GNUNET_SYSERR == GNUNET_CONFIGURATION_get_value_string (env->cfg, "transport-udp",
+                                           "BROADCAST_INTERVAL", &fancy_interval))
   {
     interval = GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 10);
   }
+  else
+  {
+     if (GNUNET_SYSERR == GNUNET_STRINGS_fancy_time_to_relative(fancy_interval, &interval))
+     {
+       interval = GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 30);
+     }
+     GNUNET_free (fancy_interval);
+  }
 
   /* Maximum datarate */
   if (GNUNET_OK != GNUNET_CONFIGURATION_get_value_number (env->cfg, "transport-udp",
@@ -2381,21 +3096,39 @@ libgnunet_plugin_transport_udp_init (void *cls)
   }
 
   p = GNUNET_malloc (sizeof (struct Plugin));
-  api = GNUNET_malloc (sizeof (struct GNUNET_TRANSPORT_PluginFunctions));
-
-  GNUNET_BANDWIDTH_tracker_init (&p->tracker,
-                                 GNUNET_BANDWIDTH_value_init ((uint32_t)udp_max_bps), 30);
-  p->sessions = GNUNET_CONTAINER_multihashmap_create (10);
-  p->defrag_ctxs = GNUNET_CONTAINER_heap_create (GNUNET_CONTAINER_HEAP_ORDER_MIN);
-  p->mst = GNUNET_SERVER_mst_create (&process_inbound_tokenized_messages, p);
   p->port = port;
   p->aport = aport;
   p->broadcast_interval = interval;
   p->enable_ipv6 = enable_v6;
+  p->enable_ipv4 = GNUNET_YES; /* default */
   p->env = env;
-
+  p->sessions = GNUNET_CONTAINER_multipeermap_create (10, GNUNET_NO);
+  p->defrag_ctxs = GNUNET_CONTAINER_heap_create (GNUNET_CONTAINER_HEAP_ORDER_MIN);
+  p->mst = GNUNET_SERVER_mst_create (&process_inbound_tokenized_messages, p);
+  GNUNET_BANDWIDTH_tracker_init (&p->tracker,
+      GNUNET_BANDWIDTH_value_init ((uint32_t) udp_max_bps), 30);
   plugin = p;
 
+  LOG(GNUNET_ERROR_TYPE_DEBUG, "Setting up sockets\n");
+  res = setup_sockets (p, (GNUNET_YES == have_bind6) ? &server_addrv6 : NULL,
+      (GNUNET_YES == have_bind4) ? &server_addrv4 : NULL );
+  if ((res == 0) || ((p->sockv4 == NULL )&& (p->sockv6 == NULL)))
+  {
+    LOG (GNUNET_ERROR_TYPE_ERROR,
+        _("Failed to create network sockets, plugin failed\n"));
+    GNUNET_CONTAINER_multipeermap_destroy (p->sessions);
+    GNUNET_CONTAINER_heap_destroy (p->defrag_ctxs);
+    GNUNET_SERVER_mst_destroy (p->mst);
+    GNUNET_free (p);
+    return NULL;
+  }
+  else if (broadcast == GNUNET_YES)
+  {
+    LOG (GNUNET_ERROR_TYPE_DEBUG, "Starting broadcasting\n");
+    setup_broadcast (p, &server_addrv6, &server_addrv4);
+  }
+
+  api = GNUNET_malloc (sizeof (struct GNUNET_TRANSPORT_PluginFunctions));
   api->cls = p;
   api->send = NULL;
   api->disconnect = &udp_disconnect;
@@ -2405,25 +3138,8 @@ libgnunet_plugin_transport_udp_init (void *cls)
   api->check_address = &udp_plugin_check_address;
   api->get_session = &udp_plugin_get_session;
   api->send = &udp_plugin_send;
+  api->get_network = &udp_get_network;
 
-  LOG (GNUNET_ERROR_TYPE_DEBUG, "Setting up sockets\n");
-  res = setup_sockets (p, &serverAddrv6, &serverAddrv4);
-  if ((res == 0) || ((p->sockv4 == NULL) && (p->sockv6 == NULL)))
-  {
-    LOG (GNUNET_ERROR_TYPE_ERROR, "Failed to create network sockets, plugin failed\n");
-    GNUNET_free (p);
-    GNUNET_free (api);
-    return NULL;
-  }
-
-  if (broadcast == GNUNET_YES)
-  {
-    LOG (GNUNET_ERROR_TYPE_DEBUG, "Starting broadcasting\n");
-    setup_broadcast (p, &serverAddrv6, &serverAddrv4);
-  }
-
-  GNUNET_free_non_null (bind4_address);
-  GNUNET_free_non_null (bind6_address);
   return api;
 }
 
@@ -2457,6 +3173,8 @@ libgnunet_plugin_transport_udp_done (void *cls)
 {
   struct GNUNET_TRANSPORT_PluginFunctions *api = cls;
   struct Plugin *plugin = api->cls;
+  struct PrettyPrinterContext *cur;
+  struct PrettyPrinterContext *next;
 
   if (NULL == plugin)
   {
@@ -2465,7 +3183,6 @@ libgnunet_plugin_transport_udp_done (void *cls)
   }
 
   stop_broadcast (plugin);
-
   if (plugin->select_task != GNUNET_SCHEDULER_NO_TASK)
   {
     GNUNET_SCHEDULER_cancel (plugin->select_task);
@@ -2478,24 +3195,29 @@ libgnunet_plugin_transport_udp_done (void *cls)
   }
 
   /* Closing sockets */
-  if (plugin->sockv4 != NULL)
+  if (GNUNET_YES ==plugin->enable_ipv4)
   {
-    GNUNET_break (GNUNET_OK == GNUNET_NETWORK_socket_close (plugin->sockv4));
-    plugin->sockv4 = NULL;
+               if (plugin->sockv4 != NULL)
+               {
+                       GNUNET_break (GNUNET_OK == GNUNET_NETWORK_socket_close (plugin->sockv4));
+                       plugin->sockv4 = NULL;
+               }
+               GNUNET_NETWORK_fdset_destroy (plugin->rs_v4);
+               GNUNET_NETWORK_fdset_destroy (plugin->ws_v4);
   }
-  GNUNET_NETWORK_fdset_destroy (plugin->rs_v4);
-  GNUNET_NETWORK_fdset_destroy (plugin->ws_v4);
-
-  if (plugin->sockv6 != NULL)
+  if (GNUNET_YES ==plugin->enable_ipv6)
   {
-    GNUNET_break (GNUNET_OK == GNUNET_NETWORK_socket_close (plugin->sockv6));
-    plugin->sockv6 = NULL;
+               if (plugin->sockv6 != NULL)
+               {
+                       GNUNET_break (GNUNET_OK == GNUNET_NETWORK_socket_close (plugin->sockv6));
+                       plugin->sockv6 = NULL;
 
-    GNUNET_NETWORK_fdset_destroy (plugin->rs_v6);
-    GNUNET_NETWORK_fdset_destroy (plugin->ws_v6);
+                       GNUNET_NETWORK_fdset_destroy (plugin->rs_v6);
+                       GNUNET_NETWORK_fdset_destroy (plugin->ws_v6);
+               }
   }
-
-  GNUNET_NAT_unregister (plugin->nat);
+  if (NULL != plugin->nat)
+       GNUNET_NAT_unregister (plugin->nat);
 
   if (plugin->defrag_ctxs != NULL)
   {
@@ -2511,35 +3233,64 @@ libgnunet_plugin_transport_udp_done (void *cls)
   }
 
   /* Clean up leftover messages */
-  struct UDPMessageWrapper * udpw;
+  struct UDP_MessageWrapper * udpw;
   udpw = plugin->ipv4_queue_head;
   while (udpw != NULL)
   {
-    struct UDPMessageWrapper *tmp = udpw->next;
-    GNUNET_CONTAINER_DLL_remove(plugin->ipv4_queue_head, plugin->ipv4_queue_tail, udpw);
+    struct UDP_MessageWrapper *tmp = udpw->next;
+    dequeue (plugin, udpw);
     call_continuation(udpw, GNUNET_SYSERR);
     GNUNET_free (udpw);
+
     udpw = tmp;
   }
   udpw = plugin->ipv6_queue_head;
   while (udpw != NULL)
   {
-    struct UDPMessageWrapper *tmp = udpw->next;
-    GNUNET_CONTAINER_DLL_remove(plugin->ipv6_queue_head, plugin->ipv6_queue_tail, udpw);
+    struct UDP_MessageWrapper *tmp = udpw->next;
+    dequeue (plugin, udpw);
     call_continuation(udpw, GNUNET_SYSERR);
     GNUNET_free (udpw);
+
     udpw = tmp;
   }
 
   /* Clean up sessions */
   LOG (GNUNET_ERROR_TYPE_DEBUG,
        "Cleaning up sessions\n");
-  GNUNET_CONTAINER_multihashmap_iterate (plugin->sessions, &disconnect_and_free_it, plugin);
-  GNUNET_CONTAINER_multihashmap_destroy (plugin->sessions);
+  GNUNET_CONTAINER_multipeermap_iterate (plugin->sessions, &disconnect_and_free_it, plugin);
+  GNUNET_CONTAINER_multipeermap_destroy (plugin->sessions);
+
+  next = ppc_dll_head;
+  for (cur = next; NULL != cur; cur = next)
+  {
+       next = cur->next;
+       GNUNET_CONTAINER_DLL_remove (ppc_dll_head, ppc_dll_tail, cur);
+       GNUNET_RESOLVER_request_cancel (cur->resolver_handle);
+       GNUNET_SCHEDULER_cancel (cur->timeout_task);
+       GNUNET_free (cur);
+       GNUNET_break (0);
+  }
 
   plugin->nat = NULL;
   GNUNET_free (plugin);
   GNUNET_free (api);
+#if DEBUG_MALLOC
+  struct Allocation *allocation;
+  while (NULL != ahead)
+  {
+      allocation = ahead;
+      GNUNET_CONTAINER_DLL_remove (ahead, atail, allocation);
+      GNUNET_free (allocation);
+  }
+  struct Allocator *allocator;
+  while (NULL != aehead)
+  {
+      allocator = aehead;
+      GNUNET_CONTAINER_DLL_remove (aehead, aetail, allocator);
+      GNUNET_free (allocator);
+  }
+#endif
   return NULL;
 }