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