mirror of
https://github.com/soypat/lneto.git
synced 2026-09-10 08:39:30 +00:00
mega refactor internet.TCPConn->tcp.Conn
This commit is contained in:
+54
-56
@@ -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) {
|
||||||
|
|||||||
@@ -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,
|
||||||
|
|||||||
@@ -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
|
||||||
|
}
|
||||||
@@ -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]
|
||||||
|
}
|
||||||
@@ -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
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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,
|
||||||
|
|||||||
@@ -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()
|
||||||
}
|
}
|
||||||
@@ -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
@@ -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
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user