test(vm/containers): build network topology with containers

get rid of mininet
This commit is contained in:
catvayor 2025-01-30 23:18:54 +01:00
parent f49c90308f
commit 7fa5d010ba
Signed by: lbailly
GPG key ID: CE3E645251AC63F3
4 changed files with 291 additions and 80 deletions

1
.gitignore vendored
View file

@ -1,4 +1,3 @@
/target
.direnv
.envrc
nixos.qcow2

66
mn.py
View file

@ -1,66 +0,0 @@
#!/usr/bin/env python
from mininet.topo import Topo
from mininet.net import Mininet
from mininet.util import dumpNodeConnections
from mininet.log import setLogLevel
class CustomTopo(Topo):
"Single switch connected to n hosts."
def build(self):
client_vnis = list(range(1000, 1004))
vtep_count = 2
switch = self.addSwitch('s0')
vault01 = self.addHost('a1')
self.addLink(vault01, switch)
vteps = []
for i in range(vtep_count):
vteps.append(self.addHost('s%s' % (i+1)))
self.addLink(node1=vteps[i], node2=switch, port1=0)
vtepCmds = []
for vni in range(1000, 1004):
for i in range(vtep_count):
vtep_name = 's%s' % (i+1)
vtepCmds.append((vtep_name, 'ip link add name s%s-vtep%s type vxlan dev s%s-eth0 id %s dstport 4789 srcport 4789 4789 remote 10.0.0.1' % (i+1, vni, i+1, vni)))
vtepCmds.append((vtep_name, 'ip link set s%s-vtep%s up' % (i+1, vni)))
vtepCmds.append((vtep_name, 'ip link add name s%s-br%s type bridge' % (i+1, vni)))
vtepCmds.append((vtep_name, 'ip link set s%s-br%s up' % (i+1, vni)))
vtepCmds.append((vtep_name, 'ip link set dev s%s-vtep%s master s%s-br%s'% (i+1, vni, i+1, vni)))
vtepCmds.append((vtep_name, 'ip link set dev s%s-eth%s master s%s-br%s' % (i+1, vni, i+1, vni)))
host = self.addHost('vni%ss%s' % (vni, i + 1))
self.addLink(node1=host, node2=vteps[i], port2=vni)
self.vtepCmds = vtepCmds
def simpleTest():
"Create and test a simple network"
topo = CustomTopo()
net = Mininet(topo)
net.start()
print( "Dumping host connections" )
dumpNodeConnections(net.hosts)
for e in topo.vtepCmds:
print(net.get(e[0]).cmd(e[1]))
print(">>>")
print(net.get("s1").cmd("ip a"))
print(">>>")
print(net.get("s2").cmd("ip a"))
print(">>>")
print(net.get("a1").cmd("ip a"))
print(net.get("a1").cmd("tcpdump > /tmp/tcp &"))
print(net.get("vni1000s1").cmd("ping -c 1 10.0.0.5"))
print(net.get("a1").cmd("kill $!"))
print(net.get("a1").cmd("cat /tmp/tcp"))
net.stop()
if __name__ == '__main__':
# Tell mininet to print useful information
setLogLevel('info')
simpleTest()

31
vm/common.nix Normal file
View file

@ -0,0 +1,31 @@
{ pkgs, lib, config, ... }: {
environment.defaultPackages = [
pkgs.inetutils
pkgs.tcpdump
];
networking.useNetworkd = true;
systemd.network.enable = true;
nix = {
nixPath = [
"nixpkgs=${toString <nixpkgs>}"
];
channel.enable = false;
settings = {
nix-path = config.nix.nixPath;
experimental-features = [
"pipe-operator"
"nix-command"
];
};
package = pkgs.lix;
};
users.users.root = {
password = "vxlan";
initialHashedPassword = lib.mkForce null;
};
system.stateVersion = "25.05";
}

273
vm/vm.nix
View file

