split out node callback between tinygo and go to avoid heap allocs

This commit is contained in:
Patricio Whittingslow
2026-01-13 10:38:21 -03:00
parent f51cdf74b6
commit 2e6e66e691
6 changed files with 78 additions and 21 deletions
+19 -17
View File
@@ -40,12 +40,15 @@ type StackNode interface {
// node is a concrete StackNode as stored in Stacks. Methods are devirtualized for performance benefits, especially on TinyGo. // node is a concrete StackNode as stored in Stacks. Methods are devirtualized for performance benefits, especially on TinyGo.
type node struct { type node struct {
currConnID uint64 currConnID uint64
connID *uint64 connID *uint64
demux func([]byte, int) error // cbnode has different definitions in tinygo and normal Go compiled programs
encapsulate func([]byte, int, int) (int, error) // for performance and heap control reasons.
proto uint16 callbacks cbnode
port uint16 // demux func([]byte, int) error
// encapsulate func([]byte, int, int) (int, error)
proto uint16
port uint16
// remoteAddr will be set on active(outbound) port connections // remoteAddr will be set on active(outbound) port connections
// that require an ARP to set the remoteAddr beforehand. // that require an ARP to set the remoteAddr beforehand.
remoteAddr []byte remoteAddr []byte
@@ -152,7 +155,7 @@ func (h *handlers) demuxByProto(buf []byte, offset int, proto uint16) (*node, er
if node == nil { if node == nil {
return nil, lneto.ErrPacketDrop return nil, lneto.ErrPacketDrop
} }
err := node.demux(buf, offset) err := node.callbacks.Demux(buf, offset)
if h.tryHandleError(node, err) { if h.tryHandleError(node, err) {
err = nil err = nil
} }
@@ -165,7 +168,7 @@ func (h *handlers) demuxByPort(buf []byte, offset int, port uint16) (*node, erro
if node == nil { if node == nil {
return nil, lneto.ErrPacketDrop return nil, lneto.ErrPacketDrop
} }
err := node.demux(buf, offset) err := node.callbacks.Demux(buf, offset)
if h.tryHandleError(node, err) { if h.tryHandleError(node, err) {
err = nil err = nil
node = nil // Node is destroyed in tryHandleError and invalidated. node = nil // Node is destroyed in tryHandleError and invalidated.
@@ -181,7 +184,7 @@ func (h *handlers) encapsulateAny(buf []byte, offsetIP, offsetThisFrame int) (_
if node.IsInvalid() { if node.IsInvalid() {
continue continue
} }
n, err = node.encapsulate(buf, offsetIP, offsetThisFrame) n, err = node.callbacks.Encapsulate(buf, offsetIP, offsetThisFrame)
if h.tryHandleError(node, err) { if h.tryHandleError(node, err) {
err = nil // CLOSE error handled gracefully by deleting node. err = nil // CLOSE error handled gracefully by deleting node.
node = nil // Node is destroyed in tryHandleError and invalidated. node = nil // Node is destroyed in tryHandleError and invalidated.
@@ -206,7 +209,7 @@ var (
) )
func (node *node) IsInvalid() bool { func (node *node) IsInvalid() bool {
return node.demux == nil || node.encapsulate == nil || (node.connID != nil && node.currConnID != *node.connID) return node.callbacks.IsZeroed() || (node.connID != nil && node.currConnID != *node.connID)
} }
func checkNodeErr(node *node, err error) (discard bool) { func checkNodeErr(node *node, err error) (discard bool) {
@@ -223,13 +226,12 @@ func nodeFromStackNode(s StackNode, port uint16, protocol uint64, remoteAddr []b
currConnID = *connIDPtr currConnID = *connIDPtr
} }
return node{ return node{
currConnID: currConnID, currConnID: currConnID,
connID: connIDPtr, connID: connIDPtr,
demux: s.Demux, callbacks: makecbnode(s),
encapsulate: s.Encapsulate, proto: uint16(protocol),
proto: uint16(protocol), port: port,
port: port, remoteAddr: remoteAddr, // SHARED MEMORY- used to signal.
remoteAddr: remoteAddr, // SHARED MEMORY- used to signal.
} }
} }
+26
View File
@@ -0,0 +1,26 @@
//go:build !tinygo
package internet
func makecbnode(s StackNode) cbnode {
return cbnode{
_s: s,
}
}
type cbnode struct {
// Do not access outside of handlers/node logic.
_s StackNode
}
func (s cbnode) Encapsulate(carrierData []byte, offsetToIP, offsetToFrame int) (int, error) {
return s._s.Encapsulate(carrierData, offsetToIP, offsetToFrame)
}
func (s cbnode) Demux(carrierData []byte, frameOffset int) error {
return s._s.Demux(carrierData, frameOffset)
}
func (s cbnode) IsZeroed() bool {
return s._s == nil
}
+29
View File
@@ -0,0 +1,29 @@
//go:build tinygo
package internet
func makecbnode(s StackNode) cbnode {
return cbnode{
demux: s.Demux,
encapsulate: s.Encapsulate,
}
}
type cbnode struct {
// Do not access outside of handlers/node logic.
demux func([]byte, int) error
// Do not access outside of handlers/node logic.
encapsulate func([]byte, int, int) (int, error)
}
func (s *cbnode) Encapsulate(carrierData []byte, offsetToIP, offsetToFrame int) (int, error) {
return s.encapsulate(carrierData, offsetToIP, offsetToFrame)
}
func (s *cbnode) Demux(carrierData []byte, frameOffset int) error {
return s.demux(carrierData, frameOffset)
}
func (s cbnode) IsZeroed() bool {
return s.demux == nil || s.encapsulate == nil
}
+1 -1
View File
@@ -136,7 +136,7 @@ func (sb *StackIP) Demux(carrierData []byte, offset int) error {
} }
} }
sb.handlers.info("ipDemux", slog.String("ipproto", proto.String()), slog.Int("plen", int(totalLen))) sb.handlers.info("ipDemux", slog.String("ipproto", proto.String()), slog.Int("plen", int(totalLen)))
err = node.demux(frame[:totalLen], off) err = node.callbacks.Demux(frame[:totalLen], off)
if sb.handlers.tryHandleError(node, err) { if sb.handlers.tryHandleError(node, err) {
sb.handlers.info("ipclose", slog.String("proto", proto.String())) sb.handlers.info("ipclose", slog.String("proto", proto.String()))
err = nil err = nil
+1 -1
View File
@@ -134,7 +134,7 @@ func (ps *StackPortsMACFiltered) Encapsulate(carrierData []byte, offsetToIP, off
if node.IsInvalid() || (len(node.remoteAddr) > 0 && internal.IsZeroed(node.remoteAddr...)) { if node.IsInvalid() || (len(node.remoteAddr) > 0 && internal.IsZeroed(node.remoteAddr...)) {
continue continue
} }
n, err = node.encapsulate(carrierData, offsetToIP, offsetToFrame) n, err = node.callbacks.Encapsulate(carrierData, offsetToIP, offsetToFrame)
if h.tryHandleError(node, err) { if h.tryHandleError(node, err) {
err = nil // CLOSE error handled gracefully by deleting node. err = nil // CLOSE error handled gracefully by deleting node.
} }
+2 -2
View File
@@ -51,7 +51,7 @@ func (sudp *StackUDPPort) Demux(carrierData []byte, frameOffset int) error {
if sudp.rmport != 0 && src != sudp.rmport { if sudp.rmport != 0 && src != sudp.rmport {
return lneto.ErrPacketDrop // Not from our target remote port. return lneto.ErrPacketDrop // Not from our target remote port.
} }
err = sudp.h.demux(carrierData, frameOffset+8) err = sudp.h.callbacks.Demux(carrierData, frameOffset+8)
if err != nil { if err != nil {
if checkNodeErr(&sudp.h, err) { if checkNodeErr(&sudp.h, err) {
sudp.h.destroy() sudp.h.destroy()
@@ -79,7 +79,7 @@ func (sudp *StackUDPPort) Encapsulate(carrierData []byte, offsetToIP, offsetToFr
} }
} }
// Child payload starts 8 bytes after UDP header start. // Child payload starts 8 bytes after UDP header start.
n, err := sudp.h.encapsulate(carrierData, offsetToIP, offsetToFrame+8) n, err := sudp.h.callbacks.Encapsulate(carrierData, offsetToIP, offsetToFrame+8)
if n == 0 { if n == 0 {
if err != nil { if err != nil {
slog.Error("stackudp:encapsulate", slog.String("err", err.Error())) slog.Error("stackudp:encapsulate", slog.String("err", err.Error()))