diff --git a/lib/ovs-router.c b/lib/ovs-router.c
index df55bb4..9a336e4 100644
--- a/lib/ovs-router.c
+++ b/lib/ovs-router.c
@@ -37,6 +37,7 @@
 #include "ovs-router.h"
 #include "ovs-thread.h"
 #include "route-table.h"
+#include "tnl-ports.h"
 #include "unixctl.h"
 #include "util.h"
 
@@ -126,6 +127,10 @@ ovs_router_insert__(uint8_t priority, ovs_be32 ip_dst, uint8_t plen,
         /* An old rule with the same match was displaced. */
         ovsrcu_postpone(rt_entry_free, ovs_router_entry_cast(cr));
     }
+    if (plen == 32) {
+        tnl_port_map_insert_ipaddr(output_bridge, ip_dst);
+    }
+
     seq_change(tnl_conf_seq);
 }
 
@@ -136,12 +141,31 @@ ovs_router_insert(ovs_be32 ip_dst, uint8_t plen, const char output_bridge[],
     ovs_router_insert__(plen, ip_dst, plen, output_bridge, gw);
 }
 
+
+static bool
+__rt_entry_delete(const struct cls_rule *cr)
+{
+    struct ovs_router_entry *p = ovs_router_entry_cast(cr);
+
+    if (p->plen == 32) {
+        tnl_port_map_delete_ipaddr(p->output_bridge, p->nw_addr);
+    }
+    /* Remove it. */
+    cr = classifier_remove(&cls, cr);
+    if (cr) {
+        ovsrcu_postpone(rt_entry_free, ovs_router_entry_cast(cr));
+        return true;
+    }
+    return false;
+}
+
 static bool
 rt_entry_delete(uint8_t priority, ovs_be32 ip_dst, uint8_t plen)
 {
     const struct cls_rule *cr;
     struct cls_rule rule;
     struct match match;
+    bool res = false;
 
     rt_init_match(&match, ip_dst, plen);
 
@@ -150,17 +174,11 @@ rt_entry_delete(uint8_t priority, ovs_be32 ip_dst, uint8_t plen)
     /* Find the exact rule. */
     cr = classifier_find_rule_exactly(&cls, &rule, CLS_MAX_VERSION);
     if (cr) {
-        /* Remove it. */
         ovs_mutex_lock(&mutex);
-        cr = classifier_remove(&cls, cr);
+        res = __rt_entry_delete(cr);
         ovs_mutex_unlock(&mutex);
-
-        if (cr) {
-            ovsrcu_postpone(rt_entry_free, ovs_router_entry_cast(cr));
-            return true;
-        }
     }
-    return false;
+    return res;
 }
 
 static bool
@@ -295,9 +313,7 @@ ovs_router_flush(void)
     classifier_defer(&cls);
     CLS_FOR_EACH(rt, cr, &cls) {
         if (rt->priority == rt->plen) {
-            if (classifier_remove(&cls, &rt->cr)) {
-                ovsrcu_postpone(rt_entry_free, rt);
-            }
+            __rt_entry_delete(&rt->cr);
         }
     }
     classifier_publish(&cls);
diff --git a/lib/tnl-ports.c b/lib/tnl-ports.c
index 8f7a2a4..532978e 100644
--- a/lib/tnl-ports.c
+++ b/lib/tnl-ports.c
@@ -21,6 +21,7 @@
 #include "classifier.h"
 #include "dynamic-string.h"
 #include "hash.h"
+#include "list.h"
 #include "ofpbuf.h"
 #include "ovs-thread.h"
 #include "odp-util.h"
@@ -33,6 +34,24 @@
 static struct ovs_mutex mutex = OVS_MUTEX_INITIALIZER;
 static struct classifier cls;   /* Tunnel ports. */
 
