func (p *Pinger) run() { var conn *icmp.PacketConn if p.ipv4 { if conn = p.listen(ipv4Proto[p.network], p.source); conn == nil { return } } else { if conn = p.listen(ipv6Proto[p.network], p.source); conn == nil { return } } defer conn.Close() defer p.finish() var wg sync.WaitGroup recv := make(chan *packet, 5) wg.Add(1) go p.recvICMP(conn, recv, &wg) err := p.sendICMP(conn) if err != nil { fmt.Println(err.Error()) } timeout := time.NewTicker(p.Timeout) interval := time.NewTicker(p.Interval) c := make(chan os.Signal, 1) signal.Notify(c, os.Interrupt) signal.Notify(c, syscall.SIGTERM) for { select { case <-c: close(p.done) case <-p.done: wg.Wait() return case <-timeout.C: close(p.done) wg.Wait() return case <-interval.C: err = p.sendICMP(conn) if err != nil { fmt.Println("FATAL: ", err.Error()) } case r := <-recv: err := p.processPacket(r) if err != nil { fmt.Println("FATAL: ", err.Error()) } default: if p.Count > 0 && p.PacketsRecv >= p.Count { close(p.done) wg.Wait() return } } } }
func (p *Pinger) run() { var conn, conn6 *icmp.PacketConn if p.hasIPv4 { if conn = p.listen(ipv4Proto[p.network], p.source); conn == nil { return } defer conn.Close() } if p.hasIPv6 { if conn6 = p.listen(ipv6Proto[p.network], p.source6); conn6 == nil { return } defer conn6.Close() } recvCtx := newContext() wg := new(sync.WaitGroup) if conn != nil { routines := p.NumGoroutines wg.Add(routines) for i := 0; i < routines; i++ { go p.recvICMP(conn, recvCtx, wg) } } if conn6 != nil { routines := p.NumGoroutines wg.Add(routines) for i := 0; i < routines; i++ { go p.recvICMP(conn6, recvCtx, wg) } } err := p.sendICMP(conn, conn6) ticker := time.NewTicker(p.MaxRTT) select { case <-recvCtx.done: err = recvCtx.err case <-ticker.C: } ticker.Stop() close(recvCtx.stop) wg.Wait() p.ctx.err = err close(p.ctx.done) if p.OnIdle != nil { p.OnIdle(p.sent) } }
func (p *Pinger) run(once bool) { p.debugln("Run(): Start") var conn, conn6 *icmp.PacketConn if p.hasIPv4 { if conn = p.listen(ipv4Proto[p.network]); conn == nil { return } defer conn.Close() } if p.hasIPv6 { if conn6 = p.listen(ipv6Proto[p.network]); conn6 == nil { return } defer conn6.Close() } recv := make(chan *packet, 1) recvCtx := newContext() wg := new(sync.WaitGroup) p.debugln("Run(): call recvICMP()") if conn != nil { wg.Add(1) go p.recvICMP(conn, recv, recvCtx, wg) } if conn6 != nil { wg.Add(1) go p.recvICMP(conn6, recv, recvCtx, wg) } p.debugln("Run(): call sendICMP()") queue, err := p.sendICMP(conn, conn6) ticker := time.NewTicker(p.MaxRTT) mainloop: for { select { case <-p.ctx.stop: p.debugln("Run(): <-p.ctx.stop") break mainloop case <-recvCtx.done: p.debugln("Run(): <-recvCtx.done") p.mu.Lock() err = recvCtx.err p.mu.Unlock() break mainloop case <-ticker.C: p.mu.Lock() handler := p.OnIdle p.mu.Unlock() if handler != nil { handler() } if once || err != nil { break mainloop } p.debugln("Run(): call sendICMP()") queue, err = p.sendICMP(conn, conn6) case r := <-recv: p.debugln("Run(): <-recv") p.procRecv(r, queue) } } ticker.Stop() p.debugln("Run(): close(recvCtx.stop)") close(recvCtx.stop) p.debugln("Run(): wait recvICMP()") wg.Wait() p.mu.Lock() p.ctx.err = err p.mu.Unlock() p.debugln("Run(): close(p.ctx.done)") close(p.ctx.done) p.debugln("Run(): End") }
// Sends a single ICMP echo to an IP and returns success and latency information. // Borrowed from BrianBrazil's blackbox exporter func Ping(ip net.IP, maxRTT time.Duration) (success bool, latency time.Duration) { deadline := time.Now().Add(maxRTT) var socket *icmp.PacketConn var err error if isIPv4(ip) { socket, err = icmp.ListenPacket("ip4:icmp", "0.0.0.0") } else if isIPv6(ip) { socket, err = icmp.ListenPacket("ip6:ipv6-icmp", "::") } else { log.Errorln("IP did not match any known types?") return } if err != nil { log.Errorf("Error listening to socket: %s", err) return } defer socket.Close() seq := getICMPSequence() pid := os.Getpid() & 0xffff // Build the packet var wm icmp.Message if isIPv4(ip) { wm = icmp.Message{ Type: ipv4.ICMPTypeEcho, Code: 0, Body: &icmp.Echo{ ID: pid, Seq: int(seq), Data: []byte("poller_exporter"), }, } } else if isIPv6(ip) { wm = icmp.Message{ Type: ipv6.ICMPTypeEchoRequest, Code: 0, Body: &icmp.Echo{ ID: pid, Seq: int(seq), Data: []byte("poller_exporter"), }, } } else { log.Errorln("IP did not match any known types?") return } wb, err := wm.Marshal(nil) if err != nil { log.Errorf("Error marshalling packet for %s: %s", ip.String(), err) return } sendTime := time.Now() var dst *net.IPAddr dst = &net.IPAddr{IP: ip} if _, err := socket.WriteTo(wb, dst); err != nil { log.Errorf("Error writing to socket for %s: %s", ip.String(), err) return } // Reply should be the same except for the message type. if isIPv4(ip) { wm.Type = ipv4.ICMPTypeEchoReply } else if isIPv6(ip) { wm.Type = ipv6.ICMPTypeEchoReply } else { log.Errorln("IP did not match any known types?") return } wb, err = wm.Marshal(nil) if err != nil { log.Errorf("Error marshalling packet for %s: %s", ip.String(), err) return } rb := make([]byte, 1500) if err := socket.SetReadDeadline(deadline); err != nil { log.Errorf("Error setting socket deadline for %s: %s", ip.String(), err) return } for { n, peer, err := socket.ReadFrom(rb) if err != nil { if nerr, ok := err.(net.Error); ok && nerr.Timeout() { log.Infof("Timeout reading from socket for %s: %s", ip.String(), err) return } log.Errorf("Error reading from socket for %s: %s", ip.String(), err) continue } if peer.String() != ip.String() { continue } if bytes.Compare(rb[:n], wb) == 0 { success = true latency = time.Now().Sub(sendTime) return } } return }
func probeICMP(target string, w http.ResponseWriter, module Module) (success bool) { var ( socket *icmp.PacketConn requestType icmp.Type replyType icmp.Type fallbackProtocol string ) deadline := time.Now().Add(module.Timeout) // Defaults to IPv4 to be compatible with older versions if module.ICMP.Protocol == "" { module.ICMP.Protocol = "icmp" } // In case of ICMP prefer IPv6 by default if module.ICMP.Protocol == "icmp" && module.ICMP.PreferredIpProtocol == "" { module.ICMP.PreferredIpProtocol = "ip6" } if module.ICMP.Protocol == "icmp4" { module.ICMP.PreferredIpProtocol = "ip4" fallbackProtocol = "" } else if module.ICMP.Protocol == "icmp6" { module.ICMP.PreferredIpProtocol = "ip6" fallbackProtocol = "" } else if module.ICMP.PreferredIpProtocol == "ip6" { fallbackProtocol = "ip4" } else { fallbackProtocol = "ip6" } ip, err := net.ResolveIPAddr(module.ICMP.PreferredIpProtocol, target) if err != nil && fallbackProtocol != "" { ip, err = net.ResolveIPAddr(fallbackProtocol, target) } if err != nil { log.Errorf("Error resolving address %s: %s", target, err) } if ip.IP.To4() == nil { requestType = ipv6.ICMPTypeEchoRequest replyType = ipv6.ICMPTypeEchoReply socket, err = icmp.ListenPacket("ip6:ipv6-icmp", "::") fmt.Fprintf(w, "probe_ip_protocol 6\n") } else { requestType = ipv4.ICMPTypeEcho replyType = ipv4.ICMPTypeEchoReply socket, err = icmp.ListenPacket("ip4:icmp", "0.0.0.0") fmt.Fprintf(w, "probe_ip_protocol 4\n") } if err != nil { log.Errorf("Error listening to socket: %s", err) return } defer socket.Close() if err != nil { log.Errorf("Error resolving address %s: %s", target, err) return } seq := getICMPSequence() pid := os.Getpid() & 0xffff wm := icmp.Message{ Type: requestType, Code: 0, Body: &icmp.Echo{ ID: pid, Seq: int(seq), Data: []byte("Prometheus Blackbox Exporter"), }, } wb, err := wm.Marshal(nil) if err != nil { log.Errorf("Error marshalling packet for %s: %s", target, err) return } if _, err := socket.WriteTo(wb, ip); err != nil { log.Errorf("Error writing to socker for %s: %s", target, err) return } // Reply should be the same except for the message type. wm.Type = replyType wb, err = wm.Marshal(nil) if err != nil { log.Errorf("Error marshalling packet for %s: %s", target, err) return } rb := make([]byte, 1500) if err := socket.SetReadDeadline(deadline); err != nil { log.Errorf("Error setting socket deadline for %s: %s", target, err) return } for { n, peer, err := socket.ReadFrom(rb) if err != nil { if nerr, ok := err.(net.Error); ok && nerr.Timeout() { log.Infof("Timeout reading from socket for %s: %s", target, err) return } log.Errorf("Error reading from socket for %s: %s", target, err) continue } if peer.String() != ip.String() { continue } if replyType == ipv6.ICMPTypeEchoReply { // Clear checksum to make comparison succeed. rb[2] = 0 rb[3] = 0 } if bytes.Compare(rb[:n], wb) == 0 { success = true return } } }