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; 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 { 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 { 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 { p: LONG POINTER TO CARDINAL _ LOOPHOLE[icmp]; cs: CARDINAL _ Basics.BITNOT[icmp.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; 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. °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 If non-NIL (probably set from the debugger), then log incoming ICMP packets. NIL if timeout (ms), 0 => Forever Reverse all the fields and send this guy. Compute the header checksum for a datagram. 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. data.inHdr.source _ FillInNetNumber[data.inHdr.source]; Κ– "cedar" style˜Icode2šœ΅™΅head™ J™3J™1J™-J™'Icode™*šΟk ˜ Kšœœœ˜Kšœ˜Kšœœœ˜Kšœœ¬˜ΈKšœœ"˜-Kšœ œ ˜Kšœ œ˜!Kšœœ3˜@——šΟnœœ˜Kšœ3˜:Kšœœ˜Kšœœœ˜šœ œœœ˜KšœL™L—Kšœœœ˜šœœœ œ˜Kšœ œœ ˜šœ œœ˜Kšœœ˜Kšœœœ˜ Kšœ  œ˜Kšœœœ˜ ——Kšžœœœ˜(šžœœœœœœœ ˜NKšœ œ ˜!Kšœ ˜ Kšœ œ œœ˜šœ œŸ˜,˜ Kšœ˜Kšœ˜—˜Kšœ˜Kšœ˜Kšœ˜K˜—šœ˜Kšœ˜Kšœ7™7K˜—šœ ˜ Kš œ œœœœœ ˜Cšœ ˜&K˜W——Kšœœ˜Kšœœ˜Kšœœ˜Kšœœ˜Kšœ œ˜šœ0˜0š œœœœœ˜@Kšœ˜Kšœœœ˜Kš œœœœœ˜5š˜šœ œœ+œ˜AKšœœœœ˜Kšœœœ˜Kšœ˜Kšœ˜ ——Kšœ˜Kšœ˜Kšœ˜ ———Kšœ˜ —Kšœ˜ ——Kšžœœœœ˜AKšœœ˜,Kšœ˜——…—’!n