appneta / tcpreplay

Pcap editing and replay tools for *NIX and Windows - Users please download source from
http://tcpreplay.appneta.com/wiki/installation.html#downloads
1.17k stars 268 forks source link

[Feature] recalculate IPv6 Fragment header checksum #897

Open ChuckCottrill opened 1 month ago

ChuckCottrill commented 1 month ago

Overview

The tcpreplay program tcprewrite does not support IPv6 FRAG(mented) header checksum recalculation. The protocol 0x2c is not included in the switch statement, and thus a TCPEDIT_WARN is generated and the header checksum is not recalculated.

The option --fixcsum does not work against the original 11465.0-0.unknown_1.v6.pcap pcap (and others) due to how the protocol value supplied to do_checksum() is handled. The protocol is IPV6-FRAG(mented). There is a switch statement where the protocol value (== 0x2c) is not one of the recognized values. The tcprewrite code explicitly returns indication (TCPEDIT_WARN) that certain packets (both TCP and HTTP) do not have their checksum recalculated. Since the checksum needs to be correctly calculated, this incorrect checksum causes the pcap to fail testing.

Expected Behavior

An IPV6 FRAGmented packet should have correct checksum calculated when packet is rewritten.

Description of desired solution

Since tcpreplay does not support IPv6-FRAG(ment) header checksum recalculation. The protocol 0x2c is not included in the switch statement, and thus a TCPEDIT_WARN is generated and the header checksum is not recalculated.

What is desired is that the protocol 0x2c be recognized and the correct header checksum be calculated and replaced, rather than the warning TCPEDIT_WARN generated, and the generated pcap file not be corrupted with an invalid checksum.

Add the protocol 0x2c to the constants src/tcpr.h

#ifndef IPPROTO_TCP_V6FRAG
#define IPPROTO_TCP_V6FRAG 0x2c
#endif
#ifndef IPPROTO_HTTP_V6FRAG
#define IPPROTO_HTTP_V6FRAG 0x2c
#endif

And the protocol added to the switch statement in src/tcpedit/checksum.c

        case IPPROTO_TCP:
        case IPPROTO_TCP_V6FRAG:
        // case IPPROTO_HTTP_V6FRAG:
protoname = "IPPROTO_TCP";
            if (len < (int)sizeof(tcp_hdr_t)) {
                tcpedit_setwarn(tcpedit, "%s", "Unable to checksum TCP with insufficient L4 data");
                return TCPEDIT_WARN;
            }

            tcp = (tcp_hdr_t *)(data + ip_hl);
#ifdef STUPID_SOLARIS_CHECKSUM_BUG
            tcp->th_sum = tcp->th_off << 2;
            return (TCPEDIT_OK);
#endif
            tcp->th_sum = 0;

            /* Note, we do both src & dst IP's at the same time, that's why the
             * length is 2x a single IP
             */
            if (ipv6 != NULL) {
                sum = do_checksum_math((uint16_t *)&ipv6->ip_src, 32);
            } else {
                sum = do_checksum_math((uint16_t *)&ipv4->ip_src, 8);
            }
            sum += ntohs(IPPROTO_TCP + len);
            sum += do_checksum_math((uint16_t *)tcp, len);
            tcp->th_sum = CHECKSUM_CARRY(sum);
            break;

see:

To Reproduce Steps to reproduce the behavior:

  1. have pcap with protocol == 0x2c (IPV6 FRAG(mented))
  2. run tcpprep on pcap
  3. run tcprewrite on pcap (change src/dest ip address to precipitate checksum recalculation)
  4. examine rewritten pcap in wireshark, observe that checksum is incorrect

Describe alternatives you've considered

The header checksum is not correctly rewritten by tcpreplay in this situation. Either:

a) do nothing (unacceptable), b) rewrite checksum using some external program, c) recalculate header checksum for these packets (this solution) d) ideas? suggestions? how else can we recalculate header checksums?

Additional context The TCP case can be fixed with a patch to allow protocol value 0x2c to be handled the same way as IPPROTO_TCP, but this patch does not work for (HTTP protocol). Further investigation is needed. Screenshots If applicable, add screenshots to help explain your problem.

System (please complete the following information):

Additional context Add any other context about the problem here.

ChuckCottrill commented 1 month ago

layer-2 checksum is correct. layer-4 (TCP) checksum is incorrect

IPv4 has checksum https://en.wikipedia.org/wiki/Internet_checksum

IPv6 does not have checksum https://networkengineering.stackexchange.com/questions/64803/why-does-the-ipv6-header-not-include-a-checksum

IPv4, IPv6 Headers https://notes.shichao.io/tcpv1/ch5/

RFC-1701 checksum https://www.packetmania.net/en/2021/12/26/IPv4-IPv6-checksum/

RFC-1141 incremental update checksum https://datatracker.ietf.org/doc/html/rfc1141

TCP pseudo-header http://www.tcpipguide.com/free/t_TCPChecksumCalculationandtheTCPPseudoHeader-2.htm

https://datatracker.ietf.org/doc/html/rfc1624

ChuckCottrill commented 3 days ago

see PR: https://github.com/ChuckCottrill/tcpreplay/tree/feat/hdr-checksum-ipv6-frag

ChuckCottrill commented 3 days ago

see issue: https://github.com/appneta/tcpreplay/issues/897