123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165 |
- // Package arping is a native go library to ping a host per arp datagram, or query a host mac address
- //
- // The currently supported platforms are: Linux and BSD.
- //
- //
- // The library requires raw socket access. So it must run as root, or with appropriate capabilities under linux:
- // `sudo setcap cap_net_raw+ep <BIN>`.
- //
- //
- // Examples:
- //
- // ping a host:
- // ------------
- // package main
- // import ("fmt"; "github.com/j-keck/arping"; "net")
- //
- // func main(){
- // dstIP := net.ParseIP("192.168.1.1")
- // if hwAddr, duration, err := arping.Ping(dstIP); err != nil {
- // fmt.Println(err)
- // } else {
- // fmt.Printf("%s (%s) %d usec\n", dstIP, hwAddr, duration/1000)
- // }
- // }
- //
- //
- // resolve mac address:
- // --------------------
- // package main
- // import ("fmt"; "github.com/j-keck/arping"; "net")
- //
- // func main(){
- // dstIP := net.ParseIP("192.168.1.1")
- // if hwAddr, _, err := arping.Ping(dstIP); err != nil {
- // fmt.Println(err)
- // } else {
- // fmt.Printf("%s is at %s\n", dstIP, hwAddr)
- // }
- // }
- //
- //
- // check if host is online:
- // ------------------------
- // package main
- // import ("fmt"; "github.com/j-keck/arping"; "net")
- //
- // func main(){
- // dstIP := net.ParseIP("192.168.1.1")
- // _, _, err := arping.Ping(dstIP)
- // if err == arping.ErrTimeout {
- // fmt.Println("offline")
- // }else if err != nil {
- // fmt.Println(err.Error())
- // }else{
- // fmt.Println("online")
- // }
- // }
- //
- package arping
- import (
- "errors"
- "io/ioutil"
- "log"
- "net"
- "os"
- "time"
- )
- var (
- // ErrTimeout error
- ErrTimeout = errors.New("timeout")
- verboseLog = log.New(ioutil.Discard, "", 0)
- timeout = time.Duration(500 * time.Millisecond)
- )
- // Ping sends an arp ping to 'dstIP'
- func Ping(dstIP net.IP) (net.HardwareAddr, time.Duration, error) {
- iface, err := findUsableInterfaceForNetwork(dstIP)
- if err != nil {
- return nil, 0, err
- }
- return PingOverIface(dstIP, *iface)
- }
- // PingOverIfaceByName sends an arp ping over interface name 'ifaceName' to 'dstIP'
- func PingOverIfaceByName(dstIP net.IP, ifaceName string) (net.HardwareAddr, time.Duration, error) {
- iface, err := net.InterfaceByName(ifaceName)
- if err != nil {
- return nil, 0, err
- }
- return PingOverIface(dstIP, *iface)
- }
- // PingOverIface sends an arp ping over interface 'iface' to 'dstIP'
- func PingOverIface(dstIP net.IP, iface net.Interface) (net.HardwareAddr, time.Duration, error) {
- srcMac := iface.HardwareAddr
- srcIP, err := findIPInNetworkFromIface(dstIP, iface)
- if err != nil {
- return nil, 0, err
- }
- broadcastMac := []byte{0xff, 0xff, 0xff, 0xff, 0xff, 0xff}
- request := newArpRequest(srcMac, srcIP, broadcastMac, dstIP)
- if err := initialize(iface); err != nil {
- return nil, 0, err
- }
- defer deinitialize()
- type PingResult struct {
- mac net.HardwareAddr
- duration time.Duration
- err error
- }
- pingResultChan := make(chan PingResult)
- go func() {
- // send arp request
- verboseLog.Printf("arping '%s' over interface: '%s' with address: '%s'\n", dstIP, iface.Name, srcIP)
- if sendTime, err := send(request); err != nil {
- pingResultChan <- PingResult{nil, 0, err}
- } else {
- for {
- // receive arp response
- response, receiveTime, err := receive()
- if err != nil {
- pingResultChan <- PingResult{nil, 0, err}
- return
- }
- if response.IsResponseOf(request) {
- duration := receiveTime.Sub(sendTime)
- verboseLog.Printf("process received arp: srcIP: '%s', srcMac: '%s'\n",
- response.SenderIP(), response.SenderMac())
- pingResultChan <- PingResult{response.SenderMac(), duration, err}
- return
- }
- verboseLog.Printf("ignore received arp: srcIP: '%s', srcMac: '%s'\n",
- response.SenderIP(), response.SenderMac())
- }
- }
- }()
- select {
- case pingResult := <-pingResultChan:
- return pingResult.mac, pingResult.duration, pingResult.err
- case <-time.After(timeout):
- return nil, 0, ErrTimeout
- }
- }
- // EnableVerboseLog enables verbose logging on stdout
- func EnableVerboseLog() {
- verboseLog = log.New(os.Stdout, "", 0)
- }
- // SetTimeout sets ping timeout
- func SetTimeout(t time.Duration) {
- timeout = t
- }
|