Skip to content

Commit

Permalink
libnetwork/netavark: allow network create with no ipam driver
Browse files Browse the repository at this point in the history
Network create now uses the ipam driver. This allows the user to
configure the ipam driver manually instead of choosing a fixed default.
If the ipam driver is `none` no ips will be assigned to this container.
This means that only the interfaces are created.

This will require a patch in netavark since it rejects the config when
no static ips are provided.

Ref containers/podman#13521

Signed-off-by: Paul Holzinger <[email protected]>
  • Loading branch information
Luap99 committed Mar 18, 2022
1 parent 1651a55 commit 8a9a59b
Show file tree
Hide file tree
Showing 4 changed files with 182 additions and 6 deletions.
36 changes: 33 additions & 3 deletions libnetwork/netavark/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,11 @@ func (n *netavarkNetwork) networkCreate(newNetwork *types.Network, defaultNet bo
return nil, err
}

err = validateIPAMDriver(newNetwork)
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.
Expand Down Expand Up @@ -153,10 +158,19 @@ func createMacvlan(network *types.Network) error {
return errors.Errorf("parent interface %s does not exist", network.NetworkInterface)
}
}
if len(network.Subnets) == 0 {
return errors.Errorf("macvlan driver needs at least one subnet specified, DHCP is not supported with netavark")

// 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 {
return errors.Errorf("macvlan driver needs at least one subnet specified, DHCP is not yet supported with netavark")
}
network.IPAMOptions[types.Driver] = types.HostLocalIPAMDriver
case types.HostLocalIPAMDriver:
if len(network.Subnets) == 0 {
return errors.Errorf("macvlan driver needs at least one subnet specified, when the host-local ipam driver is set")
}
}
network.IPAMOptions[types.Driver] = types.HostLocalIPAMDriver

// validate the given options, we do not need them but just check to make sure they are valid
for key, value := range network.Options {
Expand Down Expand Up @@ -246,3 +260,19 @@ func (n *netavarkNetwork) NetworkInspect(nameOrID string) (types.Network, error)
}
return *network, nil
}

func validateIPAMDriver(n *types.Network) error {
ipamDriver := n.IPAMOptions[types.Driver]
switch ipamDriver {
case "", types.HostLocalIPAMDriver:
case types.NoneIPAMDriver:
if len(n.Subnets) > 0 {
return errors.New("none ipam driver is set but subnets are given")
}
case types.DHCPIPAMDriver:
return errors.New("dhcp ipam driver is not yet supported with netavark")
default:
return errors.Errorf("unsupported ipam driver %q", ipamDriver)
}
return nil
}
65 changes: 64 additions & 1 deletion libnetwork/netavark/config_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -794,7 +794,7 @@ var _ = Describe("Config", func() {
network := types.Network{Driver: "macvlan"}
_, err := libpodNet.NetworkCreate(network)
Expect(err).To(HaveOccurred())
Expect(err.Error()).To(Equal("macvlan driver needs at least one subnet specified, DHCP is not supported with netavark"))
Expect(err.Error()).To(Equal("macvlan driver needs at least one subnet specified, DHCP is not yet supported with netavark"))
})

It("create macvlan config with internal", func() {
Expand Down Expand Up @@ -957,6 +957,69 @@ var _ = Describe("Config", func() {
Expect(network1.Options).To(HaveKeyWithValue("mtu", "9000"))
})

It("create bridge config with none ipam driver", func() {
network := types.Network{
Driver: "bridge",
IPAMOptions: map[string]string{
"driver": "none",
},
}
network1, err := libpodNet.NetworkCreate(network)
Expect(err).To(BeNil())
Expect(network1.Driver).To(Equal("bridge"))
Expect(network1.IPAMOptions).ToNot(BeEmpty())
Expect(network1.IPAMOptions).To(HaveKeyWithValue("driver", "none"))
Expect(network1.Subnets).To(HaveLen(0))

// reload configs from disk
libpodNet, err = getNetworkInterface(networkConfDir)
Expect(err).To(BeNil())

network2, err := libpodNet.NetworkInspect(network1.Name)
Expect(err).To(BeNil())
EqualNetwork(network2, network1)
})

It("create bridge config with none ipam driver and subnets", func() {
subnet := "10.1.0.0/24"
n, _ := types.ParseCIDR(subnet)
network := types.Network{
Driver: "bridge",
IPAMOptions: map[string]string{
"driver": "none",
},
Subnets: []types.Subnet{
{Subnet: n},
},
}
_, err := libpodNet.NetworkCreate(network)
Expect(err).To(HaveOccurred())
Expect(err.Error()).To(Equal("none ipam driver is set but subnets are given"))
})

It("create macvlan config with none ipam driver", func() {
network := types.Network{
Driver: "macvlan",
IPAMOptions: map[string]string{
"driver": "none",
},
}
network1, err := libpodNet.NetworkCreate(network)
Expect(err).To(BeNil())
Expect(network1.Driver).To(Equal("macvlan"))
Expect(network1.IPAMOptions).ToNot(BeEmpty())
Expect(network1.IPAMOptions).To(HaveKeyWithValue("driver", "none"))
Expect(network1.Subnets).To(HaveLen(0))

// reload configs from disk
libpodNet, err = getNetworkInterface(networkConfDir)
Expect(err).To(BeNil())

network2, err := libpodNet.NetworkInspect(network1.Name)
Expect(err).To(BeNil())
EqualNetwork(network2, network1)
})

})

