mirror of
https://github.com/netbirdio/gvisor.git
synced 2026-05-22 17:12:49 -07:00
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:
File diff suppressed because it is too large
Load Diff
@@ -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 ðer, 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
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
|
||||
Reference in New Issue
Block a user