[{"id":1776605,"web_url":"http://patchwork.ozlabs.org/comment/1776605/","msgid":"<CAA1+qOPyDXKpn4sasHRnThFUHHXiC-ibVy-nehx3MLee27X5Mg@mail.gmail.com>","list_archive_url":null,"date":"2017-09-27T20:42:42","subject":"Re: [ovs-dev] [PATCH v8 4/4] ovn: Generate Neighbor Solicitation\n\tpacket for unknown MAC IPv6 packets","submitter":{"id":71978,"url":"http://patchwork.ozlabs.org/api/people/71978/","name":"Mark Michelson","email":"mmichels@redhat.com"},"content":"On Thu, Sep 21, 2017 at 11:12 AM <nusiddiq@redhat.com> wrote:\n\n> From: Numan Siddique <nusiddiq@redhat.com>\n>\n> In the router ingress pipeline, if the destination mac is unresolved\n> by the time the packet reaches the ARP_REQUEST stage, OVN should generate\n> an\n> IPv6 Neighbor Solicitation packet to learn the MAC address. This feature is\n> presently missing. This patch adds this feature.  A new action \"nd_ns\" is\n> added  which replaces an IPv6 packet being processed with an IPv6 Neighbor\n> Solicitation packet. ovn-northd adds a flow in the ARP_REQUEST router\n> ingress\n> pipeline stage if the eth.dst is zero which applies this action. This\n> action is\n> similar to the IPv4 counterpart \"arp\" action.\n>\n> OVN already has the support to learn the MAC from the IPv6 Neighbor\n> Advertisement\n> packets and storing in the south bound MAC_Binding table.\n>\n> Signed-off-by: Numan Siddique <nusiddiq@redhat.com>\n> ---\n>  include/ovn/actions.h       |   9 +++-\n>  ovn/controller/pinctrl.c    | 122\n> +++++++++++++++++++++++---------------------\n>  ovn/lib/actions.c           |  22 ++++++++\n>  ovn/northd/ovn-northd.8.xml |  24 ++++++---\n>  ovn/northd/ovn-northd.c     |   8 ++-\n>  ovn/ovn-sb.xml              |  37 ++++++++++++++\n>  ovn/utilities/ovn-trace.c   |  30 +++++++++++\n>  tests/ovn.at                | 116\n> +++++++++++++++++++++++++++++++++++++++++\n>  8 files changed, 302 insertions(+), 66 deletions(-)\n>\n> diff --git a/include/ovn/actions.h b/include/ovn/actions.h\n> index 15cee478d..8c7208ffc 100644\n> --- a/include/ovn/actions.h\n> +++ b/include/ovn/actions.h\n> @@ -73,7 +73,8 @@ struct simap;\n>      OVNACT(SET_QUEUE,         ovnact_set_queue)       \\\n>      OVNACT(DNS_LOOKUP,        ovnact_dns_lookup)      \\\n>      OVNACT(LOG,               ovnact_log)             \\\n> -    OVNACT(PUT_ND_RA_OPTS,    ovnact_put_opts)\n> +    OVNACT(PUT_ND_RA_OPTS,    ovnact_put_opts)        \\\n> +    OVNACT(ND_NS,             ovnact_nest)\n>\n>  /* enum ovnact_type, with a member OVNACT_<ENUM> for each action. */\n>  enum OVS_PACKED_ENUM ovnact_type {\n> @@ -427,6 +428,12 @@ enum action_opcode {\n>       *   - Any number of ICMPv6 options.\n>       */\n>      ACTION_OPCODE_PUT_ND_RA_OPTS,\n> +\n> +    /* \"nd_ns { ...actions... }\".\n> +     *\n> +     * The actions, in OpenFlow 1.3 format, follow the action_header.\n> +     */\n> +    ACTION_OPCODE_ND_NS,\n>  };\n>\n>  /* Header. */\n> diff --git a/ovn/controller/pinctrl.c b/ovn/controller/pinctrl.c\n> index 6dbea4efa..cad0f67ba 100644\n> --- a/ovn/controller/pinctrl.c\n> +++ b/ovn/controller/pinctrl.c\n> @@ -85,6 +85,9 @@ static void pinctrl_handle_put_nd_ra_opts(\n>      const struct flow *ip_flow, struct dp_packet *pkt_in,\n>      struct ofputil_packet_in *pin, struct ofpbuf *userdata,\n>      struct ofpbuf *continuation OVS_UNUSED);\n> +static void pinctrl_handle_nd_ns(const struct flow *ip_flow,\n> +                                 const struct match *md,\n> +                                 struct ofpbuf *userdata);\n>\n>  COVERAGE_DEFINE(pinctrl_drop_put_mac_binding);\n>\n> @@ -132,6 +135,43 @@ set_switch_config(struct rconn *swconn,\n>  }\n>\n>  static void\n> +set_actions_and_enqueu_msg(const struct dp_packet *packet,\n>\n\nNit: The word is spelled \"enqueue\", not \"enqueu\". This could potentially\nlead to some confusion if left this way.\n\n\n> +                           const struct match *md,\n> +                           struct ofpbuf *userdata)\n> +{\n> +    /* Copy metadata from 'md' into the packet-out via \"set_field\"\n> +     * actions, then add actions from 'userdata'.\n> +     */\n> +    uint64_t ofpacts_stub[4096 / 8];\n> +    struct ofpbuf ofpacts = OFPBUF_STUB_INITIALIZER(ofpacts_stub);\n> +    enum ofp_version version = rconn_get_version(swconn);\n> +\n> +    reload_metadata(&ofpacts, md);\n> +    enum ofperr error = ofpacts_pull_openflow_actions(userdata,\n> userdata->size,\n> +                                                      version, NULL, NULL,\n> +                                                      &ofpacts);\n> +    if (error) {\n> +        static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5);\n> +        VLOG_WARN_RL(&rl, \"failed to parse actions from userdata (%s)\",\n> +                     ofperr_to_string(error));\n> +        ofpbuf_uninit(&ofpacts);\n> +        return;\n> +    }\n> +\n> +    struct ofputil_packet_out po = {\n> +        .packet = dp_packet_data(packet),\n> +        .packet_len = dp_packet_size(packet),\n> +        .buffer_id = UINT32_MAX,\n> +        .ofpacts = ofpacts.data,\n> +        .ofpacts_len = ofpacts.size,\n> +    };\n> +    match_set_in_port(&po.flow_metadata, OFPP_CONTROLLER);\n> +    enum ofputil_protocol proto =\n> ofputil_protocol_from_ofp_version(version);\n> +    queue_msg(ofputil_encode_packet_out(&po, proto));\n> +    ofpbuf_uninit(&ofpacts);\n> +}\n> +\n> +static void\n>  pinctrl_handle_arp(const struct flow *ip_flow, const struct match *md,\n>                     struct ofpbuf *userdata)\n>  {\n> @@ -166,40 +206,8 @@ pinctrl_handle_arp(const struct flow *ip_flow, const\n> struct match *md,\n>                        ip_flow->vlans[0].tci);\n>      }\n>\n> -    /* Compose actions.\n> -     *\n> -     * First, copy metadata from 'md' into the packet-out via \"set_field\"\n> -     * actions, then add actions from 'userdata'.\n> -     */\n> -    uint64_t ofpacts_stub[4096 / 8];\n> -    struct ofpbuf ofpacts = OFPBUF_STUB_INITIALIZER(ofpacts_stub);\n> -    enum ofp_version version = rconn_get_version(swconn);\n> -\n> -    reload_metadata(&ofpacts, md);\n> -    enum ofperr error = ofpacts_pull_openflow_actions(userdata,\n> userdata->size,\n> -                                                      version, NULL, NULL,\n> -                                                      &ofpacts);\n> -    if (error) {\n> -        static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5);\n> -        VLOG_WARN_RL(&rl, \"failed to parse arp actions (%s)\",\n> -                     ofperr_to_string(error));\n> -        goto exit;\n> -    }\n> -\n> -    struct ofputil_packet_out po = {\n> -        .packet = dp_packet_data(&packet),\n> -        .packet_len = dp_packet_size(&packet),\n> -        .buffer_id = UINT32_MAX,\n> -        .ofpacts = ofpacts.data,\n> -        .ofpacts_len = ofpacts.size,\n> -    };\n> -    match_set_in_port(&po.flow_metadata, OFPP_CONTROLLER);\n> -    enum ofputil_protocol proto =\n> ofputil_protocol_from_ofp_version(version);\n> -    queue_msg(ofputil_encode_packet_out(&po, proto));\n> -\n> -exit:\n> +    set_actions_and_enqueu_msg(&packet, md, userdata);\n>      dp_packet_uninit(&packet);\n> -    ofpbuf_uninit(&ofpacts);\n>  }\n>\n>  static void\n> @@ -994,6 +1002,10 @@ process_packet_in(const struct ofp_header *msg,\n> struct controller_ctx *ctx)\n>                                        &continuation);\n>          break;\n>\n> +    case ACTION_OPCODE_ND_NS:\n> +        pinctrl_handle_nd_ns(&headers, &pin.flow_metadata, &userdata);\n> +        break;\n> +\n>      default:\n>          VLOG_WARN_RL(&rl, \"unrecognized packet-in opcode %\"PRIu32,\n>                       ntohl(ah->opcode));\n> @@ -1812,9 +1824,6 @@ pinctrl_handle_nd_na(const struct flow *ip_flow,\n> const struct match *md,\n>          return;\n>      }\n>\n> -    enum ofp_version version = rconn_get_version(swconn);\n> -    enum ofputil_protocol proto =\n> ofputil_protocol_from_ofp_version(version);\n> -\n>      uint64_t packet_stub[128 / 8];\n>      struct dp_packet packet;\n>      dp_packet_use_stub(&packet, packet_stub, sizeof packet_stub);\n> @@ -1827,35 +1836,32 @@ pinctrl_handle_nd_na(const struct flow *ip_flow,\n> const struct match *md,\n>                    &ip_flow->nd_target, &ip_flow->ipv6_src,\n>                    htonl(ND_RSO_SOLICITED | ND_RSO_OVERRIDE));\n>\n> -    /* Reload previous packet metadata. */\n> -    uint64_t ofpacts_stub[4096 / 8];\n> -    struct ofpbuf ofpacts = OFPBUF_STUB_INITIALIZER(ofpacts_stub);\n> -    reload_metadata(&ofpacts, md);\n> +    /* Reload previous packet metadata and set actions from userdata. */\n> +    set_actions_and_enqueu_msg(&packet, md, userdata);\n> +    dp_packet_uninit(&packet);\n> +}\n>\n> -    enum ofperr error = ofpacts_pull_openflow_actions(userdata,\n> userdata->size,\n> -                                                      version, NULL, NULL,\n> -                                                      &ofpacts);\n> -    if (error) {\n> +static void\n> +pinctrl_handle_nd_ns(const struct flow *ip_flow, const struct match *md,\n> +                     struct ofpbuf *userdata)\n> +{\n> +    /* This action only works for IPv6 packets. */\n> +    if (get_dl_type(ip_flow) != htons(ETH_TYPE_IPV6)) {\n>          static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5);\n> -        VLOG_WARN_RL(&rl, \"failed to parse actions for 'na' (%s)\",\n> -                     ofperr_to_string(error));\n> -        goto exit;\n> +        VLOG_WARN_RL(&rl, \"NS action on non-IPv6 packet\");\n> +        return;\n>      }\n>\n> -    struct ofputil_packet_out po = {\n> -        .packet = dp_packet_data(&packet),\n> -        .packet_len = dp_packet_size(&packet),\n> -        .buffer_id = UINT32_MAX,\n> -        .ofpacts = ofpacts.data,\n> -        .ofpacts_len = ofpacts.size,\n> -    };\n> -    match_set_in_port(&po.flow_metadata, OFPP_CONTROLLER);\n> +    uint64_t packet_stub[128 / 8];\n> +    struct dp_packet packet;\n> +    dp_packet_use_stub(&packet, packet_stub, sizeof packet_stub);\n>\n> -    queue_msg(ofputil_encode_packet_out(&po, proto));\n> +    compose_nd_ns(&packet, ip_flow->dl_src, &ip_flow->ipv6_src,\n> +                  &ip_flow->ipv6_dst);\n>\n> -exit:\n> +    /* Reload previous packet metadata and set actions from userdata. */\n> +    set_actions_and_enqueu_msg(&packet, md, userdata);\n>      dp_packet_uninit(&packet);\n> -    ofpbuf_uninit(&ofpacts);\n>  }\n>\n>  static void\n> diff --git a/ovn/lib/actions.c b/ovn/lib/actions.c\n> index 2981d89f6..5e5c666a9 100644\n> --- a/ovn/lib/actions.c\n> +++ b/ovn/lib/actions.c\n> @@ -1134,6 +1134,12 @@ parse_ND_NA(struct action_context *ctx)\n>  }\n>\n>  static void\n> +parse_ND_NS(struct action_context *ctx)\n> +{\n> +    parse_nested_action(ctx, OVNACT_ND_NS, \"ip6\");\n> +}\n> +\n> +static void\n>  parse_CLONE(struct action_context *ctx)\n>  {\n>      parse_nested_action(ctx, OVNACT_CLONE, NULL);\n> @@ -1161,6 +1167,12 @@ format_ND_NA(const struct ovnact_nest *nest, struct\n> ds *s)\n>  }\n>\n>  static void\n> +format_ND_NS(const struct ovnact_nest *nest, struct ds *s)\n> +{\n> +    format_nested_action(nest, \"nd_ns\", s);\n> +}\n> +\n> +static void\n>  format_CLONE(const struct ovnact_nest *nest, struct ds *s)\n>  {\n>      format_nested_action(nest, \"clone\", s);\n> @@ -1207,6 +1219,14 @@ encode_ND_NA(const struct ovnact_nest *on,\n>  }\n>\n>  static void\n> +encode_ND_NS(const struct ovnact_nest *on,\n> +             const struct ovnact_encode_params *ep,\n> +             struct ofpbuf *ofpacts)\n> +{\n> +    encode_nested_neighbor_actions(on, ep, ACTION_OPCODE_ND_NS, ofpacts);\n> +}\n> +\n> +static void\n>  encode_CLONE(const struct ovnact_nest *on,\n>               const struct ovnact_encode_params *ep,\n>               struct ofpbuf *ofpacts)\n> @@ -2146,6 +2166,8 @@ parse_action(struct action_context *ctx)\n>          parse_ARP(ctx);\n>      } else if (lexer_match_id(ctx->lexer, \"nd_na\")) {\n>          parse_ND_NA(ctx);\n> +    } else if (lexer_match_id(ctx->lexer, \"nd_ns\")) {\n> +        parse_ND_NS(ctx);\n>      } else if (lexer_match_id(ctx->lexer, \"get_arp\")) {\n>          parse_get_mac_bind(ctx, 32, ovnact_put_GET_ARP(ctx->ovnacts));\n>      } else if (lexer_match_id(ctx->lexer, \"put_arp\")) {\n> diff --git a/ovn/northd/ovn-northd.8.xml b/ovn/northd/ovn-northd.8.xml\n> index a994abf78..17123c690 100644\n> --- a/ovn/northd/ovn-northd.8.xml\n> +++ b/ovn/northd/ovn-northd.8.xml\n> @@ -1915,15 +1915,15 @@ next;\n>\n>      <p>\n>        In the common case where the Ethernet destination has been\n> resolved, this\n> -      table outputs the packet.  Otherwise, it composes and sends an ARP\n> -      request.  It holds the following flows:\n> +      table outputs the packet.  Otherwise, it composes and sends an ARP\n> or\n> +      IPv6 Neighbor Solicitation request.  It holds the following flows:\n>      </p>\n>\n>      <ul>\n>        <li>\n>          <p>\n> -          Unknown MAC address.  A priority-100 flow with match\n> <code>eth.dst ==\n> -          00:00:00:00:00:00</code> has the following actions:\n> +          Unknown MAC address.  A priority-100 flow for IPv4 packets with\n> match\n> +          <code>eth.dst == 00:00:00:00:00:00</code> has the following\n> actions:\n>          </p>\n>\n>          <pre>\n> @@ -1937,13 +1937,25 @@ arp {\n>          </pre>\n>\n>          <p>\n> +          Unknown MAC address.  A priority-100 flow for IPv6 packets with\n> match\n> +          <code>eth.dst == 00:00:00:00:00:00</code> has the following\n> actions:\n> +        </p>\n> +\n> +        <pre>\n> +nd_ns {\n> +    nd.target = xxreg0;\n> +    output;\n> +};\n> +        </pre>\n> +\n> +        <p>\n>            (Ingress table <code>IP Routing</code> initialized\n> <code>reg1</code>\n>            with the IP address owned by <code>outport</code> and\n> -          <code>reg0</code> with the next-hop IP address)\n> +          <code>(xx)reg0</code> with the next-hop IP address)\n>          </p>\n>\n>          <p>\n> -          The IP packet that triggers the ARP request is dropped.\n> +          The IP packet that triggers the ARP/IPv6 NS request is dropped.\n>          </p>\n>        </li>\n>\n> diff --git a/ovn/northd/ovn-northd.c b/ovn/northd/ovn-northd.c\n> index 3da20d25b..b4ea34bc6 100644\n> --- a/ovn/northd/ovn-northd.c\n> +++ b/ovn/northd/ovn-northd.c\n> @@ -5703,7 +5703,7 @@ build_lrouter_flows(struct hmap *datapaths, struct\n> hmap *ports,\n>       *\n>       * In the common case where the Ethernet destination has been\n> resolved,\n>       * this table outputs the packet (priority 0).  Otherwise, it composes\n> -     * and sends an ARP request (priority 100). */\n> +     * and sends an ARP/IPv6 NA request (priority 100). */\n>      HMAP_FOR_EACH (od, key_node, datapaths) {\n>          if (!od->nbr) {\n>              continue;\n> @@ -5718,6 +5718,12 @@ build_lrouter_flows(struct hmap *datapaths, struct\n> hmap *ports,\n>                        \"arp.op = 1; \" /* ARP request */\n>                        \"output; \"\n>                        \"};\");\n> +        ovn_lflow_add(lflows, od, S_ROUTER_IN_ARP_REQUEST, 100,\n> +                      \"eth.dst == 00:00:00:00:00:00\",\n> +                      \"nd_ns { \"\n> +                      \"nd.target = xxreg0; \"\n> +                      \"output; \"\n> +                      \"};\");\n>          ovn_lflow_add(lflows, od, S_ROUTER_IN_ARP_REQUEST, 0, \"1\",\n> \"output;\");\n>      }\n>\n> diff --git a/ovn/ovn-sb.xml b/ovn/ovn-sb.xml\n> index 2e4f28b96..ca8cbecdd 100644\n> --- a/ovn/ovn-sb.xml\n> +++ b/ovn/ovn-sb.xml\n> @@ -1258,6 +1258,43 @@\n>            <p><b>Example:</b> <code>put_arp(inport, arp.spa,\n> arp.sha);</code></p>\n>          </dd>\n>\n> +        <dt><code>nd_ns { <var>action</var>; </code>...<code>\n> };</code></dt>\n> +        <dd>\n> +          <p>\n> +            Temporarily replaces the IPv6 packet being processed by an\n> IPv6\n> +            Neighbor Solicitation packet and executes each nested\n> +            <var>action</var> on the IPv6 NS packet.  Actions following\n> the\n> +            <var>nd_ns</var> action, if any, apply to the original,\n> unmodified\n> +            packet.\n> +          </p>\n> +\n> +          <p>\n> +            The IPv6 NS packet that this action operates on is initialized\n> +            based on the IPv6 packet being processed, as follows.  These\n> are\n> +            default values that the nested actions will probably want to\n> +            change:\n> +          </p>\n> +\n> +          <ul>\n> +            <li><code>eth.src</code> unchanged</li>\n> +            <li><code>eth.dst</code> set to IPv6 multicast MAC\n> address</li>\n> +            <li><code>eth.type = 0x86dd</code></li>\n> +            <li><code>ip6.src</code> copied from <code>ip6.src</code></li>\n> +            <li>\n> +              <code>ip6.dst</code> set to IPv6 Solicited-Node multicast\n> address\n> +            </li>\n> +            <li><code>icmp6.type = 135</code> (Neighbor Solicitation)</li>\n> +            <li><code>nd.target</code> copied from\n> <code>ip6.dst</code></li>\n> +          </ul>\n> +\n> +          <p>\n> +            The IPv6 NS packet has the same VLAN header, if any, as the IP\n> +            packet it replaces.\n> +          </p>\n> +\n> +          <p><b>Prerequisite:</b> <code>ip6</code></p>\n> +        </dd>\n> +\n>          <dt>\n>            <code>nd_na { <var>action</var>; </code>...<code> };</code>\n>          </dt>\n> diff --git a/ovn/utilities/ovn-trace.c b/ovn/utilities/ovn-trace.c\n> index 211148b8b..e457284fc 100644\n> --- a/ovn/utilities/ovn-trace.c\n> +++ b/ovn/utilities/ovn-trace.c\n> @@ -1510,6 +1510,31 @@ execute_nd_na(const struct ovnact_nest *on, const\n> struct ovntrace_datapath *dp,\n>  }\n>\n>  static void\n> +execute_nd_ns(const struct ovnact_nest *on, const struct\n> ovntrace_datapath *dp,\n> +              const struct flow *uflow, uint8_t table_id,\n> +              enum ovnact_pipeline pipeline, struct ovs_list *super)\n> +{\n> +    struct flow na_flow = *uflow;\n> +\n> +    /* Update fields for NA. */\n> +    na_flow.dl_src = uflow->dl_src;\n> +    na_flow.ipv6_src = uflow->ipv6_src;\n> +    na_flow.ipv6_dst = uflow->ipv6_dst;\n> +    struct in6_addr sn_addr;\n> +    in6_addr_solicited_node(&sn_addr, &uflow->ipv6_dst);\n> +    ipv6_multicast_to_ethernet(&na_flow.dl_dst, &sn_addr);\n> +    na_flow.tp_src = htons(135);\n> +    na_flow.arp_sha = eth_addr_zero;\n> +    na_flow.arp_tha = uflow->dl_dst;\n> +\n> +    struct ovntrace_node *node = ovntrace_node_append(\n> +        super, OVNTRACE_NODE_TRANSFORMATION, \"nd_ns\");\n> +\n> +    trace_actions(on->nested, on->nested_len, dp, &na_flow,\n> +                  table_id, pipeline, &node->subs);\n> +}\n> +\n> +static void\n>  execute_get_mac_bind(const struct ovnact_get_mac_bind *bind,\n>                       const struct ovntrace_datapath *dp,\n>                       struct flow *uflow, struct ovs_list *super)\n> @@ -1811,6 +1836,11 @@ trace_actions(const struct ovnact *ovnacts, size_t\n> ovnacts_len,\n>                            super);\n>              break;\n>\n> +        case OVNACT_ND_NS:\n> +            execute_nd_ns(ovnact_get_ND_NS(a), dp, uflow, table_id,\n> pipeline,\n> +                          super);\n> +            break;\n> +\n>          case OVNACT_GET_ARP:\n>              execute_get_mac_bind(ovnact_get_GET_ARP(a), dp, uflow, super);\n>              break;\n> diff --git a/tests/ovn.at b/tests/ovn.at\n> index 3aa4e5e22..13cdc1679 100644\n> --- a/tests/ovn.at\n> +++ b/tests/ovn.at\n> @@ -993,6 +993,16 @@ reg1[0] = put_dhcp_opts(offerip=\"xyzzy\");\n>  reg1[0] = put_dhcp_opts(offerip=1.2.3.4, domain=1.2.3.4);\n>      DHCPv4 option domain requires string value.\n>\n> +# nd_ns\n> +nd_ns { nd.target = xxreg0; output; };\n> +    encodes as\n> controller(userdata=00.00.00.09.00.00.00.00.ff.ff.00.18.00.00.23.20.00.06.00.80.00.00.00.00.00.01.de.10.00.01.2e.10.ff.ff.00.10.00.00.23.20.00.0e.ff.f8.40.00.00.00)\n> +    has prereqs ip6\n> +\n> +nd_ns { };\n> +    formats as nd_ns { drop; };\n> +    encodes as controller(userdata=00.00.00.09.00.00.00.00)\n> +    has prereqs ip6\n> +\n>  # nd_na\n>  nd_na { eth.src = 12:34:56:78:9a:bc; nd.tll = 12:34:56:78:9a:bc; outport\n> = inport; inport = \"\"; /* Allow sending out inport. */ output; };\n>      formats as nd_na { eth.src = 12:34:56:78:9a:bc; nd.tll =\n> 12:34:56:78:9a:bc; outport = inport; inport = \"\"; output; };\n> @@ -8795,6 +8805,112 @@ OVN_CLEANUP([gw1],[gw2],[hv1])\n>\n>  AT_CLEANUP\n>\n> +AT_SETUP([ovn -- IPv6 Neighbor Solicitation for unknown MAC])\n> +AT_KEYWORDS([ovn-nd_ns for unknown mac])\n> +AT_SKIP_IF([test $HAVE_PYTHON = no])\n> +ovn_start\n> +\n> +ovn-nbctl ls-add sw0_ip6\n> +ovn-nbctl lsp-add sw0_ip6 sw0_ip6-port1\n> +ovn-nbctl lsp-set-addresses sw0_ip6-port1 \\\n> +\"50:64:00:00:00:02 aef0::5264:00ff:fe00:0002\"\n> +\n> +ovn-nbctl lsp-set-port-security sw0_ip6-port1 \\\n> +\"50:64:00:00:00:02 aef0::5264:00ff:fe00:0002\"\n> +\n> +ovn-nbctl lr-add lr0_ip6\n> +ovn-nbctl lrp-add lr0_ip6 lrp0_ip6 00:00:00:00:af:01 aef0::/64\n> +ovn-nbctl lsp-add sw0_ip6 lrp0_ip6-attachment\n> +ovn-nbctl lsp-set-type lrp0_ip6-attachment router\n> +ovn-nbctl lsp-set-addresses lrp0_ip6-attachment 00:00:00:00:af:01\n> +ovn-nbctl lsp-set-options lrp0_ip6-attachment router-port=lrp0_ip6\n> +ovn-nbctl set logical_router_port lrp0_ip6\n> ipv6_ra_configs:address_mode=slaac\n> +\n> +ovn-nbctl ls-add public\n> +ovn-nbctl lsp-add public ln-public\n> +ovn-nbctl lsp-set-addresses ln-public unknown\n> +ovn-nbctl lsp-set-type ln-public localnet\n> +ovn-nbctl lsp-set-options ln-public network_name=phys\n> +\n> +ovn-nbctl lrp-add lr0_ip6 ip6_public 00:00:02:01:02:04 \\\n> +2001:db8:1:0:200:02ff:fe01:0204/64 \\\n> +-- set Logical_Router_port ip6_public options:redirect-chassis=\"hv1\"\n> +\n> +\n> +ovn-nbctl lsp-add public rp-ip6_public -- set Logical_Switch_Port \\\n> +rp-ip6_public  type=router options:router-port=ip6_public \\\n> +-- lsp-set-addresses rp-ip6_public router\n> +\n> +net_add n1\n> +sim_add hv1\n> +as hv1\n> +ovs-vsctl add-br br-phys\n> +ovn_attach n1 br-phys 192.168.0.2\n> +\n> +ovs-vsctl -- add-port br-int hv1-vif1 -- \\\n> +    set interface hv1-vif1 external-ids:iface-id=sw0_ip6-port1 \\\n> +    options:tx_pcap=hv1/vif1-tx.pcap \\\n> +    options:rxq_pcap=hv1/vif1-rx.pcap \\\n> +    ofport-request=1\n> +ovs-vsctl set open . external-ids:ovn-bridge-mappings=phys:br-phys\n> +\n> +# Allow some time for ovn-northd and ovn-controller to catch up.\n> +# XXX This should be more systematic.\n> +sleep 1\n> +\n> +trim_zeros() {\n> +    sed 's/\\(00\\)\\{1,\\}$//'\n> +}\n> +\n> +# Test the IPv6 Neighbor Solicitation (NS) - nd_ns action for unknown MAC\n> +# addresses. ovn-controller should generate an IPv6 NS request for IPv6\n> +# packets whose MAC is unknown (in the ARP_REQUEST router pipeline stage.\n> +# test_ipv6 INPORT SRC_MAC DST_MAC SRC_IP DST_IP OUTPORT...\n> +# This function sends ipv6 packet\n> +test_ipv6() {\n> +    local inport=$1 src_mac=$2 dst_mac=$3 src_ip=$4\n> +    dst_ip=20010db800010000020002fffe010205\n> +\n> +    local\n> packet=${dst_mac}${src_mac}86dd6000000000083aff${src_ip}${dst_ip}\n> +    packet=${packet}8000000000000000\n> +    shift; shift; shift; shift\n> +\n> +    dst_mac=3333ff010205\n> +    src_mac=000002010204\n> +    mcast_node_ip=ff0200000000000000000001ff010205\n> +    expected_packet=${dst_mac}${src_mac}86dd6000000000203aff${src_ip}\n> +\n> expected_packet=${expected_packet}${mcast_node_ip}8700XXXX00000000${dst_ip}\n> +    expected_packet=${expected_packet}0101${src_mac}\n> +\n> +    as hv1 ovs-appctl netdev-dummy/receive hv1-vif${inport} $packet\n> +    echo $expected_packet >> ipv6_ns.expected\n> +}\n> +\n> +src_mac=506400000002\n> +dst_mac=00000000af01\n> +src_ip=aef0000000000000526400fffe000002\n> +# Send an IPv6 packet. Generated IPv6 Neighbor solicitation packet\n> +# should be received by the ports attached to br-phys.\n> +test_ipv6 1 $src_mac $dst_mac $src_ip 2\n> +\n> +$PYTHON \"$top_srcdir/utilities/ovs-pcap.in\" hv1/br-phys_n1-tx.pcap | \\\n> +trim_zeros > 1.packets\n> +$PYTHON \"$top_srcdir/utilities/ovs-pcap.in\" hv1/br-phys-tx.pcap | \\\n> +trim_zeros > 2.packets\n> +\n> +cat ipv6_ns.expected | cut -c -112 > expout\n> +AT_CHECK([cat 1.packets | cut -c -112], [0], [expout])\n> +AT_CHECK([cat 2.packets | cut -c -112], [0], [expout])\n> +\n> +# Skipping the ICMPv6 checksum\n> +cat ipv6_ns.expected | cut -c 117- > expout\n> +AT_CHECK([cat 1.packets | cut -c 117-], [0], [expout])\n> +AT_CHECK([cat 2.packets | cut -c 117-], [0], [expout])\n> +\n> +OVN_CLEANUP([hv1])\n> +\n> +AT_CLEANUP\n> +\n>  AT_SETUP([ovn -- options:requested-chassis for logical port])\n>  ovn_start\n>\n> --\n> 2.13.3\n>\n> _______________________________________________\n> dev mailing list\n> dev@openvswitch.org\n> https://mail.openvswitch.org/mailman/listinfo/ovs-dev\n>","headers":{"Return-Path":"<ovs-dev-bounces@openvswitch.org>","X-Original-To":["incoming@patchwork.ozlabs.org","dev@openvswitch.org"],"Delivered-To":["patchwork-incoming@bilbo.ozlabs.org","ovs-dev@mail.linuxfoundation.org"],"Authentication-Results":"ozlabs.org;\n\tspf=pass (mailfrom) smtp.mailfrom=openvswitch.org\n\t(client-ip=140.211.169.12; helo=mail.linuxfoundation.org;\n\tenvelope-from=ovs-dev-bounces@openvswitch.org;\n\treceiver=<UNKNOWN>)","Received":["from mail.linuxfoundation.org (mail.linuxfoundation.org\n\t[140.211.169.12])\n\t(using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256\n\tbits)) (No client certificate requested)\n\tby ozlabs.org (Postfix) with ESMTPS id 3y2VBr2jK7z9t6B\n\tfor <incoming@patchwork.ozlabs.org>;\n\tThu, 28 Sep 2017 06:42:59 +1000 (AEST)","from mail.linux-foundation.org (localhost [127.0.0.1])\n\tby mail.linuxfoundation.org (Postfix) with ESMTP id A4680B4A;\n\tWed, 27 Sep 2017 20:42:57 +0000 (UTC)","from smtp1.linuxfoundation.org (smtp1.linux-foundation.org\n\t[172.17.192.35])\n\tby mail.linuxfoundation.org (Postfix) with ESMTPS id 70A76B2B\n\tfor <dev@openvswitch.org>; Wed, 27 Sep 2017 20:42:56 +0000 (UTC)","from mail-it0-f42.google.com (mail-it0-f42.google.com\n\t[209.85.214.42])\n\tby smtp1.linuxfoundation.org (Postfix) with ESMTPS id E87DF41E\n\tfor <dev@openvswitch.org>; Wed, 27 Sep 2017 20:42:53 +0000 (UTC)","by mail-it0-f42.google.com with SMTP id 85so8888375ith.2\n\tfor <dev@openvswitch.org>; Wed, 27 Sep 2017 13:42:53 -0700 (PDT)"],"X-Greylist":"whitelisted by SQLgrey-1.7.6","X-Google-DKIM-Signature":"v=1; a=rsa-sha256; c=relaxed/relaxed;\n\td=1e100.net; s=20161025;\n\th=x-gm-message-state:mime-version:references:in-reply-to:from:date\n\t:message-id:subject:to;\n\tbh=F0CjSEXm11KUiPhtAs4B/88P0ec42nsKJkPBe8KHwYI=;\n\tb=f8O/gGZbtG/IeyFc1koWR5zVOw2GlH5Fz3YM/A/RWhdVOaty7w1OzsrZ2s+Vh1k88k\n\tn9F16KTpvb8lxFCwT5HGLswHJHijQ30MyHTchzw9wH8fYwpfeCHOD9YBjJrHWo5M2aeG\n\t7RNfqtNhwTofSTvx69BOK3hl1Gu+iRdIH1gGB7zuCLFEWsLS4PmnOAdcAI1QZht0D0MQ\n\tfsDnW+VIQgRvIIDktNkl4KTuVaxfcPitpT6h0UTtl51jvPzogrInpjbTXKBzLInyTTxq\n\tfKC3jo0SnbCxC/h4WP0nG54hPWLIxuzW4IhO38/ZCA4hDqgEpt7Nmg7v7lTGnohjmh7c\n\t/SGw==","X-Gm-Message-State":"AHPjjUjbJbvHERCt8Q9kFjL7RMzWwrilTjXApbAivwMuxJYo5NyGZI2g\n\tU0gOWVmbvKn+k//7XEMXWA7Qd6ja5c8paNIRm9AZcg==","X-Google-Smtp-Source":"AOwi7QC9wlppSkf/Y2zTriBHltEVPCQCw7LGWgNaK+mtmnCETPgMCU3Ic5/nnKu69ngFXxYvyM0x1gUuENiO9hrKglg=","X-Received":"by 10.36.110.3 with SMTP id w3mr2738916itc.113.1506544973096;\n\tWed, 27 Sep 2017 13:42:53 -0700 (PDT)","MIME-Version":"1.0","References":"<20170921160747.24602-1-nusiddiq@redhat.com>\n\t<20170921161007.24943-1-nusiddiq@redhat.com>","In-Reply-To":"<20170921161007.24943-1-nusiddiq@redhat.com>","From":"Mark Michelson <mmichels@redhat.com>","Date":"Wed, 27 Sep 2017 20:42:42 +0000","Message-ID":"<CAA1+qOPyDXKpn4sasHRnThFUHHXiC-ibVy-nehx3MLee27X5Mg@mail.gmail.com>","To":"nusiddiq@redhat.com, dev@openvswitch.org","X-Spam-Status":"No, score=0.5 required=5.0 tests=HTML_MESSAGE,\n\tRCVD_IN_DNSWL_NONE,\n\tRCVD_IN_SORBS_SPAM autolearn=disabled version=3.3.1","X-Spam-Checker-Version":"SpamAssassin 3.3.1 (2010-03-16) on\n\tsmtp1.linux-foundation.org","X-Content-Filtered-By":"Mailman/MimeDel 2.1.12","Subject":"Re: [ovs-dev] [PATCH v8 4/4] ovn: Generate Neighbor Solicitation\n\tpacket for unknown MAC IPv6 packets","X-BeenThere":"ovs-dev@openvswitch.org","X-Mailman-Version":"2.1.12","Precedence":"list","List-Id":"<ovs-dev.openvswitch.org>","List-Unsubscribe":"<https://mail.openvswitch.org/mailman/options/ovs-dev>,\n\t<mailto:ovs-dev-request@openvswitch.org?subject=unsubscribe>","List-Archive":"<http://mail.openvswitch.org/pipermail/ovs-dev/>","List-Post":"<mailto:ovs-dev@openvswitch.org>","List-Help":"<mailto:ovs-dev-request@openvswitch.org?subject=help>","List-Subscribe":"<https://mail.openvswitch.org/mailman/listinfo/ovs-dev>,\n\t<mailto:ovs-dev-request@openvswitch.org?subject=subscribe>","Content-Type":"text/plain; charset=\"us-ascii\"","Content-Transfer-Encoding":"7bit","Sender":"ovs-dev-bounces@openvswitch.org","Errors-To":"ovs-dev-bounces@openvswitch.org"}},{"id":1776932,"web_url":"http://patchwork.ozlabs.org/comment/1776932/","msgid":"<CAH=CPzqXLhPbS4ysFjzqwUVRkJFe17bQ80PMFr5nfkz9q_q-ww@mail.gmail.com>","list_archive_url":null,"date":"2017-09-28T10:10:47","subject":"Re: [ovs-dev] [PATCH v8 4/4] ovn: Generate Neighbor Solicitation\n\tpacket for unknown MAC IPv6 packets","submitter":{"id":67480,"url":"http://patchwork.ozlabs.org/api/people/67480/","name":"Numan Siddique","email":"nusiddiq@redhat.com"},"content":"On Thu, Sep 28, 2017 at 2:12 AM, Mark Michelson <mmichels@redhat.com> wrote:\n\n>\n>\n> On Thu, Sep 21, 2017 at 11:12 AM <nusiddiq@redhat.com> wrote:\n>\n>> From: Numan Siddique <nusiddiq@redhat.com>\n>>\n>> In the router ingress pipeline, if the destination mac is unresolved\n>> by the time the packet reaches the ARP_REQUEST stage, OVN should generate\n>> an\n>> IPv6 Neighbor Solicitation packet to learn the MAC address. This feature\n>> is\n>> presently missing. This patch adds this feature.  A new action \"nd_ns\" is\n>> added  which replaces an IPv6 packet being processed with an IPv6 Neighbor\n>> Solicitation packet. ovn-northd adds a flow in the ARP_REQUEST router\n>> ingress\n>> pipeline stage if the eth.dst is zero which applies this action. This\n>> action is\n>> similar to the IPv4 counterpart \"arp\" action.\n>>\n>> OVN already has the support to learn the MAC from the IPv6 Neighbor\n>> Advertisement\n>> packets and storing in the south bound MAC_Binding table.\n>>\n>> Signed-off-by: Numan Siddique <nusiddiq@redhat.com>\n>> ---\n>>  include/ovn/actions.h       |   9 +++-\n>>  ovn/controller/pinctrl.c    | 122 +++++++++++++++++++++++-------\n>> --------------\n>>  ovn/lib/actions.c           |  22 ++++++++\n>>  ovn/northd/ovn-northd.8.xml |  24 ++++++---\n>>  ovn/northd/ovn-northd.c     |   8 ++-\n>>  ovn/ovn-sb.xml              |  37 ++++++++++++++\n>>  ovn/utilities/ovn-trace.c   |  30 +++++++++++\n>>  tests/ovn.at                | 116 ++++++++++++++++++++++++++++++\n>> +++++++++++\n>>  8 files changed, 302 insertions(+), 66 deletions(-)\n>>\n>> diff --git a/include/ovn/actions.h b/include/ovn/actions.h\n>> index 15cee478d..8c7208ffc 100644\n>> --- a/include/ovn/actions.h\n>> +++ b/include/ovn/actions.h\n>> @@ -73,7 +73,8 @@ struct simap;\n>>      OVNACT(SET_QUEUE,         ovnact_set_queue)       \\\n>>      OVNACT(DNS_LOOKUP,        ovnact_dns_lookup)      \\\n>>      OVNACT(LOG,               ovnact_log)             \\\n>> -    OVNACT(PUT_ND_RA_OPTS,    ovnact_put_opts)\n>> +    OVNACT(PUT_ND_RA_OPTS,    ovnact_put_opts)        \\\n>> +    OVNACT(ND_NS,             ovnact_nest)\n>>\n>>  /* enum ovnact_type, with a member OVNACT_<ENUM> for each action. */\n>>  enum OVS_PACKED_ENUM ovnact_type {\n>> @@ -427,6 +428,12 @@ enum action_opcode {\n>>       *   - Any number of ICMPv6 options.\n>>       */\n>>      ACTION_OPCODE_PUT_ND_RA_OPTS,\n>> +\n>> +    /* \"nd_ns { ...actions... }\".\n>> +     *\n>> +     * The actions, in OpenFlow 1.3 format, follow the action_header.\n>> +     */\n>> +    ACTION_OPCODE_ND_NS,\n>>  };\n>>\n>>  /* Header. */\n>> diff --git a/ovn/controller/pinctrl.c b/ovn/controller/pinctrl.c\n>> index 6dbea4efa..cad0f67ba 100644\n>> --- a/ovn/controller/pinctrl.c\n>> +++ b/ovn/controller/pinctrl.c\n>> @@ -85,6 +85,9 @@ static void pinctrl_handle_put_nd_ra_opts(\n>>      const struct flow *ip_flow, struct dp_packet *pkt_in,\n>>      struct ofputil_packet_in *pin, struct ofpbuf *userdata,\n>>      struct ofpbuf *continuation OVS_UNUSED);\n>> +static void pinctrl_handle_nd_ns(const struct flow *ip_flow,\n>> +                                 const struct match *md,\n>> +                                 struct ofpbuf *userdata);\n>>\n>>  COVERAGE_DEFINE(pinctrl_drop_put_mac_binding);\n>>\n>> @@ -132,6 +135,43 @@ set_switch_config(struct rconn *swconn,\n>>  }\n>>\n>>  static void\n>> +set_actions_and_enqueu_msg(const struct dp_packet *packet,\n>>\n>\n> Nit: The word is spelled \"enqueue\", not \"enqueu\". This could potentially\n> lead to some confusion if left this way.\n>\n\n\nThanks Mark for the comments. I will take care of this in the next patch\nset. I will wait for a while so that I can incorporate review comments (if\nany) from the other patches in this series.\n\n\n\n>\n>> +                           const struct match *md,\n>> +                           struct ofpbuf *userdata)\n>> +{\n>> +    /* Copy metadata from 'md' into the packet-out via \"set_field\"\n>> +     * actions, then add actions from 'userdata'.\n>> +     */\n>> +    uint64_t ofpacts_stub[4096 / 8];\n>> +    struct ofpbuf ofpacts = OFPBUF_STUB_INITIALIZER(ofpacts_stub);\n>> +    enum ofp_version version = rconn_get_version(swconn);\n>> +\n>> +    reload_metadata(&ofpacts, md);\n>> +    enum ofperr error = ofpacts_pull_openflow_actions(userdata,\n>> userdata->size,\n>> +                                                      version, NULL,\n>> NULL,\n>> +                                                      &ofpacts);\n>> +    if (error) {\n>> +        static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5);\n>> +        VLOG_WARN_RL(&rl, \"failed to parse actions from userdata (%s)\",\n>> +                     ofperr_to_string(error));\n>> +        ofpbuf_uninit(&ofpacts);\n>> +        return;\n>> +    }\n>> +\n>> +    struct ofputil_packet_out po = {\n>> +        .packet = dp_packet_data(packet),\n>> +        .packet_len = dp_packet_size(packet),\n>> +        .buffer_id = UINT32_MAX,\n>> +        .ofpacts = ofpacts.data,\n>> +        .ofpacts_len = ofpacts.size,\n>> +    };\n>> +    match_set_in_port(&po.flow_metadata, OFPP_CONTROLLER);\n>> +    enum ofputil_protocol proto = ofputil_protocol_from_ofp_\n>> version(version);\n>> +    queue_msg(ofputil_encode_packet_out(&po, proto));\n>> +    ofpbuf_uninit(&ofpacts);\n>> +}\n>> +\n>> +static void\n>>  pinctrl_handle_arp(const struct flow *ip_flow, const struct match *md,\n>>                     struct ofpbuf *userdata)\n>>  {\n>> @@ -166,40 +206,8 @@ pinctrl_handle_arp(const struct flow *ip_flow, const\n>> struct match *md,\n>>                        ip_flow->vlans[0].tci);\n>>      }\n>>\n>> -    /* Compose actions.\n>> -     *\n>> -     * First, copy metadata from 'md' into the packet-out via \"set_field\"\n>> -     * actions, then add actions from 'userdata'.\n>> -     */\n>> -    uint64_t ofpacts_stub[4096 / 8];\n>> -    struct ofpbuf ofpacts = OFPBUF_STUB_INITIALIZER(ofpacts_stub);\n>> -    enum ofp_version version = rconn_get_version(swconn);\n>> -\n>> -    reload_metadata(&ofpacts, md);\n>> -    enum ofperr error = ofpacts_pull_openflow_actions(userdata,\n>> userdata->size,\n>> -                                                      version, NULL,\n>> NULL,\n>> -                                                      &ofpacts);\n>> -    if (error) {\n>> -        static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5);\n>> -        VLOG_WARN_RL(&rl, \"failed to parse arp actions (%s)\",\n>> -                     ofperr_to_string(error));\n>> -        goto exit;\n>> -    }\n>> -\n>> -    struct ofputil_packet_out po = {\n>> -        .packet = dp_packet_data(&packet),\n>> -        .packet_len = dp_packet_size(&packet),\n>> -        .buffer_id = UINT32_MAX,\n>> -        .ofpacts = ofpacts.data,\n>> -        .ofpacts_len = ofpacts.size,\n>> -    };\n>> -    match_set_in_port(&po.flow_metadata, OFPP_CONTROLLER);\n>> -    enum ofputil_protocol proto = ofputil_protocol_from_ofp_\n>> version(version);\n>> -    queue_msg(ofputil_encode_packet_out(&po, proto));\n>> -\n>> -exit:\n>> +    set_actions_and_enqueu_msg(&packet, md, userdata);\n>>      dp_packet_uninit(&packet);\n>> -    ofpbuf_uninit(&ofpacts);\n>>  }\n>>\n>>  static void\n>> @@ -994,6 +1002,10 @@ process_packet_in(const struct ofp_header *msg,\n>> struct controller_ctx *ctx)\n>>                                        &continuation);\n>>          break;\n>>\n>> +    case ACTION_OPCODE_ND_NS:\n>> +        pinctrl_handle_nd_ns(&headers, &pin.flow_metadata, &userdata);\n>> +        break;\n>> +\n>>      default:\n>>          VLOG_WARN_RL(&rl, \"unrecognized packet-in opcode %\"PRIu32,\n>>                       ntohl(ah->opcode));\n>> @@ -1812,9 +1824,6 @@ pinctrl_handle_nd_na(const struct flow *ip_flow,\n>> const struct match *md,\n>>          return;\n>>      }\n>>\n>> -    enum ofp_version version = rconn_get_version(swconn);\n>> -    enum ofputil_protocol proto = ofputil_protocol_from_ofp_\n>> version(version);\n>> -\n>>      uint64_t packet_stub[128 / 8];\n>>      struct dp_packet packet;\n>>      dp_packet_use_stub(&packet, packet_stub, sizeof packet_stub);\n>> @@ -1827,35 +1836,32 @@ pinctrl_handle_nd_na(const struct flow *ip_flow,\n>> const struct match *md,\n>>                    &ip_flow->nd_target, &ip_flow->ipv6_src,\n>>                    htonl(ND_RSO_SOLICITED | ND_RSO_OVERRIDE));\n>>\n>> -    /* Reload previous packet metadata. */\n>> -    uint64_t ofpacts_stub[4096 / 8];\n>> -    struct ofpbuf ofpacts = OFPBUF_STUB_INITIALIZER(ofpacts_stub);\n>> -    reload_metadata(&ofpacts, md);\n>> +    /* Reload previous packet metadata and set actions from userdata. */\n>> +    set_actions_and_enqueu_msg(&packet, md, userdata);\n>> +    dp_packet_uninit(&packet);\n>> +}\n>>\n>> -    enum ofperr error = ofpacts_pull_openflow_actions(userdata,\n>> userdata->size,\n>> -                                                      version, NULL,\n>> NULL,\n>> -                                                      &ofpacts);\n>> -    if (error) {\n>> +static void\n>> +pinctrl_handle_nd_ns(const struct flow *ip_flow, const struct match *md,\n>> +                     struct ofpbuf *userdata)\n>> +{\n>> +    /* This action only works for IPv6 packets. */\n>> +    if (get_dl_type(ip_flow) != htons(ETH_TYPE_IPV6)) {\n>>          static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5);\n>> -        VLOG_WARN_RL(&rl, \"failed to parse actions for 'na' (%s)\",\n>> -                     ofperr_to_string(error));\n>> -        goto exit;\n>> +        VLOG_WARN_RL(&rl, \"NS action on non-IPv6 packet\");\n>> +        return;\n>>      }\n>>\n>> -    struct ofputil_packet_out po = {\n>> -        .packet = dp_packet_data(&packet),\n>> -        .packet_len = dp_packet_size(&packet),\n>> -        .buffer_id = UINT32_MAX,\n>> -        .ofpacts = ofpacts.data,\n>> -        .ofpacts_len = ofpacts.size,\n>> -    };\n>> -    match_set_in_port(&po.flow_metadata, OFPP_CONTROLLER);\n>> +    uint64_t packet_stub[128 / 8];\n>> +    struct dp_packet packet;\n>> +    dp_packet_use_stub(&packet, packet_stub, sizeof packet_stub);\n>>\n>> -    queue_msg(ofputil_encode_packet_out(&po, proto));\n>> +    compose_nd_ns(&packet, ip_flow->dl_src, &ip_flow->ipv6_src,\n>> +                  &ip_flow->ipv6_dst);\n>>\n>> -exit:\n>> +    /* Reload previous packet metadata and set actions from userdata. */\n>> +    set_actions_and_enqueu_msg(&packet, md, userdata);\n>>      dp_packet_uninit(&packet);\n>> -    ofpbuf_uninit(&ofpacts);\n>>  }\n>>\n>>  static void\n>> diff --git a/ovn/lib/actions.c b/ovn/lib/actions.c\n>> index 2981d89f6..5e5c666a9 100644\n>> --- a/ovn/lib/actions.c\n>> +++ b/ovn/lib/actions.c\n>> @@ -1134,6 +1134,12 @@ parse_ND_NA(struct action_context *ctx)\n>>  }\n>>\n>>  static void\n>> +parse_ND_NS(struct action_context *ctx)\n>> +{\n>> +    parse_nested_action(ctx, OVNACT_ND_NS, \"ip6\");\n>> +}\n>> +\n>> +static void\n>>  parse_CLONE(struct action_context *ctx)\n>>  {\n>>      parse_nested_action(ctx, OVNACT_CLONE, NULL);\n>> @@ -1161,6 +1167,12 @@ format_ND_NA(const struct ovnact_nest *nest,\n>> struct ds *s)\n>>  }\n>>\n>>  static void\n>> +format_ND_NS(const struct ovnact_nest *nest, struct ds *s)\n>> +{\n>> +    format_nested_action(nest, \"nd_ns\", s);\n>> +}\n>> +\n>> +static void\n>>  format_CLONE(const struct ovnact_nest *nest, struct ds *s)\n>>  {\n>>      format_nested_action(nest, \"clone\", s);\n>> @@ -1207,6 +1219,14 @@ encode_ND_NA(const struct ovnact_nest *on,\n>>  }\n>>\n>>  static void\n>> +encode_ND_NS(const struct ovnact_nest *on,\n>> +             const struct ovnact_encode_params *ep,\n>> +             struct ofpbuf *ofpacts)\n>> +{\n>> +    encode_nested_neighbor_actions(on, ep, ACTION_OPCODE_ND_NS,\n>> ofpacts);\n>> +}\n>> +\n>> +static void\n>>  encode_CLONE(const struct ovnact_nest *on,\n>>               const struct ovnact_encode_params *ep,\n>>               struct ofpbuf *ofpacts)\n>> @@ -2146,6 +2166,8 @@ parse_action(struct action_context *ctx)\n>>          parse_ARP(ctx);\n>>      } else if (lexer_match_id(ctx->lexer, \"nd_na\")) {\n>>          parse_ND_NA(ctx);\n>> +    } else if (lexer_match_id(ctx->lexer, \"nd_ns\")) {\n>> +        parse_ND_NS(ctx);\n>>      } else if (lexer_match_id(ctx->lexer, \"get_arp\")) {\n>>          parse_get_mac_bind(ctx, 32, ovnact_put_GET_ARP(ctx->ovnacts));\n>>      } else if (lexer_match_id(ctx->lexer, \"put_arp\")) {\n>> diff --git a/ovn/northd/ovn-northd.8.xml b/ovn/northd/ovn-northd.8.xml\n>> index a994abf78..17123c690 100644\n>> --- a/ovn/northd/ovn-northd.8.xml\n>> +++ b/ovn/northd/ovn-northd.8.xml\n>> @@ -1915,15 +1915,15 @@ next;\n>>\n>>      <p>\n>>        In the common case where the Ethernet destination has been\n>> resolved, this\n>> -      table outputs the packet.  Otherwise, it composes and sends an ARP\n>> -      request.  It holds the following flows:\n>> +      table outputs the packet.  Otherwise, it composes and sends an ARP\n>> or\n>> +      IPv6 Neighbor Solicitation request.  It holds the following flows:\n>>      </p>\n>>\n>>      <ul>\n>>        <li>\n>>          <p>\n>> -          Unknown MAC address.  A priority-100 flow with match\n>> <code>eth.dst ==\n>> -          00:00:00:00:00:00</code> has the following actions:\n>> +          Unknown MAC address.  A priority-100 flow for IPv4 packets\n>> with match\n>> +          <code>eth.dst == 00:00:00:00:00:00</code> has the following\n>> actions:\n>>          </p>\n>>\n>>          <pre>\n>> @@ -1937,13 +1937,25 @@ arp {\n>>          </pre>\n>>\n>>          <p>\n>> +          Unknown MAC address.  A priority-100 flow for IPv6 packets\n>> with match\n>> +          <code>eth.dst == 00:00:00:00:00:00</code> has the following\n>> actions:\n>> +        </p>\n>> +\n>> +        <pre>\n>> +nd_ns {\n>> +    nd.target = xxreg0;\n>> +    output;\n>> +};\n>> +        </pre>\n>> +\n>> +        <p>\n>>            (Ingress table <code>IP Routing</code> initialized\n>> <code>reg1</code>\n>>            with the IP address owned by <code>outport</code> and\n>> -          <code>reg0</code> with the next-hop IP address)\n>> +          <code>(xx)reg0</code> with the next-hop IP address)\n>>          </p>\n>>\n>>          <p>\n>> -          The IP packet that triggers the ARP request is dropped.\n>> +          The IP packet that triggers the ARP/IPv6 NS request is dropped.\n>>          </p>\n>>        </li>\n>>\n>> diff --git a/ovn/northd/ovn-northd.c b/ovn/northd/ovn-northd.c\n>> index 3da20d25b..b4ea34bc6 100644\n>> --- a/ovn/northd/ovn-northd.c\n>> +++ b/ovn/northd/ovn-northd.c\n>> @@ -5703,7 +5703,7 @@ build_lrouter_flows(struct hmap *datapaths, struct\n>> hmap *ports,\n>>       *\n>>       * In the common case where the Ethernet destination has been\n>> resolved,\n>>       * this table outputs the packet (priority 0).  Otherwise, it\n>> composes\n>> -     * and sends an ARP request (priority 100). */\n>> +     * and sends an ARP/IPv6 NA request (priority 100). */\n>>      HMAP_FOR_EACH (od, key_node, datapaths) {\n>>          if (!od->nbr) {\n>>              continue;\n>> @@ -5718,6 +5718,12 @@ build_lrouter_flows(struct hmap *datapaths, struct\n>> hmap *ports,\n>>                        \"arp.op = 1; \" /* ARP request */\n>>                        \"output; \"\n>>                        \"};\");\n>> +        ovn_lflow_add(lflows, od, S_ROUTER_IN_ARP_REQUEST, 100,\n>> +                      \"eth.dst == 00:00:00:00:00:00\",\n>> +                      \"nd_ns { \"\n>> +                      \"nd.target = xxreg0; \"\n>> +                      \"output; \"\n>> +                      \"};\");\n>>          ovn_lflow_add(lflows, od, S_ROUTER_IN_ARP_REQUEST, 0, \"1\",\n>> \"output;\");\n>>      }\n>>\n>> diff --git a/ovn/ovn-sb.xml b/ovn/ovn-sb.xml\n>> index 2e4f28b96..ca8cbecdd 100644\n>> --- a/ovn/ovn-sb.xml\n>> +++ b/ovn/ovn-sb.xml\n>> @@ -1258,6 +1258,43 @@\n>>            <p><b>Example:</b> <code>put_arp(inport, arp.spa,\n>> arp.sha);</code></p>\n>>          </dd>\n>>\n>> +        <dt><code>nd_ns { <var>action</var>; </code>...<code>\n>> };</code></dt>\n>> +        <dd>\n>> +          <p>\n>> +            Temporarily replaces the IPv6 packet being processed by an\n>> IPv6\n>> +            Neighbor Solicitation packet and executes each nested\n>> +            <var>action</var> on the IPv6 NS packet.  Actions following\n>> the\n>> +            <var>nd_ns</var> action, if any, apply to the original,\n>> unmodified\n>> +            packet.\n>> +          </p>\n>> +\n>> +          <p>\n>> +            The IPv6 NS packet that this action operates on is\n>> initialized\n>> +            based on the IPv6 packet being processed, as follows.  These\n>> are\n>> +            default values that the nested actions will probably want to\n>> +            change:\n>> +          </p>\n>> +\n>> +          <ul>\n>> +            <li><code>eth.src</code> unchanged</li>\n>> +            <li><code>eth.dst</code> set to IPv6 multicast MAC\n>> address</li>\n>> +            <li><code>eth.type = 0x86dd</code></li>\n>> +            <li><code>ip6.src</code> copied from\n>> <code>ip6.src</code></li>\n>> +            <li>\n>> +              <code>ip6.dst</code> set to IPv6 Solicited-Node multicast\n>> address\n>> +            </li>\n>> +            <li><code>icmp6.type = 135</code> (Neighbor\n>> Solicitation)</li>\n>> +            <li><code>nd.target</code> copied from\n>> <code>ip6.dst</code></li>\n>> +          </ul>\n>> +\n>> +          <p>\n>> +            The IPv6 NS packet has the same VLAN header, if any, as the\n>> IP\n>> +            packet it replaces.\n>> +          </p>\n>> +\n>> +          <p><b>Prerequisite:</b> <code>ip6</code></p>\n>> +        </dd>\n>> +\n>>          <dt>\n>>            <code>nd_na { <var>action</var>; </code>...<code> };</code>\n>>          </dt>\n>> diff --git a/ovn/utilities/ovn-trace.c b/ovn/utilities/ovn-trace.c\n>> index 211148b8b..e457284fc 100644\n>> --- a/ovn/utilities/ovn-trace.c\n>> +++ b/ovn/utilities/ovn-trace.c\n>> @@ -1510,6 +1510,31 @@ execute_nd_na(const struct ovnact_nest *on, const\n>> struct ovntrace_datapath *dp,\n>>  }\n>>\n>>  static void\n>> +execute_nd_ns(const struct ovnact_nest *on, const struct\n>> ovntrace_datapath *dp,\n>> +              const struct flow *uflow, uint8_t table_id,\n>> +              enum ovnact_pipeline pipeline, struct ovs_list *super)\n>> +{\n>> +    struct flow na_flow = *uflow;\n>> +\n>> +    /* Update fields for NA. */\n>> +    na_flow.dl_src = uflow->dl_src;\n>> +    na_flow.ipv6_src = uflow->ipv6_src;\n>> +    na_flow.ipv6_dst = uflow->ipv6_dst;\n>> +    struct in6_addr sn_addr;\n>> +    in6_addr_solicited_node(&sn_addr, &uflow->ipv6_dst);\n>> +    ipv6_multicast_to_ethernet(&na_flow.dl_dst, &sn_addr);\n>> +    na_flow.tp_src = htons(135);\n>> +    na_flow.arp_sha = eth_addr_zero;\n>> +    na_flow.arp_tha = uflow->dl_dst;\n>> +\n>> +    struct ovntrace_node *node = ovntrace_node_append(\n>> +        super, OVNTRACE_NODE_TRANSFORMATION, \"nd_ns\");\n>> +\n>> +    trace_actions(on->nested, on->nested_len, dp, &na_flow,\n>> +                  table_id, pipeline, &node->subs);\n>> +}\n>> +\n>> +static void\n>>  execute_get_mac_bind(const struct ovnact_get_mac_bind *bind,\n>>                       const struct ovntrace_datapath *dp,\n>>                       struct flow *uflow, struct ovs_list *super)\n>> @@ -1811,6 +1836,11 @@ trace_actions(const struct ovnact *ovnacts, size_t\n>> ovnacts_len,\n>>                            super);\n>>              break;\n>>\n>> +        case OVNACT_ND_NS:\n>> +            execute_nd_ns(ovnact_get_ND_NS(a), dp, uflow, table_id,\n>> pipeline,\n>> +                          super);\n>> +            break;\n>> +\n>>          case OVNACT_GET_ARP:\n>>              execute_get_mac_bind(ovnact_get_GET_ARP(a), dp, uflow,\n>> super);\n>>              break;\n>> diff --git a/tests/ovn.at b/tests/ovn.at\n>> index 3aa4e5e22..13cdc1679 100644\n>> --- a/tests/ovn.at\n>> +++ b/tests/ovn.at\n>> @@ -993,6 +993,16 @@ reg1[0] = put_dhcp_opts(offerip=\"xyzzy\");\n>>  reg1[0] = put_dhcp_opts(offerip=1.2.3.4, domain=1.2.3.4);\n>>      DHCPv4 option domain requires string value.\n>>\n>> +# nd_ns\n>> +nd_ns { nd.target = xxreg0; output; };\n>> +    encodes as controller(userdata=00.00.00.\n>> 09.00.00.00.00.ff.ff.00.18.00.00.23.20.00.06.00.80.00.00.00.\n>> 00.00.01.de.10.00.01.2e.10.ff.ff.00.10.00.00.23.20.00.0e.ff.\n>> f8.40.00.00.00)\n>> +    has prereqs ip6\n>> +\n>> +nd_ns { };\n>> +    formats as nd_ns { drop; };\n>> +    encodes as controller(userdata=00.00.00.09.00.00.00.00)\n>> +    has prereqs ip6\n>> +\n>>  # nd_na\n>>  nd_na { eth.src = 12:34:56:78:9a:bc; nd.tll = 12:34:56:78:9a:bc; outport\n>> = inport; inport = \"\"; /* Allow sending out inport. */ output; };\n>>      formats as nd_na { eth.src = 12:34:56:78:9a:bc; nd.tll =\n>> 12:34:56:78:9a:bc; outport = inport; inport = \"\"; output; };\n>> @@ -8795,6 +8805,112 @@ OVN_CLEANUP([gw1],[gw2],[hv1])\n>>\n>>  AT_CLEANUP\n>>\n>> +AT_SETUP([ovn -- IPv6 Neighbor Solicitation for unknown MAC])\n>> +AT_KEYWORDS([ovn-nd_ns for unknown mac])\n>> +AT_SKIP_IF([test $HAVE_PYTHON = no])\n>> +ovn_start\n>> +\n>> +ovn-nbctl ls-add sw0_ip6\n>> +ovn-nbctl lsp-add sw0_ip6 sw0_ip6-port1\n>> +ovn-nbctl lsp-set-addresses sw0_ip6-port1 \\\n>> +\"50:64:00:00:00:02 aef0::5264:00ff:fe00:0002\"\n>> +\n>> +ovn-nbctl lsp-set-port-security sw0_ip6-port1 \\\n>> +\"50:64:00:00:00:02 aef0::5264:00ff:fe00:0002\"\n>> +\n>> +ovn-nbctl lr-add lr0_ip6\n>> +ovn-nbctl lrp-add lr0_ip6 lrp0_ip6 00:00:00:00:af:01 aef0::/64\n>> +ovn-nbctl lsp-add sw0_ip6 lrp0_ip6-attachment\n>> +ovn-nbctl lsp-set-type lrp0_ip6-attachment router\n>> +ovn-nbctl lsp-set-addresses lrp0_ip6-attachment 00:00:00:00:af:01\n>> +ovn-nbctl lsp-set-options lrp0_ip6-attachment router-port=lrp0_ip6\n>> +ovn-nbctl set logical_router_port lrp0_ip6 ipv6_ra_configs:address_mode=\n>> slaac\n>> +\n>> +ovn-nbctl ls-add public\n>> +ovn-nbctl lsp-add public ln-public\n>> +ovn-nbctl lsp-set-addresses ln-public unknown\n>> +ovn-nbctl lsp-set-type ln-public localnet\n>> +ovn-nbctl lsp-set-options ln-public network_name=phys\n>> +\n>> +ovn-nbctl lrp-add lr0_ip6 ip6_public 00:00:02:01:02:04 \\\n>> +2001:db8:1:0:200:02ff:fe01:0204/64 \\\n>> +-- set Logical_Router_port ip6_public options:redirect-chassis=\"hv1\"\n>> +\n>> +\n>> +ovn-nbctl lsp-add public rp-ip6_public -- set Logical_Switch_Port \\\n>> +rp-ip6_public  type=router options:router-port=ip6_public \\\n>> +-- lsp-set-addresses rp-ip6_public router\n>> +\n>> +net_add n1\n>> +sim_add hv1\n>> +as hv1\n>> +ovs-vsctl add-br br-phys\n>> +ovn_attach n1 br-phys 192.168.0.2\n>> +\n>> +ovs-vsctl -- add-port br-int hv1-vif1 -- \\\n>> +    set interface hv1-vif1 external-ids:iface-id=sw0_ip6-port1 \\\n>> +    options:tx_pcap=hv1/vif1-tx.pcap \\\n>> +    options:rxq_pcap=hv1/vif1-rx.pcap \\\n>> +    ofport-request=1\n>> +ovs-vsctl set open . external-ids:ovn-bridge-mappings=phys:br-phys\n>> +\n>> +# Allow some time for ovn-northd and ovn-controller to catch up.\n>> +# XXX This should be more systematic.\n>> +sleep 1\n>> +\n>> +trim_zeros() {\n>> +    sed 's/\\(00\\)\\{1,\\}$//'\n>> +}\n>> +\n>> +# Test the IPv6 Neighbor Solicitation (NS) - nd_ns action for unknown MAC\n>> +# addresses. ovn-controller should generate an IPv6 NS request for IPv6\n>> +# packets whose MAC is unknown (in the ARP_REQUEST router pipeline stage.\n>> +# test_ipv6 INPORT SRC_MAC DST_MAC SRC_IP DST_IP OUTPORT...\n>> +# This function sends ipv6 packet\n>> +test_ipv6() {\n>> +    local inport=$1 src_mac=$2 dst_mac=$3 src_ip=$4\n>> +    dst_ip=20010db800010000020002fffe010205\n>> +\n>> +    local packet=${dst_mac}${src_mac}86dd6000000000083aff${src_ip}$\n>> {dst_ip}\n>> +    packet=${packet}8000000000000000\n>> +    shift; shift; shift; shift\n>> +\n>> +    dst_mac=3333ff010205\n>> +    src_mac=000002010204\n>> +    mcast_node_ip=ff0200000000000000000001ff010205\n>> +    expected_packet=${dst_mac}${src_mac}86dd6000000000203aff${src_ip}\n>> +    expected_packet=${expected_packet}${mcast_node_ip}\n>> 8700XXXX00000000${dst_ip}\n>> +    expected_packet=${expected_packet}0101${src_mac}\n>> +\n>> +    as hv1 ovs-appctl netdev-dummy/receive hv1-vif${inport} $packet\n>> +    echo $expected_packet >> ipv6_ns.expected\n>> +}\n>> +\n>> +src_mac=506400000002\n>> +dst_mac=00000000af01\n>> +src_ip=aef0000000000000526400fffe000002\n>> +# Send an IPv6 packet. Generated IPv6 Neighbor solicitation packet\n>> +# should be received by the ports attached to br-phys.\n>> +test_ipv6 1 $src_mac $dst_mac $src_ip 2\n>> +\n>> +$PYTHON \"$top_srcdir/utilities/ovs-pcap.in\" hv1/br-phys_n1-tx.pcap | \\\n>> +trim_zeros > 1.packets\n>> +$PYTHON \"$top_srcdir/utilities/ovs-pcap.in\" hv1/br-phys-tx.pcap | \\\n>> +trim_zeros > 2.packets\n>> +\n>> +cat ipv6_ns.expected | cut -c -112 > expout\n>> +AT_CHECK([cat 1.packets | cut -c -112], [0], [expout])\n>> +AT_CHECK([cat 2.packets | cut -c -112], [0], [expout])\n>> +\n>> +# Skipping the ICMPv6 checksum\n>> +cat ipv6_ns.expected | cut -c 117- > expout\n>> +AT_CHECK([cat 1.packets | cut -c 117-], [0], [expout])\n>> +AT_CHECK([cat 2.packets | cut -c 117-], [0], [expout])\n>> +\n>> +OVN_CLEANUP([hv1])\n>> +\n>> +AT_CLEANUP\n>> +\n>>  AT_SETUP([ovn -- options:requested-chassis for logical port])\n>>  ovn_start\n>>\n>> --\n>> 2.13.3\n>>\n>> _______________________________________________\n>> dev mailing list\n>> dev@openvswitch.org\n>> https://mail.openvswitch.org/mailman/listinfo/ovs-dev\n>>\n>","headers":{"Return-Path":"<ovs-dev-bounces@openvswitch.org>","X-Original-To":["incoming@patchwork.ozlabs.org","dev@openvswitch.org"],"Delivered-To":["patchwork-incoming@bilbo.ozlabs.org","ovs-dev@mail.linuxfoundation.org"],"Authentication-Results":"ozlabs.org;\n\tspf=pass (mailfrom) smtp.mailfrom=openvswitch.org\n\t(client-ip=140.211.169.12; helo=mail.linuxfoundation.org;\n\tenvelope-from=ovs-dev-bounces@openvswitch.org;\n\treceiver=<UNKNOWN>)","Received":["from mail.linuxfoundation.org (mail.linuxfoundation.org\n\t[140.211.169.12])\n\t(using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256\n\tbits)) (No client certificate requested)\n\tby ozlabs.org (Postfix) with ESMTPS id 3y2r7S5x01z9t5w\n\tfor <incoming@patchwork.ozlabs.org>;\n\tThu, 28 Sep 2017 20:11:15 +1000 (AEST)","from mail.linux-foundation.org (localhost [127.0.0.1])\n\tby mail.linuxfoundation.org (Postfix) with ESMTP id 139AA9FA;\n\tThu, 28 Sep 2017 10:10:53 +0000 (UTC)","from smtp1.linuxfoundation.org (smtp1.linux-foundation.org\n\t[172.17.192.35])\n\tby mail.linuxfoundation.org (Postfix) with ESMTPS id B378498A\n\tfor <dev@openvswitch.org>; Thu, 28 Sep 2017 10:10:51 +0000 (UTC)","from mail-oi0-f43.google.com (mail-oi0-f43.google.com\n\t[209.85.218.43])\n\tby smtp1.linuxfoundation.org (Postfix) with ESMTPS id C3DF841D\n\tfor <dev@openvswitch.org>; Thu, 28 Sep 2017 10:10:48 +0000 (UTC)","by mail-oi0-f43.google.com with SMTP id p126so1413849oih.9\n\tfor <dev@openvswitch.org>; Thu, 28 Sep 2017 03:10:48 -0700 (PDT)","by 10.157.89.219 with HTTP; Thu, 28 Sep 2017 03:10:47 -0700 (PDT)"],"X-Greylist":"whitelisted by SQLgrey-1.7.6","X-Google-DKIM-Signature":"v=1; a=rsa-sha256; c=relaxed/relaxed;\n\td=1e100.net; s=20161025;\n\th=x-gm-message-state:mime-version:in-reply-to:references:from:date\n\t:message-id:subject:to:cc;\n\tbh=u/kiuO0CAxR8W7O/PX4OBfzmoa5+xDtrKgOa0Bd2ma0=;\n\tb=YVlrT1BQx45S3DRZM04PF8vnZAWhPg62Vb8QgrfJQcWW+7lQY/SgvHs19O63+0t7NI\n\t4YF+VcmN+eOO325EjXesKNPsic5458Y0HBUWE+nfzxdGbwNqdC9uaLvGvwHC/0Q0NR/T\n\t1G+5sYLott6fnktz/mPQFfwjOH98T+L6O2qOoSwVnOTsdEchlmEquAOpef5zK6+3ZGXD\n\tgZiFJTy+OmRVos0dQ8XW+Oe7UYQO8qycSvVgXYD/JhTVTO6xg+xQlVBi0Zd4Oe3sDXr5\n\tOQvEFV6sgEscBMpkMeb8ZsgoS1fE9s3dCoyTVj5Ljo0s6HfvN9vMZ4RjZs+eF9jnSsnf\n\t4W9Q==","X-Gm-Message-State":"AMCzsaXFZIkLo8g5Kv1ycDPePKS218CVsbn0dLz2RZgfPTCBDB94S7UD\n\thHnjfIhNcpTY0AwxNITKCa5/FNKm0tljuPxkEPL3KQ==","X-Google-Smtp-Source":"AOwi7QCe6hgMvLQGJ5BrDCPsUjSJPTYm2rcSPCxFosfYwD4C82lsblh6Pi4CrPGCFDtD0AoUQyJ+DtN0NeBt6DjK0z8=","X-Received":"by 10.202.172.12 with SMTP id v12mr25201oie.105.1506593447757;\n\tThu, 28 Sep 2017 03:10:47 -0700 (PDT)","MIME-Version":"1.0","In-Reply-To":"<CAA1+qOPyDXKpn4sasHRnThFUHHXiC-ibVy-nehx3MLee27X5Mg@mail.gmail.com>","References":"<20170921160747.24602-1-nusiddiq@redhat.com>\n\t<20170921161007.24943-1-nusiddiq@redhat.com>\n\t<CAA1+qOPyDXKpn4sasHRnThFUHHXiC-ibVy-nehx3MLee27X5Mg@mail.gmail.com>","From":"Numan Siddique <nusiddiq@redhat.com>","Date":"Thu, 28 Sep 2017 15:40:47 +0530","Message-ID":"<CAH=CPzqXLhPbS4ysFjzqwUVRkJFe17bQ80PMFr5nfkz9q_q-ww@mail.gmail.com>","To":"Mark Michelson <mmichels@redhat.com>","X-Spam-Status":"No, score=0.5 required=5.0 tests=HTML_MESSAGE,\n\tRCVD_IN_DNSWL_NONE,\n\tRCVD_IN_SORBS_SPAM autolearn=disabled version=3.3.1","X-Spam-Checker-Version":"SpamAssassin 3.3.1 (2010-03-16) on\n\tsmtp1.linux-foundation.org","X-Content-Filtered-By":"Mailman/MimeDel 2.1.12","Cc":"ovs dev <dev@openvswitch.org>","Subject":"Re: [ovs-dev] [PATCH v8 4/4] ovn: Generate Neighbor Solicitation\n\tpacket for unknown MAC IPv6 packets","X-BeenThere":"ovs-dev@openvswitch.org","X-Mailman-Version":"2.1.12","Precedence":"list","List-Id":"<ovs-dev.openvswitch.org>","List-Unsubscribe":"<https://mail.openvswitch.org/mailman/options/ovs-dev>,\n\t<mailto:ovs-dev-request@openvswitch.org?subject=unsubscribe>","List-Archive":"<http://mail.openvswitch.org/pipermail/ovs-dev/>","List-Post":"<mailto:ovs-dev@openvswitch.org>","List-Help":"<mailto:ovs-dev-request@openvswitch.org?subject=help>","List-Subscribe":"<https://mail.openvswitch.org/mailman/listinfo/ovs-dev>,\n\t<mailto:ovs-dev-request@openvswitch.org?subject=subscribe>","Content-Type":"text/plain; charset=\"us-ascii\"","Content-Transfer-Encoding":"7bit","Sender":"ovs-dev-bounces@openvswitch.org","Errors-To":"ovs-dev-bounces@openvswitch.org"}}]