X-Git-Url: https://git.librecmc.org/?a=blobdiff_plain;f=src%2Fvpn%2Fgnunet-service-vpn.c;h=aa95e429f75b6c0eb56f307002dba490671934ab;hb=bb31efde0afb9875e1145a5501ef50ae4d8005ef;hp=7219471bf061100f174d4a03c14ee8f99f46e745;hpb=a72a1c6fc09216c5be7f5ee697ff55cf66907c80;p=oweals%2Fgnunet.git diff --git a/src/vpn/gnunet-service-vpn.c b/src/vpn/gnunet-service-vpn.c index 7219471bf..aa95e429f 100644 --- a/src/vpn/gnunet-service-vpn.c +++ b/src/vpn/gnunet-service-vpn.c @@ -28,11 +28,7 @@ * * TODO: * Basics: - * - need some logging - * - need some statistics * - 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) @@ -47,12 +43,19 @@ #include "gnunet_protocols.h" #include "gnunet_applications.h" #include "gnunet_mesh_service.h" +#include "gnunet_statistics_service.h" #include "gnunet_constants.h" #include "tcpip_tun.h" #include "vpn.h" #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. */ @@ -68,7 +71,7 @@ struct DestinationEntry /** * Key under which this entry is in the 'destination_map' (only valid - * if 'heap_node != NULL'. + * if 'heap_node != NULL'). */ GNUNET_HashCode key; @@ -171,6 +174,7 @@ struct TunnelMessageQueueEntry */ struct TunnelState { + /** * Information about the tunnel to use, NULL if no tunnel * is available right now. @@ -191,12 +195,12 @@ struct TunnelState /** * Head of list of messages scheduled for transmission. */ - struct TunnelMessageQueueEntry *head; + struct TunnelMessageQueueEntry *tmq_head; /** * Tail of list of messages scheduled for transmission. */ - struct TunnelMessageQueueEntry *tail; + struct TunnelMessageQueueEntry *tmq_tail; /** * Client that needs to be notified about the tunnel being @@ -204,6 +208,12 @@ struct TunnelState */ struct GNUNET_SERVER_Client *client; + /** + * Destination entry that has a pointer to this tunnel state; + * NULL if this tunnel state is in the tunnel map. + */ + struct DestinationEntry *destination_container; + /** * ID of the client request that caused us to setup this entry. */ @@ -218,16 +228,20 @@ struct TunnelState struct DestinationEntry destination; /** - * Destination entry that has a pointer to this tunnel state; - * NULL if this tunnel state is in the tunnel map. + * Task scheduled to destroy the tunnel (or NO_TASK). */ - struct DestinationEntry *destination_container; + GNUNET_SCHEDULER_TaskIdentifier destroy_task; /** * Addess family used for this tunnel on the local TUN interface. */ int af; + /** + * Length of the doubly linked 'tmq_head/tmq_tail' list. + */ + unsigned int tmq_length; + /** * IPPROTO_TCP or IPPROTO_UDP once bound. */ @@ -314,6 +328,11 @@ static struct GNUNET_CONTAINER_MultiHashMap *tunnel_map; */ static struct GNUNET_CONTAINER_Heap *tunnel_heap; +/** + * Statistics. + */ +static struct GNUNET_STATISTICS_Handle *stats; + /** * The handle to the VPN helper process "gnunet-helper-vpn". */ @@ -480,6 +499,27 @@ send_client_reply (struct GNUNET_SERVER_Client *client, } +/** + * Destroy the mesh tunnel. + * + * @param cls the 'struct TunnelState' with the tunnel to destroy + * @param ts schedule context + */ +static void +destroy_tunnel_task (void *cls, + const struct GNUNET_SCHEDULER_TaskContext *tc) +{ + struct TunnelState *ts = cls; + struct GNUNET_MESH_Tunnel *tunnel; + + ts->destroy_task = GNUNET_SCHEDULER_NO_TASK; + if (NULL == (tunnel = ts->tunnel)) + return; + ts->tunnel = NULL; + GNUNET_MESH_tunnel_destroy (tunnel); +} + + /** * Method called whenever a peer has disconnected from the tunnel. * @@ -492,7 +532,13 @@ tunnel_peer_disconnect_handler (void *cls, GNUNET_PeerIdentity * peer) { struct TunnelState *ts = cls; - + + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Peer %s disconnected from tunnel.\n", + GNUNET_i2s (peer)); + GNUNET_STATISTICS_update (stats, + gettext_noop ("# Peers connected to mesh tunnels"), + -1, GNUNET_NO); if (NULL != ts->th) { GNUNET_MESH_notify_transmit_ready_cancel (ts->th); @@ -502,7 +548,8 @@ tunnel_peer_disconnect_handler (void *cls, return; /* hope for reconnect eventually */ /* as we are most likely going to change the exit node now, we should just destroy the tunnel entirely... */ - GNUNET_MESH_tunnel_destroy (ts->tunnel); + if (GNUNET_SCHEDULER_NO_TASK == ts->destroy_task) + ts->destroy_task = GNUNET_SCHEDULER_add_now (&destroy_tunnel_task, ts); } @@ -523,6 +570,12 @@ tunnel_peer_connect_handler (void *cls, { struct TunnelState *ts = cls; + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Peer %s connected to tunnel.\n", + GNUNET_i2s (peer)); + GNUNET_STATISTICS_update (stats, + gettext_noop ("# Peers connected to mesh tunnels"), + 1, GNUNET_NO); if (NULL == ts->client) return; /* nothing to do */ send_client_reply (ts->client, @@ -552,16 +605,20 @@ send_to_peer_notify_callback (void *cls, size_t size, void *buf) ts->th = NULL; if (NULL == buf) return 0; - tnq = ts->head; + tnq = ts->tmq_head; GNUNET_assert (NULL != tnq); GNUNET_assert (size >= tnq->len); - GNUNET_CONTAINER_DLL_remove (ts->head, - ts->tail, + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Sending %u bytes via mesh tunnel\n", + tnq->len); + GNUNET_CONTAINER_DLL_remove (ts->tmq_head, + ts->tmq_tail, tnq); + ts->tmq_length--; memcpy (buf, tnq->msg, tnq->len); ret = tnq->len; GNUNET_free (tnq); - if (NULL != (tnq = ts->head)) + if (NULL != (tnq = ts->tmq_head)) ts->th = GNUNET_MESH_notify_transmit_ready (ts->tunnel, GNUNET_NO /* cork */, 42 /* priority */, @@ -570,6 +627,9 @@ send_to_peer_notify_callback (void *cls, size_t size, void *buf) tnq->len, &send_to_peer_notify_callback, ts); + GNUNET_STATISTICS_update (stats, + gettext_noop ("# Bytes given to mesh for transmission"), + ret, GNUNET_NO); return ret; } @@ -578,8 +638,6 @@ send_to_peer_notify_callback (void *cls, size_t size, void *buf) * 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 */ @@ -587,9 +645,30 @@ static void send_to_tunnel (struct TunnelMessageQueueEntry *tnq, struct TunnelState *ts) { - GNUNET_CONTAINER_DLL_insert_tail (ts->head, - ts->tail, + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Queueing %u bytes for transmission via mesh tunnel\n", + tnq->len); + GNUNET_CONTAINER_DLL_insert_tail (ts->tmq_head, + 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 */, @@ -617,6 +696,9 @@ create_tunnel_to_destination (struct DestinationEntry *de, { struct TunnelState *ts; + GNUNET_STATISTICS_update (stats, + gettext_noop ("# Mesh tunnels created"), + 1, GNUNET_NO); GNUNET_assert (NULL == de->ts); ts = GNUNET_malloc (sizeof (struct TunnelState)); if (NULL != client) @@ -637,6 +719,10 @@ create_tunnel_to_destination (struct DestinationEntry *de, ts); if (de->is_service) { + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Creating tunnel to peer %s offering service %s\n", + GNUNET_i2s (&de->details.service_destination.target), + GNUNET_h2s (&de->details.service_destination.service_descriptor)); GNUNET_MESH_peer_request_connect_add (ts->tunnel, &de->details.service_destination.target); } @@ -647,10 +733,16 @@ create_tunnel_to_destination (struct DestinationEntry *de, case AF_INET: GNUNET_MESH_peer_request_connect_by_type (ts->tunnel, GNUNET_APPLICATION_TYPE_IPV4_GATEWAY); + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Creating tunnel to exit peer for %s\n", + "IPv4"); break; case AF_INET6: GNUNET_MESH_peer_request_connect_by_type (ts->tunnel, GNUNET_APPLICATION_TYPE_IPV6_GATEWAY); + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Creating tunnel to exit peer for %s\n", + "IPv6"); break; default: GNUNET_assert (0); @@ -661,6 +753,96 @@ create_tunnel_to_destination (struct DestinationEntry *de, } +/** + * 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; + struct GNUNET_MESH_Tunnel *tunnel; + + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Cleaning up tunnel state\n"); + GNUNET_STATISTICS_update (stats, + gettext_noop ("# Active tunnels"), + -1, GNUNET_NO); + if (GNUNET_SCHEDULER_NO_TASK != ts->destroy_task) + { + GNUNET_SCHEDULER_cancel (ts->destroy_task); + ts->destroy_task = GNUNET_SCHEDULER_NO_TASK; + } + 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 != (tunnel = ts->tunnel)) + { + ts->tunnel = NULL; + GNUNET_MESH_tunnel_destroy (tunnel); + } + 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. * @@ -686,7 +868,6 @@ route_packet (struct DestinationEntry *destination, struct TunnelMessageQueueEntry *tnq; size_t alen; size_t mlen; - GNUNET_MESH_ApplicationType app_type; int is_new; const struct udp_packet *udp; const struct tcp_packet *tcp; @@ -741,29 +922,57 @@ route_packet (struct DestinationEntry *destination, (unsigned int) protocol); return; } - if (! destination->is_service) { switch (destination->details.exit_destination.af) { case AF_INET: alen = sizeof (struct in_addr); - app_type = GNUNET_APPLICATION_TYPE_IPV4_GATEWAY; break; case AF_INET6: alen = sizeof (struct in6_addr); - app_type = GNUNET_APPLICATION_TYPE_IPV6_GATEWAY; break; default: alen = 0; GNUNET_assert (0); } + + { + char sbuf[INET6_ADDRSTRLEN]; + char dbuf[INET6_ADDRSTRLEN]; + char xbuf[INET6_ADDRSTRLEN]; + + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Routing %s packet from %s:%u -> %s:%u to destination %s:%u\n", + (protocol == IPPROTO_TCP) ? "TCP" : "UDP", + inet_ntop (af, source_ip, sbuf, sizeof (sbuf)), + spt, + inet_ntop (af, destination_ip, dbuf, sizeof (dbuf)), + dpt, + inet_ntop (destination->details.exit_destination.af, + &destination->details.exit_destination.ip, + xbuf, sizeof (xbuf))); + } } else { /* make compiler happy */ alen = 0; - app_type = 0; + { + char sbuf[INET6_ADDRSTRLEN]; + char dbuf[INET6_ADDRSTRLEN]; + + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Routing %s packet from %s:%u -> %s:%u to service %s at peer %s\n", + (protocol == IPPROTO_TCP) ? "TCP" : "UDP", + inet_ntop (af, source_ip, sbuf, sizeof (sbuf)), + spt, + inet_ntop (af, destination_ip, dbuf, sizeof (dbuf)), + dpt, + GNUNET_h2s (&destination->details.service_destination.service_descriptor), + GNUNET_i2s (&destination->details.service_destination.target)); + } + } /* see if we have an existing tunnel for this destination */ @@ -803,7 +1012,11 @@ route_packet (struct DestinationEntry *destination, &key, ts, GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY)); - /* FIXME: expire OLD tunnels if we have too many! */ + GNUNET_STATISTICS_update (stats, + gettext_noop ("# Active tunnels"), + 1, GNUNET_NO); + while (GNUNET_CONTAINER_multihashmap_size (tunnel_map) > max_tunnel_mappings) + expire_tunnel (ts); } else { @@ -829,6 +1042,8 @@ route_packet (struct DestinationEntry *destination, return; } tnq = GNUNET_malloc (sizeof (struct TunnelMessageQueueEntry) + mlen); + tnq->len = mlen; + tnq->msg = &tnq[1]; usm = (struct GNUNET_EXIT_UdpServiceMessage *) &tnq[1]; usm->header.size = htons ((uint16_t) mlen); usm->header.type = htons (GNUNET_MESSAGE_TYPE_VPN_UDP_TO_SERVICE); @@ -857,6 +1072,8 @@ route_packet (struct DestinationEntry *destination, } tnq = GNUNET_malloc (sizeof (struct TunnelMessageQueueEntry) + mlen); + tnq->len = mlen; + tnq->msg = &tnq[1]; uim = (struct GNUNET_EXIT_UdpInternetMessage *) &tnq[1]; uim->header.size = htons ((uint16_t) mlen); uim->header.type = htons (GNUNET_MESSAGE_TYPE_VPN_UDP_TO_INTERNET); @@ -898,6 +1115,8 @@ route_packet (struct DestinationEntry *destination, return; } tnq = GNUNET_malloc (sizeof (struct TunnelMessageQueueEntry) + mlen); + tnq->len = mlen; + tnq->msg = &tnq[1]; tsm = (struct GNUNET_EXIT_TcpServiceStartMessage *) &tnq[1]; tsm->header.size = htons ((uint16_t) mlen); tsm->header.type = htons (GNUNET_MESSAGE_TYPE_VPN_TCP_TO_SERVICE_START); @@ -923,6 +1142,8 @@ route_packet (struct DestinationEntry *destination, return; } tnq = GNUNET_malloc (sizeof (struct TunnelMessageQueueEntry) + mlen); + tnq->len = mlen; + tnq->msg = &tnq[1]; tim = (struct GNUNET_EXIT_TcpInternetStartMessage *) &tnq[1]; tim->header.size = htons ((uint16_t) mlen); tim->header.type = htons (GNUNET_MESSAGE_TYPE_VPN_TCP_TO_INTERNET_START); @@ -960,6 +1181,8 @@ route_packet (struct DestinationEntry *destination, return; } tnq = GNUNET_malloc (sizeof (struct TunnelMessageQueueEntry) + mlen); + tnq->len = mlen; + tnq->msg = &tnq[1]; tdm = (struct GNUNET_EXIT_TcpDataMessage *) &tnq[1]; tdm->header.size = htons ((uint16_t) mlen); tdm->header.type = htons (GNUNET_MESSAGE_TYPE_VPN_TCP_DATA); @@ -998,6 +1221,9 @@ message_token (void *cls GNUNET_UNUSED, void *client GNUNET_UNUSED, GNUNET_HashCode key; struct DestinationEntry *de; + GNUNET_STATISTICS_update (stats, + gettext_noop ("# Packets received from TUN interface"), + 1, GNUNET_NO); mlen = ntohs (message->size); if ( (ntohs (message->type) != GNUNET_MESSAGE_TYPE_VPN_HELPER) || (mlen < sizeof (struct GNUNET_MessageHeader) + sizeof (struct tun_header)) ) @@ -1129,6 +1355,9 @@ receive_udp_back (void *cls GNUNET_UNUSED, struct GNUNET_MESH_Tunnel *tunnel, const struct GNUNET_EXIT_UdpReplyMessage *reply; size_t mlen; + GNUNET_STATISTICS_update (stats, + gettext_noop ("# UDP packets received from mesh"), + 1, GNUNET_NO); mlen = ntohs (message->size); if (mlen < sizeof (struct GNUNET_EXIT_UdpReplyMessage)) { @@ -1147,6 +1376,18 @@ receive_udp_back (void *cls GNUNET_UNUSED, struct GNUNET_MESH_Tunnel *tunnel, } reply = (const struct GNUNET_EXIT_UdpReplyMessage *) message; mlen -= sizeof (struct GNUNET_EXIT_UdpReplyMessage); + { + char sbuf[INET6_ADDRSTRLEN]; + char dbuf[INET6_ADDRSTRLEN]; + + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Received UDP reply from mesh, sending %u bytes from %s:%u -> %s:%u via TUN\n", + (unsigned int) mlen, + inet_ntop (ts->af, &ts->destination_ip, sbuf, sizeof (sbuf)), + ts->destination_port, + inet_ntop (ts->af, &ts->source_ip, dbuf, sizeof (dbuf)), + ts->source_port); + } switch (ts->af) { case AF_INET: @@ -1297,6 +1538,9 @@ receive_tcp_back (void *cls GNUNET_UNUSED, struct GNUNET_MESH_Tunnel *tunnel, const struct GNUNET_EXIT_TcpDataMessage *data; size_t mlen; + GNUNET_STATISTICS_update (stats, + gettext_noop ("# TCP packets received from mesh"), + 1, GNUNET_NO); mlen = ntohs (message->size); if (mlen < sizeof (struct GNUNET_EXIT_TcpDataMessage)) { @@ -1310,6 +1554,18 @@ receive_tcp_back (void *cls GNUNET_UNUSED, struct GNUNET_MESH_Tunnel *tunnel, } data = (const struct GNUNET_EXIT_TcpDataMessage *) message; mlen -= sizeof (struct GNUNET_EXIT_TcpDataMessage); + { + char sbuf[INET6_ADDRSTRLEN]; + char dbuf[INET6_ADDRSTRLEN]; + + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Received TCP reply from mesh, sending %u bytes from %s:%u -> %s:%u via TUN\n", + (unsigned int) mlen, + inet_ntop (ts->af, &ts->destination_ip, sbuf, sizeof (sbuf)), + ts->destination_port, + inet_ntop (ts->af, &ts->source_ip, dbuf, sizeof (dbuf)), + ts->source_port); + } switch (ts->af) { case AF_INET: @@ -1543,6 +1799,54 @@ allocate_v6_address (struct in6_addr *v6) } +/** + * 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 @@ -1566,7 +1870,6 @@ service_redirect_to_ip (void *cls GNUNET_UNUSED, struct GNUNET_SERVER_Client *cl void *addr; struct DestinationEntry *de; GNUNET_HashCode key; - GNUNET_MESH_ApplicationType app_type; /* validate and parse request */ mlen = ntohs (message->size); @@ -1588,7 +1891,6 @@ service_redirect_to_ip (void *cls GNUNET_UNUSED, struct GNUNET_SERVER_Client *cl GNUNET_SERVER_receive_done (client, GNUNET_SYSERR); return; } - app_type = GNUNET_APPLICATION_TYPE_IPV4_GATEWAY; break; case AF_INET6: if (alen != sizeof (struct in6_addr)) @@ -1597,7 +1899,6 @@ service_redirect_to_ip (void *cls GNUNET_UNUSED, struct GNUNET_SERVER_Client *cl GNUNET_SERVER_receive_done (client, GNUNET_SYSERR); return; } - app_type = GNUNET_APPLICATION_TYPE_IPV6_GATEWAY; break; default: GNUNET_break (0); @@ -1658,6 +1959,17 @@ service_redirect_to_ip (void *cls GNUNET_UNUSED, struct GNUNET_SERVER_Client *cl GNUNET_SERVER_receive_done (client, GNUNET_OK); return; } + + { + char sbuf[INET6_ADDRSTRLEN]; + char dbuf[INET6_ADDRSTRLEN]; + + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Allocated address %s for redirection via exit to %s\n", + inet_ntop (result_af, addr, sbuf, sizeof (sbuf)), + inet_ntop (addr_af, + &msg[1], dbuf, sizeof (dbuf))); + } /* setup destination record */ de = GNUNET_malloc (sizeof (struct DestinationEntry)); @@ -1678,7 +1990,12 @@ service_redirect_to_ip (void *cls GNUNET_UNUSED, struct GNUNET_SERVER_Client *cl 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! */ + GNUNET_STATISTICS_update (stats, + gettext_noop ("# Active destinations"), + 1, GNUNET_NO); + while (GNUNET_CONTAINER_multihashmap_size (destination_map) > max_destination_mappings) + expire_destination (de); + /* setup tunnel to destination */ (void) create_tunnel_to_destination (de, (GNUNET_NO == ntohl (msg->nac)) ? NULL : client, @@ -1763,9 +2080,21 @@ service_redirect_to_service (void *cls GNUNET_UNUSED, struct GNUNET_SERVER_Clien if (result_af == AF_UNSPEC) { /* failure, we're done */ + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + _("Failed to allocate IP address for new destination\n")); GNUNET_SERVER_receive_done (client, GNUNET_OK); return; } + + { + char sbuf[INET6_ADDRSTRLEN]; + + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Allocated address %s for redirection to service %s on peer %s\n", + inet_ntop (result_af, addr, sbuf, sizeof (sbuf)), + GNUNET_h2s (&msg->service_descriptor), + GNUNET_i2s (&msg->target)); + } /* setup destination record */ de = GNUNET_malloc (sizeof (struct DestinationEntry)); @@ -1784,7 +2113,8 @@ service_redirect_to_service (void *cls GNUNET_UNUSED, struct GNUNET_SERVER_Clien 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); @@ -1816,92 +2146,6 @@ inbound_tunnel_cb (void *cls, struct GNUNET_MESH_Tunnel *tunnel, } -/** - * 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; - - while (NULL != (tnq = ts->head)) - { - GNUNET_CONTAINER_DLL_remove (ts->head, - ts->tail, - tnq); - GNUNET_free (tnq); - } - 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) -{ - 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. @@ -1979,6 +2223,8 @@ cleanup (void *cls GNUNET_UNUSED, { unsigned int i; + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "VPN is shutting down\n"); if (NULL != destination_map) { GNUNET_CONTAINER_multihashmap_iterate (destination_map, @@ -2020,6 +2266,11 @@ cleanup (void *cls GNUNET_UNUSED, GNUNET_SERVER_notification_context_destroy (nc); nc = NULL; } + if (stats != NULL) + { + GNUNET_STATISTICS_destroy (stats, GNUNET_YES); + stats = NULL; + } for (i=0;i<5;i++) GNUNET_free_non_null (vpn_argv[i]); } @@ -2138,6 +2389,7 @@ run (void *cls, struct in6_addr v6; cfg = cfg_; + stats = GNUNET_STATISTICS_create ("vpn", cfg); if (GNUNET_OK != GNUNET_CONFIGURATION_get_value_number (cfg, "vpn", "MAX_MAPPING", &max_destination_mappings))