// Copyright 2018 The gVisor Authors. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package ipv4 import ( "encoding/binary" "gvisor.googlesource.com/gvisor/pkg/tcpip/buffer" "gvisor.googlesource.com/gvisor/pkg/tcpip/header" "gvisor.googlesource.com/gvisor/pkg/tcpip/stack" ) // handleControl handles the case when an ICMP packet contains the headers of // the original packet that caused the ICMP one to be sent. This information is // used to find out which transport endpoint must be notified about the ICMP // packet. func (e *endpoint) handleControl(typ stack.ControlType, extra uint32, vv buffer.VectorisedView) { h := header.IPv4(vv.First()) // We don't use IsValid() here because ICMP only requires that the IP // header plus 8 bytes of the transport header be included. So it's // likely that it is truncated, which would cause IsValid to return // false. // // Drop packet if it doesn't have the basic IPv4 header or if the // original source address doesn't match the endpoint's address. if len(h) < header.IPv4MinimumSize || h.SourceAddress() != e.id.LocalAddress { return } hlen := int(h.HeaderLength()) if vv.Size() < hlen || h.FragmentOffset() != 0 { // We won't be able to handle this if it doesn't contain the // full IPv4 header, or if it's a fragment not at offset 0 // (because it won't have the transport header). return } // Skip the ip header, then deliver control message. vv.TrimFront(hlen) p := h.TransportProtocol() e.dispatcher.DeliverTransportControlPacket(e.id.LocalAddress, h.DestinationAddress(), ProtocolNumber, p, typ, extra, vv) } func (e *endpoint) handleICMP(r *stack.Route, netHeader buffer.View, vv buffer.VectorisedView) { stats := r.Stats() received := stats.ICMP.V4PacketsReceived v := vv.First() if len(v) < header.ICMPv4MinimumSize { received.Invalid.Increment() return } h := header.ICMPv4(v) // TODO(b/112892170): Meaningfully handle all ICMP types. switch h.Type() { case header.ICMPv4Echo: received.Echo.Increment() if len(v) < header.ICMPv4EchoMinimumSize { received.Invalid.Increment() return } // Only send a reply if the checksum is valid. wantChecksum := h.Checksum() // Reset the checksum field to 0 to can calculate the proper // checksum. We'll have to reset this before we hand the packet // off. h.SetChecksum(0) gotChecksum := ^header.ChecksumVV(vv, 0 /* initial */) if gotChecksum != wantChecksum { // It's possible that a raw socket expects to receive this. h.SetChecksum(wantChecksum) e.dispatcher.DeliverTransportPacket(r, header.ICMPv4ProtocolNumber, netHeader, vv) received.Invalid.Increment() return } // It's possible that a raw socket expects to receive this. h.SetChecksum(wantChecksum) e.dispatcher.DeliverTransportPacket(r, header.ICMPv4ProtocolNumber, netHeader, vv) vv := vv.Clone(nil) vv.TrimFront(header.ICMPv4EchoMinimumSize) hdr := buffer.NewPrependable(int(r.MaxHeaderLength()) + header.ICMPv4EchoMinimumSize) pkt := header.ICMPv4(hdr.Prepend(header.ICMPv4EchoMinimumSize)) copy(pkt, h) pkt.SetType(header.ICMPv4EchoReply) pkt.SetChecksum(^header.Checksum(pkt, header.ChecksumVV(vv, 0))) sent := stats.ICMP.V4PacketsSent if err := r.WritePacket(nil /* gso */, hdr, vv, header.ICMPv4ProtocolNumber, r.DefaultTTL()); err != nil { sent.Dropped.Increment() return } sent.EchoReply.Increment() case header.ICMPv4EchoReply: received.EchoReply.Increment() if len(v) < header.ICMPv4EchoMinimumSize { received.Invalid.Increment() return } e.dispatcher.DeliverTransportPacket(r, header.ICMPv4ProtocolNumber, netHeader, vv) case header.ICMPv4DstUnreachable: received.DstUnreachable.Increment() if len(v) < header.ICMPv4DstUnreachableMinimumSize { received.Invalid.Increment() return } vv.TrimFront(header.ICMPv4DstUnreachableMinimumSize) switch h.Code() { case header.ICMPv4PortUnreachable: e.handleControl(stack.ControlPortUnreachable, 0, vv) case header.ICMPv4FragmentationNeeded: mtu := uint32(binary.BigEndian.Uint16(v[header.ICMPv4DstUnreachableMinimumSize-2:])) e.handleControl(stack.ControlPacketTooBig, calculateMTU(mtu), vv) } case header.ICMPv4SrcQuench: received.SrcQuench.Increment() case header.ICMPv4Redirect: received.Redirect.Increment() case header.ICMPv4TimeExceeded: received.TimeExceeded.Increment() case header.ICMPv4ParamProblem: received.ParamProblem.Increment() case header.ICMPv4Timestamp: received.Timestamp.Increment() case header.ICMPv4TimestampReply: received.TimestampReply.Increment() case header.ICMPv4InfoRequest: received.InfoRequest.Increment() case header.ICMPv4InfoReply: received.InfoReply.Increment() default: received.Invalid.Increment() } }