예제 #1
0
파일: ping.go 프로젝트: conoro/netx
func (this *Pinger) sender() {
	defer func() {
		this.wg.Done()

		// Tell the receiver the sender has quit
		this.quit()
	}()

	id := int(this.pid)

	m := &icmp.Message{
		Type: ipv4.ICMPTypeEcho,
		Code: 0,
		Body: &icmp.Echo{
			ID:   id,
			Seq:  this.seq(),
			Data: this.payload,
		},
	}

	var hf ipv4.HeaderFlags
	if this.DF {
		hf |= ipv4.DontFragment
	}

	wh := &ipv4.Header{
		Version:  ipv4.Version,
		Len:      ipv4.HeaderLen,
		TOS:      this.TOS,
		TTL:      this.TTL,
		Protocol: 1,
		Flags:    hf,
	}

	err := this.sendMessage(m, wh)
	if err != nil {
		glog.Errorf("ping/sender: Error sending echo requests: %v", err)
	}
}
예제 #2
0
파일: ping.go 프로젝트: conoro/netx
func (this *Pinger) sendMessage(m *icmp.Message, wh *ipv4.Header) error {
	select {
	case <-this.done:
		return nil

	default:
	}

	var (
		wb        []byte
		tempDelay time.Duration // how long to sleep on accept failure
		ticker    = time.NewTicker(this.Interval)
	)

	for i, dst := range this.IPs() {
		if i != 0 {
			<-ticker.C
		}

		binary.BigEndian.PutUint64(this.payload, uint64(time.Now().UnixNano()))

		wb, err := this.marshalMessage(m, wb)
		if err != nil {
			glog.Errorf("Error creating ICMP payload: %v", err)
			continue
		}

		wh.TotalLen = ipv4.HeaderLen + len(wb)
		wh.Dst = dst

		glog.Debugf("Pinging %s", wh.Dst)
		this.mu.Lock()
		this.results[wh.Dst.String()] = nil
		this.mu.Unlock()

		if err := this.rconn.SetWriteDeadline(time.Now().Add(100 * time.Millisecond)); err != nil {
			return err
		}

		if err := this.rconn.WriteTo(wh, wb, nil); err != nil {
			select {
			case <-this.done:
				return nil

			default:
			}

			// Borrowed from go1.3.3/src/pkg/net/http/server.go:1699
			if ne, ok := err.(net.Error); ok && ne.Temporary() {
				if tempDelay == 0 {
					tempDelay = 5 * time.Millisecond
				} else {
					tempDelay *= 2
				}

				if max := 1 * time.Second; tempDelay > max {
					tempDelay = max
				}

				glog.Errorf("write error: %v; retrying in %v", err, tempDelay)
				time.Sleep(tempDelay)
				continue // out of the IP list, wait for next tick
			}

			return err
		}
	}

	if err := this.rconn.SetReadDeadline(time.Now().Add(this.MaxRTT)); err != nil {
		return err
	}

	return nil
}
예제 #3
0
파일: ping.go 프로젝트: conoro/netx
func (this *Pinger) receiver() {
	defer func() {
		// Let's recover from panic
		if r := recover(); r != nil {
			glog.Errorf("Recovering from panic: %v", r)
		}

		this.wg.Done()
		close(this.reschan)
		this.Stop()
	}()

	rb := make([]byte, ipv4.HeaderLen+icmpHeaderSize+this.Size)

	var tempDelay time.Duration // how long to sleep on accept failure

loop:
	for {
		rh, b, _, err := this.rconn.ReadFrom(rb)
		if err != nil {
			select {
			case <-this.done:
				break loop

			default:
			}

			// Borrowed from go1.3.3/src/pkg/net/http/server.go:1699
			if ne, ok := err.(net.Error); ok && ne.Temporary() {
				if tempDelay == 0 {
					tempDelay = 5 * time.Millisecond
				} else {
					tempDelay *= 2
				}
				if max := this.MaxRTT; tempDelay > max {
					tempDelay = max
				}
				glog.Errorf("Accept error: %v; retrying in %v", err, tempDelay)
				time.Sleep(tempDelay)
				continue
			}

			break loop
		}

		m, err := icmp.ParseMessage(ProtocolICMP, b)
		if err != nil {
			// Hm...bad message?
			continue
		}

		if runtime.GOOS == "linux" && m.Type == ipv4.ICMPTypeEcho {
			// On Linux we must handle own sent packets.
			continue
		}

		if m.Type != ipv4.ICMPTypeEchoReply || m.Code != 0 {
			glog.Errorf("got type=%v, code=%v; want type=%v, code=%v", m.Type, m.Code, ipv4.ICMPTypeEchoReply, 0)
			continue
		}

		// Echo Reply
		er, ok := m.Body.(*icmp.Echo)
		if !ok {
			glog.Errorf("Error type requireing m.Body to *icmp.Echo")
			continue
		}

		if er.ID != int(this.pid) || er.Seq > int(this.seqnum) {
			glog.Debugf("%d != %d, %d != %d", er.ID, this.pid, er.Seq, this.seqnum)
			continue
		}

		rtt := time.Since(time.Unix(0, int64(binary.BigEndian.Uint64(er.Data))))

		src := make(net.IP, len(rh.Src))
		copy(src, rh.Src)

		dst := make(net.IP, len(rh.Dst))
		copy(dst, rh.Dst)

		pr := &PingResult{
			TOS:  rh.TOS,
			TTL:  rh.TTL,
			Type: m.Type,
			Code: m.Code,
			ID:   er.ID,
			Seq:  er.Seq,
			RTT:  rtt,
			Src:  src,
			Dst:  dst,
			Size: len(er.Data),
		}
		this.mu.Lock()
		this.results[src.String()] = pr
		this.mu.Unlock()

		this.reschan <- pr
	}

	for ip, pr := range this.results {
		if pr == nil {
			this.reschan <- &PingResult{
				Src: net.ParseIP(ip),
				ID:  int(this.pid),
				Seq: int(this.seqnum),
				Err: fmt.Errorf("%s: Request timed out for seq %d", ip, this.seqnum),
			}
		}
	}
}