mirror of
https://github.com/soypat/lneto.git
synced 2026-07-26 10:38:47 +00:00
a2970b923d
* add ipv6 to xnet.StackAsync * dns improvements * improve DNS workings of StackAsync * add tentative ICMPv6 * work on prefixes and fix some small bugs, plan UDP/TCP6 * fix bugs in StackAsync and ipv4.Prefix.Contains * update arpsubtable * completely remove legacy internet.StackIP for StackIPv4/v6 * ipv4/ipv6 tcp/udp * add TCP6/UDP6 dialing APIs * add xnet.Stack6 interface * more ipv6 integration into StackAsync; various tweaks to lneto and documentation+TODOs * add stack6 tests * replace netip.Prefix with ipv4.Prefix where it makes sense
176 lines
6.0 KiB
Go
176 lines
6.0 KiB
Go
package internet
|
|
|
|
import (
|
|
"encoding/binary"
|
|
"io"
|
|
"log/slog"
|
|
"math"
|
|
"strconv"
|
|
|
|
"github.com/soypat/lneto"
|
|
"github.com/soypat/lneto/ethernet"
|
|
"github.com/soypat/lneto/internal"
|
|
"github.com/soypat/lneto/tcp"
|
|
)
|
|
|
|
type StackPorts struct {
|
|
connID uint64
|
|
handlers handlers
|
|
dstPortOff uint16
|
|
protocol uint16
|
|
// rstQueue stores pending RST responses for TCP SYNs to unregistered ports.
|
|
rstQueue tcp.RSTQueue
|
|
}
|
|
|
|
func (ps *StackPorts) ResetUDP(maxNodes uint16) error {
|
|
return ps.Reset(uint64(lneto.IPProtoUDP), 2, maxNodes)
|
|
}
|
|
|
|
func (ps *StackPorts) ResetTCP(maxNodes uint16) error {
|
|
return ps.Reset(uint64(lneto.IPProtoTCP), 2, maxNodes)
|
|
}
|
|
|
|
func (ps *StackPorts) Reset(protocol uint64, dstPortOffset, maxNodes uint16) error {
|
|
if protocol > math.MaxUint16 {
|
|
return lneto.ErrInvalidConfig
|
|
}
|
|
ps.handlers.reset("StackPorts(proto="+strconv.Itoa(int(protocol))+")", int(maxNodes))
|
|
*ps = StackPorts{
|
|
connID: ps.connID + 1,
|
|
handlers: ps.handlers,
|
|
dstPortOff: dstPortOffset,
|
|
protocol: uint16(protocol),
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (ps *StackPorts) LocalPort() uint16 { return 0 }
|
|
|
|
func (ps *StackPorts) Protocol() uint64 { return uint64(ps.protocol) }
|
|
|
|
func (ps *StackPorts) ConnectionID() *uint64 { return &ps.connID }
|
|
|
|
func (ps *StackPorts) Encapsulate(carrierData []byte, offsetToIP, offsetToFrame int) (n int, err error) {
|
|
if int(ps.dstPortOff)+offsetToFrame+2 > len(carrierData) {
|
|
return 0, io.ErrShortBuffer
|
|
}
|
|
_, n, err = ps.handlers.encapsulateAny(carrierData, offsetToIP, offsetToFrame)
|
|
if n == 0 {
|
|
n, _ = ps.rstQueue.Drain(carrierData, offsetToIP, offsetToFrame)
|
|
}
|
|
return n, err
|
|
}
|
|
|
|
func (ps *StackPorts) Demux(b []byte, offset int) (err error) {
|
|
if int(ps.dstPortOff)+offset+2 > len(b) {
|
|
return io.ErrShortBuffer
|
|
}
|
|
port := binary.BigEndian.Uint16(b[int(ps.dstPortOff)+offset:])
|
|
_, err = ps.handlers.demuxByPort(b, offset, port)
|
|
if err == lneto.ErrPacketDrop && ps.protocol == uint16(lneto.IPProtoTCP) && offset+14 <= len(b) {
|
|
// RFC 9293 §3.10.7.1: RST for SYN to port with no listener.
|
|
flags := binary.BigEndian.Uint16(b[offset+12:]) & 0x01ff
|
|
const flagSYN, flagRST, flagACK = 0x02, 0x04, 0x10
|
|
if flags&flagSYN != 0 && flags&(flagRST|flagACK) == 0 {
|
|
srcaddr, _, _, _, iperr := internal.GetIPAddr(b)
|
|
if iperr == nil {
|
|
remotePort := binary.BigEndian.Uint16(b[offset:])
|
|
segSeq := tcp.Value(binary.BigEndian.Uint32(b[offset+4:]))
|
|
ps.rstQueue.Queue(srcaddr, remotePort, port, 0, segSeq+1, tcp.FlagRST|tcp.FlagACK)
|
|
}
|
|
}
|
|
}
|
|
return err
|
|
}
|
|
|
|
// Register registers a port StackNode on StackPorts.
|
|
// If dstMAC is set to non-nil, length six buffer then
|
|
func (ps *StackPorts) Register(h lneto.StackNode) error {
|
|
port := h.LocalPort()
|
|
proto := h.Protocol()
|
|
if port <= 0 {
|
|
return lneto.ErrZeroSource
|
|
} else if proto != uint64(ps.protocol) {
|
|
return lneto.ErrInvalidConfig
|
|
}
|
|
return ps.handlers.registerByPortProto(nodeFromStackNode(h, port, proto, nil))
|
|
}
|
|
|
|
// StackPortsMACFiltered is a StackPorts implementation but that avoids calling encapsulate on nodes
|
|
// with a non-nil MAC address registered via Register method that is set to all zero values.
|
|
// If the address is set to nil no filtering occurs. MAC Address is set automatically on the ethernet frame by StackPortsMACFiltered when non-nil.
|
|
type StackPortsMACFiltered struct {
|
|
sp StackPorts
|
|
}
|
|
|
|
func (mfsp *StackPortsMACFiltered) RegisterMACFiltered(h lneto.StackNode, macAddr []byte) error {
|
|
// TODO(soypat): We can likely constrain memory and the slice lifetime if StackPortsMACFiltered owns it
|
|
// or better yet, if the handlers node slice owns the memory. We need to think carefully of who has write access (the ARP and NDP handlers)
|
|
// and make sure that they never write after the connection has been terminated. Idea:
|
|
// RegisterMACFiltered(h lneto.StackNode, filterMAC bool) (macAddr *[6]byte, connIDthing *uint8, err error)
|
|
port := h.LocalPort()
|
|
proto := h.Protocol()
|
|
if port <= 0 {
|
|
return lneto.ErrZeroSource
|
|
} else if proto != uint64(mfsp.sp.protocol) {
|
|
return lneto.ErrInvalidConfig
|
|
} else if macAddr != nil && len(macAddr) != 6 {
|
|
return lneto.ErrInvalidAddr
|
|
}
|
|
return mfsp.sp.handlers.registerByPortProto(nodeFromStackNode(h, port, proto, macAddr))
|
|
}
|
|
|
|
func (ps *StackPortsMACFiltered) ResetUDP(maxNodes uint16) {
|
|
ps.sp.ResetUDP(maxNodes) // Can't error.
|
|
}
|
|
|
|
func (ps *StackPortsMACFiltered) ResetTCP(maxNodes uint16) {
|
|
ps.sp.ResetTCP(maxNodes) // Can't error.
|
|
}
|
|
|
|
func (ps *StackPortsMACFiltered) Reset(protocol uint64, dstPortOffset, maxNodes uint16) error {
|
|
return ps.sp.Reset(protocol, dstPortOffset, maxNodes)
|
|
}
|
|
|
|
func (ps *StackPortsMACFiltered) LocalPort() uint16 { return 0 }
|
|
|
|
func (ps *StackPortsMACFiltered) Protocol() uint64 { return uint64(ps.sp.protocol) }
|
|
|
|
func (ps *StackPortsMACFiltered) ConnectionID() *uint64 { return &ps.sp.connID }
|
|
|
|
func (ps *StackPortsMACFiltered) Demux(b []byte, offset int) (err error) {
|
|
// No MAC Filtering on ingress. TODO?
|
|
return ps.sp.Demux(b, offset)
|
|
}
|
|
|
|
func (ps *StackPortsMACFiltered) Encapsulate(carrierData []byte, offsetToIP, offsetToFrame int) (n int, err error) {
|
|
if int(ps.sp.dstPortOff)+offsetToFrame+2 > len(carrierData) {
|
|
return 0, io.ErrShortBuffer
|
|
}
|
|
h := &ps.sp.handlers
|
|
for i := range h.nodes {
|
|
node := &h.nodes[i]
|
|
if node.IsInvalid() || (len(node.remoteAddr) > 0 && internal.IsZeroed(node.remoteAddr...)) {
|
|
continue
|
|
}
|
|
n, err = node.callbacks.Encapsulate(carrierData, offsetToIP, offsetToFrame)
|
|
if h.tryHandleError(node, err) {
|
|
err = nil // CLOSE error handled gracefully by deleting node.
|
|
}
|
|
if n > 0 {
|
|
if len(node.remoteAddr) == 6 && offsetToIP >= 14 {
|
|
efrm, _ := ethernet.NewFrame(carrierData[offsetToIP-14:])
|
|
*efrm.DestinationHardwareAddr() = [6]byte(node.remoteAddr)
|
|
}
|
|
return n, err
|
|
} else if err != nil {
|
|
// Make sure not to hang on one handler that keeps returning an error.
|
|
h.error("handlers:encapsulate", slog.String("func", "encapsulateAny"), slog.String("ctx", h.context), slog.String("err", err.Error()))
|
|
}
|
|
}
|
|
if n, _ := ps.sp.rstQueue.Drain(carrierData, offsetToIP, offsetToFrame); n > 0 {
|
|
return n, nil
|
|
}
|
|
return 0, err // Return last written error.
|
|
}
|