@@ -15,8 +15,6 @@ import (
1515)
1616
1717const (
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 (
2725type 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
5860func 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
90123func (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
149183func (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
256312func (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