* TODO:
* Basics:
* - test!
- * - better message queue management (bounded state, drop oldest/RED?)
- * - actually destroy "stale" tunnels once we have too many!
*
* Features:
* - add back ICMP support (especially needed for IPv6)
#include "exit.h"
+/**
+ * Maximum number of messages we allow in the queue for mesh.
+ */
+#define MAX_MESSAGE_QUEUE_SIZE 4
+
+
/**
* State we keep for each of our tunnels.
*/
/**
* Key under which this entry is in the 'destination_map' (only valid
- * if 'heap_node != NULL'.
+ * if 'heap_node != NULL').
*/
GNUNET_HashCode key;
*/
struct TunnelState
{
+
/**
* Information about the tunnel to use, NULL if no tunnel
* is available right now.
* Add the given message to the given tunnel and trigger the
* transmission process.
*
- * FIXME: bound queue length!
- *
* @param tnq message to queue
* @param ts tunnel to queue the message for
*/
ts->tmq_tail,
tnq);
ts->tmq_length++;
+ if (ts->tmq_length > MAX_MESSAGE_QUEUE_SIZE)
+ {
+ struct TunnelMessageQueueEntry *dq;
+
+ dq = ts->tmq_head;
+ GNUNET_assert (dq != tnq);
+ GNUNET_CONTAINER_DLL_remove (ts->tmq_head,
+ ts->tmq_tail,
+ dq);
+ GNUNET_MESH_notify_transmit_ready_cancel (ts->th);
+ ts->th = NULL;
+ GNUNET_STATISTICS_update (stats,
+ gettext_noop ("# Bytes dropped in mesh queue (overflow)"),
+ dq->len,
+ GNUNET_NO);
+ GNUNET_free (dq);
+ }
if (NULL == ts->th)
ts->th = GNUNET_MESH_notify_transmit_ready (ts->tunnel,
GNUNET_NO /* cork */,
}
+/**
+ * Free resources associated with a tunnel state.
+ *
+ * @param ts state to free
+ */
+static void
+free_tunnel_state (struct TunnelState *ts)
+{
+ GNUNET_HashCode key;
+ struct TunnelMessageQueueEntry *tnq;
+
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+ "Cleaning up tunnel state\n");
+ GNUNET_STATISTICS_update (stats,
+ gettext_noop ("# Active tunnels"),
+ -1, GNUNET_NO);
+ while (NULL != (tnq = ts->tmq_head))
+ {
+ GNUNET_CONTAINER_DLL_remove (ts->tmq_head,
+ ts->tmq_tail,
+ tnq);
+ ts->tmq_length--;
+ GNUNET_free (tnq);
+ }
+ GNUNET_assert (0 == ts->tmq_length);
+ if (NULL != ts->client)
+ {
+ GNUNET_SERVER_client_drop (ts->client);
+ ts->client = NULL;
+ }
+ if (NULL != ts->th)
+ {
+ GNUNET_MESH_notify_transmit_ready_cancel (ts->th);
+ ts->th = NULL;
+ }
+ GNUNET_assert (NULL == ts->destination.heap_node);
+ if (NULL != ts->tunnel)
+ {
+ GNUNET_MESH_tunnel_destroy (ts->tunnel);
+ ts->tunnel = NULL;
+ }
+ if (NULL != ts->heap_node)
+ {
+ GNUNET_CONTAINER_heap_remove_node (ts->heap_node);
+ ts->heap_node = NULL;
+ get_tunnel_key_from_ips (ts->af,
+ ts->protocol,
+ &ts->source_ip,
+ ts->source_port,
+ &ts->destination_ip,
+ ts->destination_port,
+ &key);
+ GNUNET_assert (GNUNET_YES ==
+ GNUNET_CONTAINER_multihashmap_remove (tunnel_map,
+ &key,
+ ts));
+ }
+ if (NULL != ts->destination_container)
+ {
+ GNUNET_assert (ts == ts->destination_container->ts);
+ ts->destination_container->ts = NULL;
+ ts->destination_container = NULL;
+ }
+ GNUNET_free (ts);
+}
+
+
+/**
+ * We have too many active tunnels. Clean up the oldest tunnel.
+ *
+ * @param except tunnel that must NOT be cleaned up, even if it is the oldest
+ */
+static void
+expire_tunnel (struct TunnelState *except)
+{
+ struct TunnelState *ts;
+
+ ts = GNUNET_CONTAINER_heap_peek (tunnel_heap);
+ if (except == ts)
+ return; /* can't do this */
+ free_tunnel_state (ts);
+}
+
+
/**
* Route a packet via mesh to the given destination.
*
GNUNET_STATISTICS_update (stats,
gettext_noop ("# Active tunnels"),
1, GNUNET_NO);
- /* FIXME: expire OLD tunnels if we have too many! */
+ while (GNUNET_CONTAINER_multihashmap_size (tunnel_map) > max_tunnel_mappings)
+ expire_tunnel (ts);
}
else
{
}
+/**
+ * Free resources occupied by a destination entry.
+ *
+ * @param de entry to free
+ */
+static void
+free_destination_entry (struct DestinationEntry *de)
+{
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+ "Cleaning up destination entry\n");
+ GNUNET_STATISTICS_update (stats,
+ gettext_noop ("# Active destinations"),
+ -1, GNUNET_NO);
+ if (NULL != de->ts)
+ {
+ free_tunnel_state (de->ts);
+ GNUNET_assert (NULL == de->ts);
+ }
+ if (NULL != de->heap_node)
+ {
+ GNUNET_CONTAINER_heap_remove_node (de->heap_node);
+ de->heap_node = NULL;
+ GNUNET_assert (GNUNET_YES ==
+ GNUNET_CONTAINER_multihashmap_remove (destination_map,
+ &de->key,
+ de));
+ }
+ GNUNET_free (de);
+}
+
+
+/**
+ * We have too many active destinations. Clean up the oldest destination.
+ *
+ * @param except destination that must NOT be cleaned up, even if it is the oldest
+ */
+static void
+expire_destination (struct DestinationEntry *except)
+{
+ struct DestinationEntry *de;
+
+ de = GNUNET_CONTAINER_heap_peek (destination_heap);
+ if (except == de)
+ return; /* can't do this */
+ free_destination_entry (de);
+}
+
+
/**
* A client asks us to setup a redirection via some exit
* node to a particular IP. Setup the redirection and
GNUNET_STATISTICS_update (stats,
gettext_noop ("# Active destinations"),
1, GNUNET_NO);
+ while (GNUNET_CONTAINER_multihashmap_size (destination_map) > max_destination_mappings)
+ expire_destination (de);
- /* FIXME: expire OLD destinations if we have too many! */
/* setup tunnel to destination */
(void) create_tunnel_to_destination (de,
(GNUNET_NO == ntohl (msg->nac)) ? NULL : client,
de->heap_node = GNUNET_CONTAINER_heap_insert (destination_heap,
de,
GNUNET_TIME_absolute_ntoh (msg->expiration_time).abs_value);
- /* FIXME: expire OLD destinations if we have too many! */
+ while (GNUNET_CONTAINER_multihashmap_size (destination_map) > max_destination_mappings)
+ expire_destination (de);
(void) create_tunnel_to_destination (de,
(GNUNET_NO == ntohl (msg->nac)) ? NULL : client,
msg->request_id);
}
-/**
- * Free resources associated with a tunnel state.
- *
- * @param ts state to free
- */
-static void
-free_tunnel_state (struct TunnelState *ts)
-{
- GNUNET_HashCode key;
- struct TunnelMessageQueueEntry *tnq;
-
- GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
- "Cleaning up tunnel state\n");
- GNUNET_STATISTICS_update (stats,
- gettext_noop ("# Active tunnels"),
- -1, GNUNET_NO);
- while (NULL != (tnq = ts->tmq_head))
- {
- GNUNET_CONTAINER_DLL_remove (ts->tmq_head,
- ts->tmq_tail,
- tnq);
- ts->tmq_length--;
- GNUNET_free (tnq);
- }
- GNUNET_assert (0 == ts->tmq_length);
- if (NULL != ts->client)
- {
- GNUNET_SERVER_client_drop (ts->client);
- ts->client = NULL;
- }
- if (NULL != ts->th)
- {
- GNUNET_MESH_notify_transmit_ready_cancel (ts->th);
- ts->th = NULL;
- }
- GNUNET_assert (NULL == ts->destination.heap_node);
- if (NULL != ts->tunnel)
- {
- GNUNET_MESH_tunnel_destroy (ts->tunnel);
- ts->tunnel = NULL;
- }
- if (NULL != ts->heap_node)
- {
- GNUNET_CONTAINER_heap_remove_node (ts->heap_node);
- ts->heap_node = NULL;
- get_tunnel_key_from_ips (ts->af,
- ts->protocol,
- &ts->source_ip,
- ts->source_port,
- &ts->destination_ip,
- ts->destination_port,
- &key);
- GNUNET_assert (GNUNET_YES ==
- GNUNET_CONTAINER_multihashmap_remove (tunnel_map,
- &key,
- ts));
- }
- if (NULL != ts->destination_container)
- {
- GNUNET_assert (ts == ts->destination_container->ts);
- ts->destination_container->ts = NULL;
- ts->destination_container = NULL;
- }
- GNUNET_free (ts);
-}
-
-
-/**
- * Free resources occupied by a destination entry.
- *
- * @param de entry to free
- */
-static void
-free_destination_entry (struct DestinationEntry *de)
-{
- GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
- "Cleaning up destination entry\n");
- GNUNET_STATISTICS_update (stats,
- gettext_noop ("# Active destinations"),
- -1, GNUNET_NO);
- if (NULL != de->ts)
- {
- free_tunnel_state (de->ts);
- GNUNET_assert (NULL == de->ts);
- }
- if (NULL != de->heap_node)
- {
- GNUNET_CONTAINER_heap_remove_node (de->heap_node);
- de->heap_node = NULL;
- GNUNET_assert (GNUNET_YES ==
- GNUNET_CONTAINER_multihashmap_remove (destination_map,
- &de->key,
- de));
- }
- GNUNET_free (de);
-}
-
-
/**
* Function called whenever an inbound tunnel is destroyed. Should clean up
* any associated state.