Files
lneto/internet/definitions.go
T
2025-06-07 09:36:10 -03:00

89 lines
3.3 KiB
Go

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
// Connect
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(nodesPtr *[]node, nodeIdx int, err error) {
if nodeIdx >= len(*nodesPtr) {
panic("unreachable")
}
if err != nil {
nodes := *nodesPtr
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 {
*nodesPtr = 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,
})
}