Commit 277765cd authored by Yann Garcia's avatar Yann Garcia
Browse files

Add iperf client in monaco-harbor scenarios

parent 49016587
Loading
Loading
Loading
Loading
+1 −1
Changes for go-packages/meep-nw-simu-catalog/instance.go: 1 added line, 1 removed line.
Original line number Diff line number Diff line
@@ -208,7 +208,7 @@ func (c *Client) DeleteInstance(ctx context.Context, sandboxID string, retainArt

	path := c.cfg.NemURL + "/v1/instances/" + url.PathEscape(sandboxID)
	if retainArtifacts {
		path += "?retain_artifacts=true"
		path += "?retain_artifacts=false"
	}
	raw, status, err := c.doInstance(ctx, http.MethodDelete, path, nil)
	if err != nil {
+105 −15
Changes for tools/simu5g/install-simu5g-routes-sandbox.sh: 105 added lines, 15 removed lines.
Original line number Diff line number Diff line
@@ -32,8 +32,8 @@ CONFIG_FILE="${CONFIG_FILE:-${SCRIPT_DIR}/example-integration-config.json}"
# Host routing / TUN (same defaults as install-simu5g-routes.sh)
TUN_DEV="${TUN_DEV:-simu5g-br0}"
TUN_ADDR="${TUN_ADDR:-169.254.250.1/32}"
CNI_DEV="${CNI_DEV:-weave}"
POD_CIDR="${POD_CIDR:-10.32.0.0/12}"
CNI_DEV="${CNI_DEV:-}"
POD_CIDR="${POD_CIDR:-}"
MGMT_DEV="${MGMT_DEV:-ens160}"
UPLINK_ONLY="${UPLINK_ONLY:-0}"
STATE_DIR="${STATE_DIR:-/var/lib/simu5g-routes}"
@@ -43,6 +43,10 @@ UE_POD_SELECTOR=""
MEC_POD_SELECTOR=""
UE_CIDRS=""
MEC_CIDRS=""
mec_names=()
ue_names=()
mec_ips=()
ue_ips=()

log() { printf '[%s] %s\n' "$SCRIPT_NAME" "$*"; }
die() { log "ERROR: $*"; exit 1; }
@@ -51,6 +55,21 @@ require_root() {
  [[ "${EUID:-$(id -u)}" -eq 0 ]] || die "run as root (sudo $SCRIPT_NAME $*)"
}

# sudo resets HOME to /root, so kubectl would miss the operator's cluster.
# Prefer SUDO_USER's kubeconfig unless KUBECONFIG was set explicitly.
resolve_kube_identity() {
  local sudo_home
  if [[ "${EUID:-$(id -u)}" -eq 0 && -n "${SUDO_USER:-}" && "$SUDO_USER" != "root" ]]; then
    sudo_home="$(getent passwd "$SUDO_USER" | cut -d: -f6)"
    if [[ -z "${KUBECONFIG:-}" && -n "$sudo_home" && -f "${sudo_home}/.kube/config" ]]; then
      export KUBECONFIG="${sudo_home}/.kube/config"
      log "sudo: using ${SUDO_USER} kubeconfig ${KUBECONFIG}"
    fi
  fi
  command -v "$KUBECTL" >/dev/null 2>&1 || die "kubectl not found (PATH=$PATH)"
  log "kubectl=$(command -v "$KUBECTL") KUBECONFIG=${KUBECONFIG:-<default $HOME/.kube/config>} context=$("$KUBECTL" config current-context 2>/dev/null || echo '<none>')"
}

# Create and bring up the bridge TUN (mirrors install-simu5g-routes.sh setup_tun).
setup_tun() {
  if ! ip link show "$TUN_DEV" >/dev/null 2>&1; then
@@ -84,15 +103,15 @@ Options (environment or flags):
  -n, --namespace NS     Sandbox namespace (default: ${SANDBOX_NS})
  --scenario-selector S  kubectl -l filter before name split (default: ${SCENARIO_SELECTOR})
  --mec-prefix P         MEC pod name prefix (default: ${MEC_NAME_PREFIX})
  --config-file PATH     Passed to install-simu5g-routes.sh (default: example-integration-config.json)
  --config-file PATH     Passed to install-simu5g-routes.sh; also rewrites sandboxIpCidr from live PodIPs

TUN / host routing (passed through to ${INSTALLER##*/}):
  TUN_DEV=$TUN_DEV  TUN_ADDR=$TUN_ADDR  CNI_DEV=$CNI_DEV

Examples:
  $SCRIPT_NAME discover -n sbxvipkywt
  sudo $SCRIPT_NAME setup-tun
  sudo $SCRIPT_NAME apply -n sbxvipkywt
  $SCRIPT_NAME discover -n sbx4lesbpo --config-file ${SCRIPT_DIR}/monaco-harbor-integration-config.json
  sudo $SCRIPT_NAME apply -n sbx4lesbpo --config-file ${SCRIPT_DIR}/monaco-harbor-integration-config.json
  # If sudo still sees 0 pods: sudo KUBECONFIG=\$HOME/.kube/config $SCRIPT_NAME apply ...
  sudo $SCRIPT_NAME status
  sudo $SCRIPT_NAME delete-tun
  sudo $SCRIPT_NAME rollback
@@ -103,6 +122,7 @@ EOF

parse_args() {
  ACTION=""
  CONFIG_FILE_EXPLICIT=0
  while [[ $# -gt 0 ]]; do
    case "$1" in
      discover|setup-tun|apply|status|rollback|delete-tun)
@@ -124,6 +144,7 @@ parse_args() {
        ;;
      --config-file)
        CONFIG_FILE="${2:?config path required}"
        CONFIG_FILE_EXPLICIT=1
        shift 2
        ;;
      -h|--help|help)
@@ -155,9 +176,15 @@ pods_to_cidr_csv() {
# List scenario pods: "name<TAB>ip" per line (portable jsonpath — no ?() filter).
list_scenario_pods_with_selector() {
  local selector=$1
  local out
  out="$("$KUBECTL" -n "$SANDBOX_NS" get pods -l "$selector" \
    -o jsonpath='{range .items[*]}{.metadata.name}{"\t"}{.status.podIP}{"\n"}{end}' 2>/dev/null)" || return 1
  local out err
  err="$(mktemp)"
  if ! out="$("$KUBECTL" -n "$SANDBOX_NS" get pods -l "$selector" \
    -o jsonpath='{range .items[*]}{.metadata.name}{"\t"}{.status.podIP}{"\n"}{end}' 2>"$err")"; then
    log "WARN: kubectl get pods -l ${selector} failed: $(tr '\n' ' ' <"$err")"
    rm -f "$err"
    return 1
  fi
  rm -f "$err"
  while IFS=$'\t' read -r name ip; do
    [[ -n "$name" && -n "$ip" ]] || continue
    printf '%s\t%s\n' "$name" "$ip"
@@ -172,24 +199,81 @@ list_scenario_pods() {

  tmp="$(mktemp)"
  list_scenario_pods_with_selector "$SCENARIO_SELECTOR" >"$tmp" || true
  rows=$(grep -c . "$tmp" 2>/dev/null || echo 0)
  rows=$(wc -l <"$tmp" | tr -d ' ')

  if [[ "$rows" -eq 0 ]]; then
  if [[ "${rows:-0}" -eq 0 ]]; then
    local fallback="meepSandbox=${SANDBOX_NS}"
    log "WARN: no pods for -l ${SCENARIO_SELECTOR}; trying fallback -l ${fallback}"
    list_scenario_pods_with_selector "$fallback" >"$tmp" || true
    rows=$(grep -c . "$tmp" 2>/dev/null || echo 0)
    rows=$(wc -l <"$tmp" | tr -d ' ')
  fi

  cat "$tmp"
  rm -f "$tmp"
}

# Rewrite ueMappings/mecEndpoints sandboxIpCidr in --config-file from live pod names.
write_config_cidrs() {
  [[ "${CONFIG_FILE_EXPLICIT:-0}" == 1 ]] || return 0
  [[ -f "$CONFIG_FILE" ]] || die "--config-file not found: $CONFIG_FILE"
  command -v python3 >/dev/null 2>&1 || die "python3 required to rewrite $CONFIG_FILE"

  local pairs tmp
  pairs="$(mktemp)"
  tmp="$(mktemp)"
  local i
  for i in "${!mec_names[@]}"; do
    printf '%s %s\n' "${mec_names[$i]}" "${mec_ips[$i]}" >>"$pairs"
  done
  for i in "${!ue_names[@]}"; do
    printf '%s %s\n' "${ue_names[$i]}" "${ue_ips[$i]}" >>"$pairs"
  done
  python3 - "$CONFIG_FILE" "$pairs" "$tmp" <<'PY'
import json, pathlib, sys

cfg_path, pairs_path, out_path = sys.argv[1], sys.argv[2], sys.argv[3]
cfg = json.loads(pathlib.Path(cfg_path).read_text())
pods = []
for line in pathlib.Path(pairs_path).read_text().splitlines():
    name, ip = line.split()
    pods.append((name, ip))

def match_ue(ueid, name):
    return name == ueid or name.startswith(ueid + "-")

for mapping in cfg.get("ueMappings", []):
    ueid = mapping.get("ueId") or ""
    hits = [ip for name, ip in pods if match_ue(ueid, name)]
    if len(hits) == 1:
        mapping["sandboxIpCidr"] = hits[0] + "/32"
    elif len(hits) > 1:
        print(f"WARN: multiple pods for ueId={ueid}: {hits}", file=sys.stderr)
    else:
        print(f"WARN: no pod for ueId={ueid}", file=sys.stderr)

mec_pods = [(n, ip) for n, ip in pods if n.startswith("mep")]
for i, ep in enumerate(cfg.get("mecEndpoints", [])):
    eid = ep.get("endpointId") or ""
    hits = [ip for n, ip in mec_pods if n.startswith(eid)]
    if not hits and i < len(mec_pods):
        hits = [mec_pods[i][1]]
    if hits:
        ep["sandboxIpCidr"] = hits[0] + "/32"
    else:
        print(f"WARN: no MEC pod for endpointId={eid}", file=sys.stderr)

pathlib.Path(out_path).write_text(json.dumps(cfg, indent=2) + "\n")
PY
  mv "$tmp" "$CONFIG_FILE"
  rm -f "$pairs"
  log "updated sandboxIpCidr in $CONFIG_FILE from live PodIPs"
}

discover_pod_groups() {
  command -v "$KUBECTL" >/dev/null 2>&1 || die "kubectl not found"

  local mec_names=() ue_names=()
  local mec_ips=() ue_ips=()
  mec_names=() ue_names=()
  mec_ips=() ue_ips=()

  while IFS=$'\t' read -r name ip; do
    [[ -n "$name" && -n "$ip" ]] || continue
@@ -218,6 +302,9 @@ discover_pod_groups() {
  printf '%s\n' "MEC_CIDRS=${MEC_CIDRS:-<none>}"
  printf '%s\n' "UE_CIDRS=${UE_CIDRS:-<none>}"
  printf '%s\n' "TUN_DEV=${TUN_DEV} TUN_ADDR=${TUN_ADDR} (use setup-tun or apply to create)"
  if ip link show bpfin.cali >/dev/null 2>&1; then
    printf '%s\n' "CNI=Calico (per-pod cali* veths) — do not pass CNI_DEV=cali...; apply auto-detects"
  fi
  printf '\n%s\n' "TUN interface:"
  show_tun_status
  printf '\n%s\n' "MEC pods (${#mec_names[@]}):"
@@ -241,6 +328,7 @@ discover_pod_groups() {

  [[ -n "$MEC_CIDRS" ]] || die "no MEC pods (name prefix '${MEC_NAME_PREFIX}') in namespace ${SANDBOX_NS}"
  [[ -n "$UE_CIDRS" ]] || die "no UE pods (non-${MEC_NAME_PREFIX} scenario pods) in namespace ${SANDBOX_NS}"
  write_config_cidrs
}

run_installer() {
@@ -276,6 +364,7 @@ cmd_delete_tun() {

main() {
  parse_args "$@"
  resolve_kube_identity

  case "$ACTION" in
    discover)
@@ -289,7 +378,8 @@ main() {
      ;;
    apply)
      require_root apply
      discover_pod_groups >/dev/null
      # Do not redirect discover: errors (no pods) must be visible. TUN is created only after pods exist.
      discover_pod_groups
      log "MEC_POD_SELECTOR=${MEC_POD_SELECTOR}"
      log "UE_POD_SELECTOR=${UE_POD_SELECTOR}"
      log "MEC_CIDRS=${MEC_CIDRS}"
+390 −0
Changes for tools/simu5g/install-simu5g-routes.sh: 390 added lines, 0 removed lines.
Original line number Diff line number Diff line
#!/usr/bin/env bash
# install-simu5g-routes.sh — Host routing on sandbox host B for Simu5G TUN bridge.
#
# Steers UE→MEC (and optionally MEC→UE) pod traffic through simu5g-br0 using
# policy routing so CNI downlink from the bridge still uses the overlay route.
#
# Usage:
#   sudo ./install-simu5g-routes.sh apply
#   sudo ./install-simu5g-routes.sh status
#   sudo ./install-simu5g-routes.sh rollback
#
# Environment overrides (optional):
#   CONFIG_FILE=path/to/integration-config.json
#   KUBECTL=kubectl  SANDBOX_NS=default  DISCOVER_PODS=1|0
#   UE_CIDRS="192.168.108.154/32,..."
#   MEC_CIDRS="192.168.108.157/32"
#   TUN_DEV=simu5g-br0  CNI_DEV=  (empty: auto; Weave=weave; Calico=leave empty)
#   Do NOT set CNI_DEV to a Calico per-pod veth (cali*). Downlink uses existing /32 routes.
#   UPLINK_ONLY=1  (1: skip MEC→UE policy rules; 0: bidirectional steer; default 0)
#   STATE_DIR=/var/lib/simu5g-routes

set -euo pipefail

readonly SCRIPT_NAME="${0##*/}"
readonly RT_TABLE_NAME="${RT_TABLE_NAME:-simu5g}"
readonly RT_TABLE_ID="${RT_TABLE_ID:-100}"
readonly TUN_DEV="${TUN_DEV:-simu5g-br0}"
readonly TUN_ADDR="${TUN_ADDR:-169.254.250.1/32}"
CNI_DEV="${CNI_DEV:-}"
SKIP_CNI_HOST_ROUTES="${SKIP_CNI_HOST_ROUTES:-0}"
POD_CIDR="${POD_CIDR:-}"
readonly MGMT_DEV="${MGMT_DEV:-ens160}"
readonly MGMT_CIDR="${MGMT_CIDR:-172.29.0.0/16}"
readonly HOST_IP="${HOST_IP:-172.29.10.53}"
readonly STATE_DIR="${STATE_DIR:-/var/lib/simu5g-routes}"
readonly STATE_FILE="${STATE_DIR}/routes.state"
readonly CONFIG_FILE="${CONFIG_FILE:-$(dirname "$0")/example-integration-config.json}"
readonly KUBECTL="${KUBECTL:-kubectl}"
readonly SANDBOX_NS="${SANDBOX_NS:-}"
readonly DISCOVER_PODS="${DISCOVER_PODS:-1}"
readonly UE_POD_SELECTOR="${UE_POD_SELECTOR:-meepOrigin=scenario}"
readonly MEC_POD_SELECTOR="${MEC_POD_SELECTOR:-meepOrigin=scenario}"
readonly UPLINK_ONLY="${UPLINK_ONLY:-0}"
readonly RULE_PRIO_BASE="${RULE_PRIO_BASE:-9000}"

log() { printf '[%s] %s\n' "$SCRIPT_NAME" "$*"; }
die() { log "ERROR: $*"; exit 1; }

require_root() {
  [[ "${EUID:-$(id -u)}" -eq 0 ]] || die "run as root (sudo $SCRIPT_NAME $*)"
}

rt_table_registered() {
  local f
  for f in /etc/iproute2/rt_tables /usr/lib/iproute2/rt_tables /usr/share/iproute2/rt_tables \
           /etc/iproute2/rt_tables.d/*.conf /usr/lib/iproute2/rt_tables.d/*.conf; do
    [[ -f "$f" ]] || continue
    grep -qE "^[[:space:]]*${RT_TABLE_ID}[[:space:]]+${RT_TABLE_NAME}([[:space:]]|$)" "$f" 2>/dev/null && return 0
  done
  return 1
}

# Name table 100 as "simu5g" when possible. iproute2 on this host may have no
# /etc/iproute2/rt_tables; policy rules still work with the numeric table id.
ensure_rt_table() {
  if rt_table_registered; then
    return 0
  fi
  local dropin_dir="/etc/iproute2/rt_tables.d"
  local dropin="${dropin_dir}/simu5g.conf"
  local legacy="/etc/iproute2/rt_tables"
  if mkdir -p "$dropin_dir" 2>/dev/null; then
    printf '%s %s\n' "$RT_TABLE_ID" "$RT_TABLE_NAME" >"$dropin"
    log "added rt_tables drop-in: ${dropin} (${RT_TABLE_ID} ${RT_TABLE_NAME})"
    return 0
  fi
  if mkdir -p /etc/iproute2 2>/dev/null && touch "$legacy" 2>/dev/null; then
    printf '%s %s\n' "$RT_TABLE_ID" "$RT_TABLE_NAME" >>"$legacy"
    log "added rt_tables entry: ${RT_TABLE_ID} ${RT_TABLE_NAME}"
    return 0
  fi
  log "WARN: cannot write iproute2 rt_tables; using numeric table ${RT_TABLE_ID} (lookup still works)"
}

state_init() {
  mkdir -p "$STATE_DIR"
  : > "$STATE_FILE"
}

state_append() {
  printf '%s\n' "$1" >> "$STATE_FILE"
}

run_state() {
  local kind=$1
  shift
  case "$kind" in
    rule) ip rule add "$@" ;;
    route) ip route add "$@" ;;
    tun) ip tuntap add dev "$TUN_DEV" mode tun user root ;;
    tun_addr) ip addr add "$TUN_ADDR" dev "$TUN_DEV" 2>/dev/null || true ;;
    link_up) ip link set "$TUN_DEV" up ;;
    sysctl) sysctl -w "$1" >/dev/null ;;
    forward) echo 1 > "/proc/sys/net/ipv4/conf/${1}/forwarding" ;;
    rp_filter) echo 0 > "/proc/sys/net/ipv4/conf/${1}/rp_filter" ;;
    *) die "internal: unknown state kind $kind" ;;
  esac
  state_append "${kind}|$*"
}

cidr_to_ips() {
  local cidr=$1
  if [[ "$cidr" == */32 ]] || [[ "$cidr" != */* ]]; then
    [[ "$cidr" == */* ]] || cidr="${cidr}/32"
    echo "${cidr%%/*}"
    return
  fi
  echo "$cidr"
}

split_csv() {
  local IFS=,
  read -r -a _arr <<< "${1// /}"
  printf '%s\n' "${_arr[@]}"
}

discover_pod_ips() {
  local selector=$1
  local ns_arg=()
  [[ -n "$SANDBOX_NS" ]] && ns_arg=(-n "$SANDBOX_NS")
  "$KUBECTL" "${ns_arg[@]}" get pods -l "$selector" \
    -o jsonpath='{range .items[?(@.status.podIP)]}{.status.podIP}{"\n"}{end}' 2>/dev/null || true
}

pods_to_cidr_csv() {
  local csv=""
  while read -r ip; do
    [[ -n "$ip" ]] || continue
    csv+="${ip}/32,"
  done
  printf '%s' "${csv%,}"
}

discover_ue_mec_ips() {
  UE_DISCOVERED=""
  MEC_DISCOVERED=""
  if [[ "$DISCOVER_PODS" != "1" ]] || ! command -v "$KUBECTL" >/dev/null 2>&1; then
    return 0
  fi
  if [[ "$UE_POD_SELECTOR" == "$MEC_POD_SELECTOR" ]]; then
    log "WARN: UE_POD_SELECTOR == MEC_POD_SELECTOR; set distinct selectors or use UE_CIDRS/MEC_CIDRS"
    return 0
  fi
  UE_DISCOVERED=$(discover_pod_ips "$UE_POD_SELECTOR" | pods_to_cidr_csv)
  MEC_DISCOVERED=$(discover_pod_ips "$MEC_POD_SELECTOR" | pods_to_cidr_csv)
}

resolve_cidr_lists() {
  UE_CIDRS="${UE_CIDRS:-}"
  MEC_CIDRS="${MEC_CIDRS:-}"

  if [[ -z "$UE_CIDRS" || -z "$MEC_CIDRS" ]]; then
    discover_ue_mec_ips
    [[ -z "$UE_CIDRS" && -n "${UE_DISCOVERED:-}" ]] && UE_CIDRS="$UE_DISCOVERED"
    [[ -z "$MEC_CIDRS" && -n "${MEC_DISCOVERED:-}" ]] && MEC_CIDRS="$MEC_DISCOVERED"
  fi

  if [[ -z "$UE_CIDRS" ]] && command -v jq >/dev/null 2>&1 && [[ -f "$CONFIG_FILE" ]]; then
    UE_CIDRS=$(jq -r '[.ueMappings[]?.sandboxIpCidr] | join(",")' "$CONFIG_FILE" 2>/dev/null || true)
    log "WARN: using logical UE CIDRs from config — prefer live PodIPs (/32)"
  fi
  if [[ -z "$MEC_CIDRS" ]] && command -v jq >/dev/null 2>&1 && [[ -f "$CONFIG_FILE" ]]; then
    MEC_CIDRS=$(jq -r '[.mecEndpoints[]?.sandboxIpCidr] | join(",")' "$CONFIG_FILE" 2>/dev/null || true)
    log "WARN: using logical MEC CIDRs from config — prefer live PodIPs (/32)"
  fi

  [[ -n "$UE_CIDRS" ]] || die "set UE_CIDRS or run with kubectl + scenario pods (meepOrigin=scenario)"
  [[ -n "$MEC_CIDRS" ]] || die "set MEC_CIDRS or run with kubectl + scenario pods (meepOrigin=scenario)"
}

calico_present() {
  ip link show bpfin.cali >/dev/null 2>&1 || ip -o link show 2>/dev/null | grep -q '[: ]cali'
}

resolve_cni_dev() {
  if [[ "${CNI_DEV:-}" == "cache" ]]; then
    die "CNI_DEV=cache is not a network device (that token is from the 'cache' line of ip route get)"
  fi
  if [[ "${CNI_DEV:-}" == cali* ]]; then
    log "WARN: CNI_DEV=${CNI_DEV} is a Calico per-pod veth — ignoring. Do not pin every pod /32 onto one cali* interface."
    CNI_DEV=""
  fi
  if [[ -z "${CNI_DEV:-}" ]]; then
    if ip link show weave >/dev/null 2>&1; then
      CNI_DEV="weave"
      [[ -n "${POD_CIDR:-}" ]] || POD_CIDR="10.32.0.0/12"
    elif calico_present; then
      log "Calico detected: TUN downlink uses existing per-pod /32 routes; no shared overlay device"
      SKIP_CNI_HOST_ROUTES=1
    fi
  fi
}

verify_node() {
  resolve_cni_dev
  ip link show "$MGMT_DEV" >/dev/null 2>&1 || die "mgmt device '$MGMT_DEV' not found"
  if [[ -n "${CNI_DEV:-}" ]]; then
    ip link show "$CNI_DEV" >/dev/null 2>&1 || die "CNI device '$CNI_DEV' not found"
    if [[ -n "${POD_CIDR:-}" ]]; then
      ip route show dev "$CNI_DEV" | grep -q "$POD_CIDR" \
        || log "WARN: $POD_CIDR not seen on $CNI_DEV — continuing"
    fi
  fi
}

setup_tun() {
  if ! ip link show "$TUN_DEV" >/dev/null 2>&1; then
    run_state tun
    log "created TUN $TUN_DEV"
  else
    log "TUN $TUN_DEV already exists"
  fi
  run_state tun_addr
  run_state link_up
}

setup_sysctl() {
  run_state sysctl net.ipv4.ip_forward=1
  for dev in all default "$TUN_DEV" "$MGMT_DEV" ${CNI_DEV:+"$CNI_DEV"} bpfin.cali; do
    [[ -d "/proc/sys/net/ipv4/conf/$dev" ]] || continue
    run_state rp_filter "$dev"
  done
}

apply_policy_routes() {
  local ue mec prio=$RULE_PRIO_BASE
  ensure_rt_table

  # Cellular-steered traffic uses TUN. Creating the first route instantiates the FIB table.
  if ! ip route show table "$RT_TABLE_ID" 2>/dev/null | grep -q "dev ${TUN_DEV}"; then
    run_state route table "$RT_TABLE_ID" default dev "$TUN_DEV"
  fi

  while IFS= read -r ue; do
    [[ -n "$ue" ]] || continue
    local ue_ip=${ue%%/*}
    while IFS= read -r mec; do
      [[ -n "$mec" ]] || continue
      local mec_ip=${mec%%/*}
      # ip rule prints "lookup simu5g" (name) or "lookup 100" (id); addresses without /32.
      if ! ip rule list | grep -qE "from ${ue_ip}[[:space:]].* to ${mec_ip}[[:space:]].* lookup (${RT_TABLE_ID}|${RT_TABLE_NAME})"; then
        run_state rule from "$ue" to "$mec" lookup "$RT_TABLE_ID" priority "$prio"
        log "policy: from $ue to $mec → table ${RT_TABLE_NAME} (${RT_TABLE_ID})"
      fi
      prio=$((prio + 1))
      if [[ "$UPLINK_ONLY" != "1" ]]; then
        if ! ip rule list | grep -qE "from ${mec_ip}[[:space:]].* to ${ue_ip}[[:space:]].* lookup (${RT_TABLE_ID}|${RT_TABLE_NAME})"; then
          run_state rule from "$mec" to "$ue" lookup "$RT_TABLE_ID" priority "$prio"
          log "policy: from $mec to $ue → table ${RT_TABLE_NAME} (${RT_TABLE_ID})"
        fi
        prio=$((prio + 1))
      fi
    done < <(split_csv "$MEC_CIDRS")
  done < <(split_csv "$UE_CIDRS")

  # Weave-style shared overlay only. Calico already has per-pod /32 via cali* veths —
  # rewriting them onto one CNI_DEV would black-hole every other pod.
  if [[ "$SKIP_CNI_HOST_ROUTES" != "1" && -n "${CNI_DEV:-}" ]]; then
    while IFS= read -r cidr; do
      [[ -n "$cidr" ]] || continue
      local ip
      ip=$(cidr_to_ips "$cidr")
      if [[ "$cidr" == */32 ]] && ! ip route show "$ip/32" dev "$CNI_DEV" 2>/dev/null | grep -q .; then
        if ip route replace "$ip/32" dev "$CNI_DEV" 2>/dev/null; then
          state_append "route|${ip}/32 dev ${CNI_DEV}"
          log "CNI host route: ${ip}/32 dev ${CNI_DEV}"
        fi
      fi
    done < <({ split_csv "$UE_CIDRS"; split_csv "$MEC_CIDRS"; })
  fi
}

cmd_apply() {
  require_root apply
  verify_node
  resolve_cidr_lists
  state_init
  log "UE CIDRS: $UE_CIDRS"
  log "MEC CIDRS: $MEC_CIDRS"
  log "CNI=$CNI_DEV POD_CIDR=$POD_CIDR MGMT=$MGMT_DEV ($HOST_IP) TUN=$TUN_DEV"
  setup_tun
  setup_sysctl
  apply_policy_routes
  log "apply complete — state saved to $STATE_FILE"
  log "start bridge: go run ./go-apps/meep-simu5g-bridge --config $CONFIG_FILE"
  cmd_status
}

cmd_status() {
  log "=== routing table (main) ==="
  ip route show table main | sed 's/^/  /'
  log "=== policy rules (simu5g-related) ==="
  ip rule list | grep -E "lookup (${RT_TABLE_ID}|${RT_TABLE_NAME})( |$)" | sed 's/^/  /' || true
  log "=== table ${RT_TABLE_NAME} (${RT_TABLE_ID}) ==="
  ip route show table "$RT_TABLE_ID" 2>/dev/null | sed 's/^/  /' || true
  log "=== interfaces ==="
  ip -br addr show "$TUN_DEV" "$MGMT_DEV" ${CNI_DEV:+"$CNI_DEV"} 2>/dev/null | sed 's/^/  /' || true
  log "=== rp_filter ==="
  for dev in "$TUN_DEV" "$MGMT_DEV" ${CNI_DEV:+"$CNI_DEV"}; do
    [[ -r "/proc/sys/net/ipv4/conf/$dev/rp_filter" ]] \
      && printf '  %s rp_filter=%s\n' "$dev" "$(cat "/proc/sys/net/ipv4/conf/$dev/rp_filter")"
  done
}

rollback_rule() {
  # state: rule|from X to Y lookup Z priority N
  local args=("$@")
  ip rule del "${args[@]}" 2>/dev/null || log "WARN: could not del rule ${args[*]}"
}

rollback_route() {
  local args=("$@")
  ip route del "${args[@]}" 2>/dev/null || ip route del table "$RT_TABLE_ID" default dev "$TUN_DEV" 2>/dev/null || true
}

cmd_rollback() {
  require_root rollback
  [[ -f "$STATE_FILE" ]] || die "no state file at $STATE_FILE — nothing to roll back"

  log "rolling back from $STATE_FILE"
  tac "$STATE_FILE" | while IFS='|' read -r kind rest; do
    case "$kind" in
      rule)
        # shellcheck disable=SC2086
        rollback_rule $rest
        ;;
      route)
        # shellcheck disable=SC2086
        rollback_route $rest
        ;;
      tun)
        ip link del "$TUN_DEV" 2>/dev/null || true
        ;;
      rp_filter)
        dev=${rest%% *}
        [[ -w "/proc/sys/net/ipv4/conf/$dev/rp_filter" ]] && echo 1 > "/proc/sys/net/ipv4/conf/$dev/rp_filter"
        ;;
      sysctl)
        sysctl -w net.ipv4.ip_forward=0 >/dev/null 2>&1 || true
        ;;
      tun_addr|link_up|forward) ;;
      *) log "WARN: unknown state record: $kind|$rest" ;;
    esac
  done

  rm -f "$STATE_FILE"
  log "rollback complete"
}

usage() {
  cat <<EOF
Usage: sudo $SCRIPT_NAME {apply|status|rollback}

Host B defaults:
  MGMT_DEV=$MGMT_DEV  HOST_IP=$HOST_IP  CNI_DEV=$CNI_DEV  POD_CIDR=$POD_CIDR
  TUN_DEV=$TUN_DEV

Examples:
  sudo UE_CIDRS="192.168.108.154/32" MEC_CIDRS="192.168.108.157/32" \\
       CNI_DEV=caliXXXX POD_CIDR=192.168.108.0/24 \\
       CONFIG_FILE=/path/to/integration-config.json $SCRIPT_NAME apply

  sudo $SCRIPT_NAME status
  sudo $SCRIPT_NAME rollback

Rollback removes only changes recorded in $STATE_FILE.
EOF
}

main() {
  case "${1:-}" in
    apply) cmd_apply ;;
    status) cmd_status ;;
    rollback) cmd_rollback ;;
    -h|--help|help) usage ;;
    *) usage; exit 1 ;;
  esac
}

