hunting bugs
[oweals/gnunet.git] / src / transport / gnunet-service-transport_neighbours.c
index 918299feff94aac2a069d28258e86d74d20bd014..d10a74e36d3494cf12ff1f1922e51f88d5ab4cae 100644 (file)
@@ -368,7 +368,13 @@ enum State
   S_DISCONNECT,
 
   /**
-   * We're finished with the disconnect; clean up state now!
+   * We're finished with the disconnect; and are cleaning up the state
+   * now!  We put the struct into this state when we are really in the
+   * task that calls 'free' on it and are about to remove the record
+   * from the map.  We should never find a 'struct NeighbourMapEntry'
+   * in this state in the map.  Accessing a 'struct NeighbourMapEntry'
+   * in this state virtually always means using memory that has been
+   * freed (the exception being the cleanup code in 'free_neighbour').
    */
   S_DISCONNECT_FINISHED
 };
@@ -589,7 +595,7 @@ static void *callback_cls;
 /**
  * Function to call when we connected to a neighbour.
  */
-static GNUNET_TRANSPORT_NotifyConnect connect_notify_cb;
+static NotifyConnect connect_notify_cb;
 
 /**
  * Function to call when we disconnected from a neighbour.
@@ -634,56 +640,39 @@ print_state (int state)
   {
   case S_NOT_CONNECTED:
     return "S_NOT_CONNECTED";
-    break;
   case S_INIT_ATS:
     return "S_INIT_ATS";
-    break;
   case S_INIT_BLACKLIST:
     return "S_INIT_BLACKLIST";
-    break;
   case S_CONNECT_SENT:
     return "S_CONNECT_SENT";
-    break;
   case S_CONNECT_RECV_BLACKLIST_INBOUND:
     return "S_CONNECT_RECV_BLACKLIST_INBOUND";
-    break;
   case S_CONNECT_RECV_ATS:
     return "S_CONNECT_RECV_ATS";
-    break;
   case S_CONNECT_RECV_BLACKLIST:
     return "S_CONNECT_RECV_BLACKLIST";
-    break;
   case S_CONNECT_RECV_ACK:
     return "S_CONNECT_RECV_ACK";
-    break;
   case S_CONNECTED:
     return "S_CONNECTED";
-    break;
   case S_RECONNECT_ATS:
     return "S_RECONNECT_ATS";
-    break;
   case S_RECONNECT_BLACKLIST:
     return "S_RECONNECT_BLACKLIST";
-    break;
   case S_RECONNECT_SENT:
     return "S_RECONNECT_SENT";
-    break;
   case S_CONNECTED_SWITCHING_BLACKLIST:
     return "S_CONNECTED_SWITCHING_BLACKLIST";
-    break;
   case S_CONNECTED_SWITCHING_CONNECT_SENT:
     return "S_CONNECTED_SWITCHING_CONNECT_SENT";
-    break;
   case S_DISCONNECT:
     return "S_DISCONNECT";
-    break;
   case S_DISCONNECT_FINISHED:
     return "S_DISCONNECT_FINISHED";
-    break;
   default:
-    return "UNDEFINED";
     GNUNET_break (0);
-    break;
+    return "UNDEFINED";
   }
   GNUNET_break (0);
   return "UNDEFINED";
@@ -766,16 +755,8 @@ free_address (struct NeighbourAddress *na)
   {
     GST_validation_set_address_use (na->address, na->session, GNUNET_NO, __LINE__);
     GNUNET_ATS_address_in_use (GST_ats, na->address, na->session, GNUNET_NO);
-    GNUNET_ATS_address_destroyed (GST_ats, na->address, na->session);
-  }
-  else
-  {
-    if (NULL != na->address)
-    {
-      GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "HACK: destroy address\n");
-      GNUNET_ATS_address_destroyed (GST_ats, na->address, na->session);
-    }
   }
+
   na->ats_active = GNUNET_NO;
   if (NULL != na->address)
   {
@@ -872,6 +853,7 @@ free_neighbour (struct NeighbourMapEntry *n, int keep_sessions)
 {
   struct MessageQueue *mq;
   struct GNUNET_TRANSPORT_PluginFunctions *papi;
+  struct GNUNET_HELLO_Address *backup_primary;
 
   n->is_active = NULL; /* always free'd by its own continuation! */
 
@@ -894,6 +876,17 @@ free_neighbour (struct NeighbourMapEntry *n, int keep_sessions)
     disconnect_notify_cb (callback_cls, &n->id);
   }
 
