rewrite ControlBlock.Open to only open passive connections; now use Send for active connections

This commit is contained in:
soypat
2025-02-13 22:56:27 -03:00
parent 0cb43c47a4
commit bbd74d04d8
6 changed files with 88 additions and 29 deletions
+5 -1
View File
@@ -70,11 +70,15 @@ func main() {
if err != nil {
log.Fatal(err)
}
log.Println("success receiving packet")
n, err := lStack.HandleEth(buf)
if err != nil {
log.Fatal(n, err)
} else if n > 0 {
log.Println("success sending packet")
} else {
log.Println("no packet sent")
}
log.Println("success receiving packet")
}
type Handler interface {
+21 -13
View File
@@ -138,14 +138,13 @@ type recvSpace struct {
WND Size // receive window defined by local. Permitted number of remote unacked octets in flight.
}
// Open implements a passive/active opening of a connection.
// state must be StateListen or StateSynSent.
func (tcb *ControlBlock) Open(iss Value, wnd Size, state State) (err error) {
// Open implements a passive opening of a connection (wait for incoming packets).
// Upon success [ControlBlock] enters LISTEN state, such as that of a server.
// To open an active connection use [ControlBlock.Send] with a segment generated with [ClientSynSegment].
func (tcb *ControlBlock) Open(iss Value, wnd Size) (err error) {
switch {
case tcb._state != StateClosed && tcb._state != StateListen:
err = errTCBNotClosed
case state != StateListen && state != StateSynSent:
err = errInvalidState
case wnd > math.MaxUint16:
err = errWindowTooLarge
}
@@ -153,15 +152,17 @@ func (tcb *ControlBlock) Open(iss Value, wnd Size, state State) (err error) {
tcb.logerr("tcb:open", slog.String("err", err.Error()))
return err
}
tcb._state = state
tcb._state = StateListen
tcb.prepareToHandshake(iss, wnd)
tcb.trace("tcb:open-server")
return nil
}
// prepareToHandshake initializes the TCB send/receive spaces with initial send sequence number and local window.
func (tcb *ControlBlock) prepareToHandshake(iss Value, wnd Size) {
tcb.resetRcv(wnd, 0)
tcb.resetSnd(iss, 1)
tcb.pending = [2]Flags{}
if state == StateSynSent {
tcb.pending[0] = FlagSYN
}
tcb.trace("tcb:open", slog.String("state", tcb._state.String()))
return nil
}
// HasPending returns true if there is a pending control segment to send. Calls to Send will advance the pending queue.
@@ -305,6 +306,12 @@ func (tcb *ControlBlock) Send(seg Segment) error {
hasACK := seg.Flags.HasAny(FlagACK)
var newPending Flags
switch tcb._state {
case StateClosed:
if seg.Flags == FlagSYN {
tcb._state = StateSynSent
tcb.prepareToHandshake(seg.SEQ, seg.WND)
tcb.trace("tcb:open-client")
}
case StateSynRcvd:
if hasFIN {
tcb._state = StateFinWait1 // RFC 9293: 3.10.4 CLOSE call.
@@ -348,14 +355,15 @@ func (tcb *ControlBlock) Send(seg Segment) error {
func (tcb *ControlBlock) validateOutgoingSegment(seg Segment) (err error) {
hasAck := seg.Flags.HasAny(FlagACK)
checkSeq := !seg.Flags.HasAny(FlagRST)
isFirst := tcb._state == StateClosed && seg.isFirstSYN()
checkSeq := !isFirst && !seg.Flags.HasAny(FlagRST)
seglast := seg.Last()
// Extra check for when send Window is zero and no data is being sent.
zeroWindowOK := tcb.snd.WND == 0 && seg.DATALEN == 0 && seg.SEQ == tcb.snd.NXT
outOfWindow := checkSeq && !seg.SEQ.InWindow(tcb.snd.NXT, tcb.snd.WND) &&
!zeroWindowOK
switch {
case tcb._state == StateClosed:
case tcb._state == StateClosed && !isFirst:
err = io.ErrClosedPipe
case seg.WND > math.MaxUint16:
err = errWindowTooLarge
+19
View File
@@ -68,6 +68,25 @@ func (seg *Segment) Last() Value {
return Add(seg.SEQ, seglen) - 1
}
func (seg Segment) isFirstSYN() bool {
return seg.Flags == FlagSYN && seg.ACK == 0 && seg.DATALEN == 0 && seg.WND > 0
}
// ClientSynSegment is a the first packet sent over a TCP connection to a server. Typically the client
// will call ClientSynSegment to generate a new SYN packet to send over to the server to initiate communications:
//
// synseg := ClientSynSegment(100, 256)
// err := clientTCB.Send(synseg) // By now the client's TCB is in StateSynSent and is attempting to open a connection.
func ClientSynSegment(clientISS Value, clientWND Size) Segment {
return Segment{
SEQ: clientISS,
WND: clientWND,
Flags: FlagSYN,
ACK: 0,
DATALEN: 0,
}
}
// StringExchange returns a string representation of a segment exchange over
// a network in RFC9293 styled visualization. invertDir inverts the arrow directions.
// i.e:
+10 -8
View File
@@ -53,7 +53,7 @@ func (h *Handler) LocalPort() uint16 {
func (h *Handler) Open(state State, localPort, remotePort uint16, iss Value) error {
// Open will fail unless SCB in closed state.
err := h.scb.Open(iss, Size(h.bufRx.Size()), state)
err := h.scb.Open(iss, Size(h.bufRx.Size()))
if err != nil {
return err
}
@@ -156,13 +156,15 @@ func (h *Handler) Send(b []byte) (int, error) {
// No pending control segment or data to send. Yield.
return 0, nil
}
n, seq, err := h.bufTx.MakePacket(b[sizeHeaderTCP : sizeHeaderTCP+segment.DATALEN])
if err != nil {
return 0, err
} else if seq != segment.SEQ {
panic("mismatching sequence numbers")
} else if n != int(segment.DATALEN) {
panic("expected n == available")
if available > 0 {
n, seq, err := h.bufTx.MakePacket(b[sizeHeaderTCP : sizeHeaderTCP+segment.DATALEN])
if err != nil {
return 0, err
} else if seq != segment.SEQ {
panic("mismatching sequence numbers")
} else if n != int(segment.DATALEN) {
panic("expected n == available")
}
}
}
prevState := h.scb.State()
+20
View File
@@ -0,0 +1,20 @@
package tcp
import (
"math/rand"
"testing"
)
func TestHandler(t *testing.T) {
}
func setupClientServer(rng *rand.Rand) (client, server Handler) {
// err := server.Open(StateListen, uint16(rng.Uint32()), 0, 0)
// if err != nil {
// panic(err)
// }
// err = client.Open(State)
return client, server
}
+13 -7
View File
@@ -316,22 +316,28 @@ func TestExchange_rfc9293_figure13(t *testing.T) {
func TestExchange_noDupAckDuringEstablished(t *testing.T) {
var tcbA tcp.ControlBlock
const issA, issB, windowA, windowB = 300, 334222749, 256, 64240
err := tcbA.Open(issA, issA, tcp.StateSynSent)
synseg := tcp.ClientSynSegment(issA, windowA)
// err := tcbA.Open(issA, issA, tcp.StateSynSent)
tcbA.SetRecvWindow(windowA)
if err != nil {
t.Fatal(err)
}
// if err != nil {
// t.Fatal(err)
// }
establishA := []tcp.Exchange{
0: { // B sends SYN to A.
0: { // A sends SYN to B.
Outgoing: &synseg,
WantState: tcp.StateSynSent,
},
1: { // B sends SYN to A.
Incoming: &tcp.Segment{SEQ: issB, ACK: 0, WND: windowB, Flags: tcp.FlagSYN},
WantPending: &tcp.Segment{SEQ: issA, ACK: issB + 1, WND: windowA, Flags: SYNACK},
WantState: tcp.StateSynRcvd,
},
1: { // Send SYNACK to B.
2: { // Send SYNACK to B.
Outgoing: &tcp.Segment{SEQ: issA, ACK: issB + 1, WND: windowA, Flags: SYNACK},
WantState: tcp.StateSynRcvd,
},
2: { // B ACKs SYNACK, thus establishing the connection on both sides.
3: { // B ACKs SYNACK, thus establishing the connection on both sides.
Incoming: &tcp.Segment{SEQ: issB + 1, ACK: issA + 1, WND: windowB, Flags: tcp.FlagACK},
WantState: tcp.StateEstablished,
},