gnunet-svn
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[GNUnet-SVN] r19019 - in gnunet: . src src/exit src/vpn


From: gnunet
Subject: [GNUnet-SVN] r19019 - in gnunet: . src src/exit src/vpn
Date: Thu, 5 Jan 2012 21:31:05 +0100

Author: grothoff
Date: 2012-01-05 21:31:05 +0100 (Thu, 05 Jan 2012)
New Revision: 19019

Added:
   gnunet/src/exit/
   gnunet/src/exit/exit.conf
   gnunet/src/exit/gnunet-daemon-exit.c
   gnunet/src/exit/gnunet-helper-exit.c
Modified:
   gnunet/configure.ac
   gnunet/src/Makefile.am
   gnunet/src/vpn/vpn.conf
Log:
-towards new exit daemon

Modified: gnunet/configure.ac
===================================================================
--- gnunet/configure.ac 2012-01-05 20:20:22 UTC (rev 19018)
+++ gnunet/configure.ac 2012-01-05 20:31:05 UTC (rev 19019)
@@ -822,6 +822,7 @@
 src/dht/Makefile
 src/dns/Makefile
 src/dv/Makefile
+src/exit/Makefile
 src/fragmentation/Makefile
 src/fs/Makefile
 src/hello/Makefile

Modified: gnunet/src/Makefile.am
===================================================================
--- gnunet/src/Makefile.am      2012-01-05 20:20:22 UTC (rev 19018)
+++ gnunet/src/Makefile.am      2012-01-05 20:31:05 UTC (rev 19019)
@@ -31,6 +31,7 @@
   fs \
   mesh \
   dns \
+  exit \
   vpn \
   integration-tests \
   $(EXP_DIR) 

Added: gnunet/src/exit/exit.conf
===================================================================
--- gnunet/src/exit/exit.conf                           (rev 0)
+++ gnunet/src/exit/exit.conf   2012-01-05 20:31:05 UTC (rev 19019)
@@ -0,0 +1,12 @@
+[exit]
+CONFIG = $DEFAULTCONFIG
+BINARY = gnunet-daemon-exit
+IPV6ADDR = 1234:1::1
+IPV6PREFIX = 32
+IPV4ADDR = 10.10.1.1
+IPV4MASK = 255.255.0.0
+IFNAME = exit-gnunet
+ENABLE_UDP = NO
+ENABLE_TCP = NO
+
+# MAX_CONNECTIONS = 256

