* @author Christian Grothoff
*
* TODO:
- * - build mesh messages
- * - parse mesh replies
- * - build IP messages from mesh replies
- * - create secondary mesh tunnels if needed
- * - fully implement shutdown code
+ * Basics:
+ * - test!
* - better message queue management (bounded state, drop oldest/RED?)
- * - imrpove support for deciding which tunnels to keep and which ones to destroy
+ * - actually destroy "stale" tunnels once we have too many!
+ *
+ * Features:
* - add back ICMP support (especially needed for IPv6)
+ *
+ * Code cleanup:
+ * - consider moving IP-header building / checksumming code into shared library
+ * with dns/exit/vpn (libgnunettun_tcpip?)
*/
#include "platform.h"
#include "gnunet_util_lib.h"
#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"
+
+/**
+ * State we keep for each of our tunnels.
+ */
+struct TunnelState;
+
+
/**
* Information we track for each IP address to determine which tunnel
* to send the traffic over to the destination.
*/
struct DestinationEntry
{
+
/**
- * Information about the tunnel to use, NULL if no tunnel
- * is available right now.
+ * Key under which this entry is in the 'destination_map' (only valid
+ * if 'heap_node != NULL'.
*/
- struct GNUNET_MESH_Tunnel *tunnel;
+ GNUNET_HashCode key;
+
+ /**
+ * Pre-allocated tunnel for this destination, or NULL for none.
+ */
+ struct TunnelState *ts;
/**
* Entry for this entry in the destination_heap.
*/
struct TunnelState
{
+ /**
+ * Information about the tunnel to use, NULL if no tunnel
+ * is available right now.
+ */
+ struct GNUNET_MESH_Tunnel *tunnel;
+
/**
* Active transmission handle, NULL for none.
*/
struct DestinationEntry destination;
/**
- * GNUNET_NO if this is a tunnel to an Internet-exit,
- * GNUNET_YES if this tunnel is to a service.
+ * Destination entry that has a pointer to this tunnel state;
+ * NULL if this tunnel state is in the tunnel map.
*/
- int is_service;
+ struct DestinationEntry *destination_container;
/**
* Addess family used for this tunnel on the local TUN interface.
*/
int af;
+ /**
+ * IPPROTO_TCP or IPPROTO_UDP once bound.
+ */
+ uint8_t protocol;
+
/**
* IP address of the source on our end, initially uninitialized.
*/
static struct GNUNET_CONTAINER_MultiHashMap *tunnel_map;
/**
- * Min-Heap sorted by activity time to expire old mappings.
+ * Min-Heap sorted by activity time to expire old mappings; values are
+ * of type 'struct TunnelState'.
*/
static struct GNUNET_CONTAINER_Heap *tunnel_heap;
+/**
+ * Statistics.
+ */
+static struct GNUNET_STATISTICS_Handle *stats;
+
/**
* The handle to the VPN helper process "gnunet-helper-vpn".
*/
const struct
GNUNET_PeerIdentity * peer)
{
- /* FIXME: should we do anything here?
- - stop transmitting to the tunnel (start queueing?)
- - possibly destroy the tunnel entirely (unless service tunnel?)
- */
+ 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);
+ ts->th = NULL;
+ }
+ if (ts->destination.is_service)
+ 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);
}
{
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,
tnq = ts->head;
GNUNET_assert (NULL != tnq);
GNUNET_assert (size >= tnq->len);
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+ "Sending %u bytes via mesh tunnel\n",
+ tnq->len);
GNUNET_CONTAINER_DLL_remove (ts->head,
ts->tail,
tnq);
ret = tnq->len;
GNUNET_free (tnq);
if (NULL != (tnq = ts->head))
- ts->th = GNUNET_MESH_notify_transmit_ready (ts->destination.tunnel,
+ ts->th = GNUNET_MESH_notify_transmit_ready (ts->tunnel,
GNUNET_NO /* cork */,
42 /* priority */,
GNUNET_TIME_UNIT_FOREVER_REL,
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;
}
/**
- * Add the given message to the given tunnel and
- * trigger the transmission process.
+ * 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
send_to_tunnel (struct TunnelMessageQueueEntry *tnq,
struct TunnelState *ts)
{
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+ "Queueing %u bytes for transmission via mesh tunnel\n",
+ tnq->len);
GNUNET_CONTAINER_DLL_insert_tail (ts->head,
ts->tail,
tnq);
if (NULL == ts->th)
- ts->th = GNUNET_MESH_notify_transmit_ready (ts->destination.tunnel,
+ ts->th = GNUNET_MESH_notify_transmit_ready (ts->tunnel,
GNUNET_NO /* cork */,
42 /* priority */,
GNUNET_TIME_UNIT_FOREVER_REL,
}
+/**
+ * Initialize the given destination entry's mesh tunnel.
+ *
+ * @param de destination entry for which we need to setup a tunnel
+ * @param client client to notify on successful tunnel setup, or NULL for none
+ * @param request_id request ID to send in client notification (unused if client is NULL)
+ * @return tunnel state of the tunnel that was created
+ */
+static struct TunnelState *
+create_tunnel_to_destination (struct DestinationEntry *de,
+ struct GNUNET_SERVER_Client *client,
+ uint64_t request_id)
+{
+ 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)
+ {
+ ts->request_id = request_id;
+ ts->client = client;
+ GNUNET_SERVER_client_keep (client);
+ }
+ ts->destination = *de;
+ ts->destination.heap_node = NULL; /* copy is NOT in destination heap */
+ de->ts = ts;
+ ts->destination_container = de; /* we are referenced from de */
+ ts->af = AF_UNSPEC; /* so far, unknown */
+ ts->tunnel = GNUNET_MESH_tunnel_create (mesh_handle,
+ ts,
+ &tunnel_peer_connect_handler,
+ &tunnel_peer_disconnect_handler,
+ 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);
+ }
+ else
+ {
+ switch (de->details.exit_destination.af)
+ {
+ 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);
+ break;
+ }
+ }
+ return ts;
+}
+
+
/**
* Route a packet via mesh to the given 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;
-
+ uint16_t spt;
+ uint16_t dpt;
+
switch (protocol)
{
case IPPROTO_UDP:
return;
}
udp = payload;
+ spt = ntohs (udp->spt);
+ dpt = ntohs (udp->dpt);
get_tunnel_key_from_ips (af,
IPPROTO_UDP,
source_ip,
- ntohs (udp->spt),
+ spt,
destination_ip,
- ntohs (udp->dpt),
+ dpt,
&key);
}
break;
return;
}
tcp = payload;
+ spt = ntohs (tcp->spt);
+ dpt = ntohs (tcp->dpt);
get_tunnel_key_from_ips (af,
IPPROTO_TCP,
source_ip,
- ntohs (tcp->spt),
+ spt,
destination_ip,
- ntohs (tcp->dpt),
+ dpt,
&key);
}
break;
(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));
+ }
+
}
- // FIXME: something is horrifically wrong here about
- // how we lookup 'ts', match it and how we decide about
- // creating new tunnels!
- /* find tunnel */
- is_new = GNUNET_NO;
+ /* see if we have an existing tunnel for this destination */
ts = GNUNET_CONTAINER_multihashmap_get (tunnel_map,
&key);
if (NULL == ts)
{
- /* create new tunnel */
+ /* need to either use the existing tunnel from the destination (if still
+ available) or create a fresh one */
is_new = GNUNET_YES;
- ts = GNUNET_malloc (sizeof (struct TunnelState));
- ts->destination.tunnel = GNUNET_MESH_tunnel_create (mesh_handle,
- ts,
- &tunnel_peer_connect_handler,
- &tunnel_peer_disconnect_handler,
- ts);
- if (destination->is_service)
- GNUNET_MESH_peer_request_connect_add (ts->destination.tunnel,
- &destination->details.service_destination.target);
+ if (NULL == destination->ts)
+ ts = create_tunnel_to_destination (destination, NULL, 0);
+ else
+ ts = destination->ts;
+ destination->ts = NULL;
+ ts->destination_container = NULL; /* no longer 'contained' */
+ /* now bind existing "unbound" tunnel to our IP/port tuple */
+ ts->protocol = protocol;
+ ts->af = af;
+ if (af == AF_INET)
+ {
+ ts->source_ip.v4 = * (const struct in_addr *) source_ip;
+ ts->destination_ip.v4 = * (const struct in_addr *) destination_ip;
+ }
else
- GNUNET_MESH_peer_request_connect_by_type (ts->destination.tunnel,
- app_type);
+ {
+ ts->source_ip.v6 = * (const struct in6_addr *) source_ip;
+ ts->destination_ip.v6 = * (const struct in6_addr *) destination_ip;
+ }
+ ts->source_port = spt;
+ ts->destination_port = dpt;
+ ts->heap_node = GNUNET_CONTAINER_heap_insert (tunnel_heap,
+ ts,
+ GNUNET_TIME_absolute_get ().abs_value);
+ GNUNET_assert (GNUNET_YES ==
+ GNUNET_CONTAINER_multihashmap_put (tunnel_map,
+ &key,
+ ts,
+ GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY));
+ GNUNET_STATISTICS_update (stats,
+ gettext_noop ("# Active tunnels"),
+ 1, GNUNET_NO);
+ /* FIXME: expire OLD tunnels if we have too many! */
+ }
+ else
+ {
+ is_new = GNUNET_NO;
+ GNUNET_CONTAINER_heap_update_cost (tunnel_heap,
+ ts->heap_node,
+ GNUNET_TIME_absolute_get ().abs_value);
}
/* send via tunnel */
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);
}
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);
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);
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);
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);
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)) )
&pkt6->destination_address,
&key);
de = GNUNET_CONTAINER_multihashmap_get (destination_map, &key);
- /* FIXME: do we need to guard against hash collision? */
+ /* FIXME: do we need to guard against hash collision?
+ (if so, we need to also store the local destination IP in the
+ destination entry and then compare here; however, the risk
+ of collision seems minimal AND the impact is unlikely to be
+ super-problematic as well... */
if (NULL == de)
{
char buf[INET6_ADDRSTRLEN];
&pkt4->destination_address,
&key);
de = GNUNET_CONTAINER_multihashmap_get (destination_map, &key);
- /* FIXME: do we need to guard against hash collision? */
+ /* FIXME: do we need to guard against hash collision?
+ (if so, we need to also store the local destination IP in the
+ destination entry and then compare here; however, the risk
+ of collision seems minimal AND the impact is unlikely to be
+ super-problematic as well... */
if (NULL == de)
{
char buf[INET_ADDRSTRLEN];
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))
{
GNUNET_break_op (0);
return GNUNET_SYSERR;
}
+ if (AF_UNSPEC == ts->af)
+ {
+ GNUNET_break_op (0);
+ return GNUNET_SYSERR;
+ }
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:
default:
GNUNET_assert (0);
}
-#if 0
- // FIXME: refresh entry to avoid expiration...
- struct map_entry *me = GNUNET_CONTAINER_multihashmap_get (hashmap, key);
-
- GNUNET_CONTAINER_heap_update_cost (heap, me->heap_node,
+ GNUNET_CONTAINER_heap_update_cost (tunnel_heap,
+ ts->heap_node,
GNUNET_TIME_absolute_get ().abs_value);
-
-#endif
return GNUNET_OK;
}
const struct GNUNET_MessageHeader *message,
const struct GNUNET_ATS_Information *atsi GNUNET_UNUSED)
{
- // FIXME: parse message, build IP packet, give to TUN!
-#if 0
- GNUNET_HashCode *desc = (GNUNET_HashCode *) (message + 1);
- struct remote_addr *s = (struct remote_addr *) desc;
- struct tcp_pkt *pkt = (struct tcp_pkt *) (desc + 1);
- const struct GNUNET_PeerIdentity *other = sender;
struct TunnelState *ts = *tunnel_ctx;
+ const struct GNUNET_EXIT_TcpDataMessage *data;
+ size_t mlen;
- size_t pktlen =
- ntohs (message->size) - sizeof (struct GNUNET_MessageHeader) -
- sizeof (GNUNET_HashCode);
-
- GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
- "Received TCP-Packet back, addrlen = %d\n", s->addrlen);
-
- if (ntohs (message->type) == GNUNET_MESSAGE_TYPE_VPN_SERVICE_TCP_BACK ||
- ts->addrlen == 16)
+ 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))
{
- size_t size = pktlen + sizeof (struct ip6_tcp) - 1;
-
- struct ip6_tcp *pkt6 = alloca (size);
-
- memset (pkt6, 0, size);
-
- GNUNET_assert (pkt6 != NULL);
-
- if (ntohs (message->type) == GNUNET_MESSAGE_TYPE_VPN_SERVICE_TCP_BACK)
- new_ip6addr (&pkt6->ip6_hdr.sadr, &other->hashPubKey, desc);
- else
- new_ip6addr_remote (&pkt6->ip6_hdr.sadr, s->addr, s->addrlen);
-
- pkt6->shdr.type = htons (GNUNET_MESSAGE_TYPE_VPN_HELPER);
- pkt6->shdr.size = htons (size);
-
- pkt6->tun.flags = 0;
- pkt6->tun.type = htons (0x86dd);
-
- pkt6->ip6_hdr.version = 6;
- pkt6->ip6_hdr.tclass_h = 0;
- pkt6->ip6_hdr.tclass_l = 0;
- pkt6->ip6_hdr.flowlbl = 0;
- pkt6->ip6_hdr.paylgth = htons (pktlen);
- pkt6->ip6_hdr.nxthdr = IPPROTO_TCP;
- pkt6->ip6_hdr.hoplmt = 0xff;
-
- {
- char *ipv6addr;
-
- GNUNET_assert (GNUNET_OK ==
- GNUNET_CONFIGURATION_get_value_string (cfg, "vpn",
- "IPV6ADDR",
- &ipv6addr));
- inet_pton (AF_INET6, ipv6addr, &pkt6->ip6_hdr.dadr);
- GNUNET_free (ipv6addr);
- }
- memcpy (&pkt6->tcp_hdr, pkt, pktlen);
-
- GNUNET_HashCode *key = address6_mapping_exists (&pkt6->ip6_hdr.sadr);
-
- GNUNET_assert (key != NULL);
-
- struct map_entry *me = GNUNET_CONTAINER_multihashmap_get (hashmap, key);
-
- GNUNET_CONTAINER_heap_update_cost (heap, me->heap_node,
- GNUNET_TIME_absolute_get ().abs_value);
-
- GNUNET_free (key);
-
- GNUNET_assert (me != NULL);
-
- pkt6->tcp_hdr.crc = 0;
- uint32_t sum = 0;
- uint32_t tmp;
-
- sum =
- GNUNET_CRYPTO_crc16_step (sum, (uint16_t *) & pkt6->ip6_hdr.sadr, 16);
- sum =
- GNUNET_CRYPTO_crc16_step (sum, (uint16_t *) & pkt6->ip6_hdr.dadr, 16);
- tmp = htonl (pktlen);
- sum = GNUNET_CRYPTO_crc16_step (sum, (uint16_t *) & tmp, 4);
- tmp = htonl (((pkt6->ip6_hdr.nxthdr & 0x000000ff)));
- sum = GNUNET_CRYPTO_crc16_step (sum, (uint16_t *) & tmp, 4);
-
- sum =
- GNUNET_CRYPTO_crc16_step (sum, (uint16_t *) & pkt6->tcp_hdr,
- ntohs (pkt6->ip6_hdr.paylgth));
- pkt6->tcp_hdr.crc = GNUNET_CRYPTO_crc16_finish (sum);
-
- (void) GNUNET_HELPER_send (helper_handle,
- &pkt6->shdr,
- GNUNET_YES,
- NULL, NULL);
+ GNUNET_break_op (0);
+ return GNUNET_SYSERR;
}
- else
+ if (NULL == ts->heap_node)
{
- size_t size = pktlen + sizeof (struct ip_tcp) - 1;
-
- struct ip_tcp *pkt4 = alloca (size);
-
- GNUNET_assert (pkt4 != NULL);
- memset (pkt4, 0, size);
-
- GNUNET_assert (ntohs (message->type) ==
- GNUNET_MESSAGE_TYPE_VPN_REMOTE_TCP_BACK);
- uint32_t sadr;
-
- new_ip4addr_remote ((unsigned char *) &sadr, s->addr, s->addrlen);
- pkt4->ip_hdr.sadr.s_addr = sadr;
-
- pkt4->shdr.type = htons (GNUNET_MESSAGE_TYPE_VPN_HELPER);
- pkt4->shdr.size = htons (size);
-
- pkt4->tun.flags = 0;
- pkt4->tun.type = htons (0x0800);
-
- pkt4->ip_hdr.version = 4;
- pkt4->ip_hdr.hdr_lngth = 5;
- pkt4->ip_hdr.diff_serv = 0;
- pkt4->ip_hdr.tot_lngth = htons (20 + pktlen);
- pkt4->ip_hdr.ident = 0;
- pkt4->ip_hdr.flags = 0;
- pkt4->ip_hdr.frag_off = 0;
- pkt4->ip_hdr.ttl = 255;
- pkt4->ip_hdr.proto = IPPROTO_TCP;
- pkt4->ip_hdr.chks = 0; /* Will be calculated later */
-
+ GNUNET_break_op (0);
+ return GNUNET_SYSERR;
+ }
+ 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:
{
- char *ipv4addr;
- uint32_t dadr;
-
- GNUNET_assert (GNUNET_OK ==
- GNUNET_CONFIGURATION_get_value_string (cfg, "vpn",
- "IPV4ADDR",
- &ipv4addr));
- inet_pton (AF_INET, ipv4addr, &dadr);
- GNUNET_free (ipv4addr);
- pkt4->ip_hdr.dadr.s_addr = dadr;
+ size_t size = sizeof (struct ip4_header)
+ + sizeof (struct tcp_packet)
+ + sizeof (struct GNUNET_MessageHeader) +
+ sizeof (struct tun_header) +
+ mlen;
+ {
+ char buf[size];
+ struct GNUNET_MessageHeader *msg = (struct GNUNET_MessageHeader *) buf;
+ struct tun_header *tun = (struct tun_header*) &msg[1];
+ struct ip4_header *ipv4 = (struct ip4_header *) &tun[1];
+ struct tcp_packet *tcp = (struct tcp_packet *) &ipv4[1];
+ msg->type = htons (GNUNET_MESSAGE_TYPE_VPN_HELPER);
+ msg->size = htons (size);
+ tun->flags = htons (0);
+ tun->proto = htons (ETH_P_IPV4);
+ ipv4->version = 4;
+ ipv4->header_length = sizeof (struct ip4_header) / 4;
+ ipv4->diff_serv = 0;
+ ipv4->total_length = htons (sizeof (struct ip4_header) +
+ sizeof (struct tcp_packet) +
+ mlen);
+ ipv4->identification = (uint16_t) GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_WEAK,
+ UINT16_MAX + 1);
+ ipv4->flags = 0;
+ ipv4->fragmentation_offset = 0;
+ ipv4->ttl = 255;
+ ipv4->protocol = IPPROTO_TCP;
+ ipv4->checksum = 0;
+ ipv4->source_address = ts->destination_ip.v4;
+ ipv4->destination_address = ts->source_ip.v4;
+ ipv4->checksum =
+ GNUNET_CRYPTO_crc16_n (ipv4, sizeof (struct ip4_header));
+ *tcp = data->tcp_header;
+ tcp->spt = htons (ts->destination_port);
+ tcp->dpt = htons (ts->source_port);
+ tcp->crc = 0;
+ memcpy (&tcp[1],
+ &data[1],
+ mlen);
+ {
+ uint32_t sum = 0;
+ uint32_t tmp;
+
+ sum = GNUNET_CRYPTO_crc16_step (sum,
+ &ipv4->source_address,
+ 2 * sizeof (struct in_addr));
+ tmp = htonl ((IPPROTO_TCP << 16) | (mlen + sizeof (struct tcp_packet)));
+ sum = GNUNET_CRYPTO_crc16_step (sum, &tmp, sizeof (uint32_t));
+ sum = GNUNET_CRYPTO_crc16_step (sum, tcp, mlen + sizeof (struct tcp_packet));
+ tcp->crc = GNUNET_CRYPTO_crc16_finish (sum);
+ }
+ (void) GNUNET_HELPER_send (helper_handle,
+ msg,
+ GNUNET_YES,
+ NULL, NULL);
+ }
}
+ break;
+ case AF_INET6:
+ {
+ size_t size = sizeof (struct ip6_header)
+ + sizeof (struct tcp_packet)
+ + sizeof (struct GNUNET_MessageHeader) +
+ sizeof (struct tun_header) +
+ mlen;
+ {
+ char buf[size];
+ struct GNUNET_MessageHeader *msg = (struct GNUNET_MessageHeader *) buf;
+ struct tun_header *tun = (struct tun_header*) &msg[1];
+ struct ip6_header *ipv6 = (struct ip6_header *) &tun[1];
+ struct tcp_packet *tcp = (struct tcp_packet *) &ipv6[1];
+ msg->type = htons (GNUNET_MESSAGE_TYPE_VPN_HELPER);
+ msg->size = htons (size);
+ tun->flags = htons (0);
+ tun->proto = htons (ETH_P_IPV6);
+ ipv6->traffic_class_h = 0;
+ ipv6->version = 6;
+ ipv6->traffic_class_l = 0;
+ ipv6->flow_label = 0;
+ ipv6->payload_length = htons (sizeof (struct tcp_packet) + sizeof (struct ip6_header) + mlen);
+ ipv6->next_header = IPPROTO_TCP;
+ ipv6->hop_limit = 255;
+ ipv6->source_address = ts->destination_ip.v6;
+ ipv6->destination_address = ts->source_ip.v6;
+ tcp->spt = htons (ts->destination_port);
+ tcp->dpt = htons (ts->source_port);
+ tcp->crc = 0;
+ {
+ uint32_t sum = 0;
+ uint32_t tmp;
- memcpy (&pkt4->tcp_hdr, pkt, pktlen);
-
- GNUNET_HashCode *key = address4_mapping_exists (pkt4->ip_hdr.sadr.s_addr);
-
- GNUNET_assert (key != NULL);
-
- struct map_entry *me = GNUNET_CONTAINER_multihashmap_get (hashmap, key);
-
- GNUNET_CONTAINER_heap_update_cost (heap, me->heap_node,
- GNUNET_TIME_absolute_get ().abs_value);
-
- GNUNET_free (key);
-
- GNUNET_assert (me != NULL);
- pkt4->tcp_hdr.crc = 0;
- uint32_t sum = 0;
- uint32_t tmp;
-
- sum = GNUNET_CRYPTO_crc16_step (sum, (uint16_t *) &pkt4->ip_hdr.sadr, 4);
- sum = GNUNET_CRYPTO_crc16_step (sum, (uint16_t *) &pkt4->ip_hdr.dadr, 4);
-
- tmp = (0x06 << 16) | (0xffff & pktlen); // 0x06 for TCP?
-
- tmp = htonl (tmp);
-
- sum = GNUNET_CRYPTO_crc16_step (sum, (uint16_t *) & tmp, 4);
-
- sum = GNUNET_CRYPTO_crc16_step (sum, (uint16_t *) & pkt4->tcp_hdr, pktlen);
- pkt4->tcp_hdr.crc = GNUNET_CRYPTO_crc16_finish (sum);
-
- pkt4->ip_hdr.chks =
- GNUNET_CRYPTO_crc16_n ((uint16_t *) & pkt4->ip_hdr, 5 * 4);
-
- (void) GNUNET_HELPER_send (helper_handle,
- &pkt4->shdr,
- GNUNET_YES,
- NULL, NULL);
-
+ sum = GNUNET_CRYPTO_crc16_step (sum, &ipv6->source_address, 2 * sizeof (struct in6_addr));
+ tmp = htonl (sizeof (struct tcp_packet) + mlen);
+ sum = GNUNET_CRYPTO_crc16_step (sum, &tmp, sizeof (uint32_t));
+ tmp = htonl (IPPROTO_TCP);
+ sum = GNUNET_CRYPTO_crc16_step (sum, &tmp, sizeof (uint32_t));
+ sum = GNUNET_CRYPTO_crc16_step (sum, tcp,
+ sizeof (struct tcp_packet) + mlen);
+ tcp->crc = GNUNET_CRYPTO_crc16_finish (sum);
+ }
+ (void) GNUNET_HELPER_send (helper_handle,
+ msg,
+ GNUNET_YES,
+ NULL, NULL);
+ }
+ }
+ break;
}
-#endif
+ GNUNET_CONTAINER_heap_update_cost (tunnel_heap,
+ ts->heap_node,
+ GNUNET_TIME_absolute_get ().abs_value);
return GNUNET_OK;
}
void *addr;
struct DestinationEntry *de;
GNUNET_HashCode key;
- struct TunnelState *ts;
- GNUNET_MESH_ApplicationType app_type;
/* validate and parse request */
mlen = ntohs (message->size);
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))
GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
return;
}
- app_type = GNUNET_APPLICATION_TYPE_IPV6_GATEWAY;
break;
default:
GNUNET_break (0);
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));
get_destination_key_from_ip (result_af,
addr,
&key);
+ de->key = key;
GNUNET_assert (GNUNET_OK ==
GNUNET_CONTAINER_multihashmap_put (destination_map,
&key,
de->heap_node = GNUNET_CONTAINER_heap_insert (destination_heap,
de,
GNUNET_TIME_absolute_ntoh (msg->expiration_time).abs_value);
+ GNUNET_STATISTICS_update (stats,
+ gettext_noop ("# Active destinations"),
+ 1, GNUNET_NO);
+
+ /* FIXME: expire OLD destinations if we have too many! */
/* setup tunnel to destination */
- ts = GNUNET_malloc (sizeof (struct TunnelState));
- if (GNUNET_NO != ntohl (msg->nac))
- {
- ts->request_id = msg->request_id;
- ts->client = client;
- GNUNET_SERVER_client_keep (client);
- }
- ts->destination = *de;
- ts->destination.heap_node = NULL;
- ts->is_service = GNUNET_NO;
- ts->af = result_af;
- if (result_af == AF_INET)
- ts->destination_ip.v4 = v4;
- else
- ts->destination_ip.v6 = v6;
- de->tunnel = GNUNET_MESH_tunnel_create (mesh_handle,
- ts,
- &tunnel_peer_connect_handler,
- &tunnel_peer_disconnect_handler,
- ts);
- GNUNET_MESH_peer_request_connect_by_type (de->tunnel,
- app_type);
+ (void) create_tunnel_to_destination (de,
+ (GNUNET_NO == ntohl (msg->nac)) ? NULL : client,
+ msg->request_id);
+ GNUNET_assert (NULL != de->ts);
/* we're done */
GNUNET_SERVER_receive_done (client, GNUNET_OK);
}
void *addr;
struct DestinationEntry *de;
GNUNET_HashCode key;
- struct TunnelState *ts;
/* parse request */
msg = (const struct RedirectToServiceRequestMessage *) message;
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));
get_destination_key_from_ip (result_af,
addr,
&key);
+ de->key = key;
GNUNET_assert (GNUNET_OK ==
GNUNET_CONTAINER_multihashmap_put (destination_map,
&key,
de->heap_node = GNUNET_CONTAINER_heap_insert (destination_heap,
de,
GNUNET_TIME_absolute_ntoh (msg->expiration_time).abs_value);
-
- /* setup tunnel to destination */
- ts = GNUNET_malloc (sizeof (struct TunnelState));
- if (GNUNET_NO != ntohl (msg->nac))
- {
- ts->request_id = msg->request_id;
- ts->client = client;
- GNUNET_SERVER_client_keep (client);
- }
- ts->destination = *de;
- ts->destination.heap_node = NULL;
- ts->is_service = GNUNET_YES;
- ts->af = result_af;
- if (result_af == AF_INET)
- ts->destination_ip.v4 = v4;
- else
- ts->destination_ip.v6 = v6;
- de->tunnel = GNUNET_MESH_tunnel_create (mesh_handle,
- ts,
- &tunnel_peer_connect_handler,
- &tunnel_peer_disconnect_handler,
- ts);
- GNUNET_MESH_peer_request_connect_add (de->tunnel,
- &msg->target);
+ /* FIXME: expire OLD destinations if we have too many! */
+ (void) create_tunnel_to_destination (de,
+ (GNUNET_NO == ntohl (msg->nac)) ? NULL : client,
+ msg->request_id);
/* we're done */
GNUNET_SERVER_receive_done (client, GNUNET_OK);
}
const struct GNUNET_PeerIdentity *initiator,
const struct GNUNET_ATS_Information *atsi)
{
- /* Why should anyone open an inbound tunnel to vpn? */
+ /* How can and why should anyone open an inbound tunnel to vpn? */
GNUNET_break (0);
return NULL;
}
+/**
+ * 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->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)
+{
+ 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.
* @param cls closure (set from GNUNET_MESH_connect)
* @param tunnel connection to the other end (henceforth invalid)
* @param tunnel_ctx place where local state associated
- * with the tunnel is stored
+ * with the tunnel is stored (our 'struct TunnelState')
*/
static void
tunnel_cleaner (void *cls, const struct GNUNET_MESH_Tunnel *tunnel, void *tunnel_ctx)
{
- /* FIXME: is this function called for outbound tunnels that go down?
- Should we clean up something here? */
- GNUNET_break (0);
+ struct TunnelState *ts = tunnel_ctx;
+
+ if (NULL == ts)
+ {
+ GNUNET_break (0);
+ return;
+ }
+ GNUNET_assert (ts->tunnel == tunnel);
+ ts->tunnel = NULL;
+ free_tunnel_state (ts);
+}
+
+
+/**
+ * Free memory occupied by an entry in the destination map.
+ *
+ * @param cls unused
+ * @param key unused
+ * @param value a 'struct DestinationEntry *'
+ * @return GNUNET_OK (continue to iterate)
+ */
+static int
+cleanup_destination (void *cls,
+ const GNUNET_HashCode *key,
+ void *value)
+{
+ struct DestinationEntry *de = value;
+
+ free_destination_entry (de);
+ return GNUNET_OK;
+}
+
+
+/**
+ * Free memory occupied by an entry in the tunnel map.
+ *
+ * @param cls unused
+ * @param key unused
+ * @param value a 'struct TunnelState *'
+ * @return GNUNET_OK (continue to iterate)
+ */
+static int
+cleanup_tunnel (void *cls,
+ const GNUNET_HashCode *key,
+ void *value)
+{
+ struct TunnelState *ts = value;
+
+ free_tunnel_state (ts);
+ return GNUNET_OK;
}
/**
* Function scheduled as very last function, cleans up after us
+ *
+ * @param cls unused
+ * @param tc unused
*/
static void
cleanup (void *cls GNUNET_UNUSED,
- const struct GNUNET_SCHEDULER_TaskContext *tskctx)
+ const struct GNUNET_SCHEDULER_TaskContext *tc)
{
unsigned int i;
- // FIXME: clean up heaps and maps!
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+ "VPN is shutting down\n");
+ if (NULL != destination_map)
+ {
+ GNUNET_CONTAINER_multihashmap_iterate (destination_map,
+ &cleanup_destination,
+ NULL);
+ GNUNET_CONTAINER_multihashmap_destroy (destination_map);
+ destination_map = NULL;
+ }
+ if (NULL != destination_heap)
+ {
+ GNUNET_CONTAINER_heap_destroy (destination_heap);
+ destination_heap = NULL;
+ }
+ if (NULL != tunnel_map)
+ {
+ GNUNET_CONTAINER_multihashmap_iterate (tunnel_map,
+ &cleanup_tunnel,
+ NULL);
+ GNUNET_CONTAINER_multihashmap_destroy (tunnel_map);
+ tunnel_map = NULL;
+ }
+ if (NULL != tunnel_heap)
+ {
+ GNUNET_CONTAINER_heap_destroy (tunnel_heap);
+ tunnel_heap = NULL;
+ }
if (NULL != mesh_handle)
{
GNUNET_MESH_disconnect (mesh_handle);
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]);
}
+/**
+ * A client disconnected, clean up all references to it.
+ *
+ * @param cls the client that disconnected
+ * @param key unused
+ * @param value a 'struct TunnelState *'
+ * @return GNUNET_OK (continue to iterate)
+ */
+static int
+cleanup_tunnel_client (void *cls,
+ const GNUNET_HashCode *key,
+ void *value)
+{
+ struct GNUNET_SERVER_Client *client = cls;
+ struct TunnelState *ts = value;
+
+ if (client == ts->client)
+ {
+ GNUNET_SERVER_client_drop (ts->client);
+ ts->client = NULL;
+ }
+ return GNUNET_OK;
+}
+
+
+/**
+ * A client disconnected, clean up all references to it.
+ *
+ * @param cls the client that disconnected
+ * @param key unused
+ * @param value a 'struct DestinationEntry *'
+ * @return GNUNET_OK (continue to iterate)
+ */
+static int
+cleanup_destination_client (void *cls,
+ const GNUNET_HashCode *key,
+ void *value)
+{
+ struct GNUNET_SERVER_Client *client = cls;
+ struct DestinationEntry *de = value;
+ struct TunnelState *ts;
+
+ if (NULL == (ts = de->ts))
+ return GNUNET_OK;
+ if (client == ts->client)
+ {
+ GNUNET_SERVER_client_drop (ts->client);
+ ts->client = NULL;
+ }
+ return GNUNET_OK;
+}
+
+
/**
* A client has disconnected from us. If we are currently building
* a tunnel for it, cancel the operation.
static void
client_disconnect (void *cls, struct GNUNET_SERVER_Client *client)
{
- // FIXME: find all TunnelState's and check if they point
- // to the client and if so, clean the reference up!
+ if (NULL != tunnel_map)
+ GNUNET_CONTAINER_multihashmap_iterate (tunnel_map,
+ &cleanup_tunnel_client,
+ client);
+ if (NULL != destination_map)
+ GNUNET_CONTAINER_multihashmap_iterate (destination_map,
+ &cleanup_destination_client,
+ client);
}
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))