diff --git a/bin/dbus/cx.ring.Ring.ConfigurationManager.xml b/bin/dbus/cx.ring.Ring.ConfigurationManager.xml
index 8af7f278d703f09928760b6331f31c13d720c8cd..ae47f0778a52b570346f8afce316b505a0b17110 100644
--- a/bin/dbus/cx.ring.Ring.ConfigurationManager.xml
+++ b/bin/dbus/cx.ring.Ring.ConfigurationManager.xml
@@ -1103,5 +1103,15 @@
            </arg>
        </signal>
 
+       <method name="connectivityChanged" tp:name-for-bindings="connectivityChanged">
+           <tp:added version="2.3.0"/>
+           <tp:docstring>
+               This notifies the daemon that the network connectivity has changed, eg:
+               - interface has disconnected from the network
+               - interface connected to a different network
+               - connected interface has changed (wifi to eth)
+           </tp:docstring>
+       </method>
+
    </interface>
 </node>
diff --git a/bin/dbus/dbusconfigurationmanager.cpp b/bin/dbus/dbusconfigurationmanager.cpp
index 85c718e489cf1d3d74d69c7a051ba27728d7b6e3..129ba9ffdc55c1ca291d5f8a3f0708fb0c3a65dd 100644
--- a/bin/dbus/dbusconfigurationmanager.cpp
+++ b/bin/dbus/dbusconfigurationmanager.cpp
@@ -540,3 +540,9 @@ DBusConfigurationManager::importAccounts(const std::string& archivePath, const s
 {
     return DRing::importAccounts(archivePath, password);
 }
+
+void
+DBusConfigurationManager::connectivityChanged()
+{
+    DRing::connectivityChanged();
+}
diff --git a/bin/dbus/dbusconfigurationmanager.h b/bin/dbus/dbusconfigurationmanager.h
index 8471eec8b13e5e365b52856cbc2c2a8b9a8fb413..743d40952dd0ea8cd7e61de9fa2aae52091620be 100644
--- a/bin/dbus/dbusconfigurationmanager.h
+++ b/bin/dbus/dbusconfigurationmanager.h
@@ -139,6 +139,7 @@ class DBusConfigurationManager :
         void sendTrustRequest(const std::string& accountId, const std::string& to, const std::vector<uint8_t>& payload);
         int exportAccounts(const std::vector<std::string>& accountIDs, const std::string& filepath, const std::string& password);
         int importAccounts(const std::string& archivePath, const std::string& password);
+        void connectivityChanged();
 };
 
 #endif // __RING_DBUSCONFIGURATIONMANAGER_H__
diff --git a/src/account.h b/src/account.h
index 64e1913006ef14e6dccffe416b2863cb9c2c30fe..d35be2c0e6c00819db8cc925f8d6ebc0640981df 100644
--- a/src/account.h
+++ b/src/account.h
@@ -296,6 +296,11 @@ class Account : public Serializable, public std::enable_shared_from_this<Account
          */
         mutable std::mt19937_64 rand_;
 
+        /**
+         * Inform the account that the network status has changed.
+         */
+        virtual void connectivityChanged() {};
+
     private:
         NON_COPYABLE(Account);
 
diff --git a/src/client/configurationmanager.cpp b/src/client/configurationmanager.cpp
index 233e335df87c24592a5ce5e19658e723ed8ee5cc..32275f0280f559d22fa76401bf586bede8c1eaf0 100644
--- a/src/client/configurationmanager.cpp
+++ b/src/client/configurationmanager.cpp
@@ -40,6 +40,7 @@
 #include "system_codec_container.h"
 #include "account_const.h"
 #include "client/ring_signal.h"
+#include "upnp/upnp_context.h"
 
 #include <dirent.h>
 
@@ -790,4 +791,24 @@ setCredentials(const std::string& accountID,
     }
 }
 
