mirror of
https://github.com/soypat/lneto.git
synced 2026-08-27 09:59:04 +00:00
all in on StackNode refactor: Encapsulate/Demux on all stacks+ConnID+more abstraction
This commit is contained in:
+1
-1
@@ -39,7 +39,7 @@ func (efrm Frame) HeaderLength() int {
|
|||||||
return sizeHeaderNoVLAN
|
return sizeHeaderNoVLAN
|
||||||
}
|
}
|
||||||
|
|
||||||
// Payload returns the data portion of the ethernet packet with handling of VLAN packets.
|
// Payload returns the data portion of the ethernet packet with correct handling of VLAN packets.
|
||||||
func (efrm Frame) Payload() []byte {
|
func (efrm Frame) Payload() []byte {
|
||||||
hl := efrm.HeaderLength()
|
hl := efrm.HeaderLength()
|
||||||
et := efrm.EtherTypeOrSize()
|
et := efrm.EtherTypeOrSize()
|
||||||
|
|||||||
@@ -169,21 +169,17 @@ func NewEthernetTCPStack(ourMAC, gwMAC [6]byte, ip netip.AddrPort, mtu uint16, s
|
|||||||
gwmac: gwMAC,
|
gwmac: gwMAC,
|
||||||
}
|
}
|
||||||
|
|
||||||
var ipStack internet.StackBasic
|
var ipStack internet.StackIP
|
||||||
addr := ip.Addr()
|
addr := ip.Addr()
|
||||||
addr4 := addr.As4()
|
addr4 := addr.As4()
|
||||||
_ = addr4
|
_ = addr4
|
||||||
ipStack.SetAddr(addr)
|
ipStack.SetAddr(addr)
|
||||||
lStack.Register(handler{
|
lStack.Register(handler{
|
||||||
raddr: nil, //addr4[:],
|
raddr: nil, //addr4[:],
|
||||||
recv: func(b []byte, i int) error {
|
recv: ipStack.Demux,
|
||||||
return ipStack.Recv(b[i:])
|
handle: ipStack.Encapsulate,
|
||||||
},
|
proto: ethernet.TypeIPv4,
|
||||||
handle: func(b []byte, i int) (int, error) {
|
lport: 0,
|
||||||
return ipStack.Handle(b[i:])
|
|
||||||
},
|
|
||||||
proto: ethernet.TypeIPv4,
|
|
||||||
lport: 0,
|
|
||||||
})
|
})
|
||||||
var conn internet.TCPConn
|
var conn internet.TCPConn
|
||||||
err = conn.Configure(&internet.TCPConnConfig{
|
err = conn.Configure(&internet.TCPConnConfig{
|
||||||
|
|||||||
@@ -0,0 +1,83 @@
|
|||||||
|
package internet
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"math"
|
||||||
|
"net"
|
||||||
|
"slices"
|
||||||
|
)
|
||||||
|
|
||||||
|
// StackNode is an abstraction of a packet exchanging protocol controller. This is the building block for all protocols,
|
||||||
|
// from Ethernet to IP to TCP, practically any protocol can be expressed as a StackNode and function completely.
|
||||||
|
type StackNode interface {
|
||||||
|
// Encapsulate writes the stack node's frame into carrierData[frameOffset:]
|
||||||
|
// along with any other frame or payload the stack node encapsulates.
|
||||||
|
// The returned integer is amount of bytes written such that carrierData[frameOffset:frameOffset+n]
|
||||||
|
// contains written data. Data inside carrierData[:frameOffset] usually contains data necessary for
|
||||||
|
// a StackNode to correctly emit valid frame data: such is the case for TCP packets which require IP
|
||||||
|
// frame data for checksum calculation. Thus StackNodes must provide fields in their own frame
|
||||||
|
// required by sub-stacknodes for correct encapsulation; in the case of IPv4/6 this means including fields
|
||||||
|
// used in pseudo-header checksum like local IP (see [ipv4.CRCWriteUDPPseudo]).
|
||||||
|
//
|
||||||
|
// When [net.ErrClosed] is returned the StackNode should be discarded and any written data passed up normally.
|
||||||
|
// Errors returned by Encapsulate are "extraordinary" and should not be returned unless the StackNode is receiving invalid carrierData/frameOffset.
|
||||||
|
Encapsulate(carrierData []byte, frameOffset int) (int, error)
|
||||||
|
// Demux reads from the argument buffer where frameOffset is the offset of this StackNode's frame first byte.
|
||||||
|
// The stack node then dispatches(demuxes) the encapsulated frames to its corresponding sub-stack-node(s).
|
||||||
|
//
|
||||||
|
Demux(carrierData []byte, frameOffset int) error
|
||||||
|
LocalPort() uint16
|
||||||
|
Protocol() uint64
|
||||||
|
ConnectionID() *uint64
|
||||||
|
// SetFlagPending(flagPending func(numPendingEncapsulations int))
|
||||||
|
}
|
||||||
|
|
||||||
|
// node is a concrete StackNode as stored in Stacks. Methods are devirtualized for performance benefits, especially on TinyGo.
|
||||||
|
type node struct {
|
||||||
|
currConnID uint64
|
||||||
|
connID *uint64
|
||||||
|
demux func([]byte, int) error
|
||||||
|
encapsulate func([]byte, int) (int, error)
|
||||||
|
lastErrs [2]error
|
||||||
|
proto uint16
|
||||||
|
port uint16
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
errZeroPort = errors.New("port must be greater than zero")
|
||||||
|
errInvalidProto = errors.New("invalid protocol")
|
||||||
|
errProtoRegistered = errors.New("protocol already registered")
|
||||||
|
_ = net.ErrClosed
|
||||||
|
)
|
||||||
|
|
||||||
|
func handleNodeError(nodes *[]node, nodeIdx int, err error) {
|
||||||
|
if err != nil {
|
||||||
|
badConnID := (*nodes)[nodeIdx].connID != nil && *(*nodes)[nodeIdx].connID != (*nodes)[nodeIdx].currConnID
|
||||||
|
if err == net.ErrClosed || (*nodes)[nodeIdx].lastErrs[0] == err || (*nodes)[nodeIdx].lastErrs[1] == err || badConnID {
|
||||||
|
*nodes = slices.Delete(*nodes, nodeIdx, nodeIdx+1)
|
||||||
|
} else {
|
||||||
|
// Advance Queue of errors
|
||||||
|
(*nodes)[nodeIdx].lastErrs[1] = (*nodes)[nodeIdx].lastErrs[0]
|
||||||
|
(*nodes)[nodeIdx].lastErrs[0] = err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func addNode(nodes *[]node, h StackNode, port uint16, protocol uint64) {
|
||||||
|
if protocol > math.MaxUint16 {
|
||||||
|
panic(">16bit protocol number unsupported")
|
||||||
|
}
|
||||||
|
var currConnID uint64
|
||||||
|
connIDPtr := h.ConnectionID()
|
||||||
|
if connIDPtr != nil {
|
||||||
|
currConnID = *connIDPtr
|
||||||
|
}
|
||||||
|
*nodes = append(*nodes, node{
|
||||||
|
currConnID: currConnID,
|
||||||
|
connID: connIDPtr,
|
||||||
|
demux: h.Demux,
|
||||||
|
encapsulate: h.Encapsulate,
|
||||||
|
proto: uint16(protocol),
|
||||||
|
port: port,
|
||||||
|
})
|
||||||
|
}
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
package internet
|
||||||
|
|
||||||
|
type ARPEndpoint struct {
|
||||||
|
}
|
||||||
@@ -1,14 +0,0 @@
|
|||||||
package internet
|
|
||||||
|
|
||||||
import "github.com/soypat/lneto"
|
|
||||||
|
|
||||||
type PortStack struct {
|
|
||||||
handlers []porthandler
|
|
||||||
proto lneto.IPProto
|
|
||||||
}
|
|
||||||
|
|
||||||
type porthandler struct {
|
|
||||||
recv func([]byte, int) error
|
|
||||||
handle func([]byte, int) (int, error)
|
|
||||||
port uint16
|
|
||||||
}
|
|
||||||
@@ -9,38 +9,64 @@ import (
|
|||||||
"slices"
|
"slices"
|
||||||
|
|
||||||
"github.com/soypat/lneto"
|
"github.com/soypat/lneto"
|
||||||
|
"github.com/soypat/lneto/ethernet"
|
||||||
"github.com/soypat/lneto/internal"
|
"github.com/soypat/lneto/internal"
|
||||||
"github.com/soypat/lneto/ipv4"
|
"github.com/soypat/lneto/ipv4"
|
||||||
"github.com/soypat/lneto/tcp"
|
"github.com/soypat/lneto/tcp"
|
||||||
)
|
)
|
||||||
|
|
||||||
type StackBasic struct {
|
var _ StackNode = (*StackIP)(nil)
|
||||||
|
|
||||||
|
type StackIP struct {
|
||||||
|
connID uint64
|
||||||
ip [4]byte
|
ip [4]byte
|
||||||
validator lneto.Validator
|
validator lneto.Validator
|
||||||
handlers []handler
|
handlers []node
|
||||||
logger
|
logger
|
||||||
}
|
}
|
||||||
|
|
||||||
type handler struct {
|
func (sb *StackIP) Reset(addr netip.Addr) error {
|
||||||
recv func([]byte, int) error
|
err := sb.SetAddr(addr)
|
||||||
handle func([]byte, int) (int, error)
|
if err != nil {
|
||||||
proto lneto.IPProto
|
return err
|
||||||
port uint16
|
}
|
||||||
|
*sb = StackIP{
|
||||||
|
connID: sb.connID + 1,
|
||||||
|
validator: sb.validator,
|
||||||
|
handlers: sb.handlers[:0],
|
||||||
|
logger: sb.logger,
|
||||||
|
ip: sb.ip,
|
||||||
|
}
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (sb *StackBasic) SetAddr(addr netip.Addr) {
|
func (sb *StackIP) SetAddr(addr netip.Addr) error {
|
||||||
if !addr.Is4() {
|
if !addr.IsValid() {
|
||||||
panic("only support IPv4")
|
return errors.New("invalid IP")
|
||||||
|
} else if !addr.Is4() {
|
||||||
|
return errors.New("require IPv4")
|
||||||
}
|
}
|
||||||
sb.ip = addr.As4()
|
sb.ip = addr.As4()
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (sb *StackBasic) Addr() netip.Addr {
|
func (sb *StackIP) ConnectionID() *uint64 {
|
||||||
|
return &sb.connID
|
||||||
|
}
|
||||||
|
|
||||||
|
func (sb *StackIP) Protocol() uint64 {
|
||||||
|
return uint64(ethernet.TypeIPv4) // Only support ipv4 for now.
|
||||||
|
}
|
||||||
|
|
||||||
|
func (sb *StackIP) LocalPort() uint16 { return 0 }
|
||||||
|
|
||||||
|
func (sb *StackIP) Addr() netip.Addr {
|
||||||
return netip.AddrFrom4(sb.ip)
|
return netip.AddrFrom4(sb.ip)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (sb *StackBasic) Recv(frame []byte) error {
|
func (sb *StackIP) Demux(carrierData []byte, offset int) error {
|
||||||
sb.info("StackBasic.Recv:start")
|
sb.info("StackIP.Demux:start")
|
||||||
|
frame := carrierData[offset:] // we don't care about carrier data in IP.
|
||||||
ifrm, err := ipv4.NewFrame(frame)
|
ifrm, err := ipv4.NewFrame(frame)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
@@ -58,7 +84,7 @@ func (sb *StackBasic) Recv(frame []byte) error {
|
|||||||
gotCRC := ifrm.CRC()
|
gotCRC := ifrm.CRC()
|
||||||
wantCRC := ifrm.CalculateHeaderCRC()
|
wantCRC := ifrm.CalculateHeaderCRC()
|
||||||
if gotCRC != wantCRC {
|
if gotCRC != wantCRC {
|
||||||
sb.error("IPv4Stack:Recv:crc-mismatch", slog.Uint64("want", uint64(wantCRC)), slog.Uint64("got", uint64(gotCRC)))
|
sb.error("StackIP:Demux:crc-mismatch", slog.Uint64("want", uint64(wantCRC)), slog.Uint64("got", uint64(gotCRC)))
|
||||||
return errors.New("IPv4 CRC mismatch")
|
return errors.New("IPv4 CRC mismatch")
|
||||||
}
|
}
|
||||||
off := ifrm.HeaderLength()
|
off := ifrm.HeaderLength()
|
||||||
@@ -66,9 +92,9 @@ func (sb *StackBasic) Recv(frame []byte) error {
|
|||||||
for i := range sb.handlers {
|
for i := range sb.handlers {
|
||||||
h := &sb.handlers[i]
|
h := &sb.handlers[i]
|
||||||
proto := ifrm.Protocol()
|
proto := ifrm.Protocol()
|
||||||
if h.proto == proto {
|
if h.proto == uint16(proto) {
|
||||||
sb.info("iprecv", slog.String("ipproto", proto.String()), slog.Int("plen", int(totalLen)))
|
sb.info("ipDemux", slog.String("ipproto", proto.String()), slog.Int("plen", int(totalLen)))
|
||||||
err = h.recv(frame[:totalLen], off)
|
err = h.demux(frame[:totalLen], off)
|
||||||
if err == net.ErrClosed {
|
if err == net.ErrClosed {
|
||||||
sb.info("ipclose", slog.String("proto", proto.String()))
|
sb.info("ipclose", slog.String("proto", proto.String()))
|
||||||
sb.handlers = slices.Delete(sb.handlers, i, i+1)
|
sb.handlers = slices.Delete(sb.handlers, i, i+1)
|
||||||
@@ -83,7 +109,8 @@ DROP:
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (sb *StackBasic) Handle(frame []byte) (int, error) {
|
func (sb *StackIP) Encapsulate(carrierData []byte, frameOffset int) (int, error) {
|
||||||
|
frame := carrierData[frameOffset:]
|
||||||
if len(frame) < 256 {
|
if len(frame) < 256 {
|
||||||
return 0, io.ErrShortBuffer
|
return 0, io.ErrShortBuffer
|
||||||
}
|
}
|
||||||
@@ -91,14 +118,15 @@ func (sb *StackBasic) Handle(frame []byte) (int, error) {
|
|||||||
const ihl = 5
|
const ihl = 5
|
||||||
const headerlen = ihl * 4
|
const headerlen = ihl * 4
|
||||||
ifrm.SetVersionAndIHL(4, 5)
|
ifrm.SetVersionAndIHL(4, 5)
|
||||||
*ifrm.SourceAddr() = sb.ip
|
|
||||||
ifrm.SetToS(0)
|
ifrm.SetToS(0)
|
||||||
ifrm.SetID(0)
|
ifrm.SetID(0)
|
||||||
|
*ifrm.SourceAddr() = sb.ip
|
||||||
for i := range sb.handlers {
|
for i := range sb.handlers {
|
||||||
h := &sb.handlers[i]
|
h := &sb.handlers[i]
|
||||||
n, err := h.handle(frame[:], headerlen)
|
proto := lneto.IPProto(h.proto)
|
||||||
|
n, err := h.encapsulate(frame[:], headerlen)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
sb.error("IPv4Stack:handle", slog.String("proto", h.proto.String()), slog.String("err", err.Error()))
|
sb.error("StackIP:handle", slog.String("proto", proto.String()), slog.String("err", err.Error()))
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
if n > 0 {
|
if n > 0 {
|
||||||
@@ -107,7 +135,7 @@ func (sb *StackBasic) Handle(frame []byte) (int, error) {
|
|||||||
ifrm.SetTotalLength(uint16(totalLen))
|
ifrm.SetTotalLength(uint16(totalLen))
|
||||||
ifrm.SetFlags(dontFrag)
|
ifrm.SetFlags(dontFrag)
|
||||||
ifrm.SetTTL(64)
|
ifrm.SetTTL(64)
|
||||||
ifrm.SetProtocol(h.proto)
|
ifrm.SetProtocol(proto)
|
||||||
ifrm.SetCRC(ifrm.CalculateHeaderCRC())
|
ifrm.SetCRC(ifrm.CalculateHeaderCRC())
|
||||||
if ifrm.Protocol() == lneto.IPProtoTCP {
|
if ifrm.Protocol() == lneto.IPProtoTCP {
|
||||||
var crc lneto.CRC791
|
var crc lneto.CRC791
|
||||||
@@ -115,7 +143,7 @@ func (sb *StackBasic) Handle(frame []byte) (int, error) {
|
|||||||
tfrm, _ := tcp.NewFrame(ifrm.Payload())
|
tfrm, _ := tcp.NewFrame(ifrm.Payload())
|
||||||
tfrm.CRCWrite(&crc)
|
tfrm.CRCWrite(&crc)
|
||||||
tfrm.SetCRC(crc.Sum16())
|
tfrm.SetCRC(crc.Sum16())
|
||||||
sb.info("IPv4Stack:send", slog.String("ip", ifrm.String()), slog.String("tcp", tfrm.String()))
|
sb.info("StackIP:send", slog.String("ip", ifrm.String()), slog.String("tcp", tfrm.String()))
|
||||||
}
|
}
|
||||||
return totalLen, nil
|
return totalLen, nil
|
||||||
}
|
}
|
||||||
@@ -123,15 +151,32 @@ func (sb *StackBasic) Handle(frame []byte) (int, error) {
|
|||||||
return 0, nil
|
return 0, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (sb *StackBasic) RegisterTCPConn(conn *TCPConn) error {
|
func (sb *StackIP) Register(h StackNode) error {
|
||||||
if conn.LocalPort() == 0 {
|
port := h.LocalPort()
|
||||||
return errors.New("undefined local port")
|
proto := h.Protocol()
|
||||||
|
if port <= 0 {
|
||||||
|
return errZeroPort
|
||||||
|
} else if proto > 255 {
|
||||||
|
return errInvalidProto
|
||||||
}
|
}
|
||||||
sb.handlers = append(sb.handlers, handler{
|
sb.handlers = append(sb.handlers, node{
|
||||||
recv: conn.RecvIP,
|
demux: h.Demux,
|
||||||
handle: conn.HandleIP,
|
encapsulate: h.Encapsulate,
|
||||||
proto: lneto.IPProtoTCP,
|
proto: uint16(proto),
|
||||||
port: conn.LocalPort(),
|
port: port,
|
||||||
|
})
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (sb *StackIP) RegisterTCPConn(conn *TCPConn) error {
|
||||||
|
if conn.LocalPort() == 0 {
|
||||||
|
return errZeroPort
|
||||||
|
}
|
||||||
|
sb.handlers = append(sb.handlers, node{
|
||||||
|
demux: conn.RecvIP,
|
||||||
|
encapsulate: conn.HandleIP,
|
||||||
|
proto: uint16(lneto.IPProtoTCP),
|
||||||
|
port: conn.LocalPort(),
|
||||||
})
|
})
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,118 @@
|
|||||||
|
package internet
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"io"
|
||||||
|
"log/slog"
|
||||||
|
"math"
|
||||||
|
"net"
|
||||||
|
|
||||||
|
"github.com/soypat/lneto"
|
||||||
|
"github.com/soypat/lneto/ethernet"
|
||||||
|
)
|
||||||
|
|
||||||
|
type StackLinkLayer struct {
|
||||||
|
connID uint64
|
||||||
|
handlers []node
|
||||||
|
logger
|
||||||
|
mac [6]byte
|
||||||
|
gwmac [6]byte
|
||||||
|
mtu uint16
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ls *StackLinkLayer) Reset6(mac, gateway [6]byte, mtu int) error {
|
||||||
|
if mtu > math.MaxUint16 || mtu < 256 {
|
||||||
|
return errors.New("invalid MTU")
|
||||||
|
}
|
||||||
|
*ls = StackLinkLayer{
|
||||||
|
connID: ls.connID + 1,
|
||||||
|
handlers: ls.handlers[:0],
|
||||||
|
logger: ls.logger,
|
||||||
|
mac: mac,
|
||||||
|
gwmac: gateway,
|
||||||
|
mtu: uint16(mtu),
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ls *StackLinkLayer) ConnectionID() *uint64 { return &ls.connID }
|
||||||
|
|
||||||
|
func (ls *StackLinkLayer) LocalPort() uint16 { return 0 }
|
||||||
|
|
||||||
|
func (ls *StackLinkLayer) Protocol() uint64 { return 1 }
|
||||||
|
|
||||||
|
func (ls *StackLinkLayer) Register(h StackNode) error {
|
||||||
|
proto := h.Protocol()
|
||||||
|
if proto > math.MaxUint16 || proto <= 1500 {
|
||||||
|
return errInvalidProto
|
||||||
|
}
|
||||||
|
eproto := uint16(proto)
|
||||||
|
for i := range ls.handlers {
|
||||||
|
hgot := &ls.handlers[i]
|
||||||
|
if hgot.proto == eproto {
|
||||||
|
return errProtoRegistered
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ls.handlers = append(ls.handlers, node{
|
||||||
|
demux: h.Demux,
|
||||||
|
encapsulate: h.Encapsulate,
|
||||||
|
proto: eproto,
|
||||||
|
})
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ls *StackLinkLayer) Demux(carrierData []byte, frameOffset int) (err error) {
|
||||||
|
pkt := carrierData[frameOffset:]
|
||||||
|
efrm, err := ethernet.NewFrame(pkt)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
etype := efrm.EtherTypeOrSize()
|
||||||
|
dstaddr := efrm.DestinationHardwareAddr()
|
||||||
|
var vld lneto.Validator
|
||||||
|
if !efrm.IsBroadcast() && ls.mac != *dstaddr {
|
||||||
|
goto DROP
|
||||||
|
}
|
||||||
|
efrm.ValidateSize(&vld)
|
||||||
|
if vld.HasError() {
|
||||||
|
return vld.Err()
|
||||||
|
}
|
||||||
|
|
||||||
|
for i := range ls.handlers {
|
||||||
|
h := &ls.handlers[i]
|
||||||
|
if h.proto == uint16(etype) {
|
||||||
|
return h.demux(efrm.Payload(), 0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
DROP:
|
||||||
|
ls.info("LinkStack:drop-packet", slog.String("dsthw", net.HardwareAddr(dstaddr[:]).String()), slog.String("ethertype", efrm.EtherTypeOrSize().String()))
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ls *StackLinkLayer) Encapsulate(carrierData []byte, frameOffset int) (n int, err error) {
|
||||||
|
mtu := ls.mtu
|
||||||
|
dst := carrierData[frameOffset:]
|
||||||
|
if len(dst) < int(mtu) {
|
||||||
|
return 0, io.ErrShortBuffer
|
||||||
|
}
|
||||||
|
efrm, err := ethernet.NewFrame(dst)
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
*efrm.DestinationHardwareAddr() = ls.gwmac
|
||||||
|
for i := range ls.handlers {
|
||||||
|
h := &ls.handlers[i]
|
||||||
|
n, err = h.encapsulate(dst[:mtu], 14)
|
||||||
|
if err != nil {
|
||||||
|
ls.error("handling", slog.String("proto", ethernet.Type(h.proto).String()), slog.String("err", err.Error()))
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if n > 0 {
|
||||||
|
// Found packet
|
||||||
|
*efrm.SourceHardwareAddr() = ls.mac
|
||||||
|
efrm.SetEtherType(ethernet.Type(h.proto))
|
||||||
|
return n + 14, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
@@ -0,0 +1,81 @@
|
|||||||
|
package internet
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/binary"
|
||||||
|
"io"
|
||||||
|
)
|
||||||
|
|
||||||
|
type StackPort struct {
|
||||||
|
handlers []node
|
||||||
|
dstPortOff int
|
||||||
|
protocol uint64
|
||||||
|
connID uint64
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ps *StackPort) Reset(protocol uint64, dstPortOffset int) {
|
||||||
|
*ps = StackPort{
|
||||||
|
connID: ps.connID + 1,
|
||||||
|
handlers: ps.handlers[:0],
|
||||||
|
dstPortOff: dstPortOffset,
|
||||||
|
protocol: protocol,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
func (ps *StackPort) LocalPort() uint16 { return 0 }
|
||||||
|
|
||||||
|
func (ps *StackPort) Protocol() uint64 { return ps.protocol }
|
||||||
|
|
||||||
|
func (ps *StackPort) ConnectionID() *uint64 { return &ps.connID }
|
||||||
|
|
||||||
|
func (ps *StackPort) Encapsulate(b []byte, offset int) (n int, err error) {
|
||||||
|
if ps.dstPortOff+offset+2 > len(b) {
|
||||||
|
return 0, io.ErrShortBuffer
|
||||||
|
}
|
||||||
|
var i int
|
||||||
|
for i = 0; i < len(ps.handlers); i++ {
|
||||||
|
n, err = ps.handlers[i].encapsulate(b, offset)
|
||||||
|
if err != nil || n > 0 {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ps.handleResult(i, n, err)
|
||||||
|
return n, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ps *StackPort) Demux(b []byte, offset int) (err error) {
|
||||||
|
if ps.dstPortOff+offset+2 > len(b) {
|
||||||
|
return io.ErrShortBuffer
|
||||||
|
}
|
||||||
|
port := binary.BigEndian.Uint16(b[ps.dstPortOff+offset:])
|
||||||
|
var i int
|
||||||
|
for i = 0; i < len(ps.handlers); i++ {
|
||||||
|
if port != ps.handlers[i].port {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
err = ps.handlers[i].demux(b, offset)
|
||||||
|
if err != nil {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ps.handleResult(i, 0, err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ps *StackPort) Register(h StackNode) error {
|
||||||
|
port := h.LocalPort()
|
||||||
|
proto := h.Protocol()
|
||||||
|
if port <= 0 {
|
||||||
|
return errZeroPort
|
||||||
|
} else if proto != ps.protocol {
|
||||||
|
return errInvalidProto
|
||||||
|
}
|
||||||
|
ps.handlers = append(ps.handlers, node{
|
||||||
|
demux: h.Demux,
|
||||||
|
encapsulate: h.Encapsulate,
|
||||||
|
port: uint16(port),
|
||||||
|
})
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ps *StackPort) handleResult(handlerIdx, n int, err error) {
|
||||||
|
handleNodeError(&ps.handlers, handlerIdx, err)
|
||||||
|
}
|
||||||
@@ -10,7 +10,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 StackBasic
|
var sbCl, sbSv StackIP
|
||||||
var connCl, connSv TCPConn
|
var connCl, connSv TCPConn
|
||||||
setupClientServer(t, rng, &sbCl, &sbSv, &connCl, &connSv)
|
setupClientServer(t, rng, &sbCl, &sbSv, &connCl, &connSv)
|
||||||
var buf [2048]byte
|
var buf [2048]byte
|
||||||
@@ -36,28 +36,28 @@ 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 StackBasic
|
var sbCl, sbSv StackIP
|
||||||
var connCl, connSv TCPConn
|
var connCl, connSv TCPConn
|
||||||
setupClientServerEstablished(t, rng, &sbCl, &sbSv, &connCl, &connSv)
|
setupClientServerEstablished(t, rng, &sbCl, &sbSv, &connCl, &connSv)
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func expectExchange(t *testing.T, from, to *StackBasic, buf []byte) {
|
func expectExchange(t *testing.T, from, to *StackIP, buf []byte) {
|
||||||
t.Helper()
|
t.Helper()
|
||||||
n, err := from.Handle(buf)
|
n, err := from.Encapsulate(buf, 0)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Error("expectExchange:Handle:", err)
|
t.Error("expectExchange:Handle:", err)
|
||||||
} else if n == 0 {
|
} else if n == 0 {
|
||||||
t.Error("expected data exchange")
|
t.Error("expected data exchange")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
err = to.Recv(buf[:n])
|
err = to.Demux(buf[:n], 0)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Error("expectExchange:Recv:", err)
|
t.Error("expectExchange:Recv:", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func setupClientServerEstablished(t *testing.T, rng *rand.Rand, client, server *StackBasic, connClient, connServer *TCPConn) {
|
func setupClientServerEstablished(t *testing.T, rng *rand.Rand, client, server *StackIP, connClient, connServer *TCPConn) {
|
||||||
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 *StackBasic, connClient, connServer *TCPConn) {
|
func setupClientServer(t *testing.T, rng *rand.Rand, client, server *StackIP, connClient, connServer *TCPConn) {
|
||||||
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)
|
||||||
|
|||||||
+7
-4
@@ -10,6 +10,7 @@ import (
|
|||||||
"runtime"
|
"runtime"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/soypat/lneto"
|
||||||
"github.com/soypat/lneto/internal"
|
"github.com/soypat/lneto/internal"
|
||||||
"github.com/soypat/lneto/ipv4"
|
"github.com/soypat/lneto/ipv4"
|
||||||
"github.com/soypat/lneto/ipv6"
|
"github.com/soypat/lneto/ipv6"
|
||||||
@@ -21,7 +22,6 @@ var (
|
|||||||
)
|
)
|
||||||
|
|
||||||
type TCPConn struct {
|
type TCPConn struct {
|
||||||
// deprecated: here for debugging purposes only.
|
|
||||||
h tcp.Handler
|
h tcp.Handler
|
||||||
remoteAddr []byte
|
remoteAddr []byte
|
||||||
|
|
||||||
@@ -221,9 +221,8 @@ func (conn *TCPConn) HandleIP(buf []byte, off int) (n int, err error) {
|
|||||||
return n, nil
|
return n, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (conn *TCPConn) Send(response []byte) (n int, err error) {
|
func (conn *TCPConn) Protocol() uint64 {
|
||||||
conn.trace("tcpconn.Send:start")
|
return uint64(lneto.IPProtoTCP)
|
||||||
return conn.h.Send(response)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func getIPAddr(buf []byte) (addr []byte, id uint16, err error) {
|
func getIPAddr(buf []byte) (addr []byte, id uint16, err error) {
|
||||||
@@ -324,3 +323,7 @@ func (conn *TCPConn) SetWriteDeadline(t time.Time) error {
|
|||||||
func (conn *TCPConn) deadlineExceeded(deadline time.Time) bool {
|
func (conn *TCPConn) deadlineExceeded(deadline time.Time) bool {
|
||||||
return !deadline.IsZero() && time.Since(deadline) > 0
|
return !deadline.IsZero() && time.Since(deadline) > 0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (conn *TCPConn) ConnectionID() *uint64 {
|
||||||
|
return conn.h.ConnectionID()
|
||||||
|
}
|
||||||
|
|||||||
+7
-6
@@ -21,9 +21,10 @@ var (
|
|||||||
// 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.
|
||||||
type Handler struct {
|
type Handler struct {
|
||||||
scb ControlBlock
|
connid uint64
|
||||||
bufTx ringTx
|
scb ControlBlock
|
||||||
bufRx internal.Ring
|
bufTx ringTx
|
||||||
|
bufRx internal.Ring
|
||||||
logger
|
logger
|
||||||
validator lneto.Validator
|
validator lneto.Validator
|
||||||
localPort uint16
|
localPort uint16
|
||||||
@@ -31,7 +32,7 @@ type Handler struct {
|
|||||||
// connid is a conenction counter that is incremented each time a new
|
// connid is a conenction counter that is incremented each time a new
|
||||||
// connection is established via Open calls. This disambiguate's whether
|
// connection is established via Open calls. This disambiguate's whether
|
||||||
// Read and Write calls belong to the current connection.
|
// Read and Write calls belong to the current connection.
|
||||||
connid uint16
|
|
||||||
optcodec OptionCodec
|
optcodec OptionCodec
|
||||||
closing bool
|
closing bool
|
||||||
}
|
}
|
||||||
@@ -42,8 +43,8 @@ func (h *Handler) SetLoggers(handler, scb *slog.Logger) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// ConnectionID returns the connection identifier which is incremented every time the connection is closed or open.
|
// ConnectionID returns the connection identifier which is incremented every time the connection is closed or open.
|
||||||
func (h *Handler) ConnectionID() int {
|
func (h *Handler) ConnectionID() *uint64 {
|
||||||
return int(h.connid)
|
return &h.connid
|
||||||
}
|
}
|
||||||
|
|
||||||
// State returns the state of the TCP state machine as per RFC9293. See [State].
|
// State returns the state of the TCP state machine as per RFC9293. See [State].
|
||||||
|
|||||||
Reference in New Issue
Block a user