tImplement ICMP echo reply - icmphop - Add hops in ipv6 traceroute
 (HTM) git clone git://git.z3bra.org/icmphop.git
 (DIR) Log
 (DIR) Files
 (DIR) Refs
 (DIR) README
       ---
 (DIR) commit a17a160c01dceabd7e112266fe5566b774077c47
 (DIR) parent f2fc653a824358e5317b9900a1991cd82f462ae0
 (HTM) Author: Willy Goiffon <dev@z3bra.org>
       Date:   Thu, 13 Oct 2022 16:06:05 +0200
       
       Implement ICMP echo reply
       
       Diffstat:
         M icmphop.go                          |     131 +++++++++++++++++++------------
       
       1 file changed, 81 insertions(+), 50 deletions(-)
       ---
 (DIR) diff --git a/icmphop.go b/icmphop.go
       t@@ -1,5 +1,12 @@
        /*
       - * Copy me if you can.
       + * Insert hops in ipv6 traceroute
       + *
       + * Create a tunnel interface to send back ICMP timeout exceeded error
       + * messages when targetting a specific ipv6 address.
       + *
       + * In order to add hops, the destination address' last byte must be the
       + * TTL number
       + *
         * by wgs
         */
        
       t@@ -18,7 +25,7 @@ import (
        
        const TUN_HEADER = 4
        const IPV6_HEADER = 40
       -const ICMP_HEADER = 8
       +const ICMP_HEADER = 4
        
        /* compute IPv6 checksum for a given packet */
        func checksum(body []byte, srcIP, dstIP net.IP) (crc []byte) {
       t@@ -55,10 +62,7 @@ func ipv6_header(src, dst net.IP, len uint16) []byte {
                header := make([]byte, IPV6_HEADER)
        
                // Packet version (ipv6), Traffic class and Flow label
       -        header[0] = 0x60
       -        header[1] = 0x00
       -        header[2] = 0x00
       -        header[3] = 0x00
       +        copy(header[:4], []byte{0x60, 0, 0, 0})
        
                sz := new(bytes.Buffer)
                binary.Write(sz, binary.BigEndian, len)
       t@@ -74,22 +78,22 @@ func ipv6_header(src, dst net.IP, len uint16) []byte {
        }
        
        func icmp_message(icmp_type, icmp_code uint8, src, dst net.IP, payload []byte) []byte {
       -        len := len(payload)
       -        message := make([]byte, ICMP_HEADER+len)
       +        message := make([]byte, ICMP_HEADER)
        
                message[0] = icmp_type
                message[1] = icmp_code
        
       -        // Reserved bytes
       -        message[2] = 0x00
       -        message[3] = 0x00
       -        message[4] = 0x00
       -        message[5] = 0x00
       -        message[6] = 0x00
       -        message[7] = 0x00
       -
       -        // Copy initial packet as return packet payload
       -        copy(message[ICMP_HEADER:], payload)
       +        switch icmp_type {
       +        case 1: // Destination unreachable
       +                fallthrough
       +        case 3: // Time Exceeded
       +                message = append(message, 0, 0, 0, 0) // unused
       +                message = append(message, payload...)
       +        case 129: // Echo reply
       +                // payload must include identifier + seqnum
       +                // from original message
       +                message = append(message, payload...)
       +        }
        
                crc := checksum(message, src, dst)
                message[2] = crc[0]
       t@@ -114,62 +118,89 @@ func main() {
                hops := uint8(*hop)
        
                for {
       +                var icmp, ipv6 []byte
       +
       +                src := make([]byte, 16)
       +                dst := make([]byte, 16)
                        buf := make([]byte, *mtu+TUN_HEADER)
       -                plen, err := tun.Read(buf, TUN_HEADER)
       +                sz, err := tun.Read(buf, TUN_HEADER)
                        if err != nil {
                                log.Fatalf("Read %s: %s", *ifname, err.Error())
                        }
        
                        // Invalid packet, ignore it
       -                if plen < IPV6_HEADER {
       +                if sz < IPV6_HEADER {
                                continue
                        }
        
       -                // ICMP error message size is limited to 576 bytes.
       -                // Shring payload size to its bare minimum of 8 bytes in such case.
       -                if plen > 576 {
       -                        plen = TUN_HEADER + IPV6_HEADER + ICMP_HEADER + 8
       -                }
       +                packet := buf[TUN_HEADER : TUN_HEADER+sz]
        
       -                packet := buf[TUN_HEADER : TUN_HEADER+plen]
       -
       -                // Only handle destination address whose last byte ends
       -                // with the number of hops requested
       -                if packet[39] != 0x00+hops {
       +                // Skip packet if the specified number of hops cannot
       +                // be inserted by only changing the last byte
       +                if packet[39] < hops {
                                continue
                        }
        
                        ttl := uint8(packet[7])
        
       -                if ttl < hops+1 {
       -                        var icmp, ipv6 []byte
       -
       -                        src := make([]byte, 16)
       -                        dst := make([]byte, 16)
       +                // Invert source and destination address
       +                copy(dst, packet[8:8+16])
       +                copy(src, packet[24:24+16])
        
       -                        // Invert source and destination address
       -                        copy(dst, packet[8:8+16])
       -                        copy(src, packet[24:24+16])
       -                        src[15] = 0x00 + ttl // Reuse ttl as the last byte of source address
       +                // If TTL is lower than the configured number of hops,
       +                // start sending ICMP time exceeded replies to the
       +                // originating source.
       +                if ttl < hops+1 {
       +                        // Use hexa representation of TTL as the
       +                        // last byte of source address, thus
       +                        // incrementing hops until final destination
       +                        // is reached
       +                        src[15] = src[15] - hops + ttl
       +
       +                        // ICMP error must fit in 1280 bytes (ipv6 min. mtu)
       +                        if sz+IPV6_HEADER+ICMP_HEADER > 1280 {
       +                                sz = 1228
       +                        }
        
       -                        ipv6 = ipv6_header(src, dst, uint16(plen+8))
       +                        ipv6 = ipv6_header(src, dst, uint16(sz+ICMP_HEADER+4))
        
                                if ttl < hops {
       -                                icmp = icmp_message(0x03, 0x00, src, dst, packet)
       +                                // Time exceeded
       +                                icmp = icmp_message(3, 0, src, dst, packet[:sz])
                                } else {
       -                                icmp = icmp_message(0x01, 0x04, src, dst, packet)
       +                                // Destination port unreachable
       +                                icmp = icmp_message(1, 4, src, dst, packet[:sz])
       +                        }
       +                } else {
       +                        // Skip everything not ICMPv6
       +                        if packet[6] != 58 {
       +                                continue
                                }
        
       -                        // Construct full return packet
       -                        b := make([]byte, TUN_HEADER)
       -                        b = append(b, ipv6...)
       -                        b = append(b, icmp...)
       -
       -                        if *verbose == true {
       -                                log.Printf("%s %s > %s ICMP [%d:%d]", *ifname, net.IP(src).String(), net.IP(dst).String(), icmp[0], icmp[1])
       +                        switch packet[IPV6_HEADER] {
       +                        case 128: // ICMPv6 echo request
       +                                ipv6 = ipv6_header(src, dst, uint16(sz-IPV6_HEADER))
       +                                icmp = icmp_message(129, 0, src, dst, packet[IPV6_HEADER+4:])
       +                        default:
       +                                continue
                                }
       +                }
        
       -                        tun.Write(b, TUN_HEADER)
       +                // Construct final return packet
       +                b := make([]byte, TUN_HEADER)
       +                b = append(b, ipv6...)
       +                b = append(b, icmp...)
       +
       +                if *verbose == true {
       +                        log.Printf("%s %s > %s ICMP Type 0x%02x Code %d (%d bytes)",
       +                                *ifname,
       +                                net.IP(src).String(),
       +                                net.IP(dst).String(),
       +                                icmp[0],
       +                                icmp[1],
       +                                len(b))
                        }
       +
       +                tun.Write(b, TUN_HEADER)
                }
        }