RFC 55

Prototypical implementation of the NCP

πŸ“… published June 1970, from ietf

A Prototypical Implementation of the NCP πŸ’–

Okay bestie, so this is RFC 55, published 19 June 1970 by J. Newkirk and M. Kraley of Harvard, and J. Postel and S. Crocker of UCLA, all from the Network Working Group 🧚. The header doesn't list anything it obsoletes or updates, and it doesn't give a category or status. It's a prototype write-up, so it's basically "here's how we'd build an NCP" πŸŽ€

Intro

Okay so, while the team was trying to write the formal protocol spec, they also tried to make a prototype NCP in an Algol-like language πŸ’… After some weeks of intense effort, they dropped it because the code was getting unreadable πŸ’€ They still felt like they needed to show how an NCP could be implemented, since that would help figure out problems when you try to fit the formal specs into a real existing system 🫢 So this doc is their attempt to describe, in plain prose, what an NCP could look like ✨

Obviously this kind of project has limits πŸ™ˆ They don't, and can't, know every quirk of every system that has to run an NCP, so they had to make some assumptions about the environment, system calls, and stuff like that. They tried to stay as general as possible, but tons of sites are gonna think about the NCP totally differently 😭 Explaining their concepts and mechanisms to people who see things in completely different ways is super hard. Still, coding this for their fictional machine helped a lot, since loads of unexpected problems popped up during coding πŸ”₯ They're hoping that sharing this doc can help people dodge similar problems in their own setups 🌸

Also, there is absolutely no requirement to build anything in this doc πŸ’… The only strict rules an NCP must follow are in NWG/RFC#54. This description is just an example, not a model πŸ¦‹

Here's the roadmap ⭐ First they describe the environment they're assuming and come up with a set of system calls. Then they go over the NCP's overall architecture and the tables it uses to store info. After that come narratives of network operations 🧚 Next there's a state diagram, which is a neat way to picture the cause-and-effect order of events πŸ‘‘ Last, they go through how each kind of network event gets processed in detail. Those events are either system calls or incoming network messages πŸ’œ


II. Environment ✨

Okay so the host is assumed to have a time-sharing operating system, where the CPU gets shared between processes 🌈

Each process gets tagged with a user number πŸŽ€ More than one process can have the same user number. If they do, they're supposed to cooperate when it comes to using the network 🫢

Each process has its own set of ports πŸ’– These ports are used for input to or output from the process, and that input or output can go to or come from files, devices, or other processes πŸ¦‹

A process doesn't get put to sleep (blocked or dismissed) when it tries to LISTEN or CONNECT 😭 Instead, it gets informed when the action is complete πŸ”₯ Of course, a process can choose to dismiss itself so it only wakes up on some external event πŸ’…

To do anything on the network, a process attaches a local socket to one of its ports 🧚 Sockets are identified by user number, host, and AEN. A socket counts as local to a process when the user numbers match and they're on the same host πŸ‘‘ That means a process only needs to specify an AEN when it's talking about a local socket ✨

Every port has a status. That status gets changed by system calls and by stuff happening outside the process, like a 'close connection' command from a foreign host 🌸 The process can check a port's status at any time, using the STATUS system call πŸ’œ

We assume there's a one-to-one correspondence between ports and sockets πŸ“

IV. The NCP - Gross Structure ✨

Okay so, bestie, we see the NCP as having five component programs, several associative tables, and some queues and buffers πŸŽ€

The Component Programs (see Fig. 4.1) 🧚

1. The Input Handler

This is an interrupt-driven routine. It starts Imp-to-Host transmission into a resident buffer, and then it wakes up the input interpreter once transmission is complete πŸ”₯

2. The Output Handler

This is an interrupt-driven output routine. It starts Host-to-Imp transmission out of a resident buffer, and then it wakes up the output scheduler once transmission is complete πŸ’Œ

3. The Input Interpreter

