Was ein Echo Request, also ein Ping, ist, sollte soweit bekannt sein.
In Delphi kann man ein Ping u.a. mit den Indys oder mit den WinAPI Befehlen ICMPCreateFile etc. absetzen.

Ich habe ein ICMP-Paket mal direkt auf ein Raw-IP-Socket aufgesetzt.
Dazu müssen die einzelnen werte des ICMP-Headers gesetzt werden, sowie die Checksumme berechnet werden.

Nach dem Senden wird auf eine Antwort gewartet. Bei einem Raw-Socket bekommt man die Antwort samt IP-Header, den man erst einmal überspringen muss. (Ich kenne nur den IPv4 Header, deswegen ist auch nur dieser implementiert).

Hier ist die Funktion, welche ein Echo-Request absetzt und innerhalb eines 2 Sekunden-Intervalls auf eine Antwort wartet.
Code:

const PacketSize=32;

type PICMP = ^TICMP; //Aufbau eines ICMP-Paketes

     TICMP = packed record
       Typ : Byte;
       Code : Byte;
       CheckSum : Word;
       ID : Word;
       Seq_Num : Word;
       Data : array[1..PacketSize] of Byte;
     end;


function Ping(const aIPAddr:String;   //IP-Adresse

                  aSeqNum:Word)          //fortlaufende Sequencenummer

                  :String;                    //Antwort hier als String

var Addr:TSockAddr;  //IP-Adress-Record

    Sock:TSocket;    //Socket-Handle

    ICMP:TICMP;      //gesendetes ICMP-Paket

    ICMPret:PICMP;   //bekommenes ICMP-Paket

    i:Integer;
    Start,Ende:Integer; //Zeit merken

    
    Read:TFDSet; //Record für Wartenfunktion des Sockets

    TimeOut:TTimeVal;

    buf:array[0..1023] of Byte; //Antwortbuffer

    IPHeaderLen,IPLen:Integer;

    Checksum:Word; //alles zur Checksummenberechnung

    CheckSumTemp:Integer;
    pw:PWord;

begin

  //Socket Initialisieren

  Sock := Socket(AF_INET,SOCK_RAW, IPPROTO_ICMP);
  if Sock=invalid_Socket then
    raise Exception.Create('Konnte Socket nicht erstellen'+#13#10+syserrormessage(wsagetlasterror));
  try

    //Paket befüllen

    ICMP.Typ := 8; //Echo-Request = Ping

    ICMP.Code := 0;
    ICMP.CheckSum := 0; //mit 0 vorbelegen (für die BErechnung)

    ICMP.ID := $CDEF;       // eine "eindeutige" ID

    ICMP.Seq_Num := swap(aSeqNum); // Sequence Nr. in  Big Endian (network order)

    for i:=1 to PacketSize do
      ICMP.Data[i] := 5; //Irgendwelche Daten



    //Checksummer berechnen

    {The 16 bit one's complement of the one's complement sum of all 16
        bit words in the header.  For computing the checksum, the checksum
        field should be zero.}
    //Achtung! Big Endian (deswegen swap)

    pw:=@ICMP;
    CheckSum:=0;
    for i:=1 to sizeof(ICMP) div 2 do
    begin
      CheckSumTemp:=CheckSum+not(swap(pw^));
      CheckSum:=CheckSumtemp and $FFFF;
      inc(CheckSum,(CheckSumTemp and $10000) shr 16);
      inc(pw);
    end;
    if sizeof(ICMP) mod 2=1 then
    begin
      CheckSumTemp:=CheckSum+not(swap(word(ICMP.data[high(ICMP.data)])));
      CheckSum:=CheckSumtemp and $FFFF;
      inc(CheckSum,(CheckSumTemp and $10000) shr 16);
    end;
    ICMP.CheckSum:=swap(CheckSum);

    //Zieladdresse festlegen

    addr.sin_family := AF_INET; //ist Internetaddresse

    addr.sin_port := 0; //Port ist egal

    addr.sin_addr.S_addr := Inet_Addr(PAnsiChar(aIPAddr)); //Addresse in 32 bit


    //Paket abschicken

    if sendto(sock,ICMP,sizeof(ICMP),0,addr,sizeof(Addr))=Socket_Error then
      raise Exception.Create('Fehler bei sendto'+#13#10+syserrormessage(wsagetlasterror));
    start:=gettickcount; //Zeit merken



    //auf Antwort warten

    FD_ZERO(Read);
    FD_Set(sock,Read);
    TimeOut.tv_sec:=2;
    TimeOut.tv_usec:=0;
    if Select(0,@Read,nil,nil,@TimeOut)>0 then  //hier wird bis Timeout auf Read-Event gewartet

    begin
      IPLen:=recv(sock,buf,length(buf),0); //Antwort lesen (Achtung! Antwort ist inkl. IP-Header)

      Ende:=gettickcount;

      if IPLen=Socket_Error then
        raise Exception.Create('Fehler bei recv'+#13#10+syserrormessage(wsagetlasterror));

      if ((buf[0] and $F0) shr 4 = 4 ) then //wenn IPv4 (IPv6 geht anders)

      begin
        IPHeaderlen:=(buf[0] and $0F) * 4; //Länge des IPv4-Headers berechnen

        if IPHEaderlen+sizeof(ICMP)<IPLen then
          raise Exception.Create('Antwortpaket zu kurz');

        ICMPret:=@buf[IPHeaderlen]; //IPHeader überspringen


        if (ICMPret^.ID=ICMP.ID ) and
           (ICMPret^.Seq_Num=ICMP.Seq_Num) then
        begin
          //Antwort lesen:

          case ICMPret^.Typ of
            //Man könnte jetzt noch die Richtigkeit der Checksummer und der Daten überprüfen

            0 : //Alles ok

              result:=format('Antwort in %d ms erhalten',[Ende-Start]);
            3 : //Ziel nicht erreichbar; Hier kann noch ICMPret^.Code für den Grund ausgewertet werden

              result:='Ziel nicht erreichbar';
            11: //TTL zu klein  -  Weg zu weit

              result:='Zeitlimit (TTL) überschritten';
            else //etwas anderes sollte nicht passieren, aber falls doch:

              result:=format('Unbekannte Antwort: Typ %d',[ICMPret^.Code]);
          end;
        end else
          result:='Falsche Antwort-ID oder -Sequencenummer';
      end else
        result:='Kann IPv6 nicht lesen.';

    end else
      //Keine Rückantwort erhalten im gegebenen ZeitIntervall

      result:='TimeOut';

  finally
    closesocket(sock);
  end;

end;

Natürlich muss zu Programmbeginn noch WSAStartUp und zu Programmende noch WSACleanup aufgerufen werden:
Code:

implementation


procedure StartUp;
var wsadata:TWsaData;
begin
  WSAStartup($0101,wsadata);
end;

initialization
StartUP
finalization
WSACleanup;

end.

Aufgerufen wird das bspw mit:
Code:

procedure TForm1.Button1Click(Sender: TObject);
begin
  memo1.lines.add(format('Paket senden mit %d Bytes an %s gesendet.',[PacketSize, Edit1.text]));
  inc(FSeqnum); //Sequencenummer erhöhen

  memo1.lines.add( Ping(Edit1.Text,FSeqNum) );
end;