github.com/FlowerWrong/netstack@v0.0.0-20191009141956-e5848263af28/tcpip/network/ipv6/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 ipv6 16 17 import ( 18 "github.com/FlowerWrong/netstack/tcpip" 19 "github.com/FlowerWrong/netstack/tcpip/buffer" 20 "github.com/FlowerWrong/netstack/tcpip/header" 21 "github.com/FlowerWrong/netstack/tcpip/stack" 22 ) 23 24 const ( 25 // ndpHopLimit is the expected IP hop limit value of 255 for received 26 // NDP packets, as per RFC 4861 sections 4.1 - 4.5, 6.1.1, 6.1.2, 7.1.1, 27 // 7.1.2 and 8.1. If the hop limit value is not 255, nodes MUST silently 28 // drop the NDP packet. All outgoing NDP packets must use this value for 29 // its IP hop limit field. 30 ndpHopLimit = 255 31 ) 32 33 // handleControl handles the case when an ICMP packet contains the headers of 34 // the original packet that caused the ICMP one to be sent. This information is 35 // used to find out which transport endpoint must be notified about the ICMP 36 // packet. 37 func (e *endpoint) handleControl(typ stack.ControlType, extra uint32, vv buffer.VectorisedView) { 38 h := header.IPv6(vv.First()) 39 40 // We don't use IsValid() here because ICMP only requires that up to 41 // 1280 bytes of the original packet be included. So it's likely that it 42 // is truncated, which would cause IsValid to return false. 43 // 44 // Drop packet if it doesn't have the basic IPv6 header or if the 45 // original source address doesn't match the endpoint's address. 46 if len(h) < header.IPv6MinimumSize || h.SourceAddress() != e.id.LocalAddress { 47 return 48 } 49 50 // Skip the IP header, then handle the fragmentation header if there 51 // is one. 52 vv.TrimFront(header.IPv6MinimumSize) 53 p := h.TransportProtocol() 54 if p == header.IPv6FragmentHeader { 55 f := header.IPv6Fragment(vv.First()) 56 if !f.IsValid() || f.FragmentOffset() != 0 { 57 // We can't handle fragments that aren't at offset 0 58 // because they don't have the transport headers. 59 return 60 } 61 62 // Skip fragmentation header and find out the actual protocol 63 // number. 64 vv.TrimFront(header.IPv6FragmentHeaderSize) 65 p = f.TransportProtocol() 66 } 67 68 // Deliver the control packet to the transport endpoint. 69 e.dispatcher.DeliverTransportControlPacket(e.id.LocalAddress, h.DestinationAddress(), ProtocolNumber, p, typ, extra, vv) 70 } 71 72 func (e *endpoint) handleICMP(r *stack.Route, netHeader buffer.View, vv buffer.VectorisedView) { 73 stats := r.Stats().ICMP 74 sent := stats.V6PacketsSent 75 received := stats.V6PacketsReceived 76 v := vv.First() 77 if len(v) < header.ICMPv6MinimumSize { 78 received.Invalid.Increment() 79 return 80 } 81 h := header.ICMPv6(v) 82 83 // As per RFC 4861 sections 4.1 - 4.5, 6.1.1, 6.1.2, 7.1.1, 7.1.2 and 84 // 8.1, nodes MUST silently drop NDP packets where the Hop Limit field 85 // in the IPv6 header is not set to 255. 86 switch h.Type() { 87 case header.ICMPv6NeighborSolicit, 88 header.ICMPv6NeighborAdvert, 89 header.ICMPv6RouterSolicit, 90 header.ICMPv6RouterAdvert, 91 header.ICMPv6RedirectMsg: 92 if header.IPv6(netHeader).HopLimit() != ndpHopLimit { 93 received.Invalid.Increment() 94 return 95 } 96 } 97 98 // TODO(b/112892170): Meaningfully handle all ICMP types. 99 switch h.Type() { 100 case header.ICMPv6PacketTooBig: 101 received.PacketTooBig.Increment() 102 if len(v) < header.ICMPv6PacketTooBigMinimumSize { 103 received.Invalid.Increment() 104 return 105 } 106 vv.TrimFront(header.ICMPv6PacketTooBigMinimumSize) 107 mtu := h.MTU() 108 e.handleControl(stack.ControlPacketTooBig, calculateMTU(mtu), vv) 109 110 case header.ICMPv6DstUnreachable: 111 received.DstUnreachable.Increment() 112 if len(v) < header.ICMPv6DstUnreachableMinimumSize { 113 received.Invalid.Increment() 114 return 115 } 116 vv.TrimFront(header.ICMPv6DstUnreachableMinimumSize) 117 switch h.Code() { 118 case header.ICMPv6PortUnreachable: 119 e.handleControl(stack.ControlPortUnreachable, 0, vv) 120 } 121 122 case header.ICMPv6NeighborSolicit: 123 received.NeighborSolicit.Increment() 124 125 if len(v) < header.ICMPv6NeighborSolicitMinimumSize { 126 received.Invalid.Increment() 127 return 128 } 129 targetAddr := tcpip.Address(v[8:][:header.IPv6AddressSize]) 130 if e.linkAddrCache.CheckLocalAddress(e.nicid, ProtocolNumber, targetAddr) == 0 { 131 // We don't have a useful answer; the best we can do is ignore the request. 132 return 133 } 134 135 hdr := buffer.NewPrependable(int(r.MaxHeaderLength()) + header.ICMPv6NeighborAdvertSize) 136 pkt := header.ICMPv6(hdr.Prepend(header.ICMPv6NeighborAdvertSize)) 137 pkt.SetType(header.ICMPv6NeighborAdvert) 138 pkt[icmpV6FlagOffset] = ndpSolicitedFlag | ndpOverrideFlag 139 copy(pkt[icmpV6OptOffset-len(targetAddr):], targetAddr) 140 pkt[icmpV6OptOffset] = ndpOptDstLinkAddr 141 pkt[icmpV6LengthOffset] = 1 142 copy(pkt[icmpV6LengthOffset+1:], r.LocalLinkAddress[:]) 143 144 // ICMPv6 Neighbor Solicit messages are always sent to 145 // specially crafted IPv6 multicast addresses. As a result, the 146 // route we end up with here has as its LocalAddress such a 147 // multicast address. It would be nonsense to claim that our 148 // source address is a multicast address, so we manually set 149 // the source address to the target address requested in the 150 // solicit message. Since that requires mutating the route, we 151 // must first clone it. 152 r := r.Clone() 153 defer r.Release() 154 r.LocalAddress = targetAddr 155 pkt.SetChecksum(header.ICMPv6Checksum(pkt, r.LocalAddress, r.RemoteAddress, buffer.VectorisedView{})) 156 157 if err := r.WritePacket(nil /* gso */, hdr, buffer.VectorisedView{}, header.ICMPv6ProtocolNumber, 0, true /* useDefaultTTL */); err != nil { 158 sent.Dropped.Increment() 159 return 160 } 161 sent.NeighborAdvert.Increment() 162 163 case header.ICMPv6NeighborAdvert: 164 received.NeighborAdvert.Increment() 165 if len(v) < header.ICMPv6NeighborAdvertSize { 166 received.Invalid.Increment() 167 return 168 } 169 targetAddr := tcpip.Address(v[8:][:header.IPv6AddressSize]) 170 e.linkAddrCache.AddLinkAddress(e.nicid, targetAddr, r.RemoteLinkAddress) 171 if targetAddr != r.RemoteAddress { 172 e.linkAddrCache.AddLinkAddress(e.nicid, r.RemoteAddress, r.RemoteLinkAddress) 173 } 174 175 case header.ICMPv6EchoRequest: 176 received.EchoRequest.Increment() 177 if len(v) < header.ICMPv6EchoMinimumSize { 178 received.Invalid.Increment() 179 return 180 } 181 182 vv.TrimFront(header.ICMPv6EchoMinimumSize) 183 hdr := buffer.NewPrependable(int(r.MaxHeaderLength()) + header.ICMPv6EchoMinimumSize) 184 pkt := header.ICMPv6(hdr.Prepend(header.ICMPv6EchoMinimumSize)) 185 copy(pkt, h) 186 pkt.SetType(header.ICMPv6EchoReply) 187 pkt.SetChecksum(header.ICMPv6Checksum(pkt, r.LocalAddress, r.RemoteAddress, vv)) 188 if err := r.WritePacket(nil /* gso */, hdr, vv, header.ICMPv6ProtocolNumber, 0, true /* useDefaultTTL */); err != nil { 189 sent.Dropped.Increment() 190 return 191 } 192 sent.EchoReply.Increment() 193 194 case header.ICMPv6EchoReply: 195 received.EchoReply.Increment() 196 if len(v) < header.ICMPv6EchoMinimumSize { 197 received.Invalid.Increment() 198 return 199 } 200 e.dispatcher.DeliverTransportPacket(r, header.ICMPv6ProtocolNumber, netHeader, vv) 201 202 case header.ICMPv6TimeExceeded: 203 received.TimeExceeded.Increment() 204 205 case header.ICMPv6ParamProblem: 206 received.ParamProblem.Increment() 207 208 case header.ICMPv6RouterSolicit: 209 received.RouterSolicit.Increment() 210 211 case header.ICMPv6RouterAdvert: 212 received.RouterAdvert.Increment() 213 214 case header.ICMPv6RedirectMsg: 215 received.RedirectMsg.Increment() 216 217 default: 218 received.Invalid.Increment() 219 } 220 } 221 222 const ( 223 ndpSolicitedFlag = 1 << 6 224 ndpOverrideFlag = 1 << 5 225 226 ndpOptSrcLinkAddr = 1 227 ndpOptDstLinkAddr = 2 228 229 icmpV6FlagOffset = 4 230 icmpV6OptOffset = 24 231 icmpV6LengthOffset = 25 232 ) 233 234 var broadcastMAC = tcpip.LinkAddress([]byte{0xff, 0xff, 0xff, 0xff, 0xff, 0xff}) 235 236 var _ stack.LinkAddressResolver = (*protocol)(nil) 237 238 // LinkAddressProtocol implements stack.LinkAddressResolver. 239 func (*protocol) LinkAddressProtocol() tcpip.NetworkProtocolNumber { 240 return header.IPv6ProtocolNumber 241 } 242 243 // LinkAddressRequest implements stack.LinkAddressResolver. 244 func (*protocol) LinkAddressRequest(addr, localAddr tcpip.Address, linkEP stack.LinkEndpoint) *tcpip.Error { 245 snaddr := header.SolicitedNodeAddr(addr) 246 r := &stack.Route{ 247 LocalAddress: localAddr, 248 RemoteAddress: snaddr, 249 RemoteLinkAddress: broadcastMAC, 250 } 251 hdr := buffer.NewPrependable(int(linkEP.MaxHeaderLength()) + header.IPv6MinimumSize + header.ICMPv6NeighborAdvertSize) 252 pkt := header.ICMPv6(hdr.Prepend(header.ICMPv6NeighborAdvertSize)) 253 pkt.SetType(header.ICMPv6NeighborSolicit) 254 copy(pkt[icmpV6OptOffset-len(addr):], addr) 255 pkt[icmpV6OptOffset] = ndpOptSrcLinkAddr 256 pkt[icmpV6LengthOffset] = 1 257 copy(pkt[icmpV6LengthOffset+1:], linkEP.LinkAddress()) 258 pkt.SetChecksum(header.ICMPv6Checksum(pkt, r.LocalAddress, r.RemoteAddress, buffer.VectorisedView{})) 259 260 length := uint16(hdr.UsedLength()) 261 ip := header.IPv6(hdr.Prepend(header.IPv6MinimumSize)) 262 ip.Encode(&header.IPv6Fields{ 263 PayloadLength: length, 264 NextHeader: uint8(header.ICMPv6ProtocolNumber), 265 HopLimit: ndpHopLimit, 266 SrcAddr: r.LocalAddress, 267 DstAddr: r.RemoteAddress, 268 }) 269 270 // TODO(stijlist): count this in ICMP stats. 271 return linkEP.WritePacket(r, nil /* gso */, hdr, buffer.VectorisedView{}, ProtocolNumber) 272 } 273 274 // ResolveStaticAddress implements stack.LinkAddressResolver. 275 func (*protocol) ResolveStaticAddress(addr tcpip.Address) (tcpip.LinkAddress, bool) { 276 if header.IsV6MulticastAddress(addr) { 277 // RFC 2464 Transmission of IPv6 Packets over Ethernet Networks 278 // 279 // 7. Address Mapping -- Multicast 280 // 281 // An IPv6 packet with a multicast destination address DST, 282 // consisting of the sixteen octets DST[1] through DST[16], is 283 // transmitted to the Ethernet multicast address whose first 284 // two octets are the value 3333 hexadecimal and whose last 285 // four octets are the last four octets of DST. 286 return tcpip.LinkAddress([]byte{ 287 0x33, 288 0x33, 289 addr[header.IPv6AddressSize-4], 290 addr[header.IPv6AddressSize-3], 291 addr[header.IPv6AddressSize-2], 292 addr[header.IPv6AddressSize-1], 293 }), true 294 } 295 return "", false 296 }