This is the program that decides whether the input is a regular message meant for a user, a network control message, an Imp-to Host message, or an error πŸ‘€ For each class of message, it calls a subroutine to handle the right action πŸ’…

4. The Output Scheduler

Three classes of messages get sent to the Imp, and they are:

(a) Host-to-Imp messages (b) Control messages (c) Regular messages

We believe a priority should be set among these classes 🌸 The priority we suggest is the order listed above. The output scheduler picks the highest priority message and passes it along to the output handler πŸ¦‹

Host-to-Imp messages are processed first come, first served πŸ’– Control messages are processed one host at a time, with each host getting its turn πŸ“ There is a control message queue for each foreign host. When a particular host is scheduled for output, as many control commands for that host as will fit get concatenated into one single message 🫢 Regular messages are processed in groups by host and link, with each unique combination taking its turn ✨

5. The System Call Interpreter

This is the program that interprets requests from the user πŸŽ€ Each system call has its own routine that takes the appropriate action πŸ’œ

The two interesting components are the input interpreter and the system call interpreter, and honestly they're kind of twins πŸ™ˆ They're similar because the input interpreter services foreign requests, and the system call interpreter services local requests πŸ₯Ή

The diagram in Figure 4.1 is our idea of the Network Control Program 🌈 Squishy amoeba-like objects stand for component programs, cylinders stand for queues, and arrows stand for data paths 🫧 In this simplified diagram, tables are not shown.

["Amoeba-like" objects in original hand drawing are now firm rectangular boxes: Ed.]

The abbreviated labels in the figure mean the following:

HIQ       -     Host-to-Imp Queue
OCCQ      -     Output Control Command Queue
DQ        -     Data Queue
IHBUF     -     Input Handler Buffer
OHBUF     -     Output Handler Buffer
 ____________
|    USER    |    STRUCTURE OF THE NETWORK CONTROL PROGRAM
|____________|
   ^      |                      Fig. 4.1
      _____|______V____
     |                 |
     |     System      |
     |      Call       |
     |   Interpreter   |
     |_________________|              _____________
        ^  |      |                  |             |
        |  |      |  +---------------|    Input    |
        |  |      |  |         +-----| Interpreter |
        |  |      |  |         |     |             |
        |  V      V  V         V      -------------
      |======| |=========| |=======|     |      ^
      | D Q  | | O C C Q | | H I Q |     |      |
      |======| |=========| |=======|     |      |
        |  ^        |          |         |      |
        |  |        |          |         |      |
        |  +--------)----------)---------+      |
        |           |          |                |
        +-------+   |   +------+                |
              __V___V___V__                     |
             |             |                    |
             |   Output    |                    |
             |  Scheduler  |                    |
             |_____________|                    |
                    |                           |
                    V                           |
              (===========)               (===========)
              ( O H B U F )               ( I H B U F )
              (===========)               (===========)
                    |                           ^
              ______V______               ______|______
             |             |             |             |
             |   Output    |             |    Input    |
             |   Handler   |             |   Handler   |
             |             |             |             |
              -------------               -------------
                    |                           ^
                    |                           |
                    +----------+    +-----------+
                               |    |
                           ____V____|____
                          |              |
                          |     I M P    |
                          |______________|

V. Tables in the NCP πŸ’œ

We picture the bulk of the NCP's data base living in associative tables πŸ—‚οΈ By "associative," we mean there's some lookup routine that gets handed a key, and then it either returns successfully with a pointer to the matching entry, or it fails if no entry matches the key πŸ” The major tables are as follows:

1. The Rendezvous Table

This table holds the attributes of a connection πŸ’« The table is accessed by the local socket, but other tables may have pointers to existing entries πŸŽ€

The components of an entry are:

(a) Local Socket (b) Foreign Socket (c) Link (d) Connection State (e) Flow State (f) Data Queue (g) Call Queue (h) Port Pointer (i) Their Buffer Size (only needed on the send side) (j) Error State

