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)
}
}