remove xt/xu plugins, no longer needed for anything
[oweals/gnunet.git] / src / transport / gnunet-communicator-udp.c
index d464cd0d17700ae5eba4305e37acdf3022f02d97..fa8eb6acb913e602e45cf58158795abfccc29ff6 100644 (file)
  * @author Christian Grothoff
  *
  * TODO:
- * - implement main BOXed sending logic
- * - figure out what to do with MTU: 1280 for IPv6 is obvious;
- *   what for IPv4? 1500? Also, consider differences in 
- *   headers for with/without box: need to give MIN of both
- *   to TNG (as TNG expects a fixed MTU!), or maybe
- *   we create a FRESH MQ while we have available BOXes SQNs?
- *   (otherwise padding will REALLY hurt)
- * - add and use util/ check for IPv6 availability (#V6)
  * - consider imposing transmission limits in the absence
  *   of ACKs; or: maybe this should be done at TNG service level?
- * - support broadcasting for neighbour discovery (#)
+ *   (at least the receiver might want to enforce limits on
+ *    KX/DH operations per sender in here) (#5552)
+ * - overall, we should look more into flow control support
+ *   (either in backchannel, or general solution in TNG service)
+ * - handle addresses discovered from broadcasts (#5551)
  *   (think: what was the story again on address validation?
  *    where is the API for that!?!)
  * - support DNS names in BINDTO option (#5528)
  */ 
 #define PROTO_QUEUE_TIMEOUT GNUNET_TIME_UNIT_MINUTES
 
+/**
+ * How often do we broadcast our presence on the LAN?
+ */ 
+#define BROADCAST_FREQUENCY GNUNET_TIME_UNIT_MINUTES
+
+/**
+ * How often do we scan for changes to our network interfaces?
+ */ 
+#define INTERFACE_SCAN_FREQUENCY GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_MINUTES, 5)
+
 /**
  * AES key size.
  */
@@ -525,6 +531,12 @@ struct ReceiverAddress
    */
   struct SharedSecret *ss_tail;
 
+  /**
+   * Address of the receiver in the human-readable format
+   * with the #COMMUNICATOR_ADDRESS_PREFIX.
+   */ 
+  char *foreign_addr;
+
   /**
    * Address of the other peer.
    */
@@ -564,6 +576,12 @@ struct ReceiverAddress
    * Length of the DLL at @a ss_head.
    */ 
   unsigned int num_secrets;
+
+  /**
+   * Number of BOX keys from ACKs we have currently 
+   * available for this receiver.
+   */ 
+  unsigned int acks_available;
   
   /**
    * Which network type does this queue use?
@@ -573,6 +591,60 @@ struct ReceiverAddress
 };
 
 
+/**
+ * Interface we broadcast our presence on.
+ */
+struct BroadcastInterface
+{
+
+  /**
+   * Kept in a DLL.
+   */
+  struct BroadcastInterface *next;
+
+  /**
+   * Kept in a DLL.
+   */
+  struct BroadcastInterface *prev;
+
+  /**
+   * Task for this broadcast interface.
+   */
+  struct GNUNET_SCHEDULER_Task *broadcast_task;
+  
+  /**
+   * Sender's address of the interface.
+   */
+  struct sockaddr *sa;
+  
+  /**
+   * Broadcast address to use on the interface.
+   */
+  struct sockaddr *ba;
+
+  /**
+   * Message we broadcast on this interface.
+   */ 
+  struct UDPBroadcast bcm;
+  
+  /**
+   * If this is an IPv6 interface, this is the request
+   * we use to join/leave the group.
+   */
+  struct ipv6_mreq mcreq;
+  
+  /**
+   * Number of bytes in @e sa.
+   */ 
+  socklen_t salen;
+
+  /**
+   * Was this interface found in the last #iface_proc() scan?
+   */
+  int found;
+};
+
+
 /**
  * Cache of pre-generated key IDs.
  */
@@ -588,6 +660,11 @@ static struct GNUNET_SCHEDULER_Task *read_task;
  */
 static struct GNUNET_SCHEDULER_Task *timeout_task;
 
+/**
+ * ID of master broadcast task
+ */
+static struct GNUNET_SCHEDULER_Task *broadcast_task;
+
 /**
  * For logging statistics.
  */
@@ -618,11 +695,26 @@ static struct GNUNET_CONTAINER_Heap *senders_heap;
  */
 static struct GNUNET_CONTAINER_Heap *receivers_heap;
 
+/**
+ * Broadcast interface tasks. Kept in a DLL.
+ */
+static struct BroadcastInterface *bi_head;
+
+/**
+ * Broadcast interface tasks. Kept in a DLL.
+ */
+static struct BroadcastInterface *bi_tail;
+
 /**
  * Our socket.
  */
 static struct GNUNET_NETWORK_Handle *udp_sock;
 
+/** 
+ * #GNUNET_YES if #udp_sock supports IPv6.
+ */ 
+static int have_v6_socket;
+
 /**
  * Our public key.
  */
@@ -648,10 +740,47 @@ static struct GNUNET_NT_InterfaceScanner *is;
  */
 static struct GNUNET_NAT_Handle *nat;
 
+/**
+ * Port number to which we are actually bound.
+ */ 
+static uint16_t my_port;
+
 
 /**
- * Functions with this signature are called whenever we need
- * to close a receiving state due to timeout.
+ * An interface went away, stop broadcasting on it.
+ *
+ * @param bi entity to close down
+ */
+static void
+bi_destroy (struct BroadcastInterface *bi)
+{
+  if (AF_INET6 == bi->sa->sa_family)
+  {
+    /* Leave the multicast group */
+    if (GNUNET_OK !=
+        GNUNET_NETWORK_socket_setsockopt
+        (udp_sock,
+        IPPROTO_IPV6,
+        IPV6_LEAVE_GROUP,
+         &bi->mcreq,
+        sizeof (bi->mcreq)))
+    {
+      GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING,
+                          "setsockopt");
+    }
+  }
+  GNUNET_CONTAINER_DLL_remove (bi_head,
+                              bi_tail,
+                              bi);
+  GNUNET_SCHEDULER_cancel (bi->broadcast_task);
+  GNUNET_free (bi->sa);
+  GNUNET_free_non_null (bi->ba);
+  GNUNET_free (bi);
+}
+
+
+/**
+ * Destroys a receiving state due to timeout or shutdown.
  *
  * @param receiver entity to close down
  */
@@ -668,6 +797,11 @@ receiver_destroy (struct ReceiverAddress *receiver)
     receiver->mq = NULL;
     GNUNET_MQ_destroy (mq);
   }
+  if (NULL != receiver->qh)
+  {
+    GNUNET_TRANSPORT_communicator_mq_del (receiver->qh);
+    receiver->qh = NULL;
+  }
   GNUNET_assert (GNUNET_YES ==
                  GNUNET_CONTAINER_multipeermap_remove (receivers,
                                                       &receiver->target,
@@ -679,6 +813,7 @@ receiver_destroy (struct ReceiverAddress *receiver)
                         GNUNET_CONTAINER_multipeermap_size (receivers),
                         GNUNET_NO);
   GNUNET_free (receiver->address);
+  GNUNET_free (receiver->foreign_addr);
   GNUNET_free (receiver);
 }
 
@@ -792,6 +927,8 @@ secret_destroy (struct SharedSecret *ss)
                                 receiver->ss_tail,
                                 ss);
     receiver->num_secrets--;
+    receiver->acks_available
+      -= (ss->sequence_allowed - ss->sequence_used);
   }
   while (NULL != (kce = ss->kce_head))
     kce_destroy (kce);
@@ -1131,6 +1268,17 @@ setup_shared_secret_enc (const struct GNUNET_CRYPTO_EcdhePrivateKey *ephemeral,
 }
 
 
+/**
+ * Setup the MQ for the @a receiver.  If a queue exists,
+ * the existing one is destroyed.  Then the MTU is
+ * recalculated and a fresh queue is initialized.
+ *
+ * @param receiver receiver to setup MQ for
+ */ 
+static void
+setup_receiver_mq (struct ReceiverAddress *receiver);
+
+
 /**
  * We received an ACK for @a pid. Check if it is for
  * the receiver in @a value and if so, handle it and
@@ -1158,17 +1306,28 @@ handle_ack (void *cls,
                     &ss->cmac,
                     sizeof (struct GNUNET_HashCode)))
     {
-      ss->sequence_allowed = GNUNET_MAX (ss->sequence_allowed,
-                                        ntohl (ack->sequence_max));
-      /* move ss to head to avoid discarding it anytime soon! */
-      GNUNET_CONTAINER_DLL_remove (receiver->ss_head,
-                                  receiver->ss_tail,
-                                  ss);
-      GNUNET_CONTAINER_DLL_insert (receiver->ss_head,
-                                  receiver->ss_tail,
-                                  ss);
-      /* FIXME: if this changed sequence_allowed,
-        update MTU / MQ of 'receiver'! */
+      uint32_t allowed;
+      
+      allowed = ntohl (ack->sequence_max);
+                           
+      if (allowed > ss->sequence_allowed)
+      {
+       receiver->acks_available += (allowed - ss->sequence_allowed);
+       if ((allowed - ss->sequence_allowed)
+           == receiver->acks_available)
+       {
+         /* we just incremented from zero => MTU change! */
+         setup_receiver_mq (receiver);
+       }
+       ss->sequence_allowed = allowed;
+       /* move ss to head to avoid discarding it anytime soon! */
+       GNUNET_CONTAINER_DLL_remove (receiver->ss_head,
+                                    receiver->ss_tail,
+                                    ss);
+       GNUNET_CONTAINER_DLL_insert (receiver->ss_head,
+                                    receiver->ss_tail,
+                                    ss);
+      }
       return GNUNET_NO;
     }
   }
