<> <> <> <> <> <> <> 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; <> 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 { <> p: LONG POINTER TO CARDINAL _ LOOPHOLE[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 { <> 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 { <> p: LONG POINTER TO ARRAY [0..8) OF CARDINAL _ LOOPHOLE[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 <<>> <> headerPtr: LONG POINTER TO IPDefs.InternetHeader; headerLength: INT; data: IPDefs.Datagram; IF buffer.ovh.encap.ethernetOneType # fromImp THEN RETURN[buffer]; headerPtr _ IPRouter.DatagramPointer[buffer]; <> 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; <> ipRunning: BOOL _ FALSE; timerProcess: PROCESS; TimerProc: PROC ~ { <> 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.