golang icmp ping issue using goroutines - getting duplicates

149 views Asked by At

Would love some help on this ... I'm having difficulty using icmp ping with goroutines. My goal is to write code that will ping blocks of addresses eg 10.1.0.0/22, 10.1.4.0/22 etc in the quickest possible time.

The ping part seems to work fine, but the moment I introduce a goroutine for sending icmp packets I end up with duplicate responses - it seems like the last address in the slice I am iterating through is duplicated in the goroutine and none of the other addresses are used/displayed.

I am using a slice of 20 or so ip's for simplicity (not doing the address block piece yet). It seems the last ip in the list is the one the goroutine is acting on along with the sequence number. In my output I should see a list of each ip with a unique sequence number and echoRequest ID. Here is the code:

package main

import (
    "fmt"
    "math/rand"
    "golang.org/x/net/icmp"
    "golang.org/x/net/ipv4"
    "sync"
    "net"
    "time"
)

func sendPingRequest(targetIP net.IP, sequence int, conn *icmp.PacketConn) error {
    message := icmp.Message{
            Type: ipv4.ICMPTypeEcho,
            Code: 0,
            Body: &icmp.Echo{
                ID:   rand.Intn(65000), // os.Getpid() & 0xffff,
                Seq:  sequence,
                Data: []byte("HELLO-RPC"),
            },
    }

    messageBytes, err := message.Marshal(nil)
    if err != nil {
        return err
    }

    _, err = conn.WriteTo(messageBytes, &net.IPAddr{IP: targetIP})
    if err != nil {
        return err
    }

    return nil
}


var ips = []string{"192.168.1.1", "203.219.106.90", "23.206.224.65", "151.101.30.133", "6.6.6.6", "203.221.3.18", "23.40.165.66", "151.101.31.5", "143.143.143.143", "8.8.8.8", "23.219.61.138", "157.240.8.35", "104.94.145.204", "1.2.3.4", "160.34.1.130", "23.32.5.57", "142.250.204.17", "192.185.225.146", "7.7.7.7", "104.19.237.4", "18.67.93.50", "45.60.126.46", "184.84.252.163", "18.67.111.128"}

func main() {
    start := time.Now()
    timeout := 400 * time.Millisecond
    wg := &sync.WaitGroup{}

    conn, err := icmp.ListenPacket("ip4:icmp", "0.0.0.0")
    if err != nil {
        fmt.Println("Error listening for ICMP packets:", err)
        return
    }
    defer conn.Close()

    replyChannel := make(chan *icmp.Message, len(ips))

    go func() {
        for {
            message := make([]byte, 1500)
            _, _, err := conn.ReadFrom(message)
            if err != nil {
                fmt.Println("Error reading ICMP packet:", err)
                continue
            }

            receivedMessage, err := icmp.ParseMessage(ipv4.ICMPTypeEcho.Protocol(), message)
            if err != nil {
                fmt.Println("Error parsing ICMP packet:", err)
                continue
            }

            replyChannel <- receivedMessage
        }
    }()

    wg.Add(len(ips))
    for idx, ip := range ips {
        targetIP := net.ParseIP(ip)

        go func() {
            defer wg.Done()
            err := sendPingRequest(targetIP, idx, conn)
            if err != nil {
                fmt.Println("Error sending ICMP packet:", err)
                // continue
            }

            select {
            case <-time.After(timeout):
                fmt.Printf("No response from %s\n", ip)
            case reply := <-replyChannel:
                echoReply, _ := reply.Body.(*icmp.Echo)
                fmt.Printf("Received reply from %s ...  ", ip)
                fmt.Printf("reply id: %v, seq: %v\n", echoReply.ID, echoReply.Seq)
            }
        }()
    }
    wg.Wait()
    fmt.Printf("\nelapsed time %v\n", time.Since(start))
}

the output is as follows (NB the ip & seq are all based on the last item in the slice. Also there are a few non-existent ip's so some addresses dont respond - which is correct):-
sh-3.2# go run icmp.go
Received reply from 18.67.111.128 ... reply id: 59682, seq: 23
Received reply from 18.67.111.128 ... reply id: 22751, seq: 23
Received reply from 18.67.111.128 ... reply id: 42942, seq: 23
Received reply from 18.67.111.128 ... reply id: 37531, seq: 23
Received reply from 18.67.111.128 ... reply id: 24029, seq: 23
Received reply from 18.67.111.128 ... reply id: 39323, seq: 23
Received reply from 18.67.111.128 ... reply id: 31617, seq: 23
Received reply from 18.67.111.128 ... reply id: 38973, seq: 23
Received reply from 18.67.111.128 ...
Received reply from 18.67.111.128 ... reply id: 14149, seq: 23
Received reply from 18.67.111.128 ... reply id: 43518, seq: 23
reply id: 43433, seq: 23 Received reply from 18.67.111.128 ... reply id: 13755, seq: 23
Received reply from 18.67.111.128 ... reply id: 5795, seq: 23
Received reply from 18.67.111.128 ... reply id: 16212, seq: 23
Received reply from 18.67.111.128 ... reply id: 28927, seq: 23
Received reply from 18.67.111.128 ... reply id: 15595, seq: 23
Received reply from 18.67.111.128 ... reply id: 50925, seq: 23
Received reply from 18.67.111.128 ... reply id: 35796, seq: 23
Received reply from 18.67.111.128 ... reply id: 23039, seq: 23
No response from 18.67.111.128
No response from 18.67.111.128
No response from 18.67.111.128
No response from 18.67.111.128
No response from 18.67.111.128

elapsed time 407.883819ms Error reading ICMP packet: read ip 0.0.0.0: raw-read ip4 0.0.0.0: use of closed network connection
Error reading ICMP packet: read ip 0.0.0.0: raw-read ip4 0.0.0.0: use of closed network connection
Error reading ICMP packet: read ip 0.0.0.0: raw-read ip4 0.0.0.0: use of closed network connection
Error reading ICMP packet: read ip 0.0.0.0: raw-read ip4 0.0.0.0: use of closed network connection

0

There are 0 answers