@@ -866,6 +866,11 @@ func (c *FlowContext) addSubnetReconcileTasks(g *flow.Graph, desired, current *a
866866 return nil , err
867867 }
868868 suffix := fmt .Sprintf ("%s-%s" , zoneName , subnetKey )
869+ if ptr .Deref (desired .AssignIpv6AddressOnCreation , true ) {
870+ return c .AddTask (g , "ensure IPv6 subnet " + suffix ,
871+ c .ensureSubnetIPv6 (subnetKey , desired , current ),
872+ Timeout (defaultTimeout )), nil
873+ }
869874 return c .AddTask (g , "ensure subnet " + suffix ,
870875 c .ensureSubnet (subnetKey , desired , current ),
871876 Timeout (defaultTimeout )), nil
@@ -976,6 +981,55 @@ func (c *FlowContext) ensureSubnet(subnetKey string, desired, current *awsclient
976981 }
977982}
978983
984+ func (c * FlowContext ) ensureSubnetIPv6 (subnetKey string , desired , current * awsclient.Subnet ) flow.TaskFn {
985+ zoneChild := c .getSubnetZoneChildByItem (desired )
986+ if current == nil {
987+ return func (ctx context.Context ) error {
988+ log := LogFromContext (ctx )
989+ log .Info ("creating..." )
990+ var lastErr error
991+ for attempts := 0 ; attempts < 256 ; attempts ++ {
992+ created , err := c .client .CreateSubnet (ctx , desired , defaultTimeout )
993+ if err == nil {
994+ zoneChild .Set (subnetKey , created .SubnetId )
995+ return nil
996+ }
997+ // Check for InvalidSubnet.Conflict error
998+ apiErrCode := awsclient .GetAWSAPIErrorCode (err )
999+ if apiErrCode == "InvalidSubnet.Conflict" {
1000+ log .Info ("CIDR conflict, trying next CIDR block" )
1001+ newCIDRs , nextErr := calcNextIPv6CidrBlock (desired .Ipv6CidrBlocks [0 ])
1002+ if nextErr != nil {
1003+ return nextErr
1004+ }
1005+ desired .Ipv6CidrBlocks = []string {newCIDRs }
1006+ lastErr = err
1007+ continue
1008+ }
1009+ // Any other error, return immediately
1010+ return err
1011+ }
1012+ // If we exhausted all attempts, return the last error
1013+ if lastErr != nil {
1014+ return lastErr
1015+ }
1016+ return fmt .Errorf ("failed to create subnet after multiple attempts" )
1017+ }
1018+ }
1019+ return func (ctx context.Context ) error {
1020+ zoneChild .Set (subnetKey , current .SubnetId )
1021+ modified , err := c .updater .UpdateSubnet (ctx , desired , current )
1022+ if err != nil {
1023+ return err
1024+ }
1025+ if modified {
1026+ log := LogFromContext (ctx )
1027+ log .Info ("updated" )
1028+ }
1029+ return nil
1030+ }
1031+ }
1032+
9791033func (c * FlowContext ) ensureSubnetCidrReservation (ctx context.Context ) error {
9801034 if ! isIPv6 (c .getIpFamilies ()) {
9811035 return nil
@@ -1964,3 +2018,30 @@ func cidrSubnet(baseCIDR string, newPrefixLength int, index int) (string, error)
19642018 subnetIP := net .IP (big .NewInt (0 ).Add (big .NewInt (0 ).SetBytes (baseIP ), offset ).Bytes ())
19652019 return fmt .Sprintf ("%s/%d" , subnetIP .String (), newPrefixLength ), nil
19662020}
2021+
2022+ // calcNextIPv6CidrBlock returns the next IPv6 /64 subnet CIDR block within the same /56 VPC range.
2023+ // It increments the 8th byte of the IP address (index 7) to generate the next subnet.
2024+ // This is used to avoid subnet conflicts when creating IPv6 subnets.
2025+ // Returns an error if the maximum index (255) is reached or the input CIDR is invalid.
2026+ func calcNextIPv6CidrBlock (currentSubnetCIDR string ) (string , error ) {
2027+ ip , _ , err := net .ParseCIDR (currentSubnetCIDR )
2028+ if err != nil {
2029+ return "" , fmt .Errorf ("failed to parse CIDR: %v" , err )
2030+ }
2031+
2032+ currentIndex := int (ip [7 ])
2033+
2034+ if currentIndex >= 255 {
2035+ return "" , fmt .Errorf ("already at maximum index (255) within /56 range" )
2036+ }
2037+
2038+ nextIndex := currentIndex + 1
2039+
2040+ nextIP := make (net.IP , 16 )
2041+ copy (nextIP , ip )
2042+ nextIP [7 ] = byte (nextIndex )
2043+
2044+ nextCIDR := fmt .Sprintf ("%s/64" , nextIP .String ())
2045+
2046+ return nextCIDR , nil
2047+ }
0 commit comments