- dozygen
[oweals/gnunet.git] / src / transport / plugin_transport_udp.c
index 7067dc196d99427675e917ea6e28149ec11a4593..940bed254f220887257959352ac401bf2afa595d 100644 (file)
@@ -1,10 +1,10 @@
 /*
      This file is part of GNUnet
 /*
      This file is part of GNUnet
-     (C) 2010 Christian Grothoff (and other contributing authors)
+     (C) 2010, 2011 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
 
      GNUnet is free software; you can redistribute it and/or modify
      it under the terms of the GNU General Public License as published
-     by the Free Software Foundation; either version 2, or (at your
+     by the Free Software Foundation; either version 3, or (at your
      option) any later version.
 
      GNUnet is distributed in the hope that it will be useful, but
      option) any later version.
 
      GNUnet is distributed in the hope that it will be useful, but
 
 /**
  * @file transport/plugin_transport_udp.c
 
 /**
  * @file transport/plugin_transport_udp.c
- * @brief Implementation of the UDP NAT punching
- *        transport service
+ * @brief Implementation of the UDP transport protocol
  * @author Christian Grothoff
  * @author Nathan Evans
  * @author Christian Grothoff
  * @author Nathan Evans
- *
- * The idea with this transport is to connect gnunet peers to each other
- * when ONE is behind a NAT.  This is based on pwnat (http://samy.pl/pwnat)
- * created by Samy Kamkar.  When configured with the PWNAT options, this
- * transport will start a server daemon which sends dummy ICMP and UDP
- * messages out to a predefined address (typically 1.2.3.4).
- *
- * When a non-NAT'd peer (the client) learns of the NAT'd peer (the server)
- * address, it will send ICMP RESPONSES to the NAT'd peers external address.
- * The NAT box should forward these faked responses to the server, which
- * can then connect directly to the non-NAT'd peer.
+ * @author Matthias Wachs
  */
  */
-
 #include "platform.h"
 #include "platform.h"
+#include "plugin_transport_udp.h"
 #include "gnunet_hello_lib.h"
 #include "gnunet_hello_lib.h"
-#include "gnunet_connection_lib.h"
-#include "gnunet_container_lib.h"
-#include "gnunet_os_lib.h"
-#include "gnunet_peerinfo_service.h"
+#include "gnunet_util_lib.h"
+#include "gnunet_fragmentation_lib.h"
+#include "gnunet_nat_lib.h"
 #include "gnunet_protocols.h"
 #include "gnunet_resolver_service.h"
 #include "gnunet_protocols.h"
 #include "gnunet_resolver_service.h"
-#include "gnunet_server_lib.h"
-#include "gnunet_service_lib.h"
 #include "gnunet_signatures.h"
 #include "gnunet_signatures.h"
+#include "gnunet_constants.h"
 #include "gnunet_statistics_service.h"
 #include "gnunet_transport_service.h"
 #include "gnunet_statistics_service.h"
 #include "gnunet_transport_service.h"
-#include "plugin_transport.h"
+#include "gnunet_transport_plugin.h"
 #include "transport.h"
 
 #include "transport.h"
 
-#define DEBUG_UDP GNUNET_YES
-
-#define MAX_PROBES 20
-
-/*
- * Transport cost to peer, always 1 for UDP (direct connection)
- */
-#define UDP_DIRECT_DISTANCE 1
-
-#define DEFAULT_NAT_PORT 0
+#define LOG(kind,...) GNUNET_log_from (kind, "transport-udp", __VA_ARGS__)
 
 
-/**
- * How long until we give up on transmitting the welcome message?
- */
-#define HOSTNAME_RESOLVE_TIMEOUT GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 5)
 
 /**
 
 /**
- * Starting port for listening and sending, eventually a config value
+ * Number of messages we can defragment in parallel.  We only really
+ * defragment 1 message at a time, but if messages get re-ordered, we
+ * may want to keep knowledge about the previous message to avoid
+ * discarding the current message in favor of a single fragment of a
+ * previous message.  3 should be good since we don't expect massive
+ * message reorderings with UDP.
  */
  */
-#define UDP_NAT_DEFAULT_PORT 22086
+#define UDP_MAX_MESSAGES_IN_DEFRAG 3
 
 /**
 
 /**
- * UDP Message-Packet header.
+ * We keep a defragmentation queue per sender address.  How many
+ * sender addresses do we support at the same time? Memory consumption
+ * is roughly a factor of 32k * UDP_MAX_MESSAGES_IN_DEFRAG times this
+ * value. (So 128 corresponds to 12 MB and should suffice for
+ * connecting to roughly 128 peers via UDP).
  */
  */
-struct UDPMessage
-{
-  /**
-   * Message header.
-   */
-  struct GNUNET_MessageHeader header;
+#define UDP_MAX_SENDER_ADDRESSES_WITH_DEFRAG 128
 
 
-  /**
-   * What is the identity of the sender (GNUNET_hash of public key)
-   */
-  struct GNUNET_PeerIdentity sender;
 
 
-};
 
 /**
 
 /**
- * Network format for IPv4 addresses.
+ * Closure for 'append_port'.
  */
  */
-struct IPv4UdpAddress
+struct PrettyPrinterContext
 {
   /**
 {
   /**
-   * IPv4 address, in network byte order.
+   * Function to call with the result.
    */
    */
-  uint32_t ipv4_addr;
-
-  /**
-   * Port number, in network byte order.
-   */
-  uint16_t u_port;
-};
-
+  GNUNET_TRANSPORT_AddressStringCallback asc;
 
 
-/**
- * Network format for IPv6 addresses.
- */
-struct IPv6UdpAddress
-{
   /**
   /**
-   * IPv6 address.
+   * Clsoure for 'asc'.
    */
    */
-  struct in6_addr ipv6_addr;
+  void *asc_cls;
 
   /**
 
   /**
-   * Port number, in network byte order.
+   * Port to add after the IP address.
    */
    */
-  uint16_t u6_port;
-};
-
-/* Forward definition */
-struct Plugin;
-
-struct PrettyPrinterContext
-{
-  GNUNET_TRANSPORT_AddressStringCallback asc;
-  void *asc_cls;
   uint16_t port;
 };
 
   uint16_t port;
 };
 
-struct MessageQueue
+
+struct Session
 {
   /**
 {
   /**
-   * Linked List
+   * Which peer is this session for?
    */
    */
-  struct MessageQueue *next;
+  struct GNUNET_PeerIdentity target;
 
 
-  /**
-   * Session this message belongs to
-   */
-  struct PeerSession *session;
+  struct FragmentationContext * frag_ctx;
 
   /**
 
   /**
-   * Actual message to be sent
+   * Address of the other peer
    */
    */
-  char *msgbuf;
+  const struct sockaddr *sock_addr;
 
   /**
 
   /**
-   * Size of message buffer to be sent
+   * Desired delay for next sending we send to other peer
    */
    */
-  size_t msgbuf_size;
+  struct GNUNET_TIME_Relative flow_delay_for_other_peer;
 
   /**
 
   /**
-   * When to discard this message
+   * Desired delay for next sending we received from other peer
    */
    */
-  struct GNUNET_TIME_Absolute timeout;
+  struct GNUNET_TIME_Absolute flow_delay_from_other_peer;
 
   /**
 
   /**
-   * Continuation to call when this message goes out
+   * Session timeout task
    */
    */
-  GNUNET_TRANSPORT_TransmitContinuation cont;
+  GNUNET_SCHEDULER_TaskIdentifier timeout_task;
 
   /**
 
   /**
-   * closure for continuation
+   * expected delay for ACKs
    */
    */
-  void *cont_cls;
+  struct GNUNET_TIME_Relative last_expected_delay;
 
 
-};
+  struct GNUNET_ATS_Information ats;
 
 
-/**
- * UDP NAT Probe message definition
- */
-struct UDP_NAT_ProbeMessage
-{
-  /**
-   * Message header
-   */
-  struct GNUNET_MessageHeader header;
+  size_t addrlen;
 
 
-};
 
 
-/**
- * UDP NAT Probe message reply definition
- */
-struct UDP_NAT_ProbeMessageReply
-{
-  /**
-   * Message header
-   */
-  struct GNUNET_MessageHeader header;
+  unsigned int rc;
 
 
+  int in_destroy;
 };
 
 
 };
 
 
