summary refs log tree commit diff
diff options
context:
space:
mode:
-rwxr-xr-xnixos/modules/module-list.nix2
-rw-r--r--nixos/modules/services/networking/dhcpcd.nix2
-rw-r--r--nixos/modules/system/boot/systemd-unit-options.nix24
-rw-r--r--nixos/modules/system/boot/systemd.nix15
-rw-r--r--nixos/modules/tasks/network-interfaces-scripted.nix340
-rw-r--r--nixos/modules/tasks/network-interfaces-systemd.nix174
-rw-r--r--nixos/modules/tasks/network-interfaces.nix407
7 files changed, 615 insertions, 349 deletions
diff --git a/nixos/modules/module-list.nix b/nixos/modules/module-list.nix
index 85afcb824fb..9420ca4af40 100755
--- a/nixos/modules/module-list.nix
+++ b/nixos/modules/module-list.nix
@@ -387,6 +387,8 @@
   ./tasks/kbd.nix
   ./tasks/lvm.nix
   ./tasks/network-interfaces.nix
+  ./tasks/network-interfaces-systemd.nix
+  ./tasks/network-interfaces-scripted.nix
   ./tasks/scsi-link-power-management.nix
   ./tasks/swraid.nix
   ./tasks/trackpoint.nix
diff --git a/nixos/modules/services/networking/dhcpcd.nix b/nixos/modules/services/networking/dhcpcd.nix
index 15dbf80a987..d70008e7c23 100644
--- a/nixos/modules/services/networking/dhcpcd.nix
+++ b/nixos/modules/services/networking/dhcpcd.nix
@@ -11,7 +11,7 @@ let
   # Don't start dhcpcd on explicitly configured interfaces or on
   # interfaces that are part of a bridge, bond or sit device.
   ignoredInterfaces =
-    map (i: i.name) (filter (i: i.ip4 != [ ] || i.ipAddress != null) (attrValues config.networking.interfaces))
+    map (i: i.name) (filter (i: if i.useDHCP != null then i.useDHCP else i.ip4 != [ ] || i.ipAddress != null) (attrValues config.networking.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.bonds))
diff --git a/nixos/modules/system/boot/systemd-unit-options.nix b/nixos/modules/system/boot/systemd-unit-options.nix
index 52ad71a66c7..20851c626d7 100644
--- a/nixos/modules/system/boot/systemd-unit-options.nix
+++ b/nixos/modules/system/boot/systemd-unit-options.nix
@@ -832,6 +832,30 @@ in rec {
       '';
     };
 
+    name = mkOption {
+      type = types.nullOr types.str;
+      default = null;
+      description = ''
+        The name of the network interface to match against.
+      '';
+    };
+
+    DHCP = mkOption {
+      type = types.nullOr types.str;
+      default = null;
+      description = ''
+        Whether to enable DHCP on the interfaces matched.
+      '';
+    };
+
+    domains = mkOption {
+      type = types.nullOr (types.listOf types.str);
+      default = null;
+      description = ''
+        A list of domains to pass to the network config.
+      '';
+    };
+
     address = mkOption {
       default = [ ];
       type = types.listOf types.str;
diff --git a/nixos/modules/system/boot/systemd.nix b/nixos/modules/system/boot/systemd.nix
index 8c53d342b36..f34c16b9ac0 100644
--- a/nixos/modules/system/boot/systemd.nix
+++ b/nixos/modules/system/boot/systemd.nix
@@ -300,6 +300,19 @@ let
     };
   };
 
+  networkConfig = { name, config, ... }: {
+    config = {
+      matchConfig = optionalAttrs (config.name != null) {
+        Name = config.name;
+      };
+      networkConfig = optionalAttrs (config.DHCP != null) {
+        DHCP = config.DHCP;
+      } // optionalAttrs (config.domains != null) {
+        Domains = concatStringsSep " " config.domains;
+      };
+    };
+  };
+
   toOption = x:
     if x == true then "true"
     else if x == false then "false"
@@ -693,7 +706,7 @@ in
     systemd.network.networks = mkOption {
       default = {};
       type = types.attrsOf types.optionSet;
-      options = [ networkOptions ];
+      options = [ networkOptions networkConfig ];
       description = "Definiton of systemd networks.";
     };
 
