mirror of
https://github.com/soypat/lneto.git
synced 2026-08-21 15:09:05 +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:
@@ -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))
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user