wgengine/netstack: add local tailscale service IPs to route and terminate locally (#18461)
* wgengine/netstack: add local tailscale service IPs to route and terminate locally This commit adds the tailscales service IPs served locally to OS routes, and make interception to packets so that the traffic terminates locally without making affects to the HA traffics. Fixes tailscale/corp#34048 Signed-off-by: KevinLiang10 <37811973+KevinLiang10@users.noreply.github.com> * fix test Signed-off-by: KevinLiang10 <37811973+KevinLiang10@users.noreply.github.com> * add ready field to avoid accessing lb before netstack starts Signed-off-by: KevinLiang10 <37811973+KevinLiang10@users.noreply.github.com> * wgengine/netstack: store values from lb to avoid acquiring a lock Signed-off-by: KevinLiang10 <37811973+KevinLiang10@users.noreply.github.com> * add active services to netstack on starts with stored prefs. Signed-off-by: KevinLiang10 <37811973+KevinLiang10@users.noreply.github.com> * fix comments Signed-off-by: KevinLiang10 <37811973+KevinLiang10@users.noreply.github.com> * update comments Signed-off-by: KevinLiang10 <37811973+KevinLiang10@users.noreply.github.com> --------- Signed-off-by: KevinLiang10 <37811973+KevinLiang10@users.noreply.github.com>
This commit is contained in:
+45
-3
@@ -922,6 +922,22 @@ func (b *LocalBackend) setStateLocked(state ipn.State) {
|
||||
}
|
||||
}
|
||||
|
||||
func (b *LocalBackend) IPServiceMappings() netmap.IPServiceMappings {
|
||||
b.mu.Lock()
|
||||
defer b.mu.Unlock()
|
||||
return b.ipVIPServiceMap
|
||||
}
|
||||
|
||||
func (b *LocalBackend) SetIPServiceMappingsForTest(m netmap.IPServiceMappings) {
|
||||
b.mu.Lock()
|
||||
defer b.mu.Unlock()
|
||||
testenv.AssertInTest()
|
||||
b.ipVIPServiceMap = m
|
||||
if ns, ok := b.sys.Netstack.GetOK(); ok {
|
||||
ns.UpdateIPServiceMappings(m)
|
||||
}
|
||||
}
|
||||
|
||||
// setConfigLocked uses the provided config to update the backend's prefs
|
||||
// and other state.
|
||||
func (b *LocalBackend) setConfigLocked(conf *conffile.Config) error {
|
||||
@@ -4502,6 +4518,12 @@ func (b *LocalBackend) onEditPrefsLocked(_ ipnauth.Actor, mp *ipn.MaskedPrefs, o
|
||||
}
|
||||
}
|
||||
|
||||
if mp.AdvertiseServicesSet {
|
||||
if ns, ok := b.sys.Netstack.GetOK(); ok {
|
||||
ns.UpdateActiveVIPServices(newPrefs.AdvertiseServices())
|
||||
}
|
||||
}
|
||||
|
||||
// This is recorded here in the EditPrefs path, not the setPrefs path on purpose.
|
||||
// recordForEdit records metrics related to edits and changes, not the final state.
|
||||
// If, in the future, we want to record gauge-metrics related to the state of prefs,
|
||||
@@ -5125,7 +5147,7 @@ func (b *LocalBackend) authReconfigLocked() {
|
||||
}
|
||||
|
||||
oneCGNATRoute := shouldUseOneCGNATRoute(b.logf, b.sys.NetMon.Get(), b.sys.ControlKnobs(), version.OS())
|
||||
rcfg := b.routerConfigLocked(cfg, prefs, oneCGNATRoute)
|
||||
rcfg := b.routerConfigLocked(cfg, prefs, nm, oneCGNATRoute)
|
||||
|
||||
err = b.e.Reconfig(cfg, rcfg, dcfg)
|
||||
if err == wgengine.ErrNoChanges {
|
||||
@@ -5500,7 +5522,7 @@ func peerRoutes(logf logger.Logf, peers []wgcfg.Peer, cgnatThreshold int, routeA
|
||||
// routerConfig produces a router.Config from a wireguard config and IPN prefs.
|
||||
//
|
||||
// b.mu must be held.
|
||||
func (b *LocalBackend) routerConfigLocked(cfg *wgcfg.Config, prefs ipn.PrefsView, oneCGNATRoute bool) *router.Config {
|
||||
func (b *LocalBackend) routerConfigLocked(cfg *wgcfg.Config, prefs ipn.PrefsView, nm *netmap.NetworkMap, oneCGNATRoute bool) *router.Config {
|
||||
singleRouteThreshold := 10_000
|
||||
if oneCGNATRoute {
|
||||
singleRouteThreshold = 1
|
||||
@@ -5585,11 +5607,23 @@ func (b *LocalBackend) routerConfigLocked(cfg *wgcfg.Config, prefs ipn.PrefsView
|
||||
}
|
||||
}
|
||||
|
||||
// Get the VIPs for VIP services this node hosts. We will add all locally served VIPs to routes then
|
||||
// we terminate these connection locally in netstack instead of routing to peer.
|
||||
vipServiceIPs := nm.GetIPVIPServiceMap()
|
||||
v4, v6 := false, false
|
||||
|
||||
if slices.ContainsFunc(rs.LocalAddrs, tsaddr.PrefixIs4) {
|
||||
rs.Routes = append(rs.Routes, netip.PrefixFrom(tsaddr.TailscaleServiceIP(), 32))
|
||||
v4 = true
|
||||
}
|
||||
if slices.ContainsFunc(rs.LocalAddrs, tsaddr.PrefixIs6) {
|
||||
rs.Routes = append(rs.Routes, netip.PrefixFrom(tsaddr.TailscaleServiceIPv6(), 128))
|
||||
v6 = true
|
||||
}
|
||||
for vip := range vipServiceIPs {
|
||||
if (vip.Is4() && v4) || (vip.Is6() && v6) {
|
||||
rs.Routes = append(rs.Routes, netip.PrefixFrom(vip, vip.BitLen()))
|
||||
}
|
||||
}
|
||||
|
||||
return rs
|
||||
@@ -6267,7 +6301,15 @@ func (b *LocalBackend) setNetMapLocked(nm *netmap.NetworkMap) {
|
||||
|
||||
b.setTCPPortsInterceptedFromNetmapAndPrefsLocked(b.pm.CurrentPrefs())
|
||||
if buildfeatures.HasServe {
|
||||
b.ipVIPServiceMap = nm.GetIPVIPServiceMap()
|
||||
m := nm.GetIPVIPServiceMap()
|
||||
b.ipVIPServiceMap = m
|
||||
if ns, ok := b.sys.Netstack.GetOK(); ok {
|
||||
ns.UpdateIPServiceMappings(m)
|
||||
// In case the prefs reloaded from Profile Manager but didn't change,
|
||||
// we still need to load the active VIP services into netstack.
|
||||
ns.UpdateActiveVIPServices(b.pm.CurrentPrefs().AdvertiseServices())
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if !oldSelf.Equal(nm.SelfNodeOrZero()) {
|
||||
|
||||
@@ -7430,8 +7430,31 @@ func TestRouteAllDisabled(t *testing.T) {
|
||||
cfg := &wgcfg.Config{
|
||||
Peers: tt.peers,
|
||||
}
|
||||
ServiceIPMappings := tailcfg.ServiceIPMappings{
|
||||
"svc:test-service": []netip.Addr{
|
||||
netip.MustParseAddr("100.64.1.2"),
|
||||
netip.MustParseAddr("fd7a:abcd:1234::1"),
|
||||
},
|
||||
}
|
||||
svcIPMapJSON, err := json.Marshal(ServiceIPMappings)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to marshal ServiceIPMappings: %v", err)
|
||||
}
|
||||
nm := &netmap.NetworkMap{
|
||||
SelfNode: (&tailcfg.Node{
|
||||
Name: "test-node",
|
||||
Addresses: []netip.Prefix{
|
||||
pp("100.64.1.1/32"),
|
||||
},
|
||||
CapMap: tailcfg.NodeCapMap{
|
||||
tailcfg.NodeAttrServiceHost: []tailcfg.RawMessage{
|
||||
tailcfg.RawMessage(svcIPMapJSON),
|
||||
},
|
||||
},
|
||||
}).View(),
|
||||
}
|
||||
|
||||
rcfg := lb.routerConfigLocked(cfg, prefs.View(), false)
|
||||
rcfg := lb.routerConfigLocked(cfg, prefs.View(), nm, false)
|
||||
for _, p := range rcfg.Routes {
|
||||
found := false
|
||||
for _, r := range tt.wantEndpoints {
|
||||
|
||||
Reference in New Issue
Block a user