diff --git a/nixos/modules/tasks/network-interfaces-scripted.nix b/nixos/modules/tasks/network-interfaces-scripted.nix
new file mode 100644
index 00000000000..a39f1a09a34
--- /dev/null
+++ b/nixos/modules/tasks/network-interfaces-scripted.nix
@@ -0,0 +1,340 @@
+{ config, lib, pkgs, utils, ... }:
+
+with lib;
+with utils;
+
+let
+
+  cfg = config.networking;
+  interfaces = attrValues cfg.interfaces;
+  hasVirtuals = any (i: i.virtual) interfaces;
+
+  # We must escape interfaces due to the systemd interpretation
+  subsystemDevice = interface:
+    "sys-subsystem-net-devices-${escapeSystemdPath interface}.device";
+
+  interfaceIps = i:
+    i.ip4 ++ optionals cfg.enableIPv6 i.ip6
+    ++ optional (i.ipAddress != null) {
+      address = i.ipAddress;
+      prefixLength = i.prefixLength;
+    } ++ optional (cfg.enableIPv6 && i.ipv6Address != null) {
+      address = i.ipv6Address;
+      prefixLength = i.ipv6PrefixLength;
+    };
+
+in
+
+{
+
+  config = mkIf (!cfg.useNetworkd) {
+
+    systemd.targets."network-interfaces" =
+      { description = "All Network Interfaces";
+        wantedBy = [ "network.target" ];
+        unitConfig.X-StopOnReconfiguration = true;
+      };
+
+    systemd.services =
+      let
+
+        networkLocalCommands = {
+          after = [ "network-setup.service" ];
+          bindsTo = [ "network-setup.service" ];
+        };
+
+        networkSetup =
+          { description = "Networking Setup";
+
+            after = [ "network-interfaces.target" ];
+            before = [ "network.target" "network-online.target" ];
+            wantedBy = [ "network.target" "network-online.target" ];
+
+            unitConfig.ConditionCapability = "CAP_NET_ADMIN";
+
+            path = [ pkgs.iproute ];
+
+            serviceConfig.Type = "oneshot";
+            serviceConfig.RemainAfterExit = true;
+
+            script =
+              (optionalString (!config.services.resolved.enable) ''
+                # Set the static DNS configuration, if given.
+                ${pkgs.openresolv}/sbin/resolvconf -m 1 -a static <<EOF
+                ${optionalString (cfg.nameservers != [] && cfg.domain != null) ''
+                  domain ${cfg.domain}
+                ''}
+                ${optionalString (cfg.search != []) ("search " + concatStringsSep " " cfg.search)}
+                ${flip concatMapStrings cfg.nameservers (ns: ''
+                  nameserver ${ns}
+                '')}
+                EOF
+              '') + ''
+                # Set the default gateway.
+                ${optionalString (cfg.defaultGateway != null) ''
+                  # FIXME: get rid of "|| true" (necessary to make it idempotent).
+                  ip route add default via "${cfg.defaultGateway}" ${
+                    optionalString (cfg.defaultGatewayWindowSize != null)
+                      "window ${cfg.defaultGatewayWindowSize}"} || true
+                ''}
+              '';
+          };
+
+        # For each interface <foo>, create a job ‘<foo>-cfg.service"
+        # that performs static configuration.  It has a "wants"
+        # dependency on ‘<foo>.service’, which is supposed to create
+        # the interface and need not exist (i.e. for hardware
+        # interfaces).  It has a binds-to dependency on the actual
+        # network device, so it only gets started after the interface
+        # has appeared, and it's stopped when the interface
+        # disappears.
+        configureInterface = i:
+          let
+            ips = interfaceIps i;
+          in
+          nameValuePair "${i.name}-cfg"
+          { description = "Configuration of ${i.name}";
+            wantedBy = [ "network-interfaces.target" ];
+            bindsTo = [ (subsystemDevice i.name) ];
+            after = [ (subsystemDevice i.name) ];
+            serviceConfig.Type = "oneshot";
+            serviceConfig.RemainAfterExit = true;
+            path = [ pkgs.iproute pkgs.gawk ];
+            script =
+              ''
+                echo "bringing up interface..."
+                ip link set "${i.name}" up
+              ''
+              + optionalString (i.macAddress != null)
+                ''
+                  echo "setting MAC address to ${i.macAddress}..."
+                  ip link set "${i.name}" address "${i.macAddress}"
+                ''
+              + optionalString (i.mtu != null)
+                ''
+                  echo "setting MTU to ${toString i.mtu}..."
+                  ip link set "${i.name}" mtu "${toString i.mtu}"
+                ''
+
+              # Ip Setup
+              +
+                ''
+                  curIps=$(ip -o a show dev "${i.name}" | awk '{print $4}')
+                  # Only do an add if it's necessary.  This is
+                  # useful when the Nix store is accessed via this
+                  # interface (e.g. in a QEMU VM test).
+                ''
+              + flip concatMapStrings (ips) (ip:
+                let
+                  address = "${ip.address}/${toString ip.prefixLength}";
+                in
+                ''
+                  echo "checking ip ${address}..."
+                  if ! echo "$curIps" | grep "${address}" >/dev/null 2>&1; then
+                    if out=$(ip addr add "${address}" dev "${i.name}" 2>&1); then
+                      echo "added ip ${address}..."
+                      restart_network_setup=true
+                    elif ! echo "$out" | grep "File exists" >/dev/null 2>&1; then
+                      echo "failed to add ${address}"
+                      exit 1
+                    fi
+                  fi
+                '')
+              + optionalString (ips != [ ])
+                ''
+                  if [ restart_network_setup = true ]; then
+                    # Ensure that the default gateway remains set.
+                    # (Flushing this interface may have removed it.)
+                    ${config.systemd.package}/bin/systemctl try-restart --no-block network-setup.service
+                  fi
+                  ${config.systemd.package}/bin/systemctl start ip-up.target
+                '';
+            preStop =
+              ''
+                echo "releasing configured ip's..."
+              ''
+              + flip concatMapStrings (ips) (ip:
+                let
+                  address = "${ip.address}/${toString ip.prefixLength}";
+                in
+                ''
+                  echo -n "Deleting ${address}..."
+                  ip addr del "${address}" dev "${i.name}" >/dev/null 2>&1 || echo -n " Failed"
+                  echo ""
+                '');
+          };
+
+        createTunDevice = i: nameValuePair "${i.name}-netdev"
+          { description = "Virtual Network Interface ${i.name}";
+            requires = [ "dev-net-tun.device" ];
+            after = [ "dev-net-tun.device" ];
+            wantedBy = [ "network.target" (subsystemDevice i.name) ];
+            path = [ pkgs.iproute ];
+            serviceConfig = {
+              Type = "oneshot";
+              RemainAfterExit = true;
+            };
+            script = ''
+              ip tuntap add dev "${i.name}" \
+              ${optionalString (i.virtualType != null) "mode ${i.virtualType}"} \
+              user "${i.virtualOwner}"
+            '';
+            postStop = ''
+              ip link del ${i.name}
+            '';
+          };
+
+        createBridgeDevice = n: v: nameValuePair "${n}-netdev"
+          (let
+            deps = map subsystemDevice v.interfaces;
+          in
+          { description = "Bridge Interface ${n}";
+            wantedBy = [ "network.target" (subsystemDevice n) ];
+            bindsTo = deps;
+            after = deps;
+            serviceConfig.Type = "oneshot";
+            serviceConfig.RemainAfterExit = true;
+            path = [ pkgs.bridge_utils pkgs.iproute ];
+            script =
+              ''
+                # Remove Dead Interfaces
+                ip link show "${n}" >/dev/null 2>&1 && ip link delete "${n}"
+
+                brctl addbr "${n}"
+
+                # Set bridge's hello time to 0 to avoid startup delays.
+                brctl setfd "${n}" 0
+
+                ${flip concatMapStrings v.interfaces (i: ''
+                  brctl addif "${n}" "${i}"
+                  ip link set "${i}" up
+                  ip addr flush dev "${i}"
+
+                  echo "bringing up network device ${n}..."
+                  ip link set "${n}" up
+                '')}
+
+                # !!! Should delete (brctl delif) any interfaces that
+                # no longer belong to the bridge.
+              '';
+            postStop =
+              ''
+                ip link set "${n}" down
+                brctl delbr "${n}"
+              '';
+          });
+
+        createBondDevice = n: v: nameValuePair "${n}-netdev"
+          (let
+            deps = map subsystemDevice v.interfaces;
+          in
+          { description = "Bond Interface ${n}";
+            wantedBy = [ "network.target" (subsystemDevice n) ];
+            bindsTo = deps;
+            after = deps;
+            before = [ "${n}-cfg.service" ];
+            serviceConfig.Type = "oneshot";
+            serviceConfig.RemainAfterExit = true;
+            path = [ pkgs.ifenslave pkgs.iproute ];
+            script = ''
+              ip link add name "${n}" type bond
+
+              # !!! There must be a better way to wait for the interface
+              while [ ! -d /sys/class/net/${n} ]; do sleep 0.1; done;
+
+              # Ensure the link is down so that we can set options
+              ip link set "${n}" down
+
+              # Set the miimon and mode options
+              ${optionalString (v.miimon != null)
+                "echo \"${toString v.miimon}\" >/sys/class/net/${n}/bonding/miimon"}
+              ${optionalString (v.mode != null)
+                "echo \"${v.mode}\" >/sys/class/net/${n}/bonding/mode"}
+              ${optionalString (v.lacp_rate != null)
+                "echo \"${v.lacp_rate}\" >/sys/class/net/${n}/bonding/lacp_rate"}
+              ${optionalString (v.xmit_hash_policy != null)
+                "echo \"${v.xmit_hash_policy}\" >/sys/class/net/${n}/bonding/xmit_hash_policy"}
+
+              # Bring up the bond and enslave the specified interfaces
+              ip link set "${n}" up
+              ${flip concatMapStrings v.interfaces (i: ''
+                ifenslave "${n}" "${i}"
+              '')}
+            '';
+            postStop = ''
+              ${flip concatMapStrings v.interfaces (i: ''
+                ifenslave -d "${n}" "${i}" >/dev/null 2>&1 || true
+              '')}
+              ip link set "${n}" down >/dev/null 2>&1 || true
+              ip link del "${n}" >/dev/null 2>&1 || true
+            '';
+          });
+
+        createSitDevice = n: v: nameValuePair "${n}-netdev"
+          (let
+            deps = optional (v.dev != null) (subsystemDevice v.dev);
+          in
+          { description = "6-to-4 Tunnel Interface ${n}";
+            wantedBy = [ "network.target" (subsystemDevice n) ];
+            bindsTo = deps;
+            after = deps;
+            serviceConfig.Type = "oneshot";
+            serviceConfig.RemainAfterExit = true;
+            path = [ pkgs.iproute ];
+            script = ''
+              # Remove Dead Interfaces
+              ip link show "${n}" >/dev/null 2>&1 && ip link delete "${n}"
+              ip link add name "${n}" type sit \
+                ${optionalString (v.remote != null) "remote \"${v.remote}\""} \
+                ${optionalString (v.local != null) "local \"${v.local}\""} \
+                ${optionalString (v.ttl != null) "ttl ${toString v.ttl}"} \
+                ${optionalString (v.dev != null) "dev \"${v.dev}\""}
+              ip link set "${n}" up
+            '';
+            postStop = ''
+              ip link delete "${n}"
+            '';
+          });
+
+        createVlanDevice = n: v: nameValuePair "${n}-netdev"
+          (let
+            deps = [ (subsystemDevice v.interface) ];
+          in
+          { description = "Vlan Interface ${n}";
+            wantedBy = [ "network.target" (subsystemDevice n) ];
+            bindsTo = deps;
+            after = deps;
+            serviceConfig.Type = "oneshot";
+            serviceConfig.RemainAfterExit = true;
+            path = [ pkgs.iproute ];
+            script = ''
+              # Remove Dead Interfaces
+              ip link show "${n}" >/dev/null 2>&1 && ip link delete "${n}"
+              ip link add link "${v.interface}" name "${n}" type vlan id "${toString v.id}"
+              ip link set "${n}" up
+            '';
+            postStop = ''
+              ip link delete "${n}"
+            '';
+          });
+
+      in listToAttrs (
+           map configureInterface interfaces ++
+           map createTunDevice (filter (i: i.virtual) interfaces))
+         // mapAttrs' createBridgeDevice cfg.bridges
+         // mapAttrs' createBondDevice cfg.bonds
+         // mapAttrs' createSitDevice cfg.sits
+         // mapAttrs' createVlanDevice cfg.vlans
+         // {
+           "network-setup" = networkSetup;
+           "network-local-commands" = networkLocalCommands;
+         };
+
+    services.udev.extraRules =
+      ''
+        KERNEL=="tun", TAG+="systemd"
+      '';
+
+  };
+
+}
diff --git a/nixos/modules/tasks/network-interfaces-systemd.nix b/nixos/modules/tasks/network-interfaces-systemd.nix
new file mode 100644
index 00000000000..2ff4793acef
--- /dev/null
+++ b/nixos/modules/tasks/network-interfaces-systemd.nix
@@ -0,0 +1,174 @@
+{ config, lib, pkgs, utils, ... }:
+
+with lib;
+with utils;
+
+let
+
+  cfg = config.networking;
+  interfaces = attrValues cfg.interfaces;
+
+  interfaceIps = i:
+    i.ip4 ++ optionals cfg.enableIPv6 i.ip6
+    ++ optional (i.ipAddress != null) {
+      address = i.ipAddress;
+      prefixLength = i.prefixLength;
+    } ++ optional (cfg.enableIPv6 && i.ipv6Address != null) {
+      address = i.ipv6Address;
+      prefixLength = i.ipv6PrefixLength;
+    };
+
+  dhcpStr = useDHCP: if useDHCP then "both" else "none";
+
+  slaves =
+    concatLists (map (bond: bond.interfaces) (attrValues cfg.bonds))
+    ++ concatLists (map (bridge: bridge.interfaces) (attrValues cfg.bridges))
+    ++ map (sit: sit.dev) (attrValues cfg.sits)
+    ++ map (vlan: vlan.interface) (attrValues cfg.vlans);
+
+in
+
+{
+
+  config = mkIf cfg.useNetworkd {
+
+    assertions = [ {
+      assertion = cfg.defaultGatewayWindowSize == null;
+      message = "networking.defaultGatewayWindowSize is not supported by networkd.";
+    } {
+      assertion = ! cfg.useHostResolvConf;
+      message = "networking.useHostResolvConf is not supported by networkd.";
+    } ];
+
+    systemd.services.dhcpcd.enable = mkDefault false;
+
+    systemd.services.network-local-commands = {
+      after = [ "systemd-networkd.service" ];
+      bindsTo = [ "systemd-networkd.service" ];
+    };
+
+    systemd.network =
+      let
+        domains = cfg.search ++ (optional (cfg.domain != null) cfg.domain);
+        genericNetwork = override: {
+          DHCP = override (dhcpStr cfg.useDHCP);
+        } // optionalAttrs (cfg.defaultGateway != null) {
+          gateway = override [ cfg.defaultGateway ];
+        } // optionalAttrs (domains != [ ]) {
+          domains = override domains;
+        };
+      in mkMerge [ {
+        enable = true;
+        networks."99-main" = genericNetwork mkDefault;
+      }
+      (mkMerge (flip map interfaces (i: {
+        links."40-${i.name}" = {
+          matchConfig.Name = i.name;
+          linkConfig =
+            (optionalAttrs (i.macAddress != null) {
+              MACAddress = i.macAddress;
+            }) // (optionalAttrs (i.mtu != null) {
+              MTUBytes = toString i.mtu;
+            });
+        };
+        netdevs = mkIf i.virtual (
+          let
+            devType = if i.virtualType != null then i.virtualType
+              else (if hasPrefix "tun" i.name then "tun" else "tap");
+          in {
+            "40-${i.name}" = {
+              netdevConfig = {
+                Name = i.name;
+                Kind = devType;
+              };
+              "${devType}Config" = optionalAttrs (i.virtualOwner != null) {
+                User = i.virtualOwner;
+              };
+            };
+          });
+        networks."40-${i.name}" = mkMerge [ (genericNetwork mkDefault) {
+          name = mkDefault i.name;
+          DHCP = mkForce (dhcpStr
+            (if i.useDHCP != null then i.useDHCP else interfaceIps i == [ ]));
+          address = flip map (interfaceIps i)
+            (ip: "${ip.address}/${toString ip.prefixLength}");
+        } ];
+      })))
+      (mkMerge (flip mapAttrsToList cfg.bridges (name: bridge: {
+        netdevs."40-${name}" = {
+          netdevConfig = {
+            Name = name;
+            Kind = "bridge";
+          };
+        };
+        networks = listToAttrs (flip map bridge.interfaces (bi:
+          nameValuePair "40-${bi}" (mkMerge [ (genericNetwork (mkOverride 999)) {
+            DHCP = mkOverride 0 (dhcpStr false);
+            networkConfig.Bridge = name;
+          } ])));
+      })))
+      (mkMerge (flip mapAttrsToList cfg.bonds (name: bond: {
+        netdevs."40-${name}" = {
+          netdevConfig = {
+            Name = name;
+            Kind = "bond";
+          };
+          bondConfig =
+            (optionalAttrs (bond.lacp_rate != null) {
+              LACPTransmitRate = bond.lacp_rate;
+            }) // (optionalAttrs (bond.miimon != null) {
+              MIIMonitorSec = bond.miimon;
+            }) // (optionalAttrs (bond.mode != null) {
+              Mode = bond.mode;
+            }) // (optionalAttrs (bond.xmit_hash_policy != null) {
+              TransmitHashPolicy = bond.xmit_hash_policy;
+            });
+        };
+        networks = listToAttrs (flip map bond.interfaces (bi:
+          nameValuePair "40-${bi}" (mkMerge [ (genericNetwork (mkOverride 999)) {
+            DHCP = mkOverride 0 (dhcpStr false);
+            networkConfig.Bond = name;
+          } ])));
+      })))
+      (mkMerge (flip mapAttrsToList cfg.sits (name: sit: {
+        netdevs."40-${name}" = {
+          netdevConfig = {
+            Name = name;
+            Kind = "sit";
+          };
+          tunnelConfig =
+            (optionalAttrs (sit.remote != null) {
+              Remote = sit.remote;
+            }) // (optionalAttrs (sit.local != null) {
+              Local = sit.local;
+            }) // (optionalAttrs (sit.ttl != null) {
+              TTL = sit.ttl;
+            });
+        };
+        networks = mkIf (sit.dev != null) {
+          "40-${sit.dev}" = (mkMerge [ (genericNetwork (mkOverride 999)) {
+            tunnel = [ name ];
+          } ]);
+        };
+      })))
+      (mkMerge (flip mapAttrsToList cfg.vlans (name: vlan: {
+        netdevs."40-${name}" = {
+          netdevConfig = {
+            Name = name;
+            Kind = "vlan";
+          };
+          vlanConfig.Id = vlan.id;
+        };
+        networks."40-${vlan.interface}" = (mkMerge [ (genericNetwork (mkOverride 999)) {
+          vlan = [ name ];
+        } ]);
+      })))
+    ];
+
+    # We need to prefill the slaved devices with networking options
+    # This forces the network interface creator to initialize slaves.
+    networking.interfaces = listToAttrs (map (i: nameValuePair i { }) slaves);
+
+  };
+
+}
diff --git a/nixos/modules/tasks/network-interfaces.nix b/nixos/modules/tasks/network-interfaces.nix
index 6b22cf96e1d..11b641d40eb 100644
--- a/nixos/modules/tasks/network-interfaces.nix
+++ b/nixos/modules/tasks/network-interfaces.nix
@@ -11,10 +11,6 @@ let
   hasSits = cfg.sits != { };
   hasBonds = cfg.bonds != { };
 
