more effort into understanding why TAP interface is so slow on my linux machine to no avail

This commit is contained in:
soypat
2025-06-05 00:47:07 -03:00
parent bc14ed395d
commit 6ba727a0ef
12 changed files with 256 additions and 26 deletions
+8 -1
View File
@@ -4,7 +4,9 @@ import (
"errors"
"io"
"log/slog"
"net"
"net/netip"
"slices"
"github.com/soypat/lneto"
"github.com/soypat/lneto/internal"
@@ -66,7 +68,12 @@ func (sb *StackBasic) Recv(frame []byte) error {
proto := ifrm.Protocol()
if h.proto == proto {
sb.info("iprecv", slog.String("ipproto", proto.String()), slog.Int("plen", int(totalLen)))
return h.recv(frame[:totalLen], off)
err = h.recv(frame[:totalLen], off)
if err == net.ErrClosed {
sb.info("ipclose", slog.String("proto", proto.String()))
sb.handlers = slices.Delete(sb.handlers, i, i+1)
}
return err
}
}
}
+135 -5
View File
@@ -12,9 +12,13 @@ import (
"github.com/soypat/lneto/ethernet"
"github.com/soypat/lneto/http/httpraw"
"github.com/soypat/lneto/ipv4"
"github.com/soypat/lneto/ipv6"
"github.com/soypat/lneto/tcp"
"github.com/soypat/lneto/udp"
)
const unknownPayloadProto = "payload?"
type PacketBreakdown struct {
hdr httpraw.Header
vld lneto.Validator
@@ -115,6 +119,29 @@ func (pc *PacketBreakdown) CaptureARP(dst []Frame, pkt []byte, bitOffset int) ([
return dst, nil
}
func (pc *PacketBreakdown) CaptureIPv6(dst []Frame, pkt []byte, bitOffset int) ([]Frame, error) {
if bitOffset%8 != 0 {
return dst, errors.New("IPv6 must be parsed at byte boundary")
}
ifrm6, err := ipv6.NewFrame(pkt[bitOffset/8:])
if err != nil {
return dst, err
}
ifrm6.ValidateSize(pc.validator())
if pc.validator().HasError() {
return dst, pc.validator().Err()
}
finfo := Frame{
Protocol: ethernet.TypeIPv6,
PacketBitOffset: bitOffset,
}
finfo.Fields = append(finfo.Fields, baseIPv6Fields[:]...)
dst = append(dst, finfo)
proto := ifrm6.NextHeader()
end := 40 * octet
return pc.captureIPProto(proto, dst, pkt, end)
}
func (pc *PacketBreakdown) CaptureIPv4(dst []Frame, pkt []byte, bitOffset int) ([]Frame, error) {
if bitOffset%8 != 0 {
return dst, errors.New("IPv4 must be parsed at byte boundary")
@@ -143,11 +170,17 @@ func (pc *PacketBreakdown) CaptureIPv4(dst []Frame, pkt []byte, bitOffset int) (
proto := ifrm4.Protocol()
dst = append(dst, finfo)
end := bitOffset + octet*ifrm4.HeaderLength()
return pc.captureIPProto(proto, dst, pkt, end)
}
func (pc *PacketBreakdown) captureIPProto(proto lneto.IPProto, dst []Frame, pkt []byte, bitOffset int) (_ []Frame, err error) {
switch proto {
case lneto.IPProtoTCP:
dst, err = pc.CaptureTCP(dst, pkt, end)
dst, err = pc.CaptureTCP(dst, pkt, bitOffset)
case lneto.IPProtoUDP:
dst, err = pc.CaptureUDP(dst, pkt, bitOffset)
default:
dst = append(dst, remainingFrameInfo(proto, 0, end, octet*len(pkt)))
dst = append(dst, remainingFrameInfo(proto, 0, bitOffset, octet*len(pkt)))
}
return dst, err
}
@@ -183,12 +216,35 @@ func (pc *PacketBreakdown) CaptureTCP(dst []Frame, pkt []byte, bitOffset int) ([
if len(payload) > 0 {
dst, err = pc.CaptureHTTP(dst, pkt, end)
if err != nil {
dst = append(dst, remainingFrameInfo(nil, FieldClassPayload, end, len(pkt)))
dst = append(dst, remainingFrameInfo(unknownPayloadProto, FieldClassPayload, end, octet*len(pkt)))
}
}
return dst, nil
}
func (pc *PacketBreakdown) CaptureUDP(dst []Frame, pkt []byte, bitOffset int) ([]Frame, error) {
if bitOffset%8 != 0 {
return dst, errors.New("UDP must be parsed at byte boundary")
}
ufrm, err := udp.NewFrame(pkt[bitOffset/8:])
if err != nil {
return dst, err
}
ufrm.ValidateSize(pc.validator())
if pc.validator().HasError() {
return dst, pc.validator().Err()
}
finfo := Frame{
Protocol: lneto.IPProtoUDP,
PacketBitOffset: bitOffset,
}
finfo.Fields = append(finfo.Fields, baseUDPFields[:]...)
dst = append(dst, finfo)
end := bitOffset + 8*octet
dst = append(dst, remainingFrameInfo(unknownPayloadProto, FieldClassPayload, end, octet*len(pkt)))
return dst, nil
}
func (pc *PacketBreakdown) CaptureHTTP(dst []Frame, pkt []byte, bitOffset int) ([]Frame, error) {
const httpProtocol = "HTTP"
if bitOffset%8 != 0 {
@@ -357,7 +413,6 @@ func (frm Frame) AppendField(dst []byte, fieldIdx int, pkt []byte) ([]byte, erro
func (frm Frame) String() string {
iopt, err := frm.FieldByClass(FieldClassOptions)
hasOpts := ""
if err == nil {
hasOpts = fmt.Sprintf(" optlen=%d", (frm.Fields[iopt].BitLength+7)/8)
@@ -457,6 +512,56 @@ var baseARPFields = [...]FrameField{
},
}
var baseIPv6Fields = [...]FrameField{
{
Class: FieldClassVersion,
FrameBitOffset: 0,
BitLength: 4,
},
{
Name: "Type of Service",
Class: FieldClassFlags,
FrameBitOffset: 4,
BitLength: 1 * octet,
RightAligned: true,
},
{
Name: "Flow Label",
Class: FieldClassID,
FrameBitOffset: 12,
BitLength: 20,
RightAligned: true,
},
{
Name: "Total Length",
Class: FieldClassSize,
FrameBitOffset: 4 * octet,
BitLength: 2 * octet,
},
{
Name: "Next Header",
Class: 0,
FrameBitOffset: 6 * octet,
BitLength: 1 * octet,
},
{
Name: "Hop Limit",
Class: 0,
FrameBitOffset: 7 * octet,
BitLength: 1 * octet,
},
{
Class: FieldClassSrc,
FrameBitOffset: 8 * octet,
BitLength: 16 * octet,
},
{
Class: FieldClassSrc,
FrameBitOffset: 24 * octet,
BitLength: 16 * octet,
},
}
var baseIPv4Fields = [...]FrameField{
{
Class: FieldClassVersion,
@@ -532,7 +637,7 @@ var baseTCPFields = [...]FrameField{
},
{
Name: "Destination port",
Class: FieldClassSrc,
Class: FieldClassDst,
FrameBitOffset: 2 * octet,
BitLength: 2 * octet,
},
@@ -579,6 +684,31 @@ var baseTCPFields = [...]FrameField{
},
}
var baseUDPFields = [...]FrameField{
{
Name: "Source port",
Class: FieldClassSrc,
FrameBitOffset: 0,
BitLength: 2 * octet,
},
{
Name: "Destination port",
Class: FieldClassDst,
FrameBitOffset: 2 * octet,
BitLength: 2 * octet,
},
{
Class: FieldClassSize,
FrameBitOffset: 4 * octet,
BitLength: 2 * octet,
},
{
Class: FieldClassChecksum,
FrameBitOffset: 6 * octet,
BitLength: 2 * octet,
},
}
func remainingFrameInfo(proto any, class FieldClass, pktBitOffset, pktBitLen int) Frame {
return Frame{
Protocol: proto,
+10
View File
@@ -102,6 +102,16 @@ func TestCap(t *testing.T) {
if gotTCPFlags != wantTCPflags {
t.Errorf("want %s TCP flags, got %s", wantTCPflags.String(), gotTCPFlags.String())
}
wanDstPort := gen.DstTCP
wantSrcPort := gen.SrcTCP
gotSrcPort := uint16(getClass(ptfrm, FieldClassSrc))
gotDstPort := uint16(getClass(ptfrm, FieldClassDst))
if wantSrcPort != gotSrcPort {
t.Errorf("want %d TCP src port, got %d", wantSrcPort, gotSrcPort)
}
if wanDstPort != gotDstPort {
t.Errorf("want %d TCP dst port, got %d", wanDstPort, gotDstPort)
}
gotHeaderLen := getClass(ptfrm, FieldClassSize)
if gotHeaderLen != uint64(wantHeaderLen) {
t.Errorf("want %d TCP header length, got %d", wantHeaderLen, gotHeaderLen)
+14
View File
@@ -0,0 +1,14 @@
package internet
import "github.com/soypat/lneto"
type PortStack struct {
handlers []porthandler
proto lneto.IPProto
}
type porthandler struct {
recv func([]byte, int) error
handle func([]byte, int) (int, error)
port uint16
}