An entry gets created when either a CONNECT or a LISTEN system call is executed, or when a request for connection is received πŸ¦‹ Lots of fields stay unused until after the connection is established πŸ’€

The input interpreter uses the concatenation of the foreign host and link as a key into the input table πŸ”‘ This table gets used when processing a user-destined message on an incoming link, and it does that by providing a pointer into the rendezvous table 🌸

The input interpreter uses the output link table to access the flow state as RFNM's return from transmitted messages πŸ“¬ The output link table is keyed by host and link, and it provides a pointer into the rendezvous table πŸ’–

4. The Port Table

The system call interpreter uses the concatenation of the process identification and the port identification as a key, so it can get a pointer into the rendezvous table 🧚

5. The Output Control Command Table

The system call interpreter and the input interpreter both use this table to make entries in the right output control command queues 🫢 Commands get queued in separate table entries that match foreign hosts πŸ“ Before output, the contents of the queue get concatenated into one large control message πŸ’… The components of an entry are:

(a) Host (b) Output Control Command Queue

6. The Output Request Queue

This queue has an entry for each connection that has data needing transmission to the net ✨ There's only one entry per connection. It gets deleted when the last packet of data is transmitted, and it gets entered whenever a user makes a system request for data transmission πŸŽ€

The entry gets re-inserted if transmission isn't completed (message too long) or if it's prevented by the flow control mechanism πŸ™ˆ The only component of an entry is a local socket 🌈

7. The Host Live Table

This is a simple table that lists the hosts which are alive πŸ’œ It gets checked before establishing a connection and before sending any data, just to make sure the destination host actually exists πŸ”₯ At present, the protocol does not define the procedure to be followed for the Host up/Host down conditions. See NWG/RFC#57 πŸ¦‹

Link numbers get assigned by the receiver 🌸 This table records which links are free and can, therefore, be assigned πŸ’–

VI. Informal Description of Network Operations ✨

okay so this section is basically the story of a connection's life πŸ¦‹ we're gonna walk through the three big phases of network usage: opening, flow control, and closing. let's go bestie πŸ’…

A. Opening

