ud3tn/nix/module.nix
Felix Walter 87be9749af Re-integrate v0.13.0 routing code as "integrated default BDM"
This makes µD3TN behave the same as v0.13.0 when executed without an
additional commandline argument. A new commandline argument `-d` /
`--external-dispatch` is added, which enables the use of external BDMs.
The default forwarding implementation is now again provided using the
v0.13 code, extracted from e1621765a4 and
adapted to the new agent-based forwarding implementation.

Central changes to the old code include:

- A new "Routing Agent" that handles incoming configuration commands,
  FIB updates, and BDM dispatch requests.
- The use of the BDM authorization flag to authorize contact
  configuration commands.
- The Contact Manager now only triggers the creation and removal of
  links / FIB entries; bundle dispatch is triggered through the FIB and
  BDM callback functions of the Routing Agent.
- The fragmentation logic is adapted to store the original bundle along
  with an offset and length value, instead of pre-creating and storing
  the fragments.
- The bundle re-scheduling logic integrated into the Routing Agent is
  simplified and does not support changing the fragmentation parameters.
  A new function is added to the Router that searches for a new route
  for such fragments that were already scheduled at some point,
  considering them as un-fragmentable bundle with overridden fragment
  offset and length.

Signed-off-by: Felix Walter <felix.walter@d3tn.com>
2024-07-15 00:46:14 +02:00

271 lines
8.4 KiB
Nix