Context("network load valid existing ones", func() {
Expand Down
4 changes: 2 additions & 2 deletions libnetwork/netavark/ipam_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -399,10 +399,10 @@ var _ = Describe("IPAM", func() {
}
})

It("ipam with dhcp driver should not set ips", func() {
It("ipam with none driver should not set ips", func() {
network, err := networkInterface.NetworkCreate(types.Network{
IPAMOptions: map[string]string{
"driver": types.DHCPIPAMDriver,
"driver": types.NoneIPAMDriver,
},
})
Expect(err).ToNot(HaveOccurred())
Expand Down
83 changes: 83 additions & 0 deletions libnetwork/netavark/run_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -700,6 +700,89 @@ var _ = Describe("run netavark", func() {
Expect(err.Error()).To(ContainSubstring("interface eth0 already exists on container namespace"))
})
})

It("setup ipam driver none network", func() {
runTest(func() {
network := types.Network{
IPAMOptions: map[string]string{
types.Driver: types.NoneIPAMDriver,
},
}
network1, err := libpodNet.NetworkCreate(network)
Expect(err).To(BeNil())

intName1 := "eth0"
netName1 := network1.Name

setupOpts := types.SetupOptions{
NetworkOptions: types.NetworkOptions{
ContainerID: stringid.GenerateNonCryptoID(),
Networks: map[string]types.PerNetworkOptions{
netName1: {
InterfaceName: intName1,
},
},
},
}

res, err := libpodNet.Setup(netNSContainer.Path(), setupOpts)
Expect(err).To(BeNil())
Expect(res).To(HaveLen(1))

Expect(res).To(HaveKey(netName1))
Expect(res[netName1].Interfaces).To(HaveKey(intName1))
Expect(res[netName1].Interfaces[intName1].Subnets).To(HaveLen(0))
macInt1 := res[netName1].Interfaces[intName1].MacAddress
Expect(macInt1).To(HaveLen(6))

// check in the container namespace if the settings are applied
err = netNSContainer.Do(func(_ ns.NetNS) error {
defer GinkgoRecover()
i, err := net.InterfaceByName(intName1)
Expect(err).To(BeNil())
Expect(i.Name).To(Equal(intName1))
Expect(i.HardwareAddr).To(Equal(net.HardwareAddr(macInt1)))
addrs, err := i.Addrs()
Expect(err).To(BeNil())
// we still have the ipv6 link local address
Expect(addrs).To(HaveLen(1))
addr, ok := addrs[0].(*net.IPNet)
Expect(ok).To(BeTrue(), "cast address to ipnet")
// make sure we are link local
Expect(addr.IP.IsLinkLocalUnicast()).To(BeTrue(), "ip is link local address")

// check loopback adapter
i, err = net.InterfaceByName("lo")
Expect(err).To(BeNil())
Expect(i.Name).To(Equal("lo"))
Expect(i.Flags & net.FlagLoopback).To(Equal(net.FlagLoopback))
Expect(i.Flags&net.FlagUp).To(Equal(net.FlagUp), "Loopback adapter should be up")
return nil
})
Expect(err).To(BeNil())

err = libpodNet.Teardown(netNSContainer.Path(), types.TeardownOptions(setupOpts))
Expect(err).To(BeNil())

// check in the container namespace that the interface is removed
err = netNSContainer.Do(func(_ ns.NetNS) error {
defer GinkgoRecover()
_, err := net.InterfaceByName(intName1)
Expect(err).To(HaveOccurred())

// check that only the loopback adapter is left
ints, err := net.Interfaces()
Expect(err).To(BeNil())
Expect(ints).To(HaveLen(1))
Expect(ints[0].Name).To(Equal("lo"))
Expect(ints[0].Flags & net.FlagLoopback).To(Equal(net.FlagLoopback))
Expect(ints[0].Flags&net.FlagUp).To(Equal(net.FlagUp), "Loopback adapter should be up")

return nil
})
Expect(err).To(BeNil())
})
})
})

func runNetListener(wg *sync.WaitGroup, protocol, ip string, port int, expectedData string) {
Expand Down

0 comments on commit 8a9a59b

Please sign in to comment.