okay so to set up a connection for data, you need a pair of RFCs, no cap πŸ’– an RTS has to go from the receive-side to the send-side, and the send-side has to issue an STR back to the receive-side. the receive-side's RTS also has to name a link number πŸŽ€ these RFCs (RFC is the umbrella term for RTS and STR) can show up in any time sequence, honestly it's giving chaos but in a good way ✨ there also has to be a spot for queuing pending calls (aka RFCs the user program hasn't dealt with yet). so when a user is done with a connection, they can check the next pending call from another process and either accept or refuse it 🫢

the tea is that the user might never bother checking their pending calls 😭 so those calls just sit there taking up queue space in the NCP. we'll talk about some fixes for this later, promise 🧚

using the prototype system calls from earlier, we can picture at least four different timelines that end with a successfully opened connection 🌈

  1. the user issues a LISTEN, which basically means "i'm down to chat with anyone who sends me an RFC." when one comes in, the user gets notified. then they decide whether they want to connect to this socket and issue either an ACCEPT or a CLOSE depending on that choice. a CLOSE here "refuses" the connection (more on that under "Closing" πŸ™ˆ). an ACCEPT means yes, they're willing to connect, an RFC gets issued, and the connection becomes fully opened πŸ‘‘
  2. when the NCP handles a user's LISTEN request, it finds that a pending call already exists for that local socket. the user gets notified right away and can ACCEPT or CLOSE, same as before πŸ’œ
  3. the user issues a CONNECT, naming a specific foreign socket they'd like to reach. an RFC gets issued. if the foreign process accepts, it replies with an RFC. once that acknowledging RFC arrives, the connection is opened πŸ“
  4. when the NCP gets a CONNECT, it might discover that a pending call already exists from that specified foreign socket to this local socket. then an acknowledging RFC is issued and the connection is opened ⭐

no matter which of these happens, the user is notified when the connection opens πŸŽ€ BUT data flow can't start until buffer space gets allocated and an ALL command is transmitted. so close, but not yet πŸ₯Ί

any of these connection scenarios can get interrupted if a CLS comes in, which we'll cover under "Closing" πŸ’€

1. Pending Call Queues

some form of queuing for pending RFCs is essential, periodt πŸ’… the easiest way to see why is to look at a typical LISTEN-CONNECT sequence. one side issues a LISTEN and the other side issues a CONNECT. if the LISTEN happens before the RFC from the remote CONNECT shows up, everything is fine ✨ but the net is asynchronous, so we can never guarantee that order. if calls aren't queued, and the RFC arrives before the LISTEN is issued, it gets refused. if it arrives later, it gets accepted. so yeah, it's a super ambiguous situation 😭

unless you have infinite queue space (which, same), it's desirable to have some way to purge old RFCs that the user never got around to examining 🧹 an obvious but informal approach is to note the time each RFC enters the queue, then periodically refuse every RFC that has exceeded some arbitrary time limit. another idea, which should probably be part of any scheme, is for the NCP to send a CLS on all outstanding connections or pending calls when a user logs out or blows up πŸ’€

the scheme in this description might seem non-intuitive at first blush πŸ™ˆ but we think it's more realistic than other proposals, not gonna lie. basically, when a CONNECT is issued, the NCP assumes this socket wants to talk to the specified foreign socket and only that socket. so it purges every non-matching RFC from the pending call queue by sending back CLSs 🫢 similarly, when the connection is in the RFC-SEND state (a CONNECT has been issued), all non-matching RFCs get refused. if a LISTEN-ACCEPT or LISTEN-CLOSE sequence runs, the rest of the pending calls stay in the queue, since the user might want to accept those requests later πŸ¦‹

yes, this method might look arbitrary and/or unnecessarily restrictive. but we haven't come up with a scenario it would block, assuming we're dealing with a competent programmer (someone who is wary of race conditions and the asynchronous nature of the net) πŸ‘‘ of course, whatever scheme a particular site picks is highly implementation dependent 🌸 we do suggest that some provision for queuing RFCs be made for a period at least on the order of magnitude that they're retained in the CONNECT-clear scheme mentioned above ✨

B. Flow Control

meaningful data can only flow on a connection when it's fully opened (meaning two RFCs have been exchanged and closing hasn't started) 🌈 we assume the NCPs have a buffer for incoming data, and that there's some meaningful quantity they can advertise (per connection) saying what size message they can handle πŸŽ€ we also assume the sending side regulates its transmission according to those advertisements πŸ’–

when a connection opens, a cell called Their Size gets set to zero 🧚 the receive-side decides how much space it can allocate and sends an ALL message specifying that space. the send-side increments Their Size by the allocated space, and then it can send messages with length less than or equal to Their Size. when messages get transmitted, the message length is subtracted from Their Size πŸ“ when the receive-side allocates more buffer space (for example, when a user takes a message and frees up some system buffer space), the number of bits released gets sent to the send-side in an ALL message 🫢

so Their Size is never allowed to go negative, and no transmission can happen when Their Size equals zero πŸ™ˆ

