refactor stack TCP stuff; begin adding TCP test

This commit is contained in:
Patricio Whittingslow
2025-10-17 23:36:48 -03:00
parent d28203e75f
commit 86fc8fdaf5
4 changed files with 103 additions and 75 deletions
+23 -63
View File
@@ -2,7 +2,6 @@ package xnet
import ( import (
"errors" "errors"
"io"
"net/netip" "net/netip"
"sync" "sync"
"time" "time"
@@ -30,7 +29,6 @@ type StackAsync struct {
arp arp.Handler arp arp.Handler
udps internet.StackPorts udps internet.StackPorts
tcps internet.StackPorts tcps internet.StackPorts
tcpconns []tcp.Conn
dhcpUDP internet.StackUDPPort dhcpUDP internet.StackUDPPort
dhcp dhcpv4.Client dhcp dhcpv4.Client
@@ -59,8 +57,6 @@ type StackConfig struct {
NTPServer netip.Addr NTPServer netip.Addr
Hostname string Hostname string
MaxTCPConns int MaxTCPConns int
TCPBufferSizeTx int // Size of transmit buffer for TCP connections.
TCPBufferSizeRx int // Size of receive buffer for TCP connections.
RandSeed int64 RandSeed int64
HardwareAddress [6]byte HardwareAddress [6]byte
MTU uint16 MTU uint16
@@ -127,44 +123,14 @@ func (s *StackAsync) Reset(cfg StackConfig) error {
} }
// Enable TCP if connections present. // Enable TCP if connections present.
if cfg.MaxTCPConns > 0 {
if cfg.TCPBufferSizeRx < minTCPBuffer || cfg.TCPBufferSizeTx < minTCPBuffer { err = s.tcps.ResetTCP(cfg.MaxTCPConns)
return io.ErrShortBuffer if err != nil {
} return err
if cap(s.tcpconns) < cfg.MaxTCPConns { }
s.tcpconns = make([]tcp.Conn, cfg.MaxTCPConns) err = s.ip.Register(&s.tcps)
} if err != nil {
s.tcpconns = s.tcpconns[:cfg.MaxTCPConns] return err
for i := range s.tcpconns {
c := &s.tcpconns[i]
c.Abort() // Abort forcibly closes connection to allow buffer setting and reduce risk of race conditions.
h := c.InternalHandler()
sizetx := h.FreeTx()
sizerx := h.SizeRx()
// If size is sufficient then buffers will remain nil and memory will be reused.
var rxbuf, txbuf []byte
if sizetx < cfg.TCPBufferSizeTx || sizerx < cfg.TCPBufferSizeRx {
buf := make([]byte, cfg.TCPBufferSizeTx+cfg.TCPBufferSizeRx)
rxbuf = buf[:cfg.TCPBufferSizeRx]
txbuf = buf[cfg.TCPBufferSizeRx : cfg.TCPBufferSizeRx+cfg.TCPBufferSizeTx]
}
err = c.Configure(tcp.ConnConfig{
RxBuf: rxbuf,
TxBuf: txbuf,
TxPacketQueueSize: 4,
})
if err != nil {
return err
}
}
err = s.tcps.ResetTCP(cfg.MaxTCPConns)
if err != nil {
return err
}
err = s.ip.Register(&s.tcps)
if err != nil {
return err
}
} }
// Now setup stacks. // Now setup stacks.
@@ -231,6 +197,12 @@ func (s *StackAsync) SetIPAddr(addr netip.Addr) error {
return s.resetARP() return s.resetARP()
} }
func (s *StackAsync) Addr() netip.Addr {
s.mu.Lock()
defer s.mu.Unlock()
return s.ip.Addr()
}
func (s *StackAsync) SetHardwareAddress(hw [6]byte) error { func (s *StackAsync) SetHardwareAddress(hw [6]byte) error {
s.mu.Lock() s.mu.Lock()
defer s.mu.Unlock() defer s.mu.Unlock()
@@ -244,38 +216,26 @@ func (s *StackAsync) SetGateway6(gwhw [6]byte) {
s.link.SetGateway6(gwhw) s.link.SetGateway6(gwhw)
} }
var ( func (s *StackAsync) start() {
errNoTCP = errors.New("no TCP initialized")
errNoTCPConnsAvail = errors.New("all allocated TCP connections busy")
)
func (s *StackAsync) DialTCP(localPort uint16, addrp netip.AddrPort) (conn *tcp.Conn, err error) { }
if len(s.tcpconns) == 0 {
return nil, errNoTCP func (s *StackAsync) DialTCP(conn *tcp.Conn, localPort uint16, addrp netip.AddrPort) (err error) {
} if !conn.State().IsClosed() {
for i := range s.tcpconns { return errors.New("conn not closed")
maybeFreeConn := &s.tcpconns[i]
state := maybeFreeConn.State()
if state.IsClosed() {
conn = maybeFreeConn
break // Can be used!
}
}
if conn == nil {
return nil, errNoTCPConnsAvail
} }
conn.Abort() // Conn is closed, safe to abort. conn.Abort() // Conn is closed, safe to abort.
err = conn.OpenActive(localPort, addrp, tcp.Value(s.Prand32())) err = conn.OpenActive(localPort, addrp, tcp.Value(s.Prand32()))
if err != nil { if err != nil {
conn.Abort() conn.Abort()
return nil, err return err
} }
err = s.tcps.Register(conn) err = s.tcps.Register(conn)
if err != nil { if err != nil {
conn.Abort() conn.Abort()
return nil, err return err
} }
return conn, nil return nil
} }
var errNoDNSServer = errors.New("no DNS server- did DHCP complete? You can set a predetermined DNS server in Stack configuration") var errNoDNSServer = errors.New("no DNS server- did DHCP complete? You can set a predetermined DNS server in Stack configuration")
+8 -7
View File
@@ -113,29 +113,30 @@ func (s StackBlocking) DoLookupIP(host string, timeout time.Duration) (addrs []n
var errTCPFailedToConnect = errors.New("tcp failed to connect") var errTCPFailedToConnect = errors.New("tcp failed to connect")
func (s StackBlocking) DoDialTCP(localPort uint16, addrp netip.AddrPort, timeout time.Duration) (conn *tcp.Conn, err error) { func (s StackBlocking) DoDialTCP(conn *tcp.Conn, localPort uint16, addrp netip.AddrPort, timeout time.Duration) (err error) {
conn, err = s.async.DialTCP(localPort, addrp) err = s.async.DialTCP(conn, localPort, addrp)
if err != nil { if err != nil {
return nil, err return err
} }
sleep := timeout/maxIter + 1 sleep := timeout/maxIter + 1
deadline := time.Now().Add(timeout) deadline := time.Now().Add(timeout)
for i := 0; i < maxIter; i++ { for i := 0; i < maxIter; i++ {
state := conn.State() state := conn.State()
if state == tcp.StateEstablished { if state == tcp.StateEstablished {
return conn, nil return nil
} else if state == tcp.StateSynSent || state == tcp.StateSynRcvd || conn.InternalHandler().AwaitingSynSend() { } else if state == tcp.StateSynSent || state == tcp.StateSynRcvd || conn.InternalHandler().AwaitingSynSend() {
if err = s.checkDeadline(deadline); err != nil { if err = s.checkDeadline(deadline); err != nil {
return nil, err conn.Abort()
return err
} }
time.Sleep(sleep) time.Sleep(sleep)
} else { } else {
// Unexpected state, abort and terminate connection. // Unexpected state, abort and terminate connection.
conn.Abort() conn.Abort()
return nil, errTCPFailedToConnect return errTCPFailedToConnect
} }
} }
return conn, errDeadlineExceed return errDeadlineExceed
} }
func (s StackBlocking) checkDeadline(deadline time.Time) error { func (s StackBlocking) checkDeadline(deadline time.Time) error {
+5 -5
View File
@@ -83,16 +83,16 @@ func (s StackRetrying) DoResolveHardwareAddress6(addr netip.Addr, timeout time.D
return hw, errRetriesExceeded return hw, errRetriesExceeded
} }
func (s StackRetrying) DoDialTCP(localPort uint16, addrp netip.AddrPort, timeout time.Duration, retries int) (conn *tcp.Conn, err error) { func (s StackRetrying) DoDialTCP(conn *tcp.Conn, localPort uint16, addrp netip.AddrPort, timeout time.Duration, retries int) (err error) {
expectEnd := time.Now().Add(timeout * time.Duration(retries)) expectEnd := time.Now().Add(timeout * time.Duration(retries))
for i := 0; i < retries; i++ { for i := 0; i < retries; i++ {
conn, err = s.block.DoDialTCP(localPort, addrp, timeout) err = s.block.DoDialTCP(conn, localPort, addrp, timeout)
if err == nil { if err == nil {
return conn, nil return nil
} }
} }
if time.Now().Before(expectEnd) { if time.Now().Before(expectEnd) {
return conn, err return err
} }
return nil, errRetriesExceeded return errRetriesExceeded
} }
+67
View File
@@ -0,0 +1,67 @@
package xnet
import (
"net/netip"
"testing"
"github.com/soypat/lneto/tcp"
)
func TestABC(t *testing.T) {
const seed = 1234
const MTU = 1500
var mac = [6]byte{0x02, 0x00, 0x00, 0x00, 0x00, 0x01}
var client StackAsync
err := client.Reset(StackConfig{
StaticAddress: netip.MustParseAddr("10.0.0.1"),
MaxTCPConns: 1,
MTU: MTU,
HardwareAddress: mac,
Hostname: "client",
RandSeed: seed,
})
if err != nil {
t.Fatal(err)
}
var macsv = [6]byte{0x02, 0x00, 0x00, 0x00, 0x00, 0x01}
var sv StackAsync
err = sv.Reset(StackConfig{
StaticAddress: netip.MustParseAddr("10.0.0.2"),
MaxTCPConns: 1,
MTU: MTU,
HardwareAddress: macsv,
Hostname: "server",
RandSeed: seed,
})
if err != nil {
t.Fatal(err)
}
// IMG_1084.MOV
const svPort = 80
var clconn tcp.Conn
err = clconn.Configure(tcp.ConnConfig{
RxBuf: make([]byte, MTU),
TxBuf: make([]byte, MTU),
TxPacketQueueSize: 4,
})
if err != nil {
t.Fatal(err)
}
err = client.DialTCP(&clconn, 1337, netip.AddrPortFrom(sv.Addr(), svPort))
if err != nil {
t.Fatal(err)
}
var svconn tcp.Conn
err = svconn.Configure(tcp.ConnConfig{
RxBuf: make([]byte, MTU),
TxBuf: make([]byte, MTU),
TxPacketQueueSize: 4,
})
if err != nil {
t.Fatal(err)
}
// clconn.OpenListen()
}