github.com/google/netstack@v0.0.0-20191123085552-55fcc16cd0eb/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/google/netstack/tcpip" 19 "github.com/google/netstack/tcpip/buffer" 20 "github.com/google/netstack/tcpip/header" 21 "github.com/google/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 }