diff --git a/ovn/northd/ovn-northd.c b/ovn/northd/ovn-northd.c
index 093d5ff..0400dea 100644
--- a/ovn/northd/ovn-northd.c
+++ b/ovn/northd/ovn-northd.c
@@ -130,9 +130,10 @@ enum ovn_stage {
     PIPELINE_STAGE(ROUTER, IN,  DEFRAG,      2, "lr_in_defrag")       \
     PIPELINE_STAGE(ROUTER, IN,  UNSNAT,      3, "lr_in_unsnat")       \
     PIPELINE_STAGE(ROUTER, IN,  DNAT,        4, "lr_in_dnat")         \
-    PIPELINE_STAGE(ROUTER, IN,  IP_ROUTING,  5, "lr_in_ip_routing")   \
-    PIPELINE_STAGE(ROUTER, IN,  ARP_RESOLVE, 6, "lr_in_arp_resolve")  \
-    PIPELINE_STAGE(ROUTER, IN,  ARP_REQUEST, 7, "lr_in_arp_request")  \
+    PIPELINE_STAGE(ROUTER, IN,  RS_RSP,      5, "lr_in_rs_rsp")       \
+    PIPELINE_STAGE(ROUTER, IN,  IP_ROUTING,  6, "lr_in_ip_routing")   \
+    PIPELINE_STAGE(ROUTER, IN,  ARP_RESOLVE, 7, "lr_in_arp_resolve")  \
+    PIPELINE_STAGE(ROUTER, IN,  ARP_REQUEST, 8, "lr_in_arp_request")  \
                                                                       \
     /* Logical router egress stages. */                               \
     PIPELINE_STAGE(ROUTER, OUT, SNAT,      0, "lr_out_snat")          \
@@ -4040,7 +4041,134 @@ build_lrouter_flows(struct hmap *datapaths, struct hmap *ports,
                       "ip", "flags.loopback = 1; ct_dnat;");
     }
 