@@ -1512,7 +1671,7 @@ sock_read (void *cls)
                                "# broadcasts received",
                                1,
                                GNUNET_NO);
-      // FIXME: we effectively just got a HELLO!
+      // FIXME #5551: we effectively just got a HELLO!
       // trigger verification NOW!
       return;
     }
@@ -1624,12 +1783,12 @@ udp_address_to_sockaddr (const char *bindto,
                  bindto);
       return NULL;
     }
-    /* FIXME #V6: add test to util/ for IPv6 availability,
-       and depending on the result, go directly for v4-only */
-    if (GNUNET_YES ==
-       GNUNET_CONFIGURATION_get_value_yesno (cfg,
-                                             COMMUNICATOR_CONFIG_SECTION,
-                                             "DISABLE_V6"))
+    if ( (GNUNET_NO ==
+         GNUNET_NETWORK_test_pf (PF_INET6)) ||
+        (GNUNET_YES ==
+         GNUNET_CONFIGURATION_get_value_yesno (cfg,
+                                               COMMUNICATOR_CONFIG_SECTION,
+                                               "DISABLE_V6")) )
     {
       struct sockaddr_in *i4;
       
@@ -1733,6 +1892,43 @@ udp_address_to_sockaddr (const char *bindto,
 }
 
 
+/**
+ * Pad @a dgram by @a pad_size using @a out_cipher.
+ *
+ * @param out_cipher cipher to use
+ * @param dgram datagram to pad
+ * @param pad_size number of bytes of padding to append
+ */
+static void
+do_pad (gcry_cipher_hd_t out_cipher,
+       char *dgram,
+       size_t pad_size)
+{
+  char pad[pad_size];
+
+  GNUNET_CRYPTO_random_block (GNUNET_CRYPTO_QUALITY_WEAK,
+                             pad,
+                             sizeof (pad));
+  if (sizeof (pad) > sizeof (struct GNUNET_MessageHeader))
+  {
+    struct GNUNET_MessageHeader hdr = {
+      .size = htons (sizeof (pad)),
+      .type = htons (GNUNET_MESSAGE_TYPE_COMMUNICATOR_UDP_PAD)
+    };
+    
+    memcpy (pad,
+           &hdr,
+           sizeof (hdr));
+  }
+  GNUNET_assert (0 ==
+                gcry_cipher_encrypt (out_cipher,
+                                     dgram,
+                                     sizeof (pad),
+                                     pad,
+                                     sizeof (pad)));
+}
+
+
 /**
  * Signature of functions implementing the sending functionality of a
  * message queue.
@@ -1758,10 +1954,9 @@ mq_send (struct GNUNET_MQ_Handle *mq,
   }
   reschedule_receiver_timeout (receiver);
   
-  // FIXME: add support for BOX encryption method!
-
-  /* KX encryption method */
+  if (0 == receiver->acks_available)
   {
+    /* use KX encryption method */
     struct UdpHandshakeSignature uhs;
     struct UDPConfirmation uc;
     struct InitialKX kx;
@@ -1814,31 +2009,9 @@ mq_send (struct GNUNET_MQ_Handle *mq,
                                        msg,
                                        msize));
     dpos += msize;
-    /* Pad to MTU */
-    {
-      char pad[sizeof (dgram) - dpos];
-
-      GNUNET_CRYPTO_random_block (GNUNET_CRYPTO_QUALITY_WEAK,
-                                 pad,
-                                 sizeof (pad));
-      if (sizeof (pad) > sizeof (struct GNUNET_MessageHeader))
-      {
-       struct GNUNET_MessageHeader hdr = {
-         .size = htons (sizeof (pad)),
-         .type = htons (GNUNET_MESSAGE_TYPE_COMMUNICATOR_UDP_PAD)
-       };
-       
-       memcpy (pad,
-               &hdr,
-               sizeof (hdr));
-       GNUNET_assert (0 ==
-                      gcry_cipher_encrypt (out_cipher,
-                                           &dgram[dpos],
-                                           sizeof (pad),
-                                           pad,
-                                           sizeof (pad)));
-      }
-    }
+    do_pad (out_cipher,
+           &dgram[dpos],
+           sizeof (dgram) - dpos);
     /* Datagram starts with kx */
     kx.ephemeral = uhs.ephemeral;
     GNUNET_assert (0 ==
@@ -1858,7 +2031,65 @@ mq_send (struct GNUNET_MQ_Handle *mq,
       GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING,
                           "send");
     GNUNET_MQ_impl_send_continue (mq);
+    return;
   } /* End of KX encryption method */
+
+  /* begin "BOX" encryption method, scan for ACKs from tail! */
+  for (struct SharedSecret *ss = receiver->ss_tail;
+       NULL != ss;
+       ss = ss->prev)
+  {
+    if (ss->sequence_used < ss->sequence_allowed)
+    {
+      char dgram[sizeof (struct UDPBox) + receiver->mtu];
+      struct UDPBox *box;
+      gcry_cipher_hd_t out_cipher;
+      size_t dpos;
+
+      box = (struct UDPBox *) dgram;
+      ss->sequence_used++;
+      get_kid (&ss->master,
+              ss->sequence_used,
+              &box->kid);
+      setup_cipher (&ss->master,
+                   ss->sequence_used,
+                   &out_cipher);
+      /* Append encrypted payload to dgram */
+      dpos = sizeof (struct UDPBox);
+      GNUNET_assert (0 ==
+                    gcry_cipher_encrypt (out_cipher,
+                                         &dgram[dpos],
+                                         msize,
+                                         msg,
+                                         msize));
+      dpos += msize;
+      do_pad (out_cipher,
+             &dgram[dpos],
+             sizeof (dgram) - dpos);
+      GNUNET_assert (0 ==
+                    gcry_cipher_gettag (out_cipher,
+                                        box->gcm_tag,
+                                        sizeof (box->gcm_tag)));
+      gcry_cipher_close (out_cipher);
+      if (-1 ==
+         GNUNET_NETWORK_socket_sendto (udp_sock,
+                                       dgram,
+                                       sizeof (dgram),
+                                       receiver->address,
+                                       receiver->address_len))
+       GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING,
+                            "send");
+      GNUNET_MQ_impl_send_continue (mq);
+      receiver->acks_available--;
+      if (0 == receiver->acks_available)
+      {
+       /* We have no more ACKs => MTU change! */
+       setup_receiver_mq (receiver);
+      }
+      return;
+    }
+  }
+  GNUNET_assert (0);
 }
 
 
