mirror of
https://code.semirocket.science/wrapsix
synced 2024-12-22 04:20:18 +02:00
Generalised translation of ICMPv4 error packets
This commit is contained in:
parent
d1a25aee7f
commit
c9c167871c
427
src/icmp.c
427
src/icmp.c
@ -32,6 +32,11 @@
|
||||
#include "udp.h"
|
||||
#include "wrapper.h"
|
||||
|
||||
/* declarations */
|
||||
int sub_icmp4_error(unsigned char *payload, unsigned short payload_size,
|
||||
unsigned char *packet, unsigned short *packet_len,
|
||||
struct s_icmp **icmp, struct s_nat **connection);
|
||||
|
||||
/**
|
||||
* Processing of incoming ICMPv4 packets. Directly sends translated ICMPv6
|
||||
* packets.
|
||||
@ -61,14 +66,10 @@ int icmp_ipv4(struct s_ethernet *eth4, struct s_ipv4 *ip4,
|
||||
struct s_ipv6 *ip6;
|
||||
|
||||
/* for error messages */
|
||||
unsigned short payload_size_left, new_len;
|
||||
struct s_ipv4 *eip4;
|
||||
struct s_ipv6 *eip6;
|
||||
struct s_tcp *etcp;
|
||||
struct s_udp *eudp;
|
||||
struct s_icmp *eicmp;
|
||||
unsigned short eip4_hlen;
|
||||
struct s_ipv6_fragment *eip6_frag;
|
||||
unsigned short new_len = sizeof(struct s_ethernet) +
|
||||
sizeof(struct s_ipv6);
|
||||
|
||||
/* sanity check */
|
||||
if (payload_size < sizeof(struct s_icmp) + 4) {
|
||||
@ -112,208 +113,34 @@ int icmp_ipv4(struct s_ethernet *eth4, struct s_ipv4 *ip4,
|
||||
/* override information in original ICMP header */
|
||||
icmp->type = ICMPV6_ECHO_REPLY;
|
||||
|
||||
new_len = sizeof(struct s_ethernet) +
|
||||
sizeof(struct s_ipv6);
|
||||
|
||||
/* copy the payload data */
|
||||
payload_size_left = payload_size >
|
||||
MTU - sizeof(struct s_ipv6) ?
|
||||
MTU - sizeof(struct s_ipv6) : payload_size;
|
||||
memcpy(&packet[new_len], payload, payload_size_left);
|
||||
icmp = (struct s_icmp *) &packet[new_len];
|
||||
new_len += payload_size_left;
|
||||
if (payload_size < MTU - sizeof(struct s_ipv6)) {
|
||||
memcpy(&packet[new_len], payload, payload_size);
|
||||
icmp = (struct s_icmp *) &packet[new_len];
|
||||
new_len += payload_size;
|
||||
} else {
|
||||
memcpy(&packet[new_len], payload, MTU -
|
||||
sizeof(struct s_ipv6));
|
||||
icmp = (struct s_icmp *) &packet[new_len];
|
||||
new_len += MTU - sizeof(struct s_ipv6);
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case ICMPV4_DST_UNREACHABLE:
|
||||
if (icmp->code <= 13 || icmp->code == 15) {
|
||||
/* sanity check */
|
||||
payload_size_left = payload_size -
|
||||
sizeof(struct s_icmp) - 4;
|
||||
if (payload_size_left < 1) {
|
||||
log_debug("Too short ICMPv4 packet 2");
|
||||
if (sub_icmp4_error(icmp_data + 4,
|
||||
payload_size - sizeof(struct s_icmp) - 4,
|
||||
&packet[0], &new_len, &icmp, &connection)) {
|
||||
/* something went wrong */
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* skip unused space */
|
||||
icmp_data += 4;
|
||||
|
||||
/* parse IPv4 header */
|
||||
eip4 = (struct s_ipv4 *) icmp_data;
|
||||
|
||||
/* # of 4 byte words */
|
||||
eip4_hlen = (eip4->ver_hdrlen & 0x0f) * 4;
|
||||
|
||||
/* sanity check */
|
||||
if (payload_size_left < eip4_hlen + 4) {
|
||||
log_debug("Too short ICMPv4 packet 3");
|
||||
return 1;
|
||||
}
|
||||
/* 4 B -> L4 addrs */
|
||||
payload_size_left -= eip4_hlen + 4;
|
||||
|
||||
icmp_data += eip4_hlen;
|
||||
|
||||
new_len = sizeof(struct s_ethernet) +
|
||||
sizeof(struct s_ipv6);
|
||||
|
||||
/* define new inner IPv6 header */
|
||||
/* new_len+4+4+40=102 < 1280 */
|
||||
eip6 = (struct s_ipv6 *) &packet[new_len +
|
||||
eip4 = (struct s_ipv4 *) (icmp_data + 4);
|
||||
eip6 = (struct s_ipv6 *) &packet[
|
||||
sizeof(struct s_ethernet) +
|
||||
sizeof(struct s_ipv6) +
|
||||
sizeof(struct s_icmp) + 4];
|
||||
/* we'll need this right now */
|
||||
ipv4_to_ipv6(&eip4->ip_dest, &eip6->ip_dest);
|
||||
|
||||
/* look for the original connection */
|
||||
switch (eip4->proto) {
|
||||
case IPPROTO_TCP:
|
||||
etcp = (struct s_tcp *)
|
||||
icmp_data;
|
||||
connection = nat_in(nat4_tcp,
|
||||
eip4->ip_dest,
|
||||
etcp->port_dest,
|
||||
etcp->port_src);
|
||||
|
||||
if (connection == NULL) {
|
||||
log_debug("Incoming "
|
||||
"TCP error "
|
||||
"connection "
|
||||
"wasn't found "
|
||||
"in NAT");
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* fix port for local client */
|
||||
etcp->port_src = connection->
|
||||
ipv6_port_src;
|
||||
|
||||
break;
|
||||
|
||||
case IPPROTO_UDP:
|
||||
eudp = (struct s_udp *)
|
||||
icmp_data;
|
||||
connection = nat_in(nat4_udp,
|
||||
eip4->ip_dest,
|
||||
eudp->port_dest,
|
||||
eudp->port_src);
|
||||
|
||||
if (connection == NULL) {
|
||||
log_debug("Incoming "
|
||||
"UDP error "
|
||||
"connection "
|
||||
"wasn't found "
|
||||
"in NAT");
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* fix port for local client */
|
||||
eudp->port_src = connection->
|
||||
ipv6_port_src;
|
||||
|
||||
break;
|
||||
|
||||
case IPPROTO_ICMP:
|
||||
eicmp = (struct s_icmp *)
|
||||
icmp_data;
|
||||
|
||||
/* we translate only echo
|
||||
* requests so handle only them
|
||||
* here too */
|
||||
if (eicmp->type !=
|
||||
ICMPV4_ECHO_REQUEST) {
|
||||
log_debug("Unknown ICMP"
|
||||
" type within "
|
||||
"ICMP error");
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* else: */
|
||||
|
||||
/* sanity check */
|
||||
if (payload_size_left < 4) {
|
||||
log_debug("Too short "
|
||||
"ICMPv4 packet "
|
||||
"4");
|
||||
return 1;
|
||||
}
|
||||
|
||||
echo = (struct s_icmp_echo *)
|
||||
(icmp_data +
|
||||
sizeof(struct s_icmp));
|
||||
|
||||
connection = nat_in(nat4_icmp,
|
||||
eip4->ip_dest, 0,
|
||||
echo->id);
|
||||
|
||||
if (connection == NULL) {
|
||||
log_debug("Incoming "
|
||||
"ICMP error "
|
||||
"connection "
|
||||
"wasn't found "
|
||||
"in NAT");
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* fix port for local client */
|
||||
echo->id = connection->
|
||||
ipv6_port_src;
|
||||
|
||||
/* adjust ICMP type */
|
||||
eicmp->type =
|
||||
ICMPV6_ECHO_REQUEST;
|
||||
|
||||
break;
|
||||
|
||||
default:
|
||||
/* we don't know where to send
|
||||
* it */
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* copy ICMP header to new packet */
|
||||
memcpy(&packet[new_len], icmp,
|
||||
sizeof(struct s_icmp) + 4);
|
||||
icmp = (struct s_icmp *) &packet[new_len];
|
||||
|
||||
new_len += sizeof(struct s_icmp) + 4;
|
||||
|
||||
/* complete inner IPv6 header */
|
||||
eip6->ver = 0x60 | (eip4->tos >> 4);
|
||||
eip6->traffic_class = eip4->tos << 4;
|
||||
eip6->flow_label = 0x0;
|
||||
eip6->hop_limit = eip4->ttl;
|
||||
if (eip4->proto != IPPROTO_ICMP) {
|
||||
eip6->next_header = eip4->proto;
|
||||
} else {
|
||||
eip6->next_header = IPPROTO_ICMPV6;
|
||||
}
|
||||
eip6->ip_src = connection->ipv6;
|
||||
|
||||
new_len += sizeof(struct s_ipv6);
|
||||
|
||||
/* was the IPv4 packet fragmented? */
|
||||
if ((eip4->flags_offset & htons(0x1fff)) !=
|
||||
0x0000) {
|
||||
/* original length of error packet,
|
||||
* but without IP header, but with
|
||||
* fragment header(!) */
|
||||
eip6->len = htons(ntohs(eip4->len) -
|
||||
eip4_hlen +
|
||||
sizeof(struct s_ipv6_fragment));
|
||||
|
||||
eip6_frag = (struct s_ipv6_fragment *)
|
||||
&packet[new_len];
|
||||
eip6_frag->next_header =
|
||||
eip6->next_header;
|
||||
eip6->next_header = IPPROTO_FRAGMENT;
|
||||
eip6_frag->id = htonl(ntohs(eip4->id));
|
||||
|
||||
new_len +=
|
||||
sizeof(struct s_ipv6_fragment);
|
||||
} else {
|
||||
eip6->len = htons(ntohs(eip4->len) -
|
||||
eip4_hlen);
|
||||
}
|
||||
|
||||
/* translate ICMP type&code */
|
||||
if (icmp->code == 2) {
|
||||
@ -382,24 +209,6 @@ int icmp_ipv4(struct s_ethernet *eth4, struct s_ipv4 *ip4,
|
||||
icmp->code = 0;
|
||||
}
|
||||
|
||||
/* copy payload, aligned to MTU */
|
||||
/* we can afford to use full MTU instead of
|
||||
* just 1280 B as admin warrants this to us */
|
||||
if (payload_size - sizeof(struct s_icmp) - 4 -
|
||||
eip4_hlen > (unsigned int) MTU - new_len) {
|
||||
memcpy(&packet[new_len], icmp_data,
|
||||
MTU - new_len);
|
||||
new_len = MTU;
|
||||
} else {
|
||||
memcpy(&packet[new_len], icmp_data,
|
||||
payload_size -
|
||||
sizeof(struct s_icmp) - 4 -
|
||||
eip4_hlen);
|
||||
new_len += payload_size -
|
||||
sizeof(struct s_icmp) - 4 -
|
||||
eip4_hlen;
|
||||
}
|
||||
|
||||
/* new packet is almost finished, yay! */
|
||||
|
||||
break;
|
||||
@ -425,7 +234,8 @@ int icmp_ipv4(struct s_ethernet *eth4, struct s_ipv4 *ip4,
|
||||
ip6->ver = 0x60 | (ip4->tos >> 4);
|
||||
ip6->traffic_class = ip4->tos << 4;
|
||||
ip6->flow_label = 0x0;
|
||||
ip6->len = htons(new_len - sizeof(struct s_ethernet) - sizeof(struct s_ipv6));
|
||||
ip6->len = htons(new_len - sizeof(struct s_ethernet) -
|
||||
sizeof(struct s_ipv6));
|
||||
ip6->next_header = IPPROTO_ICMPV6;
|
||||
ip6->hop_limit = ip4->ttl;
|
||||
ipv4_to_ipv6(&ip4->ip_src, &ip6->ip_src);
|
||||
@ -818,3 +628,186 @@ int icmp6_error(struct s_mac_addr mac_dest, struct s_ipv6_addr ip_dest,
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper function for translating inner packet in ICMPv4 error.
|
||||
*
|
||||
* @param payload Original inner packet data
|
||||
* @param payload_size Length of original data
|
||||
* @param packet Space for translated packet data
|
||||
* @param packet_len Length of translated data
|
||||
* @param icmp Outer ICMP header
|
||||
* @param connection NAT connection for inner packet
|
||||
*
|
||||
* @return 0 for success
|
||||
* @return 1 for failure
|
||||
*/
|
||||
int sub_icmp4_error(unsigned char *payload, unsigned short payload_size,
|
||||
unsigned char *packet, unsigned short *packet_len,
|
||||
struct s_icmp **icmp, struct s_nat **connection)
|
||||
{
|
||||
struct s_ipv4 *eip4;
|
||||
struct s_ipv6 *eip6;
|
||||
struct s_tcp *etcp;
|
||||
struct s_udp *eudp;
|
||||
struct s_icmp *eicmp;
|
||||
unsigned short eip4_hlen;
|
||||
struct s_ipv6_fragment *eip6_frag;
|
||||
struct s_icmp_echo *echo;
|
||||
|
||||
/* sanity check */
|
||||
if (payload_size < 1) {
|
||||
log_debug("Too short ICMPv4 packet 2");
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* parse IPv4 header */
|
||||
eip4 = (struct s_ipv4 *) payload;
|
||||
|
||||
/* # of 4 byte words */
|
||||
eip4_hlen = (eip4->ver_hdrlen & 0x0f) * 4;
|
||||
|
||||
/* sanity check */
|
||||
if (payload_size < eip4_hlen + 4) {
|
||||
log_debug("Too short ICMPv4 packet 3");
|
||||
return 1;
|
||||
}
|
||||
/* 4 B -> L4 addrs */
|
||||
payload_size -= eip4_hlen;
|
||||
|
||||
payload += eip4_hlen;
|
||||
|
||||
/* define new inner IPv6 header */
|
||||
/* new_len+4+4+40=102 < 1280 */
|
||||
eip6 = (struct s_ipv6 *) &packet[*packet_len + sizeof(struct s_icmp) +
|
||||
4];
|
||||
/* we'll need this right now */
|
||||
ipv4_to_ipv6(&eip4->ip_dest, &eip6->ip_dest);
|
||||
|
||||
/* look for the original connection */
|
||||
switch (eip4->proto) {
|
||||
case IPPROTO_TCP:
|
||||
etcp = (struct s_tcp *) payload;
|
||||
*connection = nat_in(nat4_tcp, eip4->ip_dest,
|
||||
etcp->port_dest, etcp->port_src);
|
||||
|
||||
if (*connection == NULL) {
|
||||
log_debug("Incoming TCP error connection "
|
||||
"wasn't found in NAT");
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* fix port for local client */
|
||||
etcp->port_src = (*connection)->ipv6_port_src;
|
||||
|
||||
break;
|
||||
|
||||
case IPPROTO_UDP:
|
||||
eudp = (struct s_udp *) payload;
|
||||
*connection = nat_in(nat4_udp, eip4->ip_dest,
|
||||
eudp->port_dest, eudp->port_src);
|
||||
|
||||
if (*connection == NULL) {
|
||||
log_debug("Incoming UDP error connection "
|
||||
"wasn't found in NAT");
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* fix port for local client */
|
||||
eudp->port_src = (*connection)->ipv6_port_src;
|
||||
|
||||
break;
|
||||
|
||||
case IPPROTO_ICMP:
|
||||
eicmp = (struct s_icmp *) payload;
|
||||
|
||||
/* we translate only echo requests so handle only them
|
||||
* here too */
|
||||
if (eicmp->type != ICMPV4_ECHO_REQUEST) {
|
||||
log_debug("Unknown ICMP type within ICMP "
|
||||
"error");
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* else: */
|
||||
|
||||
/* sanity check */
|
||||
if (payload_size < 8) {
|
||||
log_debug("Too short ICMPv4 packet 4");
|
||||
return 1;
|
||||
}
|
||||
|
||||
echo = (struct s_icmp_echo *) (payload +
|
||||
sizeof(struct s_icmp));
|
||||
|
||||
*connection = nat_in(nat4_icmp, eip4->ip_dest, 0,
|
||||
echo->id);
|
||||
|
||||
if (*connection == NULL) {
|
||||
log_debug("Incoming ICMP error connection "
|
||||
"wasn't found in NAT");
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* fix port for local client */
|
||||
echo->id = (*connection)->ipv6_port_src;
|
||||
|
||||
/* adjust ICMP type */
|
||||
eicmp->type = ICMPV6_ECHO_REQUEST;
|
||||
|
||||
break;
|
||||
|
||||
default:
|
||||
/* we don't know where to send it */
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* copy ICMP header to new packet */
|
||||
memcpy(&packet[*packet_len], *icmp, sizeof(struct s_icmp) + 4);
|
||||
*icmp = (struct s_icmp *) &packet[*packet_len];
|
||||
|
||||
/* complete inner IPv6 header */
|
||||
eip6->ver = 0x60 | (eip4->tos >> 4);
|
||||
eip6->traffic_class = eip4->tos << 4;
|
||||
eip6->flow_label = 0x0;
|
||||
eip6->hop_limit = eip4->ttl;
|
||||
if (eip4->proto != IPPROTO_ICMP) {
|
||||
eip6->next_header = eip4->proto;
|
||||
} else {
|
||||
eip6->next_header = IPPROTO_ICMPV6;
|
||||
}
|
||||
eip6->ip_src = (*connection)->ipv6;
|
||||
|
||||
*packet_len += sizeof(struct s_icmp) + 4 + sizeof(struct s_ipv6);
|
||||
|
||||
/* was the IPv4 packet fragmented? */
|
||||
if ((eip4->flags_offset & htons(0x1fff)) != 0x0000) {
|
||||
/* original length of error packet,
|
||||
* but without IP header, but with
|
||||
* fragment header(!) */
|
||||
eip6->len = htons(ntohs(eip4->len) - eip4_hlen +
|
||||
sizeof(struct s_ipv6_fragment));
|
||||
|
||||
eip6_frag = (struct s_ipv6_fragment *) &packet[*packet_len];
|
||||
eip6_frag->next_header = eip6->next_header;
|
||||
eip6->next_header = IPPROTO_FRAGMENT;
|
||||
eip6_frag->id = htonl(ntohs(eip4->id));
|
||||
|
||||
*packet_len += sizeof(struct s_ipv6_fragment);
|
||||
} else {
|
||||
eip6->len = htons(ntohs(eip4->len) - eip4_hlen);
|
||||
}
|
||||
|
||||
/* copy payload, aligned to MTU */
|
||||
/* we can afford to use full MTU instead of just 1280 B as admin
|
||||
* warrants this to us */
|
||||
if (payload_size > (unsigned int) MTU - *packet_len) {
|
||||
memcpy(&packet[*packet_len], payload, MTU - *packet_len);
|
||||
*packet_len = MTU;
|
||||
} else {
|
||||
memcpy(&packet[*packet_len], payload, payload_size);
|
||||
*packet_len += payload_size;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user