-- File: ArpaRoutingTableImpl.mesa - last edit: -- AOF 1-Mar-88 9:54:38 -- JAV 7-Dec-87 16:54:06 -- SMA 11-Jun-86 16:24:41 -- Copyright (C) 1985, 1986, 1987, 1988 by Xerox Corporation. All rights reserved. DIRECTORY ArpaBuffer USING [ Body, Buffer, BufferBody, DestroyPool, GetBuffer, MakePool, AccessHandle, To, QueueCleanup, QueueInitialize, QueueObject, ReturnBuffer, TransferStatus], ArpaFlags USING [doStats], ArpaPortInternal USING [AddrMatch, AddrMismatch, BuildMasks, Checksum, GetGatewayAddr, GetMyBroadcastAddr, GetMyNullAddr, GetSubnetMask, SendPacket], ArpaPort USING [Handle, minIPHeaderBytes], ArpaRouter USING [GetAddress, unknownInternetAddress], ArpaRoutingTable USING [AddNetworkProc, AddRouteProc, EnumerateProc, FillTableProc, FindNetworkProc, FlushCacheProc, GetDelayProc, NetworkContext, RemoveRouteProc, RemoveNetworkProc, RoutersFunction, StateChangedProc, Handle, Object], ArpaStats USING [Incr], ArpaSysParameters USING [GetTypeOfService], ArpaTypes USING [BufferBody, Byte, Icmp, IcmpType, InternetAddress, Port], Buffer USING [], ByteBlt USING [ByteBlt], CommHeap USING [zone], Driver USING [GetDeviceChain, Device, PutOnGlobalDoneQueue], Environment USING [Byte, bytesPerWord], Inline USING [DBITAND], Process USING [MsecToTicks, Pause], Protocol1 USING [EncapsulateAndTransmit, GetContext], System USING [GetClockPulses]; ArpaRoutingTableImpl: MONITOR IMPORTS ArpaBuffer, ArpaPortInternal, ArpaRouter, ArpaStats, ArpaSysParameters, ByteBlt, CommHeap, Driver, Inline, Process, Protocol1, System EXPORTS ArpaRouter, ArpaRoutingTable, Buffer = BEGIN << This is the implementation module for local routing. Subnet local routing will probably live here also. Beware - lots of work needs to be done for this to run on multiple interfaces. >> Device: PUBLIC --Buffer-- TYPE = Driver.Device; Port: PUBLIC --ArpaPort-- TYPE = ArpaTypes.Port; InternetAddress: PUBLIC --ArpaRouter-- TYPE = ArpaTypes.InternetAddress; RTEntry: TYPE = LONG POINTER TO RTEObject; RTEObject: TYPE = RECORD [ next: RTEntry, -- pointer to next entry in "table". dest: InternetAddress, -- ultimate destination address. mask: InternetAddress, -- for comparisons. delay: CARDINAL, -- measured in hops to the dest. lastAccess: LONG CARDINAL, -- when this entry was last used. route: InternetAddress, -- host num of gateway to next net. context: ArpaRoutingTable.NetworkContext, -- my protocol specific addresses. changed: BOOLEAN ¬ FALSE]; -- set when value is changed or added pleaseStop: BOOLEAN; routingTableSize: CARDINAL ¬ 0; routingTableHead: RTEntry ¬ NIL; routerPool: ArpaPort.Handle ¬ NIL; requestPool: ArpaBuffer.AccessHandle; bpw: NATURAL = Environment.bytesPerWord; outQueue: ArpaBuffer.QueueObject; --send buffers needing routing information. startEnum: InternetAddress = ArpaRouter.unknownInternetAddress; endEnum: InternetAddress = [LAST[CARDINAL], LAST[CARDINAL]]; --Information about the primary network interface. myDevice: Device ¬ NIL; myNullAddr: InternetAddress; myGatewayAddr: InternetAddress; myBroadcastAddr: InternetAddress; myContext: ArpaRoutingTable.NetworkContext ¬ NIL; defaultRth: PUBLIC ArpaRoutingTable.Handle ¬ @rto; rto: ArpaRoutingTable.Object ¬ [ type: normal, start: Start, stop: Stop, startEnumeration: startEnum, endEnumeration: endEnum, enumerate: Enumerate, fillTable: Fill, getDelay: GetDelay, transmit: Transmit, forward: Forward, findNetwork: FindLocalNetID, addNetwork: AddNet, removeNetwork: RemoveNet, flushCache: FlushCache, stateChanged: ChangedState, addRoute: AddRoute, removeRoute: RemoveRoute]; AddEntry: INTERNAL PROC [e: RTEntry] = BEGIN entry, previous: RTEntry ¬ NIL; Greater: PROC [a, b: InternetAddress] RETURNS [BOOLEAN] = BEGIN NetWithCardinal: TYPE = MACHINE DEPENDENT RECORD [a, b: CARDINAL]; aWithCardinal: NetWithCardinal ¬ LOOPHOLE[a]; bWithCardinal: NetWithCardinal ¬ LOOPHOLE[b]; --RETURNS[a > b]; RETURN[ (aWithCardinal.a > bWithCardinal.a) OR ((aWithCardinal.a = bWithCardinal.a) AND (aWithCardinal.b > bWithCardinal.b))]; END; --Greater FOR entry ¬ routingTableHead, entry.next UNTIL entry = NIL DO IF ~Greater[e.dest, entry.dest] THEN { e.next ¬ entry; previous.next ¬ e; EXIT}; previous ¬ entry; ENDLOOP; IF entry = NIL THEN IF routingTableHead = NIL THEN routingTableHead ¬ e ELSE previous.next ¬ e; routingTableSize ¬ routingTableSize + 1; END; -- of AddEntry AddNet: PUBLIC PROCEDURE [context: ArpaRoutingTable.NetworkContext] = BEGIN --Add state for an attached net. IF context.net = ArpaRouter.unknownInternetAddress THEN NULL ELSE AddDriverInternal[context]; END; --AddNet AddDriverInternal: PROC [context: ArpaRoutingTable.NetworkContext] = BEGIN << Tells the routing table about a new attached network. The driver must know the network number at this point. Called by ExamineResponse and Locked, both of which hold the monitor. >> entry: RTEntry; SELECT TRUE FROM (~context.network.alive) => RETURN; ((entry ¬ FindNetworkNumber[context.net]) # NIL) => RETURN; ENDCASE; AddRoute[context.net, IF ArpaPortInternal.GetSubnetMask[] # ArpaRouter.unknownInternetAddress THEN ArpaPortInternal.GetSubnetMask[] ELSE context.netMask, ArpaRouter.unknownInternetAddress, 0, context]; << The route should be the unknownInternetAddress since all routes are to directly connected devices. >> END; --AddDriverInternal AddRoute: ENTRY ArpaRoutingTable.AddRouteProc = BEGIN << It is the user's responsibility to choose his host and subnet number carefully enough that there is no confusion between subnetted and non-subnetted hosts! >> e: RTEntry ¬ NIL; IF (e ¬ FindNetworkNumber[dest]) = NIL THEN BEGIN e ¬ CommHeap.zone.NEW[RTEObject ¬ [ next: NIL, dest: dest, mask: mask, delay: delay, lastAccess: System.GetClockPulses[], route: route, context: context, changed: TRUE]]; AddEntry[e]; END ELSE BEGIN e.mask ¬ mask; IF e.delay >= delay THEN BEGIN e.delay ¬ delay; e.route ¬ route; e.context ¬ context; e.changed ¬ TRUE; END; END; END; --AddRoute CleanUpRoutingTable: PROC = BEGIN temp: RTEntry; WHILE routingTableHead # NIL DO temp ¬ routingTableHead; routingTableHead ¬ routingTableHead.next; routingTableSize ¬ routingTableSize - 1; CommHeap.zone.FREE[@temp]; ENDLOOP; END; --CleanUpRoutingTable Enumerate: ENTRY ArpaRoutingTable.EnumerateProc = BEGIN --This procedure will return any but the null entry. Greater: PROC [a, b: InternetAddress] RETURNS [BOOLEAN] = BEGIN NetWithCardinal: TYPE = MACHINE DEPENDENT RECORD [a, b: CARDINAL]; aWithCardinal: NetWithCardinal ¬ LOOPHOLE[a]; bWithCardinal: NetWithCardinal ¬ LOOPHOLE[b]; --RETURNS[a > b]; RETURN[ (aWithCardinal.a > bWithCardinal.a) OR ((aWithCardinal.a = bWithCardinal.a) AND (aWithCardinal.b > bWithCardinal.b))]; END; --Greater e: RTEntry; next: InternetAddress ¬ endEnum; route ¬ endEnum; FOR e ¬ routingTableHead, e.next UNTIL e = NIL DO IF delay # e.delay THEN LOOP; IF Greater[e.dest, previous] --greater than the one he has now AND ~Greater[e.dest, next] THEN { --less than any we know about since IF onlyChangedValues THEN IF e.changed THEN {e.changed ¬ FALSE; route ¬ e.dest} ELSE LOOP ELSE route ¬ e.dest; RETURN}; ENDLOOP; END; --Enumerate Fill: ArpaRoutingTable.FillTableProc = BEGIN --PROC[maxDelay: CARDINAL] END; --Fill FindLocalNetID: ArpaRoutingTable.FindNetworkProc = BEGIN << PROC[net: ArpaRouter.InternetAddress] RETURNS[ArpaRouter.InternetAddress]; Returns the network number relative to the destination network specified. If we cannot find a number relative to the destination, we use the first network with a known number. **we need to do the dest relative part! >> myNet: InternetAddress ¬ ArpaRouter.unknownInternetAddress; FOR n: Device ¬ Driver.GetDeviceChain[], n.next UNTIL n = NIL DO c: ArpaRoutingTable.NetworkContext ¬ Protocol1.GetContext[n, arpa]; SELECT TRUE FROM (c = NIL) => NULL; --not even close (~c.network.alive) => NULL; --no better (c.net = ArpaRouter.unknownInternetAddress) => NULL; ENDCASE => {myNet ¬ c.net; EXIT}; --good enough ENDLOOP; RETURN[LOOPHOLE[myNet]]; END; --FindLocalNetID Forward: PROC[b: ArpaBuffer.Buffer] = BEGIN e: RTEntry; nextHost: InternetAddress; body: ArpaBuffer.Body ¬ b.arpa; SendIcmp: PROCEDURE [type: ArpaTypes.IcmpType, body: ArpaBuffer.Body] = BEGIN b: ArpaBuffer.Buffer ¬ ArpaBuffer.GetBuffer[ requestPool, send, TRUE, 120]; icmpBody: LONG POINTER TO icmp ArpaBuffer.BufferBody ¬ LOOPHOLE[b.arpa]; icmpData: LONG POINTER TO PACKED ARRAY CARDINAL[1..1) OF ArpaTypes.Byte ¬ LOOPHOLE[icmpBody]; --ip fields icmpBody.ipHeader.destination ¬ body.ipHeader.source; icmpBody.ipHeader.source ¬ myGatewayAddr; icmpBody.ipHeader.protocol ¬ icmp; icmpBody.ipHeader.service ¬ ArpaSysParameters.GetTypeOfService[]; icmpBody.ipHeader.identification ¬ 0; icmpBody.ipHeader.lifetime ¬ 60; icmpBody.ipHeader.ihl ¬ (ArpaPort.minIPHeaderBytes+3)/4; SELECT type FROM redirect => { icmpBody.icmp ¬ [redirect[ code: 0, checksum: 0, redirect: e.route, icmpData: ]]; WITH redirect: icmpBody.icmp SELECT FROM redirect => icmpData ¬ LOOPHOLE[@redirect.icmpData]; ENDCASE; --BLT ipHeader + first 64 bits of data or 4 words [] ¬ ByteBlt.ByteBlt[ to: [LOOPHOLE[icmpData], 0, 120 - ArpaPort.minIPHeaderBytes - SIZE[redirect ArpaTypes.Icmp]*bpw], from: [LOOPHOLE[body], 0, body.ipHeader.ihl * 4 + 8]]; icmpBody.ipHeader.length ¬ icmpBody.ipHeader.ihl*4 + body.ipHeader.ihl*4 + SIZE[redirect ArpaTypes.Icmp]*bpw + 8; WITH redirect: icmpBody.icmp SELECT FROM redirect => redirect.checksum ¬ ArpaPortInternal.Checksum[ 0, body.ipHeader.ihl*2 + SIZE[redirect ArpaTypes.Icmp] + 4, @icmpBody.icmp]; ENDCASE }; << The size of the packet is the IP overhead + ICMP fields + offending IP header + 8 bytes of the offenders data. >> timeExceeded => { icmpBody.icmp ¬ [timeExceeded[ code: toLive, checksum: 0, unused: [0, 0], icmpData: ]]; WITH timeExceeded: icmpBody.icmp SELECT FROM timeExceeded => icmpData ¬ LOOPHOLE[@timeExceeded.icmpData]; ENDCASE; --BLT ipHeader + first 64 bits of data or 4 words [] ¬ ByteBlt.ByteBlt[ to: [LOOPHOLE[icmpData], 0, 120 - ArpaPort.minIPHeaderBytes - SIZE[timeExceeded ArpaTypes.Icmp]*bpw], from: [LOOPHOLE[body], 0, body.ipHeader.ihl * 4 + 8]]; icmpBody.ipHeader.length ¬ icmpBody.ipHeader.ihl*4 + body.ipHeader.ihl*4 + SIZE[timeExceeded ArpaTypes.Icmp]*bpw + 8; WITH timeExceeded: icmpBody.icmp SELECT FROM timeExceeded => timeExceeded.checksum ¬ ArpaPortInternal.Checksum[0, body.ipHeader.ihl*2 + SIZE[timeExceeded ArpaTypes.Icmp] + 4, @icmpBody.icmp]; ENDCASE }; unreachable => { icmpBody.icmp ¬ [unreachable[ code: net, checksum: 0, unused: [0, 0], icmpData: ]]; WITH unreachable: icmpBody.icmp SELECT FROM unreachable => icmpData ¬ LOOPHOLE[@unreachable.icmpData]; ENDCASE; --BLT ipHeader + first 64 bits of data or 4 words [] ¬ ByteBlt.ByteBlt[ to: [LOOPHOLE[icmpData], 0, 120 - ArpaPort.minIPHeaderBytes - SIZE[unreachable ArpaTypes.Icmp]*bpw], from: [LOOPHOLE[body], 0, body.ipHeader.ihl * 4 + 8]]; icmpBody.ipHeader.length ¬ icmpBody.ipHeader.ihl*4 + body.ipHeader.ihl*4 + SIZE[unreachable ArpaTypes.Icmp]*bpw + 8; WITH unreachable: icmpBody.icmp SELECT FROM unreachable => unreachable.checksum ¬ ArpaPortInternal.Checksum[0, body.ipHeader.ihl*2 + SIZE[unreachable ArpaTypes.Icmp] + 4, @icmpBody.icmp]; ENDCASE }; ENDCASE; ArpaPortInternal.SendPacket[b, remote]; END; SELECT TRUE FROM (e ¬ FindNetworkNumber[body.ipHeader.destination]) = NIL => { -- noRoute - send ICMP unreachable SendIcmp[unreachable, body]; ArpaBuffer.ReturnBuffer[b]}; (e.delay = 0) AND (b.fo.context = e.context) => BEGIN << Looping to same net send redirect to source and packet to correct gateway. >> body.ipHeader.lifetime ¬ body.ipHeader.lifetime - 1; -- reduce lifetime <> IF body.ipHeader.lifetime = 0 THEN NULL; --we are directly attached so let's give it to them. nextHost ¬ body.ipHeader.destination; body.ipHeader.checksum ¬ 0; body.ipHeader.checksum ¬ ArpaPortInternal.Checksum[ 0, body.ipHeader.ihl * bpw, @body.ipHeader]; -- recompute checksum for new packet. b.fo.status ¬ ArpaBuffer.TransferStatus[goodCompletion]; SendIcmp[redirect, body]; -- do it before we lose the data Protocol1.EncapsulateAndTransmit[ArpaBuffer.To[b], @nextHost]; END; ENDCASE => BEGIN --packet destined for another net and should be forwarded to next gateway. body.ipHeader.lifetime ¬ body.ipHeader.lifetime - 1; -- reduce lifetime IF body.ipHeader.lifetime = 0 THEN BEGIN --SEND ICMP timeExceeded; SendIcmp[timeExceeded, body]; ArpaBuffer.ReturnBuffer[b]; RETURN; END; nextHost ¬ IF e.delay # 0 THEN e.route ELSE body.ipHeader.destination; b.fo.network ¬ e.context.network; b.fo.context ¬ e.context; -- what driver gets buffer body.ipHeader.checksum ¬ 0; body.ipHeader.checksum ¬ ArpaPortInternal.Checksum[ 0, body.ipHeader.ihl * bpw, @body.ipHeader]; b.fo.status ¬ ArpaBuffer.TransferStatus[goodCompletion]; Protocol1.EncapsulateAndTransmit[ArpaBuffer.To[b], @nextHost]; END; END; --Forward FindNetworkNumber: PROC [dest: InternetAddress] RETURNS [e: RTEntry] = BEGIN << Searches for and returns the entry the specified network number (dest). If an entry is not found, e is NIL. >> e ¬ routingTableHead; FOR e ¬ routingTableHead, e.next UNTIL e = NIL OR ArpaPortInternal.AddrMatch[e.mask, dest, e.dest] DO ENDLOOP; END; --FindNetworkNumber FlushCache: ArpaRoutingTable.FlushCacheProc = BEGIN END; --FlushCache GetDelay: ArpaRoutingTable.GetDelayProc = BEGIN END; --GetDelay InfoReply: PUBLIC <> PROC [b: ArpaBuffer.Buffer] = BEGIN OPEN c: NARROW[b.fo.context, ArpaRoutingTable.NetworkContext]; << Processes an ICMP information reply message. If the net number is unknown, we will take any info, regardless of whether or not it is an answer to our request. >> nmsk: InternetAddress = c.netMask; SELECT TRUE FROM (ArpaPortInternal.AddrMismatch[c.netMask, c.net, myNullAddr]) => NULL; (ArpaPortInternal.BuildMasks[b.arpa.ipHeader.source].netMask # nmsk) => NULL; --incorrect address class. ENDCASE => c.net ¬ LOOPHOLE[Inline.DBITAND[ LOOPHOLE[b.arpa.ipHeader.source], LOOPHOLE[nmsk]]]; END; --InfoReply InfoRequest: PROC = BEGIN --Sends a ICMP info request to the local gateway. b: ArpaBuffer.Buffer ¬ ArpaBuffer.GetBuffer[requestPool, send, TRUE, 120]; body: LONG POINTER TO icmp ArpaBuffer.BufferBody ¬ LOOPHOLE[b.arpa]; --ip fields body.ipHeader.destination ¬ myGatewayAddr; body.ipHeader.source ¬ ArpaRouter.GetAddress[]; body.ipHeader.protocol ¬ icmp; body.ipHeader.service ¬ ArpaSysParameters.GetTypeOfService[]; body.ipHeader.identification ¬ 0; body.ipHeader.lifetime ¬ 60; body.ipHeader.ihl ¬ (ArpaPort.minIPHeaderBytes+3)/4; body.ipHeader.length ¬ body.ipHeader.ihl*4 + 8; body.icmp ¬ [infoRequest[code: 0, identifier: 0, sequence: 0, checksum: 0]]; WITH info: body.icmp SELECT FROM infoRequest => info.checksum ¬ ArpaPortInternal.Checksum[ 0, SIZE[infoRequest ArpaTypes.Icmp], @body.icmp.body]; ENDCASE; ArpaPortInternal.SendPacket[b, remote]; END; --InfoRequest ProbeGateway: PUBLIC <> PROC = BEGIN c: ArpaRoutingTable.NetworkContext ¬ NIL; NullNet: ENTRY PROC [nw: Device] RETURNS [ct: ArpaRoutingTable.NetworkContext] = --checks if any of the devices supporting arpa have null net numbers. BEGIN ct ¬ Protocol1.GetContext[nw, arpa]; SELECT TRUE FROM (ct = NIL) => NULL; --He doesn't support us?! (~ct.network.alive) => NULL; --he would but he croaked (ct.net = ArpaRouter.unknownInternetAddress) => RETURN[ct]; ENDCASE; ct ¬ NIL; END; --NullNet FOR n: Device ¬ Driver.GetDeviceChain[], n.next UNTIL n = NIL DO IF (c ¬ NullNet[n]) # NIL THEN SELECT TRUE FROM --the "host" has a net specified in it. ArpaPortInternal.AddrMismatch[c.netMask, c.host, myNullAddr] => BEGIN e: RTEntry; AddLocked: ENTRY PROC = {AddEntry[e]}; c.net ¬ LOOPHOLE[ Inline.DBITAND[LOOPHOLE[c.host], LOOPHOLE[c.netMask]]]; e ¬ CommHeap.zone.NEW[RTEObject ¬ [ next: NIL, dest: c.net, mask: c.netMask, delay: 0, lastAccess: System.GetClockPulses[], route: ArpaRouter.unknownInternetAddress, context: c, changed: TRUE]]; AddLocked[]; END; myGatewayAddr = ArpaRouter.unknownInternetAddress => NULL; --no gateway ENDCASE => FOR i: CARDINAL IN [0..5) UNTIL c.net # ArpaRouter.unknownInternetAddress DO InfoRequest[]; Process.Pause[Process.MsecToTicks[1000]]; ENDLOOP; ENDLOOP; END; --ProbeGateway Redirect: PUBLIC <> ENTRY PROC [b: ArpaBuffer.Buffer] = BEGIN --processes an ICMP redirect packet. ENABLE UNWIND => NULL; body: ArpaBuffer.Body ¬ b.arpa; --This is the redirect icmp packet WITH redirect: body.icmp SELECT FROM redirect => BEGIN e: RTEntry ¬ NIL; route: InternetAddress; icmp: ArpaBuffer.Body ¬ LOOPHOLE[@redirect.icmpData]; --offending packet route ¬ redirect.redirect; --the new, improved gateway to use IF (e ¬ FindNetworkNumber[icmp.ipHeader.destination]) = NIL THEN BEGIN e ¬ CommHeap.zone.NEW[RTEObject ¬ [ next: NIL, dest: icmp.ipHeader.destination, mask: ArpaPortInternal.BuildMasks[icmp.ipHeader.destination].netMask, delay: 0, lastAccess: System.GetClockPulses[], route: route, context: b.fo.context, changed: TRUE]]; AddEntry[e]; END ELSE BEGIN e.route ¬ route; e.delay ¬ 0; e.context ¬ b.fo.context; END; END; ENDCASE; END; --Redirect RemoveEntry: INTERNAL PROC [e: RTEntry] = BEGIN -- Removes the specified entry from the routing table. prev: RTEntry ¬ NIL; temp: RTEntry ¬ routingTableHead; UNTIL (temp = NIL) OR (temp = e) DO prev ¬ temp; temp ¬ temp.next; ENDLOOP; IF prev = NIL THEN routingTableHead ¬ e.next ELSE prev.next ¬ e.next; e.next ¬ NIL; CommHeap.zone.FREE[@e]; routingTableSize ¬ routingTableSize - 1; END; --RemoveEntry RemoveNet: ENTRY PROCEDURE [context: ArpaRoutingTable.NetworkContext] = BEGIN --remove state for attached network. << This procedure removes the specified attached network and all entries referencing it from the routing table. Multi entries may use this net. The loop doesn't increment the count on elements removed because the number of entries will be decremented by the "RemoveElement[entry.net]" statement. In effect the number of entries is coming back to equal the number of elements counted. >> entry: RTEntry ¬ routingTableHead; UNTIL entry = NIL DO SELECT TRUE FROM (entry.context = NIL) => NULL; --don't increment count (entry.context = context) => RemoveEntry[entry]; ENDCASE; entry ¬ entry.next; ENDLOOP; END; --RemoveNet RemoveRoute: ENTRY ArpaRoutingTable.RemoveRouteProc = BEGIN e: RTEntry ¬ NIL; IF (e ¬ FindNetworkNumber[dest]) = NIL THEN RETURN; RemoveEntry[e]; END; --RemoveRoute RoutingTableActivate: PROC = BEGIN ActivateLocked: ENTRY PROC = BEGIN ArpaBuffer.QueueInitialize[@outQueue]; requestPool ¬ ArpaBuffer.MakePool[send: 2, receive: 0, type: normalPool]; myDevice ¬ Driver.GetDeviceChain[]; myContext ¬ Protocol1.GetContext[myDevice, arpa]; myGatewayAddr ¬ ArpaPortInternal.GetGatewayAddr[]; myBroadcastAddr ¬ ArpaPortInternal.GetMyBroadcastAddr[]; myNullAddr ¬ ArpaPortInternal.GetMyNullAddr[]; END; ActivateLocked[]; << Add the special null entry for talking without a net number. This entry does not show up in Enumerate. >> AddRoute[myNullAddr, myContext.netMask, ArpaRouter.unknownInternetAddress, 0, myContext]; AddRoute[myBroadcastAddr, myContext.netMask, ArpaRouter.unknownInternetAddress, 0, myContext]; END; --RoutingTableActivate RoutingTableDeactivate: ENTRY PROC = INLINE BEGIN pleaseStop ¬ TRUE; routingTableSize ¬ 0; ArpaBuffer.QueueCleanup[@outQueue]; ArpaBuffer.DestroyPool[requestPool]; END; --RoutingTableDeactivate Start: PROC = BEGIN --This procedure turns the router on. pleaseStop ¬ FALSE; RoutingTableActivate[]; END; --Start ChangedState: PUBLIC PROCEDURE [context: ArpaRoutingTable.NetworkContext] = BEGIN RemoveNet[context]; AddNet[context]; END; --ChangedState Stop: PROC = BEGIN RoutingTableDeactivate[]; CleanUpRoutingTable[]; END; --Stop Transmit: ENTRY PROC [b: ArpaBuffer.Buffer] = BEGIN body: ArpaBuffer.Body ¬ b.arpa; nextHost, nmsk: InternetAddress; e: RTEntry; nmsk ¬ ArpaPortInternal.BuildMasks[body.ipHeader.destination].netMask; SELECT TRUE FROM (e ¬ FindNetworkNumber[body.ipHeader.destination]) = NIL => BEGIN --if we don't know the route, we will try sending it to our default --gateway. IF myGatewayAddr # ArpaRouter.unknownInternetAddress THEN BEGIN nextHost ¬ myGatewayAddr; b.fo.context ¬ myContext; b.fo.network ¬ myDevice; END ELSE --we don't know how to get there, and we have no gateway to ask. BEGIN IF ArpaFlags.doStats THEN ArpaStats.Incr[noRoute]; b.fo.status ¬ noRouteToNetwork; Driver.PutOnGlobalDoneQueue[ArpaBuffer.To[b]]; RETURN; END; END; ENDCASE => BEGIN --entry exists in table already. e.lastAccess ¬ System.GetClockPulses[]; nextHost ¬ IF e.route # ArpaRouter.unknownInternetAddress THEN e.route --gateway ELSE body.ipHeader.destination; --attached to target net b.fo.context ¬ e.context; b.fo.network ¬ e.context.network; END; b.fo.status ¬ ArpaBuffer.TransferStatus[goodCompletion]; Protocol1.EncapsulateAndTransmit[ArpaBuffer.To[b], @nextHost]; END; --Transmit END. LOG 14-Jan-85 15:28:22 SMA Created file. 13-Jul-85 13:54:29 SMA Added stats. 13-Nov-85 15:07:43 SMA ArpaProtocol => Arpa10MBit and ArpaPortInternal. 7-Jan-86 19:49:30 SMA Added Redirect and InfoReply for icmp. 14-Feb-86 13:40:19 SMA Added InfoRequest. 10-Mar-86 13:28:10 SMA ICMP checksums. 10-Jun-86 14:45:45 SMA Different Enumerate. 19-Nov-86 11:52:23 JAV Added RIP 10-Mar-87 19:41:47 AOF Funston buffer manager 21-May-87 13:23:54 AOF Correct spelling of infoReqeust => infoRequest 21-May-87 13:50:33 AOF Use existance of info request/response in ArpaTypes 29-Sep-87 17:59:12 JAV Ordered routing list in ascending order for enumeration. 29-Sep-87 17:59:12 JAV Removed advanceEntry from FindNetworkNumber 1-Mar-88 9:46:24 AOF Sort out code