diff options
Diffstat (limited to 'pkg/tcpip/network/ipv6')
-rw-r--r-- | pkg/tcpip/network/ipv6/BUILD | 19 | ||||
-rw-r--r-- | pkg/tcpip/network/ipv6/icmp.go | 137 | ||||
-rw-r--r-- | pkg/tcpip/network/ipv6/icmp_test.go | 236 | ||||
-rw-r--r-- | pkg/tcpip/network/ipv6/ipv6.go | 30 |
4 files changed, 15 insertions, 407 deletions
diff --git a/pkg/tcpip/network/ipv6/BUILD b/pkg/tcpip/network/ipv6/BUILD index 2f19a659e..1c3eccae0 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", "go_test") +load("//tools/go_stateify:defs.bzl", "go_library") go_library( name = "ipv6", @@ -19,20 +19,3 @@ 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 6db004baa..8b8539def 100644 --- a/pkg/tcpip/network/ipv6/icmp.go +++ b/pkg/tcpip/network/ipv6/icmp.go @@ -17,7 +17,6 @@ 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" @@ -87,141 +86,5 @@ 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 deleted file mode 100644 index 582bbb40e..000000000 --- a/pkg/tcpip/network/ipv6/icmp_test.go +++ /dev/null @@ -1,236 +0,0 @@ -// 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 51d34b7ec..f48d120c5 100644 --- a/pkg/tcpip/network/ipv6/ipv6.go +++ b/pkg/tcpip/network/ipv6/ipv6.go @@ -42,12 +42,18 @@ const ( type address [header.IPv6AddressSize]byte type endpoint struct { - nicid tcpip.NICID - id stack.NetworkEndpointID - address address - linkEP stack.LinkEndpoint - linkAddrCache stack.LinkAddressCache - dispatcher stack.TransportDispatcher + 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 } // MTU implements stack.NetworkEndpoint.MTU. It returns the link-layer MTU minus @@ -87,7 +93,7 @@ func (e *endpoint) WritePacket(r *stack.Route, hdr *buffer.Prependable, payload ip.Encode(&header.IPv6Fields{ PayloadLength: length, NextHeader: uint8(protocol), - HopLimit: icmpV6HopLimit, + HopLimit: 65, SrcAddr: tcpip.Address(e.address[:]), DstAddr: r.RemoteAddress, }) @@ -148,15 +154,7 @@ 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) { - 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 + return newEndpoint(nicid, addr, dispatcher, linkEP), nil } // SetOption implements NetworkProtocol.SetOption. |