parse.go raw

   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