@@ -1922,6 +2153,77 @@ mq_error (void *cls,
 }
 
 
+/**
+ * Setup the MQ for the @a receiver.  If a queue exists,
+ * the existing one is destroyed.  Then the MTU is
+ * recalculated and a fresh queue is initialized.
+ *
+ * @param receiver receiver to setup MQ for
+ */ 
+static void
+setup_receiver_mq (struct ReceiverAddress *receiver)
+{
+  size_t base_mtu;
+  
+  if (NULL != receiver->qh)
+  {
+    GNUNET_TRANSPORT_communicator_mq_del (receiver->qh);
+    receiver->qh = NULL;
+  }
+  GNUNET_assert (NULL == receiver->mq);
+  switch (receiver->address->sa_family)
+  {
+  case AF_INET:
+    base_mtu
+      = 1480 /* Ethernet MTU, 1500 - Ethernet header - VLAN tag */
+      - sizeof (struct GNUNET_TUN_IPv4Header) /* 20 */
+      - sizeof (struct GNUNET_TUN_UdpHeader) /* 8 */;
+    break;
+  case AF_INET6:
+    base_mtu
+      = 1280 /* Minimum MTU required by IPv6 */
+      - sizeof (struct GNUNET_TUN_IPv6Header) /* 40 */
+      - sizeof (struct GNUNET_TUN_UdpHeader) /* 8 */;
+    break;
+  default:
+    GNUNET_assert (0);
+    break;
+  }
+  if (0 == receiver->acks_available)
+  {
+    /* MTU based on full KX messages */
+    receiver->mtu
+      = base_mtu
+      - sizeof (struct InitialKX) /* 48 */
+      - sizeof (struct UDPConfirmation); /* 104 */
+  }
+  else
+  {
+    /* MTU based on BOXed messages */
+    receiver->mtu
+      = base_mtu - sizeof (struct UDPBox);
+  }
+  /* => Effective MTU for CORE will range from 1080 (IPv6 + KX) to
+     1404 (IPv4 + Box) bytes, depending on circumstances... */
+  receiver->mq
+    = GNUNET_MQ_queue_for_callbacks (&mq_send,
+                                    &mq_destroy,
+                                    &mq_cancel,
+                                    receiver,
+                                    NULL,
+                                    &mq_error,
+                                    receiver);
+  receiver->qh
+    = GNUNET_TRANSPORT_communicator_mq_add (ch,
+                                           &receiver->target,
+                                           receiver->foreign_addr,
+                                           receiver->mtu,
+                                           receiver->nt,
+                                           GNUNET_TRANSPORT_CS_OUTBOUND,
+                                           receiver->mq);
+}
+
+
 /**
  * Setup a receiver for transmission.  Setup the MQ processing and
  * inform transport that the queue is ready. 
@@ -1952,54 +2254,35 @@ receiver_setup (const struct GNUNET_PeerIdentity *target,
   receiver->hn = GNUNET_CONTAINER_heap_insert (receivers_heap,
                                               receiver,
                                               receiver->timeout.abs_value_us);
-  receiver->mq
-    = GNUNET_MQ_queue_for_callbacks (&mq_send,
-                                    &mq_destroy,
-                                    &mq_cancel,
-                                    receiver,
-                                    NULL,
-                                    &mq_error,
-                                    receiver);
-  receiver->mtu = 1200 /* FIXME: MTU OK? */;
-  if (NULL == timeout_task)
-    timeout_task = GNUNET_SCHEDULER_add_now (&check_timeouts,
-                                            NULL);
   GNUNET_STATISTICS_set (stats,
                         "# receivers active",
                         GNUNET_CONTAINER_multipeermap_size (receivers),
                         GNUNET_NO);
