RFC 969
NETBLT: A bulk data transfer protocol
RFC 969 ๐ฎ NETBLT: A Bulk Data Transfer Protocol ๐ฆ
okay so bestie, this is the very first part of a four-part rewrite of RFC 969, which is the NETBLT spec from December 1985. NETBLT is a bulk data transfer protocol ๐. It's written by David D. Clark, Mark L. Lambert, and Lixia Zhang from the M.I.T. Laboratory for Computer Science, and it's an experimental proposal, not a standard, so it's giving "draft we're still cooking" energy ๐ณ. It's a Network Working Group document, and the title block says it doesn't obsolete or update anything, so it's a fresh start โจ.
Heads up: the title block does not say anything about this being a standard or what category it's in beyond the status section below, so we'll stick with what the status section says ๐ .
1. STATUS OF THIS MEMO ๐
Okay so this part is basically the disclaimer, and it's important. This RFC suggests a proposed protocol for the ARPA-Internet community and asks for discussion and suggestions for improvements ๐ฌ. It's a preliminary discussion of NETBLT, published for discussion and comment, and it does not constitute a standard ๐ โโ๏ธ. Since the proposal may change, implementing this document is NOT ADVISED ๐ซ. Distribution of this memo is unlimited ๐.
Quick reminder: "not advised" here is the vibe, not a BCP 14 key word, so nothing in this doc is a hard requirement yet. Don't build your whole life on it ๐ญ.
2. INTRODUCTION ๐
2.1. What NETBLT Is
Okay so NETBLT stands for Network Block Transfer ๐งฑ, and it's a transport level protocol built for rapidly moving a LOT of data between computers ๐จ. The big flex is that it's reliable and flow controlled, and it's structured to squeeze out maximum throughput across a wide variety of networks ๐๏ธ๐.
2.2. How a Connection Goes
So the protocol opens a connection between two clients (the sender and the receiver) ๐ค, transfers the data as a series of big chunks called buffers ๐ฆ, and then closes the connection ๐.
Here's the thing that's lowkey genius ๐ง : the amount of data can be arbitrarily large, so the client does not have to hand over everything at once ๐ฎ. Instead, the client gives the protocol one buffer at a time ๐ซถ. NETBLT then sends each buffer as a sequence of packets. Since every buffer has a LOT of packets in it, the back-and-forth between NETBLT and its client happens per buffer, not per packet, and that's way more efficient ๐ธ.
2.3. The Simplest Version
Okay so in its simplest form, here's how a transfer goes ๐ฌ:
- The sending client loads a buffer of data and calls down to the NETBLT layer to transfer it ๐ค.
- The sending NETBLT layer breaks the buffer into packets and sends them across the network in Internet datagrams ๐.
- The receiving NETBLT layer loads those packets into a matching buffer that the receiving client provided ๐ฅ.
- When the last packet in the buffer has been transmitted, the receiving NETBLT checks that every packet in that buffer arrived ๐.
- If some packets are missing, the receiving NETBLT asks for them to be resent ๐.
- Once the whole buffer has arrived, the receiving client gets notified by its NETBLT layer ๐.
- The receiving client disposes of the buffer and gives a new one to receive more data ๐งบ.
- The receiving NETBLT tells the sender that the buffer arrived, and the sender prepares and sends the next buffer the same way ๐.
- This keeps going until every buffer has been sent, and then the sender tells the receiver the transmission is complete ๐.
- The connection closes ๐ค.
2.4. Lock-Step vs. Multiple Buffering
Okay so as described above, NETBLT is "lock-step" ๐. Action halts after a buffer is transmitted and only starts again once the receiver confirms it got the data ๐. But NETBLT also provides multiple buffering, where several buffers can be transmitted concurrently ๐. This makes packet flow essentially continuous and can improve performance markedly ๐โจ. Literally the glow-up ๐ .
2.5. What's Coming Up
The rest of the document goes deep on NETBLT ๐ค. The next sections explain the philosophy behind a bunch of features: packetization, flow control, reliability, and connection management ๐งฉ. The final sections cover the protocol format ๐.
3. BUFFERS AND PACKETS ๐ฆ๐ง
3.1. Transfer Size
Okay so NETBLT is designed to move an essentially arbitrary amount of data between two clients ๐. During connection setup, the sending NETBLT can optionally tell the receiving NETBLT the transfer size ๐. The maximum transfer length is set by the field width and is 2**32 bytes ๐ฏ. The original doc says that limit should permit any practical application, and honestly, same ๐.
The transfer size is for the receiving client to use, and the receiving NETBLT doesn't use it at all ๐. A NETBLT receiver just keeps accepting data until the sender says the transfer is complete ๐๏ธ.
3.2. Buffer Size
The client is the one who breaks its data into buffers ๐ช. Each buffer must be the same size, except for the last buffer ๐ฏ. During connection setup, the sending and receiving NETBLTs negotiate the buffer size, based on limits the clients provide ๐ค. Buffer sizes are measured in bytes only, so the client is responsible for splitting data on byte boundaries ๐งฎ.
NETBLT is designed, and should be implemented, to work with buffers of arbitrary size ๐. The only real limit on buffer size should be how much memory the client has ๐ง . Buffers should be as large as possible, because that means fewer buffer transmissions and better performance ๐.
3.3. Memory Use
NETBLT is designed to need as little of its own memory as possible, so the client can give as much memory as possible to buffer storage ๐. In particular, NETBLT does not keep buffer copies for retransmission ๐ โโ๏ธ. Instead, data that needs retransmitting gets recopied straight from the client's buffer ๐. The tradeoff is that the client cannot release buffer storage piece by piece while the buffer is being sent ๐ฌ. The original says this hasn't been a problem in the preliminary implementations, so we're doing okay ๐ซถ.
3.4. Packets
Buffers get broken down by the NETBLT layer into sequences of DATA packets ๐ง. Just like the buffer size, the packet size is negotiated between the sending and receiving NETBLTs during connection setup ๐ค. The difference is that packet size is only visible to the NETBLT layer, not the client ๐.
All DATA packets, except the last packet in a buffer, must be the same size ๐. Packets should be as large as possible, because in most cases (including the preliminary implementation) performance is directly tied to packet size ๐๏ธโโ๏ธ. At the same time, packets should not be so large that they cause Internet fragmentation, since that normally hurts performance ๐ โโ๏ธ๐.
3.5. The Last Buffer and the LDATA Packet
Okay so all buffers except the last one must be the same size, and the last buffer can be any size needed to finish the transfer ๐. The receiving NETBLT doesn't know the transfer size ahead of time, so it needs a way to spot the last packet in each buffer ๐. That's why the last packet of every buffer is not a DATA packet but an LDATA packet ๐. DATA and LDATA packets are identical except for the packet type ๐ฅฐ.
Main character moment: the LDATA packet is the receiver's little signpost for "this is where the buffer ends" ๐ชงโจ.
Part 1 of 4 is done bestie ๐. Parts 2, 3, and 4 are coming next, so stay tuned ๐๐ฆ.
4. FLOW CONTROL โจ
okay bestie, so NETBLT has two flow control strategies ๐ one lives inside the NETBLT layer and one lives at the client level ๐
The sending and receiving NETBLTs move data in buffers, so client flow control happens at the buffer level ๐ง Before a buffer can go out, NETBLT checks that both clients have set up matching buffers, that one side is ready to send, and that the other side is ready to receive ๐ซถ That means either client can control the flow just by not handing over a new buffer. But once a buffer transfer has started, clients can't stop it ๐ญ
Buffers can be huge, so there has to be a different flow control method that runs during a buffer transfer. That's the one the NETBLT layer provides ๐ฆ
There are a few flow control problems that can pop up while a buffer is being sent ๐ If the sending NETBLT is blasting data faster than the receiving NETBLT can process it, the receiver's space for holding unprocessed packets can overflow and packets get lost ๐ฑ A slow gateway or an in-between network can do the same thing: packets pile up and overflow the network's packet buffer space. Then packets get lost inside the network and performance tanks ๐ This is extra intense for NETBLT because its buffers are usually big, which means they're made of tons of packets ๐
The classic answer to packet flow control is a window system, where the sender is only allowed to send a certain number of packets at a time ๐ธ The tea though: windows usually give you low throughput ๐ค They have to stay small so they don't overflow hosts and gateways, and they're hard to update because every change needs a full end-to-end exchange ๐
So to get high throughput across all kinds of networks and gateways at different speeds, NETBLT uses a totally new method called rate control ๐ The transmission rate gets negotiated by the sending and receiving NETBLTs during connection setup and again after each buffer transmission. The sender uses its own timers to keep to that rate, not messages from the receiver โญ
In its simplest form, rate control sets a minimum amount of time between each packet ๐ฅบ That causes performance problems for two reasons. First, one packet takes such a tiny amount of time to transmit that it's often smaller than the granularity of the timing mechanism ๐ Second, keeping timers running for every single packet has a lot of overhead, which slows things down ๐ฉ
The fix is to control the rate of groups of packets instead of single ones ๐ The sender sends a burst of packets over a negotiated interval, then sends another burst. This way the overhead drops by a factor of the burst size, and the per-burst rate is big enough for the timing mechanism to actually work properly โจ So NETBLT's rate control has two parts, a burst size and a burst rate, and (burst size)/(burst rate) equals the average transmission rate per packet ๐ง
The burst size and burst rate should be based on more than just how fast each end can transmit and process packets. They also need to account for the capacities of the gateways and networks sitting in between ๐ Here are some intuitive values for packet size, buffer size, burst size, and burst rate ๐
Packet sizes can be as small as 128 bytes ๐ Performance with packets that tiny is almost always bad because the per-packet processing overhead is so high ๐ญ Even the default Internet Protocol packet size of 576 bytes is barely big enough for decent performance ๐ฌ Most networks don't support packets much bigger than one or two thousand bytes, and packets that size can also get fragmented while they travel over intermediate networks, which hurts performance too ๐
The size of a NETBLT buffer is only limited by how much memory a client has ๐ง In theory, buffers of 100K bytes or more are possible ๐คฏ That would mean sending 50 to 100 packets per buffer ๐
The burst size and burst rate are super machine dependent ๐ The sending and receiving machines have some transmission overhead from maintaining timers and scheduling processes โฐ That overhead gets smaller when you send packets in large bursts ๐ธ Burst size is also limited by how many packet buffers are available โจ On most modern operating systems, a burst size of five to ten packets should bring the overhead down to an acceptable level ๐ซถ Fun fact: a preliminary NETBLT implementation for the IBM PC/AT sends packets in bursts of five ๐ It could send more, but it's limited by available memory ๐ฉ
The burst rate is partly determined by the granularity of the sender's timing mechanism, and partly by the processing speed of the receiver and any intermediate gateways ๐ฆ It's also directly related to the burst size ๐ Burst rates from 60 to 100 milliseconds were tried on the preliminary NETBLT implementation and gave good results within a single local-area network ๐ That value obviously depends on the network bandwidth and the packet buffering available ๐ง
All NETBLT flow control parameters (packet size, buffer size, burst size, and burst rate) get negotiated during connection setup ๐ค The negotiation works the same way for every parameter. The client that starts the connection (the active end) proposes a set of values for each parameter and sends them along with its open connection request ๐ The other client (the passive end) compares those values with the highest-performance values it can support ๐ The passive end can change any parameter, but only to make it more restrictive ๐ โโ๏ธ The modified parameters go back to the active end in the response message ๐
On top of that, the burst size and burst rate can be re-negotiated after each buffer transmission, so the transfer rate can adjust based on how the previous buffer performed ๐ The receiving end sends a pair of burst size and burst rate values in the OK message ๐ The sender compares those with the values it can support and, again, can only change them to be more restrictive ๐ The modified parameters are then communicated to the receiver in a NULL-ACK packet, which gets described later ๐
Real talk: all of these parameters depend on a ton of factors like gateway and host processing speeds, available memory, and timer granularity ๐คฏ Some of those can't be checked by either client ๐ฌ So each client has to make the best guess it can and tune for performance on later transfers ๐ฆ Learning era, bestie ๐
5. RELIABILITY ๐ก๏ธ
Each NETBLT transfer has three stages: connection setup, data transfer, and connection close ๐ซ Every stage has to finish reliably, and the ways to make that happen are described below ๐ซถ
5.1. Connection Setup
A NETBLT connection gets set up with an exchange of two packets between the active client and the passive client ๐ค Heads up, either client can send or receive data ๐
The words "active" and "passive" are just there to tell apart the client that starts the connection process from the client responding to the connection request ๐ The first packet sent is an OPEN packet, and the passive end acknowledges it by sending a RESPONSE packet โจ Once those two packets have been swapped, the transfer can begin ๐
Like we talked about in the last section, the OPEN and RESPONSE packets are used to negotiate flow control parameters ๐ง Other parameters used in moving data also get negotiated here ๐ธ Those are (1) the maximum number of buffers that can be sending at any one time (this allows multiple buffering and higher throughput) and (2) whether or not DATA/LDATA packet data gets checksummed ๐ NETBLT automatically checksums all non-DATA/LDATA packets ๐ฏ If the negotiated checksum flag is set to TRUE (1), then both the header and the data of a DATA/LDATA packet get checksummed ๐ If it's set to FALSE (0), only the header gets checksummed ๐ค NETBLT uses the same checksumming algorithm that TCP uses ๐ง
Last but not least, each end sends its death-timeout value in either the OPEN or the RESPONSE packet โณ That value decides how often KEEPALIVE packets get sent during idle periods of an open connection ๐ (Death timers and KEEPALIVE packets are covered in the next section, so stay tuned ๐)
The active end picks out a passive client using a client-specific "well-known" 16 bit port number that the passive end listens on ๐ฎ The active end identifies itself with a 32 bit Internet address and a 16 bit port number ๐ชช
To let the active and passive ends share miscellaneous useful info, the OPEN and RESPONSE messages have an unstructured, variable-length field for any client-specific information that might be needed ๐
Lost OPEN and RESPONSE packets get recovered with timers โฐ When the active end sends an OPEN packet, it starts a timer. When that timer expires, another OPEN packet goes out, and this keeps happening until some predetermined maximum number of OPEN packets have been sent ๐ The passive end uses a similar scheme when it sends a RESPONSE packet ๐ When the active end receives a RESPONSE packet, it clears its timer โ
The passive end's timer gets cleared when it receives either a GO or a DATA packet, which is explained in the data transfer section ๐ซถ
To stop OPEN and RESPONSE packets from being duplicated, the OPEN packet carries a 32 bit connection unique ID that has to come back in the RESPONSE packet ๐ This keeps the initiator from mixing up the response to the current request with the response to an earlier connection request (only one connection can exist between any two ports) ๐ซ Any OPEN or RESPONSE packet whose destination port matches an open connection gets its unique ID checked ๐ต๏ธโโ๏ธ A matching unique ID means it's a duplicate packet, so it gets ignored ๐ A non-matching unique ID must be treated as an attempt to open a second connection between the same port pair, and it must be rejected by sending an ABORT message ๐
5.2. Data Transfer
Here's the simplest model of data transfer, bestie ๐ The sending client sets up a buffer full of data ๐ฆ The receiving NETBLT sends a GO message inside a CONTROL packet to the sender, saying it has set up its own buffer too and is ready to receive data into it ๐ข Once the GO message is received, the sender transmits the buffer as a series of DATA packets followed by an LDATA packet ๐ When the last packet in the buffer has arrived, the receiver sends a RESEND message inside a CONTROL packet, listing the packets that didn't arrive ๐ The sender resends those packets ๐ This keeps going until there are no missing packets, and then the receiver sends an OK message inside a CONTROL packet to the sender, sets up another buffer to receive data, and sends another GO message ๐ The sender, having received the OK message, sets up another buffer, waits for the GO message, and repeats the whole process ๐
Okay so there are a few obvious flaws with this scheme ๐ First, if the LDATA packet gets lost, how does the receiver even know when the buffer is done transmitting? ๐ญ Second, what if the GO, OK, or RESEND messages get lost? The sender can't act on a packet it never received, so the whole protocol just hangs ๐ Lowkey a nightmare. The fix is two kinds of timers: a data timer and a control timer โฐโจ
NETBLT handles the lost LDATA problem with a data timer on the receiving end. When the first DATA packet in a buffer shows up, the receiving NETBLT starts its data timer, and at the same time it clears its control timer (more on that in a sec) ๐ If the data timer expires, the receiver assumes the buffer is done transmitting and every missing packet is lost. So it sends a RESEND message with a list of the missing packets ๐ฆ
For the missing OK, GO, and RESEND messages, NETBLT uses a control timer ๐
The receiver can send one or more control messages (OK, GO, or RESEND) inside a single CONTROL packet. Every time it sends a control packet, it starts a control timer, and at the same time it clears its data timer if one has been set ๐ธ
Now, how does the control timer get cleared? Each control message carries a sequence number that starts at one and goes up by one for every control message sent ๐ข The sending NETBLT checks the sequence number on every incoming control message against all the other sequence numbers it has already received. It keeps track of the highest sequence number where everything below it is consecutive, and returns that number in every packet going back to the receiver ๐ซถ The receiver is allowed to clear the control timer of every packet with a sequence number equal to or lower than the one the sender returned. Bestie, that's giving clean bookkeeping โจ
Ideally, a NETBLT implementation should be able to handle out-of-sequence messages ๐ง It could collect them for later, or even process them right away. And if an incoming control message fills a "hole" in a run of message sequence numbers, a really clever implementation could notice that and adjust its outgoing sequence value to match ๐ Main character energy, honestly.
When the control timer expires, the receiving NETBLT resends the control message and resets the timer ๐ After a predetermined number of resends, the receiving NETBLT can assume the sending NETBLT has died and reset the connection ๐
When the sending NETBLT gets a control message, it should act on that packet as fast as it can. It either sets up a new buffer (when an OK packet arrives for a previous buffer), resends data (when a RESEND packet arrives), or sends data (when a GO packet arrives) ๐ If the sending NETBLT isn't in a position to send data, it sends a NULL-ACK packet containing the high-received-sequence-number described above. That lets the receiving NETBLT clear the control timers of any packets that are still outstanding. Then the sender waits until it can send more data ๐ In all of these cases, the overhead of responding to a control message should be small. The total time for a response to reach the receiving NETBLT should not be much more than the network round-trip transit time, plus a variance factor โฑ๏ธ
The timer system can be summed up like this: normally, the receiving NETBLT is running under one of two types of timers, a control timer or a data timer. There's one data timer per buffer transmission and one control timer per control packet. The data timer runs while its buffer is being transferred, and a control timer runs in between buffer transfers ๐
But there are still a couple of problems ๐ค If the sending NETBLT isn't ready to send, it sends a single NULL-ACK packet to clear any outstanding control timers at the receiving end. After that, the receiver just waits. The sender could die, and then the receiver, with all its control timers cleared, would hang forever ๐ Also, this system only puts timers on the receiving NETBLT. The sending NETBLT has no timers, so if the receiving NETBLT dies, the sender just hangs waiting for control messages ๐ฅฒ
The fix for both is a death timer plus a keepalive packet on both the sending and receiving NETBLTs ๐ก๏ธ As soon as the connection opens, each end sets a death timer, and it gets reset every time a packet is received. When one end's death timer expires, it can assume the other end has died and close the connection ๐
Waiting for long periods is totally valid, though. Clients can take ages to get buffer space and fill their buffers with data, and a NETBLT in that state is perfectly fine. So the protocol needs a way to stop the other end's death timer from expiring during that time ๐ฉ The answer is a KEEPALIVE packet, sent repeatedly at fixed intervals while a NETBLT is waiting for buffer space. Since the death timer resets whenever a packet arrives, it will never expire as long as the other end keeps sending packets ๐
Here's how often KEEPALIVE packets go out. At connection startup, each NETBLT picks a death-timeout value and sends it to the other end in either the OPEN or the RESPONSE packet. The other end takes that death-timeout value and uses it to work out how often to send KEEPALIVE packets. The KEEPALIVE frequency should be high enough that several KEEPALIVE packets can be lost before the other end's death timer expires ๐ฌ
Both ends need some way of estimating the values of the death timers, the control timers, and the data timers. Timer values obviously can't be specified in a protocol document, since they depend heavily on the machine and the network load. So they have to be computed on a per-connection basis. The protocol was designed to make that easy ๐งฎ
The death timer is relatively easy to estimate. Since it's continually reset, it doesn't need to be based on the transfer size. Instead, it should be based at least in part on the type of application using NETBLT. User applications should have smaller death timeout values, so humans aren't stuck waiting forever for a death timeout to happen ๐ Machine applications can have longer ones ๐ค
The control timer must be estimated more carefully ๐ง Its initial value can be an arbitrary number, and that number can be used to send the first control packet. Subsequent control packets can have their timer values based on the network round-trip transit time (the time between sending the control packet and receiving the acknowledgment of the corresponding sequence number) plus a variance factor. The timer value should be continually updated based on a smoothed average of collected round-trip transit times ๐
The data timer doesn't depend on the network round-trip transit time. It depends on how long it takes to transfer a buffer of data. The time value can be computed from the burst rate and the number of bursts per buffer, plus a variance value <1>. During the RESENDing phase, the data timer value should be set according to the number of missing packets โณ
The timers were designed to make reasonable estimation possible. In other protocols, figuring out round-trip delay has been a pain, since what the other end does on receiving a packet can vary a lot depending on the packet type. In NETBLT, the sender's action on receiving a control message is pretty much the same in every case, which keeps the round-trip delay relatively independent of the client ๐ฏ
Timer estimation is extremely important, especially in a high-performance protocol like NETBLT ๐๏ธ If the estimates are too low, the protocol makes lots of unneeded retransmissions, and performance tanks. A short control timer makes the sending NETBLT receive duplicate control messages. It can reject them, but that still takes time. A short data timer makes the receiving NETBLT send unnecessary RESEND packets, and that hurts way more, because the sender doesn't just toss a duplicate. It has to send a bunch of DATA packets again. Since data timers are set on each buffer transfer instead of on each DATA packet transfer, we can afford to use a small variance value without worrying about performance degradation ๐
5.3. Closing the Connection ๐ชโจ
There are three ways to close a connection: a connection close, a "quit", or an "abort" ๐ฌ
A connection close happens after a successful data transfer. When the sending NETBLT has received an OK packet for the last buffer in the transfer, it sends a DONE packet <2>. On receipt of the DONE packet, the receiving NETBLT can close its half of the connection. The sending NETBLT dallies for a predetermined amount of time after sending the DONE packet, to allow for the possibility that the DONE packet was lost. If the DONE packet was lost, the receiving NETBLT keeps sending the final OK packet, which causes the sending end to resend the DONE packet. After the dally period expires, the sending NETBLT closes its half of the connection ๐ซ
During the transfer, one client may send a QUIT packet to the other if it thinks that the other client is malfunctioning. Since the QUIT happens at the client level, the QUIT can only be sent between buffer transmissions ๐
The NETBLT receiving the QUIT packet can take no action other than to immediately notify its client and transmit a QUITACK packet. The QUIT sender must time out and retransmit until a QUITACK has been received or a predetermined number of resends have taken place. The sender of the QUITACK dallies in the manner described above ๐
An ABORT takes place when a NETBLT layer thinks that it or its opposite is malfunctioning. Since the ABORT starts in the NETBLT layer, it can be sent at any time ๐จ Since the ABORT implies that the NETBLT layer is malfunctioning, no transmit reliability is expected, and the sender can immediately close its connection ๐ฅ
6. MULTIPLE BUFFERING ๐ง
okay so NETBLT is literally built to make multiple buffering a thing, because speed is the whole point bestie ๐ Multiple buffering is when the sender and receiver both allocate and send buffers in a way that lets them recover from errors in the previous buffers while the current buffer is still flying ๐ฆ ngl that's so efficient it's giving main character energy.
During connection setup, the two ends negotiate how many concurrent buffers are allowed for the transfer ๐ค The simplest option is one buffer at a time. That's basically lock-step, and it's so wasteful because the sender just sits there waiting for permission to send each new buffer ๐ฉ If there's more than one buffer available, though, the next buffer can start right after the current one finishes ๐ธ
Here's a cute example. Say buffers A and B are allowed to go concurrently, with A coming first. The moment A finishes sending its data and is waiting for either an OK or a RESEND, B can start sending right away ๐ Data keeps flowing at a steady rate, no cap. If A gets an OK, it's done โ If A gets a RESEND, then only the missing packets listed in that RESEND message get sent again ๐
All the packets go out through a priority pipe. The priority is just the buffer number, and the transfer rate comes from the burst size and burst rate โจ Buffer numbers always go up, so packets from an earlier buffer in the pipe will always get ahead of the later ones. Iconic ๐
There's one tweak to the timing algorithm you need to know about ๐ When the receiving NETBLT sets the data timer for a new buffer, the timer value should also account for how long it'll take to transfer all the missing packets from the previous buffers. Being thorough is the moment ๐ซถ
Running several buffers at once is honestly not that much harder than running one. The trick is to think of each buffer as a finite state machine ๐ง Multiple buffers are just a group of these state machines, each sitting in one of several states. The whole transfer is just moving buffers through those states until everything has been sent ๐ฆ
Now the state sequence for a send-receive buffer pair goes like this ๐ The sending and receiving buffers get created independently. The receiving NETBLT sends a GO message, which puts its buffer into a "receiving" state, and it starts its control timer โฐ The sending NETBLT gets that GO message and moves its buffer into a "sending" state ๐ฆ The sender keeps sending data until the whole buffer has gone out.
If the receiver's data timer goes off before it gets the last (LDATA) packet, or it gets the LDATA packet but some packets are missing, it sends a RESEND packet and moves the buffer into a "resending" state ๐ญ Once every DATA packet in the buffer and the LDATA packet have arrived, the receiver puts its buffer into a "received" state and sends an OK packet ๐ The sender gets that OK packet and moves its buffer into a "sent" state. Periodt ๐
7. PROTOCOL LAYERING STRUCTURE ๐
NETBLT sits directly on top of the Internet Protocol (IP), no middle layer needed ๐ It's been given a temporary protocol number of 255. This number will change once the final protocol specification is settled, so don't get too attached to it ๐
8. PACKET FORMATS ๐ฆ
okay the packet formats are where it gets really satisfying ๐คฉ NETBLT packets come in three categories, and they all share the same common packet header ๐
First, there are the packets that only travel from sender to receiver. These carry the control message sequence numbers the receiver uses for reliability. They're the NULL-ACK, DATA, and LDATA packets โจ
Second, there's one packet that only travels from receiver to sender, which is the CONTROL packet. Each CONTROL packet can hold any number of control messages (GO, OK, or RESEND), and each of those messages gets its own sequence number ๐ข
Finally, there are the packets that either have their own special way of ensuring reliability or aren't sent reliably at all. These are QUIT, QUITACK, DONE, KEEPALIVE, and ABORT ๐ซง Every one of them except DONE can be sent by both the sending and receiving NETBLTs. Lowkey the most flexible bunch ๐
Packet type numbers:
OPEN: 0
RESPONSE: 1
KEEPALIVE: 2
DONE: 3
QUIT: 4
QUITACK: 5
ABORT: 6
DATA: 7
LDATA: 8
NULL-ACK: 9
CONTROL: 10
Standard header:
local port: 2 bytes
foreign port: 2 bytes
checksum: 2 bytes
version number: 1 byte
packet type: 1 byte
packet length: 2 bytes
OPEN and RESPONSE packets:
connection unique ID: 4 bytes
standard buffer size: 4 bytes
transfer size: 4 bytes
DATA packet data segment size: 2 bytes
burst size: 2 bytes
burst rate: 2 bytes
death timeout value in seconds: 2 bytes
transfer mode (1 = SEND, 0 = RECEIVE): 1 byte
maximum number of concurrent buffers: 1 byte
checksum entire DATA packet / checksum
DATA packet data only (1/0): 1 byte
client-specific data: arbitrary
DONE, QUITACK, KEEPALIVE:
standard header only
ABORT, QUIT:
reason: arbitrary bytes
CONTROL packet format:
CONTROL packets are a standard NETBLT header of type CONTROL, followed by however many control messages you like, each in one of the formats below ๐
Control message numbers:
GO: 0
OK: 1
RESEND: 2
OK message:
message type (OK): 1 byte
buffer number: 4 bytes
sequence number: 2 bytes
new burst size: 2 bytes
new burst interval: 2 bytes
GO message:
message type (GO): 1 byte
buffer number: 4 bytes
sequence number: 2 bytes
RESEND message:
message type (RESEND): 1 byte
buffer number: 4 bytes
sequence number: 2 bytes
number of missing packets: 2 bytes
packet numbers...: n * 2 bytes
DATA, LDATA packet formats:
buffer number: 4 bytes
highest consecutive sequence number received: 2 bytes
packet number within buffer: 2 bytes
data: arbitrary bytes
NULL-ACK packet format:
highest consecutive sequence number received: 2 bytes
acknowledged new burst size: 2 bytes
acknowledged new burst interval: 2 bytes
NOTES:
<1> When the buffer size is big, the variances in the round trip delays of lots of packets can cancel each other out ๐ That means the variance value doesn't need to be very big. This expectation can be checked in further testing ๐งช
<2> The receiving end might not know the transfer size ahead of time, so it's possible it has allocated buffer space and sent GO messages for buffers past the real last buffer the sending end sent ๐ The sending end needs to be careful and ignore those extra GO messages. Not me being dramatic, this is a real gotcha ๐
๐ฆ where this page comes from
- ietf: rfc969 1985-12
- ๐ girlified by claude-haiku-5-5 on : a language model's rewrite of the original, under its terms