type TCPProbe struct {
PreferredIPProtocol string `yaml:"preferred_ip_protocol,omitempty"`
+ SourceIPAddress string `yaml:"source_ip_address,omitempty"`
QueryResponse []QueryResponse `yaml:"query_response,omitempty"`
TLS bool `yaml:"tls,omitempty"`
TLSConfig config.TLSConfig `yaml:"tls_config,omitempty"`
type ICMPProbe struct {
PreferredIPProtocol string `yaml:"preferred_ip_protocol,omitempty"` // Defaults to "ip6".
+ SourceIPAddress string `yaml:"source_ip_address,omitempty"`
PayloadSize int `yaml:"payload_size,omitempty"`
DontFragment bool `yaml:"dont_fragment,omitempty"`
// Catches all undefined fields and must be empty after parsing.
type DNSProbe struct {
PreferredIPProtocol string `yaml:"preferred_ip_protocol,omitempty"`
+ SourceIPAddress string `yaml:"source_ip_address,omitempty"`
TransportProtocol string `yaml:"transport_protocol,omitempty"`
QueryName string `yaml:"query_name,omitempty"`
QueryType string `yaml:"query_type,omitempty"` // Defaults to ANY.
func ProbeICMP(ctx context.Context, target string, module config.Module, registry *prometheus.Registry, logger log.Logger) (success bool) {
var (
socket net.PacketConn
+ socket6 *ipv6.PacketConn
requestType icmp.Type
replyType icmp.Type
)
return false
}
+ var srcIP net.IP
+ if len(module.ICMP.SourceIPAddress) > 0 {
+ if srcIP = net.ParseIP(module.ICMP.SourceIPAddress); srcIP == nil {
+ level.Error(logger).Log("msg", "Error parsing source ip address", "srcIP", module.ICMP.SourceIPAddress)
+ return false
+ }
+ level.Info(logger).Log("msg", "Using source address", "srcIP", srcIP)
+ }
+
level.Info(logger).Log("msg", "Creating socket")
if ip.IP.To4() == nil {
requestType = ipv6.ICMPTypeEchoRequest
replyType = ipv6.ICMPTypeEchoReply
- socket, err = icmp.ListenPacket("ip6:ipv6-icmp", "::")
+ icmpConn, err := icmp.ListenPacket("ip6:ipv6-icmp", "::")
if err != nil {
level.Error(logger).Log("msg", "Error listening to socket", "err", err)
return
}
+
+ socket = icmpConn
+ socket6 = icmpConn.IPv6PacketConn()
} else {
requestType = ipv4.ICMPTypeEcho
replyType = ipv4.ICMPTypeEchoReply
- 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
- }
+ 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}
+ rc, err := ipv4.NewRawConn(s)
+ if err != nil {
+ level.Error(logger).Log("msg", "Error creating raw connection", "err", err)
+ return
}
+ socket = &v4Conn{c: rc, df: module.ICMP.DontFragment, src: srcIP}
}
defer socket.Close()
return
}
level.Info(logger).Log("msg", "Writing out packet")
- if _, err = socket.WriteTo(wb, ip); err != nil {
- level.Warn(logger).Log("msg", "Error writing to socket", "err", err)
- return
+ if socket6 != nil && srcIP != nil {
+ // Also set source address for IPv6.
+ cm := &ipv6.ControlMessage{Src: srcIP}
+ if _, err = socket6.WriteTo(wb, cm, ip); err != nil {
+ level.Error(logger).Log("msg", "Error writing to IPv6 socket", "err", err)
+ return
+ }
+ } else {
+ if _, err = socket.WriteTo(wb, ip); err != nil {
+ level.Warn(logger).Log("msg", "Error writing to socket", "err", err)
+ return
+ }
}
// Reply should be the same except for the message type.
}
}
-type dfConn struct {
+type v4Conn struct {
c *ipv4.RawConn
+
+ df bool
+ src net.IP
}
-func (c *dfConn) ReadFrom(b []byte) (int, net.Addr, error) {
+func (c *v4Conn) ReadFrom(b []byte) (int, net.Addr, error) {
h, p, _, err := c.c.ReadFrom(b)
if err != nil {
return 0, nil, err
return n, &net.IPAddr{IP: h.Src}, nil
}
-func (d *dfConn) WriteTo(b []byte, addr net.Addr) (int, error) {
+func (d *v4Conn) 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{
+ header := &ipv4.Header{
Version: ipv4.Version,
Len: ipv4.HeaderLen,
Protocol: 1,
TotalLen: ipv4.HeaderLen + len(b),
- Flags: ipv4.DontFragment,
TTL: 64,
Dst: ipAddr.IP,
+ Src: d.src,
+ }
+
+ if d.df {
+ header.Flags |= ipv4.DontFragment
}
- return len(b), d.c.WriteTo(dfHeader, b, nil)
+ return len(b), d.c.WriteTo(header, b, nil)
}
-func (d *dfConn) Close() error {
+func (d *v4Conn) Close() error {
return d.c.Close()
}
-func (d *dfConn) LocalAddr() net.Addr {
+func (d *v4Conn) LocalAddr() net.Addr {
return nil
}
-func (d *dfConn) SetDeadline(t time.Time) error {
+func (d *v4Conn) SetDeadline(t time.Time) error {
return d.c.SetDeadline(t)
}
-func (d *dfConn) SetReadDeadline(t time.Time) error {
+func (d *v4Conn) SetReadDeadline(t time.Time) error {
return d.c.SetReadDeadline(t)
}
-func (d *dfConn) SetWriteDeadline(t time.Time) error {
+func (d *v4Conn) SetWriteDeadline(t time.Time) error {
return d.c.SetWriteDeadline(t)
}