Skip to content

Commit

Permalink
Advanced ICMP options (#240)
Browse files Browse the repository at this point in the history
Add payload to ICMP Probe
Add the option to send packets with DF-Bit set.

Signed-off-by: Goutham Veeramachaneni <[email protected]>
  • Loading branch information
gouthamve authored and brian-brazil committed Oct 6, 2017
1 parent 641a05b commit 4bc7b66
Show file tree
Hide file tree
Showing 3 changed files with 108 additions and 9 deletions.
6 changes: 6 additions & 0 deletions CONFIGURATION.md
Original file line number Diff line number Diff line change
Expand Up @@ -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>
Expand Down
9 changes: 8 additions & 1 deletion config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import (
"errors"
"fmt"
"io/ioutil"
"runtime"
"strings"
"sync"
"time"
Expand Down Expand Up @@ -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"`
}
Expand Down Expand Up @@ -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
}
Expand Down
102 changes: 94 additions & 8 deletions prober/icmp.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
)
Expand All @@ -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{
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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)
}

0 comments on commit 4bc7b66

Please sign in to comment.