The following pull request was submitted through Github. It can be accessed and reviewed at: https://github.com/lxc/lxd/pull/8095
This e-mail was sent by the LXC bot, direct replies will not reach the author unless they happen to be subscribed to this list. === Description (from pull-request) ===
From f899806b77a562de10764e8f52afb0ac1d6d52fd Mon Sep 17 00:00:00 2001 From: Thomas Parrott <thomas.parr...@canonical.com> Date: Thu, 29 Oct 2020 11:03:01 +0000 Subject: [PATCH 1/5] lxd/network/driver/ovn: Adds ovnProjectNetworksWithUplink function Signed-off-by: Thomas Parrott <thomas.parr...@canonical.com> --- lxd/network/driver_ovn.go | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/lxd/network/driver_ovn.go b/lxd/network/driver_ovn.go index 9ce25782e7..0f141d73e2 100644 --- a/lxd/network/driver_ovn.go +++ b/lxd/network/driver_ovn.go @@ -2353,3 +2353,27 @@ func (n *ovn) ovnNICExternalRoutes(ourDeviceInstance instance.Instance, ourDevic return externalRoutes, nil } + +// ovnProjectNetworksWithUplink accepts a map of all networks in all projects and returns a filtered map of OVN +// networks that use the uplink specified. +func (n *ovn) ovnProjectNetworksWithUplink(uplink string, projectNetworks map[string]map[int64]api.Network) map[string][]*api.Network { + ovnProjectNetworksWithOurUplink := make(map[string][]*api.Network) + for netProject, networks := range projectNetworks { + for _, ni := range networks { + network := ni // Local var creating pointer to rather than iterator. + + // Skip non-OVN networks or those networks that don't use the uplink specified. + if network.Type != "ovn" || network.Config["network"] != uplink { + continue + } + + if ovnProjectNetworksWithOurUplink[netProject] == nil { + ovnProjectNetworksWithOurUplink[netProject] = []*api.Network{&network} + } else { + ovnProjectNetworksWithOurUplink[netProject] = append(ovnProjectNetworksWithOurUplink[netProject], &network) + } + } + } + + return ovnProjectNetworksWithOurUplink +} From c6d6045b8f43cf749666eefe99e6c4fa0d36d2e9 Mon Sep 17 00:00:00 2001 From: Thomas Parrott <thomas.parr...@canonical.com> Date: Thu, 29 Oct 2020 11:04:42 +0000 Subject: [PATCH 2/5] lxd/network/driver/ovn: Updates ovnNetworkExternalSubnets to allow optional filtering of our own network's subnets Signed-off-by: Thomas Parrott <thomas.parr...@canonical.com> --- lxd/network/driver_ovn.go | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/lxd/network/driver_ovn.go b/lxd/network/driver_ovn.go index 0f141d73e2..cbfaa1f080 100644 --- a/lxd/network/driver_ovn.go +++ b/lxd/network/driver_ovn.go @@ -2260,13 +2260,18 @@ func (n *ovn) DHCPv6Subnet() *net.IPNet { return subnet } -// ovnNetworkExternalSubnets returns a list of external subnets used by OVN networks (including our own) using the -// same uplink as this OVN network. OVN networks are considered to be using external subnets if their ipv4.address -// and/or ipv6.address are in the uplink's external routes and the associated NAT is disabled for the IP family. -func (n *ovn) ovnNetworkExternalSubnets(ovnProjectNetworksWithOurUplink map[string][]*api.Network, uplinkRoutes []*net.IPNet) ([]*net.IPNet, error) { +// ovnNetworkExternalSubnets returns a list of external subnets used by OVN networks (optionally exluding our own +// if both ourProject and ourNetwork are non-empty) using the same uplink as this OVN network. OVN networks are +// considered to be using external subnets if their ipv4.address and/or ipv6.address are in the uplink's external +// routes and the associated NAT is disabled for the IP family. +func (n *ovn) ovnNetworkExternalSubnets(ourProject string, ourNetwork string, ovnProjectNetworksWithOurUplink map[string][]*api.Network, uplinkRoutes []*net.IPNet) ([]*net.IPNet, error) { externalSubnets := make([]*net.IPNet, 0) - for _, networks := range ovnProjectNetworksWithOurUplink { + for netProject, networks := range ovnProjectNetworksWithOurUplink { for _, netInfo := range networks { + if netProject == ourProject && netInfo.Name == ourNetwork { + continue + } + for _, keyPrefix := range []string{"ipv4", "ipv6"} { if !shared.IsTrue(netInfo.Config[fmt.Sprintf("%s.nat", keyPrefix)]) { _, ipNet, _ := net.ParseCIDR(netInfo.Config[fmt.Sprintf("%s.address", keyPrefix)]) From 7872cdf100bfb56814b20357c8d869da194d6db3 Mon Sep 17 00:00:00 2001 From: Thomas Parrott <thomas.parr...@canonical.com> Date: Thu, 29 Oct 2020 11:05:15 +0000 Subject: [PATCH 3/5] lxd/network/driver/ovn: Updates ovnNICExternalRoutes to optionally filter our own NIC's external routes Signed-off-by: Thomas Parrott <thomas.parr...@canonical.com> --- lxd/network/driver_ovn.go | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/lxd/network/driver_ovn.go b/lxd/network/driver_ovn.go index cbfaa1f080..c44d32547a 100644 --- a/lxd/network/driver_ovn.go +++ b/lxd/network/driver_ovn.go @@ -2295,8 +2295,9 @@ func (n *ovn) ovnNetworkExternalSubnets(ourProject string, ourNetwork string, ov return externalSubnets, nil } -// ovnNICExternalRoutes returns a list of external routes currently used by OVN NICs (excluding our own) that are -// connected to OVN networks that share the same uplink as this network uses. +// ovnNICExternalRoutes returns a list of external routes currently used by OVN NICs (optionally excluding our +// own if both ourDeviceInstance and ourDeviceName are non-empty) that are connected to OVN networks that share +// the same uplink as this network uses. func (n *ovn) ovnNICExternalRoutes(ourDeviceInstance instance.Instance, ourDeviceName string, ovnProjectNetworksWithOurUplink map[string][]*api.Network) ([]*net.IPNet, error) { externalRoutes := make([]*net.IPNet, 0) @@ -2327,8 +2328,8 @@ func (n *ovn) ovnNICExternalRoutes(ourDeviceInstance instance.Instance, ourDevic continue } - // Skip our own device. - if inst.Name == ourDeviceInstance.Name() && inst.Project == ourDeviceInstance.Project() && ourDeviceName == devName { + // Skip our own device (if instance and device name were supplied). + if ourDeviceInstance != nil && ourDeviceName != "" && inst.Name == ourDeviceInstance.Name() && inst.Project == ourDeviceInstance.Project() && ourDeviceName == devName { continue } From f1f9e0a46909c4c88c278d0e76fee077d3eb4df2 Mon Sep 17 00:00:00 2001 From: Thomas Parrott <thomas.parr...@canonical.com> Date: Thu, 29 Oct 2020 11:05:57 +0000 Subject: [PATCH 4/5] lxd/network/driver/ovn: Updates InstanceDevicePortValidateExternalRoutes to use new functions and signatures Signed-off-by: Thomas Parrott <thomas.parr...@canonical.com> --- lxd/network/driver_ovn.go | 33 +++++++-------------------------- 1 file changed, 7 insertions(+), 26 deletions(-) diff --git a/lxd/network/driver_ovn.go b/lxd/network/driver_ovn.go index c44d32547a..59a885595e 100644 --- a/lxd/network/driver_ovn.go +++ b/lxd/network/driver_ovn.go @@ -1901,26 +1901,11 @@ func (n *ovn) InstanceDevicePortValidateExternalRoutes(deviceInstance instance.I return err } - // Work out which OVN networks (in all projects and including our own) use the same uplink as us. - ovnProjectNetworksWithOurUplink := make(map[string][]*api.Network) - for netProject, networks := range projectNetworks { - for _, network := range networks { - netInfo := network // Local var for adding pointer to ovnProjectNetworksWithOurUplink. - // Skip non-OVN networks or those networks that don't use the same uplink as us. - if netInfo.Type != "ovn" || netInfo.Config["network"] != n.config["network"] { - continue - } - - if ovnProjectNetworksWithOurUplink[netProject] == nil { - ovnProjectNetworksWithOurUplink[netProject] = []*api.Network{&netInfo} - } else { - ovnProjectNetworksWithOurUplink[netProject] = append(ovnProjectNetworksWithOurUplink[netProject], &netInfo) - } - } - } + // Get OVN networks that use the same uplink as us. + ovnProjectNetworksWithOurUplink := n.ovnProjectNetworksWithUplink(n.config["network"], projectNetworks) // Get external subnets used by other OVN networks using our uplink. - ovnNetworkExternalSubnets, err := n.ovnNetworkExternalSubnets(ovnProjectNetworksWithOurUplink, uplinkRoutes) + ovnNetworkExternalSubnets, err := n.ovnNetworkExternalSubnets("", "", ovnProjectNetworksWithOurUplink, uplinkRoutes) if err != nil { return err } @@ -1931,14 +1916,10 @@ func (n *ovn) InstanceDevicePortValidateExternalRoutes(deviceInstance instance.I return err } - // If validating with an instance, get external routes configured on OVN NICs (excluding ours) using - // networks that use our uplink. - var ovnNICExternalRoutes []*net.IPNet - if deviceInstance != nil { - ovnNICExternalRoutes, err = n.ovnNICExternalRoutes(deviceInstance, deviceName, ovnProjectNetworksWithOurUplink) - if err != nil { - return err - } + // Get external routes configured on OVN NICs (excluding ours) using networks that use our uplink. + ovnNICExternalRoutes, err := n.ovnNICExternalRoutes(deviceInstance, deviceName, ovnProjectNetworksWithOurUplink) + if err != nil { + return err } for _, portExternalRoute := range portExternalRoutes { From 5a53d16c5db5ef98c3fe13682ca50ee0c9caf810 Mon Sep 17 00:00:00 2001 From: Thomas Parrott <thomas.parr...@canonical.com> Date: Thu, 29 Oct 2020 11:03:34 +0000 Subject: [PATCH 5/5] lxd/network/driver/ovn: Updates Validate to check external subnets dont overlap with other OVN networks or NICs sharing our uplink Signed-off-by: Thomas Parrott <thomas.parr...@canonical.com> --- lxd/network/driver_ovn.go | 58 +++++++++++++++++++++++++++++++++++++-- 1 file changed, 56 insertions(+), 2 deletions(-) diff --git a/lxd/network/driver_ovn.go b/lxd/network/driver_ovn.go index 59a885595e..de2f32024f 100644 --- a/lxd/network/driver_ovn.go +++ b/lxd/network/driver_ovn.go @@ -230,7 +230,8 @@ func (n *ovn) Validate(config map[string]string) error { return err } - // If NAT disabled, check subnets are within the uplink network's routes and project's subnet restrictions. + // If NAT disabled, parse the external subnets that are being requested. + var externalSubnets []*net.IPNet for _, keyPrefix := range []string{"ipv4", "ipv6"} { if !shared.IsTrue(config[fmt.Sprintf("%s.nat", keyPrefix)]) && validate.IsOneOf(config[fmt.Sprintf("%s.address", keyPrefix)], []string{"", "none", "auto"}) != nil { _, ipNet, err := net.ParseCIDR(config[fmt.Sprintf("%s.address", keyPrefix)]) @@ -238,10 +239,63 @@ func (n *ovn) Validate(config map[string]string) error { return err } - err = n.validateExternalSubnet(uplinkRoutes, projectRestrictedSubnets, ipNet) + externalSubnets = append(externalSubnets, ipNet) + } + } + + if len(externalSubnets) > 0 { + var projectNetworks map[string]map[int64]api.Network + err = n.state.Cluster.Transaction(func(tx *db.ClusterTx) error { + // Get all managed networks across all projects. + projectNetworks, err = tx.GetNonPendingNetworks() + if err != nil { + return errors.Wrapf(err, "Failed to load all networks") + } + + return nil + }) + if err != nil { + return err + } + + // Get OVN networks that use the same uplink as us. + ovnProjectNetworksWithOurUplink := n.ovnProjectNetworksWithUplink(config["network"], projectNetworks) + + // Get external subnets used by other OVN networks using our uplink. + ovnNetworkExternalSubnets, err := n.ovnNetworkExternalSubnets(n.project, n.name, ovnProjectNetworksWithOurUplink, uplinkRoutes) + if err != nil { + return err + } + + // Get external routes configured on OVN NICs using networks that use our uplink. + ovnNICExternalRoutes, err := n.ovnNICExternalRoutes(nil, "", ovnProjectNetworksWithOurUplink) + if err != nil { + return err + } + + // Check external subnets are within the uplink network's routes and project's subnet restrictions. + for _, externalSubnet := range externalSubnets { + err = n.validateExternalSubnet(uplinkRoutes, projectRestrictedSubnets, externalSubnet) if err != nil { return err } + + // Check the external port route doesn't fall within any existing OVN network external subnets. + for _, ovnNetworkExternalSubnet := range ovnNetworkExternalSubnets { + if SubnetContains(ovnNetworkExternalSubnet, externalSubnet) || SubnetContains(externalSubnet, ovnNetworkExternalSubnet) { + // This error is purposefully vague so that it doesn't reveal any names of + // resources potentially outside of the network's project. + return fmt.Errorf("External subnet %q overlaps with another OVN network's external subnet", externalSubnet.String()) + } + } + + for _, ovnNICExternalRoute := range ovnNICExternalRoutes { + if SubnetContains(ovnNICExternalRoute, externalSubnet) || SubnetContains(externalSubnet, ovnNICExternalRoute) { + // This error is purposefully vague so that it doesn't reveal any names of + // resources potentially outside of the networks's project. + return fmt.Errorf("External subnet %q overlaps with another OVN NIC's external route", externalSubnet.String()) + } + } } }
_______________________________________________ lxc-devel mailing list lxc-devel@lists.linuxcontainers.org http://lists.linuxcontainers.org/listinfo/lxc-devel