-  # We must escape interfaces due to the systemd interpretation
-  subsystemDevice = interface:
-    "sys-subsystem-net-devices-${escapeSystemdPath interface}.device";
-
   addrOpts = v:
     assert v == 4 || v == 6;
     {
@@ -45,6 +41,16 @@ let
         description = "Name of the interface.";
       };
 
+      useDHCP = mkOption {
+        type = types.nullOr types.bool;
+        default = null;
+        description = ''
+          Whether this interface should be configured with dhcp.
+          Null implies the old behavior which depends on whether ip addresses
+          are specified or not.
+        '';
+      };
+
       ip4 = mkOption {
         default = [ ];
         example = [
@@ -203,6 +209,7 @@ in
 
     networking.hostName = mkOption {
       default = "nixos";
+      type = types.str;
       description = ''
         The name of the machine.  Leave it empty if you want to obtain
         it from a DHCP server (if using DHCP).
@@ -225,14 +232,16 @@ in
 
     networking.enableIPv6 = mkOption {
       default = true;
+      type = types.bool;
       description = ''
         Whether to enable support for IPv6.
       '';
     };
 
     networking.defaultGateway = mkOption {
-      default = "";
+      default = null;
       example = "131.211.84.1";
+      type = types.nullOr types.str;
       description = ''
         The default gateway.  It can be left empty if it is auto-detected through DHCP.
       '';
@@ -266,8 +275,9 @@ in
     };
 
     networking.domain = mkOption {
-      default = "";
+      default = null;
       example = "home";
+      type = types.nullOr types.str;
       description = ''
         The domain.  It can be left empty if it is auto-detected through DHCP.
       '';
@@ -523,6 +533,15 @@ in
       '';
     };
 
+    networking.useNetworkd = mkOption {
+      default = false;
+      type = types.bool;
+      description = ''
+        Whether we should use networkd as the network configuration backend or
+        the legacy script based system.
+      '';
+    };
+
   };
 
 
@@ -552,345 +571,17 @@ in
       # from being created.
       optionalString hasBonds "options bonding max_bonds=0";
 
-    environment.systemPackages =
-      [ pkgs.host
-        pkgs.iproute
-        pkgs.iputils
-        pkgs.nettools
-        pkgs.wirelesstools
-        pkgs.iw
-        pkgs.rfkill
-        pkgs.openresolv
-      ]
-      ++ optional (cfg.bridges != {}) pkgs.bridge_utils
-      ++ optional hasVirtuals pkgs.tunctl
-      ++ optional cfg.enableIPv6 pkgs.ndisc6;
+    boot.kernel.sysctl = {
+      "net.ipv6.conf.all.disable_ipv6" = mkDefault (!cfg.enableIPv6);
+      "net.ipv6.conf.default.disable_ipv6" = mkDefault (!cfg.enableIPv6);
+      "net.ipv4.conf.all_forwarding" = mkDefault (any (i: i.proxyARP) interfaces);
+      "net.ipv6.conf.all.forwarding" = mkDefault (any (i: i.proxyARP) interfaces);
+    } // listToAttrs (concatLists (flip map (filter (i: i.proxyARP) interfaces)
+        (i: flip map [ "4" "6" ] (v: nameValuePair "net.ipv${v}.conf.${i.name}.proxy_arp" true))
+      ));
 
     security.setuidPrograms = [ "ping" "ping6" ];
 
