github.com/vpnishe/netstack@v1.10.6/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/vpnishe/netstack/tcpip" 19 "github.com/vpnishe/netstack/tcpip/buffer" 20 "github.com/vpnishe/netstack/tcpip/header" 21 "github.com/vpnishe/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.IPv6(pkt.Data.First()) 30 31 // We don't use IsValid() here because ICMP only requires that up to 32 // 1280 bytes of the original packet be included. So it's likely that it 33 // is truncated, which would cause IsValid to return false. 34 // 35 // Drop packet if it doesn't have the basic IPv6 header or if the 36 // original source address doesn't match the endpoint's address. 37 if len(h) < header.IPv6MinimumSize || h.SourceAddress() != e.id.LocalAddress { 38 return 39 } 40 41 // Skip the IP header, then handle the fragmentation header if there 42 // is one. 43 pkt.Data.TrimFront(header.IPv6MinimumSize) 44 p := h.TransportProtocol() 45 if p == header.IPv6FragmentHeader { 46 f := header.IPv6Fragment(pkt.Data.First()) 47 if !f.IsValid() || f.FragmentOffset() != 0 { 48 // We can't handle fragments that aren't at offset 0 49 // because they don't have the transport headers. 50 return 51 } 52 53 // Skip fragmentation header and find out the actual protocol 54 // number. 55 pkt.Data.TrimFront(header.IPv6FragmentHeaderSize) 56 p = f.TransportProtocol() 57 } 58 59 // Deliver the control packet to the transport endpoint. 60 e.dispatcher.DeliverTransportControlPacket(e.id.LocalAddress, h.DestinationAddress(), ProtocolNumber, p, typ, extra, pkt) 61 } 62 63 func (e *endpoint) handleICMP(r *stack.Route, netHeader buffer.View, pkt tcpip.PacketBuffer) { 64 stats := r.Stats().ICMP 65 sent := stats.V6PacketsSent 66 received := stats.V6PacketsReceived 67 v := pkt.Data.First() 68 if len(v) < header.ICMPv6MinimumSize { 69 received.Invalid.Increment() 70 return 71 } 72 h := header.ICMPv6(v) 73 iph := header.IPv6(netHeader) 74 75 // Validate ICMPv6 checksum before processing the packet. 76 // 77 // Only the first view in vv is accounted for by h. To account for the 78 // rest of vv, a shallow copy is made and the first view is removed. 79 // This copy is used as extra payload during the checksum calculation. 80 payload := pkt.Data 81 payload.RemoveFirst() 82 if got, want := h.Checksum(), header.ICMPv6Checksum(h, iph.SourceAddress(), iph.DestinationAddress(), payload); got != want { 83 received.Invalid.Increment() 84 return 85 } 86 87 // As per RFC 4861 sections 4.1 - 4.5, 6.1.1, 6.1.2, 7.1.1, 7.1.2 and 88 // 8.1, nodes MUST silently drop NDP packets where the Hop Limit field 89 // in the IPv6 header is not set to 255, or the ICMPv6 Code field is not 90 // set to 0. 91 switch h.Type() { 92 case header.ICMPv6NeighborSolicit, 93 header.ICMPv6NeighborAdvert, 94 header.ICMPv6RouterSolicit, 95 header.ICMPv6RouterAdvert, 96 header.ICMPv6RedirectMsg: 97 if iph.HopLimit() != header.NDPHopLimit { 98 received.Invalid.Increment() 99 return 100 } 101 102 if h.Code() != 0 { 103 received.Invalid.Increment() 104 return 105 } 106 } 107 108 // TODO(b/112892170): Meaningfully handle all ICMP types. 109 switch h.Type() { 110 case header.ICMPv6PacketTooBig: 111 received.PacketTooBig.Increment() 112 if len(v) < header.ICMPv6PacketTooBigMinimumSize { 113 received.Invalid.Increment() 114 return 115 } 116 pkt.Data.TrimFront(header.ICMPv6PacketTooBigMinimumSize) 117 mtu := h.MTU() 118 e.handleControl(stack.ControlPacketTooBig, calculateMTU(mtu), pkt) 119 120 case header.ICMPv6DstUnreachable: 121 received.DstUnreachable.Increment() 122 if len(v) < header.ICMPv6DstUnreachableMinimumSize { 123 received.Invalid.Increment() 124 return 125 } 126 pkt.Data.TrimFront(header.ICMPv6DstUnreachableMinimumSize) 127 switch h.Code() { 128 case header.ICMPv6PortUnreachable: 129 e.handleControl(stack.ControlPortUnreachable, 0, pkt) 130 } 131 132 case header.ICMPv6NeighborSolicit: 133 received.NeighborSolicit.Increment() 134 if len(v) < header.ICMPv6NeighborSolicitMinimumSize { 135 received.Invalid.Increment() 136 return 137 } 138 139 ns := header.NDPNeighborSolicit(h.NDPPayload()) 140 targetAddr := ns.TargetAddress() 141 s := r.Stack() 142 rxNICID := r.NICID() 143 144 isTentative, err := s.IsAddrTentative(rxNICID, targetAddr) 145 if err != nil { 146 // We will only get an error if rxNICID is unrecognized, 147 // which should not happen. For now short-circuit this 148 // packet. 149 // 150 // TODO(b/141002840): Handle this better? 151 return 152 } 153 154 if isTentative { 155 // If the target address is tentative and the source 156 // of the packet is a unicast (specified) address, then 157 // the source of the packet is attempting to perform 158 // address resolution on the target. In this case, the 159 // solicitation is silently ignored, as per RFC 4862 160 // section 5.4.3. 161 // 162 // If the target address is tentative and the source of 163 // the packet is the unspecified address (::), then we 164 // know another node is also performing DAD for the 165 // same address (since targetAddr is tentative for us, 166 // we know we are also performing DAD on it). In this 167 // case we let the stack know so it can handle such a 168 // scenario and do nothing further with the NDP NS. 169 if iph.SourceAddress() == header.IPv6Any { 170 s.DupTentativeAddrDetected(rxNICID, targetAddr) 171 } 172 173 // Do not handle neighbor solicitations targeted 174 // to an address that is tentative on the received 175 // NIC any further. 176 return 177 } 178 179 // At this point we know that targetAddr is not tentative on 180 // rxNICID so the packet is processed as defined in RFC 4861, 181 // as per RFC 4862 section 5.4.3. 182 183 if e.linkAddrCache.CheckLocalAddress(e.nicID, ProtocolNumber, targetAddr) == 0 { 184 // We don't have a useful answer; the best we can do is ignore the request. 185 return 186 } 187 188 optsSerializer := header.NDPOptionsSerializer{ 189 header.NDPTargetLinkLayerAddressOption(r.LocalLinkAddress[:]), 190 } 191 hdr := buffer.NewPrependable(int(r.MaxHeaderLength()) + header.ICMPv6NeighborAdvertMinimumSize + int(optsSerializer.Length())) 192 packet := header.ICMPv6(hdr.Prepend(header.ICMPv6NeighborAdvertSize)) 193 packet.SetType(header.ICMPv6NeighborAdvert) 194 na := header.NDPNeighborAdvert(packet.NDPPayload()) 195 na.SetSolicitedFlag(true) 196 na.SetOverrideFlag(true) 197 na.SetTargetAddress(targetAddr) 198 opts := na.Options() 199 opts.Serialize(optsSerializer) 200 201 // ICMPv6 Neighbor Solicit messages are always sent to 202 // specially crafted IPv6 multicast addresses. As a result, the 203 // route we end up with here has as its LocalAddress such a 204 // multicast address. It would be nonsense to claim that our 205 // source address is a multicast address, so we manually set 206 // the source address to the target address requested in the 207 // solicit message. Since that requires mutating the route, we 208 // must first clone it. 209 r := r.Clone() 210 defer r.Release() 211 r.LocalAddress = targetAddr 212 packet.SetChecksum(header.ICMPv6Checksum(packet, r.LocalAddress, r.RemoteAddress, buffer.VectorisedView{})) 213 214 // TODO(tamird/ghanan): there exists an explicit NDP option that is 215 // used to update the neighbor table with link addresses for a 216 // neighbor from an NS (see the Source Link Layer option RFC 217 // 4861 section 4.6.1 and section 7.2.3). 218 // 219 // Furthermore, the entirety of NDP handling here seems to be 220 // contradicted by RFC 4861. 221 e.linkAddrCache.AddLinkAddress(e.nicID, r.RemoteAddress, r.RemoteLinkAddress) 222 223 // RFC 4861 Neighbor Discovery for IP version 6 (IPv6) 224 // 225 // 7.1.2. Validation of Neighbor Advertisements 226 // 227 // The IP Hop Limit field has a value of 255, i.e., the packet 228 // could not possibly have been forwarded by a router. 229 if err := r.WritePacket(nil /* gso */, stack.NetworkHeaderParams{Protocol: header.ICMPv6ProtocolNumber, TTL: header.NDPHopLimit, TOS: stack.DefaultTOS}, tcpip.PacketBuffer{ 230 Header: hdr, 231 }); err != nil { 232 sent.Dropped.Increment() 233 return 234 } 235 sent.NeighborAdvert.Increment() 236 237 case header.ICMPv6NeighborAdvert: 238 received.NeighborAdvert.Increment() 239 if len(v) < header.ICMPv6NeighborAdvertSize { 240 received.Invalid.Increment() 241 return 242 } 243 244 na := header.NDPNeighborAdvert(h.NDPPayload()) 245 targetAddr := na.TargetAddress() 246 stack := r.Stack() 247 rxNICID := r.NICID() 248 249 isTentative, err := stack.IsAddrTentative(rxNICID, targetAddr) 250 if err != nil { 251 // We will only get an error if rxNICID is unrecognized, 252 // which should not happen. For now short-circuit this 253 // packet. 254 // 255 // TODO(b/141002840): Handle this better? 256 return 257 } 258 259 if isTentative { 260 // We just got an NA from a node that owns an address we 261 // are performing DAD on, implying the address is not 262 // unique. In this case we let the stack know so it can 263 // handle such a scenario and do nothing furthur with 264 // the NDP NA. 265 stack.DupTentativeAddrDetected(rxNICID, targetAddr) 266 return 267 } 268 269 // At this point we know that the targetAddress is not tentative 270 // on rxNICID. However, targetAddr may still be assigned to 271 // rxNICID but not tentative (it could be permanent). Such a 272 // scenario is beyond the scope of RFC 4862. As such, we simply 273 // ignore such a scenario for now and proceed as normal. 274 // 275 // TODO(b/143147598): Handle the scenario described above. Also 276 // inform the netstack integration that a duplicate address was 277 // detected outside of DAD. 278 279 e.linkAddrCache.AddLinkAddress(e.nicID, targetAddr, r.RemoteLinkAddress) 280 if targetAddr != r.RemoteAddress { 281 e.linkAddrCache.AddLinkAddress(e.nicID, r.RemoteAddress, r.RemoteLinkAddress) 282 } 283 284 case header.ICMPv6EchoRequest: 285 received.EchoRequest.Increment() 286 if len(v) < header.ICMPv6EchoMinimumSize { 287 received.Invalid.Increment() 288 return 289 } 290 pkt.Data.TrimFront(header.ICMPv6EchoMinimumSize) 291 hdr := buffer.NewPrependable(int(r.MaxHeaderLength()) + header.ICMPv6EchoMinimumSize) 292 packet := header.ICMPv6(hdr.Prepend(header.ICMPv6EchoMinimumSize)) 293 copy(packet, h) 294 packet.SetType(header.ICMPv6EchoReply) 295 packet.SetChecksum(header.ICMPv6Checksum(packet, r.LocalAddress, r.RemoteAddress, pkt.Data)) 296 if err := r.WritePacket(nil /* gso */, stack.NetworkHeaderParams{Protocol: header.ICMPv6ProtocolNumber, TTL: r.DefaultTTL(), TOS: stack.DefaultTOS}, tcpip.PacketBuffer{ 297 Header: hdr, 298 Data: pkt.Data, 299 }); err != nil { 300 sent.Dropped.Increment() 301 return 302 } 303 sent.EchoReply.Increment() 304 305 case header.ICMPv6EchoReply: 306 received.EchoReply.Increment() 307 if len(v) < header.ICMPv6EchoMinimumSize { 308 received.Invalid.Increment() 309 return 310 } 311 e.dispatcher.DeliverTransportPacket(r, header.ICMPv6ProtocolNumber, pkt) 312 313 case header.ICMPv6TimeExceeded: 314 received.TimeExceeded.Increment() 315 316 case header.ICMPv6ParamProblem: 317 received.ParamProblem.Increment() 318 319 case header.ICMPv6RouterSolicit: 320 received.RouterSolicit.Increment() 321 322 case header.ICMPv6RouterAdvert: 323 routerAddr := iph.SourceAddress() 324 325 // 326 // Validate the RA as per RFC 4861 section 6.1.2. 327 // 328 329 // Is the IP Source Address a link-local address? 330 if !header.IsV6LinkLocalAddress(routerAddr) { 331 // ...No, silently drop the packet. 332 received.Invalid.Increment() 333 return 334 } 335 336 p := h.NDPPayload() 337 338 // Is the NDP payload of sufficient size to hold a Router 339 // Advertisement? 340 if len(p) < header.NDPRAMinimumSize { 341 // ...No, silently drop the packet. 342 received.Invalid.Increment() 343 return 344 } 345 346 ra := header.NDPRouterAdvert(p) 347 opts := ra.Options() 348 349 // Are options valid as per the wire format? 350 if _, err := opts.Iter(true); err != nil { 351 // ...No, silently drop the packet. 352 received.Invalid.Increment() 353 return 354 } 355 356 // 357 // At this point, we have a valid Router Advertisement, as far 358 // as RFC 4861 section 6.1.2 is concerned. 359 // 360 361 received.RouterAdvert.Increment() 362 363 // Tell the NIC to handle the RA. 364 stack := r.Stack() 365 rxNICID := r.NICID() 366 stack.HandleNDPRA(rxNICID, routerAddr, ra) 367 368 case header.ICMPv6RedirectMsg: 369 received.RedirectMsg.Increment() 370 371 default: 372 received.Invalid.Increment() 373 } 374 } 375 376 const ( 377 ndpSolicitedFlag = 1 << 6 378 ndpOverrideFlag = 1 << 5 379 380 ndpOptSrcLinkAddr = 1 381 ndpOptDstLinkAddr = 2 382 383 icmpV6FlagOffset = 4 384 icmpV6OptOffset = 24 385 icmpV6LengthOffset = 25 386 ) 387 388 var broadcastMAC = tcpip.LinkAddress([]byte{0xff, 0xff, 0xff, 0xff, 0xff, 0xff}) 389 390 var _ stack.LinkAddressResolver = (*protocol)(nil) 391 392 // LinkAddressProtocol implements stack.LinkAddressResolver. 393 func (*protocol) LinkAddressProtocol() tcpip.NetworkProtocolNumber { 394 return header.IPv6ProtocolNumber 395 } 396 397 // LinkAddressRequest implements stack.LinkAddressResolver. 398 func (*protocol) LinkAddressRequest(addr, localAddr tcpip.Address, linkEP stack.LinkEndpoint) *tcpip.Error { 399 snaddr := header.SolicitedNodeAddr(addr) 400 r := &stack.Route{ 401 LocalAddress: localAddr, 402 RemoteAddress: snaddr, 403 RemoteLinkAddress: broadcastMAC, 404 } 405 hdr := buffer.NewPrependable(int(linkEP.MaxHeaderLength()) + header.IPv6MinimumSize + header.ICMPv6NeighborAdvertSize) 406 pkt := header.ICMPv6(hdr.Prepend(header.ICMPv6NeighborAdvertSize)) 407 pkt.SetType(header.ICMPv6NeighborSolicit) 408 copy(pkt[icmpV6OptOffset-len(addr):], addr) 409 pkt[icmpV6OptOffset] = ndpOptSrcLinkAddr 410 pkt[icmpV6LengthOffset] = 1 411 copy(pkt[icmpV6LengthOffset+1:], linkEP.LinkAddress()) 412 pkt.SetChecksum(header.ICMPv6Checksum(pkt, r.LocalAddress, r.RemoteAddress, buffer.VectorisedView{})) 413 414 length := uint16(hdr.UsedLength()) 415 ip := header.IPv6(hdr.Prepend(header.IPv6MinimumSize)) 416 ip.Encode(&header.IPv6Fields{ 417 PayloadLength: length, 418 NextHeader: uint8(header.ICMPv6ProtocolNumber), 419 HopLimit: header.NDPHopLimit, 420 SrcAddr: r.LocalAddress, 421 DstAddr: r.RemoteAddress, 422 }) 423 424 // TODO(stijlist): count this in ICMP stats. 425 return linkEP.WritePacket(r, nil /* gso */, ProtocolNumber, tcpip.PacketBuffer{ 426 Header: hdr, 427 }) 428 } 429 430 // ResolveStaticAddress implements stack.LinkAddressResolver. 431 func (*protocol) ResolveStaticAddress(addr tcpip.Address) (tcpip.LinkAddress, bool) { 432 if header.IsV6MulticastAddress(addr) { 433 // RFC 2464 Transmission of IPv6 Packets over Ethernet Networks 434 // 435 // 7. Address Mapping -- Multicast 436 // 437 // An IPv6 packet with a multicast destination address DST, 438 // consisting of the sixteen octets DST[1] through DST[16], is 439 // transmitted to the Ethernet multicast address whose first 440 // two octets are the value 3333 hexadecimal and whose last 441 // four octets are the last four octets of DST. 442 return tcpip.LinkAddress([]byte{ 443 0x33, 444 0x33, 445 addr[header.IPv6AddressSize-4], 446 addr[header.IPv6AddressSize-3], 447 addr[header.IPv6AddressSize-2], 448 addr[header.IPv6AddressSize-1], 449 }), true 450 } 451 return "", false 452 }