Refactor connections.go to make it easier to add new connection types.

Rather than have a struct for the state of each type of connection, such as
TCP/IPv4, UDP/IPv4, TCP/IPv6, etc, have a state for each layer, such as UDP,
TCP, IPv4, IPv6.  Those states can be composed into connections.

Tested:
  Existing unit tests still pass/fail as expected.
PiperOrigin-RevId: 306703180
This commit is contained in:
Eyal Soha
2020-04-15 13:01:11 -07:00
committed by gVisor bot
parent 7c13546d3b
commit 1bcc2bf17f
6 changed files with 585 additions and 340 deletions
File diff suppressed because it is too large Load Diff
+65 -32
View File
@@ -64,6 +64,9 @@ type Layer interface {
// setPrev sets the pointer to the Layer encapsulating this one.
setPrev(Layer)
// merge overrides the values in the interface with the provided values.
merge(Layer) error
}
// LayerBase is the common elements of all layers.
@@ -91,6 +94,9 @@ func (lb *LayerBase) setPrev(l Layer) {
// equalLayer compares that two Layer structs match while ignoring field in
// which either input has a nil and also ignoring the LayerBase of the inputs.
func equalLayer(x, y Layer) bool {
if x == nil || y == nil {
return true
}
// opt ignores comparison pairs where either of the inputs is a nil.
opt := cmp.FilterValues(func(x, y interface{}) bool {
for _, l := range []interface{}{x, y} {
@@ -104,6 +110,15 @@ func equalLayer(x, y Layer) bool {
return cmp.Equal(x, y, opt, cmpopts.IgnoreTypes(LayerBase{}))
}
// mergeLayer merges other in layer. Any non-nil value in other overrides the
// corresponding value in layer. If other is nil, no action is performed.
func mergeLayer(layer, other Layer) error {
if other == nil {
return nil
}
return mergo.Merge(layer, other, mergo.WithOverride)
}
func stringLayer(l Layer) string {
v := reflect.ValueOf(l).Elem()
t := v.Type()
@@ -172,14 +187,14 @@ func NetworkProtocolNumber(v tcpip.NetworkProtocolNumber) *tcpip.NetworkProtocol
return &v
}
// LayerParser parses the input bytes and returns a Layer along with the next
// LayerParser to run. If there is no more parsing to do, the returned
// LayerParser is nil.
type LayerParser func([]byte) (Layer, LayerParser)
// layerParser parses the input bytes and returns a Layer along with the next
// layerParser to run. If there is no more parsing to do, the returned
// layerParser is nil.
type layerParser func([]byte) (Layer, layerParser)
// Parse parses bytes starting with the first LayerParser and using successive
// LayerParsers until all the bytes are parsed.
func Parse(parser LayerParser, b []byte) Layers {
// parse parses bytes starting with the first layerParser and using successive
// layerParsers until all the bytes are parsed.
func parse(parser layerParser, b []byte) Layers {
var layers Layers
for {
var layer Layer
@@ -194,22 +209,22 @@ func Parse(parser LayerParser, b []byte) Layers {
return layers
}
// ParseEther parses the bytes assuming that they start with an ethernet header
// parseEther parses the bytes assuming that they start with an ethernet header
// and continues parsing further encapsulations.
func ParseEther(b []byte) (Layer, LayerParser) {
func parseEther(b []byte) (Layer, layerParser) {
h := header.Ethernet(b)
ether := Ether{
SrcAddr: LinkAddress(h.SourceAddress()),
DstAddr: LinkAddress(h.DestinationAddress()),
Type: NetworkProtocolNumber(h.Type()),
}
var nextParser LayerParser
var nextParser layerParser
switch h.Type() {
case header.IPv4ProtocolNumber:
nextParser = ParseIPv4
nextParser = parseIPv4
default:
// Assume that the rest is a payload.
nextParser = ParsePayload
nextParser = parsePayload
}
return &ether, nextParser
}
@@ -222,6 +237,12 @@ func (l *Ether) length() int {
return header.EthernetMinimumSize
}
// merge overrides the values in l with the values from other but only in fields
// where the value is not nil.
func (l *Ether) merge(other Layer) error {
return mergeLayer(l, other)
}
// IPv4 can construct and match an IPv4 encapsulation.
type IPv4 struct {
LayerBase
@@ -330,9 +351,9 @@ func Address(v tcpip.Address) *tcpip.Address {
return &v
}
// ParseIPv4 parses the bytes assuming that they start with an ipv4 header and
// parseIPv4 parses the bytes assuming that they start with an ipv4 header and
// continues parsing further encapsulations.
func ParseIPv4(b []byte) (Layer, LayerParser) {
func parseIPv4(b []byte) (Layer, layerParser) {
h := header.IPv4(b)
tos, _ := h.TOS()
ipv4 := IPv4{
@@ -348,15 +369,15 @@ func ParseIPv4(b []byte) (Layer, LayerParser) {
SrcAddr: Address(h.SourceAddress()),
DstAddr: Address(h.DestinationAddress()),
}
var nextParser LayerParser
var nextParser layerParser
switch h.TransportProtocol() {
case header.TCPProtocolNumber:
nextParser = ParseTCP
nextParser = parseTCP
case header.UDPProtocolNumber:
nextParser = ParseUDP
nextParser = parseUDP
default:
// Assume that the rest is a payload.
nextParser = ParsePayload
nextParser = parsePayload
}
return &ipv4, nextParser
}
@@ -372,6 +393,12 @@ func (l *IPv4) length() int {
return int(*l.IHL)
}
// merge overrides the values in l with the values from other but only in fields
// where the value is not nil.
func (l *IPv4) merge(other Layer) error {
return mergeLayer(l, other)
}
// TCP can construct and match a TCP encapsulation.
type TCP struct {
LayerBase
@@ -482,9 +509,9 @@ func Uint32(v uint32) *uint32 {
return &v
}
// ParseTCP parses the bytes assuming that they start with a tcp header and
// parseTCP parses the bytes assuming that they start with a tcp header and
// continues parsing further encapsulations.
func ParseTCP(b []byte) (Layer, LayerParser) {
func parseTCP(b []byte) (Layer, layerParser) {
h := header.TCP(b)
tcp := TCP{
SrcPort: Uint16(h.SourcePort()),
@@ -497,7 +524,7 @@ func ParseTCP(b []byte) (Layer, LayerParser) {
Checksum: Uint16(h.Checksum()),
UrgentPointer: Uint16(h.UrgentPointer()),
}
return &tcp, ParsePayload
return &tcp, parsePayload
}
func (l *TCP) match(other Layer) bool {
@@ -513,8 +540,8 @@ func (l *TCP) length() int {
// merge overrides the values in l with the values from other but only in fields
// where the value is not nil.
func (l *TCP) merge(other TCP) error {
return mergo.Merge(l, other, mergo.WithOverride)
func (l *TCP) merge(other Layer) error {
return mergeLayer(l, other)
}
// UDP can construct and match a UDP encapsulation.
@@ -565,9 +592,9 @@ func setUDPChecksum(h *header.UDP, udp *UDP) error {
return nil
}
// ParseUDP parses the bytes assuming that they start with a udp header and
// parseUDP parses the bytes assuming that they start with a udp header and
// returns the parsed layer and the next parser to use.
func ParseUDP(b []byte) (Layer, LayerParser) {
func parseUDP(b []byte) (Layer, layerParser) {
h := header.UDP(b)
udp := UDP{
SrcPort: Uint16(h.SourcePort()),
@@ -575,7 +602,7 @@ func ParseUDP(b []byte) (Layer, LayerParser) {
Length: Uint16(h.Length()),
Checksum: Uint16(h.Checksum()),
}
return &udp, ParsePayload
return &udp, parsePayload
}
func (l *UDP) match(other Layer) bool {
@@ -591,8 +618,8 @@ func (l *UDP) length() int {
// merge overrides the values in l with the values from other but only in fields
// where the value is not nil.
func (l *UDP) merge(other UDP) error {
return mergo.Merge(l, other, mergo.WithOverride)
func (l *UDP) merge(other Layer) error {
return mergeLayer(l, other)
}
// Payload has bytes beyond OSI layer 4.
@@ -605,9 +632,9 @@ func (l *Payload) String() string {
return stringLayer(l)
}
// ParsePayload parses the bytes assuming that they start with a payload and
// parsePayload parses the bytes assuming that they start with a payload and
// continue to the end. There can be no further encapsulations.
func ParsePayload(b []byte) (Layer, LayerParser) {
func parsePayload(b []byte) (Layer, layerParser) {
payload := Payload{
Bytes: b,
}
@@ -626,6 +653,12 @@ func (l *Payload) length() int {
return len(l.Bytes)
}
// merge overrides the values in l with the values from other but only in fields
// where the value is not nil.
func (l *Payload) merge(other Layer) error {
return mergeLayer(l, other)
}
// Layers is an array of Layer and supports similar functions to Layer.
type Layers []Layer
@@ -662,8 +695,8 @@ func (ls *Layers) match(other Layers) bool {
if len(*ls) > len(other) {
return false
}
for i := 0; i < len(*ls); i++ {
if !equalLayer((*ls)[i], other[i]) {
for i, l := range *ls {
if !equalLayer(l, other[i]) {
return false
}
}
+9 -6
View File
@@ -17,6 +17,7 @@ package testbench
import (
"encoding/binary"
"flag"
"fmt"
"math"
"net"
"testing"
@@ -120,12 +121,13 @@ func (s *Sniffer) Drain() {
}
}
// Close the socket that Sniffer is using.
func (s *Sniffer) Close() {
// close the socket that Sniffer is using.
func (s *Sniffer) close() error {
if err := unix.Close(s.fd); err != nil {
s.t.Fatalf("can't close sniffer socket: %s", err)
return fmt.Errorf("can't close sniffer socket: %w", err)
}
s.fd = -1
return nil
}
// Injector can inject raw frames.
@@ -171,10 +173,11 @@ func (i *Injector) Send(b []byte) {
}
}
// Close the underlying socket.
func (i *Injector) Close() {
// close the underlying socket.
func (i *Injector) close() error {
if err := unix.Close(i.fd); err != nil {
i.t.Fatalf("can't close sniffer socket: %s", err)
return fmt.Errorf("can't close sniffer socket: %w", err)
}
i.fd = -1
return nil
}
@@ -40,7 +40,11 @@ func TestPiggyback(t *testing.T) {
sampleData := []byte("Sample Data")
dut.Send(acceptFd, sampleData, 0)
conn.ExpectData(tb.TCP{Flags: tb.Uint8(header.TCPFlagAck | header.TCPFlagPsh)}, sampleData, time.Second)
expectedTCP := tb.TCP{Flags: tb.Uint8(header.TCPFlagAck | header.TCPFlagPsh)}
expectedPayload := tb.Payload{Bytes: sampleData}
if _, err := conn.ExpectData(&expectedTCP, &expectedPayload, time.Second); err != nil {
t.Fatalf("Expected %v but didn't get one: %s", tb.Layers{&expectedTCP, &expectedPayload}, err)
}
// Cause DUT to send us more data as soon as we ACK their first data segment because we have
// a small window.
@@ -48,6 +52,8 @@ func TestPiggyback(t *testing.T) {
// DUT should ACK our segment by piggybacking ACK to their outstanding data segment instead of
// sending a separate ACK packet.
conn.Send(tb.TCP{Flags: tb.Uint8(header.TCPFlagAck | header.TCPFlagPsh)}, &tb.Payload{Bytes: sampleData})
conn.ExpectData(tb.TCP{Flags: tb.Uint8(header.TCPFlagAck | header.TCPFlagPsh)}, sampleData, time.Second)
conn.Send(expectedTCP, &expectedPayload)
if _, err := conn.ExpectData(&expectedTCP, &expectedPayload, time.Second); err != nil {
t.Fatalf("Expected %v but didn't get one: %s", tb.Layers{&expectedTCP, &expectedPayload}, err)
}
}
@@ -38,15 +38,22 @@ func TestWindowShrink(t *testing.T) {
dut.SetSockOptInt(acceptFd, unix.IPPROTO_TCP, unix.TCP_NODELAY, 1)
sampleData := []byte("Sample Data")
samplePayload := &tb.Payload{Bytes: sampleData}
dut.Send(acceptFd, sampleData, 0)
conn.ExpectData(tb.TCP{}, sampleData, time.Second)
if _, err := conn.ExpectData(&tb.TCP{}, samplePayload, time.Second); err != nil {
t.Fatalf("expected a packet with payload %v: %s", samplePayload, err)
}
conn.Send(tb.TCP{Flags: tb.Uint8(header.TCPFlagAck)})
dut.Send(acceptFd, sampleData, 0)
dut.Send(acceptFd, sampleData, 0)
conn.ExpectData(tb.TCP{}, sampleData, time.Second)
conn.ExpectData(tb.TCP{}, sampleData, time.Second)
if _, err := conn.ExpectData(&tb.TCP{}, samplePayload, time.Second); err != nil {
t.Fatalf("expected a packet with payload %v: %s", samplePayload, err)
}
if _, err := conn.ExpectData(&tb.TCP{}, samplePayload, time.Second); err != nil {
t.Fatalf("expected a packet with payload %v: %s", samplePayload, err)
}
// We close our receiving window here
conn.Send(tb.TCP{Flags: tb.Uint8(header.TCPFlagAck), WindowSize: tb.Uint16(0)})
@@ -54,5 +61,8 @@ func TestWindowShrink(t *testing.T) {
// Note: There is another kind of zero-window probing which Windows uses (by sending one
// new byte at `RemoteSeqNum`), if netstack wants to go that way, we may want to change
// the following lines.
conn.ExpectData(tb.TCP{SeqNum: tb.Uint32(uint32(conn.RemoteSeqNum - 1))}, nil, time.Second)
expectedRemoteSeqNum := *conn.RemoteSeqNum() - 1
if _, err := conn.ExpectData(&tb.TCP{SeqNum: tb.Uint32(uint32(expectedRemoteSeqNum))}, nil, time.Second); err != nil {
t.Fatalf("expected a packet with sequence number %v: %s", expectedRemoteSeqNum, err)
}
}
@@ -30,7 +30,7 @@ func TestUDPRecvMulticast(t *testing.T) {
defer dut.Close(boundFD)
conn := tb.NewUDPIPv4(t, tb.UDP{DstPort: &remotePort}, tb.UDP{SrcPort: &remotePort})
defer conn.Close()
frame := conn.CreateFrame(tb.UDP{}, &tb.Payload{Bytes: []byte("hello world")})
frame := conn.CreateFrame(&tb.UDP{}, &tb.Payload{Bytes: []byte("hello world")})
frame[1].(*tb.IPv4).DstAddr = tb.Address(tcpip.Address(net.ParseIP("224.0.0.1").To4()))
conn.SendFrame(frame)
dut.Recv(boundFD, 100, 0)