diff --git a/ipn/ipnlocal/c2n.go b/ipn/ipnlocal/c2n.go index f3a4a3a3d..04f91954f 100644 --- a/ipn/ipnlocal/c2n.go +++ b/ipn/ipnlocal/c2n.go @@ -274,8 +274,12 @@ func handleC2NSetNetfilterKind(b *LocalBackend, w http.ResponseWriter, r *http.R func handleC2NVIPServicesGet(b *LocalBackend, w http.ResponseWriter, r *http.Request) { b.logf("c2n: GET /vip-services received") + var res tailcfg.C2NVIPServicesResponse + res.VIPServices = b.VIPServices() + res.ServicesHash = b.vipServiceHash(res.VIPServices) - json.NewEncoder(w).Encode(b.VIPServices()) + w.Header().Set("Content-Type", "application/json") + json.NewEncoder(w).Encode(res) } func handleC2NUpdateGet(b *LocalBackend, w http.ResponseWriter, r *http.Request) { diff --git a/ipn/ipnlocal/local.go b/ipn/ipnlocal/local.go index ad3bbaef3..088f1ef75 100644 --- a/ipn/ipnlocal/local.go +++ b/ipn/ipnlocal/local.go @@ -5022,7 +5022,7 @@ func (b *LocalBackend) applyPrefsToHostinfoLocked(hi *tailcfg.Hostinfo, prefs ip } hi.SSH_HostKeys = sshHostKeys - hi.ServicesHash = b.vipServiceHashLocked(prefs) + hi.ServicesHash = b.vipServiceHash(b.vipServicesFromPrefsLocked(prefs)) // The Hostinfo.WantIngress field tells control whether this node wants to // be wired up for ingress connections. If harmless if it's accidentally @@ -7661,8 +7661,7 @@ func (b *LocalBackend) VIPServices() []*tailcfg.VIPService { return b.vipServicesFromPrefsLocked(b.pm.CurrentPrefs()) } -func (b *LocalBackend) vipServiceHashLocked(prefs ipn.PrefsView) string { - services := b.vipServicesFromPrefsLocked(prefs) +func (b *LocalBackend) vipServiceHash(services []*tailcfg.VIPService) string { if len(services) == 0 { return "" } diff --git a/tailcfg/c2ntypes.go b/tailcfg/c2ntypes.go index 54efb736e..66f95785c 100644 --- a/tailcfg/c2ntypes.go +++ b/tailcfg/c2ntypes.go @@ -102,3 +102,18 @@ type C2NTLSCertInfo struct { // TODO(bradfitz): add fields for whether an ACME fetch is currently in // process and when it started, etc. } + +// C2NVIPServicesResponse is the response (from node to control) from the +// /vip-services handler. +// +// It returns the list of VIPServices that the node is currently serving with +// their port info and whether they are active or not. It also returns a hash of +// the response to allow the control server to detect changes. +type C2NVIPServicesResponse struct { + // VIPServices is the list of VIP services that the node is currently serving. + VIPServices []*VIPService `json:",omitempty"` + + // ServicesHash is the hash of VIPServices to allow the control server to detect + // changes. This value matches what is reported in latest [Hostinfo.ServicesHash]. + ServicesHash string +}