diff --git a/lib/ovs-router.c b/lib/ovs-router.c
index 09b81c6e5..9cb9b3540 100644
--- a/lib/ovs-router.c
+++ b/lib/ovs-router.c
@@ -47,6 +47,9 @@
 #include "unaligned.h"
 #include "openvswitch/vlog.h"
 
+#define PRIO_USER(plen) ((plen) + 32)
+#define PRIO_LOCAL(plen) ((plen) + 64)
+
 VLOG_DEFINE_THIS_MODULE(ovs_router);
 
 static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5);
@@ -216,7 +219,8 @@ static int
 ovs_router_insert__(uint32_t mark, uint8_t priority, bool local,
                     const struct in6_addr *ip6_dst,
                     uint8_t plen, const char output_bridge[],
-                    const struct in6_addr *gw)
+                    const struct in6_addr *gw,
+                    const struct in6_addr *ip6_src)
 {
     const struct cls_rule *cr;
     struct ovs_router_entry *p;
@@ -235,19 +239,24 @@ ovs_router_insert__(uint32_t mark, uint8_t priority, bool local,
     p->plen = plen;
     p->local = local;
     p->priority = priority;
-    err = get_src_addr(ip6_dst, output_bridge, &p->src_addr);
-    if (err && ipv6_addr_is_set(gw)) {
-        err = get_src_addr(gw, output_bridge, &p->src_addr);
+    if (!ip6_src || !ipv6_addr_is_set(ip6_src)) {
+        err = get_src_addr(ip6_dst, output_bridge, &p->src_addr);
+        if (err && ipv6_addr_is_set(gw)) {
+            err = get_src_addr(gw, output_bridge, &p->src_addr);
+        }
+        if (err) {
+            struct ds ds = DS_EMPTY_INITIALIZER;
+
+            ipv6_format_mapped(ip6_dst, &ds);
+            VLOG_DBG_RL(&rl, "src addr not available for route %s", ds_cstr(&ds));
+            free(p);
+            ds_destroy(&ds);
+            return err;
+        }
+    } else {
+        p->src_addr = *ip6_src;
     }
-    if (err) {
-        struct ds ds = DS_EMPTY_INITIALIZER;
 
-        ipv6_format_mapped(ip6_dst, &ds);
-        VLOG_DBG_RL(&rl, "src addr not available for route %s", ds_cstr(&ds));
-        free(p);
-        ds_destroy(&ds);
-        return err;
-    }
     /* Longest prefix matches first. */
     cls_rule_init(&p->cr, &match, priority);
 
@@ -270,8 +279,9 @@ ovs_router_insert(uint32_t mark, const struct in6_addr *ip_dst, uint8_t plen,
                   const struct in6_addr *gw)
 {
     if (use_system_routing_table) {
-        uint8_t priority = local ? plen + 64 : plen;
-        ovs_router_insert__(mark, priority, local, ip_dst, plen, output_bridge, gw);
+        uint8_t priority = local ? PRIO_LOCAL(plen) : plen;
+        ovs_router_insert__(mark, priority, local, ip_dst, plen,
+                            output_bridge, gw, NULL);
     }
 }
 
@@ -338,48 +348,64 @@ static void
 ovs_router_add(struct unixctl_conn *conn, int argc,
               const char *argv[], void *aux OVS_UNUSED)
 {
+    struct in6_addr src6 = in6addr_any;
     struct in6_addr gw6 = in6addr_any;
     struct in6_addr ip6;
-    uint32_t mark = 0;
-    unsigned int plen;
+    ovs_be32 src = 0;
+    ovs_be32 gw = 0;
     ovs_be32 ip;
+    bool ipv4_route_entry = true;
+    bool ipv4_local = false;
+    uint32_t lastargc = 0;
+    uint32_t mark = 0;
+    uint32_t plen;
     int err;
 
-    if (scan_ipv4_route(argv[1], &ip, &plen)) {
-        ovs_be32 gw = 0;
+    if (!scan_ipv4_route(argv[1], &ip, &plen)) {
+        if (!scan_ipv6_route(argv[1], &ip6, &plen)) {
+            unixctl_command_reply_error(conn, "Invalid parameters");
+            return;
+        }
+        ipv4_route_entry = false;
+    }
 
-        if (argc > 3) {
-            if (!ovs_scan(argv[3], "pkt_mark=%"SCNi32, &mark) &&
-                !ip_parse(argv[3], &gw)) {
-                unixctl_command_reply_error(conn, "Invalid pkt_mark or gateway");
-                return;
-            }
+    while (argc > 3 && lastargc != argc) {
+        lastargc = argc;
+
+        if (!strncmp(argv[argc - 1], "pkt_mark=", 9)) {
+            err = !ovs_scan(argv[--argc] + 9, "pkt_mark=%"SCNi32, &mark);
+        } else if (!strncmp(argv[argc - 1], "src_addr=", 9)) {
+            err = ipv4_route_entry ?
+                  !ip_parse(argv[--argc] + 9, &src) :
+                  !ipv6_parse(argv[--argc] + 9, &src6);
+        } else {
+            err = ipv4_route_entry ?
+                  !ip_parse(argv[--argc], &gw) :
+                  !ipv6_parse(argv[--argc], &gw6);
+        }
+
+        if (err) {
+            unixctl_command_reply_error(conn, "Invalid pkt_mark, src_addr or gateway");
+            return;
         }
+    }
+
+    if (ipv4_route_entry) {
         in6_addr_set_mapped_ipv4(&ip6, ip);
         if (gw) {
             in6_addr_set_mapped_ipv4(&gw6, gw);
         }
-        plen += 96;
-    } else if (scan_ipv6_route(argv[1], &ip6, &plen)) {
-        if (argc > 3) {
-            if (!ovs_scan(argv[3], "pkt_mark=%"SCNi32, &mark) &&
-                !ipv6_parse(argv[3], &gw6)) {
-                unixctl_command_reply_error(conn, "Invalid pkt_mark or IPv6 gateway");
-                return;
-            }
+        if (src) {
+            in6_addr_set_mapped_ipv4(&src6, src);
         }
-    } else {
-        unixctl_command_reply_error(conn, "Invalid parameters");
-        return;
-    }
-    if (argc > 4) {
-        if (!ovs_scan(argv[4], "pkt_mark=%"SCNi32, &mark)) {
-            unixctl_command_reply_error(conn, "Invalid pkt_mark");
-            return;
+        if (plen == 32) {
+            ipv4_local = true;
         }
+        plen += 96;
     }
 
-    err = ovs_router_insert__(mark, plen + 32, false, &ip6, plen, argv[2], &gw6);
+    err = ovs_router_insert__(mark, PRIO_USER(plen), ipv4_local, &ip6, plen,
+                              argv[2], &gw6, &src6);
     if (err) {
         unixctl_command_reply_error(conn, "Error while inserting route.");
     } else {
@@ -410,7 +436,7 @@ ovs_router_del(struct unixctl_conn *conn, int argc OVS_UNUSED,
         }
     }
 
-    if (rt_entry_delete(mark, plen + 32, &ip6, plen)) {
+    if (rt_entry_delete(mark, PRIO_USER(plen), &ip6, plen)) {
         unixctl_command_reply(conn, "OK");
         seq_change(tnl_conf_seq);
     } else {
@@ -428,7 +454,8 @@ ovs_router_show(struct unixctl_conn *conn, int argc OVS_UNUSED,
     ds_put_format(&ds, "Route Table:\n");
     CLS_FOR_EACH(rt, cr, &cls) {
         uint8_t plen;
-        if (rt->priority == rt->plen || rt->local) {
+        if (rt->priority == rt->plen ||
+            (rt->local && rt->priority == PRIO_LOCAL(rt->plen))) {
             ds_put_format(&ds, "Cached: ");
         } else {
             ds_put_format(&ds, "User: ");
@@ -505,7 +532,8 @@ ovs_router_flush(void)
     ovs_mutex_lock(&mutex);
     classifier_defer(&cls);
     CLS_FOR_EACH(rt, cr, &cls) {
-        if (rt->priority == rt->plen || rt->local) {
+        if (rt->priority == rt->plen ||
+            (rt->local && rt->priority == PRIO_LOCAL(rt->plen))) {
             rt_entry_delete__(&rt->cr);
         }
     }
@@ -530,8 +558,9 @@ ovs_router_init(void)
         classifier_init(&cls, NULL);
         unixctl_command_register("ovs/route/add",
                                  "ip_addr/prefix_len out_br_name [gw] "
-                                 "[pkt_mark=mark]",
-                                 2, 4, ovs_router_add, NULL);
+                                 "[pkt_mark=mark]"
+                                 "[src_addr=src]",
+                                 2, 5, ovs_router_add, NULL);
         unixctl_command_register("ovs/route/show", "", 0, 0,
                                  ovs_router_show, NULL);
         unixctl_command_register("ovs/route/del", "ip_addr/prefix_len "
