mirror of
https://github.com/soypat/lneto.git
synced 2026-08-25 00:49:04 +00:00
Reduce heap allocations (#42)
* 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 * reuse function for reclaiming frames among all protocols for lower memory usage * remove extraneous code
This commit is contained in:
+3
-1
@@ -5,6 +5,7 @@ import (
|
|||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
|
"log/slog"
|
||||||
"math"
|
"math"
|
||||||
"math/bits"
|
"math/bits"
|
||||||
"net"
|
"net"
|
||||||
@@ -220,7 +221,8 @@ func (c *Client) Demux(carrierData []byte, frameOffset int) error {
|
|||||||
|
|
||||||
msgOK := msgType == MsgOffer || msgType == MsgAck
|
msgOK := msgType == MsgOffer || msgType == MsgAck
|
||||||
if !msgOK {
|
if !msgOK {
|
||||||
return fmt.Errorf("invalid DHCP message received or none got=%d", msgType)
|
internal.LogAttrs(nil, slog.LevelError, "invalid DHCP message", slog.Uint64("type", uint64(msgType)))
|
||||||
|
return lneto.ErrBug
|
||||||
}
|
}
|
||||||
err = c.setOptions(frm)
|
err = c.setOptions(frm)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|||||||
+4
-5
@@ -209,8 +209,9 @@ func (m *Message) AppendTo(buf []byte, txid uint16, flags HeaderFlags) (_ []byte
|
|||||||
nans := uint16(len(m.Answers))
|
nans := uint16(len(m.Answers))
|
||||||
nauth := uint16(len(m.Authorities))
|
nauth := uint16(len(m.Authorities))
|
||||||
nadd := uint16(len(m.Additionals))
|
nadd := uint16(len(m.Additionals))
|
||||||
var hdr [SizeHeader]byte
|
buf = slices.Grow(buf, int(m.Len()))
|
||||||
f, err := NewFrame(hdr[:])
|
// Set the buffer directly with header fields.
|
||||||
|
f, err := NewFrame(buf[len(buf) : len(buf)+SizeHeader])
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return buf, err
|
return buf, err
|
||||||
}
|
}
|
||||||
@@ -220,9 +221,7 @@ func (m *Message) AppendTo(buf []byte, txid uint16, flags HeaderFlags) (_ []byte
|
|||||||
f.SetANCount(nans)
|
f.SetANCount(nans)
|
||||||
f.SetNSCount(nauth)
|
f.SetNSCount(nauth)
|
||||||
f.SetARCount(nadd)
|
f.SetARCount(nadd)
|
||||||
|
buf = buf[:len(buf)+SizeHeader]
|
||||||
buf = slices.Grow(buf, int(m.Len()))
|
|
||||||
buf = append(buf, hdr[:]...)
|
|
||||||
for _, q := range m.Questions {
|
for _, q := range m.Questions {
|
||||||
buf, err = q.appendTo(buf)
|
buf, err = q.appendTo(buf)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|||||||
@@ -20,6 +20,7 @@ type errGeneric uint8
|
|||||||
// Generic errors common to internet functioning.
|
// Generic errors common to internet functioning.
|
||||||
const (
|
const (
|
||||||
_ errGeneric = iota // non-initialized err
|
_ errGeneric = iota // non-initialized err
|
||||||
|
ErrBug // lneto-bug(use build tag "debugheaplog")
|
||||||
ErrPacketDrop // packet dropped
|
ErrPacketDrop // packet dropped
|
||||||
ErrBadCRC // incorrect checksum
|
ErrBadCRC // incorrect checksum
|
||||||
ErrZeroSource // zero source(port/addr)
|
ErrZeroSource // zero source(port/addr)
|
||||||
|
|||||||
@@ -46,3 +46,22 @@ func SliceReuse[T any](buf *[]T, n int) {
|
|||||||
*buf = (*buf)[:0]
|
*buf = (*buf)[:0]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SliceReclaim extends the slice length by one and returns a pointer to
|
||||||
|
// the new last element. The returned element is not zeroed, so callers
|
||||||
|
// can reuse any existing allocations it may hold from prior use.
|
||||||
|
//
|
||||||
|
// Beware: as the name implies SliceReclaim reuses previously held memory
|
||||||
|
// so it is up to caller to ensure the reclaimed data is coherent by zeroing out
|
||||||
|
// the needed fields and reusing the previously held slices or pointers responsibly.
|
||||||
|
func SliceReclaim[T any](ptr *[]T) *T {
|
||||||
|
b := *ptr
|
||||||
|
n := len(b)
|
||||||
|
if n == cap(b) {
|
||||||
|
var z T
|
||||||
|
*ptr = append(b, z)
|
||||||
|
} else {
|
||||||
|
*ptr = b[:n+1]
|
||||||
|
}
|
||||||
|
return &(*ptr)[n]
|
||||||
|
}
|
||||||
|
|||||||
@@ -0,0 +1,22 @@
|
|||||||
|
package internal
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/binary"
|
||||||
|
"log/slog"
|
||||||
|
)
|
||||||
|
|
||||||
|
// SlogAddr4 returns a slog.Attr for a 4-byte IPv4 address
|
||||||
|
// packed into a uint64 without allocating a string.
|
||||||
|
func SlogAddr4(key string, addr *[4]byte) slog.Attr {
|
||||||
|
u64Addr := uint64(binary.BigEndian.Uint32(addr[:]))
|
||||||
|
return slog.Uint64(key, u64Addr)
|
||||||
|
}
|
||||||
|
|
||||||
|
// SlogAddr6 returns a slog.Attr for a 6-byte hardware (MAC) address
|
||||||
|
// packed into a uint64 without allocating a string.
|
||||||
|
func SlogAddr6(key string, addr *[6]byte) slog.Attr {
|
||||||
|
var buf [8]byte
|
||||||
|
copy(buf[2:], addr[:])
|
||||||
|
u64Addr := binary.BigEndian.Uint64(buf[:])
|
||||||
|
return slog.Uint64(key, u64Addr)
|
||||||
|
}
|
||||||
+90
-106
@@ -14,6 +14,7 @@ import (
|
|||||||
"github.com/soypat/lneto/dns"
|
"github.com/soypat/lneto/dns"
|
||||||
"github.com/soypat/lneto/ethernet"
|
"github.com/soypat/lneto/ethernet"
|
||||||
"github.com/soypat/lneto/http/httpraw"
|
"github.com/soypat/lneto/http/httpraw"
|
||||||
|
"github.com/soypat/lneto/internal"
|
||||||
"github.com/soypat/lneto/ipv4"
|
"github.com/soypat/lneto/ipv4"
|
||||||
"github.com/soypat/lneto/ipv4/icmpv4"
|
"github.com/soypat/lneto/ipv4/icmpv4"
|
||||||
"github.com/soypat/lneto/ipv6"
|
"github.com/soypat/lneto/ipv6"
|
||||||
@@ -43,7 +44,31 @@ type PacketBreakdown struct {
|
|||||||
SubfieldLimit int
|
SubfieldLimit int
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// initFrames pre-allocates a []Frame with per-position Fields capacity
|
||||||
|
// sized for the protocol layers at each position. This avoids heap
|
||||||
|
// allocations on the first packet when using SliceReclaim on subsequent calls.
|
||||||
|
//
|
||||||
|
// Pre-allocated capacities per position:
|
||||||
|
//
|
||||||
|
// 0: Ethernet (3 base + VLAN tag = 4)
|
||||||
|
// 1: L3 max(IPv4=12, ARP=9, IPv6=8) = 12
|
||||||
|
// 2: L4 max(TCP=10, ICMP=8, UDP=4) = 10
|
||||||
|
// 3: App max(DHCP=15, NTP=13, HTTP=2, DNS=1) = 16
|
||||||
|
// 4-5: overflow/remaining = 2
|
||||||
|
func (pc *PacketBreakdown) initFrames() []Frame {
|
||||||
|
const nframes = 6
|
||||||
|
var fieldCaps = [nframes]int{4, 12, 10, 16, 2, 2}
|
||||||
|
frames := make([]Frame, nframes)
|
||||||
|
for i := range frames {
|
||||||
|
frames[i].Fields = make([]FrameField, 0, fieldCaps[i])
|
||||||
|
}
|
||||||
|
return frames[:0]
|
||||||
|
}
|
||||||
|
|
||||||
func (pc *PacketBreakdown) CaptureEthernet(dst []Frame, pkt []byte, bitOffset int) ([]Frame, error) {
|
func (pc *PacketBreakdown) CaptureEthernet(dst []Frame, pkt []byte, bitOffset int) ([]Frame, error) {
|
||||||
|
if dst == nil {
|
||||||
|
dst = pc.initFrames()
|
||||||
|
}
|
||||||
if bitOffset%8 != 0 {
|
if bitOffset%8 != 0 {
|
||||||
return dst, errors.New("ethernet must be parsed at byte boundary")
|
return dst, errors.New("ethernet must be parsed at byte boundary")
|
||||||
}
|
}
|
||||||
@@ -56,23 +81,17 @@ func (pc *PacketBreakdown) CaptureEthernet(dst []Frame, pkt []byte, bitOffset in
|
|||||||
return dst, pc.validator().ErrPop()
|
return dst, pc.validator().ErrPop()
|
||||||
}
|
}
|
||||||
|
|
||||||
finfo := Frame{
|
finfo := reclaimFrame(&dst, ProtoEthernet, bitOffset, baseEthernetFields[:])
|
||||||
Protocol: ProtoEthernet,
|
|
||||||
PacketBitOffset: bitOffset,
|
|
||||||
}
|
|
||||||
finfo.Fields = append(finfo.Fields, baseEthernetFields[:]...)
|
|
||||||
etype := efrm.EtherTypeOrSize()
|
etype := efrm.EtherTypeOrSize()
|
||||||
end := 14*octet + bitOffset
|
end := 14*octet + bitOffset
|
||||||
if etype.IsSize() {
|
if etype.IsSize() {
|
||||||
finfo.Fields[len(finfo.Fields)-1].Class = FieldClassSize
|
finfo.Fields[len(finfo.Fields)-1].Class = FieldClassSize
|
||||||
dst = append(dst, finfo)
|
reclaimRemainingFrame(&dst, "Ethernet payload", FieldClassPayload, end, octet*len(pkt))
|
||||||
dst = append(dst, remainingFrameInfo("Ethernet payload", FieldClassPayload, end, octet*len(pkt)))
|
|
||||||
return dst, nil
|
return dst, nil
|
||||||
}
|
}
|
||||||
dst = append(dst, finfo)
|
|
||||||
if efrm.IsVLAN() {
|
if efrm.IsVLAN() {
|
||||||
finfo.Fields = append(finfo.Fields, FrameField{Name: "VLAN Tag", Class: FieldClassType, FrameBitOffset: end, BitLength: 2 * octet})
|
finfo.Fields = append(finfo.Fields, FrameField{Name: "VLAN Tag", Class: FieldClassType, FrameBitOffset: end, BitLength: 2 * octet})
|
||||||
dst = append(dst, remainingFrameInfo("Ethernet VLAN", FieldClassPayload, end+2*octet, octet*len(pkt)))
|
reclaimRemainingFrame(&dst, "Ethernet VLAN", FieldClassPayload, end+2*octet, octet*len(pkt))
|
||||||
return dst, nil
|
return dst, nil
|
||||||
}
|
}
|
||||||
switch etype {
|
switch etype {
|
||||||
@@ -83,7 +102,7 @@ func (pc *PacketBreakdown) CaptureEthernet(dst []Frame, pkt []byte, bitOffset in
|
|||||||
case ethernet.TypeIPv6:
|
case ethernet.TypeIPv6:
|
||||||
dst, err = pc.CaptureIPv6(dst, pkt, end)
|
dst, err = pc.CaptureIPv6(dst, pkt, end)
|
||||||
default:
|
default:
|
||||||
dst = append(dst, remainingFrameInfo(etype, FieldClassPayload, end, octet*len(pkt)))
|
reclaimRemainingFrame(&dst, etype, FieldClassPayload, end, octet*len(pkt))
|
||||||
}
|
}
|
||||||
return dst, err
|
return dst, err
|
||||||
}
|
}
|
||||||
@@ -101,13 +120,8 @@ func (pc *PacketBreakdown) CaptureARP(dst []Frame, pkt []byte, bitOffset int) ([
|
|||||||
return dst, pc.validator().ErrPop()
|
return dst, pc.validator().ErrPop()
|
||||||
}
|
}
|
||||||
|
|
||||||
finfo := Frame{
|
finfo := reclaimFrame(&dst, ethernet.TypeARP, bitOffset, baseARPFields[:])
|
||||||
Protocol: ethernet.TypeARP,
|
|
||||||
PacketBitOffset: bitOffset,
|
|
||||||
}
|
|
||||||
|
|
||||||
const varstart = 8 * octet
|
const varstart = 8 * octet
|
||||||
finfo.Fields = append(finfo.Fields, baseARPFields[:]...)
|
|
||||||
_, hlen := afrm.Hardware()
|
_, hlen := afrm.Hardware()
|
||||||
_, plen := afrm.Protocol()
|
_, plen := afrm.Protocol()
|
||||||
finfo.Fields = append(finfo.Fields,
|
finfo.Fields = append(finfo.Fields,
|
||||||
@@ -136,7 +150,6 @@ func (pc *PacketBreakdown) CaptureARP(dst []Frame, pkt []byte, bitOffset int) ([
|
|||||||
BitLength: int(plen) * octet,
|
BitLength: int(plen) * octet,
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
dst = append(dst, finfo)
|
|
||||||
return dst, nil
|
return dst, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -152,12 +165,7 @@ func (pc *PacketBreakdown) CaptureIPv6(dst []Frame, pkt []byte, bitOffset int) (
|
|||||||
if pc.validator().HasError() {
|
if pc.validator().HasError() {
|
||||||
return dst, pc.validator().ErrPop()
|
return dst, pc.validator().ErrPop()
|
||||||
}
|
}
|
||||||
finfo := Frame{
|
reclaimFrame(&dst, ethernet.TypeIPv6, bitOffset, baseIPv6Fields[:])
|
||||||
Protocol: ethernet.TypeIPv6,
|
|
||||||
PacketBitOffset: bitOffset,
|
|
||||||
}
|
|
||||||
finfo.Fields = append(finfo.Fields, baseIPv6Fields[:]...)
|
|
||||||
dst = append(dst, finfo)
|
|
||||||
proto := ifrm6.NextHeader()
|
proto := ifrm6.NextHeader()
|
||||||
end := bitOffset + 40*octet
|
end := bitOffset + 40*octet
|
||||||
var protoErrs []error
|
var protoErrs []error
|
||||||
@@ -201,11 +209,7 @@ func (pc *PacketBreakdown) CaptureIPv4(dst []Frame, pkt []byte, bitOffset int) (
|
|||||||
}
|
}
|
||||||
// limit packet to the actual IPv4 frame size
|
// limit packet to the actual IPv4 frame size
|
||||||
pkt = pkt[:bitOffset/8+int(ifrm4.TotalLength())]
|
pkt = pkt[:bitOffset/8+int(ifrm4.TotalLength())]
|
||||||
finfo := Frame{
|
finfo := reclaimFrame(&dst, ethernet.TypeIPv4, bitOffset, baseIPv4Fields[:])
|
||||||
Protocol: ethernet.TypeIPv4,
|
|
||||||
PacketBitOffset: bitOffset,
|
|
||||||
}
|
|
||||||
finfo.Fields = append(finfo.Fields, baseIPv4Fields[:]...)
|
|
||||||
options := ifrm4.Options()
|
options := ifrm4.Options()
|
||||||
if len(options) > 0 {
|
if len(options) > 0 {
|
||||||
finfo.Fields = append(finfo.Fields, FrameField{
|
finfo.Fields = append(finfo.Fields, FrameField{
|
||||||
@@ -217,7 +221,6 @@ func (pc *PacketBreakdown) CaptureIPv4(dst []Frame, pkt []byte, bitOffset int) (
|
|||||||
if ifrm4.CalculateHeaderCRC() != 0 {
|
if ifrm4.CalculateHeaderCRC() != 0 {
|
||||||
finfo.Errors = append(finfo.Errors, lneto.ErrBadCRC)
|
finfo.Errors = append(finfo.Errors, lneto.ErrBadCRC)
|
||||||
}
|
}
|
||||||
dst = append(dst, finfo)
|
|
||||||
proto := ifrm4.Protocol()
|
proto := ifrm4.Protocol()
|
||||||
end := bitOffset + octet*ifrm4.HeaderLength()
|
end := bitOffset + octet*ifrm4.HeaderLength()
|
||||||
var protoErrs []error
|
var protoErrs []error
|
||||||
@@ -277,7 +280,7 @@ func (pc *PacketBreakdown) captureIPProto(proto lneto.IPProto, dst []Frame, pkt
|
|||||||
case lneto.IPProtoICMP:
|
case lneto.IPProtoICMP:
|
||||||
dst, err = pc.CaptureICMPv4(dst, pkt, bitOffset)
|
dst, err = pc.CaptureICMPv4(dst, pkt, bitOffset)
|
||||||
default:
|
default:
|
||||||
dst = append(dst, remainingFrameInfo(proto, 0, bitOffset, octet*len(pkt)))
|
reclaimRemainingFrame(&dst, proto, 0, bitOffset, octet*len(pkt))
|
||||||
}
|
}
|
||||||
if len(ipProtoErrs) > 0 && len(dst) > nextFrame {
|
if len(ipProtoErrs) > 0 && len(dst) > nextFrame {
|
||||||
dst[nextFrame].Errors = append(dst[nextFrame].Errors, ipProtoErrs...)
|
dst[nextFrame].Errors = append(dst[nextFrame].Errors, ipProtoErrs...)
|
||||||
@@ -298,11 +301,7 @@ func (pc *PacketBreakdown) CaptureTCP(dst []Frame, pkt []byte, bitOffset int) ([
|
|||||||
return dst, pc.validator().ErrPop()
|
return dst, pc.validator().ErrPop()
|
||||||
}
|
}
|
||||||
end := bitOffset + octet*tfrm.HeaderLength()
|
end := bitOffset + octet*tfrm.HeaderLength()
|
||||||
finfo := Frame{
|
finfo := reclaimFrame(&dst, lneto.IPProtoTCP, bitOffset, baseTCPFields[:])
|
||||||
Protocol: lneto.IPProtoTCP,
|
|
||||||
PacketBitOffset: bitOffset,
|
|
||||||
}
|
|
||||||
finfo.Fields = append(finfo.Fields, baseTCPFields[:]...)
|
|
||||||
options := tfrm.Options()
|
options := tfrm.Options()
|
||||||
if len(options) > 0 {
|
if len(options) > 0 {
|
||||||
finfo.Fields = append(finfo.Fields, FrameField{
|
finfo.Fields = append(finfo.Fields, FrameField{
|
||||||
@@ -311,20 +310,16 @@ func (pc *PacketBreakdown) CaptureTCP(dst []Frame, pkt []byte, bitOffset int) ([
|
|||||||
BitLength: octet * len(options),
|
BitLength: octet * len(options),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
dst = append(dst, finfo)
|
|
||||||
payload := tfrm.Payload()
|
payload := tfrm.Payload()
|
||||||
if len(payload) > 0 {
|
if len(payload) > 0 {
|
||||||
dst, err = pc.CaptureHTTP(dst, pkt, end)
|
dst, err = pc.CaptureHTTP(dst, pkt, end)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
dst = append(dst, remainingFrameInfo(unknownPayloadProto, FieldClassPayload, end, octet*len(pkt)))
|
reclaimRemainingFrame(&dst, unknownPayloadProto, FieldClassPayload, end, octet*len(pkt))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return dst, nil
|
return dst, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// func (pc *PacketBreakdown) CaptureDHCPv4(dst []Frame, pkt []byte, bitOffset int) ([]Frame, error) {
|
|
||||||
// }
|
|
||||||
|
|
||||||
func (pc *PacketBreakdown) CaptureUDP(dst []Frame, pkt []byte, bitOffset int) ([]Frame, error) {
|
func (pc *PacketBreakdown) CaptureUDP(dst []Frame, pkt []byte, bitOffset int) ([]Frame, error) {
|
||||||
if bitOffset%8 != 0 {
|
if bitOffset%8 != 0 {
|
||||||
return dst, errors.New("UDP must be parsed at byte boundary")
|
return dst, errors.New("UDP must be parsed at byte boundary")
|
||||||
@@ -337,12 +332,7 @@ func (pc *PacketBreakdown) CaptureUDP(dst []Frame, pkt []byte, bitOffset int) ([
|
|||||||
if pc.validator().HasError() {
|
if pc.validator().HasError() {
|
||||||
return dst, pc.validator().ErrPop()
|
return dst, pc.validator().ErrPop()
|
||||||
}
|
}
|
||||||
finfo := Frame{
|
reclaimFrame(&dst, lneto.IPProtoUDP, bitOffset, baseUDPFields[:])
|
||||||
Protocol: lneto.IPProtoUDP,
|
|
||||||
PacketBitOffset: bitOffset,
|
|
||||||
}
|
|
||||||
finfo.Fields = append(finfo.Fields, baseUDPFields[:]...)
|
|
||||||
dst = append(dst, finfo)
|
|
||||||
end := bitOffset + 8*octet
|
end := bitOffset + 8*octet
|
||||||
payload := ufrm.Payload()
|
payload := ufrm.Payload()
|
||||||
dstport := ufrm.DestinationPort()
|
dstport := ufrm.DestinationPort()
|
||||||
@@ -355,7 +345,7 @@ func (pc *PacketBreakdown) CaptureUDP(dst []Frame, pkt []byte, bitOffset int) ([
|
|||||||
dst, err = pc.CaptureNTP(dst, pkt, end)
|
dst, err = pc.CaptureNTP(dst, pkt, end)
|
||||||
}
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
dst = append(dst, remainingFrameInfo(unknownPayloadProto, FieldClassPayload, end, octet*len(pkt)))
|
reclaimRemainingFrame(&dst, unknownPayloadProto, FieldClassPayload, end, octet*len(pkt))
|
||||||
}
|
}
|
||||||
return dst, nil
|
return dst, nil
|
||||||
}
|
}
|
||||||
@@ -370,11 +360,7 @@ func (pc *PacketBreakdown) CaptureICMPv4(dst []Frame, pkt []byte, bitOffset int)
|
|||||||
return dst, err
|
return dst, err
|
||||||
}
|
}
|
||||||
|
|
||||||
finfo := Frame{
|
finfo := reclaimFrame(&dst, lneto.IPProtoICMP, bitOffset, baseICMPv4Fields[:])
|
||||||
Protocol: lneto.IPProtoICMP,
|
|
||||||
PacketBitOffset: bitOffset,
|
|
||||||
}
|
|
||||||
finfo.Fields = append(finfo.Fields, baseICMPv4Fields[:]...)
|
|
||||||
|
|
||||||
// Add type-specific fields.
|
// Add type-specific fields.
|
||||||
switch ifrm.Type() {
|
switch ifrm.Type() {
|
||||||
@@ -421,13 +407,12 @@ func (pc *PacketBreakdown) CaptureICMPv4(dst []Frame, pkt []byte, bitOffset int)
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
dst = append(dst, finfo)
|
|
||||||
return dst, nil
|
return dst, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (pc *PacketBreakdown) CaptureDNS(dst []Frame, pkt []byte, bitOffset int) ([]Frame, error) {
|
func (pc *PacketBreakdown) CaptureDNS(dst []Frame, pkt []byte, bitOffset int) ([]Frame, error) {
|
||||||
if bitOffset%8 != 0 {
|
if bitOffset%8 != 0 {
|
||||||
return nil, errors.New("DNS must be parsed at byte boundary")
|
return dst, errors.New("DNS must be parsed at byte boundary")
|
||||||
}
|
}
|
||||||
dnsData := pkt[bitOffset/8:]
|
dnsData := pkt[bitOffset/8:]
|
||||||
pc.dmsg.LimitResourceDecoding(20, 20, 20, 20)
|
pc.dmsg.LimitResourceDecoding(20, 20, 20, 20)
|
||||||
@@ -435,10 +420,7 @@ func (pc *PacketBreakdown) CaptureDNS(dst []Frame, pkt []byte, bitOffset int) ([
|
|||||||
if err != nil && !incomplete {
|
if err != nil && !incomplete {
|
||||||
return dst, err
|
return dst, err
|
||||||
}
|
}
|
||||||
finfo := Frame{
|
finfo := reclaimFrame(&dst, "DNS", bitOffset, nil)
|
||||||
Protocol: "DNS",
|
|
||||||
PacketBitOffset: bitOffset,
|
|
||||||
}
|
|
||||||
if incomplete {
|
if incomplete {
|
||||||
finfo.Errors = append(finfo.Errors, errors.New("pcap: could not parse all DNS resources; add higher limit"))
|
finfo.Errors = append(finfo.Errors, errors.New("pcap: could not parse all DNS resources; add higher limit"))
|
||||||
}
|
}
|
||||||
@@ -447,52 +429,46 @@ func (pc *PacketBreakdown) CaptureDNS(dst []Frame, pkt []byte, bitOffset int) ([
|
|||||||
FrameBitOffset: 0,
|
FrameBitOffset: 0,
|
||||||
BitLength: int(off) * octet,
|
BitLength: int(off) * octet,
|
||||||
})
|
})
|
||||||
dst = append(dst, finfo)
|
|
||||||
return dst, nil
|
return dst, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (pc *PacketBreakdown) CaptureNTP(dst []Frame, pkt []byte, bitOffset int) ([]Frame, error) {
|
func (pc *PacketBreakdown) CaptureNTP(dst []Frame, pkt []byte, bitOffset int) ([]Frame, error) {
|
||||||
if bitOffset%8 != 0 {
|
if bitOffset%8 != 0 {
|
||||||
return nil, errors.New("NTP must be parsed at byte boundary")
|
return dst, errors.New("NTP must be parsed at byte boundary")
|
||||||
}
|
}
|
||||||
ntpData := pkt[bitOffset/8:]
|
ntpData := pkt[bitOffset/8:]
|
||||||
_, err := ntp.NewFrame(ntpData)
|
_, err := ntp.NewFrame(ntpData)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return dst, err
|
return dst, err
|
||||||
}
|
}
|
||||||
finfo := Frame{
|
reclaimFrame(&dst, "NTP", bitOffset, baseNTPFields[:])
|
||||||
Protocol: "NTP",
|
|
||||||
PacketBitOffset: bitOffset,
|
|
||||||
}
|
|
||||||
finfo.Fields = append(finfo.Fields, baseNTPFields[:]...)
|
|
||||||
dst = append(dst, finfo)
|
|
||||||
return dst, nil
|
return dst, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (pc *PacketBreakdown) CaptureDHCPv4(dst []Frame, pkt []byte, bitOffset int) ([]Frame, error) {
|
func (pc *PacketBreakdown) CaptureDHCPv4(dst []Frame, pkt []byte, bitOffset int) ([]Frame, error) {
|
||||||
if bitOffset%8 != 0 {
|
if bitOffset%8 != 0 {
|
||||||
return nil, errors.New("DHCP must be parsed at byte boundary")
|
return dst, errors.New("DHCP must be parsed at byte boundary")
|
||||||
}
|
}
|
||||||
dhcpData := pkt[bitOffset/8:]
|
dhcpData := pkt[bitOffset/8:]
|
||||||
dfrm, err := dhcpv4.NewFrame(dhcpData)
|
dfrm, err := dhcpv4.NewFrame(dhcpData)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return dst, err
|
||||||
}
|
|
||||||
finfo := Frame{
|
|
||||||
Protocol: "DHCPv4",
|
|
||||||
PacketBitOffset: bitOffset,
|
|
||||||
}
|
}
|
||||||
|
finfo := reclaimFrame(&dst, "DHCPv4", bitOffset, baseDHCPv4Fields[:])
|
||||||
magic := dfrm.MagicCookie()
|
magic := dfrm.MagicCookie()
|
||||||
if magic != dhcpv4.MagicCookie {
|
if magic != dhcpv4.MagicCookie {
|
||||||
finfo.Errors = append(finfo.Errors, errors.New("incorrect DHCPv4 magic cookie"))
|
finfo.Errors = append(finfo.Errors, errors.New("incorrect DHCPv4 magic cookie"))
|
||||||
}
|
}
|
||||||
finfo.Fields = append(finfo.Fields, baseDHCPv4Fields[:]...)
|
|
||||||
options := dfrm.OptionsPayload()
|
options := dfrm.OptionsPayload()
|
||||||
|
|
||||||
if len(options) > 0 && pc.SubfieldLimit > 0 {
|
if len(options) > 0 && pc.SubfieldLimit > 0 {
|
||||||
var optfield FrameField
|
// Reclaim FrameField from Fields backing array to reuse its SubFields backing array.
|
||||||
optfield.Class = FieldClassOptions
|
optfield := internal.SliceReclaim(&finfo.Fields)
|
||||||
optfield.Name = "options"
|
*optfield = FrameField{ // Ensure consistent zeroing of memory, but keep slice reference for reuse.
|
||||||
|
Class: FieldClassOptions,
|
||||||
|
SubFields: optfield.SubFields[:0],
|
||||||
|
Name: "options",
|
||||||
|
}
|
||||||
err = dfrm.ForEachOption(func(optoff int, opt dhcpv4.OptNum, data []byte) error {
|
err = dfrm.ForEachOption(func(optoff int, opt dhcpv4.OptNum, data []byte) error {
|
||||||
if len(optfield.SubFields) >= pc.SubfieldLimit {
|
if len(optfield.SubFields) >= pc.SubfieldLimit {
|
||||||
return errors.New("option cap limit surpassed for DHCP")
|
return errors.New("option cap limit surpassed for DHCP")
|
||||||
@@ -544,12 +520,11 @@ func (pc *PacketBreakdown) CaptureDHCPv4(dst []Frame, pkt []byte, bitOffset int)
|
|||||||
optfield.SubFields = append(optfield.SubFields, field)
|
optfield.SubFields = append(optfield.SubFields, field)
|
||||||
return nil
|
return nil
|
||||||
})
|
})
|
||||||
finfo.Fields = append(finfo.Fields, optfield)
|
// optfield already in finfo.Fields via SliceReclaim, no append needed.
|
||||||
if err != nil {
|
if err != nil {
|
||||||
finfo.Errors = append(finfo.Errors, err)
|
finfo.Errors = append(finfo.Errors, err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
dst = append(dst, finfo)
|
|
||||||
return dst, nil
|
return dst, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -589,7 +564,7 @@ func httpBodyClass(contentType, body []byte) FieldClass {
|
|||||||
func (pc *PacketBreakdown) CaptureHTTP(dst []Frame, pkt []byte, bitOffset int) ([]Frame, error) {
|
func (pc *PacketBreakdown) CaptureHTTP(dst []Frame, pkt []byte, bitOffset int) ([]Frame, error) {
|
||||||
const httpProtocol = "HTTP"
|
const httpProtocol = "HTTP"
|
||||||
if bitOffset%8 != 0 {
|
if bitOffset%8 != 0 {
|
||||||
return nil, errors.New("HTTP must be parsed at byte boundary")
|
return dst, errors.New("HTTP must be parsed at byte boundary")
|
||||||
}
|
}
|
||||||
const asResponse = true
|
const asResponse = true
|
||||||
const asRequest = false
|
const asRequest = false
|
||||||
@@ -606,25 +581,22 @@ func (pc *PacketBreakdown) CaptureHTTP(dst []Frame, pkt []byte, bitOffset int) (
|
|||||||
hdrLen := pc.hdr.BufferParsed()
|
hdrLen := pc.hdr.BufferParsed()
|
||||||
body, _ := pc.hdr.Body()
|
body, _ := pc.hdr.Body()
|
||||||
bodyClass := httpBodyClass(pc.hdr.Get("Content-Type"), body)
|
bodyClass := httpBodyClass(pc.hdr.Get("Content-Type"), body)
|
||||||
dst = append(dst, Frame{
|
finfo := reclaimFrame(&dst, httpProtocol, bitOffset, nil)
|
||||||
Protocol: httpProtocol,
|
finfo.Fields = append(finfo.Fields,
|
||||||
PacketBitOffset: bitOffset,
|
FrameField{
|
||||||
Fields: []FrameField{
|
Name: "HTTP Header",
|
||||||
{
|
Class: FieldClassText,
|
||||||
Name: "HTTP Header",
|
FrameBitOffset: 0,
|
||||||
Class: FieldClassText,
|
BitLength: hdrLen * octet,
|
||||||
FrameBitOffset: 0,
|
|
||||||
BitLength: hdrLen * octet,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Name: "HTTP Body",
|
|
||||||
Class: bodyClass,
|
|
||||||
FrameBitOffset: hdrLen * octet,
|
|
||||||
BitLength: len(body) * octet,
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
})
|
FrameField{
|
||||||
return dst, err
|
Name: "HTTP Body",
|
||||||
|
Class: bodyClass,
|
||||||
|
FrameBitOffset: hdrLen * octet,
|
||||||
|
BitLength: len(body) * octet,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
return dst, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (pc *PacketBreakdown) validator() *lneto.Validator {
|
func (pc *PacketBreakdown) validator() *lneto.Validator {
|
||||||
@@ -653,9 +625,9 @@ func (ff Flags) IsLegacy() bool { return ff&FlagLegacy != 0 }
|
|||||||
func (ff Flags) IsRightAligned() bool { return ff&FlagRightAligned != 0 }
|
func (ff Flags) IsRightAligned() bool { return ff&FlagRightAligned != 0 }
|
||||||
|
|
||||||
type Frame struct {
|
type Frame struct {
|
||||||
|
PacketBitOffset int
|
||||||
Protocol any
|
Protocol any
|
||||||
Fields []FrameField
|
Fields []FrameField
|
||||||
PacketBitOffset int
|
|
||||||
Errors []error
|
Errors []error
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1331,14 +1303,26 @@ var baseNTPFields = [...]FrameField{
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
func remainingFrameInfo(proto any, class FieldClass, pktBitOffset, pktBitLen int) Frame {
|
// reclaimFrame extends dst via [internal.SliceReclaim], resets the reclaimed Frame
|
||||||
return Frame{
|
// with the given protocol and bit offset while preserving Fields and Errors
|
||||||
|
// backing arrays, and appends baseFields. Returns the Frame for further modification.
|
||||||
|
func reclaimFrame(dst *[]Frame, proto any, bitOffset int, baseFields []FrameField) *Frame {
|
||||||
|
finfo := internal.SliceReclaim(dst)
|
||||||
|
*finfo = Frame{
|
||||||
|
PacketBitOffset: bitOffset,
|
||||||
Protocol: proto,
|
Protocol: proto,
|
||||||
PacketBitOffset: pktBitOffset,
|
Fields: append(finfo.Fields[:0], baseFields...),
|
||||||
Fields: []FrameField{
|
Errors: finfo.Errors[:0],
|
||||||
{
|
|
||||||
Class: class,
|
|
||||||
BitLength: pktBitLen - pktBitOffset,
|
|
||||||
}},
|
|
||||||
}
|
}
|
||||||
|
return finfo
|
||||||
|
}
|
||||||
|
|
||||||
|
// reclaimRemainingFrame extends dst via SliceReclaim and populates the reclaimed
|
||||||
|
// Frame as a single-field "remaining payload" frame, reusing the old Fields backing array.
|
||||||
|
func reclaimRemainingFrame(dst *[]Frame, proto any, class FieldClass, pktBitOffset, pktBitLen int) {
|
||||||
|
finfo := reclaimFrame(dst, proto, pktBitOffset, nil)
|
||||||
|
finfo.Fields = append(finfo.Fields, FrameField{
|
||||||
|
Class: class,
|
||||||
|
BitLength: pktBitLen - pktBitOffset,
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
+43
-5
@@ -5,6 +5,7 @@ import (
|
|||||||
"encoding/hex"
|
"encoding/hex"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"math"
|
||||||
"net/netip"
|
"net/netip"
|
||||||
"slices"
|
"slices"
|
||||||
"strconv"
|
"strconv"
|
||||||
@@ -31,6 +32,7 @@ type Formatter struct {
|
|||||||
DisableLegacyFilter bool
|
DisableLegacyFilter bool
|
||||||
mubuf sync.Mutex
|
mubuf sync.Mutex
|
||||||
buf []byte
|
buf []byte
|
||||||
|
uintBuf [8]byte // scratch buffer for fieldAsUint to avoid TinyGo heap escape.
|
||||||
}
|
}
|
||||||
|
|
||||||
// FormatFrames appends the formatted frame data to the destination buffer according the Formatter state.
|
// FormatFrames appends the formatted frame data to the destination buffer according the Formatter state.
|
||||||
@@ -49,14 +51,17 @@ func (f *Formatter) FormatFrames(dst []byte, frms []Frame, pkt []byte) (_ []byte
|
|||||||
return dst, nil
|
return dst, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// FormatFrame
|
// FormatFrame formats a single frame's protocol, fields, and errors into dst.
|
||||||
func (f *Formatter) FormatFrame(dst []byte, frm Frame, pkt []byte) (_ []byte, err error) {
|
func (f *Formatter) FormatFrame(dst []byte, frm Frame, pkt []byte) (_ []byte, err error) {
|
||||||
sep := f.fieldSep()
|
sep := f.fieldSep()
|
||||||
bitlen := frm.LenBits()
|
bitlen := frm.LenBits()
|
||||||
|
dst = appendProtocol(dst, frm.Protocol)
|
||||||
if bitlen%8 == 0 {
|
if bitlen%8 == 0 {
|
||||||
dst = fmt.Appendf(dst, "%s len=%d", frm.Protocol, bitlen/8)
|
dst = append(dst, " len="...)
|
||||||
|
dst = strconv.AppendInt(dst, int64(bitlen/8), 10)
|
||||||
} else {
|
} else {
|
||||||
dst = fmt.Appendf(dst, "%s bitlen=%d", frm.Protocol, bitlen)
|
dst = append(dst, " bitlen="...)
|
||||||
|
dst = strconv.AppendInt(dst, int64(bitlen), 10)
|
||||||
}
|
}
|
||||||
|
|
||||||
for ifield := range frm.Fields {
|
for ifield := range frm.Fields {
|
||||||
@@ -68,7 +73,7 @@ func (f *Formatter) FormatFrame(dst []byte, frm Frame, pkt []byte) (_ []byte, er
|
|||||||
if field.Class == FieldClassFlags && frm.Protocol == lneto.IPProtoTCP {
|
if field.Class == FieldClassFlags && frm.Protocol == lneto.IPProtoTCP {
|
||||||
// TCP flags pretty print special case.
|
// TCP flags pretty print special case.
|
||||||
dst = append(dst, "flags="...)
|
dst = append(dst, "flags="...)
|
||||||
v, err := fieldAsUint(pkt, frm.PacketBitOffset+field.FrameBitOffset, field.BitLength, field.Flags.IsRightAligned())
|
v, err := f.fieldAsUint(pkt, frm.PacketBitOffset+field.FrameBitOffset, field.BitLength, field.Flags.IsRightAligned())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return dst, err
|
return dst, err
|
||||||
}
|
}
|
||||||
@@ -158,7 +163,7 @@ func (f *Formatter) formatField(dst []byte, pktStartOff int, field FrameField, p
|
|||||||
case FieldClassDst, FieldClassSrc, FieldClassSize, FieldClassAddress, FieldClassOperation:
|
case FieldClassDst, FieldClassSrc, FieldClassSize, FieldClassAddress, FieldClassOperation:
|
||||||
// IP, MAC addresses and ports.
|
// IP, MAC addresses and ports.
|
||||||
if field.BitLength <= 16 {
|
if field.BitLength <= 16 {
|
||||||
v, err := fieldAsUint(pkt, fieldBitStart, field.BitLength, field.Flags.IsRightAligned())
|
v, err := f.fieldAsUint(pkt, fieldBitStart, field.BitLength, field.Flags.IsRightAligned())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return dst, err
|
return dst, err
|
||||||
}
|
}
|
||||||
@@ -198,3 +203,36 @@ func (f *Formatter) subfieldSep() string {
|
|||||||
}
|
}
|
||||||
return sep
|
return sep
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// fieldAsUint evaluates a packet field as a uint64 using the Formatter's
|
||||||
|
// scratch buffer to avoid a TinyGo heap escape from a local [8]byte.
|
||||||
|
func (f *Formatter) fieldAsUint(pkt []byte, fieldBitStart, bitlen int, rightAligned bool) (uint64, error) {
|
||||||
|
const badUint64 = math.MaxUint64
|
||||||
|
octets := (bitlen + 7) / 8
|
||||||
|
if octets > 8 {
|
||||||
|
return badUint64, errors.New("field too long to be represented by uint64")
|
||||||
|
}
|
||||||
|
f.uintBuf = [8]byte{}
|
||||||
|
_, err := appendField(f.uintBuf[8-octets:8-octets], pkt, fieldBitStart, bitlen, rightAligned)
|
||||||
|
if err != nil {
|
||||||
|
return badUint64, err
|
||||||
|
}
|
||||||
|
return binary.BigEndian.Uint64(f.uintBuf[:]), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// appendProtocol appends the string representation of a Frame.Protocol value
|
||||||
|
// to dst without going through fmt.Appendf reflect machinery.
|
||||||
|
func appendProtocol(dst []byte, p any) []byte {
|
||||||
|
switch p := p.(type) {
|
||||||
|
case proto:
|
||||||
|
return append(dst, string(p)...)
|
||||||
|
case string:
|
||||||
|
return append(dst, p...)
|
||||||
|
case ethernet.Type:
|
||||||
|
return append(dst, p.String()...)
|
||||||
|
case lneto.IPProto:
|
||||||
|
return append(dst, p.String()...)
|
||||||
|
default:
|
||||||
|
return fmt.Appendf(dst, "%v", p)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -6,10 +6,10 @@ import (
|
|||||||
"io"
|
"io"
|
||||||
"log/slog"
|
"log/slog"
|
||||||
"math"
|
"math"
|
||||||
"net"
|
|
||||||
|
|
||||||
"github.com/soypat/lneto"
|
"github.com/soypat/lneto"
|
||||||
"github.com/soypat/lneto/ethernet"
|
"github.com/soypat/lneto/ethernet"
|
||||||
|
"github.com/soypat/lneto/internal"
|
||||||
)
|
)
|
||||||
|
|
||||||
// StackEthernetConfig contains configuration parameters for [StackEthernet].
|
// StackEthernetConfig contains configuration parameters for [StackEthernet].
|
||||||
@@ -132,7 +132,7 @@ func (ls *StackEthernet) Demux(carrierData []byte, frameOffset int) (err error)
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
DROP:
|
DROP:
|
||||||
ls.handlers.info("LinkStack:drop-packet", slog.String("dsthw", net.HardwareAddr(dstaddr[:]).String()), slog.String("ethertype", efrm.EtherTypeOrSize().String()))
|
ls.handlers.info("LinkStack:drop-packet", internal.SlogAddr6("dsthw", dstaddr), slog.String("ethertype", efrm.EtherTypeOrSize().String()))
|
||||||
return lneto.ErrPacketDrop
|
return lneto.ErrPacketDrop
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -104,7 +104,7 @@ func (sb *StackIP) Demux(carrierData []byte, offset int) error {
|
|||||||
// nodeIdx := getNodeByProto(sb.handlers, uint16(proto))
|
// nodeIdx := getNodeByProto(sb.handlers, uint16(proto))
|
||||||
if node == nil {
|
if node == nil {
|
||||||
// Drop packet.
|
// Drop packet.
|
||||||
sb.handlers.info("ip:demux.drop", slog.String("dstaddr", netip.AddrFrom4(*ifrm.DestinationAddr()).String()), slog.String("proto", ifrm.Protocol().String()))
|
sb.handlers.info("ip:demux.drop", internal.SlogAddr4("dstaddr", ifrm.DestinationAddr()), slog.String("proto", ifrm.Protocol().String()))
|
||||||
return lneto.ErrPacketDrop
|
return lneto.ErrPacketDrop
|
||||||
}
|
}
|
||||||
// Incoming CRC Validation of common IP Protocols.
|
// Incoming CRC Validation of common IP Protocols.
|
||||||
|
|||||||
+7
-6
@@ -194,15 +194,16 @@ func _() {
|
|||||||
// An "invalid array index" compiler error signifies that the constant values have changed.
|
// An "invalid array index" compiler error signifies that the constant values have changed.
|
||||||
// Re-run the stringer command to generate them again.
|
// Re-run the stringer command to generate them again.
|
||||||
var x [1]struct{}
|
var x [1]struct{}
|
||||||
_ = x[ErrPacketDrop-1]
|
_ = x[ErrBug-1]
|
||||||
_ = x[ErrBadCRC-2]
|
_ = x[ErrPacketDrop-2]
|
||||||
_ = x[ErrZeroSource-3]
|
_ = x[ErrBadCRC-3]
|
||||||
_ = x[ErrZeroDestination-4]
|
_ = x[ErrZeroSource-4]
|
||||||
|
_ = x[ErrZeroDestination-5]
|
||||||
}
|
}
|
||||||
|
|
||||||
const _errGeneric_name = "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)"
|
||||||
|
|
||||||
var _errGeneric_index = [...]uint8{0, 14, 32, 54, 81}
|
var _errGeneric_index = [...]uint8{0, 39, 53, 71, 93, 120}
|
||||||
|
|
||||||
func (i errGeneric) String() string {
|
func (i errGeneric) String() string {
|
||||||
i -= 1
|
i -= 1
|
||||||
|
|||||||
@@ -48,6 +48,8 @@ type StackAsync struct {
|
|||||||
|
|
||||||
prng uint32
|
prng uint32
|
||||||
|
|
||||||
|
addrBuf [6]byte // Temporary buffer for As4()/HardwareAddr6() results to avoid heap escapes.
|
||||||
|
|
||||||
totalsent uint64
|
totalsent uint64
|
||||||
totalrecv uint64
|
totalrecv uint64
|
||||||
}
|
}
|
||||||
@@ -353,8 +355,8 @@ func (s *StackAsync) StartLookupIP(host string) error {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
dns4 := s.dnssv.As4()
|
*(*[4]byte)(s.addrBuf[:4]) = s.dnssv.As4()
|
||||||
s.dnsUDP.SetStackNode(&s.dns, dns4[:], dns.ServerPort)
|
s.dnsUDP.SetStackNode(&s.dns, s.addrBuf[:4], dns.ServerPort)
|
||||||
err = s.udps.Register(&s.dnsUDP)
|
err = s.udps.Register(&s.dnsUDP)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -417,8 +419,8 @@ func (s *StackAsync) StartNTP(addr netip.Addr) error {
|
|||||||
defer s.mu.Unlock()
|
defer s.mu.Unlock()
|
||||||
s.ntp.Reset(s.sysprec, time.Now)
|
s.ntp.Reset(s.sysprec, time.Now)
|
||||||
|
|
||||||
addr4 := addr.As4()
|
*(*[4]byte)(s.addrBuf[:4]) = addr.As4()
|
||||||
s.ntpUDP.SetStackNode(&s.ntp, addr4[:], ntp.ServerPort)
|
s.ntpUDP.SetStackNode(&s.ntp, s.addrBuf[:4], ntp.ServerPort)
|
||||||
err := s.udps.Register(&s.ntpUDP)
|
err := s.udps.Register(&s.ntpUDP)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user