diff --git a/lib/strings.nix b/lib/strings.nix
index aca9ef45e61..9cbd1494a2b 100644
--- a/lib/strings.nix
+++ b/lib/strings.nix
@@ -219,6 +219,14 @@ rec {
*/
escapeShellArgs = concatMapStringsSep " " escapeShellArg;
+ /* Turn a string into a Nix expression representing that string
+
+ Example:
+ escapeNixString "hello\${}\n"
+ => "\"hello\\\${}\\n\""
+ */
+ escapeNixString = s: escape ["$"] (builtins.toJSON s);
+
/* Obsolete - use replaceStrings instead. */
replaceChars = builtins.replaceStrings or (
del: new: s:
diff --git a/lib/types.nix b/lib/types.nix
index 46ed05d288f..88fc90d0597 100644
--- a/lib/types.nix
+++ b/lib/types.nix
@@ -174,6 +174,13 @@ rec {
merge = mergeOneOption;
};
+ strMatching = pattern: mkOptionType {
+ name = "strMatching ${escapeNixString pattern}";
+ description = "string matching the pattern ${pattern}";
+ check = x: str.check x && builtins.match pattern x != null;
+ inherit (str) merge;
+ };
+
# Merge multiple definitions by concatenating them (with the given
# separator between the values).
separatedString = sep: mkOptionType rec {
diff --git a/nixos/doc/manual/development/option-types.xml b/nixos/doc/manual/development/option-types.xml
index 83dcf0232d9..ec940d5d2b8 100644
--- a/nixos/doc/manual/development/option-types.xml
+++ b/nixos/doc/manual/development/option-types.xml
@@ -110,6 +110,12 @@
A string. Multiple definitions are concatenated with a
collon ":".
+
+ types.strMatching
+ A string matching a specific regular expression. Multiple
+ definitions cannot be merged. The regular expression is processed using
+ builtins.match.
+
diff --git a/nixos/modules/services/networking/nat.nix b/nixos/modules/services/networking/nat.nix
index 9b04cf557fa..bfaf30c1178 100644
--- a/nixos/modules/services/networking/nat.nix
+++ b/nixos/modules/services/networking/nat.nix
@@ -131,15 +131,15 @@ in
type = with types; listOf (submodule {
options = {
sourcePort = mkOption {
- type = types.int;
+ type = types.either types.int (types.strMatching "[[:digit:]]+:[[:digit:]]+");
example = 8080;
- description = "Source port of the external interface";
+ description = "Source port of the external interface; to specify a port range, use a string with a colon (e.g. \"60000:61000\")";
};
destination = mkOption {
type = types.str;
example = "10.0.0.1:80";
- description = "Forward connection to destination ip:port";
+ description = "Forward connection to destination ip:port; to specify a port range, use ip:start-end";
};
proto = mkOption {