| // 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 |
| } |