Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit032d52d

Browse files
committed
chore: add configMaps component to tailnet
1 parent4c7a93d commit032d52d

File tree

2 files changed

+426
-0
lines changed

2 files changed

+426
-0
lines changed

‎tailnet/configmaps.go

Lines changed: 274 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,274 @@
1+
package tailnet
2+
3+
import (
4+
"context"
5+
"errors"
6+
"net/netip"
7+
"sync"
8+
9+
"github.com/google/uuid"
10+
"go4.org/netipx"
11+
"tailscale.com/net/dns"
12+
"tailscale.com/tailcfg"
13+
"tailscale.com/types/ipproto"
14+
"tailscale.com/types/key"
15+
"tailscale.com/types/netmap"
16+
"tailscale.com/wgengine"
17+
"tailscale.com/wgengine/filter"
18+
"tailscale.com/wgengine/router"
19+
"tailscale.com/wgengine/wgcfg"
20+
"tailscale.com/wgengine/wgcfg/nmcfg"
21+
22+
"cdr.dev/slog"
23+
"github.com/coder/coder/v2/tailnet/proto"
24+
)
25+
26+
// engineConfigurable is the subset of wgengine.Engine that we use for configuration.
27+
//
28+
// This allows us to test configuration code without faking the whole interface.
29+
typeengineConfigurableinterface {
30+
SetNetworkMap(*netmap.NetworkMap)
31+
Reconfig(*wgcfg.Config,*router.Config,*dns.Config,*tailcfg.Debug)error
32+
SetDERPMap(*tailcfg.DERPMap)
33+
SetFilter(*filter.Filter)
34+
}
35+
36+
typephaseint
37+
38+
const (
39+
idlephase=iota
40+
configuring
41+
closed
42+
)
43+
44+
typeconfigMapsstruct {
45+
sync.Cond
46+
netmapDirtybool
47+
derpMapDirtybool
48+
filterDirtybool
49+
closingbool
50+
phasephase
51+
52+
engineengineConfigurable
53+
static netmap.NetworkMap
54+
peersmap[uuid.UUID]*peerLifecycle
55+
addresses []netip.Prefix
56+
derpMap*proto.DERPMap
57+
logger slog.Logger
58+
}
59+
60+
funcnewConfigMaps(logger slog.Logger,engineengineConfigurable,nodeID tailcfg.NodeID,nodeKey key.NodePrivate,discoKey key.DiscoPublic,addresses []netip.Prefix)*configMaps {
61+
pubKey:=nodeKey.Public()
62+
c:=&configMaps{
63+
Cond:*(sync.NewCond(&sync.Mutex{})),
64+
logger:logger,
65+
engine:engine,
66+
static: netmap.NetworkMap{
67+
SelfNode:&tailcfg.Node{
68+
ID:nodeID,
69+
Key:pubKey,
70+
DiscoKey:discoKey,
71+
},
72+
NodeKey:pubKey,
73+
PrivateKey:nodeKey,
74+
PacketFilter: []filter.Match{{
75+
// Allow any protocol!
76+
IPProto: []ipproto.Proto{ipproto.TCP,ipproto.UDP,ipproto.ICMPv4,ipproto.ICMPv6,ipproto.SCTP},
77+
// Allow traffic sourced from anywhere.
78+
Srcs: []netip.Prefix{
79+
netip.PrefixFrom(netip.AddrFrom4([4]byte{}),0),
80+
netip.PrefixFrom(netip.AddrFrom16([16]byte{}),0),
81+
},
82+
// Allow traffic to route anywhere.
83+
Dsts: []filter.NetPortRange{
84+
{
85+
Net:netip.PrefixFrom(netip.AddrFrom4([4]byte{}),0),
86+
Ports: filter.PortRange{
87+
First:0,
88+
Last:65535,
89+
},
90+
},
91+
{
92+
Net:netip.PrefixFrom(netip.AddrFrom16([16]byte{}),0),
93+
Ports: filter.PortRange{
94+
First:0,
95+
Last:65535,
96+
},
97+
},
98+
},
99+
Caps: []filter.CapMatch{},
100+
}},
101+
},
102+
peers:make(map[uuid.UUID]*peerLifecycle),
103+
addresses:addresses,
104+
}
105+
goc.configLoop()
106+
returnc
107+
}
108+
109+
func (c*configMaps)configLoop() {
110+
c.L.Lock()
111+
deferc.L.Unlock()
112+
deferfunc() {
113+
c.phase=closed
114+
c.Broadcast()
115+
}()
116+
for {
117+
for!(c.closing||c.netmapDirty||c.filterDirty||c.derpMapDirty) {
118+
c.phase=idle
119+
c.Wait()
120+
}
121+
ifc.closing {
122+
return
123+
}
124+
// queue up the reconfiguration actions we will take while we have
125+
// the configMaps locked. We will execute them while unlocked to avoid
126+
// blocking during reconfig.
127+
actions:=make([]func(),0,3)
128+
ifc.derpMapDirty {
129+
derpMap:=c.derpMapLocked()
130+
actions=append(actions,func() {
131+
c.engine.SetDERPMap(derpMap)
132+
})
133+
}
134+
ifc.netmapDirty {
135+
nm:=c.netMapLocked()
136+
actions=append(actions,func() {
137+
c.engine.SetNetworkMap(nm)
138+
c.reconfig(nm)
139+
})
140+
}
141+
ifc.filterDirty {
142+
f:=c.filterLocked()
143+
actions=append(actions,func() {
144+
c.engine.SetFilter(f)
145+
})
146+
}
147+
148+
c.netmapDirty=false
149+
c.filterDirty=false
150+
c.derpMapDirty=false
151+
c.phase=configuring
152+
c.Broadcast()
153+
func() {
154+
// this may look a little odd, but here we want this code to run
155+
// without the lock, and then relock when done
156+
c.L.Unlock()
157+
deferc.L.Lock()
158+
for_,a:=rangeactions {
159+
a()
160+
}
161+
}()
162+
}
163+
}
164+
165+
func (c*configMaps)close() {
166+
c.L.Lock()
167+
deferc.L.Unlock()
168+
c.closing=true
169+
c.Broadcast()
170+
forc.phase!=closed {
171+
c.Wait()
172+
}
173+
}
174+
175+
func (c*configMaps)netMapLocked()*netmap.NetworkMap {
176+
nm:=new(netmap.NetworkMap)
177+
*nm=c.static
178+
179+
nm.Addresses=make([]netip.Prefix,len(c.addresses))
180+
copy(nm.Addresses,c.addresses)
181+
182+
nm.DERPMap=DERPMapFromProto(c.derpMap)
183+
nm.Peers=c.peerConfigLocked()
184+
nm.SelfNode.Addresses=nm.Addresses
185+
nm.SelfNode.AllowedIPs=nm.Addresses
186+
returnnm
187+
}
188+
189+
func (c*configMaps)peerConfigLocked() []*tailcfg.Node {
190+
out:=make([]*tailcfg.Node,0,len(c.peers))
191+
for_,p:=rangec.peers {
192+
out=append(out,p.node.Clone())
193+
}
194+
returnout
195+
}
196+
197+
func (c*configMaps)setAddresses(ips []netip.Prefix) {
198+
c.L.Lock()
199+
deferc.L.Unlock()
200+
ifd:=prefixesDifferent(c.addresses,ips);!d {
201+
return
202+
}
203+
c.addresses=make([]netip.Prefix,len(ips))
204+
copy(c.addresses,ips)
205+
c.netmapDirty=true
206+
c.filterDirty=true
207+
c.Broadcast()
208+
}
209+
210+
func (c*configMaps)derpMapLocked()*tailcfg.DERPMap {
211+
m:=DERPMapFromProto(c.derpMap)
212+
returnm
213+
}
214+
215+
func (c*configMaps)reconfig(nm*netmap.NetworkMap) {
216+
cfg,err:=nmcfg.WGCfg(nm,Logger(c.logger.Named("net.wgconfig")),netmap.AllowSingleHosts,"")
217+
iferr!=nil {
218+
// WGCfg never returns an error at the time this code was written. If it starts, returning
219+
// errors if/when we upgrade tailscale, we'll need to deal.
220+
c.logger.Critical(context.Background(),"update wireguard config failed",slog.Error(err))
221+
return
222+
}
223+
224+
rc:=&router.Config{LocalAddrs:nm.Addresses}
225+
err=c.engine.Reconfig(cfg,rc,&dns.Config{},&tailcfg.Debug{})
226+
iferr!=nil {
227+
iferrors.Is(err,wgengine.ErrNoChanges) {
228+
return
229+
}
230+
c.logger.Error(context.Background(),"failed to reconfigure wireguard engine",slog.Error(err))
231+
}
232+
}
233+
234+
func (c*configMaps)filterLocked()*filter.Filter {
235+
localIPSet:= netipx.IPSetBuilder{}
236+
for_,addr:=rangec.addresses {
237+
localIPSet.AddPrefix(addr)
238+
}
239+
localIPs,_:=localIPSet.IPSet()
240+
logIPSet:= netipx.IPSetBuilder{}
241+
logIPs,_:=logIPSet.IPSet()
242+
returnfilter.New(
243+
c.static.PacketFilter,
244+
localIPs,
245+
logIPs,
246+
nil,
247+
Logger(c.logger.Named("net.packet-filter")),
248+
)
249+
}
250+
251+
typepeerLifecyclestruct {
252+
node*tailcfg.Node
253+
// TODO: implement timers to track lost peers
254+
// lastHandshake time.Time
255+
// timer time.Timer
256+
}
257+
258+
// prefixesDifferent returns true if the two slices contain different prefixes
259+
// where order doesn't matter.
260+
funcprefixesDifferent(a,b []netip.Prefix)bool {
261+
iflen(a)!=len(b) {
262+
returntrue
263+
}
264+
as:=make(map[string]bool)
265+
for_,p:=rangea {
266+
as[p.String()]=true
267+
}
268+
for_,p:=rangeb {
269+
if!as[p.String()] {
270+
returntrue
271+
}
272+
}
273+
returnfalse
274+
}

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp