@@ -13,6 +13,7 @@ import (
1313 "k8s.io/apimachinery/pkg/util/intstr"
1414 "k8s.io/client-go/tools/cache"
1515 "k8s.io/klog/v2"
16+ "k8s.io/utils/set"
1617
1718 kubeovnv1 "github.com/kubeovn/kube-ovn/pkg/apis/kubeovn/v1"
1819 "github.com/kubeovn/kube-ovn/pkg/ovsdb/ovnnb"
@@ -323,6 +324,12 @@ func generateHeadlessService(slr *kubeovnv1.SwitchLBRule, oldSvc *corev1.Service
323324 }
324325
325326 name = generateSvcName (slr .Name )
327+
328+ // We need to set the correct IPFamilies and IPFamilyPolicy on the service
329+ // If the VIP is an IPv4, the Service needs to be configured in IPv4, and the opposite for an IPv6
330+ // If the VIP has a mix of IPv4s and IPv6s, the Service must be DualStack, with both families set
331+ families , policy := getIPFamilies (slr .Spec .Vip )
332+
326333 if oldSvc != nil {
327334 newSvc = oldSvc .DeepCopy ()
328335 newSvc .Name = name
@@ -331,6 +338,8 @@ func generateHeadlessService(slr *kubeovnv1.SwitchLBRule, oldSvc *corev1.Service
331338 newSvc .Spec .Ports = ports
332339 newSvc .Spec .Selector = selectors
333340 newSvc .Spec .SessionAffinity = corev1 .ServiceAffinity (slr .Spec .SessionAffinity )
341+ newSvc .Spec .IPFamilies = families
342+ newSvc .Spec .IPFamilyPolicy = & policy
334343 } else {
335344 newSvc = & corev1.Service {
336345 ObjectMeta : metav1.ObjectMeta {
@@ -344,6 +353,8 @@ func generateHeadlessService(slr *kubeovnv1.SwitchLBRule, oldSvc *corev1.Service
344353 ClusterIP : corev1 .ClusterIPNone ,
345354 Type : corev1 .ServiceTypeClusterIP ,
346355 SessionAffinity : corev1 .ServiceAffinity (slr .Spec .SessionAffinity ),
356+ IPFamilies : families ,
357+ IPFamilyPolicy : & policy ,
347358 },
348359 }
349360 }
@@ -406,3 +417,25 @@ func generateEndpoints(slr *kubeovnv1.SwitchLBRule, oldEps *corev1.Endpoints) *c
406417 }
407418 return newEps
408419}
420+
421+ // getIPFamilies returns the IP families (IPv6/IPv4) for a set of IPs within a VIP
422+ // This function is used to correctly construct the Service of a SwitchLBRule
423+ // It also returns the corresponding IPFamilyPolicy to set in the Service
424+ func getIPFamilies (vip string ) (families []corev1.IPFamily , policy corev1.IPFamilyPolicy ) {
425+ // Check every IP in the VIP, assess if it is an IPv6 or an IPv4
426+ ipFamilies := set .New [corev1.IPFamily ]()
427+ for ip := range strings .SplitSeq (vip , "," ) {
428+ switch util .CheckProtocol (ip ) {
429+ case kubeovnv1 .ProtocolIPv6 :
430+ ipFamilies .Insert (corev1 .IPv6Protocol )
431+ case kubeovnv1 .ProtocolIPv4 :
432+ ipFamilies .Insert (corev1 .IPv4Protocol )
433+ }
434+ }
435+
436+ policy = corev1 .IPFamilyPolicySingleStack
437+ if ipFamilies .Len () > 1 {
438+ policy = corev1 .IPFamilyPolicyPreferDualStack
439+ }
440+ return ipFamilies .SortedList (), policy
441+ }
0 commit comments