-- 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.