Automatic listen addresses allocation (OVN load-balancers and network forwards)

Project LXD
Status Drafting
Author(s) @markylaing
Approver(s) @tomp @mionaalex @egelinas @maria-seralessandri
Release 6.1
Internal ID LX074

Abstract

This specification describes why automatic allocation of listen addresses for OVN load-balancers and network forwards is required for LXD, and outlines changes to the CLI and API handlers that will be made for the implementation.

Rationale

When creating a load-balancer or network forward in an OVN network, the listen address of the load-balancer of the network forward must be specified. Networks that are defined in the default project are shared with projects that do not have features.networks enabled. This means that when creating a network forward or load-balancer in the restricted project, the user cannot see the external addresses that are already in use, and may encounter an error that is not their fault.

Rather than exposing a list of addresses that are already in use by the network, LXD can select an address from the pool of available addresses.

Specification

The following API handlers will be modified:

  • POST /1.0/networks/{networkName}/forwards
  • POST /1.0/networks/{networkName}/load-balancers

The listen_address field in the request body will now be allowed to be an unspecified IPv4 or IPv6 address (i.e. 0.0.0.0 or ::).

On creation of a load balancer or network forward, the OVN network driver will ascertain whether IP addresses should be automatically allocated by checking if the listen address is unspecified. If it is unspecified, the version of the allocated IP is determined by the IP version of the unspecified listen address.

To determine an appropriate IP address, the OVN network driver will:

  1. Collate subnets from restricted.networks.subnets, if set on the parent project. Otherwise, the subnets in ipv{4,6}.routes will be used.
  2. Re-order those subnets so that the order by which the subnet is chosen is random.
  3. Iterate over the re-ordered subnets, picking a random IP address until one is found that is not already in use.

The process for selecting a random valid IP address will time out after 5 seconds. If one is not found, an error will be returned to the user indicating that manual configuration may be required.

Future work

If two load balancer or network forward creation requests are received concurrently, it is possible for the same address to be selected by the OVN driver. However, one request will ultimately fail when OVN attempts to create the resource. In this case, all previous logic is appropriately reverted. It may be possible to prevent this from occurring by introducing a cluster-wide lock. In the meantime, the initial implementation reduces this risk by opting to use a random IP address in the range, rather than the next available IP.

API changes

None

CLI changes

The following commands will change:

  • lxc network forward create <network-name> <listen-address>
  • lxc network load-balancer create <network-name> <listen-address>

In both cases, the <listen-address> argument will be made optional. A new --allocate flag will be introduced which will accept two values: ipv4 or ipv6. If the --allocate=ipv4 is provided, the CLI will use an unspecified IPv4 address as the listen address (and vice versa for IPv6).

Database changes

None

Upgrade handling

None

Further information

1 Like

Thanks for this, it looks good to me. A cluster lock or some kind of serialization for selected API calls would be a nice feature.

What is the reason to do this?

So you went with the random approach in the end? the other day you mentioned you were going with the ordered approach. What changed?

. In the meantime, the initial implementation reduces this risk by opting to use a random IP address in the range, rather than the next available IP.

Is it because of this?

How about --listen-auto=ipv{4,6}?

This would avoid having 2 flags and indicate the auto mode is for the picking the listen address, which is not entirely clear with --auto-ipvN flags IMHO.

Or could we have the actual listen address argument take free-ipv4 or free-ipv6 as values rather than flags?

@tomp yes I investigated locking, I didn’t want to do a botch job specific to this feature and think it in itself needs more thought.

If we pick the next available IP address it will:

  1. Potentially expose other IP addresses that are in use (e.g. the caller can infer that the previous IP to the one they were assigned is in use).
  2. Definitely cause a collision for two requests are sent at the same time.

Re-ordering the subnets is so that we don’t always pick from the first subnet in ipv{4,6}.routes. E.g. if ipv4.routes=10.0.123.0/24,192.168.18.0.24,232.45.18.0/16, then we re-order that list of subnets before sequentially attempting to find IP addresses in each.

1 Like

I’m happy to change it to --listen-auto=ipv{4,6}. I agree this makes it clearer that we’re talking about the listen address.

I went for two flags because I felt that the argument format ipv{4,6} might be less intuitive than having separate flags, because the flags are self-documenting.

mmm, although separate flags also suggests you can use both.

How about?

lxc network forward create <network> [<listen_address>] --allocate=ipv4

or

lxc network forward create <network> [<listen_address>] --request=ipv4

The word allocate also suggests that you are reserving the IP (which you are).

Yep I like this. I’ll make the change :slight_smile: