summaryrefslogtreecommitdiffstats
path: root/src/go/collectors/go.d.plugin/modules/ping/prober.go
blob: e0d9925b4e46764714f019af1a4c4cfc39feebcd (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
// SPDX-License-Identifier: GPL-3.0-or-later

package ping

import (
	"errors"
	"fmt"
	"net"
	"time"

	"github.com/netdata/netdata/go/go.d.plugin/logger"

	probing "github.com/prometheus-community/pro-bing"
)

func newPingProber(conf pingProberConfig, log *logger.Logger) prober {
	var source string
	if conf.iface != "" {
		if addr, err := getInterfaceIPAddress(conf.iface); err != nil {
			log.Warningf("error getting interface '%s' IP address: %v", conf.iface, err)
		} else {
			log.Infof("interface '%s' IP address '%s', will use it as the source", conf.iface, addr)
			source = addr
		}
	}

	return &pingProber{
		network:    conf.network,
		privileged: conf.privileged,
		packets:    conf.packets,
		source:     source,
		interval:   conf.interval,
		deadline:   conf.deadline,
		Logger:     log,
	}
}

type pingProberConfig struct {
	network    string
	privileged bool
	packets    int
	iface      string
	interval   time.Duration
	deadline   time.Duration
}

type pingProber struct {
	*logger.Logger

	network    string
	privileged bool
	packets    int
	source     string
	interval   time.Duration
	deadline   time.Duration
}

func (p *pingProber) ping(host string) (*probing.Statistics, error) {
	pr := probing.New(host)

	pr.SetNetwork(p.network)

	if err := pr.Resolve(); err != nil {
		return nil, fmt.Errorf("DNS lookup '%s' : %v", host, err)
	}

	pr.Source = p.source
	pr.RecordRtts = false
	pr.Interval = p.interval
	pr.Count = p.packets
	pr.Timeout = p.deadline
	pr.SetPrivileged(p.privileged)
	pr.SetLogger(nil)

	if err := pr.Run(); err != nil {
		return nil, fmt.Errorf("pinging host '%s' (ip %s): %v", pr.Addr(), pr.IPAddr(), err)
	}

	stats := pr.Statistics()

	p.Debugf("ping stats for host '%s' (ip '%s'): %+v", pr.Addr(), pr.IPAddr(), stats)

	return stats, nil
}

func getInterfaceIPAddress(ifaceName string) (ipaddr string, err error) {
	iface, err := net.InterfaceByName(ifaceName)
	if err != nil {
		return "", err
	}

	addresses, err := iface.Addrs()
	if err != nil {
		return "", err
	}

	// FIXME: add IPv6 support
	var v4Addr string
	for _, addr := range addresses {
		if ipnet, ok := addr.(*net.IPNet); ok && ipnet.IP.To4() != nil {
			v4Addr = ipnet.IP.To4().String()
			break
		}
	}

	if v4Addr == "" {
		return "", errors.New("ipv4 addresses not found")
	}

	return v4Addr, nil
}