Skip to content

Commit

Permalink
Add subnet overlap check for POD and join subnets in net-attach-def
Browse files Browse the repository at this point in the history
This is to validate whether POD and join subnet mentioned in a
net-attach-def with topology "layer2" and "layer3" does not overlap
with ClusterSubnets, ServiceCIDRs, join subnet and masquerade subnet.
It also considers excluded subnets mentioned in a net-attach-def.

Signed-off-by: Arnab Ghosh <arnabghosh89@gmail.com>
  • Loading branch information
arghosh93 committed Sep 2, 2024
1 parent 05ccb99 commit 5115b72
Show file tree
Hide file tree
Showing 5 changed files with 239 additions and 63 deletions.
32 changes: 16 additions & 16 deletions go-controller/pkg/config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -1752,15 +1752,15 @@ func buildKubernetesConfig(exec kexec.Interface, cli, file *config, saPath strin

// completeKubernetesConfig completes the Kubernetes config by parsing raw values
// into their final form.
func completeKubernetesConfig(allSubnets *configSubnets) error {
func completeKubernetesConfig(allSubnets *ConfigSubnets) error {
Kubernetes.ServiceCIDRs = []*net.IPNet{}
for _, cidrString := range strings.Split(Kubernetes.RawServiceCIDRs, ",") {
_, serviceCIDR, err := net.ParseCIDR(cidrString)
if err != nil {
return fmt.Errorf("kubernetes service network CIDR %q invalid: %v", cidrString, err)
}
Kubernetes.ServiceCIDRs = append(Kubernetes.ServiceCIDRs, serviceCIDR)
allSubnets.append(configSubnetService, serviceCIDR)
allSubnets.Append(ConfigSubnetService, serviceCIDR)
}
if len(Kubernetes.ServiceCIDRs) > 2 {
return fmt.Errorf("kubernetes service-cidrs must contain either a single CIDR or else an IPv4/IPv6 pair")
Expand Down Expand Up @@ -1858,7 +1858,7 @@ func buildGatewayConfig(ctx *cli.Context, cli, file *config) error {
return nil
}

func completeGatewayConfig(allSubnets *configSubnets, masqueradeIPs *MasqueradeIPsConfig) error {
func completeGatewayConfig(allSubnets *ConfigSubnets, masqueradeIPs *MasqueradeIPsConfig) error {
// Validate v4 and v6 join subnets
v4IP, v4JoinCIDR, err := net.ParseCIDR(Gateway.V4JoinSubnet)
if err != nil || utilnet.IsIPv6(v4IP) {
Expand All @@ -1869,8 +1869,8 @@ func completeGatewayConfig(allSubnets *configSubnets, masqueradeIPs *MasqueradeI
if err != nil || !utilnet.IsIPv6(v6IP) {
return fmt.Errorf("invalid gateway v6 join subnet specified, subnet: %s: error: %v", Gateway.V6JoinSubnet, err)
}
allSubnets.append(configSubnetJoin, v4JoinCIDR)
allSubnets.append(configSubnetJoin, v6JoinCIDR)
allSubnets.Append(ConfigSubnetJoin, v4JoinCIDR)
allSubnets.Append(ConfigSubnetJoin, v6JoinCIDR)

//validate v4 and v6 masquerade subnets
v4MasqueradeIP, v4MasqueradeCIDR, err := net.ParseCIDR(Gateway.V4MasqueradeSubnet)
Expand All @@ -1889,8 +1889,8 @@ func completeGatewayConfig(allSubnets *configSubnets, masqueradeIPs *MasqueradeI
return fmt.Errorf("unable to allocate V6MasqueradeIPs: %s", err)
}

allSubnets.append(configSubnetMasquerade, v4MasqueradeCIDR)
allSubnets.append(configSubnetMasquerade, v6MasqueradeCIDR)
allSubnets.Append(ConfigSubnetMasquerade, v4MasqueradeCIDR)
allSubnets.Append(ConfigSubnetMasquerade, v6MasqueradeCIDR)

return nil
}
Expand Down Expand Up @@ -2020,7 +2020,7 @@ func buildHybridOverlayConfig(ctx *cli.Context, cli, file *config) error {

// completeHybridOverlayConfig completes the HybridOverlay config by parsing raw values
// into their final form.
func completeHybridOverlayConfig(allSubnets *configSubnets) error {
func completeHybridOverlayConfig(allSubnets *ConfigSubnets) error {
if !HybridOverlay.Enabled || len(HybridOverlay.RawClusterSubnets) == 0 {
return nil
}
Expand All @@ -2031,7 +2031,7 @@ func completeHybridOverlayConfig(allSubnets *configSubnets) error {
return fmt.Errorf("hybrid overlay cluster subnet invalid: %v", err)
}
for _, subnet := range HybridOverlay.ClusterSubnets {
allSubnets.append(configSubnetHybrid, subnet.CIDR)
allSubnets.Append(ConfigSubnetHybrid, subnet.CIDR)
}

return nil
Expand All @@ -2053,7 +2053,7 @@ func buildClusterManagerConfig(ctx *cli.Context, cli, file *config) error {

// completeClusterManagerConfig completes the ClusterManager config by parsing raw values
// into their final form.
func completeClusterManagerConfig(allSubnets *configSubnets) error {
func completeClusterManagerConfig(allSubnets *ConfigSubnets) error {
// Validate v4 and v6 transit switch subnets
v4IP, v4TransitCIDR, err := net.ParseCIDR(ClusterManager.V4TransitSwitchSubnet)
if err != nil || utilnet.IsIPv6(v4IP) {
Expand All @@ -2064,8 +2064,8 @@ func completeClusterManagerConfig(allSubnets *configSubnets) error {
if err != nil || !utilnet.IsIPv6(v6IP) {
return fmt.Errorf("invalid transit switch v6 subnet specified, subnet: %s: error: %v", ClusterManager.V6TransitSwitchSubnet, err)
}
allSubnets.append(configSubnetTransit, v4TransitCIDR)
allSubnets.append(configSubnetTransit, v6TransitCIDR)
allSubnets.Append(ConfigSubnetTransit, v4TransitCIDR)
allSubnets.Append(ConfigSubnetTransit, v6TransitCIDR)
return nil
}

Expand Down Expand Up @@ -2094,14 +2094,14 @@ func buildDefaultConfig(cli, file *config) error {

// completeDefaultConfig completes the Default config by parsing raw values
// into their final form.
func completeDefaultConfig(allSubnets *configSubnets) error {
func completeDefaultConfig(allSubnets *ConfigSubnets) error {
var err error
Default.ClusterSubnets, err = ParseClusterSubnetEntries(Default.RawClusterSubnets)
if err != nil {
return fmt.Errorf("cluster subnet invalid: %v", err)
}
for _, subnet := range Default.ClusterSubnets {
allSubnets.append(configSubnetCluster, subnet.CIDR)
allSubnets.Append(ConfigSubnetCluster, subnet.CIDR)
}

Default.HostMasqConntrackZone = Default.ConntrackZone + 1
Expand Down Expand Up @@ -2326,7 +2326,7 @@ func initConfigWithPath(ctx *cli.Context, exec kexec.Interface, saPath string, d
}

func completeConfig() error {
allSubnets := newConfigSubnets()
allSubnets := NewConfigSubnets()

if err := completeKubernetesConfig(allSubnets); err != nil {
return err
Expand All @@ -2349,7 +2349,7 @@ func completeConfig() error {
return err
}

if err := allSubnets.checkForOverlaps(); err != nil {
if err := allSubnets.CheckForOverlaps(); err != nil {
return err
}

Expand Down
88 changes: 45 additions & 43 deletions go-controller/pkg/config/utils.go
Original file line number Diff line number Diff line change
Expand Up @@ -162,67 +162,69 @@ func ParseFlowCollectors(flowCollectors string) ([]HostPort, error) {
return parsedFlowsCollectors, nil
}

type configSubnetType string
type ConfigSubnetType string

const (
configSubnetJoin configSubnetType = "built-in join subnet"
configSubnetCluster configSubnetType = "cluster subnet"
configSubnetService configSubnetType = "service subnet"
configSubnetHybrid configSubnetType = "hybrid overlay subnet"
configSubnetMasquerade configSubnetType = "masquerade subnet"
configSubnetTransit configSubnetType = "transit switch subnet"
ConfigSubnetJoin ConfigSubnetType = "built-in join subnet"
ConfigSubnetCluster ConfigSubnetType = "cluster subnet"
ConfigSubnetService ConfigSubnetType = "service subnet"
ConfigSubnetHybrid ConfigSubnetType = "hybrid overlay subnet"
ConfigSubnetMasquerade ConfigSubnetType = "masquerade subnet"
ConfigSubnetTransit ConfigSubnetType = "transit switch subnet"
UserDefinedSubnets ConfigSubnetType = "user defined subnet"
UserDefinedJoinSubnet ConfigSubnetType = "user defined join subnet"
)

type configSubnet struct {
subnetType configSubnetType
subnet *net.IPNet
type ConfigSubnet struct {
SubnetType ConfigSubnetType
Subnet *net.IPNet
}

// configSubnets represents a set of configured subnets (and their names)
type configSubnets struct {
subnets []configSubnet
v4 map[configSubnetType]bool
v6 map[configSubnetType]bool
// ConfigSubnets represents a set of configured subnets (and their names)
type ConfigSubnets struct {
Subnets []ConfigSubnet
V4 map[ConfigSubnetType]bool
V6 map[ConfigSubnetType]bool
}

// newConfigSubnets returns a new configSubnets
func newConfigSubnets() *configSubnets {
return &configSubnets{
v4: make(map[configSubnetType]bool),
v6: make(map[configSubnetType]bool),
// NewConfigSubnets returns a new ConfigSubnets
func NewConfigSubnets() *ConfigSubnets {
return &ConfigSubnets{
V4: make(map[ConfigSubnetType]bool),
V6: make(map[ConfigSubnetType]bool),
}
}

// append adds a single subnet to cs
func (cs *configSubnets) append(subnetType configSubnetType, subnet *net.IPNet) {
cs.subnets = append(cs.subnets, configSubnet{subnetType: subnetType, subnet: subnet})
if subnetType != configSubnetJoin && subnetType != configSubnetMasquerade && subnetType != configSubnetTransit {
func (cs *ConfigSubnets) Append(subnetType ConfigSubnetType, subnet *net.IPNet) {
cs.Subnets = append(cs.Subnets, ConfigSubnet{SubnetType: subnetType, Subnet: subnet})
if subnetType == ConfigSubnetCluster || subnetType == ConfigSubnetService || subnetType == ConfigSubnetHybrid {
if utilnet.IsIPv6CIDR(subnet) {
cs.v6[subnetType] = true
cs.V6[subnetType] = true
} else {
cs.v4[subnetType] = true
cs.V4[subnetType] = true
}
}
}

// checkForOverlaps checks if any of the subnets in cs overlap
func (cs *configSubnets) checkForOverlaps() error {
for i, si := range cs.subnets {
// CheckForOverlaps checks if any of the subnets in cs overlap
func (cs *ConfigSubnets) CheckForOverlaps() error {
for i, si := range cs.Subnets {
for j := 0; j < i; j++ {
sj := cs.subnets[j]
if si.subnet.Contains(sj.subnet.IP) || sj.subnet.Contains(si.subnet.IP) {
sj := cs.Subnets[j]
if si.Subnet.Contains(sj.Subnet.IP) || sj.Subnet.Contains(si.Subnet.IP) {
return fmt.Errorf("illegal network configuration: %s %q overlaps %s %q",
si.subnetType, si.subnet.String(),
sj.subnetType, sj.subnet.String())
si.SubnetType, si.Subnet.String(),
sj.SubnetType, sj.Subnet.String())
}
}
}
return nil
}

func (cs *configSubnets) describeSubnetType(subnetType configSubnetType) string {
ipv4 := cs.v4[subnetType]
ipv6 := cs.v6[subnetType]
func (cs *ConfigSubnets) describeSubnetType(subnetType ConfigSubnetType) string {
ipv4 := cs.V4[subnetType]
ipv6 := cs.V6[subnetType]
var familyType string
switch {
case ipv4 && !ipv6:
Expand All @@ -240,22 +242,22 @@ func (cs *configSubnets) describeSubnetType(subnetType configSubnetType) string
// checkIPFamilies determines if cs contains a valid single-stack IPv4 configuration, a
// valid single-stack IPv6 configuration, a valid dual-stack configuration, or none of the
// above.
func (cs *configSubnets) checkIPFamilies() (usingIPv4, usingIPv6 bool, err error) {
if len(cs.v6) == 0 {
func (cs *ConfigSubnets) checkIPFamilies() (usingIPv4, usingIPv6 bool, err error) {
if len(cs.V6) == 0 {
// Single-stack IPv4
return true, false, nil
} else if len(cs.v4) == 0 {
} else if len(cs.V4) == 0 {
// Single-stack IPv6
return false, true, nil
} else if reflect.DeepEqual(cs.v4, cs.v6) {
} else if reflect.DeepEqual(cs.V4, cs.V6) {
// Dual-stack
return true, true, nil
}

netConfig := cs.describeSubnetType(configSubnetCluster)
netConfig += ", " + cs.describeSubnetType(configSubnetService)
if cs.v4[configSubnetHybrid] || cs.v6[configSubnetHybrid] {
netConfig += ", " + cs.describeSubnetType(configSubnetHybrid)
netConfig := cs.describeSubnetType(ConfigSubnetCluster)
netConfig += ", " + cs.describeSubnetType(ConfigSubnetService)
if cs.V4[ConfigSubnetHybrid] || cs.V6[ConfigSubnetHybrid] {
netConfig += ", " + cs.describeSubnetType(ConfigSubnetHybrid)
}

return false, false, fmt.Errorf("illegal network configuration: %s", netConfig)
Expand Down
8 changes: 4 additions & 4 deletions go-controller/pkg/config/utils_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -300,15 +300,15 @@ func Test_checkForOverlap(t *testing.T) {
}

for _, tc := range tests {
allSubnets := newConfigSubnets()
allSubnets := NewConfigSubnets()
for _, joinSubnet := range tc.joinSubnetCIDRList {
allSubnets.append(configSubnetJoin, joinSubnet)
allSubnets.Append(ConfigSubnetJoin, joinSubnet)
}
for _, subnet := range tc.cidrList {
allSubnets.append(configSubnetCluster, subnet)
allSubnets.Append(ConfigSubnetCluster, subnet)
}

err := allSubnets.checkForOverlaps()
err := allSubnets.CheckForOverlaps()
if err == nil && tc.shouldError {
t.Errorf("testcase \"%s\" failed to find overlap", tc.name)
} else if err != nil && !tc.shouldError {
Expand Down
55 changes: 55 additions & 0 deletions go-controller/pkg/util/multi_network.go
Original file line number Diff line number Diff line change
Expand Up @@ -761,6 +761,61 @@ func ValidateNetConf(nadName string, netconf *ovncnitypes.NetConf) error {
return fmt.Errorf("the subnet attribute must be defined for layer2 primary user defined networks")
}

if netconf.Topology != types.LocalnetTopology && netconf.Name != types.DefaultNetworkName {
if err := subnetOverlapCheck(netconf); err != nil {
return fmt.Errorf("invalid subnet cnfiguration: %w", err)
}
}

return nil
}

// subnetOverlapCheck validates whether POD and join subnet mentioned in a net-attach-def with
// topology "layer2" and "layer3" does not overlap with ClusterSubnets, ServiceCIDRs, join subnet,
// and masquerade subnet. It also considers excluded subnets mentioned in a net-attach-def.
func subnetOverlapCheck(netconf *ovncnitypes.NetConf) error {
allSubnets := config.NewConfigSubnets()
for _, subnet := range config.Default.ClusterSubnets {
allSubnets.Append(config.ConfigSubnetCluster, subnet.CIDR)
}
for _, subnet := range config.Kubernetes.ServiceCIDRs {
allSubnets.Append(config.ConfigSubnetService, subnet)
}
_, v4JoinCIDR, _ := net.ParseCIDR(config.Gateway.V4JoinSubnet)
_, v6JoinCIDR, _ := net.ParseCIDR(config.Gateway.V6JoinSubnet)

allSubnets.Append(config.ConfigSubnetJoin, v4JoinCIDR)
allSubnets.Append(config.ConfigSubnetJoin, v6JoinCIDR)

_, v4MasqueradeCIDR, _ := net.ParseCIDR(config.Gateway.V4MasqueradeSubnet)
_, v6MasqueradeCIDR, _ := net.ParseCIDR(config.Gateway.V6MasqueradeSubnet)

allSubnets.Append(config.ConfigSubnetMasquerade, v4MasqueradeCIDR)
allSubnets.Append(config.ConfigSubnetMasquerade, v6MasqueradeCIDR)

ni, err := NewNetInfo(netconf)
if err != nil {
return fmt.Errorf("error while parsing subnets: %v", err)
}
for _, subnet := range ni.Subnets() {
allSubnets.Append(config.UserDefinedSubnets, subnet.CIDR)
}

for _, subnet := range ni.JoinSubnets() {
allSubnets.Append(config.UserDefinedJoinSubnet, subnet)
}
if ni.ExcludeSubnets() != nil {
for i, configSubnet := range allSubnets.Subnets {
if IsContainedInAnyCIDR(configSubnet.Subnet, ni.ExcludeSubnets()...) {
allSubnets.Subnets = append(allSubnets.Subnets[:i], allSubnets.Subnets[i+1:]...)
}
}
}
err = allSubnets.CheckForOverlaps()
if err != nil {
return fmt.Errorf("pod or join subnet overlaps with already configured internal subnets: %v", err)
}

return nil
}

Expand Down
Loading

0 comments on commit 5115b72

Please sign in to comment.