[ovs-dev] [PATCH v2 2/2] ovn-northd: Add logical flows to support DHCPv6

Numan Siddique nusiddiq at redhat.com
Tue Jul 26 18:31:06 UTC 2016


OVN implements native DHCPv6. DHCPv6 options are stored
in the 'DHCP_Options' NB table and logical ports refer to this
table to configure the DHCPv6 options.

For each logical port configured with DHCPv6 Options following flows
are added
 - A logical flow which copies the DHCPv6 options to the DHCPv6
   request packets using the 'put_dhcpv6_opts' action and advances the
   packet to the next stage.

 - A logical flow which implements the DHCPv6 reponder by sending
   the DHCPv6 reply back to the inport once the 'put_dhcpv6_opts' action
   is applied.

Signed-off-by: Numan Siddique <nusiddiq at redhat.com>
---
 lib/packets.c               |  29 ++++--
 ovn/northd/ovn-northd.8.xml |  58 +++++++++++-
 ovn/northd/ovn-northd.c     | 183 ++++++++++++++++++++++++++++++++++-
 ovn/ovn-nb.ovsschema        |   9 +-
 ovn/ovn-nb.xml              |  88 ++++++++++++++++-
 tests/ovn.at                | 226 ++++++++++++++++++++++++++++++++++++++++++++
 6 files changed, 573 insertions(+), 20 deletions(-)

diff --git a/lib/packets.c b/lib/packets.c
index 1bf887e..4a8f645 100644
--- a/lib/packets.c
+++ b/lib/packets.c
@@ -692,16 +692,6 @@ ipv6_addr_bitand(const struct in6_addr *a, const struct in6_addr *b)
    return dst;
 }
 
-struct in6_addr
-ipv6_addr_bitxor(const struct in6_addr *a, const struct in6_addr *b)
-{
-   struct in6_addr dst;
-   IPV6_FOR_EACH (i) {
-       dst.s6_addrX[i] = a->s6_addrX[i] ^ b->s6_addrX[i];
-   }
-   return dst;
-}
-
 bool
 ipv6_is_zero(const struct in6_addr *a)
 {
@@ -713,6 +703,25 @@ ipv6_is_zero(const struct in6_addr *a)
    return true;
 }
 
+struct in6_addr ipv6_addr_bitxor(const struct in6_addr *a,
+                                 const struct in6_addr *b)
+{
+    int i;
+    struct in6_addr dst;
+
+#ifdef s6_addr32
+    for (i=0; i<4; i++) {
+        dst.s6_addr32[i] = a->s6_addr32[i] ^ b->s6_addr32[i];
+    }
+#else
+    for (i=0; i<16; i++) {
+        dst.s6_addr[i] = a->s6_addr[i] ^ b->s6_addr[i];
+    }
+#endif
+
+    return dst;
+}
+
 /* Returns an in6_addr consisting of 'mask' high-order 1-bits and 128-N
  * low-order 0-bits. */
 struct in6_addr
diff --git a/ovn/northd/ovn-northd.8.xml b/ovn/northd/ovn-northd.8.xml
index b95caef..3ccfd7e 100644
--- a/ovn/northd/ovn-northd.8.xml
+++ b/ovn/northd/ovn-northd.8.xml
@@ -460,8 +460,9 @@ output;
     <h3>Ingress Table 10: DHCP option processing</h3>
 
     <p>
-      This table adds the DHCPv4 options to a DHCPv4 packet from the
-      logical ports configured with IPv4 address(es) and DHCPv4 options.
+      This table adds the DHCPv4 options to a DHCPv4 packet and DHCPv6 options
+      to a DHCPv6 packet from the logical ports configured with IPv4 address(es)
+      and DHCPv4 options and IPv6 address(es) and DHCPv6 options.
     </p>
 
     <ul>
@@ -489,6 +490,21 @@ next;
       </li>
 
       <li>
+        <p>
+          A priority-100 logical flow is added for these logical ports
+          which matches the IPv6 packet with <code>udp.src</code> = 546 and
+          <code>udp.dst</code> = 547 and applies the action
+          <code>put_dhcpv6_opts</code> and advances the packet to the next
+          table.
+        </p>
+
+        <pre>
+reg0[3] = put_dhcpv6_opts(<i>options</i>...);
+next;
+        </pre>
+      </li>
+
+      <li>
         A priority-0 flow that matches all packets to advances to table 11.
       </li>
     </ul>