+  n->state = S_DISCONNECT_FINISHED;
+
+  if (NULL != n->primary_address.address)
+    backup_primary = GNUNET_HELLO_address_copy(n->primary_address.address);
+  else
+    backup_primary = NULL;
+
+  /* free addresses and mark as unused */
+  free_address (&n->primary_address);
+  free_address (&n->alternative_address);
+
   /* FIXME-PLUGIN-API: This does not seem to guarantee that all
      transport sessions eventually get killed due to inactivity; they
      MUST have their own timeout logic (but at least TCP doesn't have
@@ -903,21 +896,19 @@ free_neighbour (struct NeighbourMapEntry *n, int keep_sessions)
      API gives us not even the means to selectively kill only one of
      them! Killing all sessions like this seems to be very, very
      wrong. */
+
+  /* cut transport-level connection */
   if ((GNUNET_NO == keep_sessions) &&
-      (NULL != n->primary_address.address) &&
-      (NULL != (papi = GST_plugins_find (n->primary_address.address->transport_name))))
+      (NULL != backup_primary) &&
+      (NULL != (papi = GST_plugins_find (backup_primary->transport_name))))
     papi->disconnect (papi->cls, &n->id);
 
-  n->state = S_DISCONNECT_FINISHED;
+  GNUNET_free_non_null (backup_primary);
 
   GNUNET_assert (GNUNET_YES ==
                  GNUNET_CONTAINER_multihashmap_remove (neighbours,
                                                        &n->id.hashPubKey, n));
 
-  /* cut transport-level connection */
-  free_address (&n->primary_address);
-  free_address (&n->alternative_address);
-
   // FIXME-ATS-API: we might want to be more specific about
   // which states we do this from in the future (ATS should
   // have given us a 'suggest_address' handle, and if we have
@@ -1249,8 +1240,9 @@ try_transmission_to_peer (struct NeighbourMapEntry *n)
 
 /**
  * Send keepalive message to the neighbour.  Must only be called
- * if we are on 'connected' state.  Will internally determine
- * if a keepalive is truly needed (so can always be called).
+ * if we are on 'connected' state or while trying to switch addresses.
+ * Will internally determine if a keepalive is truly needed (so can
+ * always be called).
  *
  * @param n neighbour that went idle and needs a keepalive
  */
