diff --git a/configure.ac b/configure.ac
index ab744a105d252114acb1495a5c790809d201991f..10e7970da289b3ec8e2d07ff5df498aef1c881fb 100644
--- a/configure.ac
+++ b/configure.ac
@@ -531,20 +531,6 @@ AS_IF([test "x$with_upnp" = "xyes"],
          AC_DEFINE([HAVE_LIBUPNP], 0, [Define if you have libupnp])])
       ])
 
-# LIBNATPMP
-dnl check for libnatpmp
-AC_ARG_WITH([natpmp], [AS_HELP_STRING([--without-natpmp],
-            [disable support for NAT-PMP])], [], [with_natpmp=yes])
-
-AS_IF([test "x$with_natpmp" != xno],
-        [AC_CHECK_HEADER([natpmp.h],
-            AC_CHECK_LIB([natpmp], [initnatpmp], [], [with_natpmp=no]),
-            [AC_MSG_WARN([Unable to find the libnatpmp headers (you may need to install the dev package). You may use --without-natpmp to compile without NAT-PMP support.]);
-             with_natpmp=no])
-        ],[])
-
-AC_DEFINE_UNQUOTED([HAVE_LIBNATPMP], `if test "x$with_natpmp" != xno; then echo 1; else echo 0; fi`, [Define if you have libnatpmp])
-
 AC_DEFINE_UNQUOTED([HAVE_SHM], `if test -z "${HAVE_LINUX_TRUE}" && test -z "${HAVE_ANDROID_FALSE}" ; then echo 1; else echo 0; fi`,
 	[Define if you have shared memory support])
 
diff --git a/contrib/src/natpmp/SHA512SUMS b/contrib/src/natpmp/SHA512SUMS
deleted file mode 100644
index 956a4e3cd0e0b5c63bfb954a67de4d1e6f9ff8d0..0000000000000000000000000000000000000000
--- a/contrib/src/natpmp/SHA512SUMS
+++ /dev/null
@@ -1 +0,0 @@
-e50b1f68ce9254bb2f068ddc37417a3c417b80f7b3fb3d84e3e9af4a144d89e204ab993b54c01657335e855d0124a8fcbbf96ce78db7b9ae0b03b6eb79de2e09  libnatpmp-20150609.tar.gz
diff --git a/contrib/src/natpmp/rules.mak b/contrib/src/natpmp/rules.mak
deleted file mode 100644
index 49ddcb006cb57005560838125889afe126553c98..0000000000000000000000000000000000000000
--- a/contrib/src/natpmp/rules.mak
+++ /dev/null
@@ -1,21 +0,0 @@
-# libnatpmp
-NATPMP_VERSION := 20150609
-NATPMP_URL := http://miniupnp.free.fr/files/download.php?file=libnatpmp-$(NATPMP_VERSION).tar.gz
-
-PKGS += natpmp
-ifeq ($(call need_pkg,'libnatpmp'),)
-PKGS_FOUND += natpmp
-endif
-
-$(TARBALLS)/libnatpmp-$(NATPMP_VERSION).tar.gz:
-	$(call download,$(NATPMP_URL))
-
-.sum-natpmp: libnatpmp-$(NATPMP_VERSION).tar.gz
-
-natpmp: libnatpmp-$(NATPMP_VERSION).tar.gz .sum-natpmp
-	$(UNPACK)
-	$(MOVE)
-
-.natpmp: natpmp
-	cd $< && $(MAKE) INSTALLPREFIX="$(PREFIX)" install
-	touch $@
diff --git a/src/upnp/upnp_context.cpp b/src/upnp/upnp_context.cpp
index ace784f2c440977da2945b99a18e25c2b8f80260..8c95f4ab116e0bf8b72c18f054fcc4a01b9e7a4c 100644
--- a/src/upnp/upnp_context.cpp
+++ b/src/upnp/upnp_context.cpp
@@ -38,10 +38,6 @@
 #include <upnp/upnptools.h>
 #endif
 
-#if HAVE_LIBNATPMP
-#include <natpmp.h>
-#endif
-
 #include "logger.h"
 #include "ip_utils.h"
 #include "upnp_igd.h"
@@ -65,20 +61,6 @@ getUPnPContext()
     return context;
 }
 
-/* UPnP error codes */
-constexpr static int INVALID_ARGS = 402;
-constexpr static int ARRAY_IDX_INVALID = 713;
-constexpr static int CONFLICT_IN_MAPPING = 718;
-
-/* max number of times to retry mapping if it fails due to conflict;
- * there isn't much logic in picking this number... ideally not many ports should
- * be mapped in a system, so a few number of random port retries should work;
- * a high number of retries would indicate there might be some kind of bug or else
- * incompatibility with the router; we use it to prevent an infinite loop of
- * retrying to map the entry
- */
-constexpr static unsigned MAX_RETRIES = 20;
-
 #if HAVE_LIBUPNP
 
 /* UPnP IGD definitions */
@@ -89,10 +71,23 @@ constexpr static const char * UPNP_WANCON_DEVICE = "urn:schemas-upnp-org:device:
 constexpr static const char * UPNP_WANIP_SERVICE = "urn:schemas-upnp-org:service:WANIPConnection:1";
 constexpr static const char * UPNP_WANPPP_SERVICE = "urn:schemas-upnp-org:service:WANPPPConnection:1";
 
+/* UPnP error codes */
+constexpr static int          INVALID_ARGS = 402;
 constexpr static const char * INVALID_ARGS_STR = "402";
+constexpr static int          ARRAY_IDX_INVALID = 713;
 constexpr static const char * ARRAY_IDX_INVALID_STR = "713";
+constexpr static int          CONFLICT_IN_MAPPING = 718;
 constexpr static const char * CONFLICT_IN_MAPPING_STR = "718";
 
+/* max number of times to retry mapping if it fails due to conflict;
+ * there isn't much logic in picking this number... ideally not many ports should
+ * be mapped in a system, so a few number of random port retries should work;
+ * a high number of retries would indicate there might be some kind of bug or else
+ * incompatibility with the router; we use it to prevent an infinite loop of
+ * retrying to map the entry
+ */
+constexpr static unsigned MAX_RETRIES = 20;
+
 /*
  * Local prototypes
  */
@@ -101,68 +96,18 @@ static std::string get_first_doc_item(IXML_Document*, const char*);
 static std::string get_first_element_item(IXML_Element*, const char*);
 static void checkResponseError(IXML_Document*);
 
