fix httptap incorrectly capturing GRO frames

This commit is contained in:
soypat
2025-12-31 17:08:53 -03:00
parent 7369ec0b7e
commit 243d1a720d
4 changed files with 19 additions and 41 deletions
+1 -1
View File
@@ -44,7 +44,7 @@ func (efrm Frame) Payload() []byte {
hl := efrm.HeaderLength() hl := efrm.HeaderLength()
et := efrm.EtherTypeOrSize() et := efrm.EtherTypeOrSize()
if et.IsSize() { if et.IsSize() {
return efrm.buf[hl:et] return efrm.buf[hl : hl+int(et)]
} }
return efrm.buf[hl:] return efrm.buf[hl:]
} }
+7 -31
View File
@@ -5,17 +5,16 @@ import (
"flag" "flag"
"fmt" "fmt"
"log" "log"
"math"
"net" "net"
"net/http" "net/http"
"net/netip" "net/netip"
"strings" "strings"
"time" "time"
"github.com/soypat/lneto"
"github.com/soypat/lneto/internal" "github.com/soypat/lneto/internal"
"github.com/soypat/lneto/internal/ltesto" "github.com/soypat/lneto/internal/ltesto"
"github.com/soypat/lneto/internet/pcap" "github.com/soypat/lneto/internet/pcap"
"github.com/soypat/lneto/tcp"
) )
func main() { func main() {
@@ -30,9 +29,14 @@ func main() {
func run() error { func run() error {
var ( var (
flagInterface = "tap0" 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.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() flag.Parse()
if flagMinMTU < 1500 {
return errors.New("minimum MTU too small")
}
var ( var (
flagNet = "192.168.10.1/24" flagNet = "192.168.10.1/24"
flagiface = "tap0" flagiface = "tap0"
@@ -57,7 +61,7 @@ func run() error {
iface = br iface = br
} }
sv, err := ltesto.NewHTTPTapServer(iface, flagPacketQueueSize, flagPacketQueueSize) sv, err := ltesto.NewHTTPTapServer(iface, flagMinMTU, flagPacketQueueSize, flagPacketQueueSize)
if err != nil { if err != nil {
return err return err
} }
@@ -91,31 +95,3 @@ func run() error {
http.ListenAndServe(":7070", sv) http.ListenAndServe(":7070", sv)
return errors.New("finished") 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
}
+8 -5
View File
@@ -1,11 +1,13 @@
package main package main
import ( import (
"encoding/hex"
"errors" "errors"
"flag" "flag"
"fmt" "fmt"
"log" "log"
"log/slog" "log/slog"
"math"
"net" "net"
"net/netip" "net/netip"
"os" "os"
@@ -107,7 +109,7 @@ func run() (err error) {
// Loop goroutine. // Loop goroutine.
go func() { go func() {
lastAction := time.Now() lastAction := time.Now()
buf := make([]byte, mtu) buf := make([]byte, math.MaxUint16) // Generic-receive Offload (GRO) can aggregate packets.
var cap pcap.PacketBreakdown var cap pcap.PacketBreakdown
var frames []pcap.Frame var frames []pcap.Frame
pf := pcap.Formatter{ pf := pcap.Formatter{
@@ -120,9 +122,11 @@ func run() (err error) {
} }
frames, err = cap.CaptureEthernet(frames[:0], pkt, 0) frames, err = cap.CaptureEthernet(frames[:0], pkt, 0)
if err != nil { if err != nil {
pkt := hex.EncodeToString(pkt)
slog.Error(err.Error(), slog.Any("pkt", pkt))
return err return err
} }
pfbuf = append(pfbuf[:0], context...) pfbuf = fmt.Appendf(pfbuf[:0], "%-3s %3d", context, len(pkt))
pfbuf = append(pfbuf, ' ', '[') pfbuf = append(pfbuf, ' ', '[')
pfbuf, err = pf.FormatFrames(pfbuf, frames, pkt) pfbuf, err = pf.FormatFrames(pfbuf, frames, pkt)
pfbuf = append(pfbuf, ']', '\n') pfbuf = append(pfbuf, ']', '\n')
@@ -133,7 +137,6 @@ func run() (err error) {
return err return err
} }
for { for {
clear(buf)
nwrite, err := stack.Encapsulate(buf[:], -1, 0) nwrite, err := stack.Encapsulate(buf[:], -1, 0)
if err != nil { if err != nil {
log.Println("ERR:ENCAPSULATE", err) log.Println("ERR:ENCAPSULATE", err)
@@ -150,7 +153,7 @@ func run() (err error) {
} }
} }
clear(buf) clear(buf[:nwrite])
nread, err := iface.Read(buf) nread, err := iface.Read(buf)
if err != nil { if err != nil {
log.Fatal("groutine read:", err) 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 { if nread == 0 && nwrite == 0 && time.Since(lastAction) > 4*time.Second {
time.Sleep(5 * time.Millisecond) time.Sleep(5 * time.Millisecond)
} else { } else {
+3 -4
View File
@@ -183,16 +183,15 @@ func (sv *HTTPTapServer) OnTransfer(cb func(channel int, pkt []byte)) {
sv.onTx = cb 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 { if iface == nil {
return nil, errors.New("nil interface argument to HTTP interface server") return nil, errors.New("nil interface argument to HTTP interface server")
} }
mtu, err := iface.MTU() mtu, err := iface.MTU()
if err != nil { if err != nil {
return nil, err return nil, err
} else if mtu < minMTU {
return nil, errors.New("too small MTU")
} }
bufferSize := max(mtu, minMTU)
netmask, err := iface.IPMask() netmask, err := iface.IPMask()
if err != nil { if err != nil {
return nil, err return nil, err
@@ -202,7 +201,7 @@ func NewHTTPTapServer(iface Interface, queueOut, queueIn int) (*HTTPTapServer, e
taps := &HTTPTapServer{ taps := &HTTPTapServer{
router: sv, router: sv,
tap: iface, tap: iface,
buf: make([]byte, mtu), buf: make([]byte, bufferSize),
} }
sv.HandleFunc("/send", func(w http.ResponseWriter, r *http.Request) { sv.HandleFunc("/send", func(w http.ResponseWriter, r *http.Request) {
retries := 10 retries := 10