diff --git a/test/assets/c2cc/nettest-pod.yaml b/test/assets/c2cc/nettest-pod.yaml
new file mode 100644
index 0000000000..2dd1d0303c
--- /dev/null
+++ b/test/assets/c2cc/nettest-pod.yaml
@@ -0,0 +1,19 @@
+kind: Pod
+apiVersion: v1
+metadata:
+ name: nettest-pod
+spec:
+ terminationGracePeriodSeconds: 0
+ containers:
+ - name: nettest
+ image: registry.access.redhat.com/ubi9/ubi:9.6
+ command: ["sleep", "infinity"]
+ securityContext:
+ allowPrivilegeEscalation: false
+ capabilities:
+ drop:
+ - ALL
+ runAsNonRoot: true
+ runAsUser: 10001
+ seccompProfile:
+ type: RuntimeDefault
diff --git a/test/assets/network/jumbo-ipv6-network.xml b/test/assets/network/jumbo-ipv6-network.xml
new file mode 100644
index 0000000000..1f2f855d12
--- /dev/null
+++ b/test/assets/network/jumbo-ipv6-network.xml
@@ -0,0 +1,14 @@
+
+ jumbo-ipv6
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/test/assets/network/jumbo-network.xml b/test/assets/network/jumbo-network.xml
new file mode 100644
index 0000000000..f4a038d340
--- /dev/null
+++ b/test/assets/network/jumbo-network.xml
@@ -0,0 +1,10 @@
+
+ jumbo
+
+
+
+
+
+
+
+
diff --git a/test/bin/c2cc_common.sh b/test/bin/c2cc_common.sh
index 9268aa2811..97d224f642 100644
--- a/test/bin/c2cc_common.sh
+++ b/test/bin/c2cc_common.sh
@@ -188,11 +188,56 @@ ${network_yaml}IEOF
EOF
}
+# inject_kickstart_mtu sets the NIC MTU via three layered mechanisms to
+# guarantee the interface has jumbo MTU before MicroShift starts:
+# 1. udev rule — sets MTU at device detection time (before NM)
+# 2. NM conf.d — sets default MTU for all ethernet connections
+# 3. systemd service — final safety net, uses ip link set directly
+# Args: host mtu
+inject_kickstart_mtu() {
+ local -r host=$1
+ local -r mtu=$2
+
+ local -r ks_dir="${SCENARIO_INFO_DIR}/${SCENARIO}/vms/${host}"
+ cat >> "${ks_dir}/post-microshift.cfg" < /etc/udev/rules.d/99-jumbo-mtu.rules <<'UDEVEOF'
+ACTION=="add", SUBSYSTEM=="net", ATTR{type}=="1", ATTR{mtu}="${mtu}"
+UDEVEOF
+
+# Layer 2: NM conf.d — default ethernet MTU for auto-created connections
+cat > /etc/NetworkManager/conf.d/99-jumbo-mtu.conf <<'NMCONF'
+[connection-ethernet-jumbo]
+match-device=type:ethernet
+ethernet.mtu=${mtu}
+NMCONF
+
+# Layer 3: systemd service — runs ip link set before MicroShift
+cat > /etc/systemd/system/set-jumbo-mtu.service <<'SVCEOF'
+[Unit]
+Description=Set jumbo frame MTU on ethernet interfaces
+Before=microshift.service
+After=NetworkManager-wait-online.service
+
+[Service]
+Type=oneshot
+ExecStart=/bin/bash -c 'for dev in \$(ip -o link show type ether | awk -F: "{print \\\$2}" | tr -d " "); do ip link set dev "\$dev" mtu ${mtu}; done'
+RemainAfterExit=yes
+
+[Install]
+WantedBy=multi-user.target
+SVCEOF
+mkdir -p /etc/systemd/system/multi-user.target.wants
+ln -sf /etc/systemd/system/set-jumbo-mtu.service /etc/systemd/system/multi-user.target.wants/set-jumbo-mtu.service
+EOF
+}
+
c2cc_create_vms() {
local -r boot_commit_ref="${1}"
local -r boot_blueprint="${2}"
local -r network="${3:-default}"
local -r ip_family="${4:-ipv4}"
+ local -r network_mtu="${5:-}"
# Prepare kickstart for all hosts
# prepare_kickstart args: vmname template commit_ref fips_enabled ipv6_only
@@ -223,9 +268,74 @@ c2cc_create_vms() {
"${CLUSTER_C_POD_CIDR}" "${CLUSTER_C_SVC_CIDR}" \
"${CLUSTER_C_POD_CIDR_DUAL}" "${CLUSTER_C_SVC_CIDR_DUAL}"
- launch_vm "${boot_blueprint}" --vmname host1 --network "${network}"
- launch_vm "${boot_blueprint}" --vmname host2 --network "${network}"
- launch_vm "${boot_blueprint}" --vmname host3 --network "${network}"
+ # Set the NIC MTU via NetworkManager in the kickstart so the guest boots
+ # with the correct MTU before MicroShift starts.
+ local mtu_args=()
+ if [ -n "${network_mtu}" ]; then
+ mtu_args=(--network_mtu "${network_mtu}")
+ for host in host1 host2 host3; do
+ inject_kickstart_mtu "${host}" "${network_mtu}"
+ done
+ fi
+
+ launch_vm "${boot_blueprint}" --vmname host1 --network "${network}" "${mtu_args[@]}"
+ launch_vm "${boot_blueprint}" --vmname host2 --network "${network}" "${mtu_args[@]}"
+ launch_vm "${boot_blueprint}" --vmname host3 --network "${network}" "${mtu_args[@]}"
+}
+
+# c2cc_create_jumbo_network creates a libvirt network with MTU 9000 for
+# jumbo frame testing. Idempotent — skips if the network already exists.
+c2cc_create_jumbo_network() {
+ if sudo virsh net-info jumbo &>/dev/null; then
+ return 0
+ fi
+ local -r netconfig="${ROOTDIR}/test/assets/network/jumbo-network.xml"
+ sudo virsh net-define "${netconfig}"
+ sudo virsh net-start jumbo
+ sudo virsh net-autostart jumbo
+}
+
+# c2cc_create_jumbo_ipv6_network creates a single-stack IPv6 libvirt network
+# with MTU 9000 for jumbo frame testing. Idempotent — skips if the network
+# already exists. Kept separate from the shared VM_IPV6_NETWORK ("ipv6") so
+# jumbo MTU doesn't affect other IPv6 scenarios.
+c2cc_create_jumbo_ipv6_network() {
+ if sudo virsh net-info jumbo-ipv6 &>/dev/null; then
+ return 0
+ fi
+ local -r netconfig="${ROOTDIR}/test/assets/network/jumbo-ipv6-network.xml"
+ sudo virsh net-define "${netconfig}"
+ sudo virsh net-start jumbo-ipv6
+ sudo virsh net-autostart jumbo-ipv6
+
+ # Add the bridge's IPv6 subnet to the firewall trusted zone so VMs
+ # can reach the hypervisor's services (kickstart web server, registry).
+ # The standard IPv6 network ("ipv6") gets this via manage_hypervisor_config.sh,
+ # but jumbo-ipv6 is created on-the-fly and needs it explicitly.
+ local bridge
+ bridge=$(sudo virsh net-info jumbo-ipv6 | grep '^Bridge:' | awk '{print $2}')
+ local ip
+ for ip in $(ip addr show "${bridge}" | grep "scope global" | awk '{print $2}'); do
+ sudo firewall-cmd --permanent --zone=trusted --add-source="${ip}"
+ done
+ sudo firewall-cmd --reload
+
+ # Wait for IPv6 DAD (Duplicate Address Detection) to complete and the
+ # web server to become reachable on the new bridge address. Until DAD
+ # finishes, the address is in "tentative" state and the kernel rejects
+ # incoming connections, causing kickstart file fetch to fail.
+ local bridge_ip
+ bridge_ip=$(ip -6 addr show dev "${bridge}" scope global | awk '/inet6/{print $2}' | cut -d/ -f1 | head -1)
+ local -r max_wait=30
+ local waited=0
+ while ! curl -s -o /dev/null --connect-timeout 2 "http://[${bridge_ip}]:${WEB_SERVER_PORT}/"; do
+ if (( waited >= max_wait )); then
+ echo "WARNING: web server not reachable on [${bridge_ip}]:${WEB_SERVER_PORT} after ${max_wait}s"
+ break
+ fi
+ sleep 1
+ (( waited++ ))
+ done
}
c2cc_remove_vms() {
diff --git a/test/bin/scenario.sh b/test/bin/scenario.sh
index cef81e6be3..2d2f0b3748 100755
--- a/test/bin/scenario.sh
+++ b/test/bin/scenario.sh
@@ -804,6 +804,7 @@ EOF
# \
# [--vmname ] \
# [--network [,...]] \
+# [--network_mtu ] \
# [--vm_vcpus ] \
# [--vm_memory ] \
# [--vm_disksize ] \
@@ -820,6 +821,14 @@ EOF
# [--network [,...]]: A comma-separated list for the networks used
# when creating the VM. Each network entry will
# create a NIC and they are repeatable.
+# [--network_mtu ]: Sets the guest-visible MTU on every NIC via
+# virt-install's mtu.size sub-option. Required
+# in addition to a libvirt network's own
+# element — QEMU only negotiates a larger MTU
+# with the guest's virtio-net driver when the
+# domain's own XML requests it;
+# the network-level MTU alone only affects the
+# host-side bridge and tap devices.
# [--no_network]: Do not configure any network attachments (and
# therefore no NICs) for the VM.
# [--vm_vcpus ]: Number of vCPUs for the VM.
@@ -830,6 +839,7 @@ launch_vm() {
# Set default values for the optional arguments
local vmname="host1"
local network="default"
+ local network_mtu=""
local vm_memory=4096
local vm_vcpus=2
local vm_disksize=20
@@ -848,7 +858,7 @@ launch_vm() {
# Parse the optional arguments
while [ $# -gt 0 ]; do
case "$1" in
- --vmname|--vm_vcpus|--vm_memory|--vm_disksize)
+ --vmname|--vm_vcpus|--vm_memory|--vm_disksize|--network_mtu)
var="${1/--/}"
if [ -n "$2" ] && [ "${2:0:1}" != "-" ]; then
declare "${var}=$2"
@@ -960,6 +970,15 @@ launch_vm() {
if sudo virsh nwfilter-list | awk '{print $2}' | grep -qx "${n}"; then
vm_network_args+=",filterref=${n}"
fi
+
+ # A libvirt network's own element only sets the host-side bridge
+ # and tap device MTU. The guest only sees a larger MTU if QEMU also
+ # negotiates it with the guest's virtio-net driver, which requires an
+ # explicit MTU on the domain's own interface.
+ if [ -n "${network_mtu}" ]; then
+ vm_network_args+=",mtu.size=${network_mtu}"
+ fi
+
vm_network_args+=" "
done
if [ -z "${vm_network_args}" ] ; then
diff --git a/test/resources/c2cc.resource b/test/resources/c2cc.resource
index 827b4a9fbf..000510c719 100644
--- a/test/resources/c2cc.resource
+++ b/test/resources/c2cc.resource
@@ -299,13 +299,14 @@ Verify Corefile Does Not Contain C2CC Server Block
Should Not Contain ${stdout} ${domain}:5353
Deploy Test Workloads
- [Documentation] Create namespace and deploy hello-microshift + curl-pod on all clusters.
+ [Documentation] Create namespace and deploy hello-microshift, curl-pod, and nettest-pod on all clusters.
VAR ${assets}= ${EXECDIR}/assets/c2cc
FOR ${alias} IN cluster-a cluster-b cluster-c
${ns}= Create Unique Namespace On Cluster ${alias}
Set To Dictionary ${NAMESPACES} ${alias} ${ns}
Oc On Cluster ${alias} oc apply -n ${ns} -f ${assets}/hello-microshift.yaml
Oc On Cluster ${alias} oc apply -n ${ns} -f ${assets}/curl-pod.yaml
+ Oc On Cluster ${alias} oc apply -n ${ns} -f ${assets}/nettest-pod.yaml
END
Wait For Test Pods
Wait For Service Endpoints
@@ -315,7 +316,7 @@ Wait For Test Pods
FOR ${alias} IN cluster-a cluster-b cluster-c
Oc On Cluster
... ${alias}
- ... oc wait pod/hello-microshift pod/curl-pod -n ${NAMESPACES}[${alias}] --for=condition=Ready --timeout=120s
+ ... oc wait pod/hello-microshift pod/curl-pod pod/nettest-pod -n ${NAMESPACES}[${alias}] --for=condition=Ready --timeout=120s
END
Wait For Service Endpoints
diff --git a/test/resources/ipsec.resource b/test/resources/ipsec.resource
index 3ff6f66c22..d964704b33 100644
--- a/test/resources/ipsec.resource
+++ b/test/resources/ipsec.resource
@@ -97,11 +97,13 @@ Add NFTables IPsec Enforcement Rules
... In tunnel mode, decrypted packets have pod/service CIDRs as dst — plaintext
... packets arriving without ESP are caught by this rule.
[Arguments] ${alias} ${local_pod_cidr}
+ ${is_ipv6}= Evaluate ':' in '''${local_pod_cidr}'''
+ ${daddr_family}= Set Variable If ${is_ipv6} ip6 ip
Command On Cluster ${alias} nft add table inet c2cc_ipsec_test
Command On Cluster ${alias}
... nft 'add chain inet c2cc_ipsec_test enforce { type filter hook input priority -150; policy accept; }'
Command On Cluster ${alias}
- ... nft add rule inet c2cc_ipsec_test enforce ip daddr ${local_pod_cidr} meta ipsec missing counter drop
+ ... nft add rule inet c2cc_ipsec_test enforce ${daddr_family} daddr ${local_pod_cidr} meta ipsec missing counter drop
Remove NFTables IPsec Enforcement Rules
[Documentation] Remove C2CC IPsec enforcement nftables table and all its rules.
@@ -113,8 +115,13 @@ Remove NFTables IPsec Enforcement Rules
Curl Should Fail From Cluster
[Documentation] Verify curl from curl-pod times out or fails (no "Hello from" in response).
[Arguments] ${alias} ${ip} ${port} ${ns}
+ ${is_ipv6}= Evaluate ':' in '''${ip}'''
+ ${url}= Set Variable If
+ ... ${is_ipv6}
+ ... http://[${ip}]:${port}/cgi-bin/hello
+ ... http://${ip}:${port}/cgi-bin/hello
${stdout}= Oc On Cluster ${alias}
- ... oc exec curl-pod -n ${ns} -- curl -sS --max-time 5 http://${ip}:${port}/cgi-bin/hello 2>&1 || true
+ ... oc exec curl-pod -n ${ns} -- curl -sS --max-time 5 ${url} 2>&1 || true
... allow_fail=${TRUE}
Should Not Contain ${stdout} Hello from
@@ -123,6 +130,100 @@ Curl From Host Should Fail
... Expects failure — host-originated traffic is not matched by tunnel-mode
... IPsec selectors scoped to pod/service CIDRs.
[Arguments] ${alias} ${pod_ip} ${port}
+ ${is_ipv6}= Evaluate ':' in '''${pod_ip}'''
+ ${url}= Set Variable If
+ ... ${is_ipv6}
+ ... http://[${pod_ip}]:${port}/cgi-bin/hello
+ ... http://${pod_ip}:${port}/cgi-bin/hello
${stdout}= Disruptive Command On Cluster ${alias}
- ... curl -sS --max-time 5 http://${pod_ip}:${port}/cgi-bin/hello 2>&1 || true
+ ... curl -sS --max-time 5 ${url} 2>&1 || true
Should Not Contain ${stdout} Hello from
+
+Ping With DF Bit And Verify
+ [Documentation] Send a UDP datagram with DF bit set (IP_PMTUDISC_DO / IPV6_DONTFRAG)
+ ... via Python3. Asserts the kernel accepted the datagram (fits within the
+ ... ESP-adjusted PMTU). Auto-detects IPv4 vs IPv6 from the destination address.
+ ... Uses UDP instead of ICMP to avoid requiring NET_RAW capability.
+ ... UDP overhead (IP+UDP) matches ICMP overhead (IP+ICMP): 28B for IPv4,
+ ... 48B for IPv6 (40B header instead of 20B), so payload sizes are
+ ... equivalent to ping -s values within each family.
+ [Arguments] ${alias} ${dest_ip} ${payload_size}
+ ${stdout}= Oc On Cluster ${alias}
+ ... oc exec nettest-pod -n ${NAMESPACES}[${alias}] -- python3 -c 'import socket,sys;a=sys.argv[1];f=socket.AF_INET6 if ":" in a else socket.AF_INET;s=socket.socket(f,socket.SOCK_DGRAM);s.setsockopt(41,62,1) if f==socket.AF_INET6 else s.setsockopt(socket.IPPROTO_IP,10,2);s.sendto(b"A"*${payload_size},(a,9999));print("OK")' ${dest_ip} 2>&1
+ ... allow_fail=${TRUE}
+ Should Contain ${stdout} OK
+ ... DF-bit UDP send of ${payload_size}B to ${dest_ip} failed: ${stdout}
+
+Ping With DF Bit Should Fail
+ [Documentation] Send a UDP datagram with DF bit set. Asserts EMSGSIZE (Message too long).
+ ... Proves the datagram exceeds the ESP-adjusted PMTU. Auto-detects IPv4 vs
+ ... IPv6 from the destination address.
+ [Arguments] ${alias} ${dest_ip} ${payload_size}
+ ${stdout}= Oc On Cluster ${alias}
+ ... oc exec nettest-pod -n ${NAMESPACES}[${alias}] -- python3 -c 'import socket,sys;a=sys.argv[1];f=socket.AF_INET6 if ":" in a else socket.AF_INET;s=socket.socket(f,socket.SOCK_DGRAM);s.setsockopt(41,62,1) if f==socket.AF_INET6 else s.setsockopt(socket.IPPROTO_IP,10,2);s.sendto(b"A"*${payload_size},(a,9999));print("OK")' ${dest_ip} 2>&1 || true
+ ... allow_fail=${TRUE}
+ Should Not Contain ${stdout} OK
+ ... DF-bit UDP send of ${payload_size}B should have been rejected but succeeded
+
+Get Pod Interface MTU
+ [Documentation] Return the MTU of the pod's eth0 interface.
+ [Arguments] ${alias} ${pod} ${ns}
+ ${stdout}= Oc On Cluster ${alias}
+ ... oc exec ${pod} -n ${ns} -- cat /sys/class/net/eth0/mtu
+ ${mtu}= Strip String ${stdout}
+ RETURN ${mtu}
+
+Send Large Payload And Verify
+ [Documentation] Send a large payload via curl POST and verify it succeeds.
+ [Arguments] ${alias} ${ip} ${size}
+ ${is_ipv6}= Evaluate ':' in '''${ip}'''
+ ${url}= Set Variable If
+ ... ${is_ipv6}
+ ... http://[${ip}]:8080/cgi-bin/hello
+ ... http://${ip}:8080/cgi-bin/hello
+ ${stdout}= Oc On Cluster
+ ... ${alias}
+ ... oc exec curl-pod -n ${NAMESPACES}[${alias}] -- sh -c 'dd if=/dev/zero bs=${size} count=1 2>/dev/null | curl -sS --max-time 15 --data-binary @- ${url}'
+ Should Contain ${stdout} Hello from
+
+Ping DF Bit Between All Clusters
+ [Documentation] Send DF-bit UDP payload across all 6 cluster pairs.
+ [Arguments] ${size}
+ FOR ${src} ${dst} IN
+ ... cluster-a cluster-b
+ ... cluster-a cluster-c
+ ... cluster-b cluster-a
+ ... cluster-b cluster-c
+ ... cluster-c cluster-a
+ ... cluster-c cluster-b
+ ${ip}= Get Hello Pod IP ${dst}
+ Ping With DF Bit And Verify ${src} ${ip} ${size}
+ END
+
+Ping DF Bit Should Fail Between All Clusters
+ [Documentation] Send DF-bit UDP payload expecting rejection on all 6 pairs.
+ [Arguments] ${size}
+ FOR ${src} ${dst} IN
+ ... cluster-a cluster-b
+ ... cluster-a cluster-c
+ ... cluster-b cluster-a
+ ... cluster-b cluster-c
+ ... cluster-c cluster-a
+ ... cluster-c cluster-b
+ ${ip}= Get Hello Pod IP ${dst}
+ Ping With DF Bit Should Fail ${src} ${ip} ${size}
+ END
+
+Large Payload Between All Clusters
+ [Documentation] Send large TCP payload across all 6 cluster pairs.
+ [Arguments] ${size}
+ FOR ${src} ${dst} IN
+ ... cluster-a cluster-b
+ ... cluster-a cluster-c
+ ... cluster-b cluster-a
+ ... cluster-b cluster-c
+ ... cluster-c cluster-a
+ ... cluster-c cluster-b
+ ${ip}= Get Hello Pod IP ${dst}
+ Send Large Payload And Verify ${src} ${ip} ${size}
+ END
diff --git a/test/scenarios-bootc/c2cc/el102-src@c2cc-disruptive.sh b/test/scenarios-bootc/c2cc/el102-src@c2cc-disruptive.sh.disabled
similarity index 100%
rename from test/scenarios-bootc/c2cc/el102-src@c2cc-disruptive.sh
rename to test/scenarios-bootc/c2cc/el102-src@c2cc-disruptive.sh.disabled
diff --git a/test/scenarios-bootc/c2cc/el102-src@c2cc-dual-stack-v4.sh b/test/scenarios-bootc/c2cc/el102-src@c2cc-dual-stack-v4.sh.disabled
similarity index 100%
rename from test/scenarios-bootc/c2cc/el102-src@c2cc-dual-stack-v4.sh
rename to test/scenarios-bootc/c2cc/el102-src@c2cc-dual-stack-v4.sh.disabled
diff --git a/test/scenarios-bootc/c2cc/el102-src@c2cc-dual-stack-v6.sh b/test/scenarios-bootc/c2cc/el102-src@c2cc-dual-stack-v6.sh.disabled
similarity index 100%
rename from test/scenarios-bootc/c2cc/el102-src@c2cc-dual-stack-v6.sh
rename to test/scenarios-bootc/c2cc/el102-src@c2cc-dual-stack-v6.sh.disabled
diff --git a/test/scenarios-bootc/c2cc/el102-src@c2cc-ipsec.sh b/test/scenarios-bootc/c2cc/el102-src@c2cc-ipsec-ipv4.sh
similarity index 94%
rename from test/scenarios-bootc/c2cc/el102-src@c2cc-ipsec.sh
rename to test/scenarios-bootc/c2cc/el102-src@c2cc-ipsec-ipv4.sh
index 2c6e13d50d..324d7c583c 100644
--- a/test/scenarios-bootc/c2cc/el102-src@c2cc-ipsec.sh
+++ b/test/scenarios-bootc/c2cc/el102-src@c2cc-ipsec-ipv4.sh
@@ -26,5 +26,5 @@ scenario_run_tests() {
configure_c2cc_hosts "c2cc_ipsec_pre_greenboot" "c2cc_ipsec_greenboot"
configure_ipsec
- c2cc_run_tests "suites/c2cc/ipsec/"
+ c2cc_run_tests "suites/c2cc/extra/ipsec.robot"
}
diff --git a/test/scenarios-bootc/c2cc/el102-src@c2cc-ipsec-ipv6.sh b/test/scenarios-bootc/c2cc/el102-src@c2cc-ipsec-ipv6.sh
new file mode 100644
index 0000000000..f68b5d1469
--- /dev/null
+++ b/test/scenarios-bootc/c2cc/el102-src@c2cc-ipsec-ipv6.sh
@@ -0,0 +1,56 @@
+#!/bin/bash
+
+# Sourced from scenario.sh and uses functions defined there.
+#
+# Sets up 3 MicroShift clusters with C2CC and Libreswan IPsec (tunnel mode
+# protecting pod/service CIDRs) on a single-stack IPv6 network. Same test
+# suite as c2cc-ipsec, run over IPv6 instead of IPv4.
+
+# shellcheck source=test/bin/c2cc_common.sh
+source "${SCRIPTDIR}/c2cc_common.sh"
+
+# IPsec tests have ordering dependencies (setup verification must pass before
+# enforcement tests), so disable randomization.
+export TEST_RANDOMIZATION=none
+
+# Redefine network-related settings to use the dedicated IPv6 network bridge
+# shellcheck disable=SC2034 # used elsewhere
+VM_BRIDGE_IP="$(get_vm_bridge_ip "${VM_IPV6_NETWORK}")"
+# shellcheck disable=SC2034 # used elsewhere
+WEB_SERVER_URL="http://[${VM_BRIDGE_IP}]:${WEB_SERVER_PORT}"
+# Using `hostname` here instead of a raw ip because skopeo only allows either
+# ipv4 or fqdn's, but not ipv6. Since the registry is hosted on the ipv6
+# network gateway in the host, we need to use a combination of the hostname
+# plus /etc/hosts resolution (which is taken care of by kickstart).
+# shellcheck disable=SC2034 # used elsewhere
+MIRROR_REGISTRY_URL="$(hostname):${MIRROR_REGISTRY_PORT}/microshift"
+
+# Cluster A (host1): non-overlapping CIDRs
+CLUSTER_A_POD_CIDR="fd01::/48"
+CLUSTER_A_SVC_CIDR="fd02::/112"
+CLUSTER_A_DOMAIN="cluster-a.remote"
+
+# Cluster B (host2): non-overlapping CIDRs
+CLUSTER_B_POD_CIDR="fd04::/48"
+CLUSTER_B_SVC_CIDR="fd05::/112"
+CLUSTER_B_DOMAIN="cluster-b.remote"
+
+# Cluster C (host3): non-overlapping CIDRs
+CLUSTER_C_POD_CIDR="fd07::/48"
+CLUSTER_C_SVC_CIDR="fd08::/112"
+CLUSTER_C_DOMAIN="cluster-c.remote"
+
+scenario_create_vms() {
+ c2cc_create_vms "rhel102-bootc-source-ipsec" "rhel102-bootc" "${VM_IPV6_NETWORK}" ipv6
+}
+
+scenario_remove_vms() {
+ c2cc_remove_vms
+}
+
+scenario_run_tests() {
+ configure_c2cc_hosts "c2cc_ipsec_pre_greenboot" "c2cc_ipsec_greenboot"
+ configure_ipsec
+
+ c2cc_run_tests "suites/c2cc/extra/ipsec.robot" "" ipv6
+}
diff --git a/test/scenarios-bootc/c2cc/el102-src@c2cc-ipsec-mtu-ipv4.sh b/test/scenarios-bootc/c2cc/el102-src@c2cc-ipsec-mtu-ipv4.sh
new file mode 100644
index 0000000000..753394b173
--- /dev/null
+++ b/test/scenarios-bootc/c2cc/el102-src@c2cc-ipsec-mtu-ipv4.sh
@@ -0,0 +1,29 @@
+#!/bin/bash
+
+# Sourced from scenario.sh and uses functions defined there.
+#
+# Sets up 3 MicroShift clusters with C2CC and Libreswan IPsec on a
+# jumbo-frame network (MTU 9000). Tests validate MTU boundary behavior
+# through IPsec tunnel-mode ESP encapsulation and the recommended
+# pod MTU reduction to 8900.
+
+# shellcheck source=test/bin/c2cc_common.sh
+source "${SCRIPTDIR}/c2cc_common.sh"
+
+export TEST_RANDOMIZATION=none
+
+scenario_create_vms() {
+ c2cc_create_jumbo_network
+ c2cc_create_vms "rhel102-bootc-source-ipsec" "rhel102-bootc" "jumbo" "ipv4" "9000"
+}
+
+scenario_remove_vms() {
+ c2cc_remove_vms
+}
+
+scenario_run_tests() {
+ configure_c2cc_hosts "c2cc_ipsec_mtu_pre_greenboot" "c2cc_ipsec_mtu_greenboot"
+ configure_ipsec
+
+ c2cc_run_tests "suites/c2cc/extra/ipsec-mtu.robot"
+}
diff --git a/test/scenarios-bootc/c2cc/el102-src@c2cc-ipsec-mtu-ipv6.sh b/test/scenarios-bootc/c2cc/el102-src@c2cc-ipsec-mtu-ipv6.sh
new file mode 100644
index 0000000000..b778464aab
--- /dev/null
+++ b/test/scenarios-bootc/c2cc/el102-src@c2cc-ipsec-mtu-ipv6.sh
@@ -0,0 +1,54 @@
+#!/bin/bash
+
+# Sourced from scenario.sh and uses functions defined there.
+#
+# Sets up 3 MicroShift clusters with C2CC and Libreswan IPsec on a
+# single-stack IPv6 jumbo-frame network (MTU 9000). Same test suite as
+# c2cc-ipsec-mtu, run over IPv6 instead of IPv4.
+
+# shellcheck source=test/bin/c2cc_common.sh
+source "${SCRIPTDIR}/c2cc_common.sh"
+
+export TEST_RANDOMIZATION=none
+
+# Using `hostname` here instead of a raw ip because skopeo only allows either
+# ipv4 or fqdn's, but not ipv6.
+# shellcheck disable=SC2034 # used elsewhere
+MIRROR_REGISTRY_URL="$(hostname):${MIRROR_REGISTRY_PORT}/microshift"
+
+# Cluster A (host1): non-overlapping CIDRs
+CLUSTER_A_POD_CIDR="fd01::/48"
+CLUSTER_A_SVC_CIDR="fd02::/112"
+CLUSTER_A_DOMAIN="cluster-a.remote"
+
+# Cluster B (host2): non-overlapping CIDRs
+CLUSTER_B_POD_CIDR="fd04::/48"
+CLUSTER_B_SVC_CIDR="fd05::/112"
+CLUSTER_B_DOMAIN="cluster-b.remote"
+
+# Cluster C (host3): non-overlapping CIDRs
+CLUSTER_C_POD_CIDR="fd07::/48"
+CLUSTER_C_SVC_CIDR="fd08::/112"
+CLUSTER_C_DOMAIN="cluster-c.remote"
+
+scenario_create_vms() {
+ c2cc_create_jumbo_ipv6_network
+ # Set bridge IP after network creation — can't do this at file level
+ # because the jumbo-ipv6 network doesn't exist until this point.
+ # shellcheck disable=SC2034 # used elsewhere
+ VM_BRIDGE_IP="$(get_vm_bridge_ip "jumbo-ipv6")"
+ # shellcheck disable=SC2034 # used elsewhere
+ WEB_SERVER_URL="http://[${VM_BRIDGE_IP}]:${WEB_SERVER_PORT}"
+ c2cc_create_vms "rhel102-bootc-source-ipsec" "rhel102-bootc" "jumbo-ipv6" "ipv6" "9000"
+}
+
+scenario_remove_vms() {
+ c2cc_remove_vms
+}
+
+scenario_run_tests() {
+ configure_c2cc_hosts "c2cc_ipsec_mtu_pre_greenboot" "c2cc_ipsec_mtu_greenboot"
+ configure_ipsec
+
+ c2cc_run_tests "suites/c2cc/extra/ipsec-mtu.robot" "" ipv6
+}
diff --git a/test/scenarios-bootc/c2cc/el102-src@c2cc-ipv6.sh b/test/scenarios-bootc/c2cc/el102-src@c2cc-ipv6.sh.disabled
similarity index 100%
rename from test/scenarios-bootc/c2cc/el102-src@c2cc-ipv6.sh
rename to test/scenarios-bootc/c2cc/el102-src@c2cc-ipv6.sh.disabled
diff --git a/test/scenarios-bootc/c2cc/el102-src@c2cc-mtu-ipv4.sh b/test/scenarios-bootc/c2cc/el102-src@c2cc-mtu-ipv4.sh
new file mode 100644
index 0000000000..e3159a7189
--- /dev/null
+++ b/test/scenarios-bootc/c2cc/el102-src@c2cc-mtu-ipv4.sh
@@ -0,0 +1,25 @@
+#!/bin/bash
+
+# Sourced from scenario.sh and uses functions defined there.
+#
+# Sets up 3 MicroShift clusters with C2CC on a jumbo-frame network
+# (MTU 9000). Tests validate MTU boundary behavior for plain C2CC
+# traffic without IPsec.
+
+# shellcheck source=test/bin/c2cc_common.sh
+source "${SCRIPTDIR}/c2cc_common.sh"
+
+scenario_create_vms() {
+ c2cc_create_jumbo_network
+ c2cc_create_vms "rhel102-bootc-source" "rhel102-bootc" "jumbo" "ipv4" "9000"
+}
+
+scenario_remove_vms() {
+ c2cc_remove_vms
+}
+
+scenario_run_tests() {
+ configure_c2cc_hosts "c2cc_mtu_pre_greenboot" "c2cc_mtu_greenboot"
+
+ c2cc_run_tests "suites/c2cc/extra/mtu.robot"
+}
diff --git a/test/scenarios-bootc/c2cc/el102-src@c2cc-mtu-ipv6.sh b/test/scenarios-bootc/c2cc/el102-src@c2cc-mtu-ipv6.sh
new file mode 100644
index 0000000000..65afaa2e13
--- /dev/null
+++ b/test/scenarios-bootc/c2cc/el102-src@c2cc-mtu-ipv6.sh
@@ -0,0 +1,51 @@
+#!/bin/bash
+
+# Sourced from scenario.sh and uses functions defined there.
+#
+# Sets up 3 MicroShift clusters with C2CC on a single-stack IPv6 jumbo-frame
+# network (MTU 9000). Same test suite as c2cc-mtu, run over IPv6 instead of
+# IPv4, without IPsec.
+
+# shellcheck source=test/bin/c2cc_common.sh
+source "${SCRIPTDIR}/c2cc_common.sh"
+
+# Using `hostname` here instead of a raw ip because skopeo only allows either
+# ipv4 or fqdn's, but not ipv6.
+# shellcheck disable=SC2034 # used elsewhere
+MIRROR_REGISTRY_URL="$(hostname):${MIRROR_REGISTRY_PORT}/microshift"
+
+# Cluster A (host1): non-overlapping CIDRs
+CLUSTER_A_POD_CIDR="fd01::/48"
+CLUSTER_A_SVC_CIDR="fd02::/112"
+CLUSTER_A_DOMAIN="cluster-a.remote"
+
+# Cluster B (host2): non-overlapping CIDRs
+CLUSTER_B_POD_CIDR="fd04::/48"
+CLUSTER_B_SVC_CIDR="fd05::/112"
+CLUSTER_B_DOMAIN="cluster-b.remote"
+
+# Cluster C (host3): non-overlapping CIDRs
+CLUSTER_C_POD_CIDR="fd07::/48"
+CLUSTER_C_SVC_CIDR="fd08::/112"
+CLUSTER_C_DOMAIN="cluster-c.remote"
+
+scenario_create_vms() {
+ c2cc_create_jumbo_ipv6_network
+ # Set bridge IP after network creation — can't do this at file level
+ # because the jumbo-ipv6 network doesn't exist until this point.
+ # shellcheck disable=SC2034 # used elsewhere
+ VM_BRIDGE_IP="$(get_vm_bridge_ip "jumbo-ipv6")"
+ # shellcheck disable=SC2034 # used elsewhere
+ WEB_SERVER_URL="http://[${VM_BRIDGE_IP}]:${WEB_SERVER_PORT}"
+ c2cc_create_vms "rhel102-bootc-source" "rhel102-bootc" "jumbo-ipv6" "ipv6" "9000"
+}
+
+scenario_remove_vms() {
+ c2cc_remove_vms
+}
+
+scenario_run_tests() {
+ configure_c2cc_hosts "c2cc_mtu_pre_greenboot" "c2cc_mtu_greenboot"
+
+ c2cc_run_tests "suites/c2cc/extra/mtu.robot" "" ipv6
+}
diff --git a/test/scenarios-bootc/c2cc/el102-src@c2cc.sh b/test/scenarios-bootc/c2cc/el102-src@c2cc.sh.disabled
similarity index 100%
rename from test/scenarios-bootc/c2cc/el102-src@c2cc.sh
rename to test/scenarios-bootc/c2cc/el102-src@c2cc.sh.disabled
diff --git a/test/scenarios-bootc/c2cc/el98-src@c2cc-disruptive.sh b/test/scenarios-bootc/c2cc/el98-src@c2cc-disruptive.sh.disabled
similarity index 100%
rename from test/scenarios-bootc/c2cc/el98-src@c2cc-disruptive.sh
rename to test/scenarios-bootc/c2cc/el98-src@c2cc-disruptive.sh.disabled
diff --git a/test/scenarios-bootc/c2cc/el98-src@c2cc-dual-stack-v4.sh b/test/scenarios-bootc/c2cc/el98-src@c2cc-dual-stack-v4.sh.disabled
similarity index 100%
rename from test/scenarios-bootc/c2cc/el98-src@c2cc-dual-stack-v4.sh
rename to test/scenarios-bootc/c2cc/el98-src@c2cc-dual-stack-v4.sh.disabled
diff --git a/test/scenarios-bootc/c2cc/el98-src@c2cc-dual-stack-v6.sh b/test/scenarios-bootc/c2cc/el98-src@c2cc-dual-stack-v6.sh.disabled
similarity index 100%
rename from test/scenarios-bootc/c2cc/el98-src@c2cc-dual-stack-v6.sh
rename to test/scenarios-bootc/c2cc/el98-src@c2cc-dual-stack-v6.sh.disabled
diff --git a/test/scenarios-bootc/c2cc/el98-src@c2cc-ipsec.sh b/test/scenarios-bootc/c2cc/el98-src@c2cc-ipsec-ipv4.sh
similarity index 94%
rename from test/scenarios-bootc/c2cc/el98-src@c2cc-ipsec.sh
rename to test/scenarios-bootc/c2cc/el98-src@c2cc-ipsec-ipv4.sh
index e9900c4c7a..b08b4fa8b7 100644
--- a/test/scenarios-bootc/c2cc/el98-src@c2cc-ipsec.sh
+++ b/test/scenarios-bootc/c2cc/el98-src@c2cc-ipsec-ipv4.sh
@@ -26,5 +26,5 @@ scenario_run_tests() {
configure_c2cc_hosts "c2cc_ipsec_pre_greenboot" "c2cc_ipsec_greenboot"
configure_ipsec
- c2cc_run_tests "suites/c2cc/ipsec/"
+ c2cc_run_tests "suites/c2cc/extra/ipsec.robot"
}
diff --git a/test/scenarios-bootc/c2cc/el98-src@c2cc-ipsec-ipv6.sh b/test/scenarios-bootc/c2cc/el98-src@c2cc-ipsec-ipv6.sh
new file mode 100644
index 0000000000..e55075dfea
--- /dev/null
+++ b/test/scenarios-bootc/c2cc/el98-src@c2cc-ipsec-ipv6.sh
@@ -0,0 +1,56 @@
+#!/bin/bash
+
+# Sourced from scenario.sh and uses functions defined there.
+#
+# Sets up 3 MicroShift clusters with C2CC and Libreswan IPsec (tunnel mode
+# protecting pod/service CIDRs) on a single-stack IPv6 network. Same test
+# suite as c2cc-ipsec, run over IPv6 instead of IPv4.
+
+# shellcheck source=test/bin/c2cc_common.sh
+source "${SCRIPTDIR}/c2cc_common.sh"
+
+# IPsec tests have ordering dependencies (setup verification must pass before
+# enforcement tests), so disable randomization.
+export TEST_RANDOMIZATION=none
+
+# Redefine network-related settings to use the dedicated IPv6 network bridge
+# shellcheck disable=SC2034 # used elsewhere
+VM_BRIDGE_IP="$(get_vm_bridge_ip "${VM_IPV6_NETWORK}")"
+# shellcheck disable=SC2034 # used elsewhere
+WEB_SERVER_URL="http://[${VM_BRIDGE_IP}]:${WEB_SERVER_PORT}"
+# Using `hostname` here instead of a raw ip because skopeo only allows either
+# ipv4 or fqdn's, but not ipv6. Since the registry is hosted on the ipv6
+# network gateway in the host, we need to use a combination of the hostname
+# plus /etc/hosts resolution (which is taken care of by kickstart).
+# shellcheck disable=SC2034 # used elsewhere
+MIRROR_REGISTRY_URL="$(hostname):${MIRROR_REGISTRY_PORT}/microshift"
+
+# Cluster A (host1): non-overlapping CIDRs
+CLUSTER_A_POD_CIDR="fd01::/48"
+CLUSTER_A_SVC_CIDR="fd02::/112"
+CLUSTER_A_DOMAIN="cluster-a.remote"
+
+# Cluster B (host2): non-overlapping CIDRs
+CLUSTER_B_POD_CIDR="fd04::/48"
+CLUSTER_B_SVC_CIDR="fd05::/112"
+CLUSTER_B_DOMAIN="cluster-b.remote"
+
+# Cluster C (host3): non-overlapping CIDRs
+CLUSTER_C_POD_CIDR="fd07::/48"
+CLUSTER_C_SVC_CIDR="fd08::/112"
+CLUSTER_C_DOMAIN="cluster-c.remote"
+
+scenario_create_vms() {
+ c2cc_create_vms "rhel98-bootc-source-ipsec" "rhel98-bootc" "${VM_IPV6_NETWORK}" ipv6
+}
+
+scenario_remove_vms() {
+ c2cc_remove_vms
+}
+
+scenario_run_tests() {
+ configure_c2cc_hosts "c2cc_ipsec_pre_greenboot" "c2cc_ipsec_greenboot"
+ configure_ipsec
+
+ c2cc_run_tests "suites/c2cc/extra/ipsec.robot" "" ipv6
+}
diff --git a/test/scenarios-bootc/c2cc/el98-src@c2cc-ipsec-mtu-ipv4.sh b/test/scenarios-bootc/c2cc/el98-src@c2cc-ipsec-mtu-ipv4.sh
new file mode 100644
index 0000000000..eac5896de0
--- /dev/null
+++ b/test/scenarios-bootc/c2cc/el98-src@c2cc-ipsec-mtu-ipv4.sh
@@ -0,0 +1,29 @@
+#!/bin/bash
+
+# Sourced from scenario.sh and uses functions defined there.
+#
+# Sets up 3 MicroShift clusters with C2CC and Libreswan IPsec on a
+# jumbo-frame network (MTU 9000). Tests validate MTU boundary behavior
+# through IPsec tunnel-mode ESP encapsulation and the recommended
+# pod MTU reduction to 8900.
+
+# shellcheck source=test/bin/c2cc_common.sh
+source "${SCRIPTDIR}/c2cc_common.sh"
+
+export TEST_RANDOMIZATION=none
+
+scenario_create_vms() {
+ c2cc_create_jumbo_network
+ c2cc_create_vms "rhel98-bootc-source-ipsec" "rhel98-bootc" "jumbo" "ipv4" "9000"
+}
+
+scenario_remove_vms() {
+ c2cc_remove_vms
+}
+
+scenario_run_tests() {
+ configure_c2cc_hosts "c2cc_ipsec_mtu_pre_greenboot" "c2cc_ipsec_mtu_greenboot"
+ configure_ipsec
+
+ c2cc_run_tests "suites/c2cc/extra/ipsec-mtu.robot"
+}
diff --git a/test/scenarios-bootc/c2cc/el98-src@c2cc-ipsec-mtu-ipv6.sh b/test/scenarios-bootc/c2cc/el98-src@c2cc-ipsec-mtu-ipv6.sh
new file mode 100644
index 0000000000..13f009aad1
--- /dev/null
+++ b/test/scenarios-bootc/c2cc/el98-src@c2cc-ipsec-mtu-ipv6.sh
@@ -0,0 +1,54 @@
+#!/bin/bash
+
+# Sourced from scenario.sh and uses functions defined there.
+#
+# Sets up 3 MicroShift clusters with C2CC and Libreswan IPsec on a
+# single-stack IPv6 jumbo-frame network (MTU 9000). Same test suite as
+# c2cc-ipsec-mtu, run over IPv6 instead of IPv4.
+
+# shellcheck source=test/bin/c2cc_common.sh
+source "${SCRIPTDIR}/c2cc_common.sh"
+
+export TEST_RANDOMIZATION=none
+
+# Using `hostname` here instead of a raw ip because skopeo only allows either
+# ipv4 or fqdn's, but not ipv6.
+# shellcheck disable=SC2034 # used elsewhere
+MIRROR_REGISTRY_URL="$(hostname):${MIRROR_REGISTRY_PORT}/microshift"
+
+# Cluster A (host1): non-overlapping CIDRs
+CLUSTER_A_POD_CIDR="fd01::/48"
+CLUSTER_A_SVC_CIDR="fd02::/112"
+CLUSTER_A_DOMAIN="cluster-a.remote"
+
+# Cluster B (host2): non-overlapping CIDRs
+CLUSTER_B_POD_CIDR="fd04::/48"
+CLUSTER_B_SVC_CIDR="fd05::/112"
+CLUSTER_B_DOMAIN="cluster-b.remote"
+
+# Cluster C (host3): non-overlapping CIDRs
+CLUSTER_C_POD_CIDR="fd07::/48"
+CLUSTER_C_SVC_CIDR="fd08::/112"
+CLUSTER_C_DOMAIN="cluster-c.remote"
+
+scenario_create_vms() {
+ c2cc_create_jumbo_ipv6_network
+ # Set bridge IP after network creation — can't do this at file level
+ # because the jumbo-ipv6 network doesn't exist until this point.
+ # shellcheck disable=SC2034 # used elsewhere
+ VM_BRIDGE_IP="$(get_vm_bridge_ip "jumbo-ipv6")"
+ # shellcheck disable=SC2034 # used elsewhere
+ WEB_SERVER_URL="http://[${VM_BRIDGE_IP}]:${WEB_SERVER_PORT}"
+ c2cc_create_vms "rhel98-bootc-source-ipsec" "rhel98-bootc" "jumbo-ipv6" "ipv6" "9000"
+}
+
+scenario_remove_vms() {
+ c2cc_remove_vms
+}
+
+scenario_run_tests() {
+ configure_c2cc_hosts "c2cc_ipsec_mtu_pre_greenboot" "c2cc_ipsec_mtu_greenboot"
+ configure_ipsec
+
+ c2cc_run_tests "suites/c2cc/extra/ipsec-mtu.robot" "" ipv6
+}
diff --git a/test/scenarios-bootc/c2cc/el98-src@c2cc-ipv6.sh b/test/scenarios-bootc/c2cc/el98-src@c2cc-ipv6.sh.disabled
similarity index 100%
rename from test/scenarios-bootc/c2cc/el98-src@c2cc-ipv6.sh
rename to test/scenarios-bootc/c2cc/el98-src@c2cc-ipv6.sh.disabled
diff --git a/test/scenarios-bootc/c2cc/el98-src@c2cc-mtu-ipv4.sh b/test/scenarios-bootc/c2cc/el98-src@c2cc-mtu-ipv4.sh
new file mode 100644
index 0000000000..00c9bfe2f2
--- /dev/null
+++ b/test/scenarios-bootc/c2cc/el98-src@c2cc-mtu-ipv4.sh
@@ -0,0 +1,25 @@
+#!/bin/bash
+
+# Sourced from scenario.sh and uses functions defined there.
+#
+# Sets up 3 MicroShift clusters with C2CC on a jumbo-frame network
+# (MTU 9000). Tests validate MTU boundary behavior for plain C2CC
+# traffic without IPsec.
+
+# shellcheck source=test/bin/c2cc_common.sh
+source "${SCRIPTDIR}/c2cc_common.sh"
+
+scenario_create_vms() {
+ c2cc_create_jumbo_network
+ c2cc_create_vms "rhel98-bootc-source" "rhel98-bootc" "jumbo" "ipv4" "9000"
+}
+
+scenario_remove_vms() {
+ c2cc_remove_vms
+}
+
+scenario_run_tests() {
+ configure_c2cc_hosts "c2cc_mtu_pre_greenboot" "c2cc_mtu_greenboot"
+
+ c2cc_run_tests "suites/c2cc/extra/mtu.robot"
+}
diff --git a/test/scenarios-bootc/c2cc/el98-src@c2cc-mtu-ipv6.sh b/test/scenarios-bootc/c2cc/el98-src@c2cc-mtu-ipv6.sh
new file mode 100644
index 0000000000..736bfb7c1a
--- /dev/null
+++ b/test/scenarios-bootc/c2cc/el98-src@c2cc-mtu-ipv6.sh
@@ -0,0 +1,51 @@
+#!/bin/bash
+
+# Sourced from scenario.sh and uses functions defined there.
+#
+# Sets up 3 MicroShift clusters with C2CC on a single-stack IPv6 jumbo-frame
+# network (MTU 9000). Same test suite as c2cc-mtu, run over IPv6 instead of
+# IPv4, without IPsec.
+
+# shellcheck source=test/bin/c2cc_common.sh
+source "${SCRIPTDIR}/c2cc_common.sh"
+
+# Using `hostname` here instead of a raw ip because skopeo only allows either
+# ipv4 or fqdn's, but not ipv6.
+# shellcheck disable=SC2034 # used elsewhere
+MIRROR_REGISTRY_URL="$(hostname):${MIRROR_REGISTRY_PORT}/microshift"
+
+# Cluster A (host1): non-overlapping CIDRs
+CLUSTER_A_POD_CIDR="fd01::/48"
+CLUSTER_A_SVC_CIDR="fd02::/112"
+CLUSTER_A_DOMAIN="cluster-a.remote"
+
+# Cluster B (host2): non-overlapping CIDRs
+CLUSTER_B_POD_CIDR="fd04::/48"
+CLUSTER_B_SVC_CIDR="fd05::/112"
+CLUSTER_B_DOMAIN="cluster-b.remote"
+
+# Cluster C (host3): non-overlapping CIDRs
+CLUSTER_C_POD_CIDR="fd07::/48"
+CLUSTER_C_SVC_CIDR="fd08::/112"
+CLUSTER_C_DOMAIN="cluster-c.remote"
+
+scenario_create_vms() {
+ c2cc_create_jumbo_ipv6_network
+ # Set bridge IP after network creation — can't do this at file level
+ # because the jumbo-ipv6 network doesn't exist until this point.
+ # shellcheck disable=SC2034 # used elsewhere
+ VM_BRIDGE_IP="$(get_vm_bridge_ip "jumbo-ipv6")"
+ # shellcheck disable=SC2034 # used elsewhere
+ WEB_SERVER_URL="http://[${VM_BRIDGE_IP}]:${WEB_SERVER_PORT}"
+ c2cc_create_vms "rhel98-bootc-source" "rhel98-bootc" "jumbo-ipv6" "ipv6" "9000"
+}
+
+scenario_remove_vms() {
+ c2cc_remove_vms
+}
+
+scenario_run_tests() {
+ configure_c2cc_hosts "c2cc_mtu_pre_greenboot" "c2cc_mtu_greenboot"
+
+ c2cc_run_tests "suites/c2cc/extra/mtu.robot" "" ipv6
+}
diff --git a/test/scenarios-bootc/c2cc/el98-src@c2cc.sh b/test/scenarios-bootc/c2cc/el98-src@c2cc.sh.disabled
similarity index 100%
rename from test/scenarios-bootc/c2cc/el98-src@c2cc.sh
rename to test/scenarios-bootc/c2cc/el98-src@c2cc.sh.disabled
diff --git a/test/scenarios-bootc/c2cc/el98-src@el102-src@c2cc-upgrade-ok.sh b/test/scenarios-bootc/c2cc/el98-src@el102-src@c2cc-upgrade-ok.sh.disabled
similarity index 100%
rename from test/scenarios-bootc/c2cc/el98-src@el102-src@c2cc-upgrade-ok.sh
rename to test/scenarios-bootc/c2cc/el98-src@el102-src@c2cc-upgrade-ok.sh.disabled
diff --git a/test/suites/c2cc/extra/ipsec-mtu.robot b/test/suites/c2cc/extra/ipsec-mtu.robot
new file mode 100644
index 0000000000..e5c5486f1e
--- /dev/null
+++ b/test/suites/c2cc/extra/ipsec-mtu.robot
@@ -0,0 +1,133 @@
+*** Settings ***
+Documentation MTU boundary tests for C2CC with IPsec at jumbo frame size (9000 MTU).
+... Sends UDP datagrams with the DF bit set through IPsec tunnel-mode
+... ESP encapsulation and validates the recommended pod MTU of 8900.
+... Tests all three cluster pairs.
+...
+... Phase 1: pod MTU 9000 (default, auto-detected from network).
+... Phase 2: pod MTU 8900 (set via ovn.yaml, validates ESP headroom).
+... Requires the c2cc-ipsec-mtu scenario (jumbo network + IPsec).
+... Test order is deterministic (TEST_RANDOMIZATION=none).
+
+Resource ../../../resources/microshift-process.resource
+Resource ../../../resources/kubeconfig.resource
+Resource ../../../resources/oc.resource
+Resource ../../../resources/c2cc.resource
+Resource ../../../resources/ipsec.resource
+
+Suite Setup C2CC Suite Setup deploy_workloads=${TRUE}
+Suite Teardown Teardown
+
+Test Tags c2cc ipsec mtu
+
+
+*** Test Cases ***
+# ── Phase 1: pod MTU = 9000 (auto-detected from jumbo network) ────────────────
+
+DF Bit 64B Passes Through IPsec At Jumbo MTU
+ [Documentation] Smoke test — small DF-bit payload through IPsec at jumbo MTU.
+ Ping DF Bit Between All Clusters 64
+
+DF Bit 8400B Passes Through IPsec At Jumbo MTU
+ [Documentation] Mid-range jumbo payload through IPsec.
+ Ping DF Bit Between All Clusters 8400
+
+DF Bit 8872B Passes Through IPsec At Jumbo MTU
+ [Documentation] 8872B + 28B = 8900B — the max payload at recommended pod MTU 8900.
+ Ping DF Bit Between All Clusters 8872
+
+DF Bit 8850B Passes Through IPsec At Jumbo MTU
+ [Documentation] Near pod MTU — accepted by the kernel. OVN-K subtracts
+ ... 78B Geneve overhead in multinode mode, so auto-detected pod MTU is 8922.
+ Ping DF Bit Between All Clusters 8850
+
+DF Bit 8972B Rejected Above Jumbo Pod MTU
+ [Documentation] 8972B + 28B = 9000B, well above the auto-detected pod MTU (8922B).
+ Ping DF Bit Should Fail Between All Clusters 8972
+
+Large TCP Transfer Through IPsec At Jumbo MTU
+ [Documentation] 64KB curl POST through IPsec at jumbo MTU.
+ Large Payload Between All Clusters 65536
+
+ESP Encapsulation At Jumbo MTU
+ [Documentation] Verify traffic is ESP-encapsulated at jumbo frame sizes.
+ ... Records XFRM byte counters before and after traffic on all clusters.
+ ${baseline_a}= Get XFRM Byte Counters cluster-a
+ ${baseline_b}= Get XFRM Byte Counters cluster-b
+ ${baseline_c}= Get XFRM Byte Counters cluster-c
+ Verify Full C2CC Connectivity
+ Verify XFRM Counters Incremented cluster-a ${baseline_a}
+ Verify XFRM Counters Incremented cluster-b ${baseline_b}
+ Verify XFRM Counters Incremented cluster-c ${baseline_c}
+
+# ── Phase 2 setup ─────────────────────────────────────────────────────────────
+
+Reconfigure Pod MTU To 8900
+ [Documentation] Set pod MTU to 8900 via ovn.yaml, restart MicroShift,
+ ... and redeploy workloads. Validates the doc recommendation for
+ ... ESP headroom on jumbo networks.
+ Configure Pod MTU On All Clusters 8900
+ Restart MicroShift On All Clusters
+ Redeploy Test Workloads
+
+# ── Phase 2: pod MTU = 8900 (recommended for ESP headroom) ────────────────────
+
+Pod MTU Is 8900 After Reconfiguration
+ [Documentation] Verify the pod network interface reports MTU 8900 on all clusters.
+ FOR ${alias} IN @{ALL_CLUSTERS}
+ ${mtu}= Get Pod Interface MTU ${alias} nettest-pod ${NAMESPACES}[${alias}]
+ Should Be Equal As Strings ${mtu} 8900
+ ... ${alias}: pod MTU is ${mtu}, expected 8900
+ END
+
+DF Bit 8872B Passes At Reduced Pod MTU
+ [Documentation] Full pod MTU payload (8872B + 28B = 8900B) passes because
+ ... 8900 + ESP overhead fits within the 9000 physical MTU.
+ Ping DF Bit Between All Clusters 8872
+
+Large TCP Transfer At Jumbo MTU
+ [Documentation] 64KB curl POST through IPsec at jumbo MTU.
+ Large Payload Between All Clusters 65536
+
+Cross Cluster Connectivity At Jumbo MTU
+ [Documentation] Full connectivity check: all 6 cluster pairs, pod + service,
+ ... with source IP preservation.
+ Verify Full C2CC Connectivity
+
+
+*** Keywords ***
+Configure Pod MTU On All Clusters
+ [Documentation] Write /etc/microshift/ovn.yaml with the given MTU value.
+ [Arguments] ${mtu}
+ FOR ${alias} IN @{ALL_CLUSTERS}
+ Command On Cluster ${alias}
+ ... bash -c 'echo "mtu: ${mtu}" > /etc/microshift/ovn.yaml'
+ END
+
+Restart MicroShift On All Clusters
+ [Documentation] Restart MicroShift on all clusters and wait for health + IPsec.
+ FOR ${alias} IN @{ALL_CLUSTERS}
+ Command On Cluster ${alias} systemctl restart microshift
+ END
+ FOR ${alias} IN @{ALL_CLUSTERS}
+ Wait Until Keyword Succeeds 5m 15s
+ ... Command On Cluster ${alias} microshift healthcheck --timeout=30s
+ END
+ FOR ${alias} IN @{ALL_CLUSTERS}
+ Wait For IPsec Tunnel Reestablishment ${alias} expected_count=8
+ END
+
+Redeploy Test Workloads
+ [Documentation] Delete and redeploy test workloads so pods pick up the new MTU.
+ Cleanup Test Workloads
+ Deploy Test Workloads
+
+Teardown
+ [Documentation] Clean up workloads, ensure IPsec running, close connections.
+ Ensure IPsec Running On All Clusters
+ C2CC Suite Teardown cleanup_workloads=${TRUE}
+
+Ensure IPsec Running On All Clusters
+ FOR ${alias} IN cluster-a cluster-b cluster-c
+ Start IPsec Service On Cluster ${alias}
+ END
diff --git a/test/suites/c2cc/ipsec/ipsec.robot b/test/suites/c2cc/extra/ipsec.robot
similarity index 86%
rename from test/suites/c2cc/ipsec/ipsec.robot
rename to test/suites/c2cc/extra/ipsec.robot
index 04717e4e76..50d47361ce 100644
--- a/test/suites/c2cc/ipsec/ipsec.robot
+++ b/test/suites/c2cc/extra/ipsec.robot
@@ -5,7 +5,8 @@ Documentation IPsec E2E tests for C2CC.
...
... Tests cover ESP encapsulation, connectivity through the tunnel,
... source IP preservation, policy enforcement (SA flush/restore),
-... plaintext rejection, host-to-pod rejection, and MTU validation.
+... plaintext rejection, host-to-pod rejection, MTU boundary (1500),
+... and tunnel recovery. Jumbo MTU tests are in the c2cc-ipsec-mtu scenario.
Resource ../../../resources/microshift-process.resource
Resource ../../../resources/kubeconfig.resource
@@ -95,14 +96,33 @@ Host To Remote Pod Rejected Without IPsec
${pod_ip}= Get Hello Pod IP cluster-b
Curl From Host Should Fail cluster-a ${pod_ip} 8080
-Near MTU Packet Through IPsec Tunnel
- [Documentation] Send near-MTU-sized payloads through IPsec tunnel-mode
- ... encapsulation. Verifies no MTU blackhole from DF-bit issues.
- ... ESP overhead ~36-52B total.
+Near MTU TCP Transfer Through IPsec
+ [Documentation] Send near-MTU-sized payloads via TCP (curl POST).
+ ... TCP handles PMTUD transparently through the IPsec tunnel.
${ip_dest}= Get Hello Pod IP cluster-b
Send Large Payload And Verify cluster-a ${ip_dest} 1300
Send Large Payload And Verify cluster-a ${ip_dest} 1400
+DF Bit 64B Passes Through IPsec
+ [Documentation] Smoke test — small DF-bit payload through IPsec on all pairs.
+ Ping DF Bit Between All Clusters 64
+
+DF Bit 1350B Passes Through IPsec
+ [Documentation] Mid-range DF-bit payload through IPsec on all pairs.
+ Ping DF Bit Between All Clusters 1350
+
+DF Bit 1450B Passes Through IPsec
+ [Documentation] Near pod MTU — accepted by the kernel on all pairs.
+ Ping DF Bit Between All Clusters 1450
+
+DF Bit 1472B Rejected At Pod MTU
+ [Documentation] 1472B + 28B = 1500B, hitting the pod interface MTU on all pairs.
+ Ping DF Bit Should Fail Between All Clusters 1472
+
+Large TCP Transfer Through IPsec
+ [Documentation] 64KB curl POST through IPsec on all pairs.
+ Large Payload Between All Clusters 65536
+
Cross Cluster DNS Through IPsec
[Documentation] Verify pods can resolve and reach services on remote
... clusters via DNS name through the IPsec tunnel.
@@ -162,11 +182,3 @@ Restore IPsec With Enforcement Cleanup
Remove NFTables IPsec Enforcement Rules ${enforcement_alias}
Start IPsec Service On Cluster ${ipsec_alias}
Wait For IPsec Tunnel Reestablishment ${ipsec_alias} expected_count=8
-
-Send Large Payload And Verify
- [Documentation] Send a large payload via curl POST and verify it succeeds.
- [Arguments] ${alias} ${ip} ${size}
- ${stdout}= Oc On Cluster
- ... ${alias}
- ... oc exec curl-pod -n ${NAMESPACES}[${alias}] -- sh -c 'dd if=/dev/zero bs=${size} count=1 2>/dev/null | curl -sS --max-time 15 --data-binary @- http://${ip}:8080/cgi-bin/hello'
- Should Contain ${stdout} Hello from
diff --git a/test/suites/c2cc/extra/mtu.robot b/test/suites/c2cc/extra/mtu.robot
new file mode 100644
index 0000000000..ef159b3816
--- /dev/null
+++ b/test/suites/c2cc/extra/mtu.robot
@@ -0,0 +1,48 @@
+*** Settings ***
+Documentation MTU boundary tests for plain C2CC at jumbo frame size (9000 MTU).
+... Sends UDP datagrams with the DF (Don't Fragment) bit set to
+... verify packet acceptance and rejection at the pod MTU boundary.
+... Tests all three cluster pairs. Requires the c2cc-mtu scenario.
+
+Resource ../../../resources/microshift-process.resource
+Resource ../../../resources/kubeconfig.resource
+Resource ../../../resources/oc.resource
+Resource ../../../resources/c2cc.resource
+Resource ../../../resources/ipsec.resource
+
+Suite Setup C2CC Suite Setup deploy_workloads=${TRUE}
+Suite Teardown C2CC Suite Teardown cleanup_workloads=${TRUE}
+
+Test Tags c2cc mtu
+
+
+*** Test Cases ***
+DF Bit 64B Passes At Jumbo MTU
+ [Documentation] Smoke test — small DF-bit payload at jumbo MTU on all cluster pairs.
+ Ping DF Bit Between All Clusters 64
+
+DF Bit 8400B Passes At Jumbo MTU
+ [Documentation] Mid-range jumbo payload on all cluster pairs.
+ Ping DF Bit Between All Clusters 8400
+
+DF Bit 8872B Passes At Jumbo MTU
+ [Documentation] 8872B + 28B = 8900B — the max payload at recommended pod MTU 8900.
+ Ping DF Bit Between All Clusters 8872
+
+DF Bit 8850B Passes At Jumbo MTU
+ [Documentation] Near pod MTU — accepted on all cluster pairs. OVN-K subtracts
+ ... 78B Geneve overhead in multinode mode, so auto-detected pod MTU is 8922.
+ Ping DF Bit Between All Clusters 8850
+
+DF Bit 8972B Rejected Above Pod MTU
+ [Documentation] 8972B + 28B = 9000B, well above the auto-detected pod MTU (8922B).
+ Ping DF Bit Should Fail Between All Clusters 8972
+
+Large TCP Transfer At Jumbo MTU
+ [Documentation] 64KB curl POST across all cluster pairs.
+ Large Payload Between All Clusters 65536
+
+Cross Cluster Connectivity At Jumbo MTU
+ [Documentation] Full connectivity check: all 6 cluster pairs, pod + service,
+ ... with source IP preservation.
+ Verify Full C2CC Connectivity