bgpwtf/machines: init edge01.waw

This configures our WAW edge router using NixOS. This replaces our
previous Ubuntu installation.

Change-Id: Ibd72bde66ec413164401da407c5b268ad83fd3af
diff --git a/bgpwtf/machines/tests/edge01-waw.nix b/bgpwtf/machines/tests/edge01-waw.nix
new file mode 100644
index 0000000..e0298d2
--- /dev/null
+++ b/bgpwtf/machines/tests/edge01-waw.nix
@@ -0,0 +1,288 @@
+# Smoke test edge01.waw in a multi-VM NixOS test.
+#
+# This brings up three VMs:
+# - dut/edge01
+# - bgpspeaker, which simulates bgp upstreams
+# - customs, which simulates customs.hackerspace.pl.
+#
+# We use EoIP to build up virtual ethernet links between the machines, and
+# to run VLANs on that. We don't just use plain 'vlans' from NixOS tests as
+# we actually want to run 802.1q ourselves from the edge01 config.
+#
+# Everything else is pretty much straightforward. Bring up everything, ping
+# stuff. We don't really test much else than internet routing.
+#
+# To run this:
+#  nix-build -A bgpwtf.machines.tests.edge01-waw
+#
+# To debug this:
+#  nix-build -A bgpwtf.machines.tests.edge01-waw.driver && result/bin/nixos-test-driver
+#  >>> start_all()
+
+{ hscloud, pkgsSrc, pkgs, lib, ... }:
+
+with lib;
+
+let
+
+mkBGPSpeaker = let
+in { config, pkgs, ... }: {
+  networking.hostName = "bgpspeaker";
+  virtualisation.memorySize = 1024;
+  virtualisation.vlans = [ 1 ];
+  imports = [
+    ../modules/eoip.nix
+  ];
+
+  hscloud.eoip.interfaces."nnet" = {
+    parent = "eth1";
+    localV4 = "192.168.1.3";
+    remoteV4 = "192.168.1.2";
+    id = 100;
+  };
+  networking.interfaces."nnet" = {
+    virtual = true;
+    virtualType = "tap";
+  };
+  networking.vlans = {
+    "vl-globalmix" = { interface = "nnet"; id = 466; };
+  };
+  networking.interfaces."vl-globalmix" = {
+    ipv4.addresses = [{ address = "185.235.70.44"; prefixLength = 31; }];
+    ipv6.addresses = [{ address = "2001:67c:778:fd40::b9eb:462c"; prefixLength = 127; }];
+  };
+
+  services.bird2 = {
+    enable = true;
+    config = ''
+      log syslog all;
+      debug protocols { states, interfaces, events }
+      router id 185.235.70.44;
+
+      protocol device {
+        scan time 10;
+      };
+      protocol kernel kernel_v4 {
+        ipv4 {
+          import none;
+          export all;
+        };
+      }
+      protocol kernel kernel_v6 {
+        ipv6 {
+          import none;
+          export all;
+        };
+      }
+      ipv4 table globalmix4;
+      ipv6 table globalmix6;
+
+      protocol pipe pipe_globalmix4 {
+        table master4;
+        peer table globalmix4;
+        import all;
+        export none;
+      };
+      protocol pipe pipe_globalmix6 {
+        table master6;
+        peer table globalmix6;
+        import all;
+        export none;
+      };
+
+      protocol static static_globalmix_originate_v4 {
+        ipv4 {
+          table globalmix4;
+          import all;
+        };
+        route 8.8.8.0/24 blackhole;
+      }
+      protocol static static_globalmix_originate_v6 {
+        ipv6 {
+          table globalmix6;
+          import all;
+        };
+        route 2a00:1450:4016::/48 blackhole;
+      }
+      protocol bgp bgp_globalmix_v4 {
+        ipv4 {
+          table globalmix4;
+          export all;
+          import all;
+        };
+        local 185.235.70.44 as 62081;
+        neighbor 185.235.70.45 as 204880;
+      };
+      protocol bgp bgp_globalmix_v6 {
+        ipv6 {
+          table globalmix6;
+          export all;
+          import all;
+        };
+        local 2001:67c:778:fd40::b9eb:462c as 62081;
+        neighbor 2001:67c:778:fd40::b9eb:462d as 204880;
+      };
+    '';
+  };
+  networking.firewall.enable = false;
+  networking.useDHCP = false;
+  networking.interfaces.lo.ipv4.addresses = [ { address = "8.8.8.1"; prefixLength = 32; } ];
+  networking.interfaces.lo.ipv6.addresses = [ { address = "2a00:1450:4016:801::200e"; prefixLength = 128; } ];
+  environment.systemPackages = with pkgs; [
+    tcpdump htop dstat file
+  ];
+
+};
+
+
+test = import "${pkgsSrc}/nixos/tests/make-test-python.nix" ({ pkgs, libs, ... }: {
+  name = "test-edge01-waw-e2e";
+
+  nodes = {
+    dut = { config, pkgs, ... }: {
+      imports = [
+        ../edge01.waw.bgp.wtf.nix
+        ../modules/eoip.nix
+      ];
+      virtualisation.memorySize = 1024;
+      virtualisation.vlans = [
+        1 2
+      ];
+
+      hscloud.eoip.interfaces = {
+        "e1-nnet" = { parent = "eth1"; localV4 = "192.168.1.2"; remoteV4 = "192.168.1.3"; id = 100; };
+        "e2-customs" = { parent = "eth2"; localV4 = "192.168.2.2"; remoteV4 = "192.168.2.1"; id = 200; };
+        "e3-mgmt" = { parent = "eth2"; localV4 = "192.168.2.2"; remoteV4 = "192.168.2.111"; id = 300; }; # not connected
+        "e4-oob" = { parent = "eth2"; localV4 = "192.168.2.2"; remoteV4 = "192.168.2.112"; id = 400; }; # not connected
+        "e7-dcsw" = { parent = "eth2"; localV4 = "192.168.2.2"; remoteV4 = "192.168.2.113"; id = 500; }; # not connected
+      };
+      networking.interfaces = {
+        "e1-nnet" = { virtual = true; virtualType = "tap"; };
+        "e2-customs" = { virtual = true; virtualType = "tap"; };
+        "e3-mgmt" = { virtual = true; virtualType = "tap"; };
+        "e4-oob" = { virtual = true; virtualType = "tap"; };
+        "e7-dcsw" = { virtual = true; virtualType = "tap"; };
+      };
+    };
+
+    speaker = mkBGPSpeaker;
+
+    customs = { config, pkgs, ... }: {
+      imports = [
+        ../modules/eoip.nix
+      ];
+      environment.systemPackages = with pkgs; [
+        tcpdump htop dstat file dhcpcd
+      ];
+      virtualisation.memorySize = 1024;
+      virtualisation.vlans = [
+        2
+      ];
+      networking.firewall.enable = false;
+      networking.useDHCP = false;
+      networking.defaultGateway = "185.236.240.4";
+      networking.defaultGateway6 = "2a0d:eb00:2137:1::2";
+      networking.interfaces."edge" = {
+        virtual = true;
+        virtualType = "tap";
+        ipv4.addresses = [{ address = "185.236.240.5"; prefixLength = 31; }];
+        ipv6.addresses = [{ address = "2a0d:eb00:2137:1::3"; prefixLength = 127; }];
+      };
+      hscloud.eoip.interfaces."edge" = {
+        parent = "eth2";
+        localV4 = "192.168.2.1";
+        remoteV4 = "192.168.2.2";
+        id = 200;
+      };
+      networking.bridges."lan".interfaces = [];
+      networking.interfaces."lan" = {
+        ipv4.addresses = [{ address = "10.8.1.2"; prefixLength = 23; }];
+        ipv6.addresses = [{ address = "2a0d:eb00:4242::1"; prefixLength = 64; }];
+      };
+      services.bird2 = {
+        enable = true;
+        config = ''
+          log syslog all;
+          debug protocols { states, interfaces, events }
+          router id 185.236.240.5;
+
+          protocol device {
+            scan time 10;
+          };
+          protocol kernel kernel_v4 {
+            ipv4 {
+              import none;
+              export all;
+            };
+          }
+          protocol kernel kernel_v6 {
+            ipv6 {
+              import none;
+              export all;
+            };
+          }
+
+          protocol ospf v3 ospf_hswaw {
+            ipv6 {
+              import all;
+              export all;
+            };
+            area 0.0.0.0 {
+               interface "edge" {
+                 cost 10;
+                 type bcast;
+               };
+               interface "lan" {
+                 cost 10;
+                 stub yes;
+                 type bcast;
+                 check link no;
+               };
+            };
+          }
+        '';
+      };
+    };
+  };
+
+  testScript = ''
+    start_all()
+
+    edge01.wait_for_unit("bird2.service")
+    # Wait for BGP to settle.
+    edge01.wait_until_succeeds("ping 185.235.70.44 -c 1 -w 2")
+    edge01.wait_until_succeeds("birdc show route for 8.8.8.1 table all | grep via")
+    edge01.wait_until_succeeds(
+        "birdc show route for 2a00:1450:4016:801::200e table all | grep via"
+    )
+    edge01.succeed("ping 8.8.8.1 -c 1 -w 2")
+
+    # ping from customs to globalmix must succeed.
+    customs.succeed("ping 8.8.8.1 -c 1 -w 2")
+    customs.succeed("ping 2a00:1450:4016:801::200e -c 1 -w 2")
+
+    # edge01 must announce exactly one v4 prefix.
+    bgpspeaker.succeed("birdc show route protocol bgp_globalmix_v4 | grep unicast")
+    bgpspeaker.fail(
+        "birdc show route protocol bgp_globalmix_v4 | grep unicast | grep -v 185.236.240.0/23"
+    )
+
+    # edge01 must announce exactly one v6 prefix.
+    bgpspeaker.succeed("birdc show route protocol bgp_globalmix_v6 | grep unicast")
+    bgpspeaker.fail(
+        "birdc show route protocol bgp_globalmix_v6 | grep unicast | grep -v 2a0d:eb00::/32"
+    )
+
+    # customer networks must be reachable from globalmix
+    bgpspeaker.succeed("ping 185.236.240.10 -c 1 -w 2")
+    bgpspeaker.succeed("ping 2a0d:eb00:8000::1 -c 1 -w 2")
+    bgpspeaker.succeed("ping 185.236.240.12 -c 1 -w 2")
+    bgpspeaker.succeed("ping 185.236.240.105 -c 1 -w 2")
+    bgpspeaker.succeed("ping 2a0d:eb00:8003::1 -c 1 -w 2")
+
+    # dhcp agent must be reachable
+    customs.succeed("ping 185.236.240.18 -c 1 -w 2")
+  '';
+});
+
+in test { inherit pkgs; inherit (pkgs) libs; }