@@ -1259,7 +1251,9 @@ send_keepalive (struct NeighbourMapEntry *n)
 {
   struct GNUNET_MessageHeader m;
 
-  GNUNET_assert (S_CONNECTED == n->state);
+  GNUNET_assert ((S_CONNECTED == n->state) ||
+                 (S_CONNECTED_SWITCHING_BLACKLIST == n->state) ||
+                 (S_CONNECTED_SWITCHING_CONNECT_SENT));
   if (GNUNET_TIME_absolute_get_remaining (n->keep_alive_time).rel_value > 0)
     return; /* no keepalive needed at this time */
   m.size = htons (sizeof (struct GNUNET_MessageHeader));
@@ -2048,8 +2042,12 @@ GST_neighbours_handle_connect (const struct GNUNET_MessageHeader *message,
     check_blacklist (peer, ts, address, session, ats, ats_count);
     break;
   case S_INIT_ATS:
+    /* CONNECT message takes priority over us asking ATS for address */
+    n->state = S_CONNECT_RECV_BLACKLIST_INBOUND;
+    /* fallthrough */
   case S_INIT_BLACKLIST:
   case S_CONNECT_SENT:
+  case S_CONNECT_RECV_BLACKLIST_INBOUND:
   case S_CONNECT_RECV_ATS:
   case S_CONNECT_RECV_BLACKLIST:
   case S_CONNECT_RECV_ACK:
@@ -2155,11 +2153,14 @@ GST_neighbours_switch_to_address (const struct GNUNET_PeerIdentity *peer,
   }
 
   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
-              "ATS tells us to switch to address '%s' session %p for peer `%s' in state %s\n",
+              "ATS tells us to switch to address '%s' session %p for "
+              "peer `%s' in state %s (quota in/out %u %u )\n",
               (address->address_length != 0) ? GST_plugins_a2s (address): "<inbound>",
               session,
               GNUNET_i2s (peer),
-              print_state (n->state));
+              print_state (n->state),
+              ntohl (bandwidth_in.value__),
+              ntohl (bandwidth_out.value__));
 
   if (NULL == session)
   {
@@ -2506,7 +2507,6 @@ master_task (void *cls,
                "Cleaning up connection to `%s' after sending DISCONNECT\n",
                GNUNET_i2s (&n->id));
     free_neighbour (n, GNUNET_NO);
-    n->state = S_DISCONNECT_FINISHED;
     return;
   case S_DISCONNECT_FINISHED:
     /* how did we get here!? */
@@ -2620,7 +2620,9 @@ GST_neighbours_handle_connect_ack (const struct GNUNET_MessageHeader *message,
                           gettext_noop ("# peers connected"), 
                           ++neighbours_connected,
                           GNUNET_NO);
-    connect_notify_cb (callback_cls, &n->id, ats, ats_count);
+    connect_notify_cb (callback_cls, &n->id, ats, ats_count,
+                       n->primary_address.bandwidth_in,
+                       n->primary_address.bandwidth_out);
     /* Tell ATS that the outbound session we created to send CONNECT was successfull */
     GNUNET_ATS_address_add (GST_ats,
                             n->primary_address.address,
@@ -2816,6 +2818,7 @@ GST_neighbours_session_terminated (const struct GNUNET_PeerIdentity *peer,
     break;
   case S_DISCONNECT_FINISHED:
     /* neighbour was freed and plugins told to terminate session */
+    return GNUNET_NO;
     break;
   default:
     GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Unhandled state `%s' \n",print_state (n->state));
@@ -2879,7 +2882,9 @@ GST_neighbours_handle_session_ack (const struct GNUNET_MessageHeader *message,
                         gettext_noop ("# peers connected"), 
                         ++neighbours_connected,
                         GNUNET_NO);
-  connect_notify_cb (callback_cls, &n->id, ats, ats_count);
+  connect_notify_cb (callback_cls, &n->id, ats, ats_count,
+                     n->primary_address.bandwidth_in,
+                     n->primary_address.bandwidth_out);
   GNUNET_ATS_address_add(GST_ats,
                          n->primary_address.address,
                          n->primary_address.session,
@@ -3047,7 +3052,25 @@ neighbours_iterate (void *cls, const struct GNUNET_HashCode * key, void *value)
   struct NeighbourMapEntry *n = value;
 
   if (GNUNET_YES == test_connected (n))
-    ic->cb (ic->cb_cls, &n->id, NULL, 0, n->primary_address.address);
+  {
+    struct GNUNET_BANDWIDTH_Value32NBO bandwidth_in;
+    struct GNUNET_BANDWIDTH_Value32NBO bandwidth_out;
+
+    if (NULL != n->primary_address.address)
+    {
+      bandwidth_in = n->primary_address.bandwidth_in;
+      bandwidth_out = n->primary_address.bandwidth_out;
+    }
+    else
+    {
+      bandwidth_in = GNUNET_CONSTANTS_DEFAULT_BW_IN_OUT;
+      bandwidth_out = GNUNET_CONSTANTS_DEFAULT_BW_IN_OUT;
+    }
+
+    ic->cb (ic->cb_cls, &n->id, NULL, 0,
+            n->primary_address.address,
+            bandwidth_in, bandwidth_out);
+  }
   return GNUNET_OK;
 }
 
@@ -3110,15 +3133,20 @@ GST_neighbour_get_latency (const struct GNUNET_PeerIdentity *peer)
   switch (n->state)
   {
   case S_CONNECTED:
+  case S_CONNECTED_SWITCHING_CONNECT_SENT:
+  case S_CONNECTED_SWITCHING_BLACKLIST:
   case S_RECONNECT_SENT:
   case S_RECONNECT_ATS:
+  case S_RECONNECT_BLACKLIST:
     return n->latency;
   case S_NOT_CONNECTED:
   case S_INIT_BLACKLIST:
   case S_INIT_ATS:
   case S_CONNECT_RECV_BLACKLIST_INBOUND:
-  case S_CONNECT_SENT:
+  case S_CONNECT_RECV_ATS:
   case S_CONNECT_RECV_BLACKLIST:
+  case S_CONNECT_RECV_ACK:
+  case S_CONNECT_SENT:
   case S_DISCONNECT:
   case S_DISCONNECT_FINISHED:
     return GNUNET_TIME_UNIT_FOREVER_REL;
@@ -3160,7 +3188,7 @@ GST_neighbour_get_current_address (const struct GNUNET_PeerIdentity *peer)
  */
 void
 GST_neighbours_start (void *cls,
-                      GNUNET_TRANSPORT_NotifyConnect connect_cb,
+    NotifyConnect connect_cb,
                       GNUNET_TRANSPORT_NotifyDisconnect disconnect_cb,
                       GNUNET_TRANSPORT_PeerIterateCallback peer_address_cb)
 {