diff --git a/nixos/modules/services/networking/dhcpcd.nix b/nixos/modules/services/networking/dhcpcd.nix
index 8552395fdb16..b31d479ab4fd 100644
--- a/nixos/modules/services/networking/dhcpcd.nix
+++ b/nixos/modules/services/networking/dhcpcd.nix
@@ -18,6 +18,7 @@ let
map (i: i.name) (filter (i: if i.useDHCP != null then !i.useDHCP else i.ip4 != [ ] || i.ipAddress != null) interfaces)
++ mapAttrsToList (i: _: i) config.networking.sits
++ concatLists (attrValues (mapAttrs (n: v: v.interfaces) config.networking.bridges))
+ ++ concatLists (attrValues (mapAttrs (n: v: v.interfaces) config.networking.vswitches))
++ concatLists (attrValues (mapAttrs (n: v: v.interfaces) config.networking.bonds))
++ config.networking.dhcpcd.denyInterfaces;
diff --git a/nixos/modules/tasks/network-interfaces-scripted.nix b/nixos/modules/tasks/network-interfaces-scripted.nix
index 328d94cbb05c..d8b1592c36bb 100644
--- a/nixos/modules/tasks/network-interfaces-scripted.nix
+++ b/nixos/modules/tasks/network-interfaces-scripted.nix
@@ -220,6 +220,45 @@ in
'';
});
+ createVswitchDevice = n: v: nameValuePair "${n}-netdev"
+ (let
+ managedInterfaces = filter (x: hasAttr x cfg.interfaces) v.interfaces;
+ managedInterfaceServices = concatMap (i: [ "network-addresses-${i}.service" "network-link-${i}.service" ]) managedInterfaces;
+ virtualInterfaces = filter (x: (hasAttr x cfg.interfaces) && cfg.interfaces.${x}.virtual) v.interfaces;
+ virtualInterfaceServices = concatMap (i: [ "${i}-netdev.service" ]) virtualInterfaces;
+ deps = map subsystemDevice v.interfaces;
+ ofRules = pkgs.writeText "vswitch-${n}-openFlowRules" v.openFlowRules;
+ in
+ { description = "Open vSwitch Interface ${n}";
+ wantedBy = [ "network.target" "vswitchd.service" (subsystemDevice n) ];
+ requires = optionals v.bindInterfaces (deps ++ managedInterfaceServices ++ virtualInterfaceServices);
+ requiredBy = optionals v.bindInterfaces (managedInterfaceServices ++ virtualInterfaceServices);
+ bindsTo = deps ++ [ "vswitchd.service" ];
+ partOf = [ "vswitchd.service" ];
+ after = [ "network-pre.target" "vswitchd.service" ] ++ deps ++ managedInterfaceServices ++ virtualInterfaceServices;
+ before = [ "network-interfaces.target" (subsystemDevice n) ];
+ serviceConfig.Type = "oneshot";
+ serviceConfig.RemainAfterExit = true;
+ path = [ pkgs.iproute config.virtualisation.vswitch.package ];
+ script = ''
+ echo "Removing old Open vSwitch ${n}..."
+ ovs-vsctl --if-exists del-br ${n}
+
+ echo "Adding Open vSwitch ${n}..."
+ ovs-vsctl -- add-br ${n} ${concatMapStrings (i: " -- add-port ${n} ${i}") v.interfaces} \
+ ${concatMapStrings (x: " -- set-controller ${n} " + x) v.controllers} \
+ ${concatMapStrings (x: " -- " + x) (splitString "\n" v.extraOvsctlCmds)}
+
+ echo "Adding OpenFlow rules for Open vSwitch ${n}..."
+ ovs-ofctl add-flows ${n} ${ofRules}
+ '';
+ postStop = ''
+ ip link set ${n} down || true
+ ovs-ofctl del-flows ${n} || true
+ ovs-vsctl --if-exists del-br ${n}
+ '';
+ });
+
createBondDevice = n: v: nameValuePair "${n}-netdev"
(let
deps = map subsystemDevice v.interfaces;
@@ -335,6 +374,7 @@ in
map configureAddrs interfaces ++
map createTunDevice (filter (i: i.virtual) interfaces))
// mapAttrs' createBridgeDevice cfg.bridges
+ // mapAttrs' createVswitchDevice cfg.vswitches
// mapAttrs' createBondDevice cfg.bonds
// mapAttrs' createMacvlanDevice cfg.macvlans
// mapAttrs' createSitDevice cfg.sits
diff --git a/nixos/modules/tasks/network-interfaces-systemd.nix b/nixos/modules/tasks/network-interfaces-systemd.nix
index 8223c5a4941e..301ee43fd0e5 100644
--- a/nixos/modules/tasks/network-interfaces-systemd.nix
+++ b/nixos/modules/tasks/network-interfaces-systemd.nix
@@ -35,6 +35,9 @@ in
assertions = [ {
assertion = cfg.defaultGatewayWindowSize == null;
message = "networking.defaultGatewayWindowSize is not supported by networkd.";
+ } {
+ assertion = cfg.vswitches == {};
+ message = "networking.vswichtes are not supported by networkd.";
} ] ++ flip mapAttrsToList cfg.bridges (n: { rstp, ... }: {
assertion = !rstp;
message = "networking.bridges.${n}.rstp is not supported by networkd.";
diff --git a/nixos/modules/tasks/network-interfaces.nix b/nixos/modules/tasks/network-interfaces.nix
index 9931c977e8f0..7af3160e2d42 100644
--- a/nixos/modules/tasks/network-interfaces.nix
+++ b/nixos/modules/tasks/network-interfaces.nix
@@ -12,7 +12,8 @@ let
hasBonds = cfg.bonds != { };
slaves = concatMap (i: i.interfaces) (attrValues cfg.bonds)
- ++ concatMap (i: i.interfaces) (attrValues cfg.bridges);
+ ++ concatMap (i: i.interfaces) (attrValues cfg.bridges)
+ ++ concatMap (i: i.interfaces) (attrValues cfg.vswitches);
slaveIfs = map (i: cfg.interfaces.${i}) (filter (i: cfg.interfaces ? ${i}) slaves);
@@ -371,6 +372,81 @@ in
options = [ interfaceOpts ];
};
+ networking.vswitches = mkOption {
+ default = { };
+ example =
+ { vs0.interfaces = [ "eth0" "eth1" ];
+ vs1.interfaces = [ "eth2" "wlan0" ];
+ };
+ description =
+ ''
+ This option allows you to define Open vSwitches that connect
+ physical networks together. The value of this option is an
+ attribute set. Each attribute specifies a vswitch, with the
+ attribute name specifying the name of the vswitch's network
+ interface.
+ '';
+
+ type = types.attrsOf types.optionSet;
+
+ options = {
+
+ interfaces = mkOption {
+ example = [ "eth0" "eth1" ];
+ type = types.listOf types.str;
+ description =
+ "The physical network interfaces connected by the vSwitch.";
+ };
+
+ bindInterfaces = mkOption {
+ type = types.bool;
+ default = false;
+ description = ''
+ If true, then the interfaces of the vSwitch are brought 'up' and especially
+ also 'down' together with the vSwitch. That requires that every interfaces
+ is configured as a systemd network services.
+ '';
+ };
+
+ controllers = mkOption {
+ type = types.listOf types.str;
+ default = [];
+ example = [ "ptcp:6653:[::1]" ];
+ description = ''
+ Specify the controller targets. For the allowed options see man 8 ovs-vsctl.
+ '';
+ };
+
+ openFlowRules = mkOption {
+ type = types.lines;
+ default = "";
+ example = ''
+ actions=normal
+ '';
+ description = ''
+ OpenFlow rules to insert into the Open vSwitch. All openFlowRules are
+ loaded with ovs-ofctl within one atomic operation.
+ '';
+ };
+
+ extraOvsctlCmds = mkOption {
+ type = types.lines;
+ default = "";
+ example = ''
+ set-fail-mode secure
+ set Bridge stp_enable=true
+ '';
+ description = ''
+ Commands to manipulate the Open vSwitch database. Every line executed with ovs-vsctl.
+ All commands are bundled together with the operations for adding the interfaces
+ into one atomic operation.
+ '';
+ };
+
+ };
+
+ };
+
networking.bridges = mkOption {
default = { };
example =
@@ -766,6 +842,8 @@ in
services.mstpd = mkIf needsMstpd { enable = true; };
+ virtualisation.vswitch = mkIf (cfg.vswitches != { }) { enable = true; };
+
};
}