Refactor default router state to off-link route state

This change prepares for a later change which supports the NDP
Route Information option to discover more-specific routes, as
per RFC 4191.

The newly introduced off-link route state will be used to hold
both the state for default routers (which is a default (off-link)
route through the router, and more-specific routes (which are
routes through some router to some destination subnet more specific
than the IPv6 empty subnet).

Updates #6172.

PiperOrigin-RevId: 381403761
This commit is contained in:
Ghanan Gowripalan
2021-06-24 22:39:52 -07:00
committed by gVisor bot
parent ccd2d607e5
commit 49986674aa
4 changed files with 81 additions and 79 deletions
+4 -1
View File
@@ -344,7 +344,10 @@ func (e *endpoint) onAddressAssignedLocked(addr tcpip.Address) {
func (e *endpoint) InvalidateDefaultRouter(rtr tcpip.Address) {
e.mu.Lock()
defer e.mu.Unlock()
e.mu.ndp.invalidateDefaultRouter(rtr)
// We represent default routers with a default (off-link) route through the
// router.
e.mu.ndp.invalidateOffLinkRoute(offLinkRoute{dest: header.IPv6EmptySubnet, router: rtr})
}
// SetNDPConfigurations implements NDPEndpoint.
+73 -74
View File
@@ -78,13 +78,13 @@ const (
// we cannot have a negative delay.
minimumMaxRtrSolicitationDelay = 0
// MaxDiscoveredDefaultRouters is the maximum number of discovered
// default routers. The stack should stop discovering new routers after
// discovering MaxDiscoveredDefaultRouters routers.
// MaxDiscoveredOffLinkRoutes is the maximum number of discovered off-link
// routes. The stack should stop discovering new off-link routes after
// this limit is reached.
//
// This value MUST be at minimum 2 as per RFC 4861 section 6.3.4, and
// SHOULD be more.
MaxDiscoveredDefaultRouters = 10
MaxDiscoveredOffLinkRoutes = 10
// MaxDiscoveredOnLinkPrefixes is the maximum number of discovered
// on-link prefixes. The stack should stop discovering new on-link
@@ -448,6 +448,11 @@ type timer struct {
timer tcpip.Timer
}
type offLinkRoute struct {
dest tcpip.Subnet
router tcpip.Address
}
// ndpState is the per-Interface NDP state.
type ndpState struct {
// Do not allow overwriting this state.
@@ -462,8 +467,8 @@ type ndpState struct {
// The DAD timers to send the next NS message, or resolve the address.
dad ip.DAD
// The default routers discovered through Router Advertisements.
defaultRouters map[tcpip.Address]defaultRouterState
// The off-link routes discovered through Router Advertisements.
offLinkRoutes map[offLinkRoute]offLinkRouteState
// rtrSolicitTimer is the timer used to send the next router solicitation
// message.
@@ -491,12 +496,12 @@ type ndpState struct {
temporaryAddressDesyncFactor time.Duration
}
// defaultRouterState holds data associated with a default router discovered by
// offLinkRouteState holds data associated with an off-link route discovered by
// a Router Advertisement (RA).
type defaultRouterState struct {
type offLinkRouteState struct {
prf header.NDPRoutePreference
// Job to invalidate the default router.
// Job to invalidate the route.
//
// Must not be nil.
invalidationJob *tcpip.Job
@@ -727,38 +732,9 @@ func (ndp *ndpState) handleRA(ip tcpip.Address, ra header.NDPRouterAdvert) {
prf = header.MediumRoutePreference
}
rtr, ok := ndp.defaultRouters[ip]
rl := ra.RouterLifetime()
switch {
case !ok && rl != 0:
// This is a new default router we are discovering.
//
// Only remember it if we currently know about less than
// MaxDiscoveredDefaultRouters routers.
if len(ndp.defaultRouters) < MaxDiscoveredDefaultRouters {
ndp.rememberDefaultRouter(ip, rl, prf)
}
case ok && rl != 0:
// This is an already discovered default router. Update
// the invalidation job.
rtr.invalidationJob.Cancel()
rtr.invalidationJob.Schedule(rl)
if prf != rtr.prf {
rtr.prf = prf
// Inform the integrator about router preference updates.
ndp.ep.protocol.options.NDPDisp.OnOffLinkRouteUpdated(ndp.ep.nic.ID(), header.IPv6EmptySubnet, ip, prf)
}
ndp.defaultRouters[ip] = rtr
case ok && rl == 0:
// We know about the router but it is no longer to be
// used as a default router so invalidate it.
ndp.invalidateDefaultRouter(ip)
}
// We represent default routers with a default (off-link) route through the
// router.
ndp.handleOffLinkRouteDiscovery(offLinkRoute{dest: header.IPv6EmptySubnet, router: ip}, ra.RouterLifetime(), prf)
}
// TODO(b/141556115): Do (RetransTimer, ReachableTime)) Parameter
@@ -816,52 +792,75 @@ func (ndp *ndpState) handleRA(ip tcpip.Address, ra header.NDPRouterAdvert) {
}
}
// invalidateDefaultRouter invalidates a discovered default router.
// invalidateOffLinkRoute invalidates a discovered off-link route.
//
// The IPv6 endpoint that ndp belongs to MUST be locked.
func (ndp *ndpState) invalidateDefaultRouter(ip tcpip.Address) {
rtr, ok := ndp.defaultRouters[ip]
// Is the router still discovered?
func (ndp *ndpState) invalidateOffLinkRoute(route offLinkRoute) {
state, ok := ndp.offLinkRoutes[route]
if !ok {
// ...Nope, do nothing further.
return
}
rtr.invalidationJob.Cancel()
delete(ndp.defaultRouters, ip)
state.invalidationJob.Cancel()
delete(ndp.offLinkRoutes, route)
// Let the integrator know a discovered default router is invalidated.
// Let the integrator know a discovered off-link route is invalidated.
if ndpDisp := ndp.ep.protocol.options.NDPDisp; ndpDisp != nil {
ndpDisp.OnOffLinkRouteInvalidated(ndp.ep.nic.ID(), header.IPv6EmptySubnet, ip)
ndpDisp.OnOffLinkRouteInvalidated(ndp.ep.nic.ID(), route.dest, route.router)
}
}
// rememberDefaultRouter remembers a newly discovered default router with IPv6
// link-local address ip with lifetime rl.
// handleOffLinkRouteDiscovery handles the discovery of an off-link route.
//
// The router identified by ip MUST NOT already be known by the IPv6 endpoint.
//
// The IPv6 endpoint that ndp belongs to MUST be locked.
func (ndp *ndpState) rememberDefaultRouter(ip tcpip.Address, rl time.Duration, prf header.NDPRoutePreference) {
// Precondition: ndp.ep.mu must be locked.
func (ndp *ndpState) handleOffLinkRouteDiscovery(route offLinkRoute, lifetime time.Duration, prf header.NDPRoutePreference) {
ndpDisp := ndp.ep.protocol.options.NDPDisp
if ndpDisp == nil {
return
}
// Inform the integrator when we discovered a default router.
ndpDisp.OnOffLinkRouteUpdated(ndp.ep.nic.ID(), header.IPv6EmptySubnet, ip, prf)
state, ok := ndp.offLinkRoutes[route]
switch {
case !ok && lifetime != 0:
// This is a new route we are discovering.
//
// Only remember it if we currently know about less than
// MaxDiscoveredOffLinkRoutes routers.
if len(ndp.offLinkRoutes) < MaxDiscoveredOffLinkRoutes {
// Inform the integrator when we discovered an off-link route.
ndpDisp.OnOffLinkRouteUpdated(ndp.ep.nic.ID(), route.dest, route.router, prf)
state := defaultRouterState{
prf: prf,
invalidationJob: ndp.ep.protocol.stack.NewJob(&ndp.ep.mu, func() {
ndp.invalidateDefaultRouter(ip)
}),
state := offLinkRouteState{
prf: prf,
invalidationJob: ndp.ep.protocol.stack.NewJob(&ndp.ep.mu, func() {
ndp.invalidateOffLinkRoute(route)
}),
}
state.invalidationJob.Schedule(lifetime)
ndp.offLinkRoutes[route] = state
}
case ok && lifetime != 0:
// This is an already discovered off-link route. Update the lifetime.
state.invalidationJob.Cancel()
state.invalidationJob.Schedule(lifetime)
if prf != state.prf {
state.prf = prf
// Inform the integrator about route preference updates.
ndpDisp.OnOffLinkRouteUpdated(ndp.ep.nic.ID(), route.dest, route.router, prf)
}
ndp.offLinkRoutes[route] = state
case ok && lifetime == 0:
// The already discovered off-link route is no longer considered valid so we
// invalidate it immediately.
ndp.invalidateOffLinkRoute(route)
}
state.invalidationJob.Schedule(rl)
ndp.defaultRouters[ip] = state
}
// rememberOnLinkPrefix remembers a newly discovered on-link prefix with IPv6
@@ -1677,12 +1676,12 @@ func (ndp *ndpState) cleanupState() {
panic(fmt.Sprintf("ndp: still have discovered on-link prefixes after cleaning up; found = %d", got))
}
for router := range ndp.defaultRouters {
ndp.invalidateDefaultRouter(router)
for route := range ndp.offLinkRoutes {
ndp.invalidateOffLinkRoute(route)
}
if got := len(ndp.defaultRouters); got != 0 {
panic(fmt.Sprintf("ndp: still have discovered default routers after cleaning up; found = %d", got))
if got := len(ndp.offLinkRoutes); got != 0 {
panic(fmt.Sprintf("ndp: still have discovered off-link routes after cleaning up; found = %d", got))
}
ndp.dhcpv6Configuration = 0
@@ -1845,14 +1844,14 @@ func (ndp *ndpState) stopSolicitingRouters() {
}
func (ndp *ndpState) init(ep *endpoint, dadOptions ip.DADOptions) {
if ndp.defaultRouters != nil {
if ndp.offLinkRoutes != nil {
panic("attempted to initialize NDP state twice")
}
ndp.ep = ep
ndp.configs = ep.protocol.options.NDPConfigs
ndp.dad.Init(&ndp.ep.mu, ep.protocol.options.DADConfigs, dadOptions)
ndp.defaultRouters = make(map[tcpip.Address]defaultRouterState)
ndp.offLinkRoutes = make(map[offLinkRoute]offLinkRouteState)
ndp.onLinkPrefixes = make(map[tcpip.Subnet]onLinkPrefixState)
ndp.slaacPrefixes = make(map[tcpip.Subnet]slaacPrefixState)
+1 -1
View File
@@ -93,7 +93,7 @@ func TestStackNDPEndpointInvalidateDefaultRouter(t *testing.T) {
ipv6EP := ep.(*endpoint)
ipv6EP.mu.Lock()
ipv6EP.mu.ndp.rememberDefaultRouter(lladdr1, time.Hour, header.MediumRoutePreference)
ipv6EP.mu.ndp.handleOffLinkRouteDiscovery(offLinkRoute{dest: header.IPv6EmptySubnet, router: lladdr1}, time.Hour, header.MediumRoutePreference)
ipv6EP.mu.Unlock()
if ndpDisp.addr != lladdr1 {
+3 -3
View File
@@ -1461,7 +1461,7 @@ func TestRouterDiscovery(t *testing.T) {
}
// TestRouterDiscoveryMaxRouters tests that only
// ipv6.MaxDiscoveredDefaultRouters discovered routers are remembered.
// ipv6.MaxDiscoveredOffLinkRoutes discovered routers are remembered.
func TestRouterDiscoveryMaxRouters(t *testing.T) {
const nicID = 1
@@ -1484,14 +1484,14 @@ func TestRouterDiscoveryMaxRouters(t *testing.T) {
}
// Receive an RA from 2 more than the max number of discovered routers.
for i := 1; i <= ipv6.MaxDiscoveredDefaultRouters+2; i++ {
for i := 1; i <= ipv6.MaxDiscoveredOffLinkRoutes+2; i++ {
linkAddr := []byte{2, 2, 3, 4, 5, 0}
linkAddr[5] = byte(i)
llAddr := header.LinkLocalAddr(tcpip.LinkAddress(linkAddr))
e.InjectInbound(header.IPv6ProtocolNumber, raBufSimple(llAddr, 5))
if i <= ipv6.MaxDiscoveredDefaultRouters {
if i <= ipv6.MaxDiscoveredOffLinkRoutes {
select {
case e := <-ndpDisp.offLinkRouteC:
if diff := checkOffLinkRouteEvent(e, nicID, llAddr, header.MediumRoutePreference, true); diff != "" {