-#else
-
-constexpr static int UPNP_E_SUCCESS = 0;
+static int
+cp_callback(Upnp_EventType event_type, void* event, void* user_data)
+{
+    if (auto upnpContext = static_cast<UPnPContext*>(user_data))
+        return upnpContext->handleUPnPEvents(event_type, event);
 
-#endif // HAVE_LIBUPNP
+    RING_WARN("UPnP callback without UPnPContext");
+    return 0;
+}
 
 UPnPContext::UPnPContext()
 {
-#if HAVE_LIBNATPMP
-    pmpThread_ = std::thread([this]() {
-        PMPIGD* pmp_igd = new PMPIGD();
-        natpmp_t natpmp;
-        bool found {false};
-
-        while (pmpRun_) {
-            if (initnatpmp(&natpmp, 0, 0) < 0) {
-                RING_ERR("NAT-PMP: can't initialize libnatpmp");
-                std::unique_lock<std::mutex> lk(pmpMutex_);
-                pmpCv_.wait_for(lk, std::chrono::minutes(1));
-            } else {
-                RING_DBG("NAT-PMP: initialized");
-                break;
-            }
-        }
-
-        while (pmpRun_) {
-            std::unique_lock<std::mutex> lk(pmpMutex_);
-            pmpCv_.wait_until(lk, pmp_igd->getRenewalTime(), [&] {
-                return not pmpRun_ or pmp_igd->getRenewalTime() <= clock::now();
-            });
-            if (not pmpRun_) break;
-
-            auto now = clock::now();
-
-            if (pmp_igd->renewal_ < now) {
-                PMPsearchForIGD(pmp_igd, natpmp, found);
-            }
-            if (found) {
-                if (pmp_igd->clearAll_) {
-                    PMPdeleteAllPortMapping(*pmp_igd, natpmp, NATPMP_PROTOCOL_UDP);
-                    PMPdeleteAllPortMapping(*pmp_igd, natpmp, NATPMP_PROTOCOL_TCP);
-                    pmp_igd->clearAll_ = false;
-                    pmp_igd->toRemove_.clear();
-                } else if (not pmp_igd->toRemove_.empty()) {
-                    for (auto& m : pmp_igd->toRemove_)
-                        PMPaddPortMapping(*pmp_igd, natpmp, m, true);
-                    pmp_igd->toRemove_.clear();
-                }
-                auto mapping = pmp_igd->getNextMappingToRenew();
-                if (mapping and mapping->renewal_ < now)
-                    PMPaddPortMapping(*pmp_igd, natpmp, *mapping);
-            }
-        }
-        if (not found) delete pmp_igd;
-        closenatpmp(&natpmp);
-        RING_DBG("NAT-PMP: ended");
-    });
-    clientRegistered_ = true;
-#endif
-
-#if HAVE_LIBUPNP
-
     int upnp_err;
     char* ip_address = nullptr;
     unsigned short port = 0;
@@ -210,39 +155,19 @@ UPnPContext::UPnPContext()
      * we will probably receive their advertisements either way
      */
     searchForIGD();
-#endif
 }
 
 UPnPContext::~UPnPContext()
 {
     /* make sure everything is unregistered, freed, and UpnpFinish() is called */
+
     {
         std::lock_guard<std::mutex> lock(validIGDMutex_);
         for( auto const &it : validIGDs_) {
-#if HAVE_LIBUPNP
-            if (auto igd = dynamic_cast<UPnPIGD*>(it.second.get()))
-                removeMappingsByLocalIPAndDescription(*igd, Mapping::UPNP_DEFAULT_MAPPING_DESCRIPTION);
-#endif
-#if HAVE_LIBNATPMP
-            if (auto pmp = dynamic_cast<PMPIGD*>(it.second.get())) {
-                {
-                    std::lock_guard<std::mutex> lk(pmpMutex_);
-                    pmp->clearAll();
-                }
-                pmpCv_.notify_all();
-            }
-#endif
+            removeMappingsByLocalIPAndDescription(it.second.get(), Mapping::UPNP_DEFAULT_MAPPING_DESCRIPTION);
         }
     }
 
-#if HAVE_LIBNATPMP
-    pmpRun_ = false;
-    pmpCv_.notify_all();
-    if (pmpThread_.joinable())
-        pmpThread_.join();
-#endif
-
-#if HAVE_LIBUPNP
     if (clientRegistered_)
         UpnpUnRegisterClient( ctrlptHandle_ );
 
@@ -252,7 +177,21 @@ UPnPContext::~UPnPContext()
 #ifndef _WIN32
     UpnpFinish();
 #endif
-#endif
+}
+
+void
+UPnPContext::searchForIGD()
+{
+    if (not clientRegistered_) {
+        RING_WARN("UPnP: Control Point not registered");
+        return;
+    }
+
+    /* send out search for both types, as some routers may possibly only reply to one */
+    UpnpSearchAsync(ctrlptHandle_, SEARCH_TIMEOUT, UPNP_ROOT_DEVICE, this);
+    UpnpSearchAsync(ctrlptHandle_, SEARCH_TIMEOUT, UPNP_IGD_DEVICE, this);
+    UpnpSearchAsync(ctrlptHandle_, SEARCH_TIMEOUT, UPNP_WANIP_SERVICE, this);
+    UpnpSearchAsync(ctrlptHandle_, SEARCH_TIMEOUT, UPNP_WANPPP_SERVICE, this);
 }
 
 bool
@@ -300,11 +239,7 @@ UPnPContext::chooseIGD_unlocked() const
 {
     if (validIGDs_.empty())
         return nullptr;
-    auto natpmp = validIGDs_.find("NATPMP");
-    if (natpmp == validIGDs_.end())
-        return validIGDs_.begin()->second.get();
-    else
-        return natpmp->second.get();
+    return validIGDs_.begin()->second.get();
 }
 
 /**
@@ -349,21 +284,11 @@ UPnPContext::addMapping(IGD* igd,
     }
 
     /* mapping doesn't exist, so try to add it */
-    RING_DBG("adding port mapping : %s", mapping.toString().c_str());
+    RING_DBG("UPnP: adding port mapping : %s", mapping.toString().c_str());
 
-#if HAVE_LIBUPNP
-    auto upnp = dynamic_cast<const UPnPIGD*>(igd);
-    if (not upnp or addPortMapping(*upnp, mapping, upnp_error))
-#endif
-    {
+    if(addPortMapping(igd, mapping, upnp_error)) {
         /* success; add it to global list */
         globalMappings->emplace(port_external, std::move(GlobalMapping{mapping}));
-#if HAVE_LIBNATPMP
-#if HAVE_LIBUPNP
-        if (not upnp)
-#endif
-            pmpCv_.notify_all();
-#endif
         return mapping;
     }
     return {};
@@ -379,17 +304,17 @@ generateRandomPort()
     /* define the range */
     static std::uniform_int_distribution<uint16_t> dist(Mapping::UPNP_PORT_MIN, Mapping::UPNP_PORT_MAX);
 
-    return dist(gen);
+    return dist(gen);;
 }
 
 /**
  * chooses a random port that is not yet used by the daemon for UPnP
  */
 uint16_t
