github.com/flowerwrong/netstack@v0.0.0-20191009141956-e5848263af28/tcpip/network/ipv4/icmp.go (about)

     1  // Copyright 2018 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 ipv4
    16  
    17  import (
    18  	"github.com/FlowerWrong/netstack/tcpip/buffer"
    19  	"github.com/FlowerWrong/netstack/tcpip/header"
    20  	"github.com/FlowerWrong/netstack/tcpip/stack"
    21  )
    22  
    23  // handleControl handles the case when an ICMP packet contains the headers of
    24  // the original packet that caused the ICMP one to be sent. This information is
    25  // used to find out which transport endpoint must be notified about the ICMP
    26  // packet.
    27  func (e *endpoint) handleControl(typ stack.ControlType, extra uint32, vv buffer.VectorisedView) {
    28  	h := header.IPv4(vv.First())
    29  
    30  	// We don't use IsValid() here because ICMP only requires that the IP
    31  	// header plus 8 bytes of the transport header be included. So it's
    32  	// likely that it is truncated, which would cause IsValid to return
    33  	// false.
    34  	//
    35  	// Drop packet if it doesn't have the basic IPv4 header or if the
    36  	// original source address doesn't match the endpoint's address.
    37  	if len(h) < header.IPv4MinimumSize || h.SourceAddress() != e.id.LocalAddress {
    38  		return
    39  	}
    40  
    41  	hlen := int(h.HeaderLength())
    42  	if vv.Size() < hlen || h.FragmentOffset() != 0 {
    43  		// We won't be able to handle this if it doesn't contain the
    44  		// full IPv4 header, or if it's a fragment not at offset 0
    45  		// (because it won't have the transport header).
    46  		return
    47  	}
    48  
    49  	// Skip the ip header, then deliver control message.
    50  	vv.TrimFront(hlen)
    51  	p := h.TransportProtocol()
    52  	e.dispatcher.DeliverTransportControlPacket(e.id.LocalAddress, h.DestinationAddress(), ProtocolNumber, p, typ, extra, vv)
    53  }
    54  
    55  func (e *endpoint) handleICMP(r *stack.Route, netHeader buffer.View, vv buffer.VectorisedView) {
    56  	stats := r.Stats()
    57  	received := stats.ICMP.V4PacketsReceived
    58  	v := vv.First()
    59  	if len(v) < header.ICMPv4MinimumSize {
    60  		received.Invalid.Increment()
    61  		return
    62  	}
    63  	h := header.ICMPv4(v)
    64  
    65  	// TODO(b/112892170): Meaningfully handle all ICMP types.
    66  	switch h.Type() {
    67  	case header.ICMPv4Echo:
    68  		received.Echo.Increment()
    69  
    70  		// Only send a reply if the checksum is valid.
    71  		wantChecksum := h.Checksum()
    72  		// Reset the checksum field to 0 to can calculate the proper
    73  		// checksum. We'll have to reset this before we hand the packet
    74  		// off.
    75  		h.SetChecksum(0)
    76  		gotChecksum := ^header.ChecksumVV(vv, 0 /* initial */)
    77  		if gotChecksum != wantChecksum {
    78  			// It's possible that a raw socket expects to receive this.
    79  			h.SetChecksum(wantChecksum)
    80  			e.dispatcher.DeliverTransportPacket(r, header.ICMPv4ProtocolNumber, netHeader, vv)
    81  			received.Invalid.Increment()
    82  			return
    83  		}
    84  
    85  		// It's possible that a raw socket expects to receive this.
    86  		h.SetChecksum(wantChecksum)
    87  		e.dispatcher.DeliverTransportPacket(r, header.ICMPv4ProtocolNumber, netHeader, vv)
    88  
    89  		vv := vv.Clone(nil)
    90  		vv.TrimFront(header.ICMPv4MinimumSize)
    91  		hdr := buffer.NewPrependable(int(r.MaxHeaderLength()) + header.ICMPv4MinimumSize)
    92  		pkt := header.ICMPv4(hdr.Prepend(header.ICMPv4MinimumSize))
    93  		copy(pkt, h)
    94  		pkt.SetType(header.ICMPv4EchoReply)
    95  		pkt.SetChecksum(0)
    96  		pkt.SetChecksum(^header.Checksum(pkt, header.ChecksumVV(vv, 0)))
    97  		sent := stats.ICMP.V4PacketsSent
    98  		if err := r.WritePacket(nil /* gso */, hdr, vv, header.ICMPv4ProtocolNumber, 0, true /* useDefaultTTL */); err != nil {
    99  			sent.Dropped.Increment()
   100  			return
   101  		}
   102  		sent.EchoReply.Increment()
   103  
   104  	case header.ICMPv4EchoReply:
   105  		received.EchoReply.Increment()
   106  
   107  		e.dispatcher.DeliverTransportPacket(r, header.ICMPv4ProtocolNumber, netHeader, vv)
   108  
   109  	case header.ICMPv4DstUnreachable:
   110  		received.DstUnreachable.Increment()
   111  
   112  		vv.TrimFront(header.ICMPv4MinimumSize)
   113  		switch h.Code() {
   114  		case header.ICMPv4PortUnreachable:
   115  			e.handleControl(stack.ControlPortUnreachable, 0, vv)
   116  
   117  		case header.ICMPv4FragmentationNeeded:
   118  			mtu := uint32(h.MTU())
   119  			e.handleControl(stack.ControlPacketTooBig, calculateMTU(mtu), vv)
   120  		}
   121  
   122  	case header.ICMPv4SrcQuench:
   123  		received.SrcQuench.Increment()
   124  
   125  	case header.ICMPv4Redirect:
   126  		received.Redirect.Increment()
   127  
   128  	case header.ICMPv4TimeExceeded:
   129  		received.TimeExceeded.Increment()
   130  
   131  	case header.ICMPv4ParamProblem:
   132  		received.ParamProblem.Increment()
   133  
   134  	case header.ICMPv4Timestamp:
   135  		received.Timestamp.Increment()
   136  
   137  	case header.ICMPv4TimestampReply:
   138  		received.TimestampReply.Increment()
   139  
   140  	case header.ICMPv4InfoRequest:
   141  		received.InfoRequest.Increment()
   142  
   143  	case header.ICMPv4InfoReply:
   144  		received.InfoReply.Increment()
   145  
   146  	default:
   147  		received.Invalid.Increment()
   148  	}
   149  }