-    /* Logical router ingress table 5: IP Routing.
+    /* Logical router ingress table 5: RS responder, reply for router port
+     * with IPv6 networks configured. (priority 50)*/
+    HMAP_FOR_EACH (op, key_node, ports) {
+        if (!op->nbrp || op->nbrp->peer || !op->peer) {
+            continue;
+        }
+
+        if (!op->lrp_networks.n_ipv6_addrs) {
+            continue;
+        }
+
+        ds_clear(&match);
+        ds_put_format(&match, "inport == %s && ip6.dst == ff02::2 && nd_rs",
+                              op->json_key);
+        ds_clear(&actions);
+
+        const char *hop_lmt = smap_get(
+            &op->nbrp->ipv6_ra_configs, "cur_hop_limit");
+        const char *mgd_addr = smap_get(
+            &op->nbrp->ipv6_ra_configs, "managed_address");
+        const char *other_cfg = smap_get(
+            &op->nbrp->ipv6_ra_configs, "other_config");
+        const char *rtr_lt = smap_get(
+            &op->nbrp->ipv6_ra_configs, "router_lifetime");
+        const char *rch_t = smap_get(
+            &op->nbrp->ipv6_ra_configs, "reachable_time");
+        const char *rts_t = smap_get(
+            &op->nbrp->ipv6_ra_configs, "retrans_timer");
+        const char *mtu_s = smap_get(
+            &op->nbrp->ipv6_ra_configs, "mtu");
+
+        uint8_t managed_address_flag = ND_RA_MANAGED_ADDRESS;
+        if (mgd_addr && !strcmp(mgd_addr, "0")) {
+            managed_address_flag = 0;
+        }
+        uint8_t other_config_flag = ND_RA_OTHER_CONFIG;
+        if (other_cfg && !strcmp(other_cfg, "0")) {
+            other_config_flag = 0;
+        }
+        uint32_t mtu = (mtu_s && atoi(mtu_s) >= 1280) ? atoi(mtu_s) : 0;
+
+        ds_put_format(&actions, "nd_ra{put_nd_ra(%u, %u, %u, %u, %u); "
+                                "put_nd_opt_sll(%s); ",
+                      (hop_lmt && atoi(hop_lmt) > 0) ? atoi(hop_lmt) : 64,
+                      managed_address_flag | other_config_flag,
+                      (rtr_lt && atoi(rtr_lt) > 0) ? atoi(rtr_lt) : 10800,
+                      (rch_t && atoi(rch_t) > 0) ? atoi(rch_t) : 0,
+                      (rts_t && atoi(rts_t) > 0) ? atoi(rts_t) : 0,
+                      op->lrp_networks.ea_s);
+        if (mtu > 0) {
+            ds_put_format(&actions, "put_nd_opt_mtu(%u); ",
+                                     mtu);
+        }
+        size_t prev_actions_len = actions.length;
+        struct ds lrp_prefix = DS_EMPTY_INITIALIZER;
+        for (size_t i = 0; i != op->lrp_networks.n_ipv6_addrs; i++) {
+            if (in6_is_lla(&op->lrp_networks.ipv6_addrs[i].network)) {
+                continue;
+            }
+            uint8_t onlink_flag = ND_PREFIX_ON_LINK;
+            uint8_t autonomous_address_flag = ND_PREFIX_AUTONOMOUS_ADDRESS;
+            const char *v_lt = NULL;
+            const char *p_lt = NULL;
+            ds_put_format(&lrp_prefix, "%s/%u",
+                          op->lrp_networks.ipv6_addrs[i].network_s,
+                          op->lrp_networks.ipv6_addrs[i].plen);
+            for (size_t j = 0; j != op->nbrp->n_ipv6_ra_prefix_configs; j++) {
+                if (!strcmp(ds_cstr(&lrp_prefix),
+                            op->nbrp->ipv6_ra_prefix_configs[j]->prefix)) {
+                    const char *ol = smap_get(
+                        &op->nbrp->ipv6_ra_prefix_configs[j]->options,
+                        "onlink");
+                    const char *aa = smap_get(
+                        &op->nbrp->ipv6_ra_prefix_configs[j]->options,
+                        "autonomous_address");
+                    v_lt = smap_get(
+                        &op->nbrp->ipv6_ra_prefix_configs[j]->options,
+                        "valid_lifetime");
+                    p_lt = smap_get(
+                        &op->nbrp->ipv6_ra_prefix_configs[j]->options,
+                        "preferred_lifetime");
+                    if (ol && !strcmp(ol, "0")) {
+                        onlink_flag = 0;
+                    }
+                    if (aa && !strcmp(aa, "0")) {
+                        autonomous_address_flag = 0;
+                    }
+                    break;
+                }
+            }
+            ds_put_format(&actions,
+                          "put_nd_opt_prefix(%u, %u, %u, %u, %s); ",
+                          op->lrp_networks.ipv6_addrs[i].plen,
+                          (onlink_flag | autonomous_address_flag),
+                          (v_lt && atoi(v_lt) > 0) ? atoi(v_lt) : 10800,
+                          (p_lt && atoi(p_lt) > 0) ? atoi(p_lt) : 10800,
+                          op->lrp_networks.ipv6_addrs[i].network_s);
+            ds_clear(&lrp_prefix);
+        }
+        ds_destroy(&lrp_prefix);
+
+        if (actions.length != prev_actions_len) {
+            char ip6_str[INET6_ADDRSTRLEN + 1];
+            struct in6_addr lla;
+            in6_generate_lla(op->lrp_networks.ea, &lla);
+            memset(ip6_str, 0, sizeof(ip6_str));
+            ipv6_string_mapped(ip6_str, &lla);
+            ds_put_format(&actions, "eth.src = %s; ip6.src = %s; "
+                                    "outport = inport; flags.loopback = 1; "
+                                    "output;};",
+                                    op->lrp_networks.ea_s,
+                                    ip6_str);
+            ovn_lflow_add(lflows, op->od, S_ROUTER_IN_RS_RSP, 50,
+                          ds_cstr(&match), ds_cstr(&actions));
+        }
+    }
+
+    /* Logical router ingress table 5: RS responder, by default goto next.
+     * (priority 0)*/
+    HMAP_FOR_EACH (od, key_node, datapaths) {
+        if (!od->nbr) {
+            continue;
+        }
+
+        ovn_lflow_add(lflows, od, S_ROUTER_IN_RS_RSP, 0, "1", "next;");
+    }
+
+    /* Logical router ingress table 6: IP Routing.
      *
      * A packet that arrives at this table is an IP packet that should be
      * routed to the address in 'ip[46].dst'. This table sets outport to
@@ -4082,7 +4210,7 @@ build_lrouter_flows(struct hmap *datapaths, struct hmap *ports,
 
     /* XXX destination unreachable */
 