-    systemd.targets."network-interfaces" =
-      { description = "All Network Interfaces";
-        wantedBy = [ "network.target" ];
-        unitConfig.X-StopOnReconfiguration = true;
-      };
-
-    systemd.services =
-      let
-
-        networkSetup =
-          { description = "Networking Setup";
-
-            after = [ "network-interfaces.target" ];
-            before = [ "network.target" "network-online.target" ];
-            wantedBy = [ "network.target" "network-online.target" ];
-
-            unitConfig.ConditionCapability = "CAP_NET_ADMIN";
-
-            path = [ pkgs.iproute ];
-
-            serviceConfig.Type = "oneshot";
-            serviceConfig.RemainAfterExit = true;
-
-            script =
-              (optionalString (!config.services.resolved.enable) ''
-                # Set the static DNS configuration, if given.
-                ${pkgs.openresolv}/sbin/resolvconf -m 1 -a static <<EOF
-                ${optionalString (cfg.nameservers != [] && cfg.domain != "") ''
-                  domain ${cfg.domain}
-                ''}
-                ${optionalString (cfg.search != []) ("search " + concatStringsSep " " cfg.search)}
-                ${flip concatMapStrings cfg.nameservers (ns: ''
-                  nameserver ${ns}
-                '')}
-                EOF
-              '') + ''
-                # Disable or enable IPv6.
-                ${optionalString (!config.boot.isContainer) ''
-                  if [ -e /proc/sys/net/ipv6/conf/all/disable_ipv6 ]; then
-                    echo ${if cfg.enableIPv6 then "0" else "1"} > /proc/sys/net/ipv6/conf/all/disable_ipv6
-                  fi
-                ''}
-
-                # Set the default gateway.
-                ${optionalString (cfg.defaultGateway != "") ''
-                  # FIXME: get rid of "|| true" (necessary to make it idempotent).
-                  ip route add default via "${cfg.defaultGateway}" ${
-                    optionalString (cfg.defaultGatewayWindowSize != null)
-                      "window ${cfg.defaultGatewayWindowSize}"} || true
-                ''}
-
-                # Turn on forwarding if any interface has enabled proxy_arp.
-                ${optionalString (any (i: i.proxyARP) interfaces) ''
-                  echo 1 > /proc/sys/net/ipv4/ip_forward
-                ''}
-
-                # Run any user-specified commands.
-                ${cfg.localCommands}
-              '';
-          };
-
-        # For each interface <foo>, create a job ‘<foo>-cfg.service"
-        # that performs static configuration.  It has a "wants"
-        # dependency on ‘<foo>.service’, which is supposed to create
-        # the interface and need not exist (i.e. for hardware
-        # interfaces).  It has a binds-to dependency on the actual
-        # network device, so it only gets started after the interface
-        # has appeared, and it's stopped when the interface
-        # disappears.
-        configureInterface = i:
-          let
-            ips = i.ip4 ++ optionals cfg.enableIPv6 i.ip6
-              ++ optional (i.ipAddress != null) {
-                address = i.ipAddress;
-                prefixLength = i.prefixLength;
-              } ++ optional (cfg.enableIPv6 && i.ipv6Address != null) {
-                address = i.ipv6Address;
-                prefixLength = i.ipv6PrefixLength;
-              };
-          in
-          nameValuePair "${i.name}-cfg"
-          { description = "Configuration of ${i.name}";
-            wantedBy = [ "network-interfaces.target" ];
-            bindsTo = [ (subsystemDevice i.name) ];
-            after = [ (subsystemDevice i.name) ];
-            serviceConfig.Type = "oneshot";
-            serviceConfig.RemainAfterExit = true;
-            path = [ pkgs.iproute pkgs.gawk ];
-            script =
-              ''
-                echo "bringing up interface..."
-                ip link set "${i.name}" up
-              ''
-              + optionalString (i.macAddress != null)
-                ''
-                  echo "setting MAC address to ${i.macAddress}..."
-                  ip link set "${i.name}" address "${i.macAddress}"
-                ''
-              + optionalString (i.mtu != null)
-                ''
-                  echo "setting MTU to ${toString i.mtu}..."
-                  ip link set "${i.name}" mtu "${toString i.mtu}"
-                ''
-
-              # Ip Setup
-              +
-                ''
-                  curIps=$(ip -o a show dev "${i.name}" | awk '{print $4}')
-                  # Only do an add if it's necessary.  This is
-                  # useful when the Nix store is accessed via this
-                  # interface (e.g. in a QEMU VM test).
-                ''
-              + flip concatMapStrings (ips) (ip:
-                let
-                  address = "${ip.address}/${toString ip.prefixLength}";
-                in
-                ''
-                  echo "checking ip ${address}..."
-                  if ! echo "$curIps" | grep "${address}" >/dev/null 2>&1; then
-                    if out=$(ip addr add "${address}" dev "${i.name}" 2>&1); then
-                      echo "added ip ${address}..."
-                      restart_network_setup=true
-                    elif ! echo "$out" | grep "File exists" >/dev/null 2>&1; then
-                      echo "failed to add ${address}"
-                      exit 1
-                    fi
-                  fi
-                '')
-              + optionalString (ips != [ ])
-                ''
-                  if [ restart_network_setup = true ]; then
-                    # Ensure that the default gateway remains set.
-                    # (Flushing this interface may have removed it.)
-                    ${config.systemd.package}/bin/systemctl try-restart --no-block network-setup.service
-                  fi
-                  ${config.systemd.package}/bin/systemctl start ip-up.target
-                ''
-              + optionalString i.proxyARP
-                ''
-                  echo 1 > /proc/sys/net/ipv4/conf/${i.name}/proxy_arp
-                ''
-              + optionalString (i.proxyARP && cfg.enableIPv6)
-                ''
-                  echo 1 > /proc/sys/net/ipv6/conf/${i.name}/proxy_ndp
-                '';
-            preStop =
-              ''
-                echo "releasing configured ip's..."
-              ''
-              + flip concatMapStrings (ips) (ip:
-                let
-                  address = "${ip.address}/${toString ip.prefixLength}";
-                in
-                ''
-                  echo -n "Deleting ${address}..."
-                  ip addr del "${address}" dev "${i.name}" >/dev/null 2>&1 || echo -n " Failed"
-                  echo ""
-                '');
-          };
-
-        createTunDevice = i: nameValuePair "${i.name}-netdev"
-          { description = "Virtual Network Interface ${i.name}";
-            requires = [ "dev-net-tun.device" ];
-            after = [ "dev-net-tun.device" ];
-            wantedBy = [ "network.target" (subsystemDevice i.name) ];
-            path = [ pkgs.iproute ];
-            serviceConfig = {
-              Type = "oneshot";
-              RemainAfterExit = true;
-            };
-            script = ''
-              ip tuntap add dev "${i.name}" \
-              ${optionalString (i.virtualType != null) "mode ${i.virtualType}"} \
-              user "${i.virtualOwner}"
-            '';
-            postStop = ''
-              ip link del ${i.name}
-            '';
-          };
-
-        createBridgeDevice = n: v: nameValuePair "${n}-netdev"
-          (let
-            deps = map subsystemDevice v.interfaces;
-          in
-          { description = "Bridge Interface ${n}";
-            wantedBy = [ "network.target" (subsystemDevice n) ];
-            bindsTo = deps;
-            after = deps;
-            serviceConfig.Type = "oneshot";
-            serviceConfig.RemainAfterExit = true;
-            path = [ pkgs.bridge_utils pkgs.iproute ];
-            script =
-              ''
-                # Remove Dead Interfaces
-                ip link show "${n}" >/dev/null 2>&1 && ip link delete "${n}"
-
-                brctl addbr "${n}"
-
-                # Set bridge's hello time to 0 to avoid startup delays.
-                brctl setfd "${n}" 0
-
-                ${flip concatMapStrings v.interfaces (i: ''
-                  brctl addif "${n}" "${i}"
-                  ip link set "${i}" up
-                  ip addr flush dev "${i}"
-
-                  echo "bringing up network device ${n}..."
-                  ip link set "${n}" up
-                '')}
-
-                # !!! Should delete (brctl delif) any interfaces that
-                # no longer belong to the bridge.
-              '';
-            postStop =
-              ''
-                ip link set "${n}" down
-                brctl delbr "${n}"
-              '';
-          });
-
-        createBondDevice = n: v: nameValuePair "${n}-netdev"
-          (let
-            deps = map subsystemDevice v.interfaces;
-          in
-          { description = "Bond Interface ${n}";
-            wantedBy = [ "network.target" (subsystemDevice n) ];
-            bindsTo = deps;
-            after = deps;
-            before = [ "${n}-cfg.service" ];
-            serviceConfig.Type = "oneshot";
-            serviceConfig.RemainAfterExit = true;
-            path = [ pkgs.ifenslave pkgs.iproute ];
-            script = ''
-              ip link add name "${n}" type bond
-
-              # !!! There must be a better way to wait for the interface
-              while [ ! -d /sys/class/net/${n} ]; do sleep 0.1; done;
-
-              # Ensure the link is down so that we can set options
-              ip link set "${n}" down
-
-              # Set the miimon and mode options
-              ${optionalString (v.miimon != null)
-                "echo \"${toString v.miimon}\" >/sys/class/net/${n}/bonding/miimon"}
-              ${optionalString (v.mode != null)
-                "echo \"${v.mode}\" >/sys/class/net/${n}/bonding/mode"}
-              ${optionalString (v.lacp_rate != null)
-                "echo \"${v.lacp_rate}\" >/sys/class/net/${n}/bonding/lacp_rate"}
-              ${optionalString (v.xmit_hash_policy != null)
-                "echo \"${v.xmit_hash_policy}\" >/sys/class/net/${n}/bonding/xmit_hash_policy"}
-
-              # Bring up the bond and enslave the specified interfaces
-              ip link set "${n}" up
-              ${flip concatMapStrings v.interfaces (i: ''
-                ifenslave "${n}" "${i}"
-              '')}
-            '';
-            postStop = ''
-              ${flip concatMapStrings v.interfaces (i: ''
-                ifenslave -d "${n}" "${i}" >/dev/null 2>&1 || true
-              '')}
-              ip link set "${n}" down >/dev/null 2>&1 || true
-              ip link del "${n}" >/dev/null 2>&1 || true
-            '';
-          });
-
-        createSitDevice = n: v: nameValuePair "${n}-netdev"
-          (let
-            deps = optional (v.dev != null) (subsystemDevice v.dev);
-          in
-          { description = "6-to-4 Tunnel Interface ${n}";
-            wantedBy = [ "network.target" (subsystemDevice n) ];
-            bindsTo = deps;
-            after = deps;
-            serviceConfig.Type = "oneshot";
-            serviceConfig.RemainAfterExit = true;
-            path = [ pkgs.iproute ];
-            script = ''
-              # Remove Dead Interfaces
-              ip link show "${n}" >/dev/null 2>&1 && ip link delete "${n}"
-              ip link add name "${n}" type sit \
-                ${optionalString (v.remote != null) "remote \"${v.remote}\""} \
-                ${optionalString (v.local != null) "local \"${v.local}\""} \
-                ${optionalString (v.ttl != null) "ttl ${toString v.ttl}"} \
-                ${optionalString (v.dev != null) "dev \"${v.dev}\""}
-              ip link set "${n}" up
-            '';
-            postStop = ''
-              ip link delete "${n}"
-            '';
-          });
-
-        createVlanDevice = n: v: nameValuePair "${n}-netdev"
-          (let
-            deps = [ (subsystemDevice v.interface) ];
-          in
-          { description = "Vlan Interface ${n}";
-            wantedBy = [ "network.target" (subsystemDevice n) ];
-            bindsTo = deps;
-            after = deps;
-            serviceConfig.Type = "oneshot";
-            serviceConfig.RemainAfterExit = true;
-            path = [ pkgs.iproute ];
-            script = ''
-              # Remove Dead Interfaces
-              ip link show "${n}" >/dev/null 2>&1 && ip link delete "${n}"
-              ip link add link "${v.interface}" name "${n}" type vlan id "${toString v.id}"
-              ip link set "${n}" up
-            '';
-            postStop = ''
-              ip link delete "${n}"
-            '';
-          });
-
-      in listToAttrs (
-           map configureInterface interfaces ++
-           map createTunDevice (filter (i: i.virtual) interfaces))
-         // mapAttrs' createBridgeDevice cfg.bridges
-         // mapAttrs' createBondDevice cfg.bonds
-         // mapAttrs' createSitDevice cfg.sits
-         // mapAttrs' createVlanDevice cfg.vlans
-         // { "network-setup" = networkSetup; };
-
     # Set the host and domain names in the activation script.  Don't
     # clear it if it's not configured in the NixOS configuration,
     # since it may have been set by dhcpcd in the meantime.
