nixos/network-interfaces: improve IPv6 support (#417150)
This commit is contained in:
@@ -145,6 +145,15 @@
|
|||||||
|
|
||||||
- `services.monero` now includes the `environmentFile` option for adding secrets to the Monero daemon config.
|
- `services.monero` now includes the `environmentFile` option for adding secrets to the Monero daemon config.
|
||||||
|
|
||||||
|
- The new option [networking.ipips](#opt-networking.ipips) has been added to create IP within IP kind of tunnels (including 4in6, ip6ip6 and ipip).
|
||||||
|
With the existing [networking.sits](#opt-networking.sits) option (6in4), it is now possible to create all combinations of IPv4 and IPv6 encapsulation.
|
||||||
|
|
||||||
|
- It is now possible to configure the default source address using the new options [networking.defaultGateway.source](#opt-networking.defaultGateway.source),
|
||||||
|
[networking.defaultGateway6.source](#opt-networking.defaultGateway6.source).
|
||||||
|
|
||||||
|
- Potential race conditions in the network setup when using `networking.interfaces` have been fixed by disabling duplicate address detection (DAD)
|
||||||
|
for statically configured IPv6 addresses.
|
||||||
|
|
||||||
- `amdgpu` kernel driver overdrive mode can now be enabled by setting [hardware.amdgpu.overdrive.enable](#opt-hardware.amdgpu.overdrive.enable) and customized through [hardware.amdgpu.overdrive.ppfeaturemask](#opt-hardware.amdgpu.overdrive.ppfeaturemask).
|
- `amdgpu` kernel driver overdrive mode can now be enabled by setting [hardware.amdgpu.overdrive.enable](#opt-hardware.amdgpu.overdrive.enable) and customized through [hardware.amdgpu.overdrive.ppfeaturemask](#opt-hardware.amdgpu.overdrive.ppfeaturemask).
|
||||||
This allows for fine-grained control over the GPU's performance and maybe required by overclocking softwares like Corectrl and Lact. These new options replace old options such as {option}`programs.corectrl.gpuOverclock.enable` and {option}`programs.tuxclocker.enableAMD`.
|
This allows for fine-grained control over the GPU's performance and maybe required by overclocking softwares like Corectrl and Lact. These new options replace old options such as {option}`programs.corectrl.gpuOverclock.enable` and {option}`programs.tuxclocker.enableAMD`.
|
||||||
|
|
||||||
|
|||||||
@@ -42,6 +42,14 @@ let
|
|||||||
ip link del dev "${i}" 2>/dev/null || true
|
ip link del dev "${i}" 2>/dev/null || true
|
||||||
'';
|
'';
|
||||||
|
|
||||||
|
formatIpArgs =
|
||||||
|
args:
|
||||||
|
lib.pipe args [
|
||||||
|
(lib.filterAttrs (n: v: v != null))
|
||||||
|
(lib.mapAttrsToList (n: v: "${n} ${toString v}"))
|
||||||
|
(lib.concatStringsSep " ")
|
||||||
|
];
|
||||||
|
|
||||||
# warn that these attributes are deprecated (2017-2-2)
|
# warn that these attributes are deprecated (2017-2-2)
|
||||||
# Should be removed in the release after next
|
# Should be removed in the release after next
|
||||||
bondDeprecation = rec {
|
bondDeprecation = rec {
|
||||||
@@ -99,6 +107,7 @@ let
|
|||||||
|| (hasAttr dev cfg.bonds)
|
|| (hasAttr dev cfg.bonds)
|
||||||
|| (hasAttr dev cfg.macvlans)
|
|| (hasAttr dev cfg.macvlans)
|
||||||
|| (hasAttr dev cfg.sits)
|
|| (hasAttr dev cfg.sits)
|
||||||
|
|| (hasAttr dev cfg.ipips)
|
||||||
|| (hasAttr dev cfg.vlans)
|
|| (hasAttr dev cfg.vlans)
|
||||||
|| (hasAttr dev cfg.greTunnels)
|
|| (hasAttr dev cfg.greTunnels)
|
||||||
|| (hasAttr dev cfg.vswitches)
|
|| (hasAttr dev cfg.vswitches)
|
||||||
@@ -160,39 +169,41 @@ let
|
|||||||
EOF
|
EOF
|
||||||
''}
|
''}
|
||||||
|
|
||||||
# Set the default gateway.
|
# Set the default gateway
|
||||||
${optionalString (cfg.defaultGateway != null && cfg.defaultGateway.address != "") ''
|
${flip concatMapStrings
|
||||||
${optionalString (cfg.defaultGateway.interface != null) ''
|
[
|
||||||
ip route replace ${cfg.defaultGateway.address} dev ${cfg.defaultGateway.interface} ${
|
{
|
||||||
optionalString (cfg.defaultGateway.metric != null) "metric ${toString cfg.defaultGateway.metric}"
|
version = "-4";
|
||||||
} proto static
|
gateway = cfg.defaultGateway;
|
||||||
''}
|
}
|
||||||
ip route replace default ${
|
{
|
||||||
optionalString (cfg.defaultGateway.metric != null) "metric ${toString cfg.defaultGateway.metric}"
|
version = "-6";
|
||||||
} via "${cfg.defaultGateway.address}" ${
|
gateway = cfg.defaultGateway6;
|
||||||
optionalString (
|
}
|
||||||
cfg.defaultGatewayWindowSize != null
|
]
|
||||||
) "window ${toString cfg.defaultGatewayWindowSize}"
|
(
|
||||||
} ${
|
{ version, gateway }:
|
||||||
optionalString (cfg.defaultGateway.interface != null) "dev ${cfg.defaultGateway.interface}"
|
optionalString (gateway != null && gateway.address != "") ''
|
||||||
} proto static
|
${optionalString (gateway.interface != null) ''
|
||||||
''}
|
ip ${version} route replace ${gateway.address} proto static ${
|
||||||
${optionalString (cfg.defaultGateway6 != null && cfg.defaultGateway6.address != "") ''
|
formatIpArgs {
|
||||||
${optionalString (cfg.defaultGateway6.interface != null) ''
|
metric = gateway.metric;
|
||||||
ip -6 route replace ${cfg.defaultGateway6.address} dev ${cfg.defaultGateway6.interface} ${
|
dev = gateway.interface;
|
||||||
optionalString (cfg.defaultGateway6.metric != null) "metric ${toString cfg.defaultGateway6.metric}"
|
}
|
||||||
} proto static
|
}
|
||||||
''}
|
''}
|
||||||
ip -6 route replace default ${
|
ip ${version} route replace default proto static ${
|
||||||
optionalString (cfg.defaultGateway6.metric != null) "metric ${toString cfg.defaultGateway6.metric}"
|
formatIpArgs {
|
||||||
} via "${cfg.defaultGateway6.address}" ${
|
metric = gateway.metric;
|
||||||
optionalString (
|
via = gateway.address;
|
||||||
cfg.defaultGatewayWindowSize != null
|
window = cfg.defaultGatewayWindowSize;
|
||||||
) "window ${toString cfg.defaultGatewayWindowSize}"
|
dev = gateway.interface;
|
||||||
} ${
|
src = gateway.source;
|
||||||
optionalString (cfg.defaultGateway6.interface != null) "dev ${cfg.defaultGateway6.interface}"
|
}
|
||||||
} proto static
|
}
|
||||||
''}
|
''
|
||||||
|
)
|
||||||
|
}
|
||||||
'';
|
'';
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -240,10 +251,10 @@ let
|
|||||||
''
|
''
|
||||||
echo "${cidr}" >> $state
|
echo "${cidr}" >> $state
|
||||||
echo -n "adding address ${cidr}... "
|
echo -n "adding address ${cidr}... "
|
||||||
if out=$(ip addr replace "${cidr}" dev "${i.name}" 2>&1); then
|
if out=$(ip addr replace "${cidr}" dev "${i.name}" nodad 2>&1); then
|
||||||
echo "done"
|
echo "done"
|
||||||
else
|
else
|
||||||
echo "'ip addr replace \"${cidr}\" dev \"${i.name}\"' failed: $out"
|
echo "'ip addr replace \"${cidr}\" dev \"${i.name}\"' nodad failed: $out"
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
''
|
''
|
||||||
@@ -617,7 +628,7 @@ let
|
|||||||
deps = deviceDependency v.dev;
|
deps = deviceDependency v.dev;
|
||||||
in
|
in
|
||||||
{
|
{
|
||||||
description = "6-to-4 Tunnel Interface ${n}";
|
description = "IPv6 in IPv4 Tunnel Interface ${n}";
|
||||||
wantedBy = [
|
wantedBy = [
|
||||||
"network-setup.service"
|
"network-setup.service"
|
||||||
(subsystemDevice n)
|
(subsystemDevice n)
|
||||||
@@ -631,18 +642,65 @@ let
|
|||||||
script = ''
|
script = ''
|
||||||
# Remove Dead Interfaces
|
# Remove Dead Interfaces
|
||||||
ip link show dev "${n}" >/dev/null 2>&1 && ip link delete dev "${n}"
|
ip link show dev "${n}" >/dev/null 2>&1 && ip link delete dev "${n}"
|
||||||
ip link add name "${n}" type sit \
|
ip link add name "${n}" type sit ${
|
||||||
${optionalString (v.remote != null) "remote \"${v.remote}\""} \
|
formatIpArgs {
|
||||||
${optionalString (v.local != null) "local \"${v.local}\""} \
|
inherit (v)
|
||||||
${optionalString (v.ttl != null) "ttl ${toString v.ttl}"} \
|
remote
|
||||||
${optionalString (v.dev != null) "dev \"${v.dev}\""} \
|
local
|
||||||
${optionalString (v.encapsulation != null)
|
ttl
|
||||||
"encap ${v.encapsulation.type} encap-dport ${toString v.encapsulation.port} ${
|
dev
|
||||||
optionalString (
|
;
|
||||||
v.encapsulation.sourcePort != null
|
encap = if v.encapsulation.type == "6in4" then null else v.encapsulation.type;
|
||||||
) "encap-sport ${toString v.encapsulation.sourcePort}"
|
encap-dport = v.encapsulation.port;
|
||||||
}"
|
encap-sport = v.encapsulation.sourcePort;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
ip link set dev "${n}" up
|
||||||
|
'';
|
||||||
|
postStop = ''
|
||||||
|
ip link delete dev "${n}" || true
|
||||||
|
'';
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
createIpipDevice =
|
||||||
|
n: v:
|
||||||
|
nameValuePair "${n}-netdev" (
|
||||||
|
let
|
||||||
|
deps = deviceDependency v.dev;
|
||||||
|
in
|
||||||
|
{
|
||||||
|
description = "IP in IP Tunnel Interface ${n}";
|
||||||
|
wantedBy = [
|
||||||
|
"network-setup.service"
|
||||||
|
(subsystemDevice n)
|
||||||
|
];
|
||||||
|
bindsTo = deps;
|
||||||
|
after = [ "network-pre.target" ] ++ deps;
|
||||||
|
before = [ "network-setup.service" ];
|
||||||
|
serviceConfig.Type = "oneshot";
|
||||||
|
serviceConfig.RemainAfterExit = true;
|
||||||
|
path = [ pkgs.iproute2 ];
|
||||||
|
script = ''
|
||||||
|
# Remove Dead Interfaces
|
||||||
|
ip link show dev "${n}" >/dev/null 2>&1 && ip link delete dev "${n}"
|
||||||
|
ip tunnel add name "${n}" ${
|
||||||
|
formatIpArgs {
|
||||||
|
inherit (v)
|
||||||
|
remote
|
||||||
|
local
|
||||||
|
ttl
|
||||||
|
dev
|
||||||
|
;
|
||||||
|
mode =
|
||||||
|
{
|
||||||
|
"4in6" = "ipip6";
|
||||||
|
"ipip" = "ipip";
|
||||||
|
}
|
||||||
|
.${v.encapsulation.type};
|
||||||
|
encaplimit = if v.encapsulation.type == "ipip" then null else v.encapsulation.limit;
|
||||||
|
}
|
||||||
|
}
|
||||||
ip link set dev "${n}" up
|
ip link set dev "${n}" up
|
||||||
'';
|
'';
|
||||||
postStop = ''
|
postStop = ''
|
||||||
@@ -732,6 +790,7 @@ let
|
|||||||
// mapAttrs' createMacvlanDevice cfg.macvlans
|
// mapAttrs' createMacvlanDevice cfg.macvlans
|
||||||
// mapAttrs' createFouEncapsulation cfg.fooOverUDP
|
// mapAttrs' createFouEncapsulation cfg.fooOverUDP
|
||||||
// mapAttrs' createSitDevice cfg.sits
|
// mapAttrs' createSitDevice cfg.sits
|
||||||
|
// mapAttrs' createIpipDevice cfg.ipips
|
||||||
// mapAttrs' createGreDevice cfg.greTunnels
|
// mapAttrs' createGreDevice cfg.greTunnels
|
||||||
// mapAttrs' createVlanDevice cfg.vlans
|
// mapAttrs' createVlanDevice cfg.vlans
|
||||||
// {
|
// {
|
||||||
@@ -748,6 +807,9 @@ let
|
|||||||
in
|
in
|
||||||
|
|
||||||
{
|
{
|
||||||
|
|
||||||
|
meta.maintainers = with lib.maintainers; [ rnhmjoj ];
|
||||||
|
|
||||||
config = mkMerge [
|
config = mkMerge [
|
||||||
bondWarnings
|
bondWarnings
|
||||||
(mkIf (!cfg.useNetworkd) normalConfig)
|
(mkIf (!cfg.useNetworkd) normalConfig)
|
||||||
|
|||||||
@@ -24,6 +24,7 @@ let
|
|||||||
concatLists (map (bond: bond.interfaces) (attrValues cfg.bonds))
|
concatLists (map (bond: bond.interfaces) (attrValues cfg.bonds))
|
||||||
++ concatLists (map (bridge: bridge.interfaces) (attrValues cfg.bridges))
|
++ concatLists (map (bridge: bridge.interfaces) (attrValues cfg.bridges))
|
||||||
++ map (sit: sit.dev) (attrValues cfg.sits)
|
++ map (sit: sit.dev) (attrValues cfg.sits)
|
||||||
|
++ map (ipip: ipip.dev) (attrValues cfg.ipips)
|
||||||
++ map (gre: gre.dev) (attrValues cfg.greTunnels)
|
++ map (gre: gre.dev) (attrValues cfg.greTunnels)
|
||||||
++ map (vlan: vlan.interface) (attrValues cfg.vlans)
|
++ map (vlan: vlan.interface) (attrValues cfg.vlans)
|
||||||
# add dependency to physical or independently created vswitch member interface
|
# add dependency to physical or independently created vswitch member interface
|
||||||
@@ -46,6 +47,9 @@ let
|
|||||||
// optionalAttrs (gateway.metric != null) {
|
// optionalAttrs (gateway.metric != null) {
|
||||||
Metric = gateway.metric;
|
Metric = gateway.metric;
|
||||||
}
|
}
|
||||||
|
// optionalAttrs (gateway.source != null) {
|
||||||
|
PreferredSource = gateway.source;
|
||||||
|
}
|
||||||
)
|
)
|
||||||
];
|
];
|
||||||
};
|
};
|
||||||
@@ -435,7 +439,7 @@ in
|
|||||||
// (optionalAttrs (sit.ttl != null) {
|
// (optionalAttrs (sit.ttl != null) {
|
||||||
TTL = sit.ttl;
|
TTL = sit.ttl;
|
||||||
})
|
})
|
||||||
// (optionalAttrs (sit.encapsulation != null) (
|
// (optionalAttrs (sit.encapsulation.type != "6in4") (
|
||||||
{
|
{
|
||||||
FooOverUDP = true;
|
FooOverUDP = true;
|
||||||
Encapsulation = if sit.encapsulation.type == "fou" then "FooOverUDP" else "GenericUDPEncapsulation";
|
Encapsulation = if sit.encapsulation.type == "fou" then "FooOverUDP" else "GenericUDPEncapsulation";
|
||||||
@@ -454,6 +458,38 @@ in
|
|||||||
}
|
}
|
||||||
)
|
)
|
||||||
))
|
))
|
||||||
|
(mkMerge (
|
||||||
|
flip mapAttrsToList cfg.ipips (
|
||||||
|
name: ipip: {
|
||||||
|
netdevs."40-${name}" = {
|
||||||
|
netdevConfig = {
|
||||||
|
Name = name;
|
||||||
|
Kind = if ipip.encapsulation.type == "ipip" then "ipip" else "ip6tnl";
|
||||||
|
};
|
||||||
|
tunnelConfig =
|
||||||
|
(optionalAttrs (ipip.remote != null) {
|
||||||
|
Remote = ipip.remote;
|
||||||
|
})
|
||||||
|
// (optionalAttrs (ipip.local != null) {
|
||||||
|
Local = ipip.local;
|
||||||
|
})
|
||||||
|
// (optionalAttrs (ipip.ttl != null) {
|
||||||
|
TTL = ipip.ttl;
|
||||||
|
})
|
||||||
|
// (optionalAttrs (ipip.encapsulation.type != "ipip") {
|
||||||
|
# IPv6 tunnel options
|
||||||
|
Mode = if ipip.encapsulation.type == "4in6" then "ipip6" else "ip6ip6";
|
||||||
|
EncapsulationLimit = ipip.encapsulation.type;
|
||||||
|
});
|
||||||
|
};
|
||||||
|
networks = mkIf (ipip.dev != null) {
|
||||||
|
"40-${ipip.dev}" = {
|
||||||
|
tunnel = [ name ];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
||||||
|
)
|
||||||
|
))
|
||||||
(mkMerge (
|
(mkMerge (
|
||||||
flip mapAttrsToList cfg.greTunnels (
|
flip mapAttrsToList cfg.greTunnels (
|
||||||
name: gre: {
|
name: gre: {
|
||||||
|
|||||||
@@ -19,7 +19,8 @@ let
|
|||||||
hasSits = cfg.sits != { };
|
hasSits = cfg.sits != { };
|
||||||
hasGres = cfg.greTunnels != { };
|
hasGres = cfg.greTunnels != { };
|
||||||
hasBonds = cfg.bonds != { };
|
hasBonds = cfg.bonds != { };
|
||||||
hasFous = cfg.fooOverUDP != { } || filterAttrs (_: s: s.encapsulation != null) cfg.sits != { };
|
hasFous =
|
||||||
|
cfg.fooOverUDP != { } || filterAttrs (_: s: s.encapsulation.type != "6in4") cfg.sits != { };
|
||||||
|
|
||||||
slaves =
|
slaves =
|
||||||
concatMap (i: i.interfaces) (attrValues cfg.bonds)
|
concatMap (i: i.interfaces) (attrValues cfg.bonds)
|
||||||
@@ -180,6 +181,12 @@ let
|
|||||||
description = "The default gateway metric/preference.";
|
description = "The default gateway metric/preference.";
|
||||||
};
|
};
|
||||||
|
|
||||||
|
source = mkOption {
|
||||||
|
type = types.nullOr types.str;
|
||||||
|
default = null;
|
||||||
|
description = "The default source address.";
|
||||||
|
};
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
};
|
};
|
||||||
@@ -656,6 +663,7 @@ in
|
|||||||
example = {
|
example = {
|
||||||
address = "131.211.84.1";
|
address = "131.211.84.1";
|
||||||
interface = "enp3s0";
|
interface = "enp3s0";
|
||||||
|
source = "131.211.84.2";
|
||||||
};
|
};
|
||||||
type = types.nullOr (types.coercedTo types.str gatewayCoerce (types.submodule gatewayOpts));
|
type = types.nullOr (types.coercedTo types.str gatewayCoerce (types.submodule gatewayOpts));
|
||||||
description = ''
|
description = ''
|
||||||
@@ -669,6 +677,7 @@ in
|
|||||||
example = {
|
example = {
|
||||||
address = "2001:4d0:1e04:895::1";
|
address = "2001:4d0:1e04:895::1";
|
||||||
interface = "enp3s0";
|
interface = "enp3s0";
|
||||||
|
source = "2001:4d0:1e04:895::2";
|
||||||
};
|
};
|
||||||
type = types.nullOr (types.coercedTo types.str gatewayCoerce (types.submodule gatewayOpts));
|
type = types.nullOr (types.coercedTo types.str gatewayCoerce (types.submodule gatewayOpts));
|
||||||
description = ''
|
description = ''
|
||||||
@@ -1134,6 +1143,104 @@ in
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
networking.ipips = mkOption {
|
||||||
|
default = { };
|
||||||
|
example = literalExpression ''
|
||||||
|
{
|
||||||
|
wan4in6 = {
|
||||||
|
remote = "2001:db8::1";
|
||||||
|
local = "2001:db8::3";
|
||||||
|
dev = "wan6";
|
||||||
|
encapsulation.type = "4in6";
|
||||||
|
encapsulation.limit = 0;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
'';
|
||||||
|
description = ''
|
||||||
|
This option allows you to define interfaces encapsulating IP
|
||||||
|
packets within IP packets; which should be automatically created.
|
||||||
|
|
||||||
|
For example, this allows you to create 4in6 (RFC 2473)
|
||||||
|
or IP within IP (RFC 2003) tunnels.
|
||||||
|
'';
|
||||||
|
type =
|
||||||
|
with types;
|
||||||
|
attrsOf (submodule {
|
||||||
|
options = {
|
||||||
|
|
||||||
|
remote = mkOption {
|
||||||
|
type = types.str;
|
||||||
|
example = "2001:db8::1";
|
||||||
|
description = ''
|
||||||
|
The address of the remote endpoint to forward traffic over.
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
|
||||||
|
local = mkOption {
|
||||||
|
type = types.str;
|
||||||
|
example = "2001:db8::3";
|
||||||
|
description = ''
|
||||||
|
The address of the local endpoint which the remote
|
||||||
|
side should send packets to.
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
|
||||||
|
ttl = mkOption {
|
||||||
|
type = types.nullOr types.int;
|
||||||
|
default = null;
|
||||||
|
example = 255;
|
||||||
|
description = ''
|
||||||
|
The time-to-live of the connection to the remote tunnel endpoint.
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
|
||||||
|
dev = mkOption {
|
||||||
|
type = types.nullOr types.str;
|
||||||
|
default = null;
|
||||||
|
example = "wan6";
|
||||||
|
description = ''
|
||||||
|
The underlying network device on which the tunnel resides.
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
|
||||||
|
encapsulation.type = mkOption {
|
||||||
|
type = types.enum [
|
||||||
|
"ipip"
|
||||||
|
"4in6"
|
||||||
|
"ip6ip6"
|
||||||
|
];
|
||||||
|
default = "ipip";
|
||||||
|
description = ''
|
||||||
|
Select the encapsulation type:
|
||||||
|
|
||||||
|
- `ipip` to create an IPv4 within IPv4 tunnel (RFC 2003).
|
||||||
|
|
||||||
|
- `4in6` to create a 4in6 tunnel (RFC 2473);
|
||||||
|
|
||||||
|
- `ip6ip6` to create an IPv6 within IPv6 tunnel (RFC 2473);
|
||||||
|
|
||||||
|
::: {.note}
|
||||||
|
For encapsulating IPv6 within IPv4 packets, see
|
||||||
|
the ad-hoc {option}`networking.sits` option.
|
||||||
|
:::
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
|
||||||
|
encapsulation.limit = mkOption {
|
||||||
|
type = types.either (types.enum [ "none" ]) types.ints.unsigned;
|
||||||
|
default = 4;
|
||||||
|
example = "none";
|
||||||
|
description = ''
|
||||||
|
For an IPv6-based tunnel, the maximum number of nested
|
||||||
|
encapsulation to allow. 0 means no nesting, "none" unlimited.
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
networking.sits = mkOption {
|
networking.sits = mkOption {
|
||||||
default = { };
|
default = { };
|
||||||
example = literalExpression ''
|
example = literalExpression ''
|
||||||
@@ -1151,7 +1258,8 @@ in
|
|||||||
}
|
}
|
||||||
'';
|
'';
|
||||||
description = ''
|
description = ''
|
||||||
This option allows you to define 6-to-4 interfaces which should be automatically created.
|
This option allows you to define interfaces encapsulating IPv6
|
||||||
|
packets within IPv4 packets; which should be automatically created.
|
||||||
'';
|
'';
|
||||||
type =
|
type =
|
||||||
with types;
|
with types;
|
||||||
@@ -1195,50 +1303,76 @@ in
|
|||||||
'';
|
'';
|
||||||
};
|
};
|
||||||
|
|
||||||
encapsulation =
|
encapsulation = mkOption {
|
||||||
with types;
|
type = types.nullOr (
|
||||||
mkOption {
|
types.submodule {
|
||||||
type = nullOr (submodule {
|
|
||||||
options = {
|
options = {
|
||||||
type = mkOption {
|
type = mkOption {
|
||||||
type = enum [
|
type = types.enum [
|
||||||
|
"6in4"
|
||||||
"fou"
|
"fou"
|
||||||
"gue"
|
"gue"
|
||||||
];
|
];
|
||||||
|
default = "6in4";
|
||||||
description = ''
|
description = ''
|
||||||
Selects encapsulation type. See
|
Select the encapsulation type:
|
||||||
{manpage}`ip-link(8)` for details.
|
|
||||||
|
- `6in4`: the IPv6 packets are encapsulated using the
|
||||||
|
6in4 protocol (formerly known as SIT, RFC 4213);
|
||||||
|
|
||||||
|
- `gue`: the IPv6 packets are encapsulated in UDP packets
|
||||||
|
using the Generic UDP Encapsulation (GUE) scheme;
|
||||||
|
|
||||||
|
- `foo`: the IPv6 packets are encapsulated in UDP packets
|
||||||
|
using the Foo over UDP (FOU) scheme.
|
||||||
'';
|
'';
|
||||||
};
|
};
|
||||||
|
|
||||||
port = mkOption {
|
port = mkOption {
|
||||||
type = port;
|
type = types.nullOr types.port;
|
||||||
|
default = null;
|
||||||
example = 9001;
|
example = 9001;
|
||||||
description = ''
|
description = ''
|
||||||
Destination port for encapsulated packets.
|
Destination port when using UDP encapsulation.
|
||||||
'';
|
'';
|
||||||
};
|
};
|
||||||
|
|
||||||
sourcePort = mkOption {
|
sourcePort = mkOption {
|
||||||
type = nullOr types.port;
|
type = types.nullOr types.port;
|
||||||
default = null;
|
default = null;
|
||||||
example = 9002;
|
example = 9002;
|
||||||
description = ''
|
description = ''
|
||||||
Source port for encapsulated packets. Will be chosen automatically by
|
Source port when using UDP encapsulation.
|
||||||
the kernel if unset.
|
Will be chosen automatically by the kernel if unset.
|
||||||
'';
|
'';
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
});
|
}
|
||||||
default = null;
|
);
|
||||||
example = {
|
apply =
|
||||||
type = "fou";
|
x:
|
||||||
port = 9001;
|
if x == null then
|
||||||
};
|
lib.warn
|
||||||
description = ''
|
''
|
||||||
Configures encapsulation in UDP packets.
|
The option networking.sits.*.encapsulation no longer accepts `null`
|
||||||
'';
|
as a valid value. To fix this warning simply remove this definition.
|
||||||
|
''
|
||||||
|
{
|
||||||
|
type = "6in4";
|
||||||
|
port = null;
|
||||||
|
sourcePort = null;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
x;
|
||||||
|
default = { };
|
||||||
|
example = {
|
||||||
|
type = "fou";
|
||||||
|
port = 9001;
|
||||||
};
|
};
|
||||||
|
description = ''
|
||||||
|
Configures the type of encapsulation.
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -1541,6 +1675,8 @@ in
|
|||||||
|
|
||||||
###### implementation
|
###### implementation
|
||||||
|
|
||||||
|
meta.maintainers = with lib.maintainers; [ rnhmjoj ];
|
||||||
|
|
||||||
config = {
|
config = {
|
||||||
|
|
||||||
warnings =
|
warnings =
|
||||||
|
|||||||
@@ -144,7 +144,7 @@ rec {
|
|||||||
(onFullSupported "nixos.tests.networking.scripted.macvlan")
|
(onFullSupported "nixos.tests.networking.scripted.macvlan")
|
||||||
(onFullSupported "nixos.tests.networking.scripted.privacy")
|
(onFullSupported "nixos.tests.networking.scripted.privacy")
|
||||||
(onFullSupported "nixos.tests.networking.scripted.routes")
|
(onFullSupported "nixos.tests.networking.scripted.routes")
|
||||||
(onFullSupported "nixos.tests.networking.scripted.sit")
|
(onFullSupported "nixos.tests.networking.scripted.sit-fou")
|
||||||
(onFullSupported "nixos.tests.networking.scripted.static")
|
(onFullSupported "nixos.tests.networking.scripted.static")
|
||||||
(onFullSupported "nixos.tests.networking.scripted.virtual")
|
(onFullSupported "nixos.tests.networking.scripted.virtual")
|
||||||
(onFullSupported "nixos.tests.networking.scripted.vlan")
|
(onFullSupported "nixos.tests.networking.scripted.vlan")
|
||||||
@@ -158,7 +158,7 @@ rec {
|
|||||||
#(onFullSupported "nixos.tests.networking.networkd.macvlan")
|
#(onFullSupported "nixos.tests.networking.networkd.macvlan")
|
||||||
(onFullSupported "nixos.tests.networking.networkd.privacy")
|
(onFullSupported "nixos.tests.networking.networkd.privacy")
|
||||||
(onFullSupported "nixos.tests.networking.networkd.routes")
|
(onFullSupported "nixos.tests.networking.networkd.routes")
|
||||||
(onFullSupported "nixos.tests.networking.networkd.sit")
|
(onFullSupported "nixos.tests.networking.networkd.sit-fou")
|
||||||
(onFullSupported "nixos.tests.networking.networkd.static")
|
(onFullSupported "nixos.tests.networking.networkd.static")
|
||||||
(onFullSupported "nixos.tests.networking.networkd.virtual")
|
(onFullSupported "nixos.tests.networking.networkd.virtual")
|
||||||
(onFullSupported "nixos.tests.networking.networkd.vlan")
|
(onFullSupported "nixos.tests.networking.networkd.vlan")
|
||||||
|
|||||||
@@ -39,11 +39,23 @@ let
|
|||||||
defaultGateway = {
|
defaultGateway = {
|
||||||
address = "192.168.1.1";
|
address = "192.168.1.1";
|
||||||
interface = "enp1s0";
|
interface = "enp1s0";
|
||||||
|
source = "192.168.1.3";
|
||||||
};
|
};
|
||||||
defaultGateway6 = {
|
defaultGateway6 = {
|
||||||
address = "fd00:1234:5678:1::1";
|
address = "fd00:1234:5678:1::1";
|
||||||
interface = "enp1s0";
|
interface = "enp1s0";
|
||||||
|
source = "fd00:1234:5678:1::3";
|
||||||
};
|
};
|
||||||
|
interfaces.enp1s0.ipv6.addresses = [
|
||||||
|
{
|
||||||
|
address = "fd00:1234:5678:1::2";
|
||||||
|
prefixLength = 64;
|
||||||
|
}
|
||||||
|
{
|
||||||
|
address = "fd00:1234:5678:1::3";
|
||||||
|
prefixLength = 128;
|
||||||
|
}
|
||||||
|
];
|
||||||
interfaces.enp1s0.ipv4.addresses = [
|
interfaces.enp1s0.ipv4.addresses = [
|
||||||
{
|
{
|
||||||
address = "192.168.1.2";
|
address = "192.168.1.2";
|
||||||
@@ -89,7 +101,11 @@ let
|
|||||||
|
|
||||||
with subtest("Test default gateway"):
|
with subtest("Test default gateway"):
|
||||||
client.wait_until_succeeds("ping -c 1 192.168.3.1")
|
client.wait_until_succeeds("ping -c 1 192.168.3.1")
|
||||||
client.wait_until_succeeds("ping -c 1 fd00:1234:5678:3::1")
|
client.wait_until_succeeds("ping -c 1 fd00:1234:5678:1::1")
|
||||||
|
|
||||||
|
with subtest("Test default addresses"):
|
||||||
|
client.succeed("ip -4 route show default | grep -q 'src 192.168.1.3'")
|
||||||
|
client.succeed("ip -6 route show default | grep -q 'src fd00:1234:5678:1::3'")
|
||||||
'';
|
'';
|
||||||
};
|
};
|
||||||
routeType = {
|
routeType = {
|
||||||
@@ -481,7 +497,73 @@ let
|
|||||||
} in fous, "fou4 exists"
|
} in fous, "fou4 exists"
|
||||||
'';
|
'';
|
||||||
};
|
};
|
||||||
sit =
|
sit-6in4 =
|
||||||
|
let
|
||||||
|
node =
|
||||||
|
{
|
||||||
|
address4,
|
||||||
|
remote,
|
||||||
|
address6,
|
||||||
|
}:
|
||||||
|
{
|
||||||
|
virtualisation.interfaces.enp1s0.vlan = 1;
|
||||||
|
networking = {
|
||||||
|
useNetworkd = networkd;
|
||||||
|
useDHCP = false;
|
||||||
|
sits.sit = {
|
||||||
|
inherit remote;
|
||||||
|
local = address4;
|
||||||
|
dev = "enp1s0";
|
||||||
|
};
|
||||||
|
nftables.enable = true;
|
||||||
|
firewall.extraInputRules = "meta l4proto 41 accept";
|
||||||
|
interfaces.enp1s0.ipv4.addresses = lib.mkOverride 0 [
|
||||||
|
{
|
||||||
|
address = address4;
|
||||||
|
prefixLength = 24;
|
||||||
|
}
|
||||||
|
];
|
||||||
|
interfaces.sit.ipv6.addresses = lib.mkOverride 0 [
|
||||||
|
{
|
||||||
|
address = address6;
|
||||||
|
prefixLength = 64;
|
||||||
|
}
|
||||||
|
];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
in
|
||||||
|
{
|
||||||
|
name = "Sit-6in4";
|
||||||
|
nodes.client1 = node {
|
||||||
|
address4 = "192.168.1.1";
|
||||||
|
remote = "192.168.1.2";
|
||||||
|
address6 = "fc00::1";
|
||||||
|
};
|
||||||
|
nodes.client2 = node {
|
||||||
|
address4 = "192.168.1.2";
|
||||||
|
remote = "192.168.1.1";
|
||||||
|
address6 = "fc00::2";
|
||||||
|
};
|
||||||
|
testScript = ''
|
||||||
|
start_all()
|
||||||
|
|
||||||
|
with subtest("Wait for networking to be configured"):
|
||||||
|
client1.wait_for_unit("network.target")
|
||||||
|
client2.wait_for_unit("network.target")
|
||||||
|
|
||||||
|
# Print diagnostic information
|
||||||
|
client1.succeed("ip addr >&2")
|
||||||
|
client2.succeed("ip addr >&2")
|
||||||
|
|
||||||
|
with subtest("Test ipv6"):
|
||||||
|
client1.wait_until_succeeds("ping -c 1 fc00::1")
|
||||||
|
client1.wait_until_succeeds("ping -c 1 fc00::2")
|
||||||
|
|
||||||
|
client2.wait_until_succeeds("ping -c 1 fc00::1")
|
||||||
|
client2.wait_until_succeeds("ping -c 1 fc00::2")
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
sit-fou =
|
||||||
let
|
let
|
||||||
node =
|
node =
|
||||||
{
|
{
|
||||||
@@ -516,7 +598,7 @@ let
|
|||||||
};
|
};
|
||||||
in
|
in
|
||||||
{
|
{
|
||||||
name = "Sit";
|
name = "Sit-fou";
|
||||||
# note on firewalling: the two nodes are explicitly asymmetric.
|
# note on firewalling: the two nodes are explicitly asymmetric.
|
||||||
# client1 sends SIT packets in UDP, but accepts only proto-41 incoming.
|
# client1 sends SIT packets in UDP, but accepts only proto-41 incoming.
|
||||||
# client2 does the reverse, sending in proto-41 and accepting only UDP incoming.
|
# client2 does the reverse, sending in proto-41 and accepting only UDP incoming.
|
||||||
@@ -531,7 +613,8 @@ let
|
|||||||
} args)
|
} args)
|
||||||
{
|
{
|
||||||
networking = {
|
networking = {
|
||||||
firewall.extraCommands = "iptables -A INPUT -p 41 -j ACCEPT";
|
nftables.enable = true;
|
||||||
|
firewall.extraInputRules = "meta l4proto 41 accept";
|
||||||
sits.sit.encapsulation = {
|
sits.sit.encapsulation = {
|
||||||
type = "fou";
|
type = "fou";
|
||||||
port = 9001;
|
port = 9001;
|
||||||
@@ -576,6 +659,140 @@ let
|
|||||||
client2.wait_until_succeeds("ping -c 1 fc00::2")
|
client2.wait_until_succeeds("ping -c 1 fc00::2")
|
||||||
'';
|
'';
|
||||||
};
|
};
|
||||||
|
ipip-4in6 =
|
||||||
|
let
|
||||||
|
node =
|
||||||
|
{
|
||||||
|
address4,
|
||||||
|
remote,
|
||||||
|
address6,
|
||||||
|
}:
|
||||||
|
{
|
||||||
|
virtualisation.interfaces.enp1s0.vlan = 1;
|
||||||
|
networking = {
|
||||||
|
useNetworkd = networkd;
|
||||||
|
useDHCP = false;
|
||||||
|
ipips."4in6" = {
|
||||||
|
inherit remote;
|
||||||
|
local = address6;
|
||||||
|
dev = "enp1s0";
|
||||||
|
encapsulation.type = "4in6";
|
||||||
|
};
|
||||||
|
firewall.enable = false;
|
||||||
|
nftables.enable = true;
|
||||||
|
firewall.extraInputRules = "meta l4proto ipip accept";
|
||||||
|
interfaces.enp1s0.ipv6.addresses = lib.mkOverride 0 [
|
||||||
|
{
|
||||||
|
address = address6;
|
||||||
|
prefixLength = 64;
|
||||||
|
}
|
||||||
|
];
|
||||||
|
interfaces."4in6".ipv4.addresses = lib.mkOverride 0 [
|
||||||
|
{
|
||||||
|
address = address4;
|
||||||
|
prefixLength = 24;
|
||||||
|
}
|
||||||
|
];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
in
|
||||||
|
{
|
||||||
|
name = "ipip-4in6";
|
||||||
|
nodes.client1 = node {
|
||||||
|
address6 = "fc00::1";
|
||||||
|
address4 = "192.168.1.1";
|
||||||
|
remote = "fc00::2";
|
||||||
|
};
|
||||||
|
nodes.client2 = node {
|
||||||
|
address6 = "fc00::2";
|
||||||
|
address4 = "192.168.1.2";
|
||||||
|
remote = "fc00::1";
|
||||||
|
};
|
||||||
|
testScript = ''
|
||||||
|
start_all()
|
||||||
|
|
||||||
|
with subtest("Wait for networking to be configured"):
|
||||||
|
client1.wait_for_unit("network.target")
|
||||||
|
client2.wait_for_unit("network.target")
|
||||||
|
|
||||||
|
# Print diagnostic information
|
||||||
|
client1.succeed("ip addr >&2")
|
||||||
|
client2.succeed("ip addr >&2")
|
||||||
|
|
||||||
|
with subtest("Test ipv6"):
|
||||||
|
client1.wait_until_succeeds("ping -c 1 192.168.1.1")
|
||||||
|
client1.wait_until_succeeds("ping -c 1 192.168.1.2")
|
||||||
|
|
||||||
|
client2.wait_until_succeeds("ping -c 1 192.168.1.1")
|
||||||
|
client2.wait_until_succeeds("ping -c 1 192.168.1.2")
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
ipip =
|
||||||
|
let
|
||||||
|
node =
|
||||||
|
{
|
||||||
|
local,
|
||||||
|
remote,
|
||||||
|
address,
|
||||||
|
}:
|
||||||
|
{
|
||||||
|
virtualisation.interfaces.enp1s0.vlan = 1;
|
||||||
|
networking = {
|
||||||
|
useNetworkd = networkd;
|
||||||
|
useDHCP = false;
|
||||||
|
ipips.ipip = {
|
||||||
|
inherit local remote;
|
||||||
|
dev = "enp1s0";
|
||||||
|
encapsulation.type = "ipip";
|
||||||
|
};
|
||||||
|
nftables.enable = true;
|
||||||
|
firewall.extraInputRules = "meta l4proto 4 accept";
|
||||||
|
interfaces.enp1s0.ipv4.addresses = lib.mkOverride 0 [
|
||||||
|
{
|
||||||
|
address = local;
|
||||||
|
prefixLength = 24;
|
||||||
|
}
|
||||||
|
];
|
||||||
|
interfaces.ipip.ipv4.addresses = lib.mkOverride 0 [
|
||||||
|
{
|
||||||
|
inherit address;
|
||||||
|
prefixLength = 24;
|
||||||
|
}
|
||||||
|
];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
in
|
||||||
|
{
|
||||||
|
name = "ipip";
|
||||||
|
nodes.client1 = node {
|
||||||
|
local = "192.168.1.1";
|
||||||
|
remote = "192.168.1.2";
|
||||||
|
address = "192.168.10.1";
|
||||||
|
};
|
||||||
|
nodes.client2 = node {
|
||||||
|
local = "192.168.1.2";
|
||||||
|
remote = "192.168.1.1";
|
||||||
|
address = "192.168.10.2";
|
||||||
|
};
|
||||||
|
testScript = ''
|
||||||
|
start_all()
|
||||||
|
|
||||||
|
with subtest("Wait for networking to be configured"):
|
||||||
|
client1.wait_for_unit("network.target")
|
||||||
|
client2.wait_for_unit("network.target")
|
||||||
|
|
||||||
|
# Print diagnostic information
|
||||||
|
client1.succeed("ip addr >&2")
|
||||||
|
client2.succeed("ip addr >&2")
|
||||||
|
|
||||||
|
with subtest("Test IPIP tunnel"):
|
||||||
|
client1.wait_until_succeeds("ping -c 1 192.168.10.1")
|
||||||
|
client1.wait_until_succeeds("ping -c 1 192.168.10.2")
|
||||||
|
|
||||||
|
client2.wait_until_succeeds("ping -c 1 192.168.10.1")
|
||||||
|
client2.wait_until_succeeds("ping -c 1 192.168.10.2")
|
||||||
|
'';
|
||||||
|
};
|
||||||
gre =
|
gre =
|
||||||
let
|
let
|
||||||
node =
|
node =
|
||||||
@@ -1241,6 +1458,7 @@ lib.mapAttrs (lib.const (
|
|||||||
attrs
|
attrs
|
||||||
// {
|
// {
|
||||||
name = "${attrs.name}-Networking-${if networkd then "Networkd" else "Scripted"}";
|
name = "${attrs.name}-Networking-${if networkd then "Networkd" else "Scripted"}";
|
||||||
|
meta.maintainers = with lib.maintainers; [ rnhmjoj ];
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
)) testCases
|
)) testCases
|
||||||
|
|||||||
Reference in New Issue
Block a user