+struct ip_device {
+    ovs_be32 addr;
+    struct eth_addr mac;
+    char dev_name[IFNAMSIZ];
+    struct ovs_list node;
+};
+
+static struct ovs_list addr_list;
+
+struct tnl_port {
+    odp_port_t port;
+    ovs_be16 udp_port;
+    char dev_name[IFNAMSIZ];
+    struct ovs_list node;
+};
+
+static struct ovs_list port_list;
+
 struct tnl_port_in {
     struct cls_rule cr;
     odp_port_t portno;
@@ -56,10 +75,15 @@ tnl_port_free(struct tnl_port_in *p)
 }
 
 static void
-tnl_port_init_flow(struct flow *flow, ovs_be16 udp_port)
+tnl_port_init_flow(struct flow *flow, struct eth_addr mac,
+                   ovs_be32 addr, ovs_be16 udp_port)
 {
     memset(flow, 0, sizeof *flow);
+
     flow->dl_type = htons(ETH_TYPE_IP);
+    flow->dl_dst = mac;
+    flow->nw_dst = addr;
+
     if (udp_port) {
         flow->nw_proto = IPPROTO_UDP;
     } else {
@@ -68,17 +92,17 @@ tnl_port_init_flow(struct flow *flow, ovs_be16 udp_port)
     flow->tp_dst = udp_port;
 }
 
-void
-tnl_port_map_insert(odp_port_t port, ovs_be16 udp_port, const char dev_name[])
+static void
+map_insert(odp_port_t port, struct eth_addr mac, ovs_be32 addr,
+           ovs_be16 udp_port, const char dev_name[])
 {
     const struct cls_rule *cr;
     struct tnl_port_in *p;
     struct match match;
 
     memset(&match, 0, sizeof match);
-    tnl_port_init_flow(&match.flow, udp_port);
+    tnl_port_init_flow(&match.flow, mac, addr, udp_port);
 
-    ovs_mutex_lock(&mutex);
     do {
         cr = classifier_lookup(&cls, CLS_MAX_VERSION, &match.flow, NULL);
         p = tnl_port_cast(cr);
@@ -93,6 +117,9 @@ tnl_port_map_insert(odp_port_t port, ovs_be16 udp_port, const char dev_name[])
         match.wc.masks.nw_proto = 0xff;
         match.wc.masks.nw_frag = 0xff;      /* XXX: No fragments support. */
         match.wc.masks.tp_dst = OVS_BE16_MAX;
+        match.wc.masks.nw_dst = OVS_BE32_MAX;
+        match.wc.masks.vlan_tci = OVS_BE16_MAX;
+        memset(&match.wc.masks.dl_dst, 0xff, sizeof match.wc.masks.dl_dst);
 
         cls_rule_init(&p->cr, &match, 0); /* Priority == 0. */
         ovs_refcount_init(&p->ref_cnt);
@@ -100,6 +127,33 @@ tnl_port_map_insert(odp_port_t port, ovs_be16 udp_port, const char dev_name[])
 
         classifier_insert(&cls, &p->cr, CLS_MIN_VERSION, NULL, 0);
     }
+}
+
+void
+tnl_port_map_insert(odp_port_t port,
+                    ovs_be16 udp_port, const char dev_name[])
+{
+    struct tnl_port *p;
+    struct ip_device *ip_dev;
+
+    ovs_mutex_lock(&mutex);
+    LIST_FOR_EACH(p, node, &port_list) {
+        if (udp_port == p->udp_port) {
+             goto out;
+        }
+    }
+
+    p = xzalloc(sizeof *p);
+    p->port = port;
+    p->udp_port = udp_port;
+    ovs_strlcpy(p->dev_name, dev_name, sizeof p->dev_name);
+    list_insert(&port_list, &p->node);
+
+    LIST_FOR_EACH(ip_dev, node, &addr_list) {
+        map_insert(port, ip_dev->mac, ip_dev->addr, udp_port, dev_name);
+    }
+
+out:
     ovs_mutex_unlock(&mutex);
 }
 
@@ -109,26 +163,52 @@ tnl_port_unref(const struct cls_rule *cr)
     struct tnl_port_in *p = tnl_port_cast(cr);
 
     if (cr && ovs_refcount_unref_relaxed(&p->ref_cnt) == 1) {
-        ovs_mutex_lock(&mutex);
         if (classifier_remove(&cls, cr)) {
             ovsrcu_postpone(tnl_port_free, p);
         }
-        ovs_mutex_unlock(&mutex);
     }
 }
 
