[ovs-dev,v2] tnl-ports: Add destination IP and MAC address to the match.
diff mbox

Message ID 1441266154-2333-1-git-send-email-pshelar@nicira.com
State Accepted
Headers show

Commit Message

Pravin B Shelar Sept. 3, 2015, 7:42 a.m. UTC
Currently tnl-port table wildcard destination ip and mac addresses
for given tunnel packet.  That could result accepting tunnel
packets destined for other hosts.  Following patch adds
support for matching for ip and mac address.
IP address upates to tnl-port table are piggybacked on
ovs-router updates.

Reported-by: Ben Pfaff <blp@nicira.com>
Signed-off-by: Pravin B Shelar <pshelar@nicira.com>
---
tnl-port table does not depends on /32 route.
---
 lib/dpif-netdev.c        |    2 +
 lib/ovs-router.c         |   33 ++++--
 lib/tnl-ports.c          |  261 ++++++++++++++++++++++++++++++++++++++++++---
 lib/tnl-ports.h          |    3 +
 tests/tunnel-push-pop.at |   26 +++--
 5 files changed, 284 insertions(+), 41 deletions(-)

Comments

Ben Pfaff Sept. 8, 2015, 10:50 p.m. UTC | #1
On Thu, Sep 03, 2015 at 12:42:34AM -0700, Pravin B Shelar wrote:
> Currently tnl-port table wildcard destination ip and mac addresses
> for given tunnel packet.  That could result accepting tunnel
> packets destined for other hosts.  Following patch adds
> support for matching for ip and mac address.
> IP address upates to tnl-port table are piggybacked on
> ovs-router updates.
> 
> Reported-by: Ben Pfaff <blp@nicira.com>
> Signed-off-by: Pravin B Shelar <pshelar@nicira.com>
> ---
> tnl-port table does not depends on /32 route.

Thank you!

I see that the test still adds a /32, is it still necessary?

    dnl First setup dummy interface IP address, then add the route
    dnl so that tnl-port table can get valid IP address for the device.
    AT_CHECK([ovs-appctl netdev-dummy/ip4addr br0 1.1.2.88/32], [0], [OK
    ])

Acked-by: Ben Pfaff <blp@nicira.com>
Pravin B Shelar Sept. 8, 2015, 11:32 p.m. UTC | #2
On Tue, Sep 8, 2015 at 3:50 PM, Ben Pfaff <blp@nicira.com> wrote:
> On Thu, Sep 03, 2015 at 12:42:34AM -0700, Pravin B Shelar wrote:
>> Currently tnl-port table wildcard destination ip and mac addresses
>> for given tunnel packet.  That could result accepting tunnel
>> packets destined for other hosts.  Following patch adds
>> support for matching for ip and mac address.
>> IP address upates to tnl-port table are piggybacked on
>> ovs-router updates.
>>
>> Reported-by: Ben Pfaff <blp@nicira.com>
>> Signed-off-by: Pravin B Shelar <pshelar@nicira.com>
>> ---
>> tnl-port table does not depends on /32 route.
>
> Thank you!
>
> I see that the test still adds a /32, is it still necessary?
>
No, it not necessary. I have reverted it back to /24.

>     dnl First setup dummy interface IP address, then add the route
>     dnl so that tnl-port table can get valid IP address for the device.
>     AT_CHECK([ovs-appctl netdev-dummy/ip4addr br0 1.1.2.88/32], [0], [OK
>     ])
>
> Acked-by: Ben Pfaff <blp@nicira.com>

I pushed it to master.

Thanks.
Jesse Gross Sept. 12, 2015, 2:05 a.m. UTC | #3
On Thu, Sep 3, 2015 at 12:42 AM, Pravin B Shelar <pshelar@nicira.com> wrote:
> Currently tnl-port table wildcard destination ip and mac addresses
> for given tunnel packet.  That could result accepting tunnel
> packets destined for other hosts.  Following patch adds
> support for matching for ip and mac address.
> IP address upates to tnl-port table are piggybacked on
> ovs-router updates.
>
> Reported-by: Ben Pfaff <blp@nicira.com>
> Signed-off-by: Pravin B Shelar <pshelar@nicira.com>

It looks like this patch is causing segfaults if you have existing
tunnels kernel tunnels and you change the datapath type to netdev
while the switch is running. For example, doing:
ovs-vsctl set bridge br0 datapath_type=netdev

Here is the backtrace:

Program terminated with signal 11, Segmentation fault.
#0  tnl_port_map_run () at lib/tnl-ports.c:403
403        if (ip_dev->change_seq == netdev_get_change_seq(ip_dev->dev)) {
(gdb) bt
#0  tnl_port_map_run () at lib/tnl-ports.c:403
#1  0x000000000045e0d8 in dpif_netdev_run (dpif=<optimized out>) at
lib/dpif-netdev.c:2553
#2  0x000000000042cee8 in type_run (type=<optimized out>) at
ofproto/ofproto-dpif.c:531
#3  0x0000000000419d92 in ofproto_type_run (datapath_type=<optimized out>)
    at ofproto/ofproto.c:1655
#4  0x000000000040a9ad in bridge_run__ () at vswitchd/bridge.c:2875
#5  0x000000000040ed6b in bridge_reconfigure (ovs_cfg=<optimized out>)
at vswitchd/bridge.c:700
#6  0x000000000041030d in bridge_run () at vswitchd/bridge.c:2973
#7  0x00000000004067f5 in main (argc=3, argv=0x7ffc9bf197e8) at
vswitchd/ovs-vswitchd.c:120
Pravin B Shelar Sept. 12, 2015, 5:43 p.m. UTC | #4
On Fri, Sep 11, 2015 at 7:05 PM, Jesse Gross <jesse@nicira.com> wrote:
> On Thu, Sep 3, 2015 at 12:42 AM, Pravin B Shelar <pshelar@nicira.com> wrote:
>> Currently tnl-port table wildcard destination ip and mac addresses
>> for given tunnel packet.  That could result accepting tunnel
>> packets destined for other hosts.  Following patch adds
>> support for matching for ip and mac address.
>> IP address upates to tnl-port table are piggybacked on
>> ovs-router updates.
>>
>> Reported-by: Ben Pfaff <blp@nicira.com>
>> Signed-off-by: Pravin B Shelar <pshelar@nicira.com>
>
> It looks like this patch is causing segfaults if you have existing
> tunnels kernel tunnels and you change the datapath type to netdev
> while the switch is running. For example, doing:
> ovs-vsctl set bridge br0 datapath_type=netdev
>
I posted fix for the issue. https://patchwork.ozlabs.org/patch/517105/

Thanks.


> Here is the backtrace:
>
> Program terminated with signal 11, Segmentation fault.
> #0  tnl_port_map_run () at lib/tnl-ports.c:403
> 403        if (ip_dev->change_seq == netdev_get_change_seq(ip_dev->dev)) {
> (gdb) bt
> #0  tnl_port_map_run () at lib/tnl-ports.c:403
> #1  0x000000000045e0d8 in dpif_netdev_run (dpif=<optimized out>) at
> lib/dpif-netdev.c:2553
> #2  0x000000000042cee8 in type_run (type=<optimized out>) at
> ofproto/ofproto-dpif.c:531
> #3  0x0000000000419d92 in ofproto_type_run (datapath_type=<optimized out>)
>     at ofproto/ofproto.c:1655
> #4  0x000000000040a9ad in bridge_run__ () at vswitchd/bridge.c:2875
> #5  0x000000000040ed6b in bridge_reconfigure (ovs_cfg=<optimized out>)
> at vswitchd/bridge.c:700
> #6  0x000000000041030d in bridge_run () at vswitchd/bridge.c:2973
> #7  0x00000000004067f5 in main (argc=3, argv=0x7ffc9bf197e8) at
> vswitchd/ovs-vswitchd.c:120

Patch
diff mbox

diff --git a/lib/dpif-netdev.c b/lib/dpif-netdev.c
index cf5b064..db76290 100644
--- a/lib/dpif-netdev.c
+++ b/lib/dpif-netdev.c
@@ -65,6 +65,7 @@ 
 #include "sset.h"
 #include "timeval.h"
 #include "tnl-arp-cache.h"
+#include "tnl-ports.h"
 #include "unixctl.h"
 #include "util.h"
 #include "openvswitch/vlog.h"
@@ -2549,6 +2550,7 @@  dpif_netdev_run(struct dpif *dpif)
     dp_netdev_pmd_unref(non_pmd);
 
     tnl_arp_cache_run();
+    tnl_port_map_run();
     new_tnl_seq = seq_read(tnl_conf_seq);
 
     if (dp->last_tnl_conf_seq != new_tnl_seq) {
diff --git a/lib/ovs-router.c b/lib/ovs-router.c
index df55bb4..0316a38 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,7 @@  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));
     }
+    tnl_port_map_insert_ipdev(output_bridge);
     seq_change(tnl_conf_seq);
 }
 
@@ -136,12 +138,29 @@  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);
+
+    tnl_port_map_delete_ipdev(p->output_bridge);
+    /* 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 +169,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 +308,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..f510f1e 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,26 @@ 
 static struct ovs_mutex mutex = OVS_MUTEX_INITIALIZER;
 static struct classifier cls;   /* Tunnel ports. */
 
+struct ip_device {
+    struct netdev *dev;
+    struct eth_addr mac;
+    ovs_be32 addr;
+    uint64_t change_seq;
+    struct ovs_list node;
+    char dev_name[IFNAMSIZ];
+};
+
+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 +77,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 +94,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 +119,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 (struct eth_addr));
 
         cls_rule_init(&p->cr, &match, 0); /* Priority == 0. */
         ovs_refcount_init(&p->ref_cnt);
@@ -100,6 +129,34 @@  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(p->port, ip_dev->mac, ip_dev->addr,
+                   p->udp_port, p->dev_name);
+    }
+
+out:
     ovs_mutex_unlock(&mutex);
 }
 