-/**
- * UDP NAT Probe message confirm definition
- */
-struct UDP_NAT_ProbeMessageConfirmation
+struct SessionCompareContext
 {
 {
-  /**
-   * Message header
-   */
-  struct GNUNET_MessageHeader header;
-
+  struct Session *res;
+  const struct GNUNET_HELLO_Address *addr;
 };
 
 
 /**
 };
 
 
 /**
- * Local network addresses (actual IP address follows this struct).
- * PORT is NOT included!
+ * Closure for 'process_inbound_tokenized_messages'
  */
  */
-struct LocalAddrList
+struct SourceInformation
 {
 {
-  
   /**
   /**
-   * This is a doubly linked list.
+   * Sender identity.
    */
    */
-  struct LocalAddrList *next;
+  struct GNUNET_PeerIdentity sender;
 
   /**
 
   /**
-   * This is a doubly linked list.
+   * Source address.
    */
    */
-  struct LocalAddrList *prev;
+  const void *arg;
 
 
+  struct Session *session;
   /**
   /**
-   * Number of bytes of the address that follow
+   * Number of bytes in source address.
    */
    */
-  size_t size;
+  size_t args;
 
 };
 
 
 /**
 
 };
 
 
 /**
- * UDP NAT "Session"
+ * Closure for 'find_receive_context'.
  */
  */
-struct PeerSession
+struct FindReceiveContext
 {
 {
-
-  /**
-   * Stored in a linked list.
-   */
-  struct PeerSession *next;
-
-  /**
-   * Pointer to the global plugin struct.
-   */
-  struct Plugin *plugin;
-
-  /**
-   * To whom are we talking to (set to our identity
-   * if we are still waiting for the welcome message)
-   */
-  struct GNUNET_PeerIdentity target;
-
   /**
   /**
-   * Address of the other peer (either based on our 'connect'
-   * call or on our 'accept' call).
+   * Where to store the result.
    */
    */
-  void *connect_addr;
+  struct DefragContext *rc;
 
   /**
 
   /**
-   * Length of connect_addr.
+   * Address to find.
    */
    */
-  size_t connect_alen;
+  const struct sockaddr *addr;
 
 
-  /**
-   * Are we still expecting the welcome message? (GNUNET_YES/GNUNET_NO)
-   */
-  int expecting_welcome;
+  struct Session *session;
 
   /**
 
   /**
-   * From which socket do we need to send to this peer?
+   * Number of bytes in 'addr'.
    */
    */
-  struct GNUNET_NETWORK_Handle *sock;
-
-  /*
-   * Queue of messages for this peer, in the case that
-   * we have to await a connection...
-   */
-  struct MessageQueue *messages;
+  socklen_t addr_len;
 
 };
 
 
 };
 
-struct UDP_NAT_Probes
-{
 
 
-  /**
-   * Linked list
-   */
-  struct UDP_NAT_Probes *next;
 
 
-  /**
-   * Address string that the server process returned to us
-   */
-  char *address_string;
+/**
+ * Data structure to track defragmentation contexts based
+ * on the source of the UDP traffic.
+ */
+struct DefragContext
+{
 
   /**
 
   /**
-   * Timeout for this set of probes
+   * Defragmentation context.
    */
    */
-  struct GNUNET_TIME_Absolute timeout;
+  struct GNUNET_DEFRAGMENT_Context *defrag;
 
   /**
 
   /**
-   * Count of how many probes we've attempted
+   * Source address this receive context is for (allocated at the
+   * end of the struct).
    */
    */
-  int count;
+  const struct sockaddr *src_addr;
 
   /**
 
   /**
-   * The plugin this probe belongs to
+   * Reference to master plugin struct.
    */
   struct Plugin *plugin;
 
   /**
    */
   struct Plugin *plugin;
 
   /**
-   * The task used to send these probes
+   * Node in the defrag heap.
    */
    */
-  GNUNET_SCHEDULER_TaskIdentifier task;
+  struct GNUNET_CONTAINER_HeapNode *hnode;
 
   /**
 
   /**
-   * Network address (always ipv4!)
+   * Length of 'src_addr'
    */
    */
-  struct IPv4UdpAddress addr;
-
+  size_t addr_len;
 };
 
 
 };
 
 
+
 /**
 /**
- * Encapsulation of all of the state of the plugin.
+ * Closure for 'process_inbound_tokenized_messages'
  */
  */
-struct Plugin
+struct FragmentationContext
 {
 {
-  /**
-   * Our environment.
-   */
-  struct GNUNET_TRANSPORT_PluginEnvironment *env;
+  struct FragmentationContext * next;
+  struct FragmentationContext * prev;
 
 
-  /**
-   * Handle to the network service.
-   */
-  struct GNUNET_SERVICE_Context *service;
-
-  /*
-   * Session of peers with whom we are currently connected
-   */
-  struct PeerSession *sessions;
+  struct Plugin * plugin;
+  struct GNUNET_FRAGMENT_Context * frag;
+  struct Session * session;
 
   /**
 
   /**
-   * Handle for request of hostname resolution, non-NULL if pending.
+   * Function to call upon completion of the transmission.
    */
    */
-  struct GNUNET_RESOLVER_RequestHandle *hostname_dns;
-
-  /**
-   * ID of task used to update our addresses when one expires.
-   */
-  GNUNET_SCHEDULER_TaskIdentifier address_update_task;
+  GNUNET_TRANSPORT_TransmitContinuation cont;
 
   /**
 
   /**
-   * ID of select task
+   * Closure for 'cont'.
    */
    */
-  GNUNET_SCHEDULER_TaskIdentifier select_task;
+  void *cont_cls;
 
 
-  /**
-   * Port to listen on.
-   */
-  uint16_t port;
+  struct GNUNET_TIME_Absolute timeout;
 
 
-  /**
-   * The external address given to us by the user.  Must be actual
-   * outside visible address for NAT punching to work.
-   */
-  char *external_address;
+  size_t bytes_to_send;
+};
 
 
-  /**
-   * The internal address given to us by the user (or discovered).
-   */
-  char *internal_address;
 
 
-  /**
-   * List of our IP addresses.
-   */
-  struct LocalAddrList *lal_head;
-  
-  /**
-   * Tail of our IP address list.
-   */ 
-  struct LocalAddrList *lal_tail;
+struct UDPMessageWrapper
+{
+  struct Session *session;
+  struct UDPMessageWrapper *prev;
+  struct UDPMessageWrapper *next;
+  char *udp;
 
   /**
 
   /**
-   * FD Read set
+   * Function to call upon completion of the transmission.
    */
    */
-  struct GNUNET_NETWORK_FDSet *rs;
+  GNUNET_TRANSPORT_TransmitContinuation cont;
 
   /**
 
   /**
-   * stdout pipe handle for the gnunet-nat-server process
+   * Closure for 'cont'.
    */
    */
-  struct GNUNET_DISK_PipeHandle *server_stdout;
+  void *cont_cls;
 
 
-  /**
-   * stdout file handle (for reading) for the gnunet-nat-server process
-   */
-  const struct GNUNET_DISK_FileHandle *server_stdout_handle;
+  struct FragmentationContext *frag_ctx;
 
 
-  /**
-   * ID of select gnunet-nat-server stdout read task
-   */
-  GNUNET_SCHEDULER_TaskIdentifier server_read_task;
+  size_t msg_size;
 
 
-  /**
-   * Is this transport configured to be behind a NAT?
-   */
-  int behind_nat;
+  struct GNUNET_TIME_Absolute timeout;
+};
 
 
-  /**
-   * Is this transport configured to allow connections to NAT'd peers?
-   */
-  int allow_nat;
 
 
+/**
+ * UDP ACK Message-Packet header (after defragmentation).
+ */
+struct UDP_ACK_Message
+{
   /**
   /**
-   * Should this transport advertise only NAT addresses (port set to 0)?
-   * If not, all addresses will be duplicated for NAT punching and regular
-   * ports.
+   * Message header.
    */
    */
-  int only_nat_addresses;
+  struct GNUNET_MessageHeader header;
 
   /**
 
   /**
-   * The process id of the server process (if behind NAT)
+   * Desired delay for flow control
    */
    */
-  pid_t server_pid;
+  uint32_t delay;
 
   /**
 
   /**
-   * Probes in flight
+   * What is the identity of the sender
    */
    */
-  struct UDP_NAT_Probes *probes;
+  struct GNUNET_PeerIdentity sender;
 
 };
 
 
 };
 
+/**
+ * Encapsulation of all of the state of the plugin.
+ */
+struct Plugin * plugin;
 
 
-struct UDP_Sock_Info
-{
-  /* The network handle */
-  struct GNUNET_NETWORK_Handle *desc;
 
 
-  /* The port we bound to */
-  int port;
-};
+/**
+ * We have been notified that our readset has something to read.  We don't
+ * know which socket needs to be read, so we have to check each one
+ * Then reschedule this function to be called again once more is available.
+ *
+ * @param cls the plugin handle
+ * @param tc the scheduling context (for rescheduling this function again)
+ */
+static void
+udp_plugin_select (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc);
+
+
+/**
+ * We have been notified that our readset has something to read.  We don't
+ * know which socket needs to be read, so we have to check each one
+ * Then reschedule this function to be called again once more is available.
+ *
+ * @param cls the plugin handle
+ * @param tc the scheduling context (for rescheduling this function again)
+ */
+static void
+udp_plugin_select_v6 (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc);
 
 
-/* *********** globals ************* */
 
 /**
 
 /**
- * the socket that we transmit all data with
+ * Start session timeout
  */
  */
-static struct UDP_Sock_Info udp_sock;
+static void
+start_session_timeout (struct Session *s);
 
 
+/**
+ * Increment session timeout due to activity
+ */
+static void
+reschedule_session_timeout (struct Session *s);
 
 /**
 
 /**
- * Forward declaration.
+ * Cancel timeout
  */
  */
-void
-udp_probe_continuation (void *cls, const struct GNUNET_PeerIdentity *target, int result);
+static void
+stop_session_timeout (struct Session *s);
+
 
 
 /**
 
 
 /**
- * Disconnect from a remote node.  Clean up session if we have one for this peer
+ * Function called for a quick conversion of the binary address to
+ * a numeric address.  Note that the caller must not free the
+ * address and that the next call to this function is allowed
+ * to override the address again.
  *
  *
- * @param cls closure for this call (should be handle to Plugin)
- * @param target the peeridentity of the peer to disconnect
- * @return GNUNET_OK on success, GNUNET_SYSERR if the operation failed
+ * @param cls closure
+ * @param addr binary address
+ * @param addrlen length of the address
+ * @return string representing the same address
  */
  */
-void
-udp_disconnect (void *cls, const struct GNUNET_PeerIdentity *target)
+const char *
+udp_address_to_string (void *cls, const void *addr, size_t addrlen)
 {
 {
-  /** TODO: Implement! */
-  return;
+  static char rbuf[INET6_ADDRSTRLEN + 10];
+  char buf[INET6_ADDRSTRLEN];
+  const void *sb;
+  struct in_addr a4;
+  struct in6_addr a6;
+  const struct IPv4UdpAddress *t4;
+  const struct IPv6UdpAddress *t6;
+  int af;
+  uint16_t port;
+
+  if (addrlen == sizeof (struct IPv6UdpAddress))
+  {
+    t6 = addr;
+    af = AF_INET6;
+    port = ntohs (t6->u6_port);
+    memcpy (&a6, &t6->ipv6_addr, sizeof (a6));
+    sb = &a6;
+  }
+  else if (addrlen == sizeof (struct IPv4UdpAddress))
+  {
+    t4 = addr;
+    af = AF_INET;
+    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);
+  return rbuf;
 }
 
 }
 
+
 /**
 /**
- * Shutdown the server process (stop receiving inbound traffic). Maybe
- * restarted later!
+ * Function called to convert a string address to
+ * a binary address.
  *
  *
- * @param cls Handle to the plugin for this transport
- *
- * @return returns the number of sockets successfully closed,
- *         should equal the number of sockets successfully opened
+ * @param cls closure ('struct Plugin*')
+ * @param addr string address
+ * @param addrlen length of the address
+ * @param buf location to store the buffer
+ * @param added location to store the number of bytes in the buffer.
+ *        If the function returns GNUNET_SYSERR, its contents are undefined.
+ * @return GNUNET_OK on success, GNUNET_SYSERR on failure
  */
 static int
  */
 static int
-udp_transport_server_stop (void *cls)
+udp_string_to_address (void *cls, const char *addr, uint16_t addrlen,
+    void **buf, size_t *added)
 {
 {
-  struct Plugin *plugin = cls;
-  int ret;
-  int ok;
+  struct sockaddr_storage socket_address;
+  
+  if ((NULL == addr) || (0 == addrlen))
+  {
+    GNUNET_break (0);
+    return GNUNET_SYSERR;
+  }
 
 
-  ret = 0;
-  if (plugin->select_task != GNUNET_SCHEDULER_NO_TASK)
-    {
-      GNUNET_SCHEDULER_cancel (plugin->env->sched, plugin->select_task);
-      plugin->select_task = GNUNET_SCHEDULER_NO_TASK;
-    }
+  if ('\0' != addr[addrlen - 1])
+  {
+    return GNUNET_SYSERR;
+  }
+
+  if (strlen (addr) != addrlen - 1)
+  {
+    return GNUNET_SYSERR;
+  }
 
 
-  ok = GNUNET_NETWORK_socket_close (udp_sock.desc);
-  if (ok == GNUNET_OK)
-    udp_sock.desc = NULL;
-  ret += ok;
+  if (GNUNET_OK != GNUNET_STRINGS_to_address_ip (addr, strlen (addr),
+                                                &socket_address))
+  {
+    return GNUNET_SYSERR;
+  }
 
 
-  if (plugin->behind_nat == GNUNET_YES)
+  switch (socket_address.ss_family)
+  {
+  case AF_INET:
     {
     {
-      if (0 != PLIBC_KILL (plugin->server_pid, SIGTERM))
-        {
-          GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, "kill");
-        }
-      GNUNET_OS_process_wait (plugin->server_pid);
+      struct IPv4UdpAddress *u4;
+      struct sockaddr_in *in4 = (struct sockaddr_in *) &socket_address;
+      u4 = GNUNET_malloc (sizeof (struct IPv4UdpAddress));
+      u4->ipv4_addr = in4->sin_addr.s_addr;
+      u4->u4_port = in4->sin_port;
+      *buf = u4;
+      *added = sizeof (struct IPv4UdpAddress);
+      return GNUNET_OK;
     }
     }
-
-  if (ret != GNUNET_OK)
+  case AF_INET6:
+    {
+      struct IPv6UdpAddress *u6;
+      struct sockaddr_in6 *in6 = (struct sockaddr_in6 *) &socket_address;
+      u6 = GNUNET_malloc (sizeof (struct IPv6UdpAddress));
+      u6->ipv6_addr = in6->sin6_addr;
+      u6->u6_port = in6->sin6_port;
+      *buf = u6;
+      *added = sizeof (struct IPv6UdpAddress);
+      return GNUNET_OK;
+    }
+  default:
+    GNUNET_break (0);
     return GNUNET_SYSERR;
     return GNUNET_SYSERR;
-  return ret;
+  }
 }
 
 
 }
 
 
-struct PeerSession *
-find_session (struct Plugin *plugin, const struct GNUNET_PeerIdentity *peer)
+/**
+ * Append our port and forward the result.
+ *
+ * @param cls a 'struct PrettyPrinterContext'
+ * @param hostname result from DNS resolver
+ */
+static void
+append_port (void *cls, const char *hostname)
 {
 {
-  struct PeerSession *pos;
-
-  pos = plugin->sessions;
-  while (pos != NULL)
-    {
-      if (memcmp(&pos->target, peer, sizeof(struct GNUNET_PeerIdentity)) == 0)
-        return pos;
-      pos = pos->next;
-    }
+  struct PrettyPrinterContext *ppc = cls;
+  char *ret;
 
 
-  return pos;
+  if (hostname == NULL)
+  {
+    ppc->asc (ppc->asc_cls, NULL);
+    GNUNET_free (ppc);
+    return;
+  }
+  GNUNET_asprintf (&ret, "%s:%d", hostname, ppc->port);
+  ppc->asc (ppc->asc_cls, ret);
+  GNUNET_free (ret);
 }
 
 
 /**
 }
 
 
 /**
- * Actually send out the message, assume we've got the address and
- * send_handle squared away!
+ * Convert the transports address to a nice, human-readable
+ * format.
  *
  * @param cls closure
  *
  * @param cls closure
- * @param send_handle which handle to send message on
- * @param target who should receive this message (ignored by UDP)
- * @param msgbuf one or more GNUNET_MessageHeader(s) strung together
- * @param msgbuf_size the size of the msgbuf to send
- * @param priority how important is the message (ignored by UDP)
- * @param timeout when should we time out (give up) if we can not transmit?
- * @param addr the addr to send the message to, needs to be a sockaddr for us
- * @param addrlen the len of addr
- * @param cont continuation to call once the message has
- *        been transmitted (or if the transport is ready
- *        for the next transmission call; or if the
- *        peer disconnected...)
- * @param cont_cls closure for cont
- * @return the number of bytes written
- */
-static ssize_t
-udp_real_send (void *cls,
-                  struct GNUNET_NETWORK_Handle *send_handle,
-                  const struct GNUNET_PeerIdentity *target,
-                  const char *msgbuf,
-                  size_t msgbuf_size,
-                  unsigned int priority,
-                  struct GNUNET_TIME_Relative timeout,
-                  const void *addr,
-                  size_t addrlen,
-                  GNUNET_TRANSPORT_TransmitContinuation cont,
-                  void *cont_cls)
+ * @param type name of the transport that generated the address
+ * @param addr one of the addresses of the host, NULL for the last address
+ *        the specific address format depends on the transport
+ * @param addrlen length of the address
+ * @param numeric should (IP) addresses be displayed in numeric form?
+ * @param timeout after how long should we give up?
+ * @param asc function to call on each string
+ * @param asc_cls closure for asc
+ */
+static void
+udp_plugin_address_pretty_printer (void *cls, const char *type,
+                                   const void *addr, size_t addrlen,
+                                   int numeric,
+                                   struct GNUNET_TIME_Relative timeout,
+                                   GNUNET_TRANSPORT_AddressStringCallback asc,
+                                   void *asc_cls)
 {
 {
-  struct Plugin *plugin = cls;
-  struct UDPMessage *message;
-  int ssize;
-  ssize_t sent;
-  struct sockaddr_in a4;
-  struct sockaddr_in6 a6;
-  const struct IPv4UdpAddress *t4;
-  const struct IPv6UdpAddress *t6;
+  struct PrettyPrinterContext *ppc;
   const void *sb;
   size_t sbs;
   const void *sb;
   size_t sbs;
-
-  if ((addr == NULL) || (addrlen == 0))
-    {
-#if DEBUG_UDP
-  GNUNET_log_from (GNUNET_ERROR_TYPE_INFO, "udp", _
-                   ("udp_real_send called without address, returning!\n"));
-#endif
-      if (cont != NULL)
-        cont (cont_cls, target, GNUNET_SYSERR);
-      return 0; /* Can never send if we don't have an address!! */
-    }
-
-  /* Build the message to be sent */
-  message = GNUNET_malloc (sizeof (struct UDPMessage) + msgbuf_size);
-  ssize = sizeof (struct UDPMessage) + msgbuf_size;
-
-  message->header.size = htons (ssize);
-  message->header.type = htons (0);
-  memcpy (&message->sender, plugin->env->my_identity,
-          sizeof (struct GNUNET_PeerIdentity));
-  memcpy (&message[1], msgbuf, msgbuf_size);
+  struct sockaddr_in a4;
+  struct sockaddr_in6 a6;
+  const struct IPv4UdpAddress *u4;
+  const struct IPv6UdpAddress *u6;
+  uint16_t port;
 
   if (addrlen == sizeof (struct IPv6UdpAddress))
 
   if (addrlen == sizeof (struct IPv6UdpAddress))
-    {
-      t6 = addr;
-      memset (&a6, 0, sizeof (a6));
+  {
+    u6 = addr;
+    memset (&a6, 0, sizeof (a6));
+    a6.sin6_family = AF_INET6;
 #if HAVE_SOCKADDR_IN_SIN_LEN
 #if HAVE_SOCKADDR_IN_SIN_LEN
-      a6.sin6_len = sizeof (a6);
+    a6.sin6_len = sizeof (a6);
 #endif
 #endif
-      a6.sin6_family = AF_INET6;
-      a6.sin6_port = t6->u6_port;
-      memcpy (&a6.sin6_addr,
-              &t6->ipv6_addr,
-              sizeof (struct in6_addr));
-      sb = &a6;
-      sbs = sizeof (a6);
-    }
+    a6.sin6_port = u6->u6_port;
+    memcpy (&a6.sin6_addr, &u6->ipv6_addr, sizeof (struct in6_addr));
+    port = ntohs (u6->u6_port);
+    sb = &a6;
+    sbs = sizeof (a6);
+  }
   else if (addrlen == sizeof (struct IPv4UdpAddress))
   else if (addrlen == sizeof (struct IPv4UdpAddress))
-    {
-      t4 = addr;
-      memset (&a4, 0, sizeof (a4));
+  {
+    u4 = addr;
+    memset (&a4, 0, sizeof (a4));
+    a4.sin_family = AF_INET;
 #if HAVE_SOCKADDR_IN_SIN_LEN
 #if HAVE_SOCKADDR_IN_SIN_LEN
-      a4.sin_len = sizeof (a4);
+    a4.sin_len = sizeof (a4);
 #endif
 #endif
-      a4.sin_family = AF_INET;
-      a4.sin_port = t4->u_port;
-      a4.sin_addr.s_addr = t4->ipv4_addr;
-      sb = &a4;
-      sbs = sizeof (a4);
-    }
+    a4.sin_port = u4->u4_port;
+    a4.sin_addr.s_addr = u4->ipv4_addr;
+    port = ntohs (u4->u4_port);
+    sb = &a4;
+    sbs = sizeof (a4);
+  }
+  else if (0 == addrlen)
+  {
+    asc (asc_cls, "<inbound connection>");
+    asc (asc_cls, NULL);
+    return;
+  }
   else
   else
-    {
-      GNUNET_break_op (0);
-      GNUNET_free (message);
-      return -1;
-    }
+  {
+    /* invalid address */
+    GNUNET_break_op (0);
+    asc (asc_cls, NULL);
+    return;
+  }
+  ppc = GNUNET_malloc (sizeof (struct PrettyPrinterContext));
+  ppc->asc = asc;
+  ppc->asc_cls = asc_cls;
+  ppc->port = port;
+  GNUNET_RESOLVER_hostname_get (sb, sbs, !numeric, timeout, &append_port, ppc);
+}
 
 
-  /* Actually send the message */
-  sent =
-    GNUNET_NETWORK_socket_sendto (send_handle, message, ssize,
-                                  sb,
-                                  sbs);
 
 
-  if (cont != NULL)
-    {
-      if (sent == GNUNET_SYSERR)
-        cont (cont_cls, target, GNUNET_SYSERR);
-      else
-        {
-          cont (cont_cls, target, GNUNET_OK);
-        }
-    }
+static void
+call_continuation (struct UDPMessageWrapper *udpw, int result)
+{
+  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),
+      (GNUNET_OK == result) ? "OK" : "SYSERR");
+  if (NULL != udpw->cont)
+  {
+    udpw->cont (udpw->cont_cls, &udpw->session->target,result);
+  }
 
 
-  GNUNET_free (message);
-  return sent;
 }
 
 }
 
+
 /**
 /**
- * We learned about a peer (possibly behind NAT) so run the
- * gnunet-nat-client to send dummy ICMP responses
+ * Check if the given port is plausible (must be either our listen
+ * port or our advertised port).  If it is neither, we return
+ * GNUNET_SYSERR.
  *
  *
- * @param plugin the plugin for this transport
- * @param addr the address of the peer
- * @param addrlen the length of the address
+ * @param plugin global variables
+ * @param in_port port number to check
+ * @return GNUNET_OK if port is either open_port or adv_port
  */
  */
-void
-run_gnunet_nat_client (struct Plugin *plugin, const char *addr, size_t addrlen)
+static int
+check_port (struct Plugin *plugin, uint16_t in_port)
 {
 {
-  char addr_buf[INET_ADDRSTRLEN];
-  char *address_as_string;
-  char *port_as_string;
-  pid_t pid;
-  const struct IPv4UdpAddress *t4;
-
-  GNUNET_assert(addrlen == sizeof(struct IPv4UdpAddress));
-  t4 = (struct IPv4UdpAddress *)addr;
-
-  if (NULL == inet_ntop (AF_INET,
-                         &t4->u_port,
-                         addr_buf, INET_ADDRSTRLEN))
-    {
-      GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, "inet_ntop");
-      return;
-    }
-  address_as_string = GNUNET_strdup (addr_buf);
-  GNUNET_asprintf(&port_as_string, "%d", plugin->port);
-#if DEBUG_UDP
-  GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, "udp",
-                  _("Running gnunet-nat-client with arguments: %s %s %d\n"), plugin->external_address, address_as_string, plugin->port);
-#endif
-
-  /* Start the server process */
-  pid = GNUNET_OS_start_process(NULL, NULL, "gnunet-nat-client", "gnunet-nat-client", plugin->external_address, address_as_string, port_as_string, NULL);
-  GNUNET_free(address_as_string);
-  GNUNET_free(port_as_string);
-  GNUNET_OS_process_wait (pid);
+  if ((in_port == plugin->port) || (in_port == plugin->aport))
+    return GNUNET_OK;
+  return GNUNET_SYSERR;
 }
 
 }
 
