github.com/nicocha30/gvisor-ligolo@v0.0.0-20230726075806-989fa2c0a413/pkg/tcpip/header/parse/parse.go (about) 1 // Copyright 2020 The gVisor Authors. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 // Package parse provides utilities to parse packets. 16 package parse 17 18 import ( 19 "fmt" 20 21 "github.com/nicocha30/gvisor-ligolo/pkg/tcpip" 22 "github.com/nicocha30/gvisor-ligolo/pkg/tcpip/header" 23 "github.com/nicocha30/gvisor-ligolo/pkg/tcpip/stack" 24 ) 25 26 // ARP populates pkt's network header with an ARP header found in 27 // pkt.Data. 28 // 29 // Returns true if the header was successfully parsed. 30 func ARP(pkt stack.PacketBufferPtr) bool { 31 _, ok := pkt.NetworkHeader().Consume(header.ARPSize) 32 if ok { 33 pkt.NetworkProtocolNumber = header.ARPProtocolNumber 34 } 35 return ok 36 } 37 38 // IPv4 parses an IPv4 packet found in pkt.Data and populates pkt's network 39 // header with the IPv4 header. 40 // 41 // Returns true if the header was successfully parsed. 42 func IPv4(pkt stack.PacketBufferPtr) bool { 43 hdr, ok := pkt.Data().PullUp(header.IPv4MinimumSize) 44 if !ok { 45 return false 46 } 47 ipHdr := header.IPv4(hdr) 48 49 // Header may have options, determine the true header length. 50 headerLen := int(ipHdr.HeaderLength()) 51 if headerLen < header.IPv4MinimumSize { 52 // TODO(gvisor.dev/issue/2404): Per RFC 791, IHL needs to be at least 5 in 53 // order for the packet to be valid. Figure out if we want to reject this 54 // case. 55 headerLen = header.IPv4MinimumSize 56 } 57 hdr, ok = pkt.NetworkHeader().Consume(headerLen) 58 if !ok { 59 return false 60 } 61 ipHdr = header.IPv4(hdr) 62 length := int(ipHdr.TotalLength()) - len(hdr) 63 if length < 0 { 64 return false 65 } 66 67 pkt.NetworkProtocolNumber = header.IPv4ProtocolNumber 68 pkt.Data().CapLength(length) 69 return true 70 } 71 72 // IPv6 parses an IPv6 packet found in pkt.Data and populates pkt's network 73 // header with the IPv6 header. 74 func IPv6(pkt stack.PacketBufferPtr) (proto tcpip.TransportProtocolNumber, fragID uint32, fragOffset uint16, fragMore bool, ok bool) { 75 hdr, ok := pkt.Data().PullUp(header.IPv6MinimumSize) 76 if !ok { 77 return 0, 0, 0, false, false 78 } 79 ipHdr := header.IPv6(hdr) 80 81 // Create a VV to parse the packet. We don't plan to modify anything here. 82 // dataVV consists of: 83 // - Any IPv6 header bytes after the first 40 (i.e. extensions). 84 // - The transport header, if present. 85 // - Any other payload data. 86 dataBuf := pkt.Data().ToBuffer() 87 dataBuf.TrimFront(header.IPv6MinimumSize) 88 it := header.MakeIPv6PayloadIterator(header.IPv6ExtensionHeaderIdentifier(ipHdr.NextHeader()), dataBuf) 89 defer it.Release() 90 91 // Iterate over the IPv6 extensions to find their length. 92 var nextHdr tcpip.TransportProtocolNumber 93 var extensionsSize int64 94 95 traverseExtensions: 96 for { 97 extHdr, done, err := it.Next() 98 if err != nil { 99 break 100 } 101 102 // If we exhaust the extension list, the entire packet is the IPv6 header 103 // and (possibly) extensions. 104 if done { 105 extensionsSize = dataBuf.Size() 106 break 107 } 108 109 switch extHdr := extHdr.(type) { 110 case header.IPv6FragmentExtHdr: 111 if extHdr.IsAtomic() { 112 // This fragment extension header indicates that this packet is an 113 // atomic fragment. An atomic fragment is a fragment that contains 114 // all the data required to reassemble a full packet. As per RFC 6946, 115 // atomic fragments must not interfere with "normal" fragmented traffic 116 // so we skip processing the fragment instead of feeding it through the 117 // reassembly process below. 118 continue 119 } 120 121 if fragID == 0 && fragOffset == 0 && !fragMore { 122 fragID = extHdr.ID() 123 fragOffset = extHdr.FragmentOffset() 124 fragMore = extHdr.More() 125 } 126 rawPayload := it.AsRawHeader(true /* consume */) 127 extensionsSize = dataBuf.Size() - rawPayload.Buf.Size() 128 rawPayload.Release() 129 extHdr.Release() 130 break traverseExtensions 131 132 case header.IPv6RawPayloadHeader: 133 // We've found the payload after any extensions. 134 extensionsSize = dataBuf.Size() - extHdr.Buf.Size() 135 nextHdr = tcpip.TransportProtocolNumber(extHdr.Identifier) 136 extHdr.Release() 137 break traverseExtensions 138 default: 139 extHdr.Release() 140 // Any other extension is a no-op, keep looping until we find the payload. 141 } 142 } 143 144 // Put the IPv6 header with extensions in pkt.NetworkHeader(). 145 hdr, ok = pkt.NetworkHeader().Consume(header.IPv6MinimumSize + int(extensionsSize)) 146 if !ok { 147 panic(fmt.Sprintf("pkt.Data should have at least %d bytes, but only has %d.", header.IPv6MinimumSize+extensionsSize, pkt.Data().Size())) 148 } 149 ipHdr = header.IPv6(hdr) 150 pkt.Data().CapLength(int(ipHdr.PayloadLength())) 151 pkt.NetworkProtocolNumber = header.IPv6ProtocolNumber 152 153 return nextHdr, fragID, fragOffset, fragMore, true 154 } 155 156 // UDP parses a UDP packet found in pkt.Data and populates pkt's transport 157 // header with the UDP header. 158 // 159 // Returns true if the header was successfully parsed. 160 func UDP(pkt stack.PacketBufferPtr) bool { 161 _, ok := pkt.TransportHeader().Consume(header.UDPMinimumSize) 162 pkt.TransportProtocolNumber = header.UDPProtocolNumber 163 return ok 164 } 165 166 // TCP parses a TCP packet found in pkt.Data and populates pkt's transport 167 // header with the TCP header. 168 // 169 // Returns true if the header was successfully parsed. 170 func TCP(pkt stack.PacketBufferPtr) bool { 171 // TCP header is variable length, peek at it first. 172 hdrLen := header.TCPMinimumSize 173 hdr, ok := pkt.Data().PullUp(hdrLen) 174 if !ok { 175 return false 176 } 177 178 // If the header has options, pull those up as well. 179 if offset := int(header.TCP(hdr).DataOffset()); offset > header.TCPMinimumSize && offset <= pkt.Data().Size() { 180 // TODO(gvisor.dev/issue/2404): Figure out whether to reject this kind of 181 // packets. 182 hdrLen = offset 183 } 184 185 _, ok = pkt.TransportHeader().Consume(hdrLen) 186 pkt.TransportProtocolNumber = header.TCPProtocolNumber 187 return ok 188 } 189 190 // ICMPv4 populates the packet buffer's transport header with an ICMPv4 header, 191 // if present. 192 // 193 // Returns true if an ICMPv4 header was successfully parsed. 194 func ICMPv4(pkt stack.PacketBufferPtr) bool { 195 if _, ok := pkt.TransportHeader().Consume(header.ICMPv4MinimumSize); ok { 196 pkt.TransportProtocolNumber = header.ICMPv4ProtocolNumber 197 return true 198 } 199 return false 200 } 201 202 // ICMPv6 populates the packet buffer's transport header with an ICMPv4 header, 203 // if present. 204 // 205 // Returns true if an ICMPv6 header was successfully parsed. 206 func ICMPv6(pkt stack.PacketBufferPtr) bool { 207 hdr, ok := pkt.Data().PullUp(header.ICMPv6MinimumSize) 208 if !ok { 209 return false 210 } 211 212 h := header.ICMPv6(hdr) 213 switch h.Type() { 214 case header.ICMPv6RouterSolicit, 215 header.ICMPv6RouterAdvert, 216 header.ICMPv6NeighborSolicit, 217 header.ICMPv6NeighborAdvert, 218 header.ICMPv6RedirectMsg, 219 header.ICMPv6MulticastListenerQuery, 220 header.ICMPv6MulticastListenerReport, 221 header.ICMPv6MulticastListenerV2Report, 222 header.ICMPv6MulticastListenerDone: 223 size := pkt.Data().Size() 224 if _, ok := pkt.TransportHeader().Consume(size); !ok { 225 panic(fmt.Sprintf("expected to consume the full data of size = %d bytes into transport header", size)) 226 } 227 case header.ICMPv6DstUnreachable, 228 header.ICMPv6PacketTooBig, 229 header.ICMPv6TimeExceeded, 230 header.ICMPv6ParamProblem, 231 header.ICMPv6EchoRequest, 232 header.ICMPv6EchoReply: 233 fallthrough 234 default: 235 if _, ok := pkt.TransportHeader().Consume(header.ICMPv6MinimumSize); !ok { 236 // Checked above if the packet buffer holds at least the minimum size for 237 // an ICMPv6 packet. 238 panic(fmt.Sprintf("expected to consume %d bytes", header.ICMPv6MinimumSize)) 239 } 240 } 241 pkt.TransportProtocolNumber = header.ICMPv6ProtocolNumber 242 return true 243 }