@@ -109,26 +166,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 +224,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 +241,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 +262,161 @@  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_insert_ipdev(struct ip_device *ip_dev)
+{
+    struct tnl_port *p;
+
+    LIST_FOR_EACH(p, node, &port_list) {
+        map_insert(p->port, ip_dev->mac, ip_dev->addr,
+                   p->udp_port, p->dev_name);
+    }
+}
+
+static void
+insert_ipdev(const char dev_name[])
+{
+    struct ip_device *ip_dev;
+    enum netdev_flags flags;
+    struct netdev *dev;
+    int error;
+
+    error = netdev_open(dev_name, NULL, &dev);
+    if (error) {
+        return;
+    }
+
+    error = netdev_get_flags(dev, &flags);
+    if (error || (flags & NETDEV_LOOPBACK)) {
+        netdev_close(dev);
+        return;
+    }
+
+    ip_dev = xzalloc(sizeof *ip_dev);
+    ip_dev->dev = dev;
+    ip_dev->change_seq = netdev_get_change_seq(dev);
+    error = netdev_get_etheraddr(ip_dev->dev, &ip_dev->mac);
+    if (error) {
+        return;
+    }
+    error = netdev_get_in4(ip_dev->dev, (struct in_addr *)&ip_dev->addr, NULL);
+    if (error) {
+        return;
+    }
+    ovs_strlcpy(ip_dev->dev_name, netdev_get_name(dev), sizeof ip_dev->dev_name);
+
+    list_insert(&addr_list, &ip_dev->node);
+    map_insert_ipdev(ip_dev);
+}
+
+static void
+delete_ipdev(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);
+    netdev_close(ip_dev->dev);
+    free(ip_dev);
+}
+
+void
+tnl_port_map_insert_ipdev(const char dev_name[])
+{
+    struct ip_device *ip_dev;
+
+    ovs_mutex_lock(&mutex);
+
+    LIST_FOR_EACH(ip_dev, node, &addr_list) {
+        if (!strcmp(netdev_get_name(ip_dev->dev), dev_name)) {
+            if (ip_dev->change_seq == netdev_get_change_seq(ip_dev->dev)) {
+                goto out;
+            }
+            /* Address changed. */
+            delete_ipdev(ip_dev);
+            break;
+        }
+    }
+    insert_ipdev(dev_name);
+
+out:
+    ovs_mutex_unlock(&mutex);
+}
+
+void
+tnl_port_map_delete_ipdev(const char dev_name[])
+{
+    struct ip_device *ip_dev, *next;
+
+    ovs_mutex_lock(&mutex);
+    LIST_FOR_EACH_SAFE(ip_dev, next, node, &addr_list) {
+        if (!strcmp(netdev_get_name(ip_dev->dev), dev_name)) {
+            delete_ipdev(ip_dev);
+        }
+    }
+    ovs_mutex_unlock(&mutex);
+}
+
+void
+tnl_port_map_run(void)
+{
+    struct ip_device *ip_dev;
+
+    ovs_mutex_lock(&mutex);
+    LIST_FOR_EACH(ip_dev, node, &addr_list) {
+        char dev_name[IFNAMSIZ];
+
+        if (ip_dev->change_seq == netdev_get_change_seq(ip_dev->dev)) {
+            continue;
+        }
+
+        /* Address changed. */
+        ovs_strlcpy(dev_name, ip_dev->dev_name, sizeof dev_name);
+        delete_ipdev(ip_dev);
+        insert_ipdev(dev_name);
+    }
+    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..4195e6a 100644
--- a/lib/tnl-ports.h
+++ b/lib/tnl-ports.h
@@ -30,6 +30,9 @@  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_ipdev(const char dev[]);
+void tnl_port_map_delete_ipdev(const char dev[]);
+void tnl_port_map_run(void);
 
 void tnl_port_map_init(void);
 
diff --git a/tests/tunnel-push-pop.at b/tests/tunnel-push-pop.at
index a34af3f..1267f2d 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\
@@ -27,9 +27,11 @@  dummy@ovs-dummy: hit:0 missed:0
 		t4 5/6081: (geneve: key=123, remote_ip=flow)
 ])
 
-AT_CHECK([ovs-appctl ovs/route/add 1.1.2.92/24 br0], [0], [OK
+dnl First setup dummy interface IP address, then add the route
+dnl so that tnl-port table can get valid IP address for the device.
+AT_CHECK([ovs-appctl netdev-dummy/ip4addr br0 1.1.2.88/32], [0], [OK
 ])
-AT_CHECK([ovs-appctl netdev-dummy/ip4addr br0 1.1.2.88/24], [0], [OK
+AT_CHECK([ovs-appctl ovs/route/add 1.1.2.92/24 br0], [0], [OK
 ])
 
 AT_CHECK([ovs-ofctl add-flow br0 action=normal])
@@ -47,25 +49,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 +109,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 +117,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 +130,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)