notice that the lengths in ALL messages are increments, not the absolute size of the receiving buffer πŸ’… this is necessitated by the full duplex nature of the flow control protocol πŸ¦‹ the length field of the ALL message can be 32 bits long (note: it's an unsigned integer), which gives you essentially an infinite "bit sink" if that's ever desired 🌈✨

C. Closing

just like two RFCs are required to open a connection, two CLSs are required to close one πŸ’œ closing happens under different circumstances and serves several purposes. to make the race condition analysis simpler, we split it into four cases: aborting, refusing, termination by receiver, and termination by sender 🌸

a user "aborts" a connection when they issue a CONNECT and then a CLOSE before the CONNECT gets acknowledged 😭 usually a user aborts after waiting a super long time for the acknowledgment. their system might also abort for them if they blow up πŸ’€

a user "refuses" a connection when they issue a LISTEN, get notified about a prospective caller, and then issue a CLOSE πŸ™…β€β™€οΈ any requests to connect to a socket that's expecting a call from a particular socket are also refused 🫢

after a connection is established, either side can terminate πŸ¦‹ the required sequence of events suggests that CLOSE attempts from the receive-side should be treated as "requests," which the send-side always honors as soon as possible πŸ’– any data that hasn't been passed to the user yet, or that's still traveling over the network, gets discarded πŸ—‘οΈ CLOSE requests from the send-side are honored as soon as all data transmission is complete ✨

1. Aborting

we can tell apart three cases here πŸ‘€

  • a) the simplest case: we send an RFC, then later a CLS. the other side responds with a CLS, and the connection attempt ends πŸ’…
  • b) the foreign process might accept the connection at the same time the local process aborts it. in this case, the foreign process will think the local process is terminating an open connection 😳
  • c) the foreign process might refuse the connection at the same time the local process aborts it. in this case, the foreign process will think the local process is acknowledging its refusal πŸ™ˆ

2. Refusing

after an RFC is received, the local host can respond with an RFC or a CLS, or it can fail to respond at all 🧚 (the local host may have already sent its own RFC, etc.) if the local host sends a CLS, it's said to be "refusing" the connection request πŸ’œ

we require that CLS commands get exchanged to close a connection, so the local host has to keep the rendezvous table entry until an acknowledging CLS comes back πŸŽ€

3. Terminating by the Sender

when the user on the send side issues a CLOSE system call, their NCP must accept it immediately πŸ’– but it may not send out a CLS command until all the data in the local buffers has been passed to the foreign host 🫢 so it's necessary to test for both buffer-empty and RFNM-received before sending the CLS command 🌈 as usual, the CLS has to be acknowledged before the entry can be deleted πŸ“

4. Terminating by the Receiver

when the user on the receive side issues a CLOSE system call, their NCP accepts it and sends the CLS command immediately ⭐ data might still arrive though, and that data should be discarded πŸ—‘οΈ the send side, upon receiving the CLS, should immediately stop the data flow πŸ‘‘


VII. Connection Status πŸŽ€

okay so this part is literally my favorite, bestie πŸ¦‹ an excellent way to describe how a connection gets set up and torn down is a state diagram! πŸ’… we pretend every socket has its own little state machine, and at any moment that machine can be in one of ten possible states. 🌸 in each state, certain network events push the connection into a new state, some events just get ignored πŸ™ˆ, and some are straight up errors πŸ’€. a transition can also make the local NCP do something, so it's not just vibes, it's actions too ✨

Figure 7.1 is the whole state machine. the boxes are the states (ed changed them from circles to boxes, iykyk 🧚), and the arrows are the legal transitions between them. each arrow label is the event that caused it. ngl the labels are a little tricky: CLOSE is a system call, but CLS is a control command, so don't mix them up 🫢 phrases after slashes tell you what should be done while you're traveling that arrow. the arrow labeled [E]RFC (between states 0 and 1) means that whenever a connection goes into the CLOSED state, the pending call queue for that connection gets checked. (the original had that "E" backwards, and ed fixed it, so it's [E]RFC now πŸ“)

if there are any pending calls in the queue, the connection moves to PENDING. and if an RFC shows up for a socket in the CLOSED state, it takes the same path over to PENDING too πŸ’– the events and what they trigger are covered in sections VIII and IX, so stay tuned, bestie πŸ‘‘

Here are the ten states, the main character lineup πŸ’œ

  • (0) CLOSED 🌈

The local socket isn't attached to any port, and no user has asked for a connection with it. (The table entry doesn't exist at all.)

  • (1) PENDING CALL ⭐