+
 /**
 /**
- * Function that can be used by the transport service to transmit
- * a message using the plugin.
+ * Function that will be called to check if a binary address for this
+ * plugin is well-formed and corresponds to an address for THIS peer
+ * (as per our configuration).  Naturally, if absolutely necessary,
+ * plugins can be a bit conservative in their answer, but in general
+ * plugins should make sure that the address does not redirect
+ * traffic to a 3rd party that might try to man-in-the-middle our
+ * traffic.
  *
  *
- * @param cls closure
- * @param target who should receive this message (ignored by UDP)
- * @param msgbuf one or more GNUNET_MessageHeader(s) strung together
- * @param msgbuf_size the size of the msgbuf to send
- * @param priority how important is the message (ignored by UDP)
- * @param timeout when should we time out (give up) if we can not transmit?
- * @param session identifier used for this session (can be NULL)
- * @param addr the addr to send the message to, needs to be a sockaddr for us
- * @param addrlen the len of addr
- * @param force_address not used, we had better have an address to send to
- *        because we are stateless!!
- * @param cont continuation to call once the message has
- *        been transmitted (or if the transport is ready
- *        for the next transmission call; or if the
- *        peer disconnected...)
- * @param cont_cls closure for cont
+ * @param cls closure, should be our handle to the Plugin
+ * @param addr pointer to the address
+ * @param addrlen length of addr
+ * @return GNUNET_OK if this is a plausible address for this peer
+ *         and transport, GNUNET_SYSERR if not
  *
  *
- * @return the number of bytes written (may return 0 and the message can
- *         still be transmitted later!)
  */
  */
-static ssize_t
-udp_plugin_send (void *cls,
-                    const struct GNUNET_PeerIdentity *target,
-                    const char *msgbuf,
-                    size_t msgbuf_size,
-                    unsigned int priority,
-                    struct GNUNET_TIME_Relative timeout,
-                    struct Session *session,
-                    const void *addr,
-                    size_t addrlen,
-                    int force_address,
-                    GNUNET_TRANSPORT_TransmitContinuation cont, void *cont_cls)
+static int
+udp_plugin_check_address (void *cls, const void *addr, size_t addrlen)
 {
   struct Plugin *plugin = cls;
 {
   struct Plugin *plugin = cls;
-  ssize_t sent;
-  struct MessageQueue *temp_message;
-  struct PeerSession *peer_session;
-  int other_peer_natd;
-  const struct IPv4UdpAddress *t4;
-
-  GNUNET_assert (NULL == session);
-
-  other_peer_natd = GNUNET_NO;
-  if (addrlen == sizeof(struct IPv4UdpAddress))
-    {
-      t4 = addr;
-      if (ntohs(t4->u_port) == 0)
-        other_peer_natd = GNUNET_YES;
-    }
-  else if (addrlen != sizeof(struct IPv6UdpAddress))
-    {
-      GNUNET_break_op(0);
-      return -1; /* Must have an address to send to */
-    }
-
-  sent = 0;
+  struct IPv4UdpAddress *v4;
+  struct IPv6UdpAddress *v6;
 
 
-  if ((other_peer_natd == GNUNET_YES) && (plugin->allow_nat == GNUNET_YES))
-    {
-      peer_session = find_session(plugin, target);
-      if (peer_session == NULL) /* We have a new peer to add */
-        {
-          /*
-           * The first time, we can assume we have no knowledge of a
-           * working port for this peer, call the ICMP/UDP message sender
-           * and wait...
-           */
-          peer_session = GNUNET_malloc(sizeof(struct PeerSession));
-          peer_session->connect_addr = GNUNET_malloc(addrlen);
-          memcpy(peer_session->connect_addr, addr, addrlen);
-          peer_session->connect_alen = addrlen;
-          peer_session->plugin = plugin;
-          peer_session->sock = NULL;
-          memcpy(&peer_session->target, target, sizeof(struct GNUNET_PeerIdentity));
-          peer_session->expecting_welcome = GNUNET_YES;
-
-          peer_session->next = plugin->sessions;
-          plugin->sessions = peer_session;
-
-          peer_session->messages = GNUNET_malloc(sizeof(struct MessageQueue));
-          peer_session->messages->msgbuf = GNUNET_malloc(msgbuf_size);
-          memcpy(peer_session->messages->msgbuf, msgbuf, msgbuf_size);
-          peer_session->messages->msgbuf_size = msgbuf_size;
-          peer_session->messages->timeout = GNUNET_TIME_relative_to_absolute(timeout);
-          peer_session->messages->cont = cont;
-          peer_session->messages->cont_cls = cont_cls;
-#if DEBUG_UDP
-          GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, "udp",
-                          _("Other peer is NAT'd, set up peer session for peer %s\n"), GNUNET_i2s(target));
-#endif
-          run_gnunet_nat_client(plugin, addr, addrlen);
-        }
-      else
-        {
-          if (peer_session->expecting_welcome == GNUNET_NO) /* We are "connected" */
-            {
-              sent = udp_real_send(cls, peer_session->sock, target, msgbuf, msgbuf_size, priority, timeout, peer_session->connect_addr, peer_session->connect_alen, cont, cont_cls);
-            }
-          else /* Haven't gotten a response from this peer, queue message */
-            {
-              temp_message = GNUNET_malloc(sizeof(struct MessageQueue));
-              temp_message->msgbuf = GNUNET_malloc(msgbuf_size);
-              memcpy(temp_message->msgbuf, msgbuf, msgbuf_size);
-              temp_message->msgbuf_size = msgbuf_size;
-              temp_message->timeout = GNUNET_TIME_relative_to_absolute(timeout);
-              temp_message->cont = cont;
-              temp_message->cont_cls = cont_cls;
-              temp_message->next = peer_session->messages;
-              peer_session->messages = temp_message;
-            }
-        }
-    }
-  else if (other_peer_natd == GNUNET_NO) /* Other peer not behind a NAT, so we can just send the message as is */
-    {
-      sent = udp_real_send(cls, udp_sock.desc, target, msgbuf, msgbuf_size, priority, timeout, addr, addrlen, cont, cont_cls);
-    }
-  else /* Other peer is NAT'd, but we don't want to play with them (or can't!) */
+  if ((addrlen != sizeof (struct IPv4UdpAddress)) &&
+      (addrlen != sizeof (struct IPv6UdpAddress)))
+  {
+    GNUNET_break_op (0);
     return GNUNET_SYSERR;
     return GNUNET_SYSERR;
-
-  /* When GNUNET_SYSERR is returned from udp_real_send, we will still call
-   * the callback so must not return GNUNET_SYSERR!
-   * If we do, then transport context get freed twice. */
-  if (sent == GNUNET_SYSERR)
-    return 0;
-
-  return sent;
-}
-
-
-static void
-add_to_address_list (struct Plugin *plugin,
-                    const void *arg,
-                    size_t arg_size)
-{
-  struct LocalAddrList *lal;
-
-  lal = plugin->lal_head;
-  while (NULL != lal)
+  }
+  if (addrlen == sizeof (struct IPv4UdpAddress))
+  {
+    v4 = (struct IPv4UdpAddress *) addr;
+    if (GNUNET_OK != check_port (plugin, ntohs (v4->u4_port)))
+      return GNUNET_SYSERR;
+    if (GNUNET_OK !=
+        GNUNET_NAT_test_address (plugin->nat, &v4->ipv4_addr,
+                                 sizeof (struct in_addr)))
+      return GNUNET_SYSERR;
+  }
+  else
+  {
+    v6 = (struct IPv6UdpAddress *) addr;
+    if (IN6_IS_ADDR_LINKLOCAL (&v6->ipv6_addr))
     {
     {
-      if ( (lal->size == arg_size) &&
-          (0 == memcmp (&lal[1], arg, arg_size)) )
-       return;
-      lal = lal->next;
+      GNUNET_break_op (0);
+      return GNUNET_SYSERR;
     }
     }
-  lal = GNUNET_malloc (sizeof (struct LocalAddrList) + arg_size);
-  lal->size = arg_size;
-  memcpy (&lal[1], arg, arg_size);
-  GNUNET_CONTAINER_DLL_insert (plugin->lal_head,
-                              plugin->lal_tail,
-                              lal);
+    if (GNUNET_OK != check_port (plugin, ntohs (v6->u6_port)))
+      return GNUNET_SYSERR;
+    if (GNUNET_OK !=
+        GNUNET_NAT_test_address (plugin->nat, &v6->ipv6_addr,
+                                 sizeof (struct in6_addr)))
+      return GNUNET_SYSERR;
+  }
+  return GNUNET_OK;
 }
 
 
 }
 
 
-static int
-check_local_addr (struct Plugin *plugin,
-                 const void *arg,
-                 size_t arg_size)
+/**
+ * Task to free resources associated with a session.
+ *
+ * @param s session to free
+ */
+static void
+free_session (struct Session *s)
 {
 {
-  struct LocalAddrList *lal;
-
-  lal = plugin->lal_head;
-  while (NULL != lal)
-    {
-      if ( (lal->size == arg_size) &&
-          (0 == memcmp (&lal[1], arg, arg_size)) )
-       return GNUNET_OK;
-      lal = lal->next;
-    }
-  return GNUNET_SYSERR;
+  if (s->frag_ctx != NULL)
+  {
+    GNUNET_FRAGMENT_context_destroy(s->frag_ctx->frag);
+    GNUNET_free (s->frag_ctx);
+    s->frag_ctx = NULL;
+  }
+  GNUNET_free (s);
 }
 
 
 /**
 }
 
 
 /**
- * Add the IP of our network interface to the list of
- * our external IP addresses.
+ * Functions with this signature are called whenever we need
+ * to close a session due to a disconnect or failure to
+ * establish a connection.
+ *
+ * @param s session to close down
  */
  */
-static int
-process_interfaces (void *cls,
-                    const char *name,
-                    int isDefault,
-                    const struct sockaddr *addr, socklen_t addrlen)
+static void
+disconnect_session (struct Session *s)
 {
 {
-  struct Plugin *plugin = cls;
-  int af;
-  struct IPv4UdpAddress t4;
-  struct IPv6UdpAddress t6;
-  void *arg;
-  uint16_t args;
-  void *addr_nat;
-
-  addr_nat = NULL;
-  af = addr->sa_family;
-  if (af == AF_INET)
-    {
-      t4.ipv4_addr = ((struct sockaddr_in *) addr)->sin_addr.s_addr;
-      add_to_address_list (plugin, &t4.ipv4_addr, sizeof (uint32_t));
-      if ((plugin->behind_nat == GNUNET_YES) && (plugin->only_nat_addresses == GNUNET_YES))
-        {
-          t4.u_port = htons (DEFAULT_NAT_PORT);
-        }
-      else if (plugin->behind_nat == GNUNET_YES) /* We are behind NAT, but will advertise NAT and normal addresses */
-        {
-          addr_nat = GNUNET_malloc(sizeof(t4));
-          memcpy(addr_nat, &t4, sizeof(t4));
-          t4.u_port = plugin->port;
-          ((struct IPv4UdpAddress *)addr_nat)->u_port = htons(DEFAULT_NAT_PORT);
-        }
-      else
-        {
-          t4.u_port = htons(plugin->port);
-        }
-      arg = &t4;
-      args = sizeof (t4);
-    }
-  else if (af == AF_INET6)
+  struct UDPMessageWrapper *udpw;
+  struct UDPMessageWrapper *next;
+
+  GNUNET_assert (GNUNET_YES != s->in_destroy);
+  LOG (GNUNET_ERROR_TYPE_DEBUG,
+       "Session %p to peer `%s' address ended \n",
+         s,
+         GNUNET_i2s (&s->target),
+         GNUNET_a2s (s->sock_addr, s->addrlen));
+  stop_session_timeout (s);
+  next = plugin->ipv4_queue_head;
+  while (NULL != (udpw = next))
+  {
+    next = udpw->next;
+    if (udpw->session == s)
     {
     {
-      if (IN6_IS_ADDR_LINKLOCAL (&((struct sockaddr_in6 *) addr)->sin6_addr))
-        {
-          /* skip link local addresses */
-          return GNUNET_OK;
-        }
-      memcpy (&t6.ipv6_addr,
-              &((struct sockaddr_in6 *) addr)->sin6_addr,
-              sizeof (struct in6_addr));
-      add_to_address_list (plugin, &t6.ipv6_addr, sizeof (struct in6_addr));
-      if ((plugin->behind_nat == GNUNET_YES) && (plugin->only_nat_addresses == GNUNET_YES))
-        {
-          t6.u6_port = htons (0);
-        }
-      else if (plugin->behind_nat == GNUNET_YES)
-        {
-          addr_nat = GNUNET_malloc(sizeof(t6));
-          memcpy(addr_nat, &t6, sizeof(t6));
-          t6.u6_port = plugin->port;
-          ((struct IPv6UdpAddress *)addr_nat)->u6_port = htons(DEFAULT_NAT_PORT);
-        }
-      else
-        {
-          t6.u6_port = htons (plugin->port);
-        }
-
-      arg = &t6;
-      args = sizeof (t6);
+      GNUNET_CONTAINER_DLL_remove(plugin->ipv4_queue_head, plugin->ipv4_queue_tail, udpw);
+      call_continuation(udpw, GNUNET_SYSERR);
+      GNUNET_free (udpw);
     }
     }
-  else
+  }
+  next = plugin->ipv6_queue_head;
+  while (NULL != (udpw = next))
+  {
+    next = udpw->next;
+    if (udpw->session == s)
     {
     {
-      GNUNET_break (0);
-      return GNUNET_OK;
+      GNUNET_CONTAINER_DLL_remove(plugin->ipv6_queue_head, plugin->ipv6_queue_tail, udpw);
+      call_continuation(udpw, GNUNET_SYSERR);
+      GNUNET_free (udpw);
     }
     }
-  
-  GNUNET_log (GNUNET_ERROR_TYPE_INFO |
-             GNUNET_ERROR_TYPE_BULK,
-             _("Found address `%s' (%s)\n"),
-             GNUNET_a2s (addr, addrlen), name);
-  
-  if (addr_nat != NULL)
+    udpw = next;
+  }
+  plugin->env->session_end (plugin->env->cls, &s->target, s);
+
+  if (NULL != s->frag_ctx)
+  {
+    if (NULL != s->frag_ctx->cont)
     {
     {
-      plugin->env->notify_address (plugin->env->cls,
-                                  "udp",
-                                  addr_nat, args, GNUNET_TIME_UNIT_FOREVER_REL);
-      GNUNET_log (GNUNET_ERROR_TYPE_INFO |
-                 GNUNET_ERROR_TYPE_BULK,
-                 _("Found NAT address `%s' (%s)\n"),
-                 GNUNET_a2s (addr_nat, args), name);
-      GNUNET_free(addr_nat);
+      s->frag_ctx->cont (s->frag_ctx->cont_cls, &s->target, GNUNET_SYSERR);
+      LOG (GNUNET_ERROR_TYPE_DEBUG,
+          "Calling continuation for fragemented message to `%s' with result SYSERR\n",
+          GNUNET_i2s (&s->target));
     }
     }
-  
-  plugin->env->notify_address (plugin->env->cls,
-                              "udp",
-                              arg, args, GNUNET_TIME_UNIT_FOREVER_REL);
-  return GNUNET_OK;
-}
+  }
 
 
+  GNUNET_assert (GNUNET_YES ==
+                 GNUNET_CONTAINER_multihashmap_remove (plugin->sessions,
+                                                       &s->target.hashPubKey,
+                                                       s));
+  GNUNET_STATISTICS_set(plugin->env->stats,
+                        "# UDP sessions active",
+                        GNUNET_CONTAINER_multihashmap_size(plugin->sessions),
+                        GNUNET_NO);
+  if (s->rc > 0)
+    s->in_destroy = GNUNET_YES;
+  else
+    free_session (s);
+}
 
 /**
 
 /**
- * Function called by the resolver for each address obtained from DNS
- * for our own hostname.  Add the addresses to the list of our
- * external IP addresses.
+ * Destroy a session, plugin is being unloaded.
  *
  *
- * @param cls closure
- * @param addr one of the addresses of the host, NULL for the last address
- * @param addrlen length of the address
+ * @param cls unused
+ * @param key hash of public key of target peer
+ * @param value a 'struct PeerSession*' to clean up
+ * @return GNUNET_OK (continue to iterate)
  */
  */
-static void
-process_hostname_ips (void *cls,
-                      const struct sockaddr *addr, socklen_t addrlen)
+static int
+disconnect_and_free_it (void *cls, const struct GNUNET_HashCode * key, void *value)
 {
 {
-  struct Plugin *plugin = cls;
-
-  if (addr == NULL)
-    {
-      plugin->hostname_dns = NULL;
-      return;
-    }
-  process_interfaces (plugin, "<hostname>", GNUNET_YES, addr, addrlen);
+  disconnect_session(value);
+  return GNUNET_OK;
 }
 
 
 /**
 }
 
 
 /**
- * Send UDP probe messages or UDP keepalive messages, depending on the
- * state of the connection.
+ * Disconnect from a remote node.  Clean up session if we have one for this peer
  *
  *
- * @param cls closure for this call (should be the main Plugin)
- * @param tc task context for running this
+ * @param cls closure for this call (should be handle to Plugin)
+ * @param target the peeridentity of the peer to disconnect
+ * @return GNUNET_OK on success, GNUNET_SYSERR if the operation failed
  */
 static void
  */
 static void
-send_udp_probe_message (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
+udp_disconnect (void *cls, const struct GNUNET_PeerIdentity *target)
 {
 {
-  struct UDP_NAT_Probes *probe = cls;
-  struct UDP_NAT_ProbeMessage *message;
-  struct Plugin *plugin = probe->plugin;
-
-  message = GNUNET_malloc(sizeof(struct UDP_NAT_ProbeMessage));
-  message->header.size = htons(sizeof(struct UDP_NAT_ProbeMessage));
-  message->header.type = htons(GNUNET_MESSAGE_TYPE_TRANSPORT_UDP_NAT_PROBE);
-  /* If they gave us a port, use that.  If not, try our port. */
-  if (ntohs(probe->addr.u_port) == 0)
-    probe->addr.u_port = htons(plugin->port);
-
-#if DEBUG_UDP
-      GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, "udp",
-                      _("Sending a probe to port %d\n"), ntohs(probe->addr.u_port));
-#endif
+  struct Plugin *plugin = cls;
+  GNUNET_assert (plugin != NULL);
+
+  GNUNET_assert (target != NULL);
+  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);
+}
 
 
-  probe->count++;
 
 
-  udp_real_send(plugin, udp_sock.desc, NULL,
-                   (char *)message, ntohs(message->header.size), 0, 
-                   GNUNET_TIME_relative_get_unit(), 
-                   &probe->addr, sizeof(probe->addr),
-                   &udp_probe_continuation, probe);
+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;
 
 
-  GNUNET_free(message);
+  switch (addrlen)
+  {
+  case sizeof (struct IPv4UdpAddress):
+    if (NULL == plugin->sockv4)
+    {
+      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;
+#if HAVE_SOCKADDR_IN_SIN_LEN
+    v4->sin_len = sizeof (struct sockaddr_in);
+#endif
+    v4->sin_port = t4->u4_port;
+    v4->sin_addr.s_addr = t4->ipv4_addr;
+    s->ats = plugin->env->get_address_type (plugin->env->cls, (const struct sockaddr *) v4, sizeof (struct sockaddr_in));
+    break;
+  case sizeof (struct IPv6UdpAddress):
+    if (NULL == plugin->sockv6)
+    {
+      return NULL;
+    }
+    t6 = addr;
+    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;
+#if HAVE_SOCKADDR_IN_SIN_LEN
+    v6->sin6_len = sizeof (struct sockaddr_in6);
+#endif
+    v6->sin6_port = t6->u6_port;
+    v6->sin6_addr = t6->ipv6_addr;
+    s->ats = plugin->env->get_address_type (plugin->env->cls, (const struct sockaddr *) v6, sizeof (struct sockaddr_in6));
+    break;
+  default:
+    /* Must have a valid address to send to */
+    GNUNET_break_op (0);
+    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);
+  return s;
 }
 
 
 }
 
 
-/**
- * Continuation for probe sends.  If the last probe was sent
- * "successfully", schedule sending of another one.  If not,
- *
- */
-void
-udp_probe_continuation (void *cls, const struct GNUNET_PeerIdentity *target, int result)
+static int
+session_cmp_it (void *cls,
+               const struct GNUNET_HashCode * key,
+               void *value)
 {
 {
-  struct UDP_NAT_Probes *probe = cls;
-  struct Plugin *plugin = probe->plugin;
+  struct SessionCompareContext * cctx = cls;
+  const struct GNUNET_HELLO_Address *address = cctx->addr;
+  struct Session *s = value;
+
+  socklen_t s_addrlen = s->addrlen;
 
 
-  if ((result == GNUNET_OK) && (probe->count < MAX_PROBES))
+  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 ((address->address_length == sizeof (struct IPv4UdpAddress)) &&
+      (s_addrlen == sizeof (struct sockaddr_in)))
+  {
+    struct IPv4UdpAddress * u4 = NULL;
+    u4 = (struct IPv4UdpAddress *) address->address;
+    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))
     {
     {
-#if DEBUG_UDP
-      GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, "udp",
-                       _("Scheduling next probe for 10000 milliseconds\n"));
-#endif
-      probe->task = GNUNET_SCHEDULER_add_delayed(plugin->env->sched, GNUNET_TIME_relative_multiply(GNUNET_TIME_UNIT_MILLISECONDS, 10000), &send_udp_probe_message, probe);
+      cctx->res = s;
+      return GNUNET_NO;
     }
     }
-  else /* Destroy the probe context. */
+
+  }
+  if ((address->address_length == sizeof (struct IPv6UdpAddress)) &&
+      (s_addrlen == sizeof (struct sockaddr_in6)))
+  {
+    struct IPv6UdpAddress * u6 = NULL;
+    u6 = (struct IPv6UdpAddress *) address->address;
+    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))
     {
     {
-#if DEBUG_UDP
-      GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, "udp",
-                      _("Sending probe didn't go well...\n"));
-#endif
+      cctx->res = s;
+      return GNUNET_NO;
     }
     }
