@@ -3,20 +3,19 @@ package ipalloc
33import (
44 "context"
55 "fmt"
6- "net"
7- "sync"
8-
9- ipallocator "github.com/ovn-org/ovn-kubernetes/go-controller/pkg/allocator/ip"
106 "github.com/ovn-org/ovn-kubernetes/go-controller/pkg/util"
7+ corev1 "k8s.io/api/core/v1"
118 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
129 v1 "k8s.io/client-go/kubernetes/typed/core/v1"
10+ "net"
11+ "sync"
1312)
1413
1514// primaryIPAllocator attempts to allocate an IP in the same subnet as a nodes primary network
1615type primaryIPAllocator struct {
1716 mu * sync.Mutex
18- v4 * ipallocator. Range
19- v6 * ipallocator. Range
17+ v4 * ipAllocator
18+ v6 * ipAllocator
2019 nodeClient v1.NodeInterface
2120}
2221
@@ -48,37 +47,91 @@ func newPrimaryIPAllocator(nodeClient v1.NodeInterface) (*primaryIPAllocator, er
4847 if len (nodes .Items ) == 0 {
4948 return ipa , fmt .Errorf ("expected at least one node but found zero" )
5049 }
50+ // FIXME: the approach taken here to find the first node IP+mask and then to increment the second last octet wont work in
51+ // all scenarios (node with /24). We should generate an EgressIP compatible with a Node providers primary network and then take care its unique globally.
5152
52- for _ , node := range nodes .Items {
53+ // The approach here is to grab initial starting IP from first node found, increment the second last octet.
54+ // Approach taken here won't work for Nodes handed /24 subnets.
55+ nodePrimaryIPs , err := util .ParseNodePrimaryIfAddr (& nodes .Items [0 ])
56+ if err != nil {
57+ return ipa , fmt .Errorf ("failed to parse node primary interface address from Node object: %v" , err )
58+ }
59+ if nodePrimaryIPs .V4 .IP != nil {
60+ // should be ok with /16 and /64 node primary provider subnets
61+ // TODO; fixme; what about /24 subnet Nodes like GCP
62+ nodePrimaryIPs .V4 .IP [len (nodePrimaryIPs .V4 .IP )- 2 ]++
63+ ipa .v4 = newIPAllocator (& net.IPNet {IP : nodePrimaryIPs .V4 .IP , Mask : nodePrimaryIPs .V4 .Net .Mask })
64+ }
65+ if nodePrimaryIPs .V6 .IP != nil {
66+ nodePrimaryIPs .V6 .IP [len (nodePrimaryIPs .V6 .IP )- 2 ]++
67+ ipa .v6 = newIPAllocator (& net.IPNet {IP : nodePrimaryIPs .V6 .IP , Mask : nodePrimaryIPs .V6 .Net .Mask })
68+ }
69+ // verify the new starting base IP is within all Nodes subnets
70+ if nodePrimaryIPs .V4 .IP != nil {
71+ ipNets , err := getNodePrimaryProviderIPs (nodes .Items , false )
72+ if err != nil {
73+ return ipa , err
74+ }
75+ nextIP , err := ipa .v4 .AllocateNextIP ()
76+ if err != nil {
77+ return ipa , err
78+ }
79+ if ! isIPWithinAllSubnets (ipNets , nextIP ) {
80+ return ipa , fmt .Errorf ("IP %s is not within all Node subnets" , nextIP )
81+ }
82+ }
83+ if nodePrimaryIPs .V6 .IP != nil {
84+ ipNets , err := getNodePrimaryProviderIPs (nodes .Items , true )
85+ if err != nil {
86+ return ipa , err
87+ }
88+ nextIP , err := ipa .v6 .AllocateNextIP ()
89+ if err != nil {
90+ return ipa , err
91+ }
92+ if ! isIPWithinAllSubnets (ipNets , nextIP ) {
93+ return ipa , fmt .Errorf ("IP %s is not within all Node subnets" , nextIP )
94+ }
95+ }
96+
97+ return ipa , nil
98+ }
99+
100+ func getNodePrimaryProviderIPs (nodes []corev1.Node , isIPv6 bool ) ([]* net.IPNet , error ) {
101+ ipNets := make ([]* net.IPNet , 0 , len (nodes ))
102+ for _ , node := range nodes {
53103 nodePrimaryIPs , err := util .ParseNodePrimaryIfAddr (& node )
54104 if err != nil {
55- return ipa , fmt .Errorf ("failed to parse node primary interface address from Node %s object: %v" , node .Name , err )
56- }
57- if nodePrimaryIPs .V4 .IP != nil {
58- if ipa .v4 == nil {
59- ipa .v4 , err = ipallocator .NewCIDRRange (nodePrimaryIPs .V4 .Net )
60- if err != nil {
61- return ipa , fmt .Errorf ("failed to create new CIDR range for IPv4: %v" , err )
62- }
63- }
64- if err := ipa .v4 .Allocate (nodePrimaryIPs .V4 .IP ); err != nil {
65- return ipa , fmt .Errorf ("failed to allocate IPv4 %s: %v" , nodePrimaryIPs .V4 .IP , err )
66- }
67- }
68- if nodePrimaryIPs .V6 .IP != nil {
69- if ipa .v6 == nil {
70- ipa .v6 , err = ipallocator .NewCIDRRange (nodePrimaryIPs .V6 .Net )
71- if err != nil {
72- return ipa , fmt .Errorf ("failed to create new CIDR range for IPv6: %v" , err )
73- }
74- }
75- if err := ipa .v6 .Allocate (nodePrimaryIPs .V6 .IP ); err != nil {
76- return ipa , fmt .Errorf ("failed to allocate IPv6 %s: %v" , nodePrimaryIPs .V6 .IP , err )
77- }
105+ return nil , fmt .Errorf ("failed to parse node primary interface address from Node %s object: %v" , node .Name , err )
78106 }
107+ var mask net.IPMask
108+ var ip net.IP
79109
110+ if isIPv6 {
111+ ip = nodePrimaryIPs .V6 .IP
112+ mask = nodePrimaryIPs .V6 .Net .Mask
113+ } else {
114+ ip = nodePrimaryIPs .V4 .IP
115+ mask = nodePrimaryIPs .V4 .Net .Mask
116+ }
117+ if len (ip ) == 0 || len (mask ) == 0 {
118+ return nil , fmt .Errorf ("failed to find Node %s primary Node IP and/or mask" , node .Name )
119+ }
120+ ipNets = append (ipNets , & net.IPNet {IP : ip , Mask : mask })
80121 }
81- return ipa , nil
122+ return ipNets , nil
123+ }
124+
125+ func isIPWithinAllSubnets (ipNets []* net.IPNet , ip net.IP ) bool {
126+ if len (ipNets ) == 0 {
127+ return false
128+ }
129+ for _ , ipNet := range ipNets {
130+ if ! ipNet .Contains (ip ) {
131+ return false
132+ }
133+ }
134+ return true
82135}
83136
84137func (pia * primaryIPAllocator ) IncrementAndGetNextV4 (times int ) (net.IP , error ) {
@@ -95,9 +148,12 @@ func (pia *primaryIPAllocator) AllocateNextV4() (net.IP, error) {
95148 if pia .v4 == nil {
96149 return nil , fmt .Errorf ("IPv4 is not enable " )
97150 }
151+ if pia .v4 .net == nil {
152+ return nil , fmt .Errorf ("IPv4 is not enabled but Allocation request was called" )
153+ }
98154 pia .mu .Lock ()
99155 defer pia .mu .Unlock ()
100- return pia .v4 .AllocateNext ( )
156+ return allocateIP ( pia .nodeClient , pia . v4 .AllocateNextIP )
101157}
102158
103159func (pia * primaryIPAllocator ) IncrementAndGetNextV6 (times int ) (net.IP , error ) {
@@ -114,7 +170,51 @@ func (pia primaryIPAllocator) AllocateNextV6() (net.IP, error) {
114170 if pia .v6 == nil {
115171 return nil , fmt .Errorf ("IPv6 is not enabled but Allocation request was called" )
116172 }
173+ if pia .v6 .net == nil {
174+ return nil , fmt .Errorf ("ipv6 network is not set" )
175+ }
117176 pia .mu .Lock ()
118177 defer pia .mu .Unlock ()
119- return pia .v6 .AllocateNext ()
178+ return allocateIP (pia .nodeClient , pia .v6 .AllocateNextIP )
179+ }
180+
181+ type allocNextFn func () (net.IP , error )
182+
183+ func allocateIP (nodeClient v1.NodeInterface , allocateFn allocNextFn ) (net.IP , error ) {
184+ nodeList , err := nodeClient .List (context .TODO (), metav1.ListOptions {})
185+ if err != nil {
186+ return nil , fmt .Errorf ("failed to list nodes: %v" , err )
187+ }
188+ for {
189+ nextIP , err := allocateFn ()
190+ if err != nil {
191+ return nil , fmt .Errorf ("failed to allocated next IP address: %v" , err )
192+ }
193+ firstOctet := nextIP [len (nextIP )- 1 ]
194+ // skip 0 and 1
195+ if firstOctet == 0 || firstOctet == 1 {
196+ continue
197+ }
198+ isConflict , err := isConflictWithExistingHostIPs (nodeList .Items , nextIP )
199+ if err != nil {
200+ return nil , fmt .Errorf ("failed to determine if IP conflicts with existing IPs: %v" , err )
201+ }
202+ if ! isConflict {
203+ return nextIP , nil
204+ }
205+ }
206+ }
207+
208+ func isConflictWithExistingHostIPs (nodes []corev1.Node , ip net.IP ) (bool , error ) {
209+ ipStr := ip .String ()
210+ for _ , node := range nodes {
211+ nodeIPsSet , err := util .ParseNodeHostCIDRsDropNetMask (& node )
212+ if err != nil {
213+ return false , fmt .Errorf ("failed to parse node %s primary annotation info: %v" , node .Name , err )
214+ }
215+ if nodeIPsSet .Has (ipStr ) {
216+ return true , nil
217+ }
218+ }
219+ return false , nil
120220}
0 commit comments