{ config, lib, ... }:
{
  options.mj.services.friendlyport = with lib.types; {
    ports = lib.mkOption {
      type = listOf (submodule {
        options = {
          subnets = lib.mkOption { type = listOf str; };
          tcp = lib.mkOption {
            type = listOf int;
            default = [ ];
          };
          udp = lib.mkOption {
            type = listOf int;
            default = [ ];
          };
        };
      });
    };
  };

  config =
    let
      inherit (config.mj.services.friendlyport) ports;

      mkAdd =
        proto: subnets: ints:
        let
          subnetsS = builtins.concatStringsSep "," subnets;
          intsS = builtins.concatStringsSep "," (map builtins.toString ints);
        in
        if builtins.length ints == 0 then
          ""
        else
          "iptables -A INPUT -p ${proto} --match multiport --dports ${intsS} --source ${subnetsS} -j ACCEPT";

      startTCP = map (attr: mkAdd "tcp" attr.subnets attr.tcp) ports;
      startUDP = map (attr: mkAdd "udp" attr.subnets attr.udp) ports;

      # TODO: when stopping the firewall, systemd uses the old ports. So this is a two-phase process.
      # How to stop the old one and start the new one?
      mkDel =
        proto: subnets: ints:
        let
          subnetsS = builtins.concatStringsSep "," subnets;
          intsS = builtins.concatStringsSep "," (map builtins.toString ints);
        in
        if builtins.length ints == 0 then
          ""
        else
          "iptables -D INPUT -p ${proto} --match multiport --dports ${intsS} --source ${subnetsS} -j ACCEPT || :";

      stopTCP = map (attr: mkDel "tcp" attr.subnets attr.tcp) ports;
      stopUDP = map (attr: mkDel "udp" attr.subnets attr.udp) ports;
    in
    {
      networking.firewall.extraCommands = lib.concatLines (startTCP ++ startUDP);
      networking.firewall.extraStopCommands = lib.concatLines (stopTCP ++ stopUDP);
    };
}