diff mbox

[ovs-dev] WIP OVN ND for Logical_Port

Message ID 1464316376-13278-1-git-send-email-zealokii@gmail.com
State RFC
Headers show

Commit Message

Zong Kai LI May 27, 2016, 2:32 a.m. UTC
From: lzklibj <lzklibj@cn.ibm.com>

This patch tries to implement ND for Logical_Port in OVN.

Signed-off-by: lzklibj <lzklibj@cn.ibm.com>
---
 lib/packets.c            |   2 +-
 lib/packets.h            |   3 +
 ovn/controller/pinctrl.c | 156 +++++++++++++++++++++++++++++++++++++++--------
 ovn/lib/actions.c        |  50 +++++++++++++++
 ovn/lib/actions.h        |   6 ++
 ovn/lib/expr.c           |  47 +-------------
 ovn/lib/expr.h           |  43 +++++++++++++
 ovn/northd/ovn-northd.c  |  30 ++++++++-
 8 files changed, 266 insertions(+), 71 deletions(-)

Comments

Ben Pfaff June 6, 2016, 9:08 p.m. UTC | #1
On Fri, May 27, 2016 at 10:32:56AM +0800, Zong Kai LI wrote:
> From: lzklibj <lzklibj@cn.ibm.com>
> 
> This patch tries to implement ND for Logical_Port in OVN.
> 
> Signed-off-by: lzklibj <lzklibj@cn.ibm.com>

Thanks for working on this.

I'm CCing Justin, who is working on related features, to bring this
patch to his attention.

"sparse" says:
    ../ovn/controller/pinctrl.c:748:25: warning: cast to restricted ovs_be16
about this line:
    na->rco_flags.hi = (ovs_be16)0x60;
Probably, you should use put_16aligned_be32().

In pinctrl.c, please #include <netinet/icmp6.h> after pinctrl.h.
Keeping pinctrl.h first ensures that it is self-contained.

The functions compose_na() and reload_metadata() in pinctrl.c should be
declared static, unless you want to make them public (and in that case
the prototypes should be in a header file instead).

I am concerned about alignment issues.  I am not sure that the use of
ALIGNED_CAST here is safe on RISC platforms, because IPv6 addresses
aren't always aligned on a 32-bit boundary when they are inside Ethernet
packets with different encapsulations.  It would probably be better to
use a different strategy to assure alignment, rather than to just assume
it.

Please add documentation for the new action, in ovn-sb.xml.

I see that there are a few notes and to-do items; I guess that's what
makes it a work-in-progress.

Thanks,

Ben.
Zong Kai LI June 8, 2016, 8:12 a.m. UTC | #2
On Tue, Jun 7, 2016 at 5:08 AM, Ben Pfaff <blp@ovn.org> wrote:

> On Fri, May 27, 2016 at 10:32:56AM +0800, Zong Kai LI wrote:
> > From: lzklibj <lzklibj@cn.ibm.com>
> >
> > This patch tries to implement ND for Logical_Port in OVN.
> >
> > Signed-off-by: lzklibj <lzklibj@cn.ibm.com>
>
> Thanks for working on this.
>
> I'm CCing Justin, who is working on related features, to bring this
> patch to his attention.
>
> "sparse" says:
>     ../ovn/controller/pinctrl.c:748:25: warning: cast to restricted
> ovs_be16
> about this line:
>     na->rco_flags.hi = (ovs_be16)0x60;
> Probably, you should use put_16aligned_be32().
>
> In pinctrl.c, please #include <netinet/icmp6.h> after pinctrl.h.
> Keeping pinctrl.h first ensures that it is self-contained.
>
> The functions compose_na() and reload_metadata() in pinctrl.c should be
> declared static, unless you want to make them public (and in that case
> the prototypes should be in a header file instead).
>
> I am concerned about alignment issues.  I am not sure that the use of
> ALIGNED_CAST here is safe on RISC platforms, because IPv6 addresses
> aren't always aligned on a 32-bit boundary when they are inside Ethernet
> packets with different encapsulations.  It would probably be better to
> use a different strategy to assure alignment, rather than to just assume
> it.
>
> Please add documentation for the new action, in ovn-sb.xml.
>
> I see that there are a few notes and to-do items; I guess that's what
> makes it a work-in-progress.
>
> Thanks,
>
> Ben.
>