@@ -536,6 +552,41 @@ output;
       </li>
 
       <li>
+        <p>
+          A priority 100 logical flow is added for the logical ports configured
+          with DHCPv6 options which matches IPv6 packets with <code>udp.src == 546
+          &amp;&amp; udp.dst == 547 &amp;&amp; reg0[3] == 1</code> and
+          responds back to the <code>inport</code> after applying these
+          actions.  If <code>reg0[3]</code> is set to 1, it means that the
+          action <code>put_dhcpv6_opts</code> was successful.
+        </p>
+
+        <pre>
+eth.dst = eth.src;
+eth.src = <var>E</var>;
+ip6.dst = <var>O</var>;
+ip6.src = <var>S</var>;
+udp.src = 547;
+udp.dst = 546;
+outport = <var>P</var>;
+inport = ""; /* Allow sending out inport. */
+output;
+        </pre>
+
+        <p>
+          where <var>E</var> is the server MAC address and <var>S</var> is the
+          server IPv6 LLA address  generated from the <code>SERVER_ID</code>
+          defined in the DHCPv6 options and <var>O</var> is
+          the IPv6 address defined in the logical port's addresses column.
+        </p>
+
+        <p>
+          (This terminates packet processing; the packet does not go on the
+          next ingress table.)
+        </p>
+      </li>
+
+      <li>
         A priority-0 flow that matches all packets to advances to table 12.
       </li>
     </ul>
@@ -616,7 +667,8 @@ output;
 
     <p>
       Also a priority 34000 logical flow is added for each logical port which
-      has DHCPv4 options defined to allow the DHCPv4 reply packet from the
+      has DHCPv4 options defined to allow the DHCPv4 reply packet and which has
+      DHCPv6 options defined to allow the DHCPv6 reply packet from the
       <code>Ingress Table 11: DHCP responses</code>.
     </p>
 
diff --git a/ovn/northd/ovn-northd.c b/ovn/northd/ovn-northd.c
index 578fbbb..3075634 100644
--- a/ovn/northd/ovn-northd.c
+++ b/ovn/northd/ovn-northd.c
@@ -1446,6 +1446,72 @@ build_dhcpv4_action(struct ovn_port *op, ovs_be32 offer_ip,
 }
 
 static bool
+build_dhcpv6_action(struct ovn_port *op, struct in6_addr *offer_ip,
+                    struct ds *options_action, struct ds *response_action)
+{
+    if (!op->nbsp->dhcpv6_options) {
+        /* CMS has disabled native DHCPv6 for this lport. */
+        return false;
+    }
+
+    struct in6_addr host_ip, mask;
+
+    char *error = ipv6_parse_masked(op->nbsp->dhcpv6_options->cidr, &host_ip,
+                                        &mask);
+    if (error) {
+        free(error);
+        return false;
+    }
+    struct in6_addr ip6_mask = ipv6_addr_bitxor(offer_ip, &host_ip);
+    ip6_mask = ipv6_addr_bitand(&ip6_mask, &mask);
+    if (!(ip6_mask.s6_addr32[0] == 0 && ip6_mask.s6_addr32[1] == 0 &&
+        ip6_mask.s6_addr32[2] == 0 && ip6_mask.s6_addr32[3] == 0)) {
+       /* offer_ip doesn't belongs to the cidr defined in lport's DHCPv6
+        * options.*/
+        return false;
+    }
+
+    /* SERVER_ID should be the MAC address */
+    const char *server_mac = smap_get(&op->nbsp->dhcpv6_options->options,
+                                      "SERVER_ID");
+    struct eth_addr ea;
+    if (!server_mac || !eth_addr_from_string(server_mac, &ea)) {
+        /* "SERVER_ID" should be present in the dhcpv6_options. */
+        return false;
+    }
+
+    /* Get the link local ip of the DHCPv6 server from the server mac */
+    struct in6_addr lla;
+    in6_generate_lla(ea, &lla);
+
+    char server_ip[IPV6_SCAN_LEN + 1];
+    memset(server_ip, 0, sizeof(server_ip));
+    ipv6_string_mapped(server_ip, &lla);
+
+    char ia_addr[IPV6_SCAN_LEN + 1];
+    memset(ia_addr, 0, sizeof(ia_addr));
+    ipv6_string_mapped(ia_addr, offer_ip);
+
+    ds_put_format(options_action,
+                  REGBIT_DHCP_OPTS_RESULT" = put_dhcpv6_opts(IA_ADDR = %s, ",
+                  ia_addr);
+    struct smap_node *node;
+    SMAP_FOR_EACH(node, &op->nbsp->dhcpv6_options->options) {
+        ds_put_format(options_action, "%s = %s, ", node->key, node->value);
+    }
+    ds_chomp(options_action, ' ');
+    ds_chomp(options_action, ',');
+    ds_put_cstr(options_action, "); next;");
+
+    ds_put_format(response_action, "eth.dst = eth.src; eth.src = %s; "
+                  "ip6.dst = ip6.src; ip6.src = %s; udp.src = 547; "
+                  "udp.dst = 546; outport = inport; inport = \"\";"
+                  " /* Allow sending out inport. */ output;",
+                  server_mac, server_ip);
+    return true;
+}
+
+static bool
 has_stateful_acl(struct ovn_datapath *od)
 {
     for (size_t i = 0; i < od->nbs->n_acls; i++) {
@@ -1876,6 +1942,33 @@ build_acls(struct ovn_datapath *od, struct hmap *lflows)
                         actions);
                 }
             }
