package icmp import ( "fmt" "net" "time" "golang.org/x/net/icmp" "golang.org/x/net/ipv4" ) // NewMessage create a new ICMP message func NewMessage(message string, id int) icmp.Message { return icmp.Message{ Type: ipv4.ICMPTypeEcho, Code: 0, Body: &icmp.Echo{ ID: id, Seq: 0, Data: []byte(message), }, } } // NewPacketConnection create a new ICMP packet connection func NewPacketConnection(timeout time.Duration) (*icmp.PacketConn, error) { packetconn, err := icmp.ListenPacket("ip4:icmp", "0.0.0.0") if err != nil { return nil, fmt.Errorf("error making packet connection: %w", err) } deadline := time.Now().Add(timeout) err = packetconn.SetReadDeadline(deadline) if err != nil { return nil, fmt.Errorf("failed to set read deadline on connection: %w", err) } return packetconn, nil } // SendRequest writes the icmp message to the packet connection func SendRequest(packetconn *icmp.PacketConn, im icmp.Message, dst net.Addr) error { ib, err := im.Marshal(nil) if err != nil { return fmt.Errorf("failure to marshall ICMP message: %w", err) } _, err = packetconn.WriteTo(ib, dst) if err != nil { return fmt.Errorf("failed to write ICMP bytes to packet connection: %w", err) } return nil } // ReceiveReply tries reading from the packet connection. // // There are three possible outcomes of this operation: // // - If the read operation errors, most likely due to a deadline exceeded error // then the function returns false, as this constitutes a detection failure. // // - If the received packet doesn't parse or conform to the echo reply type, // this function skips to the next iteration of the for-loop and reads the next packet. // // - Finally, if the icmp message's ID correctly identifies it, the functions returns true. func ReceiveReply(packetconn *icmp.PacketConn, id int) bool { for { rb := make([]byte, 1500) n, _, err := packetconn.ReadFrom(rb) if err != nil { return false } rm, err := icmp.ParseMessage(1, rb[:n]) if err != nil { continue } if rm.Type != ipv4.ICMPTypeEchoReply { continue } mes, ok := rm.Body.(*icmp.Echo) if !ok { continue } if id == mes.ID { return true } } }