Hi, Ben. Thanks for your time and review.

I know Justin is working on implement router patch port for IPv6 switch,
but I'm not sure whether will Justin also work on implement ND or not.
And I've separated the WIP patch to:
 - 1) ovn-controller: Add 'na' action for ND
 - 2) ovn: add lflows for 'na' action for ND
it goes to version 3 now, and I just updated test in 1). Not sure what kind
of test should be added in 2) yet.

About the comments you gave:
 - the function compose_na(), I've moved it to lib/packets.h and
lib/packets.c, and it's public function now. And after that,
<netinet/icmp6.h> is no longer needed in pinctrl.c.
 - the function reload_metadata, I keep it in pinctrl.c and make it static.
 - about na->rco_flags.hi, the na.rco_flags is in a struct has two ovs_be16
type variable, hi and lo. So I think may be it's OK to directly assign the
hi variable a ovs_be16 type value.
 - about ALIGNED_CAST, indeed, I copied most part of compose_nd() to
implement compose_na(), so I suppose they could work fine.

Thanks again & have a nice day! :)

Best regards,
Zong Kai, LI
Justin Pettit June 8, 2016, 4:43 p.m. UTC | #3
> On Jun 8, 2016, at 1:12 AM, Zong Kai LI <zealokii@gmail.com> wrote:
> 
> I know Justin is working on implement router patch port for IPv6 switch, but I'm not sure whether will Justin also work on implement ND or not.

Yes, I'm planning to add support for neighbor discovery and router advertisements as part of IPv6 support.

--Justin
Zong Kai LI June 12, 2016, 11:14 a.m. UTC | #4
On Thu, Jun 9, 2016 at 12:43 AM, Justin Pettit <jpettit@ovn.org> wrote:

>
> > On Jun 8, 2016, at 1:12 AM, Zong Kai LI <zealokii@gmail.com> wrote:
> >
> > I know Justin is working on implement router patch port for IPv6 switch,
> but I'm not sure whether will Justin also work on implement ND or not.
>
> Yes, I'm planning to add support for neighbor discovery and router
> advertisements as part of IPv6 support.
>
> --Justin
>
>
>
Hi, Justin.
Sorry for I don't know your plans like that, no offence, I suppose you're
just trying to implement the patch port part.
Can I keep working on ND? Since I've just started working on it. :)

BTW, I also implemented some prototype code for RA, to make SLAAC can work.
Shall I keep working on it if you don't start yet.

Thanks for your time and have a nice day! :)
Best regards,
Zong Kai, LI
diff mbox

Patch

diff --git a/lib/packets.c b/lib/packets.c
index 6a55d6f..ad7c389 100644
--- a/lib/packets.c
+++ b/lib/packets.c
@@ -1301,7 +1301,7 @@  compose_arp__(struct dp_packet *b)
 
 /* This function expect packet with ethernet header with correct
  * l3 pointer set. */
-static void *
+void *
 compose_ipv6(struct dp_packet *packet, uint8_t proto, const ovs_be32 src[4],
              const ovs_be32 dst[4], uint8_t key_tc, ovs_be32 key_fl,
              uint8_t key_hl, int size)