+
+            if (od->nbs->ports[i]->dhcpv6_options) {
+                const char *server_mac = smap_get(
+                    &od->nbs->ports[i]->dhcpv6_options->options, "SERVER_ID");
+                struct eth_addr ea;
+                if (server_mac && eth_addr_from_string(server_mac, &ea)) {
+                    /* Get the link local ip of the DHCPv6 server from the
+                     * server mac. */
+                    struct in6_addr lla;
+                    in6_generate_lla(ea, &lla);
+
+                    char server_ip[IPV6_SCAN_LEN + 1];
+                    memset(server_ip, 0, sizeof(server_ip));
+                    ipv6_string_mapped(server_ip, &lla);
+
+                    struct ds match = DS_EMPTY_INITIALIZER;
+                    const char *actions = has_stateful ? "ct_commit; next;" :
+                                        "next;";
+                    ds_put_format(&match, "outport == \"%s\" && eth.src == %s "
+                                  "&& ip6.src == %s && udp && udp.src == 547 "
+                                  "&& udp.dst == 546", od->nbs->ports[i]->name,
+                                  server_mac, server_ip);
+                    ovn_lflow_add(
+                        lflows, od, S_SWITCH_OUT_ACL, 34000, ds_cstr(&match),
+                        actions);
+                }
+            }
         }
     }
 }
@@ -2169,8 +2262,9 @@ build_lswitch_flows(struct hmap *datapaths, struct hmap *ports,
             continue;
         }
 
-        if (!op->nbsp->dhcpv4_options) {
-            /* CMS has disabled native DHCPv4 for this lport. */
+        if (!op->nbsp->dhcpv4_options && !op->nbsp->dhcpv6_options) {
+            /* CMS has disabled both native DHCPv4 and DHCPv6 for this lport.
+             */
             continue;
         }
 
@@ -2203,6 +2297,34 @@ build_lswitch_flows(struct hmap *datapaths, struct hmap *ports,
                     break;
                 }
             }
+
+            for (size_t j = 0; j < op->lsp_addrs[i].n_ipv6_addrs; j++) {
+                struct ds options_action = DS_EMPTY_INITIALIZER;
+                struct ds response_action = DS_EMPTY_INITIALIZER;
+                if (build_dhcpv6_action(
+                        op, &op->lsp_addrs[i].ipv6_addrs[j].addr,
+                        &options_action, &response_action)) {
+                    struct ds match = DS_EMPTY_INITIALIZER;
+                    ds_put_format(
+                        &match, "inport == %s && eth.src == %s"
+                        " && ip6.dst == ff02::1:2 && udp.src == 546 &&"
+                        " udp.dst == 547", op->json_key,
+                        op->lsp_addrs[i].ea_s);
+
+                    ovn_lflow_add(lflows, op->od, S_SWITCH_IN_DHCP_OPTIONS, 100,
+                                  ds_cstr(&match), ds_cstr(&options_action));
+
+                    /* If REGBIT_DHCP_OPTS_RESULT is set to 1, it means the
+                     * put_dhcpv6_opts action is successful */
+                    ds_put_cstr(&match, " && "REGBIT_DHCP_OPTS_RESULT);
+                    ovn_lflow_add(lflows, op->od, S_SWITCH_IN_DHCP_RESPONSE, 100,
+                                  ds_cstr(&match), ds_cstr(&response_action));
+                    ds_destroy(&match);
+                    ds_destroy(&options_action);
+                    ds_destroy(&response_action);
+                    break;
+                }
+            }
         }
     }
 