+  switch (address->sa_family)
   {
-    char *foreign_addr;
-
-    switch (address->sa_family)
-    {
-    case AF_INET:
-      GNUNET_asprintf (&foreign_addr,
-                      "%s-%s",
-                      COMMUNICATOR_ADDRESS_PREFIX,
-                      GNUNET_a2s (receiver->address,
-                                  receiver->address_len));
-      break;
-    case AF_INET6:
-      GNUNET_asprintf (&foreign_addr,
-                      "%s-%s",
-                      COMMUNICATOR_ADDRESS_PREFIX,
-                      GNUNET_a2s (receiver->address,
-                                  receiver->address_len));
-      break;
-    default:
-      GNUNET_assert (0);
-    }
-    receiver->qh
-      = GNUNET_TRANSPORT_communicator_mq_add (ch,
-                                             &receiver->target,
-                                             foreign_addr,
-                                             receiver->mtu,
-                                             receiver->nt,
-                                             GNUNET_TRANSPORT_CS_OUTBOUND,
-                                             receiver->mq);
-    GNUNET_free (foreign_addr);
+  case AF_INET:
+    GNUNET_asprintf (&receiver->foreign_addr,
+                    "%s-%s",
+                    COMMUNICATOR_ADDRESS_PREFIX,
+                    GNUNET_a2s (receiver->address,
+                                receiver->address_len));
+    break;
+  case AF_INET6:
+    GNUNET_asprintf (&receiver->foreign_addr,
+                    "%s-%s",
+                    COMMUNICATOR_ADDRESS_PREFIX,
+                    GNUNET_a2s (receiver->address,
+                                receiver->address_len));
+    break;
+  default:
+    GNUNET_assert (0);
   }