@@ -899,7 +590,7 @@ in
         hostname "${cfg.hostName}"
       '';
     system.activationScripts.domain =
-      optionalString (cfg.domain != "") ''
+      optionalString (cfg.domain != null) ''
         domainname "${cfg.domain}"
       '';
 
@@ -918,11 +609,33 @@ in
         }
       ];
 
-    services.udev.extraRules =
-      ''
-        KERNEL=="tun", TAG+="systemd"
-      '';
+    environment.systemPackages =
+      [ pkgs.host
+        pkgs.iproute
+        pkgs.iputils
+        pkgs.nettools
+        pkgs.wirelesstools
+        pkgs.iw
+        pkgs.rfkill
+        pkgs.openresolv
+      ]
+      ++ optional (cfg.bridges != {}) pkgs.bridge_utils
+      ++ optional hasVirtuals pkgs.tunctl
+      ++ optional cfg.enableIPv6 pkgs.ndisc6;
 
+    systemd.services.network-local-commands = {
+      description = "Extra networking commands.";
+      before = [ "network.target" "network-online.target" ];
+      wantedBy = [ "network.target" "network-online.target" ];
+      unitConfig.ConditionCapability = "CAP_NET_ADMIN";
+      path = [ pkgs.iproute ];
+      serviceConfig.Type = "oneshot";
+      serviceConfig.RemainAfterExit = true;
+      script = ''
+        # Run any user-specified commands.
+        ${cfg.localCommands}
+      '';
+    };
   };
 
 }