mirror of
https://github.com/tailscale/tailscale.git
synced 2025-05-24 08:18:30 +00:00
temp
This commit is contained in:
parent
1befa525cd
commit
22fc7da2dd
@ -30,12 +30,7 @@ import (
|
|||||||
// RouteAdvertiser is an interface that allows the AppConnector to advertise
|
// RouteAdvertiser is an interface that allows the AppConnector to advertise
|
||||||
// newly discovered routes that need to be served through the AppConnector.
|
// newly discovered routes that need to be served through the AppConnector.
|
||||||
type RouteAdvertiser interface {
|
type RouteAdvertiser interface {
|
||||||
// AdvertiseRoute adds one or more route advertisements skipping any that
|
AdvertiseRouteInfo(*routeinfo.RouteInfo)
|
||||||
// are already advertised.
|
|
||||||
AdvertiseRoute(...netip.Prefix) error
|
|
||||||
|
|
||||||
// UnadvertiseRoute removes any matching route advertisements.
|
|
||||||
UnadvertiseRoute(...netip.Prefix) error
|
|
||||||
|
|
||||||
// Store/ReadRouteInfo persists and retreives RouteInfo to stable storage
|
// Store/ReadRouteInfo persists and retreives RouteInfo to stable storage
|
||||||
StoreRouteInfo(*routeinfo.RouteInfo) error
|
StoreRouteInfo(*routeinfo.RouteInfo) error
|
||||||
@ -60,13 +55,13 @@ type AppConnector struct {
|
|||||||
|
|
||||||
// domains is a map of lower case domain names with no trailing dot, to an
|
// domains is a map of lower case domain names with no trailing dot, to an
|
||||||
// ordered list of resolved IP addresses.
|
// ordered list of resolved IP addresses.
|
||||||
domains map[string][]netip.Addr
|
// domains map[string][]netip.Addr
|
||||||
|
|
||||||
// controlRoutes is the list of routes that were last supplied by control.
|
// controlRoutes is the list of routes that were last supplied by control.
|
||||||
controlRoutes []netip.Prefix
|
// controlRoutes []netip.Prefix
|
||||||
|
|
||||||
// wildcards is the list of domain strings that match subdomains.
|
// wildcards is the list of domain strings that match subdomains.
|
||||||
wildcards []string
|
// wildcards []string
|
||||||
|
|
||||||
// the in memory copy of all the routes that's advertised
|
// the in memory copy of all the routes that's advertised
|
||||||
routeInfo *routeinfo.RouteInfo
|
routeInfo *routeinfo.RouteInfo
|
||||||
@ -121,12 +116,11 @@ func (e *AppConnector) RouteInfo() *routeinfo.RouteInfo {
|
|||||||
func (e *AppConnector) RecreateRouteInfoFromStore(localRoutes []netip.Prefix) {
|
func (e *AppConnector) RecreateRouteInfoFromStore(localRoutes []netip.Prefix) {
|
||||||
e.queue.Add(func() {
|
e.queue.Add(func() {
|
||||||
ri := e.RouteInfo()
|
ri := e.RouteInfo()
|
||||||
ri.Local = localRoutes
|
|
||||||
err := e.routeAdvertiser.StoreRouteInfo(ri)
|
err := e.routeAdvertiser.StoreRouteInfo(ri)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
e.logf("Appc recreate routeInfo: Error updating routeInfo in store: ", err)
|
e.logf("Appc recreate routeInfo: Error updating routeInfo in store: ", err)
|
||||||
}
|
}
|
||||||
err = e.routeAdvertiser.AdvertiseRoute(ri.Routes(false, true, true)...)
|
e.routeAdvertiser.AdvertiseRouteInfo(ri)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
e.logf("Appc recreate routeInfo: Error advertise routes: ", err)
|
e.logf("Appc recreate routeInfo: Error advertise routes: ", err)
|
||||||
}
|
}
|
||||||
@ -146,10 +140,7 @@ func (e *AppConnector) UpdateRouteInfo(ri *routeinfo.RouteInfo) {
|
|||||||
|
|
||||||
func (e *AppConnector) UnadvertiseRemoteRoutes() {
|
func (e *AppConnector) UnadvertiseRemoteRoutes() {
|
||||||
e.queue.Add(func() {
|
e.queue.Add(func() {
|
||||||
toRemove := e.RouteInfo().Routes(false, true, true)
|
e.routeAdvertiser.AdvertiseRouteInfo(nil)
|
||||||
if err := e.routeAdvertiser.UnadvertiseRoute(toRemove...); err != nil {
|
|
||||||
e.logf("failed to unadvertise routes %v: %v", toRemove, err)
|
|
||||||
}
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -173,45 +164,29 @@ func (e *AppConnector) updateDomains(domains []string) {
|
|||||||
e.mu.Lock()
|
e.mu.Lock()
|
||||||
defer e.mu.Unlock()
|
defer e.mu.Unlock()
|
||||||
|
|
||||||
var oldDomains map[string][]netip.Addr
|
|
||||||
oldDomains, e.domains = e.domains, make(map[string][]netip.Addr, len(domains))
|
|
||||||
var oldDiscovered map[string]*routeinfo.DatedRoutes
|
var oldDiscovered map[string]*routeinfo.DatedRoutes
|
||||||
var routeInfo *routeinfo.RouteInfo
|
var routeInfo *routeinfo.RouteInfo
|
||||||
shouldStoreRoutes := e.ShouldStoreRoutes
|
shouldStoreRoutes := e.ShouldStoreRoutes
|
||||||
if shouldStoreRoutes {
|
|
||||||
routeInfo = e.RouteInfo()
|
routeInfo = e.RouteInfo()
|
||||||
oldDiscovered, routeInfo.Discovered = routeInfo.Discovered, make(map[string]*routeinfo.DatedRoutes, len(domains))
|
oldDiscovered, routeInfo.Discovered = routeInfo.Discovered, make(map[string]*routeinfo.DatedRoutes, len(domains))
|
||||||
}
|
|
||||||
e.wildcards = e.wildcards[:0]
|
routeInfo.Wildcards = routeInfo.Wildcards[:0]
|
||||||
for _, d := range domains {
|
for _, d := range domains {
|
||||||
d = strings.ToLower(d)
|
d = strings.ToLower(d)
|
||||||
if len(d) == 0 {
|
if len(d) == 0 {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
if strings.HasPrefix(d, "*.") {
|
if strings.HasPrefix(d, "*.") {
|
||||||
e.wildcards = append(e.wildcards, d[2:])
|
routeInfo.Wildcards = append(routeInfo.Wildcards, d[2:])
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
e.domains[d] = oldDomains[d]
|
|
||||||
delete(oldDomains, d)
|
|
||||||
if shouldStoreRoutes {
|
|
||||||
routeInfo.Discovered[d] = oldDiscovered[d]
|
routeInfo.Discovered[d] = oldDiscovered[d]
|
||||||
delete(oldDiscovered, d)
|
delete(oldDiscovered, d)
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
// Ensure that still-live wildcards addresses are preserved as well.
|
|
||||||
for d, addrs := range oldDomains {
|
|
||||||
for _, wc := range e.wildcards {
|
|
||||||
if dnsname.HasSuffix(d, wc) {
|
|
||||||
e.domains[d] = addrs
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if shouldStoreRoutes {
|
|
||||||
for d, dr := range oldDiscovered {
|
for d, dr := range oldDiscovered {
|
||||||
for _, wc := range e.wildcards {
|
for _, wc := range routeInfo.Wildcards {
|
||||||
if dnsname.HasSuffix(d, wc) {
|
if dnsname.HasSuffix(d, wc) {
|
||||||
routeInfo.Discovered[d] = dr
|
routeInfo.Discovered[d] = dr
|
||||||
delete(oldDiscovered, d)
|
delete(oldDiscovered, d)
|
||||||
@ -219,30 +194,15 @@ func (e *AppConnector) updateDomains(domains []string) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
if shouldStoreRoutes {
|
if shouldStoreRoutes {
|
||||||
e.UpdateRouteInfo(routeInfo)
|
e.UpdateRouteInfo(routeInfo)
|
||||||
// every domain left in oldDiscovered won't be in e.domains
|
} else {
|
||||||
// routes can be unadvertised if it's not in local, control, or new discovered
|
e.routeInfo = routeInfo
|
||||||
currentRoutes := routeInfo.Routes(true, true, true)
|
|
||||||
slices.SortFunc(currentRoutes, comparePrefix)
|
|
||||||
currentRoutes = slices.Compact(currentRoutes)
|
|
||||||
for domainName, domainsRoutes := range oldDiscovered {
|
|
||||||
if domainsRoutes != nil {
|
|
||||||
toRemove := []netip.Prefix{}
|
|
||||||
for _, route := range domainsRoutes.RoutesSlice() {
|
|
||||||
_, ok := slices.BinarySearchFunc(currentRoutes, route, comparePrefix)
|
|
||||||
if !ok {
|
|
||||||
toRemove = append(toRemove, route)
|
|
||||||
}
|
}
|
||||||
}
|
e.scheduleAdvertiseRouteInfo(e.RouteInfo())
|
||||||
e.logf("unadvertising %d routes for domain: %s", len(toRemove), domainName)
|
|
||||||
e.scheduleUnadvertisement(domainName, toRemove...)
|
e.logf("handling domains: %v and wildcards: %v", xmaps.Keys(e.RouteInfo().Discovered), routeInfo.Wildcards)
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
e.logf("handling domains: %v and wildcards: %v", xmaps.Keys(e.domains), e.wildcards)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// updateRoutes merges the supplied routes into the currently configured routes. The routes supplied
|
// updateRoutes merges the supplied routes into the currently configured routes. The routes supplied
|
||||||
@ -254,64 +214,26 @@ func (e *AppConnector) updateRoutes(routes []netip.Prefix) {
|
|||||||
defer e.mu.Unlock()
|
defer e.mu.Unlock()
|
||||||
|
|
||||||
// If there was no change since the last update, no work to do.
|
// If there was no change since the last update, no work to do.
|
||||||
if slices.Equal(e.controlRoutes, routes) {
|
if slices.Equal(e.RouteInfo().Control, routes) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
var toRemove []netip.Prefix
|
|
||||||
var routeInfo *routeinfo.RouteInfo
|
var routeInfo *routeinfo.RouteInfo
|
||||||
var err error
|
var err error
|
||||||
|
e.routeInfo.Control = routes
|
||||||
if e.ShouldStoreRoutes {
|
if e.ShouldStoreRoutes {
|
||||||
routeInfo, err = e.routeAdvertiser.ReadRouteInfo()
|
routeInfo = e.RouteInfo()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if err != ipn.ErrStateNotExist {
|
if err != ipn.ErrStateNotExist {
|
||||||
e.logf("Appc: Unsuccessful Read RouteInfo: ", err)
|
e.logf("Appc: Unsuccessful Read RouteInfo: ", err)
|
||||||
}
|
}
|
||||||
routeInfo = routeinfo.NewRouteInfo()
|
routeInfo = routeinfo.NewRouteInfo()
|
||||||
}
|
}
|
||||||
oldControl := routeInfo.Control
|
|
||||||
routeInfo.Control = routes
|
routeInfo.Control = routes
|
||||||
e.routeInfo = routeInfo
|
|
||||||
e.routeAdvertiser.StoreRouteInfo(e.routeInfo)
|
e.routeAdvertiser.StoreRouteInfo(e.routeInfo)
|
||||||
oldOtherRoutes := routeInfo.Routes(true, false, true)
|
|
||||||
for _, ipp := range oldControl {
|
|
||||||
if slices.Contains(routes, ipp) {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
// unadvertise the prefix if the prefix is not recorded from other source.
|
|
||||||
if !slices.Contains(oldOtherRoutes, ipp) {
|
|
||||||
toRemove = append(toRemove, ipp)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := e.routeAdvertiser.UnadvertiseRoute(toRemove...); err != nil {
|
e.routeAdvertiser.AdvertiseRouteInfo(e.routeInfo)
|
||||||
e.logf("failed to unadvertise old routes: %v: %v", routes, err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if err := e.routeAdvertiser.AdvertiseRoute(routes...); err != nil {
|
|
||||||
e.logf("failed to advertise routes: %v: %v", routes, err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
toRemove = toRemove[:0]
|
|
||||||
|
|
||||||
nextRoute:
|
|
||||||
for _, r := range routes {
|
|
||||||
for _, addr := range e.domains {
|
|
||||||
for _, a := range addr {
|
|
||||||
if r.Contains(a) && netip.PrefixFrom(a, a.BitLen()) != r {
|
|
||||||
pfx := netip.PrefixFrom(a, a.BitLen())
|
|
||||||
toRemove = append(toRemove, pfx)
|
|
||||||
continue nextRoute
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := e.routeAdvertiser.UnadvertiseRoute(toRemove...); err != nil {
|
|
||||||
e.logf("failed to unadvertise routes: %v: %v", toRemove, err)
|
|
||||||
}
|
|
||||||
e.controlRoutes = routes
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Domains returns the currently configured domain list.
|
// Domains returns the currently configured domain list.
|
||||||
@ -319,7 +241,7 @@ func (e *AppConnector) Domains() views.Slice[string] {
|
|||||||
e.mu.Lock()
|
e.mu.Lock()
|
||||||
defer e.mu.Unlock()
|
defer e.mu.Unlock()
|
||||||
|
|
||||||
return views.SliceOf(xmaps.Keys(e.domains))
|
return views.SliceOf(xmaps.Keys(e.RouteInfo().Discovered))
|
||||||
}
|
}
|
||||||
|
|
||||||
// DomainRoutes returns a map of domains to resolved IP
|
// DomainRoutes returns a map of domains to resolved IP
|
||||||
@ -328,12 +250,7 @@ func (e *AppConnector) DomainRoutes() map[string][]netip.Addr {
|
|||||||
e.mu.Lock()
|
e.mu.Lock()
|
||||||
defer e.mu.Unlock()
|
defer e.mu.Unlock()
|
||||||
|
|
||||||
drCopy := make(map[string][]netip.Addr)
|
return e.routeInfo.DomainRoutes()
|
||||||
for k, v := range e.domains {
|
|
||||||
drCopy[k] = append(drCopy[k], v...)
|
|
||||||
}
|
|
||||||
|
|
||||||
return drCopy
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// ObserveDNSResponse is a callback invoked by the DNS resolver when a DNS
|
// ObserveDNSResponse is a callback invoked by the DNS resolver when a DNS
|
||||||
@ -429,6 +346,7 @@ func (e *AppConnector) ObserveDNSResponse(res []byte) {
|
|||||||
e.mu.Lock()
|
e.mu.Lock()
|
||||||
defer e.mu.Unlock()
|
defer e.mu.Unlock()
|
||||||
|
|
||||||
|
routeInfo := e.RouteInfo()
|
||||||
for domain, addrs := range addressRecords {
|
for domain, addrs := range addressRecords {
|
||||||
domain, isRouted := e.findRoutedDomainLocked(domain, cnameChain)
|
domain, isRouted := e.findRoutedDomainLocked(domain, cnameChain)
|
||||||
|
|
||||||
@ -439,21 +357,18 @@ func (e *AppConnector) ObserveDNSResponse(res []byte) {
|
|||||||
|
|
||||||
// advertise each address we have learned for the routed domain, that
|
// advertise each address we have learned for the routed domain, that
|
||||||
// was not already known.
|
// was not already known.
|
||||||
var toAdvertise []netip.Prefix
|
var domainPrefixs []netip.Prefix
|
||||||
for _, addr := range addrs {
|
for _, addr := range addrs {
|
||||||
if !e.isAddrKnownLocked(domain, addr) {
|
domainPrefixs = append(domainPrefixs, netip.PrefixFrom(addr, addr.BitLen()))
|
||||||
toAdvertise = append(toAdvertise, netip.PrefixFrom(addr, addr.BitLen()))
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
e.logf("[v2] observed new routes for %s: %s", domain, toAdvertise)
|
e.logf("[v2] observed new routes for %s: %s", domain, domainPrefixs)
|
||||||
if e.ShouldStoreRoutes && len(toAdvertise) != 0 {
|
routeInfo.AddRoutesInDiscoveredForDomain(domain, domainPrefixs)
|
||||||
routeInfo := e.RouteInfo()
|
if e.ShouldStoreRoutes {
|
||||||
routeInfo.AddRoutesInDiscoveredForDomain(domain, toAdvertise)
|
|
||||||
e.UpdateRouteInfo(routeInfo)
|
e.UpdateRouteInfo(routeInfo)
|
||||||
}
|
}
|
||||||
e.scheduleAdvertisement(domain, toAdvertise...)
|
|
||||||
}
|
}
|
||||||
|
e.scheduleAdvertiseRouteInfo(e.RouteInfo())
|
||||||
}
|
}
|
||||||
|
|
||||||
// starting from the given domain that resolved to an address, find it, or any
|
// starting from the given domain that resolved to an address, find it, or any
|
||||||
@ -464,15 +379,15 @@ func (e *AppConnector) ObserveDNSResponse(res []byte) {
|
|||||||
func (e *AppConnector) findRoutedDomainLocked(domain string, cnameChain map[string]string) (string, bool) {
|
func (e *AppConnector) findRoutedDomainLocked(domain string, cnameChain map[string]string) (string, bool) {
|
||||||
var isRouted bool
|
var isRouted bool
|
||||||
for {
|
for {
|
||||||
_, isRouted = e.domains[domain]
|
_, isRouted = e.RouteInfo().Discovered[domain]
|
||||||
if isRouted {
|
if isRouted {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
|
||||||
// match wildcard domains
|
// match wildcard domains
|
||||||
for _, wc := range e.wildcards {
|
for _, wc := range e.RouteInfo().Wildcards {
|
||||||
if dnsname.HasSuffix(domain, wc) {
|
if dnsname.HasSuffix(domain, wc) {
|
||||||
e.domains[domain] = nil
|
e.routeInfo.Discovered[domain] = nil
|
||||||
isRouted = true
|
isRouted = true
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
@ -487,88 +402,53 @@ func (e *AppConnector) findRoutedDomainLocked(domain string, cnameChain map[stri
|
|||||||
return domain, isRouted
|
return domain, isRouted
|
||||||
}
|
}
|
||||||
|
|
||||||
// isAddrKnownLocked returns true if the address is known to be associated with
|
// // scheduleAdvertisement schedules an advertisement of the given address
|
||||||
// the given domain. Known domain tables are updated for covered routes to speed
|
// // associated with the given domain.
|
||||||
// up future matches.
|
// func (e *AppConnector) scheduleAdvertisement(domain string, routes ...netip.Prefix) {
|
||||||
// e.mu must be held.
|
// e.queue.Add(func() {
|
||||||
func (e *AppConnector) isAddrKnownLocked(domain string, addr netip.Addr) bool {
|
// if err := e.routeAdvertiser.AdvertiseRoute(routes...); err != nil {
|
||||||
if e.hasDomainAddrLocked(domain, addr) {
|
// e.logf("failed to advertise routes for %s: %v: %v", domain, routes, err)
|
||||||
return true
|
// return
|
||||||
}
|
// }
|
||||||
for _, route := range e.controlRoutes {
|
// e.mu.Lock()
|
||||||
if route.Contains(addr) {
|
// defer e.mu.Unlock()
|
||||||
// record the new address associated with the domain for faster matching in subsequent
|
|
||||||
// requests and for diagnostic records.
|
|
||||||
e.addDomainAddrLocked(domain, addr)
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
// scheduleAdvertisement schedules an advertisement of the given address
|
// for _, route := range routes {
|
||||||
// associated with the given domain.
|
// if !route.IsSingleIP() {
|
||||||
func (e *AppConnector) scheduleAdvertisement(domain string, routes ...netip.Prefix) {
|
// continue
|
||||||
|
// }
|
||||||
|
// addr := route.Addr()
|
||||||
|
// if !e.hasDomainAddrLocked(domain, addr) {
|
||||||
|
// e.addDomainAddrLocked(domain, addr)
|
||||||
|
// e.logf("[v2] advertised route for %v: %v", domain, addr)
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// })
|
||||||
|
// }
|
||||||
|
|
||||||
|
// func (e *AppConnector) scheduleUnadvertisement(domain string, routes ...netip.Prefix) {
|
||||||
|
// e.queue.Add(func() {
|
||||||
|
// if err := e.routeAdvertiser.UnadvertiseRoute(routes...); err != nil {
|
||||||
|
// e.logf("failed to unadvertise routes for %s: %v: %v", domain, routes, err)
|
||||||
|
// return
|
||||||
|
// }
|
||||||
|
// e.mu.Lock()
|
||||||
|
// defer e.mu.Unlock()
|
||||||
|
|
||||||
|
// for _, route := range routes {
|
||||||
|
// if !route.IsSingleIP() {
|
||||||
|
// continue
|
||||||
|
// }
|
||||||
|
// addr := route.Addr()
|
||||||
|
|
||||||
|
// // e.deleteDomainAddrLocked(domain, addr)
|
||||||
|
// e.logf("[v2] unadvertised route for %v: %v", domain, addr)
|
||||||
|
// }
|
||||||
|
// })
|
||||||
|
// }
|
||||||
|
|
||||||
|
func (e *AppConnector) scheduleAdvertiseRouteInfo(ri *routeinfo.RouteInfo) {
|
||||||
e.queue.Add(func() {
|
e.queue.Add(func() {
|
||||||
if err := e.routeAdvertiser.AdvertiseRoute(routes...); err != nil {
|
e.routeAdvertiser.AdvertiseRouteInfo(ri)
|
||||||
e.logf("failed to advertise routes for %s: %v: %v", domain, routes, err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
e.mu.Lock()
|
|
||||||
defer e.mu.Unlock()
|
|
||||||
|
|
||||||
for _, route := range routes {
|
|
||||||
if !route.IsSingleIP() {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
addr := route.Addr()
|
|
||||||
if !e.hasDomainAddrLocked(domain, addr) {
|
|
||||||
e.addDomainAddrLocked(domain, addr)
|
|
||||||
e.logf("[v2] advertised route for %v: %v", domain, addr)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func (e *AppConnector) scheduleUnadvertisement(domain string, routes ...netip.Prefix) {
|
|
||||||
e.queue.Add(func() {
|
|
||||||
if err := e.routeAdvertiser.UnadvertiseRoute(routes...); err != nil {
|
|
||||||
e.logf("failed to unadvertise routes for %s: %v: %v", domain, routes, err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
e.mu.Lock()
|
|
||||||
defer e.mu.Unlock()
|
|
||||||
|
|
||||||
for _, route := range routes {
|
|
||||||
if !route.IsSingleIP() {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
addr := route.Addr()
|
|
||||||
|
|
||||||
//e.deleteDomainAddrLocked(domain, addr)
|
|
||||||
e.logf("[v2] unadvertised route for %v: %v", domain, addr)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
// hasDomainAddrLocked returns true if the address has been observed in a
|
|
||||||
// resolution of domain.
|
|
||||||
func (e *AppConnector) hasDomainAddrLocked(domain string, addr netip.Addr) bool {
|
|
||||||
_, ok := slices.BinarySearchFunc(e.domains[domain], addr, compareAddr)
|
|
||||||
return ok
|
|
||||||
}
|
|
||||||
|
|
||||||
// addDomainAddrLocked adds the address to the list of addresses resolved for
|
|
||||||
// domain and ensures the list remains sorted. Does not attempt to deduplicate.
|
|
||||||
func (e *AppConnector) addDomainAddrLocked(domain string, addr netip.Addr) {
|
|
||||||
e.domains[domain] = append(e.domains[domain], addr)
|
|
||||||
slices.SortFunc(e.domains[domain], compareAddr)
|
|
||||||
}
|
|
||||||
|
|
||||||
func compareAddr(l, r netip.Addr) int {
|
|
||||||
return l.Compare(r)
|
|
||||||
}
|
|
||||||
|
|
||||||
func comparePrefix(i, j netip.Prefix) int {
|
|
||||||
return i.Addr().Compare(j.Addr())
|
|
||||||
}
|
|
||||||
|
@ -9,29 +9,28 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
type RouteInfo struct {
|
type RouteInfo struct {
|
||||||
// routes set with --advertise-routes
|
|
||||||
Local []netip.Prefix
|
|
||||||
// routes from the 'routes' section of an app connector acl
|
// routes from the 'routes' section of an app connector acl
|
||||||
Control []netip.Prefix
|
Control []netip.Prefix
|
||||||
// routes discovered by observing dns lookups for configured domains
|
// routes discovered by observing dns lookups for configured domains
|
||||||
Discovered map[string]*DatedRoutes
|
Discovered map[string]*DatedRoutes
|
||||||
|
|
||||||
|
Wildcards []string
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewRouteInfo() *RouteInfo {
|
func NewRouteInfo() *RouteInfo {
|
||||||
discovered := make(map[string]*DatedRoutes)
|
discovered := make(map[string]*DatedRoutes)
|
||||||
return &RouteInfo{
|
return &RouteInfo{
|
||||||
Local: []netip.Prefix{},
|
|
||||||
Control: []netip.Prefix{},
|
Control: []netip.Prefix{},
|
||||||
Discovered: discovered,
|
Discovered: discovered,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// RouteInfo.Routes returns a slice containing all the routes stored from the wanted resources.
|
// RouteInfo.Routes returns a slice containing all the routes stored from the wanted resources.
|
||||||
func (ri *RouteInfo) Routes(local, control, discovered bool) []netip.Prefix {
|
func (ri *RouteInfo) Routes(control, discovered bool) []netip.Prefix {
|
||||||
var ret []netip.Prefix
|
if ri == nil {
|
||||||
if local {
|
return []netip.Prefix{}
|
||||||
ret = ri.Local
|
|
||||||
}
|
}
|
||||||
|
var ret []netip.Prefix
|
||||||
if control && len(ret) == 0 {
|
if control && len(ret) == 0 {
|
||||||
ret = ri.Control
|
ret = ri.Control
|
||||||
} else if control {
|
} else if control {
|
||||||
@ -48,6 +47,14 @@ func (ri *RouteInfo) Routes(local, control, discovered bool) []netip.Prefix {
|
|||||||
return ret
|
return ret
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (ri *RouteInfo) DomainRoutes() map[string][]netip.Addr {
|
||||||
|
drCopy := make(map[string][]netip.Addr)
|
||||||
|
for k, v := range ri.Discovered {
|
||||||
|
drCopy[k] = append(drCopy[k], v.AddrsSlice()...)
|
||||||
|
}
|
||||||
|
return drCopy
|
||||||
|
}
|
||||||
|
|
||||||
type DatedRoutes struct {
|
type DatedRoutes struct {
|
||||||
// routes discovered for a domain, and when they were last seen in a dns query
|
// routes discovered for a domain, and when they were last seen in a dns query
|
||||||
Routes map[netip.Prefix]time.Time
|
Routes map[netip.Prefix]time.Time
|
||||||
@ -63,6 +70,16 @@ func (dr *DatedRoutes) RoutesSlice() []netip.Prefix {
|
|||||||
return routes
|
return routes
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (dr *DatedRoutes) AddrsSlice() []netip.Addr {
|
||||||
|
var routes []netip.Addr
|
||||||
|
for k := range dr.Routes {
|
||||||
|
if k.IsSingleIP() {
|
||||||
|
routes = append(routes, k.Addr())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return routes
|
||||||
|
}
|
||||||
|
|
||||||
func (r *RouteInfo) AddRoutesInDiscoveredForDomain(domain string, addrs []netip.Prefix) {
|
func (r *RouteInfo) AddRoutesInDiscoveredForDomain(domain string, addrs []netip.Prefix) {
|
||||||
dr, hasKey := r.Discovered[domain]
|
dr, hasKey := r.Discovered[domain]
|
||||||
if !hasKey || dr == nil || dr.Routes == nil {
|
if !hasKey || dr == nil || dr.Routes == nil {
|
||||||
|
BIN
cmd/tailscaled/__debug_bin
Executable file
BIN
cmd/tailscaled/__debug_bin
Executable file
Binary file not shown.
@ -3189,29 +3189,6 @@ func (b *LocalBackend) SetUseExitNodeEnabled(v bool) (ipn.PrefsView, error) {
|
|||||||
return b.editPrefsLockedOnEntry(mp, unlock)
|
return b.editPrefsLockedOnEntry(mp, unlock)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (b *LocalBackend) PatchPrefsHandler(mp *ipn.MaskedPrefs) (ipn.PrefsView, error) {
|
|
||||||
// we believe that for the purpose of figuring out advertisedRoutes setPrefsLockedOnEntry is _only_ called when
|
|
||||||
// up or set is used on the tailscale cli _not_ when we calculate the new advertisedRoutes field.
|
|
||||||
if b.appConnector != nil && b.appConnector.ShouldStoreRoutes && mp.AdvertiseRoutesSet {
|
|
||||||
routeInfo := b.appConnector.RouteInfo()
|
|
||||||
curRoutes := routeInfo.Routes(false, true, true)
|
|
||||||
routeInfo.Local = mp.AdvertiseRoutes
|
|
||||||
b.appConnector.UpdateRouteInfo(routeInfo)
|
|
||||||
// When b.appConnector != nil, AppConnectorSet = true means
|
|
||||||
// The appConnector is turned off, in this case we should not
|
|
||||||
// append the remote routes to mp.AdvertiseRoutes. Appc will be
|
|
||||||
// set to nil first and unadvertise remote routes, but these remote routes
|
|
||||||
// will then be advertised again when the prefs are sent.
|
|
||||||
if !mp.AppConnectorSet {
|
|
||||||
curRoutes = append(curRoutes, mp.AdvertiseRoutes...)
|
|
||||||
slices.SortFunc(curRoutes, func(i, j netip.Prefix) int { return i.Addr().Compare(j.Addr()) })
|
|
||||||
curRoutes = slices.Compact(curRoutes)
|
|
||||||
mp.AdvertiseRoutes = curRoutes
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return b.EditPrefs(mp)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (b *LocalBackend) EditPrefs(mp *ipn.MaskedPrefs) (ipn.PrefsView, error) {
|
func (b *LocalBackend) EditPrefs(mp *ipn.MaskedPrefs) (ipn.PrefsView, error) {
|
||||||
if mp.SetsInternal() {
|
if mp.SetsInternal() {
|
||||||
return ipn.PrefsView{}, errors.New("can't set Internal fields")
|
return ipn.PrefsView{}, errors.New("can't set Internal fields")
|
||||||
@ -3653,7 +3630,8 @@ func (b *LocalBackend) authReconfig() {
|
|||||||
// If the current node is an app connector, ensure the app connector machine is started
|
// If the current node is an app connector, ensure the app connector machine is started
|
||||||
b.reconfigAppConnectorLocked(nm, prefs)
|
b.reconfigAppConnectorLocked(nm, prefs)
|
||||||
b.mu.Unlock()
|
b.mu.Unlock()
|
||||||
|
fmt.Println("kevin -- try lock1", b.mu.TryLock())
|
||||||
|
b.mu.Unlock()
|
||||||
if blocked {
|
if blocked {
|
||||||
b.logf("[v1] authReconfig: blocked, skipping.")
|
b.logf("[v1] authReconfig: blocked, skipping.")
|
||||||
return
|
return
|
||||||
@ -3666,6 +3644,8 @@ func (b *LocalBackend) authReconfig() {
|
|||||||
b.logf("[v1] authReconfig: skipping because !WantRunning.")
|
b.logf("[v1] authReconfig: skipping because !WantRunning.")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
fmt.Println("kevin -- try lock2", b.mu.TryLock())
|
||||||
|
b.mu.Unlock()
|
||||||
|
|
||||||
var flags netmap.WGConfigFlags
|
var flags netmap.WGConfigFlags
|
||||||
if prefs.RouteAll() {
|
if prefs.RouteAll() {
|
||||||
@ -3680,7 +3660,8 @@ func (b *LocalBackend) authReconfig() {
|
|||||||
flags &^= netmap.AllowSubnetRoutes
|
flags &^= netmap.AllowSubnetRoutes
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
fmt.Println("kevin -- try lock3", b.mu.TryLock())
|
||||||
|
b.mu.Unlock()
|
||||||
// Keep the dialer updated about whether we're supposed to use
|
// Keep the dialer updated about whether we're supposed to use
|
||||||
// an exit node's DNS server (so SOCKS5/HTTP outgoing dials
|
// an exit node's DNS server (so SOCKS5/HTTP outgoing dials
|
||||||
// can use it for name resolution)
|
// can use it for name resolution)
|
||||||
@ -3695,8 +3676,11 @@ func (b *LocalBackend) authReconfig() {
|
|||||||
b.logf("wgcfg: %v", err)
|
b.logf("wgcfg: %v", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
fmt.Println("kevin -- try lock 4", b.mu.TryLock())
|
||||||
|
b.mu.Unlock()
|
||||||
oneCGNATRoute := shouldUseOneCGNATRoute(b.logf, b.sys.ControlKnobs(), version.OS())
|
oneCGNATRoute := shouldUseOneCGNATRoute(b.logf, b.sys.ControlKnobs(), version.OS())
|
||||||
|
fmt.Println("kevin -- try lock 5", b.mu.TryLock())
|
||||||
|
b.mu.Unlock()
|
||||||
rcfg := b.routerConfig(cfg, prefs, oneCGNATRoute)
|
rcfg := b.routerConfig(cfg, prefs, oneCGNATRoute)
|
||||||
|
|
||||||
err = b.e.Reconfig(cfg, rcfg, dcfg)
|
err = b.e.Reconfig(cfg, rcfg, dcfg)
|
||||||
@ -4188,11 +4172,14 @@ func peerRoutes(logf logger.Logf, peers []wgcfg.Peer, cgnatThreshold int) (route
|
|||||||
|
|
||||||
// routerConfig produces a router.Config from a wireguard config and IPN prefs.
|
// routerConfig produces a router.Config from a wireguard config and IPN prefs.
|
||||||
func (b *LocalBackend) routerConfig(cfg *wgcfg.Config, prefs ipn.PrefsView, oneCGNATRoute bool) *router.Config {
|
func (b *LocalBackend) routerConfig(cfg *wgcfg.Config, prefs ipn.PrefsView, oneCGNATRoute bool) *router.Config {
|
||||||
|
fmt.Println("kevin -- try lock 6", b.mu.TryLock())
|
||||||
|
b.mu.Unlock()
|
||||||
singleRouteThreshold := 10_000
|
singleRouteThreshold := 10_000
|
||||||
if oneCGNATRoute {
|
if oneCGNATRoute {
|
||||||
singleRouteThreshold = 1
|
singleRouteThreshold = 1
|
||||||
}
|
}
|
||||||
|
fmt.Println("kevin -- try lock 7", b.mu.TryLock())
|
||||||
|
b.mu.Unlock()
|
||||||
b.mu.Lock()
|
b.mu.Lock()
|
||||||
netfilterKind := b.capForcedNetfilter // protected by b.mu
|
netfilterKind := b.capForcedNetfilter // protected by b.mu
|
||||||
b.mu.Unlock()
|
b.mu.Unlock()
|
||||||
@ -4204,9 +4191,11 @@ func (b *LocalBackend) routerConfig(cfg *wgcfg.Config, prefs ipn.PrefsView, oneC
|
|||||||
netfilterKind = prefs.NetfilterKind()
|
netfilterKind = prefs.NetfilterKind()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
toAdvertise := b.appConnector.RouteInfo().Routes(true, true)
|
||||||
|
toAdvertise = append(toAdvertise, prefs.AdvertiseRoutes().AsSlice()...)
|
||||||
rs := &router.Config{
|
rs := &router.Config{
|
||||||
LocalAddrs: unmapIPPrefixes(cfg.Addresses),
|
LocalAddrs: unmapIPPrefixes(cfg.Addresses),
|
||||||
SubnetRoutes: unmapIPPrefixes(prefs.AdvertiseRoutes().AsSlice()),
|
SubnetRoutes: unmapIPPrefixes(toAdvertise),
|
||||||
SNATSubnetRoutes: !prefs.NoSNAT(),
|
SNATSubnetRoutes: !prefs.NoSNAT(),
|
||||||
NetfilterMode: prefs.NetfilterMode(),
|
NetfilterMode: prefs.NetfilterMode(),
|
||||||
Routes: peerRoutes(b.logf, cfg.Peers, singleRouteThreshold),
|
Routes: peerRoutes(b.logf, cfg.Peers, singleRouteThreshold),
|
||||||
@ -4290,7 +4279,13 @@ func (b *LocalBackend) applyPrefsToHostinfoLocked(hi *tailcfg.Hostinfo, prefs ip
|
|||||||
if h := prefs.Hostname(); h != "" {
|
if h := prefs.Hostname(); h != "" {
|
||||||
hi.Hostname = h
|
hi.Hostname = h
|
||||||
}
|
}
|
||||||
hi.RoutableIPs = prefs.AdvertiseRoutes().AsSlice()
|
|
||||||
|
var routableIPs []netip.Prefix
|
||||||
|
if b.appConnector != nil {
|
||||||
|
routableIPs = append(routableIPs, b.appConnector.RouteInfo().Routes(true, true)...)
|
||||||
|
}
|
||||||
|
routableIPs = append(routableIPs, prefs.AdvertiseRoutes().AsSlice()...)
|
||||||
|
hi.RoutableIPs = routableIPs
|
||||||
hi.RequestTags = prefs.AdvertiseTags().AsSlice()
|
hi.RequestTags = prefs.AdvertiseTags().AsSlice()
|
||||||
hi.ShieldsUp = prefs.ShieldsUp()
|
hi.ShieldsUp = prefs.ShieldsUp()
|
||||||
hi.AllowsUpdate = envknob.AllowsRemoteUpdate() || prefs.AutoUpdate().Apply.EqualBool(true)
|
hi.AllowsUpdate = envknob.AllowsRemoteUpdate() || prefs.AutoUpdate().Apply.EqualBool(true)
|
||||||
@ -6212,38 +6207,50 @@ var ErrDisallowedAutoRoute = errors.New("route is not allowed")
|
|||||||
// AdvertiseRoute implements the appc.RouteAdvertiser interface. It sets a new
|
// AdvertiseRoute implements the appc.RouteAdvertiser interface. It sets a new
|
||||||
// route advertisement if one is not already present in the existing routes.
|
// route advertisement if one is not already present in the existing routes.
|
||||||
// If the route is disallowed, ErrDisallowedAutoRoute is returned.
|
// If the route is disallowed, ErrDisallowedAutoRoute is returned.
|
||||||
func (b *LocalBackend) AdvertiseRoute(ipps ...netip.Prefix) error {
|
func (b *LocalBackend) AdvertiseRouteInfo(ri *routeinfo.RouteInfo) {
|
||||||
finalRoutes := b.Prefs().AdvertiseRoutes().AsSlice()
|
b.mu.Lock()
|
||||||
newRoutes := false
|
defer b.mu.Unlock()
|
||||||
|
pref := b.pm.CurrentPrefs()
|
||||||
|
newRoutes := pref.AdvertiseRoutes().AsSlice()
|
||||||
|
oldHi := b.hostinfo
|
||||||
|
oldRoutes := oldHi.RoutableIPs
|
||||||
|
newHi := oldHi.Clone()
|
||||||
|
if newHi == nil {
|
||||||
|
newHi = new(tailcfg.Hostinfo)
|
||||||
|
}
|
||||||
|
routeInfoRoutes := ri.Routes(true, true)
|
||||||
|
|
||||||
for _, ipp := range ipps {
|
for _, ipp := range routeInfoRoutes {
|
||||||
if !allowedAutoRoute(ipp) {
|
if !allowedAutoRoute(ipp) {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
if slices.Contains(finalRoutes, ipp) {
|
if slices.Contains(newRoutes, ipp) {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
// If the new prefix is already contained by existing routes, skip it.
|
// If the new prefix is already contained by existing routes, skip it.
|
||||||
if coveredRouteRangeNoDefault(finalRoutes, ipp) {
|
if coveredRouteRangeNoDefault(newRoutes, ipp) {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
finalRoutes = append(finalRoutes, ipp)
|
newRoutes = append(newRoutes, ipp)
|
||||||
newRoutes = true
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if !newRoutes {
|
slices.SortFunc(oldRoutes, comparePrefix)
|
||||||
return nil
|
slices.SortFunc(newRoutes, comparePrefix)
|
||||||
|
|
||||||
|
if slices.CompareFunc(oldRoutes, newRoutes, comparePrefix) != 0 {
|
||||||
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
_, err := b.EditPrefs(&ipn.MaskedPrefs{
|
newHi.RoutableIPs = newRoutes
|
||||||
Prefs: ipn.Prefs{
|
b.hostinfo = newHi
|
||||||
AdvertiseRoutes: finalRoutes,
|
|
||||||
},
|
if !oldHi.Equal(newHi) {
|
||||||
AdvertiseRoutesSet: true,
|
b.doSetHostinfoFilterServices()
|
||||||
})
|
}
|
||||||
return err
|
|
||||||
|
b.authReconfig()
|
||||||
}
|
}
|
||||||
|
|
||||||
// coveredRouteRangeNoDefault checks if a route is already included in a slice of
|
// coveredRouteRangeNoDefault checks if a route is already included in a slice of
|
||||||
@ -6266,28 +6273,6 @@ func coveredRouteRangeNoDefault(finalRoutes []netip.Prefix, ipp netip.Prefix) bo
|
|||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
// UnadvertiseRoute implements the appc.RouteAdvertiser interface. It removes
|
|
||||||
// a route advertisement if one is present in the existing routes.
|
|
||||||
func (b *LocalBackend) UnadvertiseRoute(toRemove ...netip.Prefix) error {
|
|
||||||
currentRoutes := b.Prefs().AdvertiseRoutes().AsSlice()
|
|
||||||
finalRoutes := currentRoutes[:0]
|
|
||||||
|
|
||||||
for _, ipp := range currentRoutes {
|
|
||||||
if slices.Contains(toRemove, ipp) {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
finalRoutes = append(finalRoutes, ipp)
|
|
||||||
}
|
|
||||||
|
|
||||||
_, err := b.EditPrefs(&ipn.MaskedPrefs{
|
|
||||||
Prefs: ipn.Prefs{
|
|
||||||
AdvertiseRoutes: finalRoutes,
|
|
||||||
},
|
|
||||||
AdvertiseRoutesSet: true,
|
|
||||||
})
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// namespace a key with the profile manager's current profile key, if any
|
// namespace a key with the profile manager's current profile key, if any
|
||||||
func namespaceKeyForCurrentProfile(pm *profileManager, key ipn.StateKey) ipn.StateKey {
|
func namespaceKeyForCurrentProfile(pm *profileManager, key ipn.StateKey) ipn.StateKey {
|
||||||
return pm.CurrentProfile().Key + "||" + key
|
return pm.CurrentProfile().Key + "||" + key
|
||||||
@ -6314,8 +6299,8 @@ func (b *LocalBackend) StoreRouteInfo(ri *routeinfo.RouteInfo) error {
|
|||||||
// ReadRouteInfo implements the appc.RouteAdvertiser interface. It reads
|
// ReadRouteInfo implements the appc.RouteAdvertiser interface. It reads
|
||||||
// RouteInfo from StateStore per profile.
|
// RouteInfo from StateStore per profile.
|
||||||
func (b *LocalBackend) ReadRouteInfo() (*routeinfo.RouteInfo, error) {
|
func (b *LocalBackend) ReadRouteInfo() (*routeinfo.RouteInfo, error) {
|
||||||
b.mu.Lock()
|
// b.mu.Lock()
|
||||||
defer b.mu.Unlock()
|
// defer b.mu.Unlock()
|
||||||
if b.pm.CurrentProfile().ID == "" {
|
if b.pm.CurrentProfile().ID == "" {
|
||||||
return &routeinfo.RouteInfo{}, nil
|
return &routeinfo.RouteInfo{}, nil
|
||||||
}
|
}
|
||||||
@ -6376,3 +6361,7 @@ func mayDeref[T any](p *T) (v T) {
|
|||||||
}
|
}
|
||||||
return *p
|
return *p
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func comparePrefix(i, j netip.Prefix) int {
|
||||||
|
return i.Addr().Compare(j.Addr())
|
||||||
|
}
|
||||||
|
@ -1368,7 +1368,7 @@ func (h *Handler) servePrefs(w http.ResponseWriter, r *http.Request) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
var err error
|
var err error
|
||||||
prefs, err = h.b.PatchPrefsHandler(mp)
|
prefs, err = h.b.EditPrefs(mp)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
w.Header().Set("Content-Type", "application/json")
|
w.Header().Set("Content-Type", "application/json")
|
||||||
w.WriteHeader(http.StatusBadRequest)
|
w.WriteHeader(http.StatusBadRequest)
|
||||||
|
@ -2232,13 +2232,10 @@ const (
|
|||||||
// NodeAttrDisableWebClient disables using the web client.
|
// NodeAttrDisableWebClient disables using the web client.
|
||||||
NodeAttrDisableWebClient NodeCapability = "disable-web-client"
|
NodeAttrDisableWebClient NodeCapability = "disable-web-client"
|
||||||
|
|
||||||
<<<<<<< HEAD
|
|
||||||
// NodeAttrExitDstNetworkFlowLog enables exit node destinations in network flow logs.
|
// NodeAttrExitDstNetworkFlowLog enables exit node destinations in network flow logs.
|
||||||
NodeAttrExitDstNetworkFlowLog NodeCapability = "exit-dst-network-flow-log"
|
NodeAttrExitDstNetworkFlowLog NodeCapability = "exit-dst-network-flow-log"
|
||||||
=======
|
|
||||||
// NodeAttrStoreAppCRoutes enables storing app connector routes persistently.
|
// NodeAttrStoreAppCRoutes enables storing app connector routes persistently.
|
||||||
NodeAttrStoreAppCRoutes NodeCapability = "store-appc-routes"
|
NodeAttrStoreAppCRoutes NodeCapability = "store-appc-routes"
|
||||||
>>>>>>> 61f7b83bd (Add a control knob to toggle writing RouteInfo to StateStore)
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// SetDNSRequest is a request to add a DNS record.
|
// SetDNSRequest is a request to add a DNS record.
|
||||||
|
Loading…
x
Reference in New Issue
Block a user