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; }; + }; }