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