The socket isn't attached to any port, but one or more connection requests have come in. A LISTEN system call gets satisfied right away by the first entry in the pending call queue that matches a request. Every other pending call gets deleted πŸ’€

  • (2) LISTENING πŸ¦‹

The socket is attached to a port, and we're waiting for a user to request a connection with it. 🫢

  • (3) RFC-RCVD πŸ”₯

We're listening and an RFC came in. The local user has been told about the pending call, and they have to answer with either a CLOSE or an ACCEPT. no other options, bestie πŸ’…

  • (4) ABORT 😭

We told the user their LISTEN got satisfied, but they haven't responded yet. If the foreign user aborts the connection during this time by sending a CLS, we send a CLS back to acknowledge the abort, and we mark it with this state. Once the user accepts or refuses the call, we can let them know the connection got terminated early πŸ₯€

  • (5) RFC-SENT πŸš€

this state gets entered when:

  • a) the local user has attached this socket to a port by issuing a CONNECT,
  • b) an RFC has been sent, and
  • c) no reply has come back yet.

when the user issues a CONNECT, the pending call queue gets searched πŸ”

if there's no matching RFC in the queue, the queue gets deleted and we enter this state. as new RFCs arrive, they get compared with our user's request. if they don't match, the RFC is immediately refused πŸ™…β€β™€οΈ if the RFC does match, it finishes the initialization process, and the connection enters the OPEN state. slay πŸ‘‘

  • (6) OPEN 🌸

RFCs have been exchanged, and the connection is securely established ✨ transmission can start after we receive an ALL command from the receive side, and then it proceeds subject to flow control. πŸŽ€

  • (7) CLS-WAIT 🧚

after the local user executes a CLOSE and we've issued a CLS, we have to wait for an acknowledging CLS before the connection can be fully closed. if that appropriate CLS hasn't already arrived, this state gets entered πŸ’œ

  • (8) DATA-WAIT πŸ“

if we're on the send side and the local user runs a CLOSE system call, we can't issue a CLS if our data buffer isn't empty, or if an RFNM for the last data message is still outstanding. so the connection enters this state to wait for those conditions to be met. once the output is completed and acknowledged, a CLS may be issued, and the connection enters the CLS-WAIT state to wait for the acknowledging CLS. 🫢

and if a CLS shows up while we're in DATA-WAIT, we clear our buffer (the CLS came from a receive socket, which means it's no longer interested in our data) and enter the RFNM-WAIT state to wait for the network to clear 🌊

  • (9) RFNM-WAIT πŸ’€

if we're on the send side and a CLS command arrives, we can't issue an acknowledging CLS until we've received the RFNM for our last data message. we enter this state to wait for that RFNM, and we stop all further data transmission. when the RFNM comes in, a CLS may then be issued, and the connection gets closed πŸ”’

Figure 7.1 below is the full diagram, so you can see every one of these paths together 🌈

                  ______________
                 |              |       CLOSE
  CONN/          |    CLOSED    |<---------------------------+
  send RFC       |     (0)      |       LISTEN               |