-    /* Local router ingress table 6: ARP Resolution.
+    /* Local router ingress table 7: ARP Resolution.
      *
      * Any packet that reaches this table is an IP packet whose next-hop IP
      * address is in reg0. (ip4.dst is the final destination.) This table
@@ -4277,7 +4405,7 @@ build_lrouter_flows(struct hmap *datapaths, struct hmap *ports,
                       "get_nd(outport, xxreg0); next;");
     }
 
-    /* Local router ingress table 7: ARP request.
+    /* Local router ingress table 8: ARP request.
      *
      * In the common case where the Ethernet destination has been resolved,
      * this table outputs the packet (priority 0).  Otherwise, it composes
diff --git a/ovn/ovn-nb.ovsschema b/ovn/ovn-nb.ovsschema
index 865dd34..afc5489 100644
--- a/ovn/ovn-nb.ovsschema
+++ b/ovn/ovn-nb.ovsschema
@@ -1,7 +1,7 @@
 {
     "name": "OVN_Northbound",
-    "version": "5.4.0",
-    "cksum": "4176761817 11225",
+    "version": "5.5.0",
+    "cksum": "3485267177 12201",
     "tables": {
         "NB_Global": {
             "columns": {
@@ -188,6 +188,14 @@
                 "mac": {"type": "string"},
                 "peer": {"type": {"key": "string", "min": 0, "max": 1}},
                 "enabled": {"type": {"key": "boolean", "min": 0, "max": 1}},
+                "ipv6_ra_configs": {
+                    "type": {"key": "string", "value": "string",
+                             "min": 0, "max": "unlimited"}},
+                "ipv6_ra_prefix_configs": {"type": {"key": {"type": "uuid",
+                                                    "refTable": "Logical_Router_RA_Prefix_Config",
+                                                    "refType": "weak"},
+                                           "min": 0,
+                                           "max": "unlimited"}},
                 "external_ids": {
                     "type": {"key": "string", "value": "string",
                              "min": 0, "max": "unlimited"}}},
@@ -217,6 +225,15 @@
                 "external_ids": {
                     "type": {"key": "string", "value": "string",
                              "min": 0, "max": "unlimited"}}},
+            "isRoot": true},
+        "Logical_Router_RA_Prefix_Config": {
+            "columns": {
+                "prefix": {"type": "string"},
+                "options": {"type": {"key": "string", "value": "string",
+                                     "min": 0, "max": "unlimited"}},
+                "external_ids": {
+                    "type": {"key": "string", "value": "string",
+                             "min": 0, "max": "unlimited"}}},
             "isRoot": true}
     }
 }
diff --git a/ovn/ovn-nb.xml b/ovn/ovn-nb.xml
index e1b3136..16fec1f 100644
--- a/ovn/ovn-nb.xml
+++ b/ovn/ovn-nb.xml
@@ -1027,6 +1027,54 @@
       The Ethernet address that belongs to this router port.
     </column>
 
+    <group title="ipv6_ra_configs">
+      <p>
+        This column defines the IPv6 RA configurations and ND MTU Option to be
+        used by <code>ovn-northd</code> when it generates logical flows for
+        Router Solicitaion responder.
+        Please refer to RFC 4861 for more details on RA message fields and
+        ND MTU Option.
+      </p>
+
+      <column name="ipv6_ra_configs" key="cur_hop_limit">
+        Current hop limit. Default is 64.
+      </column>
+
+      <column name="ipv6_ra_configs" key="managed_address">
+        1-bit "Managed address configuration" flag. Default is 1.
+      </column>
+
+      <column name="ipv6_ra_configs" key="other_config">
+        1-bit "Other configuration" flag. Default is 1.
+      </column>
+
+      <column name="ipv6_ra_configs" key="router_lifetime">
+        Router lifetime. Default is 10800(seconds).
+      </column>
+
+      <column name="ipv6_ra_configs" key="reachable_time">
+        Reachable time. Default is 0.
+      </column>
+
+      <column name="ipv6_ra_configs" key="retrans_timer">
+        Retrans timer. Default is 0.
+      </column>
+
+      <column name="ipv6_ra_configs" key="mtu">
+        The recommended MTU for the link. Default is 0, which means no MTU
+        Option will be included in RA packet replied by ovn-controller.
+        Per RFC 2460, the mtu value is recommended no less than 1280, so
+        any mtu value less than 1280 will be considerred as no MTU Option.
+      </column>
+    </group>
+
+    <column name="ipv6_ra_prefix_configs">
+      This column defines the IPv6 ND Prefix Information Option configurations
+      to be used by <code>ovn-northd</code> when it generates logical flows for
+      Router Solicitaion responder.
+      Please see the <ref table="Logical_Router_RA_Prefix_Config"/> table.
+    </column>
+
     <column name="enabled">
       This column is used to administratively set port state.  If this column
       is empty or is set to <code>true</code>, the port is enabled.  If this
@@ -1449,4 +1497,59 @@
       </column>
     </group>
   </table>
+
+  <table name="Logical_Router_RA_Prefix_Config" title="Logical Router RA Prefix Config">
+    <p>
+      Each record represents a set of RA configurations for
+      Logical_Router_Port to use.
+    </p>
+
+    <column name="prefix">
+      <p>
+        The RA Prefix Information Option configurations will be used if the
+        logical router port has its network in this <ref column="prefix"/>.
+      </p>
+    </column>
+
+    <group title="configs">
+      <p>
+        The CMS should define the set of ND Prefix Information Option
+        configurations as key/value pairs in the <ref column="configs"/> column
+        of this table.
+        Records in <ref table="Logical_Router_Port"/> can refer record in this
+        table in their <ref column="ipv6_ra_prefix_configs"/> to configure Router
+        Solicitation responder. Then <code>ovn-northd</code> will build logical
+        flows per ND Prefix Information Option configurations defined in the
+        <ref column="configs"/> column.
+      </p>
+
+      <p>
+        Below are the supported ND Prefix Information Option configurations.
+        Please refer to RFC 4861 for more details.
+      </p>
+
+      <column name="options" key="onlink">
+        1-bit on-link flag. Default is 1.
+      </column>
+
+      <column name="options" key="autonomous">
+        1-bit autonomous address-configuration flag. Default is 1.
+      </column>
+
+      <column name="options" key="valid_lifetime">
+        Valid lifetime for on-link prefix. Default is 10800(seconds).
+      </column>
+
+      <column name="options" key="preferred_lifetime">
+        Preferred lifetime for on-link prefix. Default is 10800(seconds).
+      </column>
+    </group>
+
+    <group title="Common Columns">
+      <column name="external_ids">
+        See <em>External IDs</em> at the beginning of this document.
+      </column>
+    </group>
+  </table>
+
 </database>
diff --git a/ovn/utilities/ovn-nbctl.c b/ovn/utilities/ovn-nbctl.c
index ad2d2f8..62ca4cc 100644
--- a/ovn/utilities/ovn-nbctl.c
+++ b/ovn/utilities/ovn-nbctl.c
@@ -2637,6 +2637,11 @@ static const struct ctl_table_class tables[] = {
        NULL},
       {NULL, NULL, NULL}}},
 
+    {&nbrec_table_logical_router_ra_prefix_config,
+     {{&nbrec_table_logical_router_ra_prefix_config, NULL,
+       NULL},
+      {NULL, NULL, NULL}}},
+
     {NULL, {{NULL, NULL, NULL}, {NULL, NULL, NULL}}}
 };
 
diff --git a/tests/ovn.at b/tests/ovn.at
index 85f75d0..fc54833 100644
--- a/tests/ovn.at
+++ b/tests/ovn.at
@@ -5475,3 +5475,140 @@ check_tos 0
 
 OVN_CLEANUP([hv])
 AT_CLEANUP
+
+AT_SETUP([ovn -- nd_ra ])
+AT_KEYWORDS([ovn-nd_ra])
+AT_SKIP_IF([test $HAVE_PYTHON = no])
+ovn_start
+
+# In this test case we create 1 lswitch with 3 VIF ports attached,
+# and a lrouter connected to the lswitch.
+# Router solicitation packet we test, sent from VIF port, will be replied
+# by local ovn-controller.
+
+# Create hypervisors and logical switch lsw0, logical router lr0, attach lsw0
+# onto lr0, set Logical_Router_Port.slaac column to 'true' to allow lrp0 send
+# RA for SLAAC mode.
+ovn-nbctl ls-add lsw0
+ovn-nbctl lr-add lr0
+ovn-nbctl lrp-add lr0 lrp0 fa:16:3e:00:00:01 fdad:1234:5678::1/64
+ovn-nbctl set Logical_Router_Port lrp0 slaac="true"
+ovn-nbctl \
+    -- lsp-add lsw0 lsp0 \
+    -- set Logical_Switch_Port lsp0 type=router \
+                     options:router-port=lrp0 \
+                     addresses='"fa:16:3e:00:00:01 fdad:1234:5678::1"'
+net_add n1
+sim_add hv1
+as hv1
+ovs-vsctl add-br br-phys
+ovn_attach n1 br-phys 192.168.0.2
+
+# Add vif1 to hv1 and lsw0, turn on l2 port security on vif1.
+ovs-vsctl add-port br-int vif1 -- set Interface vif1 external-ids:iface-id=lp1 options:tx_pcap=hv1/vif1-tx.pcap options:rxq_pcap=hv1/vif1-rx.pcap
+ovn-nbctl lsp-add lsw0 lp1
+ovn-nbctl lsp-set-addresses lp1 "fa:16:3e:00:00:02 10.0.0.12 fdad:1234:5678:0:f816:3eff:fe:2"
+ovn-nbctl lsp-set-port-security lp1 "fa:16:3e:00:00:02 10.0.0.12 fdad:1234:5678:0:f816:3eff:fe:2"
+
+# Add vif2 to hv1 and lsw0, turn on l2 port security on vif2.
+ovs-vsctl add-port br-int vif2 -- set Interface vif2 external-ids:iface-id=lp2 options:tx_pcap=hv1/vif2-tx.pcap options:rxq_pcap=hv1/vif2-rx.pcap
+ovn-nbctl lsp-add lsw0 lp2
+ovn-nbctl lsp-set-addresses lp2 "fa:16:3e:00:00:03 10.0.0.13 fdad:1234:5678:0:f816:3eff:fe:3"
+ovn-nbctl lsp-set-port-security lp2 "fa:16:3e:00:00:03 10.0.0.13 fdad:1234:5678:0:f816:3eff:fe:3"
+
+# Add vif3 to hv1 and lsw0, turn on l2 port security on vif3.
+ovs-vsctl add-port br-int vif3 -- set Interface vif3 external-ids:iface-id=lp3 options:tx_pcap=hv1/vif3-tx.pcap options:rxq_pcap=hv1/vif3-rx.pcap
+ovn-nbctl lsp-add lsw0 lp3
+ovn-nbctl lsp-set-addresses lp3 "fa:16:3e:00:00:04 10.0.0.14 fdad:1234:5678:0:f816:3eff:fe:4"
+ovn-nbctl lsp-set-port-security lp3 "fa:16:3e:00:00:04 10.0.0.14 fdad:1234:5678:0:f816:3eff:fe:4"
+
+# Add ACL rule for ICMPv6 on lsw0
+ovn-nbctl acl-add lsw0 from-lport 1002 'ip6 && icmp6'  allow-related
+ovn-nbctl acl-add lsw0 to-lport 1002 'outport == "lp1" && ip6 && icmp6'  allow-related
+ovn-nbctl acl-add lsw0 to-lport 1002 'outport == "lp2" && ip6 && icmp6'  allow-related
+ovn-nbctl acl-add lsw0 to-lport 1002 'outport == "lp3" && ip6 && icmp6'  allow-related
+
+# Allow some time for ovn-northd and ovn-controller to catch up.
+# XXX This should be more systematic.
+sleep 1
+
+# Given the name of a logical port, prints the name of the hypervisor
+# on which it is located.
+trim_zeros() {
+    sed 's/\(00\)\{1,\}$//'
+}
+for i in 1 2 3; do
+    : > $i.expected
+done
+
+# This shell function sends a Router Solicitation packet and receives
+# Router Solicitation packet from vif.
+# test_ipv6_ra INPORT SRC_MAC SRC_LLA RA_CONFIG MTU RA_PREFIX_CONFIG
+test_ipv6_ra() {
+    local inport=$1 src_mac=$2 src_lla=$3 ra_config=$4 mtu=$5 ra_prefix_config=$6
+    local request=333300000002${src_mac}86dd6000000000103aff${src_lla}ff02000000000000000000000000000285000efc000000000101${src_mac}
+
+    local len=40
+    if test $mtu == 0; then
+        len=38
+         unset mtu
+    fi
+    local lrp_mac=fa163e000001
+    local lrp_lla=fe80000000000000f8163efffe000001
+    local lrp_prefix=fdad1234567800000000000000000000
+    local reply=${src_mac}${lrp_mac}86dd6000000000${len}3aff${lrp_lla}${src_lla}8600${ra_config}0101${lrp_mac}${mtu}0304${ra_prefix_config}00000000${lrp_prefix}
+
+    as hv1 ovs-appctl netdev-dummy/receive vif${inport} $request
+    echo $reply | trim_zeros >> $inport.expected
+}
+
+# No setting to lrp0 ipv6_ra_configs and ipv6_ra_prefix_configs, vif1 will get
+# a RA packet with default configs.
+default_ra_config=40c02a300000000000000000
+default_prefix_option_config=40c000002a3000002a30
+src_mac=fa163e000002
+src_lla=fe80000000000000f8163efffe000002
+test_ipv6_ra 1 $src_mac $src_lla $default_ra_config 0 $default_prefix_option_config
+
+# Set lrp0 ipv6_ra_configs, vif2 will get a configured RA packet
+ovn-nbctl add Logical_Router_Port lrp0 ipv6_ra_config managed_address="0" other_config="0" mtu="1450"
+sleep 1
+ra_config=40002a300000000000000000
+mtu_opt=05010000000005aa
+src_mac=fa163e000003
+src_lla=fe80000000000000f8163efffe000003
+test_ipv6_ra 2 $src_mac $src_lla $ra_config $mtu_opt $default_prefix_option_config
+
+# Create a Logical_Router_RA_Prefix_Config, and set lrp0.ipv6_ra_prefix_configs
+# to refer it, vif3 will get a configured RA packet with configured Prefix
+# Information Option.
+ovn-nbctl -- --id=@d1 create Logical_Router_RA_Prefix_Config prefix=\"fdad:1234:5678::/64\" \
+options="\"valid_lifetime\"="21600" \"preferred_lifetime\"="21600" \
+\"autonomous_address\"="0"" \
+-- add Logical_Router_Port lrp0 ipv6_ra_prefix_configs @d1
+sleep 1
+prefix_option_config=40800000546000005460
+src_mac=fa163e000004
+src_lla=fe80000000000000f8163efffe000004
+test_ipv6_ra 3 $src_mac $src_lla $ra_config $mtu_opt $prefix_option_config
+
+sleep 1
+
+echo "------ hv1 dump ------"
+as hv1 ovs-vsctl show
+as hv1 ovs-ofctl -O OpenFlow13 show br-int
+as hv1 ovs-ofctl -O OpenFlow13 dump-flows br-int
+as hv1 ovn-sbctl lflow-list
+
+for i in 1 2 3; do
+    file=hv1/vif$i-tx.pcap
+    echo $file
+    # Remove checksum to compare.
+    $PYTHON "$top_srcdir/utilities/ovs-pcap.in" $file | trim_zeros | cut -b 1-112,117- > $i.packets
+    cat $i.expected > expout
+    AT_CHECK([cat $i.packets], [0], [expout])
+done
+
+OVN_CLEANUP([hv1])
+
+AT_CLEANUP