+void
+connectivityChanged()
+{
+    RING_WARN("received connectivity changed - trying to re-connect enabled accounts");
+
+    // reset the UPnP context
+    try {
+        ring::upnp::getUPnPContext()->connectivityChanged();
+    } catch (std::runtime_error& e) {
+        RING_ERR("UPnP context error: %s", e.what());
+    }
+
+    auto account_list = ring::Manager::instance().getAccountList();
+    for (auto account_id : account_list) {
+        if (auto account = ring::Manager::instance().getAccount(account_id)) {
+            account->connectivityChanged();
+        }
+    }
+}
+
 } // namespace DRing
diff --git a/src/dring/configurationmanager_interface.h b/src/dring/configurationmanager_interface.h
index 8419f33ce87df0c89d7149deb6bba0b57c81ddec..46efa792d04a6d0305806070de86d13911cd5da5 100644
--- a/src/dring/configurationmanager_interface.h
+++ b/src/dring/configurationmanager_interface.h
@@ -156,6 +156,11 @@ void sendTrustRequest(const std::string& accountId, const std::string& to, const
 int exportAccounts(std::vector<std::string> accountIDs, std::string filepath, std::string password);
 int importAccounts(std::string archivePath, std::string password);
 
+/*
+ * Network connectivity
+ */
+void connectivityChanged();
+
 struct AudioSignal {
         struct DeviceEvent {
                 constexpr static const char* name = "audioDeviceEvent";
diff --git a/src/ringdht/ringaccount.cpp b/src/ringdht/ringaccount.cpp
index 0d38b9115a688b87f200d71f10dbc5e3a72d5fa0..697c458bf646b5f8c5c0e1b79ef8644c319ee202 100644
--- a/src/ringdht/ringaccount.cpp
+++ b/src/ringdht/ringaccount.cpp
@@ -755,7 +755,7 @@ RingAccount::mapPortUPnP()
     std::weak_ptr<RingAccount> w = std::static_pointer_cast<RingAccount>(shared_from_this());
     upnp_->setIGDListener([w] {
         if (auto shared = w.lock())
-            shared->connectivityChanged();
+            shared->igdChanged();
     });
     return added;
 }
@@ -1065,6 +1065,22 @@ RingAccount::doUnregister(std::function<void(bool)> released_cb)
         released_cb(false);
 }
 
+void
+RingAccount::connectivityChanged()
+{
+    if (not isUsable()) {
+        // nothing to do
+        return;
+    }
+
+    auto shared = std::static_pointer_cast<RingAccount>(shared_from_this());
+    doUnregister([shared](bool /* transport_free */) {
+        if (shared->isUsable())
+            shared->doRegister();
+    });
+}
+
+
 bool
 RingAccount::findCertificate(const dht::InfoHash& h, std::function<void(const std::shared_ptr<dht::crypto::Certificate>)> cb)
 {
@@ -1402,7 +1418,7 @@ RingAccount::sendTrustRequest(const std::string& to, const std::vector<uint8_t>&
 }
 
 void
-RingAccount::connectivityChanged()
+RingAccount::igdChanged()
 {
     if (not dht_.isRunning())
         return;
diff --git a/src/ringdht/ringaccount.h b/src/ringdht/ringaccount.h
index dd16a51e6198886c5c6d38d2b8617e8d21b03789..94e30770bd2638be72a93bf4ca7963805d46c288 100644
--- a/src/ringdht/ringaccount.h
+++ b/src/ringdht/ringaccount.h
@@ -254,7 +254,7 @@ class RingAccount : public SIPAccountBase {
         void sendTrustRequest(const std::string& to, const std::vector<uint8_t>& payload);
         virtual void sendTextMessage(const std::string& to, const std::map<std::string, std::string>& payloads, uint64_t id) override;
 
-        void connectivityChanged();
+        void connectivityChanged() override;
 
     private:
         NON_COPYABLE(RingAccount);
@@ -287,6 +287,8 @@ class RingAccount : public SIPAccountBase {
          */
         bool mapPortUPnP();
 
+        void igdChanged();
+
         dht::DhtRunner dht_ {};
 
         dht::InfoHash callKey_;
diff --git a/src/sip/sipaccount.cpp b/src/sip/sipaccount.cpp
index 3f8fa4016b522685272221741d3d8abea589b667..b5b6dd8a8099614af77e01bfae23da6ffa929587 100644
--- a/src/sip/sipaccount.cpp
+++ b/src/sip/sipaccount.cpp
@@ -892,6 +892,21 @@ void SIPAccount::doUnregister(std::function<void(bool)> released_cb)
     upnp_->removeMappings();
 }
 
+void
+SIPAccount::connectivityChanged()
+{
+    if (not isUsable()) {
+        // nothing to do
+        return;
+    }
+
+    auto shared = std::static_pointer_cast<SIPAccount>(shared_from_this());
+    doUnregister([shared](bool /* transport_free */) {
+        if (shared->isUsable())
+            shared->doRegister();
+    });
+}
+
 void SIPAccount::startKeepAliveTimer()
 {
     if (isTlsEnabled())
diff --git a/src/sip/sipaccount.h b/src/sip/sipaccount.h
index 5ad7e2942778ed49ba47d69d41a498570fc2cd0f..ee27d86bbf4ce2cea129a1bc2ea3a5ba2c5cb446 100644
--- a/src/sip/sipaccount.h
+++ b/src/sip/sipaccount.h
@@ -493,6 +493,8 @@ class SIPAccount : public SIPAccountBase {
                                      const std::map<std::string, std::string>& payloads,
                                      uint64_t id) override;
 
+        void connectivityChanged() override;
+
     private:
         void doRegister1_();
         void doRegister2_();
diff --git a/src/sip/sipaccountbase.h b/src/sip/sipaccountbase.h
index a4fd8284257a356bfc36f31d667c37e963ff110d..ee96aab6e9df6c63818fc25ab19b0efb97c32429 100644
--- a/src/sip/sipaccountbase.h
+++ b/src/sip/sipaccountbase.h
@@ -247,6 +247,8 @@ public:
 
     void onTextMessage(const std::string& from, const std::map<std::string, std::string>& payloads);
 
+    void connectivityChanged() override {};
+
 protected:
     virtual void serialize(YAML::Emitter &out) override;
     virtual void serializeTls(YAML::Emitter &out);
diff --git a/src/upnp/upnp_context.cpp b/src/upnp/upnp_context.cpp
index 8c95f4ab116e0bf8b72c18f054fcc4a01b9e7a4c..c7fc190991558963992439c92a62852b9f7079ae 100644
--- a/src/upnp/upnp_context.cpp
+++ b/src/upnp/upnp_context.cpp
@@ -179,6 +179,26 @@ UPnPContext::~UPnPContext()
 #endif
 }
 
+void
+UPnPContext::connectivityChanged()
+{
+    {
+        std::lock_guard<std::mutex> lock(validIGDMutex_);
+
+        /* when the network changes, we're likely no longer connected to the same IGD, or if we are
+         * we might now have a different IP, thus we clear the list of IGDs and notify the listeners
+         * so that they can attempt to re-do the port mappings once we detect an IGD
+         */
+        validIGDs_.clear();
+        validIGDCondVar_.notify_all();
+        for (const auto& l : igdListeners_)
+            l.second();
+    }
+
+    // send out a new search request
+    searchForIGD();
+}
+
 void
 UPnPContext::searchForIGD()
 {
diff --git a/src/upnp/upnp_context.h b/src/upnp/upnp_context.h
index 72d06b75eafdcb2871f38755105c9e5d217512e0..9d749c7a08d137984fe5955b9486d6bca1b2bfeb 100644
--- a/src/upnp/upnp_context.h
+++ b/src/upnp/upnp_context.h
@@ -110,6 +110,12 @@ public:
      */
     int handleUPnPEvents(Upnp_EventType event_type, void* event);
 
+    /**
+     * Inform the UPnP context that the network status has changed. This clears the list of known
+     * IGDs
+     */
+    void connectivityChanged();
+
 #else
     /* use default constructor and destructor */
     UPnPContext() = default;