diff options
Diffstat (limited to 'test/packetimpact/testbench/connections.go')
-rw-r--r-- | test/packetimpact/testbench/connections.go | 270 |
1 files changed, 201 insertions, 69 deletions
diff --git a/test/packetimpact/testbench/connections.go b/test/packetimpact/testbench/connections.go index 952a717e0..2280bd4ee 100644 --- a/test/packetimpact/testbench/connections.go +++ b/test/packetimpact/testbench/connections.go @@ -21,7 +21,6 @@ import ( "fmt" "math/rand" "net" - "strings" "testing" "time" @@ -35,45 +34,74 @@ import ( var localIPv4 = flag.String("local_ipv4", "", "local IPv4 address for test packets") var remoteIPv4 = flag.String("remote_ipv4", "", "remote IPv4 address for test packets") +var localIPv6 = flag.String("local_ipv6", "", "local IPv6 address for test packets") +var remoteIPv6 = flag.String("remote_ipv6", "", "remote IPv6 address for test packets") var localMAC = flag.String("local_mac", "", "local mac address for test packets") var remoteMAC = flag.String("remote_mac", "", "remote mac address for test packets") -// 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) { - fd, err := unix.Socket(unix.AF_INET, unix.SOCK_STREAM, 0) +// 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 pickPort(domain, typ int) (fd int, port uint16, err error) { + fd, err = unix.Socket(domain, typ, 0) if err != nil { return -1, 0, err } - var sa unix.SockaddrInet4 - copy(sa.Addr[0:4], net.ParseIP(*localIPv4).To4()) - if err := unix.Bind(fd, &sa); err != nil { - unix.Close(fd) + defer func() { + if err != nil { + err = multierr.Append(err, unix.Close(fd)) + } + }() + var sa unix.Sockaddr + switch domain { + case unix.AF_INET: + var sa4 unix.SockaddrInet4 + copy(sa4.Addr[:], net.ParseIP(*localIPv4).To4()) + sa = &sa4 + case unix.AF_INET6: + var sa6 unix.SockaddrInet6 + copy(sa6.Addr[:], net.ParseIP(*localIPv6).To16()) + 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, err } newSockAddr, err := unix.Getsockname(fd) if err != nil { - unix.Close(fd) return -1, 0, err } - newSockAddrInet4, ok := newSockAddr.(*unix.SockaddrInet4) - if !ok { - unix.Close(fd) - return -1, 0, fmt.Errorf("can't cast Getsockname result to SockaddrInet4") + switch domain { + case unix.AF_INET: + newSockAddrInet4, ok := newSockAddr.(*unix.SockaddrInet4) + if !ok { + return -1, 0, fmt.Errorf("can't cast Getsockname result %T to SockaddrInet4", newSockAddr) + } + return fd, uint16(newSockAddrInet4.Port), nil + case unix.AF_INET6: + newSockAddrInet6, ok := newSockAddr.(*unix.SockaddrInet6) + if !ok { + return -1, 0, fmt.Errorf("can't cast Getsockname result %T to SockaddrInet6", newSockAddr) + } + return fd, uint16(newSockAddrInet6.Port), nil + default: + return -1, 0, fmt.Errorf("invalid domain %d, it should be one of unix.AF_INET or unix.AF_INET6", domain) } - return fd, uint16(newSockAddrInet4.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. + // 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. The - // calles takes ownership of the returned Layer. + // 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 @@ -122,7 +150,7 @@ func newEtherState(out, in Ether) (*etherState, error) { } func (s *etherState) outgoing() Layer { - return &s.out + return deepcopy.Copy(&s.out).(Layer) } // incoming implements layerState.incoming. @@ -167,7 +195,7 @@ func newIPv4State(out, in IPv4) (*ipv4State, error) { } func (s *ipv4State) outgoing() Layer { - return &s.out + return deepcopy.Copy(&s.out).(Layer) } // incoming implements layerState.incoming. @@ -187,6 +215,54 @@ 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 newIPv6State(out, in IPv6) (*ipv6State, error) { + lIP := tcpip.Address(net.ParseIP(*localIPv6).To16()) + rIP := tcpip.Address(net.ParseIP(*remoteIPv6).To16()) + 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 @@ -205,8 +281,8 @@ func SeqNumValue(v seqnum.Value) *seqnum.Value { } // newTCPState creates a new TCPState. -func newTCPState(out, in TCP) (*tcpState, error) { - portPickerFD, localPort, err := pickPort() +func newTCPState(domain int, out, in TCP) (*tcpState, error) { + portPickerFD, localPort, err := pickPort(domain, unix.SOCK_STREAM) if err != nil { return nil, err } @@ -309,8 +385,8 @@ type udpState struct { var _ layerState = (*udpState)(nil) // newUDPState creates a new udpState. -func newUDPState(out, in UDP) (*udpState, error) { - portPickerFD, localPort, err := pickPort() +func newUDPState(domain int, out, in UDP) (*udpState, error) { + portPickerFD, localPort, err := pickPort(domain, unix.SOCK_DGRAM) if err != nil { return nil, err } @@ -329,7 +405,7 @@ func newUDPState(out, in UDP) (*udpState, error) { } func (s *udpState) outgoing() Layer { - return &s.out + return deepcopy.Copy(&s.out).(Layer) } // incoming implements layerState.incoming. @@ -363,44 +439,33 @@ type Connection struct { t *testing.T } -// match tries to match each Layer in received against the incoming filter. If -// received is longer than layerStates then that may still count as a match. The -// reverse is never a match. override overrides the default matchers for each -// Layer. -func (conn *Connection) match(override, received Layers) bool { - var layersToMatch int - if len(override) < len(conn.layerStates) { - layersToMatch = len(conn.layerStates) - } else { - layersToMatch = len(override) - } - if len(received) < layersToMatch { - return false - } - for i := 0; i < layersToMatch; i++ { - var toMatch Layer - if i < len(conn.layerStates) { - s := conn.layerStates[i] - toMatch = s.incoming(received[i]) - if toMatch == nil { - return false - } - if i < len(override) { - if err := toMatch.merge(override[i]); err != nil { - conn.t.Fatalf("failed to merge: %s", err) - } - } - } else { - toMatch = override[i] - if toMatch == nil { - conn.t.Fatalf("expect the overriding layers to be non-nil") - } - } - if !toMatch.match(received[i]) { - return false +// 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 true + return toMatch.match(received) } // Close frees associated resources held by the Connection. @@ -432,7 +497,7 @@ func (conn *Connection) CreateFrame(layer Layer, additionalLayers ...Layer) Laye // SendFrame sends a frame on the wire and updates the state of all layers. func (conn *Connection) SendFrame(frame Layers) { - outBytes, err := frame.toBytes() + outBytes, err := frame.ToBytes() if err != nil { conn.t.Fatalf("can't build outgoing TCP packet: %s", err) } @@ -470,6 +535,16 @@ func (conn *Connection) recvFrame(timeout time.Duration) Layers { 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 a frame with the final layerStates layer matching the provided Layer // within the timeout specified. If it doesn't arrive in time, it returns nil. func (conn *Connection) Expect(layer Layer, timeout time.Duration) (Layer, error) { @@ -485,21 +560,25 @@ func (conn *Connection) Expect(layer Layer, timeout time.Duration) (Layer, error return gotFrame[len(conn.layerStates)-1], nil } conn.t.Fatal("the received frame should be at least as long as the expected layers") - return nil, fmt.Errorf("the received frame should be at least as long as the expected layers") + panic("unreachable") } // ExpectFrame expects a frame that matches the provided Layers within the -// timeout specified. If it doesn't arrive in time, it returns nil. +// 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(layers Layers, timeout time.Duration) (Layers, error) { deadline := time.Now().Add(timeout) - var allLayers []string + var errs error for { var gotLayers Layers if timeout = time.Until(deadline); timeout > 0 { gotLayers = conn.recvFrame(timeout) } if gotLayers == nil { - return nil, fmt.Errorf("got %d packets:\n%s", len(allLayers), strings.Join(allLayers, "\n")) + if errs == nil { + return nil, fmt.Errorf("got no frames matching %v during %s", layers, timeout) + } + return nil, fmt.Errorf("got no frames matching %v during %s: got %w", layers, timeout, errs) } if conn.match(layers, gotLayers) { for i, s := range conn.layerStates { @@ -509,7 +588,7 @@ func (conn *Connection) ExpectFrame(layers Layers, timeout time.Duration) (Layer } return gotLayers, nil } - allLayers = append(allLayers, fmt.Sprintf("%s", gotLayers)) + errs = multierr.Combine(errs, &layersError{got: gotLayers, want: conn.incoming(gotLayers)}) } } @@ -532,7 +611,7 @@ func NewTCPIPv4(t *testing.T, outgoingTCP, incomingTCP TCP) TCPIPv4 { if err != nil { t.Fatalf("can't make ipv4State: %s", err) } - tcpState, err := newTCPState(outgoingTCP, incomingTCP) + tcpState, err := newTCPState(unix.AF_INET, outgoingTCP, incomingTCP) if err != nil { t.Fatalf("can't make tcpState: %s", err) } @@ -629,6 +708,59 @@ func (conn *TCPIPv4) SynAck() *TCP { return conn.state().synAck } +// 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 NewIPv6Conn(t *testing.T, outgoingIPv6, incomingIPv6 IPv6) IPv6Conn { + etherState, err := newEtherState(Ether{}, Ether{}) + if err != nil { + t.Fatalf("can't make EtherState: %s", err) + } + ipv6State, err := newIPv6State(outgoingIPv6, incomingIPv6) + if err != nil { + t.Fatalf("can't make IPv6State: %s", err) + } + + injector, err := NewInjector(t) + if err != nil { + t.Fatalf("can't make injector: %s", err) + } + sniffer, err := NewSniffer(t) + if err != nil { + t.Fatalf("can't make sniffer: %s", err) + } + + return IPv6Conn{ + layerStates: []layerState{etherState, ipv6State}, + injector: injector, + sniffer: sniffer, + t: t, + } +} + +// SendFrame sends a frame on the wire and updates the state of all layers. +func (conn *IPv6Conn) SendFrame(frame Layers) { + (*Connection)(conn).SendFrame(frame) +} + +// CreateFrame builds a frame for the connection with ipv6 overriding the ipv6 +// layer defaults and additionalLayers added after it. +func (conn *IPv6Conn) CreateFrame(ipv6 IPv6, additionalLayers ...Layer) Layers { + return (*Connection)(conn).CreateFrame(&ipv6, additionalLayers...) +} + +// Close to clean up any resources held. +func (conn *IPv6Conn) Close() { + (*Connection)(conn).Close() +} + +// ExpectFrame expects a frame that matches the provided Layers within the +// timeout specified. If it doesn't arrive in time, it returns nil. +func (conn *IPv6Conn) ExpectFrame(frame Layers, timeout time.Duration) (Layers, error) { + return (*Connection)(conn).ExpectFrame(frame, timeout) +} + // Drain drains the sniffer's receive buffer by receiving packets until there's // nothing else to receive. func (conn *TCPIPv4) Drain() { @@ -648,7 +780,7 @@ func NewUDPIPv4(t *testing.T, outgoingUDP, incomingUDP UDP) UDPIPv4 { if err != nil { t.Fatalf("can't make ipv4State: %s", err) } - tcpState, err := newUDPState(outgoingUDP, incomingUDP) + tcpState, err := newUDPState(unix.AF_INET, outgoingUDP, incomingUDP) if err != nil { t.Fatalf("can't make udpState: %s", err) } |