Skip to content

Commit

Permalink
Add fake implementation for net.PacketConn interface (#6419)
Browse files Browse the repository at this point in the history
In the pkg/agent/util/nettest/ package.
The ARPResponder unit tests are updated to use this new fake. It will
also be useful when writing unit tests for the NodeLatencyMonitor.

Signed-off-by: Antonin Bas <[email protected]>
  • Loading branch information
antoninbas authored Jun 11, 2024
1 parent 6fce4e8 commit 8a4682c
Show file tree
Hide file tree
Showing 3 changed files with 240 additions and 111 deletions.
176 changes: 68 additions & 108 deletions pkg/agent/ipassigner/responder/arp_responder_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,154 +15,120 @@
package responder

import (
"bytes"
"net"
"testing"
"time"

"github.com/mdlayher/arp"
"github.com/mdlayher/ethernet"
"github.com/mdlayher/packet"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"k8s.io/apimachinery/pkg/util/sets"
)

type fakePacketConn struct {
addr packet.Addr
buffer *bytes.Buffer
}

var _ net.PacketConn = (*fakePacketConn)(nil)

func (pc *fakePacketConn) ReadFrom(p []byte) (int, net.Addr, error) {
n, err := pc.buffer.Read(p)
return n, &pc.addr, err
}

func (pc *fakePacketConn) WriteTo(p []byte, addr net.Addr) (int, error) {
return pc.buffer.Write(p)
}

func (pc *fakePacketConn) Close() error {
return nil
}

func (pc *fakePacketConn) LocalAddr() net.Addr {
return &pc.addr
}

func (pc *fakePacketConn) SetDeadline(t time.Time) error {
return nil
}

func (pc *fakePacketConn) SetReadDeadline(t time.Time) error {
return nil
}

func (pc *fakePacketConn) SetWriteDeadline(t time.Time) error {
return nil
}
"antrea.io/antrea/pkg/agent/util/nettest"
)

