@@ -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,35 @@ 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 subnet
198+ // TODO: check SubnetPort interfacetype when it is supported
199+ if nsxSubnet .IpAddressType != nil && * nsxSubnet .IpAddressType == model .VpcSubnet_IP_ADDRESS_TYPE_IPV4_IPV6 {
200+ subnetPort .Status .NetworkInterfaceConfig .IPAddresses = append (
201+ subnetPort .Status .NetworkInterfaceConfig .IPAddresses ,
202+ v1alpha1.NetworkInterfaceIPAddress {Gateway : "" },
203+ )
195204 }
196205 if util .NSXSubnetStaticIPAllocationEnabled (nsxSubnet ) || len (subnetPort .Spec .AddressBindings ) > 0 {
197206 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 , "\" " )
207+ // Process all realized bindings and populate IPAddresses array
208+ // RealizedBindings can contain up to 2 entries (IPv4 and IPv6)
209+ // The MAC address is updated here when the SubnetPort's StaticIPAllocation
210+ // is enabled or spec.AddressBindings is specific. For the other cases, the MAC
211+ // address will be updated in the VIF polling.
212+ macAddress := ""
213+ for i , binding := range nsxSubnetPortState .RealizedBindings {
214+ if binding .Binding != nil && binding .Binding .IpAddress != nil {
215+ if macAddress == "" && binding .Binding .MacAddress != nil {
216+ macAddress = strings .Trim (* binding .Binding .MacAddress , "\" " )
217+ }
218+ subnetPort .Status .NetworkInterfaceConfig .IPAddresses [i ].IPAddress = * binding .Binding .IpAddress
219+ }
220+ }
221+ // MAC address is consistent across all bindings, set it once
222+ subnetPort .Status .NetworkInterfaceConfig .MACAddress = macAddress
201223 } else if ! util .NSXSubnetStaticIPAllocationEnabled (nsxSubnet ) && len (subnetPort .Spec .AddressBindings ) > 0 {
202224 // If StaticIPAllocation is disabled, propagate the MAC from spec.addressBinding to status
203225 subnetPort .Status .NetworkInterfaceConfig .MACAddress = subnetPort .Spec .AddressBindings [0 ].MACAddress
@@ -211,9 +233,7 @@ func (r *SubnetPortReconciler) Reconcile(ctx context.Context, req ctrl.Request)
211233 if subnetPort .Status .NetworkInterfaceConfig .MACAddress == "" && old_status .NetworkInterfaceConfig .MACAddress != "" {
212234 subnetPort .Status .NetworkInterfaceConfig .MACAddress = old_status .NetworkInterfaceConfig .MACAddress
213235 }
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- }
236+ subnetPort .Status .NetworkInterfaceConfig .IPAddresses = old_status .NetworkInterfaceConfig .IPAddresses
217237 }
218238 err = r .updateSubnetStatusOnSubnetPort (subnetPort , nsxSubnet )
219239 if err != nil {
@@ -924,19 +944,50 @@ func (r *SubnetPortReconciler) CheckAndGetSubnetPathForSubnetPort(ctx context.Co
924944}
925945
926946func (r * SubnetPortReconciler ) updateSubnetStatusOnSubnetPort (subnetPort * v1alpha1.SubnetPort , nsxSubnet * model.VpcSubnet ) error {
927- gateway , prefix , err := r .SubnetService .GetGatewayPrefixOfSubnet (nsxSubnet )
947+ subnetPort .Status .NetworkInterfaceConfig .LogicalSwitchUUID = * nsxSubnet .RealizationId
948+ // Get all gateways from the subnet (may be IPv4, IPv6, or both for dual-stack)
949+ gatewaysWithPrefixes , err := r .SubnetService .GetAllGatewayPrefixesOfSubnet (nsxSubnet )
928950 if err != nil {
929951 return err
930952 }
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- }
935953 // 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
954+ if len (gatewaysWithPrefixes ) == 0 {
955+ return nil
956+ }
957+
958+ // Process each IP address entry
959+ for i , ipConfig := range subnetPort .Status .NetworkInterfaceConfig .IPAddresses {
960+ var isIPv6 bool
961+
962+ if len (ipConfig .IPAddress ) > 0 {
963+ // If IP address is present, determine family dynamically
964+ isIPv6 = util .IsIPv6 (ipConfig .IPAddress )
965+ } else {
966+ // For empty IP configurations (e.g., DHCP), fall back to index-based positional inference:
967+ // Index 0 matches the first gateway family, Index 1 matches the second gateway family
968+ if i < len (gatewaysWithPrefixes ) {
969+ isIPv6 = util .IsIPv6 (gatewaysWithPrefixes [i ].Gateway )
970+ } else {
971+ continue
972+ }
973+ }
974+
975+ // Find matching gateway by IP family
976+ for _ , gwInfo := range gatewaysWithPrefixes {
977+ isGatewayIPv6 := util .IsIPv6 (gwInfo .Gateway )
978+ if isIPv6 != isGatewayIPv6 {
979+ continue
980+ }
981+
982+ // Only append the prefix notation if the IP address is actually present
983+ if len (subnetPort .Status .NetworkInterfaceConfig .IPAddresses [i ].IPAddress ) > 0 {
984+ subnetPort .Status .NetworkInterfaceConfig .IPAddresses [i ].IPAddress += fmt .Sprintf ("/%d" , gwInfo .Prefix )
985+ }
986+
987+ subnetPort .Status .NetworkInterfaceConfig .IPAddresses [i ].Gateway = gwInfo .Gateway
988+ break
989+ }
938990 }
939- subnetPort .Status .NetworkInterfaceConfig .LogicalSwitchUUID = * nsxSubnet .RealizationId
940991 return nil
941992}
942993
0 commit comments