Skip to content

Commit 6c154ae

Browse files
committed
added ipv6 support to trace route
1 parent c4247a7 commit 6c154ae

File tree

1 file changed

+114
-44
lines changed

1 file changed

+114
-44
lines changed

icmp/trace.go

Lines changed: 114 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,6 @@ import (
1515
)
1616

1717
const (
18-
// ICMP Time Exceeded type number
19-
ICMPTypeTimeExceeded int = 11
2018
// Default TX timeout
2119
DefaultTXTimeout int64 = 1000
2220
// Default RX timeout
@@ -27,9 +25,12 @@ const (
2725
type Trace struct {
2826
src string
2927
host string
28+
ip net.IP
3029
ips []net.IP
3130
ttl int
3231
fd int
32+
family int
33+
proto int
3334
timeout int64
3435
resolve bool
3536
ripe bool
@@ -42,6 +43,7 @@ type HopResp struct {
4243
ip string
4344
elapsed float64
4445
last bool
46+
err error
4547
whois Whois
4648
}
4749

@@ -56,8 +58,14 @@ type MHopResp []HopResp
5658

5759
// NewTrace creates new trace object
5860
func NewTrace(args string) (*Trace, error) {
61+
var (
62+
family int
63+
proto int
64+
ip net.IP
65+
)
5966
target, flag := cli.Flag(args)
60-
67+
forceIPv4 := cli.SetFlag(flag, "4", false).(bool)
68+
forceIPv6 := cli.SetFlag(flag, "6", false).(bool)
6169
// show help
6270
if _, ok := flag["help"]; ok || len(target) < 3 {
6371
helpTrace()
@@ -67,9 +75,34 @@ func NewTrace(args string) (*Trace, error) {
6775
if err != nil {
6876
return nil, err
6977
}
78+
for _, IP := range ips {
79+
if IsIPv4(IP) && !forceIPv6 {
80+
ip = IP
81+
break
82+
} else if IsIPv6(IP) && !forceIPv4 {
83+
ip = IP
84+
break
85+
}
86+
}
87+
88+
if ip == nil {
89+
return nil, fmt.Errorf("there is not A or AAAA record")
90+
}
91+
92+
if IsIPv4(ip) {
93+
family = syscall.AF_INET
94+
proto = syscall.IPPROTO_ICMP
95+
} else {
96+
family = syscall.AF_INET6
97+
proto = syscall.IPPROTO_ICMPV6
98+
}
99+
70100
return &Trace{
71101
host: target,
72102
ips: ips,
103+
ip: ip,
104+
family: family,
105+
proto: proto,
73106
timeout: DefaultRXTimeout,
74107
resolve: cli.SetFlag(flag, "n", true).(bool),
75108
ripe: cli.SetFlag(flag, "nr", true).(bool),
@@ -88,31 +121,32 @@ func (i *Trace) SetTTL(ttl int) {
88121

89122
// Send tries to send an UDP packet
90123
func (i *Trace) Send() error {
91-
fd, err := syscall.Socket(syscall.AF_INET, syscall.SOCK_DGRAM, syscall.IPPROTO_UDP)
124+
fd, err := syscall.Socket(i.family, syscall.SOCK_DGRAM, syscall.IPPROTO_UDP)
92125
if err != nil {
93126
println(err.Error())
94127
}
95128
defer syscall.Close(fd)
96129
// Set options
97-
syscall.SetsockoptInt(fd, 0x0, syscall.IP_TTL, i.ttl)
98-
99-
if IsIPv4(i.ips[0]) {
130+
if IsIPv4(i.ip) {
100131
var b [4]byte
101-
copy(b[:], i.ips[0].To4())
132+
copy(b[:], i.ip.To4())
102133
addr := syscall.SockaddrInet4{
103134
Port: 33434,
104135
Addr: b,
105136
}
137+
syscall.SetsockoptInt(fd, 0x0, syscall.IP_TTL, i.ttl)
106138
if err := syscall.Sendto(fd, []byte{0x0}, 0, &addr); err != nil {
107139
return err
108140
}
109-
} else if IsIPv6(i.ips[0]) {
141+
} else {
110142
var b [16]byte
111-
copy(b[:], i.ips[0].To16())
143+
copy(b[:], i.ip.To16())
112144
addr := syscall.SockaddrInet6{
113-
Port: 33434,
114-
Addr: b,
145+
Port: 33434,
146+
ZoneId: 0,
147+
Addr: b,
115148
}
149+
syscall.SetsockoptInt(fd, syscall.IPPROTO_IPV6, syscall.IP_TTL, i.ttl)
116150
if err := syscall.Sendto(fd, []byte{0x0}, 0, &addr); err != nil {
117151
return err
118152
}
@@ -148,23 +182,35 @@ func (i *Trace) SetDeadLine() error {
148182
// Bind starts to listen for ICMP reply
149183
func (i *Trace) Bind() {
150184
var err error
151-
i.fd, err = syscall.Socket(syscall.AF_INET, syscall.SOCK_RAW, syscall.IPPROTO_ICMP)
185+
i.fd, err = syscall.Socket(i.family, syscall.SOCK_RAW, i.proto)
152186
if err != nil {
153187
println("e2", err.Error())
154188
}
155189
err = i.SetDeadLine()
156190
if err != nil {
157191
println(err.Error())
158192
}
159-
// TODO: IPv6
160-
b := net.ParseIP("0.0.0.0").To4()
161-
addr := syscall.SockaddrInet4{
162-
Port: 0,
163-
Addr: [4]byte{b[0], b[1], b[2], b[3]},
164-
}
165193

166-
if err := syscall.Bind(i.fd, &addr); err != nil {
167-
println("e3", err.Error())
194+
if i.family == syscall.AF_INET {
195+
addr := syscall.SockaddrInet4{
196+
Port: 0,
197+
Addr: [4]byte{},
198+
}
199+
200+
if err := syscall.Bind(i.fd, &addr); err != nil {
201+
println("e3", err.Error())
202+
}
203+
} else {
204+
addr := syscall.SockaddrInet6{
205+
Port: 0,
206+
ZoneId: 0,
207+
Addr: [16]byte{},
208+
}
209+
210+
if err := syscall.Bind(i.fd, &addr); err != nil {
211+
println("e3", err.Error())
212+
}
213+
168214
}
169215
}
170216

@@ -178,9 +224,13 @@ func (i *Trace) Recv(fd int) (int, int, string) {
178224
typ = int(buf[20]) // ICMP Type
179225
code = int(buf[21]) // ICMP Code
180226
}
181-
if typ != 0 {
182-
b := from.(*syscall.SockaddrInet4).Addr
183-
return typ, code, fmt.Sprintf("%v.%v.%v.%v", b[0], b[1], b[2], b[3])
227+
if i.family == syscall.AF_INET && typ != 0 {
228+
fromAddrStr := net.IP((from.(*syscall.SockaddrInet4).Addr)[:]).String()
229+
return typ, code, fromAddrStr
230+
}
231+
if i.family == syscall.AF_INET6 && typ != 0 {
232+
fromAddrStr := net.IP((from.(*syscall.SockaddrInet6).Addr)[:]).String()
233+
return typ, code, fromAddrStr
184234
}
185235
return typ, code, ""
186236
}
@@ -200,21 +250,21 @@ func (i *Trace) NextHop(fd, hop int) HopResp {
200250
ts := time.Now().UnixNano()
201251
err := i.Send()
202252
if err != nil {
203-
println("e5", err.Error())
253+
return HopResp{err: err}
204254
}
205255
t, _, ip := i.Recv(fd)
206256
elapsed := time.Now().UnixNano() - ts
207-
if t == ICMPTypeTimeExceeded || ip == i.ips[0].String() {
257+
if t == 64 || t == 11 || ip == i.ip.String() {
208258
if i.resolve {
209259
name, _ = net.LookupAddr(ip)
210260
}
211261
if len(name) > 0 {
212-
r = HopResp{name[0], ip, float64(elapsed) / 1e6, false, Whois{}}
262+
r = HopResp{name[0], ip, float64(elapsed) / 1e6, false, nil, Whois{}}
213263
} else {
214-
r = HopResp{"", ip, float64(elapsed) / 1e6, false, Whois{}}
264+
r = HopResp{"", ip, float64(elapsed) / 1e6, false, nil, Whois{}}
215265
}
216266
}
217-
// readed to the target
267+
// reached to the target
218268
for _, h := range i.ips {
219269
if ip == h.String() {
220270
r.last = true
@@ -232,18 +282,24 @@ func (i *Trace) Run(retry int) chan []HopResp {
232282
)
233283
i.Bind()
234284
go func() {
285+
LOOP:
235286
for h := 1; h <= i.maxTTL; h++ {
236287
for n := 0; n < retry; n++ {
237-
r = append(r, i.NextHop(i.fd, h))
288+
hop := i.NextHop(i.fd, h)
289+
r = append(r, hop)
290+
if hop.err != nil {
291+
break
292+
}
238293
}
239294
if i.ripe {
240-
appendWhois(r[:])
295+
i.appendWhois(r[:])
241296
}
242297
c <- r
243-
if r[0].last || r[1].last || r[2].last {
244-
break
298+
for _, R := range r {
299+
if R.last || R.err != nil {
300+
break LOOP
301+
}
245302
}
246-
247303
r = r[:0]
248304
}
249305
close(c)
@@ -255,7 +311,6 @@ func (i *Trace) Run(retry int) chan []HopResp {
255311
// PrintPretty prints out trace result
256312
func (i *Trace) PrintPretty() {
257313
var (
258-
loop = true
259314
counter int
260315
sigCh = make(chan os.Signal, 1)
261316
resp = i.Run(3)
@@ -265,14 +320,19 @@ func (i *Trace) PrintPretty() {
265320
defer signal.Stop(sigCh)
266321

267322
// header
268-
fmt.Printf("trace route to %s (%s), %d hops max\n", i.host, i.ips[0], i.maxTTL)
269-
270-
for loop {
323+
fmt.Printf("trace route to %s (%s), %d hops max\n", i.host, i.ip, i.maxTTL)
324+
LOOP:
325+
for {
271326
select {
272327
case r, ok := <-resp:
273328
if !ok {
274-
loop = false
275-
break
329+
break LOOP
330+
}
331+
for _, R := range r {
332+
if R.err != nil {
333+
println(R.err.Error())
334+
break LOOP
335+
}
276336
}
277337
counter++
278338
sort.Sort(MHopResp(r))
@@ -313,7 +373,7 @@ func (i *Trace) PrintPretty() {
313373
}
314374
//fmt.Printf("%#v\n", r)
315375
case <-sigCh:
316-
loop = false
376+
break LOOP
317377
}
318378
}
319379
}
@@ -353,16 +413,24 @@ func fmtHops(m []HopResp, newLine int) string {
353413
}
354414

355415
// appendWhois adds whois info to response if available
356-
func appendWhois(R []HopResp) {
357-
var ips = make(map[string]Whois, 3)
416+
func (i *Trace) appendWhois(R []HopResp) {
417+
var (
418+
ips = make(map[string]Whois, 3)
419+
w Whois
420+
err error
421+
)
358422
for _, r := range R {
359423
ips[r.ip] = Whois{}
360424
}
361425
for ip, _ := range ips {
362426
if ip == "" {
363427
continue
364428
}
365-
w, err := whois(ip + "/24")
429+
if i.family != syscall.AF_INET6 {
430+
w, err = whois(ip)
431+
} else {
432+
w, err = whois(ip)
433+
}
366434
if err != nil {
367435
continue
368436
}
@@ -399,6 +467,8 @@ func helpTrace() {
399467
-n Do not try to map IP addresses to host names
400468
-nr Do not try to map IP addresses to ASN,Holder (RIPE NCC)
401469
-m MAX_TTL Specifies the maximum number of hops
470+
-4 Forces the trace command to use IPv4 (target should be hostname)
471+
-6 Forces the trace command to use IPv6 (target should be hostname)
402472
Example:
403473
trace 8.8.8.8
404474
`)

0 commit comments

Comments
 (0)