#!/usr/bin/env bash
# scripts/dc-dc-mtu-geneve-budget.sh --underlay-mtu <MEASURED_MTU>
#
# Phase-0 gate calculator (tooling gap register item #7): turns D-101's
# ("Tenant/MTU sub-policy", folded in from D-102) prose arithmetic into a
# runnable check, per `runbooks/dc-dc-phase0-vcloud-prep.md` Step 3. Mutates
# NOTHING -- read-only arithmetic against an operator-supplied, MEASURED
# input; safe to re-run any number of times.
#
# The underlay MTU is NEVER inferred or defaulted -- it must be measured this
# session on the real interface(s) VR1 will use (Step 3's own check:
# `ip -o link show | awk '{print $2, $0}' | grep -i mtu`) and passed
# explicitly via --underlay-mtu. There is no --underlay-mtu default on
# purpose (hard rule 2: no inferred value for anything that varies by real
# hardware).
#
# D-101 arithmetic quoted verbatim (docs/design-decisions.md, "Tenant/MTU
# sub-policy" bullet under D-101, folded in from D-102):
# "An IPv6 outer header adds 20 bytes over IPv4 (40 vs 20); geneve-over-v6
# overhead is about 40 (IPv6) + 8 (UDP) + 8 (Geneve base) = 56 bytes,
# before nested-virt and options... Recommendation: raise the vcloud
# underlay to jumbo (9000) end-to-end so tenant MTU stays 1500 and
# amphora/geneve fit; if the underlying is pinned at 1500, tenant MTU
# drops to about 1444 (v6-geneve) and MUST be set consistently across ovn
# geneve, tenant-network MTU, and amphora."
#
# This script implements exactly that: >=9000 underlay -> jumbo, tenant MTU
# stays 1500 (no reduction). Below jumbo -> tenant_mtu = underlay_mtu - 56,
# matching the doc's own worked example exactly (1500 - 56 = 1444).
#
# One extra, clearly-separated sanity floor NOT itself part of D-101's own
# arithmetic: IPv6 requires every link to carry at least a 1280-byte MTU
# (RFC 8200 Section 5) -- if the computed tenant MTU falls below 1280, the
# verdict is FAIL rather than a silent PASS, since a v6-geneve tenant network
# below the IPv6 minimum link MTU is not viable regardless of what D-101
# itself says about the 56-byte overhead.
#
# Output: a human-readable computation + verdict, and a single
# `MTU-BUDGET: ...` summary line at the end suitable for pasting verbatim
# into a changelog entry or the as-executed log (Step 3's "decision to
# record").
#
# Usage: dc-dc-mtu-geneve-budget.sh --underlay-mtu <bytes> [--jumbo-threshold N]
# --underlay-mtu N REQUIRED. The measured host L2 MTU (bytes),
# Step 3's `ip -o link show` output. No default.
# --jumbo-threshold N OPTIONAL. Override the jumbo cutoff (default 9000,
# D-101's own recommended jumbo value). Only for
# exploring alternate policy; do not use to dodge a
# real measurement.
# -h|--help this help.
#
# Exit: 0 PASS (jumbo, or a viable reduced tenant MTU) | 1 FAIL/HOLD (computed
# tenant MTU below the IPv6 minimum link MTU) | 2 usage error (missing/bad
# --underlay-mtu). ASCII + LF.
set -uo pipefail
shopt -s inherit_errexit 2>/dev/null || true
HERE="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
# D-101's own stated geneve-over-v6 overhead: 40 (IPv6 outer header) + 8 (UDP)
# + 8 (Geneve base) = 56 bytes "before nested-virt and options" -- this is
# fixed protocol-header arithmetic from the ADOPTED decision text, not a
# per-host measured value, so it is a constant here (not an argument).
readonly GENEVE_OVER_V6_OVERHEAD=56
# RFC 8200 Section 5: IPv6 requires every link to support at least a
# 1280-byte MTU. Independent of D-101; a defensive sanity floor.
readonly IPV6_MIN_LINK_MTU=1280
readonly DEFAULT_JUMBO_THRESHOLD=9000
usage() { sed -n '2,49p' "$HERE/$(basename "${BASH_SOURCE[0]}")" | sed 's/^# \{0,1\}//'; }
UNDERLAY_MTU=""
JUMBO_THRESHOLD="$DEFAULT_JUMBO_THRESHOLD"
while [ $# -gt 0 ]; do
case "$1" in
--underlay-mtu)
UNDERLAY_MTU="${2:-}"; shift 2 ;;
--underlay-mtu=*)
UNDERLAY_MTU="${1#*=}"; shift ;;
--jumbo-threshold)
JUMBO_THRESHOLD="${2:-}"; shift 2 ;;
--jumbo-threshold=*)
JUMBO_THRESHOLD="${1#*=}"; shift ;;
-h|--help)
usage; exit 0 ;;
*)
echo "FAIL: unknown argument: $1" >&2
echo " run with --help for usage" >&2
exit 2 ;;
esac
done
is_pos_int() { [[ "$1" =~ ^[0-9]+$ ]] && [ "$1" -gt 0 ]; }
if [ -z "$UNDERLAY_MTU" ]; then
echo "FAIL: --underlay-mtu is REQUIRED (no default -- measure it this session:" >&2
echo " ip -o link show | awk '{print \$2, \$0}' | grep -i mtu)" >&2
exit 2
fi
if ! is_pos_int "$UNDERLAY_MTU"; then
echo "FAIL: --underlay-mtu must be a positive integer (bytes); got '$UNDERLAY_MTU'" >&2
exit 2
fi
if ! is_pos_int "$JUMBO_THRESHOLD"; then
echo "FAIL: --jumbo-threshold must be a positive integer (bytes); got '$JUMBO_THRESHOLD'" >&2
exit 2
fi
echo "=== dc-dc-mtu-geneve-budget: D-101 MTU / geneve-over-v6 budget ==="
echo " measured underlay MTU (--underlay-mtu): $UNDERLAY_MTU"
echo " jumbo threshold: $JUMBO_THRESHOLD"
echo " geneve-over-v6 overhead (D-101: 40 IPv6 + 8 UDP + 8 Geneve base): $GENEVE_OVER_V6_OVERHEAD bytes"
echo
VERDICT=""
SUMMARY=""
if [ "$UNDERLAY_MTU" -ge "$JUMBO_THRESHOLD" ]; then
TENANT_MTU=1500
echo " Underlay is jumbo (>= $JUMBO_THRESHOLD)."
echo " Per D-101: tenant MTU stays 1500; geneve/amphora headroom is generous."
echo " Recorded decision: underlay_mtu = $UNDERLAY_MTU (jumbo); tenant MTU = 1500 (unchanged)."
VERDICT="PASS"
SUMMARY="MTU-BUDGET: underlay=$UNDERLAY_MTU jumbo=yes tenant_mtu=1500 verdict=PASS"
else
TENANT_MTU=$((UNDERLAY_MTU - GENEVE_OVER_V6_OVERHEAD))
echo " Underlay is NOT jumbo (< $JUMBO_THRESHOLD)."
echo " Per D-101: tenant_mtu = underlay_mtu - $GENEVE_OVER_V6_OVERHEAD = $UNDERLAY_MTU - $GENEVE_OVER_V6_OVERHEAD = $TENANT_MTU"
if [ "$UNDERLAY_MTU" -eq 1500 ]; then
echo " (This is D-101's own worked example: 1500 - 56 = 1444.)"
fi
if [ "$TENANT_MTU" -lt "$IPV6_MIN_LINK_MTU" ]; then
echo
echo " HOLD: computed tenant MTU ($TENANT_MTU) is BELOW the IPv6 minimum"
echo " link MTU ($IPV6_MIN_LINK_MTU, RFC 8200 Sec 5) -- this underlay MTU"
echo " cannot carry a viable v6-geneve tenant network at all. Raising the"
echo " underlay toward jumbo (or at minimum above $((IPV6_MIN_LINK_MTU + GENEVE_OVER_V6_OVERHEAD))) is required before proceeding."
VERDICT="FAIL"
SUMMARY="MTU-BUDGET: underlay=$UNDERLAY_MTU jumbo=no tenant_mtu=$TENANT_MTU verdict=FAIL (below IPv6 min link MTU $IPV6_MIN_LINK_MTU)"
else
echo
echo " Recorded decision: underlay_mtu = $UNDERLAY_MTU; tenant MTU = $TENANT_MTU (v6-geneve),"
echo " MUST be set consistently across ovn geneve, tenant-network MTU, and amphora"
echo " (propagation happens in later stages per Step 3 -- this only records the decision)."
VERDICT="PASS"
SUMMARY="MTU-BUDGET: underlay=$UNDERLAY_MTU jumbo=no tenant_mtu=$TENANT_MTU verdict=PASS"
fi
fi
echo
echo "VERDICT: $VERDICT"
echo "$SUMMARY"
[ "$VERDICT" = "PASS" ] && exit 0 || exit 1