mirror of
https://github.com/soypat/lneto.git
synced 2026-09-10 16:49:37 +00:00
enhance xnet StackAsync test and add non-linux stubs for Tap and Bridge
This commit is contained in:
+1
-1
@@ -1,4 +1,4 @@
|
|||||||
//go:build linux && !baremetal
|
//go:build linux && !tinygo
|
||||||
|
|
||||||
package internal
|
package internal
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,68 @@
|
|||||||
|
//go:build !linux || tinygo
|
||||||
|
|
||||||
|
package internal
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"net/netip"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Tap struct {
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewTap(name string, ip netip.Prefix) (*Tap, error) {
|
||||||
|
return nil, errors.ErrUnsupported
|
||||||
|
}
|
||||||
|
|
||||||
|
func (tap *Tap) IPMask() (netip.Prefix, error) {
|
||||||
|
return netip.Prefix{}, errors.ErrUnsupported
|
||||||
|
}
|
||||||
|
func (tap *Tap) Read(b []byte) (int, error) {
|
||||||
|
return -1, errors.ErrUnsupported
|
||||||
|
}
|
||||||
|
func (tap *Tap) Write(b []byte) (int, error) {
|
||||||
|
return -1, errors.ErrUnsupported
|
||||||
|
}
|
||||||
|
func (tap *Tap) Close() error {
|
||||||
|
return errors.ErrUnsupported
|
||||||
|
}
|
||||||
|
func (tap *Tap) MTU() (int, error) {
|
||||||
|
return -1, errors.ErrUnsupported
|
||||||
|
}
|
||||||
|
func (tap *Tap) HardwareAddress6() (hw [6]byte, err error) {
|
||||||
|
return hw, errors.ErrUnsupported
|
||||||
|
}
|
||||||
|
|
||||||
|
type Bridge struct {
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewBridge(name string) (*Bridge, error) {
|
||||||
|
return nil, errors.ErrUnsupported
|
||||||
|
}
|
||||||
|
func (br *Bridge) Write(frame []byte) (int, error) {
|
||||||
|
return -1, errors.ErrUnsupported
|
||||||
|
}
|
||||||
|
func (br *Bridge) Read(frame []byte) (int, error) {
|
||||||
|
return -1, errors.ErrUnsupported
|
||||||
|
}
|
||||||
|
func (br *Bridge) Close() error {
|
||||||
|
return errors.ErrUnsupported
|
||||||
|
}
|
||||||
|
func (tap *Bridge) MTU() (int, error) {
|
||||||
|
return -1, errors.ErrUnsupported
|
||||||
|
}
|
||||||
|
func (tap *Bridge) HardwareAddress6() (hw [6]byte, err error) {
|
||||||
|
return hw, errors.ErrUnsupported
|
||||||
|
}
|
||||||
|
|
||||||
|
func (br *Bridge) SetHardwareAddress6(hw [6]byte) error {
|
||||||
|
return errors.ErrUnsupported
|
||||||
|
}
|
||||||
|
|
||||||
|
func (br *Bridge) IPMask() (netip.Prefix, error) {
|
||||||
|
return netip.Prefix{}, errors.ErrUnsupported
|
||||||
|
}
|
||||||
|
|
||||||
|
func (br *Bridge) Addr() (netip.Addr, error) {
|
||||||
|
return netip.Addr{}, errors.ErrUnsupported
|
||||||
|
}
|
||||||
@@ -22,6 +22,16 @@ import (
|
|||||||
|
|
||||||
const unknownPayloadProto = "payload?"
|
const unknownPayloadProto = "payload?"
|
||||||
|
|
||||||
|
var (
|
||||||
|
ErrFieldByClassNotFound = errors.New("pcap: field by class not found")
|
||||||
|
)
|
||||||
|
|
||||||
|
type proto string
|
||||||
|
|
||||||
|
const (
|
||||||
|
ProtoEthernet proto = "Ethernet"
|
||||||
|
)
|
||||||
|
|
||||||
type PacketBreakdown struct {
|
type PacketBreakdown struct {
|
||||||
hdr httpraw.Header
|
hdr httpraw.Header
|
||||||
dmsg dns.Message
|
dmsg dns.Message
|
||||||
@@ -42,7 +52,7 @@ func (pc *PacketBreakdown) CaptureEthernet(dst []Frame, pkt []byte, bitOffset in
|
|||||||
}
|
}
|
||||||
|
|
||||||
finfo := Frame{
|
finfo := Frame{
|
||||||
Protocol: "Ethernet",
|
Protocol: ProtoEthernet,
|
||||||
PacketBitOffset: bitOffset,
|
PacketBitOffset: bitOffset,
|
||||||
}
|
}
|
||||||
finfo.Fields = append(finfo.Fields, baseEthernetFields[:]...)
|
finfo.Fields = append(finfo.Fields, baseEthernetFields[:]...)
|
||||||
@@ -208,7 +218,8 @@ func (pc *PacketBreakdown) CaptureIPv4(dst []Frame, pkt []byte, bitOffset int) (
|
|||||||
end := bitOffset + octet*ifrm4.HeaderLength()
|
end := bitOffset + octet*ifrm4.HeaderLength()
|
||||||
var protoErrs []error
|
var protoErrs []error
|
||||||
var crc lneto.CRC791
|
var crc lneto.CRC791
|
||||||
if proto == lneto.IPProtoTCP {
|
switch proto {
|
||||||
|
case lneto.IPProtoTCP:
|
||||||
ifrm4.CRCWriteTCPPseudo(&crc)
|
ifrm4.CRCWriteTCPPseudo(&crc)
|
||||||
tfrm, err := tcp.NewFrame(ifrm4.Payload())
|
tfrm, err := tcp.NewFrame(ifrm4.Payload())
|
||||||
if err == nil {
|
if err == nil {
|
||||||
@@ -224,7 +235,7 @@ func (pc *PacketBreakdown) CaptureIPv4(dst []Frame, pkt []byte, bitOffset int) (
|
|||||||
protoErrs = append(protoErrs, &crcError16{protocol: "ipv4+tcp", want: wantSum, got: gotSum})
|
protoErrs = append(protoErrs, &crcError16{protocol: "ipv4+tcp", want: wantSum, got: gotSum})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if proto == lneto.IPProtoUDP {
|
case lneto.IPProtoUDP:
|
||||||
ifrm4.CRCWriteUDPPseudo(&crc)
|
ifrm4.CRCWriteUDPPseudo(&crc)
|
||||||
ufrm, err := udp.NewFrame(ifrm4.Payload())
|
ufrm, err := udp.NewFrame(ifrm4.Payload())
|
||||||
if err == nil {
|
if err == nil {
|
||||||
@@ -504,7 +515,7 @@ func (frm Frame) FieldByClass(c FieldClass) (int, error) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if selected < 0 {
|
if selected < 0 {
|
||||||
return -1, errors.New("field by class not found")
|
return -1, ErrFieldByClassNotFound
|
||||||
}
|
}
|
||||||
if multiple && frm.Fields[selected].Name != "" {
|
if multiple && frm.Fields[selected].Name != "" {
|
||||||
return -1, errors.New("multiple classes found and none have empty name")
|
return -1, errors.New("multiple classes found and none have empty name")
|
||||||
|
|||||||
@@ -202,6 +202,9 @@ func (tcb *ControlBlock) PendingSegment(payloadLen int) (_ Segment, ok bool) {
|
|||||||
}
|
}
|
||||||
payloadLen = int(maxPayload)
|
payloadLen = int(maxPayload)
|
||||||
}
|
}
|
||||||
|
if payloadLen > 0 {
|
||||||
|
pending |= FlagPSH // By default ensure all data flushed to destination application immediately on receive.
|
||||||
|
}
|
||||||
|
|
||||||
if established {
|
if established {
|
||||||
pending |= FlagACK // ACK is always set in established state. Not in RFC9293 but somehow expected?
|
pending |= FlagACK // ACK is always set in established state. Not in RFC9293 but somehow expected?
|
||||||
|
|||||||
+90
-21
@@ -2,10 +2,12 @@ package xnet
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
|
"errors"
|
||||||
"net/netip"
|
"net/netip"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/soypat/lneto"
|
"github.com/soypat/lneto"
|
||||||
|
"github.com/soypat/lneto/ethernet"
|
||||||
"github.com/soypat/lneto/internet/pcap"
|
"github.com/soypat/lneto/internet/pcap"
|
||||||
"github.com/soypat/lneto/tcp"
|
"github.com/soypat/lneto/tcp"
|
||||||
)
|
)
|
||||||
@@ -140,16 +142,16 @@ func (tst *tester) TCPExchange(expect tcpExpectExchange, stack1, stack2 *StackAs
|
|||||||
t := tst.t
|
t := tst.t
|
||||||
buf := tst.buf
|
buf := tst.buf
|
||||||
nodata := expect.WantFlags == 0
|
nodata := expect.WantFlags == 0
|
||||||
var n int
|
var src, dst *StackAsync
|
||||||
var err error
|
|
||||||
switch expect.SourceIdx {
|
switch expect.SourceIdx {
|
||||||
case 0:
|
case 0:
|
||||||
n, err = stack1.Encapsulate(buf[:], 0)
|
src, dst = stack1, stack2
|
||||||
case 1:
|
case 1:
|
||||||
n, err = stack2.Encapsulate(buf[:], 0)
|
src, dst = stack2, stack1
|
||||||
default:
|
default:
|
||||||
panic("OOB")
|
panic("OOB")
|
||||||
}
|
}
|
||||||
|
n, err := src.Encapsulate(buf[:], 0)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
} else if n == 0 {
|
} else if n == 0 {
|
||||||
@@ -158,38 +160,105 @@ func (tst *tester) TCPExchange(expect tcpExpectExchange, stack1, stack2 *StackAs
|
|||||||
}
|
}
|
||||||
t.Error("zero bits sent")
|
t.Error("zero bits sent")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
tst.buf = tst.buf[:n]
|
||||||
|
defer func() {
|
||||||
|
tst.buf = tst.buf[:cap(tst.buf)]
|
||||||
|
}()
|
||||||
tst.frmbuf, err = tst.cap.CaptureEthernet(tst.frmbuf[:0], buf[:n], 0)
|
tst.frmbuf, err = tst.cap.CaptureEthernet(tst.frmbuf[:0], buf[:n], 0)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
tfrm := getProtoFrame(tst.frmbuf, lneto.IPProtoTCP)
|
srcEth := src.HardwareAddress()
|
||||||
if tfrm == nil {
|
dstEth := dst.HardwareAddress()
|
||||||
t.Fatal("where's the TCP?")
|
if !bytes.Equal(srcEth[:], tst.getData(pcap.ProtoEthernet, pcap.FieldClassSrc)) {
|
||||||
|
t.Errorf("mismatched ethernet src addr %x", tst.getData(pcap.ProtoEthernet, pcap.FieldClassSrc))
|
||||||
}
|
}
|
||||||
fidx, _ := tfrm.FieldByClass(pcap.FieldClassFlags)
|
if !bytes.Equal(dstEth[:], tst.getData(pcap.ProtoEthernet, pcap.FieldClassDst)) {
|
||||||
flags, _ := tfrm.FieldAsUint(fidx, buf[:n])
|
t.Errorf("mismatched ethernet dst addr %x", tst.getData(pcap.ProtoEthernet, pcap.FieldClassDst))
|
||||||
tflags := tcp.Flags(flags)
|
|
||||||
var payload []byte
|
|
||||||
fidx, _ = tfrm.FieldByClass(pcap.FieldClassPayload)
|
|
||||||
if fidx > 0 {
|
|
||||||
fieldPayload := tfrm.Fields[fidx]
|
|
||||||
payload = buf[fieldPayload.FrameBitOffset*8:]
|
|
||||||
}
|
}
|
||||||
|
if tst.getInt(ethernet.TypeIPv4, pcap.FieldClassVersion) != 4 {
|
||||||
|
t.Errorf("did not get IP version=4, got=%d", tst.getInt(ethernet.TypeIPv4, pcap.FieldClassVersion))
|
||||||
|
}
|
||||||
|
srcAddr := src.Addr()
|
||||||
|
dstAddr := dst.Addr()
|
||||||
|
if !bytes.Equal(srcAddr.AsSlice(), tst.getData(ethernet.TypeIPv4, pcap.FieldClassSrc)) {
|
||||||
|
t.Errorf("mismatched ip src addr %d", tst.getData(ethernet.TypeIPv4, pcap.FieldClassSrc))
|
||||||
|
}
|
||||||
|
if !bytes.Equal(dstAddr.AsSlice(), tst.getData(ethernet.TypeIPv4, pcap.FieldClassDst)) {
|
||||||
|
t.Errorf("mismatched ip dst addr %d", tst.getData(ethernet.TypeIPv4, pcap.FieldClassDst))
|
||||||
|
}
|
||||||
|
tflags := tcp.Flags(tst.getInt(lneto.IPProtoTCP, pcap.FieldClassFlags))
|
||||||
|
payload := tst.getPayload(lneto.IPProtoTCP)
|
||||||
if !bytes.Equal(payload, expect.WantData) {
|
if !bytes.Equal(payload, expect.WantData) {
|
||||||
t.Errorf("mismatched data sent, \nwant=%q\ngot=%q\n", expect.WantData, payload)
|
t.Errorf("mismatched data sent, \nwant=%q\ngot=%q\n", expect.WantData, payload)
|
||||||
}
|
}
|
||||||
if tflags != expect.WantFlags {
|
if tflags != expect.WantFlags {
|
||||||
t.Errorf("expected flags %s, got %s", expect.WantFlags.String(), tflags.String())
|
t.Errorf("expected flags %s, got %s", expect.WantFlags.String(), tflags.String())
|
||||||
}
|
}
|
||||||
switch expect.SourceIdx {
|
err = dst.Demux(buf[:], 0)
|
||||||
case 0:
|
|
||||||
err = stack2.Demux(buf[:], 0)
|
|
||||||
case 1:
|
|
||||||
err = stack1.Demux(buf[:], 0)
|
|
||||||
}
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
for i := range buf[:n] {
|
||||||
|
buf[i] = 0 // Set data sent to zero.
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (tst *tester) getPayload(proto any) []byte {
|
||||||
|
tst.t.Helper()
|
||||||
|
i := 0
|
||||||
|
for i = 0; i < len(tst.frmbuf); i++ {
|
||||||
|
if tst.frmbuf[i].Protocol == proto {
|
||||||
|
if i < len(tst.frmbuf)-1 {
|
||||||
|
frm := &tst.frmbuf[i+1]
|
||||||
|
bitOff := frm.PacketBitOffset
|
||||||
|
if bitOff%8 != 0 {
|
||||||
|
tst.t.Fatalf("proto %s bitoffset not multiple of 8: %d", proto, bitOff)
|
||||||
|
}
|
||||||
|
return tst.buf[bitOff/8:]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return tst.getData(proto, pcap.FieldClassPayload)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (tst *tester) getData(proto any, field pcap.FieldClass) []byte {
|
||||||
|
tst.t.Helper()
|
||||||
|
frm := getProtoFrame(tst.frmbuf, proto)
|
||||||
|
if frm == nil {
|
||||||
|
tst.t.Fatalf("no frame for proto %s found in %s", proto, tst.frmbuf)
|
||||||
|
}
|
||||||
|
fidx, err := frm.FieldByClass(field)
|
||||||
|
if err != nil {
|
||||||
|
if errors.Is(err, pcap.ErrFieldByClassNotFound) {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
tst.t.Fatal(err)
|
||||||
|
}
|
||||||
|
bitoff := frm.PacketBitOffset + frm.Fields[fidx].FrameBitOffset
|
||||||
|
bitlen := frm.Fields[fidx].BitLength
|
||||||
|
if bitoff%8 != 0 || bitlen%8 != 0 {
|
||||||
|
tst.t.Fatal("frame bitlength not multiple of 8")
|
||||||
|
}
|
||||||
|
return tst.buf[bitoff/8 : bitoff/8+bitlen/8]
|
||||||
|
}
|
||||||
|
|
||||||
|
func (tst *tester) getInt(proto any, field pcap.FieldClass) uint64 {
|
||||||
|
tst.t.Helper()
|
||||||
|
frm := getProtoFrame(tst.frmbuf, proto)
|
||||||
|
if frm == nil {
|
||||||
|
tst.t.Fatalf("no frame for proto %s found in %s", proto, tst.frmbuf)
|
||||||
|
}
|
||||||
|
fidx, err := frm.FieldByClass(field)
|
||||||
|
if err != nil {
|
||||||
|
tst.t.Fatal(err)
|
||||||
|
}
|
||||||
|
v, err := frm.FieldAsUint(fidx, tst.buf)
|
||||||
|
if err != nil {
|
||||||
|
tst.t.Fatal(err)
|
||||||
|
}
|
||||||
|
return v
|
||||||
}
|
}
|
||||||
|
|
||||||
func getProtoFrame(frms []pcap.Frame, proto any) *pcap.Frame {
|
func getProtoFrame(frms []pcap.Frame, proto any) *pcap.Frame {
|
||||||
|
|||||||
Reference in New Issue
Block a user