ICMPImpl.mesa
Last Edited by: HGM, October 7, 1984 2:01:09 am PDT
Last Edited by: Nichols, August 25, 1983 11:58 am
Last Edited by: Taft, January 5, 1984 5:24 pm
Hal Murray June 27, 1985 2:21:47 pm PDT
John Larson, June 12, 1987 11:24:12 pm PDT
DIRECTORY
Basics USING [BITNOT],
ICMP,
IO USING [STREAM],
IPDefs USING [Byte, CreateIPHandle, DataBuffer, Datagram, DByte, ICMPProtocol, Address, InternetHandle, InternetHeader, nullAddress, PrintDatagram, Receive, RequestData, SendSpecific],
IPOps USING [maxTTL, OnesComplementAddBlock],
IPRouter USING [Redirect],
IPConfig USING [ourLocalAddress],
Process USING [Detach, DisableTimeout, MsecToTicks, SetTimeout];
ICMPImpl:
CEDAR
MONITOR
IMPORTS Basics, IPConfig, IPDefs, IPOps, IPRouter, Process
EXPORTS ICMP =
BEGIN OPEN ICMP;
logStream:
IO.
STREAM ←
NIL;
If non-NIL (probably set from the debugger), then log incoming ICMP packets.
nextID: ICMP.ID ← 256;
users:
LIST
OF InfoRef ←
NIL;
InfoRef: TYPE = REF InfoRec;
InfoRec:
TYPE =
RECORD [
handle: ICMP.Handle,
id: ICMP.ID,
waiting: CONDITION,
queue: LIST OF IPDefs.Datagram];
RequestedPortAlreadyInUse: ERROR = CODE;
Create:
PUBLIC
ENTRY
PROC [him: IPDefs.Address, local:
ICMP.
ID ←
ICMP.default]
RETURNS [handle: ICMP.Handle] = {
new: InfoRef;
IF local = ICMP.default THEN local ← (nextID ← nextID.SUCC);
handle ← NEW[ICMP.HandleRec ← [local: local, him: him]];
new ← NEW[InfoRec ← [handle: handle, id: local, waiting: , queue: NIL]];
IF users = NIL THEN { users ← CONS[new, NIL]; RETURN; };
FOR user:
LIST
OF InfoRef ← users, user.rest
DO
this: InfoRef ← user.first;
IF this.id = local THEN ERROR RequestedPortAlreadyInUse;
IF user.rest = NIL THEN {user.rest ← CONS[new, NIL]; RETURN; };
ENDLOOP; };
Destroy:
PUBLIC
ENTRY
PROC [handle:
ICMP.Handle] = {
IF users = NIL THEN ERROR;
IF users.first.handle = handle THEN {users ← users.rest; RETURN; };
FOR user:
LIST
OF InfoRef ← users, user.rest
DO
IF users.rest = NIL THEN ERROR;
IF user.rest.first.handle = handle THEN {user.rest ← user.rest.rest; RETURN; };
ENDLOOP; };
Receive:
PUBLIC
ENTRY
PROC [handle:
ICMP.Handle, timeout:
INT]
RETURNS [data: IPDefs.Datagram] =
TRUSTED {
NIL if timeout (ms), 0 => Forever
FOR user:
LIST
OF InfoRef ← users, user.rest
UNTIL user =
NIL
DO
this: InfoRef ← user.first;
IF this.handle # handle THEN LOOP;
UNTIL this.queue #
NIL
DO
IF timeout = 0 THEN Process.DisableTimeout[@this.waiting]
ELSE Process.SetTimeout[@this.waiting, Process.MsecToTicks[timeout]];
WAIT this.waiting;
IF timeout # 0 AND this.queue = NIL THEN RETURN[NIL];
ENDLOOP;
data ← this.queue.first;
this.queue ← this.queue.rest;
RETURN;
REPEAT FINISHED => ERROR;
ENDLOOP; };
Send:
PUBLIC
PROC [handle:
ICMP.Handle, data: IPDefs.Datagram, length:
INT] =
TRUSTED {
icmp: LONG POINTER TO ICMP.BodyRec ← LOOPHOLE[@data.data];
IF length < ICMP.minLength THEN ERROR;
IF length > ICMP.maxLength THEN ERROR;
data.dataLength ← length;
data.inHdr.precedence ← 0;
data.inHdr.delay ← 0;
data.inHdr.throughput ← 0;
data.inHdr.reliability ← 0;
data.inHdr.dontFragment ← FALSE;
data.inHdr.timeToLive ← IPOps.maxTTL;
data.inHdr.protocol ← IPDefs.ICMPProtocol;
data.inHdr.source ← IPConfig.ourLocalAddress;
data.inHdr.destination ← handle.him;
icmp.id ← handle.local;
icmp.checksum ← Checksum[icmp, (data.dataLength+1)/2];
IPDefs.SendSpecific[data]; };
SendReply:
PUBLIC
PROC [data: IPDefs.Datagram] =
TRUSTED {
Reverse all the fields and send this guy.
icmp: LONG POINTER TO ICMP.BodyRec ← LOOPHOLE[@data.data];
tempAddress: IPDefs.Address;
tempAddress ← data.inHdr.source;
data.inHdr.source ← data.inHdr.destination;
data.inHdr.destination ← tempAddress;
icmp.checksum ← Checksum[icmp, (data.dataLength+1)/2];
IPDefs.SendSpecific[data]; };
Checksum:
UNSAFE
PROC [icmp:
LONG
POINTER
TO
ICMP.BodyRec, words:
INT]
RETURNS [checksum: IPDefs.DByte] =
UNCHECKED {
Compute the header checksum for a datagram.
p: LONG POINTER TO CARDINAL ← LOOPHOLE[icmp];
cs:
CARDINAL ← Basics.
BITNOT[icmp.checksum];
Start with negative of the checksum that's in the header so that we don't have to smash it to zero to compute the real checksum.
cs ← IPOps.OnesComplementAddBlock[ptr: p, count: words, initialSum: cs];
RETURN[Basics.BITNOT[cs]]; };
IsValidICMPDatagram:
PROC [data: IPDefs.Datagram]
RETURNS [ok:
BOOL] =
TRUSTED {
icmp: LONG POINTER TO ICMP.BodyRec ← LOOPHOLE[@data.data];
IF data.dataLength < minLength THEN RETURN[FALSE];
IF icmp.checksum # Checksum[icmp, (data.dataLength+1)/2] THEN RETURN[FALSE];
RETURN[TRUE]; };
Listener:
PROC = {
myHandle: IPDefs.InternetHandle ← IPDefs.CreateIPHandle[IPDefs.RequestData[matchProtocol: TRUE, protocol: IPDefs.ICMPProtocol, matchAddr: FALSE, address: IPDefs.nullAddress, matchLocalAddr: TRUE, localAddress: IPDefs.nullAddress]];
DO
data: IPDefs.Datagram = myHandle.Receive[];
IF data #
NIL
AND IsValidICMPDatagram[data]
THEN
TRUSTED {
icmp: LONG POINTER TO ICMP.BodyRec ← LOOPHOLE[@data.data];
IF logStream # NIL THEN IPDefs.PrintDatagram[logStream, data];
SELECT icmp.type
FROM
-- Yucky packet format
Echo => {
icmp.type ← EchoReply;
SendReply[data]; };
Timestamp => {
icmp.type ← TimestampReply;
icmp.data[minLength+4] ← 200b;
icmp.data[minLength+8] ← 200b;
SendReply[data]; };
InformationRequest => {
icmp.type ← InformationReply;
data.inHdr.source ← FillInNetNumber[data.inHdr.source];
SendReply[data]; };
Redirect => {
redirect: LONG POINTER TO ICMP.RedirectBody ← LOOPHOLE[@data.data];
IF redirect.code = redirectForNet
THEN
IPRouter.Redirect[redirect.header.destination, redirect.gateway, data.inHdr.source]; };
DestinationUnreachable => LOOP;
TimeExceeded => LOOP;
ParameterProblem => LOOP;
SourceQuench => LOOP;
Redirect => LOOP;
EchoReply, TimestampReply, InformationReply => {
FOR user:
LIST
OF InfoRef ← users, user.rest
UNTIL user =
NIL
DO
this: InfoRef ← user.first;
IF this.id # icmp.id THEN LOOP;
IF this.queue = NIL THEN this.queue ← CONS[data, NIL]
ELSE
FOR finger:
LIST
OF IPDefs.Datagram ← this.queue, finger.rest
DO
IF finger.rest # NIL THEN LOOP;
finger.rest ← CONS[data, NIL];
EXIT;
ENDLOOP;
PokeUser[this];
EXIT;
ENDLOOP; };
ENDCASE; };
ENDLOOP; };
PokeUser: ENTRY PROC [user: InfoRef] = { NOTIFY user.waiting; };
TRUSTED {Process.Detach[FORK Listener[]]; };
END.