-void
-tnl_port_map_delete(ovs_be16 udp_port)
+static void
+map_delete(struct eth_addr mac, ovs_be32 addr, ovs_be16 udp_port)
 {
     const struct cls_rule *cr;
     struct flow flow;
 
-    tnl_port_init_flow(&flow, udp_port);
+    tnl_port_init_flow(&flow, mac, addr, udp_port);
 
     cr = classifier_lookup(&cls, CLS_MAX_VERSION, &flow, NULL);
     tnl_port_unref(cr);
 }
 
+void
+tnl_port_map_delete(ovs_be16 udp_port)
+{
+    struct tnl_port *p, *next;
+    struct ip_device *ip_dev;
+    bool found = false;
+
+    ovs_mutex_lock(&mutex);
+    LIST_FOR_EACH_SAFE(p, next, node, &port_list) {
+        if (p->udp_port == udp_port) {
+            list_remove(&p->node);
+            found = true;
+            break;
+        }
+    }
+
+    if (!found) {
+        goto out;
+    }
+    LIST_FOR_EACH(ip_dev, node, &addr_list) {
+        map_delete(ip_dev->mac, ip_dev->addr, udp_port);
+    }
+
+    free(p);
+out:
+    ovs_mutex_unlock(&mutex);
+}
+
 /* 'flow' is non-const to allow for temporary modifications during the lookup.
  * Any changes are restored before returning. */
 odp_port_t
@@ -141,13 +221,10 @@ tnl_port_map_lookup(struct flow *flow, struct flow_wildcards *wc)
 }
 
 static void
