diff --git a/internal/server/device/nic_physical.go b/internal/server/device/nic_physical.go index 4d346075f99..2b37aeaeb5a 100644 --- a/internal/server/device/nic_physical.go +++ b/internal/server/device/nic_physical.go @@ -130,6 +130,12 @@ func (d *nicPhysical) validateConfig(instConf instance.ConfigReader) error { // Get actual parent device from network's parent setting. d.config["parent"] = netConfig["parent"] + // If parent is a bridge, ensure it's managed. + isParentBridge := d.config["parent"] != "" && util.PathExists(fmt.Sprintf("/sys/class/net/%s/bridge", d.config["parent"])) + if isParentBridge && d.network == nil { + return fmt.Errorf("Parent device is a bridge, use nictype=bridged instead") + } + // Copy certain keys verbatim from the network's settings. for _, field := range optionalFields { _, found := netConfig[field] @@ -174,6 +180,25 @@ func (d *nicPhysical) Start() (*deviceConfig.RunConfig, error) { return nil, err } + // Handle the case where the parent is a bridge. + isParentBridge := util.PathExists(fmt.Sprintf("/sys/class/net/%s/bridge", d.config["parent"])) + if isParentBridge { + // Convert the device to a nictype=bridged internally. + bridgedConfig := d.config.Clone() + bridgedConfig["type"] = "nic" + bridgedConfig["nictype"] = "bridged" + bridgedConfig["network"] = "" + + // Instantiate the new device. + bridged, err := load(d.inst, d.state, d.inst.Project().Name, d.inst.Name(), bridgedConfig, d.volatileGet, d.volatileSet) + if err != nil { + return nil, fmt.Errorf("Failed to initialize bridged device: %w", err) + } + + // Forward the start call. + return bridged.Start() + } + // Lock to avoid issues with containers starting in parallel. networkCreateSharedDeviceLock.Lock() defer networkCreateSharedDeviceLock.Unlock() @@ -374,6 +399,25 @@ func (d *nicPhysical) startVMUSB(name string) (*deviceConfig.RunConfig, error) { // Stop is run when the device is removed from the instance. func (d *nicPhysical) Stop() (*deviceConfig.RunConfig, error) { + // Handle the case where the parent is a bridge. + isParentBridge := util.PathExists(fmt.Sprintf("/sys/class/net/%s/bridge", d.config["parent"])) + if isParentBridge { + // Convert the device to a nictype=bridged internally. + bridgedConfig := d.config.Clone() + bridgedConfig["type"] = "nic" + bridgedConfig["nictype"] = "bridged" + bridgedConfig["network"] = "" + + // Instantiate the new device. + bridged, err := load(d.inst, d.state, d.inst.Project().Name, d.inst.Name(), bridgedConfig, d.volatileGet, d.volatileSet) + if err != nil { + return nil, fmt.Errorf("Failed to initialize bridged device: %w", err) + } + + // Forward the stop call. + return bridged.Stop() + } + v := d.volatileGet() runConf := deviceConfig.RunConfig{