summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
-rw-r--r--pkg/tcpip/link/channel/channel.go6
-rw-r--r--pkg/tcpip/link/sniffer/sniffer.go31
-rw-r--r--pkg/tcpip/network/ipv4/ipv4.go2
-rw-r--r--pkg/tcpip/network/ipv6/BUILD19
-rw-r--r--pkg/tcpip/network/ipv6/icmp.go137
-rw-r--r--pkg/tcpip/network/ipv6/icmp_test.go236
-rw-r--r--pkg/tcpip/network/ipv6/ipv6.go30
-rw-r--r--pkg/tcpip/stack/nic.go2
-rw-r--r--pkg/tcpip/transport/ping/endpoint.go38
-rw-r--r--pkg/tcpip/transport/ping/protocol.go16
10 files changed, 490 insertions, 27 deletions
diff --git a/pkg/tcpip/link/channel/channel.go b/pkg/tcpip/link/channel/channel.go
index 9d69f1d45..dd3fe7c87 100644
--- a/pkg/tcpip/link/channel/channel.go
+++ b/pkg/tcpip/link/channel/channel.go
@@ -71,6 +71,12 @@ func (e *Endpoint) Inject(protocol tcpip.NetworkProtocolNumber, vv *buffer.Vecto
e.dispatcher.DeliverNetworkPacket(e, "", protocol, &uu)
}
+// InjectLinkAddr injects an inbound packet with a remote link address.
+func (e *Endpoint) InjectLinkAddr(protocol tcpip.NetworkProtocolNumber, remoteLinkAddr tcpip.LinkAddress, vv *buffer.VectorisedView) {
+ uu := vv.Clone(nil)
+ e.dispatcher.DeliverNetworkPacket(e, remoteLinkAddr, protocol, &uu)
+}
+
// Attach saves the stack network-layer dispatcher for use later when packets
// are injected.
func (e *Endpoint) Attach(dispatcher stack.NetworkDispatcher) {
diff --git a/pkg/tcpip/link/sniffer/sniffer.go b/pkg/tcpip/link/sniffer/sniffer.go
index 414a7edb4..1e302f557 100644
--- a/pkg/tcpip/link/sniffer/sniffer.go
+++ b/pkg/tcpip/link/sniffer/sniffer.go
@@ -303,6 +303,37 @@ func logPacket(prefix string, protocol tcpip.NetworkProtocolNumber, b, plb []byt
log.Infof("%s %s %v -> %v %s len:%d id:%04x code:%d", prefix, transName, src, dst, icmpType, size, id, icmp.Code())
return
+ case header.ICMPv6ProtocolNumber:
+ transName = "icmp"
+ icmp := header.ICMPv6(b)
+ icmpType := "unknown"
+ switch icmp.Type() {
+ case header.ICMPv6DstUnreachable:
+ icmpType = "destination unreachable"
+ case header.ICMPv6PacketTooBig:
+ icmpType = "packet too big"
+ case header.ICMPv6TimeExceeded:
+ icmpType = "time exceeded"
+ case header.ICMPv6ParamProblem:
+ icmpType = "param problem"
+ case header.ICMPv6EchoRequest:
+ icmpType = "echo request"
+ case header.ICMPv6EchoReply:
+ icmpType = "echo reply"
+ case header.ICMPv6RouterSolicit:
+ icmpType = "router solicit"
+ case header.ICMPv6RouterAdvert:
+ icmpType = "router advert"
+ case header.ICMPv6NeighborSolicit:
+ icmpType = "neighbor solicit"
+ case header.ICMPv6NeighborAdvert:
+ icmpType = "neighbor advert"
+ case header.ICMPv6RedirectMsg:
+ icmpType = "redirect message"
+ }
+ log.Infof("%s %s %v -> %v %s len:%d id:%04x code:%d", prefix, transName, src, dst, icmpType, size, id, icmp.Code())
+ return
+
case header.UDPProtocolNumber:
transName = "udp"
udp := header.UDP(b)
diff --git a/pkg/tcpip/network/ipv4/ipv4.go b/pkg/tcpip/network/ipv4/ipv4.go
index e6b1f5128..512ef0d60 100644
--- a/pkg/tcpip/network/ipv4/ipv4.go
+++ b/pkg/tcpip/network/ipv4/ipv4.go
@@ -67,7 +67,7 @@ func newEndpoint(nicid tcpip.NICID, addr tcpip.Address, dispatcher stack.Transpo
fragmentation: fragmentation.NewFragmentation(fragmentation.HighFragThreshold, fragmentation.LowFragThreshold, fragmentation.DefaultReassembleTimeout),
}
copy(e.address[:], addr)
- e.id = stack.NetworkEndpointID{tcpip.Address(e.address[:])}
+ e.id = stack.NetworkEndpointID{LocalAddress: tcpip.Address(e.address[:])}
go e.echoReplier()
diff --git a/pkg/tcpip/network/ipv6/BUILD b/pkg/tcpip/network/ipv6/BUILD
index 1c3eccae0..2f19a659e 100644
--- a/pkg/tcpip/network/ipv6/BUILD
+++ b/pkg/tcpip/network/ipv6/BUILD
@@ -1,6 +1,6 @@
package(licenses = ["notice"]) # Apache 2.0
-load("//tools/go_stateify:defs.bzl", "go_library")
+load("//tools/go_stateify:defs.bzl", "go_library", "go_test")
go_library(
name = "ipv6",
@@ -19,3 +19,20 @@ go_library(
"//pkg/tcpip/stack",
],
)
+
+go_test(
+ name = "ipv6_test",
+ size = "small",
+ srcs = ["icmp_test.go"],
+ embed = [":ipv6"],
+ deps = [
+ "//pkg/tcpip",
+ "//pkg/tcpip/buffer",
+ "//pkg/tcpip/header",
+ "//pkg/tcpip/link/channel",
+ "//pkg/tcpip/link/sniffer",
+ "//pkg/tcpip/stack",
+ "//pkg/tcpip/transport/ping",
+ "//pkg/waiter",
+ ],
+)
diff --git a/pkg/tcpip/network/ipv6/icmp.go b/pkg/tcpip/network/ipv6/icmp.go
index 8b8539def..6db004baa 100644
--- a/pkg/tcpip/network/ipv6/icmp.go
+++ b/pkg/tcpip/network/ipv6/icmp.go
@@ -17,6 +17,7 @@ package ipv6
import (
"encoding/binary"
+ "gvisor.googlesource.com/gvisor/pkg/tcpip"
"gvisor.googlesource.com/gvisor/pkg/tcpip/buffer"
"gvisor.googlesource.com/gvisor/pkg/tcpip/header"
"gvisor.googlesource.com/gvisor/pkg/tcpip/stack"
@@ -86,5 +87,141 @@ func (e *endpoint) handleICMP(r *stack.Route, vv *buffer.VectorisedView) {
case header.ICMPv6PortUnreachable:
e.handleControl(stack.ControlPortUnreachable, 0, vv)
}
+
+ case header.ICMPv6NeighborSolicit:
+ if len(v) < header.ICMPv6NeighborSolicitMinimumSize {
+ return
+ }
+ targetAddr := tcpip.Address(v[8 : 8+16])
+ if e.linkAddrCache.CheckLocalAddress(e.nicid, ProtocolNumber, targetAddr) == 0 {
+ // We don't have a useful answer; the best we can do is ignore the request.
+ return
+ }
+ hdr := buffer.NewPrependable(int(r.MaxHeaderLength()) + header.IPv6MinimumSize + header.ICMPv6NeighborAdvertSize)
+ pkt := header.ICMPv6(hdr.Prepend(header.ICMPv6NeighborAdvertSize))
+ pkt.SetType(header.ICMPv6NeighborAdvert)
+ pkt[icmpV6FlagOffset] = ndpSolicitedFlag | ndpOverrideFlag
+ copy(pkt[icmpV6OptOffset-len(targetAddr):], targetAddr)
+ pkt[icmpV6OptOffset] = ndpOptDstLinkAddr
+ pkt[icmpV6LengthOffset] = 1
+ copy(pkt[icmpV6LengthOffset+1:], r.LocalLinkAddress[:])
+ pkt.SetChecksum(icmpChecksum(pkt, r.LocalAddress, r.RemoteAddress, nil))
+ r.WritePacket(&hdr, nil, header.ICMPv6ProtocolNumber)
+
+ e.linkAddrCache.AddLinkAddress(e.nicid, r.RemoteAddress, r.RemoteLinkAddress)
+
+ case header.ICMPv6NeighborAdvert:
+ if len(v) < header.ICMPv6NeighborAdvertSize {
+ return
+ }
+ targetAddr := tcpip.Address(v[8 : 8+16])
+ e.linkAddrCache.AddLinkAddress(e.nicid, targetAddr, r.RemoteLinkAddress)
+ if targetAddr != r.RemoteAddress {
+ e.linkAddrCache.AddLinkAddress(e.nicid, r.RemoteAddress, r.RemoteLinkAddress)
+ }
+
+ case header.ICMPv6EchoRequest:
+ if len(v) < header.ICMPv6EchoMinimumSize {
+ return
+ }
+ vv.TrimFront(header.ICMPv6EchoMinimumSize)
+ data := vv.ToView()
+ hdr := buffer.NewPrependable(int(r.MaxHeaderLength()) + header.IPv6MinimumSize + header.ICMPv6EchoMinimumSize)
+ 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)
+
+ case header.ICMPv6EchoReply:
+ if len(v) < header.ICMPv6EchoMinimumSize {
+ return
+ }
+ e.dispatcher.DeliverTransportPacket(r, header.ICMPv6ProtocolNumber, vv)
+
}
}
+
+const (
+ ndpSolicitedFlag = 1 << 6
+ ndpOverrideFlag = 1 << 5
+
+ ndpOptSrcLinkAddr = 1
+ ndpOptDstLinkAddr = 2
+
+ icmpV6FlagOffset = 4
+ icmpV6OptOffset = 24
+ icmpV6LengthOffset = 25
+
+ icmpV6HopLimit = 255
+)
+
+// solicitedNodeAddr computes the solicited-node multicast address.
+// This is used for NDP. Described in RFC 4291.
+func solicitedNodeAddr(addr tcpip.Address) tcpip.Address {
+ const solicitedNodeMulticastPrefix = "\xff\x02\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\xff"
+ return solicitedNodeMulticastPrefix + addr[len(addr)-3:]
+}
+
+var broadcastMAC = tcpip.LinkAddress([]byte{0xff, 0xff, 0xff, 0xff, 0xff, 0xff})
+
+var _ stack.LinkAddressResolver = (*protocol)(nil)
+
+// LinkAddressProtocol implements stack.LinkAddressResolver.
+func (*protocol) LinkAddressProtocol() tcpip.NetworkProtocolNumber {
+ return header.IPv6ProtocolNumber
+}
+
+// LinkAddressRequest implements stack.LinkAddressResolver.
+func (*protocol) LinkAddressRequest(addr, localAddr tcpip.Address, linkEP stack.LinkEndpoint) *tcpip.Error {
+ snaddr := solicitedNodeAddr(addr)
+ 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[icmpV6OptOffset-len(addr):], addr)
+ pkt[icmpV6OptOffset] = ndpOptSrcLinkAddr
+ pkt[icmpV6LengthOffset] = 1
+ copy(pkt[icmpV6LengthOffset+1:], 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: icmpV6HopLimit,
+ SrcAddr: r.LocalAddress,
+ DstAddr: r.RemoteAddress,
+ })
+
+ return linkEP.WritePacket(r, &hdr, nil, ProtocolNumber)
+}
+
+// ResolveStaticAddress implements stack.LinkAddressResolver.
+func (*protocol) ResolveStaticAddress(addr tcpip.Address) (tcpip.LinkAddress, bool) {
+ return "", false
+}
+
+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)
+
+ // h[2:4] is the checksum itself, set it aside to avoid checksumming the checksum.
+ 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
+}
diff --git a/pkg/tcpip/network/ipv6/icmp_test.go b/pkg/tcpip/network/ipv6/icmp_test.go
new file mode 100644
index 000000000..582bbb40e
--- /dev/null
+++ b/pkg/tcpip/network/ipv6/icmp_test.go
@@ -0,0 +1,236 @@
+// Copyright 2018 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package ipv6
+
+import (
+ "context"
+ "runtime"
+ "strings"
+ "testing"
+ "time"
+
+ "gvisor.googlesource.com/gvisor/pkg/tcpip"
+ "gvisor.googlesource.com/gvisor/pkg/tcpip/buffer"
+ "gvisor.googlesource.com/gvisor/pkg/tcpip/header"
+ "gvisor.googlesource.com/gvisor/pkg/tcpip/link/channel"
+ "gvisor.googlesource.com/gvisor/pkg/tcpip/link/sniffer"
+ "gvisor.googlesource.com/gvisor/pkg/tcpip/stack"
+ "gvisor.googlesource.com/gvisor/pkg/tcpip/transport/ping"
+ "gvisor.googlesource.com/gvisor/pkg/waiter"
+)
+
+const (
+ linkAddr0 = tcpip.LinkAddress("\x01\x02\x03\x04\x05\x06")
+ linkAddr1 = tcpip.LinkAddress("\x0a\x0b\x0c\x0d\x0e\x0f")
+)
+
+// linkLocalAddr computes the default IPv6 link-local address from
+// a link-layer (MAC) address.
+func linkLocalAddr(linkAddr tcpip.LinkAddress) tcpip.Address {
+ // Convert a 48-bit MAC to an EUI-64 and then prepend the
+ // link-local header, FE80::.
+ //
+ // The conversion is very nearly:
+ // aa:bb:cc:dd:ee:ff => FE80::Aabb:ccFF:FEdd:eeff
+ // Note the capital A. The conversion aa->Aa involves a bit flip.
+ lladdrb := [16]byte{
+ 0: 0xFE,
+ 1: 0x80,
+ 8: linkAddr[0] ^ 2,
+ 9: linkAddr[1],
+ 10: linkAddr[2],
+ 11: 0xFF,
+ 12: 0xFE,
+ 13: linkAddr[3],
+ 14: linkAddr[4],
+ 15: linkAddr[5],
+ }
+ return tcpip.Address(lladdrb[:])
+}
+
+var (
+ lladdr0 = linkLocalAddr(linkAddr0)
+ lladdr1 = linkLocalAddr(linkAddr1)
+)
+
+type testContext struct {
+ t *testing.T
+ s0 *stack.Stack
+ s1 *stack.Stack
+
+ linkEP0 *channel.Endpoint
+ linkEP1 *channel.Endpoint
+
+ icmpCh chan header.ICMPv6Type
+}
+
+type endpointWithResolutionCapability struct {
+ stack.LinkEndpoint
+}
+
+func (e endpointWithResolutionCapability) Capabilities() stack.LinkEndpointCapabilities {
+ return e.LinkEndpoint.Capabilities() | stack.CapabilityResolutionRequired
+}
+
+func newTestContext(t *testing.T) *testContext {
+ c := &testContext{
+ t: t,
+ s0: stack.New([]string{ProtocolName}, []string{ping.ProtocolName6}, stack.Options{}),
+ s1: stack.New([]string{ProtocolName}, []string{ping.ProtocolName6}, stack.Options{}),
+ icmpCh: make(chan header.ICMPv6Type, 10),
+ }
+
+ const defaultMTU = 65536
+ _, linkEP0 := channel.New(256, defaultMTU, linkAddr0)
+ c.linkEP0 = linkEP0
+ wrappedEP0 := endpointWithResolutionCapability{LinkEndpoint: linkEP0}
+ id0 := stack.RegisterLinkEndpoint(wrappedEP0)
+ if testing.Verbose() {
+ id0 = sniffer.New(id0)
+ }
+ if err := c.s0.CreateNIC(1, id0); err != nil {
+ t.Fatalf("CreateNIC s0: %v", err)
+ }
+ if err := c.s0.AddAddress(1, ProtocolNumber, lladdr0); err != nil {
+ t.Fatalf("AddAddress lladdr0: %v", err)
+ }
+ if err := c.s0.AddAddress(1, ProtocolNumber, solicitedNodeAddr(lladdr0)); err != nil {
+ t.Fatalf("AddAddress sn lladdr0: %v", err)
+ }
+
+ _, linkEP1 := channel.New(256, defaultMTU, linkAddr1)
+ c.linkEP1 = linkEP1
+ wrappedEP1 := endpointWithResolutionCapability{LinkEndpoint: linkEP1}
+ id1 := stack.RegisterLinkEndpoint(wrappedEP1)
+ if err := c.s1.CreateNIC(1, id1); err != nil {
+ t.Fatalf("CreateNIC failed: %v", err)
+ }
+ if err := c.s1.AddAddress(1, ProtocolNumber, lladdr1); err != nil {
+ t.Fatalf("AddAddress lladdr1: %v", err)
+ }
+ if err := c.s1.AddAddress(1, ProtocolNumber, solicitedNodeAddr(lladdr1)); err != nil {
+ t.Fatalf("AddAddress sn lladdr1: %v", err)
+ }
+
+ c.s0.SetRouteTable(
+ []tcpip.Route{{
+ Destination: lladdr1,
+ Mask: tcpip.Address(strings.Repeat("\xff", 16)),
+ NIC: 1,
+ }},
+ )
+ c.s1.SetRouteTable(
+ []tcpip.Route{{
+ Destination: lladdr0,
+ Mask: tcpip.Address(strings.Repeat("\xff", 16)),
+ NIC: 1,
+ }},
+ )
+
+ go c.routePackets(linkEP0.C, linkEP1)
+ go c.routePackets(linkEP1.C, linkEP0)
+
+ return c
+}
+
+func (c *testContext) countPacket(pkt channel.PacketInfo) {
+ if pkt.Proto != ProtocolNumber {
+ return
+ }
+ ipv6 := header.IPv6(pkt.Header)
+ transProto := tcpip.TransportProtocolNumber(ipv6.NextHeader())
+ if transProto != header.ICMPv6ProtocolNumber {
+ return
+ }
+ b := pkt.Header[header.IPv6MinimumSize:]
+ icmp := header.ICMPv6(b)
+ c.icmpCh <- icmp.Type()
+}
+
+func (c *testContext) routePackets(ch <-chan channel.PacketInfo, ep *channel.Endpoint) {
+ for pkt := range ch {
+ c.countPacket(pkt)
+ views := []buffer.View{pkt.Header, pkt.Payload}
+ size := len(pkt.Header) + len(pkt.Payload)
+ vv := buffer.NewVectorisedView(size, views)
+ ep.InjectLinkAddr(pkt.Proto, ep.LinkAddress(), &vv)
+ }
+}
+
+func (c *testContext) cleanup() {
+ close(c.linkEP0.C)
+ close(c.linkEP1.C)
+}
+
+func TestLinkResolution(t *testing.T) {
+ c := newTestContext(t)
+ defer c.cleanup()
+ r, err := c.s0.FindRoute(1, lladdr0, lladdr1, ProtocolNumber)
+ if err != nil {
+ t.Fatal(err)
+ }
+ defer r.Release()
+
+ hdr := buffer.NewPrependable(int(r.MaxHeaderLength()) + header.IPv6MinimumSize + header.ICMPv6EchoMinimumSize)
+ pkt := header.ICMPv6(hdr.Prepend(header.ICMPv6EchoMinimumSize))
+ pkt.SetType(header.ICMPv6EchoRequest)
+ pkt.SetChecksum(icmpChecksum(pkt, r.LocalAddress, r.RemoteAddress, nil))
+ payload := tcpip.SlicePayload(hdr.UsedBytes())
+
+ // We can't send our payload directly over the route because that
+ // doesn't provoke NDP discovery.
+ var wq waiter.Queue
+ ep, err := c.s0.NewEndpoint(header.ICMPv6ProtocolNumber, ProtocolNumber, &wq)
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ // This actually takes about 10 milliseconds, so no need to wait for
+ // a multi-minute go test timeout if something is broken.
+ ctx, cancel := context.WithTimeout(context.Background(), 2*time.Second)
+ defer cancel()
+
+ for {
+ if ctx.Err() != nil {
+ break
+ }
+ if _, err := ep.Write(payload, tcpip.WriteOptions{To: &tcpip.FullAddress{NIC: 1, Addr: lladdr1}}); err == tcpip.ErrNoLinkAddress {
+ // There's something asynchronous going on; yield to let it do its thing.
+ runtime.Gosched()
+ } else if err == nil {
+ break
+ } else {
+ t.Fatal(err)
+ }
+ }
+
+ stats := make(map[header.ICMPv6Type]int)
+ for {
+ select {
+ case <-ctx.Done():
+ t.Errorf("timeout waiting for ICMP, got: %#+v", stats)
+ return
+ case typ := <-c.icmpCh:
+ stats[typ]++
+
+ if stats[header.ICMPv6NeighborSolicit] > 0 &&
+ stats[header.ICMPv6NeighborAdvert] > 0 &&
+ stats[header.ICMPv6EchoRequest] > 0 &&
+ stats[header.ICMPv6EchoReply] > 0 {
+ return
+ }
+ }
+ }
+}
diff --git a/pkg/tcpip/network/ipv6/ipv6.go b/pkg/tcpip/network/ipv6/ipv6.go
index f48d120c5..51d34b7ec 100644
--- a/pkg/tcpip/network/ipv6/ipv6.go
+++ b/pkg/tcpip/network/ipv6/ipv6.go
@@ -42,18 +42,12 @@ const (
type address [header.IPv6AddressSize]byte
type endpoint struct {
- nicid tcpip.NICID
- id stack.NetworkEndpointID
- address address
- linkEP stack.LinkEndpoint
- dispatcher stack.TransportDispatcher
-}
-
-func newEndpoint(nicid tcpip.NICID, addr tcpip.Address, dispatcher stack.TransportDispatcher, linkEP stack.LinkEndpoint) *endpoint {
- e := &endpoint{nicid: nicid, linkEP: linkEP, dispatcher: dispatcher}
- copy(e.address[:], addr)
- e.id = stack.NetworkEndpointID{tcpip.Address(e.address[:])}
- return e
+ nicid tcpip.NICID
+ id stack.NetworkEndpointID
+ address address
+ linkEP stack.LinkEndpoint
+ linkAddrCache stack.LinkAddressCache
+ dispatcher stack.TransportDispatcher
}
// MTU implements stack.NetworkEndpoint.MTU. It returns the link-layer MTU minus
@@ -93,7 +87,7 @@ func (e *endpoint) WritePacket(r *stack.Route, hdr *buffer.Prependable, payload
ip.Encode(&header.IPv6Fields{
PayloadLength: length,
NextHeader: uint8(protocol),
- HopLimit: 65,
+ HopLimit: icmpV6HopLimit,
SrcAddr: tcpip.Address(e.address[:]),
DstAddr: r.RemoteAddress,
})
@@ -154,7 +148,15 @@ func (*protocol) ParseAddresses(v buffer.View) (src, dst tcpip.Address) {
// NewEndpoint creates a new ipv6 endpoint.
func (p *protocol) NewEndpoint(nicid tcpip.NICID, addr tcpip.Address, linkAddrCache stack.LinkAddressCache, dispatcher stack.TransportDispatcher, linkEP stack.LinkEndpoint) (stack.NetworkEndpoint, *tcpip.Error) {
- return newEndpoint(nicid, addr, dispatcher, linkEP), nil
+ e := &endpoint{
+ nicid: nicid,
+ linkEP: linkEP,
+ linkAddrCache: linkAddrCache,
+ dispatcher: dispatcher,
+ }
+ copy(e.address[:], addr)
+ e.id = stack.NetworkEndpointID{LocalAddress: tcpip.Address(e.address[:])}
+ return e, nil
}
// SetOption implements NetworkProtocol.SetOption.
diff --git a/pkg/tcpip/stack/nic.go b/pkg/tcpip/stack/nic.go
index c158e2005..77134c42a 100644
--- a/pkg/tcpip/stack/nic.go
+++ b/pkg/tcpip/stack/nic.go
@@ -165,7 +165,7 @@ func (n *NIC) addAddressLocked(protocol tcpip.NetworkProtocolNumber, addr tcpip.
// Set up cache if link address resolution exists for this protocol.
if n.linkEP.Capabilities()&CapabilityResolutionRequired != 0 {
- if linkRes := n.stack.linkAddrResolvers[protocol]; linkRes != nil {
+ if _, ok := n.stack.linkAddrResolvers[protocol]; ok {
ref.linkCache = n.stack
}
}
diff --git a/pkg/tcpip/transport/ping/endpoint.go b/pkg/tcpip/transport/ping/endpoint.go
index f097ac057..7e10c0aae 100644
--- a/pkg/tcpip/transport/ping/endpoint.go
+++ b/pkg/tcpip/transport/ping/endpoint.go
@@ -56,6 +56,7 @@ type endpoint struct {
// change throughout the lifetime of the endpoint.
stack *stack.Stack `state:"manual"`
netProto tcpip.NetworkProtocolNumber
+ transProto tcpip.TransportProtocolNumber
waiterQueue *waiter.Queue
// The following fields are used to manage the receive queue, and are
@@ -81,10 +82,11 @@ type endpoint struct {
route stack.Route `state:"manual"`
}
-func newEndpoint(stack *stack.Stack, netProto tcpip.NetworkProtocolNumber, waiterQueue *waiter.Queue) *endpoint {
+func newEndpoint(stack *stack.Stack, netProto tcpip.NetworkProtocolNumber, transProto tcpip.TransportProtocolNumber, waiterQueue *waiter.Queue) *endpoint {
return &endpoint{
stack: stack,
netProto: netProto,
+ transProto: transProto,
waiterQueue: waiterQueue,
rcvBufSizeMax: 32 * 1024,
sndBufSize: 32 * 1024,
@@ -98,7 +100,7 @@ func (e *endpoint) Close() {
e.shutdownFlags = tcpip.ShutdownRead | tcpip.ShutdownWrite
switch e.state {
case stateBound, stateConnected:
- e.stack.UnregisterTransportEndpoint(e.regNICID, []tcpip.NetworkProtocolNumber{e.netProto}, ProtocolNumber4, e.id)
+ e.stack.UnregisterTransportEndpoint(e.regNICID, []tcpip.NetworkProtocolNumber{e.netProto}, e.transProto, e.id)
}
// Close the receive list and drain it.
@@ -297,7 +299,7 @@ func (e *endpoint) Write(p tcpip.Payload, opts tcpip.WriteOptions) (uintptr, *tc
err = sendPing4(route, e.id.LocalPort, v)
case header.IPv6ProtocolNumber:
- // TODO: Support IPv6.
+ err = sendPing6(route, e.id.LocalPort, v)
}
return uintptr(len(v)), err
@@ -385,6 +387,30 @@ func sendPing4(r *stack.Route, ident uint16, data buffer.View) *tcpip.Error {
return r.WritePacket(&hdr, data, header.ICMPv4ProtocolNumber)
}
+func sendPing6(r *stack.Route, ident uint16, data buffer.View) *tcpip.Error {
+ if len(data) < header.ICMPv6EchoMinimumSize {
+ return tcpip.ErrInvalidEndpointState
+ }
+
+ // Set the ident. Sequence number is provided by the user.
+ binary.BigEndian.PutUint16(data[header.ICMPv6MinimumSize:], ident)
+
+ hdr := buffer.NewPrependable(header.ICMPv6EchoMinimumSize + int(r.MaxHeaderLength()))
+
+ icmpv6 := header.ICMPv6(hdr.Prepend(header.ICMPv6EchoMinimumSize))
+ copy(icmpv6, data)
+ data = data[header.ICMPv6EchoMinimumSize:]
+
+ if icmpv6.Type() != header.ICMPv6EchoRequest || icmpv6.Code() != 0 {
+ return tcpip.ErrInvalidEndpointState
+ }
+
+ icmpv6.SetChecksum(0)
+ icmpv6.SetChecksum(^header.Checksum(icmpv6, header.Checksum(data, 0)))
+
+ return r.WritePacket(&hdr, data, header.ICMPv6ProtocolNumber)
+}
+
func (e *endpoint) checkV4Mapped(addr *tcpip.FullAddress, allowMismatch bool) (tcpip.NetworkProtocolNumber, *tcpip.Error) {
netProto := e.netProto
if header.IsV4MappedAddress(addr.Addr) {
@@ -508,14 +534,14 @@ func (e *endpoint) registerWithStack(nicid tcpip.NICID, netProtos []tcpip.Networ
if id.LocalPort != 0 {
// The endpoint already has a local port, just attempt to
// register it.
- err := e.stack.RegisterTransportEndpoint(nicid, netProtos, ProtocolNumber4, id, e)
+ err := e.stack.RegisterTransportEndpoint(nicid, netProtos, e.transProto, id, e)
return id, err
}
// We need to find a port for the endpoint.
_, err := e.stack.PickEphemeralPort(func(p uint16) (bool, *tcpip.Error) {
id.LocalPort = p
- err := e.stack.RegisterTransportEndpoint(nicid, netProtos, ProtocolNumber4, id, e)
+ err := e.stack.RegisterTransportEndpoint(nicid, netProtos, e.transProto, id, e)
switch err {
case nil:
return true, nil
@@ -564,7 +590,7 @@ func (e *endpoint) bindLocked(addr tcpip.FullAddress, commit func() *tcpip.Error
if commit != nil {
if err := commit(); err != nil {
// Unregister, the commit failed.
- e.stack.UnregisterTransportEndpoint(addr.NIC, netProtos, ProtocolNumber4, id)
+ e.stack.UnregisterTransportEndpoint(addr.NIC, netProtos, e.transProto, id)
return err
}
}
diff --git a/pkg/tcpip/transport/ping/protocol.go b/pkg/tcpip/transport/ping/protocol.go
index 8a8192064..b885f3627 100644
--- a/pkg/tcpip/transport/ping/protocol.go
+++ b/pkg/tcpip/transport/ping/protocol.go
@@ -71,7 +71,7 @@ func (p *protocol) NewEndpoint(stack *stack.Stack, netProto tcpip.NetworkProtoco
if netProto != p.netProto() {
return nil, tcpip.ErrUnknownProtocol
}
- return newEndpoint(stack, netProto, waiterQueue), nil
+ return newEndpoint(stack, netProto, p.number, waiterQueue), nil
}
// MinimumPacketSize returns the minimum valid ping packet size.
@@ -87,8 +87,14 @@ func (p *protocol) MinimumPacketSize() int {
// ParsePorts returns the source and destination ports stored in the given ping
// packet.
-func (*protocol) ParsePorts(v buffer.View) (src, dst uint16, err *tcpip.Error) {
- return 0, binary.BigEndian.Uint16(v[header.ICMPv4MinimumSize:]), nil
+func (p *protocol) ParsePorts(v buffer.View) (src, dst uint16, err *tcpip.Error) {
+ switch p.number {
+ case ProtocolNumber4:
+ return 0, binary.BigEndian.Uint16(v[header.ICMPv4MinimumSize:]), nil
+ case ProtocolNumber6:
+ return 0, binary.BigEndian.Uint16(v[header.ICMPv6MinimumSize:]), nil
+ }
+ panic(fmt.Sprint("unknown protocol number: ", p.number))
}
// HandleUnknownDestinationPacket handles packets targeted at this protocol but
@@ -112,5 +118,7 @@ func init() {
return &protocol{ProtocolNumber4}
})
- // TODO: Support IPv6.
+ stack.RegisterTransportProtocolFactory(ProtocolName6, func() stack.TransportProtocol {
+ return &protocol{ProtocolNumber6}
+ })
}