-- RouterImpl.mesa (last edited by: BLyon on: March 21, 1981  11:53 AM)
-- Function: The implementation module for the Pilot OISCP Router switch.

DIRECTORY
  BufferDefs USING [OisBuffer],
  Checksums USING [SetChecksum, TestChecksum],
  CommunicationInternal USING [],
  CommFlags USING [doDebug, doStats, doStorms],
  CommUtilDefs USING [CopyLong],
  DriverDefs USING [
    ChangeNumberOfInputBuffers, DriverXmitStatus, GetDeviceChain, Glitch,
    MaybeGetFreeOisBuffer, Network, PutOnGlobalDoneQueue, RouterObject, SetOisRouter],
  Inline USING [LowHalf],
  OISCP USING [
    allHostIDs, CreditReceiveOisBuffer, EnqueueOis,
    MaybeGetFreeReceiveOisBufferFromPool, unknownNetID, unknownSocketID],
  OISCPConstants USING [routingInformationSocket],
  OISCPTypes USING [
    bytesPerPktHeader, bytesPerPktText, maxBytesPerPkt, OISErrorCode, TransPortControl],
  Process USING [Yield],
  Router USING [
    AddNetwork, FindNetworkAndTransmit, FindDestinationRelativeNetID,
    ForwardPacket, RemoveNetwork, RoutingInformationPacket, RoutingTableOn,
    RoutingTableOff, SocketTable, StateChanged, XmitStatus],
  SocketInternal USING [SocketHandle],
  SpecialCommunication USING [RoutersFunction],
  SpecialSystem USING [
    broadcastHostNumber, GetProcessorID, HostNumber, NetworkAddress, nullNetworkNumber,
    NetworkNumber, SocketNumber],
  StatsDefs USING [StatIncr],
  System USING [GetClockPulses];