-UPnPContext::chooseRandomPort(const IGD& igd, PortType type)
+UPnPContext::chooseRandomPort(const IGD* igd, PortType type)
 {
     auto globalMappings = type == PortType::UDP ?
-                          &igd.udpMappings : &igd.tcpMappings;
+                          &igd->udpMappings : &igd->tcpMappings;
 
     uint16_t port = generateRandomPort();
 
@@ -438,7 +363,7 @@ UPnPContext::addAnyMapping(uint16_t port_desired,
         auto iter = globalMappings->find(port_desired);
         if (iter != globalMappings->end()) {
             /* port already used, we need a unique port */
-            port_desired = chooseRandomPort(*igd, type);
+            port_desired = chooseRandomPort(igd, type);
         }
     }
 
@@ -463,7 +388,7 @@ UPnPContext::addAnyMapping(uint16_t port_desired,
         RING_DBG("UPnP: mapping failed (conflicting entry? err = %d), trying with a different port.",
                  upnp_error);
         /* TODO: make sure we don't try sellecting the same random port twice if it fails ? */
-        port_desired = chooseRandomPort(*igd, type);
+        port_desired = chooseRandomPort(igd, type);
         if (use_same_port)
             port_local = port_desired;
         mapping = addMapping(igd, port_desired, port_local, type, &upnp_error);
@@ -498,33 +423,21 @@ UPnPContext::removeMapping(const Mapping& mapping)
     auto iter = globalMappings->find(mapping.getPortExternal());
     if ( iter != globalMappings->end() ) {
         /* make sure its the same mapping */
-        GlobalMapping& global_mapping = iter->second;
-        if (mapping == global_mapping ) {
+        GlobalMapping *global_mapping = &iter->second;
+        if (mapping == *global_mapping ) {
             /* now check the users */
-            if (global_mapping.users > 1) {
+            if (global_mapping->users > 1) {
                 /* more than one user, simply decrement the number */
-                --(global_mapping.users);
+                --(global_mapping->users);
                 RING_DBG("UPnP: decrementing users of mapping: %s, %d users remaining",
-                         mapping.toString().c_str(), global_mapping.users);
+                         mapping.toString().c_str(), global_mapping->users);
             } else {
                 /* no other users, can delete */
                 RING_DBG("UPnP: removing port mapping : %s",
                          mapping.toString().c_str());
-#if HAVE_LIBUPNP
-                if (auto upnp = dynamic_cast<UPnPIGD*>(igd))
-                    deletePortMapping(*upnp,
-                                      mapping.getPortExternalStr(),
-                                      mapping.getTypeStr());
-#endif
-#if HAVE_LIBNATPMP
-                if (auto pmp = dynamic_cast<PMPIGD*>(igd)) {
-                    {
-                        std::lock_guard<std::mutex> lk(pmpMutex_);
-                        pmp->toRemove_.emplace_back(std::move(global_mapping));
-                    }
-                    pmpCv_.notify_all();
-                }
-#endif
+                deletePortMapping(igd,
+                                  mapping.getPortExternalStr(),
+                                  mapping.getTypeStr());
                 globalMappings->erase(iter);
             }
         } else {
@@ -565,117 +478,6 @@ UPnPContext::getExternalIP() const
     return {};
 }
 
-#if HAVE_LIBNATPMP
-
-void
-UPnPContext::PMPsearchForIGD(PMPIGD* pmp_igd, natpmp_t& natpmp, bool& found)
-{
-    if (sendpublicaddressrequest(&natpmp) < 0) {
-        RING_ERR("NAT-PMP: can't send request");
-        pmp_igd->renewal_ = clock::now() + std::chrono::minutes(1);
-        return;
-    }
-
-    while (pmpRun_) {
-        natpmpresp_t response;
-        std::this_thread::sleep_for(std::chrono::milliseconds(2));
-        auto r = readnatpmpresponseorretry(&natpmp, &response);
-        if (r < 0 && r != NATPMP_TRYAGAIN) {
-            RING_WARN("NAT-PMP: can't find device");
-            pmp_igd->renewal_ = clock::now() + std::chrono::minutes(5);
-            break;
-        }
-        else if (r != NATPMP_TRYAGAIN) {
-            pmp_igd->localIp = ip_utils::getLocalAddr(AF_INET);
-            pmp_igd->publicIp = IpAddr(response.pnu.publicaddress.addr);
-            if (not found) {
-                found = true;
-                RING_DBG("NAT-PMP: found new device");
-                RING_DBG("NAT-PMP: got external IP: %s", pmp_igd->publicIp.toString().c_str());
-                {
-                    std::lock_guard<std::mutex> lock(validIGDMutex_);
-                    validIGDs_.emplace("NATPMP", std::unique_ptr<IGD>(pmp_igd));
-                    validIGDCondVar_.notify_all();
-                    for (const auto& l : igdListeners_)
-                        l.second();
-                }
-            }
-            pmp_igd->renewal_ = clock::now() + std::chrono::minutes(1);
-            break;
-        }
-    }
-}
-
-void
-UPnPContext::PMPaddPortMapping(const PMPIGD& /*pmp_igd*/, natpmp_t& natpmp, GlobalMapping& mapping, bool remove) const
-{
-    if (sendnewportmappingrequest(&natpmp,
-                                  mapping.getType() == PortType::UDP ? NATPMP_PROTOCOL_UDP : NATPMP_PROTOCOL_TCP,
-                                  mapping.getPortInternal(),
-                                  mapping.getPortExternal(), remove ? 0 : 3600) < 0) {
-        RING_ERR("NAT-PMP: can't send port mapping request");
-        mapping.renewal_ = clock::now() + std::chrono::minutes(1);
-        return;
-    }
-    RING_DBG("NAT-PMP: sent port mapping %srequest", remove ? "removal " : "");
-    while (pmpRun_) {
-        natpmpresp_t response;
-        std::this_thread::sleep_for(std::chrono::milliseconds(2));
-        auto r = readnatpmpresponseorretry(&natpmp, &response);
-        if (r < 0 && r != NATPMP_TRYAGAIN) {
-            RING_ERR("NAT-PMP: can't %sregister port mapping", remove ? "un" : "");
-            break;
-        }
-        else if (r != NATPMP_TRYAGAIN) {
-            mapping.renewal_ = clock::now()
-                             + std::chrono::seconds(response.pnu.newportmapping.lifetime/2);
-            break;
-        }
-    }
-}
-
-void
-UPnPContext::PMPdeleteAllPortMapping(const PMPIGD& /*pmp_igd*/, natpmp_t& natpmp, int proto) const
-{
-    if (sendnewportmappingrequest(&natpmp, proto, 0, 0, 0) < 0) {
-        RING_ERR("NAT-PMP: can't send all port mapping removal request");
-        return;
-    }
-    RING_DBG("NAT-PMP: sent all port mapping removal request");
-    while (pmpRun_) {
-        natpmpresp_t response;
-        std::this_thread::sleep_for(std::chrono::milliseconds(2));
-        auto r = readnatpmpresponseorretry(&natpmp, &response);
-        if (r < 0 && r != NATPMP_TRYAGAIN) {
-            RING_ERR("NAT-PMP: can't remove all port mappings");
-            break;
-        }
-        else if (r != NATPMP_TRYAGAIN) {
-            break;
-        }
-    }
-}
-
-#endif /* HAVE_LIBNATPMP */
-
-
-#if HAVE_LIBUPNP
-
-void
-UPnPContext::searchForIGD()
-{
-    if (not clientRegistered_) {
-        RING_WARN("UPnP: Control Point not registered");
-        return;
-    }
-
-    /* send out search for both types, as some routers may possibly only reply to one */
-    UpnpSearchAsync(ctrlptHandle_, SEARCH_TIMEOUT, UPNP_ROOT_DEVICE, this);
-    UpnpSearchAsync(ctrlptHandle_, SEARCH_TIMEOUT, UPNP_IGD_DEVICE, this);
-    UpnpSearchAsync(ctrlptHandle_, SEARCH_TIMEOUT, UPNP_WANIP_SERVICE, this);
-    UpnpSearchAsync(ctrlptHandle_, SEARCH_TIMEOUT, UPNP_WANPPP_SERVICE, this);
-}
-
 /**
  * Parses the device description and adds desired devices to
  * relevant lists
@@ -726,7 +528,7 @@ UPnPContext::parseIGD(IXML_Document* doc, const Upnp_Discovery* d_event)
         }
     }
 
-    std::unique_ptr<UPnPIGD> new_igd;
+    std::unique_ptr<IGD> new_igd;
     int upnp_err;
 
     std::string friendlyName = get_first_doc_item(doc, "friendlyName");
@@ -814,9 +616,9 @@ UPnPContext::parseIGD(IXML_Document* doc, const Upnp_Discovery* d_event)
                     if (not (serviceId.empty() and controlURL.empty() and eventSubURL.empty()) ) {
                         /* RING_DBG("UPnP: got service info from device:\n\tserviceType: %s\n\tserviceID: %s\n\tcontrolURL: %s\n\teventSubURL: %s",
                                  serviceType.c_str(), serviceId.c_str(), controlURL.c_str(), eventSubURL.c_str()); */
-                        new_igd.reset(new UPnPIGD(UDN, baseURL, friendlyName, serviceType, serviceId, controlURL, eventSubURL));
-                        if (isIGDConnected(*new_igd)) {
-                            new_igd->publicIp = getExternalIP(*new_igd);
+                        new_igd.reset(new IGD(UDN, baseURL, friendlyName, serviceType, serviceId, controlURL, eventSubURL));
+                        if (isIGDConnected(new_igd.get())) {
+                            new_igd->publicIp = getExternalIP(new_igd.get());
                             if (new_igd->publicIp) {
                                 RING_DBG("UPnP: got external IP: %s", new_igd->publicIp.toString().c_str());
                                 new_igd->localIp = ip_utils::getLocalAddr(pj_AF_INET());
@@ -844,7 +646,7 @@ UPnPContext::parseIGD(IXML_Document* doc, const Upnp_Discovery* d_event)
         {
             std::lock_guard<std::mutex> lock(validIGDMutex_);
             /* delete all RING mappings first */
-            removeMappingsByLocalIPAndDescription(*new_igd, Mapping::UPNP_DEFAULT_MAPPING_DESCRIPTION);
+            removeMappingsByLocalIPAndDescription(new_igd.get(), Mapping::UPNP_DEFAULT_MAPPING_DESCRIPTION);
             validIGDs_.emplace(UDN, std::move(new_igd));
             validIGDCondVar_.notify_all();
             for (const auto& l : igdListeners_)
@@ -895,17 +697,6 @@ get_first_element_item(IXML_Element* element, const char* item)
     }
     return ret;
 }
-
-int
-UPnPContext::cp_callback(Upnp_EventType event_type, void* event, void* user_data)
-{
-    if (auto upnpContext = static_cast<UPnPContext*>(user_data))
-        return upnpContext->handleUPnPEvents(event_type, event);
-
-    RING_WARN("UPnP callback without UPnPContext");
-    return 0;
-}
-
 int
 UPnPContext::handleUPnPEvents(Upnp_EventType event_type, void* event)
 {
@@ -1075,22 +866,22 @@ checkResponseError(IXML_Document* doc)
 }
 
 bool
-UPnPContext::isIGDConnected(const UPnPIGD& igd)
+UPnPContext::isIGDConnected(const IGD* igd)
 {
     bool connected = false;
     std::unique_ptr<IXML_Document, decltype(ixmlDocument_free)&> action(nullptr, ixmlDocument_free);
-    action.reset(UpnpMakeAction("GetStatusInfo", igd.getServiceType().c_str(), 0, nullptr));
+    action.reset(UpnpMakeAction("GetStatusInfo", igd->getServiceType().c_str(), 0, nullptr));
 
     std::unique_ptr<IXML_Document, decltype(ixmlDocument_free)&> response(nullptr, ixmlDocument_free);
     IXML_Document* response_ptr = nullptr;
-    int upnp_err = UpnpSendAction(ctrlptHandle_, igd.getControlURL().c_str(),
-                                  igd.getServiceType().c_str(), nullptr, action.get(), &response_ptr);
+    int upnp_err = UpnpSendAction(ctrlptHandle_, igd->getControlURL().c_str(),
+                                  igd->getServiceType().c_str(), nullptr, action.get(), &response_ptr);
     response.reset(response_ptr);
     checkResponseError(response.get());
     if( upnp_err != UPNP_E_SUCCESS) {
         /* TODO: if failed, should we chck if the igd is disconnected? */
         RING_WARN("UPnP: Failed to get GetStatusInfo from: %s, %d: %s",
-                  igd.getServiceType().c_str(), upnp_err, UpnpGetErrorMessage(upnp_err));
+                  igd->getServiceType().c_str(), upnp_err, UpnpGetErrorMessage(upnp_err));
 
         return false;
     }
@@ -1108,21 +899,21 @@ UPnPContext::isIGDConnected(const UPnPIGD& igd)
 }
 
 IpAddr
-UPnPContext::getExternalIP(const UPnPIGD& igd)
+UPnPContext::getExternalIP(const IGD* igd)
 {
     std::unique_ptr<IXML_Document, decltype(ixmlDocument_free)&> action(nullptr, ixmlDocument_free);
-    action.reset(UpnpMakeAction("GetExternalIPAddress", igd.getServiceType().c_str(), 0, nullptr));
+    action.reset(UpnpMakeAction("GetExternalIPAddress", igd->getServiceType().c_str(), 0, nullptr));
 
     std::unique_ptr<IXML_Document, decltype(ixmlDocument_free)&> response(nullptr, ixmlDocument_free);
     IXML_Document* response_ptr = nullptr;
-    int upnp_err = UpnpSendAction(ctrlptHandle_, igd.getControlURL().c_str(),
-                                  igd.getServiceType().c_str(), nullptr, action.get(), &response_ptr);
+    int upnp_err = UpnpSendAction(ctrlptHandle_, igd->getControlURL().c_str(),
+                                  igd->getServiceType().c_str(), nullptr, action.get(), &response_ptr);
     response.reset(response_ptr);
     checkResponseError(response.get());
     if( upnp_err != UPNP_E_SUCCESS) {
         /* TODO: if failed, should we chck if the igd is disconnected? */
         RING_WARN("UPnP: Failed to get GetExternalIPAddress from: %s, %d: %s",
-                  igd.getServiceType().c_str(), upnp_err, UpnpGetErrorMessage(upnp_err));
+                  igd->getServiceType().c_str(), upnp_err, UpnpGetErrorMessage(upnp_err));
         return {};
     }
 
@@ -1131,15 +922,15 @@ UPnPContext::getExternalIP(const UPnPIGD& igd)
 }
 
 void
-UPnPContext::removeMappingsByLocalIPAndDescription(const UPnPIGD& igd, const std::string& description)
+UPnPContext::removeMappingsByLocalIPAndDescription(const IGD* igd, const std::string& description)
 {
-    if (!igd.localIp) {
+    if (!igd->localIp) {
         RING_DBG("UPnP: cannot determine local IP in function removeMappingsByLocalIPAndDescription()");
         return;
     }
 
     RING_DBG("UPnP: removing all port mappings with description: \"%s\" and local ip: %s",
-             description.c_str(), igd.localIp.toString().c_str());
+             description.c_str(), igd->localIp.toString().c_str());
 
     int entry_idx = 0;
     bool done = false;
@@ -1147,19 +938,19 @@ UPnPContext::removeMappingsByLocalIPAndDescription(const UPnPIGD& igd, const std
     do {
         std::unique_ptr<IXML_Document, decltype(ixmlDocument_free)&> action(nullptr, ixmlDocument_free);
         IXML_Document* action_ptr = nullptr;
-        UpnpAddToAction(&action_ptr, "GetGenericPortMappingEntry", igd.getServiceType().c_str(),
+        UpnpAddToAction(&action_ptr, "GetGenericPortMappingEntry", igd->getServiceType().c_str(),
                         "NewPortMappingIndex", ring::to_string(entry_idx).c_str());
         action.reset(action_ptr);
 
         std::unique_ptr<IXML_Document, decltype(ixmlDocument_free)&> response(nullptr, ixmlDocument_free);
         IXML_Document* response_ptr = nullptr;
-        int upnp_err = UpnpSendAction(ctrlptHandle_, igd.getControlURL().c_str(),
-                                      igd.getServiceType().c_str(), nullptr, action.get(), &response_ptr);
+        int upnp_err = UpnpSendAction(ctrlptHandle_, igd->getControlURL().c_str(),
+                                      igd->getServiceType().c_str(), nullptr, action.get(), &response_ptr);
         response.reset(response_ptr);
         if( not response and upnp_err != UPNP_E_SUCCESS) {
             /* TODO: if failed, should we chck if the igd is disconnected? */
             RING_WARN("UPnP: Failed to get GetGenericPortMappingEntry from: %s, %d: %s",
-                      igd.getServiceType().c_str(), upnp_err, UpnpGetErrorMessage(upnp_err));
+                      igd->getServiceType().c_str(), upnp_err, UpnpGetErrorMessage(upnp_err));
             return;
         }
 
@@ -1172,7 +963,7 @@ UPnPContext::removeMappingsByLocalIPAndDescription(const UPnPIGD& igd, const std
             std::string client_ip = get_first_doc_item(response.get(), "NewInternalClient");
 
             /* check if same IP and description */
-            if (IpAddr(client_ip) == igd.localIp and desc_actual.compare(description) == 0) {
+            if (IpAddr(client_ip) == igd->localIp and desc_actual.compare(description) == 0) {
                 /* get the rest of the needed parameters */
                 std::string port_internal = get_first_doc_item(response.get(), "NewInternalPort");
                 std::string port_external = get_first_doc_item(response.get(), "NewExternalPort");
@@ -1205,28 +996,28 @@ UPnPContext::removeMappingsByLocalIPAndDescription(const UPnPIGD& igd, const std
 }
 
 bool
-UPnPContext::deletePortMapping(const UPnPIGD& igd, const std::string& port_external, const std::string& protocol)
+UPnPContext::deletePortMapping(const IGD* igd, const std::string& port_external, const std::string& protocol)
 {
     std::string action_name{"DeletePortMapping"};
     std::unique_ptr<IXML_Document, decltype(ixmlDocument_free)&> action(nullptr, ixmlDocument_free);
     IXML_Document* action_ptr = nullptr;
-    UpnpAddToAction(&action_ptr, action_name.c_str(), igd.getServiceType().c_str(),
+    UpnpAddToAction(&action_ptr, action_name.c_str(), igd->getServiceType().c_str(),
                     "NewRemoteHost", "");
-    UpnpAddToAction(&action_ptr, action_name.c_str(), igd.getServiceType().c_str(),
+    UpnpAddToAction(&action_ptr, action_name.c_str(), igd->getServiceType().c_str(),
                     "NewExternalPort", port_external.c_str());
-    UpnpAddToAction(&action_ptr, action_name.c_str(), igd.getServiceType().c_str(),
+    UpnpAddToAction(&action_ptr, action_name.c_str(), igd->getServiceType().c_str(),
                     "NewProtocol", protocol.c_str());
     action.reset(action_ptr);
 
     std::unique_ptr<IXML_Document, decltype(ixmlDocument_free)&> response(nullptr, ixmlDocument_free);
     IXML_Document* response_ptr = nullptr;
-    int upnp_err = UpnpSendAction(ctrlptHandle_, igd.getControlURL().c_str(),
-                                  igd.getServiceType().c_str(), nullptr, action.get(), &response_ptr);
+    int upnp_err = UpnpSendAction(ctrlptHandle_, igd->getControlURL().c_str(),
+                                  igd->getServiceType().c_str(), nullptr, action.get(), &response_ptr);
     response.reset(response_ptr);
     if( upnp_err != UPNP_E_SUCCESS) {
         /* TODO: if failed, should we check if the igd is disconnected? */
         RING_WARN("UPnP: Failed to get %s from: %s, %d: %s", action_name.c_str(),
-                  igd.getServiceType().c_str(), upnp_err, UpnpGetErrorMessage(upnp_err));
+                  igd->getServiceType().c_str(), upnp_err, UpnpGetErrorMessage(upnp_err));
         return false;
     }
     /* check if there is an error code */
@@ -1241,41 +1032,41 @@ UPnPContext::deletePortMapping(const UPnPIGD& igd, const std::string& port_exter
 }
 
 bool
-UPnPContext::addPortMapping(const UPnPIGD& igd, const Mapping& mapping, int* error_code)
+UPnPContext::addPortMapping(const IGD* igd, const Mapping& mapping, int* error_code)
 {
     *error_code = UPNP_E_SUCCESS;
 
     std::string action_name{"AddPortMapping"};
     std::unique_ptr<IXML_Document, decltype(ixmlDocument_free)&> action(nullptr, ixmlDocument_free);
     IXML_Document* action_ptr = nullptr;
-    UpnpAddToAction(&action_ptr, action_name.c_str(), igd.getServiceType().c_str(),
+    UpnpAddToAction(&action_ptr, action_name.c_str(), igd->getServiceType().c_str(),
                     "NewRemoteHost", "");
-    UpnpAddToAction(&action_ptr, action_name.c_str(), igd.getServiceType().c_str(),
+    UpnpAddToAction(&action_ptr, action_name.c_str(), igd->getServiceType().c_str(),
                     "NewExternalPort", mapping.getPortExternalStr().c_str());
-    UpnpAddToAction(&action_ptr, action_name.c_str(), igd.getServiceType().c_str(),
+    UpnpAddToAction(&action_ptr, action_name.c_str(), igd->getServiceType().c_str(),
                     "NewProtocol", mapping.getTypeStr().c_str());
-    UpnpAddToAction(&action_ptr, action_name.c_str(), igd.getServiceType().c_str(),
+    UpnpAddToAction(&action_ptr, action_name.c_str(), igd->getServiceType().c_str(),
                     "NewInternalPort", mapping.getPortInternalStr().c_str());
-    UpnpAddToAction(&action_ptr, action_name.c_str(), igd.getServiceType().c_str(),
-                    "NewInternalClient", igd.localIp.toString().c_str());
-    UpnpAddToAction(&action_ptr, action_name.c_str(), igd.getServiceType().c_str(),
+    UpnpAddToAction(&action_ptr, action_name.c_str(), igd->getServiceType().c_str(),
+                    "NewInternalClient", igd->localIp.toString().c_str());
+    UpnpAddToAction(&action_ptr, action_name.c_str(), igd->getServiceType().c_str(),
                     "NewEnabled", "1");
-    UpnpAddToAction(&action_ptr, action_name.c_str(), igd.getServiceType().c_str(),
+    UpnpAddToAction(&action_ptr, action_name.c_str(), igd->getServiceType().c_str(),
                     "NewPortMappingDescription", mapping.getDescription().c_str());
     /* for now assume lease duration is always infinite */
-    UpnpAddToAction(&action_ptr, action_name.c_str(), igd.getServiceType().c_str(),
+    UpnpAddToAction(&action_ptr, action_name.c_str(), igd->getServiceType().c_str(),
                     "NewLeaseDuration", "0");
     action.reset(action_ptr);
 
     std::unique_ptr<IXML_Document, decltype(ixmlDocument_free)&> response(nullptr, ixmlDocument_free);
     IXML_Document* response_ptr = nullptr;
-    int upnp_err = UpnpSendAction(ctrlptHandle_, igd.getControlURL().c_str(),
-                                  igd.getServiceType().c_str(), nullptr, action.get(), &response_ptr);
+    int upnp_err = UpnpSendAction(ctrlptHandle_, igd->getControlURL().c_str(),
+                                  igd->getServiceType().c_str(), nullptr, action.get(), &response_ptr);
     response.reset(response_ptr);
     if( not response and upnp_err != UPNP_E_SUCCESS) {
         /* TODO: if failed, should we chck if the igd is disconnected? */
         RING_WARN("UPnP: Failed to %s from: %s, %d: %s", action_name.c_str(),
-                  igd.getServiceType().c_str(), upnp_err, UpnpGetErrorMessage(upnp_err));
+                  igd->getServiceType().c_str(), upnp_err, UpnpGetErrorMessage(upnp_err));
         *error_code = -1; /* make sure to -1 since we didn't get a response */
         return false;
     }
diff --git a/src/upnp/upnp_context.h b/src/upnp/upnp_context.h
index 3ad8285655ada1304cdb48de90018b793cd27e64..72d06b75eafdcb2871f38755105c9e5d217512e0 100644
--- a/src/upnp/upnp_context.h
+++ b/src/upnp/upnp_context.h
@@ -18,7 +18,8 @@
  *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301 USA.
  */
 
-#pragma once
+#ifndef UPNP_CONTEXT_H_
+#define UPNP_CONTEXT_H_
 
 #ifdef HAVE_CONFIG_H
 #include "config.h"
@@ -31,7 +32,6 @@
 #include <condition_variable>
 #include <chrono>
 #include <atomic>
-#include <thread>
 
 #if HAVE_LIBUPNP
 #ifdef _WIN32
@@ -43,10 +43,6 @@
 #include <upnp/upnptools.h>
 #endif
 
-#if HAVE_LIBNATPMP
-#include <natpmp.h>
-#endif
-
 #include "noncopyable.h"
 #include "upnp_igd.h"
 
@@ -60,6 +56,7 @@ class UPnPContext {
 public:
     constexpr static unsigned SEARCH_TIMEOUT {30};
 
+#if HAVE_LIBUPNP
     UPnPContext();
     ~UPnPContext();
 
@@ -107,63 +104,21 @@ public:
      */
     IpAddr getLocalIP() const;
 
-private:
-    NON_COPYABLE(UPnPContext);
-
-    std::atomic_bool clientRegistered_ {false};
-
     /**
-     * map of valid IGDs - IGDs which have the correct services and are connected
-     * to some external network (have an external IP)
-     *
-     * the UDN string is used to uniquely identify the IGD
-     *
-     * the mutex is used to access these lists and IGDs in a thread-safe manner
-     */
-    std::map<std::string, std::unique_ptr<IGD>> validIGDs_;
-    mutable std::mutex validIGDMutex_;
-    std::condition_variable validIGDCondVar_;
-
-    /**
-     * Map of valid IGD listeners.
-     */
-    std::map<size_t, IGDFoundCallback> igdListeners_;
-
-    /**
-     * Last provided token for valid IGD listeners.
-     * 0 is the invalid token.
-     */
-    size_t listenerToken_ {0};
-
-    /**
-     * chooses the IGD to use (currently selects the first one in the map)
-     * assumes you already have a lock on igd_mutex_
+     * callback function for the UPnP client (control point)
+     * all UPnP events received by the client are processed here
      */
-    IGD* chooseIGD_unlocked() const;
-
-
-    /* tries to add mapping, assumes you alreayd have lock on igd_mutex_ */
-    Mapping addMapping(IGD* igd,
-                       uint16_t port_external,
-                       uint16_t port_internal,
-                       PortType type,
-                       int *upnp_error);
-
-    uint16_t chooseRandomPort(const IGD& igd, PortType type);
-
-#if HAVE_LIBNATPMP
-
-    std::thread pmpThread_;
-    std::mutex pmpMutex_;
-    std::condition_variable pmpCv_;
-    std::atomic_bool pmpRun_ {true};
-
-    void PMPsearchForIGD(PMPIGD* pmp_igd, natpmp_t& natpmp, bool& found);
-    void PMPaddPortMapping(const PMPIGD& pmp_igd, natpmp_t& natpmp, GlobalMapping& mapping, bool remove=false) const;
-    void PMPdeleteAllPortMapping(const PMPIGD& pmp_igd, natpmp_t& natpmp, int proto) const;
+    int handleUPnPEvents(Upnp_EventType event_type, void* event);
 
+#else
+    /* use default constructor and destructor */
+    UPnPContext() = default;
+    ~UPnPContext() = default;
 #endif
 
+private:
+    NON_COPYABLE(UPnPContext);
+
 #if HAVE_LIBUPNP
 
     /**
@@ -195,18 +150,40 @@ private:
     UpnpDevice_Handle deviceHandle_ {-1};
 
     /**
-     * keep track if we've successfully registered a device
+     * keep track if we've successfully registered
+     * a client and/ore device
      */
+    std::atomic_bool clientRegistered_ {false};
     bool deviceRegistered_ {false};
 
-    static int cp_callback(Upnp_EventType event_type, void* event, void* user_data);
+    /**
+     * map of valid IGDs - IGDs which have the correct services and are connected
+     * to some external network (have an external IP)
+     *
+     * the UDN string is used to uniquely identify the IGD
+     *
+     * the mutex is used to access these lists and IGDs in a thread-safe manner
+     */
+    std::map<std::string, std::unique_ptr<IGD>> validIGDs_;
+    mutable std::mutex validIGDMutex_;
+    std::condition_variable validIGDCondVar_;
 
     /**
-     * callback function for the UPnP client (control point)
-     * all UPnP events received by the client are processed here
+     * Map of valid IGD listeners.
      */
-    int handleUPnPEvents(Upnp_EventType event_type, void* event);
+    std::map<size_t, IGDFoundCallback> igdListeners_;
 
+    /**
+     * Last provided token for valid IGD listeners.
+     * 0 is the invalid token.
+     */
+    size_t listenerToken_ {0};
+
+    /**
+     * chooses the IGD to use (currently selects the first one in the map)
+     * assumes you already have a lock on igd_mutex_
+     */
+    IGD* chooseIGD_unlocked() const;
 
     /* sends out async search for IGD */
     void searchForIGD();
@@ -219,24 +196,33 @@ private:
 
     void parseIGD(IXML_Document* doc, const Upnp_Discovery* d_event);
 
+    /* tries to add mapping, assumes you alreayd have lock on igd_mutex_ */
+    Mapping addMapping(IGD* igd,
+                       uint16_t port_external,
+                       uint16_t port_internal,
+                       PortType type,
+                       int *upnp_error);
+
+    uint16_t chooseRandomPort(const IGD* igd, PortType type);
 
     /* these functions directly create UPnP actions
      * and make synchronous UPnP control point calls
      * they assume you have a lock on the igd_mutex_ */
-    bool isIGDConnected(const UPnPIGD& igd);
+    bool isIGDConnected(const IGD* igd);
 
-    IpAddr getExternalIP(const UPnPIGD& igd);
+    IpAddr getExternalIP(const IGD* igd);
 
-    void removeMappingsByLocalIPAndDescription(const UPnPIGD& igd,
+    void removeMappingsByLocalIPAndDescription(const IGD* igd,
                                                const std::string& description);
 
-    bool deletePortMapping(const UPnPIGD& igd,
+    bool deletePortMapping(const IGD* igd,
                            const std::string& port_external,
                            const std::string& protocol);
 
-    bool addPortMapping(const UPnPIGD& igd,
+    bool addPortMapping(const IGD* igd,
                         const Mapping& mapping,
                         int* error_code);
+
 #endif /* HAVE_LIBUPNP */
 
 };
@@ -250,3 +236,5 @@ private:
 std::shared_ptr<UPnPContext> getUPnPContext();
 
 }} // namespace ring::upnp
+
+#endif /* UPNP_CONTEXT_H_ */
diff --git a/src/upnp/upnp_control.cpp b/src/upnp/upnp_control.cpp
index d6b06f345cee130e1116231c668eb0a2f0017730..4e3be9d60481ba39dadbfd817ed73aafa87485d8 100644
--- a/src/upnp/upnp_control.cpp
+++ b/src/upnp/upnp_control.cpp
@@ -46,14 +46,19 @@ Controller::~Controller()
 {
     /* remove all mappings */
     removeMappings();
+#if HAVE_LIBUPNP
     if (listToken_ and upnpContext_)
         upnpContext_->removeIGDListener(listToken_);
+#endif
 }
 
 bool
 Controller::hasValidIGD(std::chrono::seconds timeout)
 {
+#if HAVE_LIBUPNP
     return upnpContext_ and upnpContext_->hasValidIGD(timeout);
+#endif
+    return false;
 }
 
 void
@@ -61,9 +66,11 @@ Controller::setIGDListener(IGDFoundCallback&& cb)
 {
     if (not upnpContext_)
         return;
+#if HAVE_LIBUPNP
     if (listToken_)
         upnpContext_->removeIGDListener(listToken_);
     listToken_ = cb ? upnpContext_->addIGDListener(std::move(cb)) : 0;
+#endif
 }
 
 bool
@@ -74,6 +81,7 @@ Controller::addAnyMapping(uint16_t port_desired,
                           bool unique,
                           uint16_t *port_used)
 {
+#if HAVE_LIBUPNP
     if (not upnpContext_)
         return false;
 
@@ -89,6 +97,7 @@ Controller::addAnyMapping(uint16_t port_desired,
         instanceMappings.emplace(usedPort, std::move(mapping));
         return true;
     }
+#endif
     return false;
 }
 
@@ -104,6 +113,7 @@ Controller::addAnyMapping(uint16_t port_desired,
 
 void
 Controller::removeMappings(PortType type) {
+#if HAVE_LIBUPNP
     if (not upnpContext_)
         return;
 
@@ -113,28 +123,34 @@ Controller::removeMappings(PortType type) {
         upnpContext_->removeMapping(mapping);
         iter = instanceMappings.erase(iter);
     }
+#endif
 }
-
 void
 Controller::removeMappings()
 {
+#if HAVE_LIBUPNP
     removeMappings(PortType::UDP);
     removeMappings(PortType::TCP);
+#endif
 }
 
 IpAddr
 Controller::getLocalIP() const
 {
+#if HAVE_LIBUPNP
     if (upnpContext_)
         return upnpContext_->getLocalIP();
+#endif
     return {}; //  empty address
 }
 
 IpAddr
 Controller::getExternalIP() const
 {
+#if HAVE_LIBUPNP
     if (upnpContext_)
         return upnpContext_->getExternalIP();
+#endif
     return {}; //  empty address
 }
 
diff --git a/src/upnp/upnp_igd.h b/src/upnp/upnp_igd.h
index 6a65de3f361ac9370a45bfef2cb8f96e845f86e5..3fdce01678488ddf30e5c79658de74fd0c010c2a 100644
--- a/src/upnp/upnp_igd.h
+++ b/src/upnp/upnp_igd.h
@@ -18,16 +18,12 @@
  *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301 USA.
  */
 
-#pragma once
-
-#ifdef HAVE_CONFIG_H
-#include "config.h"
-#endif
+#ifndef UPNP_IGD_H_
+#define UPNP_IGD_H_
 
 #include <string>
 #include <map>
 #include <functional>
-#include <chrono>
 
 #include "noncopyable.h"
 #include "ip_utils.h"
@@ -67,13 +63,13 @@ public:
     friend bool operator== (const Mapping& cRedir1, const Mapping& cRedir2);
     friend bool operator!= (const Mapping& cRedir1, const Mapping& cRedir2);
 
-    uint16_t      getPortExternal()    const { return port_external_; }
-    std::string   getPortExternalStr() const { return ring::to_string(port_external_); }
-    uint16_t      getPortInternal()    const { return port_internal_; }
-    std::string   getPortInternalStr() const { return ring::to_string(port_internal_); }
-    PortType      getType()            const { return type_; }
+    uint16_t      getPortExternal()    const { return port_external_; };
+    std::string   getPortExternalStr() const { return ring::to_string(port_external_); };
+    uint16_t      getPortInternal()    const { return port_internal_; };
+    std::string   getPortInternalStr() const { return ring::to_string(port_internal_); };
+    PortType      getType()            const { return type_; };
     std::string   getTypeStr()         const { return type_ == PortType::UDP ? "UDP" : "TCP"; }
-    std::string   getDescription()     const { return description_; }
+    std::string   getDescription()     const { return description_; };
 
     std::string toString() const {
         return getPortExternalStr() + ":" + getPortInternalStr() + ", " + getTypeStr();
@@ -87,11 +83,6 @@ public:
         return isValid();
     }
 
-#if HAVE_LIBNATPMP
-    std::chrono::system_clock::time_point renewal_ {std::chrono::system_clock::time_point::min()};
-    bool remove {false};
-#endif
-
 private:
     NON_COPYABLE(Mapping);
 
@@ -132,6 +123,7 @@ using IGDFoundCallback = std::function<void()>;
 /* defines a UPnP capable Internet Gateway Device (a router) */
 class IGD {
 public:
+
     /* device address seen by IGD */
     IpAddr localIp;
 
@@ -144,22 +136,7 @@ public:
 
     /* constructors */
     IGD() {}
-
-    /* move constructor and operator */
-    IGD(IGD&&) = default;
-    IGD& operator=(IGD&&) = default;
-
-    virtual ~IGD() = default;
-
-private:
-    NON_COPYABLE(IGD);
-};
-
-#if HAVE_LIBUPNP
-
-class UPnPIGD : public IGD {
-public:
-    UPnPIGD(std::string UDN,
+    IGD(std::string UDN,
         std::string baseURL,
         std::string friendlyName,
         std::string serviceType,
@@ -175,6 +152,12 @@ public:
         , eventSubURL_(eventSubURL)
         {}
 
+    /* move constructor and operator */
+    IGD(IGD&&) = default;
+    IGD& operator=(IGD&&) = default;
+
+    ~IGD() = default;
+
     const std::string& getUDN() const { return UDN_; };
     const std::string& getBaseURL() const { return baseURL_; };
     const std::string& getFriendlyName() const { return friendlyName_; };
@@ -184,6 +167,8 @@ public:
     const std::string& getEventSubURL() const { return eventSubURL_; };
 
 private:
+    NON_COPYABLE(IGD);
+
     /* root device info */
     std::string UDN_ {}; /* used to uniquely identify this UPnP device */
     std::string baseURL_ {};
@@ -194,47 +179,9 @@ private:
     std::string serviceId_ {};
     std::string controlURL_ {};
     std::string eventSubURL_ {};
-};
-
-#endif
-
-#if HAVE_LIBNATPMP
-
-using clock = std::chrono::system_clock;
-using time_point = clock::time_point;
-
-class PMPIGD : public IGD {
-public:
-
-    void clearAll() {
-        toRemove_.clear();
-        udpMappings.clear();
-        tcpMappings.clear();
-        clearAll_ = true;
-    }
-
-    GlobalMapping* getNextMappingToRenew() const {
-        const GlobalMapping* mapping {nullptr};
-        for (const auto& m : udpMappings)
-            if (!mapping or m.second.renewal_ < mapping->renewal_)
-                mapping = &m.second;
-        for (const auto& m : tcpMappings)
-            if (!mapping or m.second.renewal_ < mapping->renewal_)
-                mapping = &m.second;
-        return (GlobalMapping*)mapping;
-    }
 
-    time_point getRenewalTime() const {
-        const auto next = getNextMappingToRenew();
-        auto nextTime = std::min(renewal_, next ? next->renewal_ : time_point::max());
-        return toRemove_.empty() ? nextTime : std::min(nextTime, time_point::min());
-    }
-
-    time_point renewal_ {time_point::min()};
-    std::vector<GlobalMapping> toRemove_ {};
-    bool clearAll_ {false};
 };
 
-#endif
-
 }} // namespace ring::upnp
+
+#endif /* UPNP_IGD_H_ */