-- File: ArpaRouterImpl.mesa - last edit: -- JAV 10-Dec-87 12:49:33 -- AOF 26-May-87 11:07:23 -- SMA 27-Jun-86 13:40:56 -- Copyright (C) 1985, 1986, 1987 by Xerox Corporation. All rights reserved. DIRECTORY ArpaBuffer USING [ Body, Buffer, Enqueue, ExtractFromQueue, DataBytesPerRawBuffer, From, QueueObject, QueueCleanup, QueueInitialize, ReturnBuffer, To], ArpaFlags USING [doStats], ArpaInit USING [GetArpaInitInfo], ArpaPortInternal USING [AddrMatch, AddrMismatch, Checksum, DestType, InitUniquePort, QueueForClient], ArpaPort USING [minIPHeaderBytes, SwapSourceAndDestination], Arpa10MBit USING [Capsulators, CreateCapsulators], ArpaRouter USING [unknownInternetAddress], ArpaRoutingTable USING [ContextObject, defaultRth, Handle, InfoReply, NetworkContext, Object, ProbeGateway, Redirect, RoutersFunction], ArpaStats USING [Incr], ArpaTypes USING [Cardinal32, Icmp, IcmpType, InternetAddress, Port, Timestamp], Buffer USING [], ByteBlt USING [ByteBlt], CommHeap USING [zone], CommunicationInternal USING [CommPackageGo], Driver USING [GetDeviceChain, Device, nilDevice, PutOnGlobalDoneQueue], Environment USING [Byte, bytesPerWord], IEEE8023 USING [EncapObject, maxDataBytesPerEthernetPacket], Inline USING [DBITAND, UDDivMod], Mopcodes USING [zEXCH], Process USING [Abort, EnableAborts, Pause, priorityNormal, SecondsToTicks, SetPriority, SetTimeout, Yield], ProcessorFace USING [mp, SetMP], Protocol1 USING [ Action, AddFamilyMember, EncapsulateAndTransmit, EvictFamily, Family, FamilyUnit, MatrixRecord, RegisterFamily, SetMaximumBufferSize], System USING [GetClockPulses, GetGreenwichMeanTime, PulsesToMicroseconds]; ArpaRouterImpl: MONITOR IMPORTS ArpaBuffer, ArpaInit, ArpaPort, ArpaPortInternal, Arpa10MBit, ArpaRouter, ArpaRoutingTable, ArpaStats, ByteBlt, CommHeap, CommunicationInternal, Driver, Inline, Process, ProcessorFace, Protocol1, System EXPORTS ArpaPortInternal, ArpaPort, ArpaRouter, ArpaRoutingTable, Buffer = BEGIN --IP implementation. Port: PUBLIC --ArpaRouter-- TYPE = ArpaTypes.Port; InternetAddress: PUBLIC --ArpaRouter-- TYPE = ArpaTypes.InternetAddress; Device: PUBLIC --Buffer-- TYPE = Driver.Device; driverLoopback: BOOLEAN ← FALSE; bpw: NATURAL = Environment.bytesPerWord; routersFunction: ArpaRoutingTable.RoutersFunction; unknownInternetAddress: PUBLIC <<ArpaRouter>> InternetAddress ← [0, 0]; --Start up and addresses for the primary ethernet. findAddrMP: CARDINAL = 982; --set in MP when searching for my address myArpaAddr, myBroadcastAddr, myGWAddr, mySubnetMask, myDomainNameServer, myNullAddr: InternetAddress ← unknownInternetAddress; rto: PUBLIC ArpaRoutingTable.Object; --the routing object arpaProtocolFamily: PUBLIC Protocol1.FamilyUnit ← [ name: <<Protocol1.ProtocolFamily>> arpa, state: <<LONG POINTER>> NIL, status: <<Protocol1.FamilyStatus>> dead, maxBufferSize: <<CARDINAL>> IEEE8023.maxDataBytesPerEthernetPacket, --1500 receive: <<PROC[Buffer.Buffer]>> LOOPHOLE[ReceivePacket], broadcast: <<PROC[Buffer.Buffer]>> LOOPHOLE[Broadcast], stateChanged: <<PROC[ Buffer.Device, LONG POINTER, Protocol1.Action]>> StateChanged, spy: <<PROC[ Buffer.Buffer, Buffer.Function] RETURNS [Buffer.Buffer]>> NIL]; useCount: CARDINAL ← 0; --IP reassembly. ListEntry: TYPE = LONG ORDERED POINTER TO ListObject; ListObject: TYPE = RECORD [next: ListEntry, first, last: CARDINAL]; fragQueue: ArpaBuffer.QueueObject; --queue of packets being reassembled. fragTimeout: CARDINAL = 15; --in seconds fragTimer: CONDITION; fragWatcher: PROCESS ← NIL; --addressing classes classA: InternetAddress = [0B, 0B]; classB: InternetAddress = [100000B, 0B]; classC: InternetAddress = [140000B, 0B]; classAMask: InternetAddress = [100000B, 0B]; classBMask: InternetAddress = [140000B, 0B]; classCMask: InternetAddress = [160000B, 0B]; ArpaPackageMake: PUBLIC PROC = BEGIN AlreadyStarted: ENTRY PROC RETURNS[BOOLEAN] = INLINE {RETURN[(useCount ← useCount + 1) > 1]}; driver: Device; matrix: Protocol1.MatrixRecord; oldMp: CARDINAL = ProcessorFace.mp; IF AlreadyStarted[] THEN RETURN; CommunicationInternal.CommPackageGo[]; --we need the basics driverLoopback ← TRUE; ArpaPortInternal.InitUniquePort[]; << --This should eventually be done by something like RARP. This proc should eventually broadcast an ICMP address mask reply and Receive should parse ICMP address mask replies. But for now (and until we know if any gateways actually support ICMP address mask replies), I used our favorite host table hack in GetArpaAddr... >> ProcessorFace.SetMP[findAddrMP]; <<UNTIL myArpaAddr # ArpaRouter.unknownInternetAddress>> DO [myArpaAddr, myGWAddr, mySubnetMask, myDomainNameServer] ← ArpaInit.GetArpaInitInfo[]; IF myArpaAddr # unknownInternetAddress THEN EXIT; Process.Pause[Process.SecondsToTicks[10]]; ENDLOOP; ProcessorFace.SetMP[oldMp]; --back to original value myBroadcastAddr ← SELECT TRUE FROM Inline.DBITAND[LOOPHOLE[myArpaAddr], LOOPHOLE[classAMask]] = LOOPHOLE[classA, LONG UNSPECIFIED] => [77777B, 177777B], Inline.DBITAND[LOOPHOLE[myArpaAddr], LOOPHOLE[classBMask]] = LOOPHOLE[classB, LONG UNSPECIFIED] => [137777B, 177777B], Inline.DBITAND[LOOPHOLE[myArpaAddr], LOOPHOLE[classCMask]] = LOOPHOLE[classC, LONG UNSPECIFIED] => [157777B, 177777B], ENDCASE => unknownInternetAddress; myNullAddr ← SELECT TRUE FROM Inline.DBITAND[LOOPHOLE[myArpaAddr], LOOPHOLE[classAMask]] = LOOPHOLE[classA, LONG UNSPECIFIED] => classA, Inline.DBITAND[LOOPHOLE[myArpaAddr], LOOPHOLE[classBMask]] = LOOPHOLE[classB, LONG UNSPECIFIED] => classB, Inline.DBITAND[LOOPHOLE[myArpaAddr], LOOPHOLE[classCMask]] = LOOPHOLE[classC, LONG UNSPECIFIED] => classC, ENDCASE => ArpaRouter.unknownInternetAddress; BEGIN OPEN c: LOOPHOLE[matrix.context, ArpaRoutingTable.NetworkContext]; Protocol1.RegisterFamily[@arpaProtocolFamily]; --make us known matrix.family ← @arpaProtocolFamily; --this is the same for all drivers FOR driver ← Driver.GetDeviceChain[], driver.next UNTIL driver = NIL DO IF (driver.device # ethernet) OR ~driver.alive THEN LOOP; matrix.context ← CommHeap.zone.NEW[ArpaRoutingTable.ContextObject]; c.network ← driver; c.stats ← NIL; c.protocol ← Arpa10MBit.CreateCapsulators[driver]; [matrix.decapsulator, matrix.encapsulator] ← Arpa10MBit.Capsulators[]; --after the call to ProbeGateway, c.net will either be set, or will be null --with the correct class bits turned on. c.net ← ArpaRouter.unknownInternetAddress; c.host ← myArpaAddr; [c.hostMask, c.netMask] ← BuildMasks[c.host]; IF (mySubnetMask ← GetSubnetMask[]) # ArpaRouter.unknownInternetAddress THEN c.netMask ← mySubnetMask; --are subnets in use? Protocol1.AddFamilyMember[driver, @matrix]; Protocol1.SetMaximumBufferSize[driver, @arpaProtocolFamily, IEEE8023.maxDataBytesPerEthernetPacket]; ENDLOOP; Register[]; --register default table impl ArpaBuffer.QueueInitialize[@fragQueue]; Process.SetTimeout[@fragTimer, Process.SecondsToTicks[7]]; Process.EnableAborts[@fragTimer]; END; --OPEN context ArpaRoutingTable.ProbeGateway[]; --for our own net number. END; --ArpaPackageMake ArpaPackageDestroy: PUBLIC PROC = BEGIN StillInUse: ENTRY PROC RETURNS[BOOLEAN] = {RETURN [(useCount ← useCount - 1) # 0]}; IF StillInUse[] THEN RETURN; Protocol1.EvictFamily[arpa]; --stop dispatcher's access IF (fragWatcher # NIL) THEN BEGIN Process.Abort[fragWatcher]; JOIN fragWatcher; END; ArpaBuffer.QueueCleanup[@fragQueue]; IF rto.stop # NIL THEN rto.stop[]; END; --ArpaPackageDestroy Broadcast: PROC[b: ArpaBuffer.Buffer] = BEGIN network: Device = b.fo.network; body: ArpaBuffer.Body = b.arpa; IF ~network.alive THEN {Driver.PutOnGlobalDoneQueue[ArpaBuffer.To[b]]; RETURN}; body.ipHeader.source ← myArpaAddr; body.ipHeader.destination ← myBroadcastAddr; body.ipHeader.checksum ← 0; body.ipHeader.checksum ← ArpaPortInternal.Checksum[ 0, body.ipHeader.ihl * bpw, @body.ipHeader]; b.fo.status ← goodCompletion; Protocol1.EncapsulateAndTransmit[ArpaBuffer.To[b], @myBroadcastAddr]; IF ArpaFlags.doStats THEN ArpaStats.Incr[broadcastsSent]; END; --Broadcast BuildMasks: PUBLIC PROC [addr: InternetAddress] RETURNS [hostMask, netMask: InternetAddress] = BEGIN SELECT TRUE FROM (Inline.DBITAND[LOOPHOLE[addr], LOOPHOLE[classAMask]] = LOOPHOLE[classA, LONG UNSPECIFIED]) => {hostMask ← [100377B, 177777B]; netMask ← [177400B, 0B]}; (Inline.DBITAND[LOOPHOLE[addr], LOOPHOLE[classBMask]] = LOOPHOLE[classB, LONG UNSPECIFIED]) => {hostMask ← [140000B, 177777B]; netMask ← [177777B, 0B]}; (Inline.DBITAND[LOOPHOLE[addr], LOOPHOLE[classCMask]] = LOOPHOLE[classC, LONG UNSPECIFIED]) => {hostMask ← [160000B, 377B]; netMask ← [177777B, 177400B]}; ENDCASE => --unimplemented extended addressing or unrecognizable. hostMask ← netMask ← [0B, 0B]; END; --BuildMasks GetArpaAddr, GetAddress: PUBLIC <<ArpaPortInternal, ArpaRouter>> ENTRY PROC RETURNS [ArpaTypes.InternetAddress] = {RETURN[myArpaAddr]}; GetGatewayAddr: PUBLIC <<ArpaPortInternal>> ENTRY PROC RETURNS [addr: ArpaTypes.InternetAddress] = {RETURN[myGWAddr]}; SetGatewayAddr: PUBLIC <<ArpaPortInternal>> ENTRY PROC [addr: ArpaTypes.InternetAddress] = {myGWAddr ← addr}; GetDomainNameServer: PUBLIC <<ArpaPortInternal>> ENTRY PROC RETURNS [addr: ArpaTypes.InternetAddress] = {RETURN[myDomainNameServer]}; SetDomainNameServer: PUBLIC <<ArpaPortInternal>> ENTRY PROC [addr: ArpaTypes.InternetAddress] = {myDomainNameServer ← addr}; GetSubnetMask: PUBLIC PROC RETURNS[InternetAddress] = {RETURN[mySubnetMask]}; GetMyBroadcastAddr: PUBLIC <<ArpaPortInternal>> PROC[] RETURNS[InternetAddress] = {RETURN[myBroadcastAddr]}; --GetMyBroadcastAddr GetMyNullAddr: PUBLIC <<ArpaPortInternal>> PROC[] RETURNS[InternetAddress] = {RETURN[myNullAddr]}; --GetMyNullAddr GetIPLengths: PUBLIC PROC [body: ArpaBuffer.Body] RETURNS [optionsLen, dataLen: CARDINAL] = BEGIN dataLen ← body.ipHeader.length - body.ipHeader.ihl*4; optionsLen ← body.ipHeader.ihl*4 - ArpaPort.minIPHeaderBytes - dataLen; END; --GetIPLengths ProcessICMPPacket: PROC [b: ArpaBuffer.Buffer] RETURNS [keeper: BOOLEAN ← FALSE] = BEGIN OPEN c: LOOPHOLE[b.fo.context, ArpaRoutingTable.NetworkContext]; body: ArpaBuffer.Body = b.arpa; hmsk: InternetAddress = c.hostMask; bytes: CARDINAL ← body.ipHeader.length - (body.ipHeader.ihl*4); words: CARDINAL ← (bytes + bpw - 1) / bpw; PTag: TYPE = LONG POINTER TO Tag; Tag: TYPE = ARRAY[0..2) OF ArpaTypes.IcmpType; IF ArpaFlags.doStats THEN ArpaStats.Incr[ SELECT body.icmp.type FROM unreachable => icmpUnreachable, timeExceeded => icmpTimeExceeded, paramProblem => icmpParam, quench => icmpQuench, redirect => icmpRedirect, infoRequest => icmpInfoReq, infoReply => icmpInfoRep, echo => icmpEcho, echoReply => icmpEchoRep, timestamp => icmpTimestamp, timestampReply => icmpTimestampRep, ENDCASE => icmpUnknown]; --Check the checksum. IF (bytes MOD 2) # 0 THEN body.ipBytes[bytes] ← 0; --zero the pad byte. IF ArpaPortInternal.Checksum[0, words, @body.icmp] # 0 THEN BEGIN IF ArpaFlags.doStats THEN ArpaStats.Incr[icmpChecksum]; ArpaBuffer.ReturnBuffer[b]; RETURN; END; WITH icmp: body.icmp SELECT FROM --for the routing table implementation. redirect => {ArpaRoutingTable.Redirect[b]; ArpaBuffer.ReturnBuffer[b]}; infoReply => {ArpaRoutingTable.InfoReply[b]; ArpaBuffer.ReturnBuffer[b]}; --echo and timestamp can be taken care of right here. echo => BEGIN << Need to modify the IcmpType from 'echo' to 'echoReply'. That's no small feat considering that it's also the variant tag field. >> tag: Tag = ALL[ArpaTypes.IcmpType[echoReply]]; LOOPHOLE[@body.icmp.body, PTag]↑[0] ← tag[0]; ArpaPort.SwapSourceAndDestination[b]; icmp.checksum ← 0; --first you set it to zero icmp.checksum ← ArpaPortInternal.Checksum[0, words, @body.icmp]; SendPacket[b, IF ArpaPortInternal.AddrMatch[hmsk, body.ipHeader.source, body.ipHeader.destination] THEN local ELSE remote]; END; timestamp => BEGIN << Need to modify the IcmpType from 'timestamp' to 'timestampReply'. That's no small feat considering that it's also the variant tag field. >> CardToTime: PROC[LONG CARDINAL] RETURNS[ArpaTypes.Cardinal32] = MACHINE CODE {Mopcodes.zEXCH}; tag: Tag = ALL[ArpaTypes.IcmpType[timestampReply]]; secondsInDay: LONG CARDINAL = LONG[24 --hr-- * 60 --min--] * 60 --sec--; now: LONG CARDINAL ← System.GetGreenwichMeanTime[]; --that's current now ← now MOD secondsInDay; --so that would have been midnight icmp.receive ← icmp.transmit ← CardToTime[now * 1000]; --in msecs LOOPHOLE[@body.icmp.body, PTag]↑[0] ← tag[0]; ArpaPort.SwapSourceAndDestination[b]; --get ready to send packet back icmp.checksum ← 0; --first you set it to zero icmp.checksum ← ArpaPortInternal.Checksum[0, words, @body.icmp]; SendPacket[b, IF ArpaPortInternal.AddrMatch[hmsk, body.ipHeader.source, body.ipHeader.destination] THEN local ELSE remote]; END; --These are turned over to the client to handle unreachable, timeExceeded, paramProblem, quench, echoReply => keeper ← TRUE; --infoRequest, timestampReply, and unknowns just get ignored right here ENDCASE => ArpaBuffer.ReturnBuffer[b]; END; --ProcessICMPPacket ReceivePacket: PROC[b: ArpaBuffer.Buffer] = BEGIN OPEN ArpaPortInternal, --for AddrMismatch and Checksum. c: LOOPHOLE[b.fo.context, ArpaRoutingTable.NetworkContext]; body: ArpaBuffer.Body = b.arpa; hmsk: InternetAddress = c.hostMask; CleanFragList: ENTRY PROC = INLINE {NOTIFY fragTimer}; CleanFragList[]; --get rid of possible old fragments IF ArpaFlags.doStats THEN ArpaStats.Incr[ipRecvd]; SELECT TRUE FROM (rto.type # gateway AND AddrMismatch[hmsk, c.host, b.arpa.ipHeader.destination] AND AddrMismatch[hmsk, myBroadcastAddr, b.arpa.ipHeader.destination] AND AddrMismatch[hmsk, myNullAddr, b.arpa.ipHeader.destination]) => -- this is in violation fo protocol for 4.2Bsd broadcast {IF ArpaFlags.doStats THEN ArpaStats.Incr[badDest]; ArpaBuffer.ReturnBuffer[b]}; --not for us. (Checksum[0, body.ipHeader.ihl * bpw, body] # 0) => {IF ArpaFlags.doStats THEN ArpaStats.Incr[badChecksum]; ArpaBuffer.ReturnBuffer[b]}; --bad checksum in header. (body.ipHeader.lifetime = 0) => {IF ArpaFlags.doStats THEN ArpaStats.Incr[tooOld]; ArpaBuffer.ReturnBuffer[b]}; --been around too long. ((body.ipHeader.fragment # 0) OR body.ipHeader.flags.moreFragment) AND ((b ← Reassemble[b]) = NIL) => NULL; --Reassemble will return b. (rto.type = gateway) AND --packet to forward. (AddrMismatch[hmsk, myBroadcastAddr, b.arpa.ipHeader.destination]) AND (AddrMismatch[hmsk, myNullAddr, b.arpa.ipHeader.destination]) AND -- don't forward broadcasts!!! (AddrMismatch[c.hostMask, c.host, body.ipHeader.destination]) => {IF ArpaFlags.doStats THEN ArpaStats.Incr[forwarded]; rto.forward[b]}; (body.ipHeader.protocol = icmp) AND (~ProcessICMPPacket[b]) => --icmp, maybe to client. NULL; --ProcessICMPPacket will turn around or return the buffer. (~ArpaPortInternal.QueueForClient[b: b, copy: FALSE]) => --to client ArpaBuffer.ReturnBuffer[b]; ENDCASE; END; --ReceivePacket Reassemble: ENTRY PROC[frag: ArpaBuffer.Buffer] RETURNS [b: ArpaBuffer.Buffer] = BEGIN << Takes an IP fragment and puts it into the reassembly buffer. If this is the first fragment of a packet to arrive, its buffer will be used as the reassembly buffer. The reassembly is managed via hole descriptors and the algorthms in RFC 815. The only difference is that the header is also managed with hole descriptors, hence all the additions of hdrLen in the calculations. >> body: ArpaBuffer.Body ← frag.arpa; IF fragWatcher = NIL THEN fragWatcher ← FORK FragWatcher[]; IF ArpaFlags.doStats THEN ArpaStats.Incr[fragRcvd]; IF (b ← FindReassembly[body]) = NIL THEN BEGIN listHead: ListEntry; --fragment of a new packet - this buffer will be used to reassemble into. ArpaBuffer.Enqueue[@fragQueue, frag]; IF body.ipHeader.fragment = 0 THEN --this has the header we want. BEGIN listHead ← LOOPHOLE[body + (body.ipHeader.length + 1)/2]; listHead↑ ← [NIL, body.ipHeader.length, LAST[CARDINAL]]; END ELSE BEGIN --we just want the data from any subsequent ones. listHead ← LOOPHOLE[body]; listHead↑ ← [NIL, 0, body.ipHeader.fragment * 8 - 1]; listHead.next ← LOOPHOLE[body + (body.ipHeader.fragment * 8 + 1)/2]; listHead.next↑ ← [NIL, body.ipHeader.length, LAST[CARDINAL]] END; << requeueData is used to hold the timeout for assembling this buffer. This is truly slimy, but it means we can use the queue package and not have to carry another state object around for the timeout info. >> frag.requeueData ← MAX[body.ipHeader.lifetime, fragTimeout]; frag.fo.driver.iocb ← LOOPHOLE[listHead]; END ELSE BEGIN b ← AddFrag[frag, b]; ArpaBuffer.ReturnBuffer[frag]; END; END; --Reassemble FindReassembly: INTERNAL PROC [tIp: ArpaBuffer.Body] RETURNS [b: ArpaBuffer.Buffer] = BEGIN --searches list of reassembly buffers for the one containing this fragment. FOR b ← ArpaBuffer.From[fragQueue.first], ArpaBuffer.From[b.fo.next] UNTIL b = NIL DO bIp: ArpaBuffer.Body = b.arpa; SELECT TRUE FROM (bIp.ipHeader.source # tIp.ipHeader.source) => NULL; (bIp.ipHeader.destination # tIp.ipHeader.destination) => NULL; (bIp.ipHeader.identification # tIp.ipHeader.identification) => NULL; (bIp.ipHeader.protocol # tIp.ipHeader.protocol) => NULL; ENDCASE => EXIT; --got it! ENDLOOP; END; --FindReassembly FragWatcher: ENTRY PROC = BEGIN elapsedSeconds: LONG CARDINAL; b, newB: ArpaBuffer.Buffer ← NIL; Process.SetPriority[Process.priorityNormal]; DO --until aborted ENABLE ABORTED => EXIT; WAIT fragTimer; IF fragQueue.length > 0 THEN FOR b ← ArpaBuffer.From[fragQueue.first], newB UNTIL newB = NIL DO newB ← ArpaBuffer.From[b.fo.next]; --in case we delete b << b.fo.time is in Pulses. b.requeueData is in seconds. uSecs * 1D6 is a real small time! This is going to be messy. >> [elapsedSeconds, ] ← Inline.UDDivMod[ (System.PulsesToMicroseconds[ [System.GetClockPulses[] - b.fo.time]]), 1D6]; IF elapsedSeconds > b.requeueData THEN BEGIN b ← ArpaBuffer.ExtractFromQueue[@fragQueue, b]; ArpaBuffer.ReturnBuffer[b]; --**send ICMP time exceeded. END; ENDLOOP; ENDLOOP; END; --FragWatcher Ripple: INTERNAL PROC [b: ArpaBuffer.Buffer, offset: CARDINAL] RETURNS [success: BOOLEAN ← TRUE] = BEGIN << ripples the IP data in the buffer by offset to make room for a "larger than anticipated" header when reassembling. The hole descriptors also have to be adjusted. The actual entries will be moved along with the data. This code is bogus! There's something wrong with the computation of 'length'. It can't be a long pointer plus a SIZE that gives a length. I suspect that the offset has to be subtracted from the beginning of some buffer, and I suspect that it's 'b'. The following is the original code just commented out. Also, note the comparison of the length times something that's obvoiously bytes to "DataWordsPerRawBuffer[]". e, next: ListEntry; length: LONG CARDINAL; FOR e ← LOOPHOLE[b.driver.iocb], next UNTIL next = NIL DO next ← e.next; --save link, since we are going add the offset to it. e.first ← e.first + offset; e.last ← e.last + offset; e.next ← e.next + offset; ENDLOOP; --e points to last hole descriptor, which is last piece of valuable info. length ← LOOPHOLE[e + SIZE[ListObject]]; length ← LOOPHOLE[(@b.bufferBody + length * 2) - @b.bufferBody]; IF length + SIZE[DriverTypes.Encapsulation] * 2 > Buffer.DataWordsPerRawBuffer[] THEN success ← FALSE; [] ← ByteBlt.ByteBlt[ to: [LOOPHOLE[@b.bufferBody + offset], 0, Inline.LowHalf[length]], from: [LOOPHOLE[@b.bufferBody], 0, Inline.LowHalf[length]]]; >> END; --Ripple AddFrag: INTERNAL PROC [frag, b: ArpaBuffer.Buffer] RETURNS [completed: ArpaBuffer.Buffer ← NIL] = BEGIN OPEN ip: frag.arpa.ipHeader; << Pieces the fragment into the specified reassembly buffer. If this fragment completes the reassembly, the reassembly buffer will be returned; if it is not completed, NIL will be returned. The hole descripter resides in the hole itself, which we know to be at least 8 bytes, and the descriptors are linked, with the head being in the iocb field of the fixed overhead. This field is not used at this point, and we need someplace to put a long pointer. Slimy, but it works! >> offset: CARDINAL ← 0; --difference in header from default size. hdrLen, dataLen: CARDINAL; first, last: CARDINAL ← 0; --fragment offsets. listHead: ListEntry ← LOOPHOLE[b.fo.driver.iocb]; hole, prev: ListEntry; use: BOOLEAN; AddHole: PROC [f, l: CARDINAL] = BEGIN p, e, previous: ListEntry; --this is where the hole descriptor will be stored. p ← LOOPHOLE[b.arpa + (first + 1)/2 + (hdrLen + 1)/2]; previous ← NIL; FOR e ← listHead, e.next UNTIL e = NIL DO IF (f + hdrLen < e.first) THEN EXIT; previous ← e; ENDLOOP; IF previous = NIL THEN listHead ← p ELSE previous.next ← p; p↑ ← [e, f + hdrLen, l + hdrLen]; END; --AddHole << Either we know the header length, or we will guess at the default. Ripple will take care of us if we guess wrong. If this is the first fragment, hdrLen will be updated by the code that copies the header. >> hdrLen ← IF listHead.first = 0 THEN ArpaPort.minIPHeaderBytes ELSE b.arpa.ipHeader.ihl * 4; dataLen ← ip.length - ip.ihl * 4; first ← ip.fragment * 8; last ← first + ip.length - (ip.ihl * 4) - 1; SELECT TRUE FROM << Can only reassemble to size of our buffers. Again, Ripple will take care of the case where we guessed wrong at the header size. >> last > (ArpaBuffer.DataBytesPerRawBuffer[frag] - hdrLen - (SIZE[ethernet IEEE8023.EncapObject] * bpw)) => {IF ArpaFlags.doStats THEN ArpaStats.Incr[reassemblyTooBig]; RETURN}; (ip.fragment = 0) => --first fragment processing (need to save header). SELECT TRUE FROM --already have header, let's ignore header in this fragment. (listHead.first > 0) => NULL; --first to arrive, plenty of hdr room. (listHead.last = LAST[CARDINAL]), ip.ihl = 5 => --reassembly buffer not empty, but header fits. BEGIN hdrLen ← ip.ihl * 4; IF hdrLen > arpaProtocolFamily.maxBufferSize THEN --sanity check. {IF ArpaFlags.doStats THEN ArpaStats.Incr[reassemblyTooBig]; RETURN}; [] ← ByteBlt.ByteBlt[to: [LOOPHOLE[b.arpa], 0, hdrLen], from: [LOOPHOLE[frag.arpa], 0, hdrLen]]; listHead.first ← hdrLen; --update the hole descriptor END; ENDCASE => --oops, header was greater than the size we allowed for. BEGIN hdrLen ← ip.ihl * 4; IF hdrLen > arpaProtocolFamily.maxBufferSize THEN --sanity check. {IF ArpaFlags.doStats THEN ArpaStats.Incr[reassemblyTooBig]; RETURN}; offset ← hdrLen - ArpaPort.minIPHeaderBytes; IF ~Ripple[b, offset] THEN {IF ArpaFlags.doStats THEN ArpaStats.Incr[reassemblyTooBig]; ArpaBuffer.ReturnBuffer[frag]}; [] ← ByteBlt.ByteBlt[to: [LOOPHOLE[b.arpa], 0, hdrLen], from: [LOOPHOLE[frag.arpa], 0, hdrLen]]; listHead.first ← hdrLen; --update the hole descriptor END; ENDCASE; IF ((last + 1) MOD 8) # 0 AND ip.flags.moreFragment THEN --next frag won't be 32 aligned, but if last who cares. {IF ArpaFlags.doStats THEN ArpaStats.Incr[fragAlign]; RETURN}; use ← FALSE; --is this fragment usable at all (overlaps a hole?). --find out where this fragment goes. prev ← NIL; FOR hole ← listHead, hole.next UNTIL hole = NIL DO IF (first + hdrLen > hole.last) THEN {prev ← hole; LOOP}; IF (last + hdrLen < hole.first) THEN {prev ← hole; LOOP}; use ← TRUE; --remove current entry from list. IF prev = NIL THEN listHead ← hole.next --first entry ELSE prev.next ← hole.next; --new hole descriptors? IF first + hdrLen > hole.first THEN AddHole[hole.first, ip.fragment * 8 - 1]; IF (last + hdrLen < hole.last) AND (ip.flags.moreFragment) THEN AddHole[last + 1 , hole.last]; ENDLOOP; IF use THEN BEGIN [] ← ByteBlt.ByteBlt[ to: [LOOPHOLE[ b.arpa + (hdrLen + first)/2], 0, ip.length - ip.ihl * 4], from: [LOOPHOLE[ @frag.arpa.ipBytes], 0, ip.length - ip.ihl * 4]]; << length will not be correct (i.e., will not include header), until just before we return the assembled buffer to the client! >> IF ~ip.flags.moreFragment THEN b.arpa.ipHeader.length ← last + 1; IF listHead = NIL THEN BEGIN --add in the header length. b.arpa.ipHeader.length ← b.arpa.ipHeader.length + b.arpa.ipHeader.ihl * 4; RETURN[ArpaBuffer.ExtractFromQueue[@fragQueue, b]]; END; END; --use b.fo.driver.iocb ← LOOPHOLE[listHead]; --restore END; --AddFrag Register: PUBLIC PROC[h: ArpaRoutingTable.Handle ← NIL] = BEGIN ExchangeObjects: ENTRY PROC = BEGIN IF h # NIL THEN rto ← h↑ --copy in new object ELSE rto ← ArpaRoutingTable.defaultRth↑; --default to old routersFunction ← rto.type; END; --ExchangeObjects arpaProtocolFamily.status ← dead; --slow things up a while Process.Pause[Process.SecondsToTicks[1]]; --wait for traffic to clear IF rto.stop # NIL THEN rto.stop[]; --stop current ExchangeObjects[]; --reregister IF rto.start # NIL THEN rto.start[]; --start new arpaProtocolFamily.status ← alive; --and let the good times role END; --Register SendPacket: PUBLIC PROC[ b: ArpaBuffer.Buffer, destType: ArpaPortInternal.DestType] = BEGIN body: ArpaBuffer.Body = b.arpa; body.ipHeader.version ← vers4; body.ipHeader.fragment ← 0; body.ipHeader.flags.moreFragment ← FALSE; SELECT TRUE FROM --broadcasts and local w/loopback included here driverLoopback, (destType = remote) => BEGIN body.ipHeader.checksum ← 0; body.ipHeader.checksum ← ArpaPortInternal.Checksum[ 0, body.ipHeader.ihl*2, @body.ipHeader]; IF ArpaFlags.doStats THEN ArpaStats.Incr[ipSent]; [] ← rto.transmit[b]; END; --sending to myself in non-loopback mode (~ArpaPortInternal.QueueForClient[b: b, copy: TRUE]) => {b.fo.network ← Driver.nilDevice; --**Send some icmp packet here? IF ArpaFlags.doStats THEN ArpaStats.Incr[noLocalPort]; Process.Yield[]}; ENDCASE => --we copied it for the receiver, now requeue it for the sender. {Driver.PutOnGlobalDoneQueue[ArpaBuffer.To[b]]; Process.Yield[]}; END; --SendPacket SetIPLengths: PUBLIC PROC[ body: ArpaBuffer.Body, optionsLen, dataLen: CARDINAL] = BEGIN body.ipHeader.ihl ← (optionsLen + ArpaPort.minIPHeaderBytes+3)/4; body.ipHeader.length ← body.ipHeader.ihl*4 + dataLen; END; --SetIPLengths SetOptions: PUBLIC PROC = BEGIN END; --SetOptions StateChanged: PROC[ driver: Device, context: LONG POINTER, why: Protocol1.Action] = BEGIN SELECT why FROM add => IF rto.addNetwork # NIL THEN rto.addNetwork[context]; remove => rto.removeNetwork[context]; --tell routing table to stop modify => rto.stateChanged[context]; --see if routing table cares ENDCASE; END; --StateChanged END.... --ArpaRouterImpl module. LOG 25-Jun-84 13:33:44 AOF Created file. 8-Feb-85 15:51:44 SMA Development. 13-Jul-85 13:52:31 SMA Added stats. 18-Oct-85 12:15:36 SMA Move queueing code to ArpaPortImpl - now has only IP. 8-Jan-86 10:40:03 SMA ICMP support. 10-Mar-86 13:24:25 SMA ICMP checksums. 21-Jun-86 13:13:07 SMA IP reassembly. 24-Sep-86 16:42:50 JAV Fixed IP reassembly. 9-Mar-87 20:30:15 AOF Funston buffer management 24-Mar-87 18:39:06 AOF Just tweaking 15-Jun-87 11:14:13 JAV Added SetGatewayAddr & SetDomainNameServer