func newFakeARPClient(iface *net.Interface, conn *fakePacketConn) (*arp.Client, error) {
func newFakeARPClient(iface *net.Interface, conn *nettest.PacketConn) (*arp.Client, error) {
return arp.New(iface, conn)
}

func newFakeNetworkInterface() *net.Interface {
func newFakeNetworkInterface(hardwareAddr []byte) *net.Interface {
return &net.Interface{
Index: 0,
MTU: 1500,
Name: "eth0",
HardwareAddr: []byte{0x00, 0x11, 0x22, 0x33, 0x44, 0x55},
HardwareAddr: hardwareAddr,
}
}

func getEthernetForARPPacket(p *arp.Packet, addr net.HardwareAddr) []byte {
pb, _ := p.MarshalBinary()
f := &ethernet.Frame{
Destination: addr,
Source: p.SenderHardwareAddr,
EtherType: ethernet.EtherTypeARP,
Payload: pb,
}
fb, _ := f.MarshalBinary()
return fb
}

func TestARPResponder_HandleARPRequest(t *testing.T) {
// The "local" endpoint is the one running the ARPRespondder.
// The "remote" endpoint is the one sending ARP requests to the "local" endpoint.
localHWAddr := net.HardwareAddr{0x00, 0x01, 0x02, 0x03, 0x04, 0x05}
remoteHWAddr := net.HardwareAddr{0x00, 0x11, 0x22, 0x33, 0x44, 0x55}
localIP := net.ParseIP("192.168.10.1")
remoteIP := net.ParseIP("192.168.10.2")

tests := []struct {
name string
iface *net.Interface
arpOperation arp.Operation
srcHWAddr, dstHWAddr net.HardwareAddr
srcIP, dstIP net.IP
assignedIPs []net.IP
expectError bool
expectedBytes []byte
name string
assignedIPs []net.IP
replyExpected bool
}{
{
name: "Response for assigned IP",
iface: newFakeNetworkInterface(),
arpOperation: arp.OperationRequest,
srcHWAddr: net.HardwareAddr{0x00, 0x01, 0x02, 0x03, 0x04, 0x05},
dstHWAddr: ethernet.Broadcast,
srcIP: net.ParseIP("192.168.10.2"),
dstIP: net.ParseIP("192.168.10.1"),
assignedIPs: []net.IP{net.ParseIP("192.168.10.1")},
expectError: false,
expectedBytes: []byte{
// ethernet header (16 bytes)
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, // 6 bytes: destination hardware address
0x00, 0x11, 0x22, 0x33, 0x44, 0x55, // 6 bytes: source hardware address
0x08, 0x06, // 2 bytes: ethernet type
// arp payload (46 bytes)
0x00, 0x01, // 2 bytes: hardware type
0x08, 0x00, // 2 bytes: protocol type
0x06, // 1 byte : hardware address length
0x04, // 1 byte : protocol length
0x00, 0x02, // 2 bytes: operation
0x00, 0x11, 0x22, 0x33, 0x44, 0x55, // 6 bytes: source hardware address
0xc0, 0xa8, 0x0a, 0x01, // 4 bytes: source protocol address
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, // 6 bytes: target hardware address
0xc0, 0xa8, 0x0a, 0x02, // 4 bytes: target protocol address
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 18 bytes: padding
},
name: "Response for assigned IP",
assignedIPs: []net.IP{localIP},
replyExpected: true,
},
{
name: "Response for not assigned IP",
iface: newFakeNetworkInterface(),
arpOperation: arp.OperationRequest,
srcHWAddr: net.HardwareAddr{0x00, 0x01, 0x02, 0x03, 0x04, 0x05},
dstHWAddr: ethernet.Broadcast,
srcIP: net.ParseIP("192.168.10.2"),
dstIP: net.ParseIP("192.168.10.3"),
assignedIPs: []net.IP{net.ParseIP("192.168.10.1")},
expectError: false,
expectedBytes: []byte{},
assignedIPs: []net.IP{net.ParseIP("192.168.10.3")},
replyExpected: false,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
conn := &fakePacketConn{
buffer: bytes.NewBuffer(nil),
addr: packet.Addr{
HardwareAddr: tt.iface.HardwareAddr,
},
localIface := newFakeNetworkInterface(localHWAddr)
remoteIface := newFakeNetworkInterface(remoteHWAddr)
localAddr := &packet.Addr{
HardwareAddr: localHWAddr,
}
fakeARPClient, err := newFakeARPClient(tt.iface, conn)
remoteAddr := &packet.Addr{
HardwareAddr: remoteHWAddr,
}
localConn, remoteConn := nettest.PacketConnPipe(localAddr, remoteAddr, 1)
localARPClient, err := newFakeARPClient(localIface, localConn)
require.NoError(t, err)
remoteARPClient, err := newFakeARPClient(remoteIface, remoteConn)
require.NoError(t, err)
packet, err := arp.NewPacket(tt.arpOperation, tt.srcHWAddr, tt.srcIP, tt.dstHWAddr, tt.dstIP)
request, err := arp.NewPacket(arp.OperationRequest, remoteHWAddr, remoteIP, ethernet.Broadcast, localIP)
require.NoError(t, err)
err = fakeARPClient.WriteTo(packet, tt.dstHWAddr)
expectedReply, err := arp.NewPacket(arp.OperationReply, localHWAddr, localIP, remoteHWAddr, remoteIP)
require.NoError(t, err)
expectedBytes := getEthernetForARPPacket(expectedReply, remoteHWAddr)
require.NoError(t, remoteARPClient.WriteTo(request, localAddr.HardwareAddr))
assignedIPs := sets.New[string]()
for _, ip := range tt.assignedIPs {
assignedIPs.Insert(ip.String())
}
r := arpResponder{
iface: tt.iface,
conn: fakeARPClient,
iface: localIface,
conn: localARPClient,
assignedIPs: sets.New[string](),
}
for _, ip := range tt.assignedIPs {
r.AddIP(ip)
}
err = r.handleARPRequest()
assert.NoError(t, err)
assert.Equal(t, tt.expectedBytes, conn.buffer.Bytes())
require.NoError(t, err)
// We cannot use remoteARPClient.ReadFrom as it is blocking.
replyB, addr, err := remoteConn.Receive()
if !tt.replyExpected {
assert.Error(t, err)
} else {
require.NoError(t, err)
assert.Equal(t, remoteAddr, addr)
assert.Equal(t, expectedBytes, replyB)
}
})
}
}

func Test_arpResponder_addIP(t *testing.T) {
hwAddr := []byte{0x00, 0x11, 0x22, 0x33, 0x44, 0x55}
iface := newFakeNetworkInterface(hwAddr)

tests := []struct {
name string
ip net.IP
Expand Down Expand Up @@ -193,19 +159,10 @@ func Test_arpResponder_addIP(t *testing.T) {
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
r := &arpResponder{
iface: newFakeNetworkInterface(),
iface: iface,
assignedIPs: tt.assignedIPs,
}
conn := &fakePacketConn{
buffer: bytes.NewBuffer(nil),
addr: packet.Addr{
HardwareAddr: r.iface.HardwareAddr,
},
}
fakeARPClient, err := newFakeARPClient(r.iface, conn)
require.NoError(t, err)
r.conn = fakeARPClient
err = r.AddIP(tt.ip)
err := r.AddIP(tt.ip)
if tt.expectedError {
assert.Error(t, err)
} else {
Expand All @@ -217,6 +174,9 @@ func Test_arpResponder_addIP(t *testing.T) {
}

func Test_arpResponder_removeIP(t *testing.T) {
hwAddr := []byte{0x00, 0x11, 0x22, 0x33, 0x44, 0x55}
iface := newFakeNetworkInterface(hwAddr)

tests := []struct {
name string
ip net.IP
Expand Down Expand Up @@ -247,7 +207,7 @@ func Test_arpResponder_removeIP(t *testing.T) {
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
r := &arpResponder{
iface: newFakeNetworkInterface(),
iface: iface,
assignedIPs: tt.assignedIPs,
}
err := r.RemoveIP(tt.ip)
Expand Down
15 changes: 12 additions & 3 deletions pkg/agent/ipassigner/responder/ndp_responder_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,9 @@ func (c *fakeNDPConn) LeaveGroup(ip net.IP) error {
}

func TestNDPResponder_handleNeighborSolicitation(t *testing.T) {
hwAddr := []byte{0x00, 0x11, 0x22, 0x33, 0x44, 0x55}
iface := newFakeNetworkInterface(hwAddr)

tests := []struct {
name string
requestMessage []byte
Expand Down Expand Up @@ -131,7 +134,7 @@ func TestNDPResponder_handleNeighborSolicitation(t *testing.T) {
assignedIPs.Insert(ip.String())
}
responder := &ndpResponder{
iface: newFakeNetworkInterface(),
iface: iface,
conn: fakeConn,
assignedIPs: sets.New[string](),
}
Expand Down Expand Up @@ -185,6 +188,9 @@ func Test_parseIPv6SolicitedNodeMulticastAddress(t *testing.T) {
}

func Test_ndpResponder_addIP(t *testing.T) {
hwAddr := []byte{0x00, 0x11, 0x22, 0x33, 0x44, 0x55}
iface := newFakeNetworkInterface(hwAddr)

tests := []struct {
name string
ip net.IP
Expand Down Expand Up @@ -249,7 +255,7 @@ func Test_ndpResponder_addIP(t *testing.T) {
t.Run(tt.name, func(t *testing.T) {
var joinedGroup, leftGroup []net.IP
r := &ndpResponder{
iface: newFakeNetworkInterface(),
iface: iface,
conn: &fakeNDPConn{
joinGroup: func(ip net.IP) error {
joinedGroup = append(joinedGroup, ip)
Expand Down Expand Up @@ -281,6 +287,9 @@ func Test_ndpResponder_addIP(t *testing.T) {
}

func Test_ndpResponder_removeIP(t *testing.T) {
hwAddr := []byte{0x00, 0x11, 0x22, 0x33, 0x44, 0x55}
iface := newFakeNetworkInterface(hwAddr)

tests := []struct {
name string
ip net.IP
Expand Down Expand Up @@ -352,7 +361,7 @@ func Test_ndpResponder_removeIP(t *testing.T) {
t.Run(tt.name, func(t *testing.T) {
var joinedGroup, leftGroup []net.IP
r := &ndpResponder{
iface: newFakeNetworkInterface(),
iface: iface,
conn: &fakeNDPConn{
joinGroup: func(ip net.IP) error {
joinedGroup = append(joinedGroup, ip)
Expand Down
Loading

0 comments on commit 8a4682c

Please sign in to comment.