Move IP state from NIC to NetworkEndpoint/Protocol

* Add network address to network endpoints.
Hold network-specific state in the NetworkEndpoint instead of the stack.
This results in the stack no longer needing to "know" about the network
endpoints and special case certain work for various endpoints
(e.g. IPv6 DAD).

* Provide NetworkEndpoints with an NetworkInterface interface.
Instead of just passing the NIC ID of a NIC, pass an interface so the
network endpoint may query other information about the NIC such as
whether or not it is a loopback device.

* Move NDP code and state to the IPv6 package.
NDP is IPv6 specific so there is no need for it to live in the stack.

* Control forwarding through NetworkProtocols instead of Stack
Forwarding should be controlled on a per-network protocol basis so
forwarding configurations are now controlled through network protocols.

* Remove stack.referencedNetworkEndpoint.
Now that addresses are exposed via AddressEndpoint and only one
NetworkEndpoint is created per interface, there is no need for a
referenced NetworkEndpoint.

* Assume network teardown methods are infallible.

Fixes #3871, #3916

PiperOrigin-RevId: 334319433
This commit is contained in:
Ghanan Gowripalan
2020-09-29 00:20:41 -07:00
committed by gVisor bot
parent 028e045da9
commit 48915bdedb
29 changed files with 3744 additions and 2353 deletions
+1
View File
@@ -9,6 +9,7 @@ go_test(
"ip_test.go",
],
deps = [
"//pkg/sync",
"//pkg/tcpip",
"//pkg/tcpip/buffer",
"//pkg/tcpip/header",
+64 -12
View File
@@ -18,6 +18,8 @@
package arp
import (
"sync/atomic"
"gvisor.dev/gvisor/pkg/tcpip"
"gvisor.dev/gvisor/pkg/tcpip/buffer"
"gvisor.dev/gvisor/pkg/tcpip/header"
@@ -33,15 +35,57 @@ const (
ProtocolAddress = tcpip.Address("arp")
)
// endpoint implements stack.NetworkEndpoint.
var _ stack.AddressableEndpoint = (*endpoint)(nil)
var _ stack.NetworkEndpoint = (*endpoint)(nil)
type endpoint struct {
protocol *protocol
nicID tcpip.NICID
stack.AddressableEndpointState
protocol *protocol
// enabled is set to 1 when the NIC is enabled and 0 when it is disabled.
//
// Must be accessed using atomic operations.
enabled uint32
nic stack.NetworkInterface
linkEP stack.LinkEndpoint
linkAddrCache stack.LinkAddressCache
nud stack.NUDHandler
}
func (e *endpoint) Enable() *tcpip.Error {
if !e.nic.Enabled() {
return tcpip.ErrNotPermitted
}
e.setEnabled(true)
return nil
}
func (e *endpoint) Enabled() bool {
return e.nic.Enabled() && e.isEnabled()
}
// isEnabled returns true if the endpoint is enabled, regardless of the
// enabled status of the NIC.
func (e *endpoint) isEnabled() bool {
return atomic.LoadUint32(&e.enabled) == 1
}
// setEnabled sets the enabled status for the endpoint.
func (e *endpoint) setEnabled(v bool) {
if v {
atomic.StoreUint32(&e.enabled, 1)
} else {
atomic.StoreUint32(&e.enabled, 0)
}
}
func (e *endpoint) Disable() {
e.setEnabled(false)
}
// DefaultTTL is unused for ARP. It implements stack.NetworkEndpoint.
func (e *endpoint) DefaultTTL() uint8 {
return 0
@@ -53,7 +97,7 @@ func (e *endpoint) MTU() uint32 {
}
func (e *endpoint) NICID() tcpip.NICID {
return e.nicID
return e.nic.ID()
}
func (e *endpoint) Capabilities() stack.LinkEndpointCapabilities {
@@ -64,7 +108,9 @@ func (e *endpoint) MaxHeaderLength() uint16 {
return e.linkEP.MaxHeaderLength() + header.ARPSize
}
func (e *endpoint) Close() {}
func (e *endpoint) Close() {
e.AddressableEndpointState.Cleanup()
}
func (e *endpoint) WritePacket(*stack.Route, *stack.GSO, stack.NetworkHeaderParams, *stack.PacketBuffer) *tcpip.Error {
return tcpip.ErrNotSupported
@@ -85,6 +131,10 @@ func (e *endpoint) WriteHeaderIncludedPacket(r *stack.Route, pkt *stack.PacketBu
}
func (e *endpoint) HandlePacket(r *stack.Route, pkt *stack.PacketBuffer) {
if !e.isEnabled() {
return
}
h := header.ARP(pkt.NetworkHeader().View())
if !h.IsValid() {
return
@@ -95,15 +145,15 @@ func (e *endpoint) HandlePacket(r *stack.Route, pkt *stack.PacketBuffer) {
localAddr := tcpip.Address(h.ProtocolAddressTarget())
if e.nud == nil {
if e.linkAddrCache.CheckLocalAddress(e.nicID, header.IPv4ProtocolNumber, localAddr) == 0 {
if e.linkAddrCache.CheckLocalAddress(e.NICID(), header.IPv4ProtocolNumber, localAddr) == 0 {
return // we have no useful answer, ignore the request
}
addr := tcpip.Address(h.ProtocolAddressSender())
linkAddr := tcpip.LinkAddress(h.HardwareAddressSender())
e.linkAddrCache.AddLinkAddress(e.nicID, addr, linkAddr)
e.linkAddrCache.AddLinkAddress(e.NICID(), addr, linkAddr)
} else {
if r.Stack().CheckLocalAddress(e.nicID, header.IPv4ProtocolNumber, localAddr) == 0 {
if r.Stack().CheckLocalAddress(e.NICID(), header.IPv4ProtocolNumber, localAddr) == 0 {
return // we have no useful answer, ignore the request
}
@@ -129,7 +179,7 @@ func (e *endpoint) HandlePacket(r *stack.Route, pkt *stack.PacketBuffer) {
linkAddr := tcpip.LinkAddress(h.HardwareAddressSender())
if e.nud == nil {
e.linkAddrCache.AddLinkAddress(e.nicID, addr, linkAddr)
e.linkAddrCache.AddLinkAddress(e.NICID(), addr, linkAddr)
return
}
@@ -161,14 +211,16 @@ func (*protocol) ParseAddresses(v buffer.View) (src, dst tcpip.Address) {
return tcpip.Address(h.ProtocolAddressSender()), ProtocolAddress
}
func (p *protocol) NewEndpoint(nicID tcpip.NICID, linkAddrCache stack.LinkAddressCache, nud stack.NUDHandler, dispatcher stack.TransportDispatcher, sender stack.LinkEndpoint, st *stack.Stack) stack.NetworkEndpoint {
return &endpoint{
func (p *protocol) NewEndpoint(nic stack.NetworkInterface, linkAddrCache stack.LinkAddressCache, nud stack.NUDHandler, dispatcher stack.TransportDispatcher, sender stack.LinkEndpoint, st *stack.Stack) stack.NetworkEndpoint {
e := &endpoint{
protocol: p,
nicID: nicID,
nic: nic,
linkEP: sender,
linkAddrCache: linkAddrCache,
nud: nud,
}
e.AddressableEndpointState.Init(e)
return e
}
// LinkAddressProtocol implements stack.LinkAddressResolver.LinkAddressProtocol.
+147 -7
View File
@@ -17,6 +17,7 @@ package ip_test
import (
"testing"
"gvisor.dev/gvisor/pkg/sync"
"gvisor.dev/gvisor/pkg/tcpip"
"gvisor.dev/gvisor/pkg/tcpip/buffer"
"gvisor.dev/gvisor/pkg/tcpip/header"
@@ -248,11 +249,126 @@ func buildDummyStack(t *testing.T) *stack.Stack {
return s
}
var _ stack.NetworkInterface = (*testInterface)(nil)
type testInterface struct {
mu struct {
sync.RWMutex
disabled bool
}
}
func (*testInterface) ID() tcpip.NICID {
return nicID
}
func (*testInterface) IsLoopback() bool {
return false
}
func (*testInterface) Name() string {
return ""
}
func (t *testInterface) Enabled() bool {
t.mu.RLock()
defer t.mu.RUnlock()
return !t.mu.disabled
}
func (t *testInterface) setEnabled(v bool) {
t.mu.Lock()
defer t.mu.Unlock()
t.mu.disabled = !v
}
func TestEnableWhenNICDisabled(t *testing.T) {
tests := []struct {
name string
protocolFactory stack.NetworkProtocolFactory
protoNum tcpip.NetworkProtocolNumber
}{
{
name: "IPv4",
protocolFactory: ipv4.NewProtocol,
protoNum: ipv4.ProtocolNumber,
},
{
name: "IPv6",
protocolFactory: ipv6.NewProtocol,
protoNum: ipv6.ProtocolNumber,
},
}
for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
var nic testInterface
nic.setEnabled(false)
s := stack.New(stack.Options{
NetworkProtocols: []stack.NetworkProtocolFactory{test.protocolFactory},
})
p := s.NetworkProtocolInstance(test.protoNum)
// We pass nil for all parameters except the NetworkInterface and Stack
// since Enable only depends on these.
ep := p.NewEndpoint(&nic, nil, nil, nil, nil, s)
// The endpoint should initially be disabled, regardless the NIC's enabled
// status.
if ep.Enabled() {
t.Fatal("got ep.Enabled() = true, want = false")
}
nic.setEnabled(true)
if ep.Enabled() {
t.Fatal("got ep.Enabled() = true, want = false")
}
// Attempting to enable the endpoint while the NIC is disabled should
// fail.
nic.setEnabled(false)
if err := ep.Enable(); err != tcpip.ErrNotPermitted {
t.Fatalf("got ep.Enable() = %s, want = %s", err, tcpip.ErrNotPermitted)
}
// ep should consider the NIC's enabled status when determining its own
// enabled status so we "enable" the NIC to read just the endpoint's
// enabled status.
nic.setEnabled(true)
if ep.Enabled() {
t.Fatal("got ep.Enabled() = true, want = false")
}
// Enabling the interface after the NIC has been enabled should succeed.
if err := ep.Enable(); err != nil {
t.Fatalf("ep.Enable(): %s", err)
}
if !ep.Enabled() {
t.Fatal("got ep.Enabled() = false, want = true")
}
// ep should consider the NIC's enabled status when determining its own
// enabled status.
nic.setEnabled(false)
if ep.Enabled() {
t.Fatal("got ep.Enabled() = true, want = false")
}
// Disabling the endpoint when the NIC is enabled should make the endpoint
// disabled.
nic.setEnabled(true)
ep.Disable()
if ep.Enabled() {
t.Fatal("got ep.Enabled() = true, want = false")
}
})
}
}
func TestIPv4Send(t *testing.T) {
o := testObject{t: t, v4: true}
s := buildDummyStack(t)
proto := s.NetworkProtocolInstance(ipv4.ProtocolNumber)
ep := proto.NewEndpoint(nicID, nil, nil, nil, &o, s)
ep := proto.NewEndpoint(&testInterface{}, nil, nil, nil, &o, s)
defer ep.Close()
// Allocate and initialize the payload view.
@@ -290,9 +406,13 @@ func TestIPv4Receive(t *testing.T) {
o := testObject{t: t, v4: true}
s := buildDummyStack(t)
proto := s.NetworkProtocolInstance(ipv4.ProtocolNumber)
ep := proto.NewEndpoint(nicID, nil, nil, &o, nil, s)
ep := proto.NewEndpoint(&testInterface{}, nil, nil, &o, nil, s)
defer ep.Close()
if err := ep.Enable(); err != nil {
t.Fatalf("ep.Enable(): %s", err)
}
totalLen := header.IPv4MinimumSize + 30
view := buffer.NewView(totalLen)
ip := header.IPv4(view)
@@ -361,9 +481,13 @@ func TestIPv4ReceiveControl(t *testing.T) {
o := testObject{t: t}
s := buildDummyStack(t)
proto := s.NetworkProtocolInstance(ipv4.ProtocolNumber)
ep := proto.NewEndpoint(nicID, nil, nil, &o, nil, s)
ep := proto.NewEndpoint(&testInterface{}, nil, nil, &o, nil, s)
defer ep.Close()
if err := ep.Enable(); err != nil {
t.Fatalf("ep.Enable(): %s", err)
}
const dataOffset = header.IPv4MinimumSize*2 + header.ICMPv4MinimumSize
view := buffer.NewView(dataOffset + 8)
@@ -423,9 +547,13 @@ func TestIPv4FragmentationReceive(t *testing.T) {
o := testObject{t: t, v4: true}
s := buildDummyStack(t)
proto := s.NetworkProtocolInstance(ipv4.ProtocolNumber)
ep := proto.NewEndpoint(nicID, nil, nil, &o, nil, s)
ep := proto.NewEndpoint(&testInterface{}, nil, nil, &o, nil, s)
defer ep.Close()
if err := ep.Enable(); err != nil {
t.Fatalf("ep.Enable(): %s", err)
}
totalLen := header.IPv4MinimumSize + 24
frag1 := buffer.NewView(totalLen)
@@ -501,9 +629,13 @@ func TestIPv6Send(t *testing.T) {
o := testObject{t: t}
s := buildDummyStack(t)
proto := s.NetworkProtocolInstance(ipv6.ProtocolNumber)
ep := proto.NewEndpoint(nicID, nil, nil, &o, channel.New(0, 1280, ""), s)
ep := proto.NewEndpoint(&testInterface{}, nil, nil, &o, channel.New(0, 1280, ""), s)
defer ep.Close()
if err := ep.Enable(); err != nil {
t.Fatalf("ep.Enable(): %s", err)
}
// Allocate and initialize the payload view.
payload := buffer.NewView(100)
for i := 0; i < len(payload); i++ {
@@ -539,9 +671,13 @@ func TestIPv6Receive(t *testing.T) {
o := testObject{t: t}
s := buildDummyStack(t)
proto := s.NetworkProtocolInstance(ipv6.ProtocolNumber)
ep := proto.NewEndpoint(nicID, nil, nil, &o, nil, s)
ep := proto.NewEndpoint(&testInterface{}, nil, nil, &o, nil, s)
defer ep.Close()
if err := ep.Enable(); err != nil {
t.Fatalf("ep.Enable(): %s", err)
}
totalLen := header.IPv6MinimumSize + 30
view := buffer.NewView(totalLen)
ip := header.IPv6(view)
@@ -619,9 +755,13 @@ func TestIPv6ReceiveControl(t *testing.T) {
o := testObject{t: t}
s := buildDummyStack(t)
proto := s.NetworkProtocolInstance(ipv6.ProtocolNumber)
ep := proto.NewEndpoint(nicID, nil, nil, &o, nil, s)
ep := proto.NewEndpoint(&testInterface{}, nil, nil, &o, nil, s)
defer ep.Close()
if err := ep.Enable(); err != nil {
t.Fatalf("ep.Enable(): %s", err)
}
dataOffset := header.IPv6MinimumSize*2 + header.ICMPv6MinimumSize
if c.fragmentOffset != nil {
dataOffset += header.IPv6FragmentHeaderSize
+1
View File
@@ -10,6 +10,7 @@ go_library(
],
visibility = ["//visibility:public"],
deps = [
"//pkg/sync",
"//pkg/tcpip",
"//pkg/tcpip/buffer",
"//pkg/tcpip/header",
+232 -12
View File
@@ -19,6 +19,7 @@ import (
"fmt"
"sync/atomic"
"gvisor.dev/gvisor/pkg/sync"
"gvisor.dev/gvisor/pkg/tcpip"
"gvisor.dev/gvisor/pkg/tcpip/buffer"
"gvisor.dev/gvisor/pkg/tcpip/header"
@@ -47,23 +48,118 @@ const (
fragmentblockSize = 8
)
var ipv4BroadcastAddr = header.IPv4Broadcast.WithPrefix()
var _ stack.GroupAddressableEndpoint = (*endpoint)(nil)
var _ stack.AddressableEndpoint = (*endpoint)(nil)
var _ stack.NetworkEndpoint = (*endpoint)(nil)
type endpoint struct {
nicID tcpip.NICID
nic stack.NetworkInterface
linkEP stack.LinkEndpoint
dispatcher stack.TransportDispatcher
protocol *protocol
stack *stack.Stack
// enabled is set to 1 when the enpoint is enabled and 0 when it is
// disabled.
//
// Must be accessed using atomic operations.
enabled uint32
mu struct {
sync.RWMutex
addressableEndpointState stack.AddressableEndpointState
}
}
// NewEndpoint creates a new ipv4 endpoint.
func (p *protocol) NewEndpoint(nicID tcpip.NICID, _ stack.LinkAddressCache, _ stack.NUDHandler, dispatcher stack.TransportDispatcher, linkEP stack.LinkEndpoint, st *stack.Stack) stack.NetworkEndpoint {
return &endpoint{
nicID: nicID,
func (p *protocol) NewEndpoint(nic stack.NetworkInterface, _ stack.LinkAddressCache, _ stack.NUDHandler, dispatcher stack.TransportDispatcher, linkEP stack.LinkEndpoint, st *stack.Stack) stack.NetworkEndpoint {
e := &endpoint{
nic: nic,
linkEP: linkEP,
dispatcher: dispatcher,
protocol: p,
stack: st,
}
e.mu.addressableEndpointState.Init(e)
return e
}
// Enable implements stack.NetworkEndpoint.
func (e *endpoint) Enable() *tcpip.Error {
e.mu.Lock()
defer e.mu.Unlock()
// If the NIC is not enabled, the endpoint can't do anything meaningful so
// don't enable the endpoint.
if !e.nic.Enabled() {
return tcpip.ErrNotPermitted
}
// If the endpoint is already enabled, there is nothing for it to do.
if !e.setEnabled(true) {
return nil
}
// Create an endpoint to receive broadcast packets on this interface.
ep, err := e.mu.addressableEndpointState.AddAndAcquirePermanentAddress(ipv4BroadcastAddr, stack.NeverPrimaryEndpoint, stack.AddressConfigStatic, false /* deprecated */)
if err != nil {
return err
}
// We have no need for the address endpoint.
ep.DecRef()
// As per RFC 1122 section 3.3.7, all hosts should join the all-hosts
// multicast group. Note, the IANA calls the all-hosts multicast group the
// all-systems multicast group.
_, err = e.mu.addressableEndpointState.JoinGroup(header.IPv4AllSystems)
return err
}
// Enabled implements stack.NetworkEndpoint.
func (e *endpoint) Enabled() bool {
return e.nic.Enabled() && e.isEnabled()
}
// isEnabled returns true if the endpoint is enabled, regardless of the
// enabled status of the NIC.
func (e *endpoint) isEnabled() bool {
return atomic.LoadUint32(&e.enabled) == 1
}
// setEnabled sets the enabled status for the endpoint.
//
// Returns true if the enabled status was updated.
func (e *endpoint) setEnabled(v bool) bool {
if v {
return atomic.SwapUint32(&e.enabled, 1) == 0
}
return atomic.SwapUint32(&e.enabled, 0) == 1
}
// Disable implements stack.NetworkEndpoint.
func (e *endpoint) Disable() {
e.mu.Lock()
defer e.mu.Unlock()
e.disableLocked()
}
func (e *endpoint) disableLocked() {
if !e.setEnabled(false) {
return
}
// The endpoint may have already left the multicast group.
if _, err := e.mu.addressableEndpointState.LeaveGroup(header.IPv4AllSystems); err != nil && err != tcpip.ErrBadLocalAddress {
panic(fmt.Sprintf("unexpected error when leaving group = %s: %s", header.IPv4AllSystems, err))
}
// The address may have already been removed.
if err := e.mu.addressableEndpointState.RemovePermanentAddress(ipv4BroadcastAddr.Address); err != nil && err != tcpip.ErrBadLocalAddress {
panic(fmt.Sprintf("unexpected error when removing address = %s: %s", ipv4BroadcastAddr.Address, err))
}
}
// DefaultTTL is the default time-to-live value for this endpoint.
@@ -77,14 +173,14 @@ func (e *endpoint) MTU() uint32 {
return calculateMTU(e.linkEP.MTU())
}
// Capabilities implements stack.NetworkEndpoint.Capabilities.
// Capabilities implements stack.NetworkEndpoint.
func (e *endpoint) Capabilities() stack.LinkEndpointCapabilities {
return e.linkEP.Capabilities()
}
// NICID returns the ID of the NIC this endpoint belongs to.
func (e *endpoint) NICID() tcpip.NICID {
return e.nicID
return e.nic.ID()
}
// MaxHeaderLength returns the maximum length needed by ipv4 headers (and
@@ -385,6 +481,10 @@ func (e *endpoint) WriteHeaderIncludedPacket(r *stack.Route, pkt *stack.PacketBu
// HandlePacket is called by the link layer when new ipv4 packets arrive for
// this endpoint.
func (e *endpoint) HandlePacket(r *stack.Route, pkt *stack.PacketBuffer) {
if !e.isEnabled() {
return
}
h := header.IPv4(pkt.NetworkHeader().View())
if !h.IsValid(pkt.Data.Size() + pkt.NetworkHeader().View().Size() + pkt.TransportHeader().View().Size()) {
r.Stats().IP.MalformedPacketsReceived.Increment()
@@ -475,17 +575,123 @@ func (e *endpoint) HandlePacket(r *stack.Route, pkt *stack.PacketBuffer) {
}
// Close cleans up resources associated with the endpoint.
func (e *endpoint) Close() {}
func (e *endpoint) Close() {
e.mu.Lock()
defer e.mu.Unlock()
e.disableLocked()
e.mu.addressableEndpointState.Cleanup()
}
// AddAndAcquirePermanentAddress implements stack.AddressableEndpoint.
func (e *endpoint) AddAndAcquirePermanentAddress(addr tcpip.AddressWithPrefix, peb stack.PrimaryEndpointBehavior, configType stack.AddressConfigType, deprecated bool) (stack.AddressEndpoint, *tcpip.Error) {
e.mu.Lock()
defer e.mu.Unlock()
return e.mu.addressableEndpointState.AddAndAcquirePermanentAddress(addr, peb, configType, deprecated)
}
// RemovePermanentAddress implements stack.AddressableEndpoint.
func (e *endpoint) RemovePermanentAddress(addr tcpip.Address) *tcpip.Error {
e.mu.Lock()
defer e.mu.Unlock()
return e.mu.addressableEndpointState.RemovePermanentAddress(addr)
}
// AcquireAssignedAddress implements stack.AddressableEndpoint.
func (e *endpoint) AcquireAssignedAddress(localAddr tcpip.Address, allowTemp bool, tempPEB stack.PrimaryEndpointBehavior) stack.AddressEndpoint {
e.mu.Lock()
defer e.mu.Unlock()
loopback := e.nic.IsLoopback()
addressEndpoint := e.mu.addressableEndpointState.ReadOnly().AddrOrMatching(localAddr, allowTemp, func(addressEndpoint stack.AddressEndpoint) bool {
subnet := addressEndpoint.AddressWithPrefix().Subnet()
// IPv4 has a notion of a subnet broadcast address and considers the
// loopback interface bound to an address's whole subnet (on linux).
return subnet.IsBroadcast(localAddr) || (loopback && subnet.Contains(localAddr))
})
if addressEndpoint != nil {
return addressEndpoint
}
if !allowTemp {
return nil
}
addr := localAddr.WithPrefix()
addressEndpoint, err := e.mu.addressableEndpointState.AddAndAcquireTemporaryAddress(addr, tempPEB)
if err != nil {
// AddAddress only returns an error if the address is already assigned,
// but we just checked above if the address exists so we expect no error.
panic(fmt.Sprintf("e.mu.addressableEndpointState.AddAndAcquireTemporaryAddress(%s, %d): %s", addr, tempPEB, err))
}
return addressEndpoint
}
// AcquirePrimaryAddress implements stack.AddressableEndpoint.
func (e *endpoint) AcquirePrimaryAddress(remoteAddr tcpip.Address, allowExpired bool) stack.AddressEndpoint {
e.mu.RLock()
defer e.mu.RUnlock()
return e.mu.addressableEndpointState.AcquirePrimaryAddress(remoteAddr, allowExpired)
}
// PrimaryAddresses implements stack.AddressableEndpoint.
func (e *endpoint) PrimaryAddresses() []tcpip.AddressWithPrefix {
e.mu.RLock()
defer e.mu.RUnlock()
return e.mu.addressableEndpointState.PrimaryAddresses()
}
// PermanentAddresses implements stack.AddressableEndpoint.
func (e *endpoint) PermanentAddresses() []tcpip.AddressWithPrefix {
e.mu.RLock()
defer e.mu.RUnlock()
return e.mu.addressableEndpointState.PermanentAddresses()
}
// JoinGroup implements stack.GroupAddressableEndpoint.
func (e *endpoint) JoinGroup(addr tcpip.Address) (bool, *tcpip.Error) {
if !header.IsV4MulticastAddress(addr) {
return false, tcpip.ErrBadAddress
}
e.mu.Lock()
defer e.mu.Unlock()
return e.mu.addressableEndpointState.JoinGroup(addr)
}
// LeaveGroup implements stack.GroupAddressableEndpoint.
func (e *endpoint) LeaveGroup(addr tcpip.Address) (bool, *tcpip.Error) {
e.mu.Lock()
defer e.mu.Unlock()
return e.mu.addressableEndpointState.LeaveGroup(addr)
}
// IsInGroup implements stack.GroupAddressableEndpoint.
func (e *endpoint) IsInGroup(addr tcpip.Address) bool {
e.mu.RLock()
defer e.mu.RUnlock()
return e.mu.addressableEndpointState.IsInGroup(addr)
}
var _ stack.ForwardingNetworkProtocol = (*protocol)(nil)
var _ stack.NetworkProtocol = (*protocol)(nil)
type protocol struct {
// defaultTTL is the current default TTL for the protocol. Only the
// uint8 portion of it is meaningful.
//
// Must be accessed using atomic operations.
defaultTTL uint32
// forwarding is set to 1 when the protocol has forwarding enabled and 0
// when it is disabled.
//
// Must be accessed using atomic operations.
forwarding uint32
ids []uint32
hashIV uint32
// defaultTTL is the current default TTL for the protocol. Only the
// uint8 portion of it is meaningful and it must be accessed
// atomically.
defaultTTL uint32
fragmentation *fragmentation.Fragmentation
}
@@ -558,6 +764,20 @@ func (*protocol) Parse(pkt *stack.PacketBuffer) (proto tcpip.TransportProtocolNu
return ipHdr.TransportProtocol(), !ipHdr.More() && ipHdr.FragmentOffset() == 0, true
}
// Forwarding implements stack.ForwardingNetworkProtocol.
func (p *protocol) Forwarding() bool {
return uint8(atomic.LoadUint32(&p.forwarding)) == 1
}
// SetForwarding implements stack.ForwardingNetworkProtocol.
func (p *protocol) SetForwarding(v bool) {
if v {
atomic.StoreUint32(&p.forwarding, 1)
} else {
atomic.StoreUint32(&p.forwarding, 0)
}
}
// calculateMTU calculates the network-layer payload MTU based on the link-layer
// payload mtu.
func calculateMTU(mtu uint32) uint32 {
+3
View File
@@ -5,11 +5,14 @@ package(licenses = ["notice"])
go_library(
name = "ipv6",
srcs = [
"dhcpv6configurationfromndpra_string.go",
"icmp.go",
"ipv6.go",
"ndp.go",
],
visibility = ["//visibility:public"],
deps = [
"//pkg/sync",
"//pkg/tcpip",
"//pkg/tcpip/buffer",
"//pkg/tcpip/header",
@@ -14,7 +14,7 @@
// Code generated by "stringer -type DHCPv6ConfigurationFromNDPRA"; DO NOT EDIT.
package stack
package ipv6
import "strconv"
+39 -25
View File
@@ -15,6 +15,8 @@
package ipv6
import (
"fmt"
"gvisor.dev/gvisor/pkg/tcpip"
"gvisor.dev/gvisor/pkg/tcpip/buffer"
"gvisor.dev/gvisor/pkg/tcpip/header"
@@ -207,14 +209,7 @@ func (e *endpoint) handleICMP(r *stack.Route, pkt *stack.PacketBuffer, hasFragme
return
}
s := r.Stack()
if isTentative, err := s.IsAddrTentative(e.nicID, targetAddr); err != nil {
// We will only get an error if the NIC is unrecognized, which should not
// happen. For now, drop this packet.
//
// TODO(b/141002840): Handle this better?
return
} else if isTentative {
if e.hasTentativeAddr(targetAddr) {
// If the target address is tentative and the source of the packet is a
// unicast (specified) address, then the source of the packet is
// attempting to perform address resolution on the target. In this case,
@@ -227,7 +222,20 @@ func (e *endpoint) handleICMP(r *stack.Route, pkt *stack.PacketBuffer, hasFragme
// stack know so it can handle such a scenario and do nothing further with
// the NS.
if r.RemoteAddress == header.IPv6Any {
s.DupTentativeAddrDetected(e.nicID, targetAddr)
// We would get an error if the address no longer exists or the address
// is no longer tentative (DAD resolved between the call to
// hasTentativeAddr and this point). Both of these are valid scenarios:
// 1) An address may be removed at any time.
// 2) As per RFC 4862 section 5.4, DAD is not a perfect:
// "Note that the method for detecting duplicates
// is not completely reliable, and it is possible that duplicate
// addresses will still exist"
//
// TODO(gvisor.dev/issue/4046): Handle the scenario when a duplicate
// address is detected for an assigned address.
if err := e.dupTentativeAddrDetected(targetAddr); err != nil && err != tcpip.ErrBadAddress && err != tcpip.ErrInvalidEndpointState {
panic(fmt.Sprintf("unexpected error handling duplicate tentative address: %s", err))
}
}
// Do not handle neighbor solicitations targeted to an address that is
@@ -240,7 +248,7 @@ func (e *endpoint) handleICMP(r *stack.Route, pkt *stack.PacketBuffer, hasFragme
// section 5.4.3.
// Is the NS targeting us?
if s.CheckLocalAddress(e.nicID, ProtocolNumber, targetAddr) == 0 {
if r.Stack().CheckLocalAddress(e.NICID(), ProtocolNumber, targetAddr) == 0 {
return
}
@@ -275,7 +283,7 @@ func (e *endpoint) handleICMP(r *stack.Route, pkt *stack.PacketBuffer, hasFragme
} else if e.nud != nil {
e.nud.HandleProbe(r.RemoteAddress, r.LocalAddress, header.IPv6ProtocolNumber, sourceLinkAddr, e.protocol)
} else {
e.linkAddrCache.AddLinkAddress(e.nicID, r.RemoteAddress, sourceLinkAddr)
e.linkAddrCache.AddLinkAddress(e.NICID(), r.RemoteAddress, sourceLinkAddr)
}
// ICMPv6 Neighbor Solicit messages are always sent to
@@ -353,20 +361,26 @@ func (e *endpoint) handleICMP(r *stack.Route, pkt *stack.PacketBuffer, hasFragme
// NDP datagrams are very small and ToView() will not incur allocations.
na := header.NDPNeighborAdvert(payload.ToView())
targetAddr := na.TargetAddress()
s := r.Stack()
if isTentative, err := s.IsAddrTentative(e.nicID, targetAddr); err != nil {
// We will only get an error if the NIC is unrecognized, which should not
// happen. For now short-circuit this packet.
//
// TODO(b/141002840): Handle this better?
return
} else if isTentative {
if e.hasTentativeAddr(targetAddr) {
// We just got an NA from a node that owns an address we are performing
// DAD on, implying the address is not unique. In this case we let the
// stack know so it can handle such a scenario and do nothing furthur with
// the NDP NA.
s.DupTentativeAddrDetected(e.nicID, targetAddr)
//
// We would get an error if the address no longer exists or the address
// is no longer tentative (DAD resolved between the call to
// hasTentativeAddr and this point). Both of these are valid scenarios:
// 1) An address may be removed at any time.
// 2) As per RFC 4862 section 5.4, DAD is not a perfect:
// "Note that the method for detecting duplicates
// is not completely reliable, and it is possible that duplicate
// addresses will still exist"
//
// TODO(gvisor.dev/issue/4046): Handle the scenario when a duplicate
// address is detected for an assigned address.
if err := e.dupTentativeAddrDetected(targetAddr); err != nil && err != tcpip.ErrBadAddress && err != tcpip.ErrInvalidEndpointState {
panic(fmt.Sprintf("unexpected error handling duplicate tentative address: %s", err))
}
return
}
@@ -396,7 +410,7 @@ func (e *endpoint) handleICMP(r *stack.Route, pkt *stack.PacketBuffer, hasFragme
// address cache with the link address for the target of the message.
if len(targetLinkAddr) != 0 {
if e.nud == nil {
e.linkAddrCache.AddLinkAddress(e.nicID, targetAddr, targetLinkAddr)
e.linkAddrCache.AddLinkAddress(e.NICID(), targetAddr, targetLinkAddr)
return
}
@@ -568,9 +582,9 @@ func (e *endpoint) handleICMP(r *stack.Route, pkt *stack.PacketBuffer, hasFragme
e.nud.HandleProbe(routerAddr, r.LocalAddress, header.IPv6ProtocolNumber, sourceLinkAddr, e.protocol)
}
// Tell the NIC to handle the RA.
stack := r.Stack()
stack.HandleNDPRA(e.nicID, routerAddr, ra)
e.mu.Lock()
e.mu.ndp.handleRA(routerAddr, ra)
e.mu.Unlock()
case header.ICMPv6RedirectMsg:
// TODO(gvisor.dev/issue/2285): Call `e.nud.HandleProbe` after validating
+30 -2
View File
@@ -103,6 +103,26 @@ func (*stubNUDHandler) HandleConfirmation(addr tcpip.Address, linkAddr tcpip.Lin
func (*stubNUDHandler) HandleUpperLevelConfirmation(addr tcpip.Address) {
}
var _ stack.NetworkInterface = (*testInterface)(nil)
type testInterface struct{}
func (*testInterface) ID() tcpip.NICID {
return 0
}
func (*testInterface) IsLoopback() bool {
return false
}
func (*testInterface) Name() string {
return ""
}
func (*testInterface) Enabled() bool {
return true
}
func TestICMPCounts(t *testing.T) {
tests := []struct {
name string
@@ -150,9 +170,13 @@ func TestICMPCounts(t *testing.T) {
if netProto == nil {
t.Fatalf("cannot find protocol instance for network protocol %d", ProtocolNumber)
}
ep := netProto.NewEndpoint(0, &stubLinkAddressCache{}, &stubNUDHandler{}, &stubDispatcher{}, nil, s)
ep := netProto.NewEndpoint(&testInterface{}, &stubLinkAddressCache{}, &stubNUDHandler{}, &stubDispatcher{}, nil, s)
defer ep.Close()
if err := ep.Enable(); err != nil {
t.Fatalf("ep.Enable(): %s", err)
}
r, err := s.FindRoute(nicID, lladdr0, lladdr1, ProtocolNumber, false /* multicastLoop */)
if err != nil {
t.Fatalf("FindRoute(%d, %s, %s, _, false) = (_, %s), want = (_, nil)", nicID, lladdr0, lladdr1, err)
@@ -288,9 +312,13 @@ func TestICMPCountsWithNeighborCache(t *testing.T) {
if netProto == nil {
t.Fatalf("cannot find protocol instance for network protocol %d", ProtocolNumber)
}
ep := netProto.NewEndpoint(0, nil, &stubNUDHandler{}, &stubDispatcher{}, nil, s)
ep := netProto.NewEndpoint(&testInterface{}, nil, &stubNUDHandler{}, &stubDispatcher{}, nil, s)
defer ep.Close()
if err := ep.Enable(); err != nil {
t.Fatalf("ep.Enable(): %s", err)
}
r, err := s.FindRoute(nicID, lladdr0, lladdr1, ProtocolNumber, false /* multicastLoop */)
if err != nil {
t.Fatalf("FindRoute(%d, %s, %s, _, false) = (_, %s), want = (_, nil)", nicID, lladdr0, lladdr1, err)
File diff suppressed because it is too large Load Diff
+27
View File
@@ -1895,3 +1895,30 @@ func (lm *limitedMatcher) Match(stack.Hook, *stack.PacketBuffer, string) (bool,
lm.limit--
return false, false
}
func TestClearEndpointFromProtocolOnClose(t *testing.T) {
s := stack.New(stack.Options{
NetworkProtocols: []stack.NetworkProtocolFactory{NewProtocol},
})
proto := s.NetworkProtocolInstance(ProtocolNumber).(*protocol)
ep := proto.NewEndpoint(&testInterface{}, nil, nil, nil, nil, nil).(*endpoint)
{
proto.mu.Lock()
_, hasEP := proto.mu.eps[ep]
proto.mu.Unlock()
if !hasEP {
t.Fatalf("expected protocol to have ep = %p in set of endpoints", ep)
}
}
ep.Close()
{
proto.mu.Lock()
_, hasEP := proto.mu.eps[ep]
proto.mu.Unlock()
if hasEP {
t.Fatalf("unexpectedly found ep = %p in set of protocol's endpoints", ep)
}
}
}
File diff suppressed because it is too large Load Diff
+85 -1
View File
@@ -17,6 +17,7 @@ package ipv6
import (
"strings"
"testing"
"time"
"github.com/google/go-cmp/cmp"
"gvisor.dev/gvisor/pkg/tcpip"
@@ -65,10 +66,93 @@ func setupStackAndEndpoint(t *testing.T, llladdr, rlladdr tcpip.Address, useNeig
t.Fatalf("cannot find protocol instance for network protocol %d", ProtocolNumber)
}
ep := netProto.NewEndpoint(0, &stubLinkAddressCache{}, &stubNUDHandler{}, &stubDispatcher{}, nil, s)
ep := netProto.NewEndpoint(&testInterface{}, &stubLinkAddressCache{}, &stubNUDHandler{}, &stubDispatcher{}, nil, s)
if err := ep.Enable(); err != nil {
t.Fatalf("ep.Enable(): %s", err)
}
return s, ep
}
var _ NDPDispatcher = (*testNDPDispatcher)(nil)
// testNDPDispatcher is an NDPDispatcher only allows default router discovery.
type testNDPDispatcher struct {
addr tcpip.Address
}
func (*testNDPDispatcher) OnDuplicateAddressDetectionStatus(tcpip.NICID, tcpip.Address, bool, *tcpip.Error) {
}
func (t *testNDPDispatcher) OnDefaultRouterDiscovered(_ tcpip.NICID, addr tcpip.Address) bool {
t.addr = addr
return true
}
func (t *testNDPDispatcher) OnDefaultRouterInvalidated(_ tcpip.NICID, addr tcpip.Address) {
t.addr = addr
}
func (*testNDPDispatcher) OnOnLinkPrefixDiscovered(tcpip.NICID, tcpip.Subnet) bool {
return false
}
func (*testNDPDispatcher) OnOnLinkPrefixInvalidated(tcpip.NICID, tcpip.Subnet) {
}
func (*testNDPDispatcher) OnAutoGenAddress(tcpip.NICID, tcpip.AddressWithPrefix) bool {
return false
}
func (*testNDPDispatcher) OnAutoGenAddressDeprecated(tcpip.NICID, tcpip.AddressWithPrefix) {
}
func (*testNDPDispatcher) OnAutoGenAddressInvalidated(tcpip.NICID, tcpip.AddressWithPrefix) {
}
func (*testNDPDispatcher) OnRecursiveDNSServerOption(tcpip.NICID, []tcpip.Address, time.Duration) {
}
func (*testNDPDispatcher) OnDNSSearchListOption(tcpip.NICID, []string, time.Duration) {
}
func (*testNDPDispatcher) OnDHCPv6Configuration(tcpip.NICID, DHCPv6ConfigurationFromNDPRA) {
}
func TestStackNDPEndpointInvalidateDefaultRouter(t *testing.T) {
var ndpDisp testNDPDispatcher
s := stack.New(stack.Options{
NetworkProtocols: []stack.NetworkProtocolFactory{NewProtocolWithOptions(Options{
NDPDisp: &ndpDisp,
})},
})
if err := s.CreateNIC(nicID, &stubLinkEndpoint{}); err != nil {
t.Fatalf("s.CreateNIC(%d, _): %s", nicID, err)
}
ep, err := s.GetNetworkEndpoint(nicID, ProtocolNumber)
if err != nil {
t.Fatalf("s.GetNetworkEndpoint(%d, %d): %s", nicID, ProtocolNumber, err)
}
ipv6EP := ep.(*endpoint)
ipv6EP.mu.Lock()
ipv6EP.mu.ndp.rememberDefaultRouter(lladdr1, time.Hour)
ipv6EP.mu.Unlock()
if ndpDisp.addr != lladdr1 {
t.Fatalf("got ndpDisp.addr = %s, want = %s", ndpDisp.addr, lladdr1)
}
ndpDisp.addr = ""
ndpEP := ep.(stack.NDPEndpoint)
ndpEP.InvalidateDefaultRouter(lladdr1)
if ndpDisp.addr != lladdr1 {
t.Fatalf("got ndpDisp.addr = %s, want = %s", ndpDisp.addr, lladdr1)
}
}
// TestNeighorSolicitationWithSourceLinkLayerOption tests that receiving a
// valid NDP NS message with the Source Link Layer Address option results in a
// new entry in the link address cache for the sender of the message.
+3 -2
View File
@@ -54,8 +54,8 @@ go_template_instance(
go_library(
name = "stack",
srcs = [
"addressable_endpoint_state.go",
"conntrack.go",
"dhcpv6configurationfromndpra_string.go",
"forwarder.go",
"headertype_string.go",
"icmp_rate_limit.go",
@@ -65,7 +65,6 @@ go_library(
"iptables_types.go",
"linkaddrcache.go",
"linkaddrentry_list.go",
"ndp.go",
"neighbor_cache.go",
"neighbor_entry.go",
"neighbor_entry_list.go",
@@ -106,6 +105,7 @@ go_test(
name = "stack_x_test",
size = "medium",
srcs = [
"addressable_endpoint_state_test.go",
"ndp_test.go",
"nud_test.go",
"stack_test.go",
@@ -116,6 +116,7 @@ go_test(
deps = [
":stack",
"//pkg/rand",
"//pkg/sync",
"//pkg/tcpip",
"//pkg/tcpip/buffer",
"//pkg/tcpip/checker",
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,72 @@
// Copyright 2020 The gVisor Authors.
//
// 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 stack_test
import (
"testing"
"gvisor.dev/gvisor/pkg/tcpip"
"gvisor.dev/gvisor/pkg/tcpip/stack"
)
// TestAddressableEndpointStateCleanup tests that cleaning up an addressable
// endpoint state removes permanent addresses and leaves groups.
func TestAddressableEndpointStateCleanup(t *testing.T) {
var s stack.AddressableEndpointState
s.Init(&fakeNetworkEndpoint{})
addr := tcpip.AddressWithPrefix{
Address: "\x01",
PrefixLen: 8,
}
{
ep, err := s.AddAndAcquirePermanentAddress(addr, stack.NeverPrimaryEndpoint, stack.AddressConfigStatic, false /* deprecated */)
if err != nil {
t.Fatalf("s.AddAndAcquirePermanentAddress(%s, %d, %d, false): %s", addr, stack.NeverPrimaryEndpoint, stack.AddressConfigStatic, err)
}
// We don't need the address endpoint.
ep.DecRef()
}
{
ep := s.AcquireAssignedAddress(addr.Address, false /* allowTemp */, stack.NeverPrimaryEndpoint)
if ep == nil {
t.Fatalf("got s.AcquireAssignedAddress(%s) = nil, want = non-nil", addr.Address)
}
ep.DecRef()
}
group := tcpip.Address("\x02")
if added, err := s.JoinGroup(group); err != nil {
t.Fatalf("s.JoinGroup(%s): %s", group, err)
} else if !added {
t.Fatalf("got s.JoinGroup(%s) = false, want = true", group)
}
if !s.IsInGroup(group) {
t.Fatalf("got s.IsInGroup(%s) = false, want = true", group)
}
s.Cleanup()
{
ep := s.AcquireAssignedAddress(addr.Address, false /* allowTemp */, stack.NeverPrimaryEndpoint)
if ep != nil {
ep.DecRef()
t.Fatalf("got s.AcquireAssignedAddress(%s) = %s, want = nil", addr.Address, ep.AddressWithPrefix())
}
}
if s.IsInGroup(group) {
t.Fatalf("got s.IsInGroup(%s) = true, want = false", group)
}
}
+41 -4
View File
@@ -20,6 +20,7 @@ import (
"testing"
"time"
"gvisor.dev/gvisor/pkg/sync"
"gvisor.dev/gvisor/pkg/tcpip"
"gvisor.dev/gvisor/pkg/tcpip/buffer"
"gvisor.dev/gvisor/pkg/tcpip/header"
@@ -45,6 +46,8 @@ const (
// use the first three: destination address, source address, and transport
// protocol. They're all one byte fields to simplify parsing.
type fwdTestNetworkEndpoint struct {
AddressableEndpointState
nicID tcpip.NICID
proto *fwdTestNetworkProtocol
dispatcher TransportDispatcher
@@ -53,6 +56,16 @@ type fwdTestNetworkEndpoint struct {
var _ NetworkEndpoint = (*fwdTestNetworkEndpoint)(nil)
func (*fwdTestNetworkEndpoint) Enable() *tcpip.Error {
return nil
}
func (*fwdTestNetworkEndpoint) Enabled() bool {
return true
}
func (*fwdTestNetworkEndpoint) Disable() {}
func (f *fwdTestNetworkEndpoint) MTU() uint32 {
return f.ep.MTU() - uint32(f.MaxHeaderLength())
}
@@ -106,7 +119,9 @@ func (*fwdTestNetworkEndpoint) WriteHeaderIncludedPacket(r *Route, pkt *PacketBu
return tcpip.ErrNotSupported
}
func (*fwdTestNetworkEndpoint) Close() {}
func (f *fwdTestNetworkEndpoint) Close() {
f.AddressableEndpointState.Cleanup()
}
// fwdTestNetworkProtocol is a network-layer protocol that implements Address
// resolution.
@@ -116,6 +131,11 @@ type fwdTestNetworkProtocol struct {
addrResolveDelay time.Duration
onLinkAddressResolved func(cache *linkAddrCache, neigh *neighborCache, addr tcpip.Address, _ tcpip.LinkAddress)
onResolveStaticAddress func(tcpip.Address) (tcpip.LinkAddress, bool)
mu struct {
sync.RWMutex
forwarding bool
}
}
var _ NetworkProtocol = (*fwdTestNetworkProtocol)(nil)
@@ -145,13 +165,15 @@ func (*fwdTestNetworkProtocol) Parse(pkt *PacketBuffer) (tcpip.TransportProtocol
return tcpip.TransportProtocolNumber(netHeader[protocolNumberOffset]), true, true
}
func (f *fwdTestNetworkProtocol) NewEndpoint(nicID tcpip.NICID, _ LinkAddressCache, _ NUDHandler, dispatcher TransportDispatcher, ep LinkEndpoint, _ *Stack) NetworkEndpoint {
return &fwdTestNetworkEndpoint{
nicID: nicID,
func (f *fwdTestNetworkProtocol) NewEndpoint(nic NetworkInterface, _ LinkAddressCache, _ NUDHandler, dispatcher TransportDispatcher, ep LinkEndpoint, _ *Stack) NetworkEndpoint {
e := &fwdTestNetworkEndpoint{
nicID: nic.ID(),
proto: f,
dispatcher: dispatcher,
ep: ep,
}
e.AddressableEndpointState.Init(e)
return e
}
func (*fwdTestNetworkProtocol) SetOption(tcpip.SettableNetworkProtocolOption) *tcpip.Error {
@@ -186,6 +208,21 @@ func (*fwdTestNetworkProtocol) LinkAddressProtocol() tcpip.NetworkProtocolNumber
return fwdTestNetNumber
}
// Forwarding implements stack.ForwardingNetworkProtocol.
func (f *fwdTestNetworkProtocol) Forwarding() bool {
f.mu.RLock()
defer f.mu.RUnlock()
return f.mu.forwarding
}
// SetForwarding implements stack.ForwardingNetworkProtocol.
func (f *fwdTestNetworkProtocol) SetForwarding(v bool) {
f.mu.Lock()
defer f.mu.Unlock()
f.mu.forwarding = v
}
// fwdTestPacketInfo holds all the information about an outbound packet.
type fwdTestPacketInfo struct {
RemoteLinkAddress tcpip.LinkAddress
File diff suppressed because it is too large Load Diff
+13 -4
View File
@@ -21,6 +21,7 @@ import (
"gvisor.dev/gvisor/pkg/sleep"
"gvisor.dev/gvisor/pkg/tcpip"
"gvisor.dev/gvisor/pkg/tcpip/header"
)
// NeighborEntry describes a neighboring device in the local network.
@@ -439,7 +440,7 @@ func (e *neighborEntry) handleConfirmationLocked(linkAddr tcpip.LinkAddress, fla
e.notifyWakersLocked()
}
if e.isRouter && !flags.IsRouter {
if e.isRouter && !flags.IsRouter && header.IsV6UnicastAddress(e.neigh.Addr) {
// "In those cases where the IsRouter flag changes from TRUE to FALSE as
// a result of this update, the node MUST remove that router from the
// Default Router List and update the Destination Cache entries for all
@@ -447,9 +448,17 @@ func (e *neighborEntry) handleConfirmationLocked(linkAddr tcpip.LinkAddress, fla
// 7.3.3. This is needed to detect when a node that is used as a router
// stops forwarding packets due to being configured as a host."
// - RFC 4861 section 7.2.5
e.nic.mu.Lock()
e.nic.mu.ndp.invalidateDefaultRouter(e.neigh.Addr)
e.nic.mu.Unlock()
//
// TODO(gvisor.dev/issue/4085): Remove the special casing we do for IPv6
// here.
ep, ok := e.nic.networkEndpoints[header.IPv6ProtocolNumber]
if !ok {
panic(fmt.Sprintf("have a neighbor entry for an IPv6 router but no IPv6 network endpoint"))
}
if ndpEP, ok := ep.(NDPEndpoint); ok {
ndpEP.InvalidateDefaultRouter(e.neigh.Addr)
}
}
e.isRouter = flags.IsRouter
+9 -11
View File
@@ -28,6 +28,7 @@ import (
"gvisor.dev/gvisor/pkg/sleep"
"gvisor.dev/gvisor/pkg/tcpip"
"gvisor.dev/gvisor/pkg/tcpip/faketime"
"gvisor.dev/gvisor/pkg/tcpip/header"
)
const (
@@ -233,18 +234,15 @@ func entryTestSetup(c NUDConfigurations) (*neighborEntry, *testNUDDispatcher, *e
nudDisp: &disp,
},
}
nic.networkEndpoints = map[tcpip.NetworkProtocolNumber]NetworkEndpoint{
header.IPv6ProtocolNumber: (&testIPv6Protocol{}).NewEndpoint(&nic, nil, nil, nil, nil, nil),
}
rng := rand.New(rand.NewSource(time.Now().UnixNano()))
nudState := NewNUDState(c, rng)
linkRes := entryTestLinkResolver{}
entry := newNeighborEntry(&nic, entryTestAddr1 /* remoteAddr */, entryTestAddr2 /* localAddr */, nudState, &linkRes)
// Stub out ndpState to verify modification of default routers.
nic.mu.ndp = ndpState{
nic: &nic,
defaultRouters: make(map[tcpip.Address]defaultRouterState),
}
// Stub out the neighbor cache to verify deletion from the cache.
nic.neigh = &neighborCache{
nic: &nic,
@@ -817,6 +815,8 @@ func TestEntryStaysReachableWhenConfirmationWithRouterFlag(t *testing.T) {
c := DefaultNUDConfigurations()
e, nudDisp, linkRes, _ := entryTestSetup(c)
ipv6EP := e.nic.networkEndpoints[header.IPv6ProtocolNumber].(*testIPv6Endpoint)
e.mu.Lock()
e.handlePacketQueuedLocked()
e.handleConfirmationLocked(entryTestLinkAddr1, ReachabilityConfirmationFlags{
@@ -830,9 +830,7 @@ func TestEntryStaysReachableWhenConfirmationWithRouterFlag(t *testing.T) {
if got, want := e.isRouter, true; got != want {
t.Errorf("got e.isRouter = %t, want = %t", got, want)
}
e.nic.mu.ndp.defaultRouters[entryTestAddr1] = defaultRouterState{
invalidationJob: e.nic.stack.newJob(&testLocker{}, func() {}),
}
e.handleConfirmationLocked(entryTestLinkAddr1, ReachabilityConfirmationFlags{
Solicited: false,
Override: false,
@@ -841,8 +839,8 @@ func TestEntryStaysReachableWhenConfirmationWithRouterFlag(t *testing.T) {
if got, want := e.isRouter, false; got != want {
t.Errorf("got e.isRouter = %t, want = %t", got, want)
}
if _, ok := e.nic.mu.ndp.defaultRouters[entryTestAddr1]; ok {
t.Errorf("unexpected defaultRouter for %s", entryTestAddr1)
if ipv6EP.invalidatedRtr != e.neigh.Addr {
t.Errorf("got ipv6EP.invalidatedRtr = %s, want = %s", ipv6EP.invalidatedRtr, e.neigh.Addr)
}
e.mu.Unlock()

Some files were not shown because too many files have changed in this diff Show More