MODULE Ping;
IMPORT ICMP, DNS, IP, Network, Kernel, Objects, Commands, Modules, KernelLog;
CONST
PingSize = 32;
MaxPingSize = 65535-20-8;
PingHdrSize = 4;
Timeout = 1000;
VAR
running: BOOLEAN;
timer: Objects.Timer;
timeout: LONGINT;
pingSize: LONGINT;
ms: Kernel.MilliTimer;
PROCEDURE Ping*(context : Commands.Context);
VAR
i: LONGINT;
hostname: DNS.Name;
fip: IP.Adr;
res1, res2: LONGINT;
data: ARRAY PingHdrSize+MaxPingSize OF CHAR;
BEGIN {EXCLUSIVE}
AWAIT(~running);
context.arg.SkipWhitespace; context.arg.String(hostname);
context.arg.SkipWhitespace; context.arg.Int(pingSize, FALSE);
context.arg.SkipWhitespace; context.arg.Int(timeout, FALSE);
IF pingSize = 0 THEN pingSize := PingSize END;
IF timeout = 0 THEN timeout := Timeout END;
IF pingSize > MaxPingSize THEN
pingSize := MaxPingSize;
END;
IF hostname # "" THEN
context.out.String("Ping: Resolving host name: "); context.out.String(hostname); context.out.Ln;
DNS.HostByName(hostname, fip, res1);
IF res1 = DNS.Ok THEN
ICMP.InstallReceiver(ICMP.TypeEchoReplyv4, GetReply, res1);
ICMP.InstallReceiver(ICMP.TypeEchoReplyv6, GetReply, res2);
IF (res1 = ICMP.Ok) & (res2 = ICMP.Ok) THEN
FOR i := 0 TO PingHdrSize-1 DO
data[i] := 0X;
END;
FOR i := 0 TO pingSize-1 DO
data[PingHdrSize+i] := CHR(i MOD 256);
END;
context.out.String("Ping: Pinging "); IP.OutAdr(fip); context.out.String(" with ");
context.out.Int(pingSize, 0); context.out.String(" bytes..."); context.out.Ln;
IF fip.usedProtocol = IP.IPv4 THEN
ICMP.Send(NIL, fip, data, 0, PingHdrSize+pingSize, ICMP.TypeEchoRequestv4, 0, IP.MaxTTL);
ELSIF fip.usedProtocol = IP.IPv6 THEN
ICMP.Send(NIL, fip, data, 0, PingHdrSize+pingSize, ICMP.TypeEchoRequestv6, 0, IP.MaxTTL);
END;
Objects.SetTimeout(timer, TimeoutHandler, timeout);
Kernel.SetTimer(ms, 0);
running := TRUE
ELSE
context.error.String("Ping: Couldn't install receiver in ICMP, probably reserved by another application."); context.error.Ln
END;
ELSE
context.error.String("Ping: Couldn't resolve host name: "); context.error.String(hostname); context.error.Ln
END;
ELSE
context.error.String("Ping: Parameter error: No hostname defined!"); context.error.Ln
END;
END Ping;
PROCEDURE TimeoutHandler;
VAR res: LONGINT;
BEGIN {EXCLUSIVE}
IF running THEN
KernelLog.String("Ping: Timeout! No reply received within "); KernelLog.Int(timeout, 0);
KernelLog.String(" ms."); KernelLog.Ln;
running := FALSE;
ICMP.RemoveReceiver(ICMP.TypeEchoReplyv4, res);
ICMP.RemoveReceiver(ICMP.TypeEchoReplyv6, res);
ELSE
END;
END TimeoutHandler;
PROCEDURE GetReply(int: IP.Interface; type, code: LONGINT; fip, lip: IP.Adr; buffer: Network.Buffer);
VAR res: LONGINT;
BEGIN {EXCLUSIVE}
IF running THEN
KernelLog.String("Ping: Successful! Reply received within "); KernelLog.Int(Kernel.Elapsed(ms), 0);
KernelLog.String(" ms."); KernelLog.Ln;
running := FALSE;
Objects.CancelTimeout(timer);
ICMP.RemoveReceiver(ICMP.TypeEchoReplyv4, res);
ICMP.RemoveReceiver(ICMP.TypeEchoReplyv6, res);
ELSE
END;
Network.ReturnBuffer(buffer)
END GetReply;
PROCEDURE Cleanup;
VAR res: LONGINT;
BEGIN {EXCLUSIVE}
IF running THEN
running := FALSE;
Objects.CancelTimeout(timer);
ICMP.RemoveReceiver(ICMP.TypeEchoReplyv4, res);
ICMP.RemoveReceiver(ICMP.TypeEchoReplyv6, res);
END
END Cleanup;
BEGIN
Modules.InstallTermHandler(Cleanup);
NEW(timer)
END Ping.
(*
Usage: Ping.Ping host [pingSize] [timeout]
"pingSize" is the size of the ping packet data in bytes.
"timeout" is the echo reply timeout in ms.
Aos.Call Ping.Ping 127.0.0.1~
Aos.Call Ping.Ping 10.0.0.1 1024~
Aos.Call Ping.Ping 129.132.98.12~
Aos.Call Ping.Ping 129.132.250.220~
Aos.Call Ping.Ping www.ethz.ch 128 100~
System.Free Ping~
*)