+/**
+ * Get distance information from 'atsi'.
+ *
+ * @param atsi performance data
+ * @param atsi_count number of entries in atsi
+ * @return connected transport distance
+ */
+static uint32_t
+get_atsi_distance (const struct GNUNET_ATS_Information *atsi,
+ uint32_t atsi_count)
+{
+ uint32_t i;
+
+ for (i = 0; i < atsi_count; i++)
+ if (ntohl (atsi[i].type) == GNUNET_ATS_QUALITY_NET_DISTANCE)
+ return (0 == ntohl (atsi->value)) ? DIRECT_NEIGHBOR_COST : ntohl (atsi->value); // FIXME: 0 check should not be required once ATS is fixed!
+ /* If we do not have explicit distance data, assume direct neighbor. */
+ return DIRECT_NEIGHBOR_COST;
+}
+
+
+/**
+ * Multipeermap iterator for freeing routes that go via a particular
+ * neighbor that disconnected and is thus no longer available.
+ *
+ * @param cls the direct neighbor that is now unavailable
+ * @param key key value stored under
+ * @param value a 'struct Route' that may or may not go via neighbor
+ *
+ * @return GNUNET_YES to continue iteration, GNUNET_NO to stop
+ */
+static int
+cull_routes (void *cls,
+ const struct GNUNET_PeerIdentity *key,
+ void *value)
+{
+ struct DirectNeighbor *neighbor = cls;
+ struct Route *route = value;
+
+ if (route->next_hop != neighbor)
+ return GNUNET_YES; /* not affected */
+ GNUNET_assert (GNUNET_YES ==
+ GNUNET_CONTAINER_multipeermap_remove (all_routes, key, value));
+ release_route (route);
+ send_disconnect_to_plugin (&route->target.peer);
+ GNUNET_free (route);
+ return GNUNET_YES;
+}
+
+
+/**
+ * Handle the case that a direct connection to a peer is
+ * disrupted. Remove all routes via that peer and
+ * stop the consensus with it.
+ *
+ * @param neighbor peer that was disconnected (or at least is no
+ * longer at distance 1)
+ */
+static void
+handle_direct_disconnect (struct DirectNeighbor *neighbor)
+{
+ GNUNET_CONTAINER_multipeermap_iterate (all_routes,
+ &cull_routes,
+ neighbor);
+ if (NULL != neighbor->cth)
+ {
+ GNUNET_CORE_notify_transmit_ready_cancel (neighbor->cth);
+ neighbor->cth = NULL;
+ }
+
+ if (NULL != neighbor->direct_route)
+ {
+ release_route (neighbor->direct_route);
+ GNUNET_free (neighbor->direct_route);
+ neighbor->direct_route = NULL;
+ }
+
+ if (NULL != neighbor->neighbor_table_consensus)
+ {
+ GNUNET_CONTAINER_multipeermap_iterate (neighbor->neighbor_table_consensus,
+ &free_targets,
+ NULL);
+ GNUNET_CONTAINER_multipeermap_destroy (neighbor->neighbor_table_consensus);
+ neighbor->neighbor_table_consensus = NULL;
+ }
+ if (NULL != neighbor->neighbor_table)
+ {
+ GNUNET_CONTAINER_multipeermap_iterate (neighbor->neighbor_table,
+ &free_targets,
+ NULL);
+ GNUNET_CONTAINER_multipeermap_destroy (neighbor->neighbor_table);
+ neighbor->neighbor_table = NULL;
+ }
+ if (NULL != neighbor->set_op)
+ {
+ GNUNET_SET_operation_cancel (neighbor->set_op);
+ neighbor->set_op = NULL;
+ }
+ if (NULL != neighbor->my_set)
+ {
+ GNUNET_SET_destroy (neighbor->my_set);
+ neighbor->my_set = NULL;
+ }
+ if (NULL != neighbor->listen_handle)
+ {
+ GNUNET_SET_listen_cancel (neighbor->listen_handle);
+ neighbor->listen_handle = NULL;
+ }
+ if (GNUNET_SCHEDULER_NO_TASK != neighbor->initiate_task)
+ {
+ GNUNET_SCHEDULER_cancel (neighbor->initiate_task);
+ neighbor->initiate_task = GNUNET_SCHEDULER_NO_TASK;
+ }
+}
+
+
+/**
+ * Function that is called with QoS information about an address; used
+ * to update our current distance to another peer.
+ *
+ * @param cls closure
+ * @param address the address
+ * @param active is this address in active use
+ * @param bandwidth_out assigned outbound bandwidth for the connection
+ * @param bandwidth_in assigned inbound bandwidth for the connection
+ * @param ats performance data for the address (as far as known)
+ * @param ats_count number of performance records in 'ats'
+ */
+static void
+handle_ats_update (void *cls,
+ const struct GNUNET_HELLO_Address *address,
+ int active,
+ struct GNUNET_BANDWIDTH_Value32NBO bandwidth_out,
+ struct GNUNET_BANDWIDTH_Value32NBO bandwidth_in,
+ const struct GNUNET_ATS_Information *ats,
+ uint32_t ats_count)
+{
+ struct DirectNeighbor *neighbor;
+ uint32_t distance;
+
+ if (GNUNET_NO == active)
+ return;
+ distance = get_atsi_distance (ats, ats_count);
+ /*
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+ "ATS says distance to %s is %u\n",
+ GNUNET_i2s (&address->peer),
+ (unsigned int) distance);*/
+ /* check if entry exists */
+ neighbor = GNUNET_CONTAINER_multipeermap_get (direct_neighbors,
+ &address->peer);
+ if (NULL != neighbor)
+ {
+ if ( (DIRECT_NEIGHBOR_COST == neighbor->distance) &&
+ (DIRECT_NEIGHBOR_COST == distance) )
+ return; /* no change */
+ if (DIRECT_NEIGHBOR_COST == neighbor->distance)
+ {
+ neighbor->distance = distance;
+ GNUNET_STATISTICS_update (stats,
+ "# peers connected (1-hop)",
+ -1, GNUNET_NO);
+ handle_direct_disconnect (neighbor);
+ GNUNET_CONTAINER_multipeermap_iterate (direct_neighbors,
+ &refresh_routes,
+ NULL);
+ return;
+ }
+ neighbor->distance = distance;
+ if (DIRECT_NEIGHBOR_COST != neighbor->distance)
+ return;
+ if (GNUNET_YES != neighbor->connected)
+ return;
+ handle_direct_connect (neighbor);
+ return;
+ }
+ neighbor = GNUNET_new (struct DirectNeighbor);
+ neighbor->peer = address->peer;
+ GNUNET_assert (GNUNET_YES ==
+ GNUNET_CONTAINER_multipeermap_put (direct_neighbors,
+ &address->peer,
+ neighbor,
+ GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY));
+ neighbor->connected = GNUNET_NO; /* not yet */
+ neighbor->distance = distance;
+}
+
+