Skip to content
Snippets Groups Projects
Commit da5880fa authored by Volker Schukai's avatar Volker Schukai :alien:
Browse files

V6 Tracing

parent 62140855
Branches
Tags
No related merge requests found
...@@ -12,6 +12,7 @@ stages: ...@@ -12,6 +12,7 @@ stages:
- test - test
before_script: before_script:
- go get -u golang.org/x/lint/golint
unit_tests: unit_tests:
stage: test stage: test
......
...@@ -39,6 +39,17 @@ pkttyagent --process $(ps -xa | grep "netbeans" ) ...@@ -39,6 +39,17 @@ pkttyagent --process $(ps -xa | grep "netbeans" )
pkttyagent --process $(ps -xa | grep "IntelliJ-IDEA-Ultimate/jbr/bin/java" ) pkttyagent --process $(ps -xa | grep "IntelliJ-IDEA-Ultimate/jbr/bin/java" )
``` ```
## Test
`/etc/sudoers` has restricted PATH environment variable.
When you run `sudo make test`, `/usr/local/go/bin` must be entered in
`secure_path`.
```
Defaults secure_path="....:/usr/local/go/bin"
```
## Installation ## Installation
The recommended way to install this package is The recommended way to install this package is
......
...@@ -6,4 +6,5 @@ require ( ...@@ -6,4 +6,5 @@ require (
github.com/creasty/defaults v1.5.1 github.com/creasty/defaults v1.5.1
github.com/jackpal/gateway v1.0.7 github.com/jackpal/gateway v1.0.7
golang.org/x/net v0.0.0-20210510120150-4163338589ed golang.org/x/net v0.0.0-20210510120150-4163338589ed
golang.org/x/sys v0.0.0-20210510120138-977fb7262007 // indirect
) )
...@@ -5,8 +5,9 @@ github.com/jackpal/gateway v1.0.7/go.mod h1:aRcO0UFKt+MgIZmRmvOmnejdDT4Y1DNiNOsS ...@@ -5,8 +5,9 @@ github.com/jackpal/gateway v1.0.7/go.mod h1:aRcO0UFKt+MgIZmRmvOmnejdDT4Y1DNiNOsS
golang.org/x/net v0.0.0-20210510120150-4163338589ed h1:p9UgmWI9wKpfYmgaV/IZKGdXc5qEK45tDwwwDyjS26I= golang.org/x/net v0.0.0-20210510120150-4163338589ed h1:p9UgmWI9wKpfYmgaV/IZKGdXc5qEK45tDwwwDyjS26I=
golang.org/x/net v0.0.0-20210510120150-4163338589ed/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20210510120150-4163338589ed/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210423082822-04245dca01da h1:b3NXsE2LusjYGGjL5bxEVZZORm/YEFFrWFjR8eFrw/c=
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210510120138-977fb7262007 h1:gG67DSER+11cZvqIMb8S8bt0vZtiN6xWYARwirrOSfE=
golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.6/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-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
package traceroute package traceroute
import ( import (
"strings"
"testing" "testing"
) )
...@@ -8,8 +9,12 @@ func TestGetOutboundIPV6(t *testing.T) { ...@@ -8,8 +9,12 @@ func TestGetOutboundIPV6(t *testing.T) {
ip, err := getOutboundIP(addressV6) ip, err := getOutboundIP(addressV6)
if err != nil { if err != nil {
if !strings.Contains(err.Error(), "no suitable interface was found") {
t.Errorf("this call should not return an error " + err.Error()) t.Errorf("this call should not return an error " + err.Error())
} }
return
}
if !ip.isV6() { if !ip.isV6() {
t.Errorf("this call should not return an error " + err.Error()) t.Errorf("this call should not return an error " + err.Error())
...@@ -22,8 +27,13 @@ func TestGetOutboundIPV4(t *testing.T) { ...@@ -22,8 +27,13 @@ func TestGetOutboundIPV4(t *testing.T) {
ip, err := getOutboundIP(addressV4) ip, err := getOutboundIP(addressV4)
if err != nil { if err != nil {
if !strings.Contains(err.Error(), "no suitable interface was found") {
t.Errorf("this call should not return an error " + err.Error()) t.Errorf("this call should not return an error " + err.Error())
} }
return
}
if !ip.isV4() { if !ip.isV4() {
t.Errorf("this call should not return an error " + err.Error()) t.Errorf("this call should not return an error " + err.Error())
......
...@@ -4,6 +4,7 @@ import ( ...@@ -4,6 +4,7 @@ import (
"github.com/creasty/defaults" "github.com/creasty/defaults"
"golang.org/x/net/icmp" "golang.org/x/net/icmp"
"golang.org/x/net/ipv4" "golang.org/x/net/ipv4"
"golang.org/x/net/ipv6"
"time" "time"
) )
...@@ -14,12 +15,15 @@ type Session struct { ...@@ -14,12 +15,15 @@ type Session struct {
CallBack func(result Result) CallBack func(result Result)
Timeout time.Duration `default:"2"` // in seconds Timeout time.Duration `default:"5"` // in seconds
MaxHops int `default:"30"` MaxHops int `default:"30"`
Mode int
nextHop int nextHop int
isFinale bool isFinale bool
ipV6Sock *ipv6.PacketConn
ipV4Sock *ipv4.PacketConn ipV4Sock *ipv4.PacketConn
icmpEcho icmp.Message icmpEcho icmp.Message
readBuffer []byte readBuffer []byte
...@@ -40,18 +44,21 @@ func NewSession(destination string) (*Session, error) { ...@@ -40,18 +44,21 @@ func NewSession(destination string) (*Session, error) {
} }
s.Destination = dest s.Destination = dest
var mode int
if dest.isV6() { if dest.isV6() {
mode = addressV6 s.Mode = addressV6
} else { } else {
mode = addressV4 s.Mode = addressV4
} }
s.Timeout = s.Timeout*time.Second s.Timeout = s.Timeout*time.Second
//* time.Second //* time.Second
src, err := getOutboundIP(mode) src, err := getOutboundIP(s.Mode)
if err!=nil {
return nil, err
}
s.Source = src s.Source = src
return &s, nil return &s, nil
......
...@@ -5,12 +5,20 @@ import ( ...@@ -5,12 +5,20 @@ import (
"fmt" "fmt"
"golang.org/x/net/icmp" "golang.org/x/net/icmp"
"golang.org/x/net/ipv4" "golang.org/x/net/ipv4"
"golang.org/x/net/ipv6"
"math/rand" "math/rand"
"net" "net"
"time" "time"
) )
// from internal/iana/const.go
// Protocol Numbers, Updated: 2017-10-13
const (
ProtocolIPv6ICMP = 58 // ICMP for IPv6
ProtocolICMP = 1 // Internet Control Message
)
// Result holds the data
type Result struct { type Result struct {
Hop int Hop int
Station string Station string
...@@ -23,7 +31,7 @@ type Results struct { ...@@ -23,7 +31,7 @@ type Results struct {
Hops []Result Hops []Result
} }
func (s *Session) doHop(i int) Result { func (s *Session) doV4Hop(i int) Result {
s.icmpEcho.Body.(*icmp.Echo).Seq = i s.icmpEcho.Body.(*icmp.Echo).Seq = i
r := Result{ r := Result{
...@@ -62,6 +70,7 @@ func (s *Session) doHop(i int) Result { ...@@ -62,6 +70,7 @@ func (s *Session) doHop(i int) Result {
return r return r
} }
for ; ; {
readBytes, _, hopNode, err := s.ipV4Sock.ReadFrom(s.readBuffer) readBytes, _, hopNode, err := s.ipV4Sock.ReadFrom(s.readBuffer)
if hopNode != nil { if hopNode != nil {
...@@ -73,7 +82,7 @@ func (s *Session) doHop(i int) Result { ...@@ -73,7 +82,7 @@ func (s *Session) doHop(i int) Result {
return r return r
} }
icmpAnswer, err := icmp.ParseMessage(1, s.readBuffer[:readBytes]) icmpAnswer, err := icmp.ParseMessage(ProtocolICMP, s.readBuffer[:readBytes])
if err != nil { if err != nil {
r.Err = err r.Err = err
...@@ -93,11 +102,91 @@ func (s *Session) doHop(i int) Result { ...@@ -93,11 +102,91 @@ func (s *Session) doHop(i int) Result {
return r return r
} }
r.Err = fmt.Errorf("unknown icmp answer: %d", icmpAnswer.Type.Protocol()) }
}
func (s *Session) doV6Hop(i int) Result {
s.icmpEcho.Body.(*icmp.Echo).Seq = i
r := Result{
Hop: i,
Station: "*",
}
writeBuffer, err := s.icmpEcho.Marshal(nil)
if err != nil {
r.Err = err
return r
}
if err := s.ipV6Sock.SetHopLimit(i); err != nil {
r.Err = fmt.Errorf("socket: %w", err)
return r
}
timeNow := time.Now()
dst := s.Destination
a := net.IPAddr{
IP: dst.ip,
Zone: "",
}
if _, err := s.ipV6Sock.WriteTo(writeBuffer, nil, &a); err != nil {
r.Err = err
return r
}
if err := s.ipV6Sock.SetReadDeadline(time.Now().Add(s.Timeout)); err != nil {
r.Err = err
return r
}
for ; ; {
readBytes, _, hopNode, err := s.ipV6Sock.ReadFrom(s.readBuffer)
if hopNode != nil {
r.Station = hopNode.String()
}
if err != nil {
r.Err = err
return r
}
icmpAnswer, err := icmp.ParseMessage(ProtocolIPv6ICMP, s.readBuffer[:readBytes])
if err != nil {
r.Err = err
return r
}
latency := time.Since(timeNow)
r.Latency = latency
if icmpAnswer.Type == ipv6.ICMPTypeTimeExceeded {
s.nextHop++
return r return r
} }
if icmpAnswer.Type == ipv6.ICMPTypeDestinationUnreachable {
s.nextHop++
return r
}
if icmpAnswer.Type == ipv6.ICMPTypeEchoReply {
s.isFinale = true
return r
}
}
}
// TraceRouteV4 run V4 Traceroute
func (s *Session) TraceRouteV4() (*Results, error) { func (s *Session) TraceRouteV4() (*Results, error) {
sock, err := net.ListenPacket("ip4:icmp", s.Source.ip.String()) sock, err := net.ListenPacket("ip4:icmp", s.Source.ip.String())
...@@ -107,7 +196,6 @@ func (s *Session) TraceRouteV4() (*Results, error) { ...@@ -107,7 +196,6 @@ func (s *Session) TraceRouteV4() (*Results, error) {
} }
defer sock.Close() defer sock.Close()
s.ipV4Sock = ipv4.NewPacketConn(sock) s.ipV4Sock = ipv4.NewPacketConn(sock)
defer s.ipV4Sock.Close() defer s.ipV4Sock.Close()
...@@ -119,12 +207,18 @@ func (s *Session) TraceRouteV4() (*Results, error) { ...@@ -119,12 +207,18 @@ func (s *Session) TraceRouteV4() (*Results, error) {
Type: ipv4.ICMPTypeEcho, Code: 0, Body: &icmp.Echo{ID: rand.Int(), Data: []byte("")}, Type: ipv4.ICMPTypeEcho, Code: 0, Body: &icmp.Echo{ID: rand.Int(), Data: []byte("")},
} }
return s.doTrace(func(i int) Result {
return s.doV4Hop(i)
})
}
func (s *Session) doTrace(cb func(i int) Result) (*Results, error) {
s.readBuffer = make([]byte, 1500) s.readBuffer = make([]byte, 1500)
results := Results{} results := Results{}
for i := 1; i < s.MaxHops; i++ { for i := 1; i < s.MaxHops; i++ {
r:=s.doHop(i) r := cb(i)
results.Hops = append(results.Hops, r) results.Hops = append(results.Hops, r)
if s.CallBack != nil { if s.CallBack != nil {
s.CallBack(r) s.CallBack(r)
...@@ -138,10 +232,35 @@ func (s *Session) TraceRouteV4() (*Results, error) { ...@@ -138,10 +232,35 @@ func (s *Session) TraceRouteV4() (*Results, error) {
return &results, nil return &results, nil
} }
// currently not implemented // TraceRouteV6 run V6 trace
//func (s *Session) traceRouteV6() error { func (s *Session) TraceRouteV6() (*Results, error) {
// return nil
//} if s.Source.ip==nil {
return nil, errors.New("missing source ip")
}
sock, err := net.ListenPacket("ip6:ipv6-icmp", s.Source.ip.String())
if err != nil {
return nil, err
}
defer sock.Close()
s.ipV6Sock = ipv6.NewPacketConn(sock)
defer s.ipV6Sock.Close()
if err := s.ipV6Sock.SetControlMessage(ipv6.FlagHopLimit|ipv6.FlagDst|ipv6.FlagInterface|ipv6.FlagSrc, true); err != nil {
return nil, err
}
s.icmpEcho = icmp.Message{
Type: ipv6.ICMPTypeEchoRequest, Code: 0, Body: &icmp.Echo{ID: rand.Int(), Data: []byte("")},
}
return s.doTrace(func(i int) Result {
return s.doV6Hop(i)
})
}
// TraceRoute measures the steps to the target host // TraceRoute measures the steps to the target host
func (s *Session) TraceRoute() (*Results, error) { func (s *Session) TraceRoute() (*Results, error) {
...@@ -154,12 +273,13 @@ func (s *Session) TraceRoute() (*Results, error) { ...@@ -154,12 +273,13 @@ func (s *Session) TraceRoute() (*Results, error) {
return results, nil return results, nil
} }
// currently not implemented if s.Destination.isV6() {
//if s.Destination.isV6() { results, err := s.TraceRouteV6()
// if err := s.traceRouteV6(); err != nil { if err != nil {
// return nil, err return nil, err
// } }
//} return results, nil
}
return nil, errors.New("could not traceroute " + s.Destination.String()) return nil, errors.New("could not traceroute " + s.Destination.String())
......
...@@ -2,6 +2,7 @@ package traceroute ...@@ -2,6 +2,7 @@ package traceroute
import ( import (
"fmt" "fmt"
"strings"
"testing" "testing"
) )
...@@ -12,6 +13,9 @@ func TestTraceroute(t *testing.T) { ...@@ -12,6 +13,9 @@ func TestTraceroute(t *testing.T) {
shouldSessionError bool shouldSessionError bool
}{ }{
{ {
"ipv6.google.com",
false,
}, {
"www.schukai.com", "www.schukai.com",
false, false,
}, },
...@@ -23,8 +27,11 @@ func TestTraceroute(t *testing.T) { ...@@ -23,8 +27,11 @@ func TestTraceroute(t *testing.T) {
session, err := NewSession(tc.destination) session, err := NewSession(tc.destination)
if err != nil { if err != nil {
if !strings.Contains(err.Error(), "no suitable interface was found") {
t.Errorf("%s shout not error %s", tc.destination, err.Error()) t.Errorf("%s shout not error %s", tc.destination, err.Error())
} }
return
}
_,err = session.TraceRoute() _,err = session.TraceRoute()
if err != nil { if err != nil {
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment