mirror of
https://github.com/soypat/lneto.git
synced 2026-08-11 10:23:47 +00:00
Error rewrites (#43)
* pcap: reuse Frame memory * slog: reduce heap allocations of addresses; also prevent heap alloc of dhcp options in pcap * dns: heapless improvement; add StackAsync buffer for more heapless operation; start thinking of errors * errors: begin standardise errors in lneto * errors: finish standardization of errors * fix merge issues * add more lneto errors to rest of package * format errors.go
This commit is contained in:
+13
-5
@@ -1,6 +1,10 @@
|
||||
package arp
|
||||
|
||||
import "errors"
|
||||
import (
|
||||
"errors"
|
||||
|
||||
"github.com/soypat/lneto"
|
||||
)
|
||||
|
||||
//go:generate stringer -type=Operation -linecomment -output stringers.go .
|
||||
|
||||
@@ -11,10 +15,14 @@ const (
|
||||
)
|
||||
|
||||
var (
|
||||
errARPBufferFull = errors.New("ARP client need handling:too many ops pending")
|
||||
errShortARP = errors.New("packet too short to be ARP")
|
||||
errARPUnsupported = errors.New("ARP not supported")
|
||||
errLargeSizes = errors.New("size of ARP protocol+hardware is unusually large")
|
||||
errQueryPending = errors.New("arp: query pending")
|
||||
errQueryNotFound = errors.New("arp: query not found")
|
||||
|
||||
// errGeneric aliases for common ARP errors.
|
||||
errARPBufferFull = lneto.ErrBufferFull
|
||||
errShortARP = lneto.ErrShortBuffer
|
||||
errARPUnsupported = lneto.ErrUnsupported
|
||||
errLargeSizes = lneto.ErrPacketDrop
|
||||
)
|
||||
|
||||
// Operation represents the type of ARP packet, either request or reply/response.
|
||||
|
||||
+1
-2
@@ -2,7 +2,6 @@ package arp
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"errors"
|
||||
"fmt"
|
||||
"net"
|
||||
"net/netip"
|
||||
@@ -17,7 +16,7 @@ import (
|
||||
// with payload/options of frames to avoid panics.
|
||||
func NewFrame(buf []byte) (Frame, error) {
|
||||
if len(buf) < sizeHeaderv4 {
|
||||
return Frame{buf: nil}, errors.New("ARP packet too short")
|
||||
return Frame{buf: nil}, lneto.ErrShortBuffer
|
||||
}
|
||||
return Frame{buf: buf}, nil
|
||||
}
|
||||
|
||||
+13
-14
@@ -1,7 +1,6 @@
|
||||
package arp
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"log/slog"
|
||||
|
||||
"github.com/soypat/lneto"
|
||||
@@ -36,7 +35,7 @@ func (h *Handler) ConnectionID() *uint64 { return &h.connID }
|
||||
|
||||
func (h *Handler) UpdateProtoAddr(protoAddr []byte) error {
|
||||
if len(protoAddr) != len(h.ourProtoAddr) {
|
||||
return errors.New("mismatch ARP proto size")
|
||||
return lneto.ErrMismatchLen
|
||||
}
|
||||
copy(h.ourProtoAddr, protoAddr)
|
||||
return nil
|
||||
@@ -45,9 +44,9 @@ func (h *Handler) UpdateProtoAddr(protoAddr []byte) error {
|
||||
func (h *Handler) Reset(cfg HandlerConfig) error {
|
||||
if len(cfg.HardwareAddr) == 0 || len(cfg.HardwareAddr) > 255 ||
|
||||
len(cfg.ProtocolAddr) == 0 || len(cfg.ProtocolAddr) > 255 {
|
||||
return errors.New("invalid Handler address config")
|
||||
return lneto.ErrInvalidConfig
|
||||
} else if cfg.MaxQueries <= 0 || cfg.MaxPending <= 0 {
|
||||
return errors.New("invalid Handler query or pending config")
|
||||
return lneto.ErrInvalidConfig
|
||||
}
|
||||
*h = Handler{
|
||||
connID: h.connID + 1,
|
||||
@@ -102,16 +101,16 @@ func (h *Handler) QueryResult(protoAddr []byte) (hwAddr []byte, err error) {
|
||||
for i := range h.queries {
|
||||
if internal.BytesEqual(protoAddr, h.queries[i].protoaddr) {
|
||||
if !h.queries[i].querysent {
|
||||
return nil, errors.New("query not yet sent")
|
||||
return nil, errQueryPending
|
||||
}
|
||||
mac := h.queries[i].response()
|
||||
if mac == nil {
|
||||
return nil, errors.New("no response yet")
|
||||
return nil, errQueryPending
|
||||
}
|
||||
return mac, nil
|
||||
}
|
||||
}
|
||||
return nil, errors.New("query not exist or dropped")
|
||||
return nil, errQueryNotFound
|
||||
}
|
||||
|
||||
func (h *Handler) DiscardQuery(protoAddr []byte) error {
|
||||
@@ -122,7 +121,7 @@ func (h *Handler) DiscardQuery(protoAddr []byte) error {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
return errors.New("query not found")
|
||||
return errQueryNotFound
|
||||
}
|
||||
|
||||
func (h *Handler) compactQueries() {
|
||||
@@ -150,15 +149,15 @@ func (h *Handler) StartQuery(dstHWAddr, proto []byte) error {
|
||||
if len(h.queries) == cap(h.queries) {
|
||||
h.compactQueries()
|
||||
if len(h.queries) == cap(h.queries) {
|
||||
return errors.New("too many ongoing queries")
|
||||
return lneto.ErrBufferFull
|
||||
}
|
||||
}
|
||||
if len(proto) != len(h.ourProtoAddr) {
|
||||
return errors.New("bad protocol address length")
|
||||
return lneto.ErrMismatchLen
|
||||
} else if dstHWAddr != nil && len(dstHWAddr) != len(h.ourHWAddr) {
|
||||
return errors.New("mismatch hardware size")
|
||||
return lneto.ErrMismatchLen
|
||||
} else if dstHWAddr != nil && !internal.IsZeroed(dstHWAddr...) {
|
||||
return errors.New("write-to buffer must be zeroed out")
|
||||
return lneto.ErrInvalidConfig
|
||||
}
|
||||
h.queries = h.queries[:len(h.queries)+1]
|
||||
q := &h.queries[len(h.queries)-1]
|
||||
@@ -230,11 +229,11 @@ func (h *Handler) Demux(ethFrame []byte, frameOffset int) error {
|
||||
}
|
||||
htype, hlen := afrm.Hardware()
|
||||
if htype != h.htype || int(hlen) != len(h.ourHWAddr) {
|
||||
return errors.New("bad ARP hardware")
|
||||
return lneto.ErrMismatch
|
||||
}
|
||||
protoType, protoLen := afrm.Protocol()
|
||||
if protoType != h.protoType || int(protoLen) != len(h.ourProtoAddr) {
|
||||
return errors.New("bad ARP proto")
|
||||
return lneto.ErrMismatch
|
||||
}
|
||||
switch afrm.Operation() {
|
||||
case OpRequest:
|
||||
|
||||
+12
-12
@@ -2,8 +2,6 @@ package dhcpv4
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"log/slog"
|
||||
"math"
|
||||
@@ -80,13 +78,13 @@ func (c *Client) Reset() {
|
||||
|
||||
func (c *Client) BeginRequest(xid uint32, cfg RequestConfig) error {
|
||||
if len(cfg.Hostname) > 36 {
|
||||
return errors.New("requested hostname too long")
|
||||
return lneto.ErrInvalidConfig
|
||||
} else if c.state != StateInit && c.state != 0 {
|
||||
return errors.New("dhcp client must be closed/Init before new request")
|
||||
return lneto.ErrInvalidConfig
|
||||
} else if xid == 0 {
|
||||
return errors.New("zero xid")
|
||||
return lneto.ErrInvalidConfig
|
||||
} else if len(cfg.ClientID) > 32 {
|
||||
return errors.New("client ID too long")
|
||||
return lneto.ErrInvalidConfig
|
||||
}
|
||||
c.reset(xid)
|
||||
c.state = StateInit
|
||||
@@ -143,7 +141,7 @@ func (c *Client) Encapsulate(carrierData []byte, offsetToIP, offsetToFrame int)
|
||||
}
|
||||
opts := frm.OptionsPayload()
|
||||
if len(opts) < 255 {
|
||||
return 0, errors.New("too short packet for options")
|
||||
return 0, lneto.ErrShortBuffer
|
||||
}
|
||||
|
||||
var nextState ClientState
|
||||
@@ -181,7 +179,8 @@ func (c *Client) Encapsulate(carrierData []byte, offsetToIP, offsetToFrame int)
|
||||
nextState = StateRequesting
|
||||
|
||||
default:
|
||||
return 0, errors.New("unhandled state" + c.state.String())
|
||||
internal.LogAttrs(nil, slog.LevelError, "dhcpv4:unhandled-state", slog.String("state", c.state.String()))
|
||||
return 0, lneto.ErrBug
|
||||
}
|
||||
n, _ := EncodeOption(opts[numOpts:], OptClientIdentifier, c.clientID...)
|
||||
numOpts += n
|
||||
@@ -210,13 +209,13 @@ func (c *Client) Demux(carrierData []byte, frameOffset int) error {
|
||||
if err != nil {
|
||||
return err
|
||||
} else if frm.XID() != c.currentXID {
|
||||
return errors.New("dhcpv4 unexpected transaction ID")
|
||||
return lneto.ErrMismatch
|
||||
} else if frm.MagicCookie() != MagicCookie {
|
||||
return errors.New("dhcpv4 bad magic cookie")
|
||||
return lneto.ErrInvalidField
|
||||
}
|
||||
msgType := c.getMessageType(frm)
|
||||
if msgType == MsgNack {
|
||||
return errors.New("dhcp nack received")
|
||||
return lneto.ErrPacketDrop
|
||||
}
|
||||
|
||||
msgOK := msgType == MsgOffer || msgType == MsgAck
|
||||
@@ -243,7 +242,8 @@ func (c *Client) Demux(carrierData []byte, frameOffset int) error {
|
||||
c.state = StateBound
|
||||
}
|
||||
default:
|
||||
return fmt.Errorf("dhcpv4 unexpected state in recv %s", c.state.String())
|
||||
internal.LogAttrs(nil, slog.LevelError, "dhcpv4:unexpected-recv-state", slog.String("state", c.state.String()))
|
||||
return lneto.ErrBug
|
||||
}
|
||||
if frameOffset > 28 && c.svIPtos == 0 {
|
||||
ifrm, _ := ipv4.NewFrame(carrierData)
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
package dhcpv4
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"unsafe"
|
||||
|
||||
"github.com/soypat/lneto"
|
||||
)
|
||||
|
||||
//go:generate stringer -type=OptNum,Op,MessageType,ClientState -linecomment -output stringers.go
|
||||
@@ -52,9 +53,9 @@ func EncodeOption32(dst []byte, opt OptNum, v uint32) (int, error) {
|
||||
|
||||
func EncodeOption(dst []byte, opt OptNum, data ...byte) (int, error) {
|
||||
if len(data) > 255 {
|
||||
return 0, errors.New("DHCPv4 option data too long (>255)")
|
||||
return 0, lneto.ErrInvalidLengthField
|
||||
} else if len(dst) < 2+len(data) {
|
||||
return 0, errors.New("DHCP option buffer too short")
|
||||
return 0, lneto.ErrShortBuffer
|
||||
}
|
||||
_ = dst[2+len(data)]
|
||||
dst[0] = byte(opt)
|
||||
|
||||
+5
-13
@@ -2,7 +2,6 @@ package dhcpv4
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"errors"
|
||||
|
||||
"github.com/soypat/lneto"
|
||||
)
|
||||
@@ -27,7 +26,7 @@ const (
|
||||
// An error is returned if the buffer size is smaller than 240.
|
||||
func NewFrame(buf []byte) (Frame, error) {
|
||||
if len(buf) < OptionsOffset {
|
||||
return Frame{}, errSmallFrame
|
||||
return Frame{}, lneto.ErrShortBuffer
|
||||
}
|
||||
return Frame{buf: buf}, nil
|
||||
}
|
||||
@@ -126,9 +125,9 @@ func (frm Frame) ForEachOption(fn func(off int, opt OptNum, data []byte) error)
|
||||
// Parse DHCP options.
|
||||
ptr := OptionsOffset
|
||||
if ptr > len(frm.buf) {
|
||||
return errSmallFrame
|
||||
return lneto.ErrShortBuffer
|
||||
} else if len(frm.buf[ptr:]) == 0 {
|
||||
return errNoOptions
|
||||
return lneto.ErrInvalidField
|
||||
}
|
||||
callback := fn != nil
|
||||
for ptr+1 < len(frm.buf) {
|
||||
@@ -141,7 +140,7 @@ func (frm Frame) ForEachOption(fn func(off int, opt OptNum, data []byte) error)
|
||||
}
|
||||
optlen := int(frm.buf[ptr+1])
|
||||
if ptr+2+optlen > len(frm.buf) {
|
||||
return errDHCPBadOption
|
||||
return lneto.ErrInvalidLengthField
|
||||
}
|
||||
if callback {
|
||||
optionData := frm.buf[ptr+2 : ptr+2+optlen]
|
||||
@@ -158,16 +157,9 @@ func (frm Frame) ForEachOption(fn func(off int, opt OptNum, data []byte) error)
|
||||
// Validation API.
|
||||
//
|
||||
|
||||
var (
|
||||
errSmallFrame = errors.New("DHCPv4: frame size <240")
|
||||
errDHCPBadOption = errors.New("DHCPv4: opt length exceeds payload")
|
||||
errNoOptions = errors.New("DHCPv4: no options")
|
||||
errOptionNotFit = errors.New("DHCPv4: options dont fit")
|
||||
)
|
||||
|
||||
func (frm Frame) ValidateSize(vld *lneto.Validator) {
|
||||
err := frm.ForEachOption(nil) // Does all necessary validation.
|
||||
if err != nil {
|
||||
vld.AddError(errDHCPBadOption)
|
||||
vld.AddError(lneto.ErrInvalidLengthField)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,6 +10,8 @@ import (
|
||||
"github.com/soypat/lneto/internal"
|
||||
)
|
||||
|
||||
var errOptionNotFit = errors.New("DHCPv4: options dont fit")
|
||||
|
||||
type Server struct {
|
||||
connID uint64
|
||||
nextAddr netip.Addr
|
||||
|
||||
+5
-4
@@ -1,12 +1,12 @@
|
||||
package dns
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"log/slog"
|
||||
"math"
|
||||
"net"
|
||||
|
||||
"github.com/soypat/lneto"
|
||||
"github.com/soypat/lneto/internal"
|
||||
)
|
||||
|
||||
type Client struct {
|
||||
@@ -34,7 +34,7 @@ func (sudp *Client) ConnectionID() *uint64 { return &sudp.connID }
|
||||
func (c *Client) StartResolve(localPort, txid uint16, cfg ResolveConfig) error {
|
||||
nd := len(cfg.Questions)
|
||||
if nd > math.MaxUint16 {
|
||||
return errors.New("overflow uint16 in DNS questions")
|
||||
return lneto.ErrBufferFull
|
||||
}
|
||||
c.reset(localPort, txid, dnsSendQuery, cfg.EnableRecursion)
|
||||
c.msg.LimitResourceDecoding(uint16(nd), uint16(nd), 0, 0)
|
||||
@@ -61,7 +61,8 @@ func (c *Client) Encapsulate(carrierData []byte, offsetToIP, offsetToFrame int)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
} else if len(data) > int(msglen) {
|
||||
return 0, fmt.Errorf("unexpected write %d v %d", len(data), msglen)
|
||||
internal.LogAttrs(nil, slog.LevelError, "dns:unexpected-write", slog.Int("got", len(data)), slog.Int("want", int(msglen)))
|
||||
return 0, lneto.ErrBug
|
||||
}
|
||||
c.state = dnsAwaitResponse
|
||||
// Unset don't frag since DNS requests go through LOTS of nodes.
|
||||
|
||||
+27
-23
@@ -3,35 +3,39 @@ package dns
|
||||
import (
|
||||
"encoding/binary"
|
||||
"errors"
|
||||
|
||||
"github.com/soypat/lneto"
|
||||
)
|
||||
|
||||
//go:generate stringer -type=Type,Class,RCode,OpCode -linecomment -output stringers.go .
|
||||
|
||||
// common errors. Taken from golang.org/x/net/dns/dnsmessage module.
|
||||
var (
|
||||
errNoResponse = errors.New("no DNS response")
|
||||
errNameTooLong = errors.New("DNS name exceeds maximum length")
|
||||
errNoNullTerm = errors.New("DNS name missing null terminator")
|
||||
errCalcLen = errors.New("DNS calculated name label length exceeds remaining buffer length")
|
||||
errCantAddLabel = errors.New("long/empty/zterm/escape DNS label or not enough space")
|
||||
errBaseLen = errors.New("DNS frame length too short")
|
||||
errReserved = errors.New("segment prefix is reserved")
|
||||
errTooManyPtr = errors.New("too many pointers (>10)")
|
||||
errInvalidPtr = errors.New("invalid pointer")
|
||||
errInvalidName = errors.New("invalid dns name")
|
||||
errNilResouceBody = errors.New("nil resource body")
|
||||
errResourceLen = errors.New("insufficient data for resource body length")
|
||||
errSegTooLong = errors.New("segment length too long")
|
||||
errZeroSegLen = errors.New("zero length segment")
|
||||
errResTooLong = errors.New("resource length too long")
|
||||
errTooManyQuestions = errors.New("too many Questions")
|
||||
errTooManyAnswers = errors.New("too many Answers")
|
||||
errTooManyAuthorities = errors.New("too many Authorities")
|
||||
errTooManyAdditionals = errors.New("too many Additionals")
|
||||
errNonCanonicalName = errors.New("name is not in canonical format (it must end with a .)")
|
||||
errStringTooLong = errors.New("character string exceeds maximum length (255)")
|
||||
errCompressedSRV = errors.New("compressed name in SRV resource data")
|
||||
errEmptyDomainName = errors.New("empty domain name")
|
||||
errNoResponse = errors.New("no DNS response")
|
||||
errNameTooLong = errors.New("DNS name exceeds maximum length")
|
||||
errNoNullTerm = errors.New("DNS name missing null terminator")
|
||||
errCalcLen = errors.New("DNS calculated name label length exceeds remaining buffer length")
|
||||
errCantAddLabel = errors.New("long/empty/zterm/escape DNS label or not enough space")
|
||||
errBaseLen = lneto.ErrShortBuffer
|
||||
errReserved = errors.New("segment prefix is reserved")
|
||||
errTooManyPtr = errors.New("too many pointers (>10)")
|
||||
errInvalidPtr = errors.New("invalid pointer")
|
||||
errInvalidName = errors.New("invalid dns name")
|
||||
errNilResouceBody = errors.New("nil resource body")
|
||||
errResourceLen = errors.New("insufficient data for resource body length")
|
||||
errSegTooLong = errors.New("segment length too long")
|
||||
errZeroSegLen = errors.New("zero length segment")
|
||||
errResTooLong = errors.New("resource length too long")
|
||||
|
||||
errTooManyQuestions = lneto.ErrBufferFull
|
||||
errTooManyAnswers = lneto.ErrBufferFull
|
||||
errTooManyAuthorities = lneto.ErrBufferFull
|
||||
errTooManyAdditionals = lneto.ErrBufferFull
|
||||
|
||||
errNonCanonicalName = errors.New("name is not in canonical format (it must end with a .)")
|
||||
errStringTooLong = errors.New("character string exceeds maximum length (255)")
|
||||
errCompressedSRV = errors.New("compressed name in SRV resource data")
|
||||
errEmptyDomainName = errors.New("empty domain name")
|
||||
)
|
||||
|
||||
// Frame encapsulates the raw data of a DNS packet
|
||||
|
||||
@@ -1,30 +1,25 @@
|
||||
package lneto
|
||||
|
||||
// type ErrorPacketDrop struct {
|
||||
// Message string
|
||||
// }
|
||||
|
||||
// var genericErrPacketDrop = &ErrorPacketDrop{Message: ErrPacketDrop.Error()}
|
||||
|
||||
// // ErrGenericPacketDrop returns the generic packet drop error. It performs no allocations.
|
||||
// func ErrGenericPacketDrop() error {
|
||||
// return genericErrPacketDrop
|
||||
// }
|
||||
|
||||
// func (err *ErrorPacketDrop) Error() string {
|
||||
// return err.Message
|
||||
// }
|
||||
|
||||
type errGeneric uint8
|
||||
|
||||
// Generic errors common to internet functioning.
|
||||
const (
|
||||
_ errGeneric = iota // non-initialized err
|
||||
ErrBug // lneto-bug(use build tag "debugheaplog")
|
||||
ErrPacketDrop // packet dropped
|
||||
ErrBadCRC // incorrect checksum
|
||||
ErrZeroSource // zero source(port/addr)
|
||||
ErrZeroDestination // zero destination(port/addr)
|
||||
_ errGeneric = iota // non-initialized err
|
||||
ErrBug // lneto-bug(use build tag "debugheaplog")
|
||||
ErrPacketDrop // packet dropped
|
||||
ErrBadCRC // incorrect checksum
|
||||
ErrZeroSource // zero source(port/addr)
|
||||
ErrZeroDestination // zero destination(port/addr)
|
||||
ErrShortBuffer // short buffer
|
||||
ErrBufferFull // buffer full
|
||||
ErrInvalidAddr // invalid address
|
||||
ErrUnsupported // unsupported
|
||||
ErrMismatch // mismatch
|
||||
ErrMismatchLen // mismatched length
|
||||
ErrInvalidConfig // invalid configuration
|
||||
ErrInvalidField // invalid field
|
||||
ErrInvalidLengthField // invalid length field
|
||||
ErrExhausted // resource exhausted
|
||||
)
|
||||
|
||||
func (err errGeneric) Error() string {
|
||||
|
||||
+3
-9
@@ -2,7 +2,6 @@ package ethernet
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"errors"
|
||||
|
||||
"github.com/soypat/lneto"
|
||||
)
|
||||
@@ -13,7 +12,7 @@ import (
|
||||
// with payload/options of frames to avoid panics.
|
||||
func NewFrame(buf []byte) (Frame, error) {
|
||||
if len(buf) < sizeHeaderNoVLAN {
|
||||
return Frame{buf: nil}, errShort
|
||||
return Frame{buf: nil}, lneto.ErrShortBuffer
|
||||
}
|
||||
return Frame{buf: buf}, nil
|
||||
}
|
||||
@@ -115,19 +114,14 @@ func (frm Frame) ClearHeader() {
|
||||
// Validation API.
|
||||
//
|
||||
|
||||
var (
|
||||
errShort = errors.New("ethernet: too short")
|
||||
errShortVLAN = errors.New("ethernet: short VLAN")
|
||||
)
|
||||
|
||||
// ValidateSize checks the frame's size fields and compares with the actual buffer
|
||||
// the frame. It returns a non-nil error on finding an inconsistency.
|
||||
func (efrm Frame) ValidateSize(v *lneto.Validator) {
|
||||
sz := efrm.EtherTypeOrSize()
|
||||
if sz.IsSize() && len(efrm.buf) < int(sz) {
|
||||
v.AddError(errShort)
|
||||
v.AddError(lneto.ErrInvalidLengthField)
|
||||
}
|
||||
if sz == TypeVLAN && len(efrm.buf) < 18 {
|
||||
v.AddError(errShortVLAN)
|
||||
v.AddError(lneto.ErrShortBuffer)
|
||||
}
|
||||
}
|
||||
|
||||
+5
-9
@@ -2,12 +2,8 @@ package internal
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"errors"
|
||||
)
|
||||
|
||||
var (
|
||||
errUnsupportedIP = errors.New("unsupported IP version")
|
||||
errInvalidIPVersionToSetAddr = errors.New("invalid ip version to setDstAddr")
|
||||
"github.com/soypat/lneto"
|
||||
)
|
||||
|
||||
func GetIPAddr(buf []byte) (src, dst []byte, id, ipEndOff uint16, err error) {
|
||||
@@ -25,7 +21,7 @@ func GetIPAddr(buf []byte) (src, dst []byte, id, ipEndOff uint16, err error) {
|
||||
dst = buf[24:40]
|
||||
ipEndOff = 40
|
||||
default:
|
||||
err = errUnsupportedIP
|
||||
err = lneto.ErrUnsupported
|
||||
}
|
||||
return src, dst, id, ipEndOff, err
|
||||
}
|
||||
@@ -44,13 +40,13 @@ func SetIPAddrs(buf []byte, id uint16, src, dst []byte) (err error) {
|
||||
srcaddr = buf[8:24]
|
||||
dstaddr = buf[24:40]
|
||||
default:
|
||||
return errUnsupportedIP
|
||||
return lneto.ErrUnsupported
|
||||
}
|
||||
if src != nil && len(srcaddr) != len(src) {
|
||||
return errors.New("mismatched length of ip src addr")
|
||||
return lneto.ErrMismatchLen
|
||||
}
|
||||
if dst != nil && len(dstaddr) != len(dst) {
|
||||
return errors.New("mismatched length of ip dst addr")
|
||||
return lneto.ErrMismatchLen
|
||||
}
|
||||
copy(srcaddr, src)
|
||||
copy(dstaddr, dst)
|
||||
|
||||
+10
-5
@@ -6,11 +6,16 @@ import (
|
||||
"io"
|
||||
"math"
|
||||
"unsafe"
|
||||
|
||||
"github.com/soypat/lneto"
|
||||
)
|
||||
|
||||
var (
|
||||
ErrRingBufferFull = errors.New("lneto/ring: buffer full")
|
||||
errRingNoData = errors.New("lneto/ring: empty write")
|
||||
ErrRingBufferFull = lneto.ErrBufferFull
|
||||
errRingNoData = errors.New("lneto/ring: empty write")
|
||||
errInvalidDiscard = errors.New("lneto/ring: invalid discard amount")
|
||||
errDiscardExceeds = errors.New("lneto/ring: discard exceeds length")
|
||||
errOffsetOverflow = errors.New("lneto/ring: offset too large (32 bit overflow)")
|
||||
)
|
||||
|
||||
// Ring implements basic Ring buffer functionality.
|
||||
@@ -92,12 +97,12 @@ func (r *Ring) Write(b []byte) (int, error) {
|
||||
// This method panics if amount of bytes is more than buffered (see [Ring.Buffered]).
|
||||
func (r *Ring) ReadDiscard(n int) error {
|
||||
if n <= 0 {
|
||||
return errors.New("invalid discard amount")
|
||||
return errInvalidDiscard
|
||||
}
|
||||
buffered := r.Buffered()
|
||||
switch {
|
||||
case n > buffered:
|
||||
return errors.New("discard exceeds length")
|
||||
return errDiscardExceeds
|
||||
case n == buffered:
|
||||
r.Reset()
|
||||
case n+r.Off > len(r.Buf):
|
||||
@@ -111,7 +116,7 @@ func (r *Ring) ReadDiscard(n int) error {
|
||||
// ReadAt reads data at an offset from start of readable data but does not advance read pointer. [io.EOF] returned when no data available.
|
||||
func (r *Ring) ReadAt(p []byte, off64 int64) (int, error) {
|
||||
if math.MaxInt != math.MaxInt64 && off64+int64(len(p)) > math.MaxInt32 {
|
||||
return 0, errors.New("offset too large (32 bit overflow)") // Check only compiles for 32-bit platforms.
|
||||
return 0, errOffsetOverflow // Check only compiles for 32-bit platforms.
|
||||
}
|
||||
off := int(off64)
|
||||
if off+len(p) > r.Buffered() {
|
||||
|
||||
@@ -93,7 +93,7 @@ func (h *handlers) prepAdd() error {
|
||||
if h.full() {
|
||||
h.compact()
|
||||
if h.full() {
|
||||
return errNodesFull
|
||||
return lneto.ErrBufferFull
|
||||
}
|
||||
}
|
||||
return nil
|
||||
@@ -200,11 +200,7 @@ func (h *handlers) encapsulateAny(buf []byte, offsetIP, offsetThisFrame int) (_
|
||||
}
|
||||
|
||||
var (
|
||||
errZeroMaxNodesArg = errors.New("zero max nodes arg")
|
||||
errZeroPort = errors.New("port must be greater than zero")
|
||||
errInvalidProto = errors.New("invalid protocol")
|
||||
errProtoRegistered = errors.New("protocol already registered")
|
||||
errNodesFull = errors.New("no more room for new nodes")
|
||||
_ = net.ErrClosed
|
||||
)
|
||||
|
||||
|
||||
+26
-25
@@ -28,6 +28,9 @@ const unknownPayloadProto = "payload?"
|
||||
|
||||
var (
|
||||
ErrFieldByClassNotFound = errors.New("pcap: field by class not found")
|
||||
ErrLimitExceeded = errors.New("pcap: limit exceeded")
|
||||
errNotByteAligned = errors.New("must be parsed at byte boundary")
|
||||
errInvalidFieldIdx = errors.New("invalid field index")
|
||||
)
|
||||
|
||||
type PacketBreakdown struct {
|
||||
@@ -61,13 +64,11 @@ func (pc *PacketBreakdown) initFrames() []Frame {
|
||||
}
|
||||
|
||||
func (pc *PacketBreakdown) CaptureEthernet(dst []Frame, pkt []byte, bitOffset int) ([]Frame, error) {
|
||||
debuglog("pcap:eth:start")
|
||||
if dst == nil {
|
||||
dst = pc.initFrames()
|
||||
debuglog("pcap:eth:initframes")
|
||||
}
|
||||
if bitOffset%8 != 0 {
|
||||
return dst, errors.New("ethernet must be parsed at byte boundary")
|
||||
return dst, errNotByteAligned
|
||||
}
|
||||
efrm, err := ethernet.NewFrame(pkt[bitOffset/8:])
|
||||
if err != nil {
|
||||
@@ -110,7 +111,7 @@ func (pc *PacketBreakdown) CaptureEthernet(dst []Frame, pkt []byte, bitOffset in
|
||||
func (pc *PacketBreakdown) CaptureARP(dst []Frame, pkt []byte, bitOffset int) ([]Frame, error) {
|
||||
debuglog("pcap:arp:start")
|
||||
if bitOffset%8 != 0 {
|
||||
return dst, errors.New("ARP must be parsed at byte boundary")
|
||||
return dst, errNotByteAligned
|
||||
}
|
||||
afrm, err := arp.NewFrame(pkt[bitOffset/8:])
|
||||
if err != nil {
|
||||
@@ -157,7 +158,7 @@ func (pc *PacketBreakdown) CaptureARP(dst []Frame, pkt []byte, bitOffset int) ([
|
||||
func (pc *PacketBreakdown) CaptureIPv6(dst []Frame, pkt []byte, bitOffset int) ([]Frame, error) {
|
||||
debuglog("pcap:ipv6:start")
|
||||
if bitOffset%8 != 0 {
|
||||
return dst, errors.New("IPv6 must be parsed at byte boundary")
|
||||
return dst, errNotByteAligned
|
||||
}
|
||||
ifrm6, err := ipv6.NewFrame(pkt[bitOffset/8:])
|
||||
if err != nil {
|
||||
@@ -201,7 +202,7 @@ func (pc *PacketBreakdown) CaptureIPv6(dst []Frame, pkt []byte, bitOffset int) (
|
||||
func (pc *PacketBreakdown) CaptureIPv4(dst []Frame, pkt []byte, bitOffset int) ([]Frame, error) {
|
||||
debuglog("pcap:ipv4:start")
|
||||
if bitOffset%8 != 0 {
|
||||
return dst, errors.New("IPv4 must be parsed at byte boundary")
|
||||
return dst, errNotByteAligned
|
||||
}
|
||||
ifrm4, err := ipv4.NewFrame(pkt[bitOffset/8:])
|
||||
if err != nil {
|
||||
@@ -301,7 +302,7 @@ func (pc *PacketBreakdown) captureIPProto(proto lneto.IPProto, dst []Frame, pkt
|
||||
func (pc *PacketBreakdown) CaptureTCP(dst []Frame, pkt []byte, bitOffset int) ([]Frame, error) {
|
||||
debuglog("pcap:tcp:start")
|
||||
if bitOffset%8 != 0 {
|
||||
return dst, errors.New("TCP must be parsed at byte boundary")
|
||||
return dst, errNotByteAligned
|
||||
}
|
||||
tfrm, err := tcp.NewFrame(pkt[bitOffset/8:])
|
||||
if err != nil {
|
||||
@@ -339,7 +340,7 @@ func (pc *PacketBreakdown) CaptureTCP(dst []Frame, pkt []byte, bitOffset int) ([
|
||||
func (pc *PacketBreakdown) CaptureUDP(dst []Frame, pkt []byte, bitOffset int) ([]Frame, error) {
|
||||
debuglog("pcap:udp:start")
|
||||
if bitOffset%8 != 0 {
|
||||
return dst, errors.New("UDP must be parsed at byte boundary")
|
||||
return dst, errNotByteAligned
|
||||
}
|
||||
ufrm, err := udp.NewFrame(pkt[bitOffset/8:])
|
||||
if err != nil {
|
||||
@@ -372,7 +373,7 @@ func (pc *PacketBreakdown) CaptureUDP(dst []Frame, pkt []byte, bitOffset int) ([
|
||||
func (pc *PacketBreakdown) CaptureICMPv4(dst []Frame, pkt []byte, bitOffset int) ([]Frame, error) {
|
||||
debuglog("pcap:icmp:start")
|
||||
if bitOffset%8 != 0 {
|
||||
return dst, errors.New("ICMPv4 must be parsed at byte boundary")
|
||||
return dst, errNotByteAligned
|
||||
}
|
||||
icmpData := pkt[bitOffset/8:]
|
||||
ifrm, err := icmpv4.NewFrame(icmpData)
|
||||
@@ -432,7 +433,7 @@ func (pc *PacketBreakdown) CaptureICMPv4(dst []Frame, pkt []byte, bitOffset int)
|
||||
|
||||
func (pc *PacketBreakdown) CaptureDNS(dst []Frame, pkt []byte, bitOffset int) ([]Frame, error) {
|
||||
if bitOffset%8 != 0 {
|
||||
return dst, errors.New("DNS must be parsed at byte boundary")
|
||||
return dst, errNotByteAligned
|
||||
}
|
||||
dnsData := pkt[bitOffset/8:]
|
||||
pc.dmsg.LimitResourceDecoding(20, 20, 20, 20)
|
||||
@@ -442,9 +443,9 @@ func (pc *PacketBreakdown) CaptureDNS(dst []Frame, pkt []byte, bitOffset int) ([
|
||||
}
|
||||
finfo := reclaimFrame(&dst, "DNS", bitOffset, nil)
|
||||
if incomplete {
|
||||
finfo.Errors = append(finfo.Errors, errors.New("pcap: could not parse all DNS resources; add higher limit"))
|
||||
finfo.Errors = append(finfo.Errors, ErrLimitExceeded)
|
||||
}
|
||||
finfo.Fields = append(finfo.Fields, FrameField{
|
||||
finfo.Fields = append(finfo.Fields[:0], FrameField{
|
||||
Name: "Data",
|
||||
FrameBitOffset: 0,
|
||||
BitLength: int(off) * octet,
|
||||
@@ -454,7 +455,7 @@ func (pc *PacketBreakdown) CaptureDNS(dst []Frame, pkt []byte, bitOffset int) ([
|
||||
|
||||
func (pc *PacketBreakdown) CaptureNTP(dst []Frame, pkt []byte, bitOffset int) ([]Frame, error) {
|
||||
if bitOffset%8 != 0 {
|
||||
return dst, errors.New("NTP must be parsed at byte boundary")
|
||||
return dst, errNotByteAligned
|
||||
}
|
||||
ntpData := pkt[bitOffset/8:]
|
||||
_, err := ntp.NewFrame(ntpData)
|
||||
@@ -467,7 +468,7 @@ func (pc *PacketBreakdown) CaptureNTP(dst []Frame, pkt []byte, bitOffset int) ([
|
||||
|
||||
func (pc *PacketBreakdown) CaptureDHCPv4(dst []Frame, pkt []byte, bitOffset int) ([]Frame, error) {
|
||||
if bitOffset%8 != 0 {
|
||||
return dst, errors.New("DHCP must be parsed at byte boundary")
|
||||
return dst, errNotByteAligned
|
||||
}
|
||||
dhcpData := pkt[bitOffset/8:]
|
||||
dfrm, err := dhcpv4.NewFrame(dhcpData)
|
||||
@@ -477,7 +478,7 @@ func (pc *PacketBreakdown) CaptureDHCPv4(dst []Frame, pkt []byte, bitOffset int)
|
||||
finfo := reclaimFrame(&dst, "DHCPv4", bitOffset, baseDHCPv4Fields[:])
|
||||
magic := dfrm.MagicCookie()
|
||||
if magic != dhcpv4.MagicCookie {
|
||||
finfo.Errors = append(finfo.Errors, errors.New("incorrect DHCPv4 magic cookie"))
|
||||
finfo.Errors = append(finfo.Errors, lneto.ErrInvalidField)
|
||||
}
|
||||
options := dfrm.OptionsPayload()
|
||||
|
||||
@@ -491,7 +492,7 @@ func (pc *PacketBreakdown) CaptureDHCPv4(dst []Frame, pkt []byte, bitOffset int)
|
||||
}
|
||||
err = dfrm.ForEachOption(func(optoff int, opt dhcpv4.OptNum, data []byte) error {
|
||||
if len(optfield.SubFields) >= pc.SubfieldLimit {
|
||||
return errors.New("option cap limit surpassed for DHCP")
|
||||
return ErrLimitExceeded
|
||||
}
|
||||
// optoff points to start of length and num bytes, skip over them with FrameBitOffset.
|
||||
field := FrameField{Name: opt.String(), FrameBitOffset: (optoff + 2) * octet, BitLength: len(data) * octet}
|
||||
@@ -585,7 +586,7 @@ func (pc *PacketBreakdown) CaptureHTTP(dst []Frame, pkt []byte, bitOffset int) (
|
||||
debuglog("pcap:http:start")
|
||||
const httpProtocol = "HTTP"
|
||||
if bitOffset%8 != 0 {
|
||||
return dst, errors.New("HTTP must be parsed at byte boundary")
|
||||
return dst, errNotByteAligned
|
||||
}
|
||||
const asResponse = true
|
||||
const asRequest = false
|
||||
@@ -668,7 +669,7 @@ func (frm Frame) FieldByClass(c FieldClass) (int, error) {
|
||||
}
|
||||
if field.Name == "" { // Prioritize "canonical" fields with no name.
|
||||
if selected >= 0 && frm.Fields[selected].Name == "" {
|
||||
return -1, errors.New("multiple class fields with no name")
|
||||
return -1, lneto.ErrMismatch
|
||||
}
|
||||
selected = i
|
||||
} else if selected >= 0 {
|
||||
@@ -681,7 +682,7 @@ func (frm Frame) FieldByClass(c FieldClass) (int, error) {
|
||||
return -1, ErrFieldByClassNotFound
|
||||
}
|
||||
if multiple && frm.Fields[selected].Name != "" {
|
||||
return -1, errors.New("multiple classes found and none have empty name")
|
||||
return -1, lneto.ErrMismatch
|
||||
}
|
||||
return selected, nil
|
||||
}
|
||||
@@ -690,7 +691,7 @@ func (frm Frame) FieldByClass(c FieldClass) (int, error) {
|
||||
func (frm Frame) FieldAsUint(fieldIdx int, pkt []byte) (uint64, error) {
|
||||
const badUint64 = math.MaxUint64
|
||||
if fieldIdx < 0 || fieldIdx >= len(frm.Fields) {
|
||||
return badUint64, errors.New("invalid field index")
|
||||
return badUint64, errInvalidFieldIdx
|
||||
}
|
||||
field := frm.Fields[fieldIdx]
|
||||
return fieldAsUint(pkt, frm.PacketBitOffset+field.FrameBitOffset, field.BitLength, field.Flags.IsRightAligned())
|
||||
@@ -699,7 +700,7 @@ func (frm Frame) FieldAsUint(fieldIdx int, pkt []byte) (uint64, error) {
|
||||
// AppendField appends the binary on-the-wire representation of the field and aligns the field so it starts at the first bit of appended data.
|
||||
func (frm Frame) AppendField(dst []byte, fieldIdx int, pkt []byte) ([]byte, error) {
|
||||
if fieldIdx < 0 || fieldIdx >= len(frm.Fields) {
|
||||
return dst, errors.New("invalid field index")
|
||||
return dst, errInvalidFieldIdx
|
||||
}
|
||||
field := frm.Fields[fieldIdx]
|
||||
return appendField(dst, pkt, frm.PacketBitOffset+field.FrameBitOffset, field.BitLength, field.Flags.IsRightAligned())
|
||||
@@ -709,7 +710,7 @@ func fieldAsUint(pkt []byte, fieldBitStart, bitlen int, rightAligned bool) (uint
|
||||
const badUint64 = math.MaxUint64
|
||||
octets := (bitlen + 7) / 8
|
||||
if octets > 8 {
|
||||
return badUint64, errors.New("field too long to be represented by uint64")
|
||||
return badUint64, lneto.ErrUnsupported
|
||||
}
|
||||
var buf [8]byte
|
||||
_, err := appendField(buf[8-octets:8-octets], pkt, fieldBitStart, bitlen, rightAligned)
|
||||
@@ -725,13 +726,13 @@ func appendField(dst, pkt []byte, fieldBitStart, bitlen int, rightAligned bool)
|
||||
octets := (bitlen + 7) / 8 // total octets needed to represent field.
|
||||
octetsStart := fieldBitStart / 8
|
||||
if octets+octetsStart > len(pkt) {
|
||||
return dst, errors.New("buffer overflow")
|
||||
return dst, lneto.ErrShortBuffer
|
||||
}
|
||||
firstBitOffset := fieldBitStart % 8
|
||||
lastOctetExcessBits := fieldBitEnd % 8
|
||||
if firstBitOffset == 0 {
|
||||
if rightAligned {
|
||||
return dst, errors.New("invalid right aligned set for fully aligned field")
|
||||
return dst, lneto.ErrBug
|
||||
}
|
||||
// Optimized path: field starts at byte boundary.
|
||||
dst = append(dst, pkt[octetsStart:octetsStart+octets]...)
|
||||
@@ -752,7 +753,7 @@ func appendField(dst, pkt []byte, fieldBitStart, bitlen int, rightAligned bool)
|
||||
// Right aligned with trailing bits. i.e: IPv6 Traffic Class.
|
||||
// Field spans an extra byte, so need octets+1 bytes from packet.
|
||||
if octets+octetsStart+1 > len(pkt) {
|
||||
return dst, errors.New("buffer overflow")
|
||||
return dst, lneto.ErrShortBuffer
|
||||
}
|
||||
for i := 0; i < octets; i++ {
|
||||
b := (pkt[octetsStart+i] & mask) << (8 - firstBitOffset)
|
||||
|
||||
@@ -3,7 +3,6 @@ package pcap
|
||||
import (
|
||||
"encoding/binary"
|
||||
"encoding/hex"
|
||||
"errors"
|
||||
"math"
|
||||
"net/netip"
|
||||
"slices"
|
||||
@@ -13,6 +12,7 @@ import (
|
||||
_ "time"
|
||||
"unsafe"
|
||||
|
||||
"github.com/soypat/lneto"
|
||||
"github.com/soypat/lneto/ethernet"
|
||||
"github.com/soypat/lneto/ntp"
|
||||
"github.com/soypat/lneto/tcp"
|
||||
@@ -162,7 +162,7 @@ func (f *Formatter) formatField(dst []byte, pktStartOff int, field FrameField, p
|
||||
// inspired by [time.RFC3339]
|
||||
const littlerfc3339 = "2006-01-02T15:04:05.9999"
|
||||
if len(f.buf) != 8 {
|
||||
return dst, errors.New("only timestamp8 supported")
|
||||
return dst, lneto.ErrUnsupported
|
||||
}
|
||||
ts := ntp.TimestampFromUint64(binary.BigEndian.Uint64(f.buf))
|
||||
dst = ts.Time().AppendFormat(dst, littlerfc3339)
|
||||
@@ -223,7 +223,7 @@ func (f *Formatter) fieldAsUint(pkt []byte, fieldBitStart, bitlen int, rightAlig
|
||||
const badUint64 = math.MaxUint64
|
||||
octets := (bitlen + 7) / 8
|
||||
if octets > 8 {
|
||||
return badUint64, errors.New("field too long to be represented by uint64")
|
||||
return badUint64, lneto.ErrUnsupported
|
||||
}
|
||||
f.uintBuf = [8]byte{}
|
||||
_, err := appendField(f.uintBuf[8-octets:8-octets], pkt, fieldBitStart, bitlen, rightAligned)
|
||||
|
||||
@@ -2,7 +2,6 @@ package internet
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"errors"
|
||||
"io"
|
||||
"log/slog"
|
||||
"math"
|
||||
@@ -76,11 +75,11 @@ func (ls *StackEthernet) Reset6(mac, gateway [6]byte, mtu, maxNodes int) error {
|
||||
// The connection ID is incremented on each call to invalidate existing connections.
|
||||
func (ls *StackEthernet) Configure(cfg StackEthernetConfig) error {
|
||||
if cfg.MTU > (math.MaxUint16-ethernet.MaxOverheadSize) || cfg.MTU < 256 {
|
||||
return errors.New("invalid MTU")
|
||||
return lneto.ErrInvalidConfig
|
||||
} else if cfg.MaxNodes <= 0 {
|
||||
return errZeroMaxNodesArg
|
||||
return lneto.ErrInvalidConfig
|
||||
} else if cfg.AppendCRC32 && cfg.CRC32Update == nil {
|
||||
return errors.New("need CRC32Update to append ethernet CRC")
|
||||
return lneto.ErrInvalidConfig
|
||||
}
|
||||
ls.handlers.reset("StackEthernet", cfg.MaxNodes)
|
||||
*ls = StackEthernet{
|
||||
@@ -107,7 +106,7 @@ func (ls *StackEthernet) Protocol() uint64 { return 1 }
|
||||
func (ls *StackEthernet) Register(h StackNode) error {
|
||||
proto := h.Protocol()
|
||||
if proto > math.MaxUint16 || proto <= 1500 {
|
||||
return errInvalidProto
|
||||
return lneto.ErrInvalidConfig
|
||||
}
|
||||
return ls.handlers.registerByProto(nodeFromStackNode(h, 0, proto, nil))
|
||||
}
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
package internet
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"io"
|
||||
"log/slog"
|
||||
"net/netip"
|
||||
@@ -26,7 +25,7 @@ type StackIP struct {
|
||||
|
||||
func (sb *StackIP) Reset(addr netip.Addr, maxNodes int) error {
|
||||
if maxNodes <= 0 {
|
||||
return errZeroMaxNodesArg
|
||||
return lneto.ErrInvalidConfig
|
||||
}
|
||||
err := sb.SetAddr(addr)
|
||||
if err != nil {
|
||||
@@ -44,9 +43,9 @@ func (sb *StackIP) Reset(addr netip.Addr, maxNodes int) error {
|
||||
|
||||
func (sb *StackIP) SetAddr(addr netip.Addr) error {
|
||||
if !addr.IsValid() {
|
||||
return errors.New("invalid IP")
|
||||
return lneto.ErrInvalidAddr
|
||||
} else if !addr.Is4() {
|
||||
return errors.New("require IPv4")
|
||||
return lneto.ErrUnsupported
|
||||
}
|
||||
sb.ip = addr.As4()
|
||||
return nil
|
||||
@@ -200,7 +199,7 @@ func (sb *StackIP) Encapsulate(carrierData []byte, offsetToIP, offsetToFrame int
|
||||
func (sb *StackIP) Register(h StackNode) error {
|
||||
proto := h.Protocol()
|
||||
if proto > 255 {
|
||||
return errInvalidProto
|
||||
return lneto.ErrInvalidConfig
|
||||
}
|
||||
return sb.handlers.registerByPortProto(nodeFromStackNode(h, h.LocalPort(), proto, nil))
|
||||
}
|
||||
@@ -208,7 +207,7 @@ func (sb *StackIP) Register(h StackNode) error {
|
||||
func (sb *StackIP) recvicmp(icmpData []byte) error {
|
||||
var crc lneto.CRC791
|
||||
if crc.PayloadSum16(icmpData) != 0 {
|
||||
return errors.New("ICMP CRC mismatch")
|
||||
return lneto.ErrBadCRC
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -2,7 +2,6 @@ package internet
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"errors"
|
||||
"io"
|
||||
"log/slog"
|
||||
"math"
|
||||
@@ -33,9 +32,9 @@ func (ps *StackPorts) ResetTCP(maxNodes int) error {
|
||||
|
||||
func (ps *StackPorts) Reset(protocol uint64, dstPortOffset uint16, maxNodes int) error {
|
||||
if protocol > math.MaxUint16 {
|
||||
return errInvalidProto
|
||||
return lneto.ErrInvalidConfig
|
||||
} else if maxNodes <= 0 {
|
||||
return errZeroMaxNodesArg
|
||||
return lneto.ErrInvalidConfig
|
||||
}
|
||||
ps.handlers.reset("StackPorts(proto="+strconv.Itoa(int(protocol))+")", maxNodes)
|
||||
*ps = StackPorts{
|
||||
@@ -92,9 +91,9 @@ func (ps *StackPorts) Register(h StackNode) error {
|
||||
port := h.LocalPort()
|
||||
proto := h.Protocol()
|
||||
if port <= 0 {
|
||||
return errZeroPort
|
||||
return lneto.ErrZeroSource
|
||||
} else if proto != uint64(ps.protocol) {
|
||||
return errInvalidProto
|
||||
return lneto.ErrInvalidConfig
|
||||
}
|
||||
return ps.handlers.registerByPortProto(nodeFromStackNode(h, port, proto, nil))
|
||||
}
|
||||
@@ -110,11 +109,11 @@ func (mfsp *StackPortsMACFiltered) Register(h StackNode, addr []byte) error {
|
||||
port := h.LocalPort()
|
||||
proto := h.Protocol()
|
||||
if port <= 0 {
|
||||
return errZeroPort
|
||||
return lneto.ErrZeroSource
|
||||
} else if proto != uint64(mfsp.sp.protocol) {
|
||||
return errInvalidProto
|
||||
return lneto.ErrInvalidConfig
|
||||
} else if addr != nil && len(addr) != 6 {
|
||||
return errors.New("invalid MAC")
|
||||
return lneto.ErrInvalidAddr
|
||||
}
|
||||
return mfsp.sp.handlers.registerByPortProto(nodeFromStackNode(h, port, proto, addr))
|
||||
}
|
||||
|
||||
+6
-15
@@ -2,7 +2,6 @@ package ipv4
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"errors"
|
||||
"fmt"
|
||||
"net/netip"
|
||||
|
||||
@@ -15,7 +14,7 @@ import (
|
||||
// with payload/options of frames to avoid panics.
|
||||
func NewFrame(buf []byte) (Frame, error) {
|
||||
if len(buf) < sizeHeader {
|
||||
return Frame{buf: nil}, errors.New("ipv4: short buffer")
|
||||
return Frame{buf: nil}, lneto.ErrShortBuffer
|
||||
}
|
||||
return Frame{buf: buf}, nil
|
||||
}
|
||||
@@ -191,27 +190,19 @@ func (ifrm Frame) ClearHeader() {
|
||||
// Validation API.
|
||||
//
|
||||
|
||||
var (
|
||||
errBadTL = errors.New("ipv4: bad total length")
|
||||
errShort = errors.New("ipv4: short data")
|
||||
errBadIHL = errors.New("ipv4: bad IHL")
|
||||
errBadVersion = errors.New("ipv4: bad version")
|
||||
errEvil = errors.New("ipv4: evil packet")
|
||||
)
|
||||
|
||||
// ValidateSize checks the frame's size fields and compares with the actual buffer
|
||||
// the frame. It returns a non-nil error on finding an inconsistency.
|
||||
func (ifrm Frame) ValidateSize(v *lneto.Validator) {
|
||||
ihl := ifrm.ihl()
|
||||
tl := ifrm.TotalLength()
|
||||
if tl < sizeHeader {
|
||||
v.AddError(errBadTL)
|
||||
v.AddError(lneto.ErrInvalidLengthField)
|
||||
}
|
||||
if int(tl) > len(ifrm.RawData()) {
|
||||
v.AddError(errShort)
|
||||
v.AddError(lneto.ErrShortBuffer)
|
||||
}
|
||||
if ihl < 5 || uint16(ihl)*4 > tl {
|
||||
v.AddError(errBadIHL)
|
||||
v.AddError(lneto.ErrInvalidLengthField)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -220,10 +211,10 @@ func (ifrm Frame) ValidateExceptCRC(v *lneto.Validator) {
|
||||
ifrm.ValidateSize(v)
|
||||
flags := ifrm.Flags()
|
||||
if ifrm.version() != 4 {
|
||||
v.AddError(errBadVersion)
|
||||
v.AddError(lneto.ErrInvalidField)
|
||||
}
|
||||
if v.Flags()&lneto.ValidateEvilBit != 0 && flags.IsEvil() {
|
||||
v.AddError(errEvil)
|
||||
v.AddError(lneto.ErrPacketDrop)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -2,7 +2,8 @@ package icmpv4
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"errors"
|
||||
|
||||
"github.com/soypat/lneto"
|
||||
)
|
||||
|
||||
type Type uint8
|
||||
@@ -52,13 +53,9 @@ const (
|
||||
CodeRedirectToSAndHost // redirect for ToS+host
|
||||
)
|
||||
|
||||
var (
|
||||
errShortFrame = errors.New("icmpv4: short frame")
|
||||
)
|
||||
|
||||
func NewFrame(buf []byte) (Frame, error) {
|
||||
if len(buf) < 8 {
|
||||
return Frame{}, errShortFrame
|
||||
return Frame{}, lneto.ErrShortBuffer
|
||||
}
|
||||
return Frame{buf: buf}, nil
|
||||
}
|
||||
|
||||
+2
-7
@@ -2,7 +2,6 @@ package ipv6
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"errors"
|
||||
|
||||
"github.com/soypat/lneto"
|
||||
)
|
||||
@@ -13,7 +12,7 @@ import (
|
||||
// with payload/options of frames to avoid panics.
|
||||
func NewFrame(buf []byte) (Frame, error) {
|
||||
if len(buf) < sizeHeader {
|
||||
return Frame{buf: nil}, errShortBuf
|
||||
return Frame{buf: nil}, lneto.ErrShortBuffer
|
||||
}
|
||||
return Frame{buf: buf}, nil
|
||||
}
|
||||
@@ -119,16 +118,12 @@ func (i6frm Frame) ClearHeader() {
|
||||
// Validate API.
|
||||
//
|
||||
|
||||
var (
|
||||
errShortFrame = errors.New("ipv6: short frame")
|
||||
errShortBuf = errors.New("ipv6: short buffer for frame")
|
||||
)
|
||||
|
||||
// ValidateSize checks the frame's size fields and compares with the actual buffer
|
||||
// the frame. It returns a non-nil error on finding an inconsistency.
|
||||
func (i6frm Frame) ValidateSize(v *lneto.Validator) {
|
||||
tl := i6frm.PayloadLength()
|
||||
if int(tl)+sizeHeader > len(i6frm.RawData()) {
|
||||
v.AddError(errShortFrame)
|
||||
v.AddError(lneto.ErrInvalidLengthField)
|
||||
}
|
||||
}
|
||||
|
||||
+3
-2
@@ -1,8 +1,9 @@
|
||||
package ntp
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"time"
|
||||
|
||||
"github.com/soypat/lneto"
|
||||
)
|
||||
|
||||
type state uint8
|
||||
@@ -100,7 +101,7 @@ func (c *Client) Demux(carrierData []byte, frameOffset int) error {
|
||||
xmt := frm.TransmitTime()
|
||||
orig := frm.OriginTime()
|
||||
if xmt == orig || orig != c.t[0] {
|
||||
return errors.New("bogus NTP packet")
|
||||
return lneto.ErrPacketDrop
|
||||
}
|
||||
|
||||
txelapsed := c.now().Sub(c.start)
|
||||
|
||||
+6
-5
@@ -3,11 +3,12 @@ package ntp
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"errors"
|
||||
"math"
|
||||
"math/bits"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/soypat/lneto"
|
||||
)
|
||||
|
||||
// NTP Global Parameters.
|
||||
@@ -26,7 +27,7 @@ const (
|
||||
|
||||
func NewFrame(buf []byte) (Frame, error) {
|
||||
if len(buf) < SizeHeader {
|
||||
return Frame{buf: nil}, errors.New("NTP frame too short")
|
||||
return Frame{buf: nil}, lneto.ErrShortBuffer
|
||||
}
|
||||
return Frame{buf: buf}, nil
|
||||
}
|
||||
@@ -189,12 +190,12 @@ func TimestampFromUint64(ts uint64) Timestamp {
|
||||
func TimestampFromTime(t time.Time) (Timestamp, error) {
|
||||
t = t.UTC()
|
||||
if t.Before(baseTime) {
|
||||
return Timestamp{}, errors.New("ntp.TimestampFromTime: time is before baseTime")
|
||||
return Timestamp{}, lneto.ErrUnsupported
|
||||
}
|
||||
off := t.Sub(baseTime)
|
||||
sec := uint64(off / time.Second)
|
||||
if sec > math.MaxUint32 {
|
||||
return Timestamp{}, errors.New("ntp.TimestampFromTime: time is too large")
|
||||
return Timestamp{}, lneto.ErrUnsupported
|
||||
}
|
||||
fra := uint64(off%time.Second) * math.MaxUint32 / uint64(time.Second)
|
||||
return Timestamp{
|
||||
@@ -254,7 +255,7 @@ func (d Date) Time() (time.Time, error) {
|
||||
}
|
||||
hi, seclo := bits.Mul64(uint64(sec), uint64(time.Second))
|
||||
if hi != 0 || seclo > math.MaxInt64-uint64(time.Second)-1 {
|
||||
return time.Time{}, errors.New("ntp.Date.Time overflow")
|
||||
return time.Time{}, lneto.ErrUnsupported
|
||||
}
|
||||
off := time.Duration(seclo)
|
||||
off += time.Second * time.Duration(d.frac>>32) / math.MaxUint32
|
||||
|
||||
+6
-6
@@ -9,6 +9,8 @@ package phy
|
||||
import (
|
||||
"errors"
|
||||
"time"
|
||||
|
||||
"github.com/soypat/lneto"
|
||||
)
|
||||
|
||||
// MDIOBus is a HAL for MDIO bus access supporting both Clause 22 and Clause 45 devices.
|
||||
@@ -33,7 +35,7 @@ func FindClause22PHYs(mdio MDIOBus, dst []uint8) (n int, err error) {
|
||||
const maxAddr = 31
|
||||
const regBasicStatus = 0x01
|
||||
if len(dst) < 32 {
|
||||
return -1, errors.New("require buffer length 32 for FindPHYs")
|
||||
return -1, lneto.ErrShortBuffer
|
||||
}
|
||||
n = 0
|
||||
for addr := uint8(0); addr <= maxAddr; addr++ {
|
||||
@@ -56,9 +58,7 @@ func FindClause22PHYs(mdio MDIOBus, dst []uint8) (n int, err error) {
|
||||
return n, err
|
||||
}
|
||||
|
||||
var (
|
||||
errInvalidPhyAddr = errors.New("invalid phy addr")
|
||||
)
|
||||
var errInvalidPhyAddr error = lneto.ErrInvalidAddr
|
||||
|
||||
type Device struct {
|
||||
mdio MDIOBus
|
||||
@@ -73,7 +73,7 @@ func (phy *Device) ConfigureAs22(mdio MDIOBus, phyAddr uint8) error {
|
||||
return errInvalidPhyAddr
|
||||
|
||||
} else if mdio == nil {
|
||||
return errors.New("nil mdio bus")
|
||||
return lneto.ErrInvalidConfig
|
||||
}
|
||||
phy.mdio = mdio
|
||||
phy.phyaddr = phyAddr
|
||||
@@ -176,7 +176,7 @@ func (phy *Device) SetupForced(mode LinkMode) error {
|
||||
case 10:
|
||||
// No speed bits = 10Mbps
|
||||
default:
|
||||
return errors.New("unsupported forced link mode")
|
||||
return lneto.ErrUnsupported
|
||||
}
|
||||
if mode.IsFullDuplex() {
|
||||
ctl |= BMCRFullDuplex
|
||||
|
||||
+12
-2
@@ -199,11 +199,21 @@ func _() {
|
||||
_ = x[ErrBadCRC-3]
|
||||
_ = x[ErrZeroSource-4]
|
||||
_ = x[ErrZeroDestination-5]
|
||||
_ = x[ErrShortBuffer-6]
|
||||
_ = x[ErrBufferFull-7]
|
||||
_ = x[ErrInvalidAddr-8]
|
||||
_ = x[ErrUnsupported-9]
|
||||
_ = x[ErrMismatch-10]
|
||||
_ = x[ErrMismatchLen-11]
|
||||
_ = x[ErrInvalidConfig-12]
|
||||
_ = x[ErrInvalidField-13]
|
||||
_ = x[ErrInvalidLengthField-14]
|
||||
_ = x[ErrExhausted-15]
|
||||
}
|
||||
|
||||
const _errGeneric_name = "lneto-bug(use build tag \"debugheaplog\")packet droppedincorrect checksumzero source(port/addr)zero destination(port/addr)"
|
||||
const _errGeneric_name = "lneto-bug(use build tag \"debugheaplog\")packet droppedincorrect checksumzero source(port/addr)zero destination(port/addr)short bufferbuffer fullinvalid addressunsupportedmismatchmismatched lengthinvalid configurationinvalid fieldinvalid length fieldresource exhausted"
|
||||
|
||||
var _errGeneric_index = [...]uint8{0, 39, 53, 71, 93, 120}
|
||||
var _errGeneric_index = [...]uint16{0, 39, 53, 71, 93, 120, 132, 143, 158, 169, 177, 194, 215, 228, 248, 266}
|
||||
|
||||
func (i errGeneric) String() string {
|
||||
i -= 1
|
||||
|
||||
+6
-10
@@ -15,12 +15,8 @@ import (
|
||||
)
|
||||
|
||||
var (
|
||||
errDeadlineExceeded = os.ErrDeadlineExceeded
|
||||
errNoRemoteAddr = errors.New("tcp: no remote address established")
|
||||
errInvalidIP = errors.New("tcp: invalid IP")
|
||||
errMismatchedIPVersion = errors.New("mismatched IP version")
|
||||
errBadDemuxOffset = errors.New("bad offset in TCPConn.Recv")
|
||||
errIPAddrMismatch = errors.New("IP addr mismatch on TCPConn")
|
||||
errDeadlineExceeded = os.ErrDeadlineExceeded
|
||||
errNoRemoteAddr = errors.New("tcp: no remote address established")
|
||||
)
|
||||
|
||||
// Conn builds on the [Handler] abstraction and adds IP header knowledge, time management, and familiar user facing API
|
||||
@@ -135,7 +131,7 @@ func (conn *Conn) OpenActive(localPort uint16, remote netip.AddrPort, iss Value)
|
||||
conn.mu.Lock()
|
||||
defer conn.mu.Unlock()
|
||||
if !remote.IsValid() {
|
||||
return errInvalidIP
|
||||
return lneto.ErrInvalidAddr
|
||||
}
|
||||
rport := remote.Port()
|
||||
err := conn.h.OpenActive(localPort, rport, iss)
|
||||
@@ -330,14 +326,14 @@ func (conn *Conn) Demux(buf []byte, off int) (err error) {
|
||||
conn.mu.Lock()
|
||||
defer conn.mu.Unlock()
|
||||
if off >= len(buf) {
|
||||
return errBadDemuxOffset
|
||||
return lneto.ErrShortBuffer
|
||||
}
|
||||
raddr, _, id, _, err := internal.GetIPAddr(buf[:off])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if conn.isRaddrSet() && !internal.BytesEqual(conn.remoteAddr, raddr) {
|
||||
return errIPAddrMismatch
|
||||
return lneto.ErrMismatch
|
||||
}
|
||||
conn.trace("tcpconn.Recv", slog.Uint64("lport", uint64(conn.h.LocalPort())), slog.Uint64("rport", uint64(conn.h.remotePort)))
|
||||
err = conn.h.Recv(buf[off:])
|
||||
@@ -365,7 +361,7 @@ func (conn *Conn) Encapsulate(carrierData []byte, offsetToIP, offsetToFrame int)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
} else if len(raddr) != len(conn.remoteAddr) {
|
||||
return 0, errMismatchedIPVersion
|
||||
return 0, lneto.ErrMismatchLen
|
||||
}
|
||||
n, err = conn.h.Send(carrierData[offsetToFrame:])
|
||||
if err != nil || n == 0 {
|
||||
|
||||
+12
-12
@@ -6,24 +6,24 @@ import (
|
||||
"math/bits"
|
||||
"strconv"
|
||||
"unsafe"
|
||||
|
||||
"github.com/soypat/lneto"
|
||||
)
|
||||
|
||||
//go:generate stringer -type=State,OptionKind -linecomment -output stringers.go .
|
||||
|
||||
var (
|
||||
// errDropSegment is a flag that signals to drop a segment silently.
|
||||
errDropSegment = errors.New("drop segment")
|
||||
errWindowTooLarge = errors.New("invalid window size > 2**16")
|
||||
errDropSegment error = lneto.ErrPacketDrop
|
||||
errWindowTooLarge = errors.New("invalid window size > 2**16")
|
||||
|
||||
errBufferTooSmall = errors.New("tcp buffer too small")
|
||||
errNeedClosedTCBToOpen = errors.New("need closed TCB to call open")
|
||||
errInvalidState = errors.New("invalid state")
|
||||
errConnNotExist = errors.New("connection does not exist")
|
||||
errConnectionClosing = errors.New("connection closing")
|
||||
errExpectedSYN = errors.New("seqs:expected SYN")
|
||||
errBadSegack = errors.New("seqs:bad segack")
|
||||
errFinwaitExpectedACK = errors.New("seqs:finwait1 expected ACK")
|
||||
errFinwaitExpectedFinack = errors.New("seqs:finwait2 expected FINACK")
|
||||
errBufferTooSmall error = lneto.ErrShortBuffer
|
||||
errNeedClosedTCBToOpen = errors.New("need closed TCB to call open")
|
||||
errInvalidState = errors.New("invalid state")
|
||||
errConnNotExist = errors.New("connection does not exist")
|
||||
errConnectionClosing = errors.New("connection closing")
|
||||
errExpectedSYN = errors.New("seqs:expected SYN")
|
||||
errBadSegack = errors.New("seqs:bad segack")
|
||||
errFinwaitExpectedACK = errors.New("seqs:finwait1 expected ACK")
|
||||
|
||||
errWindowOverflow = newRejectErr("wnd > 2**16")
|
||||
errSeqNotInWindow = newRejectErr("seq not in snd/rcv.wnd")
|
||||
|
||||
+5
-13
@@ -2,7 +2,6 @@ package tcp
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"errors"
|
||||
"fmt"
|
||||
"math"
|
||||
|
||||
@@ -19,7 +18,7 @@ const (
|
||||
// with payload/options of frames to avoid panics.
|
||||
func NewFrame(buf []byte) (Frame, error) {
|
||||
if len(buf) < sizeHeaderTCP {
|
||||
return Frame{buf: nil}, errors.New("TCP packet too short")
|
||||
return Frame{buf: nil}, lneto.ErrShortBuffer
|
||||
}
|
||||
return Frame{buf: buf}, nil
|
||||
}
|
||||
@@ -178,13 +177,6 @@ func (tfrm Frame) String() string {
|
||||
// Validation API
|
||||
//
|
||||
|
||||
var (
|
||||
errShortTCP = errors.New("TCP offset exceeds frame")
|
||||
errBadTCPOff = errors.New("TCP offset invalid")
|
||||
errEvilPacket = errors.New("evil packet")
|
||||
errZeroDstPort = errors.New("TCP zero destination port")
|
||||
errZeroSrcPort = errors.New("TCP zero source port")
|
||||
)
|
||||
|
||||
// func (tfrm Frame) Validate(v *lneto.Validator) {
|
||||
// tfrm.ValidateSize(v)
|
||||
@@ -196,19 +188,19 @@ var (
|
||||
func (tfrm Frame) ValidateSize(v *lneto.Validator) {
|
||||
off := tfrm.HeaderLength()
|
||||
if off < sizeHeaderTCP {
|
||||
v.AddBitPosErr(12*8, 4, errBadTCPOff)
|
||||
v.AddBitPosErr(12*8, 4, lneto.ErrInvalidLengthField)
|
||||
}
|
||||
if off > len(tfrm.RawData()) {
|
||||
v.AddBitPosErr(12*8, 4, errShortTCP)
|
||||
v.AddBitPosErr(12*8, 4, lneto.ErrInvalidLengthField)
|
||||
}
|
||||
}
|
||||
|
||||
func (tfrm Frame) ValidateExceptCRC(v *lneto.Validator) {
|
||||
tfrm.ValidateSize(v)
|
||||
if tfrm.DestinationPort() == 0 {
|
||||
v.AddBitPosErr(2*8, 16, errZeroDstPort)
|
||||
v.AddBitPosErr(2*8, 16, lneto.ErrZeroDestination)
|
||||
}
|
||||
if tfrm.SourcePort() == 0 {
|
||||
v.AddBitPosErr(0, 16, errZeroSrcPort)
|
||||
v.AddBitPosErr(0, 16, lneto.ErrZeroSource)
|
||||
}
|
||||
}
|
||||
|
||||
+5
-11
@@ -1,7 +1,6 @@
|
||||
package tcp
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"io"
|
||||
"net"
|
||||
|
||||
@@ -11,11 +10,6 @@ import (
|
||||
"github.com/soypat/lneto/internal"
|
||||
)
|
||||
|
||||
var (
|
||||
errMismatchedSrcPort = errors.New("source port mismatch")
|
||||
errMismatchedDstPort = errors.New("destination port mismatch")
|
||||
)
|
||||
|
||||
// Handler is a low level TCP handling data structure. It implements logic
|
||||
// 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.
|
||||
@@ -56,10 +50,10 @@ func (h *Handler) State() State { return h.scb.State() }
|
||||
// If the argument buffer is nil then the respective currently set buffer will be reused.
|
||||
func (h *Handler) SetBuffers(txbuf, rxbuf []byte, packets int) error {
|
||||
if h.bufRx.Buf == nil && (len(rxbuf) < minBufferSize || len(txbuf) < minBufferSize) {
|
||||
return errors.New("tcp: short buffer")
|
||||
return lneto.ErrShortBuffer
|
||||
}
|
||||
if !h.scb.State().IsClosed() {
|
||||
return errors.New("tcp.Handler must be closed before setting buffers")
|
||||
return lneto.ErrInvalidConfig
|
||||
}
|
||||
if rxbuf != nil {
|
||||
h.bufRx.Buf = rxbuf
|
||||
@@ -156,15 +150,15 @@ func (h *Handler) Recv(incomingPacket []byte) error {
|
||||
|
||||
remotePort := tfrm.SourcePort()
|
||||
if h.remotePort != 0 && remotePort != h.remotePort {
|
||||
return errMismatchedSrcPort
|
||||
return lneto.ErrMismatch
|
||||
}
|
||||
dstPort := tfrm.DestinationPort()
|
||||
if h.localPort != dstPort {
|
||||
return errMismatchedDstPort
|
||||
return lneto.ErrMismatch
|
||||
}
|
||||
payload := tfrm.Payload()
|
||||
if len(payload) > h.bufRx.Free() {
|
||||
return errors.New("rx buffer full")
|
||||
return lneto.ErrBufferFull
|
||||
}
|
||||
segIncoming := tfrm.Segment(len(payload))
|
||||
if h.scb.IncomingIsKeepalive(segIncoming) {
|
||||
|
||||
+5
-6
@@ -1,7 +1,6 @@
|
||||
package tcp
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"log/slog"
|
||||
"net"
|
||||
"sync"
|
||||
@@ -70,7 +69,7 @@ func (listener *Listener) Close() error {
|
||||
listener.mu.Lock()
|
||||
defer listener.mu.Unlock()
|
||||
if listener.isClosed() {
|
||||
return errors.New("already closed")
|
||||
return net.ErrClosed
|
||||
}
|
||||
listener.debug("listener:reset", slog.Uint64("port", uint64(listener.port)))
|
||||
listener.connID++
|
||||
@@ -80,9 +79,9 @@ func (listener *Listener) Close() error {
|
||||
|
||||
func (listener *Listener) Reset(port uint16, pool pool) error {
|
||||
if port == 0 {
|
||||
return errZeroDstPort
|
||||
return lneto.ErrZeroSource
|
||||
} else if pool == nil {
|
||||
return errors.New("nil TCP pool")
|
||||
return lneto.ErrInvalidConfig
|
||||
}
|
||||
listener.mu.Lock()
|
||||
defer listener.mu.Unlock()
|
||||
@@ -126,7 +125,7 @@ func (listener *Listener) TryAccept() (*Conn, any, error) {
|
||||
listener.incoming[i] = handler{} // discard from ready.
|
||||
return conn, userData, nil
|
||||
}
|
||||
return nil, nil, errors.New("no conns available")
|
||||
return nil, nil, lneto.ErrExhausted
|
||||
}
|
||||
|
||||
// Encapsulate implements [StackNode].
|
||||
@@ -199,7 +198,7 @@ func (listener *Listener) Demux(carrierData []byte, tcpFrameOffset int) error {
|
||||
}
|
||||
dst := tfrm.DestinationPort()
|
||||
if dst != listener.port {
|
||||
return errors.New("not our port")
|
||||
return lneto.ErrMismatch
|
||||
}
|
||||
src := tfrm.SourcePort()
|
||||
|
||||
|
||||
+8
-8
@@ -1,9 +1,9 @@
|
||||
package tcp
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/soypat/lneto"
|
||||
)
|
||||
|
||||
type OptionKind uint8
|
||||
@@ -88,11 +88,11 @@ func (op OptionCodec) PutOption32(dst []byte, kind OptionKind, v uint32) (int, e
|
||||
func (op OptionCodec) PutOption(dst []byte, kind OptionKind, data ...byte) (int, error) {
|
||||
putSize := 2 + len(data)
|
||||
if len(dst) < putSize {
|
||||
return -1, errBufferTooSmall
|
||||
return -1, lneto.ErrShortBuffer
|
||||
} else if putSize > 255 {
|
||||
return -1, errors.New("option data too large")
|
||||
return -1, lneto.ErrInvalidLengthField
|
||||
} else if kind == OptNop || kind == OptEnd {
|
||||
return -1, errors.New("cant put Nop or End option type")
|
||||
return -1, lneto.ErrInvalidField
|
||||
}
|
||||
dst[0] = byte(kind)
|
||||
dst[1] = byte(putSize)
|
||||
@@ -111,13 +111,13 @@ func (op OptionCodec) ForEachOption(opts []byte, fn func(OptionKind, []byte) err
|
||||
continue
|
||||
}
|
||||
if len(opts[off:]) < 1 {
|
||||
return errors.New("short TCP options")
|
||||
return lneto.ErrShortBuffer
|
||||
}
|
||||
size := int(opts[off]) // Total option length including kind and length bytes.
|
||||
off++
|
||||
dataLen := size - 2 // Data bytes after kind and length.
|
||||
if dataLen < 0 || len(opts[off:]) < dataLen {
|
||||
return fmt.Errorf("option %q length %d exceeds buffer size %d", kind.String(), size, len(opts[off:]))
|
||||
return lneto.ErrShortBuffer
|
||||
}
|
||||
|
||||
if !skipSizeValidation {
|
||||
@@ -133,7 +133,7 @@ func (op OptionCodec) ForEachOption(opts []byte, fn func(OptionKind, []byte) err
|
||||
expectSize = 2
|
||||
}
|
||||
if expectSize != -1 && size != expectSize {
|
||||
return fmt.Errorf("bad TCP option %q size want %d got %d", kind.String(), expectSize, size)
|
||||
return lneto.ErrInvalidLengthField
|
||||
}
|
||||
}
|
||||
if !(skipObsolete && kind.IsObsolete()) {
|
||||
|
||||
+4
-5
@@ -2,8 +2,9 @@ package tcp
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"errors"
|
||||
"io"
|
||||
|
||||
"github.com/soypat/lneto"
|
||||
)
|
||||
|
||||
// Embed low 5 bits of counter into cookie for efficient validation.
|
||||
@@ -45,15 +46,13 @@ type SYNCookieConfig struct {
|
||||
MaxCounterDelta uint32
|
||||
}
|
||||
|
||||
var (
|
||||
errInvalidCookie = errors.New("tcp: invalid SYN cookie")
|
||||
)
|
||||
var errInvalidCookie error = lneto.ErrMismatch
|
||||
|
||||
// Reset initializes or reinitializes the SYNCookie with the given configuration.
|
||||
// The counter is preserved across resets to maintain cookie validity during secret rotation.
|
||||
func (sc *SYNCookieJar) Reset(config SYNCookieConfig) error {
|
||||
if config.Rand == nil {
|
||||
return errors.New("need rand function")
|
||||
return lneto.ErrInvalidConfig
|
||||
}
|
||||
_, err := io.ReadFull(config.Rand, sc.secret[:])
|
||||
if err != nil {
|
||||
|
||||
+9
-16
@@ -1,20 +1,12 @@
|
||||
package tcp
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"log/slog"
|
||||
|
||||
"github.com/soypat/lneto"
|
||||
"github.com/soypat/lneto/internal"
|
||||
)
|
||||
|
||||
var (
|
||||
errPacketQueueFull = errors.New("packet queue full")
|
||||
errQueuedPacketsLEZ = errors.New("queued packets <=0")
|
||||
errInvalidBufSize = errors.New("invalid buffer size")
|
||||
errSeqLessThanLast = errors.New("sequence number less than last sequence number")
|
||||
errNoPacketToAck = errors.New("no packet to ack")
|
||||
errAckUnsent = errors.New("ack of unsent packet")
|
||||
)
|
||||
|
||||
const (
|
||||
// this must be at least 2 for buffer to work.
|
||||
minBufferSize = 2
|
||||
@@ -60,9 +52,9 @@ type ringidx struct {
|
||||
func (rtx *ringTx) Reset(buf []byte, maxqueuedPackets int, iss Value) error {
|
||||
buf = buf[:len(buf):len(buf)] // safely omit capacity section.
|
||||
if maxqueuedPackets <= 0 {
|
||||
return errQueuedPacketsLEZ
|
||||
return lneto.ErrInvalidConfig
|
||||
} else if len(buf) < minBufferSize || len(buf) < maxqueuedPackets {
|
||||
return errInvalidBufSize
|
||||
return lneto.ErrShortBuffer
|
||||
}
|
||||
|
||||
*rtx = ringTx{
|
||||
@@ -127,11 +119,12 @@ func (rtx *ringTx) Write(b []byte) (n int, err error) {
|
||||
func (rtx *ringTx) MakePacket(b []byte, currentSeq Value) (int, error) {
|
||||
free := rtx.slist.Free()
|
||||
if free == 0 {
|
||||
return 0, errPacketQueueFull
|
||||
return 0, lneto.ErrBufferFull
|
||||
}
|
||||
endSeq, ok := rtx.sentEndSeq()
|
||||
if ok && currentSeq.LessThan(endSeq) {
|
||||
return 0, errSeqLessThanLast
|
||||
internal.LogAttrs(nil, slog.LevelError, "txqueue:seq<endseq", slog.Uint64("seq", uint64(currentSeq)), slog.Uint64("endseq", uint64(endSeq)))
|
||||
return 0, lneto.ErrBug
|
||||
}
|
||||
// Reading unsent ring consumes unsent and converts it to "sent".
|
||||
unsent, _ := rtx.unsentRing()
|
||||
@@ -321,11 +314,11 @@ func (sl *sentlist) AddPacket(datalen, off, bufsize int, seq Value) *ringidx {
|
||||
func (sl *sentlist) RecvAck(ack Value, bufsize int) error {
|
||||
newest := sl.Newest()
|
||||
if newest == nil {
|
||||
return errNoPacketToAck
|
||||
return lneto.ErrPacketDrop
|
||||
}
|
||||
endseq := newest.endSeq()
|
||||
if endseq.LessThan(ack) {
|
||||
return errAckUnsent
|
||||
return lneto.ErrPacketDrop
|
||||
}
|
||||
// Mark fully acked.
|
||||
for i := 0; i < len(sl.pkts); i++ {
|
||||
|
||||
+3
-9
@@ -2,7 +2,6 @@ package udp
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"errors"
|
||||
|
||||
"github.com/soypat/lneto"
|
||||
)
|
||||
@@ -13,7 +12,7 @@ import (
|
||||
// with payload/options of frames to avoid panics.
|
||||
func NewFrame(buf []byte) (Frame, error) {
|
||||
if len(buf) < sizeHeader {
|
||||
return Frame{buf: buf}, errors.New("UDP packet too short")
|
||||
return Frame{buf: buf}, lneto.ErrShortBuffer
|
||||
}
|
||||
return Frame{buf: buf}, nil
|
||||
}
|
||||
@@ -90,19 +89,14 @@ func (frm Frame) ClearHeader() {
|
||||
// Validation API.
|
||||
//
|
||||
|
||||
var (
|
||||
errBadLen = errors.New("udp: bad UDP length")
|
||||
errShort = errors.New("udp: short buffer")
|
||||
)
|
||||
|
||||
// ValidateSize checks the frame's size fields and compares with the actual buffer
|
||||
// the frame. It returns a non-nil error on finding an inconsistency.
|
||||
func (ufrm Frame) ValidateSize(v *lneto.Validator) {
|
||||
ul := ufrm.Length()
|
||||
if ul < sizeHeader {
|
||||
v.AddError(errBadLen)
|
||||
v.AddError(lneto.ErrInvalidLengthField)
|
||||
}
|
||||
if int(ul) > len(ufrm.RawData()) {
|
||||
v.AddError(errShort)
|
||||
v.AddError(lneto.ErrShortBuffer)
|
||||
}
|
||||
}
|
||||
|
||||
+8
-10
@@ -99,13 +99,13 @@ func (s *StackAsync) Reset(cfg StackConfig) error {
|
||||
addr := cfg.StaticAddress
|
||||
s.prng = uint32(cfg.RandSeed)
|
||||
if s.prng == 0 {
|
||||
return errors.New("zero random seed")
|
||||
return lneto.ErrInvalidConfig
|
||||
}
|
||||
s.hostname = cfg.Hostname
|
||||
if !addr.IsValid() {
|
||||
addr = netip.AddrFrom4([4]byte{}) // If static not set DHCP will be performed and address will be zero.
|
||||
} else if addr.Is6() {
|
||||
return errors.New("IPv6 unsupported as of yet")
|
||||
return lneto.ErrUnsupported
|
||||
}
|
||||
const linkNodes = 2 // ARP and IP nodes
|
||||
ecfg := internet.StackEthernetConfig{
|
||||
@@ -171,13 +171,11 @@ func (s *StackAsync) Reset(cfg StackConfig) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
var errInvalidIPAddr = errors.New("invaldi IP address")
|
||||
|
||||
func (s *StackAsync) resetARP() error {
|
||||
mac := s.link.HardwareAddr6()
|
||||
addr := s.ip.Addr()
|
||||
if !addr.IsValid() {
|
||||
return errInvalidIPAddr
|
||||
return lneto.ErrInvalidAddr
|
||||
}
|
||||
proto := ethernet.TypeIPv4
|
||||
if addr.Is6() {
|
||||
@@ -384,7 +382,7 @@ func (s *StackAsync) ResultLookupIP(host string) ([]netip.Addr, bool, error) {
|
||||
} else if len(data) == 16 {
|
||||
addrs = append(addrs, netip.AddrFrom16([16]byte(data)))
|
||||
} else {
|
||||
err = errors.New("bogus IP")
|
||||
err = lneto.ErrInvalidAddr
|
||||
}
|
||||
}
|
||||
if err == nil && len(addrs) == 0 {
|
||||
@@ -441,7 +439,7 @@ func (s *StackAsync) StartResolveHardwareAddress6(ip netip.Addr) error {
|
||||
s.mu.Lock()
|
||||
defer s.mu.Unlock()
|
||||
if !ip.Is4() {
|
||||
return errors.New("unsupported or invalid IP address")
|
||||
return lneto.ErrUnsupported
|
||||
}
|
||||
addr := ip.As4()
|
||||
return s.arp.StartQuery(nil, addr[:])
|
||||
@@ -452,7 +450,7 @@ func (s *StackAsync) ResultResolveHardwareAddress6(ip netip.Addr) (hw [6]byte, e
|
||||
s.mu.Lock()
|
||||
defer s.mu.Unlock()
|
||||
if !ip.Is4() {
|
||||
return hw, errors.New("unsupported or invalid IP address")
|
||||
return hw, lneto.ErrUnsupported
|
||||
}
|
||||
addr := ip.As4()
|
||||
hwslice, err := s.arp.QueryResult(addr[:])
|
||||
@@ -469,7 +467,7 @@ func (s *StackAsync) DiscardResolveHardwareAddress6(ip netip.Addr) error {
|
||||
s.mu.Lock()
|
||||
defer s.mu.Unlock()
|
||||
if !ip.Is4() {
|
||||
return errors.New("unsupported or invalid IP address")
|
||||
return lneto.ErrUnsupported
|
||||
}
|
||||
addr := ip.As4()
|
||||
return s.arp.DiscardQuery(addr[:])
|
||||
@@ -526,7 +524,7 @@ func (stack *StackAsync) AssimilateDHCPResults(results *DHCPResults) error {
|
||||
}
|
||||
if len(results.DNSServers) > 0 {
|
||||
if !results.DNSServers[0].IsValid() || !results.DNSServers[0].Is4() {
|
||||
return errors.New("bad DNS server address, IPv6 or invalid")
|
||||
return lneto.ErrInvalidAddr
|
||||
}
|
||||
stack.dnssv = results.DNSServers[0]
|
||||
}
|
||||
|
||||
@@ -2,12 +2,12 @@ package xnet
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"net"
|
||||
"net/netip"
|
||||
"syscall"
|
||||
"time"
|
||||
|
||||
"github.com/soypat/lneto"
|
||||
"github.com/soypat/lneto/tcp"
|
||||
)
|
||||
|
||||
@@ -36,7 +36,7 @@ func (s StackBerkeley) Socket(ctx context.Context, network string, family, sotyp
|
||||
switch family {
|
||||
case syscall.AF_INET:
|
||||
default:
|
||||
return nil, errors.New("unsupported address family")
|
||||
return nil, lneto.ErrUnsupported
|
||||
}
|
||||
var local, remote netip.AddrPort
|
||||
if laddr != nil {
|
||||
@@ -54,10 +54,10 @@ func (s StackBerkeley) Socket(ctx context.Context, network string, family, sotyp
|
||||
|
||||
switch network {
|
||||
case "udp", "udp4":
|
||||
return nil, errors.New("udp not yet supported")
|
||||
return nil, lneto.ErrUnsupported
|
||||
case "tcp", "tcp4":
|
||||
if sotype != sockSTREAM {
|
||||
return nil, errors.New("unsupported socket type")
|
||||
return nil, lneto.ErrUnsupported
|
||||
}
|
||||
|
||||
if raddr != nil {
|
||||
@@ -106,7 +106,7 @@ func (s StackBerkeley) Socket(ctx context.Context, network string, family, sotyp
|
||||
return &l, nil
|
||||
}
|
||||
}
|
||||
return nil, errors.New("unsupported network")
|
||||
return nil, lneto.ErrUnsupported
|
||||
}
|
||||
|
||||
type tcplistener struct {
|
||||
|
||||
+2
-2
@@ -2,11 +2,11 @@ package xnet
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"log/slog"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/soypat/lneto"
|
||||
"github.com/soypat/lneto/tcp"
|
||||
)
|
||||
|
||||
@@ -52,7 +52,7 @@ type TCPPoolConfig struct {
|
||||
|
||||
func NewTCPPool(cfg TCPPoolConfig) (*TCPPool, error) {
|
||||
if cfg.EstablishedTimeout <= 0 || cfg.ClosingTimeout <= 0 {
|
||||
return nil, errors.New("invalid timeout")
|
||||
return nil, lneto.ErrInvalidConfig
|
||||
}
|
||||
n := cfg.PoolSize
|
||||
pool := &TCPPool{
|
||||
|
||||
Reference in New Issue
Block a user