-- File: Arpa10MBitImpl.mesa - last edit: -- AOF 25-Mar-87 11:28:09 -- JAV 20-Nov-86 15:26:40 -- SMA 5-Jun-86 11:02:23 -- Copyright (C) 1985, 1986, 1987 by Xerox Corporation. All rights reserved. DIRECTORY Arpa10MBit USING [], ArpaBuffer USING [Body], ArpaFlags USING [doDebug, doStats], ArpaPortInternal USING [ AddrMatch, BuildMasks, GetArpaAddr, GetMyBroadcastAddr], ArpaRouter USING [InternetAddress, unknownInternetAddress], ArpaRoutingTable USING [ContextObject, NetworkContext], ArpaStats USING [Incr], ArpaTypes USING [InternetAddress], Buffer USING [ AccessHandle, Buffer, DestroyPool, Device, GetBuffer, MakePool, Type], CommHeap USING [zone], CommPriorities USING [driver], Driver USING [Device], Environment USING [bytesPerWord], EtherMAC USING [EncapObject, Encapsulation, EthernetPacketType], HostNumbers USING [HostNumber, IsMulticastID, ProcessorID], Process USING [ Abort, DisableTimeout, EnableAborts, SecondsToTicks, SetPriority, SetTimeout], Protocol1 USING [DecapsulatorProc, EncapsulatorProc], SpecialSystem USING [ broadcastHostNumber, GetProcessorID, HostNumber, nullHostNumber, ProcessorID], System USING [GetClockPulses, MicrosecondsToPulses, Pulses]; Arpa10MBitImpl: MONITOR IMPORTS ArpaPortInternal, ArpaRouter, ArpaStats, HostNumbers, Process, Buffer, CommHeap, SpecialSystem, System EXPORTS Buffer, Arpa10MBit, ArpaRouter = BEGIN Device: PUBLIC <<Buffer>> TYPE = Driver.Device; InternetAddress: PUBLIC <<ArpaRouter>> TYPE = ArpaTypes.InternetAddress; bpw: NATURAL = Environment.bytesPerWord; bytesInArp: NATURAL = SIZE[ArpBody] * bpw; wordsOfEncapsulation: NATURAL = SIZE[EtherMAC.EncapObject]; bytesOfEncapsulation: NATURAL = SIZE[EtherMAC.EncapObject] * bpw; InternetAddressPointer: TYPE = LONG POINTER TO ArpaTypes.InternetAddress; pool: Buffer.AccessHandle; --created in $Create broadcast: InternetAddress; --assigned in $Create --ARP packet format. ArpOp: TYPE = MACHINE DEPENDENT {request(1), reply(2)}; ArpBody: TYPE = MACHINE DEPENDENT RECORD [ hardware(0): CARDINAL, --hardware address space protocol(1): EtherMAC.EthernetPacketType, --protocol address space hrdAddrLen(2: 0..7): CARDINAL[0..256), --length of hardware address in bytes prtAddrLen(2: 8..15): CARDINAL[0..256), --length of protocol address in bytes op(3): ArpOp, --operation senderEtherAddr(4): HostNumbers.HostNumber, --sender's 48-bit ether address senderIpAddr(7): InternetAddress, --sender's protocol (IP) address targetEtherAddr(9): SpecialSystem.HostNumber, --target's unknown 48-bit addr targetIpAddr(12): InternetAddress]; --target's protocol (IP) address TranslaterHandle: PUBLIC TYPE = LONG POINTER TO TranslaterObject; TranslaterObject: PUBLIC TYPE = RECORD[ me, all, head: TranslateEntry ← NIL, hmsk: InternetAddress, --based on me. event: CONDITION, demon: PROCESS ← NIL]; TranslatePair: TYPE = LONG POINTER TO TranslateRecord; TranslateRecord: TYPE = MACHINE DEPENDENT RECORD [ etherHost: SpecialSystem.HostNumber, arpaHost: InternetAddress]; TranslateEntry: TYPE = LONG POINTER TO TranslateObject; TranslateObject: TYPE = RECORD [ nextLink: TranslateEntry, translatePair: TranslateRecord, tries: CARDINAL, timeStamp: System.Pulses, status: TranslateStatus]; TranslateStatus: TYPE = { new, --new and unresolved, no request sent. pending, --unresolved, request sent, reply pending. active, --resolved zombie}; --pending entry that we were unable to resolve. ipAddrLen: CARDINAL = SIZE[InternetAddress] * bpw; etherAddrLen: CARDINAL = SIZE[SpecialSystem.HostNumber] * bpw; etherHardware: CARDINAL = 1; --cuz ARP spec says so. retryLimit: CARDINAL = 8; --gotta stop somewhere retryPulses: System.Pulses = System.MicrosecondsToPulses[2D6]; deactivatePulses: System.Pulses = System.MicrosecondsToPulses[18D7]; myEtherHost: SpecialSystem.ProcessorID = SpecialSystem.GetProcessorID[]; myArpaAddr: InternetAddress ← ArpaRouter.unknownInternetAddress; Capsulators: PUBLIC <<Arpa10MBit>>PROC RETURNS[ decap: Protocol1.DecapsulatorProc, encap: Protocol1.EncapsulatorProc] = {RETURN[DecapsulateArpaEthernet, EncapsulateArpaEthernet]}; CreateCapsulators: PUBLIC <<Arpa10MBit>> PROC[driver: Device] RETURNS[xlate: TranslaterHandle] = BEGIN xlate ← CommHeap.zone.NEW[TranslaterObject ← [ hmsk: ArpaPortInternal.BuildMasks[myArpaAddr].hostMask]]; Process.EnableAborts[@xlate.event]; broadcast ← ArpaPortInternal.GetMyBroadcastAddr[]; xlate.all ← AddTranslateRecord[ xlate, [SpecialSystem.broadcastHostNumber, broadcast]]; xlate.me ← AddTranslateRecord[xlate, [myEtherHost, myArpaAddr]]; xlate.demon ← FORK Demon[driver, xlate]; pool ← Buffer.MakePool[1, 1]; --needs a pool to move END; --Create DestroyCapsulators: PUBLIC <<Arpa10MBit>> PROC[xlate: TranslaterHandle] = BEGIN IF xlate.demon # NIL THEN {Process.Abort[xlate.demon]; JOIN xlate.demon}; --cleanup in case demon was never running UNTIL xlate.head = NIL DO e: TranslateEntry ← xlate.head; xlate.head ← e.nextLink; CommHeap.zone.FREE[@e]; ENDLOOP; Buffer.DestroyPool[pool]; pool ← NIL; END; --Delete EncapsulateArpaEthernet: PUBLIC <<Arpa10MBit>> Protocol1.EncapsulatorProc = --EncapsulatorProc: TYPE = PROC[b: Buffer.Buffer, immediate: LONG POINTER]; BEGIN context: ArpaRoutingTable.NetworkContext = NARROW[b.fo.context]; destination: InternetAddressPointer = NARROW[immediate]; protocol: TranslaterHandle = NARROW[context.protocol]; mask: InternetAddress = protocol.hmsk; body: ArpaBuffer.Body = LOOPHOLE[b.highLayer.blockPointer]; e: EtherMAC.Encapsulation = LOOPHOLE[body - wordsOfEncapsulation]; etherHost: SpecialSystem.HostNumber ← IF ArpaPortInternal.AddrMatch[ mask, broadcast, body.ipHeader.destination] THEN SpecialSystem.broadcastHostNumber ELSE Translate[context.protocol, destination↑]; e↑ ← [etherHost, myEtherHost, arpa]; b.linkLayer ← [LOOPHOLE[e], 0, bytesOfEncapsulation]; b.fo.driver.length ← body.ipHeader.length + bytesOfEncapsulation; END; --EncapsulateArpaEthernet DecapsulateArpaEthernet: PUBLIC <<Arpa10MBit>> Protocol1.DecapsulatorProc = --DecapsulatorProc: TYPE = PROC[b: Buffer.Buffer] RETURNS[Buffer.Type]; BEGIN e: EtherMAC.Encapsulation = LOOPHOLE[b.linkLayer.blockPointer]; body: ArpaBuffer.Body = LOOPHOLE[e + wordsOfEncapsulation]; IF b.fo.driver.length < bytesOfEncapsulation THEN RETURN[orphan]; IF HostNumbers.IsMulticastID[LOOPHOLE[@e.ethernetSource]] THEN RETURN[orphan]; --that's illegal from the source SELECT e.ethernetType FROM arp => {ReceiveTranslate[b]; RETURN[orphan]}; --one of those arpa => BEGIN bytes: CARDINAL = b.fo.driver.length - bytesOfEncapsulation; IF ArpaFlags.doStats THEN ArpaStats.Incr[rawArpaRcvd]; b.linkLayer.stopIndexPlusOne ← wordsOfEncapsulation; b.highLayer ← [LOOPHOLE[body], 0, body.ipHeader.length]; RETURN[IF bytes < body.ipHeader.length THEN orphan ELSE arpa]; END; ENDCASE => RETURN[vagrant]; --still unknown END; --DecapsulateArpaEthernet Translate: ENTRY PROC[ xlate: TranslaterHandle, arpaHost: ArpaTypes.InternetAddress] RETURNS [etherHost: SpecialSystem.HostNumber] = BEGIN e: TranslateEntry; SELECT TRUE FROM ((e ← FindTranslate[xlate, arpaHost]) = NIL) => BEGIN e ← CommHeap.zone.NEW[TranslateObject]; e.translatePair.arpaHost ← arpaHost; e.nextLink ← xlate.head; xlate.head ← e; NOTIFY xlate.event; e.status ← new; e.tries ← 0; etherHost ← SpecialSystem.nullHostNumber; e.timeStamp ← System.GetClockPulses[]; END; (e.status = active) => BEGIN IF e # xlate.head THEN BEGIN --sort this element to head of linked list RemoveTranslate[xlate, e]; e.nextLink ← xlate.head; xlate.head ← e; END; etherHost ← e.translatePair.etherHost; e.timeStamp ← System.GetClockPulses[]; END; (e.status = zombie) => BEGIN IF e # xlate.head THEN BEGIN --sort this element to head of linked list RemoveTranslate[xlate, e]; e.nextLink ← xlate.head; xlate.head ← e; END; NOTIFY xlate.event; e.status ← new; e.tries ← 0; etherHost ← SpecialSystem.nullHostNumber; e.timeStamp ← System.GetClockPulses[]; END; ENDCASE => etherHost ← SpecialSystem.nullHostNumber; END; --Translate --interface FindTranslate: PROC[ xlate: TranslaterHandle, arpaHost: ArpaTypes.InternetAddress] RETURNS [entry: TranslateEntry] = BEGIN mask: InternetAddress = ArpaPortInternal.BuildMasks[arpaHost].hostMask; FOR entry ← xlate.head, entry.nextLink WHILE entry # NIL DO IF ArpaPortInternal.AddrMatch[ mask, arpaHost, entry.translatePair.arpaHost] THEN RETURN; ENDLOOP; END; AddTranslate: INTERNAL PROC[xlate: TranslaterHandle, entry: TranslateEntry] = {entry.nextLink ← xlate.head; xlate.head ← entry}; --AddTranslate RemoveTranslate: INTERNAL PROC[xlate: TranslaterHandle, entry: TranslateEntry] = BEGIN e, pred: TranslateEntry; IF (pred ← xlate.head) = entry THEN {xlate.head ← xlate.head.nextLink; RETURN}; FOR e ← pred.nextLink, e.nextLink UNTIL e = NIL DO IF e # entry THEN pred ← e ELSE {pred.nextLink ← entry.nextLink; EXIT}; ENDLOOP; END; AddTranslateRecord: ENTRY PROC[xlate: TranslaterHandle, pair: TranslateRecord] RETURNS [e: TranslateEntry] = BEGIN SELECT (e ← FindTranslate[xlate, pair.arpaHost]) FROM NIL => BEGIN e ← CommHeap.zone.NEW[TranslateObject]; e.nextLink ← xlate.head; xlate.head ← e; END; xlate.all, xlate.me => RETURN; ENDCASE; e.translatePair ← pair; e.status ← active; e.timeStamp ← System.GetClockPulses[]; END; Demon: ENTRY PROC[network: Device, xlate: TranslaterHandle] = BEGIN SendRequest: INTERNAL PROC = BEGIN b: Buffer.Buffer ← Buffer.GetBuffer[, pool, send, FALSE, bytesInArp]; IF b # NIL THEN BEGIN ec: EtherMAC.Encapsulation ← LOOPHOLE[b.linkLayer.blockPointer]; body: LONG POINTER TO ArpBody ← LOOPHOLE[ec + wordsOfEncapsulation]; ec ↑ ← [SpecialSystem.broadcastHostNumber, myEtherHost, arp]; b.fo.driver.length ← bytesOfEncapsulation + bytesInArp; b.linkLayer.stopIndexPlusOne ← bytesOfEncapsulation; body↑ ← [ hardware: etherHardware, protocol: arpa, hrdAddrLen: etherAddrLen, prtAddrLen: ipAddrLen, op: request, senderEtherAddr: xlate.me.translatePair.etherHost, senderIpAddr: xlate.me.translatePair.arpaHost, targetEtherAddr: , targetIpAddr: e.translatePair.arpaHost]; network.sendRawBuffer[b]; IF ArpaFlags.doStats THEN ArpaStats.Incr[xlateReqSent]; END; END; --SendRequest age: System.Pulses; e, nextE: TranslateEntry; pendingEntries: BOOLEAN; Process.SetPriority[CommPriorities.driver]; << It would appear that the outer loop is setting the timeout on the condition variable way too often. It really only needs to do that if the previous state of pendingEntries was FALSE and now it's true. I suppose we could move the SetTimeout into the branches of the SELECT arms with some code of the form IF ~pendingEntries THEN {SetTimeout[...]; pendingEntries ← TRUE}; but then we need some more state. And it only happens once a second. >> --UNTIL ABORTED-- DO ENABLE ABORTED => EXIT; --we're shutting down WAIT xlate.event; pendingEntries ← FALSE; FOR e ← xlate.head, nextE UNTIL e = NIL DO nextE ← e.nextLink; --copy out in case we delete entry age ← [System.GetClockPulses[] - e.timeStamp]; SELECT e.status FROM active, zombie => SELECT TRUE FROM (age <= deactivatePulses) => NULL; (e = xlate.me) => e.timeStamp ← System.GetClockPulses[]; (e = xlate.all) => e.timeStamp ← System.GetClockPulses[]; ENDCASE => {RemoveTranslate[xlate, e]; CommHeap.zone.FREE[@e]}; pending => BEGIN SELECT TRUE FROM (age < retryPulses) => NULL; ((e.tries ← e.tries + 1) > retryLimit) => e.status ← zombie; ENDCASE => {SendRequest[]; e.timeStamp ← System.GetClockPulses[]}; pendingEntries ← TRUE; END; new => BEGIN e.status ← pending; SendRequest[]; e.timeStamp ← System.GetClockPulses[]; pendingEntries ← TRUE; END; ENDCASE => IF ArpaFlags.doDebug THEN ERROR; ENDLOOP; --end of queue entries loop IF ~pendingEntries THEN Process.DisableTimeout[@xlate.event] ELSE Process.SetTimeout[@xlate.event, Process.SecondsToTicks[1]]; ENDLOOP; FOR e ← xlate.head, nextE UNTIL e = NIL DO nextE ← e.nextLink; CommHeap.zone.FREE[@e]; e ← nextE; ENDLOOP; xlate.head ← xlate.me ← xlate.all ← NIL; END; --Demon ReceiveTranslate: PROC[b: Buffer.Buffer] = BEGIN OPEN context: LOOPHOLE[b.fo.context, ArpaRoutingTable.NetworkContext]; itsMe: BOOLEAN; --to suppress second call to AddrMatch xlate: TranslaterHandle = context.protocol; eb: EtherMAC.Encapsulation ← LOOPHOLE[b.linkLayer.blockPointer]; inPkt: LONG POINTER TO ArpBody ← LOOPHOLE[eb + wordsOfEncapsulation]; IF FindTranslate[xlate, inPkt.senderIpAddr] # NIL OR (itsMe ← ArpaPortInternal.AddrMatch[xlate.hmsk, inPkt.targetIpAddr, xlate.me.translatePair.arpaHost]) THEN BEGIN << The requester is probably going to talk to us or why else would he be asking for our translation? So, since we have all the information, add his translation to our table now. Then, when we have to talk back to him, we won't fault. >> [] ← AddTranslateRecord[ xlate, [inPkt.senderEtherAddr, inPkt.senderIpAddr]]; IF ArpaFlags.doStats AND (inPkt.op = reply) THEN ArpaStats.Incr[xlateRespRcvd]; IF itsMe AND (inPkt.op = request) THEN BEGIN --send the reply. a: Buffer.Buffer; IF ArpaFlags.doStats THEN ArpaStats.Incr[xlateReqRcvd]; --can't turn original around because dispatcher wants it back. IF (a ← Buffer.GetBuffer[, pool, send, FALSE, bytesInArp]) # NIL THEN BEGIN ea: EtherMAC.Encapsulation ← LOOPHOLE[a.linkLayer.blockPointer]; body: LONG POINTER TO ArpBody ← LOOPHOLE[ea + wordsOfEncapsulation]; ea↑ ← [eb.ethernetSource, myEtherHost, arp]; a.highLayer ← [LOOPHOLE[body], 0, SIZE[ArpBody] * bpw]; a.fo.driver.length ← bytesOfEncapsulation + bytesInArp; body↑ ← [etherHardware, arpa, etherAddrLen, ipAddrLen, reply, xlate.me.translatePair.etherHost, xlate.me.translatePair.arpaHost, inPkt.senderEtherAddr, inPkt.senderIpAddr]; a.linkLayer.stopIndexPlusOne ← bytesOfEncapsulation; NARROW[b.fo.network, Device].sendRawBuffer[a]; IF ArpaFlags.doStats THEN ArpaStats.Incr[xlateRespSent]; END; --send the reply. END; END; --update or target. END; --ReceiveTranslate --called at start time. myArpaAddr ← ArpaPortInternal.GetArpaAddr[]; END... LOG 8-Feb-85 15:51:44 SMA Created file. 2-May-85 13:24:00 SMA Added cheating code to assign arpa host when started. 13-Jul-85 13:25:20 SMA Added stats. 22-Aug-85 10:34:40 SMA Added GetAddress for ArpaRouter. 14-Nov-85 8:55:15 SMA Speaks ARP. 4-Dec-85 10:44:36 SMA Get my address from ArpaAddressTranslation. 2-Jun-86 18:22:27 SMA Moved GetArpaAddr to ArpaRouterImpl for subnetting. 9-Mar-87 14:33:09 AOF Funston buffer managment 24-Mar-87 16:12:06 AOF Just some fine tuning.