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.
IPOpsImpl.mesa
Last Edited by: HGM, October 7, 1984 6:33:26 am PDT
Last Edited by: Nichols, August 16, 1983 2:06 pm
Last Edited by: Taft, January 5, 1984 4:20 pm
Hal Murray May 16, 1985 2:46:55 am PDT
John Larson, June 4, 1986 11:28:05 pm PDT
DIRECTORY
Basics USING [BITNOT, HighHalf, LowHalf],
BasicTime USING [GMT, Now, Period],
CommBuffer USING [],
CommDriver USING [Buffer, GetNetworkChain, InsertReceiveProc, Network, RecvProc],
CommDriverType USING [Encapsulation],
IPDefs USING [Datagram, DatagramRec, Address, InternetHeader, maxDataLength, maxOptionLength, minIHL, DByte],
IPOps,
IPQueue USING [QueueDatagram],
IPReassembly USING [AgeFragments, Reassemble],
IPRouter USING [DatagramPointer],
PrincOpsUtils USING [ByteBlt],
Process USING [MsecToTicks, Pause],
Pup USING [Host];
IPOpsImpl: CEDAR PROGRAM
IMPORTS Basics, BasicTime, CommDriver, IPQueue, IPReassembly, IPRouter, PrincOpsUtils, Process
EXPORTS IPOps, CommBuffer =
BEGIN OPEN IPOps;
Encapsulation: PUBLIC TYPE = CommDriverType.Encapsulation;
Exported
MoveBytes: PUBLIC UNSAFE PROC [toPtr: LONG POINTER, toOffset: INT, fromPtr: LONG POINTER, fromOffset: INT, length: INT] = UNCHECKED
BEGIN
moved: INT;
to: LONG POINTER TO PACKED ARRAY [0..500) OF CHAR = LOOPHOLE[toPtr];
from: LONG POINTER TO PACKED ARRAY [0..500) OF CHAR = LOOPHOLE[fromPtr];
IF ~(length IN [0..LAST[CARDINAL]]) THEN ERROR;
moved ← PrincOpsUtils.ByteBlt[[toPtr, toOffset, toOffset+length], [fromPtr, fromOffset, fromOffset+length]];
IF length # moved THEN ERROR;
END;
UnsafeHeaderChecksum: UNSAFE PROC [header: LONG POINTER TO IPDefs.InternetHeader] RETURNS [checksum: IPDefs.DByte] ~ UNCHECKED {
Compute the header checksum for a datagram.
p: LONG POINTER TO CARDINALLOOPHOLE[header];
maxHeaderLength: INT ~ 10 + IPDefs.maxOptionLength / 2;
nWords: INT ~ MIN[header.IHL, maxHeaderLength];
cs: CARDINAL ← OnesComplementAddBlock[ptr: p, count: 2*nWords, initialSum: Basics.BITNOT[header.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.
RETURN [Basics.BITNOT[cs]]; -- return one's complement of computed sum
};
HeaderChecksum: PUBLIC PROC [data: IPDefs.Datagram] RETURNS [checksum: IPDefs.DByte] ~ TRUSTED {
Compute the header checksum for a datagram.
RETURN UnsafeHeaderChecksum[@data.inHdr];
};
ChecksumsMatch: PROC [c1, c2: IPDefs.DByte] RETURNS [BOOL] ~ {
RETURN [c1 = c2 OR ((c1 = 0 OR c1 = 65535) AND (c2 = 0 OR c2 = 65535))];
};
OnesComplementAddBlock: PUBLIC UNSAFE PROC [ptr: LONG POINTER, count: CARDINAL, initialSum: CARDINAL ← 0] RETURNS [sum: CARDINAL] ~ UNCHECKED {
Computes (efficiently) the ones-complement sum of the 16-bit words in the block [ptr .. ptr+count)^.
p: LONG POINTER TO ARRAY [0..8) OF CARDINALLOOPHOLE[ptr];
s: LONG CARDINAL ← initialSum;
FOR i: CARDINAL IN [0..count MOD 8) DO s ← s + p[i]; ENDLOOP;
p ← p+count MOD 8;
THROUGH [0..count/8) DO
s ← s + LONG[p[0]] + LONG[p[1]] + LONG[p[2]] + LONG[p[3]] + LONG[p[4]] + LONG[p[5]] + LONG[p[6]] + LONG[p[7]];
p ← p+8;
ENDLOOP;
WHILE Basics.HighHalf[s]#0 DO
s ← LONG[Basics.HighHalf[s]]+LONG[Basics.LowHalf[s]];
ENDLOOP;
RETURN[Basics.LowHalf[s]];
};
Send: PUBLIC PROC [etherDest: Pup.Host, buffer: CommDriver.Buffer, bytes: CARDINAL] = {
buffer.ovh.encap ← Encapsulation[ethernetOne [etherSpare1:, etherSpare2:, etherSpare3:, etherSpare4:, etherSpare5:, ethernetOneDest: etherDest, ethernetOneSource: network.pup.host, ethernetOneType: toImp]];
network.other.send[network, buffer, bytes];
};
network: CommDriver.Network; -- handle on the Ethernet
HandleIncomingPacket: CommDriver.RecvProc = TRUSTED
[network: CommDriver.Network, buffer: CommDriver.Buffer, bytes: NAT] RETURNS [CommDriver.Buffer]
BEGIN
The receiver process calls this procedure with new packets. While here, we check that the packet checksums and lengths are ok. Then we dive into the monitor to do reassembly and queueing.
headerPtr: LONG POINTER TO IPDefs.InternetHeader;
headerLength: INT;
data: IPDefs.Datagram;
IF buffer.ovh.encap.ethernetOneType # fromImp THEN RETURN[buffer];
headerPtr ← IPRouter.DatagramPointer[buffer];
Crosscheck IP length with bytes above
IF headerPtr.IHL < 5 OR NOT headerPtr.packetLength IN [20..IPDefs.maxDataLength+20] OR NOT ChecksumsMatch[headerPtr.checksum, UnsafeHeaderChecksum[headerPtr]] THEN { -- bad packet
RETURN [buffer]; };
data ← NEW[IPDefs.DatagramRec];
headerLength ← headerPtr.IHL*4;
MoveBytes[@data.inHdr, 0, headerPtr, 0, headerLength];
MoveBytes[@data.data, 0, headerPtr, headerLength, headerPtr.packetLength-headerLength];
data.dataLength ← headerPtr.packetLength-headerLength;
data.optionLength ← headerLength - IPDefs.minIHL*4;
data ← IPReassembly.Reassemble[data];
IF data # NIL THEN IPQueue.QueueDatagram[data];
RETURN [buffer];
END;
Private stuff
ipRunning: BOOLFALSE;
timerProcess: PROCESS;
TimerProc: PROC ~ {
Wakes up every so often and ages things.
newTime, oldTime: BasicTime.GMT;
oldTime ← BasicTime.Now[];
WHILE ipRunning DO
Process.Pause[Process.MsecToTicks[5000]];
newTime ← BasicTime.Now[];
IPReassembly.AgeFragments[BasicTime.Period[from: oldTime, to: newTime]];
oldTime ← newTime;
ENDLOOP;
};
StartupIP: PROC ~ {
network ← CommDriver.GetNetworkChain[]; -- Should be loop if > 1 ethernet board
IF network.type # ethernetOne THEN ERROR;
ipRunning ← TRUE;
timerProcess ← FORK TimerProc[];
CommDriver.InsertReceiveProc[network, other, HandleIncomingPacket]; -- Tell the driver to give us incoming packets.
};
StartupIP[];
END.