diff --git a/net/run.go b/net/run.go
@@ -10,6 +10,7 @@ import (
"golang.org/x/net/icmp"
"golang.org/x/net/ipv4"
+ "golang.org/x/net/ipv6"
)
const (
@@ -20,9 +21,11 @@ const (
echoPayload = "tiny-traceroute-djnn-sh"
/* Network Protocols & Packet sizing */
- protocolICMP = 1
+ protocolICMPv4 = 1
+ protocolICMPv6 = 58
readBufferSize = 1500
minIPv4HeaderLen = 20
+ minIPv6HeaderLen = 40
icmpHeaderLen = 8
ipv4WordSize = 4 /* IHL is measured in 32-bit (4-byte) words */
ihlMask = 0x0f
@@ -56,7 +59,7 @@ func Run(target string, ipType string) {
for probe := 0; probe < probesPerHop; probe++ {
seq := (ttl * 100) + probe
- peerAddr, rtt, err := executeProbe(c, pc, destIP, traceID, seq, ttl)
+ peerAddr, rtt, err := executeProbe(c, pc, destIP, traceID, seq, ttl, ipType)
if err != nil {
fmt.Printf("* ")
} else {
@@ -77,7 +80,7 @@ func Run(target string, ipType string) {
}
}
-func setupSocket(ipVersion string) (*icmp.PacketConn, *ipv4.PacketConn, error) {
+func setupSocket(ipVersion string) (*icmp.PacketConn, interface{}, error) {
protocol := "ip4:icmp"
network := "0.0.0.0"
if ipVersion == "ip6" {
@@ -89,21 +92,36 @@ func setupSocket(ipVersion string) (*icmp.PacketConn, *ipv4.PacketConn, error) {
if err != nil {
return nil, nil, fmt.Errorf("listen packet: %w (ensure sudo/admin)", err)
}
+
+ if ipVersion == "ip6" {
+ return c, c.IPv6PacketConn(), nil
+ }
return c, c.IPv4PacketConn(), nil
}
-func executeProbe(c *icmp.PacketConn, pc *ipv4.PacketConn, dest *net.IPAddr, id, seq, ttl int) (string, time.Duration, error) {
- if err := pc.SetTTL(ttl); err != nil {
- return "", 0, err
+func executeProbe(c *icmp.PacketConn, pc interface{}, dest *net.IPAddr, id, seq, ttl int, ipType string) (string, time.Duration, error) {
+ // Set TTL based on IP version
+ if ipType == "ip6" {
+ if pc6, ok := pc.(*ipv6.PacketConn); ok {
+ if err := pc6.SetHopLimit(ttl); err != nil {
+ return "", 0, err
+ }
+ }
+ } else {
+ if pc4, ok := pc.(*ipv4.PacketConn); ok {
+ if err := pc4.SetTTL(ttl); err != nil {
+ return "", 0, err
+ }
+ }
}
start := time.Now()
- if err := sendEchoRequest(c, dest, id, seq); err != nil {
+ if err := sendEchoRequest(c, dest, id, seq, ipType); err != nil {
return "", 0, err
}
- peer, err := awaitResponse(pc, id, seq)
+ peer, err := awaitResponse(c, pc, id, seq, ipType)
if err != nil {
return "", 0, err
}
@@ -111,9 +129,16 @@ func executeProbe(c *icmp.PacketConn, pc *ipv4.PacketConn, dest *net.IPAddr, id,
return cleanIP(peer.String()), time.Since(start), nil
}
-func sendEchoRequest(c *icmp.PacketConn, dest net.Addr, id, seq int) error {
+func sendEchoRequest(c *icmp.PacketConn, dest net.Addr, id, seq int, ipType string) error {
+ var msgType icmp.Type
+ if ipType == "ip6" {
+ msgType = ipv6.ICMPTypeEchoRequest
+ } else {
+ msgType = ipv4.ICMPTypeEcho
+ }
+
msg := icmp.Message{
- Type: ipv4.ICMPTypeEcho, Code: 0,
+ Type: msgType, Code: 0,
Body: &icmp.Echo{
ID: id, Seq: seq,
Data: []byte(echoPayload),
@@ -127,32 +152,55 @@ func sendEchoRequest(c *icmp.PacketConn, dest net.Addr, id, seq int) error {
return err
}
-func awaitResponse(pc *ipv4.PacketConn, expectedID, expectedSeq int) (net.Addr, error) {
- pc.SetReadDeadline(time.Now().Add(defaultTimeout))
+func awaitResponse(c *icmp.PacketConn, pc interface{}, expectedID, expectedSeq int, ipType string) (net.Addr, error) {
+ c.SetReadDeadline(time.Now().Add(defaultTimeout))
b := make([]byte, readBufferSize)
+ protocol := protocolICMPv4
+ if ipType == "ip6" {
+ protocol = protocolICMPv6
+ }
+
for {
- n, _, peer, err := pc.ReadFrom(b)
+ n, peer, err := c.ReadFrom(b)
if err != nil {
return nil, err
}
- rm, err := icmp.ParseMessage(protocolICMP, b[:n])
+ rm, err := icmp.ParseMessage(protocol, b[:n])
if err != nil {
continue
}
- switch rm.Type {
- case ipv4.ICMPTypeEchoReply:
- if echo, ok := rm.Body.(*icmp.Echo); ok {
- if echo.ID == expectedID && echo.Seq == expectedSeq {
- return peer, nil
+ // Handle based on IP version
+ if ipType == "ip6" {
+ switch rm.Type {
+ case ipv6.ICMPTypeEchoReply:
+ if echo, ok := rm.Body.(*icmp.Echo); ok {
+ if echo.ID == expectedID && echo.Seq == expectedSeq {
+ return peer, nil
+ }
+ }
+ case ipv6.ICMPTypeTimeExceeded:
+ if te, ok := rm.Body.(*icmp.TimeExceeded); ok {
+ if matchOriginalPacketV6(te.Data, expectedID, expectedSeq) {
+ return peer, nil
+ }
}
}
- case ipv4.ICMPTypeTimeExceeded:
- if te, ok := rm.Body.(*icmp.TimeExceeded); ok {
- if matchOriginalPacket(te.Data, expectedID, expectedSeq) {
- return peer, nil
+ } else {
+ switch rm.Type {
+ case ipv4.ICMPTypeEchoReply:
+ if echo, ok := rm.Body.(*icmp.Echo); ok {
+ if echo.ID == expectedID && echo.Seq == expectedSeq {
+ return peer, nil
+ }
+ }
+ case ipv4.ICMPTypeTimeExceeded:
+ if te, ok := rm.Body.(*icmp.TimeExceeded); ok {
+ if matchOriginalPacket(te.Data, expectedID, expectedSeq) {
+ return peer, nil
+ }
}
}
}
@@ -173,12 +221,31 @@ func matchOriginalPacket(data []byte, expectedID, expectedSeq int) bool {
}
innerICMP := data[ihl:]
+ origID, origSeq := extractICMPIDAndSeq(innerICMP)
+ return origID == expectedID && origSeq == expectedSeq
+}
- /* Inner ICMP Structure: Type(1) + Code(1) + Checksum(2) + ID(2) + Seq(2) */
- origID := binary.BigEndian.Uint16(innerICMP[4:6])
- origSeq := binary.BigEndian.Uint16(innerICMP[6:8])
+func matchOriginalPacketV6(data []byte, expectedID, expectedSeq int) bool {
+ /* For IPv6, the TimeExceeded message contains the original IPv6 header +
+ as much of the original packet as possible. IPv6 has a fixed 40-byte header */
+ if len(data) < minIPv6HeaderLen+icmpHeaderLen {
+ return false
+ }
- return int(origID) == expectedID && int(origSeq) == expectedSeq
+ /* Skip the IPv6 header (40 bytes) to get to the inner ICMPv6 packet */
+ innerICMP := data[minIPv6HeaderLen:]
+ origID, origSeq := extractICMPIDAndSeq(innerICMP)
+ return origID == expectedID && origSeq == expectedSeq
+}
+
+func extractICMPIDAndSeq(icmpData []byte) (id, seq int) {
+ /* ICMP Echo Structure: Type(1) + Code(1) + Checksum(2) + ID(2) + Seq(2) */
+ if len(icmpData) < icmpHeaderLen {
+ return 0, 0
+ }
+ origID := binary.BigEndian.Uint16(icmpData[4:6])
+ origSeq := binary.BigEndian.Uint16(icmpData[6:8])
+ return int(origID), int(origSeq)
}
func printProbeResult(peerIP, prevAddr string, rtt time.Duration) {
|