blob: dd3a348f14836d39795fd51ef4c64d54bcf8cf01 [file] [log] [blame]
// Copyright 2017 The Netstack Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package ipv6
import (
"encoding/binary"
"log"
"github.com/google/netstack/tcpip"
"github.com/google/netstack/tcpip/buffer"
"github.com/google/netstack/tcpip/header"
"github.com/google/netstack/tcpip/stack"
)
const (
ndpSolicitedFlag = 1 << 6
ndpOverrideFlag = 1 << 5
ndpOptSrcLinkAddr = 1
ndpOptDstLinkAddr = 2
)
func (e *endpoint) handleICMP(r *stack.Route, vv *buffer.VectorisedView) {
v := vv.First()
if len(v) < header.ICMPv6MinimumSize {
return
}
h := header.ICMPv6(v)
switch h.Type() {
case header.ICMPv6NeighborSolicit:
if len(v) < header.ICMPv6NeighborSolicitMinimumSize {
return
}
targetAddr := tcpip.Address(v[8 : 8+16])
if e.linkAddrCache.CheckLocalAddress(e.nicid, targetAddr) == 0 {
return // we have no useful answer, ignore the request
}
hdr := buffer.NewPrependable(int(r.MaxHeaderLength()) + header.IPv6MinimumSize + header.ICMPv6NeighborAdvertSize)
pkt := header.ICMPv6(hdr.Prepend(header.ICMPv6NeighborAdvertSize))
pkt.SetType(header.ICMPv6NeighborAdvert)
pkt[4] = ndpSolicitedFlag | ndpOverrideFlag
copy(pkt[8:24], v[8:])
pkt[24] = ndpOptDstLinkAddr
pkt[25] = 1 // address length
copy(pkt[26:], r.LocalLinkAddress[:])
r.LocalAddress = targetAddr
pkt.SetChecksum(icmpChecksum(pkt, r.LocalAddress, r.RemoteAddress, nil))
r.WritePacket(&hdr, nil, header.ICMPv6ProtocolNumber, r.DefaultTTL())
e.linkAddrCache.AddLinkAddress(e.nicid, r.RemoteAddress, r.RemoteLinkAddress)
case header.ICMPv6NeighborAdvert:
if len(v) < header.ICMPv6NeighborAdvertSize {
return
}
e.linkAddrCache.AddLinkAddress(e.nicid, r.RemoteAddress, r.RemoteLinkAddress)
case header.ICMPv6EchoRequest:
if len(v) < header.ICMPv6EchoMinimumSize {
return
}
data := v[header.ICMPv6EchoMinimumSize:]
hdr := buffer.NewPrependable(int(r.MaxHeaderLength()) + header.IPv6MinimumSize + header.ICMPv6NeighborAdvertSize)
pkt := header.ICMPv6(hdr.Prepend(header.ICMPv6EchoMinimumSize))
copy(pkt, h)
pkt.SetType(header.ICMPv6EchoReply)
pkt.SetChecksum(icmpChecksum(pkt, r.LocalAddress, r.RemoteAddress, data))
r.WritePacket(&hdr, data, header.ICMPv6ProtocolNumber, r.DefaultTTL())
default:
log.Printf("got ICMPv6: type=%v, code=%v, len(v)=%d", h.Type(), h.Code(), len(v))
}
// TODO case header.ICMPv6EchoReply
}
var broadcastMAC = tcpip.LinkAddress([]byte{0xff, 0xff, 0xff, 0xff, 0xff, 0xff})
func (*protocol) LinkAddressProtocol() tcpip.NetworkProtocolNumber {
return header.IPv6ProtocolNumber
}
func (*protocol) LinkAddressRequest(addr, localAddr tcpip.Address, linkEP stack.LinkEndpoint) *tcpip.Error {
// Solicited-Node multicast address, used for NDP. Described in RFC 4291.
snaddr := "\xff\x02\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\xff" + addr[len(addr)-3:]
r := &stack.Route{
LocalAddress: localAddr,
RemoteAddress: snaddr,
RemoteLinkAddress: broadcastMAC,
}
hdr := buffer.NewPrependable(int(linkEP.MaxHeaderLength()) + header.IPv6MinimumSize + header.ICMPv6NeighborAdvertSize)
pkt := header.ICMPv6(hdr.Prepend(header.ICMPv6NeighborAdvertSize))
pkt.SetType(header.ICMPv6NeighborSolicit)
copy(pkt[8:24], addr)
pkt[24] = ndpOptSrcLinkAddr
pkt[25] = 1 // address length
copy(pkt[26:], linkEP.LinkAddress())
pkt.SetChecksum(icmpChecksum(pkt, r.LocalAddress, r.RemoteAddress, nil))
length := uint16(hdr.UsedLength())
ip := header.IPv6(hdr.Prepend(header.IPv6MinimumSize))
ip.Encode(&header.IPv6Fields{
PayloadLength: length,
NextHeader: uint8(header.ICMPv6ProtocolNumber),
HopLimit: 255,
SrcAddr: r.LocalAddress,
DstAddr: r.RemoteAddress,
})
return linkEP.WritePacket(r, &hdr, nil, ProtocolNumber)
}
func icmpChecksum(h header.ICMPv6, src, dst tcpip.Address, data []byte) uint16 {
// Calculate the IPv6 pseudo-header upper-layer checksum.
xsum := header.Checksum([]byte(src), 0)
xsum = header.Checksum([]byte(dst), xsum)
var upperLayerLength [4]byte
binary.BigEndian.PutUint32(upperLayerLength[:], uint32(len(h)+len(data)))
xsum = header.Checksum(upperLayerLength[:], xsum)
xsum = header.Checksum([]byte{0, 0, 0, uint8(header.ICMPv6ProtocolNumber)}, xsum)
xsum = header.Checksum(data, xsum)
h2, h3 := h[2], h[3]
h[2], h[3] = 0, 0
xsum = ^header.Checksum(h, xsum)
h[2], h[3] = h2, h3
return xsum
}