summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
-rw-r--r--pkg/tcpip/network/arp/arp.go18
-rw-r--r--pkg/tcpip/network/ipv6/icmp.go38
-rw-r--r--pkg/tcpip/stack/linkaddrcache.go25
-rw-r--r--pkg/tcpip/stack/neighbor_cache.go23
-rw-r--r--pkg/tcpip/stack/neighbor_entry.go61
-rw-r--r--pkg/tcpip/stack/nic.go150
-rw-r--r--pkg/tcpip/stack/registration.go4
-rw-r--r--pkg/tcpip/stack/route.go14
-rw-r--r--pkg/tcpip/stack/stack.go24
9 files changed, 203 insertions, 154 deletions
diff --git a/pkg/tcpip/network/arp/arp.go b/pkg/tcpip/network/arp/arp.go
index 5fd4c5574..0d7fadc31 100644
--- a/pkg/tcpip/network/arp/arp.go
+++ b/pkg/tcpip/network/arp/arp.go
@@ -148,7 +148,13 @@ func (e *endpoint) HandlePacket(pkt *stack.PacketBuffer) {
remoteAddr := tcpip.Address(h.ProtocolAddressSender())
remoteLinkAddr := tcpip.LinkAddress(h.HardwareAddressSender())
- e.nic.HandleNeighborProbe(remoteAddr, remoteLinkAddr, e)
+ switch err := e.nic.HandleNeighborProbe(header.IPv4ProtocolNumber, remoteAddr, remoteLinkAddr); err.(type) {
+ case nil:
+ case *tcpip.ErrNotSupported:
+ // The stack may support ARP but the NIC may not need link resolution.
+ default:
+ panic(fmt.Sprintf("unexpected error when informing NIC of neighbor probe message: %s", err))
+ }
respPkt := stack.NewPacketBuffer(stack.PacketBufferOptions{
ReserveHeaderBytes: int(e.nic.MaxHeaderLength()) + header.ARPSize,
@@ -190,7 +196,7 @@ func (e *endpoint) HandlePacket(pkt *stack.PacketBuffer) {
// The solicited, override, and isRouter flags are not available for ARP;
// they are only available for IPv6 Neighbor Advertisements.
- e.nic.HandleNeighborConfirmation(addr, linkAddr, stack.ReachabilityConfirmationFlags{
+ switch err := e.nic.HandleNeighborConfirmation(header.IPv4ProtocolNumber, addr, linkAddr, stack.ReachabilityConfirmationFlags{
// Solicited and unsolicited (also referred to as gratuitous) ARP Replies
// are handled equivalently to a solicited Neighbor Advertisement.
Solicited: true,
@@ -199,7 +205,13 @@ func (e *endpoint) HandlePacket(pkt *stack.PacketBuffer) {
Override: false,
// ARP does not distinguish between router and non-router hosts.
IsRouter: false,
- })
+ }); err.(type) {
+ case nil:
+ case *tcpip.ErrNotSupported:
+ // The stack may support ARP but the NIC may not need link resolution.
+ default:
+ panic(fmt.Sprintf("unexpected error when informing NIC of neighbor confirmation message: %s", err))
+ }
}
}
diff --git a/pkg/tcpip/network/ipv6/icmp.go b/pkg/tcpip/network/ipv6/icmp.go
index bdc88fe5d..12e5ead5e 100644
--- a/pkg/tcpip/network/ipv6/icmp.go
+++ b/pkg/tcpip/network/ipv6/icmp.go
@@ -290,7 +290,13 @@ func (e *endpoint) handleICMP(pkt *stack.PacketBuffer, hasFragmentHeader bool) {
received.invalid.Increment()
return
} else {
- e.nic.HandleNeighborProbe(srcAddr, sourceLinkAddr, e)
+ switch err := e.nic.HandleNeighborProbe(ProtocolNumber, srcAddr, sourceLinkAddr); err.(type) {
+ case nil:
+ case *tcpip.ErrNotSupported:
+ // The stack may support ICMPv6 but the NIC may not need link resolution.
+ default:
+ panic(fmt.Sprintf("unexpected error when informing NIC of neighbor probe message: %s", err))
+ }
}
// As per RFC 4861 section 7.1.1:
@@ -456,11 +462,17 @@ func (e *endpoint) handleICMP(pkt *stack.PacketBuffer, hasFragmentHeader bool) {
// If the NA message has the target link layer option, update the link
// address cache with the link address for the target of the message.
- e.nic.HandleNeighborConfirmation(targetAddr, targetLinkAddr, stack.ReachabilityConfirmationFlags{
+ switch err := e.nic.HandleNeighborConfirmation(ProtocolNumber, targetAddr, targetLinkAddr, stack.ReachabilityConfirmationFlags{
Solicited: na.SolicitedFlag(),
Override: na.OverrideFlag(),
IsRouter: na.RouterFlag(),
- })
+ }); err.(type) {
+ case nil:
+ case *tcpip.ErrNotSupported:
+ // The stack may support ICMPv6 but the NIC may not need link resolution.
+ default:
+ panic(fmt.Sprintf("unexpected error when informing NIC of neighbor confirmation message: %s", err))
+ }
case header.ICMPv6EchoRequest:
received.echoRequest.Increment()
@@ -566,9 +578,15 @@ func (e *endpoint) handleICMP(pkt *stack.PacketBuffer, hasFragmentHeader bool) {
return
}
- // A RS with a specified source IP address modifies the NUD state
- // machine in the same way a reachability probe would.
- e.nic.HandleNeighborProbe(srcAddr, sourceLinkAddr, e)
+ // A RS with a specified source IP address modifies the neighbor table
+ // in the same way a regular probe would.
+ switch err := e.nic.HandleNeighborProbe(ProtocolNumber, srcAddr, sourceLinkAddr); err.(type) {
+ case nil:
+ case *tcpip.ErrNotSupported:
+ // The stack may support ICMPv6 but the NIC may not need link resolution.
+ default:
+ panic(fmt.Sprintf("unexpected error when informing NIC of neighbor probe message: %s", err))
+ }
}
case header.ICMPv6RouterAdvert:
@@ -617,7 +635,13 @@ func (e *endpoint) handleICMP(pkt *stack.PacketBuffer, hasFragmentHeader bool) {
// If the RA has the source link layer option, update the link address
// cache with the link address for the advertised router.
if len(sourceLinkAddr) != 0 {
- e.nic.HandleNeighborProbe(routerAddr, sourceLinkAddr, e)
+ switch err := e.nic.HandleNeighborProbe(ProtocolNumber, routerAddr, sourceLinkAddr); err.(type) {
+ case nil:
+ case *tcpip.ErrNotSupported:
+ // The stack may support ICMPv6 but the NIC may not need link resolution.
+ default:
+ panic(fmt.Sprintf("unexpected error when informing NIC of neighbor probe message: %s", err))
+ }
}
e.mu.Lock()
diff --git a/pkg/tcpip/stack/linkaddrcache.go b/pkg/tcpip/stack/linkaddrcache.go
index 4504db752..5b6b58b1d 100644
--- a/pkg/tcpip/stack/linkaddrcache.go
+++ b/pkg/tcpip/stack/linkaddrcache.go
@@ -32,6 +32,8 @@ const linkAddrCacheSize = 512 // max cache entries
type linkAddrCache struct {
nic *NIC
+ linkRes LinkAddressResolver
+
// ageLimit is how long a cache entry is valid for.
ageLimit time.Duration
@@ -196,10 +198,10 @@ func (c *linkAddrCache) getOrCreateEntryLocked(k tcpip.Address) *linkAddrEntry {
return entry
}
-// get reports any known link address for k.
-func (c *linkAddrCache) get(k tcpip.Address, linkRes LinkAddressResolver, localAddr tcpip.Address, onResolve func(LinkResolutionResult)) (tcpip.LinkAddress, <-chan struct{}, tcpip.Error) {
+// get reports any known link address for addr.
+func (c *linkAddrCache) get(addr, localAddr tcpip.Address, onResolve func(LinkResolutionResult)) (tcpip.LinkAddress, <-chan struct{}, tcpip.Error) {
c.mu.Lock()
- entry := c.getOrCreateEntryLocked(k)
+ entry := c.getOrCreateEntryLocked(addr)
entry.mu.Lock()
defer entry.mu.Unlock()
c.mu.Unlock()
@@ -222,7 +224,7 @@ func (c *linkAddrCache) get(k tcpip.Address, linkRes LinkAddressResolver, localA
}
if entry.mu.done == nil {
entry.mu.done = make(chan struct{})
- go c.startAddressResolution(k, linkRes, localAddr, entry.mu.done) // S/R-SAFE: link non-savable; wakers dropped synchronously.
+ go c.startAddressResolution(addr, localAddr, entry.mu.done) // S/R-SAFE: link non-savable; wakers dropped synchronously.
}
return entry.mu.linkAddr, entry.mu.done, &tcpip.ErrWouldBlock{}
default:
@@ -230,11 +232,11 @@ func (c *linkAddrCache) get(k tcpip.Address, linkRes LinkAddressResolver, localA
}
}
-func (c *linkAddrCache) startAddressResolution(k tcpip.Address, linkRes LinkAddressResolver, localAddr tcpip.Address, done <-chan struct{}) {
+func (c *linkAddrCache) startAddressResolution(k tcpip.Address, localAddr tcpip.Address, done <-chan struct{}) {
for i := 0; ; i++ {
// Send link request, then wait for the timeout limit and check
// whether the request succeeded.
- linkRes.LinkAddressRequest(k, localAddr, "" /* linkAddr */)
+ c.linkRes.LinkAddressRequest(k, localAddr, "" /* linkAddr */)
select {
case now := <-time.After(c.resolutionTimeout):
@@ -278,15 +280,18 @@ func (c *linkAddrCache) checkLinkRequest(now time.Time, k tcpip.Address, attempt
return true
}
-func newLinkAddrCache(nic *NIC, ageLimit, resolutionTimeout time.Duration, resolutionAttempts int) *linkAddrCache {
- c := &linkAddrCache{
+func (c *linkAddrCache) init(nic *NIC, ageLimit, resolutionTimeout time.Duration, resolutionAttempts int, linkRes LinkAddressResolver) {
+ *c = linkAddrCache{
nic: nic,
+ linkRes: linkRes,
ageLimit: ageLimit,
resolutionTimeout: resolutionTimeout,
resolutionAttempts: resolutionAttempts,
}
+
+ c.mu.Lock()
c.mu.table = make(map[tcpip.Address]*linkAddrEntry, linkAddrCacheSize)
- return c
+ c.mu.Unlock()
}
var _ neighborTable = (*linkAddrCache)(nil)
@@ -307,7 +312,7 @@ func (*linkAddrCache) removeAll() tcpip.Error {
return &tcpip.ErrNotSupported{}
}
-func (c *linkAddrCache) handleProbe(addr tcpip.Address, linkAddr tcpip.LinkAddress, _ LinkAddressResolver) {
+func (c *linkAddrCache) handleProbe(addr tcpip.Address, linkAddr tcpip.LinkAddress) {
if len(linkAddr) != 0 {
// NUD allows probes without a link address but linkAddrCache
// is a simple neighbor table which does not implement NUD.
diff --git a/pkg/tcpip/stack/neighbor_cache.go b/pkg/tcpip/stack/neighbor_cache.go
index 64b8046f5..7e3132058 100644
--- a/pkg/tcpip/stack/neighbor_cache.go
+++ b/pkg/tcpip/stack/neighbor_cache.go
@@ -43,8 +43,9 @@ type NeighborStats struct {
// Their state is always Static. The amount of static entries stored in the
// cache is unbounded.
type neighborCache struct {
- nic *NIC
- state *NUDState
+ nic *NIC
+ state *NUDState
+ linkRes LinkAddressResolver
// mu protects the fields below.
mu sync.RWMutex
@@ -69,7 +70,7 @@ type neighborCache struct {
// reset to state incomplete, and returned. If no matching entry exists and the
// cache is not full, a new entry with state incomplete is allocated and
// returned.
-func (n *neighborCache) getOrCreateEntry(remoteAddr tcpip.Address, linkRes LinkAddressResolver) *neighborEntry {
+func (n *neighborCache) getOrCreateEntry(remoteAddr tcpip.Address) *neighborEntry {
n.mu.Lock()
defer n.mu.Unlock()
@@ -85,7 +86,7 @@ func (n *neighborCache) getOrCreateEntry(remoteAddr tcpip.Address, linkRes LinkA
// The entry that needs to be created must be dynamic since all static
// entries are directly added to the cache via addStaticEntry.
- entry := newNeighborEntry(n.nic, remoteAddr, n.state, linkRes)
+ entry := newNeighborEntry(n, remoteAddr, n.state)
if n.dynamic.count == neighborCacheSize {
e := n.dynamic.lru.Back()
e.mu.Lock()
@@ -122,8 +123,8 @@ func (n *neighborCache) getOrCreateEntry(remoteAddr tcpip.Address, linkRes LinkA
// packet prompting NUD/link address resolution.
//
// TODO(gvisor.dev/issue/5151): Don't return the neighbor entry.
-func (n *neighborCache) entry(remoteAddr, localAddr tcpip.Address, linkRes LinkAddressResolver, onResolve func(LinkResolutionResult)) (NeighborEntry, <-chan struct{}, tcpip.Error) {
- entry := n.getOrCreateEntry(remoteAddr, linkRes)
+func (n *neighborCache) entry(remoteAddr, localAddr tcpip.Address, onResolve func(LinkResolutionResult)) (NeighborEntry, <-chan struct{}, tcpip.Error) {
+ entry := n.getOrCreateEntry(remoteAddr)
entry.mu.Lock()
defer entry.mu.Unlock()
@@ -202,7 +203,7 @@ func (n *neighborCache) addStaticEntry(addr tcpip.Address, linkAddr tcpip.LinkAd
entry.mu.Unlock()
}
- n.cache[addr] = newStaticNeighborEntry(n.nic, addr, linkAddr, n.state)
+ n.cache[addr] = newStaticNeighborEntry(n, addr, linkAddr, n.state)
}
// removeEntry removes a dynamic or static entry by address from the neighbor
@@ -265,8 +266,8 @@ func (n *neighborCache) neighbors() ([]NeighborEntry, tcpip.Error) {
return n.entries(), nil
}
-func (n *neighborCache) get(addr tcpip.Address, linkRes LinkAddressResolver, localAddr tcpip.Address, onResolve func(LinkResolutionResult)) (tcpip.LinkAddress, <-chan struct{}, tcpip.Error) {
- entry, ch, err := n.entry(addr, localAddr, linkRes, onResolve)
+func (n *neighborCache) get(addr, localAddr tcpip.Address, onResolve func(LinkResolutionResult)) (tcpip.LinkAddress, <-chan struct{}, tcpip.Error) {
+ entry, ch, err := n.entry(addr, localAddr, onResolve)
return entry.LinkAddr, ch, err
}
@@ -286,8 +287,8 @@ func (n *neighborCache) removeAll() tcpip.Error {
// handleProbe handles a neighbor probe as defined by RFC 4861 section 7.2.3.
//
// Validation of the probe is expected to be handled by the caller.
-func (n *neighborCache) handleProbe(remoteAddr tcpip.Address, remoteLinkAddr tcpip.LinkAddress, linkRes LinkAddressResolver) {
- entry := n.getOrCreateEntry(remoteAddr, linkRes)
+func (n *neighborCache) handleProbe(remoteAddr tcpip.Address, remoteLinkAddr tcpip.LinkAddress) {
+ entry := n.getOrCreateEntry(remoteAddr)
entry.mu.Lock()
entry.handleProbeLocked(remoteLinkAddr)
entry.mu.Unlock()
diff --git a/pkg/tcpip/stack/neighbor_entry.go b/pkg/tcpip/stack/neighbor_entry.go
index a037ca6f9..b05f96d4f 100644
--- a/pkg/tcpip/stack/neighbor_entry.go
+++ b/pkg/tcpip/stack/neighbor_entry.go
@@ -77,11 +77,7 @@ const (
type neighborEntry struct {
neighborEntryEntry
- nic *NIC
-
- // linkRes provides the functionality to send reachability probes, used in
- // Neighbor Unreachability Detection.
- linkRes LinkAddressResolver
+ cache *neighborCache
// nudState points to the Neighbor Unreachability Detection configuration.
nudState *NUDState
@@ -106,10 +102,9 @@ type neighborEntry struct {
// state, Unknown. Transition out of Unknown by calling either
// `handlePacketQueuedLocked` or `handleProbeLocked` on the newly created
// neighborEntry.
-func newNeighborEntry(nic *NIC, remoteAddr tcpip.Address, nudState *NUDState, linkRes LinkAddressResolver) *neighborEntry {
+func newNeighborEntry(cache *neighborCache, remoteAddr tcpip.Address, nudState *NUDState) *neighborEntry {
return &neighborEntry{
- nic: nic,
- linkRes: linkRes,
+ cache: cache,
nudState: nudState,
neigh: NeighborEntry{
Addr: remoteAddr,
@@ -121,18 +116,18 @@ func newNeighborEntry(nic *NIC, remoteAddr tcpip.Address, nudState *NUDState, li
// newStaticNeighborEntry creates a neighbor cache entry starting at the
// Static state. The entry can only transition out of Static by directly
// calling `setStateLocked`.
-func newStaticNeighborEntry(nic *NIC, addr tcpip.Address, linkAddr tcpip.LinkAddress, state *NUDState) *neighborEntry {
+func newStaticNeighborEntry(cache *neighborCache, addr tcpip.Address, linkAddr tcpip.LinkAddress, state *NUDState) *neighborEntry {
entry := NeighborEntry{
Addr: addr,
LinkAddr: linkAddr,
State: Static,
- UpdatedAtNanos: nic.stack.clock.NowNanoseconds(),
+ UpdatedAtNanos: cache.nic.stack.clock.NowNanoseconds(),
}
- if nic.stack.nudDisp != nil {
- nic.stack.nudDisp.OnNeighborAdded(nic.id, entry)
+ if nudDisp := cache.nic.stack.nudDisp; nudDisp != nil {
+ nudDisp.OnNeighborAdded(cache.nic.id, entry)
}
return &neighborEntry{
- nic: nic,
+ cache: cache,
nudState: state,
neigh: entry,
}
@@ -158,7 +153,7 @@ func (e *neighborEntry) notifyCompletionLocked(succeeded bool) {
// is resolved (which ends up obtaining the entry's lock) while holding the
// link resolution queue's lock. Dequeuing packets in a new goroutine avoids
// a lock ordering violation.
- go e.nic.linkResQueue.dequeue(ch, e.neigh.LinkAddr, succeeded)
+ go e.cache.nic.linkResQueue.dequeue(ch, e.neigh.LinkAddr, succeeded)
}
}
@@ -167,8 +162,8 @@ func (e *neighborEntry) notifyCompletionLocked(succeeded bool) {
//
// Precondition: e.mu MUST be locked.
func (e *neighborEntry) dispatchAddEventLocked() {
- if nudDisp := e.nic.stack.nudDisp; nudDisp != nil {
- nudDisp.OnNeighborAdded(e.nic.id, e.neigh)
+ if nudDisp := e.cache.nic.stack.nudDisp; nudDisp != nil {
+ nudDisp.OnNeighborAdded(e.cache.nic.id, e.neigh)
}
}
@@ -177,8 +172,8 @@ func (e *neighborEntry) dispatchAddEventLocked() {
//
// Precondition: e.mu MUST be locked.
func (e *neighborEntry) dispatchChangeEventLocked() {
- if nudDisp := e.nic.stack.nudDisp; nudDisp != nil {
- nudDisp.OnNeighborChanged(e.nic.id, e.neigh)
+ if nudDisp := e.cache.nic.stack.nudDisp; nudDisp != nil {
+ nudDisp.OnNeighborChanged(e.cache.nic.id, e.neigh)
}
}
@@ -187,8 +182,8 @@ func (e *neighborEntry) dispatchChangeEventLocked() {
//
// Precondition: e.mu MUST be locked.
func (e *neighborEntry) dispatchRemoveEventLocked() {
- if nudDisp := e.nic.stack.nudDisp; nudDisp != nil {
- nudDisp.OnNeighborRemoved(e.nic.id, e.neigh)
+ if nudDisp := e.cache.nic.stack.nudDisp; nudDisp != nil {
+ nudDisp.OnNeighborRemoved(e.cache.nic.id, e.neigh)
}
}
@@ -206,7 +201,7 @@ func (e *neighborEntry) cancelJobLocked() {
//
// Precondition: e.mu MUST be locked.
func (e *neighborEntry) removeLocked() {
- e.neigh.UpdatedAtNanos = e.nic.stack.clock.NowNanoseconds()
+ e.neigh.UpdatedAtNanos = e.cache.nic.stack.clock.NowNanoseconds()
e.dispatchRemoveEventLocked()
e.cancelJobLocked()
e.notifyCompletionLocked(false /* succeeded */)
@@ -222,7 +217,7 @@ func (e *neighborEntry) setStateLocked(next NeighborState) {
prev := e.neigh.State
e.neigh.State = next
- e.neigh.UpdatedAtNanos = e.nic.stack.clock.NowNanoseconds()
+ e.neigh.UpdatedAtNanos = e.cache.nic.stack.clock.NowNanoseconds()
config := e.nudState.Config()
switch next {
@@ -230,14 +225,14 @@ func (e *neighborEntry) setStateLocked(next NeighborState) {
panic(fmt.Sprintf("should never transition to Incomplete with setStateLocked; neigh = %#v, prev state = %s", e.neigh, prev))
case Reachable:
- e.job = e.nic.stack.newJob(&e.mu, func() {
+ e.job = e.cache.nic.stack.newJob(&e.mu, func() {
e.setStateLocked(Stale)
e.dispatchChangeEventLocked()
})
e.job.Schedule(e.nudState.ReachableTime())
case Delay:
- e.job = e.nic.stack.newJob(&e.mu, func() {
+ e.job = e.cache.nic.stack.newJob(&e.mu, func() {
e.setStateLocked(Probe)
e.dispatchChangeEventLocked()
})
@@ -254,14 +249,14 @@ func (e *neighborEntry) setStateLocked(next NeighborState) {
return
}
- if err := e.linkRes.LinkAddressRequest(e.neigh.Addr, "" /* localAddr */, e.neigh.LinkAddr); err != nil {
+ if err := e.cache.linkRes.LinkAddressRequest(e.neigh.Addr, "" /* localAddr */, e.neigh.LinkAddr); err != nil {
e.dispatchRemoveEventLocked()
e.setStateLocked(Failed)
return
}
retryCounter++
- e.job = e.nic.stack.newJob(&e.mu, sendUnicastProbe)
+ e.job = e.cache.nic.stack.newJob(&e.mu, sendUnicastProbe)
e.job.Schedule(config.RetransmitTimer)
}
@@ -269,7 +264,7 @@ func (e *neighborEntry) setStateLocked(next NeighborState) {
// for finishing the state transition. This is necessary to avoid
// deadlock where sending and processing probes are done synchronously,
// such as loopback and integration tests.
- e.job = e.nic.stack.newJob(&e.mu, sendUnicastProbe)
+ e.job = e.cache.nic.stack.newJob(&e.mu, sendUnicastProbe)
e.job.Schedule(immediateDuration)
case Failed:
@@ -292,12 +287,12 @@ func (e *neighborEntry) setStateLocked(next NeighborState) {
func (e *neighborEntry) handlePacketQueuedLocked(localAddr tcpip.Address) {
switch e.neigh.State {
case Failed:
- e.nic.stats.Neighbor.FailedEntryLookups.Increment()
+ e.cache.nic.stats.Neighbor.FailedEntryLookups.Increment()
fallthrough
case Unknown:
e.neigh.State = Incomplete
- e.neigh.UpdatedAtNanos = e.nic.stack.clock.NowNanoseconds()
+ e.neigh.UpdatedAtNanos = e.cache.nic.stack.clock.NowNanoseconds()
e.dispatchAddEventLocked()
@@ -340,7 +335,7 @@ func (e *neighborEntry) handlePacketQueuedLocked(localAddr tcpip.Address) {
// address SHOULD be placed in the IP Source Address of the outgoing
// solicitation.
//
- if err := e.linkRes.LinkAddressRequest(e.neigh.Addr, localAddr, ""); err != nil {
+ if err := e.cache.linkRes.LinkAddressRequest(e.neigh.Addr, localAddr, ""); err != nil {
// There is no need to log the error here; the NUD implementation may
// assume a working link. A valid link should be the responsibility of
// the NIC/stack.LinkEndpoint.
@@ -350,7 +345,7 @@ func (e *neighborEntry) handlePacketQueuedLocked(localAddr tcpip.Address) {
}
retryCounter++
- e.job = e.nic.stack.newJob(&e.mu, sendMulticastProbe)
+ e.job = e.cache.nic.stack.newJob(&e.mu, sendMulticastProbe)
e.job.Schedule(config.RetransmitTimer)
}
@@ -358,7 +353,7 @@ func (e *neighborEntry) handlePacketQueuedLocked(localAddr tcpip.Address) {
// for finishing the state transition. This is necessary to avoid
// deadlock where sending and processing probes are done synchronously,
// such as loopback and integration tests.
- e.job = e.nic.stack.newJob(&e.mu, sendMulticastProbe)
+ e.job = e.cache.nic.stack.newJob(&e.mu, sendMulticastProbe)
e.job.Schedule(immediateDuration)
case Stale:
@@ -504,7 +499,7 @@ func (e *neighborEntry) handleConfirmationLocked(linkAddr tcpip.LinkAddress, fla
//
// TODO(gvisor.dev/issue/4085): Remove the special casing we do for IPv6
// here.
- ep, ok := e.nic.networkEndpoints[header.IPv6ProtocolNumber]
+ ep, ok := e.cache.nic.networkEndpoints[header.IPv6ProtocolNumber]
if !ok {
panic(fmt.Sprintf("have a neighbor entry for an IPv6 router but no IPv6 network endpoint"))
}
diff --git a/pkg/tcpip/stack/nic.go b/pkg/tcpip/stack/nic.go
index c813b0da5..693ea064a 100644
--- a/pkg/tcpip/stack/nic.go
+++ b/pkg/tcpip/stack/nic.go
@@ -27,11 +27,11 @@ import (
type neighborTable interface {
neighbors() ([]NeighborEntry, tcpip.Error)
addStaticEntry(tcpip.Address, tcpip.LinkAddress)
- get(addr tcpip.Address, linkRes LinkAddressResolver, localAddr tcpip.Address, onResolve func(LinkResolutionResult)) (tcpip.LinkAddress, <-chan struct{}, tcpip.Error)
+ get(addr, localAddr tcpip.Address, onResolve func(LinkResolutionResult)) (tcpip.LinkAddress, <-chan struct{}, tcpip.Error)
remove(tcpip.Address) tcpip.Error
removeAll() tcpip.Error
- handleProbe(tcpip.Address, tcpip.LinkAddress, LinkAddressResolver)
+ handleProbe(tcpip.Address, tcpip.LinkAddress)
handleConfirmation(tcpip.Address, tcpip.LinkAddress, ReachabilityConfirmationFlags)
handleUpperLevelConfirmation(tcpip.Address)
@@ -41,6 +41,20 @@ type neighborTable interface {
var _ NetworkInterface = (*NIC)(nil)
+type linkResolver struct {
+ resolver LinkAddressResolver
+
+ neighborTable neighborTable
+}
+
+func (l *linkResolver) getNeighborLinkAddress(addr, localAddr tcpip.Address, onResolve func(LinkResolutionResult)) (tcpip.LinkAddress, <-chan struct{}, tcpip.Error) {
+ return l.neighborTable.get(addr, localAddr, onResolve)
+}
+
+func (l *linkResolver) confirmReachable(addr tcpip.Address) {
+ l.neighborTable.handleUpperLevelConfirmation(addr)
+}
+
// NIC represents a "network interface card" to which the networking stack is
// attached.
type NIC struct {
@@ -56,7 +70,7 @@ type NIC struct {
// The network endpoints themselves may be modified by calling the interface's
// methods, but the map reference and entries must be constant.
networkEndpoints map[tcpip.NetworkProtocolNumber]NetworkEndpoint
- linkAddrResolvers map[tcpip.NetworkProtocolNumber]LinkAddressResolver
+ linkAddrResolvers map[tcpip.NetworkProtocolNumber]linkResolver
// enabled is set to 1 when the NIC is enabled and 0 when it is disabled.
//
@@ -67,8 +81,6 @@ type NIC struct {
// complete.
linkResQueue packetsPendingLinkResolution
- neighborTable neighborTable
-
mu struct {
sync.RWMutex
spoofing bool
@@ -153,25 +165,13 @@ func newNIC(stack *Stack, id tcpip.NICID, name string, ep LinkEndpoint, ctx NICC
context: ctx,
stats: makeNICStats(),
networkEndpoints: make(map[tcpip.NetworkProtocolNumber]NetworkEndpoint),
- linkAddrResolvers: make(map[tcpip.NetworkProtocolNumber]LinkAddressResolver),
+ linkAddrResolvers: make(map[tcpip.NetworkProtocolNumber]linkResolver),
}
nic.linkResQueue.init(nic)
nic.mu.packetEPs = make(map[tcpip.NetworkProtocolNumber]*packetEndpointList)
resolutionRequired := ep.Capabilities()&CapabilityResolutionRequired != 0
- if resolutionRequired {
- if stack.useNeighborCache {
- nic.neighborTable = &neighborCache{
- nic: nic,
- state: NewNUDState(stack.nudConfigs, stack.randomGenerator),
- cache: make(map[tcpip.Address]*neighborEntry, neighborCacheSize),
- }
- } else {
- nic.neighborTable = newLinkAddrCache(nic, ageLimit, resolutionTimeout, resolutionAttempts)
- }
- }
-
// Register supported packet and network endpoint protocols.
for _, netProto := range header.Ethertypes {
nic.mu.packetEPs[netProto] = new(packetEndpointList)
@@ -185,7 +185,24 @@ func newNIC(stack *Stack, id tcpip.NICID, name string, ep LinkEndpoint, ctx NICC
if resolutionRequired {
if r, ok := netEP.(LinkAddressResolver); ok {
- nic.linkAddrResolvers[r.LinkAddressProtocol()] = r
+ l := linkResolver{
+ resolver: r,
+ }
+
+ if stack.useNeighborCache {
+ l.neighborTable = &neighborCache{
+ nic: nic,
+ state: NewNUDState(stack.nudConfigs, stack.randomGenerator),
+ linkRes: r,
+
+ cache: make(map[tcpip.Address]*neighborEntry, neighborCacheSize),
+ }
+ } else {
+ cache := new(linkAddrCache)
+ cache.init(nic, ageLimit, resolutionTimeout, resolutionAttempts, r)
+ l.neighborTable = cache
+ }
+ nic.linkAddrResolvers[r.LinkAddressProtocol()] = l
}
}
}
@@ -240,18 +257,19 @@ func (n *NIC) disableLocked() {
for _, ep := range n.networkEndpoints {
ep.Disable()
- }
- // Clear the neighbour table (including static entries) as we cannot guarantee
- // that the current neighbour table will be valid when the NIC is enabled
- // again.
- //
- // This matches linux's behaviour at the time of writing:
- // https://github.com/torvalds/linux/blob/71c061d2443814de15e177489d5cc00a4a253ef3/net/core/neighbour.c#L371
- switch err := n.clearNeighbors(); err.(type) {
- case nil, *tcpip.ErrNotSupported:
- default:
- panic(fmt.Sprintf("n.clearNeighbors(): %s", err))
+ // Clear the neighbour table (including static entries) as we cannot
+ // guarantee that the current neighbour table will be valid when the NIC is
+ // enabled again.
+ //
+ // This matches linux's behaviour at the time of writing:
+ // https://github.com/torvalds/linux/blob/71c061d2443814de15e177489d5cc00a4a253ef3/net/core/neighbour.c#L371
+ netProto := ep.NetworkProtocolNumber()
+ switch err := n.clearNeighbors(netProto); err.(type) {
+ case nil, *tcpip.ErrNotSupported:
+ default:
+ panic(fmt.Sprintf("n.clearNeighbors(%d): %s", netProto, err))
+ }
}
if !n.setEnabled(false) {
@@ -604,63 +622,49 @@ func (n *NIC) removeAddress(addr tcpip.Address) tcpip.Error {
return &tcpip.ErrBadLocalAddress{}
}
-func (n *NIC) confirmReachable(addr tcpip.Address) {
- if n.neighborTable != nil {
- n.neighborTable.handleUpperLevelConfirmation(addr)
- }
-}
-
func (n *NIC) getLinkAddress(addr, localAddr tcpip.Address, protocol tcpip.NetworkProtocolNumber, onResolve func(LinkResolutionResult)) tcpip.Error {
linkRes, ok := n.linkAddrResolvers[protocol]
if !ok {
return &tcpip.ErrNotSupported{}
}
- if linkAddr, ok := linkRes.ResolveStaticAddress(addr); ok {
+ if linkAddr, ok := linkRes.resolver.ResolveStaticAddress(addr); ok {
onResolve(LinkResolutionResult{LinkAddress: linkAddr, Success: true})
return nil
}
- _, _, err := n.getNeighborLinkAddress(addr, localAddr, linkRes, onResolve)
+ _, _, err := linkRes.getNeighborLinkAddress(addr, localAddr, onResolve)
return err
}
-func (n *NIC) getNeighborLinkAddress(addr, localAddr tcpip.Address, linkRes LinkAddressResolver, onResolve func(LinkResolutionResult)) (tcpip.LinkAddress, <-chan struct{}, tcpip.Error) {
- if n.neighborTable != nil {
- return n.neighborTable.get(addr, linkRes, localAddr, onResolve)
- }
-
- return "", nil, &tcpip.ErrNotSupported{}
-}
-
-func (n *NIC) neighbors() ([]NeighborEntry, tcpip.Error) {
- if n.neighborTable != nil {
- return n.neighborTable.neighbors()
+func (n *NIC) neighbors(protocol tcpip.NetworkProtocolNumber) ([]NeighborEntry, tcpip.Error) {
+ if linkRes, ok := n.linkAddrResolvers[protocol]; ok {
+ return linkRes.neighborTable.neighbors()
}
return nil, &tcpip.ErrNotSupported{}
}
-func (n *NIC) addStaticNeighbor(addr tcpip.Address, linkAddress tcpip.LinkAddress) tcpip.Error {
- if n.neighborTable != nil {
- n.neighborTable.addStaticEntry(addr, linkAddress)
+func (n *NIC) addStaticNeighbor(addr tcpip.Address, protocol tcpip.NetworkProtocolNumber, linkAddress tcpip.LinkAddress) tcpip.Error {
+ if linkRes, ok := n.linkAddrResolvers[protocol]; ok {
+ linkRes.neighborTable.addStaticEntry(addr, linkAddress)
return nil
}
return &tcpip.ErrNotSupported{}
}
-func (n *NIC) removeNeighbor(addr tcpip.Address) tcpip.Error {
- if n.neighborTable != nil {
- return n.neighborTable.remove(addr)
+func (n *NIC) removeNeighbor(protocol tcpip.NetworkProtocolNumber, addr tcpip.Address) tcpip.Error {
+ if linkRes, ok := n.linkAddrResolvers[protocol]; ok {
+ return linkRes.neighborTable.remove(addr)
}
return &tcpip.ErrNotSupported{}
}
-func (n *NIC) clearNeighbors() tcpip.Error {
- if n.neighborTable != nil {
- return n.neighborTable.removeAll()
+func (n *NIC) clearNeighbors(protocol tcpip.NetworkProtocolNumber) tcpip.Error {
+ if linkRes, ok := n.linkAddrResolvers[protocol]; ok {
+ return linkRes.neighborTable.removeAll()
}
return &tcpip.ErrNotSupported{}
@@ -947,9 +951,9 @@ func (n *NIC) Name() string {
}
// nudConfigs gets the NUD configurations for n.
-func (n *NIC) nudConfigs() (NUDConfigurations, tcpip.Error) {
- if n.neighborTable != nil {
- return n.neighborTable.nudConfig()
+func (n *NIC) nudConfigs(protocol tcpip.NetworkProtocolNumber) (NUDConfigurations, tcpip.Error) {
+ if linkRes, ok := n.linkAddrResolvers[protocol]; ok {
+ return linkRes.neighborTable.nudConfig()
}
return NUDConfigurations{}, &tcpip.ErrNotSupported{}
@@ -959,10 +963,10 @@ func (n *NIC) nudConfigs() (NUDConfigurations, tcpip.Error) {
//
// Note, if c contains invalid NUD configuration values, it will be fixed to
// use default values for the erroneous values.
-func (n *NIC) setNUDConfigs(c NUDConfigurations) tcpip.Error {
- if n.neighborTable != nil {
+func (n *NIC) setNUDConfigs(protocol tcpip.NetworkProtocolNumber, c NUDConfigurations) tcpip.Error {
+ if linkRes, ok := n.linkAddrResolvers[protocol]; ok {
c.resetInvalidFields()
- return n.neighborTable.setNUDConfig(c)
+ return linkRes.neighborTable.setNUDConfig(c)
}
return &tcpip.ErrNotSupported{}
@@ -1003,15 +1007,21 @@ func (n *NIC) isValidForOutgoing(ep AssignableAddressEndpoint) bool {
}
// HandleNeighborProbe implements NetworkInterface.
-func (n *NIC) HandleNeighborProbe(addr tcpip.Address, linkAddr tcpip.LinkAddress, linkRes LinkAddressResolver) {
- if n.neighborTable != nil {
- n.neighborTable.handleProbe(addr, linkAddr, linkRes)
+func (n *NIC) HandleNeighborProbe(protocol tcpip.NetworkProtocolNumber, addr tcpip.Address, linkAddr tcpip.LinkAddress) tcpip.Error {
+ if l, ok := n.linkAddrResolvers[protocol]; ok {
+ l.neighborTable.handleProbe(addr, linkAddr)
+ return nil
}
+
+ return &tcpip.ErrNotSupported{}
}
// HandleNeighborConfirmation implements NetworkInterface.
-func (n *NIC) HandleNeighborConfirmation(addr tcpip.Address, linkAddr tcpip.LinkAddress, flags ReachabilityConfirmationFlags) {
- if n.neighborTable != nil {
- n.neighborTable.handleConfirmation(addr, linkAddr, flags)
+func (n *NIC) HandleNeighborConfirmation(protocol tcpip.NetworkProtocolNumber, addr tcpip.Address, linkAddr tcpip.LinkAddress, flags ReachabilityConfirmationFlags) tcpip.Error {
+ if l, ok := n.linkAddrResolvers[protocol]; ok {
+ l.neighborTable.handleConfirmation(addr, linkAddr, flags)
+ return nil
}
+
+ return &tcpip.ErrNotSupported{}
}
diff --git a/pkg/tcpip/stack/registration.go b/pkg/tcpip/stack/registration.go
index c652c2bd7..e02f7190c 100644
--- a/pkg/tcpip/stack/registration.go
+++ b/pkg/tcpip/stack/registration.go
@@ -536,11 +536,11 @@ type NetworkInterface interface {
//
// HandleNeighborProbe assumes that the probe is valid for the network
// interface the probe was received on.
- HandleNeighborProbe(tcpip.Address, tcpip.LinkAddress, LinkAddressResolver)
+ HandleNeighborProbe(tcpip.NetworkProtocolNumber, tcpip.Address, tcpip.LinkAddress) tcpip.Error
// HandleNeighborConfirmation processes an incoming neighbor confirmation
// (e.g. ARP reply or NDP Neighbor Advertisement).
- HandleNeighborConfirmation(tcpip.Address, tcpip.LinkAddress, ReachabilityConfirmationFlags)
+ HandleNeighborConfirmation(tcpip.NetworkProtocolNumber, tcpip.Address, tcpip.LinkAddress, ReachabilityConfirmationFlags) tcpip.Error
}
// LinkResolvableNetworkEndpoint handles link resolution events.
diff --git a/pkg/tcpip/stack/route.go b/pkg/tcpip/stack/route.go
index 1c8ef6ed4..bab55ce49 100644
--- a/pkg/tcpip/stack/route.go
+++ b/pkg/tcpip/stack/route.go
@@ -53,7 +53,7 @@ type Route struct {
// linkRes is set if link address resolution is enabled for this protocol on
// the route's NIC.
- linkRes LinkAddressResolver
+ linkRes linkResolver
}
type routeInfo struct {
@@ -184,11 +184,11 @@ func makeRoute(netProto tcpip.NetworkProtocolNumber, gateway, localAddr, remoteA
return r
}
- if r.linkRes == nil {
+ if r.linkRes.resolver == nil {
return r
}
- if linkAddr, ok := r.linkRes.ResolveStaticAddress(r.RemoteAddress); ok {
+ if linkAddr, ok := r.linkRes.resolver.ResolveStaticAddress(r.RemoteAddress); ok {
r.ResolveWith(linkAddr)
return r
}
@@ -362,7 +362,7 @@ func (r *Route) resolvedFields(afterResolve func(ResolvedFieldsResult)) (RouteIn
}
afterResolveFields := fields
- linkAddr, ch, err := r.outgoingNIC.getNeighborLinkAddress(r.nextHop(), linkAddressResolutionRequestLocalAddr, r.linkRes, func(r LinkResolutionResult) {
+ linkAddr, ch, err := r.linkRes.getNeighborLinkAddress(r.nextHop(), linkAddressResolutionRequestLocalAddr, func(r LinkResolutionResult) {
if afterResolve != nil {
if r.Success {
afterResolveFields.RemoteLinkAddress = r.LinkAddress
@@ -400,7 +400,7 @@ func (r *Route) IsResolutionRequired() bool {
}
func (r *Route) isResolutionRequiredRLocked() bool {
- return len(r.mu.remoteLinkAddress) == 0 && r.linkRes != nil && r.isValidForOutgoingRLocked() && !r.local()
+ return len(r.mu.remoteLinkAddress) == 0 && r.linkRes.resolver != nil && r.isValidForOutgoingRLocked() && !r.local()
}
func (r *Route) isValidForOutgoing() bool {
@@ -528,5 +528,7 @@ func (r *Route) IsOutboundBroadcast() bool {
// "Reachable" is defined as having full-duplex communication between the
// local and remote ends of the route.
func (r *Route) ConfirmReachable() {
- r.outgoingNIC.confirmReachable(r.nextHop())
+ if r.linkRes.resolver != nil {
+ r.linkRes.confirmReachable(r.nextHop())
+ }
}
diff --git a/pkg/tcpip/stack/stack.go b/pkg/tcpip/stack/stack.go
index 73db6e031..9390aaf57 100644
--- a/pkg/tcpip/stack/stack.go
+++ b/pkg/tcpip/stack/stack.go
@@ -1560,7 +1560,7 @@ func (s *Stack) GetLinkAddress(nicID tcpip.NICID, addr, localAddr tcpip.Address,
}
// Neighbors returns all IP to MAC address associations.
-func (s *Stack) Neighbors(nicID tcpip.NICID) ([]NeighborEntry, tcpip.Error) {
+func (s *Stack) Neighbors(nicID tcpip.NICID, protocol tcpip.NetworkProtocolNumber) ([]NeighborEntry, tcpip.Error) {
s.mu.RLock()
nic, ok := s.nics[nicID]
s.mu.RUnlock()
@@ -1569,11 +1569,11 @@ func (s *Stack) Neighbors(nicID tcpip.NICID) ([]NeighborEntry, tcpip.Error) {
return nil, &tcpip.ErrUnknownNICID{}
}
- return nic.neighbors()
+ return nic.neighbors(protocol)
}
// AddStaticNeighbor statically associates an IP address to a MAC address.
-func (s *Stack) AddStaticNeighbor(nicID tcpip.NICID, addr tcpip.Address, linkAddr tcpip.LinkAddress) tcpip.Error {
+func (s *Stack) AddStaticNeighbor(nicID tcpip.NICID, protocol tcpip.NetworkProtocolNumber, addr tcpip.Address, linkAddr tcpip.LinkAddress) tcpip.Error {
s.mu.RLock()
nic, ok := s.nics[nicID]
s.mu.RUnlock()
@@ -1582,13 +1582,13 @@ func (s *Stack) AddStaticNeighbor(nicID tcpip.NICID, addr tcpip.Address, linkAdd
return &tcpip.ErrUnknownNICID{}
}
- return nic.addStaticNeighbor(addr, linkAddr)
+ return nic.addStaticNeighbor(addr, protocol, linkAddr)
}
// RemoveNeighbor removes an IP to MAC address association previously created
// either automically or by AddStaticNeighbor. Returns ErrBadAddress if there
// is no association with the provided address.
-func (s *Stack) RemoveNeighbor(nicID tcpip.NICID, addr tcpip.Address) tcpip.Error {
+func (s *Stack) RemoveNeighbor(nicID tcpip.NICID, protocol tcpip.NetworkProtocolNumber, addr tcpip.Address) tcpip.Error {
s.mu.RLock()
nic, ok := s.nics[nicID]
s.mu.RUnlock()
@@ -1597,11 +1597,11 @@ func (s *Stack) RemoveNeighbor(nicID tcpip.NICID, addr tcpip.Address) tcpip.Erro
return &tcpip.ErrUnknownNICID{}
}
- return nic.removeNeighbor(addr)
+ return nic.removeNeighbor(protocol, addr)
}
// ClearNeighbors removes all IP to MAC address associations.
-func (s *Stack) ClearNeighbors(nicID tcpip.NICID) tcpip.Error {
+func (s *Stack) ClearNeighbors(nicID tcpip.NICID, protocol tcpip.NetworkProtocolNumber) tcpip.Error {
s.mu.RLock()
nic, ok := s.nics[nicID]
s.mu.RUnlock()
@@ -1610,7 +1610,7 @@ func (s *Stack) ClearNeighbors(nicID tcpip.NICID) tcpip.Error {
return &tcpip.ErrUnknownNICID{}
}
- return nic.clearNeighbors()
+ return nic.clearNeighbors(protocol)
}
// RegisterTransportEndpoint registers the given endpoint with the stack
@@ -1980,7 +1980,7 @@ func (s *Stack) GetNetworkEndpoint(nicID tcpip.NICID, proto tcpip.NetworkProtoco
}
// NUDConfigurations gets the per-interface NUD configurations.
-func (s *Stack) NUDConfigurations(id tcpip.NICID) (NUDConfigurations, tcpip.Error) {
+func (s *Stack) NUDConfigurations(id tcpip.NICID, proto tcpip.NetworkProtocolNumber) (NUDConfigurations, tcpip.Error) {
s.mu.RLock()
nic, ok := s.nics[id]
s.mu.RUnlock()
@@ -1989,14 +1989,14 @@ func (s *Stack) NUDConfigurations(id tcpip.NICID) (NUDConfigurations, tcpip.Erro
return NUDConfigurations{}, &tcpip.ErrUnknownNICID{}
}
- return nic.nudConfigs()
+ return nic.nudConfigs(proto)
}
// SetNUDConfigurations sets the per-interface NUD configurations.
//
// Note, if c contains invalid NUD configuration values, it will be fixed to
// use default values for the erroneous values.
-func (s *Stack) SetNUDConfigurations(id tcpip.NICID, c NUDConfigurations) tcpip.Error {
+func (s *Stack) SetNUDConfigurations(id tcpip.NICID, proto tcpip.NetworkProtocolNumber, c NUDConfigurations) tcpip.Error {
s.mu.RLock()
nic, ok := s.nics[id]
s.mu.RUnlock()
@@ -2005,7 +2005,7 @@ func (s *Stack) SetNUDConfigurations(id tcpip.NICID, c NUDConfigurations) tcpip.
return &tcpip.ErrUnknownNICID{}
}
- return nic.setNUDConfigs(c)
+ return nic.setNUDConfigs(proto, c)
}
// Seed returns a 32 bit value that can be used as a seed value for port