-- File: ArpaRIPImpl.mesa - last edit: -- AOF 1-Mar-88 11:35:23 -- JAV 12-Nov-87 15:22:47 -- Copyright (C) 1987, 1988 by Xerox Corporation. All rights reserved. DIRECTORY ArpaBuffer USING [Body, Buffer, ReturnBuffer], ArpaPort USING [Create, GetPacket, GetSendBuffer, Handle, maxIPDataBytes, PutPacket, SetIPLengths, SetUDPLength, Timeout, UDPHeaderBytes], ArpaPortInternal USING [BuildMasks, GetMyBroadcastAddr, GetSubnetMask], ArpaRIP USING [], ArpaRouter USING [InternetAddress, Port, unknownInternetAddress], ArpaRoutingTable USING [defaultRth, NetworkContext], ArpaSysParameters USING [GetTypeOfService], ArpaTypes USING [Cardinal32], Environment USING [bytesPerWord], Inline USING [LowHalf], Mopcodes USING [zEXCH], Process USING [Detach, EnableAborts, MsecToTicks, Pause, SecondsToTicks, SetTimeout], System USING [GetClockPulses]; ArpaRIPImpl: MONITOR IMPORTS ArpaBuffer, ArpaPort, ArpaPortInternal, ArpaRouter, ArpaRoutingTable, ArpaSysParameters, Inline, Process, System EXPORTS ArpaRIP = BEGIN RIPPort: ArpaRouter.Port = LOOPHOLE[520]; bpw: NATURAL = Environment.bytesPerWord; pleaseStop: BOOLEAN ¬ FALSE; RoutingInfoTuple: TYPE = MACHINE DEPENDENT RECORD [ family(0: 0..15): CARDINAL ¬ 0, port(1: 0..15): CARDINAL ¬ 0, address(2: 0..31): ArpaRouter.InternetAddress, pad1(4: 0..31): ArpaTypes.Cardinal32 ¬ [0, 0], pad2(6: 0..31): ArpaTypes.Cardinal32 ¬ [0, 0], delay(8: 0..31): ArpaTypes.Cardinal32 ¬ [0, 0] ]; RoutingInfoType: TYPE = MACHINE DEPENDENT { routingInfoRequest(1), routingInfoResponse, (255)}; RoutingVersion: TYPE = MACHINE DEPENDENT {vers1(1), (255)}; RoutingInfoPacket: TYPE = MACHINE DEPENDENT RECORD [ routingType (0: 0..7): RoutingInfoType, routingVersions (0: 8..15): RoutingVersion, routingTuple(1): ARRAY INTEGER[0..0) OF RoutingInfoTuple ]; routingTupleSize: CARDINAL = SIZE[RoutingInfoTuple]; routingPcktsPerBuffer: CARDINAL = (ArpaPort.maxIPDataBytes - ArpaPort.UDPHeaderBytes - bpw) / (routingTupleSize * bpw); TCPIPFamily: CARDINAL = 2; routerTimer, auxRouterTimer: CONDITION; udpHandle: ArpaPort.Handle ¬ NIL; StringProc: TYPE = PROCEDURE [s: LONG STRING, clientData: LONG POINTER ¬ NIL]; outputProc: StringProc ¬ NIL; CardToDelay: PROC[LONG CARDINAL] RETURNS[ArpaTypes.Cardinal32] = MACHINE CODE {Mopcodes.zEXCH}; DelayToCard: PROC[ArpaTypes.Cardinal32] RETURNS[LONG CARDINAL] = MACHINE CODE {Mopcodes.zEXCH}; StartRouter: PUBLIC PROCEDURE [proc: PROCEDURE [s: LONG STRING, clientData: LONG POINTER ¬ NIL]] = BEGIN udpHandle ¬ ArpaPort.Create[RIPPort, 1, 40, normal]; outputProc ¬ proc; Process.Detach[FORK Router]; Process.Pause[Process.MsecToTicks[1000]]; Process.Detach[FORK RouterServer[]]; END; StopRouter: PUBLIC PROCEDURE = BEGIN pleaseStop ¬ TRUE; END; Router: PROCEDURE = BEGIN b: ArpaBuffer.Buffer; body: ArpaBuffer.Body; tuple: RoutingInfoTuple; ripPacket: LONG POINTER TO RoutingInfoPacket; hopsToFillTo: CARDINAL ¬ LAST[CARDINAL]; WHILE ~pleaseStop DO body ¬ (b ¬ ArpaPort.GetPacket[udpHandle ! ArpaPort.Timeout => IF pleaseStop THEN EXIT ELSE RETRY]).arpa; IF body.ipHeader.protocol # userDatagram THEN { ArpaBuffer.ReturnBuffer[b]; LOOP}; ripPacket ¬ LOOPHOLE[@body.user.bytes]; SELECT ripPacket.routingType FROM routingInfoRequest => { netNumber: ArpaRouter.InternetAddress ¬ ripPacket.routingTuple[0].address; context: ArpaRoutingTable.NetworkContext ¬ NARROW[b.fo.context]; ArpaBuffer.ReturnBuffer[b]; SendRoutingInfo[netNumber, context, udpHandle, FALSE]}; routingInfoResponse => { tuples: NATURAL = ((body.user.length - ArpaPort.UDPHeaderBytes) - 2) / (SIZE[RoutingInfoTuple] * bpw); FOR i: CARDINAL IN[0..tuples) DO tuple ¬ ripPacket.routingTuple[i]; IF DelayToCard[tuple.delay] <= hopsToFillTo THEN { ArpaRoutingTable.defaultRth.addRoute[ dest: tuple.address, mask: IF ArpaPortInternal.GetSubnetMask[] # ArpaRouter.unknownInternetAddress THEN ArpaPortInternal.GetSubnetMask[] ELSE ArpaPortInternal.BuildMasks[tuple.address].netMask, route: LOOPHOLE[body.ipHeader.source], delay: Inline.LowHalf[DelayToCard[tuple.delay]], context: NARROW[b.fo.context]]}; -- context is provided so that if the old context # new context then routes can be compared ENDLOOP; ArpaBuffer.ReturnBuffer[b]; }; ENDCASE => {ArpaBuffer.ReturnBuffer[b]; LOOP}; ENDLOOP; END; SendRoutingInfo: PROCEDURE [netNumber: ArpaRouter.InternetAddress, context: ArpaRoutingTable.NetworkContext, portHandle: ArpaPort.Handle, flash: BOOLEAN] = BEGIN b: ArpaBuffer.Buffer ¬ NIL; body: ArpaBuffer.Body; tuples: CARDINAL ¬ 0; first: BOOLEAN ¬ TRUE; myBroadcastAddr: ArpaRouter.InternetAddress ¬ ArpaPortInternal.GetMyBroadcastAddr[]; ripPacket: LONG POINTER TO RoutingInfoPacket; IF netNumber = ArpaRouter.unknownInternetAddress THEN { -- sending to all nets (gratuitious info) FOR delay: CARDINAL IN [0..16) DO net: ArpaRouter.InternetAddress ¬ ArpaRoutingTable.defaultRth.startEnumeration; UNTIL net = ArpaRoutingTable.defaultRth.endEnumeration DO IF first THEN { first ¬ FALSE; b ¬ ArpaPort.GetSendBuffer[portHandle]; b.fo.allNets ¬ TRUE; -- sending to all contected devices. body ¬ b.arpa; ripPacket ¬ LOOPHOLE[@body.user.bytes]; body.ipHeader.lifetime ¬ 60; body.ipHeader.protocol ¬ userDatagram; body.ipHeader.service ¬ ArpaSysParameters.GetTypeOfService[]; body.ipHeader.identification ¬ Inline.LowHalf[System.GetClockPulses[]]; body.ipHeader.destination ¬ LOOPHOLE[myBroadcastAddr]; --udp fields. body.user.sourcePort ¬ LOOPHOLE[RIPPort]; body.user.destinationPort ¬ LOOPHOLE[RIPPort]; ripPacket.routingVersions ¬ vers1; ripPacket.routingType ¬ routingInfoResponse; }; net ¬ ArpaRoutingTable.defaultRth.enumerate[net, delay, flash]; IF net = ArpaRoutingTable.defaultRth.endEnumeration THEN LOOP; ripPacket.routingTuple[tuples].family ¬ TCPIPFamily; ripPacket.routingTuple[tuples].port ¬ 0; ripPacket.routingTuple[tuples].address ¬ net; ripPacket.routingTuple[tuples].pad1 ¬ [0, 0]; ripPacket.routingTuple[tuples].pad2 ¬ [0, 0]; ripPacket.routingTuple[tuples].delay ¬ CardToDelay[delay + 1]; tuples ¬ tuples + 1; IF tuples >= routingPcktsPerBuffer THEN { ArpaPort.SetUDPLength[body, (tuples * routingTupleSize + 1) * bpw]; ArpaPort.SetIPLengths[ body, 0, (tuples * routingTupleSize + 1) * bpw + ArpaPort.UDPHeaderBytes]; ArpaPort.PutPacket[portHandle, b]; first ¬ TRUE; tuples ¬ 0}; ENDLOOP ENDLOOP; IF tuples > 0 THEN { ArpaPort.SetUDPLength[body, (tuples * routingTupleSize + 1) * bpw]; ArpaPort.SetIPLengths[ body, 0, (tuples * routingTupleSize + 1) * bpw + ArpaPort.UDPHeaderBytes]; ArpaPort.PutPacket[portHandle, b]; outputProc["P"]} ELSE ArpaBuffer.ReturnBuffer[b]} ELSE { -- only info for one net requested. b ¬ ArpaPort.GetSendBuffer[portHandle]; body ¬ b.arpa; ripPacket ¬ LOOPHOLE[@body.user]; body.ipHeader.lifetime ¬ 60; body.ipHeader.protocol ¬ userDatagram; body.ipHeader.service ¬ ArpaSysParameters.GetTypeOfService[]; body.ipHeader.identification ¬ Inline.LowHalf[System.GetClockPulses[]]; body.ipHeader.destination ¬ LOOPHOLE[myBroadcastAddr]; --udp fields. ripPacket.routingVersions ¬ vers1; ripPacket.routingType ¬ routingInfoResponse; body.user.destinationPort ¬ LOOPHOLE[RIPPort]; -- tuple ripPacket.routingTuple[0].family ¬ TCPIPFamily; ripPacket.routingTuple[0].port ¬ 0; ripPacket.routingTuple[0].address ¬ netNumber; ripPacket.routingTuple[0].delay ¬ CardToDelay[ArpaRoutingTable.defaultRth.getDelay[netNumber]]; ArpaPort.SetUDPLength[body, routingTupleSize * bpw]; ArpaPort.SetIPLengths[body, 0, routingTupleSize * bpw + ArpaPort.UDPHeaderBytes]; ArpaPort.PutPacket[portHandle, b]; outputProc["P"];}; END; RouterServer: PROCEDURE = BEGIN flash: BOOLEAN ¬ FALSE; Wait: ENTRY PROCEDURE [condition: CONDITION] = INLINE { ENABLE UNWIND => NULL; WAIT condition; }; --end of Wait Process.Pause[Process.MsecToTicks[100]]; UNTIL pleaseStop DO ENABLE ABORTED => EXIT; Wait[routerTimer]; IF pleaseStop THEN EXIT; -- wait a random amount of time [0..5] seconds before sending out the -- gratuitous response packet. This will help prevent all inrs -- from sending at the same time. Process.SetTimeout[@auxRouterTimer, Process.SecondsToTicks[Inline.LowHalf[System.GetClockPulses[]] MOD 5]]; Wait[auxRouterTimer]; IF pleaseStop THEN EXIT; outputProc[IF flash THEN "F" ELSE "T"]; SendRoutingInfo[ArpaRouter.unknownInternetAddress, NIL, udpHandle, flash]; flash ¬ ~flash; ENDLOOP; END; --RouterServer Process.SetTimeout[@routerTimer, Process.MsecToTicks[13500]]; -- set for 13.5 seconds so that when the extra random wait [0..5] seconds -- is added, the average time between gratuitous pkts is 30 seconds Process.SetTimeout[@auxRouterTimer, Process.MsecToTicks[500]]; Process.EnableAborts [@routerTimer]; Process.EnableAborts [@auxRouterTimer]; END.