Skip to content

Commit 3809296

Browse files
committed
ipn/yegor: update may or may not result in state change
1 parent 8d7c41a commit 3809296

3 files changed

Lines changed: 77 additions & 36 deletions

File tree

intra/backend/ipn_proxies.go

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -89,15 +89,16 @@ type RpnOps struct {
8989
forceFetchServers bool // force server-list refresh on the next Update
9090
newPort uint16 // fixed WireGuard port; 0 = random per wsRandomPort()
9191
dnsConfig string // csv of DNS filter presets: "family", "security", "social", "privacy", "all", "none", "default"
92+
forceInit bool // when false, skips expensive ops unless absolutely required.
9293
}
9394

9495
func NewRpnOps() *RpnOps {
9596
return &RpnOps{}
9697
}
9798

9899
func (o *RpnOps) String() string {
99-
return fmt.Sprintf("rotate: %t; perma: %t; forceFetchServers: %t; port: %d; dns: %s",
100-
o.rotateCreds, o.permaCreds, o.forceFetchServers, o.newPort, o.dnsConfig)
100+
return fmt.Sprintf("rotate: %t; perma: %t; forceFetchServers: %t; port: %d; dns: %s; forceInit: %t",
101+
o.rotateCreds, o.permaCreds, o.forceFetchServers, o.newPort, o.dnsConfig, o.forceInit)
101102
}
102103

103104
// SetRotateCreds forces generation of a new WireGuard keypair on the next Update.
@@ -125,6 +126,10 @@ func (o *RpnOps) SetDNSConfig(v string) {
125126
o.dnsConfig = v
126127
}
127128

129+
// SetForceInit controls whether Update forces expensive re-setup. When false,
130+
// expensive ops are skipped if called within some threshold of the previous call.
131+
func (o *RpnOps) SetForceInit(v bool) { o.forceInit = v }
132+
128133
// Rotate reports whether a new WG keypair should be generated.
129134
func (o RpnOps) Rotate() bool { return o.rotateCreds }
130135

@@ -144,6 +149,9 @@ func (o RpnOps) DNSConfig() string {
144149
return o.dnsConfig
145150
}
146151

