Merge pull request #18207 from tavyc/quagga-module
quagga service: init
This commit is contained in:
commit
3db5311be9
|
@ -388,6 +388,7 @@
|
||||||
./services/networking/prayer.nix
|
./services/networking/prayer.nix
|
||||||
./services/networking/privoxy.nix
|
./services/networking/privoxy.nix
|
||||||
./services/networking/prosody.nix
|
./services/networking/prosody.nix
|
||||||
|
./services/networking/quagga.nix
|
||||||
./services/networking/quassel.nix
|
./services/networking/quassel.nix
|
||||||
./services/networking/racoon.nix
|
./services/networking/racoon.nix
|
||||||
./services/networking/radicale.nix
|
./services/networking/radicale.nix
|
||||||
|
|
|
@ -0,0 +1,187 @@
|
||||||
|
{ config, lib, pkgs, ... }:
|
||||||
|
|
||||||
|
with lib;
|
||||||
|
|
||||||
|
let
|
||||||
|
|
||||||
|
cfg = config.services.quagga;
|
||||||
|
|
||||||
|
services = [ "babel" "bgp" "isis" "ospf6" "ospf" "pim" "rip" "ripng" ];
|
||||||
|
allServices = services ++ [ "zebra" ];
|
||||||
|
|
||||||
|
isEnabled = service: cfg.${service}.enable;
|
||||||
|
|
||||||
|
daemonName = service: if service == "zebra" then service else "${service}d";
|
||||||
|
|
||||||
|
configFile = service:
|
||||||
|
let
|
||||||
|
scfg = cfg.${service};
|
||||||
|
in
|
||||||
|
if scfg.configFile != null then scfg.configFile
|
||||||
|
else pkgs.writeText "${daemonName service}.conf"
|
||||||
|
''
|
||||||
|
! Quagga ${daemonName service} configuration
|
||||||
|
!
|
||||||
|
hostname ${config.networking.hostName}
|
||||||
|
log syslog
|
||||||
|
service password-encryption
|
||||||
|
!
|
||||||
|
${scfg.config}
|
||||||
|
!
|
||||||
|
end
|
||||||
|
'';
|
||||||
|
|
||||||
|
serviceOptions = service:
|
||||||
|
{
|
||||||
|
enable = mkEnableOption "the Quagga ${toUpper service} routing protocol";
|
||||||
|
|
||||||
|
configFile = mkOption {
|
||||||
|
type = types.nullOr types.path;
|
||||||
|
default = null;
|
||||||
|
example = "/etc/quagga/${daemonName service}.conf";
|
||||||
|
description = ''
|
||||||
|
Configuration file to use for Quagga ${daemonName service}.
|
||||||
|
By default the NixOS generated files are used.
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
|
||||||
|
config = mkOption {
|
||||||
|
type = types.lines;
|
||||||
|
default = "";
|
||||||
|
example =
|
||||||
|
let
|
||||||
|
examples = {
|
||||||
|
rip = ''
|
||||||
|
router rip
|
||||||
|
network 10.0.0.0/8
|
||||||
|
'';
|
||||||
|
|
||||||
|
ospf = ''
|
||||||
|
router ospf
|
||||||
|
network 10.0.0.0/8 area 0
|
||||||
|
'';
|
||||||
|
|
||||||
|
bgp = ''
|
||||||
|
router bgp 65001
|
||||||
|
neighbor 10.0.0.1 remote-as 65001
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
in
|
||||||
|
examples.${service} or "";
|
||||||
|
description = ''
|
||||||
|
${daemonName service} configuration statements.
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
|
||||||
|
vtyListenAddress = mkOption {
|
||||||
|
type = types.str;
|
||||||
|
default = "127.0.0.1";
|
||||||
|
description = ''
|
||||||
|
Address to bind to for the VTY interface.
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
|
||||||
|
vtyListenPort = mkOption {
|
||||||
|
type = types.nullOr types.int;
|
||||||
|
default = null;
|
||||||
|
description = ''
|
||||||
|
TCP Port to bind to for the VTY interface.
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
in
|
||||||
|
|
||||||
|
{
|
||||||
|
|
||||||
|
###### interface
|
||||||
|
|
||||||
|
options.services.quagga =
|
||||||
|
{
|
||||||
|
|
||||||
|
zebra = (serviceOptions "zebra") // {
|
||||||
|
|
||||||
|
enable = mkOption {
|
||||||
|
type = types.bool;
|
||||||
|
default = any isEnabled services;
|
||||||
|
example = true;
|
||||||
|
description = ''
|
||||||
|
Whether to enable the Zebra routing manager.
|
||||||
|
|
||||||
|
The Zebra routing manager is automatically enabled
|
||||||
|
if any routing protocols are configured.
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
} // (genAttrs services serviceOptions);
|
||||||
|
|
||||||
|
###### implementation
|
||||||
|
|
||||||
|
config = mkIf (any isEnabled allServices) {
|
||||||
|
|
||||||
|
environment.systemPackages = [
|
||||||
|
pkgs.quagga # for the vtysh tool
|
||||||
|
];
|
||||||
|
|
||||||
|
users.users.quagga = {
|
||||||
|
description = "Quagga daemon user";
|
||||||
|
isSystemUser = true;
|
||||||
|
group = "quagga";
|
||||||
|
};
|
||||||
|
|
||||||
|
users.groups = {
|
||||||
|
quagga = {};
|
||||||
|
# Members of the quaggavty group can use vtysh to inspect the Quagga daemons
|
||||||
|
quaggavty = {};
|
||||||
|
};
|
||||||
|
|
||||||
|
systemd.services =
|
||||||
|
let
|
||||||
|
quaggaService = service:
|
||||||
|
let
|
||||||
|
scfg = cfg.${service};
|
||||||
|
daemon = daemonName service;
|
||||||
|
in
|
||||||
|
nameValuePair daemon ({
|
||||||
|
wantedBy = [ "multi-user.target" ];
|
||||||
|
restartTriggers = [ (configFile service) ];
|
||||||
|
|
||||||
|
serviceConfig = {
|
||||||
|
Type = "forking";
|
||||||
|
PIDFile = "/run/quagga/${daemon}.pid";
|
||||||
|
ExecStart = "@${pkgs.quagga}/libexec/quagga/${daemon} ${daemon} -d -f ${configFile service}"
|
||||||
|
+ optionalString (scfg.vtyListenAddress != "") " -A ${scfg.vtyListenAddress}"
|
||||||
|
+ optionalString (scfg.vtyListenPort != null) " -P ${toString scfg.vtyListenPort}";
|
||||||
|
ExecReload = "${pkgs.coreutils}/bin/kill -HUP $MAINPID";
|
||||||
|
Restart = "on-abort";
|
||||||
|
};
|
||||||
|
} // (
|
||||||
|
if service == "zebra" then
|
||||||
|
{
|
||||||
|
description = "Quagga Zebra routing manager";
|
||||||
|
unitConfig.Documentation = "man:zebra(8)";
|
||||||
|
after = [ "network.target" ];
|
||||||
|
preStart = ''
|
||||||
|
install -m 0755 -o quagga -g quagga -d /run/quagga
|
||||||
|
|
||||||
|
${pkgs.iproute}/bin/ip route flush proto zebra
|
||||||
|
'';
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
description = "Quagga ${toUpper service} routing daemon";
|
||||||
|
unitConfig.Documentation = "man:${daemon}(8) man:zebra(8)";
|
||||||
|
bindsTo = [ "zebra.service" ];
|
||||||
|
after = [ "network.target" "zebra.service" ];
|
||||||
|
}
|
||||||
|
));
|
||||||
|
in
|
||||||
|
listToAttrs (map quaggaService (filter isEnabled allServices));
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
meta.maintainers = with lib.maintainers; [ tavyc ];
|
||||||
|
|
||||||
|
}
|
|
@ -279,6 +279,7 @@ in rec {
|
||||||
tests.printing = callTest tests/printing.nix {};
|
tests.printing = callTest tests/printing.nix {};
|
||||||
tests.proxy = callTest tests/proxy.nix {};
|
tests.proxy = callTest tests/proxy.nix {};
|
||||||
tests.pumpio = callTest tests/pump.io.nix {};
|
tests.pumpio = callTest tests/pump.io.nix {};
|
||||||
|
tests.quagga = callTest tests/quagga.nix {};
|
||||||
tests.quake3 = callTest tests/quake3.nix {};
|
tests.quake3 = callTest tests/quake3.nix {};
|
||||||
tests.runInMachine = callTest tests/run-in-machine.nix {};
|
tests.runInMachine = callTest tests/run-in-machine.nix {};
|
||||||
tests.sddm = callTest tests/sddm.nix {};
|
tests.sddm = callTest tests/sddm.nix {};
|
||||||
|
|
|
@ -0,0 +1,97 @@
|
||||||
|
# This test runs Quagga and checks if OSPF routing works.
|
||||||
|
#
|
||||||
|
# Network topology:
|
||||||
|
# [ client ]--net1--[ router1 ]--net2--[ router2 ]--net3--[ server ]
|
||||||
|
#
|
||||||
|
# All interfaces are in OSPF Area 0.
|
||||||
|
|
||||||
|
import ./make-test.nix ({ pkgs, ... }:
|
||||||
|
let
|
||||||
|
|
||||||
|
ifAddr = node: iface: (pkgs.lib.head node.config.networking.interfaces.${iface}.ip4).address;
|
||||||
|
|
||||||
|
ospfConf = ''
|
||||||
|
interface eth2
|
||||||
|
ip ospf hello-interval 1
|
||||||
|
ip ospf dead-interval 5
|
||||||
|
!
|
||||||
|
router ospf
|
||||||
|
network 192.168.0.0/16 area 0
|
||||||
|
'';
|
||||||
|
|
||||||
|
in
|
||||||
|
{
|
||||||
|
name = "quagga";
|
||||||
|
|
||||||
|
meta = with pkgs.stdenv.lib.maintainers; {
|
||||||
|
maintainers = [ tavyc ];
|
||||||
|
};
|
||||||
|
|
||||||
|
nodes = {
|
||||||
|
|
||||||
|
client =
|
||||||
|
{ config, pkgs, nodes, ... }:
|
||||||
|
{
|
||||||
|
virtualisation.vlans = [ 1 ];
|
||||||
|
networking.defaultGateway = ifAddr nodes.router1 "eth1";
|
||||||
|
};
|
||||||
|
|
||||||
|
router1 =
|
||||||
|
{ config, pkgs, nodes, ... }:
|
||||||
|
{
|
||||||
|
virtualisation.vlans = [ 1 2 ];
|
||||||
|
boot.kernel.sysctl."net.ipv4.ip_forward" = "1";
|
||||||
|
networking.firewall.extraCommands = "iptables -A nixos-fw -i eth2 -p ospf -j ACCEPT";
|
||||||
|
services.quagga.ospf = {
|
||||||
|
enable = true;
|
||||||
|
config = ospfConf;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
router2 =
|
||||||
|
{ config, pkgs, nodes, ... }:
|
||||||
|
{
|
||||||
|
virtualisation.vlans = [ 3 2 ];
|
||||||
|
boot.kernel.sysctl."net.ipv4.ip_forward" = "1";
|
||||||
|
networking.firewall.extraCommands = "iptables -A nixos-fw -i eth2 -p ospf -j ACCEPT";
|
||||||
|
services.quagga.ospf = {
|
||||||
|
enable = true;
|
||||||
|
config = ospfConf;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
server =
|
||||||
|
{ config, pkgs, nodes, ... }:
|
||||||
|
{
|
||||||
|
virtualisation.vlans = [ 3 ];
|
||||||
|
networking.defaultGateway = ifAddr nodes.router2 "eth1";
|
||||||
|
networking.firewall.allowedTCPPorts = [ 80 ];
|
||||||
|
networking.firewall.allowPing = true;
|
||||||
|
services.httpd.enable = true;
|
||||||
|
services.httpd.adminAddr = "foo@example.com";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
testScript =
|
||||||
|
{ nodes, ... }:
|
||||||
|
''
|
||||||
|
startAll;
|
||||||
|
|
||||||
|
# Wait for the networking to start on all machines
|
||||||
|
$_->waitForUnit("network.target") foreach values %vms;
|
||||||
|
|
||||||
|
# Wait for OSPF to form adjacencies
|
||||||
|
for my $gw ($router1, $router2) {
|
||||||
|
$gw->waitForUnit("ospfd");
|
||||||
|
$gw->waitUntilSucceeds("vtysh -c 'show ip ospf neighbor' | grep Full");
|
||||||
|
$gw->waitUntilSucceeds("vtysh -c 'show ip route' | grep '^O>'");
|
||||||
|
}
|
||||||
|
|
||||||
|
# Test ICMP.
|
||||||
|
$client->succeed("ping -c 3 server >&2");
|
||||||
|
|
||||||
|
# Test whether HTTP works.
|
||||||
|
$server->waitForUnit("httpd");
|
||||||
|
$client->succeed("curl --fail http://server/ >&2");
|
||||||
|
'';
|
||||||
|
})
|
Loading…
Reference in New Issue