Virtuelles Netzwerk mit Open vSwitch

Das Ostseepinguin-Banner zeigt einen Ostseepinguin am Strand.

Virtuelles Netzwerk mit Open vSwitch

In diesem Projekt möchte ich zeigen, wie ein virtuelles Netzwerk unter Verwendung von Openvswitch und Netzwerknamenräumen unter Linux entworfen und implementiert werden kann. Durch die Nutzung virtueller Ethernet-Tunnel und eines zentralen virtuellen Switches erstellen wir eine flexible und skalierbare Netzwerkumgebung. Mein Hauptziel ist dabei - neben einem besseren Verständnis der Linux-Namenspaces - eine isolierte Netzwerkumgebung zu haben. Als Lehrer möchte ich manchmal meinen Schülern zeigen, wie ein einzelnes Netzwerkprotokoll funktioniert oder was passiert, wenn ich eine Domain pinge. Doch selbst in einer virtuellen Maschine laufen oft viele andere Netzwerkverbindungen im Hintergrund, sodass es schwierig ist, herauszufiltern, woran ich interessiert bin. In der Konfiguration, die ich hier erstelle, ist das einzige Rauschen im virtuellen Netzwerk das IPv6-Router-Solicitation.

Der Plan

Das folgende Diagramm veranschaulicht die virtuelle Netzwerkkonfiguration, die ich erreichen möchte: Der virtuelle Switch dient als zentraler Punkt und verbindet drei verschiedene Netzwerknamenräume, die durch rot, grün und blau dargestellt sind. Jeder Namespace erhält eine IP-Adresse (eth0-r, eth0-g, eth0-b) innerhalb des Subnetzes 10.0.0.0/24 und ist über virtuelle Ethernet-Schnittstellen (veth-r, veth-g, veth-b) mit dem vSwitch verbunden.

Netzwerkschema, das die Beziehung zwischen den Namespacesn gemäß dem obigen Absatz zeigt.

Figure 1: Netzwerkschema

Funktionen für jeden Namespace erstellen

Zunächst definieren wir zwei Variablen, um die beabsichtigten Namespaces und ihre zugehörigen Farben einzurichten. Die erste Variable ist eine einfache Indexliste mit den Namen red, green und blue. Die zweite Variable wird mit dem Bash-Befehl declare mit der Option -x für Export und -A für assoziative Liste erstellt. Hier verknüpfen wir Escape-Codes für die Farbausgabe im Terminal mit den farbigen Namespace-Namen. Diese Definition legen wir in der Datei files/ovs_setup.sh ab.

Umgebungsvariablen und Farbliste definieren

Der folgende Bash-Skript definiert Umgebungsvariablen und eine Farbliste, die für das Einrichten des virtuellen Netzwerks erforderlich sind.

export namespaces=(red green blue)
declare -xA colorlist=(red '\e[31m'
                      green '\e[32m'
                      orange '\e[33m'
                      blue '\e[34m'
                      magenta '\e[35m'
                      cyan '\e[36m')

Wir verwenden die source-Funktion, um diese Datei im zweiten Skript files/ovs.sh einzubinden, zu dem wir all den folgenden Code hinzufügen. So können wir dieses Setup leicht reproduzieren und sogar in Zukunft anpassen. Innerhalb dieses Skripts erstellen wir zunächst eine Funktion namens netns(). Sie nimmt einen Namespace-Namen und die auszuführenden Befehle im Namespace als Eingabe entgegen und führt den Befehl im zugewiesenen Namespace aus, wobei die Ausgabe in der Farbe des Namespaces kodiert wird. Auf diese Weise können wir später leicht erkennen, in welchem Namespace ein Befehl ausgeführt wurde.

script_dir="$(dirname ${BASH_SOURCE[0]})"
. ${script_dir}/ovs_setup.sh
function netns () {
    color=$1
    shift
    args=$@
    if [[ ${args[0]} = "bash" ]]
    then
        echo "Zu riskant für meinen Geschmack"
    else
        if [[ $(ip netns list | grep -o ${color}) == ${color} ]]
        then
            echo -ne ${colorlist[$color]}
            sudo ip netns exec ${color} ${args[@]}
            echo -ne '\e[0m'
        else
            echo "Namespace ${color} existiert nicht"
        fi
    fi
    }

Für noch mehr Bequemlichkeit erstellen wir Aliasse für jeden Namespace, sodass ein einfaches red, green oder blue gefolgt von dem Befehl die netns-Funktion für jeden dieser Namespaces aufruft.

for ns in ${namespaces[@]}
do
    alias ${ns}="netns ${ns}" && alias ${ns} && export ${ns} 
done

Namespaces erstellen

