Handcrafted IP
Building `traceroute` in Go
A quick overview of TCP/IP
TCP/IP Packet
All the important stuff for this exercise is in the IP Layer
- Source
- Destination
- Time To Live
Time To Live (TTL)
The time-to-live value can be thought of as an upper bound on the time that an IP datagram can exist in an Internet system. The TTL field is an 8 bit integer set by the sender, and reduced by every router on the route to its destination.
Death of a Packet
When the TTL reaches 0, the packet is discarded and an ICMP error message is sent to the sender.
How do we use this to trace the route a packet took?
An ugly diagram
buf := buildTCPPacket()
conn, err := net.ListenPacket("ip4:tcp", "0.0.0.0"
if err != nil {
log.Fatalf("ListenPacket failed to listen: %s\n", err)
}
ipv4.NewPacketConn(conn).SetTTL(2)
conn.WriteTo(buf.Bytes(), &net.IPAddr{IP: dstip})
TTD - Time To Die!
Listening for the ICMP error message
func listenICMP(status chan int) {
if handle, err := pcap.OpenLive("en0", 65536, false, 1); err != nil {
panic(err)
// Type 11 is TTLExceeded
} else if err := handle.SetBPFFilter("icmp and icmp[0] == 11"); err != nil {
panic(err)
} else {
packetSource := gopacket.NewPacketSource(handle, handle.LinkType())
status <- 0
for packet := range packetSource.Packets() {
src := packet.NetworkLayer().NetworkFlow().Src()
fmt.Printf("Ip: %s ", src)
if host, err := net.LookupAddr(src.String()); err == nil {
fmt.Printf("Host: %s\n", host[0])
}
close(status)
}
}
}
~/src/tcppacket sudo ./tcppacket -ttl 6 www.google.com
Fired Packet at 216.58.216.4 with a ttl of 6
ICMP TTL Exceeded from IP: 207.38.15.78 Host: 821.ae7.irv02-mls-dc-core-b.irv02.latisys.net.
Lets run it
func main() {
ttl := flag.Int("ttl", 64, "the TTL on the packet")
flag.Parse()
dst := flag.Args()[0]
handle := listenICMP()
go handleInboundICMP(handle)
firePacket(dst, ttl)
time.Sleep(10 * time.Second)
fmt.Println("Timeout")
os.Exit(1)
}
A GoRoutine
The Code
Go Traceroute
By mdp
Go Traceroute
- 947