Copyright (C) 1983, 1984, 1985 by Xerox Corporation. All rights reserved. The following program was created in 1983 but has not been published within the meaning of the copyright law, is furnished under license, and may not be used, copied and/or disclosed except in accordance with the terms of said license.
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.STREAMNIL;
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.IDICMP.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 CARDINALLOOPHOLE[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.