@@ -3285,6 +3407,13 @@ static struct dhcp_opts_map supported_dhcp_opts[] = {
     DHCP_OPT_T2
 };
 
+static struct dhcp_opts_map supported_dhcpv6_opts[] = {
+    DHCPV6_OPT_IA_ADDR,
+    DHCPV6_OPT_SERVER_ID,
+    DHCPV6_OPT_DSL,
+    DHCPV6_OPT_DNS_SERVER
+};
+
 static void
 check_and_add_supported_dhcp_opts_to_sb_db(struct northd_context *ctx)
 {
@@ -3329,6 +3458,50 @@ check_and_add_supported_dhcp_opts_to_sb_db(struct northd_context *ctx)
     hmap_destroy(&dhcp_opts_to_add);
 }
 
+static void
+check_and_add_supported_dhcpv6_opts_to_sb_db(struct northd_context *ctx)
+{
+    static bool nothing_to_add = false;
+
+    if (nothing_to_add) {
+        return;
+    }
+
+    struct hmap dhcpv6_opts_to_add = HMAP_INITIALIZER(&dhcpv6_opts_to_add);
+    for (size_t i = 0; (i < sizeof(supported_dhcpv6_opts) /
+                            sizeof(supported_dhcpv6_opts[0])); i++) {
+        hmap_insert(&dhcpv6_opts_to_add, &supported_dhcpv6_opts[i].hmap_node,
+                    dhcp_opt_hash(supported_dhcpv6_opts[i].name));
+    }
+
+    const struct sbrec_dhcpv6_options *opt_row, *opt_row_next;
+    SBREC_DHCPV6_OPTIONS_FOR_EACH_SAFE(opt_row, opt_row_next, ctx->ovnsb_idl) {
+        struct dhcp_opts_map *dhcp_opt =
+            dhcp_opts_find(&dhcpv6_opts_to_add, opt_row->name);
+        if (dhcp_opt) {
+            hmap_remove(&dhcpv6_opts_to_add, &dhcp_opt->hmap_node);
+        }
+        else {
+            sbrec_dhcpv6_options_delete(opt_row);
+        }
+    }
+
+    if (!dhcpv6_opts_to_add.n) {
+        nothing_to_add = true;
+    }
+
+    struct dhcp_opts_map *opt;
+    HMAP_FOR_EACH_POP(opt, hmap_node, &dhcpv6_opts_to_add) {
+        struct sbrec_dhcpv6_options *sbrec_dhcpv6_option =
+            sbrec_dhcpv6_options_insert(ctx->ovnsb_txn);
+        sbrec_dhcpv6_options_set_name(sbrec_dhcpv6_option, opt->name);
+        sbrec_dhcpv6_options_set_code(sbrec_dhcpv6_option, opt->code);
+        sbrec_dhcpv6_options_set_type(sbrec_dhcpv6_option, opt->type);
+    }
+
+    hmap_destroy(&dhcpv6_opts_to_add);
+}
+
 static char *default_nb_db_;
 
 static const char *
@@ -3501,7 +3674,10 @@ main(int argc, char *argv[])
     add_column_noalert(ovnsb_idl_loop.idl, &sbrec_dhcp_options_col_code);
     add_column_noalert(ovnsb_idl_loop.idl, &sbrec_dhcp_options_col_type);
     add_column_noalert(ovnsb_idl_loop.idl, &sbrec_dhcp_options_col_name);
-
+    ovsdb_idl_add_table(ovnsb_idl_loop.idl, &sbrec_table_dhcpv6_options);
+    add_column_noalert(ovnsb_idl_loop.idl, &sbrec_dhcpv6_options_col_code);
+    add_column_noalert(ovnsb_idl_loop.idl, &sbrec_dhcpv6_options_col_type);
+    add_column_noalert(ovnsb_idl_loop.idl, &sbrec_dhcpv6_options_col_name);
     ovsdb_idl_add_table(ovnsb_idl_loop.idl, &sbrec_table_address_set);
     add_column_noalert(ovnsb_idl_loop.idl, &sbrec_address_set_col_name);
     add_column_noalert(ovnsb_idl_loop.idl, &sbrec_address_set_col_addresses);
