diff options
author | Eyal Soha <eyalsoha@google.com> | 2020-03-31 19:51:52 -0700 |
---|---|---|
committer | gVisor bot <gvisor-bot@google.com> | 2020-03-31 19:53:11 -0700 |
commit | d25036ad17a3ada7fa6ce9900f20e246e07acd2f (patch) | |
tree | f7fa1ea3c6023cdaefd4bd08388a5ea72bce992a /test/packetimpact/testbench/connections.go | |
parent | 639d94f9f71b43e86320a6e9157c932f5d7936a7 (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.go | 216 |
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 + } + } +} |