URI:
       tipv6 traceroute hops inserter - icmphop - Add hops in ipv6 traceroute
  HTML git clone git://git.z3bra.org/icmphop.git
   DIR Log
   DIR Files
   DIR Refs
   DIR README
       ---
   DIR commit f2fc653a824358e5317b9900a1991cd82f462ae0
  HTML Author: Willy Goiffon <dev@z3bra.org>
       Date:   Wed, 12 Oct 2022 17:37:16 +0200
       
       ipv6 traceroute hops inserter
       
       Diffstat:
         A README                              |      40 +++++++++++++++++++++++++++++++
         A config.mk                           |       5 +++++
         A go.mod                              |      13 +++++++++++++
         A icmphop.go                          |     175 +++++++++++++++++++++++++++++++
         A makefile                            |      19 +++++++++++++++++++
         A mkfile                              |      18 ++++++++++++++++++
       
       6 files changed, 270 insertions(+), 0 deletions(-)
       ---
   DIR diff --git a/README b/README
       t@@ -0,0 +1,40 @@
       +icmphop
       +=======
       +Insert hops in ipv6 traceroute
       +
       +The program will craft ICMP timeout exceeded error messages to effectively
       +add hops between a client and the target ipv6 address.
       +
       +This is of no use, besides printing funny stuff in traceroutes using
       +reverse DNS entries.
       +
       +Features
       +-----
       +- IPv6 only
       +- Insert up to 15 hops
       +
       +Usage
       +-----
       +
       +        # icmphop -n 5 -i hop0 &
       +        # ip -6 addr add fd00:c001:dad::1/96 dev hop0
       +        # ip link set hop0 up
       +        % tracepath -6 fd00:c001:dad::5
       +         1?: [LOCALHOST]              0.014ms pmtu 1500
       +         1:  fd00:c001:dad::1         0.108ms
       +         1:  fd00:c001:dad::1         0.081ms
       +         2:  fd00:c001:dad::2         0.091ms asymm  1
       +         3:  fd00:c001:dad::3         0.087ms asymm  1
       +         4:  fd00:c001:dad::4         0.082ms asymm  1
       +         5:  fd00:c001:dad::5         0.078ms reached
       +             Resume: pmtu 1500 hops 5 back 1
       +
       +The requested ipv6 address last byte must be the same as the number of
       +hops. For example, with 10 hops, use fd00:c001:dad:a.
       +
       +Installation
       +-----
       +Edit config.mk as needed, then build/install with the following commands:
       +
       +        make
       +        make install
   DIR diff --git a/config.mk b/config.mk
       t@@ -0,0 +1,5 @@
       +GO   = go
       +GOOS = `{uname -s | tr A-Z a-z}
       +
       +PREFIX = /usr/local
       +MANDIR = ${PREFIX}/man
   DIR diff --git a/go.mod b/go.mod
       t@@ -0,0 +1,13 @@
       +module z3bra.org/icmphop
       +
       +go 1.19
       +
       +require (
       +        golang.org/x/net v0.0.0-20221004154528-8021a29435af
       +        golang.zx2c4.com/wireguard v0.0.0-20220920152132-bb719d3a6e2c
       +)
       +
       +require (
       +        golang.org/x/sys v0.0.0-20220728004956-3c1f35247d10 // indirect
       +        golang.zx2c4.com/wintun v0.0.0-20211104114900-415007cec224 // indirect
       +)
   DIR diff --git a/icmphop.go b/icmphop.go
       t@@ -0,0 +1,175 @@
       +/*
       + * Copy me if you can.
       + * by wgs
       + */
       +
       +package main
       +
       +import (
       +        "bytes"
       +        "encoding/binary"
       +        "flag"
       +        "log"
       +        "net"
       +
       +        "golang.org/x/net/icmp"
       +        "golang.zx2c4.com/wireguard/tun"
       +)
       +
       +const TUN_HEADER = 4
       +const IPV6_HEADER = 40
       +const ICMP_HEADER = 8
       +
       +/* compute IPv6 checksum for a given packet */
       +func checksum(body []byte, srcIP, dstIP net.IP) (crc []byte) {
       +        out := make([]byte, 2)
       +        // from golang.org/x/net/icmp/message.go
       +        checksum := func(b []byte) uint16 {
       +                csumcv := len(b) - 1 // checksum coverage
       +                s := uint32(0)
       +                for i := 0; i < csumcv; i += 2 {
       +                        s += uint32(b[i+1])<<8 | uint32(b[i])
       +                }
       +                if csumcv&1 == 0 {
       +                        s += uint32(b[csumcv])
       +                }
       +                s = s>>16 + s&0xffff
       +                s = s + s>>16
       +                return ^uint16(s)
       +        }
       +
       +        b := body
       +        l := len(b)
       +        psh := icmp.IPv6PseudoHeader(srcIP, dstIP)
       +        b = append(psh, b...)
       +        off := 2 * net.IPv6len
       +        binary.BigEndian.PutUint32(b[off:off+4], uint32(l))
       +        s := checksum(b)
       +        out[0] ^= byte(s)
       +        out[1] ^= byte(s >> 8)
       +
       +        return out
       +}
       +
       +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
       +
       +        sz := new(bytes.Buffer)
       +        binary.Write(sz, binary.BigEndian, len)
       +        header[4] = sz.Bytes()[0] // Packet size
       +        header[5] = sz.Bytes()[1] // Packet size
       +        header[6] = 0x3a          // Next header (58, ICMPv6)
       +        header[7] = 0x40          // Hop Limit (64)
       +
       +        copy(header[8:], src[:16])
       +        copy(header[24:], dst[:16])
       +
       +        return header
       +}
       +
       +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[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)
       +
       +        crc := checksum(message, src, dst)
       +        message[2] = crc[0]
       +        message[3] = crc[1]
       +
       +        return message
       +}
       +
       +func main() {
       +        ifname := flag.String("i", "tun", "Interface name")
       +        mtu := flag.Int("m", 1500, "Interface MTU")
       +        hop := flag.Int("n", 4, "Hops number to insert")
       +        verbose := flag.Bool("v", false, "Enable verbose logging")
       +
       +        flag.Parse()
       +
       +        tun, err := tun.CreateTUN(*ifname, *mtu)
       +        if err != nil {
       +                log.Fatal(err)
       +        }
       +
       +        hops := uint8(*hop)
       +
       +        for {
       +                buf := make([]byte, *mtu+TUN_HEADER)
       +                plen, 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 {
       +                        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+plen]
       +
       +                // Only handle destination address whose last byte ends
       +                // with the number of hops requested
       +                if packet[39] != 0x00+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])
       +                        src[15] = 0x00 + ttl // Reuse ttl as the last byte of source address
       +
       +                        ipv6 = ipv6_header(src, dst, uint16(plen+8))
       +
       +                        if ttl < hops {
       +                                icmp = icmp_message(0x03, 0x00, src, dst, packet)
       +                        } else {
       +                                icmp = icmp_message(0x01, 0x04, src, dst, packet)
       +                        }
       +
       +                        // 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])
       +                        }
       +
       +                        tun.Write(b, TUN_HEADER)
       +                }
       +        }
       +}
   DIR diff --git a/makefile b/makefile
       t@@ -0,0 +1,19 @@
       +-include config.mk
       +
       +.SUFFIXES: .go
       +
       +all: icmphop
       +
       +.go:
       +        ${GO} build -o $@ $<
       +
       +clean:
       +        rm -f icmphop
       +
       +install: icmphop
       +        mkdir -p ${DESTDIR}${PREFIX}/bin
       +        cp icmphop ${DESTDIR}${PREFIX}/bin/icmphop
       +        chmod 755 ${DESTDIR}${PREFIX}/bin/icmphop
       +
       +uninstall:
       +        rm ${DESTDIR}${PREFIX}/bin/icmphop
   DIR diff --git a/mkfile b/mkfile
       t@@ -0,0 +1,18 @@
       +<config.mk
       +
       +all:V: icmphop
       +
       +%: %.go
       +        $GO build -o $stem $stem.go
       +
       +clean:V:
       +        rm -f icmphop
       +
       +install:V: icmphop
       +        mkdir -p ${DESTDIR}${PREFIX}/bin
       +        cp icmphop ${DESTDIR}${PREFIX}/bin/icmphop
       +        chmod 755 ${DESTDIR}${PREFIX}/bin/icmphop
       +        mkdir -p ${DESTDIR}${MANDIR}/man1
       +
       +uninstall:V:
       +        rm ${DESTDIR}${PREFIX}/bin/icmphop