+----------------|              |-----------------------+    |
|                |______________|                       |    |
|                     |    ^                            |    |
|              [E]RFC |    |  CLS/send CLS              |    |
|                  ___V____|____                     ___V____|____
|  non-matching   |             |                   |             |
|  CONN/send RFC  |   PENDING   | LISTEN        RFC |  LISTENING  |
|   +-------------|    (1)      |----------+   +----|     (2)     |
|   |             |_____________|          |   |    |_____________|
|   |       matching     |                 |   |
___V___V_____  CONN/send RFC|               __V___V______
|             |              |     ACCEPT/  |             | CLS/
|   RFC-SENT  | RFC          |     send RFC |   RFC-RECD  | send CLS
|     (5)     |----------+   |   +----------|     (3)     |---------+
|_____________|          |   |   |          |_____________|         |
   |   |                 |   |   |               |                  |
   |   |              ___V___V___V___  SND&CLOSE |   ____________   |
   |   |    RCV&CLS/ |               |-----------)->|            |  |
   |   |    send CLS |      OPEN     | SND&CLS   |  |  DATA-WAIT |  |
   |   |   +---------|      (6)      |--------+  |  |    (8)     |  |
   |   |   |         |_______________|        |  |  |____________|  |
   |   |   |      RCV&CLOSE/ |                |  |   |              |
   |   |   |       send CLS  |                |  |   |              |
   |   |   |                 |                |  |   | CLS          |
   |   |   |           ______V______          |  |   |              |
   |   |   |   CLOSE/ |             |CLOSE/   |  |   |              |
   |   |   |  send CLS|   CLS-WAIT  |send CLS |  |   |              |
   |   +---)--------->|     (8)     |<--------)--+   |              |
   |       |          |_____________|         |      |              |
   |       |                 |             ___V______V_       ______V___
   |       |                 |            |            |     |          |
   |       |                 |            |  RFNM-WAIT |     |   ABORT  |
   |       |             CLS |            |     (9)    |     |    (4)   |
   |       |                 |            |____________|     |__________|
   |       |                 |                   |                 |
   |       |           ______V_______  RFNM/     |                 |
   |       |          |              | send CLS  |                 |
   |  CLS/ +--------->|    CLOSED    |<----------+                 |
   | send CLS         |     (0)      |                ACCEPT|CLOSE |
   +----------------->|              |<----------------------------+
                      |______________|

                         Figure 7.1
                  Connection State Diagram

VIII. Algorithms for the Input Interpreter ✨

Okay bestie, this is where the NCP decides what to do when a network command slides into its DMs πŸ’Œ. CS is just Connection State, fr. And heads up: CLOSE is the system call the local user runs, while CLS is the network command. Don't mix them up πŸ™ˆ

NOP

Discard. πŸ—‘οΈ

RFC (RTS or STR)

Okay so when an RFC shows up:

  • If no entry exists, create one with status = PENDING CALL, and queue the message.
  • If CS = LISTENING, then queue the entry, enter the RFC-RCVD state, and inform the user of the request. πŸ“¬
  • If CS = RFC-SENT but the new RFC does not match the request, refuse the RFC. πŸ™…β€β™€οΈ
  • In all other cases, check the RFC for a match. If none exists, queue the RFC. If the RFC matches, then if:
    • CS = RFC-SENT, we enter the OPEN state. πŸŽ‰
    • CS = CLOSE-WAIT, the RFC is ignored.
    • otherwise, the request is illegal in all states which indicate it has already been received (these states are 1,3,4,6,8,9). πŸ’€
  • In any case, if processing the RFC causes an overflow condition (resources are exhausted), refuse the connection (send a CLS). 😭

CLS

The pending call queue gets searched first. If the CLS doesn't match the current request but does match some other request, then delete that request and issue a CLS. If there's no match at all, the CLS gets ignored 🫠.

If the CLS matches the current request, here's the tea depending on CS:

  • PENDING: delete the current request. If the request queue is empty, delete the entry; otherwise, leave the entry alone.
  • RFC-RCVD: issue a CLS and enter the ABORT state.
  • ABORT: ignore.
  • RFC-SENT: issue a CLS. If the pending call queue is empty, delete the entry, else enter the PENDING state.
  • OPEN: if we are on the receive side, the response is identical to the response for RFC-SENT. If we are on the send side, clear the data queue, and if a RFNM is still pending, enter the RFNM-WAIT state. Otherwise the response is identical to the response for RFC-SENT. 🌊
  • CLS-WAIT: issue a CLS, and if the pending call queue is empty, delete the entry, otherwise CS = PENDING.
  • DATA-WAIT: clear the data queue and enter the RFNM-WAIT state.

A matching CLS cannot occur in the CLOSED or LISTENING states. Not possible, bestie πŸ™…β€β™€οΈ