@ -1,27 +1,274 @@
{ pkgs, ... }:
{
lib,
...
}:
let
inherit (lib)
imap
flatten
listToAttrs
;
access-topology = [
[
1000
1001
1002
1003
]
[
1000
1001
1002
1003
]
];
br_name = sw: vni: "sw${toString sw}-vni${toString vni}";
client_name = sw: vni: "h-${br_name sw vni}";
vtep_name = sw: vni: "v-${toString sw}-${toString vni}";
sw_name = sw: "sw${toString sw}";
vtep_br_name = sw: vni: "br${vtep_name sw vni}";
vtep_vxlan_name = sw: vni: "x${vtep_name sw vni}";
clients =
listToAttrs
<| flatten
<| imap (
sw:
map (vni: {
name = client_name sw vni;
value = {
privateNetwork = true;
ephemeral = true;
hostBridge = br_name sw vni;
autoStart = true;
config = {
imports = [ ./common.nix ];
networking.hostName = client_name sw vni;
services.resolved.enable = false;
systemd.network.networks = {
"10-eth0" = {
name = "eth0";
address = [ "10.0.${toString sw}.${toString (vni - 999)}/16" ];
};
};
};
};
})
) access-topology;
switchs =
listToAttrs
<| imap (sw: vnis: {
name = sw_name sw;
value = {
privateNetwork = true;
ephemeral = true;
hostBridge = "br0";
autoStart = true;
extraVeths =
listToAttrs
<| map (vni: {
name = vtep_name sw vni;
value.hostBridge = br_name sw vni;
}) vnis;
config = {
imports = [ ./common.nix ];
networking.hostName = sw_name sw;
services.resolved.enable = false;
systemd.network = {
networks =
{
"10-eth0" = {
name = "eth0";
address = [ "10.0.0.${toString (sw + 1)}/24" ];
networkConfig.VXLAN = map (vtep_vxlan_name sw) vnis;
};
}
// listToAttrs (
flatten
<| map (vni: [
{
name = "10-${vtep_name sw vni}";
value = {
name = vtep_name sw vni;
networkConfig = {
Bridge = vtep_br_name sw vni;
LinkLocalAddressing = false;
LLDP = false;
EmitLLDP = false;
IPv6AcceptRA = false;
IPv6SendRA = false;
};
};
}
{
name = "10-${vtep_br_name sw vni}";
value = {
name = vtep_br_name sw vni;
networkConfig = {
LinkLocalAddressing = false;
LLDP = false;
EmitLLDP = false;
IPv6AcceptRA = false;
IPv6SendRA = false;
};
};
}
{
name = "10-${vtep_vxlan_name sw vni}";
value = {
name = vtep_vxlan_name sw vni;
networkConfig = {
Bridge = vtep_br_name sw vni;
LinkLocalAddressing = false;
LLDP = false;
EmitLLDP = false;
IPv6AcceptRA = false;
IPv6SendRA = false;
};
};
}
]) vnis
);
netdevs = listToAttrs (
flatten
<| map (vni: [
{
name = "10-${vtep_br_name sw vni}";
value = {
netdevConfig = {
Name = vtep_br_name sw vni;
Kind = "bridge";
};
bridgeConfig.STP = false;
};
}
{
name = "10-${vtep_vxlan_name sw vni}";
value = {
netdevConfig = {
Name = vtep_vxlan_name sw vni;
Kind = "vxlan";
};
vxlanConfig = {
VNI = vni;
Remote = "10.0.0.1";
Local = "10.0.0.${toString (sw + 1)}";
DestinationPort = 4789;
PortRange = 4789;
};
};
}
]) vnis
);
};
};
};
}) access-topology;
in
{
virtualisation = {
memorySize = 4 * 1024;
cores = 4;
diskImage = null;
forwardPorts = [
{
from = "host";
host.port = 2222;
guest.port = 22;
}
];
};
nixos-shell.mounts = {
mountHome = false;
extraMounts."/root/vxlan" = {
extraMounts."/vxlan" = {
target = ./..;
cache = "none";
};
};
programs.mininet.enable = true;
users.users.root.packages = [
pkgs.inetutils
pkgs.tcpdump
(pkgs.python3.withPackages (ps: with ps; [
mininet-python
distutils
packaging
]))
];
imports = [ ./common.nix ];
system.stateVersion = "25.05";
services.openssh = {
enable = true;
settings.PermitRootLogin = "yes";
};
containers =
{
"router" = {
privateNetwork = true;
ephemeral = true;
hostBridge = "br0";
bindMounts."/vxlan".hostPath = "/vxlan";
autoStart = true;
config = {
imports = [ ./common.nix ];
services.resolved.enable = false;
systemd.network.networks = {
"10-eth0" = {
name = "eth0";
address = [ "10.0.0.1/24" ];
};
};
};
};
}
// switchs
// clients;
systemd.network =
let
brs = [ "br0" ] ++ flatten (imap (sw: map (br_name sw)) access-topology);
in
{
networks =
listToAttrs
<|
map (name: {
name = "10-${name}";
value = {
inherit name;
networkConfig = {
LinkLocalAddressing = false;
LLDP = false;
EmitLLDP = false;
IPv6AcceptRA = false;
IPv6SendRA = false;
};
};
}) brs
++ flatten (
imap (
sw:
map (vni: {
name = "10-${vtep_name sw vni}";
value = {
name = vtep_name sw vni;
networkConfig = {
LinkLocalAddressing = false;
LLDP = false;
EmitLLDP = false;
IPv6AcceptRA = false;
IPv6SendRA = false;
};
};
})
) access-topology
);
netdevs =
listToAttrs
<| map (name: {
name = "10-${name}";
value = {
netdevConfig = {
Name = name;
Kind = "bridge";
};
bridgeConfig.STP = false;
};
}) brs;
};
}