-- File: NewNetworkStreamImpl.mesa - last edit:
-- MXI 27-Oct-87 15:04:05
-- Copyright (C) 1984, 1985, 1986, 1987 by Xerox Corporation. All rights reserved.
DIRECTORY
Buffer USING [],
BufferOps USING [GetSizes],
CommSwitches USING [xmtRateControl, xmtRateT1, xmtRate64],
CommFlags USING [doDebug],
CommUtil USING [PulsesToTicks],
Driver USING [Device, Glitch, nilDevice],
Environment USING [bytesPerWord, nullBlock],
HostNumbers USING [IsMulticastID],
Inline USING [BITAND, LongCOPY],
NetworkStream USING [ConnectionFailed, ConnectionID, CreateTransducer,
defaultWaitTime, infiniteWaitTime, ListenError, WaitTime],
NetworkStreamInternal USING [ListenerHandle],
NetworkStreamImpl USING [attn, connection, packetStreamObject, probe,
rcvr, rexmtr, startByteStream, stopByteStream, xmtr],
NewNetworkStream USING [NegotiationParameters,
nullTypeValuePairs, TSap, TSapObject, TypeValuePairs],
NSBuffer USING [Body, Buffer, Dequeue, GetBuffer, ReturnBuffer],
NSTypes USING [bytesPerIDPHeader, bytesPerSppHeader,
ConnectionID, maxDataBytesPerSpp, maxIDPBytesPerPacket, WaitTime],
PacketStream USING [bytesPerSequencedPktHeader,
ConnectionAlreadyThere, ConnectionFailed, Handle, unknownConnID],
Protocol1 USING [Family, GetFamilyUnit, SetMaximumBufferSize],
Process USING [DisableTimeout, EnableAborts, SetTimeout],
Router USING [GetDelayToNet, NoTableEntryForNet],
RouterInternal USING [SendErrorPacket],
Runtime USING [GetCaller],
SpecialSystem USING [HostNumber],
SppNegotiation USING [NegotiationHints, NewMake],
SppOps USING [DisableChecksums, DisableProbes, EnableChecksums, EnableProbes,
GlobalFrameFromByteStream, GlobalFrameFromPacketStream,
nullAttention, PacketStreamFromByteStream, sppWindowSize],
Socket USING [ChannelHandle, Create, GetBufferPool,
GetAssignedAddress, GetPacket, PutPacket, SetWaitTime, TimeOut],
Stream USING [Handle],
System USING [GetClockPulses, MicrosecondsToPulses, NetworkAddress,
NetworkNumber, nullNetworkAddress, switches];
NewNetworkStreamImpl: MONITOR
IMPORTS
BufferOps, CommUtil, Driver, HostNumbers, Inline, NetworkStream,
NSBuffer, PacketStream, Protocol1, Process, Router,
RouterInternal, Runtime, SppNegotiation, SppOps, Socket, System
EXPORTS
Buffer, NetworkStream, NewNetworkStream, SppNegotiation, System =
BEGIN
--EXPORTED TYPE(S) and READONLY Variables
HostNumber: PUBLIC <<System>> TYPE = SpecialSystem.HostNumber;
ListenerHandle: PUBLIC <<NetworkStream>> TYPE = NetworkStreamInternal.ListenerHandle;
ConnectionID: PUBLIC <<NetworkStream>> TYPE = NSTypes.ConnectionID;
Device: PUBLIC <<Buffer>> TYPE = Driver.Device;
-- Types
NegotiationParameters: TYPE = NewNetworkStream.NegotiationParameters;
TSap: TYPE = NewNetworkStream.TSap;
TSapObject: TYPE = NewNetworkStream.TSapObject;
-- Internal variables
unknownCID, uniqueCID: ConnectionID = PacketStream.unknownConnID;
maxAlloc: CARDINAL ← SppOps.sppWindowSize;
uniqueTSapObject: TSapObject = [
address: System.nullNetworkAddress, connectionID: uniqueCID];
bytesPerHeader: CARDINAL = NSTypes.bytesPerIDPHeader + NSTypes.bytesPerSppHeader;
maxSppBytesWithMaxBuffer: CARDINAL ← BufferOps.GetSizes[][maxBuffer] - bytesPerHeader;
-- Following default value should be identical to ones in NetworkStreamImpl.
xmtrDefaults: RECORD [allocation, rateControlPause, delay1Hop, delayNHopT1,
delayNHop64, delayNHop96, totalQuench: CARDINAL] =
[1, 50, 50, 100, 120, 300, 16];
rcvrDefaults: RECORD [accept, duplicate: INTEGER] =
[2, 20];
rexmtrDefaults: RECORD [flushRoute, giveUp, <<numbers of packets>>
initialLocal, initialRemote, ceiling, floor, calculationInterval: CARDINAL <<msecs>>] =
[8, 30, 500, 6000, 24000, 200, 5000];
probeDefaults: RECORD [giveUp, inactiveInterval, allocInterval, hopDelay: CARDINAL] =
[8, 5000, 2000, 2000];
streamParms: RECORD [send, receive, xmtrAlloc, rcvrAlloc: CARDINAL] =
[3, 4, 0, 0];
maxMsecToPulses: LONG CARDINAL = LAST[LONG CARDINAL] / 1000;
-- Internal errors
BadDestintationAddress: ERROR = CODE; --for glitching
-- Public procedures
NewApprove: PUBLIC PROC[listenerH: ListenerHandle,
negotiationParameters: NegotiationParameters ← NIL,
streamTimeout: NetworkStream.WaitTime ← NetworkStream.infiniteWaitTime]
RETURNS [sH: Stream.Handle] =
BEGIN
localTSapObject: TSapObject ← uniqueTSapObject;
remoteTSapObject: TSapObject ← [
listenerH.buffer.ns.source, listenerH.buffer.ns.sourceConnectionID];
IF (listenerH = NIL) OR (listenerH.seal # listenerSeal)
THEN ERROR NetworkStream.ListenError[illegalHandle];
IF listenerH.state # pending THEN ERROR NetworkStream.ListenError[illegalState];
IF GetNegBit[listenerH.buffer.ns] THEN
sH ← NewTransducer[
@localTSapObject, @remoteTSapObject, NIL,
[TRUE, listenerH.buffer.fo.driver.device], streamTimeout!
UNWIND => {
NSBuffer.ReturnBuffer[listenerH.buffer]; listenerH.state ← idle}]
ELSE
sH ← NetworkStream.CreateTransducer[
localTSapObject.address, listenerH.buffer.ns.source,
Environment.nullBlock,
unknownCID, listenerH.buffer.ns.sourceConnectionID,
TRUE, streamTimeout, bulk !
UNWIND => {
NSBuffer.ReturnBuffer[listenerH.buffer]; listenerH.state ← idle}];
NSBuffer.ReturnBuffer[listenerH.buffer];
listenerH.state ← idle;
END;
NewCreate: PUBLIC PROC[
remote: System.NetworkAddress,
negotiationParameters: NegotiationParameters ← NIL,
timeout: NetworkStream.WaitTime ← NetworkStream.defaultWaitTime]
RETURNS [Stream.Handle] =
BEGIN
localTSapObject: TSapObject ← uniqueTSapObject;
remoteTSapObject: TSapObject ← [remote, uniqueCID];
RETURN[
NewTransducer[
@localTSapObject, @remoteTSapObject, NIL, [TRUE, ethernet], timeout]];
END; --Create
NewTransducer: PUBLIC PROC[
local, remote: TSap, --for the connection to be created
negotiationParameters: NegotiationParameters ← NIL,
hints: SppNegotiation.NegotiationHints,
timeout: NetworkStream.WaitTime ← NetworkStream.defaultWaitTime]
RETURNS [sH: Stream.Handle] =
BEGIN
psH: PacketStream.Handle;
instanceN: LONG POINTER TO FRAME[NetworkStreamImpl];
SELECT TRUE FROM
(~HostNumbers.IsMulticastID[@remote.address.host]) => NULL; --okay
(~CommFlags.doDebug) => --DON'T LET HIM GET AWAY WITH THIS-- DO
SIGNAL NetworkStream.ConnectionFailed[noTranslationForDestination];
ENDLOOP;
ENDCASE => Driver.Glitch[BadDestintationAddress];
psH ← SppNegotiation.NewMake[
local, remote, negotiationParameters, hints, timeout];
instanceN ← SppOps.GlobalFrameFromPacketStream[psH];
sH ← instanceN.startByteStream[psH];
sH.delete ← Delete; --don't use instance's version of delete
END; --CreateTransducer
NewListen: PUBLIC PROC[
listenerH: ListenerHandle,
listenTimeout: NetworkStream.WaitTime ← NetworkStream.infiniteWaitTime]
RETURNS [remote: System.NetworkAddress] =
BEGIN
GetPacket: ENTRY PROC =
BEGIN
ENABLE UNWIND => NULL;
WHILE (b ← NSBuffer.Dequeue[@listenerH.queue]) = NIL DO
WAIT listenerH.condition; ENDLOOP;
END; --GetPacket
b: NSBuffer.Buffer;
IF (listenerH = NIL) OR (listenerH.seal # listenerSeal)
THEN ERROR NetworkStream.ListenError[illegalHandle];
b ← listenerH.buffer;
SELECT listenerH.state FROM
pending => RouterInternal.SendErrorPacket[b, listenerReject];
listening => ERROR NetworkStream.ListenError[illegalState];
ENDCASE;
listenerH.state ← listening;
Socket.SetWaitTime[listenerH.socket, listenTimeout];
DO
--until the packet is a sequenced packet and is not a duplicate,
--or timeout, or aborted socket
body: NSBuffer.Body;
GetPacket[ ! UNWIND => listenerH.state ← idle];
IF b = NIL THEN LOOP; --nothing happening here
body ← b.ns; --makes for cheaper access
--discard any cases with something wrong, ENDCASE is only acceptable packet
SELECT TRUE FROM
(b.fo.status # goodCompletion) =>
NSBuffer.ReturnBuffer[b]; --don't chance it with bad buffers
(HostNumbers.IsMulticastID[@body.destination.host]) =>
NSBuffer.ReturnBuffer[b]; --he's insane, but can't error broadcast
(HostNumbers.IsMulticastID[@body.source.host]) =>
NSBuffer.ReturnBuffer[b]; --he's busted, and I don't care
(body.packetType # sequencedPacket) =>
RouterInternal.SendErrorPacket[b, invalidPacketType];
(body.destinationConnectionID # unknownCID) =>
RouterInternal.SendErrorPacket[b, protocolViolation];
(body.sourceConnectionID = unknownCID) =>
RouterInternal.SendErrorPacket[b, protocolViolation];
(body.sequenceNumber # 0) =>
RouterInternal.SendErrorPacket[b, protocolViolation];
(~body.systemPacket) =>
RouterInternal.SendErrorPacket[b, protocolViolation];
(~PacketStream.ConnectionAlreadyThere[
body.source, body.sourceConnectionID]) =>
BEGIN
listenerH.buffer ← b;
listenerH.state ← pending;
RETURN [body.source];
END;
ENDCASE => NSBuffer.ReturnBuffer[b];
ENDLOOP;
END; --Listen
NewReject: PUBLIC PROC[listenerH: ListenerHandle] =
BEGIN
IF (listenerH = NIL) OR (listenerH.seal # listenerSeal)
THEN ERROR NetworkStream.ListenError[illegalHandle];
IF listenerH.state # pending THEN ERROR NetworkStream.ListenError[illegalState];
-- Should send SppError packet. But currently let them timeout...
NSBuffer.ReturnBuffer[listenerH.buffer];
listenerH.state ← idle;
END;
InitializeInternal: PROC [hops: NATURAL,
instance: LONG POINTER TO FRAME[NetworkStreamImpl]] =
BEGIN
time: LONG CARDINAL = System.GetClockPulses[];
instance.xmtr.clientProcess ← NIL;
instance.xmtr.maxSeq ← xmtrDefaults.allocation - 1;
instance.xmtr.unackedSeq ← instance.xmtr.nextSeq ← 0;
instance.xmtr.maxAlloc ← streamParms.xmtrAlloc + SppOps.sppWindowSize;
instance.xmtr.blocked ← instance.xmtr.checksums ← TRUE; --default & starting point
instance.xmtr.transmitProc ← Socket.PutPacket; --default & starting point
instance.xmtr.rateControlling ← (hops # 0) AND
(System.switches[CommSwitches.xmtRateControl] = down);
instance.xmtr.interval ← SELECT TRUE FROM
(~instance.xmtr.rateControlling) => 0, --we're not doing this
(hops = 1) => System.MicrosecondsToPulses[xmtrDefaults.delay1Hop],
(System.switches[CommSwitches.xmtRateT1] = down) =>
System.MicrosecondsToPulses[xmtrDefaults.delayNHopT1],
(System.switches[CommSwitches.xmtRate64] = down) =>
System.MicrosecondsToPulses[xmtrDefaults.delayNHop64],
ENDCASE => System.MicrosecondsToPulses[xmtrDefaults.delayNHop96];
instance.xmtr.interval ← 1000 * instance.xmtr.interval; --we really wanted usecs
instance.xmtr.quenchIncr ← 0; --we haven't started yet
instance.xmtr.lastXmt ← time; --that's to start with
Process.DisableTimeout[@instance.xmtr.newAllocation];
Process.EnableAborts[@instance.xmtr.newAllocation];
instance.probe.inactiveInterval ← 1000 * System.MicrosecondsToPulses[
probeDefaults.inactiveInterval + (probeDefaults.hopDelay * hops)];
instance.probe.allocInterval ← 1000 * System.MicrosecondsToPulses[
probeDefaults.allocInterval + (probeDefaults.hopDelay * hops)];
instance.probe.lastTime ← time;
instance.probe.unacked ← 0;
instance.probe.probing ← TRUE;
instance.rexmtr.ceiling ← 1000 * System.MicrosecondsToPulses[rexmtrDefaults.ceiling];
instance.rexmtr.floor ← 1000 * System.MicrosecondsToPulses[rexmtrDefaults.floor];
instance.rexmtr.giveUp ← rexmtrDefaults.giveUp;
instance.rexmtr.count ← 0;
instance.rexmtr.delay ← 0;
instance.rexmtr.interval ← 1000 * System.MicrosecondsToPulses[
IF hops = 0 THEN rexmtrDefaults.initialLocal
ELSE rexmtrDefaults.initialRemote];
instance.rexmtr.calculationInterval ← 1000 * System.MicrosecondsToPulses[
rexmtrDefaults.calculationInterval];
instance.rexmtr.calculation ← time;
instance.rexmtr.process ← NIL;
Process.EnableAborts[@instance.rexmtr.condition];
Process.SetTimeout[
@instance.rexmtr.condition, CommUtil.PulsesToTicks[[instance.rexmtr.floor / 2]]];
instance.rcvr.nextSeq ← 0;
instance.rcvr.blocked ← instance.rcvr.acksReqested ← FALSE;
instance.rcvr.maxSeq ← instance.rcvr.maxAlloc ← streamParms.rcvrAlloc + SppOps.sppWindowSize;
instance.rcvr.process ← NIL;
instance.rcvr.table.slot ← ALL[NIL];
instance.rcvr.table.index ← instance.rcvr.table.length ← 0;
Process.EnableAborts[@instance.rcvr.table.condition];
Process.DisableTimeout[@instance.rcvr.table.condition];
instance.rexmtr.table.index ← instance.rexmtr.table.length ← 0;
instance.rexmtr.table.slot ← ALL[NIL];
Process.EnableAborts[@instance.rexmtr.table.condition];
Process.DisableTimeout[@instance.rexmtr.table.condition];
instance.attn.table.index ← instance.attn.table.length ← 0;
instance.attn.table.slot ← ALL[SppOps.nullAttention];
Process.EnableAborts[@instance.attn.table.condition];
Process.DisableTimeout[@instance.attn.table.condition];
END; --InitializeInternal;
InitializeSppState: PUBLIC PROC [
local, remote: System.NetworkAddress,
localID, remoteID: ConnectionID, timeout: NSTypes.WaitTime] =
BEGIN
hops: NATURAL;
instance: LONG POINTER TO FRAME[NetworkStreamImpl] ← LOOPHOLE[Runtime.GetCaller[]];
socket: Socket.ChannelHandle;
hops ← Router.GetDelayToNet[remote.net!
Router.NoTableEntryForNet => {hops ← 0; CONTINUE}];
InitializeInternal[hops, instance];
socket ← Socket.Create[
socket: local.socket, type: sequencedPacket, send: 16, receive: 17];
Socket.SetWaitTime[socket, LAST[NSTypes.WaitTime]]; --no timeouts here
instance.connection ← [
state: IF remoteID = unknownCID THEN unestablished ELSE established,
stateBeforeSuspension: unestablished, whySuspended: notSuspended,
maxSppBytes: NSTypes.maxDataBytesPerSpp, timeout: timeout, hops: hops,
socket: socket, remoteAddr: remote, remoteID: remoteID,
localAddr: Socket.GetAssignedAddress[socket], localID: localID,
pool: Socket.GetBufferPool[socket], whyFailed: timeout];
END; --InitializeSppState;
<<
TABLE: Four case of establishment (local address/id is valid)
startProc remoteAddr/Id actEst state
NewCreate no (socket)/no TRUE idle -(TX)-> passiveNegotiation
NewApprove valid/valid TRUE activeNegotiation -(TX)-> waitForDecision
NewTransducer valid/no TRUE idle -(TX)-> passiveNegotiation
NewTransducer valid/no FALSE idle
>>
NegTypes: TYPE = MACHINE DEPENDENT {
size(1), -- (packets)
-- The size of the data portion of the SPP data unit.
window(2), -- (packets)
-- The number of packets to be advertised in the allocation window.
xmti(3), -- (millisecs) The minimum interpacket transmit interval.
rxmtf(4), -- (millisecs) The minimum value of the retransmission interval
-- that is calculated based on stream history.
rxmtc(5), -- (millisecs) The maximum value of the retransmission interval
-- that is calculated based on stream history.
rxmtl(6), -- (packets) The number of times a data packet should be retransmitted
-- at the current retransmit interval before the connection is abandoned.
check(7), -- (switch) Should the connection generate/check software checksums?
probe(8) -- (switch) Should the connection generate idle line probes?
};
NegState: TYPE = {idle, passiveNegotiation, activeNegotiation, negotiating, waitForDecision};
NewInitializeSppState: PUBLIC PROC [
local, remote: TSap,
negotiationParameters: NegotiationParameters,
hints: SppNegotiation.NegotiationHints, timeout: NSTypes.WaitTime] =
BEGIN
hops: NATURAL;
instance: LONG POINTER TO FRAME[NetworkStreamImpl] ← LOOPHOLE[Runtime.GetCaller[]];
socket: Socket.ChannelHandle;
localParams: ARRAY [0..NegTypes.LAST.ORD - NegTypes.FIRST.ORD]
OF NewNetworkStream.TypeValuePairs ← ALL[NewNetworkStream.nullTypeValuePairs];
CollectNegotiationParams: PROC = BEGIN
index: CARDINAL ← 0;
IF negotiationParameters = NIL THEN BEGIN
negotiationParameters ← DESCRIPTOR[localParams];
FOR n: NegTypes IN NegTypes DO
negotiationParameters[index].type ← n.ORD;
index ← index + 1;
ENDLOOP;
FOR index IN [0..negotiationParameters.LENGTH) DO
SELECT localParams[index].type FROM
NegTypes.size.ORD => localParams[index].value ← [value[
IF hops = 0 THEN maxSppBytesWithMaxBuffer ELSE instance.connection.maxSppBytes]];
NegTypes.window.ORD => localParams[index].value ← [value[
MAX [instance.xmtr.maxAlloc, MIN [hops * 2, 13]]]];
NegTypes.xmti.ORD => localParams[index].value ← [value[CARDINAL[
instance.xmtr.interval / 1000]]]; -- usec to msec
NegTypes.rxmtf.ORD => localParams[index].value ← [value[CARDINAL[
instance.rexmtr.floor / 1000]]]; -- usec to msec
NegTypes.rxmtc.ORD => localParams[index].value ← [value[CARDINAL[
instance.rexmtr.ceiling / 1000]]]; -- usec to msec
NegTypes.rxmtl.ORD => localParams[index].value ← [value[CARDINAL[
instance.rexmtr.interval / 1000]]]; -- usec to msec
NegTypes.check.ORD => localParams[index].value ← [switch[
IF hops = 0 AND hints.driver = ethernet THEN off ELSE on]];
NegTypes.probe.ORD => localParams[index].value ← [switch[on]];
ENDCASE;
ENDLOOP;
END
ELSE BEGIN -- Copy client's data
valid: CARDINAL ← 0;
FOR index IN [0..negotiationParameters.LENGTH) DO
IF negotiationParameters[index].type IN
[NegTypes.FIRST.ORD..NegTypes.LAST.ORD] THEN BEGIN
localParams[index] ← negotiationParameters[index];
valid ← valid + 1;
END;
ENDLOOP;
negotiationParameters ← DESCRIPTOR[@localParams, valid];
END;
END;
ApplyNegotiationParams: PROC = BEGIN
FOR index: CARDINAL IN [0..negotiationParameters.LENGTH) DO
SELECT localParams[index].type FROM
NegTypes.size.ORD => instance.connection.maxSppBytes ← localParams[index].value.v;
NegTypes.window.ORD => instance.xmtr.maxAlloc ← localParams[index].value.v;
NegTypes.xmti.ORD => instance.xmtr.interval ← LONG[localParams[index].value.v];
NegTypes.rxmtf.ORD => instance.rexmtr.floor ← LONG[localParams[index].value.v];
NegTypes.rxmtc.ORD => instance.rexmtr.ceiling ← LONG[localParams[index].value.v];
NegTypes.rxmtl.ORD => instance.rexmtr.interval ← LONG[localParams[index].value.v];
NegTypes.check.ORD => SELECT localParams[index].value.s FROM
on => SppOps.EnableChecksums[@instance.packetStreamObject];
off => SppOps.DisableChecksums[@instance.packetStreamObject];
ENDCASE;
NegTypes.probe.ORD => SELECT localParams[index].value.s FROM
on => SppOps.EnableProbes[@instance.packetStreamObject];
off => SppOps.DisableProbes[@instance.packetStreamObject];
ENDCASE;
ENDCASE;
ENDLOOP;
END;
systemPacketSize: CARDINAL = PacketStream.bytesPerSequencedPktHeader;
bytesPerTypeValuePairs: CARDINAL =
SIZE[NewNetworkStream.TypeValuePairs] * Environment.bytesPerWord;
previousType: [0..17B);
SendPacket: PROC [type: [0..17B)] = BEGIN
buffer: NSBuffer.Buffer ← NIL;
body: NSBuffer.Body ← NIL;
paramsBytes: CARDINAL ← 0;
IF type = paramType THEN
paramsBytes ← negotiationParameters.LENGTH * bytesPerTypeValuePairs;
buffer ← NSBuffer.GetBuffer[aH: instance.connection.pool,
wait: TRUE, function: send, size: systemPacketSize + paramsBytes];
body ← buffer.ns;
body.pktLength ← systemPacketSize + paramsBytes;
body.packetType ← sequencedPacket;
body.destination ← instance.connection.remoteAddr;
body.systemPacket ← body.sendAck ← TRUE;
body.attention ← body.endOfMessage ← FALSE;
body.unusedType ← type;
body.subtype ← 0;
body.sourceConnectionID ← instance.connection.localID;
body.destinationConnectionID ← instance.connection.remoteID;
body.sequenceNumber ← 0;
body.acknowledgeNumber ← 0;
body.allocationNumber ← maxAlloc;
IF paramsBytes # 0 THEN Inline.LongCOPY[from: @localParams,
nwords: paramsBytes / Environment.bytesPerWord, to: @body.sppWords];
Socket.PutPacket[instance.connection.socket, buffer];
previousType ← type;
END;
CheckParameters: PROC [body: NSBuffer.Body] RETURNS [BOOLEAN] = BEGIN
remoteParams: NegotiationParameters ← DESCRIPTOR[
@body.sppWords,
(body.pktLength - systemPacketSize) / bytesPerTypeValuePairs];
-- Do checking
-- 'negotiationParameters' will points local parameters
-- 'remoteParams' will points local parameters
-- negotiated result should be stored in negotiationParameters
-- Apply params will use negotiationParameters
-- New proposition also have to be stored in local parameters
RETURN[TRUE]; -- For now
END;
-- When this procedure is called,
-- negotiated parameter should be stored in localParams
SetMaxBufferSize: PROC = BEGIN
maxSize: CARDINAL ← NSTypes.maxIDPBytesPerPacket;
family: Protocol1.Family;
-- Get negotiated max buffer size
FOR i: CARDINAL IN [0..negotiationParameters.LENGTH) DO
IF negotiationParameters[i].type # NegTypes.size.ORD THEN LOOP;
maxSize ← bytesPerHeader + negotiationParameters[i].value.v;
EXIT
ENDLOOP;
family ← Protocol1.GetFamilyUnit[ns];
IF family.maxBufferSize < maxSize THEN
Protocol1.SetMaximumBufferSize[Driver.nilDevice, family, maxSize];
END;
Negotiate: PROC RETURNS [succeed: BOOLEAN ← FALSE] = BEGIN
state: NegState ← idle;
buffer: NSBuffer.Buffer ← NIL;
body: NSBuffer.Body ← NIL;
continue: BOOLEAN ← TRUE;
time, interval: LONG CARDINAL;
-- Initial action
SELECT TRUE FROM
remote.connectionID # LOOPHOLE[unknownCID] => BEGIN
-- NewApprove case
SendPacket[paramType];
state ← waitForDecision;
END;
hints.activeEstablish => BEGIN
-- NewCreate or NewTransducer (active side) send RFC
SendPacket[negType];
state ← passiveNegotiation;
END;
ENDCASE => BEGIN
-- NewTransducer (passive side) wait RFC
state ← idle;
END;
-- Set timeout interval
timeout ← MAX[4000, timeout]; -- Argument timeout could be updated here.
interval ← IF timeout > maxMsecToPulses
THEN LAST[LONG CARDINAL]
ELSE System.MicrosecondsToPulses[timeout*1000];
time ← System.GetClockPulses[];
Socket.SetWaitTime[socket, 2000];
-- Wait for response
WHILE continue DO
buffer ← NIL;
buffer ← Socket.GetPacket[
instance.connection.socket!Socket.TimeOut => CONTINUE];
IF buffer = NIL THEN
IF System.GetClockPulses[] - time > interval THEN << Timeout >>
SIGNAL PacketStream.ConnectionFailed[timeout]
ELSE << Retry >> BEGIN
SendPacket[previousType]; LOOP;
END;
time ← System.GetClockPulses[];
body ← buffer.ns;
IF body.packetType = sequencedPacket AND body.systemPacket THEN SELECT state FROM
idle => IF body.source = instance.connection.remoteAddr THEN BEGIN
instance.connection.remoteID ← body.sourceConnectionID;
IF GetNegBit[body] THEN SendPacket[paramType] -- Send parameter
ELSE continue ← FALSE; -- Remote doesn't understand negotiation. Failed...
END;
passiveNegotiation => IF body.source.host = instance.connection.remoteAddr.host THEN BEGIN
instance.connection.remoteAddr.socket ← body.source.socket;
instance.connection.remoteID ← body.sourceConnectionID;
IF GetParamBit[body] THEN
-- If parameter is acceptable then end negotiation
IF CheckParameters[body] THEN BEGIN
continue ← FALSE;
succeed ← TRUE; --Yeah!!
SetMaxBufferSize[];
END
-- If parameter is noy acceptable then continue
-- Propose defferenct parameters
ELSE SendPacket[paramType]
ELSE continue ← FALSE; -- Remote doesn't understand negotiation. Failed...
END;
waitForDecision => BEGIN
IF GetParamBit[body] THEN
-- Remote didn't agree to the parameters. They sent their proposition.
IF CheckParameters[body] THEN BEGIN
-- We agree to their proposition
continue ← FALSE;
succeed ← TRUE; --Yeah!!
SetMaxBufferSize[];
END
-- We don't agree to their proposition. Send our new proposition.
ELSE SendPacket[paramType]
ELSE BEGIN
-- Remote agreed to the parameters
continue ← FALSE;
succeed ← TRUE; --Yeah!!
SetMaxBufferSize[];
END;
END;
ENDCASE;
NSBuffer.ReturnBuffer[buffer];
ENDLOOP;
IF remote.connectionID # LOOPHOLE[unknownCID]
THEN instance.connection.state ← established;
Socket.SetWaitTime[socket, LAST[NSTypes.WaitTime]]; --no timeouts here
END;
hops ← Router.GetDelayToNet[remote.address.net!
Router.NoTableEntryForNet => {hops ← 0; CONTINUE}];
InitializeInternal[hops, instance];
socket ← Socket.Create[
socket: local.address.socket, type: sequencedPacket, send: 16, receive: 17];
Socket.SetWaitTime[socket, LAST[NSTypes.WaitTime]]; --no timeouts here
instance.connection ← [
state: unestablished, stateBeforeSuspension: unestablished, whySuspended: notSuspended,
maxSppBytes: NSTypes.maxDataBytesPerSpp, timeout: timeout, hops: hops,
socket: socket, remoteAddr: remote.address, remoteID: remote.connectionID,
localAddr: Socket.GetAssignedAddress[socket], localID: local.connectionID,
pool: Socket.GetBufferPool[socket], whyFailed: timeout];
CollectNegotiationParams[];
IF Negotiate[] THEN ApplyNegotiationParams[];
END; --NewInitializeSppState;
-- Internal procedures
-- Assignment
-- 0: systemPacket
-- 1: sendAck
-- 2: attention
-- 3: endOfMessage
-- 4: negotiation
-- 5: parameter
negMask: UNSPECIFIED = 0008H;
paramMask: UNSPECIFIED = 0004H;
negType: [0..17B) = 8H;
paramType: [0..17B) = 4H;
nullType: [0..17B) = 0H;
GetNegBit: PROCEDURE [b: NSBuffer.Body] RETURNS [BOOLEAN] = INLINE BEGIN
RETURN[Inline.BITAND[negMask, b.unusedType]];
END;
GetParamBit: PROCEDURE [b: NSBuffer.Body] RETURNS [BOOLEAN] = INLINE BEGIN
RETURN[Inline.BITAND[paramMask, b.unusedType]];
END;
Delete: PROC[sH: Stream.Handle] =
BEGIN
--delete instance associcated with this stream
instanceN: LONG POINTER TO FRAME[NetworkStreamImpl] =
SppOps.GlobalFrameFromByteStream[sH];
psH: PacketStream.Handle = SppOps.PacketStreamFromByteStream[sH];
instanceN.stopByteStream[]; --shut down the bytestream
psH.destroy[psH]; --then the underlying packet stream
END;
END. -- NewNetworkStreamImpl
LOG
27-Oct-87 15:05:06 MXI Created for SPP negotiation.