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  }