@@ -3520,6 +3696,7 @@ main(int argc, char *argv[])
         ovnsb_db_run(&ctx);
         if (ctx.ovnsb_txn) {
             check_and_add_supported_dhcp_opts_to_sb_db(&ctx);
+            check_and_add_supported_dhcpv6_opts_to_sb_db(&ctx);
         }
 
         unixctl_server_run(unixctl);
diff --git a/ovn/ovn-nb.ovsschema b/ovn/ovn-nb.ovsschema
index 3cf07c1..348d8fb 100644
--- a/ovn/ovn-nb.ovsschema
+++ b/ovn/ovn-nb.ovsschema
@@ -1,7 +1,7 @@
 {
     "name": "OVN_Northbound",
-    "version": "5.1.0",
-    "cksum": "2201958537 8295",
+    "version": "5.1.1",
+    "cksum": "75805987 8587",
     "tables": {
         "Logical_Switch": {
             "columns": {
@@ -53,6 +53,11 @@
                                             "refType": "weak"},
                                  "min": 0,
                                  "max": 1}},
+                "dhcpv6_options": {"type": {"key": {"type": "uuid",
+                                            "refTable": "DHCP_Options",
+                                            "refType": "weak"},
+                                 "min": 0,
+                                 "max": 1}},
                 "external_ids": {
                     "type": {"key": "string", "value": "string",
                              "min": 0, "max": "unlimited"}}},
diff --git a/ovn/ovn-nb.xml b/ovn/ovn-nb.xml
index 86dbfa7..21ef1d4 100644
--- a/ovn/ovn-nb.xml
+++ b/ovn/ovn-nb.xml
@@ -515,6 +515,12 @@
         Please see the <ref table="DHCP_Options"/> table.
       </column>
 
+      <column name="dhcpv6_options">
+        This column defines the DHCPv6 Options to be included by the
+        <code>ovn-controller</code> when it replies to the DHCPv6 requests.
+        Please see the <ref table="DHCP_Options"/> table.
+      </column>
+
       <column name="external_ids">
         See <em>External IDs</em> at the beginning of this document.
       </column>
@@ -941,11 +947,15 @@
       DHCPv4 options to be configured and applied at each compute host
       running ovn-controller.
     </p>
+    <p>
+      OVN also implements a native DHCPv6 support which provides stateless
+      replies to DHCPv6 requests.
+    </p>
 
     <column name="cidr">
       <p>
-        The DHCPv4 options will be included if the logical port has the IPv4
-        address in this <ref column="cidr"/>.
+        The DHCPv4/DHCPv6 options will be included if the logical port has the
+        IP address in this <ref column="cidr"/>.
       </p>
     </column>
 
@@ -1190,6 +1200,80 @@
       </group>
     </group>
 
