From b53c68458d1b7daa7a4320adb401daac62c4daed Mon Sep 17 00:00:00 2001 From: Christian Grothoff Date: Sat, 21 Jan 2012 20:05:03 +0000 Subject: [PATCH] -implementing ICMP PT and type checking on VPN->TUN link --- src/exit/gnunet-daemon-exit.c | 4 +- src/vpn/gnunet-service-vpn.c | 252 +++++++++++++++++++++++++++++++++- 2 files changed, 248 insertions(+), 8 deletions(-) diff --git a/src/exit/gnunet-daemon-exit.c b/src/exit/gnunet-daemon-exit.c index d2bec5203..f55106243 100644 --- a/src/exit/gnunet-daemon-exit.c +++ b/src/exit/gnunet-daemon-exit.c @@ -649,7 +649,7 @@ icmp_from_helper (const struct GNUNET_TUN_IcmpHeader *icmp, break; default: GNUNET_STATISTICS_update (stats, - gettext_noop ("# ICMPv4 packets dropped (not allowed)"), + gettext_noop ("# ICMPv4 packets dropped (type not allowed)"), 1, GNUNET_NO); return; } @@ -684,7 +684,7 @@ icmp_from_helper (const struct GNUNET_TUN_IcmpHeader *icmp, break; default: GNUNET_STATISTICS_update (stats, - gettext_noop ("# ICMPv6 packets dropped (not allowed)"), + gettext_noop ("# ICMPv6 packets dropped (type not allowed)"), 1, GNUNET_NO); return; } diff --git a/src/vpn/gnunet-service-vpn.c b/src/vpn/gnunet-service-vpn.c index 403570e26..12b4eef4a 100644 --- a/src/vpn/gnunet-service-vpn.c +++ b/src/vpn/gnunet-service-vpn.c @@ -1599,6 +1599,44 @@ message_token (void *cls GNUNET_UNUSED, void *client GNUNET_UNUSED, } +/** + * Synthesize a plausible ICMP payload for an ICMP error + * response on the given tunnel. + * + * @param ts tunnel information + * @param ipp IPv4 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 *ts, + struct GNUNET_TUN_IPv4Header *ipp, + struct GNUNET_TUN_UdpHeader *udp) +{ + /* FIXME */ + GNUNET_break (0); +} + + +/** + * Synthesize a plausible ICMP payload for an ICMP error + * response on the given tunnel. + * + * @param ts 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 *ts, + struct GNUNET_TUN_IPv6Header *ipp, + struct GNUNET_TUN_UdpHeader *udp) +{ + /* FIXME */ + GNUNET_break (0); +} + + /** * We got an ICMP packet back from the MESH tunnel. Pass it on to the * local virtual interface via the helper. @@ -1663,13 +1701,14 @@ receive_icmp_back (void *cls GNUNET_UNUSED, struct GNUNET_MESH_Tunnel *tunnel, sizeof (struct GNUNET_TUN_Layer2PacketHeader) + mlen; { - char buf[size]; + /* reserve some extra space in case we have an ICMP type here where + we will need to make up the payload ourselves */ + char buf[size + sizeof (struct GNUNET_TUN_IPv4Header) + 8]; 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_IcmpHeader *icmp = (struct GNUNET_TUN_IcmpHeader *) &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, @@ -1681,7 +1720,108 @@ receive_icmp_back (void *cls GNUNET_UNUSED, struct GNUNET_MESH_Tunnel *tunnel, memcpy (&icmp[1], &i2v[1], mlen); - /* FIXME: for some ICMP types, we need to adjust the payload here... */ + /* For some ICMP types, we need to adjust (make up) the payload here. + Also, depending on the AF used on the other side, we have to + do ICMP PT (translate ICMP types) */ + switch (ntohl (i2v->af)) + { + case AF_INET: + switch (icmp->type) + { + case GNUNET_TUN_ICMPTYPE_ECHO_REPLY: + case GNUNET_TUN_ICMPTYPE_ECHO_REQUEST: + break; + case GNUNET_TUN_ICMPTYPE_DESTINATION_UNREACHABLE: + case GNUNET_TUN_ICMPTYPE_SOURCE_QUENCH: + case GNUNET_TUN_ICMPTYPE_TIME_EXCEEDED: + { + struct GNUNET_TUN_IPv4Header *ipp = (struct GNUNET_TUN_IPv4Header *) &icmp[1]; + struct GNUNET_TUN_UdpHeader *udp = (struct GNUNET_TUN_UdpHeader *) &ipp[1]; + + if (mlen != 0) + { + /* sender did not strip ICMP payload? */ + GNUNET_break_op (0); + return GNUNET_SYSERR; + } + size += sizeof (struct GNUNET_TUN_IPv4Header) + 8; + GNUNET_assert (8 == sizeof (struct GNUNET_TUN_UdpHeader)); + make_up_icmpv4_payload (ts, ipp, udp); + } + break; + default: + GNUNET_break_op (0); + GNUNET_STATISTICS_update (stats, + gettext_noop ("# ICMPv4 packets dropped (type not allowed)"), + 1, GNUNET_NO); + return GNUNET_SYSERR; + } + /* end AF_INET */ + break; + case AF_INET6: + /* ICMP PT 6-to-4 and possibly making up payloads */ + switch (icmp->type) + { + case GNUNET_TUN_ICMPTYPE6_DESTINATION_UNREACHABLE: + icmp->type = GNUNET_TUN_ICMPTYPE_DESTINATION_UNREACHABLE; + { + struct GNUNET_TUN_IPv4Header *ipp = (struct GNUNET_TUN_IPv4Header *) &icmp[1]; + struct GNUNET_TUN_UdpHeader *udp = (struct GNUNET_TUN_UdpHeader *) &ipp[1]; + + if (mlen != 0) + { + /* sender did not strip ICMP payload? */ + GNUNET_break_op (0); + return GNUNET_SYSERR; + } + size += sizeof (struct GNUNET_TUN_IPv4Header) + 8; + GNUNET_assert (8 == sizeof (struct GNUNET_TUN_UdpHeader)); + make_up_icmpv4_payload (ts, ipp, udp); + } + break; + case GNUNET_TUN_ICMPTYPE6_TIME_EXCEEDED: + icmp->type = GNUNET_TUN_ICMPTYPE_TIME_EXCEEDED; + { + struct GNUNET_TUN_IPv4Header *ipp = (struct GNUNET_TUN_IPv4Header *) &icmp[1]; + struct GNUNET_TUN_UdpHeader *udp = (struct GNUNET_TUN_UdpHeader *) &ipp[1]; + + if (mlen != 0) + { + /* sender did not strip ICMP payload? */ + GNUNET_break_op (0); + return GNUNET_SYSERR; + } + size += sizeof (struct GNUNET_TUN_IPv4Header) + 8; + GNUNET_assert (8 == sizeof (struct GNUNET_TUN_UdpHeader)); + make_up_icmpv4_payload (ts, ipp, udp); + } + break; + case GNUNET_TUN_ICMPTYPE6_PACKET_TOO_BIG: + case GNUNET_TUN_ICMPTYPE6_PARAMETER_PROBLEM: + GNUNET_STATISTICS_update (stats, + gettext_noop ("# ICMPv6 packets dropped (impossible PT to v4)"), + 1, GNUNET_NO); + return GNUNET_OK; + case GNUNET_TUN_ICMPTYPE6_ECHO_REQUEST: + icmp->type = GNUNET_TUN_ICMPTYPE_ECHO_REQUEST; + break; + case GNUNET_TUN_ICMPTYPE6_ECHO_REPLY: + icmp->type = GNUNET_TUN_ICMPTYPE_ECHO_REPLY; + break; + default: + GNUNET_break_op (0); + GNUNET_STATISTICS_update (stats, + gettext_noop ("# ICMPv6 packets dropped (type not allowed)"), + 1, GNUNET_NO); + return GNUNET_SYSERR; + } + /* end AF_INET6 */ + break; + default: + GNUNET_break_op (0); + return GNUNET_SYSERR; + } + msg->size = htons (size); GNUNET_TUN_calculate_icmp_checksum (icmp, &i2v[1], mlen); @@ -1700,13 +1840,12 @@ receive_icmp_back (void *cls GNUNET_UNUSED, struct GNUNET_MESH_Tunnel *tunnel, sizeof (struct GNUNET_TUN_Layer2PacketHeader) + mlen; { - char buf[size]; + char buf[size + sizeof (struct GNUNET_TUN_IPv6Header) + 8]; 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_IcmpHeader *icmp = (struct GNUNET_TUN_IcmpHeader *) &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, @@ -1718,7 +1857,108 @@ receive_icmp_back (void *cls GNUNET_UNUSED, struct GNUNET_MESH_Tunnel *tunnel, memcpy (&icmp[1], &i2v[1], mlen); - /* FIXME: for some ICMP types, we need to adjust the payload here... */ + + /* For some ICMP types, we need to adjust (make up) the payload here. + Also, depending on the AF used on the other side, we have to + do ICMP PT (translate ICMP types) */ + switch (ntohl (i2v->af)) + { + case AF_INET: + /* ICMP PT 4-to-6 and possibly making up payloads */ + switch (icmp->type) + { + case GNUNET_TUN_ICMPTYPE_ECHO_REPLY: + icmp->type = GNUNET_TUN_ICMPTYPE6_ECHO_REPLY; + break; + case GNUNET_TUN_ICMPTYPE_ECHO_REQUEST: + icmp->type = GNUNET_TUN_ICMPTYPE6_ECHO_REQUEST; + break; + case GNUNET_TUN_ICMPTYPE_DESTINATION_UNREACHABLE: + icmp->type = GNUNET_TUN_ICMPTYPE6_DESTINATION_UNREACHABLE; + { + struct GNUNET_TUN_IPv6Header *ipp = (struct GNUNET_TUN_IPv6Header *) &icmp[1]; + struct GNUNET_TUN_UdpHeader *udp = (struct GNUNET_TUN_UdpHeader *) &ipp[1]; + + if (mlen != 0) + { + /* sender did not strip ICMP payload? */ + GNUNET_break_op (0); + return GNUNET_SYSERR; + } + size += sizeof (struct GNUNET_TUN_IPv6Header) + 8; + GNUNET_assert (8 == sizeof (struct GNUNET_TUN_UdpHeader)); + make_up_icmpv6_payload (ts, ipp, udp); + } + break; + case GNUNET_TUN_ICMPTYPE_TIME_EXCEEDED: + icmp->type = GNUNET_TUN_ICMPTYPE6_TIME_EXCEEDED; + { + struct GNUNET_TUN_IPv6Header *ipp = (struct GNUNET_TUN_IPv6Header *) &icmp[1]; + struct GNUNET_TUN_UdpHeader *udp = (struct GNUNET_TUN_UdpHeader *) &ipp[1]; + + if (mlen != 0) + { + /* sender did not strip ICMP payload? */ + GNUNET_break_op (0); + return GNUNET_SYSERR; + } + size += sizeof (struct GNUNET_TUN_IPv6Header) + 8; + GNUNET_assert (8 == sizeof (struct GNUNET_TUN_UdpHeader)); + make_up_icmpv6_payload (ts, ipp, udp); + } + break; + case GNUNET_TUN_ICMPTYPE_SOURCE_QUENCH: + GNUNET_STATISTICS_update (stats, + gettext_noop ("# ICMPv4 packets dropped (impossible PT to v6)"), + 1, GNUNET_NO); + return GNUNET_OK; + default: + GNUNET_break_op (0); + GNUNET_STATISTICS_update (stats, + gettext_noop ("# ICMPv4 packets dropped (type not allowed)"), + 1, GNUNET_NO); + return GNUNET_SYSERR; + } + /* end AF_INET */ + break; + case AF_INET6: + switch (icmp->type) + { + case GNUNET_TUN_ICMPTYPE6_DESTINATION_UNREACHABLE: + case GNUNET_TUN_ICMPTYPE6_TIME_EXCEEDED: + case GNUNET_TUN_ICMPTYPE6_PACKET_TOO_BIG: + case GNUNET_TUN_ICMPTYPE6_PARAMETER_PROBLEM: + { + struct GNUNET_TUN_IPv6Header *ipp = (struct GNUNET_TUN_IPv6Header *) &icmp[1]; + struct GNUNET_TUN_UdpHeader *udp = (struct GNUNET_TUN_UdpHeader *) &ipp[1]; + + if (mlen != 0) + { + /* sender did not strip ICMP payload? */ + GNUNET_break_op (0); + return GNUNET_SYSERR; + } + size += sizeof (struct GNUNET_TUN_IPv6Header) + 8; + GNUNET_assert (8 == sizeof (struct GNUNET_TUN_UdpHeader)); + make_up_icmpv6_payload (ts, ipp, udp); + } + break; + case GNUNET_TUN_ICMPTYPE6_ECHO_REQUEST: + break; + default: + GNUNET_break_op (0); + GNUNET_STATISTICS_update (stats, + gettext_noop ("# ICMPv6 packets dropped (type not allowed)"), + 1, GNUNET_NO); + return GNUNET_SYSERR; + } + /* end AF_INET6 */ + break; + default: + GNUNET_break_op (0); + return GNUNET_SYSERR; + } + msg->size = htons (size); GNUNET_TUN_calculate_icmp_checksum (icmp, &i2v[1], mlen); (void) GNUNET_HELPER_send (helper_handle, -- 2.25.1