11package config
22
33import (
4- "encoding/base64"
54 "fmt"
65 "net/netip"
76 "os"
@@ -27,7 +26,7 @@ type AutogenConfig struct {
2726 VxlanMTU int `yaml:"vxlan_mtu"`
2827 ProbePort uint16 `yaml:"probe_port"`
2928 Priority int `yaml:"priority"`
30- ForwardCost float64 `yaml:"forward_cost"`
29+ ForwardCost float64 `yaml:"forward_cost"`
3130
3231 Nodes map [string ]map [string ]AutogenAF `yaml:"nodes"`
3332 Controllers []string `yaml:"controllers"`
@@ -36,10 +35,10 @@ type AutogenConfig struct {
3635}
3736
3837type AutogenWebUI struct {
39- BindAddr string `yaml:"bind_addr"`
40- Title string `yaml:"title"`
41- URL string `yaml:"url"`
42- Nodes map [string ]* WebUINodeFile `yaml:"nodes"`
38+ BindAddr string `yaml:"bind_addr"`
39+ Title string `yaml:"title"`
40+ URL string `yaml:"url"`
41+ Nodes map [string ]* WebUINode `yaml:"nodes"`
4342}
4443
4544// AutogenAF represents a per-AF bind config.
@@ -82,6 +81,20 @@ type autogenNodeKeys struct {
8281 Pub [32 ]byte
8382}
8483
84+ var DefaultAutogenConfig = AutogenConfig {
85+ BridgeName : "br-vxlan" ,
86+ VxlanNamePrefix : "vxlan-" ,
87+ VxlanDstPort : 4789 ,
88+ VxlanSrcPortStart : 4789 ,
89+ VxlanSrcPortEnd : 4789 ,
90+ CommunicationPort : 5000 ,
91+ VxlanVNI : 100 ,
92+ VxlanMTU : 1400 ,
93+ ProbePort : 5010 ,
94+ Priority : 10 ,
95+ ForwardCost : 20 ,
96+ }
97+
8598// Autogen loads a topology file and generates controller+client configs.
8699// Output files are written to the same directory as the input file.
87100func Autogen (path string ) error {
@@ -90,46 +103,11 @@ func Autogen(path string) error {
90103 return fmt .Errorf ("read topology: %w" , err )
91104 }
92105
93- var ag AutogenConfig
106+ ag := DefaultAutogenConfig
94107 if err := yaml .Unmarshal (data , & ag ); err != nil {
95108 return fmt .Errorf ("parse topology: %w" , err )
96109 }
97110
98- // Apply defaults
99- if ag .BridgeName == "" {
100- ag .BridgeName = "br-vxlan"
101- }
102- if ag .VxlanNamePrefix == "" {
103- ag .VxlanNamePrefix = "vxlan-"
104- }
105- if ag .VxlanDstPort == 0 {
106- ag .VxlanDstPort = 4789
107- }
108- if ag .VxlanSrcPortStart == 0 {
109- ag .VxlanSrcPortStart = 4789
110- }
111- if ag .VxlanSrcPortEnd == 0 {
112- ag .VxlanSrcPortEnd = 4789
113- }
114- if ag .CommunicationPort == 0 {
115- ag .CommunicationPort = 5000
116- }
117- if ag .VxlanVNI == 0 {
118- ag .VxlanVNI = 100
119- }
120- if ag .VxlanMTU == 0 {
121- ag .VxlanMTU = 1400
122- }
123- if ag .ProbePort == 0 {
124- ag .ProbePort = 5010
125- }
126- if ag .Priority == 0 {
127- ag .Priority = 10
128- }
129- if ag .ForwardCost == 0 {
130- ag .ForwardCost = 20
131- }
132-
133111 // Validate node references
134112 allRoles := map [string ]bool {}
135113 for _ , name := range ag .Controllers {
@@ -166,7 +144,11 @@ func Autogen(path string) error {
166144 // Generate controller configs
167145 for _ , name := range ag .Controllers {
168146 cfg := ag .buildControllerConfig (name , keys )
169- if err := writeYAML (filepath .Join (outDir , name + ".controller.yaml" ), cfg ); err != nil {
147+ data , err := MarshalControllerConfig (cfg )
148+ if err != nil {
149+ return fmt .Errorf ("marshal controller config for %s: %w" , name , err )
150+ }
151+ if err := os .WriteFile (filepath .Join (outDir , name + ".controller.yaml" ), data , 0644 ); err != nil {
170152 return fmt .Errorf ("write controller config for %s: %w" , name , err )
171153 }
172154 fmt .Printf (" %s.controller.yaml\n " , name )
@@ -175,7 +157,11 @@ func Autogen(path string) error {
175157 // Generate client configs
176158 for _ , name := range ag .Clients {
177159 cfg := ag .buildClientConfig (name , keys )
178- if err := writeYAML (filepath .Join (outDir , name + ".client.yaml" ), cfg ); err != nil {
160+ data , err := MarshalClientConfig (cfg )
161+ if err != nil {
162+ return fmt .Errorf ("marshal client config for %s: %w" , name , err )
163+ }
164+ if err := os .WriteFile (filepath .Join (outDir , name + ".client.yaml" ), data , 0644 ); err != nil {
179165 return fmt .Errorf ("write client config for %s: %w" , name , err )
180166 }
181167 fmt .Printf (" %s.client.yaml\n " , name )
@@ -184,17 +170,17 @@ func Autogen(path string) error {
184170 return nil
185171}
186172
187- func (ag * AutogenConfig ) buildControllerConfig (name string , keys map [string ]* autogenNodeKeys ) * ControllerConfigFile {
173+ func (ag * AutogenConfig ) buildControllerConfig (name string , keys map [string ]* autogenNodeKeys ) * ControllerConfig {
188174 k := keys [name ]
189- cfg := DefaultControllerConfig
175+ cfg := cloneControllerConfig ( & DefaultControllerConfig )
190176
191- cfg .PrivateKey = base64 .StdEncoding .EncodeToString (k .Priv [:])
192- cfg .PublicKey = base64 .StdEncoding .EncodeToString (k .Pub [:])
177+ cfg .PrivateKey = k .Priv
193178
194179 // AF settings from this node's AFs
195- cfg .AFSettings = make (map [string ] * ControllerAFConfigFile )
180+ cfg .AFSettings = make (map [types. AFName ] * ControllerAFConfig )
196181 for afName , af := range ag .Nodes [name ] {
197- afCfg := & ControllerAFConfigFile {
182+ afCfg := & ControllerAFConfig {
183+ Name : types .AFName (afName ),
198184 Enable : true ,
199185 CommunicationPort : ag .CommunicationPort ,
200186 VxlanVNI : ag .VxlanVNI ,
@@ -205,9 +191,9 @@ func (ag *AutogenConfig) buildControllerConfig(name string, keys map[string]*aut
205191 if af .IsAutoIP () {
206192 afCfg .AutoIPInterface = af .Bind
207193 } else {
208- afCfg .BindAddr = af .Bind
194+ afCfg .BindAddr = netip . MustParseAddr ( af .Bind )
209195 }
210- cfg .AFSettings [afName ] = afCfg
196+ cfg .AFSettings [types . AFName ( afName ) ] = afCfg
211197 }
212198
213199 // WebUI config
@@ -216,15 +202,15 @@ func (ag *AutogenConfig) buildControllerConfig(name string, keys map[string]*aut
216202 if bindAddr == "" {
217203 bindAddr = ":8080"
218204 }
219- cfg .WebUI = & WebUIConfigFile {
205+ cfg .WebUI = & WebUIConfig {
220206 BindAddr : bindAddr ,
221207 Title : ag .WebUI .Title ,
222208 URL : ag .WebUI .URL ,
223209 }
224210 if len (ag .WebUI .Nodes ) > 0 {
225- cfg .WebUI .Nodes = make (map [string ]* WebUINodeFile , len (ag .WebUI .Nodes ))
211+ cfg .WebUI .Nodes = make (map [string ]* WebUINode , len (ag .WebUI .Nodes ))
226212 for nodeName , n := range ag .WebUI .Nodes {
227- cfg .WebUI .Nodes [nodeName ] = & WebUINodeFile {
213+ cfg .WebUI .Nodes [nodeName ] = & WebUINode {
228214 Label : n .Label ,
229215 Pos : n .Pos ,
230216 }
@@ -236,37 +222,34 @@ func (ag *AutogenConfig) buildControllerConfig(name string, keys map[string]*aut
236222 cfg .AllowedClients = nil
237223 for _ , clientName := range ag .Clients {
238224 ck := keys [clientName ]
239- pc := PerClientConfigFile {
240- ClientID : base64 . StdEncoding . EncodeToString (ck .Pub [:] ),
225+ pc := types. PerClientConfig {
226+ ClientID : types . ClientID (ck .Pub ),
241227 ClientName : clientName ,
242228 }
243- // Same-node client: connects via LAN IP, but controller should distribute
244- // the public IP/hostname to other clients. Only needed when client == controller node.
245229 if clientName == name {
246230 for afName , af := range ag .Nodes [clientName ] {
247231 if af .DDNS == "" {
248232 continue
249233 }
250234 if pc .AFSettings == nil {
251- pc .AFSettings = make (map [string ]* types.PerClientAFConfig )
235+ pc .AFSettings = make (map [types. AFName ]* types.PerClientAFConfig )
252236 }
253- pc .AFSettings [afName ] = & types.PerClientAFConfig {
237+ pc .AFSettings [types . AFName ( afName ) ] = & types.PerClientAFConfig {
254238 EndpointOverride : af .DDNS ,
255239 }
256240 }
257241 }
258242 cfg .AllowedClients = append (cfg .AllowedClients , pc )
259243 }
260244
261- return & cfg
245+ return cfg
262246}
263247
264- func (ag * AutogenConfig ) buildClientConfig (name string , keys map [string ]* autogenNodeKeys ) * ClientConfigFile {
248+ func (ag * AutogenConfig ) buildClientConfig (name string , keys map [string ]* autogenNodeKeys ) * ClientConfig {
265249 k := keys [name ]
266- cfg := DefaultClientConfig
250+ cfg := cloneClientConfig ( & DefaultClientConfig )
267251
268- cfg .PrivateKey = base64 .StdEncoding .EncodeToString (k .Priv [:])
269- cfg .PublicKey = base64 .StdEncoding .EncodeToString (k .Pub [:])
252+ cfg .PrivateKey = k .Priv
270253 cfg .BridgeName = ag .BridgeName
271254 if ag .ClampMSSToMTU != nil {
272255 cfg .ClampMSSToMTU = * ag .ClampMSSToMTU
@@ -276,9 +259,10 @@ func (ag *AutogenConfig) buildClientConfig(name string, keys map[string]*autogen
276259 }
277260
278261 // AF settings from this node's AFs
279- cfg .AFSettings = make (map [string ] * ClientAFConfigFile )
262+ cfg .AFSettings = make (map [types. AFName ] * ClientAFConfig )
280263 for afName , af := range ag .Nodes [name ] {
281- afCfg := & ClientAFConfigFile {
264+ afCfg := & ClientAFConfig {
265+ Name : types .AFName (afName ),
282266 Enable : true ,
283267 ProbePort : ag .ProbePort ,
284268 VxlanName : ag .VxlanNamePrefix + afName ,
@@ -288,12 +272,12 @@ func (ag *AutogenConfig) buildClientConfig(name string, keys map[string]*autogen
288272 VxlanSrcPortStart : ag .VxlanSrcPortStart ,
289273 VxlanSrcPortEnd : ag .VxlanSrcPortEnd ,
290274 Priority : ag .Priority ,
291- ForwardCost : ag .ForwardCost ,
275+ ForwardCost : ag .ForwardCost ,
292276 }
293277 if af .IsAutoIP () {
294278 afCfg .AutoIPInterface = af .Bind
295279 } else {
296- afCfg .BindAddr = af .Bind
280+ afCfg .BindAddr = netip . MustParseAddr ( af .Bind )
297281 }
298282
299283 // Add controllers that have this AF
@@ -302,38 +286,29 @@ func (ag *AutogenConfig) buildClientConfig(name string, keys map[string]*autogen
302286 if ! ok {
303287 continue
304288 }
305- // Same node: connect via bind addr (LAN); different node: use ddns/endpoint
306289 var addr string
307290 if ctrlName == name {
308291 addr = ctrlAF .Bind
309292 } else {
310293 addr , _ = ctrlAF .Endpoint () // already validated
311294 }
312295 ck := keys [ctrlName ]
313- afCfg .Controllers = append (afCfg .Controllers , ControllerEndpointFile {
314- PubKey : base64 .StdEncoding .EncodeToString (ck .Pub [:]),
315- Addr : formatAddrPort (addr , ag .CommunicationPort ),
296+ ap , _ := netip .ParseAddrPort (formatAddrPort (addr , ag .CommunicationPort ))
297+ afCfg .Controllers = append (afCfg .Controllers , ControllerEndpoint {
298+ PubKey : ck .Pub ,
299+ Addr : ap ,
316300 })
317301 }
318302
319- cfg .AFSettings [afName ] = afCfg
303+ cfg .AFSettings [types . AFName ( afName ) ] = afCfg
320304 }
321305
322- return & cfg
306+ return cfg
323307}
324308
325309func formatAddrPort (host string , port uint16 ) string {
326- // If it's an IPv6 address, wrap in brackets
327310 if addr , err := netip .ParseAddr (host ); err == nil && addr .Is6 () {
328311 return fmt .Sprintf ("[%s]:%d" , host , port )
329312 }
330313 return fmt .Sprintf ("%s:%d" , host , port )
331314}
332-
333- func writeYAML (path string , v interface {}) error {
334- data , err := yaml .Marshal (v )
335- if err != nil {
336- return err
337- }
338- return os .WriteFile (path , data , 0644 )
339- }
0 commit comments