+  }
+  return GNUNET_YES;
 }
 
 }
 
+
 /**
 /**
- * Find probe message by address
+ * Creates a new outbound session the transport service will use to send data to the
+ * peer
  *
  *
- * @param plugin the plugin for this transport
- * @param address_string the ip address as a string
+ * @param cls the plugin
+ * @param address the address
+ * @return the session or NULL of max connections exceeded
  */
  */
-struct UDP_NAT_Probes *
-find_probe(struct Plugin *plugin, char * address_string)
+static struct Session *
+udp_plugin_get_session (void *cls,
+                  const struct GNUNET_HELLO_Address *address)
 {
 {
-  struct UDP_NAT_Probes *pos;
+  struct Session * s = NULL;
+  struct Plugin * plugin = cls;
+  struct IPv6UdpAddress * udp_a6;
+  struct IPv4UdpAddress * udp_a4;
 
 
-  pos = plugin->probes;
-  while (pos != NULL)
-    if (strcmp(pos->address_string, address_string) == 0)
-      return pos;
+  GNUNET_assert (plugin != NULL);
+  GNUNET_assert (address != NULL);
 
 
-  return pos;
-}
 
 
+  if ((address->address == NULL) ||
+      ((address->address_length != sizeof (struct IPv4UdpAddress)) &&
+      (address->address_length != sizeof (struct IPv6UdpAddress))))
+  {
+    GNUNET_break (0);
+    return NULL;
+  }
 
 
-/*
- * @param cls the plugin handle
- * @param tc the scheduling context (for rescheduling this function again)
- *
- * We have been notified that gnunet-nat-server has written something to stdout.
- * Handle the output, then reschedule this function to be called again once
- * more is available.
+  if (address->address_length == sizeof (struct IPv4UdpAddress))
+  {
+    if (plugin->sockv4 == NULL)
+      return NULL;
+    udp_a4 = (struct IPv4UdpAddress *) address->address;
+    if (udp_a4->u4_port == 0)
+      return NULL;
+  }
+
+  if (address->address_length == sizeof (struct IPv6UdpAddress))
+  {
+    if (plugin->sockv6 == NULL)
+      return NULL;
+    udp_a6 = (struct IPv6UdpAddress *) address->address;
+    if (udp_a6->u6_port == 0)
+      return NULL;
+  }
+
+  /* check if session already exists */
+  struct SessionCompareContext cctx;
+  cctx.addr = address;
+  cctx.res = NULL;
+  LOG (GNUNET_ERROR_TYPE_DEBUG,
+       "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);
+  if (cctx.res != NULL)
+  {
+    LOG (GNUNET_ERROR_TYPE_DEBUG, "Found existing session %p\n", cctx.res);
+    return cctx.res;
+  }
+
+  /* otherwise create new */
+  s = create_session (plugin,
+      &address->peer,
+      address->address,
+      address->address_length,
+      NULL, NULL);
+  LOG (GNUNET_ERROR_TYPE_DEBUG,
+       "Creating new session %p for peer `%s' address `%s'\n",
+       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,
+                                                    s,
+                                                    GNUNET_CONTAINER_MULTIHASHMAPOPTION_MULTIPLE));
+
+  GNUNET_STATISTICS_set(plugin->env->stats,
+                        "# UDP sessions active",
+                        GNUNET_CONTAINER_multihashmap_size(plugin->sessions),
+                        GNUNET_NO);
+
+  return s;
+}
+
+
+static void 
+enqueue (struct Plugin *plugin, struct UDPMessageWrapper * udpw)
+{
+
+  if (udpw->session->addrlen == sizeof (struct sockaddr_in))
+    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);
+}
+
+
+/**
+ * Fragment message was transmitted via UDP, let fragmentation know
+ * to send the next fragment now.
  *
  *
+ * @param cls the 'struct UDPMessageWrapper' of the fragment
+ * @param target destination peer (ignored)
+ * @param result GNUNET_OK on success (ignored)
  */
 static void
  */
 static void
-udp_plugin_server_read (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
+send_next_fragment (void *cls,
+                   const struct GNUNET_PeerIdentity *target,
+                   int result)
 {
 {
-  struct Plugin *plugin = cls;
-  char mybuf[40];
-  ssize_t bytes;
-  memset(&mybuf, 0, sizeof(mybuf));
-  int i;
-  struct UDP_NAT_Probes *temp_probe;
-  int port;
-  char *port_start;
-  struct IPv4UdpAddress a4;
-
-  if (tc->reason == GNUNET_SCHEDULER_REASON_SHUTDOWN)
-    return;
+  struct UDPMessageWrapper *udpw = cls;
+
+  GNUNET_FRAGMENT_context_transmission_done (udpw->frag_ctx->frag);  
+}
 
 
-  bytes = GNUNET_DISK_file_read(plugin->server_stdout_handle, &mybuf, sizeof(mybuf));
 
 
-  if (bytes < 1)
+/**
+ * Function that is called with messages created by the fragmentation
+ * module.  In the case of the 'proc' callback of the
+ * GNUNET_FRAGMENT_context_create function, this function must
+ * eventually call 'GNUNET_FRAGMENT_context_transmission_done'.
+ *
+ * @param cls closure, the 'struct FragmentationContext'
+ * @param msg the message that was created
+ */
+static void
+enqueue_fragment (void *cls, const struct GNUNET_MessageHeader *msg)
+{
+  struct FragmentationContext *frag_ctx = cls;
+  struct Plugin *plugin = frag_ctx->plugin;
+  struct UDPMessageWrapper * udpw;
+  struct Session *s;
+  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);
+  udpw->session = frag_ctx->session;
+  s = udpw->session;
+  udpw->udp = (char *) &udpw[1];
+
+  udpw->msg_size = msg_len;
+  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);
+  enqueue (plugin, udpw);
+
+  if (s->addrlen == sizeof (struct sockaddr_in))
+  {
+    if (plugin->with_v4_ws == GNUNET_NO)
     {
     {
-#if DEBUG_UDP
-      GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, "udp",
-                      _("Finished reading from server stdout with code: %d\n"), bytes);
-#endif
-      return;
-    }
+      if (plugin->select_task != GNUNET_SCHEDULER_NO_TASK)
+        GNUNET_SCHEDULER_cancel(plugin->select_task);
 
 
-  port = 0;
-  port_start = NULL;
-  for (i = 0; i < sizeof(mybuf); i++)
+      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 (mybuf[i] == '\n')
-        mybuf[i] = '\0';
+      if (plugin->select_task_v6 != GNUNET_SCHEDULER_NO_TASK)
+        GNUNET_SCHEDULER_cancel(plugin->select_task_v6);
 
 
-      if ((mybuf[i] == ':') && (i + 1 < sizeof(mybuf)))
-        {
-          mybuf[i] = '\0';
-          port_start = &mybuf[i + 1];
-        }
+      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;
     }
     }
+  }
+}
+
 
 
-  if (port_start != NULL)
-    port = atoi(port_start);
+/**
+ * Function that can be used by the transport service to transmit
+ * a message using the plugin.   Note that in the case of a
+ * peer disconnecting, the continuation MUST be called
+ * prior to the disconnect notification itself.  This function
+ * will be called with this peer's HELLO message to initiate
+ * a fresh connection to another peer.
+ *
+ * @param cls closure
+ * @param s which session must be used
+ * @param msgbuf the message to transmit
+ * @param msgbuf_size number of bytes in 'msgbuf'
+ * @param priority how important is the message (most plugins will
+ *                 ignore message priority and just FIFO)
+ * @param to how long to wait at most for the transmission (does not
+ *                require plugins to discard the message after the timeout,
+ *                just advisory for the desired delay; most plugins will ignore
+ *                this as well)
+ * @param cont continuation to call once the message has
+ *        been transmitted (or if the transport is ready
+ *        for the next transmission call; or if the
+ *        peer disconnected...); can be NULL
+ * @param cont_cls closure for cont
+ * @return number of bytes used (on the physical network, with overheads);
+ *         -1 on hard errors (i.e. address invalid); 0 is a legal value
+ *         and does NOT mean that the message was not transmitted (DV)
+ */
+static ssize_t
+udp_plugin_send (void *cls,
+                  struct Session *s,
+                  const char *msgbuf, size_t msgbuf_size,
+                  unsigned int priority,
+                  struct GNUNET_TIME_Relative to,
+                  GNUNET_TRANSPORT_TransmitContinuation cont, void *cont_cls)
+{
+  struct Plugin *plugin = cls;
+  size_t mlen = msgbuf_size + sizeof (struct UDPMessage);
+  struct UDPMessageWrapper * udpw;
+  struct UDPMessage *udp;
+  char mbuf[mlen];
+  GNUNET_assert (plugin != NULL);
+  GNUNET_assert (s != NULL);
+
+  if ((s->addrlen == sizeof (struct sockaddr_in6)) && (plugin->sockv6 == NULL))
+    return GNUNET_SYSERR;
+  if ((s->addrlen == sizeof (struct sockaddr_in)) && (plugin->sockv4 == NULL))
+    return GNUNET_SYSERR;
+  if (mlen >= 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))
+  {
+    GNUNET_break (0);
+    return GNUNET_SYSERR;
+  }
+  LOG (GNUNET_ERROR_TYPE_DEBUG,
+       "UDP transmits %u-byte message to `%s' using address `%s'\n",
+       mlen,
+       GNUNET_i2s (&s->target),
+       GNUNET_a2s(s->sock_addr, s->addrlen));
+  
+  /* Message */
+  udp = (struct UDPMessage *) mbuf;
+  udp->header.size = htons (mlen);
+  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)
+  {
+    udpw = GNUNET_malloc (sizeof (struct UDPMessageWrapper) + mlen);
+    udpw->session = s;
+    udpw->udp = (char *) &udpw[1];
+    udpw->msg_size = mlen;
+    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);
+
+    enqueue (plugin, udpw);
+  }
   else
   else
+  {
+    LOG (GNUNET_ERROR_TYPE_DEBUG,
+         "UDP has to fragment message \n");
+    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->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->frag = GNUNET_FRAGMENT_context_create (plugin->env->stats,
+              UDP_MTU,
+              &plugin->tracker,
+              s->last_expected_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)
     {
     {
-      plugin->server_read_task =
-           GNUNET_SCHEDULER_add_read_file (plugin->env->sched,
-                                           GNUNET_TIME_UNIT_FOREVER_REL,
-                                           plugin->server_stdout_handle, &udp_plugin_server_read, plugin);
-      return;
+      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;
     }
     }
+  }
 
 
-#if DEBUG_UDP
-  GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, "udp",
-                  _("nat-server-read read: %s port %d\n"), &mybuf, port);
-#endif
+  return mlen;
+}
 
 
-  /**
-   * We have received an ICMP response, ostensibly from a non-NAT'd peer
-   *  that wants to connect to us! Send a message to establish a connection.
-   */
-  if (inet_pton(AF_INET, &mybuf[0], &a4.ipv4_addr) != 1)
-    {
 
 
-      GNUNET_log_from (GNUNET_ERROR_TYPE_WARNING, "udp",
-                  _("nat-server-read malformed address\n"), &mybuf, port);
+/**
+ * Our external IP address/port mapping has changed.
+ *
+ * @param cls closure, the 'struct LocalAddrList'
+ * @param add_remove GNUNET_YES to mean the new public IP address, GNUNET_NO to mean
+ *     the previous (now invalid) one
+ * @param addr either the previous or the new public IP address
+ * @param addrlen actual lenght of the address
+ */
+static void
+udp_nat_port_map_callback (void *cls, int add_remove,
+                           const struct sockaddr *addr, socklen_t addrlen)
+{
+  struct Plugin *plugin = cls;
+  struct IPv4UdpAddress u4;
+  struct IPv6UdpAddress u6;
+  void *arg;
+  size_t args;
 
 
-      plugin->server_read_task =
-          GNUNET_SCHEDULER_add_read_file (plugin->env->sched,
-                                          GNUNET_TIME_UNIT_FOREVER_REL,
-                                          plugin->server_stdout_handle, &udp_plugin_server_read, plugin);
-      return;
-    }
+  /* convert 'addr' to our internal format */
+  switch (addr->sa_family)
+  {
+  case AF_INET:
+    GNUNET_assert (addrlen == sizeof (struct sockaddr_in));
+    u4.ipv4_addr = ((struct sockaddr_in *) addr)->sin_addr.s_addr;
+    u4.u4_port = ((struct sockaddr_in *) addr)->sin_port;
+    arg = &u4;
+    args = sizeof (u4);
+    break;
+  case AF_INET6:
+    GNUNET_assert (addrlen == sizeof (struct sockaddr_in6));
+    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);
+    break;
+  default:
+    GNUNET_break (0);
+    return;
+  }
+  /* modify our published address list */
+  plugin->env->notify_address (plugin->env->cls, add_remove, arg, args);
+}
 
 
-  temp_probe = find_probe(plugin, &mybuf[0]);
 
 
-  if (temp_probe == NULL)
-    {
-      temp_probe = GNUNET_malloc(sizeof(struct UDP_NAT_Probes));
-      temp_probe->address_string = strdup(&mybuf[0]);
-      GNUNET_assert (1 == inet_pton(AF_INET, &mybuf[0], &temp_probe->addr.ipv4_addr));
-      temp_probe->addr.u_port = htons(port);
-      temp_probe->next = plugin->probes;
-      temp_probe->plugin = plugin;
-      temp_probe->task = GNUNET_SCHEDULER_add_delayed(plugin->env->sched, GNUNET_TIME_relative_multiply(GNUNET_TIME_UNIT_MILLISECONDS, 500), &send_udp_probe_message, temp_probe);
-      plugin->probes = temp_probe;
-    }
 
 
-  plugin->server_read_task =
-       GNUNET_SCHEDULER_add_read_file (plugin->env->sched,
-                                       GNUNET_TIME_UNIT_FOREVER_REL,
-                                       plugin->server_stdout_handle, &udp_plugin_server_read, plugin);
+/**
+ * Message tokenizer has broken up an incomming message. Pass it on
+ * to the service.
+ *
+ * @param cls the 'struct Plugin'
+ * @param client the 'struct SourceInformation'
+ * @param hdr the actual message
+ */
+static int
+process_inbound_tokenized_messages (void *cls, void *client,
+                                    const struct GNUNET_MessageHeader *hdr)
+{
+  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);
+  delay = plugin->env->receive (plugin->env->cls,
+                               &si->sender,
+                               hdr,
+                               (const struct GNUNET_ATS_Information *) &ats, 2,
+                               si->session,
+                               si->arg,
+                               si->args);
+  si->session->flow_delay_for_other_peer = delay;
+  reschedule_session_timeout(si->session);
+  return GNUNET_OK;
 }
 
 
 /**
 }
 
 
 /**
- * Demultiplexer for UDP NAT messages
+ * We've received a UDP Message.  Process it (pass contents to main service).
  *
  *
- * @param plugin the main plugin for this transport
- * @param sender from which peer the message was received
- * @param currhdr pointer to the header of the message
- * @param sender_addr the address from which the message was received
- * @param fromlen the length of the address
- * @param sockinfo which socket did we receive the message on
+ * @param plugin plugin context
+ * @param msg the message
+ * @param sender_addr sender address
+ * @param sender_addr_len number of bytes in sender_addr
  */
 static void
  */
 static void
-udp_demultiplexer(struct Plugin *plugin, struct GNUNET_PeerIdentity *sender,
-                  const struct GNUNET_MessageHeader *currhdr,
-                  const void *sender_addr,
-                  size_t fromlen, struct UDP_Sock_Info *sockinfo)
+process_udp_message (struct Plugin *plugin, const struct UDPMessage *msg,
+                     const struct sockaddr *sender_addr,
+                     socklen_t sender_addr_len)
 {
 {
-  struct UDP_NAT_ProbeMessageReply *outgoing_probe_reply;
-  struct UDP_NAT_ProbeMessageConfirmation *outgoing_probe_confirmation;
+  struct SourceInformation si;
+  struct Session * s;
+  struct IPv4UdpAddress u4;
+  struct IPv6UdpAddress u6;
+  const void *arg;
+  size_t args;
+
+  if (0 != ntohl (msg->reserved))
+  {
+    GNUNET_break_op (0);
+    return;
+  }
+  if (ntohs (msg->header.size) <
+      sizeof (struct GNUNET_MessageHeader) + sizeof (struct UDPMessage))
+  {
+    GNUNET_break_op (0);
+    return;
+  }
 
 
-  char addr_buf[INET_ADDRSTRLEN];
-  struct UDP_NAT_Probes *outgoing_probe;
-  struct PeerSession *peer_session;
-  struct MessageQueue *pending_message;
-  struct MessageQueue *pending_message_temp;
-  uint16_t incoming_port;
+  /* convert address */
+  switch (sender_addr->sa_family)
+  {
+  case AF_INET:
+    GNUNET_assert (sender_addr_len == sizeof (struct sockaddr_in));
+    u4.ipv4_addr = ((struct sockaddr_in *) sender_addr)->sin_addr.s_addr;
+    u4.u4_port = ((struct sockaddr_in *) sender_addr)->sin_port;
+    arg = &u4;
+    args = sizeof (u4);
+    break;
+  case AF_INET6:
+    GNUNET_assert (sender_addr_len == sizeof (struct sockaddr_in6));
+    u6.ipv6_addr = ((struct sockaddr_in6 *) sender_addr)->sin6_addr;
+    u6.u6_port = ((struct sockaddr_in6 *) sender_addr)->sin6_port;
+    arg = &u6;
+    args = sizeof (u6);
+    break;
+  default:
+    GNUNET_break (0);
+    return;
+  }
+  LOG (GNUNET_ERROR_TYPE_DEBUG,
+       "Received message with %u bytes from peer `%s' at `%s'\n",
+       (unsigned int) ntohs (msg->header.size), GNUNET_i2s (&msg->sender),
+       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);
+  GNUNET_free (address);
+
+  /* iterate over all embedded messages */
+  si.session = s;
+  si.sender = msg->sender;
+  si.arg = arg;
+  si.args = args;
+  s->rc++;
+  GNUNET_SERVER_mst_receive (plugin->mst, &si, (const char *) &msg[1],
+                             ntohs (msg->header.size) -
+                             sizeof (struct UDPMessage), GNUNET_YES, GNUNET_NO);
+  s->rc--;
+  if ( (0 == s->rc) && (GNUNET_YES == s->in_destroy))
+    free_session (s);
+}
 
 
-  if (memcmp(sender, plugin->env->my_identity, sizeof(struct GNUNET_PeerIdentity)) == 0)
-    {
-#if DEBUG_UDP
-      GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, "udp",
-                      _("Received a message from myself, dropping!!!\n"));
-#endif
-      return;
-    }
 
 
-  incoming_port = 0;
-  GNUNET_assert(sender_addr != NULL); /* Can recvfrom have a NULL address? */
-  if (fromlen == sizeof(struct IPv4UdpAddress))
-    {
-      incoming_port = ntohs(((struct IPv4UdpAddress *)sender_addr)->u_port);
-    }
-  else if (fromlen == sizeof(struct IPv6UdpAddress))
-    {
-      incoming_port = ntohs(((struct IPv6UdpAddress *)sender_addr)->u6_port);
-    }
+/**
+ * Scan the heap for a receive context with the given address.
+ *
+ * @param cls the 'struct FindReceiveContext'
+ * @param node internal node of the heap
+ * @param element value stored at the node (a 'struct ReceiveContext')
+ * @param cost cost associated with the node
+ * @return GNUNET_YES if we should continue to iterate,
+ *         GNUNET_NO if not.
+ */
+static int
+find_receive_context (void *cls, struct GNUNET_CONTAINER_HeapNode *node,
+                      void *element, GNUNET_CONTAINER_HeapCostType cost)
+{
+  struct FindReceiveContext *frc = cls;
+  struct DefragContext *e = element;
 
 
-  switch (ntohs(currhdr->type))
+  if ((frc->addr_len == e->addr_len) &&
+      (0 == memcmp (frc->addr, e->src_addr, frc->addr_len)))
   {
   {
-    case GNUNET_MESSAGE_TYPE_TRANSPORT_UDP_NAT_PROBE:
-      /* Send probe reply */
-      outgoing_probe_reply = GNUNET_malloc(sizeof(struct UDP_NAT_ProbeMessageReply));
-      outgoing_probe_reply->header.size = htons(sizeof(struct UDP_NAT_ProbeMessageReply));
-      outgoing_probe_reply->header.type = htons(GNUNET_MESSAGE_TYPE_TRANSPORT_UDP_NAT_PROBE_REPLY);
-
-#if DEBUG_UDP
-      GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, "udp",
-                      _("Received a probe on listen port %d, sent_from port %d\n"), sockinfo->port, incoming_port);
-#endif
+    frc->rc = e;
+    return GNUNET_NO;
+  }
+  return GNUNET_YES;
+}
 
 
-      udp_real_send(plugin, sockinfo->desc, NULL,
-                    (char *)outgoing_probe_reply,
-                    ntohs(outgoing_probe_reply->header.size), 0,
-                    GNUNET_TIME_relative_get_unit(),
-                    sender_addr, fromlen,
-                    NULL, NULL);
 
 
-#if DEBUG_UDP
-      GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, "udp",
-                      _("Sent PROBE REPLY to port %d on outgoing port %d\n"), incoming_port, sockinfo->port);
-#endif
-      GNUNET_free(outgoing_probe_reply);
-      break;
-    case GNUNET_MESSAGE_TYPE_TRANSPORT_UDP_NAT_PROBE_REPLY:
-      /* Check for existing probe, check ports returned, send confirmation if all is well */
-#if DEBUG_UDP
-      GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, "udp",
-                      _("Received PROBE REPLY from port %d on incoming port %d\n"), incoming_port, sockinfo->port);
-#endif
-      if (sizeof(sender_addr) == sizeof(struct IPv4UdpAddress))
-        {
-          memset(&addr_buf, 0, sizeof(addr_buf));
-          if (NULL == inet_ntop (AF_INET, 
-                                &((struct IPv4UdpAddress *) sender_addr)->ipv4_addr, addr_buf,
-                                INET_ADDRSTRLEN))
-           {
-             GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, "inet_ntop");
-             return;
-           }
-          outgoing_probe = find_probe(plugin, &addr_buf[0]);
-          if (outgoing_probe != NULL)
-            {
-#if DEBUG_UDP
-              GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, "udp",
-                              _("Sending confirmation that we were reached!\n"));
-#endif
-              outgoing_probe_confirmation = GNUNET_malloc(sizeof(struct UDP_NAT_ProbeMessageConfirmation));
-              outgoing_probe_confirmation->header.size = htons(sizeof(struct UDP_NAT_ProbeMessageConfirmation));
-              outgoing_probe_confirmation->header.type = htons(GNUNET_MESSAGE_TYPE_TRANSPORT_UDP_NAT_PROBE_CONFIRM);
-
-              udp_real_send(plugin, sockinfo->desc, NULL, (char *)outgoing_probe_confirmation, ntohs(outgoing_probe_confirmation->header.size), 0, GNUNET_TIME_relative_get_unit(), sender_addr, fromlen, NULL, NULL);
-
-              if (outgoing_probe->task != GNUNET_SCHEDULER_NO_TASK)
-                {
-                  GNUNET_SCHEDULER_cancel(plugin->env->sched, outgoing_probe->task);
-                  outgoing_probe->task = GNUNET_SCHEDULER_NO_TASK;
-                  /* Schedule task to timeout and remove probe if confirmation not received */
-                }
-              GNUNET_free(outgoing_probe_confirmation);
-            }
-          else
-            {
-#if DEBUG_UDP
-              GNUNET_log_from (GNUNET_ERROR_TYPE_INFO, "udp",
-                              _("Received a probe reply, but have no record of a sent probe!\n"));
-#endif
-            }
-        }
-      break;
-    case GNUNET_MESSAGE_TYPE_TRANSPORT_UDP_NAT_PROBE_CONFIRM:
-      peer_session = find_session(plugin, sender);
-#if DEBUG_UDP
-          GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, "udp",
-                          _("Looking up peer session for peer %s\n"), GNUNET_i2s(sender));
-#endif
-      if (peer_session == NULL) /* Shouldn't this NOT happen? */
-        {
-#if DEBUG_UDP
-          GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, "udp",
-                          _("Peer not in list, adding (THIS MAY BE A MISTAKE) %s\n"), GNUNET_i2s(sender));
-#endif
-          peer_session = GNUNET_malloc(sizeof(struct PeerSession));
-          peer_session->connect_addr = GNUNET_malloc(fromlen);
-          memcpy(peer_session->connect_addr, sender_addr, fromlen);
-          peer_session->connect_alen = fromlen;
-          peer_session->plugin = plugin;
-          peer_session->sock = sockinfo->desc;
-          memcpy(&peer_session->target, sender, sizeof(struct GNUNET_PeerIdentity));
-          peer_session->expecting_welcome = GNUNET_NO;
-
-          peer_session->next = plugin->sessions;
-          plugin->sessions = peer_session;
-
-          peer_session->messages = NULL;
-        }
-      else if (peer_session->expecting_welcome == GNUNET_YES)
-        {
-          peer_session->expecting_welcome = GNUNET_NO;
-          peer_session->sock = sockinfo->desc;
-          if (peer_session->connect_alen == sizeof(struct IPv4UdpAddress))
-            {
-              ((struct IPv4UdpAddress *)peer_session->connect_addr)->u_port = htons(incoming_port);
-            }
-          else if (peer_session->connect_alen == sizeof(struct IPv4UdpAddress))
-            {
-              ((struct IPv6UdpAddress *)peer_session->connect_addr)->u6_port = htons(incoming_port);
-            }
-
-#if DEBUG_UDP
-              GNUNET_log_from (GNUNET_ERROR_TYPE_INFO, "udp",
-                              _("Received a probe confirmation, will send to peer on port %d\n"), incoming_port);
-#endif
-          if (peer_session->messages != NULL)
-            {
-#if DEBUG_UDP
-              GNUNET_log_from (GNUNET_ERROR_TYPE_INFO, "udp",
-                              _("Received a probe confirmation, sending queued messages.\n"));
-#endif
-              pending_message = peer_session->messages;
-              int count = 0;
-              while (pending_message != NULL)
-                {
-#if DEBUG_UDP
-                  GNUNET_log_from (GNUNET_ERROR_TYPE_INFO, "udp",
-                                  _("sending queued message %d\n"), count);
-#endif
-                  udp_real_send(plugin,
-                                peer_session->sock,
-                                &peer_session->target,
-                                pending_message->msgbuf,
-                                pending_message->msgbuf_size, 0,
-                                GNUNET_TIME_relative_get_unit(),
-                                peer_session->connect_addr,
-                                peer_session->connect_alen,
-                                pending_message->cont,
-                                pending_message->cont_cls);
-
-                  pending_message_temp = pending_message;
-                  pending_message = pending_message->next;
-                  GNUNET_free(pending_message_temp->msgbuf);
-                  GNUNET_free(pending_message_temp);
-#if DEBUG_UDP
-                  GNUNET_log_from (GNUNET_ERROR_TYPE_INFO, "udp",
-                                  _("finished sending queued message %d\n"), count);
-#endif
-                  count++;
-                }
-            }
+/**
+ * Process a defragmented message.
+ *
+ * @param cls the 'struct ReceiveContext'
+ * @param msg the message
+ */
+static void
+fragment_msg_proc (void *cls, const struct GNUNET_MessageHeader *msg)
+{
+  struct DefragContext *rc = cls;
 
 
-        }
-      else
-        {
-#if DEBUG_UDP
-          GNUNET_log_from (GNUNET_ERROR_TYPE_INFO, "udp",
-                          _("Received probe confirmation for already confirmed peer!\n"));
-#endif
-        }
-      /* Received confirmation, add peer with address/port specified */
-      break;
-    case GNUNET_MESSAGE_TYPE_TRANSPORT_UDP_NAT_PROBE_KEEPALIVE:
-      /* Once we've sent NAT_PROBE_CONFIRM change to sending keepalives */
-      /* If we receive these just ignore! */
-      break;
-    default:
-#if DEBUG_UDP
-      GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
-                       _("Sending message type %d to transport\n!"), ntohs(currhdr->type));
-#endif
-      plugin->env->receive (plugin->env->cls, sender, currhdr, UDP_DIRECT_DISTANCE, 
-                           NULL, sender_addr, fromlen);
+  if (ntohs (msg->type) != GNUNET_MESSAGE_TYPE_TRANSPORT_UDP_MESSAGE)
+  {
+    GNUNET_break (0);
+    return;
   }
   }
+  if (ntohs (msg->size) < sizeof (struct UDPMessage))
+  {
+    GNUNET_break (0);
+    return;
+  }
+  process_udp_message (rc->plugin, (const struct UDPMessage *) msg,
+                       rc->src_addr, rc->addr_len);
+}
+
+
+struct LookupContext
+{
+  const struct sockaddr * addr;
+
+  struct Session *res;
+
+  size_t addrlen;
+};
+
+
+static int
+lookup_session_by_addr_it (void *cls, const struct GNUNET_HashCode * key, void *value)
+{
+  struct LookupContext *l_ctx = cls;
+  struct Session * s = value;
 
 
+  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;
 }
 
 
 }
 
 
-/*
- * @param cls the plugin handle
- * @param tc the scheduling context (for rescheduling this function again)
- *
- * We have been notified that our writeset has something to read.  We don't
- * know which socket needs to be read, so we have to check each one
- * Then reschedule this function to be called again once more is available.
+/**
+ * Transmit an acknowledgement.
  *
  *
+ * @param cls the 'struct ReceiveContext'
+ * @param id message ID (unused)
+ * @param msg ack to transmit
  */
 static void
  */
 static void
