diff --git a/NEWS b/NEWS
index fe6055a2700b..929437733e3d 100644
--- a/NEWS
+++ b/NEWS
@@ -4,6 +4,8 @@ Post-v3.1.0
      * OVS now collects per-interface upcall statistics that can be obtained
        via 'ovs-appctl dpctl/show -s' or the interface's statistics column
        in OVSDB.  Available with upstream kernel 6.2+.
+   - ovs-appctl:
+     * "ovs-appctl ovs/route/add" command can now support "src" option.
 
 
 v3.1.0 - xx xxx xxxx
diff --git a/lib/ovs-router.c b/lib/ovs-router.c
index 5d0fbd503e9e..fc95a91012f9 100644
--- a/lib/ovs-router.c
+++ b/lib/ovs-router.c
@@ -217,12 +217,13 @@ 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;
     struct match match;
-    int err;
+    int err = 0;
 
     rt_init_match(&match, mark, ip6_dst, plen);
 
@@ -236,8 +237,14 @@ ovs_router_insert__(uint32_t mark, uint8_t priority, bool local,
     p->plen = plen;
     p->local = local;
     p->priority = priority;
-    err = ovs_router_get_netdev_source_address(ip6_dst, output_bridge,
-                                               &p->src_addr);
+
+    if (ipv6_addr_is_set(ip6_src)) {
+        p->src_addr = *ip6_src;
+    } else {
+        err = ovs_router_get_netdev_source_address(ip6_dst, output_bridge,
+                                                   &p->src_addr);
+    }
+
     if (err && ipv6_addr_is_set(gw)) {
         err = ovs_router_get_netdev_source_address(gw, output_bridge,
                                                    &p->src_addr);
@@ -274,7 +281,8 @@ ovs_router_insert(uint32_t mark, const struct in6_addr *ip_dst, uint8_t plen,
 {
     if (use_system_routing_table) {
         uint8_t priority = local ? plen + 64 : plen;
-        ovs_router_insert__(mark, priority, local, ip_dst, plen, output_bridge, gw);
+        ovs_router_insert__(mark, priority, local, ip_dst, plen,
+                            output_bridge, gw, &in6addr_any);
     }
 }
 
@@ -342,6 +350,7 @@ ovs_router_add(struct unixctl_conn *conn, int argc,
               const char *argv[], void *aux OVS_UNUSED)
 {
     struct in6_addr gw6 = in6addr_any;
+    struct in6_addr src6 = in6addr_any;
     struct in6_addr ip6;
     uint32_t mark = 0;
     unsigned int plen;
@@ -350,11 +359,27 @@ ovs_router_add(struct unixctl_conn *conn, int argc,
 
     if (scan_ipv4_route(argv[1], &ip, &plen)) {
         ovs_be32 gw = 0;
+        ovs_be32 src = 0;
 
         if (argc > 3) {
-            if (!ovs_scan(argv[3], "pkt_mark=%"SCNi32, &mark) &&
+            if (!ovs_scan(argv[3], "src="IP_SCAN_FMT, IP_SCAN_ARGS(&src)) &&
+                !ovs_scan(argv[3], "pkt_mark=%"SCNi32, &mark) &&
                 !ip_parse(argv[3], &gw)) {
-                unixctl_command_reply_error(conn, "Invalid pkt_mark or gateway");
+                unixctl_command_reply_error(
+                    conn, "Invalid src, pkt_mark or gateway");
+                return;
+            }
+        }
+        if (argc > 4) {
+            if (!ovs_scan(argv[4], "src="IP_SCAN_FMT, IP_SCAN_ARGS(&src)) &&
+                !ovs_scan(argv[4], "pkt_mark=%"SCNi32, &mark)) {
+                unixctl_command_reply_error(conn, "Invalid src or pkt_mark");
+                return;
+            }
+        }
+        if (argc > 5) {
+            if (!ovs_scan(argv[5], "src="IP_SCAN_FMT, IP_SCAN_ARGS(&src))) {
+                unixctl_command_reply_error(conn, "Invalid src");
                 return;
             }
         }
@@ -362,12 +387,35 @@ ovs_router_add(struct unixctl_conn *conn, int argc,
         if (gw) {
             in6_addr_set_mapped_ipv4(&gw6, gw);
         }
+        if (src) {
+            in6_addr_set_mapped_ipv4(&src6, src);
+        }
         plen += 96;
     } else if (scan_ipv6_route(argv[1], &ip6, &plen)) {
+        char src6_s[IPV6_SCAN_LEN + 1];
+
         if (argc > 3) {
-            if (!ovs_scan(argv[3], "pkt_mark=%"SCNi32, &mark) &&
+            if (!(ovs_scan(argv[3], "src="IPV6_SCAN_FMT, src6_s) &&
+                  ipv6_parse(src6_s, &src6)) &&
+                !ovs_scan(argv[3], "pkt_mark=%"SCNi32, &mark) &&
                 !ipv6_parse(argv[3], &gw6)) {
-                unixctl_command_reply_error(conn, "Invalid pkt_mark or IPv6 gateway");
+                unixctl_command_reply_error(
+                    conn, "Invalid src, pkt_mark or IPv6 gateway");
+                return;
+            }
+        }
+        if (argc > 4) {
+            if (!(ovs_scan(argv[4], "src="IPV6_SCAN_FMT, src6_s) &&
+                  ipv6_parse(src6_s, &src6)) &&
+                !ovs_scan(argv[4], "pkt_mark=%"SCNi32, &mark)) {
+                unixctl_command_reply_error(conn, "Invalid src or pkt_mark");
+                return;
+            }
+        }
+        if (argc > 5) {
+            if (!(ovs_scan(argv[5], "src="IPV6_SCAN_FMT, src6_s) &&
+                  ipv6_parse(src6_s, &src6))) {
+                unixctl_command_reply_error(conn, "Invalid src");
                 return;
             }
         }
@@ -375,14 +423,9 @@ ovs_router_add(struct unixctl_conn *conn, int argc,
         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;
-        }
-    }
 
-    err = ovs_router_insert__(mark, plen + 32, false, &ip6, plen, argv[2], &gw6);
+    err = ovs_router_insert__(mark, plen + 32, false, &ip6, plen, argv[2],
+                              &gw6, &src6);
     if (err) {
         unixctl_command_reply_error(conn, "Error while inserting route.");
     } else {
@@ -533,8 +576,8 @@ 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=src_ip_addr]",
+                                 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 "
diff --git a/tests/ovs-router.at b/tests/ovs-router.at
index 6dacc2954bc6..d4e39b4dd995 100644
--- a/tests/ovs-router.at
+++ b/tests/ovs-router.at
@@ -12,6 +12,52 @@ AT_CHECK([ovs-appctl ovs/route/add 1.1.1.0/24 br0 2.2.2.10], [0], [OK
 OVS_VSWITCHD_STOP
 AT_CLEANUP
 
+AT_SETUP([appctl - route/add with src - ipv4])
+AT_KEYWORDS([ovs_router])
+OVS_VSWITCHD_START([add-port br0 p1 -- set Interface p1 type=dummy])
+AT_CHECK([ovs-appctl netdev-dummy/ip4addr br0 192.168.9.2/24], [0], [OK
+])
+AT_CHECK([ovs-appctl netdev-dummy/ip4addr br0 192.168.9.3/24], [0], [OK
+])
+AT_CHECK([ovs-appctl ovs/route/add 192.168.10.12/32 br0 192.168.9.1 src=192.168.9.3], [0], [OK
+])
+AT_CHECK([ovs-appctl ovs/route/add 192.168.10.13/32 br0 192.168.9.1 pkt_mark=13 src=192.168.9.3], [0], [OK
+])
+AT_CHECK([ovs-appctl ovs/route/add 192.168.10.14/32 br0 192.168.9.1 pkt_mark=14 src=192.168.9.2], [0], [OK
+])
+AT_CHECK([ovs-appctl ovs/route/add 192.168.10.15/32 br0 192.168.9.1 src=foo.bar.9.200], [2], [], [dnl
+Invalid src or pkt_mark
+ovs-appctl: ovs-vswitchd: server returned an error
+])
+AT_CHECK([ovs-appctl ovs/route/show | grep User | grep 192.168.10 | sort], [0], [dnl
+User: 192.168.10.12/32 dev br0 GW 192.168.9.1 SRC 192.168.9.3
+User: 192.168.10.13/32 MARK 13 dev br0 GW 192.168.9.1 SRC 192.168.9.3
+User: 192.168.10.14/32 MARK 14 dev br0 GW 192.168.9.1 SRC 192.168.9.2
+])
+OVS_VSWITCHD_STOP
+AT_CLEANUP
+
+AT_SETUP([appctl - route/add with src - ipv6])
+AT_KEYWORDS([ovs_router])
+OVS_VSWITCHD_START([add-port br0 p1 -- set Interface p1 type=dummy])
+AT_CHECK([ovs-appctl netdev-dummy/ip6addr br0 2001:db8:cafe::2/64], [0], [OK
+])
+AT_CHECK([ovs-appctl netdev-dummy/ip6addr br0 2001:db8:cafe::3/64], [0], [OK
+])
+AT_CHECK([ovs-appctl ovs/route/add 2001:db8:beef::12/128 br0 2001:db8:cafe::1 src=2001:db8:cafe::3], [0], [OK
+])
+AT_CHECK([ovs-appctl ovs/route/add 2001:db8:beef::13/128 br0 2001:db8:cafe::1 pkt_mark=13 src=2001:db8:cafe::3], [0], [OK
+])
+AT_CHECK([ovs-appctl ovs/route/add 2001:db8:beef::14/128 br0 2001:db8:cafe::1 pkt_mark=14 src=2001:db8:cafe::2], [0], [OK
+])
+AT_CHECK([ovs-appctl ovs/route/show | grep User | grep 2001:db8:beef | sort], [0], [dnl
+User: 2001:db8:beef::12/128 dev br0 GW 2001:db8:cafe::1 SRC 2001:db8:cafe::3
+User: 2001:db8:beef::13/128 MARK 13 dev br0 GW 2001:db8:cafe::1 SRC 2001:db8:cafe::3
+User: 2001:db8:beef::14/128 MARK 14 dev br0 GW 2001:db8:cafe::1 SRC 2001:db8:cafe::2
+])
+OVS_VSWITCHD_STOP
+AT_CLEANUP
+
 AT_SETUP([appctl - route/lookup])
 AT_KEYWORDS([ovs_router])
 OVS_VSWITCHD_START([add-port br0 p1 -- set Interface p1 type=dummy])
