659 lines
16 KiB
Go
659 lines
16 KiB
Go
// 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"
|
|
"reflect"
|
|
"strings"
|
|
|
|
"github.com/google/go-cmp/cmp"
|
|
"github.com/google/go-cmp/cmp/cmpopts"
|
|
"github.com/imdario/mergo"
|
|
"gvisor.dev/gvisor/pkg/tcpip"
|
|
"gvisor.dev/gvisor/pkg/tcpip/buffer"
|
|
"gvisor.dev/gvisor/pkg/tcpip/header"
|
|
)
|
|
|
|
// Layer is the interface that all encapsulations must implement.
|
|
//
|
|
// A Layer is an encapsulation in a packet, such as TCP, IPv4, IPv6, etc. A
|
|
// Layer contains all the fields of the encapsulation. Each field is a pointer
|
|
// and may be nil.
|
|
type Layer interface {
|
|
fmt.Stringer
|
|
|
|
// toBytes converts the Layer into bytes. In places where the Layer's field
|
|
// isn't nil, the value that is pointed to is used. When the field is nil, a
|
|
// reasonable default for the Layer is used. For example, "64" for IPv4 TTL
|
|
// and a calculated checksum for TCP or IP. Some layers require information
|
|
// from the previous or next layers in order to compute a default, such as
|
|
// TCP's checksum or Ethernet's type, so each Layer has a doubly-linked list
|
|
// to the layer's neighbors.
|
|
toBytes() ([]byte, error)
|
|
|
|
// match checks if the current Layer matches the provided Layer. If either
|
|
// Layer has a nil in a given field, that field is considered matching.
|
|
// Otherwise, the values pointed to by the fields must match. The LayerBase is
|
|
// ignored.
|
|
match(Layer) bool
|
|
|
|
// length in bytes of the current encapsulation
|
|
length() int
|
|
|
|
// next gets a pointer to the encapsulated Layer.
|
|
next() Layer
|
|
|
|
// prev gets a pointer to the Layer encapsulating this one.
|
|
prev() Layer
|
|
|
|
// setNext sets the pointer to the encapsulated Layer.
|
|
setNext(Layer)
|
|
|
|
// setPrev sets the pointer to the Layer encapsulating this one.
|
|
setPrev(Layer)
|
|
}
|
|
|
|
// LayerBase is the common elements of all layers.
|
|
type LayerBase struct {
|
|
nextLayer Layer
|
|
prevLayer Layer
|
|
}
|
|
|
|
func (lb *LayerBase) next() Layer {
|
|
return lb.nextLayer
|
|
}
|
|
|
|
func (lb *LayerBase) prev() Layer {
|
|
return lb.prevLayer
|
|
}
|
|
|
|
func (lb *LayerBase) setNext(l Layer) {
|
|
lb.nextLayer = l
|
|
}
|
|
|
|
func (lb *LayerBase) setPrev(l Layer) {
|
|
lb.prevLayer = l
|
|
}
|
|
|
|
// 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 {
|
|
// 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} {
|
|
v := reflect.ValueOf(l)
|
|
if (v.Kind() == reflect.Ptr || v.Kind() == reflect.Slice) && v.IsNil() {
|
|
return true
|
|
}
|
|
}
|
|
return false
|
|
}, cmp.Ignore())
|
|
return cmp.Equal(x, y, opt, cmpopts.IgnoreTypes(LayerBase{}))
|
|
}
|
|
|
|
func stringLayer(l Layer) string {
|
|
v := reflect.ValueOf(l).Elem()
|
|
t := v.Type()
|
|
var ret []string
|
|
for i := 0; i < v.NumField(); i++ {
|
|
t := t.Field(i)
|
|
if t.Anonymous {
|
|
// Ignore the LayerBase in the Layer struct.
|
|
continue
|
|
}
|
|
v := v.Field(i)
|
|
if v.IsNil() {
|
|
continue
|
|
}
|
|
ret = append(ret, fmt.Sprintf("%s:%v", t.Name, v))
|
|
}
|
|
return fmt.Sprintf("&%s{%s}", t, strings.Join(ret, " "))
|
|
}
|
|
|
|
// Ether can construct and match an ethernet encapsulation.
|
|
type Ether struct {
|
|
LayerBase
|
|
SrcAddr *tcpip.LinkAddress
|
|
DstAddr *tcpip.LinkAddress
|
|
Type *tcpip.NetworkProtocolNumber
|
|
}
|
|
|
|
func (l *Ether) String() string {
|
|
return stringLayer(l)
|
|
}
|
|
|
|
func (l *Ether) toBytes() ([]byte, error) {
|
|
b := make([]byte, header.EthernetMinimumSize)
|
|
h := header.Ethernet(b)
|
|
fields := &header.EthernetFields{}
|
|
if l.SrcAddr != nil {
|
|
fields.SrcAddr = *l.SrcAddr
|
|
}
|
|
if l.DstAddr != nil {
|
|
fields.DstAddr = *l.DstAddr
|
|
}
|
|
if l.Type != nil {
|
|
fields.Type = *l.Type
|
|
} else {
|
|
switch n := l.next().(type) {
|
|
case *IPv4:
|
|
fields.Type = header.IPv4ProtocolNumber
|
|
default:
|
|
// TODO(b/150301488): Support more protocols, like IPv6.
|
|
return nil, fmt.Errorf("can't deduce the ethernet header's next protocol: %d", n)
|
|
}
|
|
}
|
|
h.Encode(fields)
|
|
return h, nil
|
|
}
|
|
|
|
// LinkAddress is a helper routine that allocates a new tcpip.LinkAddress value
|
|
// to store v and returns a pointer to it.
|
|
func LinkAddress(v tcpip.LinkAddress) *tcpip.LinkAddress {
|
|
return &v
|
|
}
|
|
|
|
// NetworkProtocolNumber is a helper routine that allocates a new
|
|
// tcpip.NetworkProtocolNumber value to store v and returns a pointer to it.
|
|
func NetworkProtocolNumber(v tcpip.NetworkProtocolNumber) *tcpip.NetworkProtocolNumber {
|
|
return &v
|
|
}
|
|
|
|
// ParseEther parses the bytes assuming that they start with an ethernet header
|
|
// and continues parsing further encapsulations.
|
|
func ParseEther(b []byte) (Layers, error) {
|
|
h := header.Ethernet(b)
|
|
ether := Ether{
|
|
SrcAddr: LinkAddress(h.SourceAddress()),
|
|
DstAddr: LinkAddress(h.DestinationAddress()),
|
|
Type: NetworkProtocolNumber(h.Type()),
|
|
}
|
|
layers := Layers{ðer}
|
|
switch h.Type() {
|
|
case header.IPv4ProtocolNumber:
|
|
moreLayers, err := ParseIPv4(b[ether.length():])
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return append(layers, moreLayers...), nil
|
|
default:
|
|
// TODO(b/150301488): Support more protocols, like IPv6.
|
|
return nil, fmt.Errorf("can't deduce the ethernet header's next protocol: %#v", b)
|
|
}
|
|
}
|
|
|
|
func (l *Ether) match(other Layer) bool {
|
|
return equalLayer(l, other)
|
|
}
|
|
|
|
func (l *Ether) length() int {
|
|
return header.EthernetMinimumSize
|
|
}
|
|
|
|
// IPv4 can construct and match an IPv4 encapsulation.
|
|
type IPv4 struct {
|
|
LayerBase
|
|
IHL *uint8
|
|
TOS *uint8
|
|
TotalLength *uint16
|
|
ID *uint16
|
|
Flags *uint8
|
|
FragmentOffset *uint16
|
|
TTL *uint8
|
|
Protocol *uint8
|
|
Checksum *uint16
|
|
SrcAddr *tcpip.Address
|
|
DstAddr *tcpip.Address
|
|
}
|
|
|
|
func (l *IPv4) String() string {
|
|
return stringLayer(l)
|
|
}
|
|
|
|
func (l *IPv4) toBytes() ([]byte, error) {
|
|
b := make([]byte, header.IPv4MinimumSize)
|
|
h := header.IPv4(b)
|
|
fields := &header.IPv4Fields{
|
|
IHL: 20,
|
|
TOS: 0,
|
|
TotalLength: 0,
|
|
ID: 0,
|
|
Flags: 0,
|
|
FragmentOffset: 0,
|
|
TTL: 64,
|
|
Protocol: 0,
|
|
Checksum: 0,
|
|
SrcAddr: tcpip.Address(""),
|
|
DstAddr: tcpip.Address(""),
|
|
}
|
|
if l.TOS != nil {
|
|
fields.TOS = *l.TOS
|
|
}
|
|
if l.TotalLength != nil {
|
|
fields.TotalLength = *l.TotalLength
|
|
} else {
|
|
fields.TotalLength = uint16(l.length())
|
|
current := l.next()
|
|
for current != nil {
|
|
fields.TotalLength += uint16(current.length())
|
|
current = current.next()
|
|
}
|
|
}
|
|
if l.ID != nil {
|
|
fields.ID = *l.ID
|
|
}
|
|
if l.Flags != nil {
|
|
fields.Flags = *l.Flags
|
|
}
|
|
if l.FragmentOffset != nil {
|
|
fields.FragmentOffset = *l.FragmentOffset
|
|
}
|
|
if l.TTL != nil {
|
|
fields.TTL = *l.TTL
|
|
}
|
|
if l.Protocol != nil {
|
|
fields.Protocol = *l.Protocol
|
|
} else {
|
|
switch n := l.next().(type) {
|
|
case *TCP:
|
|
fields.Protocol = uint8(header.TCPProtocolNumber)
|
|
case *UDP:
|
|
fields.Protocol = uint8(header.UDPProtocolNumber)
|
|
default:
|
|
// TODO(b/150301488): Support more protocols as needed.
|
|
return nil, fmt.Errorf("can't deduce the ip header's next protocol: %#v", n)
|
|
}
|
|
}
|
|
if l.SrcAddr != nil {
|
|
fields.SrcAddr = *l.SrcAddr
|
|
}
|
|
if l.DstAddr != nil {
|
|
fields.DstAddr = *l.DstAddr
|
|
}
|
|
if l.Checksum != nil {
|
|
fields.Checksum = *l.Checksum
|
|
}
|
|
h.Encode(fields)
|
|
if l.Checksum == nil {
|
|
h.SetChecksum(^h.CalculateChecksum())
|
|
}
|
|
return h, nil
|
|
}
|
|
|
|
// Uint16 is a helper routine that allocates a new
|
|
// uint16 value to store v and returns a pointer to it.
|
|
func Uint16(v uint16) *uint16 {
|
|
return &v
|
|
}
|
|
|
|
// Uint8 is a helper routine that allocates a new
|
|
// uint8 value to store v and returns a pointer to it.
|
|
func Uint8(v uint8) *uint8 {
|
|
return &v
|
|
}
|
|
|
|
// Address is a helper routine that allocates a new tcpip.Address value to store
|
|
// v and returns a pointer to it.
|
|
func Address(v tcpip.Address) *tcpip.Address {
|
|
return &v
|
|
}
|
|
|
|
// ParseIPv4 parses the bytes assuming that they start with an ipv4 header and
|
|
// continues parsing further encapsulations.
|
|
func ParseIPv4(b []byte) (Layers, error) {
|
|
h := header.IPv4(b)
|
|
tos, _ := h.TOS()
|
|
ipv4 := IPv4{
|
|
IHL: Uint8(h.HeaderLength()),
|
|
TOS: &tos,
|
|
TotalLength: Uint16(h.TotalLength()),
|
|
ID: Uint16(h.ID()),
|
|
Flags: Uint8(h.Flags()),
|
|
FragmentOffset: Uint16(h.FragmentOffset()),
|
|
TTL: Uint8(h.TTL()),
|
|
Protocol: Uint8(h.Protocol()),
|
|
Checksum: Uint16(h.Checksum()),
|
|
SrcAddr: Address(h.SourceAddress()),
|
|
DstAddr: Address(h.DestinationAddress()),
|
|
}
|
|
layers := Layers{&ipv4}
|
|
switch h.TransportProtocol() {
|
|
case header.TCPProtocolNumber:
|
|
moreLayers, err := ParseTCP(b[ipv4.length():])
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return append(layers, moreLayers...), nil
|
|
case header.UDPProtocolNumber:
|
|
moreLayers, err := ParseUDP(b[ipv4.length():])
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return append(layers, moreLayers...), nil
|
|
}
|
|
return nil, fmt.Errorf("can't deduce the ethernet header's next protocol: %d", h.Protocol())
|
|
}
|
|
|
|
func (l *IPv4) match(other Layer) bool {
|
|
return equalLayer(l, other)
|
|
}
|
|
|
|
func (l *IPv4) length() int {
|
|
if l.IHL == nil {
|
|
return header.IPv4MinimumSize
|
|
}
|
|
return int(*l.IHL)
|
|
}
|
|
|
|
// TCP can construct and match a TCP encapsulation.
|
|
type TCP struct {
|
|
LayerBase
|
|
SrcPort *uint16
|
|
DstPort *uint16
|
|
SeqNum *uint32
|
|
AckNum *uint32
|
|
DataOffset *uint8
|
|
Flags *uint8
|
|
WindowSize *uint16
|
|
Checksum *uint16
|
|
UrgentPointer *uint16
|
|
}
|
|
|
|
func (l *TCP) String() string {
|
|
return stringLayer(l)
|
|
}
|
|
|
|
func (l *TCP) toBytes() ([]byte, error) {
|
|
b := make([]byte, header.TCPMinimumSize)
|
|
h := header.TCP(b)
|
|
if l.SrcPort != nil {
|
|
h.SetSourcePort(*l.SrcPort)
|
|
}
|
|
if l.DstPort != nil {
|
|
h.SetDestinationPort(*l.DstPort)
|
|
}
|
|
if l.SeqNum != nil {
|
|
h.SetSequenceNumber(*l.SeqNum)
|
|
}
|
|
if l.AckNum != nil {
|
|
h.SetAckNumber(*l.AckNum)
|
|
}
|
|
if l.DataOffset != nil {
|
|
h.SetDataOffset(*l.DataOffset)
|
|
} else {
|
|
h.SetDataOffset(uint8(l.length()))
|
|
}
|
|
if l.Flags != nil {
|
|
h.SetFlags(*l.Flags)
|
|
}
|
|
if l.WindowSize != nil {
|
|
h.SetWindowSize(*l.WindowSize)
|
|
} else {
|
|
h.SetWindowSize(32768)
|
|
}
|
|
if l.UrgentPointer != nil {
|
|
h.SetUrgentPoiner(*l.UrgentPointer)
|
|
}
|
|
if l.Checksum != nil {
|
|
h.SetChecksum(*l.Checksum)
|
|
return h, nil
|
|
}
|
|
if err := setTCPChecksum(&h, l); err != nil {
|
|
return nil, err
|
|
}
|
|
return h, nil
|
|
}
|
|
|
|
// totalLength returns the length of the provided layer and all following
|
|
// layers.
|
|
func totalLength(l Layer) int {
|
|
var totalLength int
|
|
for ; l != nil; l = l.next() {
|
|
totalLength += l.length()
|
|
}
|
|
return totalLength
|
|
}
|
|
|
|
// layerChecksum calculates the checksum of the Layer header, including the
|
|
// peusdeochecksum of the layer before it and all the bytes after it..
|
|
func layerChecksum(l Layer, protoNumber tcpip.TransportProtocolNumber) (uint16, error) {
|
|
totalLength := uint16(totalLength(l))
|
|
var xsum uint16
|
|
switch s := l.prev().(type) {
|
|
case *IPv4:
|
|
xsum = header.PseudoHeaderChecksum(protoNumber, *s.SrcAddr, *s.DstAddr, totalLength)
|
|
default:
|
|
// TODO(b/150301488): Support more protocols, like IPv6.
|
|
return 0, fmt.Errorf("can't get src and dst addr from previous layer: %#v", s)
|
|
}
|
|
var payloadBytes buffer.VectorisedView
|
|
for current := l.next(); current != nil; current = current.next() {
|
|
payload, err := current.toBytes()
|
|
if err != nil {
|
|
return 0, fmt.Errorf("can't get bytes for next header: %s", payload)
|
|
}
|
|
payloadBytes.AppendView(payload)
|
|
}
|
|
xsum = header.ChecksumVV(payloadBytes, xsum)
|
|
return xsum, nil
|
|
}
|
|
|
|
// setTCPChecksum calculates the checksum of the TCP header and sets it in h.
|
|
func setTCPChecksum(h *header.TCP, tcp *TCP) error {
|
|
h.SetChecksum(0)
|
|
xsum, err := layerChecksum(tcp, header.TCPProtocolNumber)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
h.SetChecksum(^h.CalculateChecksum(xsum))
|
|
return nil
|
|
}
|
|
|
|
// Uint32 is a helper routine that allocates a new
|
|
// uint32 value to store v and returns a pointer to it.
|
|
func Uint32(v uint32) *uint32 {
|
|
return &v
|
|
}
|
|
|
|
// ParseTCP parses the bytes assuming that they start with a tcp header and
|
|
// continues parsing further encapsulations.
|
|
func ParseTCP(b []byte) (Layers, error) {
|
|
h := header.TCP(b)
|
|
tcp := TCP{
|
|
SrcPort: Uint16(h.SourcePort()),
|
|
DstPort: Uint16(h.DestinationPort()),
|
|
SeqNum: Uint32(h.SequenceNumber()),
|
|
AckNum: Uint32(h.AckNumber()),
|
|
DataOffset: Uint8(h.DataOffset()),
|
|
Flags: Uint8(h.Flags()),
|
|
WindowSize: Uint16(h.WindowSize()),
|
|
Checksum: Uint16(h.Checksum()),
|
|
UrgentPointer: Uint16(h.UrgentPointer()),
|
|
}
|
|
layers := Layers{&tcp}
|
|
moreLayers, err := ParsePayload(b[tcp.length():])
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return append(layers, moreLayers...), nil
|
|
}
|
|
|
|
func (l *TCP) match(other Layer) bool {
|
|
return equalLayer(l, other)
|
|
}
|
|
|
|
func (l *TCP) length() int {
|
|
if l.DataOffset == nil {
|
|
return header.TCPMinimumSize
|
|
}
|
|
return int(*l.DataOffset)
|
|
}
|
|
|
|
// 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)
|
|
}
|
|
|
|
// UDP can construct and match a UDP encapsulation.
|
|
type UDP struct {
|
|
LayerBase
|
|
SrcPort *uint16
|
|
DstPort *uint16
|
|
Length *uint16
|
|
Checksum *uint16
|
|
}
|
|
|
|
func (l *UDP) String() string {
|
|
return stringLayer(l)
|
|
}
|
|
|
|
func (l *UDP) toBytes() ([]byte, error) {
|
|
b := make([]byte, header.UDPMinimumSize)
|
|
h := header.UDP(b)
|
|
if l.SrcPort != nil {
|
|
h.SetSourcePort(*l.SrcPort)
|
|
}
|
|
if l.DstPort != nil {
|
|
h.SetDestinationPort(*l.DstPort)
|
|
}
|
|
if l.Length != nil {
|
|
h.SetLength(*l.Length)
|
|
} else {
|
|
h.SetLength(uint16(totalLength(l)))
|
|
}
|
|
if l.Checksum != nil {
|
|
h.SetChecksum(*l.Checksum)
|
|
return h, nil
|
|
}
|
|
if err := setUDPChecksum(&h, l); err != nil {
|
|
return nil, err
|
|
}
|
|
return h, nil
|
|
}
|
|
|
|
// setUDPChecksum calculates the checksum of the UDP header and sets it in h.
|
|
func setUDPChecksum(h *header.UDP, udp *UDP) error {
|
|
h.SetChecksum(0)
|
|
xsum, err := layerChecksum(udp, header.UDPProtocolNumber)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
h.SetChecksum(^h.CalculateChecksum(xsum))
|
|
return nil
|
|
}
|
|
|
|
// ParseUDP parses the bytes assuming that they start with a udp header and
|
|
// continues parsing further encapsulations.
|
|
func ParseUDP(b []byte) (Layers, error) {
|
|
h := header.UDP(b)
|
|
udp := UDP{
|
|
SrcPort: Uint16(h.SourcePort()),
|
|
DstPort: Uint16(h.DestinationPort()),
|
|
Length: Uint16(h.Length()),
|
|
Checksum: Uint16(h.Checksum()),
|
|
}
|
|
layers := Layers{&udp}
|
|
moreLayers, err := ParsePayload(b[udp.length():])
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return append(layers, moreLayers...), nil
|
|
}
|
|
|
|
func (l *UDP) match(other Layer) bool {
|
|
return equalLayer(l, other)
|
|
}
|
|
|
|
func (l *UDP) length() int {
|
|
if l.Length == nil {
|
|
return header.UDPMinimumSize
|
|
}
|
|
return int(*l.Length)
|
|
}
|
|
|
|
// 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)
|
|
}
|
|
|
|
// Payload has bytes beyond OSI layer 4.
|
|
type Payload struct {
|
|
LayerBase
|
|
Bytes []byte
|
|
}
|
|
|
|
func (l *Payload) String() string {
|
|
return stringLayer(l)
|
|
}
|
|
|
|
// 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) (Layers, error) {
|
|
payload := Payload{
|
|
Bytes: b,
|
|
}
|
|
return Layers{&payload}, nil
|
|
}
|
|
|
|
func (l *Payload) toBytes() ([]byte, error) {
|
|
return l.Bytes, nil
|
|
}
|
|
|
|
func (l *Payload) match(other Layer) bool {
|
|
return equalLayer(l, other)
|
|
}
|
|
|
|
func (l *Payload) length() int {
|
|
return len(l.Bytes)
|
|
}
|
|
|
|
// Layers is an array of Layer and supports similar functions to Layer.
|
|
type Layers []Layer
|
|
|
|
func (ls *Layers) toBytes() ([]byte, error) {
|
|
for i, l := range *ls {
|
|
if i > 0 {
|
|
l.setPrev((*ls)[i-1])
|
|
}
|
|
if i+1 < len(*ls) {
|
|
l.setNext((*ls)[i+1])
|
|
}
|
|
}
|
|
outBytes := []byte{}
|
|
for _, l := range *ls {
|
|
layerBytes, err := l.toBytes()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
outBytes = append(outBytes, layerBytes...)
|
|
}
|
|
return outBytes, nil
|
|
}
|
|
|
|
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]) {
|
|
return false
|
|
}
|
|
}
|
|
return true
|
|
}
|