diff --git a/pkg/controller/subnet.go b/pkg/controller/subnet.go index 59c23c03069..f938371d833 100644 --- a/pkg/controller/subnet.go +++ b/pkg/controller/subnet.go @@ -1051,11 +1051,11 @@ func calcDualSubnetStatusIP(subnet *kubeovnv1.Subnet, c *Controller) error { v6UsingIPs = append(v6UsingIPs, splitIPs[1]) } } - v4availableIPs := util.AddressCount(v4CIDR) - float64(util.CountIpNums(v4toSubIPs)) + v4availableIPs := util.AddressCount(v4CIDR) - util.CountIpNums(v4toSubIPs) if v4availableIPs < 0 { v4availableIPs = 0 } - v6availableIPs := util.AddressCount(v6CIDR) - float64(util.CountIpNums(v6toSubIPs)) + v6availableIPs := util.AddressCount(v6CIDR) - util.CountIpNums(v6toSubIPs) if v6availableIPs < 0 { v6availableIPs = 0 } @@ -1091,7 +1091,7 @@ func calcSubnetStatusIP(subnet *kubeovnv1.Subnet, c *Controller) error { for _, podUsedIP := range podUsedIPs.Items { toSubIPs = append(toSubIPs, podUsedIP.Spec.IPAddress) } - availableIPs := util.AddressCount(cidr) - float64(util.CountIpNums(toSubIPs)) + availableIPs := util.AddressCount(cidr) - util.CountIpNums(toSubIPs) if availableIPs < 0 { availableIPs = 0 } diff --git a/pkg/ipam/ipam.go b/pkg/ipam/ipam.go index 1f162a40456..9bc6a3d4f2a 100644 --- a/pkg/ipam/ipam.go +++ b/pkg/ipam/ipam.go @@ -148,7 +148,7 @@ func (ipam *IPAM) AddOrUpdateSubnet(name, cidrStr string, excludeIps []string) e _, cidr, _ := net.ParseCIDR(v4cidrStr) subnet.V4CIDR = cidr subnet.V4ReservedIPList = convertExcludeIps(v4ExcludeIps) - firstIP, _ := util.FirstSubnetIP(v4cidrStr) + firstIP, _ := util.FirstIP(v4cidrStr) lastIP, _ := util.LastIP(v4cidrStr) subnet.V4FreeIPList = IPRangeList{&IPRange{Start: IP(firstIP), End: IP(lastIP)}} subnet.joinFreeWithReserve() @@ -163,7 +163,7 @@ func (ipam *IPAM) AddOrUpdateSubnet(name, cidrStr string, excludeIps []string) e _, cidr, _ := net.ParseCIDR(v6cidrStr) subnet.V6CIDR = cidr subnet.V6ReservedIPList = convertExcludeIps(v6ExcludeIps) - firstIP, _ := util.FirstSubnetIP(v6cidrStr) + firstIP, _ := util.FirstIP(v6cidrStr) lastIP, _ := util.LastIP(v6cidrStr) subnet.V6FreeIPList = IPRangeList{&IPRange{Start: IP(firstIP), End: IP(lastIP)}} subnet.joinFreeWithReserve() diff --git a/pkg/ipam/subnet.go b/pkg/ipam/subnet.go index 13dd2fdb524..23b40203c12 100644 --- a/pkg/ipam/subnet.go +++ b/pkg/ipam/subnet.go @@ -46,7 +46,7 @@ func NewSubnet(name, cidrStr string, excludeIps []string) (*Subnet, error) { subnet := Subnet{} protocol := util.CheckProtocol(cidrStr) if protocol == kubeovnv1.ProtocolIPv4 { - firstIP, _ := util.FirstSubnetIP(cidrStr) + firstIP, _ := util.FirstIP(cidrStr) lastIP, _ := util.LastIP(cidrStr) subnet = Subnet{ @@ -66,7 +66,7 @@ func NewSubnet(name, cidrStr string, excludeIps []string) (*Subnet, error) { } subnet.joinFreeWithReserve() } else if protocol == kubeovnv1.ProtocolIPv6 { - firstIP, _ := util.FirstSubnetIP(cidrStr) + firstIP, _ := util.FirstIP(cidrStr) lastIP, _ := util.LastIP(cidrStr) subnet = Subnet{ @@ -87,9 +87,9 @@ func NewSubnet(name, cidrStr string, excludeIps []string) (*Subnet, error) { subnet.joinFreeWithReserve() } else { cidrBlocks := strings.Split(cidrStr, ",") - v4FirstIP, _ := util.FirstSubnetIP(cidrBlocks[0]) + v4FirstIP, _ := util.FirstIP(cidrBlocks[0]) v4LastIP, _ := util.LastIP(cidrBlocks[0]) - v6FirstIP, _ := util.FirstSubnetIP(cidrBlocks[1]) + v6FirstIP, _ := util.FirstIP(cidrBlocks[1]) v6LastIP, _ := util.LastIP(cidrBlocks[1]) subnet = Subnet{ diff --git a/pkg/util/net.go b/pkg/util/net.go index c69a68a81c1..b55bbf02ec5 100644 --- a/pkg/util/net.go +++ b/pkg/util/net.go @@ -53,7 +53,7 @@ func SubnetNumber(subnet string) string { return cidr.IP.String() } -func SubnetBroadCast(subnet string) string { +func SubnetBroadcast(subnet string) string { _, cidr, _ := net.ParseCIDR(subnet) var length uint if CheckProtocol(subnet) == kubeovnv1.ProtocolIPv4 { @@ -68,7 +68,7 @@ func SubnetBroadCast(subnet string) string { return BigInt2Ip(ipInt.Add(ipInt, size)) } -func FirstSubnetIP(subnet string) (string, error) { +func FirstIP(subnet string) (string, error) { _, cidr, err := net.ParseCIDR(subnet) if err != nil { return "", fmt.Errorf("%s is not a valid cidr", subnet) @@ -225,7 +225,7 @@ func CheckCidrs(cidr string) error { func GetGwByCidr(cidrStr string) (string, error) { var gws []string for _, cidr := range strings.Split(cidrStr, ",") { - gw, err := FirstSubnetIP(cidr) + gw, err := FirstIP(cidr) if err != nil { return "", err } @@ -242,7 +242,7 @@ func AppendGwByCidr(gateway, cidrStr string) (string, error) { gws = append(gws, gateway) continue } else { - gw, err := FirstSubnetIP(cidr) + gw, err := FirstIP(cidr) if err != nil { return "", err } @@ -336,54 +336,52 @@ func ExpandExcludeIPs(excludeIPs []string, cidr string) []string { rv := []string{} for _, excludeIP := range excludeIPs { if strings.Contains(excludeIP, "..") { + parts := strings.Split(excludeIP, "..") + if len(parts) != 2 || CheckProtocol(parts[0]) != CheckProtocol(parts[1]) { + klog.Errorf("invalid exlcude IP: %s", excludeIP) + continue + } + s := Ip2BigInt(parts[0]) + e := Ip2BigInt(parts[1]) + if s.Cmp(e) > 0 { + continue + } + for _, cidrBlock := range strings.Split(cidr, ",") { - subnetNum := SubnetNumber(cidrBlock) - broadcast := SubnetBroadCast(cidrBlock) - parts := strings.Split(excludeIP, "..") - s := Ip2BigInt(parts[0]) - e := Ip2BigInt(parts[1]) - - // limit range in cidr - firstIP, _ := FirstSubnetIP(cidrBlock) - lastIP, _ := LastIP(cidrBlock) - if s.Cmp(Ip2BigInt(firstIP)) < 0 { - s = Ip2BigInt(firstIP) - } - if e.Cmp(Ip2BigInt(lastIP)) > 0 { - e = Ip2BigInt(lastIP) + if CheckProtocol(cidrBlock) != CheckProtocol(parts[0]) { + continue } - changed := false - // exclude cidr and broadcast address - if ContainsIPs(excludeIP, subnetNum) { - v := Ip2BigInt(subnetNum) - if s.Cmp(v) == 0 { - s.Add(s, big.NewInt(1)) - rv = append(rv, BigInt2Ip(s)+".."+BigInt2Ip(e)) - } else if e.Cmp(v) == 0 { - e.Sub(e, big.NewInt(1)) - rv = append(rv, BigInt2Ip(s)+".."+BigInt2Ip(e)) - } else { - var low, high big.Int - lowp := (&low).Sub(v, big.NewInt(1)) - highp := (&high).Add(v, big.NewInt(1)) - rv = append(rv, BigInt2Ip(s)+".."+BigInt2Ip(lowp)) - rv = append(rv, BigInt2Ip(highp)+".."+BigInt2Ip(e)) - } - changed = true + firstIP, err := FirstIP(cidrBlock) + if err != nil { + klog.Error(err) + continue + } + if firstIP == SubnetBroadcast(cidrBlock) { + klog.Errorf("no available IP address in CIDR %s", cidrBlock) + continue + } + lastIP, _ := LastIP(cidrBlock) + s1, e1 := s, e + if s1.Cmp(Ip2BigInt(firstIP)) < 0 { + s1 = Ip2BigInt(firstIP) } - if ContainsIPs(excludeIP, broadcast) { - v := Ip2BigInt(broadcast) - v.Sub(v, big.NewInt(1)) - rv = append(rv, BigInt2Ip(s)+".."+BigInt2Ip(v)) - changed = true + if e1.Cmp(Ip2BigInt(lastIP)) > 0 { + e1 = Ip2BigInt(lastIP) } - if !changed && s.Cmp(e) < 0 { - rv = append(rv, BigInt2Ip(s)+".."+BigInt2Ip(e)) + if c := s1.Cmp(e1); c == 0 { + rv = append(rv, BigInt2Ip(s1)) + } else if c < 0 { + rv = append(rv, BigInt2Ip(s1)+".."+BigInt2Ip(e1)) } } } else { - rv = append(rv, excludeIP) + for _, cidrBlock := range strings.Split(cidr, ",") { + if CIDRContainIP(cidrBlock, excludeIP) && excludeIP != SubnetNumber(cidrBlock) && excludeIP != SubnetBroadcast(cidrBlock) { + rv = append(rv, excludeIP) + break + } + } } } klog.V(3).Infof("expand exclude ips %v", rv) @@ -407,15 +405,16 @@ func ContainsIPs(excludeIP string, ip string) bool { return false } -func CountIpNums(excludeIPs []string) int64 { - var count int64 +func CountIpNums(excludeIPs []string) float64 { + var count float64 for _, excludeIP := range excludeIPs { if strings.Contains(excludeIP, "..") { var val big.Int parts := strings.Split(excludeIP, "..") s := Ip2BigInt(parts[0]) e := Ip2BigInt(parts[1]) - count = val.Add(val.Sub(e, s), big.NewInt(1)).Int64() + v, _ := new(big.Float).SetInt(val.Add(val.Sub(e, s), big.NewInt(1))).Float64() + count += v } else { count++ } diff --git a/pkg/util/validator.go b/pkg/util/validator.go index aeae022c502..77efcdf84b5 100644 --- a/pkg/util/validator.go +++ b/pkg/util/validator.go @@ -184,7 +184,7 @@ func ValidatePodCidr(cidr, ip string) error { } ipStr := IPToString(ipAddr) - if SubnetBroadCast(cidrBlock) == ipStr { + if SubnetBroadcast(cidrBlock) == ipStr { return fmt.Errorf("%s is the broadcast ip in cidr %s", ipStr, cidrBlock) } if SubnetNumber(cidrBlock) == ipStr { diff --git a/test/unittest/unit_suite_test.go b/test/unittest/unit_suite_test.go index bbb8927c71a..51c5c7652fd 100644 --- a/test/unittest/unit_suite_test.go +++ b/test/unittest/unit_suite_test.go @@ -8,6 +8,7 @@ import ( // tests to run _ "github.com/kubeovn/kube-ovn/test/unittest/ipam" + _ "github.com/kubeovn/kube-ovn/test/unittest/util" ) func TestE2e(t *testing.T) { diff --git a/test/unittest/util/net.go b/test/unittest/util/net.go new file mode 100644 index 00000000000..21fccfe8e72 --- /dev/null +++ b/test/unittest/util/net.go @@ -0,0 +1,146 @@ +package util + +import ( + "net" + + . "github.com/onsi/ginkgo" + . "github.com/onsi/gomega" + + "github.com/kubeovn/kube-ovn/pkg/util" +) + +var _ = Describe("[Net]", func() { + It("AddressCount", func() { + args := []*net.IPNet{ + {IP: net.ParseIP("10.0.0.0"), Mask: net.CIDRMask(32, 32)}, + {IP: net.ParseIP("10.0.0.0"), Mask: net.CIDRMask(31, 32)}, + {IP: net.ParseIP("10.0.0.0"), Mask: net.CIDRMask(30, 32)}, + {IP: net.ParseIP("10.0.0.0"), Mask: net.CIDRMask(24, 32)}, + } + wants := []float64{ + 0, + 0, + 2, + 254, + } + Expect(len(args)).To(Equal(len(args))) + + for i := range args { + Expect(args[i].IP).NotTo(BeNil()) + Expect(util.AddressCount(args[i])).To(Equal(wants[i])) + } + }) + + It("CountIpNums", func() { + args := [][]string{ + {"10.0.0.101"}, + {"10.0.0.101..10.0.0.105"}, + {"10.0.0.101..10.0.0.105", "10.0.0.111..10.0.0.120"}, + } + wants := []float64{ + 1, + 5, + 15, + } + Expect(len(args)).To(Equal(len(args))) + + for i := range args { + Expect(util.CountIpNums(args[i])).To(Equal(wants[i])) + } + }) + + It("ExpandExcludeIPs", func() { + type arg struct { + cidr string + excludeIps []string + } + + args := []arg{ + {"10.0.1.0/24", []string{"10.0.0.255"}}, + {"10.0.1.0/24", []string{"10.0.1.0"}}, + {"10.0.1.0/24", []string{"10.0.1.1"}}, + {"10.0.1.0/24", []string{"10.0.1.254"}}, + {"10.0.1.0/24", []string{"10.0.1.255"}}, + {"10.0.1.0/24", []string{"10.0.2.0"}}, + {"10.0.1.0/24", []string{"10.0.1.101..10.0.1.105"}}, + {"10.0.1.0/24", []string{"10.0.0.101..10.0.1.105"}}, + {"10.0.1.0/24", []string{"10.0.1.101..10.0.2.105"}}, + {"10.0.1.0/24", []string{"10.0.1.101..10.0.1.101"}}, + {"10.0.1.0/24", []string{"10.0.1.105..10.0.1.101"}}, + {"10.0.1.0/24", []string{"10.0.1.1", "10.0.1.101"}}, + {"10.0.1.0/24", []string{"10.0.1.1", "10.0.1.101..10.0.1.105"}}, + {"10.0.1.0/24", []string{"10.0.1.1", "10.0.1.101..10.0.1.105", "10.0.1.111..10.0.1.120"}}, + {"10.0.1.0/30", []string{"10.0.1.1", "179.17.0.0..179.17.0.10"}}, + {"10.0.1.0/31", []string{"10.0.1.1", "179.17.0.0..179.17.0.10"}}, + {"10.0.1.0/32", []string{"10.0.1.1", "179.17.0.0..179.17.0.10"}}, + + {"fe00::100/120", []string{"fe00::ff"}}, + {"fe00::100/120", []string{"fe00::100"}}, + {"fe00::100/120", []string{"fe00::101"}}, + {"fe00::100/120", []string{"fe00::1fe"}}, + {"fe00::100/120", []string{"fe00::1ff"}}, + {"fe00::100/120", []string{"fe00::200"}}, + {"fe00::100/120", []string{"fe00::1a1..fe00::1a5"}}, + {"fe00::100/120", []string{"fe00::a1..fe00::1a5"}}, + {"fe00::100/120", []string{"fe00::1a1..fe00::2a5"}}, + {"fe00::100/120", []string{"fe00::1a1..fe00::1a1"}}, + {"fe00::100/120", []string{"fe00::1a5..fe00::1a1"}}, + {"fe00::100/120", []string{"fe00::101", "fe00::1a1"}}, + {"fe00::100/120", []string{"fe00::101", "fe00::1a1..fe00::1a5"}}, + {"fe00::100/120", []string{"fe00::101", "fe00::1a1..fe00::1a5", "fe00::1b1..fe00::1c0"}}, + {"fe00::100/126", []string{"fe00::101", "feff::..feff::a"}}, + {"fe00::100/127", []string{"fe00::101", "feff::..feff::a"}}, + {"fe00::100/128", []string{"fe00::101", "feff::..feff::a"}}, + + {"10.0.1.0/24,fe00::100/120", []string{"10.0.1.1", "10.0.1.101..10.0.1.105"}}, + {"10.0.1.0/24,fe00::100/120", []string{"fe00::101", "fe00::1a1..fe00::1a5"}}, + {"10.0.1.0/24,fe00::100/120", []string{"10.0.1.1", "10.0.1.101..10.0.1.105", "fe00::101", "fe00::1a1..fe00::1a5"}}, + } + wants := [][]string{ + {}, + {}, + {"10.0.1.1"}, + {"10.0.1.254"}, + {}, + {}, + {"10.0.1.101..10.0.1.105"}, + {"10.0.1.1..10.0.1.105"}, + {"10.0.1.101..10.0.1.254"}, + {"10.0.1.101"}, + {}, + {"10.0.1.1", "10.0.1.101"}, + {"10.0.1.1", "10.0.1.101..10.0.1.105"}, + {"10.0.1.1", "10.0.1.101..10.0.1.105", "10.0.1.111..10.0.1.120"}, + {"10.0.1.1"}, + {}, + {}, + + {}, + {}, + {"fe00::101"}, + {"fe00::1fe"}, + {}, + {}, + {"fe00::1a1..fe00::1a5"}, + {"fe00::101..fe00::1a5"}, + {"fe00::1a1..fe00::1fe"}, + {"fe00::1a1"}, + {}, + {"fe00::101", "fe00::1a1"}, + {"fe00::101", "fe00::1a1..fe00::1a5"}, + {"fe00::101", "fe00::1a1..fe00::1a5", "fe00::1b1..fe00::1c0"}, + {"fe00::101"}, + {}, + {}, + + {"10.0.1.1", "10.0.1.101..10.0.1.105"}, + {"fe00::101", "fe00::1a1..fe00::1a5"}, + {"10.0.1.1", "10.0.1.101..10.0.1.105", "fe00::101", "fe00::1a1..fe00::1a5"}, + } + Expect(len(args)).To(Equal(len(args))) + + for i := range args { + Expect(util.ExpandExcludeIPs(args[i].excludeIps, args[i].cidr)).To(Equal(wants[i])) + } + }) +})