Advanced ICMP options (#240)
authorGoutham Veeramachaneni <cs14btech11014@iith.ac.in>
Fri, 6 Oct 2017 10:33:46 +0000 (16:03 +0530)
committerBrian Brazil <brian.brazil@robustperception.io>
Fri, 6 Oct 2017 10:33:46 +0000 (11:33 +0100)
Add payload to ICMP Probe
Add the option to send packets with DF-Bit set.

Signed-off-by: Goutham Veeramachaneni <cs14btech11014@iith.ac.in>
CONFIGURATION.md
config/config.go
prober/icmp.go

index 1ba3c5511b42c5a45b2714647554cc85250eb8f6..3f665849c20dd9945d08e56dbda6294fe464b8fd 100644 (file)
@@ -168,6 +168,12 @@ validate_additional_rrs:
 # The preferred IP protocol of the ICMP probe (ip4, ip6).
 [ preferred_ip_protocol: <string> | default = "ip6" ]
 
+# Set the DF-bit in the IP-header. Only works with ip4 and on *nix systems.
+[ dont_fragment: <boolean> | default = false ]
+
+# The size of the payload.
+[ payload_size: <int> ]
+
 ```
 
 ### <tls_config>
index 81b504f7a929d7774ce20253829145a8d57e7276..299889ecd77a19606c4081f9dffa8e3a44294c85 100644 (file)
@@ -4,6 +4,7 @@ import (
        "errors"
        "fmt"
        "io/ioutil"
+       "runtime"
        "strings"
        "sync"
        "time"
@@ -96,7 +97,8 @@ type TCPProbe struct {
 
 type ICMPProbe struct {
        PreferredIPProtocol string `yaml:"preferred_ip_protocol,omitempty"` // Defaults to "ip6".
-
+       PayloadSize         int    `yaml:"payload_size,omitempty"`
+       DontFragment        bool   `yaml:"dont_fragment,omitempty"`
        // Catches all undefined fields and must be empty after parsing.
        XXX map[string]interface{} `yaml:",inline"`
 }
@@ -215,6 +217,11 @@ func (s *ICMPProbe) UnmarshalYAML(unmarshal func(interface{}) error) error {
        if err := unmarshal((*plain)(s)); err != nil {
                return err
        }
+
+       if runtime.GOOS == "windows" && s.DontFragment {
+               return errors.New("\"dont_fragment\" is not supported on windows platforms")
+       }
+
        if err := checkOverflow(s.XXX, "icmp probe"); err != nil {
                return err
        }
index 052cae54e64f7567a574a7778084525b9996d9d2..dcb9096135ad311eded3086a0736b009843c4bf6 100644 (file)
@@ -45,7 +45,7 @@ func getICMPSequence() uint16 {
 
 func ProbeICMP(ctx context.Context, target string, module config.Module, registry *prometheus.Registry, logger log.Logger) (success bool) {
        var (
-               socket      *icmp.PacketConn
+               socket      net.PacketConn
                requestType icmp.Type
                replyType   icmp.Type
        )
@@ -62,23 +62,52 @@ func ProbeICMP(ctx context.Context, target string, module config.Module, registr
        if ip.IP.To4() == nil {
                requestType = ipv6.ICMPTypeEchoRequest
                replyType = ipv6.ICMPTypeEchoReply
+
                socket, err = icmp.ListenPacket("ip6:ipv6-icmp", "::")
+               if err != nil {
+                       level.Error(logger).Log("msg", "Error listening to socket", "err", err)
+                       return
+               }
        } else {
                requestType = ipv4.ICMPTypeEcho
                replyType = ipv4.ICMPTypeEchoReply
-               socket, err = icmp.ListenPacket("ip4:icmp", "0.0.0.0")
-       }
 
-       if err != nil {
-               level.Error(logger).Log("msg", "Error listening to socket", "err", err)
-               return
+               if !module.ICMP.DontFragment {
+                       socket, err = icmp.ListenPacket("ip4:icmp", "0.0.0.0")
+                       if err != nil {
+                               level.Error(logger).Log("msg", "Error listening to socket", "err", err)
+                               return
+                       }
+               } else {
+                       s, err := net.ListenPacket("ip4:icmp", "0.0.0.0")
+                       if err != nil {
+                               level.Error(logger).Log("msg", "Error listening to socket", "err", err)
+                               return
+                       }
+
+                       rc, err := ipv4.NewRawConn(s)
+                       if err != nil {
+                               level.Error(logger).Log("msg", "cannot construct raw connection", "err", err)
+                               return
+                       }
+                       socket = &dfConn{c: rc}
+               }
        }
+
        defer socket.Close()
 
+       var data []byte
+       if module.ICMP.PayloadSize != 0 {
+               data = make([]byte, module.ICMP.PayloadSize)
+               copy(data, "Prometheus Blackbox Exporter")
+       } else {
+               data = []byte("Prometheus Blackbox Exporter")
+       }
+
        body := &icmp.Echo{
                ID:   os.Getpid() & 0xffff,
                Seq:  int(getICMPSequence()),
-               Data: []byte("Prometheus Blackbox Exporter"),
+               Data: data,
        }
        level.Info(logger).Log("msg", "Creating ICMP packet", "seq", body.Seq, "id", body.ID)
        wm := icmp.Message{
@@ -106,7 +135,7 @@ func ProbeICMP(ctx context.Context, target string, module config.Module, registr
                return
        }
 
-       rb := make([]byte, 1500)
+       rb := make([]byte, 65536)
        if err := socket.SetReadDeadline(deadline); err != nil {
                level.Error(logger).Log("msg", "Error setting socket deadline", "err", err)
                return
@@ -136,3 +165,60 @@ func ProbeICMP(ctx context.Context, target string, module config.Module, registr
                }
        }
 }
+
+type dfConn struct {
+       c *ipv4.RawConn
+}
+
+func (c *dfConn) ReadFrom(b []byte) (int, net.Addr, error) {
+       h, p, _, err := c.c.ReadFrom(b)
+       if err != nil {
+               return 0, nil, err
+       }
+
+       copy(b, p)
+       n := len(b)
+       if len(p) < len(b) {
+               n = len(p)
+       }
+       return n, &net.IPAddr{IP: h.Src}, nil
+}
+
+func (d *dfConn) WriteTo(b []byte, addr net.Addr) (int, error) {
+       ipAddr, err := net.ResolveIPAddr(addr.Network(), addr.String())
+       if err != nil {
+               return 0, err
+       }
+
+       dfHeader := &ipv4.Header{
+               Version:  ipv4.Version,
+               Len:      ipv4.HeaderLen,
+               Protocol: 1,
+               TotalLen: ipv4.HeaderLen + len(b),
+               Flags:    ipv4.DontFragment,
+               TTL:      64,
+               Dst:      ipAddr.IP,
+       }
+
+       return len(b), d.c.WriteTo(dfHeader, b, nil)
+}
+
+func (d *dfConn) Close() error {
+       return d.c.Close()
+}
+
+func (d *dfConn) LocalAddr() net.Addr {
+       return nil
+}
+
+func (d *dfConn) SetDeadline(t time.Time) error {
+       return d.c.SetDeadline(t)
+}
+
+func (d *dfConn) SetReadDeadline(t time.Time) error {
+       return d.c.SetReadDeadline(t)
+}
+
+func (d *dfConn) SetWriteDeadline(t time.Time) error {
+       return d.c.SetWriteDeadline(t)
+}