-udp_plugin_select (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
+ack_proc (void *cls, uint32_t id, const struct GNUNET_MessageHeader *msg)
 {
 {
-  struct Plugin *plugin = cls;
-  char *buf;
-  struct UDPMessage *msg;
-  struct GNUNET_PeerIdentity *sender;
-  unsigned int buflen;
-  socklen_t fromlen;
-  char addr[32];
-  ssize_t ret;
-  int offset;
-  int count;
-  int tsize;
-  char *msgbuf;
-  const struct GNUNET_MessageHeader *currhdr;
-  struct IPv4UdpAddress t4;
-  struct IPv6UdpAddress t6;
-  const struct sockaddr_in *s4;
-  const struct sockaddr_in6 *s6;
-  const void *ca;
-  size_t calen;
+  struct DefragContext *rc = cls;
+  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 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,
+      &lookup_session_by_addr_it,
+      &l_ctx);
+  s = l_ctx.res;
+
+  if (NULL == s)
+    return;
 
 
+  if (s->flow_delay_for_other_peer.rel_value <= UINT32_MAX)
+    delay = s->flow_delay_for_other_peer.rel_value;
+
+  LOG (GNUNET_ERROR_TYPE_DEBUG,
+       "Sending ACK to `%s' including delay of %u ms\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);
+  udpw->msg_size = msize;
+  udpw->session = s;
+  udpw->timeout = GNUNET_TIME_UNIT_FOREVER_ABS;
+  udpw->udp = (char *)&udpw[1];
+  udp_ack = (struct UDP_ACK_Message *) udpw->udp;
+  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);
+}
 
 
-  plugin->select_task = GNUNET_SCHEDULER_NO_TASK;
 
 
-  if (tc->reason == GNUNET_SCHEDULER_REASON_SHUTDOWN)
+static void 
+read_process_msg (struct Plugin *plugin,
+                 const struct GNUNET_MessageHeader *msg,
+                 const char *addr,
+                 socklen_t fromlen)
+{
+  if (ntohs (msg->size) < sizeof (struct UDPMessage))
+  {
+    GNUNET_break_op (0);
     return;
     return;
+  }
+  process_udp_message (plugin, (const struct UDPMessage *) msg,
+                       (const struct sockaddr *) addr, fromlen);
+}
+
 
 
-  buf = NULL;
-  sender = NULL;
+static void 
+read_process_ack (struct Plugin *plugin,
+                 const struct GNUNET_MessageHeader *msg,
+                 char *addr,
+                 socklen_t fromlen)
+{
+  const struct GNUNET_MessageHeader *ack;
+  const struct UDP_ACK_Message *udp_ack;
+  struct LookupContext l_ctx;
+  struct Session *s;
+  struct GNUNET_TIME_Relative flow_delay;
+
+  if (ntohs (msg->size) <
+      sizeof (struct UDP_ACK_Message) + sizeof (struct GNUNET_MessageHeader))
+  {
+    GNUNET_break_op (0);
+    return;
+  }
+  udp_ack = (const struct UDP_ACK_Message *) msg;
+  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);
+  s = l_ctx.res;
+
+  if ((s == NULL) || (s->frag_ctx == NULL))
+    return;
 
 
-  buflen = GNUNET_NETWORK_socket_recvfrom_amount (udp_sock.desc);
+  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);
+  s->flow_delay_from_other_peer =
+      GNUNET_TIME_relative_to_absolute (flow_delay);
 
 
-  if (buflen == GNUNET_NO)
+  ack = (const struct GNUNET_MessageHeader *) &udp_ack[1];
+  if (ntohs (ack->size) !=
+      ntohs (msg->size) - sizeof (struct UDP_ACK_Message))
+  {
+    GNUNET_break_op (0);
     return;
     return;
+  }
 
 
-  buf = GNUNET_malloc (buflen);
-  fromlen = sizeof (addr);
-  memset (&addr, 0, sizeof(addr));
-  ret =
-    GNUNET_NETWORK_socket_recvfrom (udp_sock.desc, buf, buflen,
-                                    (struct sockaddr *)&addr, &fromlen);
+  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));
+    return;
+  }
 
 
-  if (AF_INET == ((struct sockaddr *)addr)->sa_family)
-    {
-      s4 = (const struct sockaddr_in*) &addr;
-      t4.u_port = s4->sin_port;
-      t4.ipv4_addr = s4->sin_addr.s_addr;
-      ca = &t4;
-      calen = sizeof (t4);
-    }
-  else if (AF_INET6 == ((struct sockaddr *)addr)->sa_family)
-    {
-      s6 = (const struct sockaddr_in6*) &addr;
-      t6.u6_port = s6->sin6_port;
-      memcpy (&t6.ipv6_addr,
-              &s6->sin6_addr,
-              sizeof (struct in6_addr));
-      ca = &t6;
-      calen = sizeof (t6);
-    }
-  else
-    {
-      GNUNET_break (0);
-      ca = NULL;
-      calen = 0;
-    }
+  LOG (GNUNET_ERROR_TYPE_DEBUG,
+       "FULL MESSAGE ACKed\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);
 
 
-  if (ret <= 0)
+  struct UDPMessageWrapper * udpw;
+  struct UDPMessageWrapper * tmp;
+  if (s->addrlen == sizeof (struct sockaddr_in6))
+  {
+    udpw = plugin->ipv6_queue_head;
+    while (NULL != udpw)
     {
     {
-      GNUNET_free (buf);
-      return;
+      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;
     }
     }
-  msg = (struct UDPMessage *) buf;
-
-  if (ntohs (msg->header.size) < sizeof (struct UDPMessage))
+  }
+  if (s->addrlen == sizeof (struct sockaddr_in))
+  {
+    udpw = plugin->ipv4_queue_head;
+    while (udpw!= NULL)
     {
     {
-      GNUNET_free (buf);
-      return;
+      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;
     }
     }
+  }
 
 
-  msgbuf = (char *)&msg[1];
-  sender = GNUNET_malloc (sizeof (struct GNUNET_PeerIdentity));
-  memcpy (sender, &msg->sender, sizeof (struct GNUNET_PeerIdentity));
-
-  offset = 0;
-  count = 0;
-  tsize = ntohs (msg->header.size) - sizeof(struct UDPMessage);
+  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);
+  }
 
 
-  while (offset < tsize)
-    {
-      currhdr = (struct GNUNET_MessageHeader *)&msgbuf[offset];
-      udp_demultiplexer(plugin, sender, currhdr, ca, calen, &udp_sock);
-      offset += ntohs(currhdr->size);
-      count++;
-    }
-  GNUNET_free_non_null (buf);
-  GNUNET_free_non_null (sender);
+  GNUNET_free (s->frag_ctx);
+  s->frag_ctx = NULL;
+}
 
 
 
 
-  plugin->select_task =
-    GNUNET_SCHEDULER_add_select (plugin->env->sched,
-                                 GNUNET_SCHEDULER_PRIORITY_DEFAULT,
-                                 GNUNET_SCHEDULER_NO_TASK,
-                                 GNUNET_TIME_UNIT_FOREVER_REL, plugin->rs,
-                                 NULL, &udp_plugin_select, plugin);
+static void 
+read_process_fragment (struct Plugin *plugin,
+                      const struct GNUNET_MessageHeader *msg,
+                      char *addr,
+                      socklen_t fromlen)
+{
+  struct DefragContext *d_ctx;
+  struct GNUNET_TIME_Absolute now;
+  struct FindReceiveContext frc;
+
+  frc.rc = NULL;
+  frc.addr = (const struct sockaddr *) 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));
+  /* Lookup existing receive context for this address */
+  GNUNET_CONTAINER_heap_iterate (plugin->defrag_ctxs,
+                                 &find_receive_context,
+                                 &frc);
+  now = GNUNET_TIME_absolute_get ();
+  d_ctx = frc.rc;
+
+  if (d_ctx == NULL)
+  {
+    /* Create a new defragmentation context */
+    d_ctx = GNUNET_malloc (sizeof (struct DefragContext) + fromlen);
+    memcpy (&d_ctx[1], addr, fromlen);
+    d_ctx->src_addr = (const struct sockaddr *) &d_ctx[1];
+    d_ctx->addr_len = fromlen;
+    d_ctx->plugin = plugin;
+    d_ctx->defrag =
+        GNUNET_DEFRAGMENT_context_create (plugin->env->stats, UDP_MTU,
+                                          UDP_MAX_MESSAGES_IN_DEFRAG, d_ctx,
+                                          &fragment_msg_proc, &ack_proc);
+    d_ctx->hnode =
+        GNUNET_CONTAINER_heap_insert (plugin->defrag_ctxs, d_ctx,
+                                      (GNUNET_CONTAINER_HeapCostType)
+                                      now.abs_value);
+    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));
+  }
+  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));
+  }
 
 
+  if (GNUNET_OK == GNUNET_DEFRAGMENT_process_fragment (d_ctx->defrag, msg))
+  {
+    /* keep this 'rc' from expiring */
+    GNUNET_CONTAINER_heap_update_cost (plugin->defrag_ctxs, d_ctx->hnode,
+                                       (GNUNET_CONTAINER_HeapCostType)
+                                       now.abs_value);
+  }
+  if (GNUNET_CONTAINER_heap_get_size (plugin->defrag_ctxs) >
+      UDP_MAX_SENDER_ADDRESSES_WITH_DEFRAG)
+  {
+    /* remove 'rc' that was inactive the longest */
+    d_ctx = GNUNET_CONTAINER_heap_remove_root (plugin->defrag_ctxs);
+    GNUNET_assert (NULL != d_ctx);
+    GNUNET_DEFRAGMENT_context_destroy (d_ctx->defrag);
+    GNUNET_free (d_ctx);
+  }
 }
 
 }
 
+
 /**
 /**
- * Create a slew of UDP sockets.  If possible, use IPv6, otherwise
- * try IPv4.
+ * Read and process a message from the given socket.
  *
  *
- * @param cls closure for server start, should be a struct Plugin *
- *
- * @return number of sockets created or GNUNET_SYSERR on error
+ * @param plugin the overall plugin
+ * @param rsock socket to read from
  */
  */
-static int
-udp_transport_server_start (void *cls)
+static void
+udp_select_read (struct Plugin *plugin, struct GNUNET_NETWORK_Handle *rsock)
 {
 {
-  struct Plugin *plugin = cls;
-  struct sockaddr_in serverAddrv4;
-  struct sockaddr_in6 serverAddrv6;
-  struct sockaddr *serverAddr;
-  socklen_t addrlen;
-  int sockets_created;
-
-  sockets_created = 0;
+  socklen_t fromlen;
+  char addr[32];
+  char buf[65536] GNUNET_ALIGN;
+  ssize_t size;
+  const struct GNUNET_MessageHeader *msg;
 
 
-  if (plugin->behind_nat == GNUNET_YES)
-    {
-      /* Pipe to read from started processes stdout (on read end) */
-      plugin->server_stdout = GNUNET_DISK_pipe(GNUNET_YES);
-      if (plugin->server_stdout == NULL)
-        return sockets_created;
-#if DEBUG_UDP
-  GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG,
-                   "udp",
-                   "Starting gnunet-nat-server process cmd: %s %s\n", "gnunet-nat-server", plugin->internal_address);
-#endif
-      /* Start the server process */
-      plugin->server_pid = GNUNET_OS_start_process(NULL, plugin->server_stdout, "gnunet-nat-server", "gnunet-nat-server", plugin->internal_address, NULL);
-      if (plugin->server_pid == GNUNET_SYSERR)
-        {
-#if DEBUG_UDP
-          GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG,
-                           "udp",
-                           "Failed to start gnunet-nat-server process\n");
+  fromlen = sizeof (addr);
+  memset (&addr, 0, sizeof (addr));
+  size = GNUNET_NETWORK_socket_recvfrom (rsock, buf, sizeof (buf),
+                                      (struct sockaddr *) &addr, &fromlen);
+#if MINGW
+  /* On SOCK_DGRAM UDP sockets recvfrom might fail with a
+   * WSAECONNRESET error to indicate that previous sendto() (???)
+   * on this socket has failed.
+   */
+  if ( (-1 == size) && (ECONNRESET == errno) )
+    return;
 #endif
 #endif
-          return GNUNET_SYSERR;
-        }
-      /* Close the write end of the read pipe */
-      GNUNET_DISK_pipe_close_end(plugin->server_stdout, GNUNET_DISK_PIPE_END_WRITE);
-
-      plugin->server_stdout_handle = GNUNET_DISK_pipe_handle(plugin->server_stdout, GNUNET_DISK_PIPE_END_READ);
-      plugin->server_read_task =
-          GNUNET_SCHEDULER_add_read_file (plugin->env->sched,
-                                          GNUNET_TIME_UNIT_FOREVER_REL,
-                                          plugin->server_stdout_handle, &udp_plugin_server_read, plugin);
-    }
+  if ( (-1 == size) || (size < sizeof (struct GNUNET_MessageHeader)))
+  {
+    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));
+
+  if (size != ntohs (msg->size))
+  {
+    GNUNET_break_op (0);
+    return;
+  }
+
+  switch (ntohs (msg->type))
+  {
+  case GNUNET_MESSAGE_TYPE_TRANSPORT_BROADCAST_BEACON:
+    udp_broadcast_receive (plugin, &buf, size, addr, fromlen);
+    return;
+
+  case GNUNET_MESSAGE_TYPE_TRANSPORT_UDP_MESSAGE:
+    read_process_msg (plugin, msg, addr, fromlen);
+    return;
+
+  case GNUNET_MESSAGE_TYPE_TRANSPORT_UDP_ACK:
+    read_process_ack (plugin, msg, addr, fromlen);
+    return;
 
 
-    udp_sock.desc = NULL;
+  case GNUNET_MESSAGE_TYPE_FRAGMENT:
+    read_process_fragment (plugin, msg, addr, fromlen);
+    return;
 
 
+  default:
+    GNUNET_break_op (0);
+    return;
+  }
+}
 
 
-    udp_sock.desc = GNUNET_NETWORK_socket_create (PF_INET, SOCK_DGRAM, 17);
-    if (NULL == udp_sock.desc)
+
+static size_t
+udp_select_send (struct Plugin *plugin, struct GNUNET_NETWORK_Handle *sock)
+{
+  ssize_t sent;
+  size_t slen;
+  struct GNUNET_TIME_Absolute max;
+  struct UDPMessageWrapper *udpw = NULL;
+  static int network_down_error;
+
+  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());
+
+  while (udpw != NULL)
+  {
+    if (max.abs_value != udpw->timeout.abs_value)
+    {
+      /* Message timed out */
+      call_continuation(udpw, GNUNET_SYSERR);
+      if (udpw->frag_ctx != NULL)
       {
       {
-        GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, "udp", "socket");
-        return sockets_created;
+        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
+      else
       {
       {
-        memset (&serverAddrv4, 0, sizeof (serverAddrv4));
-#if HAVE_SOCKADDR_IN_SIN_LEN
-        serverAddrv4.sin_len = sizeof (serverAddrv4);
-#endif
-        serverAddrv4.sin_family = AF_INET;
-        serverAddrv4.sin_addr.s_addr = INADDR_ANY;
-        serverAddrv4.sin_port = htons (plugin->port);
-        addrlen = sizeof (serverAddrv4);
-        serverAddr = (struct sockaddr *) &serverAddrv4;
-#if DEBUG_UDP
-        GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG,
-                         "udp",
-                         "Binding to port %d\n", ntohs(serverAddrv4.sin_port));
-#endif
-        while (GNUNET_NETWORK_socket_bind (udp_sock.desc, serverAddr, addrlen) !=
-                       GNUNET_OK)
-          {
-            serverAddrv4.sin_port = htons (GNUNET_CRYPTO_random_u32(GNUNET_CRYPTO_QUALITY_STRONG, 33537) + 32000); /* Find a good, non-root port */
-#if DEBUG_UDP
-        GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG,
-                        "udp",
-                        "Binding failed, trying new port %d\n", ntohs(serverAddrv4.sin_port));
-#endif
-          }
-        udp_sock.port = ntohs(serverAddrv4.sin_port);
-        sockets_created++;
+        LOG (GNUNET_ERROR_TYPE_DEBUG, 
+            "Message for peer `%s' with size %u timed out\n",
+            GNUNET_i2s(&udpw->session->target), udpw->msg_size);
       }
 
       }
 
-
-  if ((udp_sock.desc == NULL) && (GNUNET_YES !=
-      GNUNET_CONFIGURATION_get_value_yesno (plugin->env->cfg, "GNUNETD",
-                                            "DISABLE-IPV6")))
+      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
     {
     {
-      udp_sock.desc = GNUNET_NETWORK_socket_create (PF_INET6, SOCK_DGRAM, 17);
-      if (udp_sock.desc != NULL)
-        {
-          memset (&serverAddrv6, 0, sizeof (serverAddrv6));
-#if HAVE_SOCKADDR_IN_SIN_LEN
-          serverAddrv6.sin6_len = sizeof (serverAddrv6);
-#endif
-          serverAddrv6.sin6_family = AF_INET6;
-          serverAddrv6.sin6_addr = in6addr_any;
-          serverAddrv6.sin6_port = htons (plugin->port);
-          addrlen = sizeof (serverAddrv6);
-          serverAddr = (struct sockaddr *) &serverAddrv6;
-          sockets_created++;
-        }
+      struct GNUNET_TIME_Relative delta = GNUNET_TIME_absolute_get_remaining (udpw->session->flow_delay_from_other_peer);
+      if (delta.rel_value == 0)
+      {
+        /* 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;
+      }
+      else
+      {
+        /* this 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);
+        udpw = udpw->next;
+      }
     }
     }
+  }
 
 
-  plugin->rs = GNUNET_NETWORK_fdset_create ();
+  if (udpw == NULL)
+  {
+    /* No message left */
+    return 0;
+  }
 
 
-  GNUNET_NETWORK_fdset_zero (plugin->rs);
+  sent = GNUNET_NETWORK_socket_sendto (sock, udpw->udp, 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);
 
 
-  GNUNET_NETWORK_fdset_set (plugin->rs, udp_sock.desc);
+    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 message to `%s': "
+            "Please check your network configuration and disable IPv6 if your "
+            "connection does not have a global IPv6 address\n"),
+          GNUNET_a2s (sa, slen));
+      }
+    }
+    else
+    {
+      LOG (GNUNET_ERROR_TYPE_WARNING,
+         "UDP could not transmit %u-byte message to `%s': `%s'\n",
+         (unsigned int) (udpw->msg_size), GNUNET_a2s (sa, slen),
+         STRERROR (errno));
+    }
+    call_continuation(udpw, GNUNET_SYSERR);
+  }
+  else
+  {
+    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,
+         (sent < 0) ? STRERROR (errno) : "ok");
+    call_continuation(udpw, GNUNET_OK);
+    network_down_error = GNUNET_NO;
+  }
 
 
-  plugin->select_task =
-    GNUNET_SCHEDULER_add_select (plugin->env->sched,
-                                 GNUNET_SCHEDULER_PRIORITY_DEFAULT,
-                                 GNUNET_SCHEDULER_NO_TASK,
-                                 GNUNET_TIME_UNIT_FOREVER_REL, plugin->rs,
-                                 NULL, &udp_plugin_select, plugin);
+  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_free (udpw);
+  udpw = NULL;
 
 
-  return sockets_created;
+  return sent;
 }
 
 
 }
 
 
