-- 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 <> TYPE = SpecialSystem.HostNumber; ListenerHandle: PUBLIC <> TYPE = NetworkStreamInternal.ListenerHandle; ConnectionID: PUBLIC <> TYPE = NSTypes.ConnectionID; Device: PUBLIC <> 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, <> initialLocal, initialRemote, ceiling, floor, calculationInterval: CARDINAL <>] = [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.