ERR

Errors get queued up for later attention by system programmers, and they're treated as a system error in the host that originated the exchange. This one isn't tied to any state at all 🧾.

ECO

The op code gets changed to ERP and retransmitted. Also not tied to any state πŸ”.

ERP

When an ERP arrives, the system passes the text of the command back to the process that issued the ECO. Full circle moment πŸ¦‹.

INR, INS

These commands are only enabled in the OPEN state. When an INTERRUPT comes in, the system sends an event to the associated process. An INTERRUPT is ignored in the CLS-WAIT, DATA-WAIT, and RFNM-WAIT states. In any other state it's an error ⚑.

ALL

ALLOCATE is only valid in the OPEN state, and it may be sent only to a send socket. The NCP increments the 'Their Size' field in the associated rendezvous table entry by the size specified in the ALLOCATE command πŸ“ˆ.

In the CLS-WAIT and DATA-WAIT states this command gets ignored. In any other state it's an error πŸ™ƒ.

Data-RFNM

  • If in the OPEN state, mark the Flow Control Status field in the appropriate rendezvous table entry as RFNM-RECVD, and send more data if required. πŸ“¨
  • If in the DATA-WAIT state, maintain the Flow Control Status. If the data queue is empty, issue a CLS and enter the CLS-WAIT state; otherwise, transmit the next message. πŸš€
  • If in the RFNM-WAIT state, maintain the Flow Control Status and issue a CLS. If the Pending Call queue is empty, delete the rendezvous table entry, otherwise CS = PENDING.

A Data-RFNM is an error in all other states 🚨.


IX. Algorithms for the System Call Interpreter πŸ’…

Each System Call gets its own moment, and the state changes it may effect are listed right under it 🌸.

CONNECT

  • If there is no entry, create one, issue an RFC, and enter the RFC-SENT state. πŸ“ž
  • If CS = PENDING, search the queue and reject all non-matching requests. If no match is found, issue an RFC and enter the RFC-SENT state. If a match is found, issue an RFC and enter the OPEN state. Transmission can commence as soon as buffer space has been allocated. ✨
  • In any other state this command is illegal.

LISTEN

  • If an entry doesn't exist, create one, and enter the LISTENING state. πŸ‘‚
  • If CS = PENDING, inform the user and enter the RFC-RCVD state.
  • In any other state this command is illegal.

ACCEPT

  • If CS = RFC-RCVD, then issue an RFC and enter the OPEN state. Data transmission can occur as soon as buffer space is allocated. πŸ₯³
  • If CS = ABORT, inform the user of the premature termination of the connection. If the pending call queue is empty, delete the entry; otherwise, enter the PENDING state. πŸ’”
  • This command cannot be legally executed in any other state.

CLOSE

If CS =:

  • LISTENING, then delete the entry.
  • RFC-RCVD, then issue a CLS and enter the CLS-WAIT state.
  • ABORT, inform the user of the premature termination of the connection. If the pending call queue is empty, delete the entry; otherwise, enter the PENDING state.
  • RFC-SENT, then issue a CLS and enter the CLS-WAIT state.
  • OPEN, if we are on the send side, and the data queue is not empty, or if a Data-RFNM is still outstanding, enter the DATA-WAIT state; otherwise, issue a CLS and enter the CLS-WAIT state. 🧹
  • CLS-WAIT, issuing a CLOSE in this state is a USER ERROR. 🚫
  • DATA-WAIT, issuing a CLOSE in this state is also an illegal sequence.
  • RFNM-WAIT, ignore the CLOSE.

A valid CLOSE cannot be issued if an entry does not exist, or if a socket is in the PENDING state. Main character energy, but only when the timing is right πŸ‘‘πŸŽ€

           [ This RFC was put into machine readable form for entry   ]
           [ into the online RFC archives by Anthony Anderberg 5/00 ]
πŸ“¦ where this page comes from