diff --git a/test/assets/c2cc/df-bit-send.py b/test/assets/c2cc/df-bit-send.py new file mode 100644 index 0000000000..a581aa7b13 --- /dev/null +++ b/test/assets/c2cc/df-bit-send.py @@ -0,0 +1,38 @@ +#!/usr/bin/env python3 +"""Send a UDP datagram with the DF (Don't Fragment) bit set. + +Uses IP_PMTUDISC_DO (IPv4) or IPV6_DONTFRAG (IPv6) so the kernel +rejects datagrams that exceed the path MTU with EMSGSIZE instead of +fragmenting them. Auto-detects the address family 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, so payload sizes are equivalent to ping -s values +within each address family. + +Usage: python3 df-bit-send.py +Exit: prints "OK" on success, raises OSError on rejection. +""" + +import socket +import sys + +IPPROTO_IPV6 = 41 +IPV6_DONTFRAG = 62 +IP_MTU_DISCOVER = 10 +IP_PMTUDISC_DO = 2 + +dest = sys.argv[1] +size = int(sys.argv[2]) + +family = socket.AF_INET6 if ":" in dest else socket.AF_INET +sock = socket.socket(family, socket.SOCK_DGRAM) + +if family == socket.AF_INET6: + sock.setsockopt(IPPROTO_IPV6, IPV6_DONTFRAG, 1) +else: + sock.setsockopt(socket.IPPROTO_IP, IP_MTU_DISCOVER, IP_PMTUDISC_DO) + +sock.sendto(b"A" * size, (dest, 9999)) +print("OK") 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..76f85602ff 100644 --- a/test/bin/c2cc_common.sh +++ b/test/bin/c2cc_common.sh @@ -188,11 +188,84 @@ ${network_yaml}IEOF EOF } +# inject_kickstart_mtu configures jumbo MTU on the guest VM via kickstart. +# Two things must happen before MicroShift first starts: +# A) NIC MTU must be set so OVN-K creates br-ex at jumbo size. +# B) OVN config must specify pod MTU explicitly -- C2CC VMs have no default +# route, so MicroShift auto-detection falls back to 1500. +# For (A): NM conf.d, NM dispatcher, and systemd service set the NIC MTU. +# For (B): writes mtu to /etc/microshift/ovn.yaml. +# Args: host nic_mtu [pod_mtu] +# pod_mtu defaults to nic_mtu - 78 (conservative headroom matching +# OVN-K's IPv6 Geneve constant). Pass explicitly for ipsec-mtu +# scenarios that need the recommended 8900. +inject_kickstart_mtu() { + local -r host="${1}" + local -r mtu="${2}" + local -r pod_mtu="${3:-$(( mtu - 78 ))}" + + local -r ks_dir="${SCENARIO_INFO_DIR}/${SCENARIO}/vms/${host}" + cat >> "${ks_dir}/post-microshift.cfg" < /etc/NetworkManager/conf.d/99-jumbo-mtu.conf <<'NMCONF' +[connection-ethernet-jumbo] +match-device=type:ethernet +ethernet.mtu=${mtu} +NMCONF + +# NM dispatcher -- runs ip link set during connection activation +mkdir -p /etc/NetworkManager/dispatcher.d +cat > /etc/NetworkManager/dispatcher.d/99-jumbo-mtu <<'DISPEOF' +#!/bin/bash +[ "\$2" != "up" ] && exit 0 +ip link set dev "\$1" mtu ${mtu} 2>&1 || logger -t jumbo-mtu "Failed to set MTU ${mtu} on \$1" +DISPEOF +chmod 755 /etc/NetworkManager/dispatcher.d/99-jumbo-mtu +restorecon -R /etc/NetworkManager/dispatcher.d/ 2>&1 || logger -t jumbo-mtu "restorecon failed for dispatcher.d" + +# Systemd service -- runs before microshift.service. +# Script in /etc/ because /usr/ is read-only on bootc images. +cat > /etc/set-jumbo-mtu.sh <<'SCRIPTEOF' +#!/bin/bash +for dev in \$(ip -o link show type ether | awk -F: '{print \$2}' | tr -d ' '); do + ip link set dev "\$dev" mtu ${mtu} +done +SCRIPTEOF +chmod 755 /etc/set-jumbo-mtu.sh +restorecon /etc/set-jumbo-mtu.sh 2>&1 || logger -t jumbo-mtu "restorecon failed for set-jumbo-mtu.sh" + +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=/etc/set-jumbo-mtu.sh +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 + +# Set explicit pod MTU -- MicroShift auto-detection fails in C2CC VMs +# (no default route), falling back to 1500 regardless of NIC MTU. +mkdir -p /etc/microshift +echo "mtu: ${pod_mtu}" > /etc/microshift/ovn.yaml +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:-}" + local -r pod_mtu="${6:-}" # Prepare kickstart for all hosts # prepare_kickstart args: vmname template commit_ref fips_enabled ipv6_only @@ -223,9 +296,62 @@ 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 + if [ -n "${pod_mtu}" ]; then + inject_kickstart_mtu "${host}" "${network_mtu}" "${pod_mtu}" + else + inject_kickstart_mtu "${host}" "${network_mtu}" + fi + 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. Race-safe — multiple parallel scenarios may call +# this simultaneously, so each step tolerates "already done" errors and +# a final check verifies the network is active. +c2cc_create_jumbo_network() { + local -r netconfig="${ROOTDIR}/test/assets/network/jumbo-network.xml" + sudo virsh net-define "${netconfig}" 2>/dev/null || true + sudo virsh net-start jumbo 2>/dev/null || true + sudo virsh net-autostart jumbo 2>/dev/null || true + sudo virsh net-info jumbo | grep "Active:.*yes" >/dev/null +} + +# 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() { + local -r netconfig="${ROOTDIR}/test/assets/network/jumbo-ipv6-network.xml" + sudo virsh net-define "${netconfig}" 2>/dev/null || true + sudo virsh net-start jumbo-ipv6 2>/dev/null || true + sudo virsh net-autostart jumbo-ipv6 2>/dev/null || true + sudo virsh net-info jumbo-ipv6 | grep "Active:.*yes" >/dev/null + + # Add the bridge's IPv6 subnet to the firewall trusted zone so VMs + # can reach the hypervisor's services (registry mirror via hostname). + # Re-applied on every run in case the firewall was reset since creation. + local bridge + bridge=$(sudo virsh net-info jumbo-ipv6 | grep '^Bridge:' | awk '{print $2}') + if [[ -z "${bridge}" ]]; then + echo "ERROR: Could not determine bridge name for jumbo-ipv6 network" >&2 + return 1 + fi + 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 } c2cc_remove_vms() { diff --git a/test/bin/ci_phase_boot_and_test.sh b/test/bin/ci_phase_boot_and_test.sh index 7ff5278ff3..77fcd496af 100755 --- a/test/bin/ci_phase_boot_and_test.sh +++ b/test/bin/ci_phase_boot_and_test.sh @@ -85,16 +85,41 @@ else progress="" fi -# Limit amount of parallel scenarios for the C2CC jobs to avoid -# over-provisioning the resources: each C2CC scenario runs 3 VMs -# (2 vCPUs / 4GiB each) and the c7g.metal instance has 64 cores / 128GiB. -# Powering off the VMs of passed scenarios makes the job limit an -# actual cap on the number of running VMs. +# Each C2CC scenario runs 3 VMs (2 vCPUs / 4GiB each) +# and the c7g.metal instance has 64 cores / 128GiB. jobs_arg="" scenario_action="create-and-run" if [[ "${SCENARIO_SOURCES}" =~ c2cc ]]; then + # Limit amount of parallel scenarios for the C2CC jobs + # to avoid over-provisioning the resources. jobs_arg="-j 8" + + # Power off passed VMs so the job limit is an actual cap on running VMs. scenario_action="create-run-shutdown" + + # Each arch runs one RHEL version to halve the scenario count. + # The assignment rotates per commit so both combos get coverage: + # flip=0: x86 → el98, ARM → el102 + # flip=1: x86 → el102, ARM → el98 + commit_digit=$(git rev-parse HEAD | cut -c1) + flip=$(( 16#${commit_digit} % 2 )) + + if [[ "$(uname -m)" == "x86_64" ]]; then + if [[ "${flip}" -eq 0 ]]; then + delete_ver=el102 + else + delete_ver=el98 + fi + else + if [[ "${flip}" -eq 0 ]]; then + delete_ver=el98 + else + delete_ver=el102 + fi + fi + + echo "C2CC arch split: deleting ${delete_ver}-* (flip=${flip})" + find "${SCENARIOS_TO_RUN}" -name "${delete_ver}-*.sh" -delete fi TEST_OK=true diff --git a/test/bin/scenario.sh b/test/bin/scenario.sh index cef81e6be3..736d5aab17 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,12 @@ launch_vm() { if sudo virsh nwfilter-list | awk '{print $2}' | grep -qx "${n}"; then vm_network_args+=",filterref=${n}" fi + + # Propagate MTU to the domain interface (see --network_mtu doc above). + if [ -n "${network_mtu}" ]; then + vm_network_args+=",mtu.size=${network_mtu}" + fi + vm_network_args+=" " done if [ -z "${vm_network_args}" ] ; then @@ -1001,6 +1017,7 @@ launch_vm() { # to attach to the console. `unbuffer` provides the TTY. # shellcheck disable=SC2086 if ${timeout_install} unbuffer sudo virt-install \ + --check disk_size=off \ --autoconsole text \ --graphics "${graphics_args}" \ --name "${full_vmname}" \ 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 7aa5cbc7e6..3514fd3b1f 100644 --- a/test/resources/ipsec.resource +++ b/test/resources/ipsec.resource @@ -10,6 +10,7 @@ Resource c2cc.resource *** Variables *** ${IPSEC_SA_TIMEOUT} 60s ${IPSEC_SA_RETRY} 5s +${DF_SCRIPT} ${EXECDIR}/assets/c2cc/df-bit-send.py *** Keywords *** @@ -100,11 +101,14 @@ Add NFTables IPsec Enforcement Rules ... to the host but forwarded into the OVN network (local gateway mode), so an ... input-hook chain would never see them. [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 forward 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 + Command On Cluster + ... ${alias} + ... 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. @@ -115,17 +119,122 @@ 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). + ... Asserts a recognizable curl error to distinguish IPsec blocking from broken infra. [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 + Should Match Regexp ${stdout} (?i)(timed out|curl:|refused|unreachable) + ... Expected curl timeout or connection error but got: ${stdout} Curl From Host Should Fail [Documentation] Curl directly from the host (not from a pod) to a remote pod IP. ... Expects failure — host-originated traffic is not matched by tunnel-mode ... IPsec selectors scoped to pod/service CIDRs. + ... Asserts a recognizable curl error to distinguish from broken infra. [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 + Should Match Regexp ${stdout} (?i)(timed out|curl:|refused|unreachable) + ... Expected curl timeout or connection error but got: ${stdout} + +Ping With DF Bit And Verify + [Documentation] Send a DF-bit UDP datagram via df-bit-send.py and assert success. + ... Auto-detects IPv4/IPv6. See the script for protocol details. + [Arguments] ${alias} ${dest_ip} ${payload_size} + ${stdout}= Oc On Cluster + ... ${alias} + ... oc exec -i nettest-pod -n ${NAMESPACES}[${alias}] -- python3 - ${dest_ip} ${payload_size} < ${DF_SCRIPT} 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. Checks for the specific EMSGSIZE error + ... to distinguish MTU rejection from other failures. + [Arguments] ${alias} ${dest_ip} ${payload_size} + ${stdout}= Oc On Cluster + ... ${alias} + ... oc exec -i nettest-pod -n ${NAMESPACES}[${alias}] -- python3 - ${dest_ip} ${payload_size} < ${DF_SCRIPT} 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 + Should Contain ${stdout} Message too long + ... DF-bit UDP send of ${payload_size}B expected EMSGSIZE but got: ${stdout} + +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 'head -c ${size} /dev/zero | 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-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..2914a27877 --- /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" "8900" +} + +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..4d5dacd604 --- /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 for /etc/hosts resolution. + # Keep WEB_SERVER_URL at the default IPv4 — bootc kickstarts don't + # use REPLACE_OSTREE_SERVER_URL, and the hypervisor-side curl can't + # reach the IPv6 bridge due to libvirt's nftables filtering. + # shellcheck disable=SC2034 # used elsewhere + VM_BRIDGE_IP="$(get_vm_bridge_ip "jumbo-ipv6")" + c2cc_create_vms "rhel102-bootc-source-ipsec" "rhel102-bootc" "jumbo-ipv6" "ipv6" "9000" "8900" +} + +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-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..c5eaf371c9 --- /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 for /etc/hosts resolution. + # Keep WEB_SERVER_URL at the default IPv4 — bootc kickstarts don't + # use REPLACE_OSTREE_SERVER_URL, and the hypervisor-side curl can't + # reach the IPv6 bridge due to libvirt's nftables filtering. + # shellcheck disable=SC2034 # used elsewhere + VM_BRIDGE_IP="$(get_vm_bridge_ip "jumbo-ipv6")" + 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/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..63c10d174f --- /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" "8900" +} + +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..02a2f62c6f --- /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 for /etc/hosts resolution. + # Keep WEB_SERVER_URL at the default IPv4 — bootc kickstarts don't + # use REPLACE_OSTREE_SERVER_URL, and the hypervisor-side curl can't + # reach the IPv6 bridge due to libvirt's nftables filtering. + # shellcheck disable=SC2034 # used elsewhere + VM_BRIDGE_IP="$(get_vm_bridge_ip "jumbo-ipv6")" + c2cc_create_vms "rhel98-bootc-source-ipsec" "rhel98-bootc" "jumbo-ipv6" "ipv6" "9000" "8900" +} + +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-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..c2785c84a9 --- /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 for /etc/hosts resolution. + # Keep WEB_SERVER_URL at the default IPv4 — bootc kickstarts don't + # use REPLACE_OSTREE_SERVER_URL, and the hypervisor-side curl can't + # reach the IPv6 bridge due to libvirt's nftables filtering. + # shellcheck disable=SC2034 # used elsewhere + VM_BRIDGE_IP="$(get_vm_bridge_ip "jumbo-ipv6")" + 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/suites/c2cc/extra/ipsec-mtu.robot b/test/suites/c2cc/extra/ipsec-mtu.robot new file mode 100644 index 0000000000..8b06bce183 --- /dev/null +++ b/test/suites/c2cc/extra/ipsec-mtu.robot @@ -0,0 +1,140 @@ +*** 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 8900 (set via ovn.yaml in kickstart). +... Phase 2: validates the same config survives a MicroShift restart. +... 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 = 8900 (set via kickstart ovn.yaml) ────────────────────── + +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 8852B Passes Through IPsec At Jumbo MTU + [Documentation] 8852B is the max payload at pod MTU 8900 for IPv6 + ... (8852 + 40 IPv6 + 8 UDP = 8900). Also within IPv4 boundary (8880). + Ping DF Bit Between All Clusters 8852 + +DF Bit 8850B Passes Through IPsec At Jumbo MTU + [Documentation] Near pod MTU (8900, set via ovn.yaml) — accepted by the kernel. + Ping DF Bit Between All Clusters 8850 + +DF Bit 8972B Rejected Above Jumbo Pod MTU + [Documentation] 8972B + 28B = 9000B, well above the pod MTU (8900B). + 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: restart validation ───────────────────────────────────────── + +Reconfigure Pod MTU To 8900 + [Documentation] Re-write pod MTU 8900 via ovn.yaml (same value as kickstart), + ... restart MicroShift, and redeploy workloads. Validates the config + ... survives a restart cycle with IPsec tunnel reestablishment. + Configure Pod MTU On All Clusters 8900 + Restart MicroShift On All Clusters + Redeploy Test Workloads + +# ── Phase 2: validate pod MTU 8900 after restart ────────────────────────────── + +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 8852B Passes At Reduced Pod MTU + [Documentation] Max IPv6 payload at pod MTU 8900 (8852 + 48 = 8900). + ... Verifies ESP overhead fits within the 9000 physical MTU. + Ping DF Bit Between All Clusters 8852 + +DF Bit 8873B Rejected Above Pod MTU After Restart + [Documentation] 8873B + 28B (IPv4) = 8901B > 8900 pod MTU. Rejected. + ... Also 8873B + 48B (IPv6) = 8921B > 8900. Verifies the pod MTU + ... boundary is correctly enforced after restart. + Ping DF Bit Should Fail Between All Clusters 8873 + +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 + [Documentation] Start IPsec on all clusters. Tests may have stopped it. + FOR ${alias} IN cluster-a cluster-b cluster-c + Start IPsec Service On Cluster ${alias} + END diff --git a/test/suites/c2cc/extra/ipsec.robot b/test/suites/c2cc/extra/ipsec.robot new file mode 100644 index 0000000000..9949fb48d2 --- /dev/null +++ b/test/suites/c2cc/extra/ipsec.robot @@ -0,0 +1,178 @@ +*** Settings *** +Documentation IPsec E2E tests for C2CC. +... Validates that C2CC cross-cluster connectivity works through a +... Libreswan tunnel-mode IPsec mesh (subnet selectors, no Geneve). +... +... Tests cover ESP encapsulation, connectivity through the tunnel, +... source IP preservation, policy enforcement (SA flush/restore), +... 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 +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 + + +*** Test Cases *** +IPsec Tunnels Established On All Clusters + [Documentation] Verify all 3 hosts have 8 IPsec tunnel SAs each. + ... Each host has 2 remote hosts x 4 subnet pairs (2 local x 2 remote CIDRs). + Verify All IPsec Tunnels On Cluster cluster-a expected_count=8 + Verify All IPsec Tunnels On Cluster cluster-b expected_count=8 + Verify All IPsec Tunnels On Cluster cluster-c expected_count=8 + +ESP Encapsulation On Wire + [Documentation] Capture packets on the wire and verify ESP encapsulation. + ... Records XFRM byte counters before and after traffic, captures ESP packets + ... via tcpdump, and verifies counters incremented. + ${baseline_a}= Get XFRM Byte Counters cluster-a + ${baseline_b}= Get XFRM Byte Counters cluster-b + + ${pcap_file}= Start Tcpdump For ESP On Cluster cluster-b + ${ip_dest}= Get Hello Pod IP cluster-b + Curl From Cluster cluster-a ${ip_dest} 8080 + Wait For Tcpdump And Verify ESP cluster-b ${pcap_file} + + Verify XFRM Counters Incremented cluster-a ${baseline_a} + Verify XFRM Counters Incremented cluster-b ${baseline_b} + +Cross Cluster Connectivity Through IPsec + [Documentation] Verify pods on all clusters can reach pods/services on all + ... other clusters through the IPsec tunnel. + [Template] Verify Connectivity Between Clusters + cluster-a cluster-b pod + cluster-a cluster-b service + cluster-a cluster-c pod + cluster-a cluster-c service + cluster-b cluster-a pod + cluster-b cluster-a service + cluster-b cluster-c pod + cluster-b cluster-c service + cluster-c cluster-a pod + cluster-c cluster-a service + cluster-c cluster-b pod + cluster-c cluster-b service + +Source IP Preserved Through IPsec + [Documentation] Verify cross-cluster traffic through IPsec preserves the + ... source pod IP (no SNAT). + [Template] Verify Source IP Preserved Between Clusters + cluster-a cluster-b pod + cluster-a cluster-b service + cluster-a cluster-c pod + cluster-a cluster-c service + cluster-b cluster-a pod + cluster-b cluster-a service + cluster-b cluster-c pod + cluster-b cluster-c service + cluster-c cluster-a pod + cluster-c cluster-a service + cluster-c cluster-b pod + cluster-c cluster-b service + +Plaintext Rejection When IPsec Stopped + [Documentation] Stop IPsec on cluster-a. With nftables enforcement rules on + ... cluster-b, verify traffic is dropped rather than sent in plaintext. + [Setup] Add NFTables IPsec Enforcement Rules cluster-b ${CLUSTER_B_POD_CIDR} + + Stop IPsec Service On Cluster cluster-a + Sleep 3s reason=Wait for SAs to expire + ${ip_dest}= Get Hello Pod IP cluster-b + Curl Should Fail From Cluster cluster-a ${ip_dest} 8080 ${NAMESPACES}[cluster-a] + + [Teardown] Restore IPsec With Enforcement Cleanup cluster-a cluster-b + +Host To Remote Pod Rejected Without IPsec + [Documentation] Curl directly from cluster-a's host to a pod on cluster-b. + ... Host-originated traffic is not matched by tunnel-mode IPsec selectors + ... scoped to pod/service CIDRs, so it cannot reach the remote pod. + ${pod_ip}= Get Hello Pod IP cluster-b + Curl From Host Should Fail cluster-a ${pod_ip} 8080 + +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, exceeding the ESP-adjusted PMTU 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. + [Template] Verify Remote Service Via DNS + cluster-a cluster-b + cluster-a cluster-c + cluster-b cluster-a + cluster-b cluster-c + cluster-c cluster-a + cluster-c cluster-b + +IPsec Tunnel Recovers After Local Restart + [Documentation] Restart IPsec on cluster-a and verify tunnels recover + ... and cross-cluster connectivity is restored. + Restart IPsec Service On Cluster cluster-a + Wait For IPsec Tunnel Reestablishment cluster-a expected_count=8 + Wait For IPsec Tunnel Reestablishment cluster-b expected_count=8 + Wait For IPsec Tunnel Reestablishment cluster-c expected_count=8 + ${ip_dest}= Get Hello Pod IP cluster-b + Curl From Cluster cluster-a ${ip_dest} 8080 + +IPsec Tunnel Recovers After Remote Stop Start + [Documentation] Stop IPsec on cluster-b, start it back, and verify + ... tunnels recover and cross-cluster connectivity is restored. + Stop IPsec Service On Cluster cluster-b + Sleep 5s reason=Wait for tunnel to go down + Start IPsec Service On Cluster cluster-b + Wait For IPsec Tunnel Reestablishment cluster-a expected_count=8 + Wait For IPsec Tunnel Reestablishment cluster-b expected_count=8 + Wait For IPsec Tunnel Reestablishment cluster-c expected_count=8 + ${ip_dest}= Get Hello Pod IP cluster-b + Curl From Cluster cluster-a ${ip_dest} 8080 + + +*** Keywords *** +Teardown + [Documentation] Ensure IPsec is running, then remove test workloads and close connections. + Ensure IPsec Running On All Clusters + C2CC Suite Teardown cleanup_workloads=${TRUE} + +Ensure IPsec Running On All Clusters + [Documentation] Make sure ipsec service is running on all clusters. + ... Tests may have stopped it. + FOR ${alias} IN cluster-a cluster-b cluster-c + Start IPsec Service On Cluster ${alias} + END + +Restore IPsec With Enforcement Cleanup + [Documentation] Remove enforcement rules and restore IPsec. + [Arguments] ${ipsec_alias} ${enforcement_alias} + Remove NFTables IPsec Enforcement Rules ${enforcement_alias} + Start IPsec Service On Cluster ${ipsec_alias} + Wait For IPsec Tunnel Reestablishment ${ipsec_alias} expected_count=8 diff --git a/test/suites/c2cc/extra/mtu.robot b/test/suites/c2cc/extra/mtu.robot new file mode 100644 index 0000000000..fca275983d --- /dev/null +++ b/test/suites/c2cc/extra/mtu.robot @@ -0,0 +1,53 @@ +*** 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, within the pod MTU (8922, set via ovn.yaml). + Ping DF Bit Between All Clusters 8872 + +DF Bit 8850B Passes At Jumbo MTU + [Documentation] Near pod MTU (8922, NIC MTU 9000 minus 78B headroom, set via + ... ovn.yaml) — accepted on all cluster pairs. + Ping DF Bit Between All Clusters 8850 + +DF Bit 8895B Rejected Above Pod MTU + [Documentation] 8895B + 28B (IPv4) = 8923B > 8922B pod MTU. Rejected. + ... Also 8895B + 48B (IPv6) = 8943B > 8922B. + Ping DF Bit Should Fail Between All Clusters 8895 + +DF Bit 8972B Rejected Well Above Pod MTU + [Documentation] 8972B + 28B = 9000B, well above the 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