summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
-rw-r--r--pkg/tcpip/stack/neighbor_cache.go7
-rw-r--r--pkg/tcpip/stack/nic.go6
-rw-r--r--pkg/tcpip/stack/nud.go4
-rw-r--r--pkg/tcpip/stack/route.go23
-rw-r--r--pkg/tcpip/transport/tcp/connect.go8
-rw-r--r--pkg/tcpip/transport/tcp/snd.go11
6 files changed, 45 insertions, 14 deletions
diff --git a/pkg/tcpip/stack/neighbor_cache.go b/pkg/tcpip/stack/neighbor_cache.go
index 204196d00..eea32dcf5 100644
--- a/pkg/tcpip/stack/neighbor_cache.go
+++ b/pkg/tcpip/stack/neighbor_cache.go
@@ -297,10 +297,9 @@ func (n *neighborCache) HandleConfirmation(addr tcpip.Address, linkAddr tcpip.Li
// no matching entry for the remote address.
}
-// HandleUpperLevelConfirmation implements
-// NUDHandler.HandleUpperLevelConfirmation by following the logic defined in
-// RFC 4861 section 7.3.1.
-func (n *neighborCache) HandleUpperLevelConfirmation(addr tcpip.Address) {
+// handleUpperLevelConfirmation processes a confirmation of reachablity from
+// some protocol that operates at a layer above the IP/link layer.
+func (n *neighborCache) handleUpperLevelConfirmation(addr tcpip.Address) {
n.mu.RLock()
entry, ok := n.cache[addr]
n.mu.RUnlock()
diff --git a/pkg/tcpip/stack/nic.go b/pkg/tcpip/stack/nic.go
index 1bbfe6213..f59416fd3 100644
--- a/pkg/tcpip/stack/nic.go
+++ b/pkg/tcpip/stack/nic.go
@@ -561,6 +561,12 @@ func (n *NIC) removeAddress(addr tcpip.Address) *tcpip.Error {
return tcpip.ErrBadLocalAddress
}
+func (n *NIC) confirmReachable(addr tcpip.Address) {
+ if n := n.neigh; n != nil {
+ n.handleUpperLevelConfirmation(addr)
+ }
+}
+
func (n *NIC) getNeighborLinkAddress(addr, localAddr tcpip.Address, linkRes LinkAddressResolver, onResolve func(LinkResolutionResult)) (tcpip.LinkAddress, <-chan struct{}, *tcpip.Error) {
if n.neigh != nil {
entry, ch, err := n.neigh.entry(addr, localAddr, linkRes, onResolve)
diff --git a/pkg/tcpip/stack/nud.go b/pkg/tcpip/stack/nud.go
index 12d67409a..77926e289 100644
--- a/pkg/tcpip/stack/nud.go
+++ b/pkg/tcpip/stack/nud.go
@@ -174,10 +174,6 @@ type NUDHandler interface {
// HandleConfirmation processes an incoming neighbor confirmation (e.g. ARP
// reply or Neighbor Advertisement for ARP or NDP, respectively).
HandleConfirmation(addr tcpip.Address, linkAddr tcpip.LinkAddress, flags ReachabilityConfirmationFlags)
-
- // HandleUpperLevelConfirmation processes an incoming upper-level protocol
- // (e.g. TCP acknowledgements) reachability confirmation.
- HandleUpperLevelConfirmation(addr tcpip.Address)
}
// NUDConfigurations is the NUD configurations for the netstack. This is used
diff --git a/pkg/tcpip/stack/route.go b/pkg/tcpip/stack/route.go
index d9a8554e2..9c8c155fa 100644
--- a/pkg/tcpip/stack/route.go
+++ b/pkg/tcpip/stack/route.go
@@ -354,11 +354,6 @@ func (r *Route) resolvedFields(afterResolve func(ResolvedFieldsResult)) (RouteIn
return fields, nil, nil
}
- nextAddr := r.NextHop
- if nextAddr == "" {
- nextAddr = r.RemoteAddress
- }
-
// If specified, the local address used for link address resolution must be an
// address on the outgoing interface.
var linkAddressResolutionRequestLocalAddr tcpip.Address
@@ -367,7 +362,7 @@ func (r *Route) resolvedFields(afterResolve func(ResolvedFieldsResult)) (RouteIn
}
afterResolveFields := fields
- linkAddr, ch, err := r.outgoingNIC.getNeighborLinkAddress(nextAddr, linkAddressResolutionRequestLocalAddr, r.linkRes, func(r LinkResolutionResult) {
+ linkAddr, ch, err := r.outgoingNIC.getNeighborLinkAddress(r.nextHop(), linkAddressResolutionRequestLocalAddr, r.linkRes, func(r LinkResolutionResult) {
if afterResolve != nil {
if r.Success {
afterResolveFields.RemoteLinkAddress = r.LinkAddress
@@ -382,6 +377,13 @@ func (r *Route) resolvedFields(afterResolve func(ResolvedFieldsResult)) (RouteIn
return fields, ch, err
}
+func (r *Route) nextHop() tcpip.Address {
+ if len(r.NextHop) == 0 {
+ return r.RemoteAddress
+ }
+ return r.NextHop
+}
+
// local returns true if the route is a local route.
func (r *Route) local() bool {
return r.Loop == PacketLoop || r.outgoingNIC.IsLoopback()
@@ -519,3 +521,12 @@ func (r *Route) IsOutboundBroadcast() bool {
// Only IPv4 has a notion of broadcast.
return r.isV4Broadcast(r.RemoteAddress)
}
+
+// ConfirmReachable informs the network/link layer that the neighbour used for
+// the route is reachable.
+//
+// "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())
+}
diff --git a/pkg/tcpip/transport/tcp/connect.go b/pkg/tcpip/transport/tcp/connect.go
index 62954d7e4..6df4e6525 100644
--- a/pkg/tcpip/transport/tcp/connect.go
+++ b/pkg/tcpip/transport/tcp/connect.go
@@ -1335,6 +1335,14 @@ func (e *endpoint) protocolMainLoop(handshake bool, wakerInitDone chan<- struct{
}
}
+ // Reaching this point means that we successfully completed the 3-way
+ // handshake with our peer.
+ //
+ // Completing the 3-way handshake is an indication that the route is valid
+ // and the remote is reachable as the only way we can complete a handshake
+ // is if our SYN reached the remote and their ACK reached us.
+ e.route.ConfirmReachable()
+
drained := e.drainDone != nil
if drained {
close(e.drainDone)
diff --git a/pkg/tcpip/transport/tcp/snd.go b/pkg/tcpip/transport/tcp/snd.go
index 28ef9f899..027c2a4a8 100644
--- a/pkg/tcpip/transport/tcp/snd.go
+++ b/pkg/tcpip/transport/tcp/snd.go
@@ -1390,6 +1390,17 @@ func (s *sender) handleRcvdSegment(rcvdSeg *segment) {
acked := s.sndUna.Size(ack)
s.sndUna = ack
+ // The remote ACK-ing at least 1 byte is an indication that we have a
+ // full-duplex connection to the remote as the only way we will receive an
+ // ACK is if the remote received data that we previously sent.
+ //
+ // As of writing, linux seems to only confirm a route as reachable when
+ // forward progress is made which is indicated by an ACK that removes data
+ // from the retransmit queue.
+ if acked > 0 {
+ s.ep.route.ConfirmReachable()
+ }
+
ackLeft := acked
originalOutstanding := s.outstanding
for ackLeft > 0 {