From a06009512e9951dd46cd64ada9e511741987245a Mon Sep 17 00:00:00 2001 From: Anton Skorokhod Date: Mon, 17 Aug 2015 10:31:27 +0200 Subject: [PATCH] Option to specify source ip for ping --- cmd/ping/ping.go | 11 ++++++++++- fastping.go | 33 +++++++++++++++++++++++++++++---- 2 files changed, 39 insertions(+), 5 deletions(-) diff --git a/cmd/ping/ping.go b/cmd/ping/ping.go index eddd695..456cd74 100644 --- a/cmd/ping/ping.go +++ b/cmd/ping/ping.go @@ -23,7 +23,7 @@ func main() { flag.BoolVar(&useUDP, "udp", false, "use non-privileged datagram-oriented UDP as ICMP endpoints") flag.BoolVar(&useUDP, "u", false, "use non-privileged datagram-oriented UDP as ICMP endpoints (shorthand)") flag.Usage = func() { - fmt.Fprintf(os.Stderr, "Usage:\n %s [options] hostname\n\nOptions:\n", os.Args[0]) + fmt.Fprintf(os.Stderr, "Usage:\n %s [options] hostname [source]\n\nOptions:\n", os.Args[0]) flag.PrintDefaults() } flag.Parse() @@ -34,6 +34,11 @@ func main() { os.Exit(1) } + source := "" + if flag.NArg() > 1 { + source = flag.Arg(1) + } + p := fastping.NewPinger() if useUDP { p.Network("udp") @@ -49,6 +54,10 @@ func main() { os.Exit(1) } + if source != "" { + p.Source(source) + } + results := make(map[string]*response) results[ra.String()] = nil p.AddIPAddr(ra) diff --git a/fastping.go b/fastping.go index 3bb23cb..b2bb348 100644 --- a/fastping.go +++ b/fastping.go @@ -130,6 +130,7 @@ type Pinger struct { // key string is IPAddr.String() addrs map[string]*net.IPAddr network string + source string hasIPv4 bool hasIPv6 bool ctx *context @@ -158,6 +159,7 @@ func NewPinger() *Pinger { seq: rand.Intn(0xffff), addrs: make(map[string]*net.IPAddr), network: "ip", + source: "", hasIPv4: false, hasIPv6: false, Size: TimeSliceLength, @@ -185,6 +187,29 @@ func (p *Pinger) Network(network string) (string, error) { return origNet, nil } +// Source sets source IP for sending ICMP packets and returns the previous +// setting. Empty value indicates to use system default one. +func (p *Pinger) Source(source string) (string, error) { + origSource := p.source + if "" == source { + p.mu.Lock() + p.source = source + p.mu.Unlock() + return origSource, nil + } + + addr := net.ParseIP(source) + if addr == nil { + return origSource, errors.New(source + " is not a valid textual representation of an IP address") + } + + p.mu.Lock() + p.source = source + p.mu.Unlock() + + return origSource, nil +} + // AddIP adds an IP address to Pinger. ipaddr arg should be a string like // "192.0.2.1". func (p *Pinger) AddIP(ipaddr string) error { @@ -347,8 +372,8 @@ func (p *Pinger) Err() error { return p.ctx.err } -func (p *Pinger) listen(netProto string) *icmp.PacketConn { - conn, err := icmp.ListenPacket(netProto, "") +func (p *Pinger) listen(netProto string, source string) *icmp.PacketConn { + conn, err := icmp.ListenPacket(netProto, source) if err != nil { p.mu.Lock() p.ctx.err = err @@ -364,14 +389,14 @@ func (p *Pinger) run(once bool) { p.debugln("Run(): Start") var conn, conn6 *icmp.PacketConn if p.hasIPv4 { - if conn = p.listen(ipv4Proto[p.network]); conn == nil { + if conn = p.listen(ipv4Proto[p.network], p.source); conn == nil { return } defer conn.Close() } if p.hasIPv6 { - if conn6 = p.listen(ipv6Proto[p.network]); conn6 == nil { + if conn6 = p.listen(ipv6Proto[p.network], p.source); conn6 == nil { return } defer conn6.Close()