fix couple bugs in ARP and add benchmarks

This commit is contained in:
Patricio Whittingslow
2026-01-13 10:15:18 -03:00
parent a5af5b58ce
commit f51cdf74b6
3 changed files with 202 additions and 17 deletions
+18 -17
View File
@@ -191,24 +191,25 @@ func (h *Handler) Encapsulate(carrierData []byte, offsetToIP, offsetToFrame int)
return n, nil
}
for i := range h.queries {
if !h.queries[i].querysent {
h.queries[i].querysent = true
afrm, _ := NewFrame(b)
afrm.SetHardware(h.htype, uint8(len(h.ourHWAddr)))
afrm.SetProtocol(h.protoType, uint8(len(h.ourProtoAddr)))
afrm.SetOperation(OpRequest)
hwSender, protoSender := afrm.Sender()
copy(hwSender, h.ourHWAddr)
copy(protoSender, h.ourProtoAddr)
hwTarget, protoTarget := afrm.Target()
copy(protoTarget, h.queries[i].protoaddr)
for j := range hwTarget {
hwTarget[j] = 0
}
broadcast := ethernet.BroadcastAddr()
trySetEthernetDst(carrierData[:offsetToFrame], broadcast[:])
return n, nil
if h.queries[i].isInvalid() || h.queries[i].querysent {
continue
}
h.queries[i].querysent = true
afrm, _ := NewFrame(b)
afrm.SetHardware(h.htype, uint8(len(h.ourHWAddr)))
afrm.SetProtocol(h.protoType, uint8(len(h.ourProtoAddr)))
afrm.SetOperation(OpRequest)
hwSender, protoSender := afrm.Sender()
copy(hwSender, h.ourHWAddr)
copy(protoSender, h.ourProtoAddr)
hwTarget, protoTarget := afrm.Target()
copy(protoTarget, h.queries[i].protoaddr)
for j := range hwTarget {
hwTarget[j] = 0
}
broadcast := ethernet.BroadcastAddr()
trySetEthernetDst(carrierData[:offsetToFrame], broadcast[:])
return n, nil
}
return 0, nil
}
+11
View File
@@ -450,6 +450,17 @@ func (s *StackAsync) ResultResolveHardwareAddress6(ip netip.Addr) (hw [6]byte, e
return [6]byte(hwslice), nil
}
// DiscardResolveHardwareAddress6 discards a pending ARP query for the given IP address.
func (s *StackAsync) DiscardResolveHardwareAddress6(ip netip.Addr) error {
s.mu.Lock()
defer s.mu.Unlock()
if !ip.Is4() {
return errors.New("unsupported or invalid IP address")
}
addr := ip.As4()
return s.arp.DiscardQuery(addr[:])
}
type DHCPResults struct {
DNSServers []netip.Addr
Router netip.Addr
+173
View File
@@ -0,0 +1,173 @@
package xnet
import (
"net/netip"
"testing"
"github.com/soypat/lneto/tcp"
)
func BenchmarkARPExchange(b *testing.B) {
const MTU = 1500
c1, c2 := new(StackAsync), new(StackAsync)
queryAddr := netip.AddrFrom4([4]byte{192, 168, 1, 2})
err := c1.Reset(StackConfig{
Hostname: "C1",
RandSeed: 1,
StaticAddress: netip.AddrFrom4([4]byte{192, 168, 1, 1}),
HardwareAddress: [6]byte{0xde, 0xad, 0xbe, 0xef, 0x00, 0x00},
MTU: MTU,
})
if err != nil {
b.Fatal(err)
}
err = c2.Reset(StackConfig{
Hostname: "C2",
RandSeed: 2,
StaticAddress: queryAddr,
HardwareAddress: [6]byte{0xc0, 0xff, 0xee, 0xc0, 0xff, 0xee},
MTU: MTU,
})
if err != nil {
b.Fatal(err)
}
// Set gateways so ethernet frames are properly addressed.
c1.SetGateway6(c2.HardwareAddress())
c2.SetGateway6(c1.HardwareAddress())
var buf [MTU]byte
b.ResetTimer()
for i := 0; i < b.N; i++ {
err = c1.StartResolveHardwareAddress6(queryAddr)
if err != nil {
b.Fatal(err)
}
n, err := c1.Encapsulate(buf[:], -1, 0) // Send Request.
if err != nil {
b.Fatal(err)
} else if n == 0 {
b.Fatal("expected send of data after first query")
}
err = c2.Demux(buf[:n], 0) // Receive request.
if err != nil {
b.Fatal(err)
}
n, err = c2.Encapsulate(buf[:], -1, 0) // Send response.
if err != nil {
b.Fatal(err)
} else if n == 0 {
b.Fatal("got no response to request")
}
err = c1.Demux(buf[:n], 0) // Receive response.
if err != nil {
b.Fatal(err)
}
_, err = c1.ResultResolveHardwareAddress6(queryAddr)
if err != nil {
b.Fatal("expected query result:", err)
}
// Discard query for next iteration.
c1.DiscardResolveHardwareAddress6(queryAddr)
}
}
func BenchmarkTCPHandshake(b *testing.B) {
const MTU = 1500
const svPort = 8080
client, sv := new(StackAsync), new(StackAsync)
clconn, svconn := new(tcp.Conn), new(tcp.Conn)
err := sv.Reset(StackConfig{
Hostname: "Server",
RandSeed: 1,
StaticAddress: netip.AddrFrom4([4]byte{10, 0, 0, 1}),
MaxTCPConns: 1,
HardwareAddress: [6]byte{0xbe, 0xef, 0, 0, 0, 1},
MTU: MTU,
})
if err != nil {
b.Fatal(err)
}
err = client.Reset(StackConfig{
Hostname: "Client",
RandSeed: 2,
StaticAddress: netip.AddrFrom4([4]byte{10, 0, 0, 2}),
MaxTCPConns: 1,
HardwareAddress: [6]byte{0xbe, 0xef, 0, 0, 0, 2},
MTU: MTU,
})
if err != nil {
b.Fatal(err)
}
sv.SetGateway6(client.HardwareAddress())
client.SetGateway6(sv.HardwareAddress())
buf := make([]byte, MTU*4)
err = clconn.Configure(tcp.ConnConfig{
RxBuf: buf[:MTU],
TxBuf: buf[MTU : MTU*2],
TxPacketQueueSize: 4,
})
if err != nil {
b.Fatal(err)
}
err = svconn.Configure(tcp.ConnConfig{
RxBuf: buf[2*MTU : 3*MTU],
TxBuf: buf[3*MTU : 4*MTU],
TxPacketQueueSize: 4,
})
if err != nil {
b.Fatal(err)
}
var pktbuf [MTU]byte
b.ResetTimer()
for i := 0; i < b.N; i++ {
// Setup connections.
err = sv.ListenTCP(svconn, svPort)
if err != nil {
b.Fatal(err)
}
err = client.DialTCP(clconn, 1337, netip.AddrPortFrom(sv.Addr(), svPort))
if err != nil {
b.Fatal(err)
}
// SYN from client.
n, err := client.Encapsulate(pktbuf[:], -1, 0)
if err != nil {
b.Fatal(err)
}
err = sv.Demux(pktbuf[:n], 0)
if err != nil {
b.Fatal(err)
}
// SYN-ACK from server.
n, err = sv.Encapsulate(pktbuf[:], -1, 0)
if err != nil {
b.Fatal(err)
}
err = client.Demux(pktbuf[:n], 0)
if err != nil {
b.Fatal(err)
}
// ACK from client.
n, err = client.Encapsulate(pktbuf[:], -1, 0)
if err != nil {
b.Fatal(err)
}
err = sv.Demux(pktbuf[:n], 0)
if err != nil {
b.Fatal(err)
}
// Abort connections for next iteration.
clconn.Abort()
svconn.Abort()
}
}