+    <group title="DHCPv6 options">
+      <p>
+        OVN also implements native DHCPv6 support. CMS should define
+        the set of DHCPv6 options as key/value pairs. The define DHCPv6
+        options will be included in the DHCPv6 response to the DHCPv6
+        Solicit/Request/Confirm packet from the logical ports having the
+        IPv6 addresses in the <ref column="cidr"/>.
+      </p>
+
+      <group title="Supported DHCPv6 options">
+        <p>
+          Below are the supported DHCPv6 options. Please refer the RFC 3315
+          which describes the DHCPv6 protocol.
+        </p>
+
+        <column name="options" key="SERVER_ID">
+          <p>
+            The DHCPv6 option code for this option is 2. This option is used
+            to carry a DUID which identifies the Server.
+            <code>ovn-controller</code> defines DUID Based on
+            Link-layer Address [DUID-LL]
+          </p>
+
+          <p>
+            The value of this DHCPv6 option is of type <code>MAC</code>.
+
+            Example. key="SERVER_ID", value="F0:00:00:00:00:01".
+          </p>
+        </column>
+
+        <column name="options" key="IA_ADDR">
+          <p>
+            The DHCPv6 option code for this option is 5. This option is used to
+            specify the offered IPv6 address to the client.
+          </p>
+
+          <p>
+            The value of this DHCPv6 option is of type <code>IPv6 address</code>.
+
+            Example. key="SERVER_ID", value="aef0::23:04".
+          </p>
+        </column>
+
+        <column name="options" key="DNS_RECURSIVE_SERVER">
+          <p>
+            The DHCPv6 option code for this option is 23. This option is used
+            to specify the DNS servers.
+          </p>
+
+          <p>
+            The value of this DHCPv6 option is of type
+            <code>IPv6 address(es)</code>.
+
+            Example. key="DNS_RECURSIVE_SERVER",
+            value="{aef0::23:01, aef0::23:02}".
+          </p>
+        </column>
+
+        <column name="options" key="DOMAIN_SEARCH_LIST">
+          <p>
+            The DHCPv6 option code for this option is 24. This option is used
+            to specify the domain search list the client can use when resolving
+            hostnames with DNS.
+          </p>
+
+          <p>
+            The value of this DHCPv6 option is of type <code>string</code>.
+
+            Example. key="DOMAIN_SEARCH_LIST", value="ovn.org".
+          </p>
+        </column>
+      </group>
+    </group>
+
     <group title="Common Columns">
       <column name="external_ids">
         See <em>External IDs</em> at the beginning of this document.
diff --git a/tests/ovn.at b/tests/ovn.at
index b08bf7a..da5d3b6 100644
--- a/tests/ovn.at
+++ b/tests/ovn.at
@@ -3359,6 +3359,232 @@ OVS_APP_EXIT_AND_WAIT([ovsdb-server])
 
 AT_CLEANUP
 
