mirror of
https://github.com/soypat/lneto.git
synced 2026-08-11 10:23:47 +00:00
rewrite ControlBlock.Open to only open passive connections; now use Send for active connections
This commit is contained in:
@@ -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
@@ -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
|
||||
|
||||
@@ -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
@@ -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()
|
||||
|
||||
@@ -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
@@ -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,
|
||||
},
|
||||
|
||||
Reference in New Issue
Block a user