diff --git a/type/__ipset/explorer/content b/type/__ipset/explorer/content
new file mode 100755
index 0000000..87f6b51
--- /dev/null
+++ b/type/__ipset/explorer/content
@@ -0,0 +1,26 @@
+#!/bin/sh
+#
+# 2021 Mesar Hameed (mesar.hameed at gmail.com)
+#
+# This file is part of cdist.
+#
+# cdist is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# cdist is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with cdist. If not, see .
+#
+
+name="$__object_id"
+if ipset -t list | grep -qFx "Name: $name"; then
+ ipset list "$name" | sed '0,/^Members:/d'
+else
+ echo "x_missing_x"
+fi
diff --git a/type/__ipset/explorer/state b/type/__ipset/explorer/state
new file mode 100755
index 0000000..9ece28d
--- /dev/null
+++ b/type/__ipset/explorer/state
@@ -0,0 +1,26 @@
+#!/bin/sh
+#
+# 2021 Mesar Hameed (mesar.hameed at gmail.com)
+#
+# This file is part of cdist.
+#
+# cdist is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# cdist is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with cdist. If not, see .
+#
+
+name="$__object_id"
+if ipset -t list "$name" >/dev/null; then
+ echo "present"
+else
+ echo "absent"
+fi
diff --git a/type/__ipset/explorer/type b/type/__ipset/explorer/type
new file mode 100755
index 0000000..9413cda
--- /dev/null
+++ b/type/__ipset/explorer/type
@@ -0,0 +1,26 @@
+#!/bin/sh
+#
+# 2021 Mesar Hameed (mesar.hameed at gmail.com)
+#
+# This file is part of cdist.
+#
+# cdist is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# cdist is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with cdist. If not, see .
+#
+
+name="$__object_id"
+if ipset -t list | grep -qFx "Name: $name"; then
+ ipset -t list "$name" | grep "^Type: " | awk '{print $2}'
+else
+ echo "x_missing_x"
+fi
diff --git a/type/__ipset/files/ipset-persistent b/type/__ipset/files/ipset-persistent
new file mode 100755
index 0000000..e812c30
--- /dev/null
+++ b/type/__ipset/files/ipset-persistent
@@ -0,0 +1,48 @@
+#!/bin/sh
+#
+# 2021 Mesar Hameed (mesar.hameed at gmail.com)
+#
+# This file is part of cdist.
+#
+# cdist is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# cdist is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with cdist. If not, see .
+#
+#
+### BEGIN INIT INFO
+# Provides: ipset
+# Required-Start: $local_fs $remote_fs
+# Required-Stop: $local_fs $remote_fs
+# X-Start-Before: iptables
+# Default-Start: 2 3 4 5
+# Default-Stop: 0 1 6
+# Short-Description: Create ipset lists before iptables rules require them
+# Description: Applies lists found in /etc/ipset.d/*.saved
+# and saves/restores previous status
+### END INIT INFO
+
+case $1 in
+ start)
+ # Restore previous state:
+ /usr/local/bin/ipsets-restore
+ ;;
+ stop)
+ # Save current state before exiting:
+ /usr/local/bin/ipsets-save
+ ;;
+ restart)
+ "$0" stop && "$0" start
+ ;;
+ reset)
+ ipset flush
+ ;;
+esac
diff --git a/type/__ipset/files/ipsets-restore b/type/__ipset/files/ipsets-restore
new file mode 100755
index 0000000..30df3a1
--- /dev/null
+++ b/type/__ipset/files/ipsets-restore
@@ -0,0 +1,28 @@
+#!/bin/sh
+#
+# 2021 Mesar Hameed (mesar.hameed at gmail.com)
+#
+# This file is part of cdist.
+#
+# cdist is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# cdist is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with cdist. If not, see .
+#
+
+mkdir -p /etc/ipset.d/
+if [ -n "$1" ]; then
+ ipset -! restore < "/etc/ipset.d/$1"
+else
+find /etc/ipset.d/ -iname "*.saved" | while read s; do
+ ipset -! restore <$s
+done
+fi
diff --git a/type/__ipset/files/ipsets-save b/type/__ipset/files/ipsets-save
new file mode 100755
index 0000000..9f5a9f3
--- /dev/null
+++ b/type/__ipset/files/ipsets-save
@@ -0,0 +1,28 @@
+#!/bin/sh
+#
+# 2021 Mesar Hameed (mesar.hameed at gmail.com)
+#
+# This file is part of cdist.
+#
+# cdist is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# cdist is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with cdist. If not, see .
+#
+
+mkdir -p /etc/ipset.d/
+if [ -n "$1" ]; then
+ ipset save "$1" > "/etc/ipset.d/${1}.saved"
+else
+ipset -t list | grep "^Name:" | awk '{print $2}' | while read s; do
+ ipset save $s > /etc/ipset.d/$s.saved
+done
+fi
diff --git a/type/__ipset/gencode-remote b/type/__ipset/gencode-remote
new file mode 100755
index 0000000..427bc7e
--- /dev/null
+++ b/type/__ipset/gencode-remote
@@ -0,0 +1,103 @@
+#!/bin/sh
+#
+# 2021 Mesar Hameed (mesar.hameed at gmail.com)
+#
+# This file is part of cdist.
+#
+# cdist is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# cdist is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with cdist. If not, see .
+#
+
+e="$__object/explorer"
+p="$__object/parameter"
+name="$__object_id"
+type_is="$(cat "$e/type")"
+type_should="$(cat "$p/type")"
+state_is="$(cat "$e/state")"
+state_should="$(cat "$p/state")"
+mode="$(cat "$p/mode")"
+needToSave=0
+
+if [ "$mode" != "strict" ] && [ "$mode" != "relaxed" ]; then
+ echo "ERROR: --mode needs to be either strict or relaxed" >&2
+ exit 1
+elif [ "$mode" = "strict" ] && [ -f "$p/ensure-absent" ]; then
+ echo "ERROR: --mode strict automatically removes elements that are not in the ensure-present list. --ensure-absent is ment to be used with --mode relaxed" >&2
+ exit 1
+elif [ "$state_should" = "absent" ] && \( [ -f "$p/ensure-present" ] || [ -f "$p/ensure-absent" ] \); then
+ echo "ERROR: ipset state absent is incompatible with --ensure-present or --ensure-absent" >&2
+ exit 1
+fi
+
+
+case $state_should in
+ present)
+ if [ "$state_is" = "absent" ]; then
+ echo ipset create "$name" "$type_should"
+ needToSave=1
+ elif [ "$state_is" = "present" ] && [ "$type_is" != "$type_should" ]; then
+ echo ipset destroy "$name"
+ echo "rm \"/etc/ipset.d/${name}.saved\" || true"
+ echo ipset create "$name" "$type_should"
+ needToSave=1
+ fi
+ ;;
+ absent)
+ if [ "$state_is" = "present" ]; then
+ echo ipset destroy "$name"
+ echo "rm \"/etc/ipset.d/${name}.saved\" || true"
+ fi
+ ;;
+ *)
+ echo "Unknown state: $state_should" >&2
+ exit 1
+ ;;
+esac
+
+if [ "$state_should" = "present" ]; then
+ if [ -f "$p/ensure-present" ]; then
+ # add elements that we want to ensure are present but are not currently in the set:
+ while read -r value; do
+ if ! grep -qFx "$value" "$e/content"; then
+ echo "ipset -! add $name $value"
+ needToSave=1
+ fi
+ done < "$p/ensure-present"
+
+ # if strict mode is required, then remove any other elements in the set that that are not specified by ensure-present
+ if [ "$mode" = "strict" ]; then
+ while read -r value; do
+ if [ "$value" = "x_missing_x" ]; then continue; fi
+ if ! grep -qFx "$value" "$p/ensure-present"; then
+ echo "ipset -! del $name $value"
+ needToSave=1
+ fi
+ done < "$e/content"
+ fi
+ fi
+
+ if [ -f "$p/ensure-absent" ]; then
+ # ensure-absent makes sure we do not accidentally block particular elements
+ # if they are in the set then remove.
+ while read -r value; do
+ if grep -qFx "$value" "$e/content"; then
+ echo "ipset -! del $name $value"
+ needToSave=1
+ fi
+ done < "$p/ensure-absent"
+ fi
+fi
+
+if [ $needToSave -ne 0 ]; then
+ echo /usr/local/bin/ipsets-save "$name"
+fi
diff --git a/type/__ipset/man.rst b/type/__ipset/man.rst
new file mode 100644
index 0000000..7fc473e
--- /dev/null
+++ b/type/__ipset/man.rst
@@ -0,0 +1,81 @@
+cdist-type__ipset(7)
+====================
+
+NAME
+----
+cdist-type__ipset - Manage ipset sets
+
+DESCRIPTION
+-----------
+Making use of ipset sets in iptable rules can make your rules more expressive, maintainable and efficient.
+
+REQUIRED PARAMETERS
+-------------------
+type
+ One of the supported ipset set types, for a full list see:
+
+ ``ipset help``
+
+OPTIONAL PARAMETERS
+-------------------
+ensure-present
+ The entry that must exist in the given set.
+
+ Can be used multiple times.
+
+ensure-absent
+ The entry that must not exist in the given set.
+
+ Can be used multiple times.
+
+mode
+ Can be:
+
+ - ``strict``: ensure only the specified elements in the set are present.
+ - ``relaxed``: ensure that the elements specified are in the set, but allow for other elements to co-exist.
+
+state
+ Can be:
+
+ - ``present``: ensure that the given set exists.
+ - ``absent``: ensure the given set doesn't exist.
+
+BOOLEAN PARAMETERS
+------------------
+None.
+
+EXAMPLES
+--------
+
+.. code-block:: sh
+
+ # Make sure a set with the given name/type exists:
+ __ipset testset1 --type hash:ip
+
+ # ensure only the given ip address is in the allowed vnc set:
+ __ipset allowed_vnc --type hash:ip --ensure-present 10.1.1.1
+
+ # Ensure allowed_ssh_clients contains at least the specified private range:
+ __ipset allowed_ssh_hosts --type hash:net --mode relaxed \
+ --ensure-present 192.168.0.0/24 --ensure-present 10.0.0.0/8
+
+ # Make sure host is not on the blocked list:
+ __ipset blocked_hosts --type hash:ip --mode relaxed \
+ --ensure-absent 1.2.3.4
+
+
+
+SEE ALSO
+--------
+:strong:`cdist-type__iptables_rule`\ (7), :strong:`iptables`\ (8)
+
+AUTHORS
+-------
+Mesar Hameed
+
+COPYING
+-------
+Copyright \(C) 2021 Mesar Hameed. You can redistribute it
+and/or modify it under the terms of the GNU General Public License as
+published by the Free Software Foundation, either version 3 of the
+License, or (at your option) any later version.
diff --git a/type/__ipset/manifest b/type/__ipset/manifest
new file mode 100755
index 0000000..4f075f8
--- /dev/null
+++ b/type/__ipset/manifest
@@ -0,0 +1,42 @@
+#!/bin/sh -e
+#
+# 2021 Mesar Hameed (mesar.hameed at gmail.com)
+#
+# This file is part of cdist.
+#
+# cdist is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# cdist is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with cdist. If not, see .
+#
+
+os=$(cat "$__global/explorer/os")
+case "$os" in
+ debian|ubuntu)
+ :
+ ;;
+ *)
+ echo "OS $os currently not supported" >&2
+ exit 1
+ ;;
+esac
+
+export CDIST_ORDER_DEPENDENCY=on
+
+# install packages
+__package ipset
+
+__file /etc/init.d/ipset-persistent --mode 0755 --source "${__type}/files/ipset-persistent"
+__file /usr/local/bin/ipsets-restore --mode 0755 --source "${__type}/files/ipsets-restore"
+__file /usr/local/bin/ipsets-save --mode 0755 --source "${__type}/files/ipsets-save"
+__systemd_unit ipset-persistent --enablement-state enabled --restart
+
+unset CDIST_ORDER_DEPENDENCY
diff --git a/type/__ipset/parameter/default/mode b/type/__ipset/parameter/default/mode
new file mode 100644
index 0000000..d8d945f
--- /dev/null
+++ b/type/__ipset/parameter/default/mode
@@ -0,0 +1 @@
+strict
diff --git a/type/__ipset/parameter/default/state b/type/__ipset/parameter/default/state
new file mode 100644
index 0000000..e7f6134
--- /dev/null
+++ b/type/__ipset/parameter/default/state
@@ -0,0 +1 @@
+present
diff --git a/type/__ipset/parameter/optional b/type/__ipset/parameter/optional
new file mode 100644
index 0000000..bc8c425
--- /dev/null
+++ b/type/__ipset/parameter/optional
@@ -0,0 +1,2 @@
+state
+mode
diff --git a/type/__ipset/parameter/optional_multiple b/type/__ipset/parameter/optional_multiple
new file mode 100644
index 0000000..7592623
--- /dev/null
+++ b/type/__ipset/parameter/optional_multiple
@@ -0,0 +1,2 @@
+ensure-present
+ensure-absent
diff --git a/type/__ipset/parameter/required b/type/__ipset/parameter/required
new file mode 100644
index 0000000..aa80e64
--- /dev/null
+++ b/type/__ipset/parameter/required
@@ -0,0 +1 @@
+type