diff --git a/lib/netdev-bsd.c b/lib/netdev-bsd.c
index edf04bf..d7d73af 100644
--- a/lib/netdev-bsd.c
+++ b/lib/netdev-bsd.c
@@ -92,7 +92,6 @@ struct netdev_bsd {
     struct eth_addr etheraddr;
     struct in_addr in4;
     struct in_addr netmask;
-    struct in6_addr in6;
     int mtu;
     int carrier;
 
@@ -1244,37 +1243,20 @@ netdev_bsd_set_in4(struct netdev *netdev_, struct in_addr addr,
 }
 
 static int
-netdev_bsd_get_in6(const struct netdev *netdev_, struct in6_addr *in6)
+netdev_bsd_get_addr_list(const struct netdev *netdev_,
+                         struct in6_addr **addr, struct in6_addr **mask, int *n_cnt)
 {
     struct netdev_bsd *netdev = netdev_bsd_cast(netdev_);
-    if (!(netdev->cache_valid & VALID_IN6)) {
-        struct ifaddrs *ifa, *head;
-        struct sockaddr_in6 *sin6;
-        const char *netdev_name = netdev_get_name(netdev_);
-
-        if (getifaddrs(&head) != 0) {
-            VLOG_ERR("getifaddrs on %s device failed: %s", netdev_name,
-                    ovs_strerror(errno));
-            return errno;
-        }
+    int error;
 
-        for (ifa = head; ifa; ifa = ifa->ifa_next) {
-            if (ifa->ifa_addr->sa_family == AF_INET6 &&
-                    !strcmp(ifa->ifa_name, netdev_name)) {
-                sin6 = ALIGNED_CAST(struct sockaddr_in6 *, ifa->ifa_addr);
-                if (sin6) {
-                    memcpy(&netdev->in6, &sin6->sin6_addr, sin6->sin6_len);
-                    netdev->cache_valid |= VALID_IN6;
-                    *in6 = netdev->in6;
-                    freeifaddrs(head);
-                    return 0;
-                }
-            }
-        }
-        return EADDRNOTAVAIL;
+    if (!(netdev->cache_valid & VALID_IN6)) {
+        netdev_get_addrs_list_flush();
     }
-    *in6 = netdev->in6;
-    return 0;
+    error = netdev_get_addrs(netdev_get_name(netdev_), addr, mask, n_cnt);
+    if (!error) {
+        netdev->cache_valid |= VALID_IN6;
+    }
+    return error;
 }
 
 #if defined(__NetBSD__)
@@ -1595,7 +1577,7 @@ netdev_bsd_update_flags(struct netdev *netdev_, enum netdev_flags off,
                                                      \
     netdev_bsd_get_in4,                              \
     netdev_bsd_set_in4,                              \
-    netdev_bsd_get_in6,                              \
+    netdev_bsd_get_addr_list,                        \
     NULL, /* add_router */                           \
     netdev_bsd_get_next_hop,                         \
     NULL, /* get_status */                           \
diff --git a/lib/netdev-dpdk.c b/lib/netdev-dpdk.c
index f402354..78e8f0b 100644
--- a/lib/netdev-dpdk.c
+++ b/lib/netdev-dpdk.c
@@ -2689,7 +2689,7 @@ static const struct dpdk_qos_ops egress_policer_ops = {
                                                               \
     NULL,                       /* get_in4 */                 \
     NULL,                       /* set_in4 */                 \
-    NULL,                       /* get_in6 */                 \
+    NULL,                       /* get_addr_list */           \
     NULL,                       /* add_router */              \
     NULL,                       /* get_next_hop */            \
     GET_STATUS,                                               \
diff --git a/lib/netdev-dummy.c b/lib/netdev-dummy.c
index ccd4a0a..9f8bed4 100644
--- a/lib/netdev-dummy.c
+++ b/lib/netdev-dummy.c
@@ -116,7 +116,7 @@ struct netdev_dummy {
     FILE *tx_pcap, *rxq_pcap OVS_GUARDED;
 
     struct in_addr address, netmask;
-    struct in6_addr ipv6;
+    struct in6_addr ipv6, ipv6_mask;
     struct ovs_list rxes OVS_GUARDED; /* List of child "netdev_rxq_dummy"s. */
 };
 
@@ -731,15 +731,50 @@ netdev_dummy_get_in4(const struct netdev *netdev_,
 }
 
 static int
-netdev_dummy_get_in6(const struct netdev *netdev_, struct in6_addr *in6)
+netdev_dummy_get_addr_list(const struct netdev *netdev_, struct in6_addr **paddr,
+                           struct in6_addr **pmask, int *n_addr)
 {
     struct netdev_dummy *netdev = netdev_dummy_cast(netdev_);
+    int cnt = 0, i = 0, err = 0;
+    struct in6_addr *addr, *mask;
 
     ovs_mutex_lock(&netdev->mutex);
-    *in6 = netdev->ipv6;
+    if (netdev->address.s_addr != INADDR_ANY) {
+        cnt++;
+    }
+
+    if (ipv6_addr_is_set(&netdev->ipv6)) {
+        cnt++;
+    }
+    if (!cnt) {
+        err = EADDRNOTAVAIL;
+        goto out;
+    }
+    addr = xmalloc(sizeof *addr * cnt);
+    mask = xmalloc(sizeof *mask * cnt);
+    if (netdev->address.s_addr != INADDR_ANY) {
+        in6_addr_set_mapped_ipv4(&addr[i], netdev->address.s_addr);
+        in6_addr_set_mapped_ipv4(&mask[i], netdev->netmask.s_addr);
+        i++;
+    }
+
+    if (ipv6_addr_is_set(&netdev->ipv6)) {
+        memcpy(&addr[i], &netdev->ipv6, sizeof *addr);
+        memcpy(&mask[i], &netdev->ipv6_mask, sizeof *mask);
+        i++;
+    }
+    if (paddr) {
+        *paddr = addr;
+        *pmask = mask;
+        *n_addr = cnt;
+    } else {
+        free(addr);
+        free(mask);
+    }
+out:
     ovs_mutex_unlock(&netdev->mutex);
 
-    return ipv6_addr_is_set(in6) ? 0 : EADDRNOTAVAIL;
+    return err;
 }
 
 static int
@@ -757,12 +792,14 @@ netdev_dummy_set_in4(struct netdev *netdev_, struct in_addr address,
 }
 
 static int
-netdev_dummy_set_in6(struct netdev *netdev_, struct in6_addr *in6)
+netdev_dummy_set_in6(struct netdev *netdev_, struct in6_addr *in6,
+                     struct in6_addr *mask)
 {
     struct netdev_dummy *netdev = netdev_dummy_cast(netdev_);
 
     ovs_mutex_lock(&netdev->mutex);
     netdev->ipv6 = *in6;
+    netdev->ipv6_mask = *mask;
     ovs_mutex_unlock(&netdev->mutex);
 
     return 0;
@@ -1244,7 +1281,7 @@ static const struct netdev_class dummy_class = {
 
     netdev_dummy_get_in4,       /* get_in4 */
     NULL,                       /* set_in4 */
-    netdev_dummy_get_in6,       /* get_in6 */
+    netdev_dummy_get_addr_list,
     NULL,                       /* add_router */
     NULL,                       /* get_next_hop */
     NULL,                       /* get_status */
@@ -1537,15 +1574,20 @@ netdev_dummy_ip6addr(struct unixctl_conn *conn, int argc OVS_UNUSED,
     struct netdev *netdev = netdev_from_name(argv[1]);
 
     if (netdev && is_dummy_class(netdev->netdev_class)) {
-        char ip6_s[IPV6_SCAN_LEN + 1];
         struct in6_addr ip6;
+        char *error;
+        uint32_t plen;
 
-        if (ovs_scan(argv[2], IPV6_SCAN_FMT, ip6_s) &&
-            inet_pton(AF_INET6, ip6_s, &ip6) == 1) {
-            netdev_dummy_set_in6(netdev, &ip6);
+        error = ipv6_parse_cidr(argv[2], &ip6, &plen);
+        if (!error) {
+            struct in6_addr mask;
+
+            mask = ipv6_create_mask(plen);
+            netdev_dummy_set_in6(netdev, &ip6, &mask);
             unixctl_command_reply(conn, "OK");
         } else {
-            unixctl_command_reply_error(conn, "Invalid parameters");
+            unixctl_command_reply_error(conn, error);
+            free(error);
         }
         netdev_close(netdev);
     } else {
diff --git a/lib/netdev-linux.c b/lib/netdev-linux.c
index 570677e..7068493 100644
--- a/lib/netdev-linux.c
+++ b/lib/netdev-linux.c
@@ -483,7 +483,6 @@ struct netdev_linux {
     int ifindex;
     struct eth_addr etheraddr;
     struct in_addr address, netmask;
-    struct in6_addr in6;
     int mtu;
     unsigned int ifi_flags;
     long long int carrier_resets;
@@ -727,6 +726,9 @@ netdev_linux_changed(struct netdev_linux *dev,
     dev->ifi_flags = ifi_flags;
 
     dev->cache_valid &= mask;
+    if (!(mask & (VALID_IN4 | VALID_IN6))) {
+        netdev_get_addrs_list_flush();
+    }
 }
 
 static void
@@ -2535,61 +2537,18 @@ netdev_linux_set_in4(struct netdev *netdev_, struct in_addr address,
     return error;
 }
 
-static bool
-parse_if_inet6_line(const char *line,
-                    struct in6_addr *in6, char ifname[16 + 1])
-{
-    uint8_t *s6 = in6->s6_addr;
-#define X8 "%2"SCNx8
-    return ovs_scan(line,
-                    " "X8 X8 X8 X8 X8 X8 X8 X8 X8 X8 X8 X8 X8 X8 X8 X8
-                    "%*x %*x %*x %*x %16s\n",
-                    &s6[0], &s6[1], &s6[2], &s6[3],
-                    &s6[4], &s6[5], &s6[6], &s6[7],
-                    &s6[8], &s6[9], &s6[10], &s6[11],
-                    &s6[12], &s6[13], &s6[14], &s6[15],
-                    ifname);
-}
-
 /* If 'netdev' has an assigned IPv6 address, sets '*in6' to that address.
  * Otherwise, sets '*in6' to 'in6addr_any' and returns the corresponding
  * error. */
 static int
-netdev_linux_get_in6(const struct netdev *netdev_, struct in6_addr *in6)
+netdev_linux_get_addr_list(const struct netdev *netdev_,
+                          struct in6_addr **addr, struct in6_addr **mask, int *n_cnt)
 {
     struct netdev_linux *netdev = netdev_linux_cast(netdev_);
     int error;
 
     ovs_mutex_lock(&netdev->mutex);
-    if (!(netdev->cache_valid & VALID_IN6)) {
-        FILE *file;
-        char line[128];
-
-        netdev->in6 = in6addr_any;
-        netdev->in6_error = EADDRNOTAVAIL;
-
-        file = fopen("/proc/net/if_inet6", "r");
-        if (file != NULL) {
-            const char *name = netdev_get_name(netdev_);
-            while (fgets(line, sizeof line, file)) {
-                struct in6_addr in6_tmp;
-                char ifname[16 + 1];
-                if (parse_if_inet6_line(line, &in6_tmp, ifname)
-                    && !strcmp(name, ifname))
-                {
-                    netdev->in6 = in6_tmp;
-                    netdev->in6_error = 0;
-                    break;
-                }
-            }
-            fclose(file);
-        } else {
-            netdev->in6_error = EOPNOTSUPP;
-        }
-        netdev->cache_valid |= VALID_IN6;
-    }
-    *in6 = netdev->in6;
-    error = netdev->in6_error;
+    error = netdev_get_addrs(netdev_get_name(netdev_), addr, mask, n_cnt);
     ovs_mutex_unlock(&netdev->mutex);
 
     return error;
@@ -2890,7 +2849,7 @@ netdev_linux_update_flags(struct netdev *netdev_, enum netdev_flags off,
                                                                 \
     netdev_linux_get_in4,                                       \
     netdev_linux_set_in4,                                       \
-    netdev_linux_get_in6,                                       \
+    netdev_linux_get_addr_list,                                 \
     netdev_linux_add_router,                                    \
     netdev_linux_get_next_hop,                                  \
     GET_STATUS,                                                 \
diff --git a/lib/netdev-provider.h b/lib/netdev-provider.h
index 1952a02..99d7f5d 100644
--- a/lib/netdev-provider.h
+++ b/lib/netdev-provider.h
@@ -636,7 +636,11 @@ struct netdev_class {
     int (*set_in4)(struct netdev *netdev, struct in_addr addr,
                    struct in_addr mask);
 
-    /* If 'netdev' has an assigned IPv6 address, sets '*in6' to that address.
+    /* Returns all assigned IP address to  'netdev' and returns 0.
+     * API allocates array of address and masks and set it to
+     * '*addr' and '*mask'.
+     * Otherwise, returns a positive errno value and sets '*addr', '*mask
+     * and '*n_addr' to NULL.
      *
      * The following error values have well-defined meanings:
      *
@@ -644,9 +648,9 @@ struct netdev_class {
      *
      *   - EOPNOTSUPP: No IPv6 network stack attached to 'netdev'.
      *
-     * This function may be set to null if it would always return EOPNOTSUPP
-     * anyhow. */
-    int (*get_in6)(const struct netdev *netdev, struct in6_addr *in6);
+     * 'addr' may be null, in which case the address itself is not reported. */
+    int (*get_addr_list)(const struct netdev *netdev, struct in6_addr **in,
+                         struct in6_addr **mask, int *n_in6);
 
     /* Adds 'router' as a default IP gateway for the TCP/IP stack that
      * corresponds to 'netdev'.
diff --git a/lib/netdev-vport.c b/lib/netdev-vport.c
index df6d8cf..0d1b899 100644
--- a/lib/netdev-vport.c
+++ b/lib/netdev-vport.c
@@ -319,7 +319,7 @@ tunnel_check_status_change__(struct netdev_vport *netdev)
 
     iface[0] = '\0';
     route = &netdev->tnl_cfg.ipv6_dst;
-    if (ovs_router_lookup(route, iface, &gw)) {
+    if (ovs_router_lookup(route, iface, NULL, &gw)) {
         struct netdev *egress_netdev;
 
         if (!netdev_open(iface, "system", &egress_netdev)) {
@@ -1530,7 +1530,7 @@ netdev_vport_range(struct unixctl_conn *conn, int argc,
                                                             \
     NULL,                       /* get_in4 */               \
     NULL,                       /* set_in4 */               \
-    NULL,                       /* get_in6 */               \
+    NULL,                       /* get_addr_list */         \
     NULL,                       /* add_router */            \
     NULL,                       /* get_next_hop */          \
     GET_STATUS,                                             \
diff --git a/lib/netdev.c b/lib/netdev.c
index 150f8d8..c6fadb4 100644
--- a/lib/netdev.c
+++ b/lib/netdev.c
@@ -24,6 +24,13 @@
 #include <string.h>
 #include <unistd.h>
 
+#ifndef _WIN32
+#include <ifaddrs.h>
+#include <net/if.h>
+#include <sys/ioctl.h>
+#include <sys/types.h>
+#endif
+
 #include "coverage.h"
 #include "dpif.h"
 #include "dp-packet.h"
@@ -45,6 +52,7 @@
 #include "svec.h"
 #include "openvswitch/vlog.h"
 #include "flow.h"
+#include "util.h"
 
 VLOG_DEFINE_THIS_MODULE(netdev);
 
@@ -1128,9 +1136,11 @@ netdev_get_status(const struct netdev *netdev, struct smap *smap)
             : EOPNOTSUPP);
 }
 
-/* If 'netdev' has an assigned IPv6 address, sets '*in6' to that address and
- * returns 0.  Otherwise, returns a positive errno value and sets '*in6' to
- * all-zero-bits (in6addr_any).
+/* Returns all assigned IP address to  'netdev' and returns 0.
+ * API allocates array of address and masks and set it to
+ * '*addr' and '*mask'.
+ * Otherwise, returns a positive errno value and sets '*addr', '*mask
+ * and '*n_addr' to NULL.
  *
  * The following error values have well-defined meanings:
  *
@@ -1138,20 +1148,21 @@ netdev_get_status(const struct netdev *netdev, struct smap *smap)
  *
  *   - EOPNOTSUPP: No IPv6 network stack attached to 'netdev'.
  *
- * 'in6' may be null, in which case the address itself is not reported. */
+ * 'addr' may be null, in which case the address itself is not reported. */
 int
-netdev_get_in6(const struct netdev *netdev, struct in6_addr *in6)
+netdev_get_addr_list(const struct netdev *netdev, struct in6_addr **addr,
+                     struct in6_addr **mask, int *n_addr)
 {
-    struct in6_addr dummy;
     int error;
 
-    error = (netdev->netdev_class->get_in6
-             ? netdev->netdev_class->get_in6(netdev,
-                    in6 ? in6 : &dummy)
-             : EOPNOTSUPP);
-    if (error && in6) {
-        memset(in6, 0, sizeof *in6);
+    error = (netdev->netdev_class->get_addr_list
+             ? netdev->netdev_class->get_addr_list(netdev, addr, mask, n_addr): EOPNOTSUPP);
+    if (error && addr) {
+        *addr = NULL;
+        *mask = NULL;
+        *n_addr = 0;
     }
+
     return error;
 }
 
@@ -1854,3 +1865,95 @@ netdev_get_change_seq(const struct netdev *netdev)
 {
     return netdev->change_seq;
 }
+
+#ifndef _WIN32
+/* This implementation is shared by Linux and BSD. */
+
+static struct ifaddrs *if_addr_list;
+static struct ovs_mutex if_addr_list_lock = OVS_MUTEX_INITIALIZER;
+
+void
+netdev_get_addrs_list_flush(void)
+{
+    ovs_mutex_lock(&if_addr_list_lock);
+    if (if_addr_list) {
+        freeifaddrs(if_addr_list);
+        if_addr_list = NULL;
+    }
+    ovs_mutex_unlock(&if_addr_list_lock);
+}
+
+int
+netdev_get_addrs(const char dev[], struct in6_addr **paddr,
+                 struct in6_addr **pmask, int *n_in)
+{
+    struct in6_addr *addr_array, *mask_array;
+    const struct ifaddrs *ifa;
+    int cnt = 0, i = 0;
+
+    ovs_mutex_lock(&if_addr_list_lock);
+    if (!if_addr_list) {
+        int err;
+
+        err = getifaddrs(&if_addr_list);
+        if (err) {
+            ovs_mutex_unlock(&if_addr_list_lock);
+            return -err;
+        }
+    }
+
+    for (ifa = if_addr_list; ifa; ifa = ifa->ifa_next) {
+        int family;
+
+        family = ifa->ifa_addr->sa_family;
+        if (family == AF_INET || family == AF_INET6) {
+            if (!strncmp(ifa->ifa_name, dev, IFNAMSIZ)) {
+                cnt++;
+            }
+        }
+    }
+
+    if (!cnt) {
+        ovs_mutex_unlock(&if_addr_list_lock);
+        return EADDRNOTAVAIL;
+    }
+    addr_array = xzalloc(sizeof *addr_array * cnt);
+    mask_array = xzalloc(sizeof *mask_array * cnt);
+    for (ifa = if_addr_list; ifa; ifa = ifa->ifa_next) {
+        int family;
+
+        if (strncmp(ifa->ifa_name, dev, IFNAMSIZ)) {
+            continue;
+        }
+
+        family = ifa->ifa_addr->sa_family;
+        if (family == AF_INET) {
+            const struct sockaddr_in *sin;
+
+            sin = ALIGNED_CAST(const struct sockaddr_in *, ifa->ifa_addr);
+            in6_addr_set_mapped_ipv4(&addr_array[i], sin->sin_addr.s_addr);
+            sin = (struct sockaddr_in *) &ifa->ifa_netmask;
+            in6_addr_set_mapped_ipv4(&mask_array[i], sin->sin_addr.s_addr);
+            i++;
+        } else if (family == AF_INET6) {
+            const struct sockaddr_in6 *sin6;
+
+            sin6 = ALIGNED_CAST(const struct sockaddr_in6 *, ifa->ifa_addr);
+            memcpy(&addr_array[i], &sin6->sin6_addr, sizeof *addr_array);
+            sin6 = (struct sockaddr_in6 *) &ifa->ifa_netmask;
+            memcpy(&mask_array[i], &sin6->sin6_addr, sizeof *mask_array);
+            i++;
+        }
+    }
+    ovs_mutex_unlock(&if_addr_list_lock);
+    if (paddr) {
+        *n_in = cnt;
+        *paddr = addr_array;
+        *pmask = mask_array;
+    } else {
+        free(addr_array);
+        free(mask_array);
+    }
+    return 0;
+}
+#endif
diff --git a/lib/netdev.h b/lib/netdev.h
index a81989e..1d2725d 100644
--- a/lib/netdev.h
+++ b/lib/netdev.h
@@ -256,7 +256,9 @@ int netdev_get_in4(const struct netdev *, struct in_addr *address,
                    struct in_addr *netmask);
 int netdev_set_in4(struct netdev *, struct in_addr addr, struct in_addr mask);
 int netdev_get_in4_by_name(const char *device_name, struct in_addr *in4);
-int netdev_get_in6(const struct netdev *, struct in6_addr *);
+int netdev_get_addr_list(const struct netdev *netdev, struct in6_addr **addr,
+                         struct in6_addr **mask, int *n_in6);
+
 int netdev_add_router(struct netdev *, struct in_addr router);
 int netdev_get_next_hop(const struct netdev *, const struct in_addr *host,
                         struct in_addr *next_hop, char **);
@@ -343,6 +345,12 @@ int netdev_dump_queue_stats(const struct netdev *,
 enum { NETDEV_MAX_BURST = 32 }; /* Maximum number packets in a batch. */
 extern struct seq *tnl_conf_seq;
 
+#ifndef _WIN32
+void netdev_get_addrs_list_flush(void);
+int netdev_get_addrs(const char dev[], struct in6_addr **paddr,
+                     struct in6_addr **pmask, int *n_in6);
+#endif
+
 #ifdef  __cplusplus
 }
 #endif
diff --git a/lib/ovs-router.c b/lib/ovs-router.c
index d416bdb..9eaa128 100644
--- a/lib/ovs-router.c
+++ b/lib/ovs-router.c
@@ -42,6 +42,9 @@
 #include "tnl-ports.h"
 #include "unixctl.h"
 #include "util.h"
+#include "unaligned.h"
+#include "unixctl.h"
+#include "util.h"
 
 static struct ovs_mutex mutex = OVS_MUTEX_INITIALIZER;
 static struct classifier cls;
@@ -51,6 +54,7 @@ struct ovs_router_entry {
     char output_bridge[IFNAMSIZ];
     struct in6_addr gw;
     struct in6_addr nw_addr;
+    struct in6_addr src_addr;
     uint8_t plen;
     uint8_t priority;
 };
@@ -67,7 +71,7 @@ ovs_router_entry_cast(const struct cls_rule *cr)
 
 bool
 ovs_router_lookup(const struct in6_addr *ip6_dst, char output_bridge[],
-                  struct in6_addr *gw)
+                  struct in6_addr *src, struct in6_addr *gw)
 {
     const struct cls_rule *cr;
     struct flow flow = {.ipv6_dst = *ip6_dst};
@@ -78,6 +82,9 @@ ovs_router_lookup(const struct in6_addr *ip6_dst, char output_bridge[],
 
         ovs_strlcpy(output_bridge, p->output_bridge, IFNAMSIZ);
         *gw = p->gw;
+        if (src) {
+            *src = p->src_addr;
+        }
         return true;
     }
     return false;
@@ -89,7 +96,7 @@ ovs_router_lookup4(ovs_be32 ip_dst, char output_bridge[], ovs_be32 *gw)
     struct in6_addr ip6_dst = in6_addr_mapped_ipv4(ip_dst);
     struct in6_addr gw6;
 
-    if (ovs_router_lookup(&ip6_dst, output_bridge, &gw6)) {
+    if (ovs_router_lookup(&ip6_dst, output_bridge, NULL, &gw6)) {
         *gw = in6_addr_get_mapped_ipv4(&gw6);
         return true;
     }
@@ -117,7 +124,48 @@ static void rt_init_match(struct match *match, const struct in6_addr *ip6_dst,
     match->wc.masks.ipv6_dst = mask;
 }
 
-static void
+static int
+get_src_addr(const struct in6_addr *ip6_dst,
+             const char output_bridge[], struct in6_addr *psrc)
+{
+    struct in6_addr *mask, *addr6;
+    int err, n_in6, i, max_plen = -1;
+    struct netdev *dev;
+
+    err = netdev_open(output_bridge, NULL, &dev);
+    if (err) {
+        return err;
+    }
+
+    err = netdev_get_addr_list(dev, &addr6, &mask, &n_in6);
+    if (err) {
+        goto out;
+    }
+
+    for (i = 0; i < n_in6; i++) {
+        struct in6_addr a1, a2;
+        int mask_bits;
+
+        a1 = ipv6_addr_bitand(ip6_dst, &mask[i]);
+        a2 = ipv6_addr_bitand(&addr6[i], &mask[i]);
+        mask_bits = bitmap_count1(ALIGNED_CAST(const unsigned long *, &mask[i]), 128);
+
+        if (!memcmp(&a1, &a2, sizeof (a1)) && mask_bits > max_plen) {
+            *psrc = addr6[i];
+            max_plen = mask_bits;
+        }
+    }
+    if (max_plen == -1) {
+        err = ENOENT;
+    }
+out:
+    free(addr6);
+    free(mask);
+    netdev_close(dev);
+    return err;
+}
+
+static int
 ovs_router_insert__(uint8_t priority, const struct in6_addr *ip6_dst,
                     uint8_t plen, const char output_bridge[],
                     const struct in6_addr *gw)
@@ -125,6 +173,7 @@ ovs_router_insert__(uint8_t priority, const struct in6_addr *ip6_dst,
     const struct cls_rule *cr;
     struct ovs_router_entry *p;
     struct match match;
+    int err;
 
     rt_init_match(&match, ip6_dst, plen);
 
@@ -136,6 +185,10 @@ ovs_router_insert__(uint8_t priority, const struct in6_addr *ip6_dst,
     p->nw_addr = match.flow.ipv6_dst;
     p->plen = plen;
     p->priority = priority;
+    err = get_src_addr(ip6_dst, output_bridge, &p->src_addr);
+    if (err) {
+        return err;
+    }
     /* Longest prefix matches first. */
     cls_rule_init(&p->cr, &match, priority);
 
@@ -149,6 +202,7 @@ ovs_router_insert__(uint8_t priority, const struct in6_addr *ip6_dst,
     }
     tnl_port_map_insert_ipdev(output_bridge);
     seq_change(tnl_conf_seq);
+    return 0;
 }
 
 void
@@ -226,6 +280,7 @@ ovs_router_add(struct unixctl_conn *conn, int argc,
     unsigned int plen;
     struct in6_addr ip6;
     struct in6_addr gw6;
+    int err;
 
     if (scan_ipv4_route(argv[1], &ip, &plen)) {
         ovs_be32 gw = 0;
@@ -246,8 +301,12 @@ ovs_router_add(struct unixctl_conn *conn, int argc,
         unixctl_command_reply_error(conn, "Invalid parameters");
         return;
     }
-    ovs_router_insert__(plen + 32, &ip6, plen, argv[2], &gw6);
-    unixctl_command_reply(conn, "OK");
+    err = ovs_router_insert__(plen + 32, &ip6, plen, argv[2], &gw6);
+    if (err) {
+        unixctl_command_reply(conn, "Error while inserting route.");
+    } else {
+        unixctl_command_reply(conn, "OK");
+    }
 }
 
 static void
@@ -298,6 +357,8 @@ ovs_router_show(struct unixctl_conn *conn, int argc OVS_UNUSED,
             ds_put_format(&ds, " GW ");
             ipv6_format_mapped(&rt->gw, &ds);
         }
+        ds_put_format(&ds, " SRC ");
+        ipv6_format_mapped(&rt->src_addr, &ds);
         ds_put_format(&ds, "\n");
     }
     unixctl_command_reply(conn, ds_cstr(&ds));
@@ -312,7 +373,7 @@ ovs_router_lookup_cmd(struct unixctl_conn *conn, int argc OVS_UNUSED,
     struct in6_addr ip6;
     unsigned int plen;
     char iface[IFNAMSIZ];
-    struct in6_addr gw;
+    struct in6_addr gw, src;
 
     if (scan_ipv4_route(argv[1], &ip, &plen) && plen == 32) {
         in6_addr_set_mapped_ipv4(&ip6, ip);
@@ -321,10 +382,12 @@ ovs_router_lookup_cmd(struct unixctl_conn *conn, int argc OVS_UNUSED,
         return;
     }
 
-    if (ovs_router_lookup(&ip6, iface, &gw)) {
+    if (ovs_router_lookup(&ip6, iface, &src, &gw)) {
         struct ds ds = DS_EMPTY_INITIALIZER;
+        ds_put_format(&ds, "src ");
+        ipv6_format_mapped(&src, &ds);
         ds_put_format(&ds, "gateway ");
-        ipv6_format_mapped(&ip6, &ds);
+        ipv6_format_mapped(&gw, &ds);
         ds_put_format(&ds, "\ndev %s\n", iface);
         unixctl_command_reply(conn, ds_cstr(&ds));
         ds_destroy(&ds);
diff --git a/lib/ovs-router.h b/lib/ovs-router.h
index c23d554..9ac61ce 100644
--- a/lib/ovs-router.h
+++ b/lib/ovs-router.h
@@ -26,7 +26,7 @@ extern "C" {
 #endif
 
 bool ovs_router_lookup(const struct in6_addr *ip_dst, char out_dev[],
-                        struct in6_addr *gw);
+                       struct in6_addr *src, struct in6_addr *gw);
 bool ovs_router_lookup4(ovs_be32 ip_dst, char out_dev[], ovs_be32 *gw);
 void ovs_router_init(void);
 void ovs_router_insert(const struct in6_addr *ip_dst, uint8_t plen,
diff --git a/lib/route-table.c b/lib/route-table.c
index 9dc2038..649b656 100644
--- a/lib/route-table.c
+++ b/lib/route-table.c
@@ -276,8 +276,6 @@ route_table_parse(struct ofpbuf *buf, struct route_table_msg *change)
                 change->rd.rta_gw = nl_attr_get_in6_addr(attrs[RTA_GATEWAY]);
             }
         }
-
-
     } else {
         VLOG_DBG_RL(&rl, "received unparseable rtnetlink route message");
     }
diff --git a/lib/tnl-ports.c b/lib/tnl-ports.c
index e7f2066..4f9ae9e 100644
--- a/lib/tnl-ports.c
+++ b/lib/tnl-ports.c
@@ -40,8 +40,8 @@ static struct classifier cls;   /* Tunnel ports. */
 struct ip_device {
     struct netdev *dev;
     struct eth_addr mac;
-    ovs_be32 addr4;
-    struct in6_addr addr6;
+    struct in6_addr *addr;
+    int n_addr;
     uint64_t change_seq;
     struct ovs_list node;
     char dev_name[IFNAMSIZ];
@@ -150,6 +150,20 @@ map_insert(odp_port_t port, struct eth_addr mac, struct in6_addr *addr,
     }
 }
 
+static void
+map_insert_ipdev__(struct ip_device *ip_dev, char dev_name[],
+                   odp_port_t port, ovs_be16 udp_port)
+{
+    if (ip_dev->n_addr) {
+        int i;
+
+        for (i = 0; i < ip_dev->n_addr; i++) {
+            map_insert(port, ip_dev->mac, &ip_dev->addr[i],
+                       udp_port, dev_name);
+        }
+    }
+}
+
 void
 tnl_port_map_insert(odp_port_t port,
                     ovs_be16 udp_port, const char dev_name[])
@@ -171,15 +185,7 @@ tnl_port_map_insert(odp_port_t port,
     list_insert(&port_list, &p->node);
 
     LIST_FOR_EACH(ip_dev, node, &addr_list) {
-        if (ip_dev->addr4 != INADDR_ANY) {
-            struct in6_addr addr4 = in6_addr_mapped_ipv4(ip_dev->addr4);
-            map_insert(p->port, ip_dev->mac, &addr4,
-                       p->udp_port, p->dev_name);
-        }
-        if (ipv6_addr_is_set(&ip_dev->addr6)) {
-            map_insert(p->port, ip_dev->mac, &ip_dev->addr6,
-                       p->udp_port, p->dev_name);
-        }
+        map_insert_ipdev__(ip_dev, p->dev_name, p->port, p->udp_port);
     }
 
 out:
@@ -210,6 +216,18 @@ map_delete(struct eth_addr mac, struct in6_addr *addr, ovs_be16 udp_port)
     tnl_port_unref(cr);
 }
 
+static void
+ipdev_map_delete(struct ip_device *ip_dev, ovs_be16 udp_port)
+{
+    if (ip_dev->n_addr) {
+        int i;
+
+        for (i = 0; i < ip_dev->n_addr; i++) {
+            map_delete(ip_dev->mac, &ip_dev->addr[i], udp_port);
+        }
+    }
+}
+
 void
 tnl_port_map_delete(ovs_be16 udp_port)
 {
@@ -230,13 +248,7 @@ tnl_port_map_delete(ovs_be16 udp_port)
         goto out;
     }
     LIST_FOR_EACH(ip_dev, node, &addr_list) {
-        if (ip_dev->addr4 != INADDR_ANY) {
-            struct in6_addr addr4 = in6_addr_mapped_ipv4(ip_dev->addr4);
-            map_delete(ip_dev->mac, &addr4, udp_port);
-        }
-        if (ipv6_addr_is_set(&ip_dev->addr6)) {
-            map_delete(ip_dev->mac, &ip_dev->addr6, udp_port);
-        }
+        ipdev_map_delete(ip_dev, p->udp_port);
     }
 
     free(p);
@@ -331,56 +343,64 @@ map_insert_ipdev(struct ip_device *ip_dev)
     struct tnl_port *p;
 
     LIST_FOR_EACH(p, node, &port_list) {
-        if (ip_dev->addr4 != INADDR_ANY) {
-            struct in6_addr addr4 = in6_addr_mapped_ipv4(ip_dev->addr4);
-            map_insert(p->port, ip_dev->mac, &addr4,
-                       p->udp_port, p->dev_name);
-        }
-        if (ipv6_addr_is_set(&ip_dev->addr6)) {
-            map_insert(p->port, ip_dev->mac, &ip_dev->addr6,
-                       p->udp_port, p->dev_name);
-        }
+        map_insert_ipdev__(ip_dev, p->dev_name, p->port, p->udp_port);
     }
 }
 
 static void
-insert_ipdev(const char dev_name[])
+insert_ipdev__(struct netdev *dev,
+               struct in6_addr *addr, int n_addr)
 {
     struct ip_device *ip_dev;
     enum netdev_flags flags;
-    struct netdev *dev;
     int error;
-    int error4, error6;
-
-    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;
+        goto err;
     }
 
     ip_dev = xzalloc(sizeof *ip_dev);
-    ip_dev->dev = dev;
+    ip_dev->dev = netdev_ref(dev);
     ip_dev->change_seq = netdev_get_change_seq(dev);
     error = netdev_get_etheraddr(ip_dev->dev, &ip_dev->mac);
     if (error) {
-        free(ip_dev);
-        return;
-    }
-    error4 = netdev_get_in4(ip_dev->dev, (struct in_addr *)&ip_dev->addr4, NULL);
-    error6 = netdev_get_in6(ip_dev->dev, &ip_dev->addr6);
-    if (error4 && error6) {
-        free(ip_dev);
-        return;
+        goto err_free_ipdev;
     }
+    ip_dev->addr = addr;
+    ip_dev->n_addr = n_addr;
     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);
+    return;
+
+err_free_ipdev:
+    netdev_close(ip_dev->dev);
+    free(ip_dev);
+err:
+    free(addr);
+}
+
+static void
+insert_ipdev(const char dev_name[])
+{
+    struct in6_addr *addr, *mask;
+    struct netdev *dev;
+    int error, n_in6;
+
+    error = netdev_open(dev_name, NULL, &dev);
+    if (error) {
+        return;
+    }
+
+    error = netdev_get_addr_list(dev, &addr, &mask, &n_in6);
+    if (error) {
+        netdev_close(dev);
+        return;
+    }
+    free(mask);
+    insert_ipdev__(dev, addr, n_in6);
+    netdev_close(dev);
 }
 
 static void
@@ -389,17 +409,12 @@ delete_ipdev(struct ip_device *ip_dev)
     struct tnl_port *p;
 
     LIST_FOR_EACH(p, node, &port_list) {
-        if (ip_dev->addr4 != INADDR_ANY) {
-            struct in6_addr addr4 = in6_addr_mapped_ipv4(ip_dev->addr4);
-            map_delete(ip_dev->mac, &addr4, p->udp_port);
-        }
-        if (ipv6_addr_is_set(&ip_dev->addr6)) {
-            map_delete(ip_dev->mac, &ip_dev->addr6, p->udp_port);
-        }
+        ipdev_map_delete(ip_dev, p->udp_port);
     }
 
     list_remove(&ip_dev->node);
     netdev_close(ip_dev->dev);
+    free(ip_dev->addr);
     free(ip_dev);
 }
 
@@ -417,7 +432,6 @@ tnl_port_map_insert_ipdev(const char dev_name[])
             }
             /* Address changed. */
             delete_ipdev(ip_dev);
-            break;
         }
     }
     insert_ipdev(dev_name);
diff --git a/ofproto/ofproto-dpif-xlate.c b/ofproto/ofproto-dpif-xlate.c
index e4692b3..903cd1a 100644
--- a/ofproto/ofproto-dpif-xlate.c
+++ b/ofproto/ofproto-dpif-xlate.c
@@ -2750,7 +2750,8 @@ process_special(struct xlate_ctx *ctx, const struct xport *xport)
 
 static int
 tnl_route_lookup_flow(const struct flow *oflow,
-                      struct in6_addr *ip, struct xport **out_port)
+                      struct in6_addr *ip, struct in6_addr *src,
+                      struct xport **out_port)
 {
     char out_dev[IFNAMSIZ];
     struct xbridge *xbridge;
@@ -2759,7 +2760,7 @@ tnl_route_lookup_flow(const struct flow *oflow,
     struct in6_addr dst;
 
     dst = flow_tnl_dst(&oflow->tunnel);
-    if (!ovs_router_lookup(&dst, out_dev, &gw)) {
+    if (!ovs_router_lookup(&dst, out_dev, src, &gw)) {
         return -ENOENT;
     }
 
@@ -2850,7 +2851,7 @@ build_tunnel_send(struct xlate_ctx *ctx, const struct xport *xport,
     char buf_sip6[INET6_ADDRSTRLEN];
     char buf_dip6[INET6_ADDRSTRLEN];
 
-    err = tnl_route_lookup_flow(flow, &d_ip6, &out_dev);
+    err = tnl_route_lookup_flow(flow, &d_ip6, &s_ip6, &out_dev);
     if (err) {
         xlate_report(ctx, "native tunnel routing failed");
         return err;
@@ -2869,18 +2870,7 @@ build_tunnel_send(struct xlate_ctx *ctx, const struct xport *xport,
 
     d_ip = in6_addr_get_mapped_ipv4(&d_ip6);
     if (d_ip) {
-        err = netdev_get_in4(out_dev->netdev, (struct in_addr *) &s_ip, NULL);
-        if (err) {
-            xlate_report(ctx, "tunnel output device lacks IPv4 address");
-            return err;
-        }
-        in6_addr_set_mapped_ipv4(&s_ip6, s_ip);
-    } else {
-        err = netdev_get_in6(out_dev->netdev, &s_ip6);
-        if (err) {
-            xlate_report(ctx, "tunnel output device lacks IPv6 address");
-            return err;
-        }
+        s_ip = in6_addr_get_mapped_ipv4(&s_ip6);
     }
 
     err = tnl_neigh_lookup(out_dev->xbridge->name, &d_ip6, &dmac);
diff --git a/tests/ofproto-dpif.at b/tests/ofproto-dpif.at
index bf6661d..2d81711 100644
--- a/tests/ofproto-dpif.at
+++ b/tests/ofproto-dpif.at
@@ -5476,12 +5476,10 @@ dummy@ovs-dummy: hit:0 missed:0
 ])
 
 dnl set up route to 1.1.2.92 via br0 and action=normal
-AT_CHECK([ovs-appctl ovs/route/add 1.1.2.92/24 br0], [0], [OK
-])
-AT_CHECK([ovs-appctl ovs/route/add 192.168.0.0/16 br0], [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])
 
 dnl Prime ARP Cache for 1.1.2.92
@@ -5493,6 +5491,12 @@ ovs-vsctl \
    --id=@sf create sflow targets=\"127.0.0.1:$SFLOW_PORT\" \
      header=128 sampling=1 polling=0
 
+dnl set up route to 192.168.1.2 via br0
+AT_CHECK([ovs-appctl netdev-dummy/ip4addr br0 192.168.1.1/16], [0], [OK
+])
+AT_CHECK([ovs-appctl ovs/route/add 192.168.0.0/16 br0], [0], [OK
+])
+
 dnl add rule for int-br to force packet onto tunnel. There is no ifindex
 dnl for this port so the sFlow output will just report that it went to
 dnl 1 output (out_format=2, out_ifindex=1)
diff --git a/vswitchd/bridge.c b/vswitchd/bridge.c
index 25a0663..7113d3a 100644
--- a/vswitchd/bridge.c
+++ b/vswitchd/bridge.c
@@ -4916,8 +4916,7 @@ collect_splinter_vlans(const struct ovsrec_open_vswitch *ovs_cfg)
                 struct netdev *netdev;
 
                 if (!netdev_open(vlan_dev->name, "system", &netdev)) {
-                    if (!netdev_get_in4(netdev, NULL, NULL) ||
-                        !netdev_get_in6(netdev, NULL)) {
+                    if (!netdev_get_addr_list(netdev, NULL, NULL, NULL)) {
                         /* It has an IP address configured, so we don't own
                          * it.  Don't delete it. */
                     } else {