+
+  setup_receiver_mq (receiver);
+
+  if (NULL == timeout_task)
+    timeout_task = GNUNET_SCHEDULER_add_now (&check_timeouts,
+                                            NULL);
   return receiver;
 }
 
@@ -2106,6 +2389,13 @@ do_shutdown (void *cls)
      GNUNET_NAT_unregister (nat);
      nat = NULL;
   }
+  while (NULL != bi_head)
+    bi_destroy (bi_head);
+  if (NULL != broadcast_task)
+  {
+    GNUNET_SCHEDULER_cancel (broadcast_task);
+    broadcast_task = NULL;
+  }
   if (NULL != read_task)
   {
     GNUNET_SCHEDULER_cancel (read_task);
@@ -2235,6 +2525,240 @@ nat_address_cb (void *cls,
 }
 
 
+/**
+ * Broadcast our presence on one of our interfaces.
+ *
+ * @param cls a `struct BroadcastInterface`
+ */
+static void
+ifc_broadcast (void *cls)
+{
+  struct BroadcastInterface *bi = cls;
+  struct GNUNET_TIME_Relative delay;
+
+  delay = BROADCAST_FREQUENCY;
+  delay.rel_value_us = GNUNET_CRYPTO_random_u64 (GNUNET_CRYPTO_QUALITY_WEAK,
+                                                delay.rel_value_us);
+  bi->broadcast_task
+    = GNUNET_SCHEDULER_add_delayed (INTERFACE_SCAN_FREQUENCY,
+                                   &ifc_broadcast,
+                                   bi);
+  
+  switch (bi->sa->sa_family) {
+  case AF_INET:
+    {
+      static int yes = 1;
+      static int no = 0;
+      ssize_t sent;
+    
+      if (GNUNET_OK !=
+         GNUNET_NETWORK_socket_setsockopt (udp_sock,
+                                           SOL_SOCKET,
+                                           SO_BROADCAST,
+                                           &yes,
+                                           sizeof (int)))
+       GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING,
+                            "setsockopt");
+      sent = GNUNET_NETWORK_socket_sendto (udp_sock,
+                                          &bi->bcm,
+                                          sizeof (bi->bcm),
+                                          bi->ba,
+                                          bi->salen);
+      if (-1 == sent)
+       GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING,
+                            "sendto");
+      if (GNUNET_OK !=
+         GNUNET_NETWORK_socket_setsockopt (udp_sock,
+                                           SOL_SOCKET,
+                                           SO_BROADCAST,
+                                           &no,
+                                           sizeof (int)))
+       GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING,
+                            "setsockopt");
+      break;
+    }
+  case AF_INET6:
+    {
+      ssize_t sent;
+      struct sockaddr_in6 dst;
+
+      dst.sin6_family = AF_INET6;
+      dst.sin6_port = htons (my_port);
+      dst.sin6_addr = bi->mcreq.ipv6mr_multiaddr;
+      dst.sin6_scope_id = ((struct sockaddr_in6*) bi->ba)->sin6_scope_id;
+
+      sent = GNUNET_NETWORK_socket_sendto (udp_sock,
+                                          &bi->bcm,
+                                          sizeof (bi->bcm),
+                                          (const struct sockaddr *)
+                                          &dst,
+                                          sizeof (dst));
+      if (-1 == sent)
+       GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING,
+                            "sendto");
+      break;
+    }
+  default:
+    GNUNET_break (0);
+    break;
+  }
+}
+
+
+/**
+ * Callback function invoked for each interface found.
+ * Activates/deactivates broadcast interfaces.
+ *
+ * @param cls NULL
+ * @param name name of the interface (can be NULL for unknown)
+ * @param isDefault is this presumably the default interface
+ * @param addr address of this interface (can be NULL for unknown or unassigned)
+ * @param broadcast_addr the broadcast address (can be NULL for unknown or unassigned)
+ * @param netmask the network mask (can be NULL for unknown or unassigned)
+ * @param addrlen length of the address
+ * @return #GNUNET_OK to continue iteration, #GNUNET_SYSERR to abort
+ */
+static int
+iface_proc (void *cls,
+            const char *name,
+            int isDefault,
+            const struct sockaddr *addr,
+            const struct sockaddr *broadcast_addr,
+            const struct sockaddr *netmask, socklen_t addrlen)
+{
+  struct BroadcastInterface *bi;
+  enum GNUNET_NetworkType network;
+  struct UdpBroadcastSignature ubs;
+
+  (void) cls;
+  (void) netmask;
+  network = GNUNET_NT_scanner_get_type (is,
+                                       addr,
+                                       addrlen);
+  if (GNUNET_NT_LOOPBACK == network)
+  {
+    /* Broadcasting on loopback does not make sense */
+    return GNUNET_YES;
+  }
+  if (NULL == addr)
+    return GNUNET_YES; /* need to know our address! */
+  for (bi = bi_head; NULL != bi; bi = bi->next)
+  {
+    if ( (bi->salen == addrlen) &&
+        (0 == memcmp (addr,
+                      bi->sa,
+                      addrlen)) )
+    {
+      bi->found = GNUNET_YES;
+      return GNUNET_OK;
+    }
+  }
+
+  if ( (AF_INET6 == addr->sa_family) &&
+       (NULL == broadcast_addr) )
+    return GNUNET_OK; /* broadcast_addr is required for IPv6! */
+  if ( (AF_INET6 == addr->sa_family) &&
+       (GNUNET_YES != have_v6_socket) )
+    return GNUNET_OK; /* not using IPv6 */
+  
+  bi = GNUNET_new (struct BroadcastInterface);
+  bi->sa = GNUNET_memdup (addr,
+                         addrlen);
+  if (NULL != broadcast_addr)
+    bi->ba = GNUNET_memdup (broadcast_addr,
+                           addrlen);
+  bi->salen = addrlen;
+  bi->found = GNUNET_YES;
+  bi->bcm.sender = my_identity;
+  ubs.purpose.purpose = htonl (GNUNET_SIGNATURE_COMMUNICATOR_UDP_BROADCAST);
+  ubs.purpose.size = htonl (sizeof (ubs));
+  ubs.sender = my_identity;
+  GNUNET_CRYPTO_hash (addr,
+                     addrlen,
+                     &ubs.h_address);
+  GNUNET_assert (GNUNET_OK ==
+                GNUNET_CRYPTO_eddsa_sign (my_private_key,
+                                          &ubs.purpose,
+                                          &bi->bcm.sender_sig));
+  bi->broadcast_task = GNUNET_SCHEDULER_add_now (&ifc_broadcast,
+                                                bi);
+  GNUNET_CONTAINER_DLL_insert (bi_head,
+                              bi_tail,
+                              bi);
+  if ( (AF_INET6 == addr->sa_family) &&
+       (NULL != broadcast_addr) )
+  {
+    /* Create IPv6 multicast request */
+    const struct sockaddr_in6 *s6
+      = (const struct sockaddr_in6 *) broadcast_addr;
+
+    GNUNET_assert (1 ==
+                   inet_pton (AF_INET6,
+                             "FF05::13B",
+                              &bi->mcreq.ipv6mr_multiaddr));
+    
+    /* http://tools.ietf.org/html/rfc2553#section-5.2:
+     *
+     * IPV6_JOIN_GROUP
+     *
+     * Join a multicast group on a specified local interface.  If the
+     * interface index is specified as 0, the kernel chooses the local
+     * interface.  For example, some kernels look up the multicast
+     * group in the normal IPv6 routing table and using the resulting
+     * interface; we do this for each interface, so no need to use
+     * zero (anymore...).
+     */
+    bi->mcreq.ipv6mr_interface = s6->sin6_scope_id;
+
+    /* Join the multicast group */
+    if (GNUNET_OK !=
+        GNUNET_NETWORK_socket_setsockopt
+        (udp_sock,
+        IPPROTO_IPV6,
+        IPV6_JOIN_GROUP,
+         &bi->mcreq,
+        sizeof (bi->mcreq)))
+    {
+      GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING,
+                          "setsockopt");
+    }
+  }
+  return GNUNET_OK;
+}
+
+
+/**
+ * Scan interfaces to broadcast our presence on the LAN.
+ *
+ * @param cls NULL, unused
+ */
+static void
+do_broadcast (void *cls)
+{
+  struct BroadcastInterface *bin;
+  
+  (void) cls;
+  for (struct BroadcastInterface *bi = bi_head;
+       NULL != bi;
+       bi = bi->next)
+    bi->found = GNUNET_NO;
+  GNUNET_OS_network_interfaces_list (&iface_proc,
+                                    NULL);
+  for (struct BroadcastInterface *bi = bi_head;
+       NULL != bi;
+       bi = bin)
+  {
+    bin = bi->next;
+    if (GNUNET_NO == bi->found)
+      bi_destroy (bi);
+  }
+  broadcast_task
+    = GNUNET_SCHEDULER_add_delayed (INTERFACE_SCAN_FREQUENCY,
+                                   &do_broadcast,
+                                   NULL);
+}
+
+
 /**
  * Setup communicator and launch network interactions.
  *
@@ -2290,6 +2814,8 @@ run (void *cls,
     GNUNET_free (bindto);
     return;
   }
+  if (AF_INET6 == in->sa_family)
+    have_v6_socket = GNUNET_YES;
   if (GNUNET_OK !=
       GNUNET_NETWORK_socket_bind (udp_sock,
                                   in,
@@ -2324,6 +2850,18 @@ run (void *cls,
              "Bound to `%s'\n",
              GNUNET_a2s ((const struct sockaddr *) &in_sto,
                          sto_len));
+  switch (in->sa_family)
+  {
+  case AF_INET:
+    my_port = ntohs (((struct sockaddr_in *) in)->sin_port);
+    break;
+  case AF_INET6:
+    my_port = ntohs (((struct sockaddr_in6 *) in)->sin6_port);
+    break;
+  default:
+    GNUNET_break (0);
+    my_port = 0;
+  }
   stats = GNUNET_STATISTICS_create ("C-UDP",
                                    cfg);
   senders = GNUNET_CONTAINER_multipeermap_create (32,
@@ -2366,6 +2904,15 @@ run (void *cls,
     GNUNET_SCHEDULER_shutdown ();
     return;
   }
+  /* start broadcasting */
+  if (GNUNET_YES !=
+      GNUNET_CONFIGURATION_get_value_yesno (cfg,
+                                           COMMUNICATOR_CONFIG_SECTION,
+                                           "DISABLE_BROADCAST"))
+  {
+    broadcast_task = GNUNET_SCHEDULER_add_now (&do_broadcast,
+                                              NULL);
+  }  
   nat = GNUNET_NAT_register (cfg,
                             COMMUNICATOR_CONFIG_SECTION,
                             IPPROTO_UDP,