diff --git a/src/conf/nwfilter_params.c b/src/conf/nwfilter_params.c
index 2fba351390..12c65246c3 100644
--- a/src/conf/nwfilter_params.c
+++ b/src/conf/nwfilter_params.c
@@ -37,7 +37,7 @@
 static bool isValidVarValue(const char *value);
 
 
-static void
+void
 virNWFilterVarValueFree(virNWFilterVarValuePtr val)
 {
     unsigned i;
@@ -60,7 +60,7 @@ virNWFilterVarValueFree(virNWFilterVarValuePtr val)
     VIR_FREE(val);
 }
 
-static virNWFilterVarValuePtr
+virNWFilterVarValuePtr
 virNWFilterVarValueCopy(const virNWFilterVarValuePtr val)
 {
     virNWFilterVarValuePtr res;
@@ -222,6 +222,56 @@ virNWFilterVarValueAddValue(virNWFilterVarValuePtr val, char *value)
     return rc;
 }
 
+static int
+virNWFilterVarValueDelNthValue(virNWFilterVarValuePtr val, unsigned int pos)
+{
+    switch (val->valType) {
+    case NWFILTER_VALUE_TYPE_SIMPLE:
+        return -1;
+
+    case NWFILTER_VALUE_TYPE_ARRAY:
+        if (pos < val->u.array.nValues) {
+            VIR_FREE(val->u.array.values[pos]);
+            val->u.array.nValues--;
+
+            if (pos < val->u.array.nValues)
+                memmove(&val->u.array.values[pos],
+                        &val->u.array.values[pos + 1],
+                        sizeof(val->u.array.values[0]) *
+                            (val->u.array.nValues - pos));
+            return 0;
+        }
+        break;
+
+    case NWFILTER_VALUE_TYPE_LAST:
+        break;
+    }
+
+    return -1;
+}
+
+int
+virNWFilterVarValueDelValue(virNWFilterVarValuePtr val, const char *value)
+{
+    unsigned int i;
+
+    switch (val->valType) {
+    case NWFILTER_VALUE_TYPE_SIMPLE:
+        return -1;
+
+    case NWFILTER_VALUE_TYPE_ARRAY:
+        for (i = 0; i < val->u.array.nValues; i++)
+            if (STREQ(value, val->u.array.values[i]))
+                return virNWFilterVarValueDelNthValue(val, i);
+        break;
+
+    case NWFILTER_VALUE_TYPE_LAST:
+        break;
+    }
+
+    return -1;
+}
+
 void
 virNWFilterVarCombIterFree(virNWFilterVarCombIterPtr ci)
 {
@@ -521,14 +571,14 @@ virNWFilterHashTableCreate(int n) {
 }
 
 
-int
+void *
 virNWFilterHashTableRemoveEntry(virNWFilterHashTablePtr ht,
                                 const char *entry)
 {
     int i;
-    int rc = virHashRemoveEntry(ht->hashTable, entry);
+    void *value = virHashSteal(ht->hashTable, entry);
 
-    if (rc == 0) {
+    if (value) {
         for (i = 0; i < ht->nNames; i++) {
             if (STREQ(ht->names[i], entry)) {
                 VIR_FREE(ht->names[i]);
@@ -538,7 +588,7 @@ virNWFilterHashTableRemoveEntry(virNWFilterHashTablePtr ht,
             }
         }
     }
-    return rc;
+    return value;
 }
 
 
diff --git a/src/conf/nwfilter_params.h b/src/conf/nwfilter_params.h
index 57becc1702..81f104920b 100644
--- a/src/conf/nwfilter_params.h
+++ b/src/conf/nwfilter_params.h
@@ -50,11 +50,14 @@ struct _virNWFilterVarValue {
 
 virNWFilterVarValuePtr virNWFilterVarValueCreateSimple(char *);
 virNWFilterVarValuePtr virNWFilterVarValueCreateSimpleCopyValue(const char *);
+virNWFilterVarValuePtr virNWFilterVarValueCopy(const virNWFilterVarValuePtr);
+void virNWFilterVarValueFree(virNWFilterVarValuePtr val);
 const char *virNWFilterVarValueGetSimple(const virNWFilterVarValuePtr val);
 const char *virNWFilterVarValueGetNthValue(virNWFilterVarValuePtr val,
                                            unsigned int idx);
 unsigned int virNWFilterVarValueGetCardinality(const virNWFilterVarValuePtr);
 int virNWFilterVarValueAddValue(virNWFilterVarValuePtr val, char *value);
+int virNWFilterVarValueDelValue(virNWFilterVarValuePtr val, const char *value);
 
 typedef struct _virNWFilterHashTable virNWFilterHashTable;
 typedef virNWFilterHashTable *virNWFilterHashTablePtr;
@@ -77,8 +80,8 @@ int virNWFilterHashTablePut(virNWFilterHashTablePtr table,
                             const char *name,
                             virNWFilterVarValuePtr val,
                             int freeName);
-int virNWFilterHashTableRemoveEntry(virNWFilterHashTablePtr table,
-                                    const char *name);
+void *virNWFilterHashTableRemoveEntry(virNWFilterHashTablePtr table,
+                                      const char *name);
 int virNWFilterHashTablePutAll(virNWFilterHashTablePtr src,
                                virNWFilterHashTablePtr dest);
 
diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms
index 1c64311e55..687c3795cd 100644
--- a/src/libvirt_private.syms
+++ b/src/libvirt_private.syms
@@ -846,8 +846,14 @@ virNWFilterVarCombIterCreate;
 virNWFilterVarCombIterFree;
 virNWFilterVarCombIterGetVarValue;
 virNWFilterVarCombIterNext;
+virNWFilterVarValueAddValue;
+virNWFilterVarValueCopy;
 virNWFilterVarValueCreateSimple;
 virNWFilterVarValueCreateSimpleCopyValue;
+virNWFilterVarValueDelValue;
+virNWFilterVarValueFree;
+virNWFilterVarValueGetCardinality;
+virNWFilterVarValueGetNthValue;
 virNWFilterVarValueGetSimple;
 
 
diff --git a/src/nwfilter/nwfilter_gentech_driver.c b/src/nwfilter/nwfilter_gentech_driver.c
index bba7d0b678..84a959b386 100644
--- a/src/nwfilter/nwfilter_gentech_driver.c
+++ b/src/nwfilter/nwfilter_gentech_driver.c
@@ -145,7 +145,7 @@ virNWFilterRuleInstFree(virNWFilterRuleInstPtr inst)
 static int
 virNWFilterVarHashmapAddStdValues(virNWFilterHashTablePtr table,
                                   char *macaddr,
-                                  char *ipaddr)
+                                  const virNWFilterVarValuePtr ipaddr)
 {
     virNWFilterVarValue *val;
 
@@ -164,7 +164,7 @@ virNWFilterVarHashmapAddStdValues(virNWFilterHashTablePtr table,
     }
 
     if (ipaddr) {
-        val = virNWFilterVarValueCreateSimple(ipaddr);
+        val = virNWFilterVarValueCopy(ipaddr);
         if (!val)
             return 1;
 
@@ -194,7 +194,8 @@ virNWFilterVarHashmapAddStdValues(virNWFilterHashTablePtr table,
  * is attached to the virConnect object.
  */
 virNWFilterHashTablePtr
-virNWFilterCreateVarHashmap(char *macaddr, char *ipaddr) {
+virNWFilterCreateVarHashmap(char *macaddr,
+                            const virNWFilterVarValuePtr ipaddr) {
     virNWFilterHashTablePtr table = virNWFilterHashTableCreate(0);
     if (!table) {
         virReportOOMError();
@@ -796,7 +797,7 @@ __virNWFilterInstantiateFilter(virConnectPtr conn,
     virNWFilterDefPtr filter;
     char vmmacaddr[VIR_MAC_STRING_BUFLEN] = {0};
     char *str_macaddr = NULL;
-    const char *ipaddr;
+    virNWFilterVarValuePtr ipaddr;
     char *str_ipaddr = NULL;
 
     techdriver = virNWFilterTechDriverForName(drvname);
@@ -836,16 +837,8 @@ __virNWFilterInstantiateFilter(virConnectPtr conn,
     }
 
     ipaddr = virNWFilterGetIpAddrForIfname(ifname);
-    if (ipaddr) {
-        str_ipaddr = strdup(ipaddr);
-        if (!str_ipaddr) {
-            virReportOOMError();
-            rc = 1;
-            goto err_exit;
-        }
-    }
 
-    vars1 = virNWFilterCreateVarHashmap(str_macaddr, str_ipaddr);
+    vars1 = virNWFilterCreateVarHashmap(str_macaddr, ipaddr);
     if (!vars1) {
         rc = 1;
         goto err_exit;
@@ -1101,7 +1094,7 @@ _virNWFilterTeardownFilter(const char *ifname)
 
     techdriver->allTeardown(ifname);
 
-    virNWFilterDelIpAddrForIfname(ifname);
+    virNWFilterDelIpAddrForIfname(ifname, NULL);
 
     virNWFilterUnlockIface(ifname);
 
diff --git a/src/nwfilter/nwfilter_gentech_driver.h b/src/nwfilter/nwfilter_gentech_driver.h
index fa8603011b..e057142cdc 100644
--- a/src/nwfilter/nwfilter_gentech_driver.h
+++ b/src/nwfilter/nwfilter_gentech_driver.h
@@ -61,7 +61,7 @@ int virNWFilterInstantiateFilterLate(virConnectPtr conn,
 int virNWFilterTeardownFilter(const virDomainNetDefPtr net);
 
 virNWFilterHashTablePtr virNWFilterCreateVarHashmap(char *macaddr,
-                                                    char *ipaddr);
+                                       const virNWFilterVarValuePtr);
 
 void virNWFilterDomainFWUpdateCB(void *payload,
                                  const void *name,
diff --git a/src/nwfilter/nwfilter_learnipaddr.c b/src/nwfilter/nwfilter_learnipaddr.c
index 6e066eeb5e..675ef5b6f3 100644
--- a/src/nwfilter/nwfilter_learnipaddr.c
+++ b/src/nwfilter/nwfilter_learnipaddr.c
@@ -310,41 +310,99 @@ virNWFilterDeregisterLearnReq(int ifindex) {
     return res;
 }
 
-
-
+/* Add an IP address to the list of IP addresses an interface is
+ * known to use. This function feeds the per-interface cache that
+ * is used to instantiate filters with variable '$IP'.
+ *
+ * @ifname: The name of the (tap) interface
+ * @addr: An IPv4 address in dotted decimal format that the (tap)
+ *        interface is known to use.
+ *
+ * This function returns 0 on success, -1 otherwise
+ */
 static int
-virNWFilterAddIpAddrForIfname(const char *ifname, char *addr) {
+virNWFilterAddIpAddrForIfname(const char *ifname, char *addr)
+{
     int ret;
-    virNWFilterVarValuePtr val = virNWFilterVarValueCreateSimple(addr);
-
-    if (!val)
-        return 1;
+    virNWFilterVarValuePtr val;
 
     virMutexLock(&ipAddressMapLock);
 
-    ret = virNWFilterHashTablePut(ipAddressMap, ifname, val, 1);
+    val = virHashLookup(ipAddressMap->hashTable, ifname);
+    if (!val) {
+        val = virNWFilterVarValueCreateSimple(addr);
+        if (!val) {
+            virReportOOMError();
+            ret = -1;
+            goto err_exit;
+        }
+        ret = virNWFilterHashTablePut(ipAddressMap, ifname, val, 1);
+    } else {
+        if (virNWFilterVarValueAddValue(val, addr) < 0)
+            ret = -1;
+    }
 
+err_exit:
     virMutexUnlock(&ipAddressMapLock);
 
     return ret;
 }
 #endif
 
-
-void
-virNWFilterDelIpAddrForIfname(const char *ifname) {
+/* Delete all or a specific IP address from an interface. After this
+ * call either all or the given IP address will not be associated
+ * with the interface anymore.
+ *
+ * @ifname: The name of the (tap) interface
+ * @addr: An IPv4 address in dotted decimal format that the (tap)
+ *        interface is not using anymore; provide NULL to remove all IP
+ *        addresses associated with the given interface
+ *
+ * This function returns the number of IP addresses that are still
+ * known to be associated with this interface, in case of an error
+ * -1 is returned. Error conditions are:
+ * - IP addresses is not known to be associated with the interface
+ */
+int
+virNWFilterDelIpAddrForIfname(const char *ifname, const char *ipaddr)
+{
+    int ret = -1;
+    virNWFilterVarValuePtr val = NULL;
 
     virMutexLock(&ipAddressMapLock);
 
-    if (virHashLookup(ipAddressMap->hashTable, ifname))
-        virNWFilterHashTableRemoveEntry(ipAddressMap, ifname);
+    if (ipaddr != NULL) {
+        val = virHashLookup(ipAddressMap->hashTable, ifname);
+        if (val) {
+            if (virNWFilterVarValueGetCardinality(val) == 1 &&
+                STREQ(ipaddr,
+                      virNWFilterVarValueGetNthValue(val, 0)))
+                goto remove_entry;
+            virNWFilterVarValueDelValue(val, ipaddr);
+            ret = virNWFilterVarValueGetCardinality(val);
+        }
+    } else {
+remove_entry:
+        /* remove whole entry */
+        val = virNWFilterHashTableRemoveEntry(ipAddressMap, ifname);
+        virNWFilterVarValueFree(val);
+        ret = 0;
+    }
 
     virMutexUnlock(&ipAddressMapLock);
+
+    return ret;
 }
 
-
-const char *
-virNWFilterGetIpAddrForIfname(const char *ifname) {
+/* Get the list of IP addresses known to be in use by an interface
+ *
+ * This function returns NULL in case no IP address is known to be
+ * associated with the interface, a virNWFilterVarValuePtr otherwise
+ * that then can contain one or multiple entries.
+ */
+virNWFilterVarValuePtr
+virNWFilterGetIpAddrForIfname(const char *ifname)
+{
     virNWFilterVarValuePtr res;
 
     virMutexLock(&ipAddressMapLock);
@@ -353,10 +411,7 @@ virNWFilterGetIpAddrForIfname(const char *ifname) {
 
     virMutexUnlock(&ipAddressMapLock);
 
-    if (res)
-        return virNWFilterVarValueGetSimple(res);
-
-    return NULL;
+    return res;
 }
 
 
@@ -642,7 +697,10 @@ learnIPAddressThread(void *arg)
         char *inetaddr;
 
         if ((inetaddr = virSocketAddrFormat(&sa))!= NULL) {
-            virNWFilterAddIpAddrForIfname(req->ifname, inetaddr);
+            if (virNWFilterAddIpAddrForIfname(req->ifname, inetaddr) < 0) {
+                VIR_ERROR(_("Failed to add IP address %s to IP address "
+                          "cache for interface %s"), inetaddr, req->ifname);
+            }
 
             ret = virNWFilterInstantiateFilterLate(NULL,
                                                    req->ifname,
diff --git a/src/nwfilter/nwfilter_learnipaddr.h b/src/nwfilter/nwfilter_learnipaddr.h
index e4b9811a4b..5db9bf8c4b 100644
--- a/src/nwfilter/nwfilter_learnipaddr.h
+++ b/src/nwfilter/nwfilter_learnipaddr.h
@@ -25,6 +25,8 @@
 #ifndef __NWFILTER_LEARNIPADDR_H
 # define __NWFILTER_LEARNIPADDR_H
 
+# include "conf/nwfilter_params.h"
+
 enum howDetect {
   DETECT_DHCP = 1,
   DETECT_STATIC = 2,
@@ -63,8 +65,8 @@ int virNWFilterLearnIPAddress(virNWFilterTechDriverPtr techdriver,
 virNWFilterIPAddrLearnReqPtr virNWFilterLookupLearnReq(int ifindex);
 int virNWFilterTerminateLearnReq(const char *ifname);
 
-void virNWFilterDelIpAddrForIfname(const char *ifname);
-const char *virNWFilterGetIpAddrForIfname(const char *ifname);
+int virNWFilterDelIpAddrForIfname(const char *ifname, const char *ipaddr);
+virNWFilterVarValuePtr virNWFilterGetIpAddrForIfname(const char *ifname);
 
 int virNWFilterLockIface(const char *ifname) ATTRIBUTE_RETURN_CHECK;
 void virNWFilterUnlockIface(const char *ifname);