+ state->specifics.tcp_udp.ri.remote_address.af = af;
+ switch (af)
+ {
+ case AF_INET:
+ if (pkt_len < sizeof (struct in_addr))
+ {
+ GNUNET_break_op (0);
+ return GNUNET_SYSERR;
+ }
+ if (! ipv4_exit)
+ {
+ GNUNET_break_op (0);
+ return GNUNET_SYSERR;
+ }
+ v4 = (const struct in_addr*) &start[1];
+ payload = &v4[1];
+ pkt_len -= sizeof (struct in_addr);
+ state->specifics.tcp_udp.ri.remote_address.address.ipv4 = *v4;
+ break;
+ case AF_INET6:
+ if (pkt_len < sizeof (struct in6_addr))
+ {
+ GNUNET_break_op (0);
+ return GNUNET_SYSERR;
+ }
+ if (! ipv6_exit)
+ {
+ GNUNET_break_op (0);
+ return GNUNET_SYSERR;
+ }
+ v6 = (const struct in6_addr*) &start[1];
+ payload = &v6[1];
+ pkt_len -= sizeof (struct in6_addr);
+ state->specifics.tcp_udp.ri.remote_address.address.ipv6 = *v6;
+ break;
+ default:
+ GNUNET_break_op (0);
+ return GNUNET_SYSERR;
+ }
+ {
+ char buf[INET6_ADDRSTRLEN];
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+ "Received data from %s for starting TCP stream to %s:%u\n",
+ GNUNET_i2s (&state->peer),
+ inet_ntop (af,
+ &state->specifics.tcp_udp.ri.remote_address.address,
+ buf, sizeof (buf)),
+ (unsigned int) ntohs (start->tcp_header.destination_port));
+ }
+ state->specifics.tcp_udp.ri.remote_address.proto = IPPROTO_TCP;
+ state->specifics.tcp_udp.ri.remote_address.port = ntohs (start->tcp_header.destination_port);
+ setup_state_record (state);
+ send_tcp_packet_via_tun (&state->specifics.tcp_udp.ri.remote_address,
+ &state->specifics.tcp_udp.ri.local_address,
+ &start->tcp_header,
+ payload, pkt_len);
+ return GNUNET_YES;
+}
+
+
+/**
+ * Process a request to forward TCP data on an established
+ * connection via this peer.
+ *
+ * @param cls closure, NULL
+ * @param tunnel connection to the other end
+ * @param tunnel_ctx pointer to our 'struct TunnelState *'
+ * @param message the actual message
+ *
+ * @return GNUNET_OK to keep the connection open,
+ * GNUNET_SYSERR to close it (signal serious error)
+ */
+static int
+receive_tcp_data (void *cls GNUNET_UNUSED, struct GNUNET_MESH_Tunnel *tunnel,
+ void **tunnel_ctx GNUNET_UNUSED,
+ const struct GNUNET_MessageHeader *message)
+{
+ struct TunnelState *state = *tunnel_ctx;
+ const struct GNUNET_EXIT_TcpDataMessage *data;
+ uint16_t pkt_len = ntohs (message->size);
+
+ if (GNUNET_YES == state->is_dns)
+ {
+ GNUNET_break_op (0);
+ return GNUNET_SYSERR;
+ }
+ if (GNUNET_SYSERR == state->is_dns)
+ {
+ /* tunnel is UDP/TCP from now on */
+ state->is_dns = GNUNET_NO;
+ }
+ GNUNET_STATISTICS_update (stats,
+ gettext_noop ("# Bytes received from MESH"),
+ pkt_len, GNUNET_NO);
+ GNUNET_STATISTICS_update (stats,
+ gettext_noop ("# TCP data requests received via mesh"),
+ 1, GNUNET_NO);
+ if (pkt_len < sizeof (struct GNUNET_EXIT_TcpDataMessage))
+ {
+ GNUNET_break_op (0);
+ return GNUNET_SYSERR;
+ }
+ data = (const struct GNUNET_EXIT_TcpDataMessage*) message;
+ pkt_len -= sizeof (struct GNUNET_EXIT_TcpDataMessage);
+ if ( (NULL == state) ||
+ (NULL == state->specifics.tcp_udp.heap_node) )
+ {
+ /* connection should have been up! */
+ GNUNET_STATISTICS_update (stats,
+ gettext_noop ("# TCP DATA requests dropped (no session)"),
+ 1, GNUNET_NO);
+ return GNUNET_SYSERR;
+ }
+ if (data->tcp_header.off * 4 < sizeof (struct GNUNET_TUN_TcpHeader))
+ {
+ GNUNET_break_op (0);
+ return GNUNET_SYSERR;
+ }
+
+ GNUNET_break_op (ntohl (data->reserved) == 0);
+ {
+ char buf[INET6_ADDRSTRLEN];
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+ "Received additional %u bytes of data from %s for TCP stream to %s:%u\n",
+ pkt_len,
+ GNUNET_i2s (&state->peer),
+ inet_ntop (state->specifics.tcp_udp.ri.remote_address.af,
+ &state->specifics.tcp_udp.ri.remote_address.address,
+ buf, sizeof (buf)),
+ (unsigned int) state->specifics.tcp_udp.ri.remote_address.port);
+ }
+
+ send_tcp_packet_via_tun (&state->specifics.tcp_udp.ri.remote_address,
+ &state->specifics.tcp_udp.ri.local_address,
+ &data->tcp_header,
+ &data[1], pkt_len);
+ return GNUNET_YES;
+}
+
+
+/**
+ * Send an ICMP packet via the TUN interface.
+ *
+ * @param destination_address IP to use for the ICMP packet's destination
+ * @param source_address IP to use for the ICMP packet's source
+ * @param icmp_header ICMP header to send
+ * @param payload payload of the ICMP packet (does NOT include ICMP header)
+ * @param payload_length number of bytes of data in payload
+ */
+static void
+send_icmp_packet_via_tun (const struct SocketAddress *destination_address,
+ const struct SocketAddress *source_address,
+ const struct GNUNET_TUN_IcmpHeader *icmp_header,
+ const void *payload, size_t payload_length)
+{
+ size_t len;
+ struct GNUNET_TUN_IcmpHeader *icmp;
+
+ GNUNET_STATISTICS_update (stats,
+ gettext_noop ("# ICMP packets sent via TUN"),
+ 1, GNUNET_NO);
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+ "Sending packet with %u bytes ICMP payload via TUN\n",
+ (unsigned int) payload_length);
+ len = sizeof (struct GNUNET_MessageHeader) + sizeof (struct GNUNET_TUN_Layer2PacketHeader);
+ switch (destination_address->af)
+ {
+ case AF_INET:
+ len += sizeof (struct GNUNET_TUN_IPv4Header);
+ break;
+ case AF_INET6:
+ len += sizeof (struct GNUNET_TUN_IPv6Header);
+ break;
+ default:
+ GNUNET_break (0);
+ return;
+ }
+ len += sizeof (struct GNUNET_TUN_IcmpHeader);
+ len += payload_length;
+ if (len >= GNUNET_SERVER_MAX_MESSAGE_SIZE)
+ {
+ GNUNET_break (0);
+ return;
+ }
+ {
+ char buf[len] GNUNET_ALIGN;
+ struct GNUNET_MessageHeader *hdr;
+ struct GNUNET_TUN_Layer2PacketHeader *tun;
+
+ hdr= (struct GNUNET_MessageHeader *) buf;
+ hdr->type = htons (GNUNET_MESSAGE_TYPE_VPN_HELPER);
+ hdr->size = htons (len);
+ tun = (struct GNUNET_TUN_Layer2PacketHeader*) &hdr[1];
+ tun->flags = htons (0);
+ switch (source_address->af)
+ {
+ case AF_INET:
+ {
+ struct GNUNET_TUN_IPv4Header * ipv4 = (struct GNUNET_TUN_IPv4Header*) &tun[1];
+
+ tun->proto = htons (ETH_P_IPV4);
+ GNUNET_TUN_initialize_ipv4_header (ipv4,
+ IPPROTO_ICMP,
+ sizeof (struct GNUNET_TUN_IcmpHeader) + payload_length,
+ &source_address->address.ipv4,
+ &destination_address->address.ipv4);
+ icmp = (struct GNUNET_TUN_IcmpHeader*) &ipv4[1];
+ }
+ break;
+ case AF_INET6:
+ {
+ struct GNUNET_TUN_IPv6Header * ipv6 = (struct GNUNET_TUN_IPv6Header*) &tun[1];
+
+ tun->proto = htons (ETH_P_IPV6);
+ GNUNET_TUN_initialize_ipv6_header (ipv6,
+ IPPROTO_ICMPV6,
+ sizeof (struct GNUNET_TUN_IcmpHeader) + payload_length,
+ &source_address->address.ipv6,
+ &destination_address->address.ipv6);
+ icmp = (struct GNUNET_TUN_IcmpHeader*) &ipv6[1];
+ }
+ break;
+ default:
+ GNUNET_assert (0);
+ break;
+ }
+ *icmp = *icmp_header;
+ memcpy (&icmp[1],
+ payload,
+ payload_length);
+ GNUNET_TUN_calculate_icmp_checksum (icmp,
+ payload,
+ payload_length);
+ if (NULL != helper_handle)
+ (void) GNUNET_HELPER_send (helper_handle,
+ (const struct GNUNET_MessageHeader*) buf,
+ GNUNET_YES,
+ NULL, NULL);
+ }
+}
+
+
+/**
+ * Synthesize a plausible ICMP payload for an ICMPv4 error
+ * response on the given tunnel.
+ *
+ * @param state tunnel information
+ * @param ipp IPv6 header to fill in (ICMP payload)
+ * @param udp "UDP" header to fill in (ICMP payload); might actually
+ * also be the first 8 bytes of the TCP header
+ */
+static void
+make_up_icmpv4_payload (struct TunnelState *state,
+ struct GNUNET_TUN_IPv4Header *ipp,
+ struct GNUNET_TUN_UdpHeader *udp)
+{
+ GNUNET_TUN_initialize_ipv4_header (ipp,
+ state->specifics.tcp_udp.ri.remote_address.proto,
+ sizeof (struct GNUNET_TUN_TcpHeader),
+ &state->specifics.tcp_udp.ri.remote_address.address.ipv4,
+ &state->specifics.tcp_udp.ri.local_address.address.ipv4);
+ udp->source_port = htons (state->specifics.tcp_udp.ri.remote_address.port);
+ udp->destination_port = htons (state->specifics.tcp_udp.ri.local_address.port);
+ udp->len = htons (0);
+ udp->crc = htons (0);
+}
+
+
+/**
+ * Synthesize a plausible ICMP payload for an ICMPv6 error
+ * response on the given tunnel.
+ *
+ * @param state tunnel information
+ * @param ipp IPv6 header to fill in (ICMP payload)
+ * @param udp "UDP" header to fill in (ICMP payload); might actually
+ * also be the first 8 bytes of the TCP header
+ */
+static void
+make_up_icmpv6_payload (struct TunnelState *state,
+ struct GNUNET_TUN_IPv6Header *ipp,
+ struct GNUNET_TUN_UdpHeader *udp)
+{
+ GNUNET_TUN_initialize_ipv6_header (ipp,
+ state->specifics.tcp_udp.ri.remote_address.proto,
+ sizeof (struct GNUNET_TUN_TcpHeader),
+ &state->specifics.tcp_udp.ri.remote_address.address.ipv6,
+ &state->specifics.tcp_udp.ri.local_address.address.ipv6);
+ udp->source_port = htons (state->specifics.tcp_udp.ri.remote_address.port);
+ udp->destination_port = htons (state->specifics.tcp_udp.ri.local_address.port);
+ udp->len = htons (0);
+ udp->crc = htons (0);
+}
+
+
+/**
+ * Process a request to forward ICMP data to the Internet via this peer.
+ *
+ * @param cls closure, NULL
+ * @param tunnel connection to the other end
+ * @param tunnel_ctx pointer to our 'struct TunnelState *'
+ * @param message the actual message
+ *
+ * @return GNUNET_OK to keep the connection open,
+ * GNUNET_SYSERR to close it (signal serious error)
+ */
+static int
+receive_icmp_remote (void *cls GNUNET_UNUSED, struct GNUNET_MESH_Tunnel *tunnel,
+ void **tunnel_ctx GNUNET_UNUSED,
+ const struct GNUNET_MessageHeader *message)
+{
+ struct TunnelState *state = *tunnel_ctx;
+ const struct GNUNET_EXIT_IcmpInternetMessage *msg;
+ uint16_t pkt_len = ntohs (message->size);
+ const struct in_addr *v4;
+ const struct in6_addr *v6;
+ const void *payload;
+ char buf[sizeof (struct GNUNET_TUN_IPv6Header) + 8] GNUNET_ALIGN;
+ int af;
+
+ if (GNUNET_YES == state->is_dns)
+ {
+ GNUNET_break_op (0);
+ return GNUNET_SYSERR;
+ }
+ if (GNUNET_SYSERR == state->is_dns)
+ {
+ /* tunnel is UDP/TCP from now on */
+ state->is_dns = GNUNET_NO;
+ }
+ GNUNET_STATISTICS_update (stats,
+ gettext_noop ("# Bytes received from MESH"),
+ pkt_len, GNUNET_NO);
+ GNUNET_STATISTICS_update (stats,
+ gettext_noop ("# ICMP IP-exit requests received via mesh"),
+ 1, GNUNET_NO);
+ if (pkt_len < sizeof (struct GNUNET_EXIT_IcmpInternetMessage))
+ {
+ GNUNET_break_op (0);
+ return GNUNET_SYSERR;
+ }
+ msg = (const struct GNUNET_EXIT_IcmpInternetMessage*) message;
+ pkt_len -= sizeof (struct GNUNET_EXIT_IcmpInternetMessage);
+
+ af = (int) ntohl (msg->af);
+ if ( (NULL != state->specifics.tcp_udp.heap_node) &&
+ (af != state->specifics.tcp_udp.ri.remote_address.af) )
+ {
+ /* other peer switched AF on this tunnel; not allowed */
+ GNUNET_break_op (0);
+ return GNUNET_SYSERR;
+ }
+