mega refactor internet.TCPConn->tcp.Conn

This commit is contained in:
soypat
2025-06-07 20:43:20 -03:00
parent dab7951cbd
commit 9a60642283
9 changed files with 318 additions and 150 deletions
+54 -56
View File
@@ -43,7 +43,9 @@ func main() {
gatewayMAC := tap.HardwareAddr6() gatewayMAC := tap.HardwareAddr6()
mtu := tap.MTU() mtu := tap.MTU()
stack, err := NewEthernetTCPStack(stackHWAddr, gatewayMAC, addrPort, uint16(mtu))
var stack Stack
err := stack.Reset(stackHWAddr, gatewayMAC, addrPort.Addr(), mtu)
if err != nil { if err != nil {
log.Fatal(err) log.Fatal(err)
} }
@@ -113,7 +115,7 @@ func main() {
} }
} }
func doHTTP(conn *internet.TCPConn, hdr *httpraw.Header) error { func doHTTP(conn *tcp.Conn, hdr *httpraw.Header) error {
const asRequest = false const asRequest = false
if conn.State() != tcp.StateEstablished || conn.BufferedInput() == 0 { if conn.State() != tcp.StateEstablished || conn.BufferedInput() == 0 {
return nil // No data yet. return nil // No data yet.
@@ -160,10 +162,57 @@ type Stack struct {
arp internet.NodeARP arp internet.NodeARP
} }
func (stack *Stack) OpenPassiveTCP(port uint16, iss tcp.Value) (*internet.TCPConn, error) { func (stack *Stack) Reset(ourMAC, gwMAC [6]byte, ip netip.Addr, mtu int) (err error) {
err = stack.ethernet.Reset6(ourMAC, gwMAC, mtu)
if err != nil {
return err
}
err = stack.ip.Reset(ip)
if err != nil {
return err
}
stack.tcpports.Reset(uint64(lneto.IPProtoTCP), 2)
ipaddr := ip.As4()
err = stack.arp.Reset(arp.HandlerConfig{
HardwareAddr: ourMAC[:],
ProtocolAddr: ipaddr[:],
MaxQueries: 2,
MaxPending: 2,
HardwareType: 1,
ProtocolType: ethernet.TypeIPv4,
})
if err != nil {
return err
}
// Register stacks and nodes.
err = stack.ethernet.Register(&stack.arp)
if err != nil {
return err
}
err = stack.ethernet.Register(&stack.ip)
if err != nil {
return err
}
err = stack.ip.Register(&stack.tcpports)
if err != nil {
return err
}
return nil
}
func (stack *Stack) Recv(b []byte) error {
return stack.ethernet.Demux(b, 0)
}
func (stack *Stack) Send(b []byte) (int, error) {
return stack.ethernet.Encapsulate(b, 0)
}
func (stack *Stack) OpenPassiveTCP(port uint16, iss tcp.Value) (*tcp.Conn, error) {
mtu := stack.ethernet.MTU() mtu := stack.ethernet.MTU()
conn := new(internet.TCPConn) conn := new(tcp.Conn)
err := conn.Configure(&internet.TCPConnConfig{ err := conn.Configure(&tcp.ConnConfig{
RxBuf: make([]byte, mtu), RxBuf: make([]byte, mtu),
TxBuf: make([]byte, mtu), TxBuf: make([]byte, mtu),
TxPacketQueueSize: 3, TxPacketQueueSize: 3,
@@ -182,57 +231,6 @@ func (stack *Stack) OpenPassiveTCP(port uint16, iss tcp.Value) (*internet.TCPCon
return conn, nil return conn, nil
} }
func NewEthernetTCPStack(ourMAC, gwMAC [6]byte, ip netip.AddrPort, mtu uint16) (*Stack, error) {
var stack Stack
var err error
err = stack.ethernet.Reset6(ourMAC, gwMAC, int(mtu))
if err != nil {
return nil, err
}
err = stack.ip.Reset(ip.Addr())
if err != nil {
return nil, err
}
stack.tcpports.Reset(uint64(lneto.IPProtoTCP), 2)
ipaddr := ip.Addr().As4()
err = stack.arp.Reset(arp.HandlerConfig{
HardwareAddr: ourMAC[:],
ProtocolAddr: ipaddr[:],
MaxQueries: 2,
MaxPending: 2,
HardwareType: 1,
ProtocolType: ethernet.TypeIPv4,
})
if err != nil {
return nil, err
}
// Register stacks and nodes.
err = stack.ethernet.Register(&stack.arp)
if err != nil {
return nil, err
}
err = stack.ethernet.Register(&stack.ip)
if err != nil {
return nil, err
}
err = stack.ip.Register(&stack.tcpports)
if err != nil {
return nil, err
}
return &stack, nil
}
func debugHex(b []byte) string {
var d []byte
for i := 0; i < len(b); i++ {
c1 := tblhex[b[i]&0xf]
c2 := tblhex[b[i]>>4]
d = append(d, c2, c1, ' ')
}
return string(d)
}
const tblhex = "0123456789abcdef" const tblhex = "0123456789abcdef"
func getTCPFlags(frames []pcap.Frame, pkt []byte) (flags tcp.Flags) { func getTCPFlags(frames []pcap.Frame, pkt []byte) (flags tcp.Flags) {
+4 -4
View File
@@ -120,7 +120,7 @@ func main() {
} }
} }
func doHTTP(conn *internet.TCPConn, hdr *httpraw.Header) error { func doHTTP(conn *tcp.Conn, hdr *httpraw.Header) error {
const asRequest = false const asRequest = false
if conn.State() != tcp.StateEstablished || conn.BufferedInput() == 0 { if conn.State() != tcp.StateEstablished || conn.BufferedInput() == 0 {
return nil // No data yet. return nil // No data yet.
@@ -160,7 +160,7 @@ func doHTTP(conn *internet.TCPConn, hdr *httpraw.Header) error {
return nil return nil
} }
func NewEthernetTCPStack(ourMAC, gwMAC [6]byte, ip netip.AddrPort, mtu uint16, slogger logger) (*LinkStack, *internet.TCPConn, error) { func NewEthernetTCPStack(ourMAC, gwMAC [6]byte, ip netip.AddrPort, mtu uint16, slogger logger) (*LinkStack, *tcp.Conn, error) {
var err error var err error
lStack := LinkStack{ lStack := LinkStack{
logger: slogger, logger: slogger,
@@ -181,8 +181,8 @@ func NewEthernetTCPStack(ourMAC, gwMAC [6]byte, ip netip.AddrPort, mtu uint16, s
proto: ethernet.TypeIPv4, proto: ethernet.TypeIPv4,
lport: 0, lport: 0,
}) })
var conn internet.TCPConn var conn tcp.Conn
err = conn.Configure(&internet.TCPConnConfig{ err = conn.Configure(&tcp.ConnConfig{
RxBuf: make([]byte, mtu), RxBuf: make([]byte, mtu),
TxBuf: make([]byte, mtu), TxBuf: make([]byte, mtu),
TxPacketQueueSize: 3, TxPacketQueueSize: 3,
+44
View File
@@ -0,0 +1,44 @@
package internal
import (
"encoding/binary"
"errors"
)
var (
errUnsupportedIP = errors.New("unsupported IP version")
errInvalidIPVersionToSetAddr = errors.New("invalid ip version to setDstAddr")
)
func GetIPSourceAddr(buf []byte) (addr []byte, id uint16, err error) {
version := buf[0] >> 4
switch version { //
case 4:
addr = buf[12:16]
id = binary.BigEndian.Uint16(buf[4:6])
case 6:
addr = buf[8:24]
default:
err = errUnsupportedIP
}
return addr, id, err
}
func SetIPDestinationAddr(buf []byte, id uint16, addr []byte) (err error) {
var dstaddr []byte
version := buf[0] >> 4
switch version {
case 4:
dstaddr = buf[16:20]
binary.BigEndian.PutUint16(buf[4:6], id)
case 6:
dstaddr = buf[24:40]
default:
err = errUnsupportedIP
}
if err == nil && len(dstaddr) != len(addr) {
return errInvalidIPVersionToSetAddr
}
copy(dstaddr, addr)
return nil
}
+149
View File
@@ -0,0 +1,149 @@
package internet
import (
"bytes"
"errors"
"log/slog"
"net"
"time"
"github.com/soypat/lneto"
"github.com/soypat/lneto/internal"
"github.com/soypat/lneto/tcp"
)
var _ StackNode = (*NodeTCPListener)(nil)
type NodeTCPListener struct {
connID uint64
conns []tcp.Conn
accepted []bool
port uint16
getISS func() uint32
}
func (listener *NodeTCPListener) AcceptRaw() (*tcp.Conn, error) {
connid := listener.connID
for {
if listener.isClosed() || connid != listener.connID {
return nil, net.ErrClosed
}
for i := range listener.conns {
isAvailable := listener.connReceivedSyn(i) && !listener.connAccepted(i)
if !isAvailable {
continue
}
// Connection received as SYN and is not yet accepted.
listener.accepted[i] = true
return &listener.conns[i], nil
}
time.Sleep(5 * time.Millisecond)
}
panic("unreachable")
}
func (listener *NodeTCPListener) Close() error {
if listener.isClosed() {
return errors.New("already closed")
}
listener.connID++
listener.port = 0
return nil
}
func (listener *NodeTCPListener) LocalPort() uint16 { return listener.port }
func (listener *NodeTCPListener) ConnectionID() *uint64 { return &listener.connID }
func (listener *NodeTCPListener) Protocol() uint64 { return uint64(lneto.IPProtoTCP) }
func (listener *NodeTCPListener) Encapsulate(carrierData []byte, tcpFrameOffset int) (int, error) {
if listener.isClosed() {
return 0, net.ErrClosed
}
for i := range listener.conns {
conn := &listener.conns[i]
if conn.State().IsClosed() {
continue
}
n, err := conn.Encapsulate(carrierData, tcpFrameOffset)
if err != nil {
listener.maintainConn(i, err)
}
if n == 0 {
continue
}
return n, err
}
return 0, nil
}
func (listener *NodeTCPListener) Demux(carrierData []byte, tcpFrameOffset int) error {
if listener.isClosed() {
return net.ErrClosed
}
tfrm, err := tcp.NewFrame(carrierData[tcpFrameOffset:])
if err != nil {
return err
}
addr, _, err := internal.GetIPSourceAddr(carrierData)
if err != nil {
return err
}
dst := tfrm.DestinationPort()
if dst != listener.port {
return errors.New("not our port")
}
src := tfrm.DestinationPort()
_, flags := tfrm.OffsetAndFlags()
for i := range listener.conns {
if listener.conns[i].RemotePort() != src || !bytes.Equal(listener.conns[i].RemoteAddr(), addr) {
continue
}
conn := &listener.conns[i]
err := conn.Demux(carrierData, tcpFrameOffset)
if err != nil {
listener.maintainConn(i, err)
}
return err
}
if !flags.HasAll(tcp.FlagSYN) {
return nil // Not a synchronizing packet, drop it.
}
// New connection must be assigned.
for i := range listener.conns {
conn := &listener.conns[i]
isOpen := !conn.State().IsClosed()
if isOpen {
continue
}
if conn.State() == tcp.StateTimeWait {
conn.Abort()
}
err = conn.OpenListen(dst, tcp.Value(listener.getISS()))
if err != nil {
return err
}
return conn.Demux(carrierData, tcpFrameOffset)
}
slog.Error("tcpListener:no-free-conn")
return nil
}
func (listener *NodeTCPListener) maintainConn(connIdx int, err error) {
if err == net.ErrClosed {
listener.conns[connIdx].Abort()
}
}
func (listener *NodeTCPListener) isClosed() bool {
return listener.port == 0
}
func (listener *NodeTCPListener) connReceivedSyn(idx int) bool {
return listener.conns[idx].RemotePort() != 0
}
func (listener *NodeTCPListener) connAccepted(idx int) bool {
return listener.accepted[idx]
}
+1 -1
View File
@@ -164,7 +164,7 @@ func (sb *StackIP) Register(h StackNode) error {
return nil return nil
} }
func (sb *StackIP) RegisterTCPConn(conn *TCPConn) error { func (sb *StackIP) RegisterTCPConn(conn *tcp.Conn) error {
if conn.LocalPort() == 0 { if conn.LocalPort() == 0 {
return errZeroPort return errZeroPort
} }
+6 -6
View File
@@ -11,7 +11,7 @@ import (
func TestBasicStack(t *testing.T) { func TestBasicStack(t *testing.T) {
rng := rand.New(rand.NewSource(1)) rng := rand.New(rand.NewSource(1))
var sbCl, sbSv StackIP var sbCl, sbSv StackIP
var connCl, connSv TCPConn var connCl, connSv tcp.Conn
setupClientServer(t, rng, &sbCl, &sbSv, &connCl, &connSv) setupClientServer(t, rng, &sbCl, &sbSv, &connCl, &connSv)
var buf [2048]byte var buf [2048]byte
nextToSend := &sbCl nextToSend := &sbCl
@@ -37,7 +37,7 @@ func TestBasicStack(t *testing.T) {
func TestBasicStack2(t *testing.T) { func TestBasicStack2(t *testing.T) {
rng := rand.New(rand.NewSource(1)) rng := rand.New(rand.NewSource(1))
var sbCl, sbSv StackIP var sbCl, sbSv StackIP
var connCl, connSv TCPConn var connCl, connSv tcp.Conn
setupClientServerEstablished(t, rng, &sbCl, &sbSv, &connCl, &connSv) setupClientServerEstablished(t, rng, &sbCl, &sbSv, &connCl, &connSv)
} }
@@ -57,7 +57,7 @@ func expectExchange(t *testing.T, from, to *StackIP, buf []byte) {
} }
} }
func setupClientServerEstablished(t *testing.T, rng *rand.Rand, client, server *StackIP, connClient, connServer *TCPConn) { func setupClientServerEstablished(t *testing.T, rng *rand.Rand, client, server *StackIP, connClient, connServer *tcp.Conn) {
t.Helper() t.Helper()
setupClientServer(t, rng, client, server, connClient, connServer) setupClientServer(t, rng, client, server, connClient, connServer)
var buf [2048]byte var buf [2048]byte
@@ -85,7 +85,7 @@ func setupClientServerEstablished(t *testing.T, rng *rand.Rand, client, server *
} }
} }
func setupClientServer(t *testing.T, rng *rand.Rand, client, server *StackIP, connClient, connServer *TCPConn) { func setupClientServer(t *testing.T, rng *rand.Rand, client, server *StackIP, connClient, connServer *tcp.Conn) {
bufsize := 2048 bufsize := 2048
// Ensure buffer sizes are OK with reused buffers. // Ensure buffer sizes are OK with reused buffers.
svip := netip.AddrPortFrom(netip.AddrFrom4([4]byte{192, 168, 1, 0}), 80) svip := netip.AddrPortFrom(netip.AddrFrom4([4]byte{192, 168, 1, 0}), 80)
@@ -93,7 +93,7 @@ func setupClientServer(t *testing.T, rng *rand.Rand, client, server *StackIP, co
server.SetAddr(svip.Addr()) server.SetAddr(svip.Addr())
client.SetAddr(clip.Addr()) client.SetAddr(clip.Addr())
err := connServer.Configure(&TCPConnConfig{ err := connServer.Configure(&tcp.ConnConfig{
RxBuf: make([]byte, bufsize), RxBuf: make([]byte, bufsize),
TxBuf: make([]byte, bufsize), TxBuf: make([]byte, bufsize),
TxPacketQueueSize: 3, TxPacketQueueSize: 3,
@@ -102,7 +102,7 @@ func setupClientServer(t *testing.T, rng *rand.Rand, client, server *StackIP, co
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
err = connClient.Configure(&TCPConnConfig{ err = connClient.Configure(&tcp.ConnConfig{
RxBuf: make([]byte, bufsize), RxBuf: make([]byte, bufsize),
TxBuf: make([]byte, bufsize), TxBuf: make([]byte, bufsize),
TxPacketQueueSize: 3, TxPacketQueueSize: 3,
+47 -81
View File
@@ -1,4 +1,4 @@
package internet package tcp
import ( import (
"bytes" "bytes"
@@ -12,17 +12,19 @@ import (
"github.com/soypat/lneto" "github.com/soypat/lneto"
"github.com/soypat/lneto/internal" "github.com/soypat/lneto/internal"
"github.com/soypat/lneto/ipv4"
"github.com/soypat/lneto/ipv6"
"github.com/soypat/lneto/tcp"
) )
var ( var (
errDeadlineExceeded = os.ErrDeadlineExceeded errDeadlineExceeded = os.ErrDeadlineExceeded
) )
type TCPConn struct { // Conn builds on the [Handler] abstraction and adds IP header knowledge, time management, and familiar user facing API
h tcp.Handler // like Write and Read methods.
//
// Note that the complete emulation of [net.TCPConn] at this level of abstraction is yet a non-goal,
// even though the functionality provided is similar.
type Conn struct {
h Handler
remoteAddr []byte remoteAddr []byte
rdead time.Time rdead time.Time
@@ -35,14 +37,14 @@ type TCPConn struct {
logger logger
} }
type TCPConnConfig struct { type ConnConfig struct {
RxBuf []byte RxBuf []byte
TxBuf []byte TxBuf []byte
TxPacketQueueSize int TxPacketQueueSize int
Logger *slog.Logger Logger *slog.Logger
} }
func (conn *TCPConn) Configure(config *TCPConnConfig) (err error) { func (conn *Conn) Configure(config *ConnConfig) (err error) {
err = conn.h.SetBuffers(config.TxBuf, config.RxBuf, config.TxPacketQueueSize) err = conn.h.SetBuffers(config.TxBuf, config.RxBuf, config.TxPacketQueueSize)
if err != nil { if err != nil {
return err return err
@@ -52,20 +54,22 @@ func (conn *TCPConn) Configure(config *TCPConnConfig) (err error) {
} }
// LocalPort returns the local port on which the socket is listening or connected to. // LocalPort returns the local port on which the socket is listening or connected to.
func (conn *TCPConn) LocalPort() uint16 { return conn.h.LocalPort() } func (conn *Conn) LocalPort() uint16 { return conn.h.LocalPort() }
// RemotePort returns the port of the incoming remote connection. Is non-zero if connection is established. // RemotePort returns the port of the incoming remote connection. Is non-zero if connection is established.
func (conn *TCPConn) RemotePort() uint16 { return conn.h.RemotePort() } func (conn *Conn) RemotePort() uint16 { return conn.h.RemotePort() }
func (conn *Conn) RemoteAddr() []byte { return conn.remoteAddr }
// State returns the TCP state of the socket. // State returns the TCP state of the socket.
func (conn *TCPConn) State() tcp.State { return conn.h.State() } func (conn *Conn) State() State { return conn.h.State() }
// BufferedInput returns the number of bytes in the socket's receive/input buffer. // BufferedInput returns the number of bytes in the socket's receive/input buffer.
func (conn *TCPConn) BufferedInput() int { return conn.h.BufferedInput() } func (conn *Conn) BufferedInput() int { return conn.h.BufferedInput() }
// OpenActive opens a connection to a remote peer with a known IP address and port combination. // OpenActive opens a connection to a remote peer with a known IP address and port combination.
// iss is the initial send sequence number which is ideally a random number which is far away from the last sequence number used on a connection to the same host. // iss is the initial send sequence number which is ideally a random number which is far away from the last sequence number used on a connection to the same host.
func (conn *TCPConn) OpenActive(remote netip.AddrPort, localPort uint16, iss tcp.Value) error { func (conn *Conn) OpenActive(remote netip.AddrPort, localPort uint16, iss Value) error {
err := conn.h.OpenActive(localPort, remote.Port(), iss) err := conn.h.OpenActive(localPort, remote.Port(), iss)
if err != nil { if err != nil {
return err return err
@@ -84,7 +88,7 @@ func (conn *TCPConn) OpenActive(remote netip.AddrPort, localPort uint16, iss tcp
// OpenListen opens a passive connection which listens for the first SYN packet to be received on a local port. // OpenListen opens a passive connection which listens for the first SYN packet to be received on a local port.
// iss is the initial send sequence number which is usually a randomly chosen number. // iss is the initial send sequence number which is usually a randomly chosen number.
func (conn *TCPConn) OpenListen(localPort uint16, iss tcp.Value) error { func (conn *Conn) OpenListen(localPort uint16, iss Value) error {
err := conn.h.OpenListen(localPort, iss) err := conn.h.OpenListen(localPort, iss)
if err != nil { if err != nil {
return err return err
@@ -93,17 +97,27 @@ func (conn *TCPConn) OpenListen(localPort uint16, iss tcp.Value) error {
return nil return nil
} }
func (conn *TCPConn) Close() error { func (conn *Conn) Close() error {
conn.trace("TCPConn.Close") conn.trace("TCPConn.Close")
return conn.h.Close() return conn.h.Close()
} }
func (conn *TCPConn) Demux(buf []byte, off int) (err error) { // Abort terminates all state of the connection forcibly.
func (conn *Conn) Abort() {
conn.h.Abort()
*conn = Conn{
h: conn.h,
remoteAddr: conn.remoteAddr[:0],
logger: conn.logger,
}
}
func (conn *Conn) Demux(buf []byte, off int) (err error) {
conn.trace("tcpconn.Recv:start") conn.trace("tcpconn.Recv:start")
if off >= len(buf) { if off >= len(buf) {
return errors.New("bad offset in TCPConn.Recv") return errors.New("bad offset in TCPConn.Recv")
} }
raddr, id, err := getIPAddr(buf[:off]) raddr, id, err := internal.GetIPSourceAddr(buf[:off])
if err != nil { if err != nil {
return err return err
} }
@@ -122,7 +136,7 @@ func (conn *TCPConn) Demux(buf []byte, off int) (err error) {
} }
// Write writes argument data to the TCPConns's output buffer which is queued to be sent. // Write writes argument data to the TCPConns's output buffer which is queued to be sent.
func (conn *TCPConn) Write(b []byte) (int, error) { func (conn *Conn) Write(b []byte) (int, error) {
err := conn.checkPipeOpen() err := conn.checkPipeOpen()
if err != nil { if err != nil {
return 0, err return 0, err
@@ -164,7 +178,7 @@ func (conn *TCPConn) Write(b []byte) (int, error) {
// Read reads data from the socket's input buffer. If the buffer is empty, // Read reads data from the socket's input buffer. If the buffer is empty,
// Read will block until data is available or connection closes. // Read will block until data is available or connection closes.
func (conn *TCPConn) Read(b []byte) (int, error) { func (conn *Conn) Read(b []byte) (int, error) {
err := conn.checkPipeOpen() err := conn.checkPipeOpen()
if err != nil { if err != nil {
return 0, err return 0, err
@@ -172,7 +186,7 @@ func (conn *TCPConn) Read(b []byte) (int, error) {
conn.trace("TCPConn.Read:start") conn.trace("TCPConn.Read:start")
connid := conn.h.ConnectionID() connid := conn.h.ConnectionID()
backoff := internal.NewBackoff(internal.BackoffTCPConn) backoff := internal.NewBackoff(internal.BackoffTCPConn)
for conn.h.BufferedInput() == 0 && conn.State() == tcp.StateEstablished { for conn.h.BufferedInput() == 0 && conn.State() == StateEstablished {
if conn.abortErr != nil { if conn.abortErr != nil {
return 0, conn.abortErr return 0, conn.abortErr
} else if connid != conn.h.ConnectionID() { } else if connid != conn.h.ConnectionID() {
@@ -187,7 +201,7 @@ func (conn *TCPConn) Read(b []byte) (int, error) {
return n, err return n, err
} }
func (conn *TCPConn) checkPipeOpen() error { func (conn *Conn) checkPipeOpen() error {
if conn.abortErr != nil { if conn.abortErr != nil {
return conn.abortErr return conn.abortErr
} }
@@ -198,11 +212,11 @@ func (conn *TCPConn) checkPipeOpen() error {
return nil return nil
} }
func (conn *TCPConn) Encapsulate(buf []byte, off int) (n int, err error) { func (conn *Conn) Encapsulate(buf []byte, off int) (n int, err error) {
if len(conn.remoteAddr) == 0 { if len(conn.remoteAddr) == 0 {
return 0, errors.New("unset IP address") return 0, errors.New("unset IP address")
} }
raddr, _, err := getIPAddr(buf[:off]) raddr, _, err := internal.GetIPSourceAddr(buf[:off])
if err != nil { if err != nil {
return 0, err return 0, err
} else if len(raddr) != len(conn.remoteAddr) { } else if len(raddr) != len(conn.remoteAddr) {
@@ -212,8 +226,7 @@ func (conn *TCPConn) Encapsulate(buf []byte, off int) (n int, err error) {
if err != nil { if err != nil {
return 0, err return 0, err
} }
err = internal.SetIPDestinationAddr(buf[:off], conn.ipID, conn.remoteAddr)
err = setDstAddr(buf[:off], conn.ipID, conn.remoteAddr)
if err != nil { if err != nil {
return 0, err return 0, err
} }
@@ -221,63 +234,16 @@ func (conn *TCPConn) Encapsulate(buf []byte, off int) (n int, err error) {
return n, nil return n, nil
} }
func (conn *TCPConn) Protocol() uint64 { func (conn *Conn) Protocol() uint64 {
return uint64(lneto.IPProtoTCP) return uint64(lneto.IPProtoTCP)
} }
func getIPAddr(buf []byte) (addr []byte, id uint16, err error) { func (conn *Conn) isRaddrSet() bool {
switch buf[0] >> 4 {
case 4:
ifrm4, err := ipv4.NewFrame(buf)
if err != nil {
return addr, 0, err
}
addr = ifrm4.SourceAddr()[:]
id = ifrm4.ID()
case 6:
ifrm6, err := ipv6.NewFrame(buf)
if err != nil {
return addr, 0, err
}
addr = ifrm6.SourceAddr()[:]
default:
err = errors.New("unsupported IP version")
}
return addr, id, err
}
func setDstAddr(buf []byte, id uint16, addr []byte) (err error) {
var dstaddr []byte
switch buf[0] >> 4 {
case 4:
ifrm4, err := ipv4.NewFrame(buf)
if err != nil {
return err
}
dstaddr = ifrm4.DestinationAddr()[:]
ifrm4.SetID(id)
case 6:
ifrm6, err := ipv6.NewFrame(buf)
if err != nil {
return err
}
dstaddr = ifrm6.DestinationAddr()[:]
default:
err = errors.New("unsupported IP version")
}
if err == nil && len(dstaddr) != len(addr) {
return errors.New("invalid ip version to setDstAddr")
}
copy(dstaddr, addr)
return nil
}
func (conn *TCPConn) isRaddrSet() bool {
return len(conn.remoteAddr) != 0 return len(conn.remoteAddr) != 0
} }
func (conn *TCPConn) reset(h tcp.Handler) { func (conn *Conn) reset(h Handler) {
*conn = TCPConn{ *conn = Conn{
h: h, h: h,
remoteAddr: conn.remoteAddr[:0], remoteAddr: conn.remoteAddr[:0],
logger: conn.logger, logger: conn.logger,
@@ -287,7 +253,7 @@ func (conn *TCPConn) reset(h tcp.Handler) {
// SetDeadline sets the read and write deadlines associated // SetDeadline sets the read and write deadlines associated
// with the connection. It is equivalent to calling both // with the connection. It is equivalent to calling both
// SetReadDeadline and SetWriteDeadline. Implements [net.Conn]. // SetReadDeadline and SetWriteDeadline. Implements [net.Conn].
func (conn *TCPConn) SetDeadline(t time.Time) error { func (conn *Conn) SetDeadline(t time.Time) error {
err := conn.SetReadDeadline(t) err := conn.SetReadDeadline(t)
if err != nil { if err != nil {
return err return err
@@ -297,7 +263,7 @@ func (conn *TCPConn) SetDeadline(t time.Time) error {
// SetReadDeadline sets the deadline for future Read calls // SetReadDeadline sets the deadline for future Read calls
// and any currently-blocked Read call. A zero value for t means Read will not time out. // and any currently-blocked Read call. A zero value for t means Read will not time out.
func (conn *TCPConn) SetReadDeadline(t time.Time) error { func (conn *Conn) SetReadDeadline(t time.Time) error {
conn.trace("TCPConn.SetReadDeadline:start") conn.trace("TCPConn.SetReadDeadline:start")
err := conn.checkPipeOpen() err := conn.checkPipeOpen()
if err == nil { if err == nil {
@@ -311,7 +277,7 @@ func (conn *TCPConn) SetReadDeadline(t time.Time) error {
// Even if write times out, it may return n > 0, indicating that // Even if write times out, it may return n > 0, indicating that
// some of the data was successfully written. // some of the data was successfully written.
// A zero value for t means Write will not time out. // A zero value for t means Write will not time out.
func (conn *TCPConn) SetWriteDeadline(t time.Time) error { func (conn *Conn) SetWriteDeadline(t time.Time) error {
conn.trace("TCPConn.SetWriteDeadline:start") conn.trace("TCPConn.SetWriteDeadline:start")
err := conn.checkPipeOpen() err := conn.checkPipeOpen()
if err == nil { if err == nil {
@@ -320,10 +286,10 @@ func (conn *TCPConn) SetWriteDeadline(t time.Time) error {
return err return err
} }
func (conn *TCPConn) deadlineExceeded(deadline time.Time) bool { func (conn *Conn) deadlineExceeded(deadline time.Time) bool {
return !deadline.IsZero() && time.Since(deadline) > 0 return !deadline.IsZero() && time.Since(deadline) > 0
} }
func (conn *TCPConn) ConnectionID() *uint64 { func (conn *Conn) ConnectionID() *uint64 {
return conn.h.ConnectionID() return conn.h.ConnectionID()
} }
+4
View File
@@ -19,6 +19,10 @@ import (
// A ControlBlock's internal state is modified by the available "System Calls" as defined in // A ControlBlock's internal state is modified by the available "System Calls" as defined in
// RFC9293, such as Close, Listen/Open, Send, and Receive. // RFC9293, such as Close, Listen/Open, Send, and Receive.
// Sent and received data is represented with the [Segment] struct type. // Sent and received data is represented with the [Segment] struct type.
//
// Note that [ControlBlock] is the lowest level implementation of TCP and as such is missing most useful functionality.
// See [Handler], which uses ControlBlock, for a higher level implementation. [Conn] is an even higher level implementation
// which makes use of a [Handler].
type ControlBlock struct { type ControlBlock struct {
// # Send Sequence Space // # Send Sequence Space
// //
+9 -2
View File
@@ -20,6 +20,8 @@ var (
// related to data buffering, frame sequencing and connection state handling. // related to data buffering, frame sequencing and connection state handling.
// Does NOT implement IP related logic, so no CRC calculation/validation or pseudo header logic. // Does NOT implement IP related logic, so no CRC calculation/validation or pseudo header logic.
// Does NOT implement connection lifetime handling, so NO deadlines, keepalives, backoffs or anything that requires use of time package. // Does NOT implement connection lifetime handling, so NO deadlines, keepalives, backoffs or anything that requires use of time package.
//
// See [Conn] for a higher level abstraction of a TCP connection, and see [ControlBlock] for the lower level bits of a TCP connection.
type Handler struct { type Handler struct {
connid uint64 connid uint64
scb ControlBlock scb ControlBlock
@@ -117,10 +119,10 @@ func (h *Handler) Abort() {
func (h *Handler) reset(localPort, remotePort uint16, iss Value) { func (h *Handler) reset(localPort, remotePort uint16, iss Value) {
*h = Handler{ *h = Handler{
connid: h.connid + 1,
scb: h.scb, scb: h.scb,
bufTx: h.bufTx, bufTx: h.bufTx,
bufRx: h.bufRx, bufRx: h.bufRx,
connid: h.connid + 1,
localPort: localPort, localPort: localPort,
remotePort: remotePort, remotePort: remotePort,
validator: h.validator, validator: h.validator,
@@ -300,7 +302,12 @@ func (h *Handler) BufferedInput() int {
return h.bufRx.Buffered() return h.bufRx.Buffered()
} }
// AwaitingSynResponse checks if the Handler is waiting for a Syn to arrive. // InUse returns true if the connection has been initialized and is being used to reach a remote port or if it is awaiting a remote packet.
func (h *Handler) InUse() bool {
return h.remotePort != 0 || !h.State().IsClosed()
}
// AwaitingSynResponse returns true if the Handler is an active client opened with [Handler.OpenActive] and has already sent out the first SYN packet to the remote client.
func (h *Handler) AwaitingSynResponse() bool { func (h *Handler) AwaitingSynResponse() bool {
return h.remotePort != 0 && h.scb.State() == StateSynSent return h.remotePort != 0 && h.scb.State() == StateSynSent
} }