* @file topology/gnunet-daemon-topology.c
* @brief code for maintaining the mesh topology
* @author Christian Grothoff
- *
- * TODO:
- * - make sure that PEERINFO *also* notifies us when a HELLO expires
- * (otherwise the 'soft state' of this module does not work nicely)
- * - CORE API's connect/disconnect API is not nice (we think we are
- * connected when we are not; disconnect using bandwidth-limiting
- * does not really work and is not nice)
- * OPTIMIZATIONS:
- * - move code to use hash table instead of linked list
- * - instead of periodically discarding blacklisted entries,
- * simply add task that is triggered at the right time (earlier free,
- * more balanced load)
- * - check if new HELLO learned is different from old HELLO
- * before resetting entire state!
*/
#include <stdlib.h>
* Our handle for the request to connect to this peer; NULL if no
* such request is pending.
*/
- struct GNUNET_CORE_TransmitHandle *connect_req;
+ struct GNUNET_CORE_PeerRequestHandle *connect_req;
/**
* Pointer to the HELLO message of this peer; can be NULL.
*/
struct GNUNET_CONTAINER_BloomFilter *filter;
+ /**
+ * Our request handle for *whitelisting* this peer (NULL if
+ * no whitelisting request is pending).
+ */
+ struct GNUNET_TRANSPORT_BlacklistRequest *wh;
+
/**
* Is this peer listed here because he is a friend?
*/
*/
int is_connected;
+ /**
+ * Are we currently blocking this peer (via blacklist)?
+ */
+ int is_blocked;
+
/**
* Until what time should we not try to connect again
* to this peer?
/**
* Our request handle.
*/
- struct GNUNET_CORE_InformationRequestContext *rh;
-
+ struct GNUNET_TRANSPORT_BlacklistRequest *rh;
+
/**
* Peer we tried to disconnect.
*/
* has completed.
*
* @param cls our 'struct DisconnectList'
- * @param peer NULL on error (then what?)
- * @param bpm_in set to the current bandwidth limit (receiving) for this peer
- * @param bpm_out set to the current bandwidth limit (sending) for this peer
- * @param latency current latency estimate, "FOREVER" if we have been
- * disconnected
- * @param amount set to the amount that was actually reserved or unreserved
- * @param preference current traffic preference for the given peer
+ * @param tc unused
*/
static void
disconnect_done (void *cls,
- const struct
- GNUNET_PeerIdentity * peer,
- unsigned int bpm_in,
- unsigned int bpm_out,
- struct GNUNET_TIME_Relative
- latency, int amount,
- unsigned long long preference)
+ const struct GNUNET_SCHEDULER_TaskContext *tc)
{
struct DisconnectList *dl = cls;
/**
- * Force a disconnect from the specified peer. This is currently done by
- * changing the bandwidth policy to 0 bytes per second.
- * FIXME: maybe we want a nicer CORE API for both connect and disconnect...
- * FIXME: this policy change is never undone; how do we reconnect ever?
+ * Force a disconnect from the specified peer.
*/
static void
-force_disconnect (const struct GNUNET_PeerIdentity *peer)
+force_disconnect (struct PeerList *pl)
{
+ const struct GNUNET_PeerIdentity *peer = &pl->id;
struct DisconnectList *dl;
+ if (NULL != pl->wh)
+ {
+ GNUNET_TRANSPORT_blacklist_cancel (pl->wh);
+ pl->wh = NULL;
+ }
+ pl->is_blocked = GNUNET_YES;
dl = GNUNET_malloc (sizeof (struct DisconnectList));
dl->peer = *peer;
GNUNET_CONTAINER_DLL_insert (disconnect_head,
disconnect_tail,
dl);
- dl->rh = GNUNET_CORE_peer_get_info (sched, cfg,
- peer,
- GNUNET_TIME_UNIT_FOREVER_REL,
- 0,
- 0,
- 0,
- &disconnect_done,
- dl);
+ dl->rh = GNUNET_TRANSPORT_blacklist (sched, cfg,
+ peer,
+ GNUNET_TIME_UNIT_FOREVER_REL,
+ GNUNET_TIME_UNIT_FOREVER_REL,
+ &disconnect_done,
+ dl);
+}
+
+
+
+/**
+ * Function called once our request to 'whitelist' a peer
+ * has completed.
+ *
+ * @param cls our 'struct PeerList'
+ * @param tc unused
+ */
+static void
+whitelist_done (void *cls,
+ const struct GNUNET_SCHEDULER_TaskContext *tc)
+{
+ struct PeerList *pl = cls;
+
+ pl->wh = NULL;
+}
+
+
+/**
+ * Whitelist all peers that we blacklisted; we've passed
+ * the minimum number of friends.
+ */
+static void
+whitelist_peers ()
+{
+ struct PeerList *pl;
+ struct DisconnectList *dl;
+
+ /* first, cancel all blacklisting requests */
+ while (NULL != (dl = disconnect_head))
+ {
+ GNUNET_CONTAINER_DLL_remove (disconnect_head,
+ disconnect_tail,
+ dl);
+ GNUNET_TRANSPORT_blacklist_cancel (dl->rh);
+ GNUNET_free (dl);
+ }
+ /* then, specifically whitelist all peers that we
+ know to have blacklisted */
+ pl = peers;
+ while (pl != NULL)
+ {
+ if (pl->is_blocked)
+ {
+ pl->wh = GNUNET_TRANSPORT_blacklist (sched, cfg,
+ &pl->id,
+ GNUNET_TIME_UNIT_ZERO,
+ GNUNET_TIME_UNIT_FOREVER_REL,
+ &whitelist_done,
+ pl);
+ pl->is_blocked = GNUNET_NO;
+ }
+ pl = pl->next;
+ }
}
/**
* Function called by core when our attempt to connect succeeded.
- * Transmits a 'DUMMY' message to trigger the session key exchange.
- * FIXME: this is an issue with the current CORE API.
*/
-static size_t
-ready_callback (void *cls,
- size_t size, void *buf)
+static void
+connect_completed_callback (void *cls,
+ const struct GNUNET_SCHEDULER_TaskContext *tc)
{
struct PeerList *pos = cls;
- struct GNUNET_MessageHeader hdr;
pos->connect_req = NULL;
- if (buf == NULL)
- {
-#if DEBUG_TOPOLOGY
- GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
- "Core told us that our attempt to connect failed.\n");
-#endif
- return 0;
- }
-#if DEBUG_TOPOLOGY
- GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
- "Sending dummy message to establish connection.\n");
-#endif
- hdr.size = htons (sizeof (struct GNUNET_MessageHeader));
- hdr.type = htons (GNUNET_MESSAGE_TYPE_TOPOLOGY_DUMMY);
- memcpy (buf, &hdr, sizeof (struct GNUNET_MessageHeader));
- return sizeof (struct GNUNET_MessageHeader);
}
"Asking core to connect to `%s'\n",
GNUNET_i2s (&pos->id));
#endif
- pos->connect_req = GNUNET_CORE_notify_transmit_ready (handle,
- 0 /* priority */,
- GNUNET_TIME_UNIT_MINUTES,
- &pos->id,
- sizeof(struct GNUNET_MessageHeader),
- &ready_callback,
- pos);
+ pos->connect_req = GNUNET_CORE_peer_request_connect (sched, cfg,
+ GNUNET_TIME_UNIT_MINUTES,
+ &pos->id,
+ &connect_completed_callback,
+ pos);
}
{
struct PeerList *pos;
struct PeerList *prev;
- struct PeerList *next;
prev = NULL;
- next = peers;
- while (peer != (pos = next))
+ pos = peers;
+ while (peer != pos)
{
- next = pos->next;
prev = pos;
+ pos = pos->next;
}
GNUNET_assert (pos != NULL);
if (prev == NULL)
- peers = next;
+ peers = pos->next;
else
- prev->next = next;
+ prev->next = pos->next;
if (pos->hello_req != NULL)
GNUNET_CORE_notify_transmit_ready_cancel (pos->hello_req);
+ if (pos->wh != NULL)
+ GNUNET_TRANSPORT_blacklist_cancel (pos->wh);
if (pos->connect_req != NULL)
- GNUNET_CORE_notify_transmit_ready_cancel (pos->connect_req);
+ GNUNET_CORE_peer_request_connect_cancel (pos->connect_req);
if (pos->hello_delay_task != GNUNET_SCHEDULER_NO_TASK)
GNUNET_SCHEDULER_cancel (sched,
pos->hello_delay_task);
+ GNUNET_free_non_null (pos->hello);
+ if (pos->filter != NULL)
+ GNUNET_CONTAINER_bloomfilter_free (peer->filter);
GNUNET_free (pos);
}
*
* @param cls closure
* @param peer peer identity this notification is about
+ * @param latency reported latency of the connection with 'other'
+ * @param distance reported distance (DV) to 'other'
*/
static void
connect_notify (void *cls,
const struct
- GNUNET_PeerIdentity * peer)
+ GNUNET_PeerIdentity * peer,
+ struct GNUNET_TIME_Relative latency,
+ uint32_t distance)
{
struct PeerList *pos;
"Connection to `%s' is forbidden, forcing disconnect!\n",
GNUNET_i2s (peer));
#endif
- force_disconnect (&pos->id);
+ force_disconnect (pos);
return;
}
}
}
pos->is_connected = GNUNET_YES;
if (pos->is_friend)
- friend_count++;
+ {
+ if ( (friend_count == minimum_friend_count - 1) &&
+ (GNUNET_YES != friends_only) )
+ whitelist_peers ();
+ friend_count++;
+ }
reschedule_hellos (pos);
}
"Connection to `%s' is not from a friend, forcing disconnect!\n",
GNUNET_i2s (&pos->id));
#endif
- force_disconnect (&pos->id);
+ force_disconnect (pos);
}
pos = pos->next;
}
if (peer == NULL)
peer = make_peer (&pid, hello, GNUNET_NO);
// FIXME: check if 'hello' is any different from peer->hello?
- GNUNET_free_non_null (peer->hello);
#if DEBUG_TOPOLOGY
GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
"Found `%s' from peer `%s' for advertising\n",
GNUNET_i2s (&pid));
#endif
size = GNUNET_HELLO_size (hello);
+ GNUNET_free_non_null (peer->hello);
peer->hello = GNUNET_malloc (size);
memcpy (peer->hello, hello, size);
if (peer->filter != NULL)
}
consider_for_advertising (hello);
pos = find_peer (peer);
+ GNUNET_assert (NULL != pos);
#if DEBUG_TOPOLOGY
GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
"Considering connecting to peer `%s'\n",
next = pos->next;
if ( (GNUNET_NO == pos->is_friend) &&
(GNUNET_NO == pos->is_connected) &&
+ (GNUNET_NO == pos->is_blocked) &&
(0 == GNUNET_TIME_absolute_get_remaining (pos->blacklisted_until).value) )
free_peer (pos);
}
* @param other the other peer involved (sender or receiver, NULL
* for loopback messages where we are both sender and receiver)
* @param message the actual HELLO message
+ * @param latency reported latency of the connection with 'other'
+ * @param distance reported distance (DV) to 'other'
* @return GNUNET_OK to keep the connection open,
* GNUNET_SYSERR to close it (signal serious error)
*/
handle_encrypted_hello (void *cls,
const struct GNUNET_PeerIdentity * other,
const struct GNUNET_MessageHeader *
- message)
+ message,
+ struct GNUNET_TIME_Relative latency,
+ uint32_t distance)
{
#if DEBUG_TOPOLOGY
GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
GNUNET_CONTAINER_DLL_remove (disconnect_head,
disconnect_tail,
dl);
- GNUNET_CORE_peer_get_info_cancel (dl->rh);
+ GNUNET_TRANSPORT_blacklist_cancel (dl->rh);
GNUNET_free (dl);
}
}
GNUNET_TIME_UNIT_FOREVER_REL,
NULL,
&core_init,
+ NULL,
&connect_notify,
&disconnect_notify,
NULL, GNUNET_NO,