github.com/castai/kvisor@v1.7.1-0.20240516114728-b3572a2607b5/pkg/net/packet/payload.go (about)

     1  package packet
     2  
     3  import (
     4  	"encoding/binary"
     5  	"errors"
     6  	"fmt"
     7  )
     8  
     9  const (
    10  	ipV4             = 4
    11  	ipV6             = 6
    12  	ipV4HeaderLength = 20
    13  	ipV6HeaderLength = 40
    14  
    15  	minTcpHeaderLength = 20
    16  	udpHeaderLength    = 8
    17  )
    18  
    19  type SubProtocol byte
    20  
    21  const (
    22  	UnsupportedSubProtocol SubProtocol = 0
    23  	SubProtocolTCP         SubProtocol = 0x06
    24  	SubProtocolUDP         SubProtocol = 0x11
    25  )
    26  
    27  var subProtocolName = map[SubProtocol]string{
    28  	SubProtocolTCP: "TCP",
    29  	SubProtocolUDP: "UDP",
    30  }
    31  
    32  func (s SubProtocol) String() string {
    33  	if name, found := subProtocolName[s]; found {
    34  		return name
    35  	}
    36  
    37  	return "UNKNOWN"
    38  }
    39  
    40  var (
    41  	ErrUnsupportedIPVersion    = errors.New("ip version not supported")
    42  	ErrUnsupportedSubProtocol  = errors.New("sub protocol not supported")
    43  	ErrOffsetBiggerThanData    = errors.New("sub protocol offset is larger than given data")
    44  	ErrSubProtocolDataTooSmall = errors.New("sub protocol data is too small")
    45  	ErrPacketTooSmall          = errors.New("packet to small")
    46  	ErrNoData                  = errors.New("no data provided")
    47  )
    48  
    49  // ExtractPayload will try to extract the payload for a given IPv4/IPv6 packet.
    50  func ExtractPayload(data []byte) ([]byte, SubProtocol, error) {
    51  	if len(data) == 0 {
    52  		return nil, UnsupportedSubProtocol, ErrNoData
    53  	}
    54  
    55  	version := data[0] >> 4
    56  
    57  	switch version {
    58  	case 4:
    59  		return extractPayloadV4(data)
    60  	case 6:
    61  		return extractPayloadV6(data)
    62  	default:
    63  		return nil, UnsupportedSubProtocol, fmt.Errorf("cannot extract payload for IP packet version `%d`: %w", version, ErrUnsupportedIPVersion)
    64  	}
    65  }
    66  
    67  func extractPayloadV4(data []byte) ([]byte, SubProtocol, error) {
    68  	subProtocol := SubProtocol(data[9])
    69  	totalLength := int(binary.BigEndian.Uint16(data[2:4]))
    70  	if len(data) < totalLength {
    71  		return nil, UnsupportedSubProtocol, ErrPacketTooSmall
    72  	}
    73  
    74  	subOffset := int((data[0] & 0x0F) << 2)
    75  
    76  	if subOffset > totalLength {
    77  		return nil, UnsupportedSubProtocol, ErrOffsetBiggerThanData
    78  	}
    79  
    80  	switch subProtocol {
    81  	case SubProtocolTCP:
    82  		if (totalLength - subOffset) < minTcpHeaderLength {
    83  			return nil, UnsupportedSubProtocol, ErrSubProtocolDataTooSmall
    84  		}
    85  		dataOffset := (data[subOffset+12] & 0xF0) >> 2
    86  		return data[subOffset+int(dataOffset) : totalLength], SubProtocolTCP, nil
    87  
    88  	case SubProtocolUDP:
    89  		if (totalLength - subOffset) < udpHeaderLength {
    90  			return nil, UnsupportedSubProtocol, ErrSubProtocolDataTooSmall
    91  		}
    92  		return data[subOffset+udpHeaderLength : totalLength], SubProtocolUDP, nil
    93  	}
    94  
    95  	return nil, UnsupportedSubProtocol, fmt.Errorf("error while parsing sub protocol `%d`: %w", subProtocol, ErrUnsupportedSubProtocol)
    96  }
    97  
    98  func extractPayloadV6(data []byte) ([]byte, SubProtocol, error) {
    99  	subProtocol := SubProtocol(data[6])
   100  	totalLength := int(binary.BigEndian.Uint16(data[4:6])) + ipV6HeaderLength
   101  	if len(data) < totalLength {
   102  		return nil, UnsupportedSubProtocol, ErrPacketTooSmall
   103  	}
   104  	// We ignore ipv6 extension headers for now.
   105  	subOffset := ipV6HeaderLength
   106  
   107  	switch subProtocol {
   108  	case SubProtocolTCP:
   109  		if (totalLength - subOffset) < minTcpHeaderLength {
   110  			return nil, UnsupportedSubProtocol, ErrSubProtocolDataTooSmall
   111  		}
   112  		dataOffset := (data[subOffset+12] & 0xF0) >> 2
   113  		return data[subOffset+int(dataOffset) : totalLength], SubProtocolTCP, nil
   114  
   115  	case SubProtocolUDP:
   116  		if (totalLength - subOffset) < udpHeaderLength {
   117  			return nil, UnsupportedSubProtocol, ErrSubProtocolDataTooSmall
   118  		}
   119  		return data[subOffset+udpHeaderLength : totalLength], SubProtocolUDP, nil
   120  	}
   121  
   122  	return nil, UnsupportedSubProtocol, nil
   123  }