From 243d1a720d7d59f78888512d147bbab425f1bd2c Mon Sep 17 00:00:00 2001 From: soypat Date: Wed, 31 Dec 2025 17:08:53 -0300 Subject: [PATCH] fix httptap incorrectly capturing GRO frames --- ethernet/frame.go | 2 +- examples/tap/main.go | 38 +++++++------------------------------- examples/xnet/main.go | 13 ++++++++----- internal/ltesto/httptap.go | 7 +++---- 4 files changed, 19 insertions(+), 41 deletions(-) diff --git a/ethernet/frame.go b/ethernet/frame.go index 062be85..22e6bb0 100644 --- a/ethernet/frame.go +++ b/ethernet/frame.go @@ -44,7 +44,7 @@ func (efrm Frame) Payload() []byte { hl := efrm.HeaderLength() et := efrm.EtherTypeOrSize() if et.IsSize() { - return efrm.buf[hl:et] + return efrm.buf[hl : hl+int(et)] } return efrm.buf[hl:] } diff --git a/examples/tap/main.go b/examples/tap/main.go index c3e780a..5c38af2 100644 --- a/examples/tap/main.go +++ b/examples/tap/main.go @@ -5,17 +5,16 @@ import ( "flag" "fmt" "log" + "math" "net" "net/http" "net/netip" "strings" "time" - "github.com/soypat/lneto" "github.com/soypat/lneto/internal" "github.com/soypat/lneto/internal/ltesto" "github.com/soypat/lneto/internet/pcap" - "github.com/soypat/lneto/tcp" ) func main() { @@ -30,9 +29,14 @@ func main() { func run() error { var ( flagInterface = "tap0" + flagMinMTU = math.MaxUint16 ) flag.StringVar(&flagInterface, "i", flagInterface, "Interface to select. tap* creates a tap interface. Any other name will create a bridge to the name of the interface i.e: 'enp7s0', 'wlp8s0', 'lo'") + flag.IntVar(&flagMinMTU, "mtu", flagMinMTU, "Set the interface minimum MTU to use for buffer.") flag.Parse() + if flagMinMTU < 1500 { + return errors.New("minimum MTU too small") + } var ( flagNet = "192.168.10.1/24" flagiface = "tap0" @@ -57,7 +61,7 @@ func run() error { iface = br } - sv, err := ltesto.NewHTTPTapServer(iface, flagPacketQueueSize, flagPacketQueueSize) + sv, err := ltesto.NewHTTPTapServer(iface, flagMinMTU, flagPacketQueueSize, flagPacketQueueSize) if err != nil { return err } @@ -91,31 +95,3 @@ func run() error { http.ListenAndServe(":7070", sv) return errors.New("finished") } - -func getTCPData(frames []pcap.Frame, pkt []byte) (flags tcp.Flags, src, dst uint16) { - for i := range frames { - proto := frames[i].Protocol - if proto == lneto.IPProtoTCP { - return tcp.Flags(getFrameClassUint(frames[i], pkt, pcap.FieldClassFlags)), - uint16(getFrameClassUint(frames[i], pkt, pcap.FieldClassSrc)), - uint16(getFrameClassUint(frames[i], pkt, pcap.FieldClassDst)) - } else if proto == lneto.IPProtoUDP { - return 0, - uint16(getFrameClassUint(frames[i], pkt, pcap.FieldClassSrc)), - uint16(getFrameClassUint(frames[i], pkt, pcap.FieldClassDst)) - } - } - return 0, 0, 0 -} - -func getFrameClassUint(frame pcap.Frame, pkt []byte, class pcap.FieldClass) uint64 { - iflags, err := frame.FieldByClass(class) - if err != nil { - return 0 - } - v, err := frame.FieldAsUint(iflags, pkt) - if err != nil { - return 0 - } - return v -} diff --git a/examples/xnet/main.go b/examples/xnet/main.go index 21e8a55..6e68f19 100644 --- a/examples/xnet/main.go +++ b/examples/xnet/main.go @@ -1,11 +1,13 @@ package main import ( + "encoding/hex" "errors" "flag" "fmt" "log" "log/slog" + "math" "net" "net/netip" "os" @@ -107,7 +109,7 @@ func run() (err error) { // Loop goroutine. go func() { lastAction := time.Now() - buf := make([]byte, mtu) + buf := make([]byte, math.MaxUint16) // Generic-receive Offload (GRO) can aggregate packets. var cap pcap.PacketBreakdown var frames []pcap.Frame pf := pcap.Formatter{ @@ -120,9 +122,11 @@ func run() (err error) { } frames, err = cap.CaptureEthernet(frames[:0], pkt, 0) if err != nil { + pkt := hex.EncodeToString(pkt) + slog.Error(err.Error(), slog.Any("pkt", pkt)) return err } - pfbuf = append(pfbuf[:0], context...) + pfbuf = fmt.Appendf(pfbuf[:0], "%-3s %3d", context, len(pkt)) pfbuf = append(pfbuf, ' ', '[') pfbuf, err = pf.FormatFrames(pfbuf, frames, pkt) pfbuf = append(pfbuf, ']', '\n') @@ -133,7 +137,6 @@ func run() (err error) { return err } for { - clear(buf) nwrite, err := stack.Encapsulate(buf[:], -1, 0) if err != nil { log.Println("ERR:ENCAPSULATE", err) @@ -150,7 +153,7 @@ func run() (err error) { } } - clear(buf) + clear(buf[:nwrite]) nread, err := iface.Read(buf) if err != nil { log.Fatal("groutine read:", err) @@ -165,7 +168,7 @@ func run() (err error) { } } } - + clear(buf[:nread]) if nread == 0 && nwrite == 0 && time.Since(lastAction) > 4*time.Second { time.Sleep(5 * time.Millisecond) } else { diff --git a/internal/ltesto/httptap.go b/internal/ltesto/httptap.go index eaaf022..f016390 100644 --- a/internal/ltesto/httptap.go +++ b/internal/ltesto/httptap.go @@ -183,16 +183,15 @@ func (sv *HTTPTapServer) OnTransfer(cb func(channel int, pkt []byte)) { sv.onTx = cb } -func NewHTTPTapServer(iface Interface, queueOut, queueIn int) (*HTTPTapServer, error) { +func NewHTTPTapServer(iface Interface, minMTU, queueOut, queueIn int) (*HTTPTapServer, error) { if iface == nil { return nil, errors.New("nil interface argument to HTTP interface server") } mtu, err := iface.MTU() if err != nil { return nil, err - } else if mtu < minMTU { - return nil, errors.New("too small MTU") } + bufferSize := max(mtu, minMTU) netmask, err := iface.IPMask() if err != nil { return nil, err @@ -202,7 +201,7 @@ func NewHTTPTapServer(iface Interface, queueOut, queueIn int) (*HTTPTapServer, e taps := &HTTPTapServer{ router: sv, tap: iface, - buf: make([]byte, mtu), + buf: make([]byte, bufferSize), } sv.HandleFunc("/send", func(w http.ResponseWriter, r *http.Request) { retries := 10