diff --git a/src/core/common/netutil/netutil.go b/src/core/common/netutil/netutil.go index c35121cd..6b321574 100644 --- a/src/core/common/netutil/netutil.go +++ b/src/core/common/netutil/netutil.go @@ -40,7 +40,7 @@ func init() { // Models type NetworkConfig struct { - BaseNetwork Network `json:"baseNetwork"` + NetworkConfiguration Network `json:"networkConfiguration"` } // NetworkInterface defines the methods that both Network and NetworkDetails should implement. @@ -302,3 +302,54 @@ func GetSizeOfHosts(cidrBlock string) (int, error) { return CalculateHostCapacity(ipNet) } + +// /////////////////////////////////////////////////////////////////// +// ValidateNetwork recursively validates the network and its subnets. +func ValidateNetwork(network Network) error { + // Check if the CIDR block is valid + if _, _, err := net.ParseCIDR(network.CIDRBlock); err != nil { + return fmt.Errorf("invalid CIDR block '%s': %w", network.CIDRBlock, err) + } + + // Check for overlapping subnets within the same network + if err := hasOverlappingSubnets(network.Subnets); err != nil { + return fmt.Errorf("in network '%s': %w", network.CIDRBlock, err) + } + + // Recursively validate each subnet + for _, subnet := range network.Subnets { + if !isSubnetOf(network.CIDRBlock, subnet.CIDRBlock) { + return fmt.Errorf("subnet '%s' is not a valid subnet of '%s'", subnet.CIDRBlock, network.CIDRBlock) + } + if err := ValidateNetwork(subnet); err != nil { + return err + } + } + return nil +} + +// isSubnetOf checks if childCIDR is a subnet of parentCIDR. +func isSubnetOf(parentCIDR, childCIDR string) bool { + _, parentNet, _ := net.ParseCIDR(parentCIDR) + _, childNet, _ := net.ParseCIDR(childCIDR) + return parentNet.Contains(childNet.IP) +} + +// hasOverlappingSubnets checks if there are overlapping subnets within the same network. +func hasOverlappingSubnets(subnets []Network) error { + for i := 0; i < len(subnets); i++ { + for j := i + 1; j < len(subnets); j++ { + if cidrOverlap(subnets[i].CIDRBlock, subnets[j].CIDRBlock) { + return fmt.Errorf("overlapping subnets found: '%s' and '%s'", subnets[i].CIDRBlock, subnets[j].CIDRBlock) + } + } + } + return nil +} + +// cidrOverlap checks if two CIDR blocks overlap. +func cidrOverlap(cidr1, cidr2 string) bool { + _, net1, _ := net.ParseCIDR(cidr1) + _, net2, _ := net.ParseCIDR(cidr2) + return net1.Contains(net2.IP) || net2.Contains(net1.IP) +} diff --git a/src/examples/netutil/netutil.go b/src/examples/netutil/netutil.go index 6b1ba980..4ccf8d6b 100644 --- a/src/examples/netutil/netutil.go +++ b/src/examples/netutil/netutil.go @@ -128,55 +128,15 @@ func runExample(cmd *cobra.Command, args []string) { fmt.Printf(" GetSubnets(): %v\n", networkDetails.GetSubnets()) /////////////////////////////////////////////////////////////////////////////////////////////////// - fmt.Println("\n(Under development) Network template example") - // Define the base network - baseNetwork := netutil.Network{ - CIDRBlock: "10.0.0.0/16", - Subnets: []netutil.Network{ - { - // Define a VPC Network - CIDRBlock: "10.0.1.0/24", - Subnets: []netutil.Network{ - { - // Define a Subnetwork within the VPC - CIDRBlock: "10.0.1.0/28", - }, - { - // Another Subnetwork within the VPC - CIDRBlock: "10.0.1.16/28", - }, - }, - }, - { - // Another VPC Network - CIDRBlock: "10.0.2.0/24", - Subnets: []netutil.Network{ - { - // Subnetwork within the second VPC - CIDRBlock: "10.0.2.0/28", - }, - }, - }, - }, - } - - fmt.Println("Base Network CIDR:", baseNetwork.CIDRBlock) - for i, vpc := range baseNetwork.Subnets { - fmt.Printf("VPC Network %d CIDR: %s\n", i+1, vpc.CIDRBlock) - for j, subnet := range vpc.Subnets { - fmt.Printf("\tSubnetwork %d CIDR: %s\n", j+1, subnet.CIDRBlock) - } - } + fmt.Println("\nValidate a network configuration") - /////////////////////////////////////////////////////////////////////////////////////////////////// - fmt.Println("\n(Under development) Design multi-cloud network") - jsonData := `{ - "baseNetwork": { - "name": "BaseNetwork1", + expectedInput := `{ + "networkConfiguration": { + "name": "BaseNetwork (note - a CIDR block of logical global mutli-cloud network)", "cidrBlock": "10.0.0.0/16", "subnets": [ { - "name": "CloudNetwork1", + "name": "CloudNetwork1 (note - a CIDR block to be assigned to cloud network such as VPC network)", "cidrBlock": "10.0.1.0/24", "subnets": [ {"name": "Subnet1", "cidrBlock": "10.0.1.0/26"}, @@ -184,20 +144,39 @@ func runExample(cmd *cobra.Command, args []string) { {"name": "Subnet3", "cidrBlock": "10.0.1.128/26"}, {"name": "Subnet4", "cidrBlock": "10.0.1.192/26"} ] + }, + { + "name": "CloudNetwork2 (note - a CIDR block to be assigned to cloud network such as VPC network)", + "cidrBlock": "10.0.2.0/24", + "subnets": [ + {"name": "Subnet1", "cidrBlock": "10.0.2.0/26"}, + {"name": "Subnet2", "cidrBlock": "10.0.2.64/26"}, + {"name": "Subnet3", "cidrBlock": "10.0.2.128/26"}, + {"name": "Subnet4", "cidrBlock": "10.0.2.192/26"} + ] } ] } }` + fmt.Printf("[Expected input]\n%s\n", expectedInput) - var config netutil.NetworkConfig - err = json.Unmarshal([]byte(jsonData), &config) + var netConf netutil.NetworkConfig + err = json.Unmarshal([]byte(expectedInput), &netConf) if err != nil { - log.Fatalf("Error occurred during unmarshaling. Error: %s", err.Error()) + fmt.Printf("Error occurred during unmarshaling. Error: %s\n", err.Error()) } - prettyConfig, err := json.MarshalIndent(config, "", " ") + network := netConf.NetworkConfiguration + pretty, err := json.MarshalIndent(network, "", " ") if err != nil { - log.Fatalf("marshaling error: %s", err) + fmt.Printf("marshaling error: %s\n", err) + } + fmt.Printf("[Network configuration to validate]\n%s\n", string(pretty)) + + if err := netutil.ValidateNetwork(network); err != nil { + fmt.Println("Network configuration is valid.") + } else { + fmt.Println("Network configuration is invalid.") } - fmt.Printf("[Configuration]\n%s", string(prettyConfig)) + }