main "$@"
+42 −10
Changes for tools/simu5g/monaco-harbor-integration-config.json: 42 added lines, 10 removed lines.
Original line number Diff line number Diff line
@@ -29,19 +29,51 @@
    "meAppInst": "monaco-harbor"
  },
  "ueMappings": [
    {"ueId": "ue-car-0", "sandboxIpCidr": "10.0.0.1/32", "simu5gModulePath": "MonacoHarbor.ue_car[0]"},
    {"ueId": "ue-car-1", "sandboxIpCidr": "10.0.0.2/32", "simu5gModulePath": "MonacoHarbor.ue_car[1]"},
    {"ueId": "ue-car-2", "sandboxIpCidr": "10.0.0.3/32", "simu5gModulePath": "MonacoHarbor.ue_car[2]"},
    {"ueId": "ue-car-3", "sandboxIpCidr": "10.0.0.4/32", "simu5gModulePath": "MonacoHarbor.ue_car[3]"},
    {"ueId": "ue-ped-0", "sandboxIpCidr": "10.0.0.11/32", "simu5gModulePath": "MonacoHarbor.ue_ped[0]"},
    {"ueId": "ue-ped-1", "sandboxIpCidr": "10.0.0.12/32", "simu5gModulePath": "MonacoHarbor.ue_ped[1]"},
    {"ueId": "ue-ped-2", "sandboxIpCidr": "10.0.0.13/32", "simu5gModulePath": "MonacoHarbor.ue_ped[2]"},
    {"ueId": "ue-ped-3", "sandboxIpCidr": "10.0.0.14/32", "simu5gModulePath": "MonacoHarbor.ue_ped[3]"}
    {
      "ueId": "ue-car-0",
      "sandboxIpCidr": "192.168.108.187/32",
      "simu5gModulePath": "MonacoHarbor.ue_car[0]"
    },
    {
      "ueId": "ue-car-1",
      "sandboxIpCidr": "192.168.108.148/32",
      "simu5gModulePath": "MonacoHarbor.ue_car[1]"
    },
    {
      "ueId": "ue-car-2",
      "sandboxIpCidr": "192.168.108.184/32",
      "simu5gModulePath": "MonacoHarbor.ue_car[2]"
    },
    {
      "ueId": "ue-car-3",
      "sandboxIpCidr": "192.168.108.149/32",
      "simu5gModulePath": "MonacoHarbor.ue_car[3]"
    },
    {
      "ueId": "ue-ped-0",
      "sandboxIpCidr": "192.168.108.150/32",
      "simu5gModulePath": "MonacoHarbor.ue_ped[0]"
    },
    {
      "ueId": "ue-ped-1",
      "sandboxIpCidr": "192.168.108.182/32",
      "simu5gModulePath": "MonacoHarbor.ue_ped[1]"
    },
    {
      "ueId": "ue-ped-2",
      "sandboxIpCidr": "192.168.108.171/32",
      "simu5gModulePath": "MonacoHarbor.ue_ped[2]"
    },
    {
      "ueId": "ue-ped-3",
      "sandboxIpCidr": "192.168.108.188/32",
      "simu5gModulePath": "MonacoHarbor.ue_ped[3]"
    }
  ],
  "mecEndpoints": [
    {
      "endpointId": "mec1",
      "sandboxIpCidr": "10.20.0.0/24",
      "endpointId": "mep1",
      "sandboxIpCidr": "192.168.108.181/32",
      "simu5gModulePath": "router"
    }
  ],
+39 −0

File added.

Preview size limit exceeded, changes collapsed.