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

V6 Tracing

parent 62140855
No related branches found
No related tags found
No related merge requests found
......@@ -12,6 +12,7 @@ stages:
- test
before_script:
- go get -u golang.org/x/lint/golint
unit_tests:
stage: test
......
......@@ -39,6 +39,17 @@ pkttyagent --process $(ps -xa | grep "netbeans" )
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
The recommended way to install this package is
......
......@@ -6,4 +6,5 @@ require (
github.com/creasty/defaults v1.5.1
github.com/jackpal/gateway v1.0.7
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
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/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-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/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
package traceroute
import (
"strings"
"testing"
)
......@@ -8,7 +9,11 @@ func TestGetOutboundIPV6(t *testing.T) {
ip, err := getOutboundIP(addressV6)
if err != nil {
t.Errorf("this call should not return an error " + err.Error())
if !strings.Contains(err.Error(), "no suitable interface was found") {
t.Errorf("this call should not return an error " + err.Error())
}
return
}
if !ip.isV6() {
......@@ -22,7 +27,12 @@ func TestGetOutboundIPV4(t *testing.T) {
ip, err := getOutboundIP(addressV4)
if err != nil {
t.Errorf("this call should not return an error " + err.Error())
if !strings.Contains(err.Error(), "no suitable interface was found") {
t.Errorf("this call should not return an error " + err.Error())
}
return
}
if !ip.isV4() {
......
......@@ -4,6 +4,7 @@ import (
"github.com/creasty/defaults"
"golang.org/x/net/icmp"
"golang.org/x/net/ipv4"
"golang.org/x/net/ipv6"
"time"
)
......@@ -14,12 +15,15 @@ type Session struct {
CallBack func(result Result)
Timeout time.Duration `default:"2"` // in seconds
Timeout time.Duration `default:"5"` // in seconds
MaxHops int `default:"30"`
Mode int
nextHop int
isFinale bool
ipV6Sock *ipv6.PacketConn
ipV4Sock *ipv4.PacketConn
icmpEcho icmp.Message
readBuffer []byte
......@@ -40,18 +44,21 @@ func NewSession(destination string) (*Session, error) {
}
s.Destination = dest
var mode int
if dest.isV6() {
mode = addressV6
s.Mode = addressV6
} else {
mode = addressV4
s.Mode = addressV4
}
s.Timeout = s.Timeout*time.Second
//* time.Second
src, err := getOutboundIP(mode)
src, err := getOutboundIP(s.Mode)
if err!=nil {
return nil, err
}
s.Source = src
return &s, nil
......
......@@ -5,12 +5,20 @@ import (
"fmt"
"golang.org/x/net/icmp"
"golang.org/x/net/ipv4"
"golang.org/x/net/ipv6"
"math/rand"
"net"
"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 {
Hop int
Station string
......@@ -23,7 +31,7 @@ type Results struct {
Hops []Result
}
func (s *Session) doHop(i int) Result {
func (s *Session) doV4Hop(i int) Result {
s.icmpEcho.Body.(*icmp.Echo).Seq = i
r := Result{
......@@ -62,42 +70,123 @@ func (s *Session) doHop(i int) Result {
return r
}
readBytes, _, hopNode, err := s.ipV4Sock.ReadFrom(s.readBuffer)
for ; ; {
readBytes, _, hopNode, err := s.ipV4Sock.ReadFrom(s.readBuffer)
if hopNode != nil {
r.Station = hopNode.String()
}
if err != nil {
r.Err = err
return r
}
icmpAnswer, err := icmp.ParseMessage(ProtocolICMP, s.readBuffer[:readBytes])
if err != nil {
r.Err = err
return r
}
latency := time.Since(timeNow)
r.Latency = latency
if icmpAnswer.Type == ipv4.ICMPTypeTimeExceeded {
s.nextHop++
return r
}
if icmpAnswer.Type == ipv4.ICMPTypeEchoReply {
s.isFinale = true
return r
}
if hopNode != nil {
r.Station = hopNode.String()
}
if err != nil {
r.Err = err
return r
}
func (s *Session) doV6Hop(i int) Result {
s.icmpEcho.Body.(*icmp.Echo).Seq = i
r := Result{
Hop: i,
Station: "*",
}
icmpAnswer, err := icmp.ParseMessage(1, s.readBuffer[:readBytes])
writeBuffer, err := s.icmpEcho.Marshal(nil)
if err != nil {
r.Err = err
return r
}
latency := time.Since(timeNow)
r.Latency = latency
if err := s.ipV6Sock.SetHopLimit(i); err != nil {
r.Err = fmt.Errorf("socket: %w", err)
return r
}
if icmpAnswer.Type == ipv4.ICMPTypeTimeExceeded {
s.nextHop++
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 icmpAnswer.Type == ipv4.ICMPTypeEchoReply {
s.isFinale = true
if err := s.ipV6Sock.SetReadDeadline(time.Now().Add(s.Timeout)); err != nil {
r.Err = err
return r
}
r.Err = fmt.Errorf("unknown icmp answer: %d", icmpAnswer.Type.Protocol())
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
}
if icmpAnswer.Type == ipv6.ICMPTypeDestinationUnreachable {
s.nextHop++
return r
}
if icmpAnswer.Type == ipv6.ICMPTypeEchoReply {
s.isFinale = true
return r
}
}
return r
}
// TraceRouteV4 run V4 Traceroute
func (s *Session) TraceRouteV4() (*Results, error) {
sock, err := net.ListenPacket("ip4:icmp", s.Source.ip.String())
......@@ -107,7 +196,6 @@ func (s *Session) TraceRouteV4() (*Results, error) {
}
defer sock.Close()
s.ipV4Sock = ipv4.NewPacketConn(sock)
defer s.ipV4Sock.Close()
......@@ -119,14 +207,20 @@ func (s *Session) TraceRouteV4() (*Results, error) {
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)
results := Results{}
for i := 1; i < s.MaxHops; i++ {
r:=s.doHop(i)
r := cb(i)
results.Hops = append(results.Hops, r)
if s.CallBack!=nil {
if s.CallBack != nil {
s.CallBack(r)
}
......@@ -138,10 +232,35 @@ func (s *Session) TraceRouteV4() (*Results, error) {
return &results, nil
}
// currently not implemented
//func (s *Session) traceRouteV6() error {
// return nil
//}
// TraceRouteV6 run V6 trace
func (s *Session) TraceRouteV6() (*Results, error) {
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
func (s *Session) TraceRoute() (*Results, error) {
......@@ -154,12 +273,13 @@ func (s *Session) TraceRoute() (*Results, error) {
return results, nil
}
// currently not implemented
//if s.Destination.isV6() {
// if err := s.traceRouteV6(); err != nil {
// return nil, err
// }
//}
if s.Destination.isV6() {
results, err := s.TraceRouteV6()
if err != nil {
return nil, err
}
return results, nil
}
return nil, errors.New("could not traceroute " + s.Destination.String())
......
......@@ -2,6 +2,7 @@ package traceroute
import (
"fmt"
"strings"
"testing"
)
......@@ -12,6 +13,9 @@ func TestTraceroute(t *testing.T) {
shouldSessionError bool
}{
{
"ipv6.google.com",
false,
}, {
"www.schukai.com",
false,
},
......@@ -23,7 +27,10 @@ func TestTraceroute(t *testing.T) {
session, err := NewSession(tc.destination)
if err != nil {
t.Errorf("%s shout not error %s", tc.destination, err.Error())
if !strings.Contains(err.Error(), "no suitable interface was found") {
t.Errorf("%s shout not error %s", tc.destination, err.Error())
}
return
}
_,err = session.TraceRoute()
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment