+ * @param ts channel state to create the channel for
+ * @param target peer to connect to
+ * @param port destination port
+ * @return the channel handle
+ */
+static struct GNUNET_CADET_Channel *
+create_channel (struct ChannelState *ts,
+ const struct GNUNET_PeerIdentity *target,
+ const struct GNUNET_HashCode *port)
+{
+ struct GNUNET_MQ_MessageHandler cadet_handlers[] = {
+ GNUNET_MQ_hd_var_size (udp_back,
+ GNUNET_MESSAGE_TYPE_VPN_UDP_REPLY,
+ struct GNUNET_EXIT_UdpReplyMessage,
+ ts),
+ GNUNET_MQ_hd_var_size (tcp_back,
+ GNUNET_MESSAGE_TYPE_VPN_TCP_DATA_TO_VPN,
+ struct GNUNET_EXIT_TcpDataMessage,
+ ts),
+ GNUNET_MQ_hd_var_size (icmp_back,
+ GNUNET_MESSAGE_TYPE_VPN_ICMP_TO_VPN,
+ struct GNUNET_EXIT_IcmpToVPNMessage,
+ ts),
+ GNUNET_MQ_handler_end()
+ };
+
+ return GNUNET_CADET_channel_create (cadet_handle,
+ ts,
+ target,
+ port,
+ GNUNET_CADET_OPTION_DEFAULT,
+ NULL,
+ &channel_cleaner,
+ cadet_handlers);
+}
+
+
+/**
+ * Regex has found a potential exit peer for us; consider using it.
+ *
+ * @param cls the `struct ChannelState`
+ * @param id Peer providing a regex that matches the string.
+ * @param get_path Path of the get request.
+ * @param get_path_length Lenght of @a get_path.
+ * @param put_path Path of the put request.
+ * @param put_path_length Length of the @a put_path.
+ */
+static void
+handle_regex_result (void *cls,
+ const struct GNUNET_PeerIdentity *id,
+ const struct GNUNET_PeerIdentity *get_path,
+ unsigned int get_path_length,
+ const struct GNUNET_PeerIdentity *put_path,
+ unsigned int put_path_length)
+{
+ struct ChannelState *ts = cls;
+ struct GNUNET_HashCode port;
+
+ GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+ "Exit %s found for destination %s!\n",
+ GNUNET_i2s (id),
+ print_channel_destination (&ts->destination));
+ GNUNET_REGEX_search_cancel (ts->search);
+ ts->search = NULL;
+ switch (ts->af)
+ {
+ case AF_INET:
+ /* these must match the strings used in gnunet-daemon-exit */
+ GNUNET_CRYPTO_hash (GNUNET_APPLICATION_PORT_IPV4_GATEWAY,
+ strlen (GNUNET_APPLICATION_PORT_IPV4_GATEWAY),
+ &port);
+ break;
+ case AF_INET6:
+ /* these must match the strings used in gnunet-daemon-exit */
+ GNUNET_CRYPTO_hash (GNUNET_APPLICATION_PORT_IPV6_GATEWAY,
+ strlen (GNUNET_APPLICATION_PORT_IPV6_GATEWAY),
+ &port);
+ break;
+ default:
+ GNUNET_break (0);
+ return;
+ }
+ GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+ "Creating tunnel to %s for destination %s!\n",
+ GNUNET_i2s (id),
+ print_channel_destination (&ts->destination));
+ ts->channel = create_channel (ts,
+ id,
+ &port);
+}
+
+
+/**
+ * Initialize the given destination entry's cadet channel.
+ *
+ * @param dt destination channel for which we need to setup a channel
+ * @param client_af address family of the address returned to the client
+ * @return channel state of the channel that was created
+ */
+static struct ChannelState *
+create_channel_to_destination (struct DestinationChannel *dt,
+ int client_af)
+{
+ struct ChannelState *ts;
+
+ GNUNET_STATISTICS_update (stats,
+ gettext_noop ("# Cadet channels created"),
+ 1,
+ GNUNET_NO);
+ ts = GNUNET_new (struct ChannelState);
+ ts->af = client_af;
+ ts->destination = *dt->destination;
+ ts->destination.heap_node = NULL; /* copy is NOT in destination heap */
+ ts->destination_port = dt->destination_port;
+ if (dt->destination->is_service)
+ {
+ struct GNUNET_HashCode cadet_port;
+
+ GNUNET_TUN_compute_service_cadet_port (&ts->destination.details.service_destination.service_descriptor,
+ ts->destination_port,
+ &cadet_port);
+ ts->channel = create_channel (ts,
+ &dt->destination->details.service_destination.target,
+ &cadet_port);
+
+ if (NULL == ts->channel)
+ {
+ GNUNET_break (0);
+ GNUNET_free (ts);
+ return NULL;
+ }
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+ "Creating channel to peer %s offering service %s on port %u\n",
+ GNUNET_i2s (&dt->destination->details.service_destination.target),
+ GNUNET_h2s (&ts->destination.details.service_destination.service_descriptor),
+ (unsigned int) ts->destination_port);
+ }
+ else
+ {
+ char *policy;
+
+ switch (dt->destination->details.exit_destination.af)
+ {
+ case AF_INET:
+ {
+ char address[GNUNET_TUN_IPV4_REGEXLEN];
+
+ GNUNET_TUN_ipv4toregexsearch (&dt->destination->details.exit_destination.ip.v4,
+ dt->destination_port,
+ address);
+ GNUNET_asprintf (&policy,
+ "%s%s",
+ GNUNET_APPLICATION_TYPE_EXIT_REGEX_PREFIX,
+ address);
+ break;
+ }
+ case AF_INET6:
+ {
+ char address[GNUNET_TUN_IPV6_REGEXLEN];
+
+ GNUNET_TUN_ipv6toregexsearch (&dt->destination->details.exit_destination.ip.v6,
+ dt->destination_port,
+ address);
+ GNUNET_asprintf (&policy,
+ "%s%s",
+ GNUNET_APPLICATION_TYPE_EXIT_REGEX_PREFIX,
+ address);
+ break;
+ }
+ default:
+ GNUNET_assert (0);
+ break;
+ }
+
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+ "Requesting connect by string: %s\n",
+ policy);
+ ts->search = GNUNET_REGEX_search (cfg,
+ policy,
+ &handle_regex_result,
+ ts);
+ GNUNET_free (policy);
+ }
+ return ts;
+}
+
+
+/**
+ * We have too many active channels. Clean up the oldest channel.
+ *
+ * @param except channel that must NOT be cleaned up, even if it is the oldest
+ */
+static void
+expire_channel (struct ChannelState *except)
+{
+ struct ChannelState *ts;
+
+ ts = GNUNET_CONTAINER_heap_peek (channel_heap);
+ GNUNET_assert (NULL != ts);
+ if (except == ts)
+ return; /* can't do this */
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+ "Tearing down expired channel to %s\n",
+ print_channel_destination (&except->destination));
+ free_channel_state (ts);
+}
+
+
+/**
+ * Route a packet via cadet to the given destination.
+ *
+ * @param destination description of the destination
+ * @param af address family on this end (AF_INET or AF_INET6)
+ * @param protocol IPPROTO_TCP or IPPROTO_UDP or IPPROTO_ICMP or IPPROTO_ICMPV6
+ * @param source_ip source IP used by the sender (struct in_addr or struct in6_addr)
+ * @param destination_ip destination IP used by the sender (struct in_addr or struct in6_addr)
+ * @param payload payload of the packet after the IP header
+ * @param payload_length number of bytes in @a payload