Skip to content

Commit

Permalink
feat(VpcV2): add BYOIP IPv6 to VPCv2
Browse files Browse the repository at this point in the history
  • Loading branch information
shikha372 committed Jan 14, 2025
1 parent c7d6fb6 commit 3bede9a
Show file tree
Hide file tree
Showing 14 changed files with 1,045 additions and 25 deletions.
2 changes: 1 addition & 1 deletion packages/@aws-cdk/aws-ec2-alpha/lib/subnet-v2.ts
Original file line number Diff line number Diff line change
Expand Up @@ -455,7 +455,7 @@ function storeSubnetToVpcByType(vpc: IVpcV2, subnet: SubnetV2, type: SubnetType)
function validateSupportIpv6(vpc: IVpcV2) {
if (vpc.secondaryCidrBlock) {
if (vpc.secondaryCidrBlock.some((secondaryAddress) => secondaryAddress.amazonProvidedIpv6CidrBlock === true ||
secondaryAddress.ipv6IpamPoolId != undefined)) {
secondaryAddress.ipv6IpamPoolId != undefined || secondaryAddress.ipv6Pool != undefined)) {
return true;
} else {
throw new Error('To use IPv6, the VPC must enable IPv6 support.');
Expand Down
86 changes: 65 additions & 21 deletions packages/@aws-cdk/aws-ec2-alpha/lib/util.ts
Original file line number Diff line number Diff line change
Expand Up @@ -323,13 +323,65 @@ export class CidrBlockIpv6 {
}

/**
* @returns Maximum IPv6 address for a provided CIDR
* Calculates the maximum IPv6 address in the CIDR block
*
* For example, with CIDR 2001:db8::8/56:
*
* 1. networkPartLength = Math.ceil(56/16) = 4 parts needed for network portion
* 2. remainingBits = 56 % 16 = 8 bits remaining in last network part
* 3. endIP starts as [2001, db8, 0, 0] (network part)
* 4. For remaining bits (8), creates mask: (1 << (16-8)) - 1 = 0x00FF
* 5. Applies mask to last network part: 0x0000 | 0x00FF = 0x00FF
* 6. Fills remaining parts with 0xFFFF: [2001, db8, 0, ff, ffff, ffff, ffff, ffff]
* 7. Final address: 2001:db8:0:ff:ffff:ffff:ffff:ffff
*
* @returns The maximum IPv6 address as a string
*/
public maxIp(): string {
/**
* Calculate how many 16-bit blocks are needed for the network portion
* e.g. for /56, networkPartLength = ceil(56/16) = 4 blocks
*/
const networkPartLength = Math.ceil(this.cidrPrefix / 16);
/**
* Calculate remaining bits in last network block
* e.g. for /56, remainingBits = 56 % 16 = 8 bits
*/
const remainingBits = this.cidrPrefix % 16;
/**
* Create copy of network portion of address
* e.g. [2001, db8, 0, 0] for 2001:db8::/56
*/
const endIP = [...this.networkPart];
const hostPart = Array(8 - this.networkPart.length).fill(BigInt(0xffff));
endIP.push(...hostPart);

/**
* If there are remaining bits in last network block,
* create appropriate bitmask and apply to last network block
* e.g. for /56: mask = (1 << (16-8)) - 1 = 0x00FF
*/
if (remainingBits > 0) {
const lastNetworkIndex = networkPartLength - 1;
const mask = (BigInt(1) << BigInt(16 - remainingBits)) - BigInt(1);
/**
* Apply bitmask to last network block using bitwise OR
* e.g. if lastNetworkIndex=3 and mask=0x00FF:
* networkPart[3]=0x0000 | 0x00FF = 0x00FF
*/
endIP[lastNetworkIndex] = this.networkPart[lastNetworkIndex] | mask;
}

/**
* Fill remaining blocks with maximum value 0xFFFF
* e.g. [2001, db8, 0, ff, ffff, ffff, ffff, ffff]
*/
for (let i = networkPartLength; i < 8; i++) {
endIP.push(BigInt('0xffff'));
}

/**
* Convert blocks to hex strings and join with colons
* e.g. 2001:db8:0:ff:ffff:ffff:ffff:ffff
*/
return endIP.map(this.formatIPv6Part).join(':');
}

Expand All @@ -342,26 +394,18 @@ export class CidrBlockIpv6 {
* @returns true if two ranges overlap, false otherwise
*/
public rangesOverlap(range1: string, range2: string): boolean {
const [start1, end1] = this.getIPv6Range(range1);
const [start2, end2] = this.getIPv6Range(range2);
// Create new CidrBlockIpv6 instances for both ranges
const cidr1 = new CidrBlockIpv6(range1);
const cidr2 = new CidrBlockIpv6(range2);

return (start1 <= end2) && (start2 <= end1);
}
// Convert min and max IPs to numeric values for comparison
const start1 = this.ipv6ToNumber(cidr1.minIp());
const end1 = this.ipv6ToNumber(cidr1.maxIp());
const start2 = this.ipv6ToNumber(cidr2.minIp());
const end2 = this.ipv6ToNumber(cidr2.maxIp());

/**
*
* @param cidr
* @returns Range in the from of big int number [start, end]
*/
private getIPv6Range(cidr: string): [bigint, bigint] {
const [ipv6Address, prefixLength] = cidr.split('/');
const ipv6Number = this.ipv6ToNumber(ipv6Address);
const mask = (BigInt(1) << BigInt(128 - Number(prefixLength))) - BigInt(1);
const networkPrefix = ipv6Number & ~mask;
const start = networkPrefix;
const end = networkPrefix | mask;

return [start, end];
// Check for overlap
return (start1 <= end2) && (start2 <= end1);
}

/**
Expand Down
2 changes: 1 addition & 1 deletion packages/@aws-cdk/aws-ec2-alpha/lib/vpc-v2-base.ts
Original file line number Diff line number Diff line change
Expand Up @@ -430,7 +430,7 @@ export abstract class VpcV2Base extends Resource implements IVpcV2 {
let useIpv6;
if (this.secondaryCidrBlock) {
useIpv6 = (this.secondaryCidrBlock.some((secondaryAddress) => secondaryAddress.amazonProvidedIpv6CidrBlock === true ||
secondaryAddress.ipv6IpamPoolId != undefined));
secondaryAddress.ipv6IpamPoolId != undefined || secondaryAddress.ipv6CidrBlock !== undefined));
}

if (!useIpv6) {
Expand Down
99 changes: 98 additions & 1 deletion packages/@aws-cdk/aws-ec2-alpha/lib/vpc-v2.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,27 @@ export interface SecondaryAddressProps {
readonly cidrBlockName: string;
}

/**
* Additional props needed for BYOIP IPv6 address props
*/
export interface Ipv6PoolSecondaryAddressProps extends SecondaryAddressProps {
/**
* ID of the IPv6 address pool from which to allocate the IPv6 CIDR block
* Note: BYOIP Pool ID is different than the pool ID of IPAM.
* To onboard your IPv6 address range to AWS account please refer to below documentation
* @see https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/byoip-onboard.html
*/
readonly ipv6Pool: string;

/**
* An valid IPv6 CIDR block from the IPv6 address pool onboarded to AWS using BYOIP.
* The most specific IPv6 address range that you can bring is /48 for CIDRs that are publicly advertisable
* and /56 for CIDRs that are not publicly advertisable.
* @see https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/ec2-byoip.html#byoip-definitions
*/
readonly ipv6CidrBlock: string;
}

/**
* IpAddress options to define VPC V2
*/
Expand Down Expand Up @@ -49,6 +70,13 @@ export class IpAddresses {
public static amazonProvidedIpv6(props: SecondaryAddressProps) : IIpAddresses {
return new AmazonProvided(props);
}

/**
* A BYOIP IPv6 address pool
*/
public static ipv6Pool(props: Ipv6PoolSecondaryAddressProps): IIpAddresses {
return new Ipv6Pool(props);
}
}

/**
Expand Down Expand Up @@ -121,6 +149,20 @@ export interface VpcCidrOptions {
* @default - no IPAM IPv4 CIDR range is provisioned using IPAM
*/
readonly ipv4IpamProvisionedCidrs?: string[];

/**
* IPv6 CIDR block from the IPv6 address pool.
*
* @default - None
*/
readonly ipv6CidrBlock?: string;

/**
* ID of the IPv6 address pool from which to allocate the IPv6 CIDR block
*
* @default - None
*/
readonly ipv6Pool?: string;
}

/**
Expand Down Expand Up @@ -499,7 +541,7 @@ export class VpcV2 extends VpcV2Base {
throw new Error('Cidr Block Name is required to create secondary IP address');
}

if (secondaryVpcOptions.amazonProvided || secondaryVpcOptions.ipv6IpamPool) {
if (secondaryVpcOptions.amazonProvided || secondaryVpcOptions.ipv6IpamPool || secondaryVpcOptions.ipv6Pool) {
this.useIpv6 = true;
}
//validate CIDR ranges per RFC 1918
Expand All @@ -520,6 +562,10 @@ export class VpcV2 extends VpcV2Base {
ipv6NetmaskLength: secondaryVpcOptions.ipv6NetmaskLength,
ipv6IpamPoolId: secondaryVpcOptions.ipv6IpamPool?.ipamPoolId,
amazonProvidedIpv6CidrBlock: secondaryVpcOptions.amazonProvided,
//BYOIP IPv6 Address
ipv6CidrBlock: secondaryVpcOptions?.ipv6CidrBlock,
//BYOIP Pool for IPv6 address
ipv6Pool: secondaryVpcOptions?.ipv6Pool,
});
if (secondaryVpcOptions.dependencies) {
for (const dep of secondaryVpcOptions.dependencies) {
Expand Down Expand Up @@ -633,6 +679,23 @@ class IpamIpv4 implements IIpAddresses {
}
}

/**
* Supports assigning IPv6 address to VPC in an address pool
*/
class Ipv6Pool implements IIpAddresses {

constructor(private readonly props: Ipv6PoolSecondaryAddressProps) {
}

allocateVpcCidr(): VpcCidrOptions {
return {
ipv6CidrBlock: this.props.ipv6CidrBlock,
ipv6Pool: this.props.ipv6Pool,
cidrBlockName: this.props?.cidrBlockName,
};
}
}

/**
* Interface to create L2 for VPC Cidr Block
*/
Expand All @@ -658,6 +721,16 @@ export interface IVPCCidrBlock {
* IPAM pool for IPv4 address type
*/
readonly ipv4IpamPoolId ?: string;

/**
* The IPv6 CIDR block from the specified IPv6 address pool.
*/
readonly ipv6CidrBlock?: string;

/**
* The ID of the IPv6 address pool from which to allocate the IPv6 CIDR block.
*/
readonly ipv6Pool?: string;
}

/**
Expand Down Expand Up @@ -721,6 +794,20 @@ export interface VPCCidrBlockattributes {
* @default - no IPAM IPv4 CIDR range is provisioned using IPAM
*/
readonly ipv4IpamProvisionedCidrs?: string[];

/**
* The IPv6 CIDR block from the specified IPv6 address pool.
*
* @default - No IPv6 CIDR block associated with VPC.
*/
readonly ipv6CidrBlock?: string;

/**
* The ID of the IPv6 address pool from which to allocate the IPv6 CIDR block.
* Note: BYOIP Pool ID is different than IPAM Pool ID.
* @default - No BYOIP pool associated with VPC.
*/
readonly ipv6Pool?: string;
}

/**
Expand Down Expand Up @@ -748,6 +835,9 @@ class VPCCidrBlock extends Resource implements IVPCCidrBlock {
public readonly amazonProvidedIpv6CidrBlock ?: boolean = props.amazonProvidedIpv6CidrBlock;
public readonly ipv6IpamPoolId ?: string = props.ipv6IpamPoolId;
public readonly ipv4IpamPoolId ?: string = props.ipv4IpamPoolId;
//BYOIP Pool Attributes
public readonly ipv6Pool?: string = props.ipv6Pool;
public readonly ipv6CidrBlock?: string = props.ipv6CidrBlock;
}
return new Import(scope, id);
}
Expand All @@ -762,6 +852,10 @@ class VPCCidrBlock extends Resource implements IVPCCidrBlock {

public readonly ipv4IpamPoolId?: string;

public readonly ipv6CidrBlock?: string;

public readonly ipv6Pool?: string;

constructor(scope: Construct, id: string, props: VPCCidrBlockProps) {
super(scope, id);
this.resource = new CfnVPCCidrBlock(this, id, props);
Expand All @@ -770,6 +864,9 @@ class VPCCidrBlock extends Resource implements IVPCCidrBlock {
this.ipv6IpamPoolId = props.ipv6IpamPoolId;
this.ipv4IpamPoolId = props.ipv4IpamPoolId;
this.amazonProvidedIpv6CidrBlock = props.amazonProvidedIpv6CidrBlock;
//BYOIP Pool and CIDR Block
this.ipv6CidrBlock = props.ipv6CidrBlock;
this.ipv6Pool = props.ipv6Pool;
}
}

Expand Down

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading

0 comments on commit 3bede9a

Please sign in to comment.