nixos/networking: add options for configuring a GRE tunnel
Add `networking.greTunnels` option that allows a GRE tunnel to be configured in NixOS.
This commit is contained in:
parent
b56d7a70a7
commit
5ce7061945
|
@ -532,6 +532,33 @@ let
|
||||||
'';
|
'';
|
||||||
});
|
});
|
||||||
|
|
||||||
|
createGreDevice = n: v: nameValuePair "${n}-netdev"
|
||||||
|
(let
|
||||||
|
deps = deviceDependency v.dev;
|
||||||
|
in
|
||||||
|
{ description = "GRE Tunnel Interface ${n}";
|
||||||
|
wantedBy = [ "network-setup.service" (subsystemDevice n) ];
|
||||||
|
bindsTo = deps;
|
||||||
|
partOf = [ "network-setup.service" ];
|
||||||
|
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 "${n}" >/dev/null 2>&1 && ip link delete "${n}"
|
||||||
|
ip link add name "${n}" type ${v.type} \
|
||||||
|
${optionalString (v.remote != null) "remote \"${v.remote}\""} \
|
||||||
|
${optionalString (v.local != null) "local \"${v.local}\""} \
|
||||||
|
${optionalString (v.dev != null) "dev \"${v.dev}\""}
|
||||||
|
ip link set "${n}" up
|
||||||
|
'';
|
||||||
|
postStop = ''
|
||||||
|
ip link delete "${n}" || true
|
||||||
|
'';
|
||||||
|
});
|
||||||
|
|
||||||
createVlanDevice = n: v: nameValuePair "${n}-netdev"
|
createVlanDevice = n: v: nameValuePair "${n}-netdev"
|
||||||
(let
|
(let
|
||||||
deps = deviceDependency v.interface;
|
deps = deviceDependency v.interface;
|
||||||
|
@ -570,6 +597,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' createGreDevice cfg.greTunnels
|
||||||
// mapAttrs' createVlanDevice cfg.vlans
|
// mapAttrs' createVlanDevice cfg.vlans
|
||||||
// {
|
// {
|
||||||
network-setup = networkSetup;
|
network-setup = networkSetup;
|
||||||
|
|
|
@ -18,6 +18,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 (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
|
||||||
# TODO: warn the user that any address configured on those interfaces will be useless
|
# TODO: warn the user that any address configured on those interfaces will be useless
|
||||||
|
@ -245,6 +246,25 @@ in
|
||||||
} ]);
|
} ]);
|
||||||
};
|
};
|
||||||
})))
|
})))
|
||||||
|
(mkMerge (flip mapAttrsToList cfg.greTunnels (name: gre: {
|
||||||
|
netdevs."40-${name}" = {
|
||||||
|
netdevConfig = {
|
||||||
|
Name = name;
|
||||||
|
Kind = gre.type;
|
||||||
|
};
|
||||||
|
tunnelConfig =
|
||||||
|
(optionalAttrs (gre.remote != null) {
|
||||||
|
Remote = gre.remote;
|
||||||
|
}) // (optionalAttrs (gre.local != null) {
|
||||||
|
Local = gre.local;
|
||||||
|
});
|
||||||
|
};
|
||||||
|
networks = mkIf (gre.dev != null) {
|
||||||
|
"40-${gre.dev}" = (mkMerge [ (genericNetwork (mkOverride 999)) {
|
||||||
|
tunnel = [ name ];
|
||||||
|
} ]);
|
||||||
|
};
|
||||||
|
})))
|
||||||
(mkMerge (flip mapAttrsToList cfg.vlans (name: vlan: {
|
(mkMerge (flip mapAttrsToList cfg.vlans (name: vlan: {
|
||||||
netdevs."40-${name}" = {
|
netdevs."40-${name}" = {
|
||||||
netdevConfig = {
|
netdevConfig = {
|
||||||
|
|
|
@ -9,6 +9,7 @@ let
|
||||||
interfaces = attrValues cfg.interfaces;
|
interfaces = attrValues cfg.interfaces;
|
||||||
hasVirtuals = any (i: i.virtual) interfaces;
|
hasVirtuals = any (i: i.virtual) interfaces;
|
||||||
hasSits = cfg.sits != { };
|
hasSits = cfg.sits != { };
|
||||||
|
hasGres = cfg.greTunnels != { };
|
||||||
hasBonds = cfg.bonds != { };
|
hasBonds = cfg.bonds != { };
|
||||||
hasFous = cfg.fooOverUDP != { }
|
hasFous = cfg.fooOverUDP != { }
|
||||||
|| filterAttrs (_: s: s.encapsulation != null) cfg.sits != { };
|
|| filterAttrs (_: s: s.encapsulation != null) cfg.sits != { };
|
||||||
|
@ -996,6 +997,65 @@ in
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
networking.greTunnels = mkOption {
|
||||||
|
default = { };
|
||||||
|
example = literalExpression ''
|
||||||
|
{
|
||||||
|
greBridge = {
|
||||||
|
remote = "10.0.0.1";
|
||||||
|
local = "10.0.0.22";
|
||||||
|
dev = "enp4s0f0";
|
||||||
|
type = "tap";
|
||||||
|
};
|
||||||
|
}
|
||||||
|
'';
|
||||||
|
description = ''
|
||||||
|
This option allows you to define Generic Routing Encapsulation (GRE) tunnels.
|
||||||
|
'';
|
||||||
|
type = with types; attrsOf (submodule {
|
||||||
|
options = {
|
||||||
|
|
||||||
|
remote = mkOption {
|
||||||
|
type = types.nullOr types.str;
|
||||||
|
default = null;
|
||||||
|
example = "10.0.0.1";
|
||||||
|
description = ''
|
||||||
|
The address of the remote endpoint to forward traffic over.
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
|
||||||
|
local = mkOption {
|
||||||
|
type = types.nullOr types.str;
|
||||||
|
default = null;
|
||||||
|
example = "10.0.0.22";
|
||||||
|
description = ''
|
||||||
|
The address of the local endpoint which the remote
|
||||||
|
side should send packets to.
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
|
||||||
|
dev = mkOption {
|
||||||
|
type = types.nullOr types.str;
|
||||||
|
default = null;
|
||||||
|
example = "enp4s0f0";
|
||||||
|
description = ''
|
||||||
|
The underlying network device on which the tunnel resides.
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
|
||||||
|
type = mkOption {
|
||||||
|
type = with types; enum [ "tun" "tap" ];
|
||||||
|
default = "tap";
|
||||||
|
example = "tap";
|
||||||
|
apply = v: if v == "tun" then "gre" else "gretap";
|
||||||
|
description = ''
|
||||||
|
Whether the tunnel routes layer 2 (tap) or layer 3 (tun) traffic.
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
};
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
networking.vlans = mkOption {
|
networking.vlans = mkOption {
|
||||||
default = { };
|
default = { };
|
||||||
example = literalExpression ''
|
example = literalExpression ''
|
||||||
|
@ -1225,6 +1285,7 @@ in
|
||||||
boot.kernelModules = [ ]
|
boot.kernelModules = [ ]
|
||||||
++ optional hasVirtuals "tun"
|
++ optional hasVirtuals "tun"
|
||||||
++ optional hasSits "sit"
|
++ optional hasSits "sit"
|
||||||
|
++ optional hasGres "gre"
|
||||||
++ optional hasBonds "bonding"
|
++ optional hasBonds "bonding"
|
||||||
++ optional hasFous "fou";
|
++ optional hasFous "fou";
|
||||||
|
|
||||||
|
|
|
@ -489,6 +489,77 @@ let
|
||||||
client2.wait_until_succeeds("ping -c 1 fc00::2")
|
client2.wait_until_succeeds("ping -c 1 fc00::2")
|
||||||
'';
|
'';
|
||||||
};
|
};
|
||||||
|
gre = let
|
||||||
|
node = { pkgs, ... }: with pkgs.lib; {
|
||||||
|
networking = {
|
||||||
|
useNetworkd = networkd;
|
||||||
|
useDHCP = false;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
in {
|
||||||
|
name = "GRE";
|
||||||
|
nodes.client1 = args@{ pkgs, ... }:
|
||||||
|
mkMerge [
|
||||||
|
(node args)
|
||||||
|
{
|
||||||
|
virtualisation.vlans = [ 1 2 ];
|
||||||
|
networking = {
|
||||||
|
greTunnels = {
|
||||||
|
greTunnel = {
|
||||||
|
local = "192.168.2.1";
|
||||||
|
remote = "192.168.2.2";
|
||||||
|
dev = "eth2";
|
||||||
|
type = "tap";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
bridges.bridge.interfaces = [ "greTunnel" "eth1" ];
|
||||||
|
interfaces.eth1.ipv4.addresses = mkOverride 0 [];
|
||||||
|
interfaces.bridge.ipv4.addresses = mkOverride 0 [
|
||||||
|
{ address = "192.168.1.1"; prefixLength = 24; }
|
||||||
|
];
|
||||||
|
};
|
||||||
|
}
|
||||||
|
];
|
||||||
|
nodes.client2 = args@{ pkgs, ... }:
|
||||||
|
mkMerge [
|
||||||
|
(node args)
|
||||||
|
{
|
||||||
|
virtualisation.vlans = [ 2 3 ];
|
||||||
|
networking = {
|
||||||
|
greTunnels = {
|
||||||
|
greTunnel = {
|
||||||
|
local = "192.168.2.2";
|
||||||
|
remote = "192.168.2.1";
|
||||||
|
dev = "eth1";
|
||||||
|
type = "tap";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
bridges.bridge.interfaces = [ "greTunnel" "eth2" ];
|
||||||
|
interfaces.eth2.ipv4.addresses = mkOverride 0 [];
|
||||||
|
interfaces.bridge.ipv4.addresses = mkOverride 0 [
|
||||||
|
{ address = "192.168.1.2"; prefixLength = 24; }
|
||||||
|
];
|
||||||
|
};
|
||||||
|
}
|
||||||
|
];
|
||||||
|
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 GRE tunnel bridge over VLAN"):
|
||||||
|
client1.wait_until_succeeds("ping -c 1 192.168.1.2")
|
||||||
|
|
||||||
|
client2.wait_until_succeeds("ping -c 1 192.168.1.1")
|
||||||
|
'';
|
||||||
|
};
|
||||||
vlan = let
|
vlan = let
|
||||||
node = address: { pkgs, ... }: with pkgs.lib; {
|
node = address: { pkgs, ... }: with pkgs.lib; {
|
||||||
#virtualisation.vlans = [ 1 ];
|
#virtualisation.vlans = [ 1 ];
|
||||||
|
|
Loading…
Reference in a new issue