Copied: gnunet/src/exit/gnunet-daemon-exit.c (from rev 19007, 
gnunet/src/vpn/gnunet-daemon-exit.c)
===================================================================
--- gnunet/src/exit/gnunet-daemon-exit.c                                (rev 0)
+++ gnunet/src/exit/gnunet-daemon-exit.c        2012-01-05 20:31:05 UTC (rev 
19019)
@@ -0,0 +1,1912 @@
+/*
+     This file is part of GNUnet.
+     (C) 2010, 2012 Christian Grothoff
+
+     GNUnet is free software; you can redistribute it and/or modify
+     it under the terms of the GNU General Public License as published
+     by the Free Software Foundation; either version 3, or (at your
+     option) any later version.
+
+     GNUnet is distributed in the hope that it will be useful, but
+     WITHOUT ANY WARRANTY; without even the implied warranty of
+     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+     General Public License for more details.
+
+     You should have received a copy of the GNU General Public License
+     along with GNUnet; see the file COPYING.  If not, write to the
+     Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+     Boston, MA 02111-1307, USA.
+*/
+
+/**
+ * @file exit/gnunet-daemon-exit.c
+ * @brief tool to allow IP traffic exit from the GNUnet mesh to the Internet
+ * @author Philipp Toelke
+ * @author Christian Grothoff
+ */
+#include <platform.h>
+#include <gnunet_common.h>
+#include <gnunet_program_lib.h>
+#include <gnunet_protocols.h>
+#include <gnunet_applications.h>
+#include <gnunet_mesh_service.h>
+#include <gnunet_constants.h>
+#include <string.h>
+
+
+/* see http://www.iana.org/assignments/ethernet-numbers */
+#ifndef ETH_P_IPV4
+#define ETH_P_IPV4 0x0800
+#endif
+
+#ifndef ETH_P_IPV6
+#define ETH_P_IPV6 0x86DD
+#endif
+
+
+GNUNET_NETWORK_STRUCT_BEGIN
+/**
+ * Header from Linux TUN interface.
+ */ 
+struct tun_header
+{
+  /**
+   * Some flags (unused).
+   */ 
+  uint16_t flags;
+
+  /**
+   * Here we get an ETH_P_-number.
+   */
+  uint16_t proto;
+};
+
+/**
+ * Standard IPv4 header.
+ */
+struct ip4_header
+{
+  unsigned header_length:4 GNUNET_PACKED;
+  unsigned version:4 GNUNET_PACKED;
+  uint8_t diff_serv;
+  uint16_t total_length GNUNET_PACKED;
+  uint16_t identification GNUNET_PACKED;
+  unsigned flags:3 GNUNET_PACKED;
+  unsigned fragmentation_offset:13 GNUNET_PACKED;
+  uint8_t ttl;
+  uint8_t protocol;
+  uint16_t checksum GNUNET_PACKED;
+  struct in_addr source_address GNUNET_PACKED;
+  struct in_addr destination_address GNUNET_PACKED;
+};
+
+/**
+ * Standard IPv6 header.
+ */
+struct ip6_header
+{
+  unsigned traffic_class_h:4 GNUNET_PACKED;
+  unsigned version:4 GNUNET_PACKED;
+  unsigned traffic_class_l:4 GNUNET_PACKED;
+  unsigned flow_label:20 GNUNET_PACKED;
+  uint16_t payload_length GNUNET_PACKED;
+  uint8_t next_header;
+  uint8_t hop_limit;
+  struct in6_addr source_address GNUNET_PACKED;
+  struct in6_addr destination_address GNUNET_PACKED;
+};
+
+#define TCP_FLAG_SYN 2
+
+struct tcp_packet
+{
+  unsigned spt:16 GNUNET_PACKED;
+  unsigned dpt:16 GNUNET_PACKED;
+  unsigned seq:32 GNUNET_PACKED;
+  unsigned ack:32 GNUNET_PACKED;
+  unsigned off:4 GNUNET_PACKED;
+  unsigned rsv:4 GNUNET_PACKED;
+  unsigned flg:8 GNUNET_PACKED;
+  unsigned wsz:16 GNUNET_PACKED;
+  unsigned crc:16 GNUNET_PACKED;
+  unsigned urg:16 GNUNET_PACKED;
+};
+
+/**
+ * UDP packet header.
+ */
+struct udp_packet
+{
+  uint16_t spt GNUNET_PACKED;
+  uint16_t dpt GNUNET_PACKED;
+  uint16_t len GNUNET_PACKED;
+  uint16_t crc GNUNET_PACKED;
+};
+
+/**
+ * DNS header.
+ */
+struct dns_header
+{
+  uint16_t id GNUNET_PACKED;
+  uint16_t flags GNUNET_PACKED;
+  uint16_t qdcount GNUNET_PACKED;
+  uint16_t ancount GNUNET_PACKED;
+  uint16_t nscount GNUNET_PACKED;
+  uint16_t arcount GNUNET_PACKED;
+};
+GNUNET_NETWORK_STRUCT_END
+
+/**
+ * Information about a remote address.
+ */
+struct remote_addr
+{
+  /**
+   * AF_INET or AF_INET6.
+   */
+  int af;
+
+  /**
+   * Remote address information.
+   */
+  union
+  {
+    /**
+     * Address, if af is AF_INET.
+     */
+    struct in_addr ipv4;
+
+    /**
+     * Address, if af is AF_INET6.
+     */
+    struct in6_addr ipv6;
+  } address;
+
+  /**
+   * Remote port, in host byte order!
+   */
+  uint16_t port;
+  
+  /**
+   * IPPROTO_TCP or IPPROTO_UDP;
+   */
+  uint8_t proto;
+
+};
+
+/**
+ * This struct is saved into the services-hashmap
+ */
+struct redirect_service
+{
+
+  /**
+   * Remote address to use for the service.
+   */
+  struct remote_addr address;
+
+  /**
+   * Descriptor for this service (also key of this entry in the service hash 
map).
+   */
+  GNUNET_HashCode desc;
+
+  /**
+   * Port I am listening on within GNUnet for this service, in host byte order.
+   */
+  uint16_t my_port;
+
+};
+
+/**
+ * Information we use to track a connection.
+ */
+struct redirect_info 
+{
+
+  /**
+   * Address information for the other party.
+   */
+  struct remote_addr remote_address;
+
+  /**
+   * The source-port of this connection, in host byte order
+   */
+  uint16_t source_port;
+
+};
+
+/**
+ * This struct is saved into {tcp,udp}_connections;
+ */
+struct redirect_state
+{
+  /**
+   * Mesh tunnel that is used for this connection.
+   */
+  struct GNUNET_MESH_Tunnel *tunnel;
+
+  /**
+   * Heap node for this state in the connections_heap.
+   */
+  struct GNUNET_CONTAINER_HeapNode *heap_node;
+
+  /**
+   * Key this state has in the connections_map.
+   */
+  GNUNET_HashCode state_key;
+
+  /**
+   * Associated service record, or NULL for no service.
+   */
+  struct redirect_service *serv;
+
+  /**
+   * Source port we use for this connection.  FIXME: needed? used?
+   */
+  uint16_t source_port__;
+
+};
+
+/**
+ * Queue of messages to a tunnel.
+ */
+struct tunnel_notify_queue
+{
+  /**
+   * This is a doubly-linked list.
+   */
+  struct tunnel_notify_queue *next;
+
+  /**
+   * This is a doubly-linked list.
+   */
+  struct tunnel_notify_queue *prev;
+
+  /**
+   * Payload to send via the tunnel.
+   */
+  const void *payload;
+
+  /**
+   * Number of bytes in 'cls'.
+   */
+  size_t len;
+};
+
+
+/**
+ * Information we track per mesh tunnel.
+ */
+struct tunnel_state
+{
+  struct tunnel_notify_queue *head;
+  struct tunnel_notify_queue *tail;
+  struct GNUNET_MESH_TransmitHandle *th;
+  struct GNUNET_MESH_Tunnel *tunnel;
+};
+
+
+/**
+ * The handle to the configuration used throughout the process
+ */
+static const struct GNUNET_CONFIGURATION_Handle *cfg;
+
+/**
+ * The handle to the helper
+ */
+static struct GNUNET_HELPER_Handle *helper_handle;
+
+/**
+ * Arguments to the exit helper.
+ */
+static char *exit_argv[7];
+
+/**
+ * IPv6 prefix (0..127) from configuration file.
+ */
+static unsigned long long ipv6prefix;
+
+/**
+ * Final status code.
+ */
+static int ret;
+
+/**
+ * The handle to mesh
+ */
+static struct GNUNET_MESH_Handle *mesh_handle;
+
+/**
+ * This hashmaps contains the mapping from peer, service-descriptor,
+ * source-port and destination-port to a struct redirect_state
+ */
+static struct GNUNET_CONTAINER_MultiHashMap *connections_map;
+
+/**
+ * Heap so we can quickly find "old" connections.
+ */
+static struct GNUNET_CONTAINER_Heap *connections_heap;
+
+/**
+ * If there are at least this many connections, old ones will be removed
+ */
+static long long unsigned int max_connections = 200;
+
+/**
+ * This hashmaps saves interesting things about the configured UDP services
+ */
+static struct GNUNET_CONTAINER_MultiHashMap *udp_services;
+
+/**
+ * This hashmaps saves interesting things about the configured TCP services
+ */
+static struct GNUNET_CONTAINER_MultiHashMap *tcp_services;
+
+
+/**
+ * Given IP information about a connection, calculate the respective
+ * hash we would use for the 'connections_map'.
+ *
+ * @param hash resulting hash
+ * @param ri information about the connection
+ */
+static void
+hash_redirect_info (GNUNET_HashCode * hash, 
+                   const struct redirect_info *ri)
+{
+  char *off;
+
+  memset (hash, 0, sizeof (GNUNET_HashCode));
+  /* the GNUnet hashmap only uses the first sizeof(unsigned int) of the hash */
+  off = (char*) hash;
+  switch (ri->remote_address.af)
+  {
+  case AF_INET:
+    memcpy (off, &ri->remote_address.address.ipv4, sizeof (struct in_addr));
+    off += sizeof (struct in_addr);
+    break;
+  case AF_INET6:
+    memcpy (off, &ri->remote_address.address.ipv6, sizeof (struct in6_addr));
+    off += sizeof (struct in_addr);
+    break;
+  default:
+    GNUNET_assert (0);
+  }
+  memcpy (off, &ri->remote_address.port, sizeof (uint16_t));
+  memcpy (off, &ri->remote_address.proto, sizeof (uint8_t));
+  memcpy (off, &ri->source_port, sizeof (uint8_t));
+}
+
+
+/**
+ * Given a service descriptor and a destination port, find the
+ * respective service entry.
+ *
+ * @param service_map map of services (TCP or UDP)
+ * @param desc service descriptor
+ * @param dpt destination port
+ * @return NULL if we are not aware of such a service
+ */
+static struct redirect_service *
+find_service (struct GNUNET_CONTAINER_MultiHashMap *service_map,
+             const GNUNET_HashCode *desc,
+             uint16_t dpt)
+{
+  char key[sizeof (GNUNET_HashCode) + sizeof (uint16_t)];
+
+  memcpy (&key[0], &dpt, sizeof (uint16_t));
+  memcpy (&key[sizeof(uint16_t)], desc, sizeof (GNUNET_HashCode));
+  return GNUNET_CONTAINER_multihashmap_get (service_map,
+                                           (GNUNET_HashCode *) key);
+}
+
+
+/**
+ * Free memory associated with a service record.
+ *
+ * @param cls unused
+ * @param key service descriptor
+ * @param value service record to free
+ * @return GNUNET_OK
+ */
+static int
+free_service_record (void *cls,
+                    const GNUNET_HashCode *key,
+                    void *value)
+{
+  struct redirect_service *service = value;
+
+  GNUNET_free (service);
+  return GNUNET_OK;
+}
+
+
+/**
+ * Given a service descriptor and a destination port, find the
+ * respective service entry.
+ *
+ * @param service_map map of services (TCP or UDP)
+ * @param name name of the service 
+ * @param dpt destination port
+ * @param service service information record to store (service->desc will be 
set).
+ */
+static void
+store_service (struct GNUNET_CONTAINER_MultiHashMap *service_map,
+              const char *name,
+              uint16_t dpt,
+              struct redirect_service *service)
+{
+  char key[sizeof (GNUNET_HashCode) + sizeof (uint16_t)];
+  GNUNET_HashCode desc;
+
+  GNUNET_CRYPTO_hash (name, strlen (name) + 1, &desc);
+  service->desc = desc;
+  memcpy (&key[0], &dpt, sizeof (uint16_t));
+  memcpy (&key[sizeof(uint16_t)], &desc, sizeof (GNUNET_HashCode));
+  if (GNUNET_OK !=
+      GNUNET_CONTAINER_multihashmap_put (service_map,
+                                        (GNUNET_HashCode *) key,
+                                        service,
+                                        
GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY))
+  {
+    free_service_record (NULL, (GNUNET_HashCode *) key, service);
+    GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
+               _("Got duplicate service records for `%s:%u'\n"),
+               name,
+               (unsigned int) dpt);
+  }
+}
+
+
+/**
+ * MESH is ready to receive a message for the tunnel.  Transmit it.
+ *
+ * @param cls the 'struct tunnel_state'.
+ * @param size number of bytes available in buf
+ * @param buf where to copy the message
+ * @return number of bytes copied to buf
+ */
+static size_t
+send_to_peer_notify_callback (void *cls, size_t size, void *buf)
+{
+  struct tunnel_state *s = cls;
+  struct GNUNET_MESH_Tunnel *tunnel = s->tunnel;
+  struct tunnel_notify_queue *tnq;
+
+  s->th = NULL;
+  tnq = s->head;
+  GNUNET_assert (size >= tnq->len);
+  memcpy (buf, tnq->payload, tnq->len);
+  size = tnq->len;
+  GNUNET_CONTAINER_DLL_remove (s->head, 
+                              s->tail,
+                              tnq);  
+  GNUNET_free (tnq);
+  if (NULL != (tnq = s->head))
+    s->th = GNUNET_MESH_notify_transmit_ready (tunnel, 
+                                              GNUNET_NO /* corking */, 
+                                              0 /* priority */,
+                                              GNUNET_TIME_UNIT_FOREVER_REL,
+                                              NULL,
+                                              tnq->len,
+                                              &send_to_peer_notify_callback,
+                                              s);
+  return size;
+}
+
+
+/**
+ * Send the given packet via the mesh tunnel.
+ *
+ * @param mesh_tunnel destination
+ * @param payload message to transmit
+ * @param payload_length number of bytes in payload
+ * @param desc descriptor to add 
+ * @param mtype message type to use
+ */
+static void
+send_packet_to_mesh_tunnel (struct GNUNET_MESH_Tunnel *mesh_tunnel,
+                           const void *payload,
+                           size_t payload_length,
+                           const GNUNET_HashCode *desc,
+                           uint16_t mtype)
+{
+  struct tunnel_state *s;
+  struct tunnel_notify_queue *tnq;
+  struct GNUNET_MessageHeader *msg;
+  size_t len;
+  GNUNET_HashCode *dp;
+
+  len = sizeof (struct GNUNET_MessageHeader) + sizeof (GNUNET_HashCode) + 
payload_length;
+  if (len >= GNUNET_SERVER_MAX_MESSAGE_SIZE)
+  {
+    GNUNET_break (0);
+    return;
+  }
+  tnq = GNUNET_malloc (sizeof (struct tunnel_notify_queue) + len);
+  tnq->payload = &tnq[1];
+  tnq->len = len;
+  msg = (struct GNUNET_MessageHeader *) &tnq[1];
+  msg->size = htons ((uint16_t) len);
+  msg->type = htons (mtype);
+  dp = (GNUNET_HashCode *) &msg[1];
+  *dp = *desc;
+  memcpy (&dp[1], payload, payload_length);
+  s = GNUNET_MESH_tunnel_get_data (mesh_tunnel);
+  GNUNET_assert (NULL != s);
+  GNUNET_CONTAINER_DLL_insert_tail (s->head, s->tail, tnq);
+  if (NULL == s->th)
+    s->th = GNUNET_MESH_notify_transmit_ready (mesh_tunnel, GNUNET_NO /* cork 
*/, 0 /* priority */,
+                                              GNUNET_TIME_UNIT_FOREVER_REL,
+                                              NULL, len,
+                                              &send_to_peer_notify_callback,
+                                              s);
+}
+
+
+/**
+ * Get our connection tracking state.  Warns if it does not exists,
+ * refreshes the timestamp if it does exist.
+ *
+ * @param af address family
+ * @param protocol IPPROTO_UDP or IPPROTO_TCP
+ * @param destination_ip target IP
+ * @param destination_port target port
+ * @param source_port source port
+ * @return NULL if we have no tracking information for this tuple
+ */
+static struct redirect_state *
+get_redirect_state (int af,
+                   int protocol,
+                   const void *destination_ip,
+                   uint16_t destination_port,
+                   uint16_t source_port)
+{
+  struct redirect_info ri;
+  GNUNET_HashCode state_key;
+  struct redirect_state *state;
+
+  ri.remote_address.af = af;
+  if (af == AF_INET)
+    ri.remote_address.address.ipv4 = *((struct in_addr*) destination_ip);
+  else
+    ri.remote_address.address.ipv6 = * ((struct in6_addr*) destination_ip);
+  ri.remote_address.port = destination_port;
+  ri.remote_address.proto = IPPROTO_UDP;
+  ri.source_port = source_port;
+
+  hash_redirect_info (&state_key, &ri);
+  state = GNUNET_CONTAINER_multihashmap_get (connections_map, &state_key);
+  if (NULL == state)
+  {
+    GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+               _("Packet dropped, have no matching connection information\n"));
+    return NULL;
+  }
+  /* Mark this connection as freshly used */
+  GNUNET_CONTAINER_heap_update_cost (connections_heap, 
+                                    state->heap_node,
+                                     GNUNET_TIME_absolute_get ().abs_value);
+  return state;
+}
+
+
+/**
+ * @brief Handles an UDP packet received from the helper.
+ *
+ * @param udp A pointer to the Packet
+ * @param pktlen number of bytes in 'udp'
+ * @param destination_ip destination IP-address
+ * @param af address family (AFINET or AF_INET6)
+ */
+static void
+udp_from_helper (const struct udp_packet *udp, 
+                size_t pktlen,
+                const void *destination_ip, int af)
+{
+  struct redirect_state *state;
+  struct GNUNET_MESH_Tunnel *tunnel;
+  GNUNET_HashCode desc;
+
+  if (pktlen < sizeof (struct udp_packet))
+  {
+    /* blame kernel */
+    GNUNET_break (0);
+    return;
+  }
+  if (pktlen != ntohs (udp->len))
+  {
+    /* blame kernel */
+    GNUNET_break (0);
+    return;
+  }
+  state = get_redirect_state (af, IPPROTO_UDP,
+                             destination_ip,
+                             ntohs (udp->dpt), 
+                             ntohs (udp->spt));
+  if (NULL == state)
+    return;
+  tunnel = state->tunnel;
+
+  // FIXME...
+#if 0
+  if (state->type == SERVICE)
+  {
+    /* check if spt == serv.remote if yes: set spt = serv.myport ("nat") */
+    if (ntohs (udp->spt) == state->serv->remote_port)
+    {
+      udp->spt = htons (state->serv->my_port);
+    }
+    else
+    {
+      /* otherwise the answer came from a different port (tftp does this)
+       * add this new port to the list of all services, so that the packets
+       * coming back from the client to this new port will be routed correctly
+       */
+      struct redirect_service *serv =
+          GNUNET_malloc (sizeof (struct redirect_service));
+      memcpy (serv, state->serv, sizeof (struct redirect_service));
+      serv->my_port = ntohs (udp->spt);
+      serv->remote_port = ntohs (udp->spt);
+      uint16_t *desc = alloca (sizeof (GNUNET_HashCode) + 2);
+
+      memcpy ((GNUNET_HashCode *) (desc + 1), &state->desc,
+              sizeof (GNUNET_HashCode));
+      *desc = ntohs (udp->spt);
+      GNUNET_assert (GNUNET_OK ==
+                     GNUNET_CONTAINER_multihashmap_put (udp_services,
+                                                        (GNUNET_HashCode *)
+                                                        desc, serv,
+                                                        
GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY));
+
+      state->serv = serv;
+    }
+  }
+  
+  if (state->type == SERVICE)
+    memcpy (&desc, &state->desc, sizeof (GNUNET_HashCode));
+  else
+    memcpy (&desc, &state->remote, sizeof (struct remote_addr));
+#else
+  memset (&desc, 0, sizeof (desc));
+#endif
+
+  /* send udp-packet back */
+  send_packet_to_mesh_tunnel (tunnel,
+                             udp, pktlen,
+                             &desc,
+                             state->serv != NULL
+                             ? GNUNET_MESSAGE_TYPE_VPN_SERVICE_UDP_BACK 
+                             : GNUNET_MESSAGE_TYPE_VPN_REMOTE_UDP_BACK);
+}
+
+
+/**
+ * @brief Handles a TCP packet received from the helper.
+ *
+ * @param tcp A pointer to the Packet
+ * @param pktlen the length of the packet, including its header
+ * @param destination_ip destination IP-address
+ * @param af address family (AFINET or AF_INET6)
+ */
+static void
+tcp_from_helper (const struct tcp_packet *tcp, 
+                size_t pktlen,
+                const void *destination_ip, int af)
+{
+  struct redirect_state *state;
+  struct GNUNET_MESH_Tunnel *tunnel;
+  GNUNET_HashCode desc;
+
+  if (pktlen < sizeof (struct tcp_packet))
+  {
+    /* blame kernel */
+    GNUNET_break (0);
+    return;
+  }
+  state = get_redirect_state (af, IPPROTO_TCP,
+                             destination_ip, 
+                             ntohs (tcp->dpt),
+                             ntohs (tcp->spt));
+  if (NULL == state)
+    return;
+  tunnel = state->tunnel;
+
+  // FIXME...
+#if 0
+  if (state->type == SERVICE)
+  {
+    /* check if spt == serv.remote if yes: set spt = serv.myport ("nat") */
+    if (ntohs (tcp->spt) == state->serv->remote_port)
+    {
+      tcp->spt = htons (state->serv->my_port);
+    }
+    else
+    {
+      // This is an illegal packet.
+      return;
+    }
+  }
+
+  /* send tcp-packet back */
+  if (state->type == SERVICE)
+    memcpy (&desc, &state->desc, sizeof (GNUNET_HashCode));
+  else
+    memcpy (&desc, &state->remote, sizeof (struct remote_addr));
+#else
+  memset (&desc, 0, sizeof (desc));
+#endif
+  
+
+  send_packet_to_mesh_tunnel (tunnel,
+                             tcp, pktlen,
+                             &desc,
+                             state->serv != NULL
+                             ? GNUNET_MESSAGE_TYPE_VPN_SERVICE_TCP_BACK 
+                             : GNUNET_MESSAGE_TYPE_VPN_REMOTE_TCP_BACK);
+}
+
+
+/**
+ * Receive packets from the helper-process
+ *
+ * @param cls unused
+ * @param client unsued
+ * @param message message received from helper
+ */
+static void
+message_token (void *cls GNUNET_UNUSED, void *client GNUNET_UNUSED,
+               const struct GNUNET_MessageHeader *message)
+{
+  const struct tun_header *pkt_tun;
+  size_t size;
+
+  if (ntohs (message->type) != GNUNET_MESSAGE_TYPE_VPN_HELPER)
+  {
+    GNUNET_break (0);
+    return;
+  }
+  size = ntohs (message->size);
+  if (size < sizeof (struct tun_header) + sizeof (struct GNUNET_MessageHeader))
+  {
+    GNUNET_break (0);
+    return;
+  }
+  pkt_tun = (const struct tun_header *) &message[1];
+  size -= sizeof (struct tun_header) + sizeof (struct GNUNET_MessageHeader);
+  switch (ntohs (pkt_tun->proto))
+  {
+  case ETH_P_IPV6:
+    {
+      const struct ip6_header *pkt6;
+
+      if (size < sizeof (struct ip6_header))
+      {
+       /* Kernel to blame? */
+       GNUNET_break (0);
+       return;
+      }
+      pkt6 = (struct ip6_header *) &pkt_tun[1];
+      if (size != ntohs (pkt6->payload_length))
+      {
+       /* Kernel to blame? */
+       GNUNET_break (0);
+       return;
+      }
+      size -= sizeof (struct ip6_header);
+      switch (pkt6->next_header)
+      {
+      case IPPROTO_UDP:
+       udp_from_helper ( (const struct udp_packet *) &pkt6[1], size,
+                         &pkt6->destination_address, 
+                         AF_INET6);
+       break;
+      case IPPROTO_TCP:
+       tcp_from_helper ((const struct tcp_packet *) &pkt6[1], size,
+                        &pkt6->destination_address, 
+                        AF_INET6);
+       break;
+      default:
+       GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
+                   _("IPv6 packet with unsupported next header received.  
Ignored.\n"));
+       return;
+      }
+    }
+    break;
+  case ETH_P_IPV4:
+    {
+      const struct ip4_header *pkt4;
+
+      if (size < sizeof (struct ip4_header))
+      {
+       /* Kernel to blame? */
+       GNUNET_break (0);
+       return;
+      }
+      pkt4 = (const struct ip4_header *) &pkt_tun[1];
+      if (size != ntohs (pkt4->total_length))
+      {
+       /* Kernel to blame? */
+       GNUNET_break (0);
+       return;
+      }
+      if (pkt4->header_length * 4 != sizeof (struct ip4_header))
+      {
+       GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
+                   _("IPv4 packet options received.  Ignored.\n"));
+       return;
+      }
+      size -= sizeof (struct ip4_header);
+      switch (pkt4->protocol)
+      {
+      case IPPROTO_UDP:
+       udp_from_helper ((const struct udp_packet *) &pkt4[1], size,
+                        &pkt4->destination_address, AF_INET);
+       break;
+      case IPPROTO_TCP:
+       tcp_from_helper ((const struct tcp_packet *) &pkt4[1], size,
+                        &pkt4->destination_address, AF_INET);
+       break;
+      default:
+       GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
+                   _("IPv4 packet with unsupported next header received.  
Ignored.\n"));
+       return;
+      }
+    }
+    break;
+  default:
+    GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
+               _("Packet from unknown protocol %u received.  Ignored.\n"),
+               ntohs (pkt_tun->proto));
+    break;
+  }
+}
+
+
+
+
+static void
+prepare_ipv4_packet (size_t len, uint16_t pktlen, void *payload,
+                     uint8_t protocol, void *ipaddress, void *tunnel,
+                     struct redirect_info *state, struct ip4_header *pkt4)
+{
+  const char *ipv4addr = exit_argv[4];
+  const char *ipv4mask = exit_argv[5];
+  uint32_t tmp;
+  uint32_t tmp2;
+
+  GNUNET_assert (1 == inet_pton (AF_INET, ipv4addr, &tmp));
+  GNUNET_assert (1 == inet_pton (AF_INET, ipv4mask, &tmp2));
+  memcpy (&pkt4[1], payload, pktlen);
+  pkt4->version = 4;
+  pkt4->header_length = sizeof (struct ip4_header) / 4;
+  pkt4->diff_serv = 0;
+  pkt4->total_length = htons (sizeof (struct ip4_header) + pktlen);
+  pkt4->identification = 0; // FIXME!
+  pkt4->flags = 0;
+  pkt4->fragmentation_offset = 0;
+  pkt4->ttl = 255;
+  pkt4->protocol = protocol;
+  pkt4->checksum = 0;        /* Will be calculated later */
+
+  memcpy (&pkt4->destination_address, ipaddress, sizeof (struct in_addr));
+
+  /* Generate a new src-address  -- FIXME: not always, right!? */
+
+  /* This should be a noop */
+  tmp = tmp & tmp2;
+  tmp |= ntohl (*((uint32_t *) tunnel)) & (~tmp2);
+
+  pkt4->source_address.s_addr = tmp;
+  pkt4->checksum = GNUNET_CRYPTO_crc16_n (pkt4, sizeof (struct ip4_header));
+
+  // FIXME:  memcpy (&state->addr, &tmp, 4);
+
+  switch (protocol)
+  {
+  case IPPROTO_UDP:
+    {
+      struct udp_packet *pkt4_udp = (struct udp_packet *) &pkt4[1];
+      // FIXME: state->pt = pkt4_udp->spt;
+      pkt4_udp->crc = 0;  /* Optional for IPv4 */
+    }
+    break;
+  case IPPROTO_TCP:
+    {
+      struct tcp_packet *pkt4_tcp = (struct tcp_packet *) &pkt4[1];
+      
+      // FIXME: state->pt = pkt4_tcp->spt;
+      pkt4_tcp->crc = 0;
+      uint32_t sum = 0;
+      sum = GNUNET_CRYPTO_crc16_step (sum, 
+                                     &pkt4->source_address,
+                                     sizeof (struct in_addr) * 2);
+      tmp = (protocol << 16) | (0xffff & pktlen);
+      tmp = htonl (tmp);
+      sum = GNUNET_CRYPTO_crc16_step (sum, & tmp, 4);
+      sum = GNUNET_CRYPTO_crc16_step (sum, & pkt4_tcp, pktlen);
+      pkt4_tcp->crc = GNUNET_CRYPTO_crc16_finish (sum);
+    }
+    break;
+  default:
+    GNUNET_assert (0);
+  }
+}
+
+
+static void
+prepare_ipv6_packet (size_t len, uint16_t pktlen, void *payload,
+                     uint16_t protocol, void *ipaddress, void *tunnel,
+                     struct redirect_info *state, struct ip6_header *pkt6)
+{
+  const char *ipv6addr = exit_argv[2];
+  uint32_t tmp;
+
+
+  memcpy (&pkt6[1], payload, pktlen);
+
+  pkt6->version = 6;
+  pkt6->next_header = protocol;
+  pkt6->payload_length = htons (pktlen);
+  pkt6->hop_limit = 64;
+
+  memcpy (&pkt6->destination_address, ipaddress, sizeof (struct in6_addr));
+
+  /* Generate a new src-address
+   * This takes as much from the address of the tunnel as fits into
+   * the host-mask*/
+
+  unsigned long long ipv6prefix_r = (ipv6prefix + 7) / 8;
+
+  inet_pton (AF_INET6, ipv6addr, &pkt6->source_address);
+
+  if (ipv6prefix_r < (16 - sizeof (void *)))
+    ipv6prefix_r = 16 - sizeof (void *);
+
+  unsigned int offset = ipv6prefix_r - (16 - sizeof (void *));
+
+  memcpy ((((char *) &pkt6->source_address)) + ipv6prefix_r,
+          ((char *) &tunnel) + offset, 16 - ipv6prefix_r);
+
+  /* copy the needed information into the state */
+  // FIXME: memcpy (&state->addr, &pkt6->source_address, 16);
+
+  switch (protocol)
+  {
+  case IPPROTO_UDP:
+    {
+      struct udp_packet *pkt6_udp = (struct udp_packet *) &pkt6[1];
+      
+      // FIXME: state->pt = pkt6_udp->spt;      
+      pkt6_udp->crc = 0;
+      uint32_t sum = 0;
+      sum =
+        GNUNET_CRYPTO_crc16_step (sum, & pkt6->source_address,
+                                 16 * 2);
+      tmp = (htons (pktlen) & 0xffff);
+      sum = GNUNET_CRYPTO_crc16_step (sum, & tmp, 4);
+      tmp = htons (pkt6->next_header & 0x00ff);
+      sum = GNUNET_CRYPTO_crc16_step (sum, & tmp, 4);
+      sum =
+        GNUNET_CRYPTO_crc16_step (sum, pkt6_udp,
+                                 ntohs (pkt6_udp->len));
+      pkt6_udp->crc = GNUNET_CRYPTO_crc16_finish (sum);
+    }
+    break;
+  case IPPROTO_TCP:
+    {
+      struct tcp_packet *pkt6_tcp = (struct tcp_packet *) pkt6;
+      
+      // FIXME: state->pt = pkt6_tcp->spt;
+      pkt6_tcp->crc = 0;
+      uint32_t sum = 0;
+      
+      sum =
+        GNUNET_CRYPTO_crc16_step (sum, & pkt6->source_address, 16 * 2);
+      tmp = htonl (pktlen);
+      sum = GNUNET_CRYPTO_crc16_step (sum, & tmp, 4);
+      tmp = htonl (((pkt6->next_header & 0x000000ff)));
+      sum = GNUNET_CRYPTO_crc16_step (sum, & tmp, 4);
+      
+      sum =
+        GNUNET_CRYPTO_crc16_step (sum,  pkt6_tcp,
+                                 ntohs (pkt6->payload_length));
+      pkt6_tcp->crc = GNUNET_CRYPTO_crc16_finish (sum);
+    }
+    break;
+  default:
+    GNUNET_assert (0);
+    break;
+  }
+}
+
+
+/**
+ * We've just experienced a connection in use.  Track it, or if it is
+ * already tracked, update the tracking.
+ *
+ * @param u_i IP-level connection tracking state
+ * @param tunnel associated mesh tunnel
+ * @param desc service descriptor (or NULL)
+ * @param serv service information
+ */
+static void
+update_state_map (const struct redirect_info *ri,
+                 struct GNUNET_MESH_Tunnel *tunnel,
+                 const GNUNET_HashCode *desc,
+                 struct redirect_service *serv)
+{
+  struct redirect_state *state;
+  GNUNET_HashCode state_key;
+
+  hash_redirect_info (&state_key,
+                     ri);
+  state = GNUNET_CONTAINER_multihashmap_get (connections_map, &state_key);
+  if (NULL == state)
+  {
+    state = GNUNET_malloc (sizeof (struct redirect_state));
+    state->tunnel = tunnel;
+    state->state_key = state_key;
+    state->serv = serv;
+    // FIXME? if (NULL != desc) state->desc = *desc;
+    // FIXME? state->redirect_info = *ri;
+    GNUNET_assert (GNUNET_OK ==
+                  GNUNET_CONTAINER_multihashmap_put (connections_map, 
&state_key, state,
+                                                     
GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY));
+    state->heap_node =
+      GNUNET_CONTAINER_heap_insert (connections_heap,
+                                   state,
+                                   GNUNET_TIME_absolute_get ().abs_value);
+  }
+  else
+  {
+    if (state->tunnel != tunnel) 
+    {
+      /* Stats / warning: two tunnels got exactly the same connection state!? 
*/
+      GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
+                 _("Two different mesh tunnels got the same connection state. 
Oops.\n"));
+      return;
+    }
+    GNUNET_CONTAINER_heap_update_cost (connections_heap,
+                                      state->heap_node,
+                                      GNUNET_TIME_absolute_get ().abs_value);
+  }
+  while (GNUNET_CONTAINER_heap_get_size (connections_heap) > max_connections)
+  {
+    state = GNUNET_CONTAINER_heap_remove_root (connections_heap);
+    state->heap_node = NULL;
+    GNUNET_MESH_tunnel_destroy (state->tunnel);
+    GNUNET_assert (GNUNET_OK ==
+                  GNUNET_CONTAINER_multihashmap_remove (connections_map,
+                                                        &state->state_key, 
+                                                        state));
+    GNUNET_free (state);
+  }
+}
+
+                 
+
+
+/**
+ * The messages are one GNUNET_HashCode for the service followed by a struct 
tcp_packet
+ */
+static int
+receive_tcp_service (void *unused GNUNET_UNUSED, struct GNUNET_MESH_Tunnel 
*tunnel,
+                     void **tunnel_ctx GNUNET_UNUSED,
+                     const struct GNUNET_PeerIdentity *sender GNUNET_UNUSED,
+                     const struct GNUNET_MessageHeader *message,
+                     const struct GNUNET_ATS_Information *atsi GNUNET_UNUSED)
+{
+#if 0
+  const GNUNET_HashCode *desc = (const GNUNET_HashCode *) &message[1];
+  const struct tcp_packet *pkt = (const struct tcp_packet *) &desc[1];
+  uint16_t pkt_len = ntohs (message->size);
+  struct redirect_service *serv;
+  struct redirect_info u_i;
+  GNUNET_HashCode state_key;
+
+  /* check that we got at least a valid header */
+  if (pkt_len < sizeof (struct GNUNET_MessageHeader) + sizeof 
(GNUNET_HashCode) + sizeof (struct tcp_packet))
+  {
+    GNUNET_break_op (0);
+    return GNUNET_YES;
+  }
+  pkt_len -= (sizeof (struct GNUNET_MessageHeader) + sizeof (GNUNET_HashCode));
+
+  if (NULL == (serv = find_service (tcp_services, desc, ntohs (pkt->dpt))))
+  {
+    GNUNET_log (GNUNET_ERROR_TYPE_INFO, 
+               _("No service found for %s on port %d!\n"),
+               "TCP",
+                ntohs (pkt->dpt));
+    return GNUNET_YES;
+  }
+  pkt->dpt = htons (serv->remote_port);
+
+  /* At this point it would be possible to check against some kind of ACL. */
+
+  switch (serv->version)
+    {
+    case 4:
+      {
+       size_t len =
+         sizeof (struct GNUNET_MessageHeader) + sizeof (struct tun_header) +
+         sizeof (struct ip4_header) + pkt_len;       
+       char buf[len];
+       struct tun_header *hdr;
+       struct GNUNET_MessageHeader *mhdr;
+       
+       memset (buf, 0, len);
+       mhdr = (struct GNUNET_MessageHeader*) buf;
+       hdr = (struct tun_header *) &mhdr[1];
+       mhdr->type = htons (GNUNET_MESSAGE_TYPE_VPN_HELPER);
+       mhdr->size = htons (len);
+       hdr->flags = 0;
+       hdr->proto = htons (0x0800);
+       prepare_ipv4_packet (len, pkt_len, pkt, IPPROTO_TCP, 
&serv->v4.ip4address,
+                            tunnel, &u_i, (struct ip4_header *) &hdr[1]);
+       /* FIXME: here, flow-control with mesh would be nice to have... */
+       (void) GNUNET_HELPER_send (helper_handle,
+                                  mhdr,
+                                  GNUNET_YES,
+                                  NULL, NULL);
+       break;
+      }
+    case 6:
+      {
+       size_t len =
+         sizeof (struct GNUNET_MessageHeader) + sizeof (struct tun_header) +
+         sizeof (struct ip6_header) + pkt_len;
+       char buf[len];
+       struct tun_header *hdr;
+       struct GNUNET_MessageHeader *mhdr;
+       
+       memset (buf, 0, len);
+       mhdr = (struct GNUNET_MessageHeader*) buf;
+       hdr = (struct tun_header *) &mhdr[1];
+       mhdr->type = htons (GNUNET_MESSAGE_TYPE_VPN_HELPER);
+       mhdr->size = htons (len);
+       hdr->flags = 0;
+       hdr->proto = htons (0x86dd);
+       prepare_ipv6_packet (len, pkt_len, pkt, IPPROTO_TCP, 
&serv->v6.ip6address,
+                            tunnel, &u_i, (struct ip6_header *) buf);
+           /* FIXME: here, flow-control with mesh would be nice to have... */
+       (void) GNUNET_HELPER_send (helper_handle,
+                                  (const struct GNUNET_MessageHeader*) buf,
+                                  GNUNET_YES,
+                                  NULL, NULL);
+
+       break;
+      }
+    default:
+      GNUNET_assert (0);
+      break;
+    }
+
+
+  update_state_map (&u_i, desc, tunnel, serv);
+#endif
+  return GNUNET_YES;
+}
+
+
+static int
+receive_tcp_remote (void *cls GNUNET_UNUSED, struct GNUNET_MESH_Tunnel *tunnel,
+                    void **tunnel_ctx GNUNET_UNUSED,
+                    const struct GNUNET_PeerIdentity *sender GNUNET_UNUSED,
+                    const struct GNUNET_MessageHeader *message,
+                    const struct GNUNET_ATS_Information *atsi GNUNET_UNUSED)
+{
+  // FIXME
+#if 0
+  GNUNET_HashCode *desc = (GNUNET_HashCode *) (message + 1);
+  struct tcp_packet *pkt = (struct tcp_packet *) (desc + 1);
+  struct remote_addr *s = (struct remote_addr *) desc;
+  char *buf;
+  size_t len;
+  uint16_t pkt_len =
+      ntohs (message->size) - sizeof (struct GNUNET_MessageHeader) -
+      sizeof (GNUNET_HashCode);
+
+  struct redirect_state *state = GNUNET_malloc (sizeof (struct 
redirect_state));
+
+  state->tunnel = tunnel;
+  state->type = REMOTE;
+  state->hashmap = tcp_connections;
+  memcpy (&state->remote, s, sizeof (struct remote_addr));
+
+  hash_redirect_info (&state->hash, &state->redirect_info, s->addrlen);
+
+  if (GNUNET_NO ==
+      GNUNET_CONTAINER_multihashmap_contains (tcp_connections, &state->hash))
+  {
+    GNUNET_CONTAINER_multihashmap_put (tcp_connections, &state->hash, state,
+                                       
GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY);
+
+    state->heap_node =
+        GNUNET_CONTAINER_heap_insert (tcp_connections_heap, state,
+                                      GNUNET_TIME_absolute_get ().abs_value);
+
+    if (GNUNET_CONTAINER_heap_get_size (tcp_connections_heap) >
+        max_tcp_connections)
+      GNUNET_SCHEDULER_add_now (collect_connections, tcp_connections_heap);
+  }
+  else
+    GNUNET_free (state);
+
+
+
+  len =
+      sizeof (struct GNUNET_MessageHeader) + sizeof (struct pkt_tun) +
+      sizeof (struct ip6_hdr) + pkt_len;
+  buf = alloca (len);
+
+  memset (buf, 0, len);
+
+  switch (s->addrlen)
+  {
+  case 4:
+    prepare_ipv4_packet (len, pkt_len, pkt, IPPROTO_TCP, &s->addr, tunnel,
+                         state, (struct ip4_header *) buf);
+    break;
+  case 16:
+    prepare_ipv6_packet (len, pkt_len, pkt, IPPROTO_TCP, &s->addr, tunnel,
+                         state, (struct ip6_header *) buf);
+    break;
+  default:
+    GNUNET_free (state);
+    return GNUNET_SYSERR;
+  }
+
+  /* FIXME: here, flow-control with mesh would be nice to have... */
+  (void) GNUNET_HELPER_send (helper_handle,
+                            (const struct GNUNET_MessageHeader*) buf,
+                            GNUNET_YES,
+                            NULL, NULL);
+
+#endif
+  return GNUNET_YES;
+}
+
+static int
+receive_udp_remote (void *cls GNUNET_UNUSED, struct GNUNET_MESH_Tunnel *tunnel,
+                    void **tunnel_ctx GNUNET_UNUSED,
+                    const struct GNUNET_PeerIdentity *sender GNUNET_UNUSED,
+                    const struct GNUNET_MessageHeader *message,
+                    const struct GNUNET_ATS_Information *atsi GNUNET_UNUSED)
+{
+  // FIXME
+#if 0
+  GNUNET_HashCode *desc = (GNUNET_HashCode *) (message + 1);
+  struct udp_packet *pkt = (struct udp_packet *) (desc + 1);
+  struct remote_addr *s = (struct remote_addr *) desc;
+  char *buf;
+  size_t len;
+
+  GNUNET_assert (ntohs (pkt->len) ==
+                 ntohs (message->size) - sizeof (struct GNUNET_MessageHeader) -
+                 sizeof (GNUNET_HashCode));
+
+  /* Prepare the state.
+   * This will be saved in the hashmap, so that the receiving procedure knows
+   * through which tunnel this connection has to be routed.
+   */
+  struct redirect_state *state = GNUNET_malloc (sizeof (struct 
redirect_state));
+
+  state->tunnel = tunnel;
+  state->hashmap = udp_connections;
+  state->type = REMOTE;
+  memcpy (&state->remote, s, sizeof (struct remote_addr));
+
+  len =
+      sizeof (struct GNUNET_MessageHeader) + sizeof (struct pkt_tun) +
+      sizeof (struct ip6_hdr) + ntohs (pkt->len);
+  buf = alloca (len);
+
+  memset (buf, 0, len);
+
+  switch (s->addrlen)
+  {
+  case 4:
+    prepare_ipv4_packet (len, ntohs (pkt->len), pkt, IPPROTO_UDP, &s->addr,
+                         tunnel, state, (struct ip4_header *) buf);
+    break;
+  case 16:
+    prepare_ipv6_packet (len, ntohs (pkt->len), pkt, IPPROTO_UDP, &s->addr,
+                         tunnel, state, (struct ip6_header *) buf);
+    break;
+  default:
+    GNUNET_assert (0);
+    break;
+  }
+
+  hash_redirect_info (&state->hash, &state->redirect_info, s->addrlen);
+
+  (void) GNUNET_HELPER_send (helper_handle,
+                            (const struct GNUNET_MessageHeader*) buf,
+                            GNUNET_YES,
+                            NULL, NULL);
+
+
+  if (GNUNET_NO ==
+      GNUNET_CONTAINER_multihashmap_contains (udp_connections, &state->hash))
+  {
+    GNUNET_CONTAINER_multihashmap_put (udp_connections, &state->hash, state,
+                                       
GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY);
+
+    state->heap_node =
+        GNUNET_CONTAINER_heap_insert (udp_connections_heap, state,
+                                      GNUNET_TIME_absolute_get ().abs_value);
+
+    if (GNUNET_CONTAINER_heap_get_size (udp_connections_heap) >
+        max_udp_connections)
+      GNUNET_SCHEDULER_add_now (collect_connections, udp_connections_heap);
+  }
+  else
+    GNUNET_free (state);
+#endif
+  return GNUNET_YES;
+}
+
+/**
+ * The messages are one GNUNET_HashCode for the service, followed by a struct 
udp_packet
+ */
+static int
+receive_udp_service (void *cls GNUNET_UNUSED, struct GNUNET_MESH_Tunnel 
*tunnel,
+                     void **tunnel_ctx,
+                     const struct GNUNET_PeerIdentity *sender GNUNET_UNUSED,
+                     const struct GNUNET_MessageHeader *message,
+                     const struct GNUNET_ATS_Information *atsi GNUNET_UNUSED)
+{
+  // FIXME
+#if 0
+  GNUNET_HashCode *desc = (GNUNET_HashCode *) (message + 1);
+  struct udp_packet *pkt = (struct udp_packet *) (desc + 1);
+  uint16_t pkt_len = ntohs (message->size);
+  struct redirect_service *serv;
+
+  /* check that we got at least a valid header */
+  if (pkt_len < sizeof (struct GNUNET_MessageHeader) + sizeof 
(GNUNET_HashCode) + sizeof (struct udp_packet))
+  {
+    GNUNET_break_op (0);
+    return GNUNET_YES;
+  }
+  pkt_len -= (sizeof (struct GNUNET_MessageHeader) + sizeof (GNUNET_HashCode));
+
+  GNUNET_assert (ntohs (pkt->len) ==
+                 ntohs (message->size) - sizeof (struct GNUNET_MessageHeader) -
+                 sizeof (GNUNET_HashCode));
+
+  if (NULL == (serv = find_service (udp_services, desc, ntohs (pkt->dpt))))
+  {
+    GNUNET_log (GNUNET_ERROR_TYPE_INFO, 
+               _("No service found for %s on port %d!\n"),
+               "UDP",
+                ntohs (pkt->dpt));
+    return GNUNET_YES;
+  }
+  pkt->dpt = htons (serv->remote_port);
+
+  /*
+   * At this point it would be possible to check against some kind of ACL.
+   */
+
+  char *buf;
+  size_t len;
+
+  /* Prepare the state.
+   * This will be saved in the hashmap, so that the receiving procedure knows
+   * through which tunnel this connection has to be routed.
+   */
+  struct redirect_state *state = GNUNET_malloc (sizeof (struct 
redirect_state));
+
+  state->tunnel = tunnel;
+  state->serv = serv;
+  state->type = SERVICE;
+  state->hashmap = udp_connections;
+  memcpy (&state->desc, desc, sizeof (GNUNET_HashCode));
+
+  len =
+      sizeof (struct GNUNET_MessageHeader) + sizeof (struct pkt_tun) +
+      sizeof (struct ip6_hdr) + ntohs (pkt->len);
+  buf = alloca (len);
+
+  memset (buf, 0, len);
+
+  switch (serv->version)
+  {
+  case 4:
+    prepare_ipv4_packet (len, ntohs (pkt->len), pkt, IPPROTO_UDP,
+                         &serv->v4.ip4address, tunnel, state,
+                         (struct ip4_header *) buf);
+    break;
+  case 6:
+    prepare_ipv6_packet (len, ntohs (pkt->len), pkt, IPPROTO_UDP,
+                         &serv->v6.ip6address, tunnel, state,
+                         (struct ip6_header *) buf);
+
+    break;
+  default:
+    GNUNET_assert (0);
+    break;
+  }
+
+  hash_redirect_info (&state->hash, &state->redirect_info,
+                      serv->version == 4 ? 4 : 16);
+
+  if (GNUNET_NO ==
+      GNUNET_CONTAINER_multihashmap_contains (udp_connections, &state->hash))
+  {
+    GNUNET_CONTAINER_multihashmap_put (udp_connections, &state->hash, state,
+                                       
GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY);
+
+    state->heap_node =
+        GNUNET_CONTAINER_heap_insert (udp_connections_heap, state,
+                                      GNUNET_TIME_absolute_get ().abs_value);
+
+    if (GNUNET_CONTAINER_heap_get_size (udp_connections_heap) >
+        max_udp_connections)
+      GNUNET_SCHEDULER_add_now (collect_connections, udp_connections_heap);
+  }
+  else
+    GNUNET_free (state);
+
+  (void) GNUNET_HELPER_send (helper_handle,
+                            (const struct GNUNET_MessageHeader*) buf,
+                            GNUNET_YES,
+                            NULL, NULL);
+#endif
+  return GNUNET_YES;
+}
+
+
+
+
+
+
+
+/**
+ * Callback from GNUNET_MESH for new tunnels.
+ *
+ * @param cls closure
+ * @param tunnel new handle to the tunnel
+ * @param initiator peer that started the tunnel
+ * @param atsi performance information for the tunnel
+ * @return initial tunnel context for the tunnel
+ */
+static void *
+new_tunnel (void *cls GNUNET_UNUSED, struct GNUNET_MESH_Tunnel *tunnel,
+            const struct GNUNET_PeerIdentity *initiator GNUNET_UNUSED,
+            const struct GNUNET_ATS_Information *ats GNUNET_UNUSED)
+{
+  struct tunnel_state *s = GNUNET_malloc (sizeof (struct tunnel_state));
+  
+  s->tunnel = tunnel;
+  return s;
+}
+
+
+/**
+ * Function called by mesh 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
+ */
+static void
+clean_tunnel (void *cls GNUNET_UNUSED, const struct GNUNET_MESH_Tunnel *tunnel,
+              void *tunnel_ctx)
+{
+  struct tunnel_state *s = tunnel_ctx;
+  struct tunnel_notify_queue *tnq;
+
+  while (NULL != (tnq = s->head))
+  {
+    GNUNET_CONTAINER_DLL_remove (s->head,
+                                s->tail,
+                                tnq);
+    GNUNET_free (tnq);
+  }
+  if (NULL != s->th)
+  {
+    GNUNET_MESH_notify_transmit_ready_cancel (s->th);
+    s->th = NULL;
+  }
+  GNUNET_free (s);
+}
+
+
+/**
+ * Function that frees everything from a hashmap
+ *
+ * @param cls unused
+ * @param hash key
+ * @param value value to free
+ */
+static int
+free_iterate (void *cls GNUNET_UNUSED,
+              const GNUNET_HashCode * hash GNUNET_UNUSED, void *value)
+{
+  GNUNET_free (value);
+  return GNUNET_YES;
+}
+
+
+/**
+ * Function scheduled as very last function, cleans up after us
+ */
+static void
+cleanup (void *cls GNUNET_UNUSED,
+         const struct GNUNET_SCHEDULER_TaskContext *tskctx)
+{
+  unsigned int i;
+
+  if (helper_handle != NULL)
+  {
+    GNUNET_HELPER_stop (helper_handle);
+    helper_handle = NULL;
+  }
+  if (mesh_handle != NULL)
+  {
+    GNUNET_MESH_disconnect (mesh_handle);
+    mesh_handle = NULL;
+  }
+  if (NULL != connections_map)
+  {
+    GNUNET_CONTAINER_multihashmap_iterate (connections_map, &free_iterate, 
NULL);
+    GNUNET_CONTAINER_multihashmap_destroy (connections_map);
+    connections_map = NULL;
+  }
+  if (NULL != connections_heap)
+  {
+    GNUNET_CONTAINER_heap_destroy (connections_heap);
+    connections_heap = NULL;
+  }
+  if (NULL != tcp_services)
+  {
+    GNUNET_CONTAINER_multihashmap_iterate (tcp_services, &free_service_record, 
NULL);
+    GNUNET_CONTAINER_multihashmap_destroy (tcp_services);
+    tcp_services = NULL;
+  }
+  if (NULL != udp_services)
+  {
+    GNUNET_CONTAINER_multihashmap_iterate (udp_services, &free_service_record, 
NULL);
+    GNUNET_CONTAINER_multihashmap_destroy (udp_services);
+    udp_services = NULL;
+  }
+  for (i=0;i<5;i++)
+    GNUNET_free_non_null (exit_argv[i]);
+}
+
+
+/**
+ * Add services to the service map.
+ *
+ * @param proto IPPROTO_TCP or IPPROTO_UDP
+ * @param cpy copy of the service descriptor (can be mutilated)
+ * @param name DNS name of the service
+ */
+static void
+add_services (int proto,
+             char *cpy,
+             const char *name)
+{
+  char *redirect;
+  char *hostname;
+  char *hostport;
+  struct redirect_service *serv;
+
+  for (redirect = strtok (cpy, " "); redirect != NULL;
+       redirect = strtok (NULL, " "))
+  {
+    if (NULL == (hostname = strstr (redirect, ":")))
+    {
+      GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
+                 "option `%s' for domain `%s' is not formatted correctly!\n",
+                 redirect,
+                 name);
+      continue;
+    }
+    hostname[0] = '\0';
+    hostname++;
+    if (NULL == (hostport = strstr (hostname, ":")))
+    {
+      GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
+                 "option `%s' for domain `%s' is not formatted correctly!\n",
+                 redirect,
+                 name);
+      continue;
+    }
+    hostport[0] = '\0';
+    hostport++;
+    
+    int local_port = atoi (redirect);
+    int remote_port = atoi (hostport);
+    
+    if (!((local_port > 0) && (local_port < 65536)))
+    {
+      GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
+                 "`%s' is not a valid port number (for domain `%s')!", 
redirect,
+                 name);
+      continue;
+    }
+    if (!((remote_port > 0) && (remote_port < 65536)))
+    {
+      GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
+                 "`%s' is not a valid port number (for domain `%s')!", 
hostport,
+                 name);
+      continue;
+    }
+
+    serv = GNUNET_malloc (sizeof (struct redirect_service));
+    serv->my_port = (uint16_t) local_port;
+    serv->address.port = remote_port;
+    if (0 == strcmp ("localhost4", hostname))
+    {
+      const char *ip4addr = exit_argv[4];
+
+      serv->address.af = AF_INET;      
+      GNUNET_assert (1 != inet_pton (AF_INET, ip4addr, 
&serv->address.address.ipv4));
+    }
+    else if (0 == strcmp ("localhost6", hostname))
+    {
+      const char *ip6addr = exit_argv[2];
+
+      serv->address.af = AF_INET6;
+      GNUNET_assert (1 == inet_pton (AF_INET6, ip6addr, 
&serv->address.address.ipv6));
+    }
+    else
+    {
+      struct addrinfo *res;      
+      int ret;
+
+      ret = getaddrinfo (hostname, NULL, NULL, &res);      
+      if ( (ret != 0) || (res == NULL) )
+      {
+       GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
+                   _("No addresses found for hostname `%s' of service 
`%s'!\n"),
+                   hostname,
+                   name);
+       GNUNET_free (serv);
+       continue;
+      }
+      
+      serv->address.af = res->ai_family;
+      switch (res->ai_family)
+      {
+       case AF_INET:
+         serv->address.address.ipv4 = ((struct sockaddr_in *) 
res->ai_addr)->sin_addr;
+         break;
+       case AF_INET6:
+         serv->address.address.ipv6 = ((struct sockaddr_in6 *) 
res->ai_addr)->sin6_addr;
+         break;
+      default:
+       freeaddrinfo (res);
+       GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
+                   _("No IP addresses found for hostname `%s' of service 
`%s'!\n"),
+                   hostname,
+                   name);
+       GNUNET_free (serv);
+       continue;
+      }
+      freeaddrinfo (res);
+    }
+    store_service ((IPPROTO_UDP == proto) ? udp_services : tcp_services,
+                  name,
+                  local_port,
+                  serv);
+  }
+}
+
+
+/**
+ * Reads the configuration servicecfg and populates udp_services
+ *
+ * @param cls unused
+ * @param section name of section in config, equal to hostname
+ */
+static void
+read_service_conf (void *cls GNUNET_UNUSED, const char *section)
+{
+  char *cpy;
+
+  if ((strlen (section) < 8) ||
+      (0 != strcmp (".gnunet.", section + (strlen (section) - 8))))
+    return;
+  if (GNUNET_OK ==
+      GNUNET_CONFIGURATION_get_value_string (cfg, section, "UDP_REDIRECTS",
+                                            &cpy))
+  {
+    add_services (IPPROTO_UDP, cpy, section);
+    GNUNET_free (cpy);
+  }
+  if (GNUNET_OK ==
+      GNUNET_CONFIGURATION_get_value_string (cfg, section, "TCP_REDIRECTS",
+                                            &cpy))
+  {
+    add_services (IPPROTO_TCP, cpy, section);
+    GNUNET_free (cpy);
+  }
+}
+
+
+/**
+ * @brief Main function that will be run by the scheduler.
+ *
+ * @param cls closure
+ * @param args remaining command-line arguments
+ * @param cfgfile name of the configuration file used (for saving, can be 
NULL!)
+ * @param cfg_ configuration
+ */
+static void
+run (void *cls, char *const *args GNUNET_UNUSED,
+     const char *cfgfile GNUNET_UNUSED,
+     const struct GNUNET_CONFIGURATION_Handle *cfg_)
+{
+  static struct GNUNET_MESH_MessageHandler handlers[] = {
+    {&receive_udp_service, GNUNET_MESSAGE_TYPE_VPN_SERVICE_UDP, 0},
+    {&receive_tcp_service, GNUNET_MESSAGE_TYPE_VPN_SERVICE_TCP, 0},
+    {NULL, 0, 0},
+    {NULL, 0, 0},
+    {NULL, 0, 0}
+  };
+
+  static GNUNET_MESH_ApplicationType apptypes[] = {
+    GNUNET_APPLICATION_TYPE_END,
+    GNUNET_APPLICATION_TYPE_END,
+    GNUNET_APPLICATION_TYPE_END
+  };
+  unsigned int handler_idx;
+  unsigned int app_idx;
+  int udp;
+  int tcp;
+  char *ifname;
+  char *ipv6addr;
+  char *ipv6prefix_s;
+  char *ipv4addr;
+  char *ipv4mask;
+  struct in_addr v4;
+  struct in6_addr v6;
+
+  cfg = cfg_;
+  GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_FOREVER_REL, &cleanup, cls);
+  if (GNUNET_OK !=
+      GNUNET_CONFIGURATION_get_value_number (cfg, "exit", "MAX_CONNECTIONS",
+                                             &max_connections))
+    max_connections = 1024;
+  exit_argv[0] = GNUNET_strdup ("exit-gnunet");
+  if (GNUNET_SYSERR ==
+      GNUNET_CONFIGURATION_get_value_string (cfg, "exit", "IFNAME", &ifname))
+  {
+    GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+                "No entry 'IFNAME' in configuration!\n");
+    GNUNET_SCHEDULER_shutdown ();
+    return;
+  }
+  exit_argv[1] = ifname;
+  if ( (GNUNET_SYSERR ==
+       GNUNET_CONFIGURATION_get_value_string (cfg, "exit", "IPV6ADDR",
+                                              &ipv6addr) ||
+       (1 != inet_pton (AF_INET6, ipv6addr, &v6))) )
+  {
+    GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+                "No valid entry 'IPV6ADDR' in configuration!\n");
+    GNUNET_SCHEDULER_shutdown ();
+    return;
+  }
+  exit_argv[2] = ipv6addr;
+  if (GNUNET_SYSERR ==
+      GNUNET_CONFIGURATION_get_value_string (cfg, "exit", "IPV6PREFIX",
+                                             &ipv6prefix_s))
+  {
+    GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+                "No entry 'IPV6PREFIX' in configuration!\n");
+    GNUNET_SCHEDULER_shutdown ();
+    return;
+  }
+  exit_argv[3] = ipv6prefix_s;
+  if ( (GNUNET_OK !=
+       GNUNET_CONFIGURATION_get_value_number (cfg, "exit",
+                                              "IPV6PREFIX",
+                                              &ipv6prefix)) ||
+       (ipv6prefix >= 127) )
+  {
+    GNUNET_SCHEDULER_shutdown ();
+    return;
+  }
+
+  if ( (GNUNET_SYSERR ==
+       GNUNET_CONFIGURATION_get_value_string (cfg, "exit", "IPV4ADDR",
+                                              &ipv4addr) ||
+       (1 != inet_pton (AF_INET, ipv4addr, &v4))) )
+  {
+    GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+                "No valid entry for 'IPV4ADDR' in configuration!\n");
+    GNUNET_SCHEDULER_shutdown ();
+    return;
+  }
+  exit_argv[4] = ipv4addr;
+  if ( (GNUNET_SYSERR ==
+       GNUNET_CONFIGURATION_get_value_string (cfg, "exit", "IPV4MASK",
+                                              &ipv4mask) ||
+       (1 != inet_pton (AF_INET, ipv4mask, &v4))) )
+  {
+    GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+                "No valid entry 'IPV4MASK' in configuration!\n");
+    GNUNET_SCHEDULER_shutdown ();
+    return;
+  }
+  exit_argv[5] = ipv4mask;
+  exit_argv[6] = NULL;
+
+  app_idx = 0;
+  handler_idx = 2;
+  udp = GNUNET_CONFIGURATION_get_value_yesno (cfg, "exit", "ENABLE_UDP");
+  tcp = GNUNET_CONFIGURATION_get_value_yesno (cfg, "exit", "ENABLE_TCP");
+  if (GNUNET_YES == udp)
+  {
+    handlers[handler_idx].callback = &receive_udp_remote;
+    handlers[handler_idx].expected_size = 0;
+    handlers[handler_idx].type = GNUNET_MESSAGE_TYPE_VPN_REMOTE_UDP;
+    apptypes[app_idx] = GNUNET_APPLICATION_TYPE_INTERNET_UDP_GATEWAY;
+    handler_idx++;
+    app_idx++;
+  }
+
+  if (GNUNET_YES == tcp)
+  {
+    handlers[handler_idx].callback = &receive_tcp_remote;
+    handlers[handler_idx].expected_size = 0;
+    handlers[handler_idx].type = GNUNET_MESSAGE_TYPE_VPN_REMOTE_TCP;
+    apptypes[app_idx] = GNUNET_APPLICATION_TYPE_INTERNET_TCP_GATEWAY;
+    handler_idx++;
+    app_idx++;
+  }
+  udp_services = GNUNET_CONTAINER_multihashmap_create (65536);
+  tcp_services = GNUNET_CONTAINER_multihashmap_create (65536);
+  GNUNET_CONFIGURATION_iterate_sections (cfg, &read_service_conf, NULL);
+
+  connections_map = GNUNET_CONTAINER_multihashmap_create (65536);
+  connections_heap = GNUNET_CONTAINER_heap_create 
(GNUNET_CONTAINER_HEAP_ORDER_MIN);
+  mesh_handle 
+    = GNUNET_MESH_connect (cfg, 42 /* queue size */, NULL, 
+                          &new_tunnel, 
+                          &clean_tunnel, handlers,
+                           apptypes);
+  if (NULL == mesh_handle)
+  {
+    GNUNET_SCHEDULER_shutdown ();
+    return;
+  }
+  helper_handle = GNUNET_HELPER_start ("gnunet-helper-vpn", 
+                                      exit_argv,
+                                      &message_token, NULL);
+}
+
+
+/**
+ * The main function
+ *
+ * @param argc number of arguments from the command line
+ * @param argv command line arguments
+ * @return 0 ok, 1 on error
+ */
+int
+main (int argc, char *const *argv)
+{
+  static const struct GNUNET_GETOPT_CommandLineOption options[] = {
+    GNUNET_GETOPT_OPTION_END
+  };
+
+  return (GNUNET_OK ==
+          GNUNET_PROGRAM_run (argc, argv, "gnunet-daemon-exit",
+                              gettext_noop
+                              ("Daemon to run to provide an IP exit node for 
the VPN"),
+                              options, &run, NULL)) ? ret : 1;
+}
+
+
+/* end of gnunet-daemon-exit.c */

Added: gnunet/src/exit/gnunet-helper-exit.c
===================================================================
--- gnunet/src/exit/gnunet-helper-exit.c                                (rev 0)
+++ gnunet/src/exit/gnunet-helper-exit.c        2012-01-05 20:31:05 UTC (rev 
19019)
@@ -0,0 +1,596 @@
+/*
+     This file is part of GNUnet.
+     (C) 2010 Christian Grothoff
+
+     GNUnet is free software; you can redistribute it and/or modify
+     it under the terms of the GNU General Public License as published
+     by the Free Software Foundation; either version 3, or (at your
+     option) any later version.
+
+     GNUnet is distributed in the hope that it will be useful, but
+     WITHOUT ANY WARRANTY; without even the implied warranty of
+     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+     General Public License for more details.
+
+     You should have received a copy of the GNU General Public License
+     along with GNUnet; see the file COPYING.  If not, write to the
+     Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+     Boston, MA 02111-1307, USA.
+*/
+
+/**
+ * @file vpn/gnunet-daemon-vpn.c
+ * @brief the helper for various vpn-daemons. Opens a virtual 
network-interface,
+ * sends data received on the if to stdout, sends data received on stdin to the
+ * interface
+ * @author Philipp Tölke
+ *
+ * The following list of people have reviewed this code and considered
+ * it safe since the last modification (if you reviewed it, please
+ * have your name added to the list):
+ *
+ * - Philipp Tölke
+ */
+#include "platform.h"
+#include <linux/if_tun.h>
+
+/**
+ * Need 'struct GNUNET_MessageHeader'.
+ */
+#include "gnunet_common.h"
+
+/**
+ * Need VPN message types.
+ */
+#include "gnunet_protocols.h"
+
+/**
+ * Maximum size of a GNUnet message (GNUNET_SERVER_MAX_MESSAGE_SIZE)
+ */
+#define MAX_SIZE 65536
+
+#ifndef _LINUX_IN6_H
+/**
+ * This is in linux/include/net/ipv6.h, but not always exported...
+ */
+struct in6_ifreq
+{
+  struct in6_addr ifr6_addr;
+  uint32_t ifr6_prefixlen;
+  unsigned int ifr6_ifindex;
+};
+#endif
+
+
+/**
+ * Creates a tun-interface called dev;
+ *
+ * @param dev is asumed to point to a char[IFNAMSIZ]
+ *        if *dev == '\\0', uses the name supplied by the kernel;
+ * @return the fd to the tun or -1 on error
+ */
+static int
+init_tun (char *dev)
+{
+  struct ifreq ifr;
+  int fd;
+
+  if (NULL == dev)
+  {
+    errno = EINVAL;
+    return -1;
+  }
+
+  if (-1 == (fd = open ("/dev/net/tun", O_RDWR)))
+  {
+    fprintf (stderr, "Error opening `%s': %s\n", "/dev/net/tun",
+             strerror (errno));
+    return -1;
+  }
+
+  if (fd >= FD_SETSIZE)
+  {
+    fprintf (stderr, "File descriptor to large: %d", fd);
+    return -1;
+  }
+
+  memset (&ifr, 0, sizeof (ifr));
+  ifr.ifr_flags = IFF_TUN;
+
+  if ('\0' != *dev)
+    strncpy (ifr.ifr_name, dev, IFNAMSIZ);
+
+  if (-1 == ioctl (fd, TUNSETIFF, (void *) &ifr))
+  {
+    fprintf (stderr, "Error with ioctl on `%s': %s\n", "/dev/net/tun",
+             strerror (errno));
+    (void) close (fd);
+    return -1;
+  }
+  strcpy (dev, ifr.ifr_name);
+  return fd;
+}
+
+
+/**
+ * @brief Sets the IPv6-Address given in address on the interface dev
+ *
+ * @param dev the interface to configure
+ * @param address the IPv6-Address
+ * @param prefix_len the length of the network-prefix
+ */
+static void
+set_address6 (const char *dev, const char *address, unsigned long prefix_len)
+{
+  struct ifreq ifr;
+  struct in6_ifreq ifr6;
+  struct sockaddr_in6 sa6;
+  int fd;
+
+  /*
+   * parse the new address
+   */
+  memset (&sa6, 0, sizeof (struct sockaddr_in6));
+  sa6.sin6_family = AF_INET6;
+  if (1 != inet_pton (AF_INET6, address, sa6.sin6_addr.s6_addr))
+  {
+    fprintf (stderr, "Failed to parse address `%s': %s\n", address,
+             strerror (errno));
+    exit (1);
+  }
+
+  if (-1 == (fd = socket (PF_INET6, SOCK_DGRAM, 0)))
+  {
+    fprintf (stderr, "Error creating socket: %s\n", strerror (errno));
+    exit (1);
+  }
+
+  memset (&ifr, 0, sizeof (struct ifreq));
+  /*
+   * Get the index of the if
+   */
+  strncpy (ifr.ifr_name, dev, IFNAMSIZ);
+  if (-1 == ioctl (fd, SIOGIFINDEX, &ifr))
+  {
+    fprintf (stderr, "ioctl failed at %d: %s\n", __LINE__, strerror (errno));
+    (void) close (fd);
+    exit (1);
+  }
+
+  memset (&ifr6, 0, sizeof (struct in6_ifreq));
+  ifr6.ifr6_addr = sa6.sin6_addr;
+  ifr6.ifr6_ifindex = ifr.ifr_ifindex;
+  ifr6.ifr6_prefixlen = prefix_len;
+
+  /*
+   * Set the address
+   */
+  if (-1 == ioctl (fd, SIOCSIFADDR, &ifr6))
+  {
+    fprintf (stderr, "ioctl failed at line %d: %s\n", __LINE__,
+             strerror (errno));
+    (void) close (fd);
+    exit (1);
+  }
+
+  /*
+   * Get the flags
+   */
+  if (-1 == ioctl (fd, SIOCGIFFLAGS, &ifr))
+  {
+    fprintf (stderr, "ioctl failed at line %d: %s\n", __LINE__,
+             strerror (errno));
+    (void) close (fd);
+    exit (1);
+  }
+
+  /*
+   * Add the UP and RUNNING flags
+   */
+  ifr.ifr_flags |= IFF_UP | IFF_RUNNING;
+  if (-1 == ioctl (fd, SIOCSIFFLAGS, &ifr))
+  {
+    fprintf (stderr, "ioctl failed at line %d: %s\n", __LINE__,
+             strerror (errno));
+    (void) close (fd);
+    exit (1);
+  }
+
+  if (0 != close (fd))
+  {
+    fprintf (stderr, "close failed: %s\n", strerror (errno));
+    exit (1);
+  }
+}
+
+
+/**
+ * @brief Sets the IPv4-Address given in address on the interface dev
+ *
+ * @param dev the interface to configure
+ * @param address the IPv4-Address
+ * @param mask the netmask
+ */
+static void
+set_address4 (const char *dev, const char *address, const char *mask)
+{
+  int fd;
+  struct sockaddr_in *addr;
+  struct ifreq ifr;
+
+  memset (&ifr, 0, sizeof (struct ifreq));
+  addr = (struct sockaddr_in *) &(ifr.ifr_addr);
+  addr->sin_family = AF_INET;
+
+  /*
+   * Parse the address
+   */
+  if (1 != inet_pton (AF_INET, address, &addr->sin_addr.s_addr))
+  {
+    fprintf (stderr, "Failed to parse address `%s': %s\n", address,
+             strerror (errno));
+    exit (1);
+  }
+
+  if (-1 == (fd = socket (PF_INET, SOCK_DGRAM, 0)))
+  {
+    fprintf (stderr, "Error creating socket: %s\n", strerror (errno));
+    exit (1);
+  }
+
+  strncpy (ifr.ifr_name, dev, IFNAMSIZ);
+
+  /*
+   * Set the address
+   */
+  if (-1 == ioctl (fd, SIOCSIFADDR, &ifr))
+  {
+    fprintf (stderr, "ioctl failed at %d: %s\n", __LINE__, strerror (errno));
+    (void) close (fd);
+    exit (1);
+  }
+
+  /*
+   * Parse the netmask
+   */
+  addr = (struct sockaddr_in *) &(ifr.ifr_netmask);
+  if (1 != inet_pton (AF_INET, mask, &addr->sin_addr.s_addr))
+  {
+    fprintf (stderr, "Failed to parse address `%s': %s\n", mask,
+             strerror (errno));
+    (void) close (fd);
+    exit (1);
+  }
+
+  /*
+   * Set the netmask
+   */
+  if (-1 == ioctl (fd, SIOCSIFNETMASK, &ifr))
+  {
+    fprintf (stderr, "ioctl failed at line %d: %s\n", __LINE__,
+             strerror (errno));
+    (void) close (fd);
+    exit (1);
+  }
+
+  /*
+   * Get the flags
+   */
+  if (-1 == ioctl (fd, SIOCGIFFLAGS, &ifr))
+  {
+    fprintf (stderr, "ioctl failed at line %d: %s\n", __LINE__,
+             strerror (errno));
+    (void) close (fd);
+    exit (1);
+  }
+
+  /*
+   * Add the UP and RUNNING flags
+   */
+  ifr.ifr_flags |= IFF_UP | IFF_RUNNING;
+  if (-1 == ioctl (fd, SIOCSIFFLAGS, &ifr))
+  {
+    fprintf (stderr, "ioctl failed at line %d: %s\n", __LINE__,
+             strerror (errno));
+    (void) close (fd);
+    exit (1);
+  }
+
+  if (0 != close (fd))
+  {
+    fprintf (stderr, "close failed: %s\n", strerror (errno));
+    (void) close (fd);
+    exit (1);
+  }
+}
+
+
+/**
+ * Start forwarding to and from the tunnel.
+ *
+ * @param fd_tun tunnel FD
+ */
+static void
+run (int fd_tun)
+{
+  /*
+   * The buffer filled by reading from fd_tun
+   */
+  unsigned char buftun[MAX_SIZE];
+  ssize_t buftun_size = 0;
+  unsigned char *buftun_read = NULL;
+
+  /*
+   * The buffer filled by reading from stdin
+   */
+  unsigned char bufin[MAX_SIZE];
+  ssize_t bufin_size = 0;
+  size_t bufin_rpos = 0;
+  unsigned char *bufin_read = NULL;
+
+  fd_set fds_w;
+  fd_set fds_r;
+
+  /* read refers to reading from fd_tun, writing to stdout */
+  int read_open = 1;
+
+  /* write refers to reading from stdin, writing to fd_tun */
+  int write_open = 1;
+
+  while ((1 == read_open) || (1 == write_open))
+  {
+    FD_ZERO (&fds_w);
+    FD_ZERO (&fds_r);
+
+    /*
+     * We are supposed to read and the buffer is empty
+     * -> select on read from tun
+     */
+    if (read_open && (0 == buftun_size))
+      FD_SET (fd_tun, &fds_r);
+
+    /*
+     * We are supposed to read and the buffer is not empty
+     * -> select on write to stdout
+     */
+    if (read_open && (0 != buftun_size))
+      FD_SET (1, &fds_w);
+
+    /*
+     * We are supposed to write and the buffer is empty
+     * -> select on read from stdin
+     */
+    if (write_open && (NULL == bufin_read))
+      FD_SET (0, &fds_r);
+
+    /*
+     * We are supposed to write and the buffer is not empty
+     * -> select on write to tun
+     */
+    if (write_open && (NULL != bufin_read))
+      FD_SET (fd_tun, &fds_w);
+
+    int r = select (fd_tun + 1, &fds_r, &fds_w, NULL, NULL);
+
+    if (-1 == r)
+    {
+      if (EINTR == errno)
+        continue;
+      fprintf (stderr, "select failed: %s\n", strerror (errno));
+      exit (1);
+    }
+
+    if (r > 0)
+    {
+      if (FD_ISSET (fd_tun, &fds_r))
+      {
+        buftun_size =
+            read (fd_tun, buftun + sizeof (struct GNUNET_MessageHeader),
+                  MAX_SIZE - sizeof (struct GNUNET_MessageHeader));
+        if (-1 == buftun_size)
+        {
+          fprintf (stderr, "read-error: %s\n", strerror (errno));
+          shutdown (fd_tun, SHUT_RD);
+          shutdown (1, SHUT_WR);
+          read_open = 0;
+          buftun_size = 0;
+        }
+        else if (0 == buftun_size)
+        {
+          fprintf (stderr, "EOF on tun\n");
+          shutdown (fd_tun, SHUT_RD);
+          shutdown (1, SHUT_WR);
+          read_open = 0;
+          buftun_size = 0;
+        }
+        else
+        {
+          buftun_read = buftun;
+          struct GNUNET_MessageHeader *hdr =
+              (struct GNUNET_MessageHeader *) buftun;
+          buftun_size += sizeof (struct GNUNET_MessageHeader);
+          hdr->type = htons (GNUNET_MESSAGE_TYPE_VPN_HELPER);
+          hdr->size = htons (buftun_size);
+        }
+      }
+      else if (FD_ISSET (1, &fds_w))
+      {
+        ssize_t written = write (1, buftun_read, buftun_size);
+
+        if (-1 == written)
+        {
+          fprintf (stderr, "write-error to stdout: %s\n", strerror (errno));
+          shutdown (fd_tun, SHUT_RD);
+          shutdown (1, SHUT_WR);
+          read_open = 0;
+          buftun_size = 0;
+        }
+        else if (0 == written)
+        {
+          fprintf (stderr, "write returned 0!?\n");
+          exit (1);
+        }
+        else
+        {
+          buftun_size -= written;
+          buftun_read += written;
+        }
+      }
+
+      if (FD_ISSET (0, &fds_r))
+      {
+        bufin_size = read (0, bufin + bufin_rpos, MAX_SIZE - bufin_rpos);
+        if (-1 == bufin_size)
+        {
+          fprintf (stderr, "read-error: %s\n", strerror (errno));
+          shutdown (0, SHUT_RD);
+          shutdown (fd_tun, SHUT_WR);
+          write_open = 0;
+          bufin_size = 0;
+        }
+        else if (0 == bufin_size)
+        {
+          fprintf (stderr, "EOF on stdin\n");
+          shutdown (0, SHUT_RD);
+          shutdown (fd_tun, SHUT_WR);
+          write_open = 0;
+          bufin_size = 0;
+        }
+        else
+        {
+          struct GNUNET_MessageHeader *hdr;
+
+PROCESS_BUFFER:
+          bufin_rpos += bufin_size;
+          if (bufin_rpos < sizeof (struct GNUNET_MessageHeader))
+            continue;
+          hdr = (struct GNUNET_MessageHeader *) bufin;
+          if (ntohs (hdr->type) != GNUNET_MESSAGE_TYPE_VPN_HELPER)
+          {
+            fprintf (stderr, "protocol violation!\n");
+            exit (1);
+          }
+          if (ntohs (hdr->size) > bufin_rpos)
+            continue;
+          bufin_read = bufin + sizeof (struct GNUNET_MessageHeader);
+          bufin_size = ntohs (hdr->size) - sizeof (struct 
GNUNET_MessageHeader);
+          bufin_rpos -= bufin_size + sizeof (struct GNUNET_MessageHeader);
+        }
+      }
+      else if (FD_ISSET (fd_tun, &fds_w))
+      {
+        ssize_t written = write (fd_tun, bufin_read, bufin_size);
+
+        if (-1 == written)
+        {
+          fprintf (stderr, "write-error to tun: %s\n", strerror (errno));
+          shutdown (0, SHUT_RD);
+          shutdown (fd_tun, SHUT_WR);
+          write_open = 0;
+          bufin_size = 0;
+        }
+        else if (0 == written)
+        {
+          fprintf (stderr, "write returned 0!?\n");
+          exit (1);
+        }
+        else
+        {
+          bufin_size -= written;
+          bufin_read += written;
+          if (0 == bufin_size)
+          {
+            memmove (bufin, bufin_read, bufin_rpos);
+            bufin_read = NULL;  /* start reading again */
+            bufin_size = 0;
+            goto PROCESS_BUFFER;
+          }
+        }
+      }
+    }
+  }
+}
+
+
+/**
+ * Open VPN tunnel interface.
+ *
+ * @param argc must be 6
+ * @param argv 0: binary name (gnunet-helper-vpn)
+ *             1: tunnel interface name (gnunet-vpn)
+ *             2: IPv6 address (::1)
+ *             3: IPv6 netmask length in bits (64)
+ *             4: IPv4 address (1.2.3.4)
+ *             5: IPv4 netmask (255.255.0.0)
+ */
+int
+main (int argc, char **argv)
+{
+  char dev[IFNAMSIZ];
+  int fd_tun;
+  int global_ret;
+
+  if (6 != argc)
+  {
+    fprintf (stderr, "Fatal: must supply 5 arguments!\n");
+    return 1;
+  }
+
+  strncpy (dev, argv[1], IFNAMSIZ);
+  dev[IFNAMSIZ - 1] = '\0';
+
+  if (-1 == (fd_tun = init_tun (dev)))
+  {
+    fprintf (stderr, "Fatal: could not initialize tun-interface\n");
+    return 1;
+  }
+
+  {
+    const char *address = argv[2];
+    long prefix_len = atol (argv[3]);
+
+    if ((prefix_len < 1) || (prefix_len > 127))
+    {
+      fprintf (stderr, "Fatal: prefix_len out of range\n");
+      return 1;
+    }
+
+    set_address6 (dev, address, prefix_len);
+  }
+
+  {
+    const char *address = argv[4];
+    const char *mask = argv[5];
+
+    set_address4 (dev, address, mask);
+  }
+  
+  uid_t uid = getuid ();
+#ifdef HAVE_SETRESUID
+  if (0 != setresuid (uid, uid, uid))
+  {
+    fprintf (stderr, "Failed to setresuid: %s\n", strerror (errno));
+    global_ret = 2;
+    goto cleanup;
+  }
+#else
+  if (0 != (setuid (uid) | seteuid (uid)))
+  {
+    fprintf (stderr, "Failed to setuid: %s\n", strerror (errno));
+    global_ret = 2;
+    goto cleanup;
+  }
+#endif
+
+  if (SIG_ERR == signal (SIGPIPE, SIG_IGN))
+  {
+    fprintf (stderr, "Failed to protect against SIGPIPE: %s\n",
+             strerror (errno));
+    /* no exit, we might as well die with SIGPIPE should it ever happen */
+  }
+  run (fd_tun);
+  global_ret = 0;
+ cleanup:
+  close (fd_tun);
+  return global_ret;
+}

Modified: gnunet/src/vpn/vpn.conf
===================================================================
--- gnunet/src/vpn/vpn.conf     2012-01-05 20:20:22 UTC (rev 19018)
+++ gnunet/src/vpn/vpn.conf     2012-01-05 20:31:05 UTC (rev 19019)
@@ -9,14 +9,3 @@
 VIRTDNS6 = 1234::17
 IFNAME = vpn-gnunet
 
-[exit]
-CONFIG = $DEFAULTCONFIG
-BINARY = gnunet-daemon-exit
-IPV6ADDR = 1234:1::1
-IPV6PREFIX = 32
-IPV4ADDR = 10.10.1.1
-IPV4MASK = 255.255.0.0
-IFNAME = exit-gnunet
-ENABLE_UDP = NO
-ENABLE_TCP = NO
-




reply via email to

[Prev in Thread] Current Thread [Next in Thread]