Der nächste Schritt besteht darin, die drei Netzwerk-Namespaces mit den Namen red, green und blue mithilfe des Befehls ip netns add zu initialisieren. Ich verwende eine for-Schleife, um über die Namespace-Namen zu iterieren. So ist es einfacher, später falls erforderlich neue Namespaces hinzuzufügen. Wo nötig, verweise ich mit der Variablen ${ns} darauf.

for ns in ${namespaces[@]}
do
    if [[ ! $(ip netns list | grep -o ${ns}) == ${ns} ]]
    then
        sudo ip netns add ${ns}
        echo "${ns} Namespace hinzugefügt."     
    fi
done
ip netns list
sleep 1

Anschließend wird das Loopback-Gerät in jedem Namespace mithilfe des Befehls ip l dev lo up aktiviert, um eine lokale Kommunikation innerhalb des Namespaces zu ermöglichen.

for ns in ${namespaces[@]}
do
  netns ${ns} ip link set lo up
    echo "Loopback in ${ns} is up."
done

Schließlich können wir den aktuellen Zustand der Netzwerkschnittstellen in jedem Namespace mithilfe des Befehls ip l überprüfen.

for ns in ${namespaces[@]}
do
  netns ${ns} ip link
done

Tunnel erstellen

Jetzt erstellen wir virtuelle Ethernet-Tunnel (veth) mit den Namen veth-r, veth-g und veth-b, die jeweils mit einer Schnittstelle in ihrem entsprechenden Netzwerk-Namespace (eth0-r, eth0-g, eth0-b) für die Kommunikation verbunden sind. Diese Tunnel erleichtern die Kommunikation zwischen dem vSwitch und den Netzwerk-Namespaces. Beachte, dass ich in den Gerätenamen nur den ersten Buchstaben verwende, um die Namespaces zu unterscheiden. Hierfür verwende ich den Mechanismus der Variablenersetzung von Bash mit ${ns::1}, um nur den ersten Buchstaben jeder Zeichenkette zu erhalten. Beachte, dass dies eine Bash-Funktion ist, wenn Du eine andere Shell verwendest.

for ns in ${namespaces[@]}
do
    sudo ip link add veth-${ns::1} type veth peer eth0-${ns::1}
    echo "Verknüpfe veth-${ns} mit eth0-${ns}."
done

Link in Namespaces platzieren

Nachdem die Tunnel erstellt wurden, ist der nächste Schritt, die Schnittstellen ihren jeweiligen Namespaces zuzuweisen. Dies wird erreicht, indem jedem Endpunkt eines Tunnels (eth0-r, eth0-g, eth0-b) sein entsprechender Namespace (rot, grün, blau) zugeordnet wird.

for ns in ${namespaces[@]}
do
    sudo ip link set eth0-${ns::1} netns ${ns}
done

IPv4-Adressen hinzufügen

Schließlich werden IPv4-Adressen den Schnittstellen in jedem Namespace zugewiesen, um die Netzwerkkommunikation zu ermöglichen. Die IP-Adressen 10.0.0.2, 10.0.0.3 und 10.0.0.4 mit einer Subnetzmaske /24 werden jeweils den Schnittstellen eth0-r, eth0-g und eth0-b zugewiesen. Zusätzlich werden die Schnittstellen aktiviert, um die Netzwerkkonfiguration zu aktivieren.

ip=1
for ns in ${namespaces[@]}
do
    ip=$((ip+1))
  netns ${ns} ip address add 10.0.0.${ip}/24 dev eth0-${ns::1}
  netns ${ns} ip link set dev eth0-${ns:0:1} up
    echo "Add IP 10.0.0.${ip} to eth0-${ns::1}."
done

Virtuellen Switch öffnen

Um den virtuellen Switch einzurichten, wird zunächst das Openvswitch-Paket mithilfe des entsprechenden Paketmanagers installiert. Anschließend wird der Dienst ovs-vswitchd gestartet, um den Switch zu verwalten.

Installieren

if [ -f /etc/os-release ]; then
    . /etc/os-release
else
    echo "Die Linux-Distribution kann nicht bestimmt werden."
    exit 1
fi
case $ID_LIKE in
    debian|ubuntu)
        sudo apt install -y openvswitch-switch
        ;;
    fedora|rhel|centos)
        sudo yum install -y openvswitch
        ;;
    suse)
        sudo zypper install -y openvswitch
        ;;
    arch)
        sudo pacman -Syu openvswitch
        ;;
    *)
        echo "Nicht unterstützte Distribution."
        exit 1
        ;;
esac
if  [[ $ID == arch ]]; then
    sudo pacman -Syu openvswitch
else
    echo "Nicht unterstützte Distribution."
    exit 1
