pro-bing

repository·main·Indexed 19 days ago

https://github.com/prometheus-community/pro-bing

A Go library for performing ICMP echo (ping) and HTTP probing. It provides a modern alternative to older Go ping libraries with rich callback support, HTTP monitoring capabilities via NewHTTPCaller, and a CLI tool for sending ICMP echo requests. Features include real-time monitoring callbacks (OnRecv, OnFinish), detailed HTTP request timing via TraceSuite, and support for privileged raw sockets and unprivileged UDP pings on Linux.

Tokens
5.3K
Snippets
17
Records
29
Agent score
67%

What's inside pro-bing

  1. Tune HTTP probing performance

    main

    When using HTTPCaller, you can control the load applied to the target using two primary options:

    • callFrequency: Sets how often calls are made.
    • maxConcurrentCalls: Sets the maximum number of concurrent requests.

    If you set a callFrequency that cannot be met, you may need to increase maxConcurrentCalls. Note that the logic within your callbacks can also impact execution performance.

  2. Configure ICMP privileges on Linux

    main

    On Linux, pro-bing attempts to use unprivileged UDP pings. To allow this, you must enable the ping_group_range sysctl:

    sudo sysctl -w net.ipv4.ping_group_range="0 2147483647"

    Alternatively, you can use privileged raw sockets by:

    1. Calling pinger.SetPrivileged(true) in your Go code.
    2. Granting the capability to your binary using setcap or running as root:
    setcap cap_net_raw=+ep /path/to/your/compiled/binary

    Additional Linux Features:

    • Socket Marking: Use pinger.SetMark(int) to set the SO_MARK option (requires CAP_NET_ADMIN).
    • Don't Fragment: Use pinger.SetDoNotFragment(true) to set the 'Don't Fragment' bit.
  3. Install pro-bing

    main

    To use the library in your Go projects, use go get to add it to your dependencies:

    go get -u github.com/prometheus-community/pro-bing

    To install the native Go ping executable binary, run:

    go get -u github.com/prometheus-community/pro-bing/...
    $GOPATH/bin/ping
  4. Use callbacks to handle packet reception and completion

    main

    The Pinger provides several callback hooks to handle the lifecycle of a ping operation:

    • OnRecv: Called when a packet is received and processed.
    • OnFinish: Called when the pinger exits (e.g., after Count packets or timeout).
    • OnDuplicateRecv: Called when a packet is received that has already been processed.
    • OnSend: Called when a packet is sent.
    • OnSendError: Called when an error occurs while attempting to send a packet.
    • OnRecvError: Called when an error occurs while attempting to receive a packet.
    • OnSetup: Called after the listening socket is set up.
    pinger.OnRecv = func(pkt *probing.Packet) {
    	fmt.Printf("%d bytes from %s: icmp_seq=%d time=%v\n",
    		pkt.Nbytes, pkt.IPAddr, pkt.Seq, pkt.Rtt)
    }
    
    pinger.OnFinish = func(stats *probing.Statistics) {
    	fmt.Printf("\n--- %s ping statistics ---\n", stats.Addr)
    	fmt.Printf("%d packets transmitted, %d packets received, %v%% packet loss\n",
    		stats.PacketsSent, stats.PacketsRecv, stats.PacketLoss)
    }
  5. Manage network socket properties via packetConn interface

    main

    The packetConn interface defines the capabilities for managing network socket properties used for ICMP probing. While the interface itself is unexported, it is implemented by icmpv4Conn and icmpV6Conn to provide protocol-specific control over TTL/Hop Limit, source IP, interface indexing, and traffic class/TOS.

    Key capabilities include:

    • TTL/Hop Limit Control: Use SetTTL(ttl int) to set the time-to-live and SetFlagTTL() to enable TTL control messages.
    • Source IP Selection: Use SetSource(source net.IP) to specify a particular source address.
    • Interface Binding: Use SetIfIndex(ifIndex int) to bind the connection to a specific network interface index.
    • Traffic Control: Use SetTrafficClass(tclass uint8) to set the Type of Service (IPv4) or Traffic Class (IPv6).
    • Deadline Management: Use SetReadDeadline(t time.Time) to set an absolute timeout for read operations.
  6. Access request timing with TraceSuite

    main

    The TraceSuite struct is passed to all lifecycle callbacks. It provides thread-safe access to timestamps for various stages of the HTTP request. You can use these to calculate latencies for specific parts of the network stack.

    Timing Methods:

    • GetGeneralStart() / GetGeneralEnd()
    • GetDNSStart() / GetDNSEnd()
    • GetConnStart() / GetConnEnd()
    • GetTLSStart() / GetTLSEnd()
    • GetWroteHeaders()
    • GetFirstByteReceived()

    Additionally, TraceSuite contains an Extra any field that allows you to pass custom data between callbacks.

    // Example: measuring DNS latency in a callback
    caller := probing.NewHTTPCaller("https://example.com",
        probing.WithHTTPCallerOnDNSDone(func(suite *probing.TraceSuite, info httptrace.DNSDoneInfo) {
        	dnsLatency := suite.GetDNSEnd().Sub(suite.GetDNSStart())
        fmt.Printf("DNS lookup took: %v\n", dnsLatency)
        }),
    )
  7. Configure ICMP privileges on Windows

    main

    On Windows, you must call pinger.SetPrivileged(true). Failure to do so will result in the following error:

    socket: The requested protocol has not been configured into the system, or no implementation for it exists.

    Note: Accessing packet TTL values is not supported on Windows due to limitations in the Go x/net/ipv4 and x/net/ipv6 packages.

  8. Perform a basic ICMP ping

    main

    Use probing.NewPinger to initialize a pinger for a specific host. You can set the Count of packets to send and then call Run(), which blocks until the process is complete. After Run() returns, you can retrieve statistics using Statistics().

    pinger, err := probing.NewPinger("www.google.com")
    if err != nil {
    	panic(err)
    }
    pinger.Count = 3
    err = pinger.Run() // Blocks until finished.
    if err != nil {
    	panic(err)
    }
    stats := pinger.Statistics() // get send/receive/duplicate/rtt stats
  9. Perform HTTP probing

    main

    The library supports HTTP probing via probing.NewHTTPCaller. This allows you to monitor HTTP endpoint availability and latency. You can configure the caller using functional options like WithHTTPCallerCallFrequency and WithHTTPCallerOnResp.

    httpCaller := probing.NewHTTPCaller("https://www.google.com",
        probing.WithHTTPCallerCallFrequency(time.Second),
        probing.WithHTTPCallerOnResp(func(suite *probing.TraceSuite, info *probing.HTTPCallInfo) {
            fmt.Printf("got resp, status code: %d, latency: %s\n",
                info.StatusCode,
                suite.GetGeneralEnd().Sub(suite.GetGeneralStart()),
            )
        }),
    )
    
    // To stop the caller, call:
    // httpCaller.Stop()
    
    httpCaller.Run()
  10. Use ICMP callbacks for real-time monitoring

    main

    To emulate a traditional UNIX ping command with real-time output, assign functions to the following callback fields on the Pinger instance:

    • OnRecv: Called when an Echo Reply is received.
    • OnDuplicateRecv: Called when a packet with a sequence number that has already been received arrives.
    • OnFinish: Called when the ping session completes, receiving a *probing.Statistics object.
    pinger, err := probing.NewPinger("www.google.com")
    if err != nil {
    	panic(err)
    }
    
    pinger.OnRecv = func(pkt *probing.Packet) {
    	fmt.Printf("%d bytes from %s: icmp_seq=%d time=%v\n",
    		pkt.Nbytes, pkt.IPAddr, pkt.Seq, pkt.Rtt)
    }
    
    pinger.OnDuplicateRecv = func(pkt *probing.Packet) {
    	fmt.Printf("%d bytes from %s: icmp_seq=%d time=%v ttl=%v (DUP!)\n",
    		pkt.Nbytes, pkt.IPAddr, pkt.Seq, pkt.Rtt, pkt.TTL)
    }
    
    pinger.OnFinish = func(stats *probing.Statistics) {
    	fmt.Printf("\n--- %s ping statistics ---\n", stats.Addr)
    	fmt.Printf("%d packets transmitted, %d packets received, %v%% packet loss\n",
    		stats.PacketsSent, stats.PacketsRecv, stats.PacketLoss)
    }
    
    err = pinger.Run()
  11. Run a simple ping sequence

    main

    To perform a basic ping, set the Count field to the number of packets you wish to send, and call Run(). Note that Run() is a blocking function.

    pinger, err := probing.NewPinger("www.google.com")
    if err != nil {
    	panic(err)
    }
    pinger.Count = 3
    err = pinger.Run() // blocks until finished
    if err != nil {
    	panic(err)
    }
    stats := pinger.Statistics() // get send/receive/rtt stats