-tnl_port_show(struct unixctl_conn *conn, int argc OVS_UNUSED,
-              const char *argv[] OVS_UNUSED, void *aux OVS_UNUSED)
+tnl_port_show_v(struct ds *ds)
 {
-    struct ds ds = DS_EMPTY_INITIALIZER;
     const struct tnl_port_in *p;
 
-    ds_put_format(&ds, "Listening ports:\n");
     CLS_FOR_EACH(p, cr, &cls) {
         struct odputil_keybuf keybuf;
         struct odputil_keybuf maskbuf;
@@ -161,7 +238,7 @@ tnl_port_show(struct unixctl_conn *conn, int argc OVS_UNUSED,
             .mask = &wc.masks,
         };
 
-        ds_put_format(&ds, "%s (%"PRIu32") : ", p->dev_name, p->portno);
+        ds_put_format(ds, "%s (%"PRIu32") : ", p->dev_name, p->portno);
         minimask_expand(p->cr.match.mask, &wc);
         miniflow_expand(p->cr.match.flow, &flow);
 
@@ -182,16 +259,125 @@ tnl_port_show(struct unixctl_conn *conn, int argc OVS_UNUSED,
         mask_len = buf.size;
 
         /* build string. */
-        odp_flow_format(key, key_len, mask, mask_len, NULL, &ds, false);
-        ds_put_format(&ds, "\n");
+        odp_flow_format(key, key_len, mask, mask_len, NULL, ds, false);
+        ds_put_format(ds, "\n");
     }
+}
+
+static void
+tnl_port_show(struct unixctl_conn *conn, int argc OVS_UNUSED,
+               const char *argv[] OVS_UNUSED, void *aux OVS_UNUSED)
+{
+    struct ds ds = DS_EMPTY_INITIALIZER;
+    struct tnl_port *p;
+
+    ds_put_format(&ds, "Listening ports:\n");
+    ovs_mutex_lock(&mutex);
+    if (argc > 1) {
+        if (!strcasecmp(argv[1], "-v")) {
+            tnl_port_show_v(&ds);
+            goto out;
+        }
+    }
+
+    LIST_FOR_EACH(p, node, &port_list) {
+        ds_put_format(&ds, "%s (%"PRIu32")\n", p->dev_name, p->port);
+    }
+
+out:
+    ovs_mutex_unlock(&mutex);
     unixctl_command_reply(conn, ds_cstr(&ds));
     ds_destroy(&ds);
 }
 
+static void
+map_delete_ipaddr(struct ip_device *ip_dev)
+{
+    struct tnl_port *p;
+
+    LIST_FOR_EACH(p, node, &port_list) {
+        map_delete(ip_dev->mac, ip_dev->addr, p->udp_port);
+    }
+
+    list_remove(&ip_dev->node);
+    free(ip_dev);
+}
+
+void
+tnl_port_map_insert_ipaddr(const char dev_name[], ovs_be32 addr)
+{
+    struct ip_device *ip_dev, *next;
+    enum netdev_flags flags;
+    struct eth_addr mac;
+    struct tnl_port *p;
+    struct netdev *dev;
+    int error;
+
+    ovs_mutex_lock(&mutex);
+
+    LIST_FOR_EACH_SAFE(ip_dev, next, node, &addr_list) {
+        if (!strcmp(ip_dev->dev_name, dev_name)) {
+            if (ip_dev->addr == addr) {
+                goto err;
+            }
+            /* Address changed. */
+            map_delete_ipaddr(ip_dev);
+            if (!addr) {
+                goto err;
+            }
+        }
+    }
+
+    error = netdev_open(dev_name, "system", &dev);
+    if (error) {
+        goto err;
+    }
+
+    error = netdev_get_flags(dev, &flags);
+    if (error || (flags & NETDEV_LOOPBACK)) {
+        goto err_close;
+    }
+
+    error = netdev_get_etheraddr(dev, &mac);
+    if (error) {
+        goto err_close;
+    }
+
+    ip_dev = xzalloc(sizeof *ip_dev);
+    ip_dev->addr = addr;
+    ip_dev->mac = mac;
+    ovs_strlcpy(ip_dev->dev_name, dev_name, sizeof p->dev_name);
+    list_insert(&addr_list, &ip_dev->node);
+
+    LIST_FOR_EACH(p, node, &port_list) {
+        map_insert(p->port, mac, addr, p->udp_port, p->dev_name);
+    }
+
+err_close:
+    netdev_close(dev); 
+err:
+    ovs_mutex_unlock(&mutex);
+}
+
+void
+tnl_port_map_delete_ipaddr(const char dev_name[], ovs_be32 addr)
+{
+    struct ip_device *ip_dev, *next;
+
+    ovs_mutex_lock(&mutex);
+    LIST_FOR_EACH_SAFE(ip_dev, next, node, &addr_list) {
+        if (ip_dev->addr == addr && !strcmp(ip_dev->dev_name, dev_name)) {
+            map_delete_ipaddr(ip_dev);
+        }
+    }
+    ovs_mutex_unlock(&mutex);
+}
+
 void
 tnl_port_map_init(void)
 {
     classifier_init(&cls, flow_segment_u64s);
-    unixctl_command_register("tnl/ports/show", "", 0, 0, tnl_port_show, NULL);
+    list_init(&addr_list);
+    list_init(&port_list);
+    unixctl_command_register("tnl/ports/show", "-v", 0, 1, tnl_port_show, NULL);
 }
diff --git a/lib/tnl-ports.h b/lib/tnl-ports.h
index 8e4911d..4016720 100644
--- a/lib/tnl-ports.h
+++ b/lib/tnl-ports.h
@@ -30,6 +30,8 @@ void tnl_port_map_insert(odp_port_t port, ovs_be16 udp_port,
                          const char dev_name[]);
 
 void tnl_port_map_delete(ovs_be16 udp_port);
+void tnl_port_map_insert_ipaddr(const char dev[], ovs_be32 addr);
+void tnl_port_map_delete_ipaddr(const char dev[], ovs_be32 addr);
 
 void tnl_port_map_init(void);
 
diff --git a/tests/tunnel-push-pop.at b/tests/tunnel-push-pop.at
index a34af3f..71daaf3 100644
--- a/tests/tunnel-push-pop.at
+++ b/tests/tunnel-push-pop.at
@@ -2,7 +2,7 @@ AT_BANNER([tunnel_push_pop])
 
 AT_SETUP([tunnel_push_pop - action])
 
-OVS_VSWITCHD_START([add-port br0 p0 -- set Interface p0 type=dummy ofport_request=1])
+OVS_VSWITCHD_START([add-port br0 p0 -- set Interface p0 type=dummy ofport_request=1 other-config:hwaddr=aa:55:aa:55:00:00])
 AT_CHECK([ovs-vsctl add-br int-br -- set bridge int-br datapath_type=dummy], [0])
 AT_CHECK([ovs-vsctl add-port int-br t2 -- set Interface t2 type=vxlan \
                        options:remote_ip=1.1.2.92 options:key=123 ofport_request=2\
@@ -29,7 +29,10 @@ dummy@ovs-dummy: hit:0 missed:0
 
 AT_CHECK([ovs-appctl ovs/route/add 1.1.2.92/24 br0], [0], [OK
 ])
-AT_CHECK([ovs-appctl netdev-dummy/ip4addr br0 1.1.2.88/24], [0], [OK
+AT_CHECK([ovs-appctl netdev-dummy/ip4addr br0 1.1.2.88/32], [0], [OK
+])
+
+AT_CHECK([ovs-appctl ovs/route/add 1.1.2.88/32 br0], [0], [OK
 ])
 
 AT_CHECK([ovs-ofctl add-flow br0 action=normal])
@@ -47,25 +50,25 @@ IP               MAC                 Bridge
 
 AT_CHECK([ovs-appctl tnl/ports/show |sort], [0], [dnl
 Listening ports:
-genev_sys_6081 (6081) : eth_type(0x0800),ipv4(proto=17,frag=no),udp(dst=6081)
-gre_sys (3) : eth_type(0x0800),ipv4(proto=47,frag=no)
-vxlan_sys_4789 (4789) : eth_type(0x0800),ipv4(proto=17,frag=no),udp(dst=4789)
+genev_sys_6081 (6081)
+gre_sys (3)
+vxlan_sys_4789 (4789)
 ])
 
 dnl Check VXLAN tunnel pop
-AT_CHECK([ovs-appctl ofproto/trace ovs-dummy 'in_port(1),eth(src=f8:bc:12:44:34:b6,dst=f8:bc:12:46:58:e0),eth_type(0x0800),ipv4(src=1.1.2.92,dst=1.1.2.88,proto=17,tos=0,ttl=64,frag=no),udp(src=51283,dst=4789)'], [0], [stdout])
+AT_CHECK([ovs-appctl ofproto/trace ovs-dummy 'in_port(1),eth(src=f8:bc:12:44:34:b6,dst=aa:55:aa:55:00:00),eth_type(0x0800),ipv4(src=1.1.2.92,dst=1.1.2.88,proto=17,tos=0,ttl=64,frag=no),udp(src=51283,dst=4789)'], [0], [stdout])
 AT_CHECK([tail -1 stdout], [0],
   [Datapath actions: tnl_pop(4789)
 ])
 
 dnl Check GRE tunnel pop
-AT_CHECK([ovs-appctl ofproto/trace ovs-dummy 'in_port(1),eth(src=f8:bc:12:44:34:b6,dst=f8:bc:12:46:58:e0),eth_type(0x0800),ipv4(src=1.1.2.92,dst=1.1.2.88,proto=47,tos=0,ttl=64,frag=no)'], [0], [stdout])
+AT_CHECK([ovs-appctl ofproto/trace ovs-dummy 'in_port(1),eth(src=f8:bc:12:44:34:b6,dst=aa:55:aa:55:00:00),eth_type(0x0800),ipv4(src=1.1.2.92,dst=1.1.2.88,proto=47,tos=0,ttl=64,frag=no)'], [0], [stdout])
 AT_CHECK([tail -1 stdout], [0],
   [Datapath actions: tnl_pop(3)
 ])
 
 dnl Check Geneve tunnel pop
-AT_CHECK([ovs-appctl ofproto/trace ovs-dummy 'in_port(1),eth(src=f8:bc:12:44:34:b6,dst=f8:bc:12:46:58:e0),eth_type(0x0800),ipv4(src=1.1.2.92,dst=1.1.2.88,proto=17,tos=0,ttl=64,frag=no),udp(src=51283,dst=6081)'], [0], [stdout])
+AT_CHECK([ovs-appctl ofproto/trace ovs-dummy 'in_port(1),eth(src=f8:bc:12:44:34:b6,dst=aa:55:aa:55:00:00),eth_type(0x0800),ipv4(src=1.1.2.92,dst=1.1.2.88,proto=17,tos=0,ttl=64,frag=no),udp(src=51283,dst=6081)'], [0], [stdout])
 AT_CHECK([tail -1 stdout], [0],
   [Datapath actions: tnl_pop(6081)
 ])
@@ -107,7 +110,7 @@ AT_CHECK([tail -1 stdout], [0],
 ])
 
 dnl Check decapsulation of GRE packet
-AT_CHECK([ovs-appctl netdev-dummy/receive p0 '001b213cac30001b213cab6408004500007e79464000402f99080101025c0101025820006558000001c8fe71d883724fbeb6f4e1494a080045000054ba200000400184861e0000011e00000200004227e75400030af3195500000000f265010000000000101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f3031323334353637'])
+AT_CHECK([ovs-appctl netdev-dummy/receive p0 'aa55aa550000001b213cab6408004500007e79464000402f99080101025c0101025820006558000001c8fe71d883724fbeb6f4e1494a080045000054ba200000400184861e0000011e00000200004227e75400030af3195500000000f265010000000000101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f3031323334353637'])
 ovs-appctl time/warp 1000
 
 AT_CHECK([ovs-ofctl dump-ports int-br | grep 'port  3'], [0], [dnl
@@ -115,7 +118,7 @@ AT_CHECK([ovs-ofctl dump-ports int-br | grep 'port  3'], [0], [dnl
 ])
 
 dnl Check GRE only accepts encapsulated Ethernet frames
-AT_CHECK([ovs-appctl netdev-dummy/receive p0 '001b213cac30001b213cab6408004500007e79464000402f99080101025c0101025820000800000001c8fe71d883724fbeb6f4e1494a080045000054ba200000400184861e0000011e00000200004227e75400030af3195500000000f265010000000000101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f3031323334353637'])
+AT_CHECK([ovs-appctl netdev-dummy/receive p0 'aa55aa550000001b213cab6408004500007e79464000402f99080101025c0101025820000800000001c8fe71d883724fbeb6f4e1494a080045000054ba200000400184861e0000011e00000200004227e75400030af3195500000000f265010000000000101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f3031323334353637'])
 ovs-appctl time/warp 1000
 
 AT_CHECK([ovs-ofctl dump-ports int-br | grep 'port  3'], [0], [dnl
@@ -128,7 +131,7 @@ AT_CHECK([ovs-ofctl monitor int-br 65534 --detach --no-chdir --pidfile 2> ofctl_
 
 AT_CHECK([ovs-ofctl del-flows int-br])
 AT_CHECK([ovs-ofctl add-flow int-br "tun_metadata0=0xa/0xf,actions=5,controller"])
-AT_CHECK([ovs-appctl netdev-dummy/receive p0 '001b213cac30001b213cab64080045000096794640004011ba630101025c01010258308817c1008200000400655800007b00ffff80010000000affff00010000000bfe71d883724fbeb6f4e1494a080045000054ba200000400184861e0000011e00000200004227e75400030af3195500000000f265010000000000101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f3031323334353637'])
+AT_CHECK([ovs-appctl netdev-dummy/receive p0 'aa55aa550000001b213cab64080045000096794640004011ba630101025c01010258308817c1008200000400655800007b00ffff80010000000affff00010000000bfe71d883724fbeb6f4e1494a080045000054ba200000400184861e0000011e00000200004227e75400030af3195500000000f265010000000000101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f3031323334353637'])
 
 OVS_WAIT_UNTIL([test `wc -l < ofctl_monitor.log` -ge 2])
 OVS_APP_EXIT_AND_WAIT(ovs-ofctl)
