github.com/lightlus/netstack@v1.2.0/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/lightlus/netstack/tcpip"
    19  	"github.com/lightlus/netstack/tcpip/buffer"
    20  	"github.com/lightlus/netstack/tcpip/header"
    21  	"github.com/lightlus/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  }