summaryrefslogtreecommitdiffstats
path: root/translate.c
diff options
context:
space:
mode:
Diffstat (limited to 'translate.c')
-rw-r--r--translate.c49
1 files changed, 17 insertions, 32 deletions
diff --git a/translate.c b/translate.c
index 9a0f1b5..00ea0b9 100644
--- a/translate.c
+++ b/translate.c
@@ -208,10 +208,12 @@ int icmp_to_icmp6(clat_packet out, int pos, const struct icmphdr *icmp, uint32_t
// The pseudo-header checksum was calculated on the transport length of the original IPv4
// packet that we were asked to translate. This transport length is 20 bytes smaller than it
// needs to be, because the ICMP error contains an IPv4 header, which we will be translating to
- // an IPv6 header, which is 20 bytes longer. Fix it up here.
+ // an IPv6 header, which is 20 bytes longer. Fix it up here. This is simpler than the
+ // alternative, which is to always update the pseudo-header checksum in all UDP/TCP/ICMP
+ // translation functions (rather than pre-calculating it when translating the IPv4 header).
// We only need to do this for ICMP->ICMPv6, not ICMPv6->ICMP, because ICMP does not use the
// pseudo-header when calculating its checksum (as the IPv4 header has its own checksum).
- checksum = checksum + htons(20);
+ checksum = htonl(ntohl(checksum) + 20);
} else if (icmp6_type == ICMP6_ECHO_REQUEST || icmp6_type == ICMP6_ECHO_REPLY) {
// Ping packet.
icmp6_targ->icmp6_id = icmp->un.echo.id;
@@ -296,12 +298,10 @@ int generic_packet(clat_packet out, int pos, const char *payload, size_t len) {
* takes a udp packet and sets it up for translation
* out - output packet
* udp - pointer to udp header in packet
- * old_sum - pseudo-header checksum of old header
- * new_sum - pseudo-header checksum of new header
+ * checksum - pseudo-header checksum
* len - size of ip payload
*/
-int udp_packet(clat_packet out, int pos, const struct udphdr *udp,
- uint32_t old_sum, uint32_t new_sum, size_t len) {
+int udp_packet(clat_packet out, int pos, const struct udphdr *udp, uint32_t checksum, size_t len) {
const char *payload;
size_t payload_size;
@@ -313,7 +313,7 @@ int udp_packet(clat_packet out, int pos, const struct udphdr *udp,
payload = (const char *) (udp + 1);
payload_size = len - sizeof(struct udphdr);
- return udp_translate(out, pos, udp, old_sum, new_sum, payload, payload_size);
+ return udp_translate(out, pos, udp, checksum, payload, payload_size);
}
/* function: tcp_packet
@@ -324,8 +324,7 @@ int udp_packet(clat_packet out, int pos, const struct udphdr *udp,
* len - size of ip payload
* returns: the highest position in the output clat_packet that's filled in
*/
-int tcp_packet(clat_packet out, int pos, const struct tcphdr *tcp,
- uint32_t old_sum, uint32_t new_sum, size_t len) {
+int tcp_packet(clat_packet out, int pos, const struct tcphdr *tcp, uint32_t checksum, size_t len) {
const char *payload;
size_t payload_size, header_size;
@@ -348,21 +347,20 @@ int tcp_packet(clat_packet out, int pos, const struct tcphdr *tcp,
payload = ((const char *) tcp) + header_size;
payload_size = len - header_size;
- return tcp_translate(out, pos, tcp, header_size, old_sum, new_sum, payload, payload_size);
+ return tcp_translate(out, pos, tcp, header_size, checksum, payload, payload_size);
}
/* function: udp_translate
* common between ipv4/ipv6 - setup checksum and send udp packet
* out - output packet
* udp - udp header
- * old_sum - pseudo-header checksum of old header
- * new_sum - pseudo-header checksum of new header
+ * checksum - pseudo-header checksum
* payload - tcp payload
* payload_size - size of payload
* returns: the highest position in the output clat_packet that's filled in
*/
-int udp_translate(clat_packet out, int pos, const struct udphdr *udp, uint32_t old_sum,
- uint32_t new_sum, const char *payload, size_t payload_size) {
+int udp_translate(clat_packet out, int pos, const struct udphdr *udp, uint32_t checksum,
+ const char *payload, size_t payload_size) {
struct udphdr *udp_targ = out[pos].iov_base;
memcpy(udp_targ, udp, sizeof(struct udphdr));
@@ -371,22 +369,8 @@ int udp_translate(clat_packet out, int pos, const struct udphdr *udp, uint32_t o
out[CLAT_POS_PAYLOAD].iov_base = (char *) payload;
out[CLAT_POS_PAYLOAD].iov_len = payload_size;
- if (udp_targ->check) {
- udp_targ->check = ip_checksum_adjust(udp->check, old_sum, new_sum);
- } else {
- // Zero checksums are special. RFC 768 says, "An all zero transmitted checksum value means that
- // the transmitter generated no checksum (for debugging or for higher level protocols that
- // don't care)." However, in IPv6 zero UDP checksums were only permitted by RFC 6935 (2013). So
- // for safety we recompute it.
- udp_targ->check = 0; // Checksum field must be 0 when calculating checksum.
- udp_targ->check = packet_checksum(new_sum, out, pos);
- }
-
- // RFC 768: "If the computed checksum is zero, it is transmitted as all ones (the equivalent
- // in one's complement arithmetic)."
- if (!udp_targ->check) {
- udp_targ->check = 0xffff;
- }
+ udp_targ->check = 0; // Checksum field must be 0 when calculating checksum.
+ udp_targ->check = packet_checksum(checksum, out, pos);
return CLAT_POS_PAYLOAD + 1;
}
@@ -405,7 +389,7 @@ int udp_translate(clat_packet out, int pos, const struct udphdr *udp, uint32_t o
* TODO: hosts without pmtu discovery - non DF packets will rely on fragmentation (unimplemented)
*/
int tcp_translate(clat_packet out, int pos, const struct tcphdr *tcp, size_t header_size,
- uint32_t old_sum, uint32_t new_sum, const char *payload, size_t payload_size) {
+ uint32_t checksum, const char *payload, size_t payload_size) {
struct tcphdr *tcp_targ = out[pos].iov_base;
out[pos].iov_len = header_size;
@@ -422,7 +406,8 @@ int tcp_translate(clat_packet out, int pos, const struct tcphdr *tcp, size_t hea
out[CLAT_POS_PAYLOAD].iov_base = (char *)payload;
out[CLAT_POS_PAYLOAD].iov_len = payload_size;
- tcp_targ->check = ip_checksum_adjust(tcp->check, old_sum, new_sum);
+ tcp_targ->check = 0; // Checksum field must be 0 when calculating checksum.
+ tcp_targ->check = packet_checksum(checksum, out, pos);
return CLAT_POS_PAYLOAD + 1;
}