# SPDX-License-Identifier: BSD-3-Clause OR Apache-2.0
{ config, lib, pkgs, ... }:
let
cfg = config.services.ud3tn;
mkClaOptions = { defaultPort, enable ? true, addActiveOption ? false }: {
enable = lib.mkOption {
description = "Whether to enable the CLA.";
default = enable;
type = lib.types.bool;
};
host = lib.mkOption {
description = "IP address / host name that the CLA should listen on.";
default = "*";
type = lib.types.str;
};
port = lib.mkOption {
description = "Port number that the CLA should listen on.";
default = defaultPort;
type = lib.types.port;
};
} // lib.optionalAttrs addActiveOption {
active = lib.mkOption {
description = ''
If true the provided host name or IP address and port number are used for initiating a TCP
connection.
'';
default = false;
type = lib.types.bool;
};
};
ud3tnOptions = {
options = {
enable = lib.mkEnableOption "μD3TN";
aap = {
bpVersion = lib.mkOption {
description = "Bundle protocol version of bundles created via AAP.";
default = 7;
type = lib.types.enum [ 6 7 ];
};
host = lib.mkOption {
description = "IP / hostname of the application agent service.";
default = null;
example = [ "127.0.0.1" "localhost" "::" "::1" "dtn.example" ];
type = with lib.types; nullOr str;
};
port = lib.mkOption {
description = "Port number of the application agent service.";
default = null;
type = with lib.types; nullOr port;
};
};
allowRemoteConfig = lib.mkOption {
description = ''
Allow configuration via bundles received from CLAs or from unauthorized AAP clients.
'';
default = false;
type = lib.types.bool;
};
bdmSecretFile = lib.mkOption {
description = ''
Restrict AAP 2.0 BDM functions to clients providing the secret in the give file."
'';
type = lib.types.path;
};
cla = {
mtcp = mkClaOptions { defaultPort = 4224; };
smtcp = mkClaOptions { defaultPort = 4222; addActiveOption = true; };
sqlite = {
enable = lib.mkOption {
description = "Whether to enable the SQLiteCLA.";
default = true;
type = lib.types.bool;
};
};
tcpclv3 = mkClaOptions { defaultPort = 4556; };
tcpspp = mkClaOptions { defaultPort = 4223; enable = false; addActiveOption = true; };
};
eid = lib.mkOption {
description = "Local endpoint identifier.";
default = "dtn://ud3tn.dtn/";
example = [ "dtn://ud3tn.dtn/" "ipn:1.0" ];
type = lib.types.str;
};
externalDispatch = lib.mkOption {
description = "Do not load the internal minimal router, allow for using an AAP 2.0 BDM.";
default = false;
type = lib.types.bool;
};
lifetime = lib.mkOption {
description = "Lifetime in seconds of bundles created via AAP.";
default = 86400;
type = lib.types.ints.positive;
};
logLevel = lib.mkOption {
description = "Higher or lower log level 1|2|3 specifies more or less detailed output.";
default = 1;
type = lib.types.enum [ 1 2 3 ];
};
maxBundleSize = lib.mkOption {
description = ''
Threshold used to determine if a bundle has to be fragmented.
The effective threshold will be determined as the minimum of this argument and all
maximum bundle sizes reported by the activated CLAs. A value of zero means "unlimited"
(2^64 bytes).
'';
default = 0;
type = lib.types.ints.unsigned;
};
openFirewall = lib.mkOption {
description = "Open firewall ports.";
default = false;
type = lib.types.bool;
};
statusReports = lib.mkOption {
description = "Enable status reporting.";
default = false;
type = lib.types.bool;
};
};
};
mkPortList = _: cfg: (
(lib.attrsets.mapAttrsToList (_: cla: (lib.mkIf cfg.enable cla.port)) (lib.attrsets.filterAttrs (_: v: v ? "port") cfg.cla))
++ lib.optional (cfg.aap.port != null) cfg.aap.port
);
mkNetworkingConfig = _: cfg: {
firewall.allowedTCPPorts = lib.mkIf cfg.openFirewall (mkPortList _ cfg);
};
mkServiceConfig = name: cfg:
let
mkClaStr = clas: (lib.concatStrings (
lib.attrsets.mapAttrsToList
(
name: cla@{ enable, host, port, ... }: (
lib.optionalString enable (
"${name}:${host},${toString port}"
+ lib.optionalString (cla ? "active") ",${lib.boolToString cla.active}"
+ ";"
)
)
)
(lib.attrsets.filterAttrs (_: v: v ? "host" && v ? "port") clas)
++ lib.optional (clas.sqlite.enable) "sqlite:/var/lib/ud3tn@${name}/u3dtn.sqlite;"
));
in
{
"ud3tn@${name}" = lib.mkIf cfg.enable {
wantedBy = [ "multi-user.target" ];
environment."UD3TN_BDM_SECRET" = (builtins.readFile cfg.bdmSecretFile);
serviceConfig = {
RuntimeDirectory = "ud3tn@${name}"; # created in /run/
StateDirectory = "ud3tn@${name}"; # created in /var/lib/
ExecStart = lib.concatStringsSep " " (
[
"${pkgs.ud3tn}/bin/ud3tn"
"--aap2-socket /run/ud3tn@${name}/aap2.socket"
"--bdm-secret-var UD3TN_BDM_SECRET"
"--bp-version ${toString cfg.aap.bpVersion}"
"--cla ${mkClaStr cfg.cla}"
"--eid ${cfg.eid}"
"--lifetime ${toString cfg.lifetime}"
"--log-level ${toString cfg.logLevel}"
"--max-bundle-size ${toString cfg.maxBundleSize}"
]
# If a UNIX and a TCP socket parameter is provided, μD3TN favours the UNIX socket, so
# the --aap-socket parameter is only set if neither aap.host nor aap.port is defined
++ lib.optional (cfg.aap.host == null && cfg.aap.port == null)
"--aap-socket /run/ud3tn@${name}/aap.socket"
++ lib.optional (cfg.aap.host != null) "--aap-host ${cfg.aap.host}"
++ lib.optional (cfg.aap.port != null) "--aap-port ${toString cfg.aap.port}"
++ lib.optional cfg.allowRemoteConfig "--allow-remote-config"
++ lib.optional cfg.externalDispatch "--external-dispatch"
++ lib.optional cfg.statusReports "--status-reports"
);
};
};
};
in
{
options.services.ud3tn = lib.mkOption {
type = lib.types.attrsOf (lib.types.submodule ud3tnOptions);
default = { };
description = "Attribute set to configure multiple μD3TN instances";
example = lib.literalExpression ''
{
services.ud3tn.a = {
enable = true;
bdmSecretFile = /var/lib/ud3tn@a/bdm-secret;
};
services.ud3tn.b = {
enable = true;
openFirewall = true;
aap = {
bpVersion = 6;
host = "0.0.0.0";
port = 2345;
};
allowRemoteConfig = true;
bdmSecretFile = /var/lib/ud3tn@b/bdm-secret;
cla = {
mtcp = {
host = "0.0.0.0";
port = 3456;
};
smtcp = {
host = "localhost";
port = 4567;
active = true;
};
tcpclv3 = {
host = "::";
port = 5678;
};
tcpspp = {
host = "::1";
port = 6789;
active = true;
};
};
eid = "dtn://ud3tn-b.dtn/";
externalDispatch = true;
lifetime = 43200;
logLevel = 3;
maxBundleSize = 4294967295; # u32_max
statusReports = true;
};
}
'';
};
config = {
assertions = [{
assertion = (lib.lists.allUnique (lib.lists.flatten (lib.attrsets.mapAttrsToList mkPortList cfg)));
message = ''
Colliding port definitions.
Make sure that you configure different ports when operating multiple μD3TN instances.
'';
}];
networking = lib.mkMerge (
lib.attrsets.mapAttrsToList mkNetworkingConfig cfg
);
systemd.services = lib.mkMerge (
lib.attrsets.mapAttrsToList mkServiceConfig cfg
);
};
}