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 }