#!/bin/bash # Подключение ВМ приватных сегментов к мосту br-lb (шаг 3). # # В целевой среде порты виртуальных машин создаёт гипервизор, и каждая ВМ уже # висит на vSwitch. В контейнерном стенде эту роль выполняет этот скрипт: для # каждого участника сегмента он создаёт veth-пару, один конец кладёт в сетевое # пространство имён контейнера, другой — в netns балансировщика и добавляет в # br-lb с детерминированным ofport. # # Адрес, MAC и маршруты назначаются СНАРУЖИ, как это делают гипервизор и DHCP. # Внутри ОС ВМ ничего не настраивается — это условие шага 3. # # Запускать на хосте с правами root, после docker compose up: # sudo scripts/attach-segment.sh # подключить # sudo scripts/attach-segment.sh --status # показать порты моста # sudo scripts/attach-segment.sh --detach # снять порты set -euo pipefail cd "$(dirname "$0")/.." # shellcheck disable=SC1091 source lb/topology.env LB_CONTAINER=hpnn-lb log() { echo "[attach] $*"; } die() { echo "[attach] ОШИБКА: $*" >&2; exit 1; } [ "$(id -u)" = 0 ] || die "нужны права root (sudo scripts/attach-segment.sh)" pid_of() { local pid pid=$(docker inspect -f '{{.State.Pid}}' "$1" 2>/dev/null) || die "контейнер $1 не найден" [ "$pid" != "0" ] || die "контейнер $1 не запущен" echo "$pid" } # attach <контейнер> <имя порта> <префикс> <шлюз|-> [сеть via LB]... attach() { local cname="$1" port="$2" ofport="$3" mac="$4" ip="$5" plen="$6" gw="$7" shift 7 local pid pid_lb tmp pid=$(pid_of "$cname") pid_lb=$(pid_of "$LB_CONTAINER") if nsenter -t "$pid" -n ip link show eth0 >/dev/null 2>&1; then log "$cname уже подключён, пропуск" return 0 fi # Временное имя нужно потому, что оба конца veth рождаются в netns хоста и # имя eth0 там почти наверняка занято. tmp="tmp$$-${ofport}" ip link add "$port" type veth peer name "$tmp" ip link set "$port" netns "$pid_lb" ip link set "$tmp" netns "$pid" nsenter -t "$pid" -n ip link set "$tmp" name eth0 nsenter -t "$pid" -n ip link set eth0 address "$mac" nsenter -t "$pid" -n ip addr replace "$ip/$plen" dev eth0 nsenter -t "$pid" -n ip link set eth0 up # Профиль A: default остаётся на шлюзе Docker, через LB маршрутизируются # только клиентские префиксы и соседний сегмент. Клиентам не выдаётся ничего. [ "$gw" = "-" ] || nsenter -t "$pid" -n ip route replace default via "$gw" while [ $# -ge 1 ]; do nsenter -t "$pid" -n ip route replace "$1" via "$2" shift 2 done nsenter -t "$pid_lb" -n ip link set "$port" up docker exec "$LB_CONTAINER" ovs-vsctl --may-exist add-port "$BR" "$port" \ -- set Interface "$port" ofport_request="$ofport" >/dev/null log "$cname -> порт $port (ofport $ofport), $ip/$plen, MAC $mac" } detach() { local port for port in be1 be3 cli2 be2 be4 cli3; do docker exec "$LB_CONTAINER" ovs-vsctl --if-exists del-port "$BR" "$port" >/dev/null 2>&1 || true done log "порты сегментов сняты с моста (интерфейсы исчезнут вместе с контейнерами)" } status() { docker exec "$LB_CONTAINER" ovs-ofctl -O "$OF" show "$BR" | grep -E '^\s+[0-9]+\(' } case "${1:-}" in --status) status; exit 0 ;; --detach) detach; exit 0 ;; esac p2len=${P2_NET##*/} p3len=${P3_NET##*/} # Бэкенды сегмента 2: default на docker-шлюз, клиентский префикс и соседний # сегмент — через балансировщик. attach hpnn-be1 be1 "$BE1_OFPORT" "$BE1_MAC" "$BE1_IP" "$p2len" "$P2_DOCKER_GW" \ 192.168.5.0/24 "$LB_P2_IP" "$P3_NET" "$LB_P2_IP" attach hpnn-be3 be3 "$BE3_OFPORT" "$BE3_MAC" "$BE3_IP" "$p2len" "$P2_DOCKER_GW" \ 192.168.5.0/24 "$LB_P2_IP" "$P3_NET" "$LB_P2_IP" attach hpnn-be2 be2 "$BE2_OFPORT" "$BE2_MAC" "$BE2_IP" "$p3len" "$P3_DOCKER_GW" \ 192.168.5.0/24 "$LB_P3_IP" "$P2_NET" "$LB_P3_IP" attach hpnn-be4 be4 "$BE4_OFPORT" "$BE4_MAC" "$BE4_IP" "$p3len" "$P3_DOCKER_GW" \ 192.168.5.0/24 "$LB_P3_IP" "$P2_NET" "$LB_P3_IP" # Клиенты: только адрес и маска. Ни маршрутов, ни дополнительных интерфейсов — # ровно то, что требует условие шага 3. attach hpnn-cli2 cli2 "$CLI2_OFPORT" "$CLI2_MAC" "$CLI2_IP" "$p2len" - attach hpnn-cli3 cli3 "$CLI3_OFPORT" "$CLI3_MAC" "$CLI3_IP" "$p3len" - log "готово" status