summaryrefslogtreecommitdiffhomepage
path: root/test/packetimpact/testbench/connections.go
diff options
context:
space:
mode:
authorEyal Soha <eyalsoha@google.com>2020-03-31 19:51:52 -0700
committergVisor bot <gvisor-bot@google.com>2020-03-31 19:53:11 -0700
commitd25036ad17a3ada7fa6ce9900f20e246e07acd2f (patch)
treef7fa1ea3c6023cdaefd4bd08388a5ea72bce992a /test/packetimpact/testbench/connections.go
parent639d94f9f71b43e86320a6e9157c932f5d7936a7 (diff)
Test receiving multicast packets over UDP
PiperOrigin-RevId: 304098611
Diffstat (limited to 'test/packetimpact/testbench/connections.go')
-rw-r--r--test/packetimpact/testbench/connections.go216
1 files changed, 189 insertions, 27 deletions
diff --git a/test/packetimpact/testbench/connections.go b/test/packetimpact/testbench/connections.go
index b7aa63934..8d1f562ee 100644
--- a/test/packetimpact/testbench/connections.go
+++ b/test/packetimpact/testbench/connections.go
@@ -36,19 +36,6 @@ var remoteIPv4 = flag.String("remote_ipv4", "", "remote IPv4 address for test pa
var localMAC = flag.String("local_mac", "", "local mac address for test packets")
var remoteMAC = flag.String("remote_mac", "", "remote mac address for test packets")
-// TCPIPv4 maintains state about a TCP/IPv4 connection.
-type TCPIPv4 struct {
- outgoing Layers
- incoming Layers
- LocalSeqNum seqnum.Value
- RemoteSeqNum seqnum.Value
- SynAck *TCP
- sniffer Sniffer
- injector Injector
- portPickerFD int
- t *testing.T
-}
-
// pickPort makes a new socket and returns the socket FD and port. The caller
// must close the FD when done with the port if there is no error.
func pickPort() (int, uint16, error) {
@@ -75,12 +62,25 @@ func pickPort() (int, uint16, error) {
return fd, uint16(newSockAddrInet4.Port), nil
}
+// TCPIPv4 maintains state about a TCP/IPv4 connection.
+type TCPIPv4 struct {
+ outgoing Layers
+ incoming Layers
+ LocalSeqNum seqnum.Value
+ RemoteSeqNum seqnum.Value
+ SynAck *TCP
+ sniffer Sniffer
+ injector Injector
+ portPickerFD int
+ t *testing.T
+}
+
// tcpLayerIndex is the position of the TCP layer in the TCPIPv4 connection. It
// is the third, after Ethernet and IPv4.
const tcpLayerIndex int = 2
// NewTCPIPv4 creates a new TCPIPv4 connection with reasonable defaults.
-func NewTCPIPv4(t *testing.T, dut DUT, outgoingTCP, incomingTCP TCP) TCPIPv4 {
+func NewTCPIPv4(t *testing.T, outgoingTCP, incomingTCP TCP) TCPIPv4 {
lMAC, err := tcpip.ParseMACAddress(*localMAC)
if err != nil {
t.Fatalf("can't parse localMAC %q: %s", *localMAC, err)
@@ -109,18 +109,16 @@ func NewTCPIPv4(t *testing.T, dut DUT, outgoingTCP, incomingTCP TCP) TCPIPv4 {
}
newOutgoingTCP := &TCP{
- DataOffset: Uint8(header.TCPMinimumSize),
- WindowSize: Uint16(32768),
- SrcPort: &localPort,
+ SrcPort: &localPort,
}
if err := newOutgoingTCP.merge(outgoingTCP); err != nil {
- t.Fatalf("can't merge %v into %v: %s", outgoingTCP, newOutgoingTCP, err)
+ t.Fatalf("can't merge %+v into %+v: %s", outgoingTCP, newOutgoingTCP, err)
}
newIncomingTCP := &TCP{
DstPort: &localPort,
}
if err := newIncomingTCP.merge(incomingTCP); err != nil {
- t.Fatalf("can't merge %v into %v: %s", incomingTCP, newIncomingTCP, err)
+ t.Fatalf("can't merge %+v into %+v: %s", incomingTCP, newIncomingTCP, err)
}
return TCPIPv4{
outgoing: Layers{
@@ -149,8 +147,9 @@ func (conn *TCPIPv4) Close() {
conn.portPickerFD = -1
}
-// Send a packet with reasonable defaults and override some fields by tcp.
-func (conn *TCPIPv4) Send(tcp TCP, additionalLayers ...Layer) {
+// CreateFrame builds a frame for the connection with tcp overriding defaults
+// and additionalLayers added after the TCP header.
+func (conn *TCPIPv4) CreateFrame(tcp TCP, additionalLayers ...Layer) Layers {
if tcp.SeqNum == nil {
tcp.SeqNum = Uint32(uint32(conn.LocalSeqNum))
}
@@ -159,30 +158,41 @@ func (conn *TCPIPv4) Send(tcp TCP, additionalLayers ...Layer) {
}
layersToSend := deepcopy.Copy(conn.outgoing).(Layers)
if err := layersToSend[tcpLayerIndex].(*TCP).merge(tcp); err != nil {
- conn.t.Fatalf("can't merge %v into %v: %s", tcp, layersToSend[tcpLayerIndex], err)
+ conn.t.Fatalf("can't merge %+v into %+v: %s", tcp, layersToSend[tcpLayerIndex], err)
}
layersToSend = append(layersToSend, additionalLayers...)
- outBytes, err := layersToSend.toBytes()
+ return layersToSend
+}
+
+// SendFrame sends a frame with reasonable defaults.
+func (conn *TCPIPv4) SendFrame(frame Layers) {
+ outBytes, err := frame.toBytes()
if err != nil {
conn.t.Fatalf("can't build outgoing TCP packet: %s", err)
}
conn.injector.Send(outBytes)
// Compute the next TCP sequence number.
- for i := tcpLayerIndex + 1; i < len(layersToSend); i++ {
- conn.LocalSeqNum.UpdateForward(seqnum.Size(layersToSend[i].length()))
+ for i := tcpLayerIndex + 1; i < len(frame); i++ {
+ conn.LocalSeqNum.UpdateForward(seqnum.Size(frame[i].length()))
}
+ tcp := frame[tcpLayerIndex].(*TCP)
if tcp.Flags != nil && *tcp.Flags&(header.TCPFlagSyn|header.TCPFlagFin) != 0 {
conn.LocalSeqNum.UpdateForward(1)
}
}
+// Send a packet with reasonable defaults and override some fields by tcp.
+func (conn *TCPIPv4) Send(tcp TCP, additionalLayers ...Layer) {
+ conn.SendFrame(conn.CreateFrame(tcp, additionalLayers...))
+}
+
// Recv gets a packet from the sniffer within the timeout provided. If no packet
// arrives before the timeout, it returns nil.
func (conn *TCPIPv4) Recv(timeout time.Duration) *TCP {
deadline := time.Now().Add(timeout)
for {
- timeout = deadline.Sub(time.Now())
+ timeout = time.Until(deadline)
if timeout <= 0 {
break
}
@@ -192,6 +202,7 @@ func (conn *TCPIPv4) Recv(timeout time.Duration) *TCP {
}
layers, err := ParseEther(b)
if err != nil {
+ conn.t.Logf("can't parse frame: %s", err)
continue // Ignore packets that can't be parsed.
}
if !conn.incoming.match(layers) {
@@ -215,7 +226,7 @@ func (conn *TCPIPv4) Recv(timeout time.Duration) *TCP {
func (conn *TCPIPv4) Expect(tcp TCP, timeout time.Duration) *TCP {
deadline := time.Now().Add(timeout)
for {
- timeout = deadline.Sub(time.Now())
+ timeout = time.Until(deadline)
if timeout <= 0 {
return nil
}
@@ -243,3 +254,154 @@ func (conn *TCPIPv4) Handshake() {
// Send an ACK.
conn.Send(TCP{Flags: Uint8(header.TCPFlagAck)})
}
+
+// UDPIPv4 maintains state about a UDP/IPv4 connection.
+type UDPIPv4 struct {
+ outgoing Layers
+ incoming Layers
+ sniffer Sniffer
+ injector Injector
+ portPickerFD int
+ t *testing.T
+}
+
+// udpLayerIndex is the position of the UDP layer in the UDPIPv4 connection. It
+// is the third, after Ethernet and IPv4.
+const udpLayerIndex int = 2
+
+// NewUDPIPv4 creates a new UDPIPv4 connection with reasonable defaults.
+func NewUDPIPv4(t *testing.T, outgoingUDP, incomingUDP UDP) UDPIPv4 {
+ lMAC, err := tcpip.ParseMACAddress(*localMAC)
+ if err != nil {
+ t.Fatalf("can't parse localMAC %q: %s", *localMAC, err)
+ }
+
+ rMAC, err := tcpip.ParseMACAddress(*remoteMAC)
+ if err != nil {
+ t.Fatalf("can't parse remoteMAC %q: %s", *remoteMAC, err)
+ }
+
+ portPickerFD, localPort, err := pickPort()
+ if err != nil {
+ t.Fatalf("can't pick a port: %s", err)
+ }
+ lIP := tcpip.Address(net.ParseIP(*localIPv4).To4())
+ rIP := tcpip.Address(net.ParseIP(*remoteIPv4).To4())
+
+ sniffer, err := NewSniffer(t)
+ if err != nil {
+ t.Fatalf("can't make new sniffer: %s", err)
+ }
+
+ injector, err := NewInjector(t)
+ if err != nil {
+ t.Fatalf("can't make new injector: %s", err)
+ }
+
+ newOutgoingUDP := &UDP{
+ SrcPort: &localPort,
+ }
+ if err := newOutgoingUDP.merge(outgoingUDP); err != nil {
+ t.Fatalf("can't merge %+v into %+v: %s", outgoingUDP, newOutgoingUDP, err)
+ }
+ newIncomingUDP := &UDP{
+ DstPort: &localPort,
+ }
+ if err := newIncomingUDP.merge(incomingUDP); err != nil {
+ t.Fatalf("can't merge %+v into %+v: %s", incomingUDP, newIncomingUDP, err)
+ }
+ return UDPIPv4{
+ outgoing: Layers{
+ &Ether{SrcAddr: &lMAC, DstAddr: &rMAC},
+ &IPv4{SrcAddr: &lIP, DstAddr: &rIP},
+ newOutgoingUDP},
+ incoming: Layers{
+ &Ether{SrcAddr: &rMAC, DstAddr: &lMAC},
+ &IPv4{SrcAddr: &rIP, DstAddr: &lIP},
+ newIncomingUDP},
+ sniffer: sniffer,
+ injector: injector,
+ portPickerFD: portPickerFD,
+ t: t,
+ }
+}
+
+// Close the injector and sniffer associated with this connection.
+func (conn *UDPIPv4) Close() {
+ conn.sniffer.Close()
+ conn.injector.Close()
+ if err := unix.Close(conn.portPickerFD); err != nil {
+ conn.t.Fatalf("can't close portPickerFD: %s", err)
+ }
+ conn.portPickerFD = -1
+}
+
+// CreateFrame builds a frame for the connection with the provided udp
+// overriding defaults and the additionalLayers added after the UDP header.
+func (conn *UDPIPv4) CreateFrame(udp UDP, additionalLayers ...Layer) Layers {
+ layersToSend := deepcopy.Copy(conn.outgoing).(Layers)
+ if err := layersToSend[udpLayerIndex].(*UDP).merge(udp); err != nil {
+ conn.t.Fatalf("can't merge %+v into %+v: %s", udp, layersToSend[udpLayerIndex], err)
+ }
+ layersToSend = append(layersToSend, additionalLayers...)
+ return layersToSend
+}
+
+// SendFrame sends a frame with reasonable defaults.
+func (conn *UDPIPv4) SendFrame(frame Layers) {
+ outBytes, err := frame.toBytes()
+ if err != nil {
+ conn.t.Fatalf("can't build outgoing UDP packet: %s", err)
+ }
+ conn.injector.Send(outBytes)
+}
+
+// Send a packet with reasonable defaults and override some fields by udp.
+func (conn *UDPIPv4) Send(udp UDP, additionalLayers ...Layer) {
+ conn.SendFrame(conn.CreateFrame(udp, additionalLayers...))
+}
+
+// Recv gets a packet from the sniffer within the timeout provided. If no packet
+// arrives before the timeout, it returns nil.
+func (conn *UDPIPv4) Recv(timeout time.Duration) *UDP {
+ deadline := time.Now().Add(timeout)
+ for {
+ timeout = time.Until(deadline)
+ if timeout <= 0 {
+ break
+ }
+ b := conn.sniffer.Recv(timeout)
+ if b == nil {
+ break
+ }
+ layers, err := ParseEther(b)
+ if err != nil {
+ conn.t.Logf("can't parse frame: %s", err)
+ continue // Ignore packets that can't be parsed.
+ }
+ if !conn.incoming.match(layers) {
+ continue // Ignore packets that don't match the expected incoming.
+ }
+ return (layers[udpLayerIndex]).(*UDP)
+ }
+ return nil
+}
+
+// Expect a packet that matches the provided udp within the timeout specified.
+// If it doesn't arrive in time, the test fails.
+func (conn *UDPIPv4) Expect(udp UDP, timeout time.Duration) *UDP {
+ deadline := time.Now().Add(timeout)
+ for {
+ timeout = time.Until(deadline)
+ if timeout <= 0 {
+ return nil
+ }
+ gotUDP := conn.Recv(timeout)
+ if gotUDP == nil {
+ return nil
+ }
+ if udp.match(gotUDP) {
+ return gotUDP
+ }
+ }
+}