Skip to content

Commit fc2f4df

Browse files
avagingvisor-bot
authored andcommitted
netstack: set loopback as outgoing NIC for local traffic
This CL adjusts the network stack's behavior to route traffic addressed to any of the local IP addresses through the loopback interface. This change is required to match the Linux behavior. The CL introduces a loopbackNIC field in the Stack struct to cache the loopback interface. This cached interface is then used in findLocalRouteRLocked to create a local route for traffic destined to any local address. PiperOrigin-RevId: 795893254
1 parent 5077500 commit fc2f4df

File tree

1 file changed

+51
-0
lines changed

1 file changed

+51
-0
lines changed

pkg/tcpip/stack/stack.go

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,8 @@ type Stack struct {
9696
// +checklocks:mu
9797
nics map[tcpip.NICID]*nic `state:"nosave"`
9898
// +checklocks:mu
99+
loopbackNIC *nic
100+
// +checklocks:mu
99101
defaultForwardingEnabled map[tcpip.NetworkProtocolNumber]struct{}
100102

101103
// nicIDGen is used to generate NIC IDs.
@@ -936,6 +938,9 @@ func (s *Stack) CreateNICWithOptions(id tcpip.NICID, ep LinkEndpoint, opts NICOp
936938
}
937939
}
938940
s.nics[id] = n
941+
if n.IsLoopback() {
942+
s.loopbackNIC = n
943+
}
939944
ep.SetOnCloseAction(func() {
940945
s.RemoveNIC(id)
941946
})
@@ -1048,6 +1053,9 @@ func (s *Stack) removeNICLocked(id tcpip.NICID) (func(), tcpip.Error) {
10481053
}
10491054
s.routeMu.Unlock()
10501055

1056+
if s.loopbackNIC == nic {
1057+
s.loopbackNIC = nil
1058+
}
10511059
return nic.remove(true /* closeLinkEndpoint */)
10521060
}
10531061

@@ -1385,6 +1393,29 @@ func (s *Stack) findLocalRouteFromNICRLocked(localAddressNIC *nic, localAddr, re
13851393
return r
13861394
}
13871395

1396+
func (s *Stack) loopbackLocalRoute(localAddressNIC *nic, localAddr, remoteAddr tcpip.Address, netProto tcpip.NetworkProtocolNumber) *Route {
1397+
localAddressEndpoint := localAddressNIC.getAddressOrCreateTempInner(netProto, localAddr, true /* createTemp */, NeverPrimaryEndpoint)
1398+
if localAddressEndpoint == nil {
1399+
return nil
1400+
}
1401+
1402+
r := makeLocalRoute(
1403+
netProto,
1404+
localAddr,
1405+
remoteAddr,
1406+
localAddressNIC,
1407+
localAddressNIC,
1408+
localAddressEndpoint,
1409+
)
1410+
1411+
if r.IsOutboundBroadcast() {
1412+
r.Release()
1413+
return nil
1414+
}
1415+
1416+
return r
1417+
}
1418+
13881419
// findLocalRouteRLocked returns a local route.
13891420
//
13901421
// A local route is a route to some remote address which the stack owns. That
@@ -1397,6 +1428,22 @@ func (s *Stack) findLocalRouteRLocked(localAddressNICID tcpip.NICID, localAddr,
13971428
}
13981429

13991430
if localAddressNICID == 0 {
1431+
if s.loopbackNIC != nil {
1432+
// Send all packets directed to local ip addresses through the loopback device.
1433+
for _, nic := range s.nics {
1434+
if !nic.hasAddress(netProto, remoteAddr) {
1435+
continue
1436+
}
1437+
if isSubnetBroadcastOnNIC(nic, netProto, remoteAddr) {
1438+
break
1439+
}
1440+
if r := s.loopbackLocalRoute(s.loopbackNIC, localAddr, remoteAddr, netProto); r != nil {
1441+
return r
1442+
}
1443+
break
1444+
}
1445+
}
1446+
14001447
for _, localAddressNIC := range s.nics {
14011448
if r := s.findLocalRouteFromNICRLocked(localAddressNIC, localAddr, remoteAddr, netProto); r != nil {
14021449
return r
@@ -1992,9 +2039,13 @@ func (s *Stack) ReplaceConfig(st *Stack) {
19922039
s.mu.Lock()
19932040
defer s.mu.Unlock()
19942041
s.nics = make(map[tcpip.NICID]*nic)
2042+
s.loopbackNIC = nil
19952043
for id, nic := range nics {
19962044
nic.stack = s
19972045
s.nics[id] = nic
2046+
if nic.IsLoopback() {
2047+
s.loopbackNIC = nic
2048+
}
19982049
_ = s.NextNICID()
19992050
}
20002051
s.tables = st.tables

0 commit comments

Comments
 (0)