A step-by-step walkthrough of enabling IPv6 on two OCI VCNs, connecting them, and validating cross-VCN reachability with ICMPv6 — including the one protocol mistake that trips up almost everyone.
IPv6 adoption inside Oracle Cloud Infrastructure (OCI) tends to expose a gap most engineers don't hit until they're already mid-deployment: enabling IPv6 on a VCN is straightforward, but getting two separate VCNs to actually talk to each other over IPv6 — and proving it with something as simple as a ping6 — involves several layers most IPv4-only deployments never touch. This post walks through configuring IPv6 on two OCI VCNs end to end, connecting them, and validating connectivity with ICMPv6.
It's tempting to treat IPv6 configuration in OCI as a drop-in replacement for IPv4 — same VCN, same subnets, just bigger addresses. In practice, three things make cross-VCN IPv6 its own configuration surface.
ICMP and ICMPv6 as separate options — allowing one does nothing for the otherIPv6 is enabled at VCN creation or added to an existing VCN. Via the OCI CLI:
# Create a new VCN with IPv6 enabled (Oracle-allocated GUA) oci network vcn create \ --compartment-id "ocid1.compartment.oc1...." \ --display-name "vcn-a" \ --cidr-blocks '["10.0.0.0/16"]' \ --is-ipv6-enabled true # Or add an IPv6 CIDR to an existing VCN oci network vcn add-ipv6-vcn-cidr \ --vcn-id "ocid1.vcn.oc1...." \ --is-oracle-gua-allocation-enabled true
Repeat this for the second VCN (vcn-b), using a separate IPv4 CIDR block. Each VCN receives its own /56 IPv6 GUA prefix from Oracle — you don't choose the prefix yourself unless you're using ULA or BYOIPv6.
Each subnet needs its own /64 slice of the VCN's /56. In Terraform, this is typically done with cidrsubnet() against the VCN's allocated IPv6 block:
resource "oci_core_subnet" "subnet_a" {
compartment_id = var.compartment_id
vcn_id = oci_core_vcn.vcn_a.id
display_name = "subnet-a"
cidr_block = "10.0.1.0/24"
ipv6cidr_block = cidrsubnet(oci_core_vcn.vcn_a.ipv6cidr_blocks[0], 8, 0)
route_table_id = oci_core_route_table.rt_a.id
security_list_ids = [oci_core_security_list.sl_a.id]
}Do the same for subnet_b in vcn_b. At this point both subnets are IPv6-capable, but no instance has an IPv6 address yet, and the two VCNs still can't route to each other.
Launching an instance into an IPv6-enabled subnet does not automatically give it an IPv6 address — you have to request one explicitly at VNIC creation:
create_vnic_details {
subnet_id = oci_core_subnet.subnet_a.id
assign_public_ip = true
assign_ipv6ip = true
}Inside the instance itself, the address often still needs to be fetched via DHCPv6, depending on the OS image:
sudo dhclient -6 ens3 ip -6 addr show
Confirm the address actually landed on the interface before moving on — a surprising number of “connectivity” issues later in this process turn out to be a VNIC with an IPv6 address assigned by OCI but never picked up by the guest OS.
For two VCNs in the same region, the simplest path is a Local Peering Gateway (LPG) on each VCN, peered to each other. For cross-region or hub-and-spoke topologies, a Dynamic Routing Gateway (DRG) with both VCNs attached is the standard approach. Either way, IPv6 traffic needs its own route rules — dual-stack VCNs don't inherit IPv6 routing from their IPv4 route rules.
Route table entry on vcn-a, pointing IPv6 traffic destined for vcn-b's prefix at the peering connection:
oci network route-table update \
--rt-id "ocid1.routetable.oc1...." \
--route-rules '[
{
"destination": "<vcn-b-ipv6-prefix>/56",
"destinationType": "CIDR_BLOCK",
"networkEntityId": "<lpg-or-drg-ocid>"
}
]'vcn-b's route table, pointing back at vcn-a's IPv6 prefix. Both directions need explicit rules — OCI routing is not automatically bidirectional just because a peering connection exists.This is where most cross-VCN IPv6 setups actually fail, and it's almost always the same mistake: security lists configured for ICMP instead of ICMPv6. In the OCI console or CLI, these are distinct protocol options, and permitting one does nothing for the other.
Ingress rule on vcn-a's security list, allowing ICMPv6 from vcn-b's prefix:
Egress rule, permitting outbound IPv6 traffic to vcn-b: destination <vcn-b-ipv6-prefix>/56, IP Protocol All. Repeat symmetrically on vcn-b's security list.
From an instance in vcn-a, ping the IPv6 address of an instance in vcn-b:
ping6 -c 4 <vcn-b-instance-ipv6-address>
If this fails, work through the failure points in order rather than guessing:
ip -6 addr show)ip6tables -L INPUT -n -v on Linux imagesOnce ping6 succeeds in both directions, you've confirmed the full path: IPv6 addressing, subnet allocation, inter-VCN routing, and security rules are all correctly aligned.
Cross-VCN IPv6 connectivity matters beyond the lab exercise of getting ping6 to succeed. It's the same underlying mechanism used for IPv6-enabled hybrid connectivity over FastConnect or Site-to-Site VPN, for resolving overlapping IPv4 CIDR conflicts between VCNs by routing over IPv6 instead, and for any architecture where dual-stack support is a compliance or interoperability requirement rather than a preference. Getting the fundamentals right at the two-VCN scale — correct prefix allocation, explicit routing in both directions, and protocol-correct security rules — is what makes those larger topologies tractable rather than a fresh source of the same ICMP/ICMPv6 mistake at greater scale.
Recap: what to take away
- A VCN gets a /56 IPv6 GUA prefix; each subnet gets a /64 carved from it — and instances still need an explicit IPv6 address assignment.
- Connecting two VCNs requires an LPG (same region) or DRG (cross-region), plus explicit IPv6 route rules in both directions.
- ICMP and ICMPv6 are separate protocol options in Oracle OCI security lists and NSGs — this mismatch is the most common reason
ping6fails. - When troubleshooting, check address assignment, routing, security rules, and the guest OS firewall — in that order.