...

Source file src/edge-infra.dev/pkg/lib/icmp/icmp.go

Documentation: edge-infra.dev/pkg/lib/icmp

     1  package icmp
     2  
     3  import (
     4  	"fmt"
     5  	"net"
     6  	"time"
     7  
     8  	"golang.org/x/net/icmp"
     9  	"golang.org/x/net/ipv4"
    10  )
    11  
    12  // NewMessage create a new ICMP message
    13  func NewMessage(message string, id int) icmp.Message {
    14  	return icmp.Message{
    15  		Type: ipv4.ICMPTypeEcho,
    16  		Code: 0,
    17  		Body: &icmp.Echo{
    18  			ID:   id,
    19  			Seq:  0,
    20  			Data: []byte(message),
    21  		},
    22  	}
    23  }
    24  
    25  // NewPacketConnection create a new ICMP packet connection
    26  func NewPacketConnection(timeout time.Duration) (*icmp.PacketConn, error) {
    27  	packetconn, err := icmp.ListenPacket("ip4:icmp", "0.0.0.0")
    28  	if err != nil {
    29  		return nil, fmt.Errorf("error making packet connection: %w", err)
    30  	}
    31  
    32  	deadline := time.Now().Add(timeout)
    33  	err = packetconn.SetReadDeadline(deadline)
    34  	if err != nil {
    35  		return nil, fmt.Errorf("failed to set read deadline on connection: %w", err)
    36  	}
    37  	return packetconn, nil
    38  }
    39  
    40  // SendRequest writes the icmp message to the packet connection
    41  func SendRequest(packetconn *icmp.PacketConn, im icmp.Message, dst net.Addr) error {
    42  	ib, err := im.Marshal(nil)
    43  	if err != nil {
    44  		return fmt.Errorf("failure to marshall ICMP message: %w", err)
    45  	}
    46  
    47  	_, err = packetconn.WriteTo(ib, dst)
    48  	if err != nil {
    49  		return fmt.Errorf("failed to write ICMP bytes to packet connection: %w", err)
    50  	}
    51  	return nil
    52  }
    53  
    54  // ReceiveReply tries reading from the packet connection.
    55  //
    56  // There are three possible outcomes of this operation:
    57  //
    58  // - If the read operation errors, most likely due to a deadline exceeded error
    59  // then the function returns false, as this constitutes a detection failure.
    60  //
    61  // - If the received packet doesn't parse or conform to the echo reply type,
    62  // this function skips to the next iteration of the for-loop and reads the next packet.
    63  //
    64  // - Finally, if the icmp message's ID correctly identifies it, the functions returns true.
    65  func ReceiveReply(packetconn *icmp.PacketConn, id int) bool {
    66  	for {
    67  		rb := make([]byte, 1500)
    68  		n, _, err := packetconn.ReadFrom(rb)
    69  		if err != nil {
    70  			return false
    71  		}
    72  
    73  		rm, err := icmp.ParseMessage(1, rb[:n])
    74  		if err != nil {
    75  			continue
    76  		}
    77  
    78  		if rm.Type != ipv4.ICMPTypeEchoReply {
    79  			continue
    80  		}
    81  
    82  		mes, ok := rm.Body.(*icmp.Echo)
    83  		if !ok {
    84  			continue
    85  		}
    86  
    87  		if id == mes.ID {
    88  			return true
    89  		}
    90  	}
    91  }
    92  

View as plain text