@@ -17,21 +17,10 @@ import (
1717 "strings"
1818 "time"
1919
20- "github.com/alexflint/go-filemutex"
2120 "github.com/containernetworking/cni/libcni"
22- "github.com/containernetworking/cni/pkg/types"
23- types100 "github.com/containernetworking/cni/pkg/types/100"
24- "github.com/containernetworking/plugins/pkg/ns"
25- "github.com/containernetworking/plugins/pkg/testutils"
26- "github.com/containernetworking/plugins/pkg/utils"
27- "github.com/coreos/go-iptables/iptables"
2821 "github.com/google/uuid"
29- "github.com/siderolabs/gen/xslices"
30- "github.com/siderolabs/go-blockdevice/v2/blkid"
31- sideronet "github.com/siderolabs/net"
3222
3323 "github.com/siderolabs/talos/pkg/provision"
34- "github.com/siderolabs/talos/pkg/provision/internal/cniutils"
3524 "github.com/siderolabs/talos/pkg/provision/providers/vm"
3625)
3726
@@ -94,7 +83,7 @@ type LaunchConfig struct {
9483 // filled by CNI invocation
9584 tapName string
9685 vmMAC string
97- ns ns. NetNS
86+ nsPath string
9887
9988 // signals
10089 c chan os.Signal
@@ -108,190 +97,6 @@ type tpm2Config struct {
10897 StateDir string
10998}
11099
111- // withCNIOperationLocked ensures that CNI operations don't run concurrently.
112- //
113- // There are race conditions in the CNI plugins that can cause a failure if called concurrently.
114- func withCNIOperationLocked [T any ](config * LaunchConfig , f func () (T , error )) (T , error ) {
115- var zeroT T
116-
117- lock , err := filemutex .New (filepath .Join (config .StatePath , "cni.lock" ))
118- if err != nil {
119- return zeroT , fmt .Errorf ("failed to create CNI lock: %w" , err )
120- }
121-
122- if err = lock .Lock (); err != nil {
123- return zeroT , fmt .Errorf ("failed to acquire CNI lock: %w" , err )
124- }
125-
126- defer func () {
127- if err := lock .Close (); err != nil {
128- log .Printf ("failed to release CNI lock: %s" , err )
129- }
130- }()
131-
132- return f ()
133- }
134-
135- // withCNIOperationLockedNoResult ensures that CNI operations don't run concurrently.
136- func withCNIOperationLockedNoResult (config * LaunchConfig , f func () error ) error {
137- _ , err := withCNIOperationLocked (config , func () (struct {}, error ) {
138- return struct {}{}, f ()
139- })
140-
141- return err
142- }
143-
144- // withCNI creates network namespace, launches CNI and passes control to the next function
145- // filling config with netNS and interface details.
146- //
147- //nolint:gocyclo
148- func withCNI (ctx context.Context , config * LaunchConfig , f func (config * LaunchConfig ) error ) error {
149- // random ID for the CNI, maps to single VM
150- containerID := uuid .New ().String ()
151-
152- cniConfig := libcni .NewCNIConfigWithCacheDir (config .CNI .BinPath , config .CNI .CacheDir , nil )
153-
154- // create a network namespace
155- ns , err := testutils .NewNS ()
156- if err != nil {
157- return err
158- }
159-
160- defer func () {
161- ns .Close () //nolint:errcheck
162- testutils .UnmountNS (ns ) //nolint:errcheck
163- }()
164-
165- ips := make ([]string , len (config .IPs ))
166- for j := range ips {
167- ips [j ] = sideronet .FormatCIDR (config .IPs [j ], config .CIDRs [j ])
168- }
169-
170- gatewayAddrs := xslices .Map (config .GatewayAddrs , netip .Addr .String )
171-
172- runtimeConf := libcni.RuntimeConf {
173- ContainerID : containerID ,
174- NetNS : ns .Path (),
175- IfName : "veth0" ,
176- Args : [][2 ]string {
177- {"IP" , strings .Join (ips , "," )},
178- {"GATEWAY" , strings .Join (gatewayAddrs , "," )},
179- {"IgnoreUnknown" , "1" },
180- },
181- }
182-
183- // attempt to clean up network in case it was deployed previously
184- err = withCNIOperationLockedNoResult (
185- config ,
186- func () error {
187- return cniConfig .DelNetworkList (ctx , config .NetworkConfig , & runtimeConf )
188- },
189- )
190- if err != nil {
191- return fmt .Errorf ("error deleting CNI network: %w" , err )
192- }
193-
194- res , err := withCNIOperationLocked (
195- config ,
196- func () (types.Result , error ) {
197- return cniConfig .AddNetworkList (ctx , config .NetworkConfig , & runtimeConf )
198- },
199- )
200- if err != nil {
201- return fmt .Errorf ("error provisioning CNI network: %w" , err )
202- }
203-
204- defer func () {
205- if e := withCNIOperationLockedNoResult (
206- config ,
207- func () error {
208- return cniConfig .DelNetworkList (ctx , config .NetworkConfig , & runtimeConf )
209- },
210- ); e != nil {
211- log .Printf ("error cleaning up CNI: %s" , e )
212- }
213- }()
214-
215- currentResult , err := types100 .NewResultFromResult (res )
216- if err != nil {
217- return fmt .Errorf ("failed to parse cni result: %w" , err )
218- }
219-
220- vmIface , tapIface , err := cniutils .VMTapPair (currentResult , containerID )
221- if err != nil {
222- return errors .New (
223- "failed to parse VM network configuration from CNI output, ensure CNI is configured with a plugin " +
224- "that supports automatic VM network configuration such as tc-redirect-tap" )
225- }
226-
227- cniChain := utils .FormatChainName (config .NetworkConfig .Name , containerID )
228-
229- ipt , err := iptables .New ()
230- if err != nil {
231- return fmt .Errorf ("failed to initialize iptables: %w" , err )
232- }
233-
234- // don't masquerade traffic with "broadcast" destination from the VM
235- //
236- // no need to clean up the rule, as CNI drops the whole chain
237- if err = ipt .InsertUnique ("nat" , cniChain , 1 , "--destination" , "255.255.255.255/32" , "-j" , "ACCEPT" ); err != nil {
238- return fmt .Errorf ("failed to insert iptables rule to allow broadcast traffic: %w" , err )
239- }
240-
241- for _ , cidr := range config .NoMasqueradeCIDRs {
242- if err = ipt .InsertUnique ("nat" , cniChain , 1 , "--destination" , cidr .String (), "-j" , "ACCEPT" ); err != nil {
243- return fmt .Errorf ("failed to insert iptables rule to allow non-masquerade traffic to cidr %q: %w" , cidr .String (), err )
244- }
245- }
246-
247- config .tapName = tapIface .Name
248- config .vmMAC = vmIface .Mac
249- config .ns = ns
250-
251- for j := range config .CIDRs {
252- nameservers := make ([]netip.Addr , 0 , len (config .Nameservers ))
253-
254- // filter nameservers by IPv4/IPv6 matching IPs
255- for i := range config .Nameservers {
256- if config .IPs [j ].Is6 () {
257- if config .Nameservers [i ].Is6 () {
258- nameservers = append (nameservers , config .Nameservers [i ])
259- }
260- } else {
261- if config .Nameservers [i ].Is4 () {
262- nameservers = append (nameservers , config .Nameservers [i ])
263- }
264- }
265- }
266-
267- // dump node IP/mac/hostname for dhcp
268- if err = vm .DumpIPAMRecord (config .StatePath , vm.IPAMRecord {
269- IP : config .IPs [j ],
270- Netmask : byte (config .CIDRs [j ].Bits ()),
271- MAC : vmIface .Mac ,
272- Hostname : config .Hostname ,
273- Gateway : config .GatewayAddrs [j ],
274- MTU : config .MTU ,
275- Nameservers : nameservers ,
276- TFTPServer : config .TFTPServer ,
277- IPXEBootFilename : config .IPXEBootFileName ,
278- }); err != nil {
279- return err
280- }
281- }
282-
283- return f (config )
284- }
285-
286- func checkPartitions (config * LaunchConfig ) (bool , error ) {
287- info , err := blkid .ProbePath (config .DiskPaths [0 ], blkid .WithSectorSize (config .DiskBlockSizes [0 ]))
288- if err != nil {
289- return false , fmt .Errorf ("error probing disk: %w" , err )
290- }
291-
292- return info .Name == "gpt" && len (info .Parts ) > 0 , nil
293- }
294-
295100// launchVM runs qemu with args built based on config.
296101//
297102//nolint:gocyclo,cyclop
@@ -521,9 +326,7 @@ func launchVM(config *LaunchConfig) error {
521326 cmd .Stdout = os .Stdout
522327 cmd .Stderr = os .Stderr
523328
524- if err := ns .WithNetNSPath (config .ns .Path (), func (_ ns.NetNS ) error {
525- return cmd .Start ()
526- }); err != nil {
329+ if err := startQemuCmd (config , cmd ); err != nil {
527330 return err
528331 }
529332
@@ -608,7 +411,7 @@ func Launch() error {
608411 config .sdStubExtraCmdlineConfig = fmt .Sprintf (" talos.config=http://%s/config.yaml" , httpServer .GetAddr ())
609412 }
610413
611- return withCNI (ctx , & config , func (config * LaunchConfig ) error {
414+ return withNetworkContext (ctx , & config , func (config * LaunchConfig ) error {
612415 for {
613416 for config .controller .PowerState () != PoweredOn {
614417 select {
0 commit comments