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 "gvisor.dev/gvisor/pkg/tcpip"
22 "gvisor.dev/gvisor/pkg/tcpip/header"
23 "gvisor.dev/gvisor/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.PacketBuffer) 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.PacketBuffer) 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.PacketBuffer) (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.PacketBuffer) 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.PacketBuffer) 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.PacketBuffer) 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.PacketBuffer) 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 }
244