+ char buf[size] GNUNET_ALIGN;
+ struct GNUNET_MessageHeader *msg = (struct GNUNET_MessageHeader *) buf;
+ struct GNUNET_TUN_Layer2PacketHeader *tun = (struct GNUNET_TUN_Layer2PacketHeader*) &msg[1];
+ struct GNUNET_TUN_IPv4Header *ipv4 = (struct GNUNET_TUN_IPv4Header *) &tun[1];
+ struct GNUNET_TUN_UdpHeader *udp = (struct GNUNET_TUN_UdpHeader *) &ipv4[1];
+ msg->type = htons (GNUNET_MESSAGE_TYPE_VPN_HELPER);
+ msg->size = htons (size);
+ tun->flags = htons (0);
+ tun->proto = htons (ETH_P_IPV4);
+ GNUNET_TUN_initialize_ipv4_header (ipv4,
+ IPPROTO_UDP,
+ sizeof (struct GNUNET_TUN_UdpHeader) + mlen,
+ &ts->destination_ip.v4,
+ &ts->source_ip.v4);
+ if (0 == ntohs (reply->source_port))
+ udp->source_port = htons (ts->destination_port);
+ else
+ udp->source_port = reply->source_port;
+ if (0 == ntohs (reply->destination_port))
+ udp->destination_port = htons (ts->source_port);
+ else
+ udp->destination_port = reply->destination_port;
+ udp->len = htons (mlen + sizeof (struct GNUNET_TUN_UdpHeader));
+ GNUNET_TUN_calculate_udp4_checksum (ipv4,
+ udp,
+ &reply[1],
+ mlen);
+ GNUNET_memcpy (&udp[1],
+ &reply[1],
+ mlen);
+ (void) GNUNET_HELPER_send (helper_handle,
+ msg,
+ GNUNET_YES,
+ NULL, NULL);
+ }
+ }
+ break;
+ case AF_INET6:
+ {
+ size_t size = sizeof (struct GNUNET_TUN_IPv6Header)
+ + sizeof (struct GNUNET_TUN_UdpHeader)
+ + sizeof (struct GNUNET_MessageHeader) +
+ sizeof (struct GNUNET_TUN_Layer2PacketHeader) +
+ mlen;
+ {
+ char buf[size] GNUNET_ALIGN;
+ struct GNUNET_MessageHeader *msg = (struct GNUNET_MessageHeader *) buf;
+ struct GNUNET_TUN_Layer2PacketHeader *tun = (struct GNUNET_TUN_Layer2PacketHeader*) &msg[1];
+ struct GNUNET_TUN_IPv6Header *ipv6 = (struct GNUNET_TUN_IPv6Header *) &tun[1];
+ struct GNUNET_TUN_UdpHeader *udp = (struct GNUNET_TUN_UdpHeader *) &ipv6[1];
+ msg->type = htons (GNUNET_MESSAGE_TYPE_VPN_HELPER);
+ msg->size = htons (size);
+ tun->flags = htons (0);
+ tun->proto = htons (ETH_P_IPV6);
+ GNUNET_TUN_initialize_ipv6_header (ipv6,
+ IPPROTO_UDP,
+ sizeof (struct GNUNET_TUN_UdpHeader) + mlen,
+ &ts->destination_ip.v6,
+ &ts->source_ip.v6);
+ if (0 == ntohs (reply->source_port))
+ udp->source_port = htons (ts->destination_port);
+ else
+ udp->source_port = reply->source_port;
+ if (0 == ntohs (reply->destination_port))
+ udp->destination_port = htons (ts->source_port);
+ else
+ udp->destination_port = reply->destination_port;
+ udp->len = htons (mlen + sizeof (struct GNUNET_TUN_UdpHeader));
+ GNUNET_TUN_calculate_udp6_checksum (ipv6,
+ udp,
+ &reply[1], mlen);
+ GNUNET_memcpy (&udp[1],
+ &reply[1],
+ mlen);
+ (void) GNUNET_HELPER_send (helper_handle,
+ msg,
+ GNUNET_YES,
+ NULL, NULL);
+ }
+ }
+ break;
+ default:
+ GNUNET_assert (0);
+ }
+ GNUNET_CONTAINER_heap_update_cost (ts->heap_node,
+ GNUNET_TIME_absolute_get ().abs_value_us);
+ GNUNET_CADET_receive_done (ts->channel);
+}
+
+
+/**
+ * We got a TCP packet back from the CADET channel. Check it is OK.
+ *
+ * @param cls our `struct ChannelState *`
+ * @param data the actual message
+ * @return #GNUNET_OK to keep the connection open,
+ * #GNUNET_SYSERR to close it (signal serious error)
+ */
+static int
+check_tcp_back (void *cls,
+ const struct GNUNET_EXIT_TcpDataMessage *data)
+{
+ struct ChannelState *ts = cls;
+
+ if (NULL == ts->heap_node)
+ {
+ GNUNET_break_op (0);
+ return GNUNET_SYSERR;
+ }
+ if (data->tcp_header.off * 4 < sizeof (struct GNUNET_TUN_TcpHeader))
+ {
+ GNUNET_break_op (0);
+ return GNUNET_SYSERR;
+ }
+ return GNUNET_OK;
+}
+
+
+/**
+ * We got a TCP packet back from the CADET channel. Pass it on to the
+ * local virtual interface via the helper.
+ *
+ * @param cls our `struct ChannelState *`
+ * @param data the actual message
+ */
+static void
+handle_tcp_back (void *cls,
+ const struct GNUNET_EXIT_TcpDataMessage *data)
+{
+ struct ChannelState *ts = cls;
+ size_t mlen;
+
+ GNUNET_STATISTICS_update (stats,
+ gettext_noop ("# TCP packets received from cadet"),
+ 1,
+ GNUNET_NO);
+ mlen = ntohs (data->header.size) - sizeof (struct GNUNET_EXIT_TcpDataMessage);
+ {
+ char sbuf[INET6_ADDRSTRLEN];
+ char dbuf[INET6_ADDRSTRLEN];
+
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+ "Received TCP reply from cadet, 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:
+ {
+ size_t size = sizeof (struct GNUNET_TUN_IPv4Header)
+ + sizeof (struct GNUNET_TUN_TcpHeader)
+ + sizeof (struct GNUNET_MessageHeader) +
+ sizeof (struct GNUNET_TUN_Layer2PacketHeader) +
+ mlen;
+ {
+ char buf[size] GNUNET_ALIGN;
+ struct GNUNET_MessageHeader *msg = (struct GNUNET_MessageHeader *) buf;
+ struct GNUNET_TUN_Layer2PacketHeader *tun = (struct GNUNET_TUN_Layer2PacketHeader*) &msg[1];
+ struct GNUNET_TUN_IPv4Header *ipv4 = (struct GNUNET_TUN_IPv4Header *) &tun[1];
+ struct GNUNET_TUN_TcpHeader *tcp = (struct GNUNET_TUN_TcpHeader *) &ipv4[1];
+ msg->type = htons (GNUNET_MESSAGE_TYPE_VPN_HELPER);
+ msg->size = htons (size);
+ tun->flags = htons (0);
+ tun->proto = htons (ETH_P_IPV4);
+ GNUNET_TUN_initialize_ipv4_header (ipv4,
+ IPPROTO_TCP,
+ sizeof (struct GNUNET_TUN_TcpHeader) + mlen,
+ &ts->destination_ip.v4,
+ &ts->source_ip.v4);
+ *tcp = data->tcp_header;
+ tcp->source_port = htons (ts->destination_port);
+ tcp->destination_port = htons (ts->source_port);
+ GNUNET_TUN_calculate_tcp4_checksum (ipv4,
+ tcp,
+ &data[1],
+ mlen);
+ GNUNET_memcpy (&tcp[1],
+ &data[1],
+ mlen);
+ (void) GNUNET_HELPER_send (helper_handle,
+ msg,
+ GNUNET_YES,
+ NULL, NULL);
+ }
+ }
+ break;
+ case AF_INET6:
+ {
+ size_t size = sizeof (struct GNUNET_TUN_IPv6Header)
+ + sizeof (struct GNUNET_TUN_TcpHeader)
+ + sizeof (struct GNUNET_MessageHeader) +
+ sizeof (struct GNUNET_TUN_Layer2PacketHeader) +
+ mlen;
+ {
+ char buf[size] GNUNET_ALIGN;
+ struct GNUNET_MessageHeader *msg = (struct GNUNET_MessageHeader *) buf;
+ struct GNUNET_TUN_Layer2PacketHeader *tun = (struct GNUNET_TUN_Layer2PacketHeader*) &msg[1];
+ struct GNUNET_TUN_IPv6Header *ipv6 = (struct GNUNET_TUN_IPv6Header *) &tun[1];
+ struct GNUNET_TUN_TcpHeader *tcp = (struct GNUNET_TUN_TcpHeader *) &ipv6[1];
+ msg->type = htons (GNUNET_MESSAGE_TYPE_VPN_HELPER);
+ msg->size = htons (size);
+ tun->flags = htons (0);
+ tun->proto = htons (ETH_P_IPV6);
+ GNUNET_TUN_initialize_ipv6_header (ipv6,
+ IPPROTO_TCP,
+ sizeof (struct GNUNET_TUN_TcpHeader) + mlen,
+ &ts->destination_ip.v6,
+ &ts->source_ip.v6);
+ *tcp = data->tcp_header;
+ tcp->source_port = htons (ts->destination_port);
+ tcp->destination_port = htons (ts->source_port);
+ GNUNET_TUN_calculate_tcp6_checksum (ipv6,
+ tcp,
+ &data[1],
+ mlen);
+ GNUNET_memcpy (&tcp[1],
+ &data[1],
+ mlen);
+ (void) GNUNET_HELPER_send (helper_handle,
+ msg,
+ GNUNET_YES,
+ NULL, NULL);
+ }
+ }
+ break;
+ }
+ GNUNET_CONTAINER_heap_update_cost (ts->heap_node,
+ GNUNET_TIME_absolute_get ().abs_value_us);
+ GNUNET_CADET_receive_done (ts->channel);
+}
+
+
+/**
+ * Create a channel for @a ts to @a target at @a port
+ *
+ * @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
+ */
+static void
+route_packet (struct DestinationEntry *destination,
+ int af,
+ uint8_t protocol,
+ const void *source_ip,
+ const void *destination_ip,
+ const void *payload,
+ size_t payload_length)
+{
+ struct GNUNET_HashCode key;
+ struct ChannelState *ts;
+ size_t alen;
+ size_t mlen;
+ struct GNUNET_MQ_Envelope *env;
+ const struct GNUNET_TUN_UdpHeader *udp;
+ const struct GNUNET_TUN_TcpHeader *tcp;
+ const struct GNUNET_TUN_IcmpHeader *icmp;
+ struct DestinationChannel *dt;
+ uint16_t source_port;
+ uint16_t destination_port;
+
+ switch (protocol)
+ {
+ case IPPROTO_UDP:
+ {
+ if (payload_length < sizeof (struct GNUNET_TUN_UdpHeader))
+ {
+ /* blame kernel? */