Skip to content

Instantly share code, notes, and snippets.

@soypat
Created April 16, 2021 15:08
Show Gist options
  • Save soypat/cae6f7c7a88fc580d9f77c4c741fab47 to your computer and use it in GitHub Desktop.
Save soypat/cae6f7c7a88fc580d9f77c4c741fab47 to your computer and use it in GitHub Desktop.
Ping addresses on a network for discovery. Use CIDR addresses, i.e. 192.168.1.0/24 (pings 256 addresses)
package main
import (
"errors"
"fmt"
"net"
"os"
"os/signal"
"strconv"
"sync"
"github.com/go-ping/ping"
"github.com/spf13/pflag"
)
func run() error {
pflag.Parse()
target := pflag.Arg(0)
if target == "" {
return errors.New("no host argument found")
}
hosts, n, err := Hosts(target)
if err != nil {
return err
}
if n > 256*16 {
return errors.New("too many addresses to ping. got " + strconv.Itoa(n) + "addresses")
}
c := make(chan os.Signal, 1)
signal.Notify(c, os.Interrupt)
var pingers = make([]*ping.Pinger, n)
go func() {
for _ = range c {
for i := range pingers {
if pingers[i] == nil {
return
}
pingers[i].Stop()
}
}
}()
var wg = &sync.WaitGroup{}
for i := range hosts {
p, err := hostPinger(hosts[i])
if err != nil {
return err
}
pingers[i] = p
wg.Add(1)
go func() {
p.Run()
wg.Done()
}()
}
wg.Wait()
return nil
}
func hostPinger(addr string) (*ping.Pinger, error) {
pinger, err := ping.NewPinger(addr)
if err != nil {
return pinger, err
}
pinger.OnRecv = func(pkt *ping.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 *ping.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 *ping.Statistics) {
if stats.PacketLoss == 100 {
//fmt.Printf("%s 100%% packet loss\n", stats.Addr)
return
}
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)
fmt.Printf("round-trip min/avg/max/stddev = %v/%v/%v/%v\n",
stats.MinRtt, stats.AvgRtt, stats.MaxRtt, stats.StdDevRtt)
}
pinger.Count = 1
return pinger, nil
}
func Hosts(cidr string) ([]string, int, error) {
ip, ipnet, err := net.ParseCIDR(cidr)
if err != nil {
return nil, 0, err
}
var ips []string
for ip := ip.Mask(ipnet.Mask); ipnet.Contains(ip); inc(ip) {
ips = append(ips, ip.String())
}
// remove network address and broadcast address
lenIPs := len(ips)
switch {
case lenIPs < 2:
return ips, lenIPs, nil
default:
return ips[1 : len(ips)-1], lenIPs - 2, nil
}
}
func inc(ip net.IP) {
for j := len(ip) - 1; j >= 0; j-- {
ip[j]++
if ip[j] > 0 {
break
}
}
}
func main() {
if err := run(); err != nil {
fmt.Println("error in run: ", err.Error())
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment