diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..5e8cd60 --- /dev/null +++ b/go.mod @@ -0,0 +1,10 @@ +module traceroute + +go 1.18 + +require ( + github.com/google/gopacket v1.1.19 + golang.org/x/net v0.0.0-20210316092652-d523dce5a7f4 +) + +require golang.org/x/sys v0.0.0-20210315160823-c6e025ad8005 // indirect diff --git a/go.sum b/go.sum new file mode 100644 index 0000000..ed1b577 --- /dev/null +++ b/go.sum @@ -0,0 +1,22 @@ +github.com/google/gopacket v1.1.19 h1:ves8RnFZPGiFnTS0uPQStjwru6uO6h+nlr9j6fL7kF8= +github.com/google/gopacket v1.1.19/go.mod h1:iJ8V8n6KS+z2U1A8pUwu8bW5SyEMkXJB8Yo/Vo+TKTo= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= +golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= +golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20210316092652-d523dce5a7f4 h1:b0LrWgu8+q7z4J+0Y3Umo5q1dL7NXBkKBWkaVkAq17E= +golang.org/x/net v0.0.0-20210316092652-d523dce5a7f4/go.mod h1:RBQZq4jEuRlivfhVLdyRGr576XBO4/greRjx4P4O3yc= +golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210315160823-c6e025ad8005 h1:pDMpM2zh2MT0kHy037cKlSby2nEhD50SYqwQk76Nm40= +golang.org/x/sys v0.0.0-20210315160823-c6e025ad8005/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= diff --git a/listener_channel/listener-channel.go b/listener_channel/listener-channel.go new file mode 100644 index 0000000..e6ef8c1 --- /dev/null +++ b/listener_channel/listener-channel.go @@ -0,0 +1,61 @@ +package listener_channel + +import ( + "golang.org/x/net/context" + "net" + "time" +) + +type ReceivedMessage struct { + N *int + Peer net.Addr + Msg []byte + Err error +} + +type ListenerChannel struct { + ctx context.Context + cancel context.CancelFunc + Conn net.PacketConn + Messages chan ReceivedMessage +} + +func New(conn net.PacketConn) *ListenerChannel { + ctx, cancel := context.WithCancel(context.Background()) + results := make(chan ReceivedMessage, 50) + + return &ListenerChannel{Conn: conn, ctx: ctx, cancel: cancel, Messages: results} +} + +func (l *ListenerChannel) Start() { + for { + select { + case <-l.ctx.Done(): + return + default: + } + + reply := make([]byte, 1500) + err := l.Conn.SetReadDeadline(time.Now().Add(2 * time.Second)) + if err != nil { + l.Messages <- ReceivedMessage{Err: err} + continue + } + + n, peer, err := l.Conn.ReadFrom(reply) + if err != nil { + l.Messages <- ReceivedMessage{Err: err} + continue + } + l.Messages <- ReceivedMessage{ + N: &n, + Peer: peer, + Err: nil, + Msg: reply, + } + } +} + +func (l *ListenerChannel) Stop() { + l.cancel() +} diff --git a/main.go b/main.go new file mode 100644 index 0000000..800bde4 --- /dev/null +++ b/main.go @@ -0,0 +1,211 @@ +package main + +import ( + "traceroute/methods" + "traceroute/methods/tcp" + "traceroute/methods/udp" + "os" + "net" + "time" + "fmt" + "net/http" + "io/ioutil" + "encoding/json" + "flag" + "strings" +) + +type IPGeoData struct { + Asnumber string `json:"asnumber"` + Country string `json:"country"` + Prov string `json:"prov"` + City string `json:"city"` + District string `json:"district"` + Owner string `json:"owner"` + Isp string `json:"isp"` +} + +var tcpSYNFlag = flag.Bool("T", false, "Use TCP SYN for tracerouting (default port is 80 in TCP, 53 in UDP)") +var port = flag.Int("p", 80, "Set SYN Traceroute Port") +var numMeasurements = flag.Int("q", 3, "Set the number of probes per each hop.") +var parallelRequests = flag.Int("r", 18, "Set ParallelRequests number. It should be 1 when there is a multi-routing.") +var maxHops = flag.Int("m", 30, "Set the max number of hops (max TTL to be reached).") + + +func main() { + fmt.Println("ManGoTrace v0.0.1 Alpha \nOwO Organiztion Leo (leo.moe) & Vincent (vincent.moe)") + ip := domainLookUp(flagApply()) + fmt.Printf("traceroute to %s, 30 hops max, 32 byte packets\n", ip.String()) + + if (*tcpSYNFlag) { + tcpTraceroute := tcp.New(ip, methods.TracerouteConfig{ + MaxHops: uint16(*maxHops), + NumMeasurements: uint16(*numMeasurements), + ParallelRequests: uint16(*parallelRequests), + Port: *port, + Timeout: time.Second / 2, + }) + res, _ := tcpTraceroute.Start() + + traceroutePrinter(ip, res) + } else { + if (*port == 80) { + *port = 53 + } + udpTraceroute := udp.New(ip, true, methods.TracerouteConfig{ + MaxHops: uint16(*maxHops), + NumMeasurements: uint16(*numMeasurements), + ParallelRequests: uint16(*parallelRequests), + Port: *port, + Timeout: 2 * time.Second, + }) + res, _ := udpTraceroute.Start() + + traceroutePrinter(ip, res) + } +} + +func traceroutePrinter(ip net.IP, res *map[uint16][]methods.TracerouteHop) { + hopIndex := uint16(1) + for ; hopIndex <= 29 ; { + for k,v := range *res { + if (k == hopIndex) { + fmt.Print(k) + for _,v2 := range v { + ch := make(chan uint16) + go hopPrinter(hopIndex, ip, v2, ch) + hopIndex = <- ch + } + hopIndex = hopIndex + 1 + break + } + } + } +} + +func flagApply() string{ + flag.Parse() + ipArg := flag.Args() + if (flag.NArg() != 1) { + fmt.Println("Args Error") + os.Exit(2) + } + return ipArg[0] +} + +func getIPGeo(ip string, c chan IPGeoData) { + resp, err := http.Get("https://leo.moe/api.php?ip=" + ip) + if err != nil { + fmt.Println(err) + } + defer resp.Body.Close() + body, _ := ioutil.ReadAll(resp.Body) + + ipGeoData := IPGeoData{} + err = json.Unmarshal(body,&ipGeoData) + + if err != nil { + fmt.Println(err) + } + c <- ipGeoData +} + +func domainLookUp(host string) net.IP { + ips, err := net.LookupIP(host) + if (err != nil) { + fmt.Println("Domain Lookup Fail.") + os.Exit(1) + } + + var ipSlice = []net.IP{} + + for _, ip := range ips { + ipSlice = append(ipSlice, ip) + } + if (len(ipSlice) == 1) { + return ipSlice[0] + } else { + fmt.Println("Please Choose the IP You Want To TraceRoute") + for i, ip := range ipSlice { + fmt.Printf("%d. %s\n",i, ip) + } + var index int + fmt.Printf("Your Option: ") + fmt.Scanln(&index) + if (index >= len(ipSlice) || index < 0) { + fmt.Println("Your Option is invalid") + os.Exit(3) + } + return ipSlice[index] + } +} + +func hopPrinter(hopIndex uint16, ip net.IP, v2 methods.TracerouteHop, c chan uint16) { + if (v2.Address == nil) { + fmt.Println("\t*") + } else { + ip_str := fmt.Sprintf("%s", v2.Address) + + ptr, err := net.LookupAddr(ip_str) + + ch_b := make(chan IPGeoData) + go getIPGeo(ip_str, ch_b) + iPGeoData := <-ch_b + + if (ip.String() == ip_str) { + hopIndex = 30 + iPGeoData.Owner = iPGeoData.Isp + } + + if (strings.Index(ip_str, "9.31.") == 0 || strings.Index(ip_str, "11.72.") == 0) { + fmt.Printf("\t%-15s %.2fms * 局域网, 腾讯云\n", v2.Address, v2.RTT.Seconds()*1000) + c <- hopIndex + return + } + + if (strings.Index(ip_str, "11.13.") == 0) { + fmt.Printf("\t%-15s %.2fms * 局域网, 阿里云\n", v2.Address, v2.RTT.Seconds()*1000) + c <- hopIndex + return + } + + + + if (iPGeoData.Owner == "") { + iPGeoData.Owner = iPGeoData.Isp + } + + if (iPGeoData.Asnumber == "") { + iPGeoData.Asnumber = "*" + } else { + iPGeoData.Asnumber = "AS" + iPGeoData.Asnumber + } + + if (iPGeoData.District != "") { + iPGeoData.City = iPGeoData.City + ", " + iPGeoData.District + } + + if (iPGeoData.Country == "") { + fmt.Printf("\t%-15s %.2fms * 局域网\n", v2.Address, v2.RTT.Seconds()*1000) + c <- hopIndex + return + } + + if (iPGeoData.Prov == "" && iPGeoData.City == "") { + + if err != nil { + fmt.Printf("\t%-15s %.2fms %s %s, %s, %s 骨干网\n",v2.Address, v2.RTT.Seconds()*1000, iPGeoData.Asnumber, iPGeoData.Country, iPGeoData.Owner, iPGeoData.Owner) + } else { + fmt.Printf("\t%-15s (%s) %.2fms %s %s, %s, %s 骨干网\n",ptr[0], v2.Address, v2.RTT.Seconds()*1000, iPGeoData.Asnumber, iPGeoData.Country, iPGeoData.Owner, iPGeoData.Owner) + } + } else { + + if err != nil { + fmt.Printf("\t%-15s %.2fms %s %s, %s, %s, %s\n",v2.Address, v2.RTT.Seconds()*1000, iPGeoData.Asnumber, iPGeoData.Country, iPGeoData.Prov, iPGeoData.City, iPGeoData.Owner) + } else { + fmt.Printf("\t%-15s (%s) %.2fms %s %s, %s, %s, %s\n",ptr[0], v2.Address, v2.RTT.Seconds()*1000, iPGeoData.Asnumber, iPGeoData.Country, iPGeoData.Prov, iPGeoData.City, iPGeoData.Owner) + } + } + } + c <- hopIndex +} diff --git a/methods/methods.go b/methods/methods.go new file mode 100644 index 0000000..7fb5aa1 --- /dev/null +++ b/methods/methods.go @@ -0,0 +1,79 @@ +package methods + +import ( + "encoding/binary" + "errors" + "net" + "time" +) + +// TracerouteHop type +type TracerouteHop struct { + Success bool + Address net.Addr + TTL uint16 + RTT *time.Duration +} + +type TracerouteConfig struct { + MaxHops uint16 + NumMeasurements uint16 + ParallelRequests uint16 + + Port int + Timeout time.Duration +} + +func GetIPHeaderLength(data []byte) (int, error) { + if len(data) < 1 { + return 0, errors.New("received invalid IP header") + } + return int((data[0] & 0x0F) * 4), nil +} + +func GetICMPResponsePayload(data []byte) ([]byte, error) { + length, err := GetIPHeaderLength(data) + if err != nil { + return nil, err + } + + if len(data) < length { + return nil, errors.New("length of packet too short") + } + + return data[length:], nil +} + +func GetUDPSrcPort(data []byte) uint16 { + srcPortBytes := data[:2] + srcPort := binary.BigEndian.Uint16(srcPortBytes) + return srcPort +} + +func GetTCPSeq(data []byte) uint32 { + seqBytes := data[4:8] + return binary.BigEndian.Uint32(seqBytes) +} + +func ReduceFinalResult(preliminary map[uint16][]TracerouteHop, maxHops uint16, destIP net.IP) map[uint16][]TracerouteHop { + // reduce the results to remove all hops after the first encounter to final destination + finalResults := map[uint16][]TracerouteHop{} + for i := uint16(1); i < maxHops; i++ { + foundFinal := false + probes := preliminary[i] + if probes == nil { + break + } + finalResults[i] = []TracerouteHop{} + for _, probe := range probes { + if probe.Success && probe.Address.String() == destIP.String() { + foundFinal = true + } + finalResults[i] = append(finalResults[i], probe) + } + if foundFinal { + break + } + } + return finalResults +} diff --git a/methods/quic/quic.go b/methods/quic/quic.go new file mode 100644 index 0000000..12f6569 --- /dev/null +++ b/methods/quic/quic.go @@ -0,0 +1,37 @@ +package quic + +import ( + "math/rand" +) + +var Packet = []byte{202, 255, 255, 255, 255, 10, 253, 187, 139, 161, 69, 45, 65, 177, 68, 23, 4, 83, 226, 136, 32, 0, 68, 204, 168, 172, 81, 54, 106, 24, 100, 29, 160, 51, 70, 95, 107, 100, 4, 127, 168, 161, 43, 243, 194, 192, 4, 192, 99, 149, 103, 193, 233, 86, 254, 220, 167, 6, 45, 209, 193, 11, 77, 123, 131, 80, 17, 201, 248, 246, 246, 45, 153, 229, 169, 191, 76, 131, 162, 109, 188, 151, 22, 36, 1, 229, 201, 194, 26, 8, 63, 197, 207, 43, 190, 55, 224, 59, 247, 19, 142, 34, 186, 122, 108, 162, 110, 221, 15, 36, 224, 90, 73, 182, 177, 119, 26, 226, 16, 13, 18, 201, 96, 249, 192, 162, 13, 8, 132, 41, 63, 221, 43, 62, 143, 236, 219, 207, 10, 4, 115, 117, 214, 53, 64, 25, 180, 81, 144, 173, 155, 11, 32, 239, 253, 131, 115, 136, 227, 77, 132, 144, 48, 135, 137, 138, 214, 14, 67, 63, 187, 54, 197, 18, 191, 243, 128, 157, 74, 27, 225, 33, 12, 163, 0, 249, 126, 252, 242, 2, 33, 70, 255, 204, 200, 7, 4, 65, 154, 157, 83, 125, 197, 130, 53, 187, 254, 96, 54, 114, 93, 108, 198, 218, 198, 86, 33, 50, 74, 131, 154, 26, 206, 122, 148, 212, 177, 163, 99, 0, 138, 68, 235, 222, 55, 252, 232, 32, 103, 68, 119, 33, 188, 227, 71, 13, 123, 229, 99, 206, 245, 115, 21, 216, 31, 61, 30, 174, 97, 36, 96, 28, 66, 173, 89, 139, 37, 102, 81, 232, 158, 212, 8, 33, 123, 216, 191, 133, 200, 13, 197, 94, 223, 246, 29, 246, 19, 44, 135, 113, 59, 231, 36, 186, 53, 228, 33, 206, 85, 59, 105, 6, 225, 227, 111, 146, 211, 195, 73, 108, 200, 70, 73, 220, 163, 120, 47, 167, 139, 24, 149, 53, 11, 224, 22, 241, 52, 214, 38, 17, 137, 96, 134, 144, 16, 44, 116, 151, 15, 57, 87, 167, 137, 194, 63, 101, 195, 31, 21, 51, 7, 95, 101, 114, 195, 75, 211, 137, 149, 2, 80, 183, 82, 120, 185, 39, 192, 82, 102, 141, 153, 156, 118, 137, 99, 59, 4, 93, 74, 96, 164, 80, 142, 16, 197, 111, 178, 230, 250, 189, 26, 184, 19, 236, 249, 170, 68, 11, 41, 37, 243, 176, 208, 209, 208, 108, 74, 205, 55, 165, 30, 182, 170, 62, 184, 89, 109, 34, 135, 174, 237, 207, 52, 18, 32, 229, 131, 237, 204, 33, 77, 67, 242, 248, 72, 137, 32, 173, 226, 234, 146, 203, 35, 208, 156, 107, 190, 255, 29, 163, 167, 25, 187, 27, 26, 31, 227, 202, 135, 50, 248, 122, 68, 80, 218, 58, 142, 193, 71, 154, 27, 255, 109, 253, 216, 250, 219, 112, 89, 56, 23, 47, 98, 104, 163, 17, 152, 102, 16, 139, 157, 172, 39, 191, 129, 132, 29, 189, 68, 216, 138, 3, 163, 101, 165, 200, 61, 56, 197, 142, 226, 156, 172, 62, 251, 193, 100, 94, 96, 166, 96, 19, 116, 5, 237, 177, 39, 188, 32, 177, 9, 171, 16, 224, 209, 53, 217, 144, 245, 215, 217, 196, 55, 232, 16, 121, 217, 58, 44, 34, 213, 5, 184, 171, 81, 167, 10, 34, 5, 116, 239, 179, 206, 83, 181, 235, 35, 121, 196, 234, 191, 119, 189, 238, 103, 235, 147, 155, 47, 151, 29, 56, 35, 72, 251, 107, 121, 21, 188, 184, 183, 72, 94, 191, 230, 100, 214, 161, 143, 235, 10, 236, 25, 172, 110, 95, 99, 99, 55, 25, 217, 131, 113, 57, 124, 195, 165, 204, 242, 109, 156, 106, 194, 233, 42, 175, 241, 213, 164, 207, 131, 207, 74, 52, 207, 155, 210, 227, 136, 41, 251, 47, 81, 192, 96, 26, 133, 85, 24, 57, 75, 188, 214, 146, 17, 209, 229, 217, 151, 71, 166, 174, 232, 162, 69, 147, 116, 247, 172, 154, 110, 7, 170, 78, 185, 18, 43, 70, 23, 215, 105, 118, 125, 64, 222, 16, 149, 205, 159, 138, 194, 46, 99, 228, 115, 205, 30, 92, 223, 29, 72, 86, 57, 14, 209, 176, 244, 200, 26, 208, 198, 153, 82, 154, 233, 32, 55, 219, 161, 243, 150, 233, 70, 142, 96, 52, 254, 200, 133, 56, 246, 80, 153, 59, 74, 245, 93, 30, 241, 70, 192, 93, 32, 253, 254, 181, 9, 179, 152, 218, 173, 216, 175, 3, 69, 249, 82, 145, 41, 96, 162, 241, 83, 245, 162, 71, 128, 58, 116, 200, 236, 93, 207, 174, 173, 68, 184, 61, 172, 100, 39, 118, 250, 136, 66, 154, 165, 178, 153, 156, 116, 230, 10, 33, 237, 91, 132, 57, 11, 111, 141, 162, 242, 159, 124, 255, 142, 101, 205, 127, 214, 97, 17, 146, 130, 151, 37, 222, 140, 227, 152, 92, 217, 210, 64, 198, 97, 72, 11, 2, 182, 114, 101, 68, 246, 183, 55, 46, 227, 62, 163, 65, 123, 208, 223, 132, 66, 202, 82, 126, 31, 166, 92, 126, 105, 239, 255, 25, 34, 249, 205, 67, 217, 139, 129, 147, 178, 81, 251, 226, 194, 8, 69, 208, 67, 243, 210, 181, 121, 59, 22, 186, 120, 31, 27, 186, 54, 81, 145, 89, 53, 152, 72, 107, 125, 202, 124, 112, 134, 124, 226, 95, 3, 148, 225, 191, 107, 209, 139, 58, 200, 72, 213, 100, 164, 187, 164, 86, 61, 65, 45, 167, 253, 128, 253, 133, 52, 21, 31, 67, 178, 173, 68, 186, 82, 135, 50, 123, 122, 56, 178, 37, 233, 82, 83, 61, 229, 47, 195, 2, 169, 129, 154, 226, 71, 163, 211, 232, 156, 97, 67, 125, 104, 61, 0, 81, 253, 234, 225, 1, 164, 113, 33, 229, 24, 101, 238, 128, 227, 219, 40, 218, 221, 78, 213, 172, 12, 69, 179, 142, 97, 156, 138, 54, 50, 145, 14, 191, 120, 37, 128, 171, 75, 201, 79, 78, 144, 21, 163, 233, 54, 107, 234, 134, 11, 204, 233, 156, 77, 200, 9, 26, 32, 156, 132, 116, 81, 161, 218, 131, 110, 30, 175, 118, 48, 150, 146, 234, 195, 109, 228, 74, 43, 247, 114, 31, 139, 235, 214, 147, 206, 145, 170, 54, 83, 160, 48, 59, 65, 250, 192, 13, 87, 240, 234, 215, 209, 158, 10, 91, 208, 151, 177, 71, 96, 209, 184, 125, 82, 156, 34, 74, 15, 97, 73, 249, 185, 79, 34, 42, 157, 175, 52, 234, 131, 51, 144, 203, 6, 81, 245, 215, 107, 71, 68, 113, 82, 210, 116, 18, 88, 92, 141, 68, 227, 185, 55, 114, 54, 243, 152, 47, 117, 250, 87, 180, 30, 57, 187, 98, 90, 127, 243, 94, 122, 48, 23, 200, 40, 89, 89, 53, 83, 221, 56, 231, 117, 200, 201, 101, 159, 147, 25, 194, 236, 249, 5, 0, 70, 209, 122, 162, 103, 178, 217, 242, 36, 226, 16, 215, 144, 98, 198, 173, 134, 89, 115, 171, 81, 0, 112, 6, 152, 154, 119, 28, 36, 209, 174, 21, 93, 62, 33, 39, 57, 67, 71, 239, 11, 158, 53, 79, 103, 157, 100, 234, 28, 222, 212, 196, 216, 162, 49, 104, 49, 18, 175, 140, 255, 152, 175, 25, 233, 200, 60, 203, 225, 232, 121, 46, 220, 243, 13, 125, 255, 158, 2, 153, 203, 203, 223, 159, 65, 2, 219, 105, 235, 109, 126, 124, 53, 122} + +var beginHeader = []byte{202, 255, 255, 255, 255} + +//var endPacket = []byte{0, 68, 204, 168, 172, 81, 54, 106, 24, 100, 29, 160, 51, 70, 95, 107, 100, 4, 127, 168, 161, 43, 243, 194, 192, 4, 192, 99, 149, 103, 193, 233, 86, 254, 220, 167, 6, 45, 209, 193, 11, 77, 123, 131, 80, 17, 201, 248, 246, 246, 45, 153, 229, 169, 191, 76, 131, 162, 109, 188, 151, 22, 36, 1, 229, 201, 194, 26, 8, 63, 197, 207, 43, 190, 55, 224, 59, 247, 19, 142, 34, 186, 122, 108, 162, 110, 221, 15, 36, 224, 90, 73, 182, 177, 119, 26, 226, 16, 13, 18, 201, 96, 249, 192, 162, 13, 8, 132, 41, 63, 221, 43, 62, 143, 236, 219, 207, 10, 4, 115, 117, 214, 53, 64, 25, 180, 81, 144, 173, 155, 11, 32, 239, 253, 131, 115, 136, 227, 77, 132, 144, 48, 135, 137, 138, 214, 14, 67, 63, 187, 54, 197, 18, 191, 243, 128, 157, 74, 27, 225, 33, 12, 163, 0, 249, 126, 252, 242, 2, 33, 70, 255, 204, 200, 7, 4, 65, 154, 157, 83, 125, 197, 130, 53, 187, 254, 96, 54, 114, 93, 108, 198, 218, 198, 86, 33, 50, 74, 131, 154, 26, 206, 122, 148, 212, 177, 163, 99, 0, 138, 68, 235, 222, 55, 252, 232, 32, 103, 68, 119, 33, 188, 227, 71, 13, 123, 229, 99, 206, 245, 115, 21, 216, 31, 61, 30, 174, 97, 36, 96, 28, 66, 173, 89, 139, 37, 102, 81, 232, 158, 212, 8, 33, 123, 216, 191, 133, 200, 13, 197, 94, 223, 246, 29, 246, 19, 44, 135, 113, 59, 231, 36, 186, 53, 228, 33, 206, 85, 59, 105, 6, 225, 227, 111, 146, 211, 195, 73, 108, 200, 70, 73, 220, 163, 120, 47, 167, 139, 24, 149, 53, 11, 224, 22, 241, 52, 214, 38, 17, 137, 96, 134, 144, 16, 44, 116, 151, 15, 57, 87, 167, 137, 194, 63, 101, 195, 31, 21, 51, 7, 95, 101, 114, 195, 75, 211, 137, 149, 2, 80, 183, 82, 120, 185, 39, 192, 82, 102, 141, 153, 156, 118, 137, 99, 59, 4, 93, 74, 96, 164, 80, 142, 16, 197, 111, 178, 230, 250, 189, 26, 184, 19, 236, 249, 170, 68, 11, 41, 37, 243, 176, 208, 209, 208, 108, 74, 205, 55, 165, 30, 182, 170, 62, 184, 89, 109, 34, 135, 174, 237, 207, 52, 18, 32, 229, 131, 237, 204, 33, 77, 67, 242, 248, 72, 137, 32, 173, 226, 234, 146, 203, 35, 208, 156, 107, 190, 255, 29, 163, 167, 25, 187, 27, 26, 31, 227, 202, 135, 50, 248, 122, 68, 80, 218, 58, 142, 193, 71, 154, 27, 255, 109, 253, 216, 250, 219, 112, 89, 56, 23, 47, 98, 104, 163, 17, 152, 102, 16, 139, 157, 172, 39, 191, 129, 132, 29, 189, 68, 216, 138, 3, 163, 101, 165, 200, 61, 56, 197, 142, 226, 156, 172, 62, 251, 193, 100, 94, 96, 166, 96, 19, 116, 5, 237, 177, 39, 188, 32, 177, 9, 171, 16, 224, 209, 53, 217, 144, 245, 215, 217, 196, 55, 232, 16, 121, 217, 58, 44, 34, 213, 5, 184, 171, 81, 167, 10, 34, 5, 116, 239, 179, 206, 83, 181, 235, 35, 121, 196, 234, 191, 119, 189, 238, 103, 235, 147, 155, 47, 151, 29, 56, 35, 72, 251, 107, 121, 21, 188, 184, 183, 72, 94, 191, 230, 100, 214, 161, 143, 235, 10, 236, 25, 172, 110, 95, 99, 99, 55, 25, 217, 131, 113, 57, 124, 195, 165, 204, 242, 109, 156, 106, 194, 233, 42, 175, 241, 213, 164, 207, 131, 207, 74, 52, 207, 155, 210, 227, 136, 41, 251, 47, 81, 192, 96, 26, 133, 85, 24, 57, 75, 188, 214, 146, 17, 209, 229, 217, 151, 71, 166, 174, 232, 162, 69, 147, 116, 247, 172, 154, 110, 7, 170, 78, 185, 18, 43, 70, 23, 215, 105, 118, 125, 64, 222, 16, 149, 205, 159, 138, 194, 46, 99, 228, 115, 205, 30, 92, 223, 29, 72, 86, 57, 14, 209, 176, 244, 200, 26, 208, 198, 153, 82, 154, 233, 32, 55, 219, 161, 243, 150, 233, 70, 142, 96, 52, 254, 200, 133, 56, 246, 80, 153, 59, 74, 245, 93, 30, 241, 70, 192, 93, 32, 253, 254, 181, 9, 179, 152, 218, 173, 216, 175, 3, 69, 249, 82, 145, 41, 96, 162, 241, 83, 245, 162, 71, 128, 58, 116, 200, 236, 93, 207, 174, 173, 68, 184, 61, 172, 100, 39, 118, 250, 136, 66, 154, 165, 178, 153, 156, 116, 230, 10, 33, 237, 91, 132, 57, 11, 111, 141, 162, 242, 159, 124, 255, 142, 101, 205, 127, 214, 97, 17, 146, 130, 151, 37, 222, 140, 227, 152, 92, 217, 210, 64, 198, 97, 72, 11, 2, 182, 114, 101, 68, 246, 183, 55, 46, 227, 62, 163, 65, 123, 208, 223, 132, 66, 202, 82, 126, 31, 166, 92, 126, 105, 239, 255, 25, 34, 249, 205, 67, 217, 139, 129, 147, 178, 81, 251, 226, 194, 8, 69, 208, 67, 243, 210, 181, 121, 59, 22, 186, 120, 31, 27, 186, 54, 81, 145, 89, 53, 152, 72, 107, 125, 202, 124, 112, 134, 124, 226, 95, 3, 148, 225, 191, 107, 209, 139, 58, 200, 72, 213, 100, 164, 187, 164, 86, 61, 65, 45, 167, 253, 128, 253, 133, 52, 21, 31, 67, 178, 173, 68, 186, 82, 135, 50, 123, 122, 56, 178, 37, 233, 82, 83, 61, 229, 47, 195, 2, 169, 129, 154, 226, 71, 163, 211, 232, 156, 97, 67, 125, 104, 61, 0, 81, 253, 234, 225, 1, 164, 113, 33, 229, 24, 101, 238, 128, 227, 219, 40, 218, 221, 78, 213, 172, 12, 69, 179, 142, 97, 156, 138, 54, 50, 145, 14, 191, 120, 37, 128, 171, 75, 201, 79, 78, 144, 21, 163, 233, 54, 107, 234, 134, 11, 204, 233, 156, 77, 200, 9, 26, 32, 156, 132, 116, 81, 161, 218, 131, 110, 30, 175, 118, 48, 150, 146, 234, 195, 109, 228, 74, 43, 247, 114, 31, 139, 235, 214, 147, 206, 145, 170, 54, 83, 160, 48, 59, 65, 250, 192, 13, 87, 240, 234, 215, 209, 158, 10, 91, 208, 151, 177, 71, 96, 209, 184, 125, 82, 156, 34, 74, 15, 97, 73, 249, 185, 79, 34, 42, 157, 175, 52, 234, 131, 51, 144, 203, 6, 81, 245, 215, 107, 71, 68, 113, 82, 210, 116, 18, 88, 92, 141, 68, 227, 185, 55, 114, 54, 243, 152, 47, 117, 250, 87, 180, 30, 57, 187, 98, 90, 127, 243, 94, 122, 48, 23, 200, 40, 89, 89, 53, 83, 221, 56, 231, 117, 200, 201, 101, 159, 147, 25, 194, 236, 249, 5, 0, 70, 209, 122, 162, 103, 178, 217, 242, 36, 226, 16, 215, 144, 98, 198, 173, 134, 89, 115, 171, 81, 0, 112, 6, 152, 154, 119, 28, 36, 209, 174, 21, 93, 62, 33, 39, 57, 67, 71, 239, 11, 158, 53, 79, 103, 157, 100, 234, 28, 222, 212, 196, 216, 162, 49, 104, 49, 18, 175, 140, 255, 152, 175, 25, 233, 200, 60, 203, 225, 232, 121, 46, 220, 243, 13, 125, 255, 158, 2, 153, 203, 203, 223, 159, 65, 2, 219, 105, 235, 109, 126, 124, 53, 122} + +func GenerateWithRandomIds() []byte { + var packet []byte + packet = append(packet, beginHeader...) + // append the length of destConnectionID + packet = append(packet, 10) + + // generate random destConnectionId and append + destConnectionId := make([]byte, 10) + rand.Read(destConnectionId) + packet = append(packet, destConnectionId...) + + // append the length of destConnectionID + packet = append(packet, 4) + + // generate random srcConnectionId and append + srcConnectionId := make([]byte, 4) + rand.Read(srcConnectionId) + packet = append(packet, srcConnectionId...) + + for i := len(packet); i < 1200; i++ { + packet = append(packet, 0) + } + + return packet +} diff --git a/methods/tcp/tcp.go b/methods/tcp/tcp.go new file mode 100644 index 0000000..f1ee832 --- /dev/null +++ b/methods/tcp/tcp.go @@ -0,0 +1,328 @@ +package tcp + +import ( + "github.com/google/gopacket" + "github.com/google/gopacket/layers" + "traceroute/listener_channel" + "traceroute/methods" + "traceroute/parallel_limiter" + "traceroute/signal" + "traceroute/util" + "golang.org/x/net/context" + "golang.org/x/net/icmp" + "golang.org/x/net/ipv4" + "log" + "math" + "math/rand" + "net" + "sync" + "time" +) + +type inflightData struct { + start time.Time + ttl uint16 +} + +type results struct { + inflightRequests sync.Map + + results map[uint16][]methods.TracerouteHop + resultsMu sync.Mutex + err error + + concurrentRequests *parallel_limiter.ParallelLimiter + reachedFinalHop *signal.Signal +} + +type Traceroute struct { + opConfig opConfig + trcrtConfig methods.TracerouteConfig + results results +} + +type opConfig struct { + icmpConn net.PacketConn + tcpConn net.PacketConn + tcpMu sync.Mutex + + destIP net.IP + srcIP net.IP + + wg *sync.WaitGroup + + ctx context.Context + cancel context.CancelFunc +} + +func New(destIP net.IP, config methods.TracerouteConfig) *Traceroute { + return &Traceroute{ + opConfig: opConfig{ + destIP: destIP, + }, + trcrtConfig: config, + } +} + +func (tr *Traceroute) Start() (*map[uint16][]methods.TracerouteHop, error) { + tr.opConfig.ctx, tr.opConfig.cancel = context.WithCancel(context.Background()) + + tr.opConfig.srcIP, _ = util.LocalIPPort(tr.opConfig.destIP) + + var err error + tr.opConfig.tcpConn, err = net.ListenPacket("ip4:tcp", tr.opConfig.srcIP.String()) + if err != nil { + return nil, err + } + + tr.opConfig.icmpConn, err = icmp.ListenPacket("ip4:icmp", "0.0.0.0") + if err != nil { + return nil, err + } + + var wg sync.WaitGroup + tr.opConfig.wg = &wg + + tr.results = results{ + inflightRequests: sync.Map{}, + concurrentRequests: parallel_limiter.New(int(tr.trcrtConfig.ParallelRequests)), + reachedFinalHop: signal.New(), + + results: map[uint16][]methods.TracerouteHop{}, + } + + return tr.start() +} + +func (tr *Traceroute) timeoutLoop() { + ticker := time.NewTicker(tr.trcrtConfig.Timeout / 4) + go func() { + for range ticker.C { + tr.results.inflightRequests.Range(func(key, value interface{}) bool { + request := value.(inflightData) + expired := time.Since(request.start) > tr.trcrtConfig.Timeout + if !expired { + return true + } + tr.results.inflightRequests.Delete(key) + tr.addToResult(request.ttl, methods.TracerouteHop{ + Success: false, + TTL: request.ttl, + }) + tr.results.concurrentRequests.Finished() + tr.opConfig.wg.Done() + return true + }) + } + }() + select { + case <-tr.opConfig.ctx.Done(): + ticker.Stop() + } +} + +func (tr *Traceroute) addToResult(ttl uint16, hop methods.TracerouteHop) { + tr.results.resultsMu.Lock() + defer tr.results.resultsMu.Unlock() + if tr.results.results[ttl] == nil { + tr.results.results[ttl] = []methods.TracerouteHop{} + } + + tr.results.results[ttl] = append(tr.results.results[ttl], hop) +} + +func (tr *Traceroute) handleICMPMessage(msg listener_channel.ReceivedMessage, data []byte) { + header, err := methods.GetICMPResponsePayload(data) + if err != nil { + return + } + sequenceNumber := methods.GetTCPSeq(header) + val, ok := tr.results.inflightRequests.LoadAndDelete(sequenceNumber) + if !ok { + return + } + request := val.(inflightData) + elapsed := time.Since(request.start) + if msg.Peer.String() == tr.opConfig.destIP.String() { + tr.results.reachedFinalHop.Signal() + } + tr.addToResult(request.ttl, methods.TracerouteHop{ + Success: true, + Address: msg.Peer, + TTL: request.ttl, + RTT: &elapsed, + }) + tr.results.concurrentRequests.Finished() + tr.opConfig.wg.Done() +} + +func (tr *Traceroute) icmpListener() { + lc := listener_channel.New(tr.opConfig.icmpConn) + + defer lc.Stop() + + go lc.Start() + + for { + select { + case <-tr.opConfig.ctx.Done(): + return + case msg := <-lc.Messages: + if msg.N == nil { + continue + } + rm, err := icmp.ParseMessage(1, msg.Msg[:*msg.N]) + if err != nil { + log.Println(err) + continue + } + switch rm.Type { + case ipv4.ICMPTypeTimeExceeded: + body := rm.Body.(*icmp.TimeExceeded).Data + tr.handleICMPMessage(msg, body) + case ipv4.ICMPTypeDestinationUnreachable: + body := rm.Body.(*icmp.DstUnreach).Data + tr.handleICMPMessage(msg, body) + default: + log.Println("received icmp message of unknown type") + } + } + } +} + +func (tr *Traceroute) tcpListener() { + lc := listener_channel.New(tr.opConfig.tcpConn) + + defer lc.Stop() + + go lc.Start() + + for { + select { + case <-tr.opConfig.ctx.Done(): + return + case msg := <-lc.Messages: + if msg.N == nil { + continue + } + if msg.Peer.String() != tr.opConfig.destIP.String() { + continue + } + // Decode a packet + packet := gopacket.NewPacket(msg.Msg[:*msg.N], layers.LayerTypeTCP, gopacket.Default) + // Get the TCP layer from this packet + if tcpLayer := packet.Layer(layers.LayerTypeTCP); tcpLayer != nil { + tcp, _ := tcpLayer.(*layers.TCP) + + val, ok := tr.results.inflightRequests.LoadAndDelete(tcp.Ack - 1) + if !ok { + continue + } + request := val.(inflightData) + tr.results.concurrentRequests.Finished() + elapsed := time.Since(request.start) + if msg.Peer.String() == tr.opConfig.destIP.String() { + tr.results.reachedFinalHop.Signal() + } + tr.addToResult(request.ttl, methods.TracerouteHop{ + Success: true, + Address: msg.Peer, + TTL: request.ttl, + RTT: &elapsed, + }) + tr.opConfig.wg.Done() + } + } + } +} + +func (tr *Traceroute) sendMessage(ttl uint16) { + _, srcPort := util.LocalIPPort(tr.opConfig.destIP) + ipHeader := &layers.IPv4{ + SrcIP: tr.opConfig.srcIP, + DstIP: tr.opConfig.destIP, + Protocol: layers.IPProtocolTCP, + TTL: uint8(ttl), + } + + sequenceNumber := uint32(rand.Intn(math.MaxUint32)) + + tcpHeader := &layers.TCP{ + SrcPort: layers.TCPPort(srcPort), + DstPort: layers.TCPPort(tr.trcrtConfig.Port), + Seq: sequenceNumber, + SYN: true, + Window: 14600, + } + _ = tcpHeader.SetNetworkLayerForChecksum(ipHeader) + + buf := gopacket.NewSerializeBuffer() + opts := gopacket.SerializeOptions{ + ComputeChecksums: true, + FixLengths: true, + } + if err := gopacket.SerializeLayers(buf, opts, tcpHeader); err != nil { + tr.results.err = err + tr.opConfig.cancel() + return + } + + tr.opConfig.tcpMu.Lock() + defer tr.opConfig.tcpMu.Unlock() + err := ipv4.NewPacketConn(tr.opConfig.tcpConn).SetTTL(int(ttl)) + if err != nil { + tr.results.err = err + tr.opConfig.cancel() + return + } + + start := time.Now() + if _, err := tr.opConfig.tcpConn.WriteTo(buf.Bytes(), &net.IPAddr{IP: tr.opConfig.destIP}); err != nil { + tr.results.err = err + tr.opConfig.cancel() + return + } + tr.results.inflightRequests.Store(sequenceNumber, inflightData{start: start, ttl: ttl}) +} + +func (tr *Traceroute) sendLoop() { + rand.Seed(time.Now().UTC().UnixNano()) + defer tr.opConfig.wg.Done() + + for ttl := uint16(1); ttl <= tr.trcrtConfig.MaxHops; ttl++ { + select { + case <-tr.results.reachedFinalHop.Chan(): + return + default: + } + for i := 0; i < int(tr.trcrtConfig.NumMeasurements); i++ { + select { + case <-tr.opConfig.ctx.Done(): + return + case <-tr.results.concurrentRequests.Start(): + tr.opConfig.wg.Add(1) + go tr.sendMessage(ttl) + } + } + } +} + +func (tr *Traceroute) start() (*map[uint16][]methods.TracerouteHop, error) { + go tr.timeoutLoop() + go tr.icmpListener() + go tr.tcpListener() + + tr.opConfig.wg.Add(1) + go tr.sendLoop() + + tr.opConfig.wg.Wait() + tr.opConfig.cancel() + + if tr.results.err != nil { + return nil, tr.results.err + } + + result := methods.ReduceFinalResult(tr.results.results, tr.trcrtConfig.MaxHops, tr.opConfig.destIP) + + return &result, tr.results.err +} diff --git a/methods/udp/udp.go b/methods/udp/udp.go new file mode 100644 index 0000000..2ce3d4e --- /dev/null +++ b/methods/udp/udp.go @@ -0,0 +1,310 @@ +package udp + +import ( + "github.com/google/gopacket" + "github.com/google/gopacket/layers" + "traceroute/listener_channel" + "traceroute/methods" + "traceroute/methods/quic" + "traceroute/parallel_limiter" + "traceroute/signal" + "traceroute/taskgroup" + "traceroute/util" + "golang.org/x/net/context" + "golang.org/x/net/icmp" + "golang.org/x/net/ipv4" + "log" + "math/rand" + "net" + "sync" + time "time" +) + +type inflightData struct { + icmpMsg chan<- net.Addr +} + +type opConfig struct { + quic bool + destIP net.IP + wg *taskgroup.TaskGroup + + icmpConn net.PacketConn + + ctx context.Context + cancel context.CancelFunc +} + +type results struct { + inflightRequests sync.Map + + results map[uint16][]methods.TracerouteHop + resultsMu sync.Mutex + err error + + concurrentRequests *parallel_limiter.ParallelLimiter + reachedFinalHop *signal.Signal +} + +type Traceroute struct { + trcrtConfig methods.TracerouteConfig + opConfig opConfig + results results +} + +func New(destIP net.IP, quic bool, config methods.TracerouteConfig) *Traceroute { + return &Traceroute{ + opConfig: opConfig{ + quic: quic, + destIP: destIP, + }, + trcrtConfig: config, + } +} + +func (tr *Traceroute) Start() (*map[uint16][]methods.TracerouteHop, error) { + tr.opConfig.ctx, tr.opConfig.cancel = context.WithCancel(context.Background()) + + tr.results = results{ + inflightRequests: sync.Map{}, + concurrentRequests: parallel_limiter.New(int(tr.trcrtConfig.ParallelRequests)), + results: map[uint16][]methods.TracerouteHop{}, + reachedFinalHop: signal.New(), + } + + var err error + tr.opConfig.icmpConn, err = icmp.ListenPacket("ip4:icmp", "0.0.0.0") + if err != nil { + return nil, err + } + + return tr.start() +} + +func (tr *Traceroute) addToResult(ttl uint16, hop methods.TracerouteHop) { + tr.results.resultsMu.Lock() + defer tr.results.resultsMu.Unlock() + if tr.results.results[ttl] == nil { + tr.results.results[ttl] = []methods.TracerouteHop{} + } + + tr.results.results[ttl] = append(tr.results.results[ttl], hop) +} + +func (tr *Traceroute) getUDPConn(try int) (net.IP, int, net.PacketConn) { + srcIP, _ := util.LocalIPPort(tr.opConfig.destIP) + + var ipString string + + if srcIP == nil { + ipString = "" + } else { + ipString = srcIP.String() + } + + udpConn, err := net.ListenPacket("udp", ipString+":0") + if err != nil { + if try > 3 { + log.Fatal(err) + } + return tr.getUDPConn(try + 1) + } + + return srcIP, udpConn.LocalAddr().(*net.UDPAddr).Port, udpConn +} + +func (tr *Traceroute) sendMessage(ttl uint16) { + srcIP, srcPort, udpConn := tr.getUDPConn(0) + + var payload []byte + if tr.opConfig.quic { + payload = quic.GenerateWithRandomIds() + } else { + ipHeader := &layers.IPv4{ + SrcIP: srcIP, + DstIP: tr.opConfig.destIP, + Protocol: layers.IPProtocolTCP, + TTL: uint8(ttl), + } + + udpHeader := &layers.UDP{ + SrcPort: layers.UDPPort(srcPort), + DstPort: layers.UDPPort(tr.trcrtConfig.Port), + } + _ = udpHeader.SetNetworkLayerForChecksum(ipHeader) + buf := gopacket.NewSerializeBuffer() + opts := gopacket.SerializeOptions{ + ComputeChecksums: true, + FixLengths: true, + } + if err := gopacket.SerializeLayers(buf, opts, udpHeader, gopacket.Payload("HAJSFJHKAJSHFKJHAJKFHKASHKFHHKAFKHFAHSJK")); err != nil { + tr.results.err = err + tr.opConfig.cancel() + return + } + + payload = buf.Bytes() + } + + err := ipv4.NewPacketConn(udpConn).SetTTL(int(ttl)) + if err != nil { + tr.results.err = err + tr.opConfig.cancel() + return + } + + icmpMsg := make(chan net.Addr, 1) + udpMsg := make(chan net.Addr, 1) + + start := time.Now() + if _, err := udpConn.WriteTo(payload, &net.UDPAddr{IP: tr.opConfig.destIP, Port: tr.trcrtConfig.Port}); err != nil { + tr.results.err = err + tr.opConfig.cancel() + return + } + + inflight := inflightData{ + icmpMsg: icmpMsg, + } + + tr.results.inflightRequests.Store(uint16(srcPort), inflight) + + go func() { + reply := make([]byte, 1500) + _, peer, err := udpConn.ReadFrom(reply) + if err != nil { + // probably because we closed the connection + return + } + udpMsg <- peer + }() + + select { + case peer := <-icmpMsg: + rtt := time.Since(start) + if peer.(*net.IPAddr).IP.Equal(tr.opConfig.destIP) { + tr.results.reachedFinalHop.Signal() + } + tr.addToResult(ttl, methods.TracerouteHop{ + Success: true, + Address: peer, + TTL: ttl, + RTT: &rtt, + }) + case peer := <-udpMsg: + rtt := time.Since(start) + ip := peer.(*net.UDPAddr).IP + if ip.Equal(tr.opConfig.destIP) { + tr.results.reachedFinalHop.Signal() + } + tr.addToResult(ttl, methods.TracerouteHop{ + Success: true, + Address: &net.IPAddr{IP: ip}, + TTL: ttl, + RTT: &rtt, + }) + case <-time.After(tr.trcrtConfig.Timeout): + tr.addToResult(ttl, methods.TracerouteHop{ + Success: false, + Address: nil, + TTL: ttl, + RTT: nil, + }) + } + + tr.results.inflightRequests.Delete(uint16(srcPort)) + udpConn.Close() + tr.results.concurrentRequests.Finished() + tr.opConfig.wg.Done() +} + +func (tr *Traceroute) handleICMPMessage(msg listener_channel.ReceivedMessage, data []byte) { + header, err := methods.GetICMPResponsePayload(data) + if err != nil { + return + } + srcPort := methods.GetUDPSrcPort(header) + val, ok := tr.results.inflightRequests.LoadAndDelete(srcPort) + if !ok { + return + } + request := val.(inflightData) + request.icmpMsg <- msg.Peer +} + +func (tr *Traceroute) icmpListener() { + lc := listener_channel.New(tr.opConfig.icmpConn) + + defer lc.Stop() + + go lc.Start() + + for { + select { + case <-tr.opConfig.ctx.Done(): + return + case msg := <-lc.Messages: + if msg.N == nil { + continue + } + rm, err := icmp.ParseMessage(1, msg.Msg[:*msg.N]) + if err != nil { + log.Println(err) + continue + } + switch rm.Type { + case ipv4.ICMPTypeTimeExceeded: + body := rm.Body.(*icmp.TimeExceeded).Data + tr.handleICMPMessage(msg, body) + case ipv4.ICMPTypeDestinationUnreachable: + body := rm.Body.(*icmp.DstUnreach).Data + tr.handleICMPMessage(msg, body) + default: + log.Println("received icmp message of unknown type", rm.Type) + } + } + } +} + +func (tr *Traceroute) sendLoop() { + rand.Seed(time.Now().UTC().UnixNano()) + + for ttl := uint16(1); ttl <= tr.trcrtConfig.MaxHops; ttl++ { + select { + case <-tr.results.reachedFinalHop.Chan(): + return + default: + } + for i := 0; i < int(tr.trcrtConfig.NumMeasurements); i++ { + select { + case <-tr.opConfig.ctx.Done(): + return + case <-tr.results.concurrentRequests.Start(): + tr.opConfig.wg.Add() + go tr.sendMessage(ttl) + } + } + } +} + +func (tr *Traceroute) start() (*map[uint16][]methods.TracerouteHop, error) { + go tr.icmpListener() + + wg := taskgroup.New() + tr.opConfig.wg = wg + + tr.sendLoop() + + wg.Wait() + + tr.opConfig.cancel() + tr.opConfig.icmpConn.Close() + + if tr.results.err != nil { + return nil, tr.results.err + } + + result := methods.ReduceFinalResult(tr.results.results, tr.trcrtConfig.MaxHops, tr.opConfig.destIP) + + return &result, tr.results.err +} diff --git a/parallel_limiter/parallel_limiter.go b/parallel_limiter/parallel_limiter.go new file mode 100644 index 0000000..a88447c --- /dev/null +++ b/parallel_limiter/parallel_limiter.go @@ -0,0 +1,52 @@ +package parallel_limiter + +import ( + "sync" +) + +type ParallelLimiter struct { + maxCount int + + mu sync.Mutex + currentRunning int + + waiting []chan struct{} +} + +func New(count int) *ParallelLimiter { + return &ParallelLimiter{ + maxCount: count, + + currentRunning: 0, + waiting: []chan struct{}{}, + } +} + +func (p *ParallelLimiter) Start() chan struct{} { + p.mu.Lock() + if p.currentRunning+1 > p.maxCount { + waitChan := make(chan struct{}) + p.waiting = append(p.waiting, waitChan) + p.mu.Unlock() + return waitChan + } + p.currentRunning++ + p.mu.Unlock() + instantResolveChan := make(chan struct{}) + go func() { + instantResolveChan <- struct{}{} + }() + return instantResolveChan +} + +func (p *ParallelLimiter) Finished() { + p.mu.Lock() + if len(p.waiting) > 0 { + first := p.waiting[0] + p.waiting = p.waiting[1:] + first <- struct{}{} + p.currentRunning++ + } + p.currentRunning-- + p.mu.Unlock() +} diff --git a/signal/signal.go b/signal/signal.go new file mode 100644 index 0000000..6095a25 --- /dev/null +++ b/signal/signal.go @@ -0,0 +1,19 @@ +package signal + +type Signal struct { + sigChan chan struct{} +} + +func New() *Signal { + return &Signal{sigChan: make(chan struct{}, 1)} +} + +func (s *Signal) Signal() { + if len(s.sigChan) == 0 { + s.sigChan <- struct{}{} + } +} + +func (s *Signal) Chan() chan struct{} { + return s.sigChan +} diff --git a/taskgroup/taskgroup.go b/taskgroup/taskgroup.go new file mode 100644 index 0000000..ca2e472 --- /dev/null +++ b/taskgroup/taskgroup.go @@ -0,0 +1,45 @@ +package taskgroup + +import ( + "sync" +) + +type TaskGroup struct { + count int + mu sync.Mutex + done []chan struct{} +} + +func New() *TaskGroup { + return &TaskGroup{ + count: 0, + mu: sync.Mutex{}, + done: []chan struct{}{}, + } +} + +func (t *TaskGroup) Add() { + t.mu.Lock() + defer t.mu.Unlock() + t.count++ +} + +func (t *TaskGroup) Done() { + t.mu.Lock() + defer t.mu.Unlock() + if t.count-1 == 0 { + for _, doneChannel := range t.done { + doneChannel <- struct{}{} + } + t.done = []chan struct{}{} + } + t.count-- +} + +func (t *TaskGroup) Wait() { + doneChannel := make(chan struct{}) + t.mu.Lock() + t.done = append(t.done, doneChannel) + t.mu.Unlock() + <-doneChannel +} diff --git a/util/util.go b/util/util.go new file mode 100644 index 0000000..81189f2 --- /dev/null +++ b/util/util.go @@ -0,0 +1,24 @@ +package util + +import ( + "log" + "net" +) + +// get the local ip and port based on our destination ip +func LocalIPPort(dstip net.IP) (net.IP, int) { + serverAddr, err := net.ResolveUDPAddr("udp", dstip.String()+":12345") + if err != nil { + log.Fatal(err) + } + + // We don't actually connect to anything, but we can determine + // based on our destination ip what source ip we should use. + if con, err := net.DialUDP("udp", nil, serverAddr); err == nil { + defer con.Close() + if udpaddr, ok := con.LocalAddr().(*net.UDPAddr); ok { + return udpaddr.IP, udpaddr.Port + } + } + return nil, -1 +}