-
 /**
 /**
- * Check if the given port is plausible (must be either
- * our listen port or our advertised port).  If it is
- * neither, we return GNUNET_SYSERR.
+ * We have been notified that our readset has something to read.  We don't
+ * know which socket needs to be read, so we have to check each one
+ * Then reschedule this function to be called again once more is available.
  *
  *
- * @param plugin global variables
- * @param in_port port number to check
- * @return GNUNET_OK if port is either open_port or adv_port
+ * @param cls the plugin handle
+ * @param tc the scheduling context (for rescheduling this function again)
  */
  */
-static int
-check_port (struct Plugin *plugin, uint16_t in_port)
+static void
+udp_plugin_select (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
 {
 {
-  if ( (plugin->behind_nat == GNUNET_YES) && (in_port == 0) )
-    return GNUNET_OK;
-  if ( (plugin->only_nat_addresses == GNUNET_YES) &&
-       (plugin->behind_nat == GNUNET_YES) &&
-       (in_port != 0) )
-    return GNUNET_SYSERR; /* odd case... */
-  if (in_port == plugin->port) 
-    return GNUNET_OK;
-  return GNUNET_SYSERR;
+  struct Plugin *plugin = cls;
+
+  plugin->select_task = GNUNET_SCHEDULER_NO_TASK;
+  if ((tc->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN) != 0)
+    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;
 }
 
 
 /**
 }
 
 
 /**
- * Function that will be called to check if a binary address for this
- * plugin is well-formed and corresponds to an address for THIS peer
- * (as per our configuration).  Naturally, if absolutely necessary,
- * plugins can be a bit conservative in their answer, but in general
- * plugins should make sure that the address does not redirect
- * traffic to a 3rd party that might try to man-in-the-middle our
- * traffic.
- *
- * @param cls closure, should be our handle to the Plugin
- * @param addr pointer to the address
- * @param addrlen length of addr
- * @return GNUNET_OK if this is a plausible address for this peer
- *         and transport, GNUNET_SYSERR if not
+ * We have been notified that our readset has something to read.  We don't
+ * know which socket needs to be read, so we have to check each one
+ * Then reschedule this function to be called again once more is available.
  *
  *
+ * @param cls the plugin handle
+ * @param tc the scheduling context (for rescheduling this function again)
  */
  */
+static void
+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)
+    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;
+}
+
+
 static int
 static int
-udp_check_address (void *cls, 
-                  const void *addr, 
-                  size_t addrlen)
+setup_sockets (struct Plugin *plugin, struct sockaddr_in6 *serverAddrv6, struct sockaddr_in *serverAddrv4)
 {
 {
-  struct Plugin *plugin = cls;
-  struct IPv4UdpAddress *v4;
-  struct IPv6UdpAddress *v6;
+  int tries;
+  int sockets_created = 0;
+  struct sockaddr *serverAddr;
+  struct sockaddr *addrs[2];
+  socklen_t addrlens[2];
+  socklen_t addrlen;
 
 
-  if ((addrlen != sizeof (struct IPv4UdpAddress)) &&
-      (addrlen != sizeof (struct IPv6UdpAddress)))
+  /* Create IPv6 socket */
+  if (plugin->enable_ipv6 == GNUNET_YES)
+  {
+    plugin->sockv6 = GNUNET_NETWORK_socket_create (PF_INET6, SOCK_DGRAM, 0);
+    if (NULL == plugin->sockv6)
     {
     {
-      GNUNET_break_op (0);
-      return GNUNET_SYSERR;
+      LOG (GNUNET_ERROR_TYPE_WARNING, "Disabling IPv6 since it is not supported on this system!\n");
+      plugin->enable_ipv6 = GNUNET_NO;
     }
     }
-  if (addrlen == sizeof (struct IPv4UdpAddress))
+    else
     {
     {
-      v4 = (struct IPv4UdpAddress *) addr;
-      if (GNUNET_OK !=
-         check_port (plugin, ntohs (v4->u_port)))
-       return GNUNET_SYSERR;
-      if (GNUNET_OK !=
-         check_local_addr (plugin, &v4->ipv4_addr, sizeof (uint32_t)))
-       return GNUNET_SYSERR;
+#if HAVE_SOCKADDR_IN_SIN_LEN
+      serverAddrv6->sin6_len = sizeof (serverAddrv6);
+#endif
+      serverAddrv6->sin6_family = AF_INET6;
+      serverAddrv6->sin6_addr = in6addr_any;
+      serverAddrv6->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));
+      tries = 0;
+      while (GNUNET_NETWORK_socket_bind (plugin->sockv6, serverAddr, addrlen) !=
+             GNUNET_OK)
+      {
+        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;
+        }
+      }
+      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;
+        addrlens[sockets_created] = sizeof (struct sockaddr_in6);
+        sockets_created++;
+      }
     }
     }
+  }
+
+  /* Create IPv4 socket */
+  plugin->sockv4 = GNUNET_NETWORK_socket_create (PF_INET, SOCK_DGRAM, 0);
+  if (NULL == plugin->sockv4)
+  {
+    GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, "socket");
+  }
   else
   else
+  {
+#if HAVE_SOCKADDR_IN_SIN_LEN
+    serverAddrv4->sin_len = sizeof (serverAddrv4);
+#endif
+    serverAddrv4->sin_family = AF_INET;
+    serverAddrv4->sin_addr.s_addr = INADDR_ANY;
+    serverAddrv4->sin_port = htons (plugin->port);
+    addrlen = sizeof (struct sockaddr_in);
+    serverAddr = (struct sockaddr *) serverAddrv4;
+
+    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)
     {
     {
-      v6 = (struct IPv6UdpAddress *) addr;
-      if (IN6_IS_ADDR_LINKLOCAL (&v6->ipv6_addr))
-       {
-         GNUNET_break_op (0);
-         return GNUNET_SYSERR;
-       }
-      if (GNUNET_OK != 
-         check_port (plugin, ntohs (v6->u6_port)))
-       return GNUNET_SYSERR;
-      if (GNUNET_OK !=
-         check_local_addr (plugin, &v6->ipv6_addr, sizeof (struct in6_addr)))
-       return GNUNET_SYSERR;
+      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)
+      {
+        GNUNET_NETWORK_socket_close (plugin->sockv4);
+        plugin->sockv4 = NULL;
+        break;
+      }
     }
     }
-#if DEBUG_UDP
-  GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG,
-                   "udp",
-                   "Informing transport service about my address `%s'.\n",
-                   GNUNET_a2s (addr, addrlen));
-#endif
-  return GNUNET_OK;
-}
+    if (plugin->sockv4 != NULL)
+    {
+      addrs[sockets_created] = (struct sockaddr *) serverAddrv4;
+      addrlens[sockets_created] = sizeof (struct sockaddr_in);
+      sockets_created++;
+    }
+  }
 
 
+  /* 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)
+  {
+    GNUNET_NETWORK_fdset_set (plugin->rs_v4, plugin->sockv4);
+    GNUNET_NETWORK_fdset_set (plugin->ws_v4, plugin->sockv4);
+  }
 
 
-/**
- * Append our port and forward the result.
- */
-static void
-append_port (void *cls, const char *hostname)
-{
-  struct PrettyPrinterContext *ppc = cls;
-  char *ret;
+  if (sockets_created == 0)
+    LOG (GNUNET_ERROR_TYPE_WARNING, _("Failed to open UDP sockets\n"));
 
 
-  if (hostname == NULL)
+  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;
+
+  if (plugin->enable_ipv6 == GNUNET_YES)
+  {
+    plugin->rs_v6 = GNUNET_NETWORK_fdset_create ();
+    plugin->ws_v6 = GNUNET_NETWORK_fdset_create ();
+    GNUNET_NETWORK_fdset_zero (plugin->rs_v6);
+    GNUNET_NETWORK_fdset_zero (plugin->ws_v6);
+    if (NULL != plugin->sockv6)
     {
     {
-      ppc->asc (ppc->asc_cls, NULL);
-      GNUNET_free (ppc);
-      return;
+      GNUNET_NETWORK_fdset_set (plugin->rs_v6, plugin->sockv6);
+      GNUNET_NETWORK_fdset_set (plugin->ws_v6, plugin->sockv6);
     }
     }
-  GNUNET_asprintf (&ret, "%s:%d", hostname, ppc->port);
-  ppc->asc (ppc->asc_cls, ret);
-  GNUNET_free (ret);
-}
 
 
+    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;
+  }
+
+  plugin->nat = GNUNET_NAT_register (plugin->env->cfg,
+                           GNUNET_NO, plugin->port,
+                           sockets_created,
+                           (const struct sockaddr **) addrs, addrlens,
+                           &udp_nat_port_map_callback, NULL, plugin);
+
+  return sockets_created;
+}
 
 /**
 
 /**
- * Convert the transports address to a nice, human-readable
- * format.
- *
- * @param cls closure
- * @param type name of the transport that generated the address
- * @param addr one of the addresses of the host, NULL for the last address
- *        the specific address format depends on the transport
- * @param addrlen length of the address
- * @param numeric should (IP) addresses be displayed in numeric form?
- * @param timeout after how long should we give up?
- * @param asc function to call on each string
- * @param asc_cls closure for asc
+ * Session was idle, so disconnect it
  */
 static void
  */
 static void
-udp_plugin_address_pretty_printer (void *cls,
-                                   const char *type,
-                                   const void *addr,
-                                   size_t addrlen,
-                                   int numeric,
-                                   struct GNUNET_TIME_Relative timeout,
-                                   GNUNET_TRANSPORT_AddressStringCallback asc,
-                                   void *asc_cls)
+session_timeout (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
 {
 {
-  struct Plugin *plugin = cls;
-  const struct sockaddr_in *v4;
-  const struct sockaddr_in6 *v6;
-  struct PrettyPrinterContext *ppc;
-
-  if ((addrlen != sizeof (struct sockaddr_in)) &&
-      (addrlen != sizeof (struct sockaddr_in6)))
-    {
-      /* invalid address */
-      GNUNET_break_op (0);
-      asc (asc_cls, NULL);
-      return;
-    }
-  ppc = GNUNET_malloc (sizeof (struct PrettyPrinterContext));
-  ppc->asc = asc;
-  ppc->asc_cls = asc_cls;
-  if (addrlen == sizeof (struct sockaddr_in))
-    {
-      v4 = (const struct sockaddr_in *) addr;
-      ppc->port = ntohs (v4->sin_port);
-    }
-  else
-    {
-      v6 = (const struct sockaddr_in6 *) addr;
-      ppc->port = ntohs (v6->sin6_port);
-
-    }
-  GNUNET_RESOLVER_hostname_get (plugin->env->sched,
-                                plugin->env->cfg,
-                                addr,
-                                addrlen,
-                                !numeric, timeout, &append_port, ppc);
+  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 ms, disconnecting\n",
+              s, (unsigned long long) GNUNET_CONSTANTS_IDLE_CONNECTION_TIMEOUT.rel_value);
+  /* call session destroy function */
+  disconnect_session(s);
 }
 
 }
 
+
 /**
 /**
- * Return the actual path to a file found in the current
- * PATH environment variable.
- *
- * @param binary the name of the file to find
+ * Start session timeout
  */
  */
-static char *
-get_path_from_PATH (char *binary)
+static void
+start_session_timeout (struct Session *s)
 {
 {
-  char *path;
-  char *pos;
-  char *end;
-  char *buf;
-  const char *p;
-
-  p = getenv ("PATH");
-  if (p == NULL)
-    return NULL;
-  path = GNUNET_strdup (p);     /* because we write on it */
-  buf = GNUNET_malloc (strlen (path) + 20);
-  pos = path;
-
-  while (NULL != (end = strchr (pos, ':')))
-    {
-      *end = '\0';
-      sprintf (buf, "%s/%s", pos, binary);
-      if (GNUNET_DISK_file_test (buf) == GNUNET_YES)
-        {
-          GNUNET_free (path);
-          return buf;
-        }
-      pos = end + 1;
-    }
-  sprintf (buf, "%s/%s", pos, binary);
-  if (GNUNET_DISK_file_test (buf) == GNUNET_YES)
-    {
-      GNUNET_free (path);
-      return buf;
-    }
-  GNUNET_free (buf);
-  GNUNET_free (path);
-  return NULL;
+  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 ms\n",
+              s,  (unsigned long long) GNUNET_CONSTANTS_IDLE_CONNECTION_TIMEOUT.rel_value);
 }
 
 }
 
+
 /**
 /**
- * Check whether the suid bit is set on a file.
- * Attempts to find the file using the current
- * PATH environment variable as a search path.
- *
- * @param binary the name of the file to check
+ * Increment session timeout due to activity
  */
  */
-static int
-check_gnunet_nat_binary(char *binary)
+static void
+reschedule_session_timeout (struct Session *s)
 {
 {
-  struct stat statbuf;
-  char *p;
-
-  p = get_path_from_PATH (binary);
-  if (p == NULL)
-    return GNUNET_NO;
-  if (0 != STAT (p, &statbuf))
-    {
-      GNUNET_free (p);
-      return GNUNET_SYSERR;
-    }
-  GNUNET_free (p);
-  if ( (0 != (statbuf.st_mode & S_ISUID)) &&
-       (statbuf.st_uid == 0) )
-    return GNUNET_YES;
-  return GNUNET_NO;
+  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 ms\n",
+              s, (unsigned long long) GNUNET_CONSTANTS_IDLE_CONNECTION_TIMEOUT.rel_value);
 }
 
 }
 
+
 /**
 /**
- * Function called for a quick conversion of the binary address to
- * a numeric address.  Note that the caller must not free the
- * address and that the next call to this function is allowed
- * to override the address again.
- *
- * @param cls closure
- * @param addr binary address
- * @param addrlen length of the address
- * @return string representing the same address
+ * Cancel timeout
  */
  */
-static const char*
-udp_address_to_string (void *cls,
-                       const void *addr,
-                       size_t addrlen)
+static void
+stop_session_timeout (struct Session *s)
 {
 {
-  static char rbuf[INET6_ADDRSTRLEN + 10];
-  char buf[INET6_ADDRSTRLEN];
-  const void *sb;
-  struct in_addr a4;
-  struct in6_addr a6;
-  const struct IPv4UdpAddress *t4;
-  const struct IPv6UdpAddress *t6;
-  int af;
-  uint16_t port;
+  GNUNET_assert (NULL != s);
 
 
-  if (addrlen == sizeof (struct IPv6UdpAddress))
-    {
-      t6 = addr;
-      af = AF_INET6;
-      port = ntohs (t6->u6_port);
-      memcpy (&a6, &t6->ipv6_addr, sizeof (a6));
-      sb = &a6;
-    }
-  else if (addrlen == sizeof (struct IPv4UdpAddress))
-    {
-      t4 = addr;
-      af = AF_INET;
-      port = ntohs (t4->u_port);
-      memcpy (&a4, &t4->ipv4_addr, sizeof (a4));
-      sb = &a4;
-    }
-  else
-    return NULL;
-  inet_ntop (af, sb, buf, INET6_ADDRSTRLEN);
-  GNUNET_snprintf (rbuf,
-                   sizeof (rbuf),
-                   "%s:%u",
-                   buf,
-                   port);
-  return rbuf;
+  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, (unsigned long long) GNUNET_CONSTANTS_IDLE_CONNECTION_TIMEOUT.rel_value);
+  }
 }
 
 }
 
