diff --git a/README.md b/README.md index 4a2d23a..729131f 100644 --- a/README.md +++ b/README.md @@ -59,7 +59,7 @@ The arguments have the following meaning : | Name | Description | | ----------- | ----------- | | loxiURL | API server address of loxilb. This is the docker IP address loxilb docker of Step 1. If unspecified, kube-loxilb assumes loxilb is running in-cluster mode and autoconfigures this. | -| cidrPools | CIDR or IPAddress range to allocate addresses from. By default address allocated are shared for different services(shared Mode). Multiple pools can be defined and any of them can be selected during service creation using "loxilb.io/poolSelect" annotation. | +| cidrPools | CIDR or IPAddress range to allocate addresses from. By default address allocated are shared for different services(shared Mode). Multiple pools can be defined and any of them can be selected during service creation using "loxilb.io/poolSelect" annotation. cidrPools can be specified as ip-cidr e.g 123.123.123.0/23 or as ip-ranges e.g 123.123.123.1-123.123.123.3 | | cidr6Pools | Ipv6 CIDR or IPAddress range to allocate addresses from. By default address allocated are shared for different services(shared Mode). Multiple pools can be defined and any of them can be selected during service creation using "loxilb.io/poolSelect" annotation. | | monitor | Enable liveness probe for the LB end-points (default : unset) | | setBGP | Use specified BGP AS-ID to advertise this service. If not specified BGP will be disabled. Please check [here](https://github.com/loxilb-io/loxilbdocs/blob/main/docs/integrate_bgp_eng.md) how it works. | diff --git a/cmd/loxilb-agent/options.go b/cmd/loxilb-agent/options.go index 0831d0c..8614ac8 100644 --- a/cmd/loxilb-agent/options.go +++ b/cmd/loxilb-agent/options.go @@ -115,10 +115,28 @@ func (o *Options) validate(args []string) error { return fmt.Errorf("externalCIDR %s config is invalid", o.config.ExternalCIDRPoolDefs) } if _, _, err := net.ParseCIDR(poolStrSlice[1]); err != nil { - return fmt.Errorf("externalCIDR %s config is invalid", poolStrSlice[1]) - } - if !lib.IsNetIPv4(poolStrSlice[1]) { - return fmt.Errorf("externalCIDR %s config is invalid", poolStrSlice[1]) + if strings.Contains(poolStrSlice[1], "-") { + ipBlock := strings.Split(poolStrSlice[1], "-") + if len(ipBlock) != 2 { + return fmt.Errorf("invalid ip-range") + } + + startIP := net.ParseIP(ipBlock[0]) + lastIP := net.ParseIP(ipBlock[1]) + if startIP == nil || lastIP == nil { + return fmt.Errorf("invalid ip-range ips") + } + if lib.IsNetIPv4(startIP.String()) && lib.IsNetIPv6(lastIP.String()) || + lib.IsNetIPv6(startIP.String()) && lib.IsNetIPv4(lastIP.String()) { + return fmt.Errorf("invalid ip-types ips") + } + } else { + return fmt.Errorf("externalCIDR %s config is invalid", poolStrSlice[1]) + } + } else { + if !lib.IsNetIPv4(poolStrSlice[1]) { + return fmt.Errorf("externalCIDR %s config is invalid", poolStrSlice[1]) + } } } } @@ -131,10 +149,28 @@ func (o *Options) validate(args []string) error { return fmt.Errorf("externalCIDR6 %s config is invalid", o.config.ExternalCIDR6PoolDefs) } if _, _, err := net.ParseCIDR(poolStrSlice[1]); err != nil { - return fmt.Errorf("externalCIDR6 %s config is invalid", poolStrSlice[1]) - } - if !lib.IsNetIPv6(poolStrSlice[1]) { - return fmt.Errorf("externalCIDR6 %s config is invalid", poolStrSlice[1]) + if strings.Contains(poolStrSlice[1], "-") { + ipBlock := strings.Split(poolStrSlice[1], "-") + if len(ipBlock) != 2 { + return fmt.Errorf("invalid ip-range") + } + + startIP := net.ParseIP(ipBlock[0]) + lastIP := net.ParseIP(ipBlock[1]) + if startIP == nil || lastIP == nil { + return fmt.Errorf("invalid ip-range ips") + } + if lib.IsNetIPv4(startIP.String()) && lib.IsNetIPv6(lastIP.String()) || + lib.IsNetIPv6(startIP.String()) && lib.IsNetIPv4(lastIP.String()) { + return fmt.Errorf("invalid ip-types ips") + } + } else { + return fmt.Errorf("externalCIDR6 %s config is invalid", poolStrSlice[1]) + } + } else { + if !lib.IsNetIPv6(poolStrSlice[1]) { + return fmt.Errorf("externalCIDR6 %s config is invalid", poolStrSlice[1]) + } } } } diff --git a/go.mod b/go.mod index 94867e3..132a6a9 100644 --- a/go.mod +++ b/go.mod @@ -3,7 +3,7 @@ module github.com/loxilb-io/kube-loxilb go 1.23.0 require ( - github.com/loxilb-io/loxilib v0.8.9-0.20240315085933-0925d8a579ed + github.com/loxilb-io/loxilib v0.8.9-0.20241122154322-9908d97c825d github.com/spf13/cobra v1.7.0 github.com/spf13/pflag v1.0.5 gopkg.in/yaml.v2 v2.4.0 @@ -40,7 +40,7 @@ require ( github.com/inconshreveable/mousetrap v1.1.0 // indirect github.com/josharian/intern v1.0.0 // indirect github.com/json-iterator/go v1.1.12 // indirect - github.com/loxilb-io/sctp v0.0.0-20230519081703-6d1baec82fd4 // indirect + github.com/loxilb-io/sctp v0.0.0-20240912025756-01894eac308b // indirect github.com/mailru/easyjson v0.7.7 // indirect github.com/matttproud/golang_protobuf_extensions/v2 v2.0.0 // indirect github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect diff --git a/go.sum b/go.sum index a3f4ed5..e475241 100644 --- a/go.sum +++ b/go.sum @@ -65,10 +65,10 @@ github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= -github.com/loxilb-io/loxilib v0.8.9-0.20240315085933-0925d8a579ed h1:+JQ/l/sOI7KB9OxarKwRQxj5TjGgdia2UtQQRfvVa20= -github.com/loxilb-io/loxilib v0.8.9-0.20240315085933-0925d8a579ed/go.mod h1:LoQCxBz+N0fO9rGwRmPHrQPHol/jUf4MNpph63Cydkg= -github.com/loxilb-io/sctp v0.0.0-20230519081703-6d1baec82fd4 h1:oDc2lsbfuQEcVP3k+Pw4v6Xdm3t4M9vBc1Y9egszv6g= -github.com/loxilb-io/sctp v0.0.0-20230519081703-6d1baec82fd4/go.mod h1:1a6hv8ISVQhnW5IVpW9o+OL6BAFlWiVpC0O4d19g+wQ= +github.com/loxilb-io/loxilib v0.8.9-0.20241122154322-9908d97c825d h1:BE/A/Qq69ijTswOi8KJw+bI3E8ucVYHQO+a3wfwZ2js= +github.com/loxilb-io/loxilib v0.8.9-0.20241122154322-9908d97c825d/go.mod h1:72c3DmIKC53G5f4eNhTElemB08S64Xm/4QsDtwc66vw= +github.com/loxilb-io/sctp v0.0.0-20240912025756-01894eac308b h1:QZHlUZTWMpghNQW/OzdKFY2PhhPFMPAjsfRSLZkAONU= +github.com/loxilb-io/sctp v0.0.0-20240912025756-01894eac308b/go.mod h1:g3xKRvSWoeijv487mRGw3sLDacD9bC+wRQ4QebiafiQ= github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0= github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= github.com/matttproud/golang_protobuf_extensions/v2 v2.0.0 h1:jWpvCLoY8Z/e3VKvlsiIGKtc+UG6U5vzxaoagmhXfyg= diff --git a/pkg/agent/manager/loadbalancer/loadbalancer.go b/pkg/agent/manager/loadbalancer/loadbalancer.go index a1cb9db..6fe6288 100644 --- a/pkg/agent/manager/loadbalancer/loadbalancer.go +++ b/pkg/agent/manager/loadbalancer/loadbalancer.go @@ -2422,8 +2422,25 @@ func (m *Manager) AddLoxiCIDRPool(poolName string, cidr string) error { addr, _, err := net.ParseCIDR(cidr) if err != nil { - klog.Errorf("failed to parse (CIDR: %s)", cidr) - return err + if strings.Contains(cidr, "-") { + ipBlock := strings.Split(cidr, "-") + if len(ipBlock) != 2 { + return fmt.Errorf("invalid ip-range") + } + + startIP := net.ParseIP(ipBlock[0]) + lastIP := net.ParseIP(ipBlock[1]) + if startIP == nil || lastIP == nil { + return fmt.Errorf("invalid ip-range ips") + } + if tk.IsNetIPv4(startIP.String()) && tk.IsNetIPv6(lastIP.String()) || + tk.IsNetIPv6(startIP.String()) && tk.IsNetIPv4(lastIP.String()) { + return fmt.Errorf("invalid ip-types ips") + } + } else { + klog.Errorf("failed to parse (CIDR: %s)", cidr) + return err + } } newIPPoolTbl := make(map[string]*ippool.IPPool) @@ -2478,8 +2495,25 @@ func (m *Manager) DeleteLoxiCIDRPool(poolName string, cidr string) error { addr, _, err := net.ParseCIDR(cidr) if err != nil { - klog.Errorf("failed to parse (CIDR: %s)", cidr) - return err + if strings.Contains(cidr, "-") { + ipBlock := strings.Split(cidr, "-") + if len(ipBlock) != 2 { + return fmt.Errorf("invalid ip-range") + } + + startIP := net.ParseIP(ipBlock[0]) + lastIP := net.ParseIP(ipBlock[1]) + if startIP == nil || lastIP == nil { + return fmt.Errorf("invalid ip-range ips") + } + if tk.IsNetIPv4(startIP.String()) && tk.IsNetIPv6(lastIP.String()) || + tk.IsNetIPv6(startIP.String()) && tk.IsNetIPv4(lastIP.String()) { + return fmt.Errorf("invalid ip-types ips") + } + } else { + klog.Errorf("failed to parse (CIDR: %s)", cidr) + return err + } } newIPPoolTbl := make(map[string]*ippool.IPPool) diff --git a/pkg/ippool/ippool.go b/pkg/ippool/ippool.go index 96ed97d..df3bf9d 100644 --- a/pkg/ippool/ippool.go +++ b/pkg/ippool/ippool.go @@ -17,16 +17,19 @@ package ippool import ( "errors" + "k8s.io/klog/v2" "net" + "strings" "sync" - "k8s.io/klog/v2" - tk "github.com/loxilb-io/loxilib" ) type IPPool struct { CIDR string + isRange bool + startIP net.IP + lastIP net.IP NetCIDR *net.IPNet IPAlloc *tk.IPAllocator mutex sync.Mutex @@ -35,16 +38,41 @@ type IPPool struct { // Initailize IP Pool func NewIPPool(ipa *tk.IPAllocator, CIDR string, Shared bool) (*IPPool, error) { + var startIP net.IP + var lastIP net.IP + isRange := false + ipa.AddIPRange(tk.IPClusterDefault, CIDR) _, ipn, err := net.ParseCIDR(CIDR) if err != nil { - return nil, errors.New("CIDR parse failed") + if strings.Contains(CIDR, "-") { + ipBlock := strings.Split(CIDR, "-") + if len(ipBlock) != 2 { + return nil, errors.New("invalid ip-range") + } + + startIP = net.ParseIP(ipBlock[0]) + lastIP = net.ParseIP(ipBlock[1]) + if startIP == nil || lastIP == nil { + return nil, errors.New("invalid ip-range ips") + } + if tk.IsNetIPv4(startIP.String()) && tk.IsNetIPv6(lastIP.String()) || + tk.IsNetIPv6(startIP.String()) && tk.IsNetIPv4(lastIP.String()) { + return nil, errors.New("invalid ip-types ips") + } + isRange = true + } else { + return nil, errors.New("CIDR parse failed") + } } return &IPPool{ CIDR: CIDR, + isRange: isRange, NetCIDR: ipn, + startIP: startIP, + lastIP: lastIP, IPAlloc: ipa, mutex: sync.Mutex{}, Shared: Shared, @@ -90,7 +118,7 @@ func (i *IPPool) ReturnIPAddr(ip string, identStr string) { defer i.mutex.Unlock() IP := net.ParseIP(ip) - if IP == nil || !i.NetCIDR.Contains(IP) { + if IP == nil || !i.Contains(IP) { return } @@ -122,10 +150,53 @@ func (i *IPPool) ReserveIPAddr(ip string, name string, sIdent uint32, proto stri } +func diffIPIndex(baseIP net.IP, IP net.IP) uint64 { + index := uint64(0) + iplen := 0 + if tk.IsNetIPv4(baseIP.String()) { + iplen = 4 + } else { + iplen = 16 + } + + arrIndex := len(baseIP) - iplen + arrIndex1 := len(IP) - iplen + + for i := 0; i < 4 && arrIndex < len(baseIP) && arrIndex1 < len(IP); i++ { + + basev := uint8(baseIP[arrIndex]) + ipv := uint8(IP[arrIndex1]) + + if basev > ipv { + return ^uint64(0) + } + + index = uint64(ipv - basev) + arrIndex++ + arrIndex1++ + index |= index << (8 * (iplen - i - 1)) + } + + return index +} + +func (i *IPPool) Contains(IP net.IP) bool { + if i.isRange { + d1 := diffIPIndex(i.startIP, i.lastIP) + d2 := diffIPIndex(i.startIP, IP) + if d2 > d1 { + return false + } + return true + } else { + return i.NetCIDR.Contains(IP) + } +} + // CheckAndReserveIP check and reserve this IPaddress in IP Pool func (i *IPPool) CheckAndReserveIP(ip string, name string, sIdent uint32, proto string) (bool, bool, string) { IP := net.ParseIP(ip) - if IP != nil && i.NetCIDR.Contains(IP) { + if IP != nil && i.Contains(IP) { err, idStr := i.ReserveIPAddr(ip, name, sIdent, proto) if err != nil { return true, false, idStr