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