diff --git a/common/libnetwork/netavark/config.go b/common/libnetwork/netavark/config.go index dafd7a937b..1de4a81500 100644 --- a/common/libnetwork/netavark/config.go +++ b/common/libnetwork/netavark/config.go @@ -11,11 +11,11 @@ import ( "path/filepath" "reflect" "slices" - "strconv" - "time" + "strings" internalutil "go.podman.io/common/libnetwork/internal/util" "go.podman.io/common/libnetwork/types" + "go.podman.io/common/pkg/config" "go.podman.io/storage/pkg/stringid" ) @@ -103,7 +103,7 @@ func (n *netavarkNetwork) NetworkCreate(net types.Network, options *types.Networ } network, err := n.networkCreate(&net, false) if err != nil { - if options != nil && options.IgnoreIfExists && errors.Is(err, types.ErrNetworkExists) { + if options != nil && options.IgnoreIfExists && strings.Contains(err.Error(), "already exists") { if network, ok := n.networks[net.Name]; ok { return *network, nil } @@ -140,251 +140,81 @@ func (n *netavarkNetwork) networkCreate(newNetwork *types.Network, defaultNet bo return nil, errors.New("failed to create random network ID") } } - - err := internalutil.CommonNetworkCreate(n, newNetwork) - if err != nil { - return nil, err + if newNetwork.Name == "" { + name, err := internalutil.GetFreeDeviceName(n) + if err != nil { + return nil, err + } + newNetwork.Name = name } - err = validateIPAMDriver(newNetwork) + usedSubnets, err := internalutil.GetUsedSubnets(n) if err != nil { return nil, err } - // Only get the used networks for validation if we do not create the default network. - // The default network should not be validated against used subnets, we have to ensure - // that this network can always be created even when a subnet is already used on the host. - // This could happen if you run a container on this net, then the cni interface will be - // created on the host and "block" this subnet from being used again. - // Therefore the next podman command tries to create the default net again and it would - // fail because it thinks the network is used on the host. - var usedNetworks []*net.IPNet - if !defaultNet && newNetwork.Driver == types.BridgeNetworkDriver { - usedNetworks, err = internalutil.GetUsedSubnets(n) - if err != nil { - return nil, err - } + usedNames := make(map[string]string, len(n.networks)) + for name, network := range n.networks { + usedNames[name] = network.ID } - switch newNetwork.Driver { - case types.BridgeNetworkDriver: - internalutil.MapDockerBridgeDriverOptions(newNetwork) - - checkBridgeConflict := true - // validate the given options, - for key, value := range newNetwork.Options { - switch key { - case types.MTUOption: - _, err = internalutil.ParseMTU(value) - if err != nil { - return nil, err - } - - case types.VLANOption: - _, err = internalutil.ParseVlan(value) - if err != nil { - return nil, err - } - // Unset used networks here to ensure that when using vlan networks - // we do not error if the subnet is already in use on the host. - // https://github.com/containers/podman/issues/25736 - usedNetworks = nil - // If there is no vlan there should be no other config with the same bridge. - // However with vlan we want to allow that so that you can have different - // configs on the same bridge but different vlans - // https://github.com/containers/common/issues/2095 - checkBridgeConflict = false - - case types.IsolateOption: - val, err := internalutil.ParseIsolate(value) - if err != nil { - return nil, err - } - newNetwork.Options[types.IsolateOption] = val - case types.MetricOption: - _, err := strconv.ParseUint(value, 10, 32) - if err != nil { - return nil, err - } - case types.NoDefaultRoute: - val, err := strconv.ParseBool(value) - if err != nil { - return nil, err - } - // rust only support "true" or "false" while go can parse 1 and 0 as well so we need to change it - newNetwork.Options[types.NoDefaultRoute] = strconv.FormatBool(val) - case types.VRFOption: - if len(value) == 0 { - return nil, errors.New("invalid vrf name") - } - case types.ModeOption: - switch value { - case types.BridgeModeManaged: - case types.BridgeModeUnmanaged: - // Unset used networks here to ensure that when using unmanaged networks - // we do not error if the subnet is already in use on the host. - // https://github.com/containers/common/issues/2322 - usedNetworks = nil - // Also make sure we don't error if the bridge name is already used as well. - checkBridgeConflict = false - default: - return nil, fmt.Errorf("unknown bridge mode %q", value) - } - default: - return nil, fmt.Errorf("unsupported bridge network option %s", key) - } - } - - err = internalutil.CreateBridge(n, newNetwork, usedNetworks, n.defaultsubnetPools, checkBridgeConflict) - if err != nil { - return nil, err - } - - case types.MacVLANNetworkDriver, types.IPVLANNetworkDriver: - err = createIpvlanOrMacvlan(newNetwork) - if err != nil { - return nil, err - } - default: - net, err := n.createPlugin(newNetwork) - if err != nil { - return nil, err - } - newNetwork = net + type Used struct { + Interfaces []string `json:"interfaces"` + Names map[string]string `json:"names"` + Subnets []types.IPNet `json:"subnets"` } - // when we do not have ipam we must disable dns - internalutil.IpamNoneDisableDNS(newNetwork) - - // process NetworkDNSServers - if len(newNetwork.NetworkDNSServers) > 0 && !newNetwork.DNSEnabled { - return nil, fmt.Errorf("cannot set NetworkDNSServers if DNS is not enabled for the network: %w", types.ErrInvalidArg) + type ConfigOpts struct { + SubnetPools []config.SubnetPool `json:"subnet_pools"` + DefaultInterfaceName string `json:"default_interface_name"` + CheckUsedSubnets bool `json:"check_used_subnets"` } - // validate ip address - for _, dnsServer := range newNetwork.NetworkDNSServers { - if net.ParseIP(dnsServer) == nil { - return nil, fmt.Errorf("unable to parse ip %s specified in NetworkDNSServers: %w", dnsServer, types.ErrInvalidArg) - } + + type CreateConfigOptions struct { + Network types.Network `json:"network"` + Used Used `json:"used"` + Options ConfigOpts `json:"options"` } - // add gateway when not internal or dns enabled - addGateway := !newNetwork.Internal || newNetwork.DNSEnabled - err = internalutil.ValidateSubnets(newNetwork, addGateway, usedNetworks) - if err != nil { - return nil, err + subnets := make([]types.IPNet, len(usedSubnets)) + for i, subnet := range usedSubnets { + subnets[i] = types.IPNet{IPNet: *subnet} } - // validate routes - err = internalutil.ValidateRoutes(newNetwork.Routes) + opts := CreateConfigOptions{ + Network: *newNetwork, + Used: Used{ + Interfaces: internalutil.GetBridgeInterfaceNames(n), + Names: usedNames, + Subnets: subnets, + }, + Options: ConfigOpts{ + SubnetPools: n.defaultsubnetPools, + DefaultInterfaceName: n.DefaultInterfaceName(), + CheckUsedSubnets: !defaultNet, + }, + } + var needsPlugin bool + if !slices.Contains(BuiltinDrivers, newNetwork.Driver) { + needsPlugin = true + } + var result *types.Network + err = n.execNetavark([]string{"create"}, needsPlugin, &opts, &result) if err != nil { return nil, err } - newNetwork.Created = time.Now() + // normalize network fields + parseNetwork(result) if !defaultNet { - err = n.commitNetwork(newNetwork) + err := n.commitNetwork(result) if err != nil { return nil, err } } - return newNetwork, nil -} - -// ipvlan shares the same mac address so supporting DHCP is not really possible. -var errIpvlanNoDHCP = errors.New("ipam driver dhcp is not supported with ipvlan") - -func createIpvlanOrMacvlan(network *types.Network) error { - if network.NetworkInterface != "" { - interfaceNames, err := internalutil.GetLiveNetworkNames() - if err != nil { - return err - } - if !slices.Contains(interfaceNames, network.NetworkInterface) { - return fmt.Errorf("parent interface %s does not exist", network.NetworkInterface) - } - } - - driver := network.Driver - isMacVlan := driver != types.IPVLANNetworkDriver - - // always turn dns off with macvlan, it is not implemented in netavark - // and makes little sense to support with macvlan - // see https://github.com/containers/netavark/pull/467 - network.DNSEnabled = false - - // we already validated the drivers before so we just have to set the default here - switch network.IPAMOptions[types.Driver] { - case "": - if len(network.Subnets) == 0 { - // if no subnets and no driver choose dhcp - network.IPAMOptions[types.Driver] = types.DHCPIPAMDriver - if !isMacVlan { - return errIpvlanNoDHCP - } - } else { - network.IPAMOptions[types.Driver] = types.HostLocalIPAMDriver - } - case types.HostLocalIPAMDriver: - if len(network.Subnets) == 0 { - return fmt.Errorf("%s driver needs at least one subnet specified when the host-local ipam driver is set", driver) - } - case types.DHCPIPAMDriver: - if !isMacVlan { - return errIpvlanNoDHCP - } - if len(network.Subnets) > 0 { - return errors.New("ipam driver dhcp set but subnets are set") - } - } - - // validate the given options, we do not need them but just check to make sure they are valid - for key, value := range network.Options { - switch key { - case types.ModeOption: - if isMacVlan { - if !slices.Contains(types.ValidMacVLANModes, value) { - return fmt.Errorf("unknown macvlan mode %q", value) - } - } else { - if !slices.Contains(types.ValidIPVLANModes, value) { - return fmt.Errorf("unknown ipvlan mode %q", value) - } - } - case types.MetricOption: - _, err := strconv.ParseUint(value, 10, 32) - if err != nil { - return err - } - case types.MTUOption: - _, err := internalutil.ParseMTU(value) - if err != nil { - return err - } - case types.NoDefaultRoute: - val, err := strconv.ParseBool(value) - if err != nil { - return err - } - // rust only support "true" or "false" while go can parse 1 and 0 as well so we need to change it - network.Options[types.NoDefaultRoute] = strconv.FormatBool(val) - case types.BclimOption: - if isMacVlan { - _, err := strconv.ParseInt(value, 10, 32) - if err != nil { - return fmt.Errorf("failed to parse %q option: %w", key, err) - } - // do not fallthrough for macvlan - break - } - // bclim is only valid for macvlan not ipvlan so fallthrough to error case - fallthrough - default: - return fmt.Errorf("unsupported %s network option %s", driver, key) - } - } - return nil + return result, nil } // NetworkRemove will remove the Network with the given name or ID. diff --git a/common/libnetwork/netavark/config_test.go b/common/libnetwork/netavark/config_test.go index 62c5017324..433ce426ee 100644 --- a/common/libnetwork/netavark/config_test.go +++ b/common/libnetwork/netavark/config_test.go @@ -14,10 +14,8 @@ import ( . "github.com/onsi/gomega" gomegaTypes "github.com/onsi/gomega/types" "github.com/sirupsen/logrus" - "go.podman.io/common/libnetwork/netavark" "go.podman.io/common/libnetwork/types" "go.podman.io/common/libnetwork/util" - "go.podman.io/common/pkg/config" ) var _ = Describe("Config", func() { @@ -146,7 +144,8 @@ var _ = Describe("Config", func() { network = types.Network{Name: network1.Name} _, err = libpodNet.NetworkCreate(network, nil) - Expect(err).To(MatchError(types.ErrNetworkExists)) + Expect(err).To(HaveOccurred()) + Expect(err.Error()).To(ContainSubstring("network already exists")) }) It("return the same network when creating two networks with the same name and ignore", func() { @@ -560,7 +559,6 @@ var _ = Describe("Config", func() { } _, err := libpodNet.NetworkCreate(network, nil) Expect(err).To(HaveOccurred()) - Expect(err.Error()).To(ContainSubstring("subnet ip is nil")) }) It("create network with name", func() { @@ -639,7 +637,7 @@ var _ = Describe("Config", func() { }) It("create network with more than max interface name size", func() { - name := strings.Repeat("a", types.MaxInterfaceNameLength+1) + name := strings.Repeat("a", types.MaxInterfaceNameLength+2) network := types.Network{ NetworkInterface: name, } @@ -706,12 +704,7 @@ var _ = Describe("Config", func() { }) It("update NetworkDNSServers AddDNSServers", func() { - libpodNet, err := netavark.NewNetworkInterface(&netavark.InitConfig{ - Config: &config.Config{}, - NetworkConfigDir: networkConfDir, - NetworkRunDir: networkConfDir, - NetavarkBinary: "true", - }) + libpodNet, err := getNetworkInterface(networkConfDir) if err != nil { Fail("Failed to create NewNetavarkNetworkInterface") } @@ -733,12 +726,7 @@ var _ = Describe("Config", func() { }) It("update NetworkDNSServers RemoveDNSServers", func() { - libpodNet, err := netavark.NewNetworkInterface(&netavark.InitConfig{ - Config: &config.Config{}, - NetworkConfigDir: networkConfDir, - NetworkRunDir: networkConfDir, - NetavarkBinary: "true", - }) + libpodNet, err := getNetworkInterface(networkConfDir) if err != nil { Fail("Failed to create NewNetavarkNetworkInterface") } @@ -760,12 +748,7 @@ var _ = Describe("Config", func() { }) It("update NetworkDNSServers Add and Remove DNSServers", func() { - libpodNet, err := netavark.NewNetworkInterface(&netavark.InitConfig{ - Config: &config.Config{}, - NetworkConfigDir: networkConfDir, - NetworkRunDir: networkConfDir, - NetavarkBinary: "true", - }) + libpodNet, err := getNetworkInterface(networkConfDir) if err != nil { Fail("Failed to create NewNetavarkNetworkInterface") } @@ -801,7 +784,7 @@ var _ = Describe("Config", func() { } _, err := libpodNet.NetworkCreate(network, nil) Expect(err).To(HaveOccurred()) - Expect(err.Error()).To(ContainSubstring(`unable to parse ip a.b.c.d`)) + Expect(err.Error()).To(ContainSubstring("invalid IP address syntax")) }) It("create network with NetworDNSServers with DNSEnabled=false", func() { @@ -851,7 +834,7 @@ var _ = Describe("Config", func() { } _, err := libpodNet.NetworkCreate(network, nil) Expect(err).To(HaveOccurred()) - Expect(err.Error()).To(ContainSubstring(`parsing "abc": invalid syntax`)) + Expect(err.Error()).To(ContainSubstring("Failed to parse mtu")) network = types.Network{ Options: map[string]string{ @@ -860,7 +843,7 @@ var _ = Describe("Config", func() { } _, err = libpodNet.NetworkCreate(network, nil) Expect(err).To(HaveOccurred()) - Expect(err.Error()).To(ContainSubstring(`mtu -1 is less than zero`)) + Expect(err.Error()).To(ContainSubstring(`Failed to parse mtu`)) }) It("create network with com.docker.network.driver.mtu option", func() { @@ -888,7 +871,7 @@ var _ = Describe("Config", func() { } _, err := libpodNet.NetworkCreate(network, nil) Expect(err).To(HaveOccurred()) - Expect(err.Error()).To(ContainSubstring(`parsing "abc": invalid syntax`)) + Expect(err.Error()).To(ContainSubstring(`Failed to parse mtu`)) network = types.Network{ Options: map[string]string{ @@ -897,7 +880,7 @@ var _ = Describe("Config", func() { } _, err = libpodNet.NetworkCreate(network, nil) Expect(err).To(HaveOccurred()) - Expect(err.Error()).To(ContainSubstring(`mtu -1 is less than zero`)) + Expect(err.Error()).To(ContainSubstring(`Failed to parse mtu`)) }) It("create network with vlan option", func() { @@ -924,7 +907,7 @@ var _ = Describe("Config", func() { } _, err := libpodNet.NetworkCreate(network, nil) Expect(err).To(HaveOccurred()) - Expect(err.Error()).To(ContainSubstring(`parsing "abc": invalid syntax`)) + Expect(err.Error()).To(ContainSubstring(`Failed to parse: invalid digit`)) network = types.Network{ Options: map[string]string{ @@ -933,7 +916,7 @@ var _ = Describe("Config", func() { } _, err = libpodNet.NetworkCreate(network, nil) Expect(err).To(HaveOccurred()) - Expect(err.Error()).To(ContainSubstring(`vlan ID -1 must be between 0 and 4094`)) + Expect(err.Error()).To(ContainSubstring(`Failed to parse: invalid digit`)) }) It("create two networks with vlan option and same bridge", func() { @@ -1010,7 +993,7 @@ var _ = Describe("Config", func() { } _, err := libpodNet.NetworkCreate(network, nil) Expect(err).To(HaveOccurred()) - Expect(err.Error()).To(ContainSubstring(`failed to find driver or plugin "someDriver"`)) + Expect(err.Error()).To(ContainSubstring(`plugin directory not provided`)) }) It("network create internal and dns", func() { @@ -1045,7 +1028,7 @@ var _ = Describe("Config", func() { network = types.Network{Name: "net"} _, err = libpodNet.NetworkCreate(network, nil) Expect(err).To(HaveOccurred()) - Expect(err.Error()).To(ContainSubstring("network name net already used")) + Expect(err.Error()).To(ContainSubstring("network already exists net")) }) It("remove default network config should fail", func() { @@ -1284,7 +1267,7 @@ var _ = Describe("Config", func() { } _, err = libpodNet.NetworkCreate(network, nil) Expect(err).To(HaveOccurred()) - Expect(err.Error()).To(Equal("failed to parse \"bclim\" option: strconv.ParseInt: parsing \"abc\": invalid syntax")) + Expect(err.Error()).To(ContainSubstring("failed to parse")) }) It("create ipvlan config with bclim should fail", func() { @@ -1301,7 +1284,7 @@ var _ = Describe("Config", func() { } _, err := libpodNet.NetworkCreate(network, nil) Expect(err).To(HaveOccurred()) - Expect(err.Error()).To(Equal("unsupported ipvlan network option bclim")) + Expect(err.Error()).To(ContainSubstring("unsupported ipvlan network option bclim")) }) It("create macvlan config with mode", func() { @@ -1336,7 +1319,7 @@ var _ = Describe("Config", func() { } _, err := libpodNet.NetworkCreate(network, nil) Expect(err).To(HaveOccurred()) - Expect(err.Error()).To(Equal("unknown macvlan mode \"abc\"")) + Expect(err.Error()).To(ContainSubstring("unknown macvlan mode \"abc\"")) }) It("create macvlan config with invalid option", func() { @@ -1353,7 +1336,7 @@ var _ = Describe("Config", func() { } _, err := libpodNet.NetworkCreate(network, nil) Expect(err).To(HaveOccurred()) - Expect(err.Error()).To(Equal("unsupported macvlan network option abc")) + Expect(err.Error()).To(ContainSubstring("unsupported macvlan network option abc")) }) It("create macvlan config with mtu", func() { @@ -1411,7 +1394,7 @@ var _ = Describe("Config", func() { } _, err := libpodNet.NetworkCreate(network, nil) Expect(err).To(HaveOccurred()) - Expect(err.Error()).To(Equal("none ipam driver is set but subnets are given")) + Expect(err.Error()).To(ContainSubstring("None ipam driver is set but subnets are given")) }) It("create macvlan config with none ipam driver", func() { @@ -1443,7 +1426,7 @@ var _ = Describe("Config", func() { network := types.Network{Driver: "ipvlan"} _, err := libpodNet.NetworkCreate(network, nil) Expect(err).To(HaveOccurred()) - Expect(err.Error()).To(Equal("ipam driver dhcp is not supported with ipvlan")) + Expect(err.Error()).To(ContainSubstring("ipam driver dhcp is not supported with ipvlan")) }) It("create ipvlan config without subnet and host-local", func() { @@ -1491,7 +1474,7 @@ var _ = Describe("Config", func() { } _, err := libpodNet.NetworkCreate(network, nil) Expect(err).To(HaveOccurred()) - Expect(err.Error()).To(Equal("ipam driver dhcp is not supported with ipvlan")) + Expect(err.Error()).To(ContainSubstring("ipam driver dhcp is not supported with ipvlan")) }) It("create ipvlan config with mode", func() { @@ -1526,7 +1509,7 @@ var _ = Describe("Config", func() { } _, err := libpodNet.NetworkCreate(network, nil) Expect(err).To(HaveOccurred()) - Expect(err.Error()).To(Equal("unknown ipvlan mode \"abc\"")) + Expect(err.Error()).To(ContainSubstring("unknown ipvlan mode \"abc\"")) }) It("create network with isolate option 'true'", func() { @@ -1712,7 +1695,7 @@ var _ = Describe("Config", func() { } _, err := libpodNet.NetworkCreate(network, nil) Expect(err).To(HaveOccurred()) - Expect(err.Error()).To(Equal("route destination invalid")) + Expect(err.Error()).To(ContainSubstring("route destination invalid")) }) It("create macvlan config with invalid static route (destination is address)", func() { @@ -1728,7 +1711,7 @@ var _ = Describe("Config", func() { } _, err := libpodNet.NetworkCreate(network, nil) Expect(err).To(HaveOccurred()) - Expect(err.Error()).To(Equal("route destination invalid")) + Expect(err.Error()).To(ContainSubstring("route destination invalid")) }) It("create ipvlan config with invalid static route (destination is address)", func() { @@ -1749,7 +1732,7 @@ var _ = Describe("Config", func() { } _, err := libpodNet.NetworkCreate(network, nil) Expect(err).To(HaveOccurred()) - Expect(err.Error()).To(Equal("route destination invalid")) + Expect(err.Error()).To(ContainSubstring("route destination invalid")) }) It("create bridge config with invalid static route (dest = \"foo\")", func() { @@ -1765,7 +1748,7 @@ var _ = Describe("Config", func() { } _, err := libpodNet.NetworkCreate(network, nil) Expect(err).To(HaveOccurred()) - Expect(err.Error()).To(Equal("route destination ip nil")) + Expect(err.Error()).To(ContainSubstring("invalid type: null")) }) It("create macvlan config with invalid static route (dest = \"foo\")", func() { @@ -1781,7 +1764,7 @@ var _ = Describe("Config", func() { } _, err := libpodNet.NetworkCreate(network, nil) Expect(err).To(HaveOccurred()) - Expect(err.Error()).To(Equal("route destination ip nil")) + Expect(err.Error()).To(ContainSubstring("invalid type: null")) }) It("create ipvlan config with invalid static route (dest = \"foo\")", func() { @@ -1802,7 +1785,7 @@ var _ = Describe("Config", func() { } _, err := libpodNet.NetworkCreate(network, nil) Expect(err).To(HaveOccurred()) - Expect(err.Error()).To(Equal("route destination ip nil")) + Expect(err.Error()).To(ContainSubstring("invalid type: null")) }) It("create bridge config with invalid static route (gw = \"foo\")", func() { @@ -1818,7 +1801,7 @@ var _ = Describe("Config", func() { } _, err := libpodNet.NetworkCreate(network, nil) Expect(err).To(HaveOccurred()) - Expect(err.Error()).To(Equal("route gateway nil")) + Expect(err.Error()).To(ContainSubstring("invalid IP address syntax")) }) It("create macvlan config with invalid static route (gw = \"foo\")", func() { @@ -1834,7 +1817,7 @@ var _ = Describe("Config", func() { } _, err := libpodNet.NetworkCreate(network, nil) Expect(err).To(HaveOccurred()) - Expect(err.Error()).To(Equal("route gateway nil")) + Expect(err.Error()).To(ContainSubstring("invalid IP address syntax")) }) It("create ipvlan config with invalid static route (gw = \"foo\")", func() { @@ -1855,7 +1838,7 @@ var _ = Describe("Config", func() { } _, err := libpodNet.NetworkCreate(network, nil) Expect(err).To(HaveOccurred()) - Expect(err.Error()).To(Equal("route gateway nil")) + Expect(err.Error()).To(ContainSubstring("invalid IP address syntax")) }) It("create macvlan config with metric option", func() { @@ -1924,7 +1907,7 @@ var _ = Describe("Config", func() { } _, err := libpodNet.NetworkCreate(network, nil) Expect(err).To(HaveOccurred()) - Expect(err.Error()).To(ContainSubstring("parsing \"foo\": invalid syntax")) + Expect(err.Error()).To(ContainSubstring("invalid no_default_route value foo")) }) It("create macvlan config with invalid no_default_route", func() { @@ -1936,7 +1919,7 @@ var _ = Describe("Config", func() { } _, err := libpodNet.NetworkCreate(network, nil) Expect(err).To(HaveOccurred()) - Expect(err.Error()).To(ContainSubstring("parsing \"foo\": invalid syntax")) + Expect(err.Error()).To(ContainSubstring("invalid no_default_route value foo")) }) It("create ipvlan config with invalid no_default_route", func() { @@ -1953,7 +1936,7 @@ var _ = Describe("Config", func() { } _, err := libpodNet.NetworkCreate(network, nil) Expect(err).To(HaveOccurred()) - Expect(err.Error()).To(ContainSubstring("parsing \"foo\": invalid syntax")) + Expect(err.Error()).To(ContainSubstring("invalid no_default_route value foo")) }) Context("network load valid existing ones", func() { diff --git a/common/libnetwork/netavark/ipam_test.go b/common/libnetwork/netavark/ipam_test.go index 26ad4f81ba..58b011aa8a 100644 --- a/common/libnetwork/netavark/ipam_test.go +++ b/common/libnetwork/netavark/ipam_test.go @@ -6,6 +6,7 @@ import ( "bytes" "fmt" "net" + "os" . "github.com/onsi/ginkgo/v2" . "github.com/onsi/gomega" @@ -29,10 +30,12 @@ var _ = Describe("IPAM", func() { }) JustBeforeEach(func() { + netavarkBinary := os.Getenv("NETAVARK_BINARY") libpodNet, err := NewNetworkInterface(&InitConfig{ Config: &config.Config{}, NetworkConfigDir: networkConfDir, NetworkRunDir: networkConfDir, + NetavarkBinary: netavarkBinary, }) if err != nil { Fail("Failed to create NewCNINetworkInterface") diff --git a/common/libnetwork/netavark/network.go b/common/libnetwork/netavark/network.go index 9dbe3b452d..b465a79ad5 100644 --- a/common/libnetwork/netavark/network.go +++ b/common/libnetwork/netavark/network.go @@ -165,13 +165,13 @@ func NewNetworkInterface(conf *InitConfig) (types.ContainerNetwork, error) { return n, nil } -var builtinDrivers = []string{types.BridgeNetworkDriver, types.MacVLANNetworkDriver, types.IPVLANNetworkDriver} +var BuiltinDrivers = []string{types.BridgeNetworkDriver, types.MacVLANNetworkDriver, types.IPVLANNetworkDriver} // Drivers will return the list of supported network drivers // for this interface. func (n *netavarkNetwork) Drivers() []string { paths := getAllPlugins(n.pluginDirs) - return append(builtinDrivers, paths...) + return append(BuiltinDrivers, paths...) } // DefaultNetworkName will return the default netavark network name. diff --git a/common/libnetwork/netavark/run.go b/common/libnetwork/netavark/run.go index a57f2761af..09c239961f 100644 --- a/common/libnetwork/netavark/run.go +++ b/common/libnetwork/netavark/run.go @@ -175,7 +175,7 @@ func (n *netavarkNetwork) convertNetOpts(opts types.NetworkOptions) (*netavarkOp return nil, false, err } netavarkOptions.Networks[network] = net - if !slices.Contains(builtinDrivers, net.Driver) { + if !slices.Contains(BuiltinDrivers, net.Driver) { needsPlugin = true } } diff --git a/common/libnetwork/types/network.go b/common/libnetwork/types/network.go index aea41705a2..bdd8abebbc 100644 --- a/common/libnetwork/types/network.go +++ b/common/libnetwork/types/network.go @@ -136,6 +136,34 @@ func (n *IPNet) MarshalText() ([]byte, error) { return []byte(n.String()), nil } +func (n IPNet) MarshalJSON() ([]byte, error) { + // Check if IPNet is empty (zero value) + if n.IP == nil || len(n.IP) == 0 || n.Mask == nil { + return []byte("null"), nil + } + // Use MarshalText for non-empty values + text, err := n.MarshalText() + if err != nil { + return nil, err + } + // JSON-encode the text as a string + return json.Marshal(string(text)) +} + +func (n *IPNet) UnmarshalJSON(data []byte) error { + // Handle null values + if string(data) == "null" { + *n = IPNet{} + return nil + } + // Unmarshal as a JSON string and then parse as CIDR + var s string + if err := json.Unmarshal(data, &s); err != nil { + return err + } + return n.UnmarshalText([]byte(s)) +} + func (n *IPNet) UnmarshalText(text []byte) error { subnet, err := ParseCIDR(string(text)) if err != nil { diff --git a/common/pkg/config/config.go b/common/pkg/config/config.go index 51fa71056e..187d2fc22a 100644 --- a/common/pkg/config/config.go +++ b/common/pkg/config/config.go @@ -638,10 +638,10 @@ type NetworkConfig struct { type SubnetPool struct { // Base is a bigger subnet which will be used to allocate a subnet with // the given size. - Base *types.IPNet `toml:"base,omitempty"` + Base *types.IPNet `toml:"base,omitempty" json:"base,omitempty"` // Size is the CIDR for the new subnet. It must be equal or small // than the CIDR from the base subnet. - Size int `toml:"size,omitempty"` + Size int `toml:"size,omitempty" json:"size,omitempty"` } // SecretConfig represents the "secret" TOML config table.