+AT_SETUP([ovn -- dhcpv6 : 1 HV, 2 LS, 2 lsps/ls])
+AT_KEYWORDS([dhcpv6])
+AT_SKIP_IF([test $HAVE_PYTHON = no])
+ovn_start
+
+ovn-nbctl ls-add ls1
+ovn-nbctl lsp-add ls1 ls1-lp1 \
+-- lsp-set-addresses ls1-lp1 "f0:00:00:00:00:01 10.0.0.4 ae70::4"
+
+ovn-nbctl lsp-set-port-security ls1-lp1 "f0:00:00:00:00:01 10.0.0.4 ae70::4"
+
+ovn-nbctl lsp-add ls1 ls1-lp2 \
+-- lsp-set-addresses ls1-lp2 "f0:00:00:00:00:02 ae70::5"
+
+ovn-nbctl lsp-set-port-security ls1-lp2 "f0:00:00:00:00:02 ae70::5"
+
+ovn-nbctl -- --id=@d1 create DHCP_Options cidr="ae70\:\:/64" \
+options="\"SERVER_ID\"=\"00:00:00:10:00:01\"" \
+-- add Logical_Switch_Port ls1-lp1 dhcpv6_options @d1 \
+-- add Logical_Switch_Port ls1-lp2 dhcpv6_options @d1
+
+ovn-nbctl ls-add ls2
+ovn-nbctl lsp-add ls2 ls2-lp1 \
+-- lsp-set-addresses ls2-lp1 "f0:00:00:00:00:03 be70::3"
+ovn-nbctl lsp-set-port-security ls2-lp1 "f0:00:00:00:00:03 be70::3"
+ovn-nbctl lsp-add ls2 ls2-lp2 \
+-- lsp-set-addresses ls2-lp2 "f0:00:00:00:00:04 be70::4"
+ovn-nbctl lsp-set-port-security ls2-lp2 "f0:00:00:00:00:04 be70::4"
+
+net_add n1
+sim_add hv1
+
+as hv1
+ovs-vsctl add-br br-phys
+ovn_attach n1 br-phys 192.168.0.1
+ovs-vsctl -- add-port br-int hv1-vif1 -- \
+    set interface hv1-vif1 external-ids:iface-id=ls1-lp1 \
+    options:tx_pcap=hv1/vif1-tx.pcap \
+    options:rxq_pcap=hv1/vif1-rx.pcap \
+    ofport-request=1
+
+ovs-vsctl -- add-port br-int hv1-vif2 -- \
+    set interface hv1-vif2 external-ids:iface-id=ls1-lp2 \
+    options:tx_pcap=hv1/vif2-tx.pcap \
+    options:rxq_pcap=hv1/vif2-rx.pcap \
+    ofport-request=2
+
+ovs-vsctl -- add-port br-int hv1-vif3 -- \
+    set interface hv1-vif3 external-ids:iface-id=ls2-lp1 \
+    options:tx_pcap=hv1/vif3-tx.pcap \
+    options:rxq_pcap=hv1/vif3-rx.pcap \
+    ofport-request=3
+
+ovs-vsctl -- add-port br-int hv1-vif4 -- \
+    set interface hv1-vif4 external-ids:iface-id=ls2-lp2 \
+    options:tx_pcap=hv1/vif4-tx.pcap \
+    options:rxq_pcap=hv1/vif4-rx.pcap \
+    ofport-request=4
+
+ovn_populate_arp
+
+sleep 2
+
+trim_zeros() {
+    sed 's/\(00\)\{1,\}$//'
+}
+
+# This shell function sends a DHCPv6 request packet
+# test_dhcp INPORT SRC_MAC DHCPv6_MSG_TYPE OUTPORT...
+# The OUTPORTs (zero or more) list the VIFs on which the original DHCP
+# packet should be received twice (one from ovn-controller and the other
+# from the "ovs-ofctl monitor br-int resume"
+test_dhcpv6() {
+    local inport=$1 src_mac=$2 src_lla=$3 msg_code=$4 offer_ip=$5
+    local request=ffffffffffff${src_mac}86dd00000000002a1101${src_lla}
+    # dst ip ff02::1:2
+    request+=ff020000000000000000000000010002
+    # udp header and dhcpv6 header
+    request+=02220223002affff${msg_code}010203
+    # Client identifier
+    request+=0001000a00030001${src_mac}
+    # IA-NA (Identity Association for Non Temporary Address)
+    request+=0003000c0102030400000e1000001518
+    shift; shift; shift; shift; shift;
+    if test $offer_ip != 0; then
+        local server_mac=000000100001
+        local server_lla=fe80000000000000020000fffe100001
+        local reply_code=07
+        if test $msg_code = 01; then
+            reply_code=02
+        fi
+        local reply=${src_mac}${server_mac}86dd0000000000541101${server_lla}${src_lla}
+        # udp header and dhcpv6 header
+        reply+=022302220054ffff${reply_code}010203
+        # Client identifier
+        reply+=0001000a00030001${src_mac}
+        # IA-NA
+        reply+=0003002801020304ffffffffffffffff00050018${offer_ip}ffffffffffffffff
+        # Server identifier
+        reply+=0002000a00030001${server_mac}
+        echo $reply | trim_zeros >> $inport.expected
+    else
+        for outport; do
+            echo $request | trim_zeros >> $outport.expected
+        done
+    fi
+
+    as hv1 ovs-appctl netdev-dummy/receive hv1-vif$inport $request
+}
+
+reset_pcap_file() {
+    local iface=$1
+    local pcap_file=$2
+    ovs-vsctl -- set Interface $iface options:tx_pcap=dummy-tx.pcap \
+options:rxq_pcap=dummy-rx.pcap
+    rm -f ${pcap_file}*.pcap
+    ovs-vsctl -- set Interface $iface options:tx_pcap=${pcap_file}-tx.pcap \
+options:rxq_pcap=${pcap_file}-rx.pcap
+}
+
+AT_CAPTURE_FILE([ofctl_monitor0.log])
+as hv1 ovs-ofctl monitor br-int resume --detach --no-chdir \
+--pidfile=ovs-ofctl0.pid 2> ofctl_monitor0.log
+
+echo "---------NB dump-----"
+ovn-nbctl show
+echo "---------------------"
+echo "---------SB dump-----"
+ovn-sbctl list datapath_binding
+echo "---------------------"
+ovn-sbctl list logical_flow
+echo "---------------------"
+
+echo "---------------------"
+ovn-sbctl dump-flows
+echo "---------------------"
+
+echo "------ hv1 dump ----------"
+as hv1 ovs-ofctl dump-flows br-int
+
+src_mac=f00000000001
+src_lla=fe80000000000000f20000fffe000001
+offer_ip=ae700000000000000000000000000004
+test_dhcpv6 1 $src_mac $src_lla 01 $offer_ip
+
+# NXT_RESUMEs should be 1.
+OVS_WAIT_UNTIL([test 1 = `cat ofctl_monitor*.log | grep -c NXT_RESUME`])
+
+$PYTHON "$top_srcdir/utilities/ovs-pcap.in" hv1/vif1-tx.pcap | trim_zeros > 1.packets
+# cat 1.expected | trim_zeros > expout
+cat 1.expected | cut -c -120 > expout
+AT_CHECK([cat 1.packets | cut -c -120], [0], [expout])
+# Skipping the UDP checksum
+cat 1.expected | cut -c 125- > expout
+AT_CHECK([cat 1.packets | cut -c 125-], [0], [expout])
+
+rm  1.expected
+
+# Send invalid packet on ls1-lp2. ovn-controller should resume the packet
+# without any modifications and the packet should be received by ls1-lp1.
+# ls1-lp1 will receive the packet twice, one from the ovn-controller after the
+# resume and the other from ovs-ofctl monitor resume.
+
+reset_pcap_file hv1-vif1 hv1/vif1
+reset_pcap_file hv1-vif2 hv1/vif2
+
+src_mac=f00000000002
+src_lla=fe80000000000000f20000fffe000002
+offer_ip=ae700000000000000000000000000005
+# Set invalid msg_type
+
+test_dhcpv6 2 $src_mac $src_lla 10 0 1 1
+
+# NXT_RESUMEs should be 2.
+OVS_WAIT_UNTIL([test 2 = `cat ofctl_monitor*.log | grep -c NXT_RESUME`])
+
+# vif2-tx.pcap should not have received the DHCPv6 reply packet
+rm 2.packets
+$PYTHON "$top_srcdir/utilities/ovs-pcap.in" hv1/vif2-tx.pcap | trim_zeros > 2.packets
+AT_CHECK([cat 2.packets], [0], [])
+
+# vif1-tx.pcap should have received the DHCPv6 (invalid) request packet
+$PYTHON "$top_srcdir/utilities/ovs-pcap.in" hv1/vif1-tx.pcap | trim_zeros > 1.packets
+cat 1.expected > expout
+AT_CHECK([cat 1.packets], [0], [expout])
+
+# Send DHCPv6 packet on ls2-lp1. native DHCPv6 is disabled on this port.
+# There should be no DHCPv6 reply from ovn-controller and the request packet
+# should be received by ls2-lp2.
+
+src_mac=f00000000003
+src_lla=fe80000000000000f20000fffe000003
+test_dhcpv6 3 $src_mac $src_lla 01 0 4
+
+# NXT_RESUMEs should be 2 only.
+OVS_WAIT_UNTIL([test 2 = `cat ofctl_monitor*.log | grep -c NXT_RESUME`])
+
+# vif3-tx.pcap should not have received the DHCPv6 reply packet
+$PYTHON "$top_srcdir/utilities/ovs-pcap.in" hv1/vif3-tx.pcap | trim_zeros > 3.packets
+AT_CHECK([cat 3.packets], [0], [])
+
+# vif4-tx.pcap should have received the DHCPv6 request packet
+$PYTHON "$top_srcdir/utilities/ovs-pcap.in" hv1/vif4-tx.pcap | trim_zeros > 4.packets
+cat 4.expected > expout
+AT_CHECK([cat 4.packets], [0], [expout])
+
+as hv1
+OVS_APP_EXIT_AND_WAIT([ovn-controller])
+OVS_APP_EXIT_AND_WAIT([ovs-vswitchd])
+OVS_APP_EXIT_AND_WAIT([ovsdb-server])
+
+as ovn-sb
+OVS_APP_EXIT_AND_WAIT([ovsdb-server])
+
+as ovn-nb
+OVS_APP_EXIT_AND_WAIT([ovsdb-server])
+
+as northd
+OVS_APP_EXIT_AND_WAIT([ovn-northd])
+
+as main
+OVS_APP_EXIT_AND_WAIT([ovs-vswitchd])
+OVS_APP_EXIT_AND_WAIT([ovsdb-server])
+
+AT_CLEANUP
+
 AT_SETUP([ovn -- 2 HVs, 2 LRs connected via LS, gateway router])
 AT_KEYWORDS([ovngatewayrouter])
 AT_SKIP_IF([test $HAVE_PYTHON = no])
-- 
2.7.4




More information about the dev mailing list