+
+
 /**
  * The exported method. Makes the core api available via a global and
  * returns the udp transport API.
 /**
  * The exported method. Makes the core api available via a global and
  * returns the udp transport API.
+ *
+ * @param cls our 'struct GNUNET_TRANSPORT_PluginEnvironment'
+ * @return our 'struct GNUNET_TRANSPORT_PluginFunctions'
  */
 void *
 libgnunet_plugin_transport_udp_init (void *cls)
 {
   struct GNUNET_TRANSPORT_PluginEnvironment *env = cls;
  */
 void *
 libgnunet_plugin_transport_udp_init (void *cls)
 {
   struct GNUNET_TRANSPORT_PluginEnvironment *env = cls;
-  unsigned long long mtu;
-  unsigned long long port;
   struct GNUNET_TRANSPORT_PluginFunctions *api;
   struct GNUNET_TRANSPORT_PluginFunctions *api;
-  struct Plugin *plugin;
-  struct GNUNET_SERVICE_Context *service;
-  int sockets_created;
-  int behind_nat;
-  int allow_nat;
-  int only_nat_addresses;
-  char *internal_address;
-  char *external_address;
-  struct IPv4UdpAddress v4_address;
-
-  service = GNUNET_SERVICE_start ("transport-udp", env->sched, env->cfg);
-  if (service == NULL)
-    {
-      GNUNET_log_from (GNUNET_ERROR_TYPE_WARNING, "udp", _
-                       ("Failed to start service for `%s' transport plugin.\n"),
-                       "udp");
-      return NULL;
-    }
+  struct Plugin *p;
+  unsigned long long port;
+  unsigned long long aport;
+  unsigned long long broadcast;
+  unsigned long long udp_max_bps;
+  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;
+  int res;
 
 
-  if (GNUNET_YES == GNUNET_CONFIGURATION_get_value_yesno (env->cfg,
-                                                         "transport-udp",
-                                                         "BEHIND_NAT"))
-    {
-      /* We are behind nat (according to the user) */
-      if (check_gnunet_nat_binary("gnunet-nat-server") == GNUNET_YES)
-        behind_nat = GNUNET_YES;
-      else
-        {
-          behind_nat = GNUNET_NO;
-          GNUNET_log_from (GNUNET_ERROR_TYPE_WARNING, "udp", "Configuration specified you are behind a NAT, but gnunet-nat-server is not installed properly (suid bit not set)!\n");
-        }
-    }
-  else
-    behind_nat = GNUNET_NO; /* We are not behind nat! */
+  if (NULL == env->receive)
+  {
+    /* run in 'stub' mode (i.e. as part of gnunet-peerinfo), don't fully
+       initialze the plugin or the API */
+    api = GNUNET_malloc (sizeof (struct GNUNET_TRANSPORT_PluginFunctions));
+    api->cls = NULL;
+    api->address_pretty_printer = &udp_plugin_address_pretty_printer;
+    api->address_to_string = &udp_address_to_string;
+    api->string_to_address = &udp_string_to_address;
+    return api;
+  }
 
 
-  if (GNUNET_YES == GNUNET_CONFIGURATION_get_value_yesno (env->cfg,
-                                                         "transport-udp",
-                                                         "ALLOW_NAT"))
-    {
-      if (check_gnunet_nat_binary("gnunet-nat-client") == GNUNET_YES)
-        allow_nat = GNUNET_YES; /* We will try to connect to NAT'd peers */
-      else
-      {
-        allow_nat = GNUNET_NO;
-        GNUNET_log_from (GNUNET_ERROR_TYPE_WARNING, "udp", "Configuration specified you want to connect to NAT'd peers, but gnunet-nat-client is not installed properly (suid bit not set)!\n");
-      }
+  GNUNET_assert( NULL != env->stats);
 
 
-    }
-  else
-    allow_nat = GNUNET_NO; /* We don't want to try to help NAT'd peers */
+  /* Get port number */
+  if (GNUNET_OK !=
+      GNUNET_CONFIGURATION_get_value_number (env->cfg, "transport-udp", "PORT",
+                                             &port))
+    port = 2086;
+  if (GNUNET_OK !=
+      GNUNET_CONFIGURATION_get_value_number (env->cfg, "transport-udp",
+                                             "ADVERTISED_PORT", &aport))
+    aport = port;
+  if (port > 65535)
+  {
+    LOG (GNUNET_ERROR_TYPE_WARNING,
+         _("Given `%s' option is out of range: %llu > %u\n"), "PORT", port,
+         65535);
+    return NULL;
+  }
 
 
-  if (GNUNET_YES == GNUNET_CONFIGURATION_get_value_yesno (env->cfg,
-                                                           "transport-udp",
-                                                           "ONLY_NAT_ADDRESSES"))
-    only_nat_addresses = GNUNET_YES; /* We will only report our addresses as NAT'd */
+  /* Protocols */
+  if ((GNUNET_YES ==
+       GNUNET_CONFIGURATION_get_value_yesno (env->cfg, "nat",
+                                             "DISABLEV6")))
+  {
+    enable_v6 = GNUNET_NO;
+  }
   else
   else
-    only_nat_addresses = GNUNET_NO; /* We will report our addresses as NAT'd and non-NAT'd */
-
-  external_address = NULL;
-  if (((GNUNET_YES == behind_nat) || (GNUNET_YES == allow_nat)) && (GNUNET_OK !=
-         GNUNET_CONFIGURATION_get_value_string (env->cfg,
-                                                "transport-udp",
-                                                "EXTERNAL_ADDRESS",
-                                                &external_address)))
-    {
-      GNUNET_log_from (GNUNET_ERROR_TYPE_ERROR,
-                       "udp",
-                       _
-                       ("Require EXTERNAL_ADDRESS for service `%s' in configuration (either BEHIND_NAT or ALLOW_NAT set to YES)!\n"),
-                       "transport-udp");
-      GNUNET_SERVICE_stop (service);
-      return NULL;
-    }
+    enable_v6 = GNUNET_YES;
 
 
-  if ((external_address != NULL) && (inet_pton(AF_INET, external_address, &v4_address.ipv4_addr) != 1))
-    {
-      GNUNET_log_from(GNUNET_ERROR_TYPE_WARNING, "udp", "Malformed EXTERNAL_ADDRESS %s given in configuration!\n", external_address);
-    }
+  /* Addresses */
+  memset (&serverAddrv6, 0, sizeof (serverAddrv6));
+  memset (&serverAddrv4, 0, sizeof (serverAddrv4));
 
 
-  internal_address = NULL;
-  if ((GNUNET_YES == behind_nat) && (GNUNET_OK !=
-         GNUNET_CONFIGURATION_get_value_string (env->cfg,
-                                                "transport-udp",
-                                                "INTERNAL_ADDRESS",
-                                                &internal_address)))
+  if (GNUNET_YES ==
+      GNUNET_CONFIGURATION_get_value_string (env->cfg, "transport-udp",
+                                             "BINDTO", &bind4_address))
+  {
+    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))
     {
     {
-      GNUNET_log_from (GNUNET_ERROR_TYPE_ERROR,
-                       "udp",
-                       _
-                       ("Require INTERNAL_ADDRESS for service `%s' in configuration!\n"),
-                       "transport-udp");
-      GNUNET_SERVICE_stop (service);
-      GNUNET_free_non_null(external_address);
+      GNUNET_free (bind4_address);
       return NULL;
     }
       return NULL;
     }
+  }
 
 
-  if ((internal_address != NULL) && (inet_pton(AF_INET, internal_address, &v4_address.ipv4_addr) != 1))
+  if (GNUNET_YES ==
+      GNUNET_CONFIGURATION_get_value_string (env->cfg, "transport-udp",
+                                             "BINDTO6", &bind6_address))
+  {
+    LOG (GNUNET_ERROR_TYPE_DEBUG,
+         "Binding udp plugin to specific address: `%s'\n",
+         bind6_address);
+    if (1 !=
+        inet_pton (AF_INET6, bind6_address, &serverAddrv6.sin6_addr))
     {
     {
-      GNUNET_log_from(GNUNET_ERROR_TYPE_WARNING, "udp", "Malformed INTERNAL_ADDRESS %s given in configuration!\n", internal_address);
+      LOG (GNUNET_ERROR_TYPE_ERROR, _("Invalid IPv6 address: `%s'\n"),
+           bind6_address);
+      GNUNET_free_non_null (bind4_address);
+      GNUNET_free (bind6_address);
+      return NULL;
     }
     }
+  }
 
 
-  if (GNUNET_OK !=
-      GNUNET_CONFIGURATION_get_value_number (env->cfg,
-                                            "transport-udp",
-                                            "PORT",
-                                            &port))
-    port = UDP_NAT_DEFAULT_PORT;
-  else if (port > 65535)
-    {
-      GNUNET_log_from (GNUNET_ERROR_TYPE_WARNING,
-                      "udp",
-                      _("Given `%s' option is out of range: %llu > %u\n"),
-                      "PORT",
-                      port,
-                      65535);
-      GNUNET_SERVICE_stop (service);
-      GNUNET_free_non_null(external_address);
-      GNUNET_free_non_null(internal_address);
-      return NULL;      
-    }
+  /* Enable neighbour discovery */
+  broadcast = GNUNET_CONFIGURATION_get_value_yesno (env->cfg, "transport-udp",
+                                            "BROADCAST");
+  if (broadcast == GNUNET_SYSERR)
+    broadcast = GNUNET_NO;
+
+  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);
+  }
 
 
-  mtu = 1240;
-  if (mtu < 1200)
-    GNUNET_log_from (GNUNET_ERROR_TYPE_INFO,
-                     "udp",
-                     _("MTU %llu for `%s' is probably too low!\n"), mtu,
-                     "UDP");
-
-  plugin = GNUNET_malloc (sizeof (struct Plugin));
-  plugin->external_address = external_address;
-  plugin->internal_address = internal_address;
-  plugin->port = port;
-  plugin->behind_nat = behind_nat;
-  plugin->allow_nat = allow_nat;
-  plugin->only_nat_addresses = only_nat_addresses;
-  plugin->env = env;
+  /* Maximum datarate */
+  if (GNUNET_OK != GNUNET_CONFIGURATION_get_value_number (env->cfg, "transport-udp",
+                                             "MAX_BPS", &udp_max_bps))
+  {
+    udp_max_bps = 1024 * 1024 * 50;     /* 50 MB/s == infinity for practical purposes */
+  }
 
 
+  p = GNUNET_malloc (sizeof (struct Plugin));
   api = GNUNET_malloc (sizeof (struct GNUNET_TRANSPORT_PluginFunctions));
   api = GNUNET_malloc (sizeof (struct GNUNET_TRANSPORT_PluginFunctions));
-  api->cls = plugin;
 
 
-  api->send = &udp_plugin_send;
+  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->env = env;
+
+  plugin = p;
+
+  api->cls = p;
+  api->send = NULL;
   api->disconnect = &udp_disconnect;
   api->address_pretty_printer = &udp_plugin_address_pretty_printer;
   api->address_to_string = &udp_address_to_string;
   api->disconnect = &udp_disconnect;
   api->address_pretty_printer = &udp_plugin_address_pretty_printer;
   api->address_to_string = &udp_address_to_string;
-  api->check_address = &udp_check_address;
+  api->string_to_address = &udp_string_to_address;
+  api->check_address = &udp_plugin_check_address;
+  api->get_session = &udp_plugin_get_session;
+  api->send = &udp_plugin_send;
 
 
-  plugin->service = service;
+  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 (plugin->behind_nat == GNUNET_NO)
-    {
-      GNUNET_OS_network_interfaces_list (&process_interfaces, plugin);
-    }
+  if (broadcast == GNUNET_YES)
+  {
+    LOG (GNUNET_ERROR_TYPE_DEBUG, "Starting broadcasting\n");
+    setup_broadcast (p, &serverAddrv6, &serverAddrv4);
+  }
 
 
-  plugin->hostname_dns = GNUNET_RESOLVER_hostname_resolve (env->sched,
-                                                           env->cfg,
-                                                           AF_UNSPEC,
-                                                           HOSTNAME_RESOLVE_TIMEOUT,
-                                                           &process_hostname_ips,
-                                                           plugin);
+  GNUNET_free_non_null (bind4_address);
+  GNUNET_free_non_null (bind6_address);
+  return api;
+}
 
 
-  if ((plugin->behind_nat == GNUNET_YES) && (inet_pton(AF_INET, plugin->external_address, &v4_address.ipv4_addr) == 1))
-    {
-      v4_address.u_port = htons(0);
-      plugin->env->notify_address (plugin->env->cls,
-                                  "udp",
-                                  &v4_address, sizeof(v4_address), GNUNET_TIME_UNIT_FOREVER_REL);
-    }
-  else if ((plugin->external_address != NULL) && (inet_pton(AF_INET, plugin->external_address, &v4_address.ipv4_addr) == 1))
-    {
-      v4_address.u_port = htons(plugin->port);
-      plugin->env->notify_address (plugin->env->cls,
-                                  "udp",
-                                  &v4_address, sizeof(v4_address), GNUNET_TIME_UNIT_FOREVER_REL);
-    }
 
 
-  sockets_created = udp_transport_server_start (plugin);
+static int
+heap_cleanup_iterator (void *cls,
+                      struct GNUNET_CONTAINER_HeapNode *
+                      node, void *element,
+                      GNUNET_CONTAINER_HeapCostType
+                      cost)
+{
+  struct DefragContext * d_ctx = element;
 
 
-  GNUNET_assert (sockets_created == 1);
+  GNUNET_CONTAINER_heap_remove_node (node);
+  GNUNET_DEFRAGMENT_context_destroy(d_ctx->defrag);
+  GNUNET_free (d_ctx);
 
 
-  return api;
+  return GNUNET_YES;
 }
 
 }
 
+
+/**
+ * The exported method. Makes the core api available via a global and
+ * returns the udp transport API.
+ *
+ * @param cls our 'struct GNUNET_TRANSPORT_PluginEnvironment'
+ * @return NULL
+ */
 void *
 libgnunet_plugin_transport_udp_done (void *cls)
 {
   struct GNUNET_TRANSPORT_PluginFunctions *api = cls;
   struct Plugin *plugin = api->cls;
 void *
 libgnunet_plugin_transport_udp_done (void *cls)
 {
   struct GNUNET_TRANSPORT_PluginFunctions *api = cls;
   struct Plugin *plugin = api->cls;
-  struct LocalAddrList *lal;
 
 
-  udp_transport_server_stop (plugin);
-  if (NULL != plugin->hostname_dns)
-    {
-      GNUNET_RESOLVER_request_cancel (plugin->hostname_dns);
-      plugin->hostname_dns = NULL;
-    }
+  if (NULL == plugin)
+  {
+    GNUNET_free (api);
+    return NULL;
+  }
 
 
-  GNUNET_SERVICE_stop (plugin->service);
+  stop_broadcast (plugin);
 
 
-  GNUNET_NETWORK_fdset_destroy (plugin->rs);
-  while (NULL != (lal = plugin->lal_head))
-    {
-      GNUNET_CONTAINER_DLL_remove (plugin->lal_head,
-                                  plugin->lal_tail,
-                                  lal);
-      GNUNET_free (lal);
-    }
+  if (plugin->select_task != GNUNET_SCHEDULER_NO_TASK)
+  {
+    GNUNET_SCHEDULER_cancel (plugin->select_task);
+    plugin->select_task = GNUNET_SCHEDULER_NO_TASK;
+  }
+  if (plugin->select_task_v6 != GNUNET_SCHEDULER_NO_TASK)
+  {
+    GNUNET_SCHEDULER_cancel (plugin->select_task_v6);
+    plugin->select_task_v6 = GNUNET_SCHEDULER_NO_TASK;
+  }
+
+  /* Closing sockets */
+  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);
+
+  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_NAT_unregister (plugin->nat);
+
+  if (plugin->defrag_ctxs != NULL)
+  {
+    GNUNET_CONTAINER_heap_iterate(plugin->defrag_ctxs,
+        heap_cleanup_iterator, NULL);
+    GNUNET_CONTAINER_heap_destroy(plugin->defrag_ctxs);
+    plugin->defrag_ctxs = NULL;
+  }
+  if (plugin->mst != NULL)
+  {
+    GNUNET_SERVER_mst_destroy(plugin->mst);
+    plugin->mst = NULL;
+  }
+
+  /* Clean up leftover messages */
+  struct UDPMessageWrapper * 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);
+    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);
+    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);
+
+  plugin->nat = NULL;
   GNUNET_free (plugin);
   GNUNET_free (api);
   return NULL;
 }
 
   GNUNET_free (plugin);
   GNUNET_free (api);
   return NULL;
 }
 
+
 /* end of plugin_transport_udp.c */
 /* end of plugin_transport_udp.c */