@@ -167,7 +167,7 @@ func (r *SubnetPortReconciler) Reconcile(ctx context.Context, req ctrl.Request)
167167 r .StatusUpdater .UpdateFail (ctx , subnetPort , err , "Failed to create NSX IPAddressAllocation for AddressBinding restore" , setSubnetPortReadyStatusFalse , r .SubnetPortService , r .restoreMode )
168168 return common .ResultRequeue , err
169169 }
170- nsxSubnetPortState , enableDHCP , err := r .SubnetPortService .CreateOrUpdateSubnetPort (subnetPort , nsxSubnet , "" , labels , isVmSubnetPort , r .restoreMode )
170+ nsxSubnetPortState , err := r .SubnetPortService .CreateOrUpdateSubnetPort (subnetPort , nsxSubnet , "" , labels , isVmSubnetPort , r .restoreMode )
171171 if err != nil {
172172 r .StatusUpdater .UpdateFail (ctx , subnetPort , err , "" , setSubnetPortReadyStatusFalse , r .SubnetPortService , r .restoreMode )
173173 if nsxutil .IsRealizeStateError (err ) {
@@ -191,13 +191,34 @@ func (r *SubnetPortReconciler) Reconcile(ctx context.Context, req ctrl.Request)
191191 Gateway : "" ,
192192 },
193193 },
194- DHCPDeactivatedOnSubnet : ! enableDHCP ,
194+ DHCPDeactivatedOnSubnet : ! util .NSXSubnetDHCPEnabled (nsxSubnet ),
195+ DHCPv6DeactivatedOnSubnet : ! util .NSXSubnetDHCPv6Enabled (nsxSubnet ),
196+ }
197+ // Append one more ipaddress for dual stack SubnetPort
198+ if subnetPort .Spec .InterfaceIPType == v1alpha1 .IPAddressTypeIPv4IPv6 {
199+ subnetPort .Status .NetworkInterfaceConfig .IPAddresses = append (
200+ subnetPort .Status .NetworkInterfaceConfig .IPAddresses ,
201+ v1alpha1.NetworkInterfaceIPAddress {Gateway : "" },
202+ )
195203 }
196204 if util .NSXSubnetStaticIPAllocationEnabled (nsxSubnet ) || len (subnetPort .Spec .AddressBindings ) > 0 {
197205 if len (nsxSubnetPortState .RealizedBindings ) > 0 {
198- subnetPort .Status .NetworkInterfaceConfig .IPAddresses [0 ].IPAddress = * nsxSubnetPortState .RealizedBindings [0 ].Binding .IpAddress
199- // The MAC address is updated here when the SubnetPort's StaticIPAllocation is enabled or spec.AddressBindings is specific. For the other cases, the MAC address will be updated in the VIF polling.
200- subnetPort .Status .NetworkInterfaceConfig .MACAddress = strings .Trim (* nsxSubnetPortState .RealizedBindings [0 ].Binding .MacAddress , "\" " )
206+ // Process all realized bindings and populate IPAddresses array
207+ // RealizedBindings can contain up to 2 entries (IPv4 and IPv6)
208+ // The MAC address is updated here when the SubnetPort's StaticIPAllocation
209+ // is enabled or spec.AddressBindings is specific. For the other cases, the MAC
210+ // address will be updated in the VIF polling.
211+ macAddress := ""
212+ for i , binding := range nsxSubnetPortState .RealizedBindings {
213+ if binding .Binding != nil && binding .Binding .IpAddress != nil {
214+ if macAddress == "" && binding .Binding .MacAddress != nil {
215+ macAddress = strings .Trim (* binding .Binding .MacAddress , "\" " )
216+ }
217+ subnetPort .Status .NetworkInterfaceConfig .IPAddresses [i ].IPAddress = * binding .Binding .IpAddress
218+ }
219+ }
220+ // MAC address is consistent across all bindings, set it once
221+ subnetPort .Status .NetworkInterfaceConfig .MACAddress = macAddress
201222 } else if ! util .NSXSubnetStaticIPAllocationEnabled (nsxSubnet ) && len (subnetPort .Spec .AddressBindings ) > 0 {
202223 // If StaticIPAllocation is disabled, propagate the MAC from spec.addressBinding to status
203224 subnetPort .Status .NetworkInterfaceConfig .MACAddress = subnetPort .Spec .AddressBindings [0 ].MACAddress
@@ -211,9 +232,7 @@ func (r *SubnetPortReconciler) Reconcile(ctx context.Context, req ctrl.Request)
211232 if subnetPort .Status .NetworkInterfaceConfig .MACAddress == "" && old_status .NetworkInterfaceConfig .MACAddress != "" {
212233 subnetPort .Status .NetworkInterfaceConfig .MACAddress = old_status .NetworkInterfaceConfig .MACAddress
213234 }
214- if subnetPort .Status .NetworkInterfaceConfig .IPAddresses [0 ].IPAddress == "" && old_status .NetworkInterfaceConfig .IPAddresses [0 ].IPAddress != "" {
215- subnetPort .Status .NetworkInterfaceConfig .IPAddresses [0 ].IPAddress = old_status .NetworkInterfaceConfig .IPAddresses [0 ].IPAddress
216- }
235+ subnetPort .Status .NetworkInterfaceConfig .IPAddresses = old_status .NetworkInterfaceConfig .IPAddresses
217236 }
218237 err = r .updateSubnetStatusOnSubnetPort (subnetPort , nsxSubnet )
219238 if err != nil {
@@ -924,19 +943,50 @@ func (r *SubnetPortReconciler) CheckAndGetSubnetPathForSubnetPort(ctx context.Co
924943}
925944
926945func (r * SubnetPortReconciler ) updateSubnetStatusOnSubnetPort (subnetPort * v1alpha1.SubnetPort , nsxSubnet * model.VpcSubnet ) error {
927- gateway , prefix , err := r .SubnetService .GetGatewayPrefixOfSubnet (nsxSubnet )
946+ subnetPort .Status .NetworkInterfaceConfig .LogicalSwitchUUID = * nsxSubnet .RealizationId
947+ // Get all gateways from the subnet (may be IPv4, IPv6, or both for dual-stack)
948+ gatewaysWithPrefixes , err := r .SubnetService .GetAllGatewayPrefixesOfSubnet (nsxSubnet )
928949 if err != nil {
929950 return err
930951 }
931- // For now, we have an assumption that one subnetport only have one IP address
932- if len (subnetPort .Status .NetworkInterfaceConfig .IPAddresses [0 ].IPAddress ) > 0 && prefix > 0 {
933- subnetPort .Status .NetworkInterfaceConfig .IPAddresses [0 ].IPAddress += fmt .Sprintf ("/%d" , prefix )
934- }
935952 // The gateway can be empty for L2_Only Subnet which has the vlan_connection without gateway
936- if len (gateway ) > 0 {
937- subnetPort .Status .NetworkInterfaceConfig .IPAddresses [0 ].Gateway = gateway
953+ if len (gatewaysWithPrefixes ) == 0 {
954+ return nil
955+ }
956+
957+ // Process each IP address entry
958+ for i , ipConfig := range subnetPort .Status .NetworkInterfaceConfig .IPAddresses {
959+ var isIPv6 bool
960+
961+ if len (ipConfig .IPAddress ) > 0 {
962+ // If IP address is present, determine family dynamically
963+ isIPv6 = util .IsIPv6 (ipConfig .IPAddress )
964+ } else {
965+ // For empty IP configurations (e.g., DHCP), fall back to index-based positional inference:
966+ // Index 0 matches the first gateway family, Index 1 matches the second gateway family
967+ if i < len (gatewaysWithPrefixes ) {
968+ isIPv6 = util .IsIPv6 (gatewaysWithPrefixes [i ].Gateway )
969+ } else {
970+ continue
971+ }
972+ }
973+
974+ // Find matching gateway by IP family
975+ for _ , gwInfo := range gatewaysWithPrefixes {
976+ isGatewayIPv6 := util .IsIPv6 (gwInfo .Gateway )
977+ if isIPv6 != isGatewayIPv6 {
978+ continue
979+ }
980+
981+ // Only append the prefix notation if the IP address is actually present
982+ if len (subnetPort .Status .NetworkInterfaceConfig .IPAddresses [i ].IPAddress ) > 0 {
983+ subnetPort .Status .NetworkInterfaceConfig .IPAddresses [i ].IPAddress += fmt .Sprintf ("/%d" , gwInfo .Prefix )
984+ }
985+
986+ subnetPort .Status .NetworkInterfaceConfig .IPAddresses [i ].Gateway = gwInfo .Gateway
987+ break
988+ }
938989 }
939- subnetPort .Status .NetworkInterfaceConfig .LogicalSwitchUUID = * nsxSubnet .RealizationId
940990 return nil
941991}
942992
0 commit comments