152+
// ForceInit returns false if expensive update ops may be skipped if approp.
153+
func (o RpnOps) ForceInit() bool { return o.forceInit }
154+
147155
type Rpn interface {
148156
// EntitlementFrom returns the RpnEntitlement represented by entitlementOrStateJson.
149157
// `did` is the device identifier to use for this entitlement, if applicable; and `rpnProviderID` is the RPN provider for this entitlement, if applicable.

intra/ipn/rpn.go

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -547,7 +547,10 @@ func (r *rpnp) flattenKids() (ccs []string) {
547547
// Update implements RpnAcc.
548548
func (r *rpnp) Update(ops *x.RpnOps) (newState []byte, err error) {
549549
newState, err = r.RpnAcc.Update(ops)
550-
if err == nil {
550+
if len(newState) <= 0 && err == nil {
551+
// updated and no state change
552+
return nil, nil
553+
} else if err == nil {
551554
core.Gxe("rpn.fork."+r.ProviderID(), r.forkAll)
552555
}
553556
return

intra/ipn/rpn/yegor.go

Lines changed: 63 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -840,8 +840,12 @@ type WsWgConfig struct {
840840
Servers []WsServerList `json:"servers"` // all servers in the server list
841841
Creds *WsWgCreds `json:"creds"` // base64 encoded private key
842842
PermaCreds *WsWgPermanentConfig `json:"permacreds,omitempty"` // permanent WG config; nil if not yet fetched
843+
LastUpdate time.Time
843844
}
844845

846+
// wsUpdateThreshold is the minimum interval between session calls when ops.ForceInit() is false.
847+
const wsUpdateThreshold = 40 * time.Minute
848+
845849
/*
846850
{
847851
"kind": "ws#v1",
@@ -1083,7 +1087,7 @@ func (a *WsClient) Update(ops *x.RpnOps) (newstate []byte, err error) {
10831087
ops = a.Ops()
10841088
}
10851089
start := time.Now()
1086-
b, refreshed, err := makeWsWgFrom(a.http, c, *ops, true /*updating*/)
1090+
b, refreshed, needsRedo, err := makeWsWgFrom(a.http, c, *ops, true /*updating*/)
10871091
if err != nil || !refreshed {
10881092
log.E("ws: update: refreshed? %t; err: %v", refreshed, err)
10891093
return nil, core.OneErr(err, errWsRetryUpdate)
@@ -1094,7 +1098,11 @@ func (a *WsClient) Update(ops *x.RpnOps) (newstate []byte, err error) {
10941098
if _, err := a.shallowCopyConfig(b); err != nil {
10951099
return nil, log.EE("ws: update: shallow copy err: %v", err)
10961100
}
1097-
log.I("ws: update: refreshed? %t; took %v", refreshed, core.FmtTimeAsPeriod(start))
1101+
log.I("ws: update: refreshed? %t / redo? %t; took %v", refreshed, needsRedo, core.FmtTimeAsPeriod(start))
1102+
if !needsRedo {
1103+
return nil, nil
1104+
}
1105+
10981106
return a.State()
10991107
}
11001108

@@ -2090,11 +2098,11 @@ func (w *BaseClient) MakeWsWgFrom(entitlementOrWsConfigJson []byte, did string,
20902098
}
20912099

20922100
func (w *BaseClient) makeWsWgFrom(existingConf *WsWgConfig, ops x.RpnOps) (*WsClient, error) {
2093-
ws, _, err := makeWsWgFrom(&w.h2, existingConf, ops, false /*not updating*/)
2101+
ws, _, _, err := makeWsWgFrom(&w.h2, existingConf, ops, false /*not updating*/)
20942102
return ws, err
20952103
}
20962104

2097-
func makeWsWgFrom(h *http.Client, existingConf *WsWgConfig, ops x.RpnOps, updating bool) (ws *WsClient, refreshedSess bool, err error) {
2105+
func makeWsWgFrom(h *http.Client, existingConf *WsWgConfig, ops x.RpnOps, updating bool) (ws *WsClient, refreshedSess, needsRedo bool, err error) {
20982106
existingEnt := existingConf.Entitlement
20992107
if existingEnt == nil || len(existingEnt.SessionToken) <= 0 {
21002108
err = errWsNoEntitlement
@@ -2124,19 +2132,36 @@ func makeWsWgFrom(h *http.Client, existingConf *WsWgConfig, ops x.RpnOps, updati
21242132
}
21252133

21262134
usingExitingSess := false
2127-
newSess, err := getSession(h, existingEnt)
2128-
if err == nil {
2129-
existingConf.Session = newSess // update session with the latest info
2130-
refreshedSess = true
2131-
} else {
2135+
2136+
var newSess *WsSession
2137+
skipSess := !ops.ForceInit() &&
2138+
!existingConf.LastUpdate.IsZero() &&
2139+
time.Since(existingConf.LastUpdate) < wsUpdateThreshold
2140+
2141+
if skipSess {
2142+
if settings.Debug {
2143+
log.D("ws: make: using existing session (from: %s); tok? %s", fmtTime(existingConf.LastUpdate), tokst)
2144+
}
2145+
newSess = existingConf.Session
21322146
usingExitingSess = true
2133-
log.W("ws: make: get session err: %v; using existing; tok? %s", err, tokst)
2134-
newSess = existingConf.Session // use existing session
2147+
refreshedSess = true // treated as refreshed even though we skipped the network call
2148+
} else {
2149+
var sessErr error
2150+
newSess, sessErr = getSession(h, existingEnt)
2151+
if sessErr == nil {
2152+
existingConf.Session = newSess // update session with the latest info
2153+
existingConf.LastUpdate = time.Now()
2154+
refreshedSess = true
2155+
} else {
2156+
usingExitingSess = true
2157+
log.W("ws: make: get session err: %v; using existing; tok? %s", sessErr, tokst)
2158+
newSess = existingConf.Session // use existing session
2159+
}
21352160
}
21362161

21372162
exp, err := time.Parse(time.DateOnly, newSess.ExpiryDate)
21382163
if err != nil {
2139-
err = log.EE("ws: make: parsing expiry %s (newSess? %t); err: %v", newSess.ExpiryDate, !usingExitingSess, err)
2164+
err = log.EE("ws: make: parsing expiry %s (newSess? %t / skipSess? %t); err: %v", newSess.ExpiryDate, !usingExitingSess, skipSess, err)
21402165
return
21412166
}
21422167

@@ -2145,6 +2170,11 @@ func makeWsWgFrom(h *http.Client, existingConf *WsWgConfig, ops x.RpnOps, updati
21452170
// skip server refresh if ops requests it; but honour loc hash change regardless
21462171
downloadServerList := (existingLocHash != newSess.LocHash) || ops.FetchServers()
21472172
if active {
2173+
// sync Robert DNS filters with the desired preset configuration (best-effort; non-fatal).
2174+
if dnsConfig := ops.DNSConfig(); len(dnsConfig) > 0 {
2175+
go syncDNSFilters(h, existingEnt, newSess, dnsConfig)
2176+
} // else: no-op
2177+
21482178
maybeNewServers := existingServers
21492179
hasnew := false
21502180
if downloadServerList {
@@ -2159,30 +2189,30 @@ func makeWsWgFrom(h *http.Client, existingConf *WsWgConfig, ops x.RpnOps, updati
21592189
}
21602190

21612191
if len(maybeNewServers) <= 0 { // no new servers, no existing servers; bail
2162-
return nil, refreshedSess, core.OneErr(err, errWsNoServerList)
2192+
return nil, refreshedSess, needsRedo, core.OneErr(err, errWsNoServerList)
21632193
}
21642194
}
21652195

2166-
// sync Robert DNS filters with the desired preset configuration (best-effort; non-fatal).
2167-
if dnsConfig := ops.DNSConfig(); len(dnsConfig) > 0 {
2168-
go syncDNSFilters(h, existingEnt, newSess, dnsConfig)
2169-
} // else: no-op
2170-
2171-
// create wg confs from new or existing server list
2172-
// always reconfigure (as /WgConfigs/connect must be done once every wg_ttl, which is 60m)
2173-
maybeNewCreds, maybeNewPermaCreds, maybeNewWgConfs, uerr := genWgConfs(h, existingCreds, existingConf.PermaCreds, newSess, maybeNewServers, existingConf.Entitlement, ops)
2174-
loge(uerr)("ws: make: gen wg confs; tok? %s; downloadloc? %t / hasnewloc? %t len (%d/%d); ops: %v; err? %v",
2175-
tokst, downloadServerList, hasnew, len(existingServers), len(maybeNewServers), &ops, uerr)
2176-
2177-
if uerr == nil {
2178-
existingConf.Servers = maybeNewServers
2179-
existingConf.Configs = maybeNewWgConfs
2180-
existingConf.Creds = maybeNewCreds
2181-
existingConf.PermaCreds = maybeNewPermaCreds // may be nil
2182-
} else if performingUpdate {
2183-
// error out early as this was meant to create an update config for later use
2184-
// but it itself is not the currently active config aka "existingConf"
2185-
return nil, refreshedSess, uerr
2196+
skipGen := !ops.ForceInit() && !hasnew
2197+
if skipGen {
2198+
log.D("ws: make: skip gen (use existing servers and creds); tok? %s", tokst)
2199+
} else {
2200+
maybeNewCreds, maybeNewPermaCreds, maybeNewWgConfs, uerr := genWgConfs(h, existingCreds, existingConf.PermaCreds, newSess, maybeNewServers, existingConf.Entitlement, ops)
2201+
loge(uerr)("ws: make: gen wg confs; tok? %s; downloadloc? %t / hasnewloc? %t len (%d/%d); ops: %v; err? %v",
2202+
tokst, downloadServerList, hasnew, len(existingServers), len(maybeNewServers), &ops, uerr)
2203+
2204+
if uerr == nil {
2205+
// TODO: needsRedo must be set iff creds and/or serverlist has changed
2206+
needsRedo = true
2207+
existingConf.Servers = maybeNewServers
2208+
existingConf.Configs = maybeNewWgConfs
2209+
existingConf.Creds = maybeNewCreds
2210+
existingConf.PermaCreds = maybeNewPermaCreds // may be nil
2211+
} else if performingUpdate {
2212+
// error out early as this was meant to create an update config for later use
2213+
// but it itself is not the currently active config aka "existingConf"
2214+
return nil, refreshedSess, needsRedo, uerr
2215+
}
21862216
}
21872217
} else {
21882218
log.W("ws: make: session expired at %s (newSess? %t); tok? %s", fmtTime(exp), !usingExitingSess, tokst)

0 commit comments

Comments
 (0)