// Copyright 2020 The gVisor Authors. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package testbench import ( "fmt" "math/rand" "testing" "time" "github.com/mohae/deepcopy" "go.uber.org/multierr" "golang.org/x/sys/unix" "gvisor.dev/gvisor/pkg/tcpip" "gvisor.dev/gvisor/pkg/tcpip/header" "gvisor.dev/gvisor/pkg/tcpip/seqnum" ) func portFromSockaddr(sa unix.Sockaddr) (uint16, error) { switch sa := sa.(type) { case *unix.SockaddrInet4: return uint16(sa.Port), nil case *unix.SockaddrInet6: return uint16(sa.Port), nil } return 0, fmt.Errorf("sockaddr type %T does not contain port", sa) } // pickPort makes a new socket and returns the socket FD and port. The domain // should be AF_INET or AF_INET6. The caller must close the FD when done with // the port if there is no error. func (n *DUTTestNet) pickPort(domain, typ int) (fd int, port uint16, err error) { fd, err = unix.Socket(domain, typ, 0) if err != nil { return -1, 0, fmt.Errorf("creating socket: %w", err) } defer func() { if err != nil { if cerr := unix.Close(fd); cerr != nil { err = multierr.Append(err, fmt.Errorf("failed to close socket %d: %w", fd, cerr)) } } }() var sa unix.Sockaddr switch domain { case unix.AF_INET: var sa4 unix.SockaddrInet4 copy(sa4.Addr[:], n.LocalIPv4) sa = &sa4 case unix.AF_INET6: sa6 := unix.SockaddrInet6{ZoneId: n.LocalDevID} copy(sa6.Addr[:], n.LocalIPv6) sa = &sa6 default: return -1, 0, fmt.Errorf("invalid domain %d, it should be one of unix.AF_INET or unix.AF_INET6", domain) } if err = unix.Bind(fd, sa); err != nil { return -1, 0, fmt.Errorf("binding to %+v: %w", sa, err) } sa, err = unix.Getsockname(fd) if err != nil { return -1, 0, fmt.Errorf("unix.Getsocketname(%d): %w", fd, err) } port, err = portFromSockaddr(sa) if err != nil { return -1, 0, fmt.Errorf("extracting port from socket address %+v: %w", sa, err) } return fd, port, nil } // layerState stores the state of a layer of a connection. type layerState interface { // outgoing returns an outgoing layer to be sent in a frame. It should not // update layerState, that is done in layerState.sent. outgoing() Layer // incoming creates an expected Layer for comparing against a received Layer. // Because the expectation can depend on values in the received Layer, it is // an input to incoming. For example, the ACK number needs to be checked in a // TCP packet but only if the ACK flag is set in the received packet. It // should not update layerState, that is done in layerState.received. The // caller takes ownership of the returned Layer. incoming(received Layer) Layer // sent updates the layerState based on the Layer that was sent. The input is // a Layer with all prev and next pointers populated so that the entire frame // as it was sent is available. sent(sent Layer) error // received updates the layerState based on a Layer that is received. The // input is a Layer with all prev and next pointers populated so that the // entire frame as it was received is available. received(received Layer) error // close frees associated resources held by the LayerState. close() error } // etherState maintains state about an Ethernet connection. type etherState struct { out, in Ether } var _ layerState = (*etherState)(nil) // newEtherState creates a new etherState. func (n *DUTTestNet) newEtherState(out, in Ether) (*etherState, error) { lmac := tcpip.LinkAddress(n.LocalMAC) rmac := tcpip.LinkAddress(n.RemoteMAC) s := etherState{ out: Ether{SrcAddr: &lmac, DstAddr: &rmac}, in: Ether{SrcAddr: &rmac, DstAddr: &lmac}, } if err := s.out.merge(&out); err != nil { return nil, err } if err := s.in.merge(&in); err != nil { return nil, err } return &s, nil } func (s *etherState) outgoing() Layer { return deepcopy.Copy(&s.out).(Layer) } // incoming implements layerState.incoming. func (s *etherState) incoming(Layer) Layer { return deepcopy.Copy(&s.in).(Layer) } func (*etherState) sent(Layer) error { return nil } func (*etherState) received(Layer) error { return nil } func (*etherState) close() error { return nil } // ipv4State maintains state about an IPv4 connection. type ipv4State struct { out, in IPv4 } var _ layerState = (*ipv4State)(nil) // newIPv4State creates a new ipv4State. func (n *DUTTestNet) newIPv4State(out, in IPv4) (*ipv4State, error) { lIP := tcpip.Address(n.LocalIPv4) rIP := tcpip.Address(n.RemoteIPv4) s := ipv4State{ out: IPv4{SrcAddr: &lIP, DstAddr: &rIP}, in: IPv4{SrcAddr: &rIP, DstAddr: &lIP}, } if err := s.out.merge(&out); err != nil { return nil, err } if err := s.in.merge(&in); err != nil { return nil, err } return &s, nil } func (s *ipv4State) outgoing() Layer { return deepcopy.Copy(&s.out).(Layer) } // incoming implements layerState.incoming. func (s *ipv4State) incoming(Layer) Layer { return deepcopy.Copy(&s.in).(Layer) } func (*ipv4State) sent(Layer) error { return nil } func (*ipv4State) received(Layer) error { return nil } func (*ipv4State) close() error { return nil } // ipv6State maintains state about an IPv6 connection. type ipv6State struct { out, in IPv6 } var _ layerState = (*ipv6State)(nil) // newIPv6State creates a new ipv6State. func (n *DUTTestNet) newIPv6State(out, in IPv6) (*ipv6State, error) { lIP := tcpip.Address(n.LocalIPv6) rIP := tcpip.Address(n.RemoteIPv6) s := ipv6State{ out: IPv6{SrcAddr: &lIP, DstAddr: &rIP}, in: IPv6{SrcAddr: &rIP, DstAddr: &lIP}, } if err := s.out.merge(&out); err != nil { return nil, err } if err := s.in.merge(&in); err != nil { return nil, err } return &s, nil } // outgoing returns an outgoing layer to be sent in a frame. func (s *ipv6State) outgoing() Layer { return deepcopy.Copy(&s.out).(Layer) } func (s *ipv6State) incoming(Layer) Layer { return deepcopy.Copy(&s.in).(Layer) } func (s *ipv6State) sent(Layer) error { // Nothing to do. return nil } func (s *ipv6State) received(Layer) error { // Nothing to do. return nil } // close cleans up any resources held. func (s *ipv6State) close() error { return nil } // tcpState maintains state about a TCP connection. type tcpState struct { out, in TCP localSeqNum, remoteSeqNum *seqnum.Value synAck *TCP portPickerFD int finSent bool } var _ layerState = (*tcpState)(nil) // SeqNumValue is a helper routine that allocates a new seqnum.Value value to // store v and returns a pointer to it. func SeqNumValue(v seqnum.Value) *seqnum.Value { return &v } // newTCPState creates a new TCPState. func (n *DUTTestNet) newTCPState(domain int, out, in TCP) (*tcpState, error) { portPickerFD, localPort, err := n.pickPort(domain, unix.SOCK_STREAM) if err != nil { return nil, err } s := tcpState{ out: TCP{SrcPort: &localPort}, in: TCP{DstPort: &localPort}, localSeqNum: SeqNumValue(seqnum.Value(rand.Uint32())), portPickerFD: portPickerFD, finSent: false, } if err := s.out.merge(&out); err != nil { return nil, err } if err := s.in.merge(&in); err != nil { return nil, err } return &s, nil } func (s *tcpState) outgoing() Layer { newOutgoing := deepcopy.Copy(s.out).(TCP) if s.localSeqNum != nil { newOutgoing.SeqNum = Uint32(uint32(*s.localSeqNum)) } if s.remoteSeqNum != nil { newOutgoing.AckNum = Uint32(uint32(*s.remoteSeqNum)) } return &newOutgoing } // incoming implements layerState.incoming. func (s *tcpState) incoming(received Layer) Layer { tcpReceived, ok := received.(*TCP) if !ok { return nil } newIn := deepcopy.Copy(s.in).(TCP) if s.remoteSeqNum != nil { newIn.SeqNum = Uint32(uint32(*s.remoteSeqNum)) } if seq, flags := s.localSeqNum, tcpReceived.Flags; seq != nil && flags != nil && *flags&header.TCPFlagAck != 0 { // The caller didn't specify an AckNum so we'll expect the calculated one, // but only if the ACK flag is set because the AckNum is not valid in a // header if ACK is not set. newIn.AckNum = Uint32(uint32(*seq)) } return &newIn } func (s *tcpState) sent(sent Layer) error { tcp, ok := sent.(*TCP) if !ok { return fmt.Errorf("can't update tcpState with %T Layer", sent) } if !s.finSent { // update localSeqNum by the payload only when FIN is not yet sent by us for current := tcp.next(); current != nil; current = current.next() { s.localSeqNum.UpdateForward(seqnum.Size(current.length())) } } if tcp.Flags != nil && *tcp.Flags&(header.TCPFlagSyn|header.TCPFlagFin) != 0 { s.localSeqNum.UpdateForward(1) } if *tcp.Flags&(header.TCPFlagFin) != 0 { s.finSent = true } return nil } func (s *tcpState) received(l Layer) error { tcp, ok := l.(*TCP) if !ok { return fmt.Errorf("can't update tcpState with %T Layer", l) } s.remoteSeqNum = SeqNumValue(seqnum.Value(*tcp.SeqNum)) if *tcp.Flags&(header.TCPFlagSyn|header.TCPFlagFin) != 0 { s.remoteSeqNum.UpdateForward(1) } for current := tcp.next(); current != nil; current = current.next() { s.remoteSeqNum.UpdateForward(seqnum.Size(current.length())) } return nil } // close frees the port associated with this connection. func (s *tcpState) close() error { if err := unix.Close(s.portPickerFD); err != nil { return err } s.portPickerFD = -1 return nil } // udpState maintains state about a UDP connection. type udpState struct { out, in UDP portPickerFD int } var _ layerState = (*udpState)(nil) // newUDPState creates a new udpState. func (n *DUTTestNet) newUDPState(domain int, out, in UDP) (*udpState, error) { portPickerFD, localPort, err := n.pickPort(domain, unix.SOCK_DGRAM) if err != nil { return nil, fmt.Errorf("picking port: %w", err) } s := udpState{ out: UDP{SrcPort: &localPort}, in: UDP{DstPort: &localPort}, portPickerFD: portPickerFD, } if err := s.out.merge(&out); err != nil { return nil, err } if err := s.in.merge(&in); err != nil { return nil, err } return &s, nil } func (s *udpState) outgoing() Layer { return deepcopy.Copy(&s.out).(Layer) } // incoming implements layerState.incoming. func (s *udpState) incoming(Layer) Layer { return deepcopy.Copy(&s.in).(Layer) } func (*udpState) sent(l Layer) error { return nil } func (*udpState) received(l Layer) error { return nil } // close frees the port associated with this connection. func (s *udpState) close() error { if err := unix.Close(s.portPickerFD); err != nil { return err } s.portPickerFD = -1 return nil } // Connection holds a collection of layer states for maintaining a connection // along with sockets for sniffer and injecting packets. type Connection struct { layerStates []layerState injector Injector sniffer Sniffer } // Returns the default incoming frame against which to match. If received is // longer than layerStates then that may still count as a match. The reverse is // never a match and nil is returned. func (conn *Connection) incoming(received Layers) Layers { if len(received) < len(conn.layerStates) { return nil } in := Layers{} for i, s := range conn.layerStates { toMatch := s.incoming(received[i]) if toMatch == nil { return nil } in = append(in, toMatch) } return in } func (conn *Connection) match(override, received Layers) bool { toMatch := conn.incoming(received) if toMatch == nil { return false // Not enough layers in gotLayers for matching. } if err := toMatch.merge(override); err != nil { return false // Failing to merge is not matching. } return toMatch.match(received) } // Close frees associated resources held by the Connection. func (conn *Connection) Close(t *testing.T) { t.Helper() errs := multierr.Combine(conn.sniffer.close(), conn.injector.close()) for _, s := range conn.layerStates { if err := s.close(); err != nil { errs = multierr.Append(errs, fmt.Errorf("unable to close %+v: %s", s, err)) } } if errs != nil { t.Fatalf("unable to close %+v: %s", conn, errs) } } // CreateFrame builds a frame for the connection with defaults overridden // from the innermost layer out, and additionalLayers added after it. // // Note that overrideLayers can have a length that is less than the number // of layers in this connection, and in such cases the innermost layers are // overridden first. As an example, valid values of overrideLayers for a TCP- // over-IPv4-over-Ethernet connection are: nil, [TCP], [IPv4, TCP], and // [Ethernet, IPv4, TCP]. func (conn *Connection) CreateFrame(t *testing.T, overrideLayers Layers, additionalLayers ...Layer) Layers { t.Helper() var layersToSend Layers for i, s := range conn.layerStates { layer := s.outgoing() // overrideLayers and conn.layerStates have their tails aligned, so // to find the index we move backwards by the distance i is to the // end. if j := len(overrideLayers) - (len(conn.layerStates) - i); j >= 0 { if err := layer.merge(overrideLayers[j]); err != nil { t.Fatalf("can't merge %+v into %+v: %s", layer, overrideLayers[j], err) } } layersToSend = append(layersToSend, layer) } layersToSend = append(layersToSend, additionalLayers...) return layersToSend } // SendFrameStateless sends a frame without updating any of the layer states. // // This method is useful for sending out-of-band control messages such as // ICMP packets, where it would not make sense to update the transport layer's // state using the ICMP header. func (conn *Connection) SendFrameStateless(t *testing.T, frame Layers) { t.Helper() outBytes, err := frame.ToBytes() if err != nil { t.Fatalf("can't build outgoing packet: %s", err) } conn.injector.Send(t, outBytes) } // SendFrame sends a frame on the wire and updates the state of all layers. func (conn *Connection) SendFrame(t *testing.T, frame Layers) { t.Helper() outBytes, err := frame.ToBytes() if err != nil { t.Fatalf("can't build outgoing packet: %s", err) } conn.injector.Send(t, outBytes) // frame might have nil values where the caller wanted to use default values. // sentFrame will have no nil values in it because it comes from parsing the // bytes that were actually sent. sentFrame := parse(parseEther, outBytes) // Update the state of each layer based on what was sent. for i, s := range conn.layerStates { if err := s.sent(sentFrame[i]); err != nil { t.Fatalf("Unable to update the state of %+v with %s: %s", s, sentFrame[i], err) } } } // send sends a packet, possibly with layers of this connection overridden and // additional layers added. // // Types defined with Connection as the underlying type should expose // type-safe versions of this method. func (conn *Connection) send(t *testing.T, overrideLayers Layers, additionalLayers ...Layer) { t.Helper() conn.SendFrame(t, conn.CreateFrame(t, overrideLayers, additionalLayers...)) } // recvFrame gets the next successfully parsed frame (of type Layers) within the // timeout provided. If no parsable frame arrives before the timeout, it returns // nil. func (conn *Connection) recvFrame(t *testing.T, timeout time.Duration) Layers { t.Helper() if timeout <= 0 { return nil } b := conn.sniffer.Recv(t, timeout) if b == nil { return nil } return parse(parseEther, b) } // layersError stores the Layers that we got and the Layers that we wanted to // match. type layersError struct { got, want Layers } func (e *layersError) Error() string { return e.got.diff(e.want) } // Expect expects a frame with the final layerStates layer matching the // provided Layer within the timeout specified. If it doesn't arrive in time, // an error is returned. func (conn *Connection) Expect(t *testing.T, layer Layer, timeout time.Duration) (Layer, error) { t.Helper() // Make a frame that will ignore all but the final layer. layers := make([]Layer, len(conn.layerStates)) layers[len(layers)-1] = layer gotFrame, err := conn.ExpectFrame(t, layers, timeout) if err != nil { return nil, err } if len(conn.layerStates)-1 < len(gotFrame) { return gotFrame[len(conn.layerStates)-1], nil } t.Fatalf("the received frame should be at least as long as the expected layers, got %d layers, want at least %d layers, got frame: %#v", len(gotFrame), len(conn.layerStates), gotFrame) panic("unreachable") } // ExpectFrame expects a frame that matches the provided Layers within the // timeout specified. If one arrives in time, the Layers is returned without an // error. If it doesn't arrive in time, it returns nil and error is non-nil. func (conn *Connection) ExpectFrame(t *testing.T, layers Layers, timeout time.Duration) (Layers, error) { t.Helper() deadline := time.Now().Add(timeout) var errs error for { var gotLayers Layers if timeout := time.Until(deadline); timeout > 0 { gotLayers = conn.recvFrame(t, timeout) } if gotLayers == nil { if errs == nil { return nil, fmt.Errorf("got no frames matching %s during %s", layers, timeout) } return nil, fmt.Errorf("got frames:\n%w want %s during %s", errs, layers, timeout) } if conn.match(layers, gotLayers) { for i, s := range conn.layerStates { if err := s.received(gotLayers[i]); err != nil { t.Fatalf("failed to update test connection's layer states based on received frame: %s", err) } } return gotLayers, nil } want := conn.incoming(layers) if err := want.merge(layers); err != nil { errs = multierr.Combine(errs, err) } else { errs = multierr.Combine(errs, &layersError{got: gotLayers, want: want}) } } } // Drain drains the sniffer's receive buffer by receiving packets until there's // nothing else to receive. func (conn *Connection) Drain(t *testing.T) { t.Helper() conn.sniffer.Drain(t) } // TCPIPv4 maintains the state for all the layers in a TCP/IPv4 connection. type TCPIPv4 Connection // NewTCPIPv4 creates a new TCPIPv4 connection with reasonable defaults. func (n *DUTTestNet) NewTCPIPv4(t *testing.T, outgoingTCP, incomingTCP TCP) TCPIPv4 { t.Helper() etherState, err := n.newEtherState(Ether{}, Ether{}) if err != nil { t.Fatalf("can't make etherState: %s", err) } ipv4State, err := n.newIPv4State(IPv4{}, IPv4{}) if err != nil { t.Fatalf("can't make ipv4State: %s", err) } tcpState, err := n.newTCPState(unix.AF_INET, outgoingTCP, incomingTCP) if err != nil { t.Fatalf("can't make tcpState: %s", err) } injector, err := n.NewInjector(t) if err != nil { t.Fatalf("can't make injector: %s", err) } sniffer, err := n.NewSniffer(t) if err != nil { t.Fatalf("can't make sniffer: %s", err) } return TCPIPv4{ layerStates: []layerState{etherState, ipv4State, tcpState}, injector: injector, sniffer: sniffer, } } // Connect performs a TCP 3-way handshake. The input Connection should have a // final TCP Layer. func (conn *TCPIPv4) Connect(t *testing.T) { t.Helper() // Send the SYN. conn.Send(t, TCP{Flags: Uint8(header.TCPFlagSyn)}) // Wait for the SYN-ACK. synAck, err := conn.Expect(t, TCP{Flags: Uint8(header.TCPFlagSyn | header.TCPFlagAck)}, time.Second) if err != nil { t.Fatalf("didn't get synack during handshake: %s", err) } conn.layerStates[len(conn.layerStates)-1].(*tcpState).synAck = synAck // Send an ACK. conn.Send(t, TCP{Flags: Uint8(header.TCPFlagAck)}) } // ConnectWithOptions performs a TCP 3-way handshake with given TCP options. // The input Connection should have a final TCP Layer. func (conn *TCPIPv4) ConnectWithOptions(t *testing.T, options []byte) { t.Helper() // Send the SYN. conn.Send(t, TCP{Flags: Uint8(header.TCPFlagSyn), Options: options}) // Wait for the SYN-ACK. synAck, err := conn.Expect(t, TCP{Flags: Uint8(header.TCPFlagSyn | header.TCPFlagAck)}, time.Second) if err != nil { t.Fatalf("didn't get synack during handshake: %s", err) } conn.layerStates[len(conn.layerStates)-1].(*tcpState).synAck = synAck // Send an ACK. conn.Send(t, TCP{Flags: Uint8(header.TCPFlagAck)}) } // ExpectData is a convenient method that expects a Layer and the Layer after // it. If it doesn't arrive in time, it returns nil. func (conn *TCPIPv4) ExpectData(t *testing.T, tcp *TCP, payload *Payload, timeout time.Duration) (Layers, error) { t.Helper() expected := make([]Layer, len(conn.layerStates)) expected[len(expected)-1] = tcp if payload != nil { expected = append(expected, payload) } return (*Connection)(conn).ExpectFrame(t, expected, timeout) } // ExpectNextData attempts to receive the next incoming segment for the // connection and expects that to match the given layers. // // It differs from ExpectData() in that here we are only interested in the next // received segment, while ExpectData() can receive multiple segments for the // connection until there is a match with given layers or a timeout. func (conn *TCPIPv4) ExpectNextData(t *testing.T, tcp *TCP, payload *Payload, timeout time.Duration) (Layers, error) { t.Helper() // Receive the first incoming TCP segment for this connection. got, err := conn.ExpectData(t, &TCP{}, nil, timeout) if err != nil { return nil, err } expected := make([]Layer, len(conn.layerStates)) expected[len(expected)-1] = tcp if payload != nil { expected = append(expected, payload) tcp.SeqNum = Uint32(uint32(*conn.RemoteSeqNum(t)) - uint32(payload.Length())) } if !(*Connection)(conn).match(expected, got) { return nil, fmt.Errorf("next frame is not matching %s during %s: got %s", expected, timeout, got) } return got, nil } // Send a packet with reasonable defaults. Potentially override the TCP layer in // the connection with the provided layer and add additionLayers. func (conn *TCPIPv4) Send(t *testing.T, tcp TCP, additionalLayers ...Layer) { t.Helper() (*Connection)(conn).send(t, Layers{&tcp}, additionalLayers...) } // Close frees associated resources held by the TCPIPv4 connection. func (conn *TCPIPv4) Close(t *testing.T) { t.Helper() (*Connection)(conn).Close(t) } // Expect expects a frame with the TCP layer matching the provided TCP within // the timeout specified. If it doesn't arrive in time, an error is returned. func (conn *TCPIPv4) Expect(t *testing.T, tcp TCP, timeout time.Duration) (*TCP, error) { t.Helper() layer, err := (*Connection)(conn).Expect(t, &tcp, timeout) if layer == nil { return nil, err } gotTCP, ok := layer.(*TCP) if !ok { t.Fatalf("expected %s to be TCP", layer) } return gotTCP, err } func (conn *TCPIPv4) tcpState(t *testing.T) *tcpState { t.Helper() state, ok := conn.layerStates[2].(*tcpState) if !ok { t.Fatalf("got transport-layer state type=%T, expected tcpState", conn.layerStates[2]) } return state } func (conn *TCPIPv4) ipv4State(t *testing.T) *ipv4State { t.Helper() state, ok := conn.layerStates[1].(*ipv4State) if !ok { t.Fatalf("expected network-layer state type=%T, expected ipv4State", conn.layerStates[1]) } return state } // RemoteSeqNum returns the next expected sequence number from the DUT. func (conn *TCPIPv4) RemoteSeqNum(t *testing.T) *seqnum.Value { t.Helper() return conn.tcpState(t).remoteSeqNum } // LocalSeqNum returns the next sequence number to send from the testbench. func (conn *TCPIPv4) LocalSeqNum(t *testing.T) *seqnum.Value { t.Helper() return conn.tcpState(t).localSeqNum } // SynAck returns the SynAck that was part of the handshake. func (conn *TCPIPv4) SynAck(t *testing.T) *TCP { t.Helper() return conn.tcpState(t).synAck } // LocalAddr gets the local socket address of this connection. func (conn *TCPIPv4) LocalAddr(t *testing.T) *unix.SockaddrInet4 { t.Helper() sa := &unix.SockaddrInet4{Port: int(*conn.tcpState(t).out.SrcPort)} copy(sa.Addr[:], *conn.ipv4State(t).out.SrcAddr) return sa } // Drain drains the sniffer's receive buffer by receiving packets until there's // nothing else to receive. func (conn *TCPIPv4) Drain(t *testing.T) { t.Helper() conn.sniffer.Drain(t) } // IPv4Conn maintains the state for all the layers in a IPv4 connection. type IPv4Conn Connection // NewIPv4Conn creates a new IPv4Conn connection with reasonable defaults. func (n *DUTTestNet) NewIPv4Conn(t *testing.T, outgoingIPv4, incomingIPv4 IPv4) IPv4Conn { t.Helper() etherState, err := n.newEtherState(Ether{}, Ether{}) if err != nil { t.Fatalf("can't make EtherState: %s", err) } ipv4State, err := n.newIPv4State(outgoingIPv4, incomingIPv4) if err != nil { t.Fatalf("can't make IPv4State: %s", err) } injector, err := n.NewInjector(t) if err != nil { t.Fatalf("can't make injector: %s", err) } sniffer, err := n.NewSniffer(t) if err != nil { t.Fatalf("can't make sniffer: %s", err) } return IPv4Conn{ layerStates: []layerState{etherState, ipv4State}, injector: injector, sniffer: sniffer, } } // Send sends a frame with ipv4 overriding the IPv4 layer defaults and // additionalLayers added after it. func (c *IPv4Conn) Send(t *testing.T, ipv4 IPv4, additionalLayers ...Layer) { t.Helper() (*Connection)(c).send(t, Layers{&ipv4}, additionalLayers...) } // Close cleans up any resources held. func (c *IPv4Conn) Close(t *testing.T) { t.Helper() (*Connection)(c).Close(t) } // ExpectFrame expects a frame that matches the provided Layers within the // timeout specified. If it doesn't arrive in time, an error is returned. func (c *IPv4Conn) ExpectFrame(t *testing.T, frame Layers, timeout time.Duration) (Layers, error) { t.Helper() return (*Connection)(c).ExpectFrame(t, frame, timeout) } // IPv6Conn maintains the state for all the layers in a IPv6 connection. type IPv6Conn Connection // NewIPv6Conn creates a new IPv6Conn connection with reasonable defaults. func (n *DUTTestNet) NewIPv6Conn(t *testing.T, outgoingIPv6, incomingIPv6 IPv6) IPv6Conn { t.Helper() etherState, err := n.newEtherState(Ether{}, Ether{}) if err != nil { t.Fatalf("can't make EtherState: %s", err) } ipv6State, err := n.newIPv6State(outgoingIPv6, incomingIPv6) if err != nil { t.Fatalf("can't make IPv6State: %s", err) } injector, err := n.NewInjector(t) if err != nil { t.Fatalf("can't make injector: %s", err) } sniffer, err := n.NewSniffer(t) if err != nil { t.Fatalf("can't make sniffer: %s", err) } return IPv6Conn{ layerStates: []layerState{etherState, ipv6State}, injector: injector, sniffer: sniffer, } } // Send sends a frame with ipv6 overriding the IPv6 layer defaults and // additionalLayers added after it. func (conn *IPv6Conn) Send(t *testing.T, ipv6 IPv6, additionalLayers ...Layer) { t.Helper() (*Connection)(conn).send(t, Layers{&ipv6}, additionalLayers...) } // Close to clean up any resources held. func (conn *IPv6Conn) Close(t *testing.T) { t.Helper() (*Connection)(conn).Close(t) } // ExpectFrame expects a frame that matches the provided Layers within the // timeout specified. If it doesn't arrive in time, an error is returned. func (conn *IPv6Conn) ExpectFrame(t *testing.T, frame Layers, timeout time.Duration) (Layers, error) { t.Helper() return (*Connection)(conn).ExpectFrame(t, frame, timeout) } // UDPIPv4 maintains the state for all the layers in a UDP/IPv4 connection. type UDPIPv4 Connection // NewUDPIPv4 creates a new UDPIPv4 connection with reasonable defaults. func (n *DUTTestNet) NewUDPIPv4(t *testing.T, outgoingUDP, incomingUDP UDP) UDPIPv4 { t.Helper() etherState, err := n.newEtherState(Ether{}, Ether{}) if err != nil { t.Fatalf("can't make etherState: %s", err) } ipv4State, err := n.newIPv4State(IPv4{}, IPv4{}) if err != nil { t.Fatalf("can't make ipv4State: %s", err) } udpState, err := n.newUDPState(unix.AF_INET, outgoingUDP, incomingUDP) if err != nil { t.Fatalf("can't make udpState: %s", err) } injector, err := n.NewInjector(t) if err != nil { t.Fatalf("can't make injector: %s", err) } sniffer, err := n.NewSniffer(t) if err != nil { t.Fatalf("can't make sniffer: %s", err) } return UDPIPv4{ layerStates: []layerState{etherState, ipv4State, udpState}, injector: injector, sniffer: sniffer, } } func (conn *UDPIPv4) udpState(t *testing.T) *udpState { t.Helper() state, ok := conn.layerStates[2].(*udpState) if !ok { t.Fatalf("got transport-layer state type=%T, expected udpState", conn.layerStates[2]) } return state } func (conn *UDPIPv4) ipv4State(t *testing.T) *ipv4State { t.Helper() state, ok := conn.layerStates[1].(*ipv4State) if !ok { t.Fatalf("got network-layer state type=%T, expected ipv4State", conn.layerStates[1]) } return state } // LocalAddr gets the local socket address of this connection. func (conn *UDPIPv4) LocalAddr(t *testing.T) *unix.SockaddrInet4 { t.Helper() sa := &unix.SockaddrInet4{Port: int(*conn.udpState(t).out.SrcPort)} copy(sa.Addr[:], *conn.ipv4State(t).out.SrcAddr) return sa } // SrcPort returns the source port of this connection. func (conn *UDPIPv4) SrcPort(t *testing.T) uint16 { t.Helper() return *conn.udpState(t).out.SrcPort } // Send sends a packet with reasonable defaults, potentially overriding the UDP // layer and adding additionLayers. func (conn *UDPIPv4) Send(t *testing.T, udp UDP, additionalLayers ...Layer) { t.Helper() (*Connection)(conn).send(t, Layers{&udp}, additionalLayers...) } // SendIP sends a packet with reasonable defaults, potentially overriding the // UDP and IPv4 headers and adding additionLayers. func (conn *UDPIPv4) SendIP(t *testing.T, ip IPv4, udp UDP, additionalLayers ...Layer) { t.Helper() (*Connection)(conn).send(t, Layers{&ip, &udp}, additionalLayers...) } // SendFrame sends a frame on the wire and updates the state of all layers. func (conn *UDPIPv4) SendFrame(t *testing.T, overrideLayers Layers, additionalLayers ...Layer) { (*Connection)(conn).send(t, overrideLayers, additionalLayers...) } // Expect expects a frame with the UDP layer matching the provided UDP within // the timeout specified. If it doesn't arrive in time, an error is returned. func (conn *UDPIPv4) Expect(t *testing.T, udp UDP, timeout time.Duration) (*UDP, error) { t.Helper() layer, err := (*Connection)(conn).Expect(t, &udp, timeout) if err != nil { return nil, err } gotUDP, ok := layer.(*UDP) if !ok { t.Fatalf("expected %s to be UDP", layer) } return gotUDP, nil } // ExpectData is a convenient method that expects a Layer and the Layer after // it. If it doesn't arrive in time, it returns nil. func (conn *UDPIPv4) ExpectData(t *testing.T, udp UDP, payload Payload, timeout time.Duration) (Layers, error) { t.Helper() expected := make([]Layer, len(conn.layerStates)) expected[len(expected)-1] = &udp if payload.length() != 0 { expected = append(expected, &payload) } return (*Connection)(conn).ExpectFrame(t, expected, timeout) } // ExpectFrame expects a frame that matches the provided Layers within the // timeout specified. If it doesn't arrive in time, an error is returned. func (conn *UDPIPv4) ExpectFrame(t *testing.T, frame Layers, timeout time.Duration) (Layers, error) { t.Helper() return (*Connection)(conn).ExpectFrame(t, frame, timeout) } // Close frees associated resources held by the UDPIPv4 connection. func (conn *UDPIPv4) Close(t *testing.T) { t.Helper() (*Connection)(conn).Close(t) } // Drain drains the sniffer's receive buffer by receiving packets until there's // nothing else to receive. func (conn *UDPIPv4) Drain(t *testing.T) { t.Helper() conn.sniffer.Drain(t) } // UDPIPv6 maintains the state for all the layers in a UDP/IPv6 connection. type UDPIPv6 Connection // NewUDPIPv6 creates a new UDPIPv6 connection with reasonable defaults. func (n *DUTTestNet) NewUDPIPv6(t *testing.T, outgoingUDP, incomingUDP UDP) UDPIPv6 { t.Helper() etherState, err := n.newEtherState(Ether{}, Ether{}) if err != nil { t.Fatalf("can't make etherState: %s", err) } ipv6State, err := n.newIPv6State(IPv6{}, IPv6{}) if err != nil { t.Fatalf("can't make IPv6State: %s", err) } udpState, err := n.newUDPState(unix.AF_INET6, outgoingUDP, incomingUDP) if err != nil { t.Fatalf("can't make udpState: %s", err) } injector, err := n.NewInjector(t) if err != nil { t.Fatalf("can't make injector: %s", err) } sniffer, err := n.NewSniffer(t) if err != nil { t.Fatalf("can't make sniffer: %s", err) } return UDPIPv6{ layerStates: []layerState{etherState, ipv6State, udpState}, injector: injector, sniffer: sniffer, } } func (conn *UDPIPv6) udpState(t *testing.T) *udpState { t.Helper() state, ok := conn.layerStates[2].(*udpState) if !ok { t.Fatalf("got transport-layer state type=%T, expected udpState", conn.layerStates[2]) } return state } func (conn *UDPIPv6) ipv6State(t *testing.T) *ipv6State { t.Helper() state, ok := conn.layerStates[1].(*ipv6State) if !ok { t.Fatalf("got network-layer state type=%T, expected ipv6State", conn.layerStates[1]) } return state } // LocalAddr gets the local socket address of this connection. func (conn *UDPIPv6) LocalAddr(t *testing.T, zoneID uint32) *unix.SockaddrInet6 { t.Helper() sa := &unix.SockaddrInet6{ Port: int(*conn.udpState(t).out.SrcPort), // Local address is in perspective to the remote host, so it's scoped to the // ID of the remote interface. ZoneId: zoneID, } copy(sa.Addr[:], *conn.ipv6State(t).out.SrcAddr) return sa } // SrcPort returns the source port of this connection. func (conn *UDPIPv6) SrcPort(t *testing.T) uint16 { t.Helper() return *conn.udpState(t).out.SrcPort } // Send sends a packet with reasonable defaults, potentially overriding the UDP // layer and adding additionLayers. func (conn *UDPIPv6) Send(t *testing.T, udp UDP, additionalLayers ...Layer) { t.Helper() (*Connection)(conn).send(t, Layers{&udp}, additionalLayers...) } // SendIPv6 sends a packet with reasonable defaults, potentially overriding the // UDP and IPv6 headers and adding additionLayers. func (conn *UDPIPv6) SendIPv6(t *testing.T, ip IPv6, udp UDP, additionalLayers ...Layer) { t.Helper() (*Connection)(conn).send(t, Layers{&ip, &udp}, additionalLayers...) } // SendFrame sends a frame on the wire and updates the state of all layers. func (conn *UDPIPv6) SendFrame(t *testing.T, overrideLayers Layers, additionalLayers ...Layer) { (*Connection)(conn).send(t, overrideLayers, additionalLayers...) } // Expect expects a frame with the UDP layer matching the provided UDP within // the timeout specified. If it doesn't arrive in time, an error is returned. func (conn *UDPIPv6) Expect(t *testing.T, udp UDP, timeout time.Duration) (*UDP, error) { t.Helper() layer, err := (*Connection)(conn).Expect(t, &udp, timeout) if err != nil { return nil, err } gotUDP, ok := layer.(*UDP) if !ok { t.Fatalf("expected %s to be UDP", layer) } return gotUDP, nil } // ExpectData is a convenient method that expects a Layer and the Layer after // it. If it doesn't arrive in time, it returns nil. func (conn *UDPIPv6) ExpectData(t *testing.T, udp UDP, payload Payload, timeout time.Duration) (Layers, error) { t.Helper() expected := make([]Layer, len(conn.layerStates)) expected[len(expected)-1] = &udp if payload.length() != 0 { expected = append(expected, &payload) } return (*Connection)(conn).ExpectFrame(t, expected, timeout) } // ExpectFrame expects a frame that matches the provided Layers within the // timeout specified. If it doesn't arrive in time, an error is returned. func (conn *UDPIPv6) ExpectFrame(t *testing.T, frame Layers, timeout time.Duration) (Layers, error) { t.Helper() return (*Connection)(conn).ExpectFrame(t, frame, timeout) } // Close frees associated resources held by the UDPIPv6 connection. func (conn *UDPIPv6) Close(t *testing.T) { t.Helper() (*Connection)(conn).Close(t) } // Drain drains the sniffer's receive buffer by receiving packets until there's // nothing else to receive. func (conn *UDPIPv6) Drain(t *testing.T) { t.Helper() conn.sniffer.Drain(t) } // TCPIPv6 maintains the state for all the layers in a TCP/IPv6 connection. type TCPIPv6 Connection // NewTCPIPv6 creates a new TCPIPv6 connection with reasonable defaults. func (n *DUTTestNet) NewTCPIPv6(t *testing.T, outgoingTCP, incomingTCP TCP) TCPIPv6 { etherState, err := n.newEtherState(Ether{}, Ether{}) if err != nil { t.Fatalf("can't make etherState: %s", err) } ipv6State, err := n.newIPv6State(IPv6{}, IPv6{}) if err != nil { t.Fatalf("can't make ipv6State: %s", err) } tcpState, err := n.newTCPState(unix.AF_INET6, outgoingTCP, incomingTCP) if err != nil { t.Fatalf("can't make tcpState: %s", err) } injector, err := n.NewInjector(t) if err != nil { t.Fatalf("can't make injector: %s", err) } sniffer, err := n.NewSniffer(t) if err != nil { t.Fatalf("can't make sniffer: %s", err) } return TCPIPv6{ layerStates: []layerState{etherState, ipv6State, tcpState}, injector: injector, sniffer: sniffer, } } // SrcPort returns the source port from the given Connection. func (conn *TCPIPv6) SrcPort() uint16 { state := conn.layerStates[2].(*tcpState) return *state.out.SrcPort } // ExpectData is a convenient method that expects a Layer and the Layer after // it. If it doesn't arrive in time, it returns nil. func (conn *TCPIPv6) ExpectData(t *testing.T, tcp *TCP, payload *Payload, timeout time.Duration) (Layers, error) { t.Helper() expected := make([]Layer, len(conn.layerStates)) expected[len(expected)-1] = tcp if payload != nil { expected = append(expected, payload) } return (*Connection)(conn).ExpectFrame(t, expected, timeout) } // Close frees associated resources held by the TCPIPv6 connection. func (conn *TCPIPv6) Close(t *testing.T) { t.Helper() (*Connection)(conn).Close(t) }