fi
sudo systemctl start ovs-vswitchd.service
echo "ovs-vswitchd gestartet"

Switch hinzufügen

Nach der Installation erstelle ich einen virtuellen Switch mit dem Namen SW1 mithilfe des Befehls ovs-vsctl add-br.

sudo ovs-vsctl add-br SW1

Die Switch-Konfiguration wird mithilfe von ovs-vsctl show angezeigt.

sudo ovs-vsctl show

Ports hinzufügen

Als Nächstes fügen wir Ports zum virtuellen Switch hinzu, um ihn mit den Netzwerk-Namespaces zu verbinden. Jeder Port ist mit einer entsprechenden Tunnel-Schnittstelle (veth-r, veth-g, veth-b) verbunden.

for ns in {r,g,b}
do
    sudo ovs-vsctl add-port SW1 veth-${ns}
    echo "veth-${ns} zu SW1 hinzugefügt."
done

Erneut können wir bestätigen, dass alles wie beabsichtigt funktioniert hat:

sudo ovs-vsctl show

Diese Ports müssen nun aktiviert werden.

for ns in {r,g,b}
do
    sudo ip link set veth-${ns} up
    echo "Link veth-{ns} ist aktiviert."
done

Ich überprüfe immer gerne noch einmal. Daher 'grepe' ich nach der veth-Schnittstelle und verwende die -A-Option, um immer die ersten 3 Zeilen jeder Schnittstellenkonfiguration anzuzeigen.

sudo ip a | grep veth -A3

Aufräumen

Ein Neustart wird in der Regel den Großteil des Chaos hinter uns aufräumen, aber ich möchte ein gutes Kind sein und selbst aufräumen. Daher habe ich ein weiteres Skript erstellt, das genau das tut.

script_dir="$(dirname ${BASH_SOURCE[0]})"
#Source Setup
. ${script_dir}/ovs_setup.sh

# Create a list of target interfaces
for ns in ${namespaces[@]}
do
    target_interfaces=(${target_interfaces[@]} veth-${ns::1})
done

# Überprüfen Sie auf vorhandene Namespaces
if [[ -n $(ip netns list) ]]; then
  echo "Netzwerk-Namespaces werden entfernt..."

  # Durchlaufen Sie jeden Namespace und entfernen Sie nur passende
  for ns in $(ip netns list | awk '{print $1}'); do
    if [[ "${namespaces[@]}" =~ "$ns" ]]; then
      sudo ip netns del $ns
      echo "Namespace entfernt: $ns"
    fi
  done
else
  echo "Keine Netzwerk-Namespaces gefunden."
fi

# Überprüfen Sie auf vorhandene veth-Schnittstellen
if [[ $(ip link show | grep veth -c) -gt 0 ]]; then
  echo "Veth-Schnittstellen werden entfernt..."

  # Durchlaufen Sie jede veth-Schnittstelle und entfernen Sie nur passende
  for veth in $(ip link show | grep veth | awk '{print $2}' |sed 's/@.*$//g'); do
    if [[ "${target_interfaces[@]}" =~ "$veth" ]]; then
      sudo ip link del $veth
      echo "Schnittstelle entfernt: $veth"
    fi
  done
else
  echo "Keine veth-Schnittstellen gefunden."
fi

echo "Open vSwitch wird gestoppt und deaktiviert..."
sudo systemctl stop ovs-vswitchd.service

# Überprüfen, ob das benannte Ausführungsverzeichnis für den blauen Namespace vorhanden ist
if [[ -d $(ls -d blue_named_run_?????) ]]; then 
    rm -rf blue_named_run_?????
    echo "Ausführungsverzeichnis für den benannten Dienst im blauen Namespace entfernt".
fi

for ns in ${namespaces[@]}
do
    if [[ $(alias ${ns}) ]] ; then
        unalias ${ns}
        "${ns} entaliasiert."
    fi
done

echo "Unseting netns function and variables"
unset -f netns
unset namespaces
unset colorlist

echo "Aufräumen abgeschlossen!"

Fazit

Es ist überraschend einfach, dieses grundlegende Setup zu erstellen.

Wir können jetzt die verschiedenen Namespaces erkunden, indem wir netzwerkbezogene Befehle mit den red(), blue() und green() Funktionen aufrufen, z. B.:

sudo blue ip route
sudo red ping 10.0.0.3 -c 5
sudo green nstat -s | grep -i icmp

Dies ist ein großartiger Spielplatz, um grundlegende Netzwerkfunktionalitäten zu erkunden.

Anerkennung

Date: 2024-02-24 Sa 00:00

Author: Sebastian Meisel

Created: 2025-06-06 Fr 20:05

Validate