From 3628055f08151db622cbfae20134c0c5baa59794 Mon Sep 17 00:00:00 2001 From: Brian Brazil Date: Sat, 5 Sep 2015 20:16:32 +0100 Subject: [PATCH] Add ICMP support --- AUTHORS.md | 12 +++++++ blackbox.yml | 3 ++ icmp.go | 95 ++++++++++++++++++++++++++++++++++++++++++++++++++++ main.go | 12 ++++++- 4 files changed, 121 insertions(+), 1 deletion(-) create mode 100644 AUTHORS.md create mode 100644 icmp.go diff --git a/AUTHORS.md b/AUTHORS.md new file mode 100644 index 0000000..a50e8e7 --- /dev/null +++ b/AUTHORS.md @@ -0,0 +1,12 @@ +The Prometheus project was started by Matt T. Proud (emeritus) and +Julius Volz in 2012. + +Maintainers of this repository: + +* Brian Brazil + +The following individuals have contributed code to this repository +(listed in alphabetical order): + +* Brian Brazil + diff --git a/blackbox.yml b/blackbox.yml index dee0bf0..3b4a850 100644 --- a/blackbox.yml +++ b/blackbox.yml @@ -6,3 +6,6 @@ modules: tcpconnect: prober: tcp timeout: 5s + icmp: + prober: icmp + timeout: 5s diff --git a/icmp.go b/icmp.go new file mode 100644 index 0000000..04c40d3 --- /dev/null +++ b/icmp.go @@ -0,0 +1,95 @@ +package main + +import ( + "net" + "net/http" + "os" + "sync" + "time" + "golang.org/x/net/internal/iana" + "golang.org/x/net/icmp" + "golang.org/x/net/ipv4" + + "github.com/prometheus/log" +) + +var ( + icmpSequence uint16 + icmpSequenceMutex sync.Mutex + +) + +func getICMPSequence() uint16 { + icmpSequenceMutex.Lock() + defer icmpSequenceMutex.Unlock() + icmpSequence+=1 + return icmpSequence +} + +func probeICMP(target string, w http.ResponseWriter, module Module) (success bool) { + deadline := time.Now().Add(module.Timeout) + socket, err := icmp.ListenPacket("ip4:icmp", "0.0.0.0") + if err != nil { + log.Errorf("Error listening to socket: %s", err) + return + } + defer socket.Close() + + ip, err := net.ResolveIPAddr("ip4", target) + if err != nil { + log.Errorf("Error resolving address %s: %s", target, err) + return + } + + seq := getICMPSequence() + pid := os.Getpid() & 0xffff + + wm := icmp.Message{ + Type: ipv4.ICMPTypeEcho, 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 + } + + 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 + } + rm, err := icmp.ParseMessage(iana.ProtocolICMP, rb[:n]) + if err != nil { + log.Warnf("Error parsing ICMP message for %s: %s", target, err) + continue + } + if rm.Type == ipv4.ICMPTypeEchoReply { + // The ICMP package does not support unmarshalling + // messages, so assume this is the right sequence number. + success = true + return + } + } + return +} diff --git a/main.go b/main.go index 9275676..6d96a1f 100644 --- a/main.go +++ b/main.go @@ -24,6 +24,7 @@ type Module struct { Timeout time.Duration `yaml:"timeout"` HTTP HTTPProbe `yaml:"http"` TCP TCPProbe `yaml:"tcp"` + ICMP ICMPProbe `yaml:"icmp"` } type HTTPProbe struct { @@ -37,9 +38,13 @@ type HTTPProbe struct { type TCPProbe struct { } +type ICMPProbe struct { +} + var Probers = map[string]func(string, http.ResponseWriter, Module) bool{ "http": probeHTTP, "tcp": probeTCP, + "icmp": probeICMP, } func probeHandler(w http.ResponseWriter, r *http.Request, config *Config) { @@ -58,8 +63,13 @@ func probeHandler(w http.ResponseWriter, r *http.Request, config *Config) { http.Error(w, fmt.Sprintf("Unkown module %s", moduleName), 400) return } + prober, ok := Probers[module.Prober] + if !ok { + http.Error(w, fmt.Sprintf("Unkown prober %s", module.Prober), 400) + return + } start := time.Now() - success := Probers[module.Prober](target, w, module) + success := prober(target, w, module) fmt.Fprintf(w, "probe_duration_seconds %f\n", float64(time.Now().Sub(start))/1e9) if success { fmt.Fprintf(w, "probe_success %d\n", 1) -- 2.25.1