diff --git a/lib/packets.h b/lib/packets.h
index 5945940..82d793c 100644
--- a/lib/packets.h
+++ b/lib/packets.h
@@ -274,6 +274,9 @@  static inline uint32_t hash_mac(const struct eth_addr ea,
 bool eth_addr_is_reserved(const struct eth_addr);
 bool eth_addr_from_string(const char *, struct eth_addr *);
 
+void *compose_ipv6(struct dp_packet *packet, uint8_t proto, const ovs_be32 src[4],
+                   const ovs_be32 dst[4], uint8_t key_tc, ovs_be32 key_fl,
+                   uint8_t key_hl, int size);
 void compose_rarp(struct dp_packet *, const struct eth_addr);
 
 void eth_push_vlan(struct dp_packet *, ovs_be16 tpid, ovs_be16 tci);
diff --git a/ovn/controller/pinctrl.c b/ovn/controller/pinctrl.c
index bc57c40..5265207 100644
--- a/ovn/controller/pinctrl.c
+++ b/ovn/controller/pinctrl.c
@@ -14,6 +14,7 @@ 
  */
 
 #include <config.h>
+#include <netinet/icmp6.h>
 
 #include "pinctrl.h"
 
@@ -23,6 +24,8 @@ 
 #include "flow.h"
 #include "lport.h"
 #include "ovn-controller.h"
+#include "lib/csum.h"
+#include "lib/packets.h"
 #include "lib/sset.h"
 #include "openvswitch/ofp-actions.h"
 #include "openvswitch/ofp-msgs.h"
@@ -64,6 +67,14 @@  static void send_garp_run(const struct ovsrec_bridge *,
                           const char *chassis_id,
                           const struct lport_index *lports,
                           struct hmap *local_datapaths);
+static void pinctrl_handle_nd(const struct flow *ip_flow,
+                              const struct match *md,
+                              struct ofpbuf *userdata);
+void compose_na(struct dp_packet *,
+                const struct eth_addr eth_src, const struct eth_addr eth_dst,
+                const struct in6_addr *, const struct in6_addr *);
+void reload_metadata(struct ofpbuf *ofpacts,
+                     const struct match *md);
 
 COVERAGE_DEFINE(pinctrl_drop_put_arp);
 
@@ -153,31 +164,7 @@  pinctrl_handle_arp(const struct flow *ip_flow, const struct match *md,
     struct ofpbuf ofpacts = OFPBUF_STUB_INITIALIZER(ofpacts_stub);
     enum ofp_version version = rconn_get_version(swconn);
 
-    enum mf_field_id md_fields[] = {
-#if FLOW_N_REGS == 8
-        MFF_REG0,
-        MFF_REG1,
-        MFF_REG2,
-        MFF_REG3,
-        MFF_REG4,
-        MFF_REG5,
-        MFF_REG6,
-        MFF_REG7,
-#else
-#error
-#endif
-        MFF_METADATA,
-    };
-    for (size_t i = 0; i < ARRAY_SIZE(md_fields); i++) {
-        const struct mf_field *field = mf_from_id(md_fields[i]);
-        if (!mf_is_all_wild(field, &md->wc)) {
-            struct ofpact_set_field *sf = ofpact_put_SET_FIELD(&ofpacts);
-            sf->field = field;
-            sf->flow_has_vlan = false;
-            mf_get_value(field, &md->flow, &sf->value);
-            memset(&sf->mask, 0xff, field->n_bytes);
-        }
-    }
+    reload_metadata(&ofpacts, md);
     enum ofperr error = ofpacts_pull_openflow_actions(userdata, userdata->size,
                                                       version, &ofpacts);
     if (error) {
@@ -242,6 +229,10 @@  process_packet_in(const struct ofp_header *msg)
         pinctrl_handle_put_arp(&pin.flow_metadata.flow, &headers);
         break;
 
+    case ACTION_OPCODE_ND:
+        pinctrl_handle_nd(&headers, &pin.flow_metadata, &userdata);
+        break;
+
     default:
         VLOG_WARN_RL(&rl, "unrecognized packet-in opcode %"PRIu32,
                      ntohl(ah->opcode));
@@ -734,3 +725,118 @@  send_garp_run(const struct ovsrec_bridge *br_int, const char *chassis_id,
     sset_destroy(&localnet_vifs);
     simap_destroy(&localnet_ofports);
 }
+
+void
+compose_na(struct dp_packet *b,
+           const struct eth_addr eth_src, const struct eth_addr eth_dst,
+           const struct in6_addr *ipv6_src, const struct in6_addr *ipv6_dst)
+{
+    struct ovs_nd_msg *na;
+    struct ovs_nd_opt *nd_opt;
+    uint32_t icmp_csum;
+
+    eth_compose(b, eth_dst, eth_src, ETH_TYPE_IPV6, IPV6_HEADER_LEN);
+    na = compose_ipv6(b, IPPROTO_ICMPV6,
+                      ALIGNED_CAST(ovs_be32 *, ipv6_src->s6_addr),
+                      ALIGNED_CAST(ovs_be32 *, ipv6_dst->s6_addr),
+                      0, 0, 255,
+                      ND_MSG_LEN + ND_OPT_LEN);
+
+    na->icmph.icmp6_type = ND_NEIGHBOR_ADVERT;
+    na->icmph.icmp6_code = 0;
+    //TODO(lizk): RSO ?
+    na->rco_flags.hi = (ovs_be16)0x60;
+
+    nd_opt = &na->options[0];
+    nd_opt->nd_opt_type = ND_OPT_TARGET_LINKADDR;
+    nd_opt->nd_opt_len = 1;
+
+    packet_set_nd(b, ALIGNED_CAST(ovs_be32 *, ipv6_src->s6_addr),
+                  eth_addr_zero, eth_src);
+    na->icmph.icmp6_cksum = 0;
+    icmp_csum = packet_csum_pseudoheader6(dp_packet_l3(b));
+    na->icmph.icmp6_cksum = csum_finish(csum_continue(icmp_csum, na,
+                                                      ND_MSG_LEN + ND_OPT_LEN));
+}
+
+void reload_metadata(struct ofpbuf *ofpacts, const struct match *md)
+{
+    enum mf_field_id md_fields[] = {
+#if FLOW_N_REGS == 8
+        MFF_REG0,
+        MFF_REG1,
+        MFF_REG2,
+        MFF_REG3,
+        MFF_REG4,
+        MFF_REG5,
+        MFF_REG6,
+        MFF_REG7,
+#else
+#error
+#endif
+        MFF_METADATA,
+    };
+    for (size_t i = 0; i < ARRAY_SIZE(md_fields); i++) {
+        const struct mf_field *field = mf_from_id(md_fields[i]);
+        if (!mf_is_all_wild(field, &md->wc)) {
+            struct ofpact_set_field *sf = ofpact_put_SET_FIELD(ofpacts);
+            sf->field = field;
+            sf->flow_has_vlan = false;
+            mf_get_value(field, &md->flow, &sf->value);
+            memset(&sf->mask, 0xff, field->n_bytes);
+        }
+    }
+}
+
+static void
+pinctrl_handle_nd(const struct flow *ip_flow,
+                  const struct match *md,
+                  struct ofpbuf *userdata)
+{
+    enum ofp_version version = rconn_get_version(swconn);
+    enum ofputil_protocol proto = ofputil_protocol_from_ofp_version(version);
+
+    const struct eth_addr *dl_reply = ofpbuf_try_pull(userdata,
+                                                      sizeof *dl_reply);
+    if (!dl_reply) {
+        goto exit;
+    }
+
+    //TODO(lizk): Validate the NS packet.
+
+    // Frame the NA packet.
+    uint64_t packet_stub[128 / 8];
+    struct dp_packet packet;
+    dp_packet_use_stub(&packet, packet_stub, sizeof packet_stub);
+    compose_na(&packet,
+               *dl_reply, ip_flow->dl_src,
+               &(ip_flow->nd_target), &(ip_flow->ipv6_src));
+
+    uint64_t ofpacts_stub[4096 / 8];
+    struct ofpbuf ofpacts = OFPBUF_STUB_INITIALIZER(ofpacts_stub);
+    reload_metadata(&ofpacts, md);
+
+    enum ofperr error = ofpacts_pull_openflow_actions(userdata, userdata->size,
+                                                      version, &ofpacts);
+    if (error) {
+        static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5);
+        VLOG_WARN_RL(&rl, "failed to parse actions for put_nd_tll (%s)",
+                     ofperr_to_string(error));
+        goto exit;
+    }
+
+    struct ofputil_packet_out po = {
+        .packet = dp_packet_data(&packet),
+        .packet_len = dp_packet_size(&packet),
+        .buffer_id = UINT32_MAX,
+        .in_port = OFPP_CONTROLLER,
+        .ofpacts = ofpacts.data,
+        .ofpacts_len = ofpacts.size,
+    };
+
+    queue_msg(ofputil_encode_packet_out(&po, proto));
+
+exit:
+    dp_packet_uninit(&packet);
+    ofpbuf_uninit(&ofpacts);
+}
diff --git a/ovn/lib/actions.c b/ovn/lib/actions.c
index 5f0bf19..2fce543 100644
--- a/ovn/lib/actions.c
+++ b/ovn/lib/actions.c
@@ -442,6 +442,54 @@  emit_ct(struct action_context *ctx, bool recirc_next, bool commit)
     add_prerequisite(ctx, "ip");
 }
 
+static void
+parse_put_nd_action(struct action_context *ctx)
+{
+    if (!lexer_match(ctx->lexer, LEX_T_LPAREN)) {
+        action_syntax_error(ctx, "expecting `('");
+        return;
+    }
+
+    struct expr_constant_set cs;
+    struct expr_context expr_ctx = {
+       .lexer = ctx->lexer,
+       .symtab = NULL,
+    };
+    if (!parse_constant_set(&expr_ctx, &cs)) {
+        action_syntax_error(ctx, "expecting NA mac");
+        return;
+    }
+    if (!lexer_match(ctx->lexer, LEX_T_SEMICOLON)) {
+        action_syntax_error(ctx, "expecting ';'");
+        return;
+    }
+
+    struct ofpbuf *outer_ofpacts = ctx->ofpacts;
+    uint64_t inner_ofpacts_stub[1024 / 8];
+    struct ofpbuf inner_ofpacts = OFPBUF_STUB_INITIALIZER(inner_ofpacts_stub);
+    ctx->ofpacts = &inner_ofpacts;
+
+    /* Parse inner actions. */
+    while (!lexer_match(ctx->lexer, LEX_T_RPAREN)) {
+        if (!parse_action(ctx)) {
+            break;
+        }
+    }
+
+    ctx->ofpacts = outer_ofpacts;
+
+    /* controller. */
+    size_t oc_offset = start_controller_op(ctx->ofpacts, ACTION_OPCODE_ND);
+    ofpbuf_put(ctx->ofpacts, &cs.values[0].value.mac, sizeof(struct eth_addr));
+    ofpacts_put_openflow_actions(inner_ofpacts.data, inner_ofpacts.size,
+                                 ctx->ofpacts, OFP13_VERSION);
+    finish_controller_op(ctx->ofpacts, oc_offset);
+
+    /* Free memory. */
+    expr_constant_set_destroy(&cs);
+    ofpbuf_uninit(&inner_ofpacts);
+}
+
 static bool
 parse_action(struct action_context *ctx)
 {
@@ -475,6 +523,8 @@  parse_action(struct action_context *ctx)
         parse_get_arp_action(ctx);
     } else if (lexer_match_id(ctx->lexer, "put_arp")) {
         parse_put_arp_action(ctx);
+    } else if (lexer_match_id(ctx->lexer, "put_nd_tll")) {
+        parse_put_nd_action(ctx);
     } else {
         action_syntax_error(ctx, "expecting action");
     }
diff --git a/ovn/lib/actions.h b/ovn/lib/actions.h
index 29af06f..c5cc84c 100644
--- a/ovn/lib/actions.h
+++ b/ovn/lib/actions.h
@@ -44,6 +44,12 @@  enum action_opcode {
      *     MFF_ETH_SRC = mac
      */
     ACTION_OPCODE_PUT_ARP,
+
+    /* "put_nd_tll(mac; ...actions... )".
+     *
+     * The actions, in OpenFlow 1.3 format, follow the action_header.
+     */
+    ACTION_OPCODE_ND
 };
 
 /* Header. */
diff --git a/ovn/lib/expr.c b/ovn/lib/expr.c
index f274ab4..b89b207 100644
--- a/ovn/lib/expr.c
+++ b/ovn/lib/expr.c
@@ -405,39 +405,6 @@  expr_print(const struct expr *e)
 
 /* Parsing. */
 
-/* Type of a "union expr_constant" or "struct expr_constant_set". */
-enum expr_constant_type {
-    EXPR_C_INTEGER,
-    EXPR_C_STRING
-};
-
-/* A string or integer constant (one must know which from context). */
-union expr_constant {
-    /* Integer constant.
-     *
-     * The width of a constant isn't always clear, e.g. if you write "1",
-     * there's no way to tell whether you mean for that to be a 1-bit constant
-     * or a 128-bit constant or somewhere in between. */
-    struct {
-        union mf_subvalue value;
-        union mf_subvalue mask; /* Only initialized if 'masked'. */
-        bool masked;
-
-        enum lex_format format; /* From the constant's lex_token. */
-    };
-
-    /* Null-terminated string constant. */
-    char *string;
-};
-
-/* A collection of "union expr_constant"s of the same type. */
-struct expr_constant_set {
-    union expr_constant *values;  /* Constants. */
-    size_t n_values;              /* Number of constants. */
-    enum expr_constant_type type; /* Type of the constants. */
-    bool in_curlies;              /* Whether the constants were in {}. */
-};
-
 /* A reference to a symbol or a subfield of a symbol.
  *
  * For string fields, ofs and n_bits are 0. */
@@ -447,17 +414,9 @@  struct expr_field {
     int n_bits;                       /* Number of bits. */
 };
 
-/* Context maintained during expr_parse(). */
-struct expr_context {
-    struct lexer *lexer;        /* Lexer for pulling more tokens. */
-    const struct shash *symtab; /* Symbol table. */
-    char *error;                /* Error, if any, otherwise NULL. */
-    bool not;                   /* True inside odd number of NOT operators. */
-};
-
 struct expr *expr_parse__(struct expr_context *);
 static void expr_not(struct expr *);
-static void expr_constant_set_destroy(struct expr_constant_set *);
+void expr_constant_set_destroy(struct expr_constant_set *);
 static bool parse_field(struct expr_context *, struct expr_field *);
 
 static bool
@@ -812,7 +771,7 @@  parse_constant(struct expr_context *ctx, struct expr_constant_set *cs,
  * which the caller need not have initialized.  Returns true on success, in
  * which case the caller owns 'cs', false on failure, in which case 'cs' is
  * indeterminate. */
-static bool
+bool
 parse_constant_set(struct expr_context *ctx, struct expr_constant_set *cs)
 {
     size_t allocated_values = 0;
@@ -838,7 +797,7 @@  parse_constant_set(struct expr_context *ctx, struct expr_constant_set *cs)
     return ok;
 }
 
-static void
+void
 expr_constant_set_destroy(struct expr_constant_set *cs)
 {
     if (cs) {
diff --git a/ovn/lib/expr.h b/ovn/lib/expr.h
index 1327789..2742d2a 100644
--- a/ovn/lib/expr.h
+++ b/ovn/lib/expr.h
@@ -391,4 +391,47 @@  char *expr_parse_field(struct lexer *, int n_bits, bool rw,
                        const struct shash *symtab, struct mf_subfield *,
                        struct expr **prereqsp);
 
+/* Context maintained during expr_parse(). */
+struct expr_context {
+    struct lexer *lexer;        /* Lexer for pulling more tokens. */
+    const struct shash *symtab; /* Symbol table. */
+    char *error;                /* Error, if any, otherwise NULL. */
+    bool not;                   /* True inside odd number of NOT operators. */
+};
+
+/* Type of a "union expr_constant" or "struct expr_constant_set". */
+enum expr_constant_type {
+    EXPR_C_INTEGER,
+    EXPR_C_STRING
+};
+
+/* A string or integer constant (one must know which from context). */
+union expr_constant {
+    /* Integer constant.
+     *
+     * The width of a constant isn't always clear, e.g. if you write "1",
+     * there's no way to tell whether you mean for that to be a 1-bit constant
+     * or a 128-bit constant or somewhere in between. */
+    struct {
+        union mf_subvalue value;
+        union mf_subvalue mask; /* Only initialized if 'masked'. */
+        bool masked;
+
+        enum lex_format format; /* From the constant's lex_token. */
+    };
+
+    /* Null-terminated string constant. */
+    char *string;
+};
+
+/* A collection of "union expr_constant"s of the same type. */
+struct expr_constant_set {
+    union expr_constant *values;  /* Constants. */
+    size_t n_values;              /* Number of constants. */
+    enum expr_constant_type type; /* Type of the constants. */
+    bool in_curlies;              /* Whether the constants were in {}. */
+};
+
+bool parse_constant_set(struct expr_context *ctx, struct expr_constant_set *cs);
+void expr_constant_set_destroy(struct expr_constant_set *cs);
 #endif /* ovn/expr.h */
diff --git a/ovn/northd/ovn-northd.c b/ovn/northd/ovn-northd.c
index 44e9430..38796a1 100644
--- a/ovn/northd/ovn-northd.c
+++ b/ovn/northd/ovn-northd.c
@@ -1381,6 +1381,15 @@  build_acls(struct ovn_datapath *od, struct hmap *lflows, struct hmap *ports)
         bool ingress = !strcmp(acl->direction, "from-lport") ? true :false;
         enum ovn_stage stage = ingress ? S_SWITCH_IN_ACL : S_SWITCH_OUT_ACL;
 
+        //NOTE(lizk)
+        /* why it's allow-related for icmp6 ? */
+        if (strstr(acl->match, "ip6 && icmp6")) {
+            ovn_lflow_add(lflows, od, S_SWITCH_OUT_PRE_ACL,
+                          acl->priority + OVN_ACL_PRI_OFFSET,
+                          acl->match, "output;");
+            continue;
+        }
+
         if (!strcmp(acl->action, "allow")) {
             /* If there are any stateful flows, we must even commit "allow"
              * actions.  This is because, while the initiater's
@@ -1531,7 +1540,7 @@  build_lswitch_flows(struct hmap *datapaths, struct hmap *ports,
         for (size_t i = 0; i < op->nbs->n_addresses; i++) {
             struct lport_addresses laddrs;
             if (!extract_lport_addresses(op->nbs->addresses[i], &laddrs,
-                                         false)) {
+                                         true)) {
                 continue;
             }
             for (size_t j = 0; j < laddrs.n_ipv4_addrs; j++) {
@@ -1557,8 +1566,27 @@  build_lswitch_flows(struct hmap *datapaths, struct hmap *ports,
                 free(match);
                 free(actions);
             }
+            char ip6_str[INET6_ADDRSTRLEN + 1];
+            for (size_t j = 0; j < laddrs.n_ipv6_addrs; j++) {
+                ipv6_string_mapped(ip6_str, &(laddrs.ipv6_addrs[i].addr));
+
+                struct ds match = DS_EMPTY_INITIALIZER;
+                ds_put_cstr(&match, "ip6 && nd && icmp6.type == 135 && nd.target == ");
+                ds_put_format(&match, "%s", ip6_str);
+                struct ds actions = DS_EMPTY_INITIALIZER;
+                ds_put_cstr(&actions, "put_nd_tll(");
+                ds_put_format(&actions, ETH_ADDR_FMT, ETH_ADDR_ARGS(laddrs.ea));
+                ds_put_cstr(&actions, ";outport = inport; inport = \"\";output;);");
+
+                ovn_lflow_add(lflows, op->od, S_SWITCH_IN_ARP_RSP, 50,
+                              ds_cstr(&match), ds_cstr(&actions));
+
+                ds_destroy(&actions);
+                ds_destroy(&match);
+            }
 
             free(laddrs.ipv4_addrs);
+            free(laddrs.ipv6_addrs);
         }
     }