RouterImpl: MONITOR LOCKS socketRouterLock
  IMPORTS
    BufferDefs, Checksums, CommUtilDefs, DriverDefs, Inline, OISCP,
    Process, Router, SpecialSystem, StatsDefs, System
  EXPORTS BufferDefs, CommunicationInternal, OISCP, Router
  SHARES BufferDefs =
  BEGIN OPEN DriverDefs, OISCP, OISCPTypes, SocketInternal, StatsDefs;

  -- Many of these variables must eventually live in outerspace so that multiple MDSs
  -- access the same router variables.  The modules in the primary MDS will have the
  -- proceses and will perform the initialization of the "globals", while the others will not.

  -- The lock covers both SocketImpl and RouterImpl, specifically the socket objects,
  -- the socket table, and spare socket ID counter.  These will all live in outerspace.
  -- The monitor locks some variables on the global frame too.  They can be MDS specific.

  -- EXPORTed TYPEs
  Network: PUBLIC TYPE = DriverDefs.Network;

  -- switches and variables that don't change during execution unless for diagnostics
  primaryMDS: PUBLIC BOOLEAN ← TRUE; -- we are in the primary MDS.
  checkIt: PUBLIC BOOLEAN ← TRUE; -- checksums on for everybody
  driverLoopback: BOOLEAN ← FALSE; -- loopback in router
  stormy: BOOLEAN ← FALSE; -- storms for debugging are not on
  routersFunction: PUBLIC SpecialCommunication.RoutersFunction ← vanillaRouting;
  myHostID: SpecialSystem.HostNumber; -- processor ID of this system element

  initialSpareSocketID: SpecialSystem.SocketNumber = [1001];
  initialTransportControl: OISCPTypes.TransPortControl =
    [trace: FALSE, filler: 0, hopCount: 0];

  -- oiscp router object for the dispatcher.  There will only be one for all MDSs, but it
  -- can live on all global frame instances or we can put it in hyperspace and have the
  -- dispatcher access it via a long pointer.  The former is preferable.
  oiscpRouter: DriverDefs.RouterObject ←
    [input: LOOPHOLE[ReceivePacket], broadcast: LOOPHOLE[SendBroadcastPacketToCorrectNet],
      addNetwork: Router.AddNetwork, removeNetwork: Router.RemoveNetwork,
      stateChanged: Router.StateChanged];

  -- monitor protected data.
  -- parameters for killing packets for debugging
  lightning: INTEGER ← 30;
  bolt: INTEGER ← 10;
  -- I think only the following need be in outerspace, accesible via a pointer since these
  -- will be touched by multiple MDS processes via their copy of RouterImpl and SocketImpl.
  socketRouterLock: PUBLIC MONITORLOCK;
  -- lock for the router and all the sockets.
  -- Some day the socketIDs in use will be remembered across wrap around, and maybe
  -- the rebooting of Pilot.
  spareSocketID: SpecialSystem.SocketNumber;
  -- socket table
  socketTable: Router.SocketTable;

  -- various Glitches generated by the router
  IllegalOisPktLength: ERROR = CODE;

  -- Cool Procedures 

  -- This procedure assigns a temporary socket number in an SpecialSystem.NetworkAddress.
  -- Some day the active socket numbers in use will be kept around, so that on wrap
  -- around an unused number will be assigned.  When the system element is connected to
  -- more than one network, this procedure must return a list of SpecialSystem.NetworkAddress.
  AssignOisAddress: PUBLIC ENTRY PROCEDURE
    RETURNS [localAddr: SpecialSystem.NetworkAddress] =
    BEGIN
    localAddr ← [net: Router.FindDestinationRelativeNetID[SpecialSystem.nullNetworkNumber],
      host: myHostID, socket: spareSocketID];
    IF (spareSocketID ← [spareSocketID + 1]) = SpecialSystem.SocketNumber[0] THEN
      spareSocketID ← initialSpareSocketID;
    END; -- AssignOisAddress

  -- This procedure is just like the previous one except that the network number is relative
  -- to the destination network.  That is, we pick that one of our locally connected
  -- networks that is the best way to get to destNet, with the hope that it is the best
  -- way to get from destNet to us.
  AssignDestinationRelativeOisAddress: PUBLIC ENTRY PROCEDURE [destNet: SpecialSystem.NetworkNumber]
    RETURNS [localAddr: SpecialSystem.NetworkAddress] =
    BEGIN
    localAddr ←
      [net: Router.FindDestinationRelativeNetID[destNet], host: myHostID,
	socket: spareSocketID];
    IF (spareSocketID ← [spareSocketID + 1]) = SpecialSystem.SocketNumber[0] THEN
      spareSocketID ← initialSpareSocketID;
    END; -- AssignDestinationRelativeOisAddress

  -- This procedure tells the OISCP Router about a new socket.
  AddSocket: PUBLIC ENTRY PROCEDURE [sH: SocketHandle] =
    BEGIN

    MaybeIncreaseDriversBuffers: PROCEDURE = INLINE
      BEGIN
      previousSH: SocketHandle ← socketTable.first;
      UNTIL previousSH = NIL DO
	IF previousSH.pool.total > 0 THEN RETURN;
	previousSH ← previousSH.next;
	ENDLOOP;
      DriverDefs.ChangeNumberOfInputBuffers[TRUE]; -- TRUE => increase
      END;

    -- add new socket to the head of the table
    IF sH.pool.total > 0 THEN MaybeIncreaseDriversBuffers[];
    sH.next ← socketTable.first;
    socketTable.first ← sH;
    socketTable.length ← socketTable.length + 1;
    END; -- AddSocket

  -- This procedure removes a socket from the OISCP Router's tables.
  RemoveSocket: PUBLIC ENTRY PROCEDURE [sH: SocketHandle] =
    BEGIN

    MaybeDecreaseDriversBuffers: PROCEDURE = INLINE
      BEGIN
      previousSH ← socketTable.first;
      UNTIL previousSH = NIL DO
	IF previousSH.pool.total > 0 THEN RETURN;
	previousSH ← previousSH.next;
	ENDLOOP;
      DriverDefs.ChangeNumberOfInputBuffers[FALSE]; -- FALSE => decrease
      END;

    previousSH: SocketHandle;
    IF socketTable.first = sH THEN socketTable.first ← sH.next
    ELSE
      BEGIN
      previousSH ← socketTable.first;
      UNTIL previousSH = NIL DO
	IF previousSH.next = sH THEN BEGIN previousSH.next ← sH.next; EXIT; END;
	previousSH ← previousSH.next;
	ENDLOOP;
      END;
    socketTable.length ← socketTable.length - 1;
    IF sH.pool.total > 0 THEN MaybeDecreaseDriversBuffers[];
    END; -- RemoveSocket

  -- This is not an entry procedure because we change it only for debugging and can
  -- live with a race condition.
  SetOisStormy: PUBLIC PROCEDURE [new: BOOLEAN] = BEGIN stormy ← new; END;
  -- SetOisStormy

  -- This is not an entry procedure because we change it only for debugging and can
  -- live with a race condition.
  SetOisCheckit: PUBLIC PROCEDURE [new: BOOLEAN] = BEGIN checkIt ← new; END;
  -- SetOisCheckIt

  -- This is not an entry procedure because we change it only for debugging and can
  -- live with a race condition.
  SetOisDriverLoopback: PUBLIC PROCEDURE [new: BOOLEAN] =
    BEGIN driverLoopback ← new; END; -- SetOisDriverLoopback

  -- This procedure returns the processor ID of this machine.
  FindMyHostID: PUBLIC PROCEDURE RETURNS [SpecialSystem.HostNumber] =
    BEGIN RETURN[myHostID]; END; -- FindMyHostID

  GetOisPacketTextLength: PUBLIC PROCEDURE [b: BufferDefs.OisBuffer] RETURNS [CARDINAL] =
    BEGIN RETURN[b.ois.pktLength - bytesPerPktHeader]; END;
  -- GetOisPacketTextLength

  SetOisPacketTextLength: PUBLIC PROCEDURE [b: BufferDefs.OisBuffer, len: CARDINAL] =
    BEGIN
    IF len IN [0..bytesPerPktText] THEN
      b.ois.pktLength ← len + bytesPerPktHeader
    ELSE IF CommFlags.doDebug THEN Glitch[IllegalOisPktLength];
    END; -- SetOisPacketTextLength

  SetOisPacketLength: PUBLIC PROCEDURE [b: BufferDefs.OisBuffer, len: CARDINAL] =
    BEGIN
    IF len IN [bytesPerPktHeader..maxBytesPerPkt] THEN b.ois.pktLength ← len
    ELSE IF CommFlags.doDebug THEN Glitch[IllegalOisPktLength];
    END; -- SetOisPacketLength

  -- Hot Procedures

  -- This procedure transmits a packet over a locally connected network.
  -- The procedure assumes that all fields of the buffer have been filled in appropriately,
  -- except the encapsulation and buffer length which depend on the network.  If the
  -- packet is destined for a local socket and we do NOT want to do loopback at the
  -- driver or at the network,  then it gets looped back here.  Broadcast
  -- packets are NOT delivered to the source socket.
  -- Packets are no longer copied into system buffers.
  -- The caller owns the buffer.  The buffer is asynchronously sent and retruned to the 
  -- caller process by the dispatcher using b.requeueProcedure.
  SendPacket: PUBLIC PROCEDURE [b: BufferDefs.OisBuffer] =
    BEGIN
    IF CommFlags.doStorms AND stormy AND PacketHit[] THEN 
      BEGIN
      DriverDefs.PutOnGlobalDoneQueue[b];
      RETURN; -- for debugging only
      END;

      IF b.ois.destination.host = myHostID AND
      NOT driverLoopback THEN
      -- packet is for a local socket; broadcast packets are not delivered locally
      BEGIN
      IF NOT DeliveredToLocalSocket[b, TRUE] THEN -- TRUE -> copy this buffer
        BEGIN
        nullNetwork: Network = NIL; -- because of EXPORTed TYPEs bug
        -- no socket
        IF CommFlags.doStats THEN StatIncr[statJunkOisForUsNoLocalSocket];
        b.network ← nullNetwork; SendErrorPacket[b, noSocketOisErrorCode, 0];
        END;
      -- we requeue here because we shorted the drivers and dispatcher.
      -- we Yield here to prevent local communication from using all of the cycles.
      DriverDefs.PutOnGlobalDoneQueue[b];
      Process.Yield[];
      END
    ELSE
      -- packet is for a remote machine
      BEGIN
      --  If the destination net is inaccessible then we will have suffered the overhead of 
      --  computing a checksum, but we don't expect inaccessible nets that often.
      -- set the checksum if appropriate
      b.ois.transCntlAndPktTp.transportControl ← initialTransportControl;
      IF checkIt THEN Checksums.SetChecksum[b] ELSE b.ois.checksum ← 177777B;
      [] ← Router.FindNetworkAndTransmit[b];
      END;
    END; -- SendPacket

  -- This procedure is called by the Dispatcher when it sees a valid ois packet in a system
  -- buffer.  The procedure checks the checksum field and then routes the packet to a
  -- local socket.  If this is an internetwork router, then the packets are forwarded over
  -- a suitable network.  We now own this packet.
  ReceivePacket: PUBLIC PROCEDURE [b: BufferDefs.OisBuffer] =
    BEGIN
    badChecksum: BOOLEAN;
    incomingNet: Network;
    destHost: SpecialSystem.HostNumber ← b.ois.destination.host;
    broadcastPacket: BOOLEAN  ← FALSE;

    IF b.ois.pktLength < OISCPTypes.bytesPerPktHeader THEN
      BEGIN
      IF CommFlags.doStats THEN StatIncr[statOisDiscarded];
      DriverDefs.PutOnGlobalDoneQueue[b]; -- done with this packet
      RETURN;
      END;

    IF (badChecksum ← IF checkIt THEN NOT Checksums.TestChecksum[b] ELSE FALSE)
      THEN
      -- hardware says ok, but bad end-to-end software checksum.
      BEGIN
      IF CommFlags.doStats THEN
	BEGIN
	StatIncr[statReceivedBadOisChecksum];
	StatIncr[statOisDiscarded];
	END;
      DriverDefs.PutOnGlobalDoneQueue[b]; -- done with this packet
      RETURN;
      END;

    -- packet is in good shape, route it.

      IF CommFlags.doStorms AND stormy AND PacketHit[] THEN
      BEGIN DriverDefs.PutOnGlobalDoneQueue[b]; RETURN; END; -- for debugging only

      incomingNet ← b.network;
      IF destHost = myHostID
      OR ((broadcastPacket←(destHost = allHostIDs))
        AND (incomingNet.netNumber = b.ois.destination.net)
          OR (b.ois.destination.net = SpecialSystem.nullNetworkNumber)) THEN
      BEGIN -- incomming packet for us (we may have broadcast it)
      IF NOT DeliveredToLocalSocket[b, FALSE] THEN
	-- FALSE -> do not copy this buffer, but deliver it
	BEGIN
	-- routing information socket is part of router in another module
	IF b.ois.destination.socket = OISCPConstants.routingInformationSocket THEN
	  Router.RoutingInformationPacket[b] -- we still own this packet!

	ELSE
	  BEGIN -- packet for unknown socket
     IF NOT broadcastPacket THEN
        BEGIN
        SendErrorPacket[b, noSocketOisErrorCode, 0];
        IF CommFlags.doStats THEN StatIncr[statJunkOisForUsNoLocalSocket];
        END
     ELSE IF CommFlags.doStats THEN StatIncr[statJunkBroadcastOis]
	  END;
	DriverDefs.PutOnGlobalDoneQueue[b]; -- done with this packet

	END;
      END
    ELSE SELECT TRUE FROM
      routersFunction=interNetworkRouting =>
        BEGIN
	Router.ForwardPacket[b]; -- dispatcher returns to system pool
        END;
      b.ois.destination.socket=OISCPConstants.routingInformationSocket =>
        BEGIN
	Router.RoutingInformationPacket[b]; -- we still own this packet!
	DriverDefs.PutOnGlobalDoneQueue[b];
        END;
      ENDCASE =>
        BEGIN
	IF CommFlags.doStats THEN StatIncr[statOisDiscarded];
	DriverDefs.PutOnGlobalDoneQueue[b]; -- we got the packet when we shouldn't have!
        END;
    END; -- ReceivePacket

  -- Hot, except should really not exist; therefor cold?
  -- This procedure attempts to hit a packet with a bolt of lightening, and if it succeeds,
  -- then it returns true else false.  The caller dispenses with the buffer.
  PacketHit: ENTRY PROCEDURE RETURNS [BOOLEAN] =
    BEGIN
    IF (lightning ← lightning + 1) > bolt OR lightning < 0 THEN
      BEGIN
      IF lightning > bolt THEN
	BEGIN
	IF bolt > 100 THEN
	  BEGIN
	  randLong: LONG CARDINAL ← System.GetClockPulses[];
	  rand: CARDINAL ← Inline.LowHalf[randLong];
	  lightning ← -INTEGER[rand MOD 20B];
	  bolt ← 10;
	  END
	ELSE BEGIN lightning ← 0; bolt ← bolt + 1; END;
	END;
      IF CommFlags.doStats THEN StatIncr[statZappedP];
      RETURN[TRUE];
      END
    ELSE RETURN[FALSE];
    END; -- PacketHit

  -- This procedure finds a local socket object to deliver the packet to, and if it succeeds,
  -- then it returns true else false.  Caller  retains ownership of buffer if useCopy is true
  -- or return is false;  caller loses buffer ownership if NOT useCopy and return is true.

  DeliveredToLocalSocket: ENTRY PROCEDURE [b: BufferDefs.OisBuffer, useCopy: BOOLEAN]
    RETURNS [BOOLEAN] =
    BEGIN
    ENABLE UNWIND => NULL;
    destSocket: SpecialSystem.SocketNumber ← b.ois.destination.socket;
    sH, prevSH: SocketHandle;
    -- find the correct socket for this packet
    FOR sH ← (prevSH ← socketTable.first), sH.next UNTIL sH = NIL DO
      IF sH.localAddr.socket = destSocket THEN
	BEGIN
	IF useCopy THEN EnqueueCopyOfNewInput[sH, b] ELSE EnqueueNewInput[sH, b];
	-- do some dynamic socket ordering before returning; rearange the list only if 
	-- sH was not in the first two entries of the list.
	IF prevSH # socketTable.first THEN
	  BEGIN
	  prevSH.next ← sH.next; -- this removes sH from the list
	  sH.next ← socketTable.first;
	  socketTable.first ← sH; -- this puts sH at head of list

	  END;
	RETURN[TRUE];
	END;
      prevSH ← sH;
      ENDLOOP;
    RETURN[FALSE];
    END; -- DeliveredToLocalSocket

  -- This procedure enqueues a new input packet at the socket object.
  -- The incoming buffer is from a  network driver and NOT a local socket.
  -- The caller of this routine relinquishes ownership of this buffer.
  EnqueueNewInput: INTERNAL PROCEDURE [sH: SocketHandle, b: BufferDefs.OisBuffer] =
    INLINE
    BEGIN
    IF (sH.channelState = aborted) OR NOT
      (OISCP.CreditReceiveOisBuffer[sH.pool, b]) THEN
      BEGIN
      IF CommFlags.doStats THEN StatsDefs.StatIncr[statOisInputQueueOverflow];
      DriverDefs.PutOnGlobalDoneQueue[b];
      RETURN;
      END;
    b.status ← LOOPHOLE[Router.XmitStatus[goodCompletion]];
    EnqueueOis[@sH.completedUserGetQueue, b];
    BROADCAST sH.newUserInput;
    END; -- EnqueueNewInput

  -- This procedure enqueues a new input packet at the socket object.
  -- The incoming buffer is only from a  local socket.
  -- The caller of this routine retains ownership of this buffer.
  EnqueueCopyOfNewInput: INTERNAL PROCEDURE [
    sH: SocketHandle, b: BufferDefs.OisBuffer] = INLINE
    BEGIN
    getBuffer: BufferDefs.OisBuffer ←
      IF sH.channelState = aborted THEN NIL
      ELSE MaybeGetFreeReceiveOisBufferFromPool[sH.pool];
    -- Copy b into the first OisBuffer on the pendingGetQueue, if there is one.
    IF (getBuffer # NIL) THEN
      BEGIN
      getBuffer.status ← LOOPHOLE[Router.XmitStatus[goodCompletion]];
      -- assume always good
      -- getBuffer is full sized OisBuffer, therefore no need to check length
      CommUtilDefs.CopyLong[
	from: @b.ois.checksum, nwords: (b.ois.pktLength + 1)/2,
	to: @getBuffer.ois.checksum];
      EnqueueOis[@sH.completedUserGetQueue, getBuffer];
      BROADCAST sH.newUserInput;
      END
    ELSE IF CommFlags.doStats THEN StatsDefs.StatIncr[statOisInputQueueOverflow];
    -- funny name
    END; -- EnqueueNewLocalInput

  -- This procedure causes a broadcast packet to be sent over all networks.
  BroadcastThisPacket: PUBLIC PROCEDURE [b: BufferDefs.OisBuffer] =
   BEGIN
   network: Network;
   IF CommFlags.doStats THEN StatIncr[statOisBroadcast];
   b.allNets ← TRUE; -- this is where it gets turned on
   b.ois.destination.host ← SpecialSystem.broadcastHostNumber;
   b.network ← network ← DriverDefs.GetDeviceChain[];
   IF network = NIL THEN 
     BEGIN
     DriverDefs.PutOnGlobalDoneQueue[b];
     IF CommFlags.doStats THEN StatsDefs.StatIncr[statOisSentNowhere];
     RETURN; 
     END;
   SendBroadcastPacketToCorrectNet[b];
   END;  -- BroadcastThisPacket

  -- This procedure causes a broadcast packet to be sent out over the right network.
  SendBroadcastPacketToCorrectNet: PUBLIC PROCEDURE [b: BufferDefs.OisBuffer] =
   BEGIN
   network: Network ← b.network;
   IF ~network.alive OR (b.bypassZeroNet AND network.netNumber = [0, 0]) THEN
     BEGIN DriverDefs.PutOnGlobalDoneQueue[b]; RETURN; END;
   -- goes (slowly) around in circles
   b.ois.destination.net ← b.ois.source.net ← network.netNumber;
   b.ois.source.host ← myHostID;
   IF checkIt THEN Checksums.SetChecksum[b] ELSE b.ois.checksum ← 177777B;
   LOOPHOLE[b.status, DriverDefs.DriverXmitStatus] ← goodCompletion;
   network.encapsulateOis[b, SpecialSystem.broadcastHostNumber];
   network.sendBuffer[b];
   END;  -- SendBroadcastPacketToCorrectNet

  -- This procedure generates and sends an error packet.  All or most of the 
  -- offending packet is copied into the the error packet.  The caller of this 
  -- Procedure still owns offendingPkt.
  SendErrorPacket: PUBLIC PROCEDURE [offendingPkt: BufferDefs.OisBuffer, errCode: OISCPTypes.OISErrorCode, errParm: CARDINAL] =
    BEGIN
    net: Network = offendingPkt.network;
    b: BufferDefs.OisBuffer;
    offenseLen: CARDINAL;
    IF offendingPkt.ois.transCntlAndPktTp.packetType=error THEN RETURN;  -- don't send errors about errors
    IF (b ← DriverDefs.MaybeGetFreeOisBuffer[])=NIL THEN RETURN;  -- give up!
    b.ois.destination ← offendingPkt.ois.source;
    b.ois.source ← [ IF net=NIL THEN OISCP.unknownNetID ELSE net.netNumber,
      myHostID, OISCP.unknownSocketID];
    b.ois.transCntlAndPktTp ← [initialTransportControl, error];
    b.ois.errorType ← errCode;
    b.ois.errorParameter ← errParm;
    offenseLen ← MIN[offendingPkt.ois.pktLength, OISCPTypes.bytesPerPktText-4];  -- four is for errorType and errorParameter.
    CommUtilDefs.CopyLong[@offendingPkt.ois, (offenseLen+1)/2, @b.ois.errorBody];
    SetOisPacketTextLength[b, offenseLen+4];
    SendPacket[b];
    END;  -- SendErrorPacket

  --Cold Procedures

  -- This procedure turns the router on.  The Communication software is written with the
  -- idea of eventually turning the code on and off during execution, and so we should
  -- get the latest values of netID and hostID when being turned back on.
  OisRouterOn: PUBLIC PROCEDURE =
    BEGIN
    OisRouterActivate[];
    Router.RoutingTableOn[];
    DriverDefs.SetOisRouter[@oiscpRouter];
    END; -- OisRouterOn

  OisRouterActivate: ENTRY PROCEDURE = INLINE
    BEGIN
    myHostID ← SpecialSystem.GetProcessorID[];
    IF primaryMDS THEN socketTable ← [length: 0, first: NIL];
    END; -- OisRouterActivate

  OisRouterOff: PUBLIC PROCEDURE =
    BEGIN
    DriverDefs.SetOisRouter[NIL];
    OisRouterDeactivate[];
    Router.RoutingTableOff[];
    END; -- OisRouterOff

  OisRouterDeactivate: ENTRY PROCEDURE = INLINE
    BEGIN
    -- cleanup the socket table.
    --IF primaryMDS THEN 

    END; -- OisRouterDeactivate

  --Cold
  -- initialization

  IF primaryMDS THEN spareSocketID ← initialSpareSocketID;

  END.  -- RouterImpl module.

LOG

Time: January 19, 1980  4:05 PM By: Dalal  Action: Split OISCPRouter into two.
Time: January 21, 1980  6:07 PM By: Dalal  Action: one lock for SocketImpl and RouterImpl.
Time: March 13, 1980  4:55 PM  By: BLyon  Action: modified SendPacket.
Time: March 18, 1980  4:09 PM  By: BLyon  Action: Modified EnqueueNewInput where it gets an input buffer.
Time: May 12, 1980  6:38 PM  By: BLyon  Action: Put checksum into microcode (switched order parameters too).
Time: May 16, 1980  10:07 AM PM  By: BLyon  Action: Removed all ShortenPointer to allow multiple MDS. 
Time: June 30, 1980  1:02 PM  By: BLyon  Action: Checkit init to FALSE instead of TRUE.. 
Time: July 22, 1980  11:03 AM  By: BLyon  Action: Checkit changed back to TRUE; checksums stuff put in seperate modules; we now receive out own broadcasts. 
Time: August 1, 1980  1:29 PM  By: BLyon  Action: replaced internetRouter by routersFunction. 
Time: September 13, 1980  6:15 PM  By: HGM  Action: Add StateChanged. 
Time: September 18, 1980  3:34 PM  By: BLyon  Action: AssignOisAddress puts unknownNetID in network field rather than primaryNetID. 
Time: February 24, 1981  3:25 PM  By: BLyon  Action: put extra clause in ReceivePacket so that an INR would forward a broadcast instead of eating it with a local socket.