github.com/nxtrace/NTrace-core@v1.3.1-0.20240513132635-39169291e8c9/trace/icmp_ipv6.go (about)

     1  package trace
     2  
     3  import (
     4  	"bytes"
     5  	"encoding/binary"
     6  	"log"
     7  	"net"
     8  	"os"
     9  	"strconv"
    10  	"sync"
    11  	"time"
    12  
    13  	"golang.org/x/net/context"
    14  	"golang.org/x/net/icmp"
    15  	"golang.org/x/net/ipv6"
    16  
    17  	"github.com/nxtrace/NTrace-core/trace/internal"
    18  )
    19  
    20  type ICMPTracerv6 struct {
    21  	Config
    22  	wg                    sync.WaitGroup
    23  	res                   Result
    24  	ctx                   context.Context
    25  	resCh                 chan Hop
    26  	inflightRequest       map[int]chan Hop
    27  	inflightRequestRWLock sync.RWMutex
    28  	icmpListen            net.PacketConn
    29  	final                 int
    30  	finalLock             sync.Mutex
    31  	fetchLock             sync.Mutex
    32  }
    33  
    34  func (t *ICMPTracerv6) PrintFunc() {
    35  	defer t.wg.Done()
    36  	var ttl = t.Config.BeginHop - 1
    37  	for {
    38  		if t.AsyncPrinter != nil {
    39  			t.AsyncPrinter(&t.res)
    40  		}
    41  
    42  		// 接收的时候检查一下是不是 3 跳都齐了
    43  		if len(t.res.Hops)-1 > ttl {
    44  			if len(t.res.Hops[ttl]) == t.NumMeasurements {
    45  				if t.RealtimePrinter != nil {
    46  					t.RealtimePrinter(&t.res, ttl)
    47  				}
    48  				ttl++
    49  
    50  				if ttl == t.final-1 || ttl >= t.MaxHops-1 {
    51  					return
    52  				}
    53  			}
    54  		}
    55  		<-time.After(200 * time.Millisecond)
    56  	}
    57  }
    58  
    59  func (t *ICMPTracerv6) Execute() (*Result, error) {
    60  	t.inflightRequestRWLock.Lock()
    61  	t.inflightRequest = make(map[int]chan Hop)
    62  	t.inflightRequestRWLock.Unlock()
    63  
    64  	if len(t.res.Hops) > 0 {
    65  		return &t.res, ErrTracerouteExecuted
    66  	}
    67  
    68  	var err error
    69  
    70  	t.icmpListen, err = internal.ListenICMP("ip6:58", t.SrcAddr)
    71  	if err != nil {
    72  		return &t.res, err
    73  	}
    74  	defer t.icmpListen.Close()
    75  
    76  	var cancel context.CancelFunc
    77  	t.ctx, cancel = context.WithCancel(context.Background())
    78  	defer cancel()
    79  	t.resCh = make(chan Hop)
    80  	t.final = -1
    81  
    82  	go t.listenICMP()
    83  	t.wg.Add(1)
    84  	go t.PrintFunc()
    85  	for ttl := t.BeginHop; ttl <= t.MaxHops; ttl++ {
    86  		t.inflightRequestRWLock.Lock()
    87  		t.inflightRequest[ttl] = make(chan Hop, t.NumMeasurements)
    88  		t.inflightRequestRWLock.Unlock()
    89  		if t.final != -1 && ttl > t.final {
    90  			break
    91  		}
    92  		for i := 0; i < t.NumMeasurements; i++ {
    93  			t.wg.Add(1)
    94  			go t.send(ttl)
    95  			<-time.After(time.Millisecond * time.Duration(t.Config.PacketInterval))
    96  		}
    97  		<-time.After(time.Millisecond * time.Duration(t.Config.TTLInterval))
    98  	}
    99  	// for ttl := t.BeginHop; ttl <= t.MaxHops; ttl++ {
   100  	// 	if t.final != -1 && ttl > t.final {
   101  	// 		break
   102  	// 	}
   103  	// 	for i := 0; i < t.NumMeasurements; i++ {
   104  	// 		t.wg.Add(1)
   105  	// 		go t.send(ttl)
   106  	// 	}
   107  	// 	// 一组TTL全部退出(收到应答或者超时终止)以后,再进行下一个TTL的包发送
   108  	// 	t.wg.Wait()
   109  	// 	if t.RealtimePrinter != nil {
   110  	// 		t.RealtimePrinter(&t.res, ttl-1)
   111  	// 	}
   112  
   113  	// 	if t.AsyncPrinter != nil {
   114  	// 		t.AsyncPrinter(&t.res)
   115  	// 	}
   116  	// }
   117  	t.wg.Wait()
   118  	t.res.reduce(t.final)
   119  	if t.final != -1 {
   120  		if t.RealtimePrinter != nil {
   121  			t.RealtimePrinter(&t.res, t.final-1)
   122  		}
   123  	} else {
   124  		for i := 0; i < t.NumMeasurements; i++ {
   125  			t.res.add(Hop{
   126  				Success: false,
   127  				Address: nil,
   128  				TTL:     30,
   129  				RTT:     0,
   130  				Error:   ErrHopLimitTimeout,
   131  			})
   132  		}
   133  		if t.RealtimePrinter != nil {
   134  			t.RealtimePrinter(&t.res, t.MaxHops-1)
   135  		}
   136  	}
   137  
   138  	return &t.res, nil
   139  }
   140  
   141  func (t *ICMPTracerv6) listenICMP() {
   142  	lc := NewPacketListener(t.icmpListen, t.ctx)
   143  	psize = t.Config.PktSize
   144  	go lc.Start()
   145  	for {
   146  		select {
   147  		case <-t.ctx.Done():
   148  			return
   149  		case msg := <-lc.Messages:
   150  			if msg.N == nil {
   151  				continue
   152  			}
   153  			if msg.Msg[0] == 129 {
   154  				rm, err := icmp.ParseMessage(58, msg.Msg[:*msg.N])
   155  				if err != nil {
   156  					log.Println(err)
   157  					continue
   158  				}
   159  
   160  				echoReply, ok := rm.Body.(*icmp.Echo)
   161  
   162  				if ok {
   163  					ttl := echoReply.Seq // This is the TTL value
   164  
   165  					if ttl > 100 {
   166  						continue
   167  					}
   168  					if msg.Peer.String() == t.DestIP.String() {
   169  						t.handleICMPMessage(msg, 1, rm.Body.(*icmp.Echo).Data, ttl)
   170  					}
   171  				}
   172  
   173  			}
   174  			ttl := int64(binary.BigEndian.Uint16(msg.Msg[54:56]))
   175  			packetId := strconv.FormatInt(int64(binary.BigEndian.Uint16(msg.Msg[52:54])), 2)
   176  			if processId, _, err := reverseID(packetId); err == nil {
   177  				if processId == int64(os.Getpid()&0x7f) {
   178  					dstip := net.IP(msg.Msg[32:48])
   179  					// 无效包本地环回包
   180  					if dstip.String() == "::" {
   181  						continue
   182  					}
   183  					if dstip.Equal(t.DestIP) || dstip.Equal(net.IPv6zero) {
   184  						// 匹配再继续解析包,否则直接丢弃
   185  						rm, err := icmp.ParseMessage(58, msg.Msg[:*msg.N])
   186  						if err != nil {
   187  							log.Println(err)
   188  							continue
   189  						}
   190  
   191  						switch rm.Type {
   192  						case ipv6.ICMPTypeTimeExceeded:
   193  							t.handleICMPMessage(msg, 0, rm.Body.(*icmp.TimeExceeded).Data, int(ttl))
   194  						case ipv6.ICMPTypeEchoReply:
   195  							t.handleICMPMessage(msg, 1, rm.Body.(*icmp.Echo).Data, int(ttl))
   196  						case ipv6.ICMPTypeDestinationUnreachable:
   197  							t.handleICMPMessage(msg, 2, rm.Body.(*icmp.DstUnreach).Data, int(ttl))
   198  						default:
   199  							// log.Println("received icmp message of unknown type", rm.Type)
   200  						}
   201  					}
   202  				}
   203  			}
   204  			// dstip := net.IP(msg.Msg[32:48])
   205  			// if binary.BigEndian.Uint16(msg.Msg[52:54]) != uint16(os.Getpid()&0xffff) {
   206  			// 	//	// 如果类型为应答消息,且应答消息包的进程ID与主进程相同时不跳过
   207  			// 	if binary.BigEndian.Uint16(msg.Msg[52:54]) != 0 {
   208  			// 		continue
   209  			// 	} else {
   210  			// 		if dstip.String() != "::" {
   211  			// 			continue
   212  			// 		}
   213  			// 		if msg.Peer.String() != t.DestIP.String() {
   214  			// 			continue
   215  			// 		}
   216  			// 	}
   217  			// }
   218  
   219  			// if dstip.Equal(t.DestIP) || dstip.Equal(net.IPv6zero) {
   220  			// 	rm, err := icmp.ParseMessage(58, msg.Msg[:*msg.N])
   221  			// 	if err != nil {
   222  			// 		log.Println(err)
   223  			// 		continue
   224  			// 	}
   225  			// 	// log.Println(msg.Peer)
   226  			// 	switch rm.Type {
   227  			// 	case ipv6.ICMPTypeTimeExceeded:
   228  			// 		t.handleICMPMessage(msg, 0, rm.Body.(*icmp.TimeExceeded).Data)
   229  			// 	case ipv6.ICMPTypeEchoReply:
   230  			// 		t.handleICMPMessage(msg, 1, rm.Body.(*icmp.Echo).Data)
   231  			// 	default:
   232  			// 		// log.Println("received icmp message of unknown type", rm.Type)
   233  			// 	}
   234  			// }
   235  		}
   236  	}
   237  
   238  }
   239  
   240  func (t *ICMPTracerv6) handleICMPMessage(msg ReceivedMessage, icmpType int8, data []byte, ttl int) {
   241  	if icmpType == 2 {
   242  		if t.DestIP.String() != msg.Peer.String() {
   243  			return
   244  		}
   245  	}
   246  	t.inflightRequestRWLock.RLock()
   247  	defer t.inflightRequestRWLock.RUnlock()
   248  
   249  	mpls := extractMPLS(msg, data)
   250  	if _, ok := t.inflightRequest[ttl]; ok {
   251  		t.inflightRequest[ttl] <- Hop{
   252  			Success: true,
   253  			Address: msg.Peer,
   254  			MPLS:    mpls,
   255  		}
   256  	}
   257  }
   258  
   259  func (t *ICMPTracerv6) send(ttl int) error {
   260  	defer t.wg.Done()
   261  	if t.final != -1 && ttl > t.final {
   262  		return nil
   263  	}
   264  	//id := gernerateID(ttl)
   265  	id := gernerateID(0)
   266  
   267  	//data := []byte{byte(ttl)}
   268  	data := []byte{byte(0)}
   269  	data = append(data, bytes.Repeat([]byte{1}, t.Config.PktSize-5)...)
   270  	data = append(data, 0x00, 0x00, 0x4f, 0xff)
   271  
   272  	icmpHeader := icmp.Message{
   273  		Type: ipv6.ICMPTypeEchoRequest, Code: 0,
   274  		Body: &icmp.Echo{
   275  			ID: id,
   276  			//Data: []byte("HELLO-R-U-THERE"),
   277  			Data: data,
   278  			Seq:  ttl,
   279  		},
   280  	}
   281  
   282  	p := ipv6.NewPacketConn(t.icmpListen)
   283  
   284  	icmpHeader.Body.(*icmp.Echo).Seq = ttl
   285  	err := p.SetHopLimit(ttl)
   286  	if err != nil {
   287  		return err
   288  	}
   289  
   290  	wb, err := icmpHeader.Marshal(nil)
   291  	if err != nil {
   292  		log.Fatal(err)
   293  	}
   294  
   295  	start := time.Now()
   296  	if _, err := t.icmpListen.WriteTo(wb, &net.IPAddr{IP: t.DestIP}); err != nil {
   297  		log.Fatal(err)
   298  	}
   299  	if err := t.icmpListen.SetReadDeadline(time.Now().Add(3 * time.Second)); err != nil {
   300  		log.Fatal(err)
   301  	}
   302  
   303  	select {
   304  	case <-t.ctx.Done():
   305  		return nil
   306  	case h := <-t.inflightRequest[ttl]:
   307  		rtt := time.Since(start)
   308  		if t.final != -1 && ttl > t.final {
   309  			return nil
   310  		}
   311  		if addr, ok := h.Address.(*net.IPAddr); ok && addr.IP.Equal(t.DestIP) {
   312  			t.finalLock.Lock()
   313  			if t.final == -1 || ttl < t.final {
   314  				t.final = ttl
   315  			}
   316  			t.finalLock.Unlock()
   317  		} else if addr, ok := h.Address.(*net.TCPAddr); ok && addr.IP.Equal(t.DestIP) {
   318  			t.finalLock.Lock()
   319  			if t.final == -1 || ttl < t.final {
   320  				t.final = ttl
   321  			}
   322  			t.finalLock.Unlock()
   323  		}
   324  
   325  		h.TTL = ttl
   326  		h.RTT = rtt
   327  
   328  		t.fetchLock.Lock()
   329  		defer t.fetchLock.Unlock()
   330  		err := h.fetchIPData(t.Config)
   331  		if err != nil {
   332  			return err
   333  		}
   334  
   335  		t.res.add(h)
   336  
   337  	case <-time.After(t.Timeout):
   338  		if t.final != -1 && ttl > t.final {
   339  			return nil
   340  		}
   341  
   342  		t.res.add(Hop{
   343  			Success: false,
   344  			Address: nil,
   345  			TTL:     ttl,
   346  			RTT:     0,
   347  			Error:   ErrHopLimitTimeout,
   348  		})
   349  	}
   350  
   351  	return nil
   352  }