+ * @param cls the 'struct DirectNeighbor' we're building the consensus with
+ * @param element a result element, only valid if status is GNUNET_SET_STATUS_OK
+ * @param status see enum GNUNET_SET_Status
+ */
+static void
+handle_set_union_result (void *cls,
+ const struct GNUNET_SET_Element *element,
+ enum GNUNET_SET_Status status)
+{
+ struct DirectNeighbor *neighbor = cls;
+ struct Target *target;
+ char *status_str;
+
+ switch (status) {
+ case GNUNET_SET_STATUS_OK:
+ status_str = "GNUNET_SET_STATUS_OK";
+ break;
+ case GNUNET_SET_STATUS_TIMEOUT:
+ status_str = "GNUNET_SET_STATUS_TIMEOUT";
+ break;
+ case GNUNET_SET_STATUS_FAILURE:
+ status_str = "GNUNET_SET_STATUS_FAILURE";
+ break;
+ case GNUNET_SET_STATUS_HALF_DONE:
+ status_str = "GNUNET_SET_STATUS_HALF_DONE";
+ break;
+ case GNUNET_SET_STATUS_DONE:
+ status_str = "GNUNET_SET_STATUS_DONE";
+ break;
+ default:
+ status_str = "UNDEFINED";
+ break;
+ }
+
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+ "Got SET union result: %s\n",
+ status_str);
+ switch (status)
+ {
+ case GNUNET_SET_STATUS_OK:
+ if (sizeof (struct Target) != element->size)
+ {
+ GNUNET_break_op (0);
+ return;
+ }
+ target = GNUNET_new (struct Target);
+ memcpy (target, element->data, sizeof (struct Target));
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+ "Received information about peer `%s' with distance %u\n",
+ GNUNET_i2s (&target->peer), ntohl(target->distance));
+ if (NULL == neighbor->neighbor_table_consensus)
+ neighbor->neighbor_table_consensus = GNUNET_CONTAINER_multipeermap_create (10, GNUNET_NO);
+ if (GNUNET_YES !=
+ GNUNET_CONTAINER_multipeermap_put (neighbor->neighbor_table_consensus,
+ &target->peer,
+ target,
+ GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY))
+ {
+ GNUNET_break_op (0);
+ GNUNET_free (target);
+ }
+ break;
+ case GNUNET_SET_STATUS_TIMEOUT:
+ case GNUNET_SET_STATUS_FAILURE:
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+ "Failed to establish DV union, will try again later\n");
+ neighbor->set_op = 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 (0 < memcmp (&neighbor->peer,
+ &my_identity,
+ sizeof (struct GNUNET_PeerIdentity)))
+ neighbor->initiate_task = GNUNET_SCHEDULER_add_delayed (GNUNET_DV_CONSENSUS_FREQUENCY,
+ &initiate_set_union,
+ neighbor);
+ break;
+ case GNUNET_SET_STATUS_HALF_DONE:
+ break;
+ case GNUNET_SET_STATUS_DONE:
+ /* we got all of our updates; integrate routing table! */
+ neighbor->target_removed = GNUNET_NO;
+ if (NULL == neighbor->neighbor_table_consensus)
+ neighbor->neighbor_table_consensus = GNUNET_CONTAINER_multipeermap_create (10, GNUNET_NO);
+ if (NULL != neighbor->neighbor_table)
+ GNUNET_CONTAINER_multipeermap_iterate (neighbor->neighbor_table,
+ &check_target_removed,
+ neighbor);
+ if (GNUNET_YES == neighbor->target_removed)
+ {
+ /* check if we got an alternative for the removed routes */
+ GNUNET_CONTAINER_multipeermap_iterate (direct_neighbors,
+ &refresh_routes,
+ NULL);
+ }
+ /* add targets that appeared (and check for improved routes) */
+ GNUNET_CONTAINER_multipeermap_iterate (neighbor->neighbor_table_consensus,
+ &check_target_added,
+ neighbor);
+ 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;
+ }
+ neighbor->neighbor_table = neighbor->neighbor_table_consensus;
+ neighbor->neighbor_table_consensus = NULL;
+
+ /* operation done, schedule next run! */
+ neighbor->set_op = NULL;
+ if (0 < memcmp (&neighbor->peer,
+ &my_identity,
+ sizeof (struct GNUNET_PeerIdentity)))
+ neighbor->initiate_task = GNUNET_SCHEDULER_add_delayed (GNUNET_DV_CONSENSUS_FREQUENCY,
+ &initiate_set_union,
+ neighbor);
+ break;
+ default:
+ GNUNET_break (0);
+ return;
+ }
+}
+
+
+/**
+ * Start creating a new DV set union construction, our neighbour has
+ * asked for it (callback for listening peer).
+ *
+ * @param cls the 'struct DirectNeighbor' of the peer we're building
+ * a routing consensus with
+ * @param other_peer the other peer
+ * @param context_msg message with application specific information from
+ * the other peer
+ * @param request request from the other peer, use GNUNET_SET_accept
+ * to accept it, otherwise the request will be refused
+ * Note that we don't use a return value here, as it is also
+ * necessary to specify the set we want to do the operation with,
+ * whith sometimes can be derived from the context message.
+ * Also necessary to specify the timeout.
+ */
+static void
+listen_set_union (void *cls,
+ const struct GNUNET_PeerIdentity *other_peer,
+ const struct GNUNET_MessageHeader *context_msg,
+ struct GNUNET_SET_Request *request)
+{
+ struct DirectNeighbor *neighbor = cls;
+
+ if (NULL == request)
+ return; /* why??? */
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+ "Starting to create consensus with %s\n",
+ GNUNET_i2s (&neighbor->peer));
+ 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;
+ }
+ neighbor->my_set = GNUNET_SET_create (cfg,
+ GNUNET_SET_OPERATION_UNION);
+ neighbor->set_op = GNUNET_SET_accept (request,
+ GNUNET_SET_RESULT_ADDED,
+ &handle_set_union_result,
+ neighbor);
+ neighbor->consensus_insertion_offset = 0;
+ neighbor->consensus_insertion_distance = 0;
+ neighbor->consensus_elements = 0;
+ build_set (neighbor);
+}
+
+
+/**
+ * Start creating a new DV set union by initiating the connection.
+ *
+ * @param cls the 'struct DirectNeighbor' of the peer we're building
+ * a routing consensus with
+ * @param tc scheduler context
+ */
+static void
+initiate_set_union (void *cls,
+ const struct GNUNET_SCHEDULER_TaskContext *tc)
+{
+ struct DirectNeighbor *neighbor = cls;
+
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+ "Initiating SET union with peer `%s'\n",
+ GNUNET_i2s (&neighbor->peer));
+ neighbor->initiate_task = GNUNET_SCHEDULER_NO_TASK;
+ neighbor->my_set = GNUNET_SET_create (cfg,
+ GNUNET_SET_OPERATION_UNION);
+ neighbor->set_op = GNUNET_SET_prepare (&neighbor->peer,
+ &neighbor->real_session_id,
+ NULL,
+ 0 /* FIXME: salt */,
+ GNUNET_SET_RESULT_ADDED,
+ &handle_set_union_result,
+ neighbor);
+ neighbor->consensus_insertion_offset = 0;
+ neighbor->consensus_insertion_distance = 0;
+ neighbor->consensus_elements = 0;
+ build_set (neighbor);
+}
+
+
+/**
+ * Core handler for DV data messages. Whatever this message
+ * contains all we really have to do is rip it out of its
+ * DV layering and give it to our pal the DV plugin to report
+ * in with.
+ *
+ * @param cls closure
+ * @param peer peer which sent the message (immediate sender)
+ * @param message the message
+ * @return GNUNET_OK on success, GNUNET_SYSERR if the other peer violated the protocol