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

Numan Siddique nusiddiq at redhat.com
Fri Aug 5 04:06:39 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.h               |   1 +
 ovn/northd/ovn-northd.8.xml |  66 ++++++++++++-
 ovn/northd/ovn-northd.c     | 171 ++++++++++++++++++++++++++++++++-
 ovn/ovn-nb.ovsschema        |   9 +-
 ovn/ovn-nb.xml              |  75 ++++++++++++++-
 tests/ovn.at                | 226 ++++++++++++++++++++++++++++++++++++++++++++
 6 files changed, 538 insertions(+), 10 deletions(-)

diff --git a/lib/packets.h b/lib/packets.h
index 9b98b29..dcfcd04 100644
--- a/lib/packets.h
+++ b/lib/packets.h
@@ -920,6 +920,7 @@ static inline bool ipv6_addr_equals(const struct in6_addr *a,
 #endif
 }
 
+/* Checks the IPv6 address in 'mask' for all zeroes. */
 static inline bool ipv6_mask_is_any(const struct in6_addr *mask) {
     return ipv6_addr_equals(mask, &in6addr_any);
 }
diff --git a/ovn/northd/ovn-northd.8.xml b/ovn/northd/ovn-northd.8.xml
index 7797417..a26c4aa 100644
--- a/ovn/northd/ovn-northd.8.xml
+++ b/ovn/northd/ovn-northd.8.xml
@@ -487,8 +487,9 @@ nd_na {
     <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>
@@ -516,6 +517,29 @@ 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(ia_addr = <var>0</var>, <i>options</i>...);
+next;
+        </pre>
+
+        <p>
+          For DHCPv6 Solicit/Request/Confirm packets, this transforms the
+          packet into a DHCPv6 Advertise/Reply, adds the DHCPv6 offer IP
+          <var>O</var> and options to the packet, and stores 1 into reg0[3].
+          For other kinds of packets, it just stores 0 into reg0[3]. Either
+          way, it continues to the next table.
+        </p>
+      </li>
+
+      <li>
         A priority-0 flow that matches all packets to advances to table 11.
       </li>
     </ul>
@@ -563,6 +587,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>
@@ -643,7 +702,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 d6c14cf..79683c1 100644
--- a/ovn/northd/ovn-northd.c
+++ b/ovn/northd/ovn-northd.c
@@ -1811,6 +1811,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 (!ipv6_mask_is_any(&ip6_mask)) {
+        /* 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. */
+        static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(5, 1);
+        VLOG_WARN_RL(&rl, "server_id not present in the DHCPv6 options"
+                          " for lport %s", op->json_key);
+        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[INET6_ADDRSTRLEN + 1];
+    ipv6_string_mapped(server_ip, &lla);
+
+    char ia_addr[INET6_ADDRSTRLEN + 1];
+    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; flags.loopback = 1; "
+                  "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++) {
@@ -2241,6 +2307,32 @@ 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];
+                    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);
+                }
+            }
         }
     }
 }
@@ -2533,8 +2625,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;
         }
 
@@ -2567,6 +2660,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;
+                }
+            }
         }
     }
 
@@ -3940,6 +4061,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_DOMAIN_SEARCH,
+    DHCPV6_OPT_DNS_SERVER
+};
+
 static void
 check_and_add_supported_dhcp_opts_to_sb_db(struct northd_context *ctx)
 {
@@ -3973,6 +4101,39 @@ 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)
+{
+    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);
+        }
+    }
+
+    struct dhcp_opts_map *opt;
+    HMAP_FOR_EACH(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);
+}
+
 /* Updates the sb_cfg and hv_cfg columns in the northbound NB_Global table. */
 static void
 update_northbound_cfg(struct northd_context *ctx,
@@ -4193,7 +4354,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);
@@ -4215,6 +4379,7 @@ main(int argc, char *argv[])
         ovnsb_db_run(&ctx, &ovnsb_idl_loop);
         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 660db76..6e33135 100644
--- a/ovn/ovn-nb.ovsschema
+++ b/ovn/ovn-nb.ovsschema
@@ -1,7 +1,7 @@
 {
     "name": "OVN_Northbound",
-    "version": "5.3.0",
-    "cksum": "1305504870 9051",
+    "version": "5.3.1",
+    "cksum": "3864094809 9343",
     "tables": {
         "NB_Global": {
             "columns": {
@@ -69,6 +69,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 4ce295a..e5f06fb 100644
--- a/ovn/ovn-nb.xml
+++ b/ovn/ovn-nb.xml
@@ -593,6 +593,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>
@@ -1025,11 +1031,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>
 
@@ -1236,6 +1246,67 @@
       </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="Mandatory DHCPv4 options">
+        <p>
+          The following options must be defined.
+        </p>
+
+        <column name="options" key="server_id">
+          <p>
+            The Ethernet address for the DHCP server to use. This is also
+            included in the DHCPv6 reply as option 2, ``Server Identifier''
+            to carry a DUID identifying a server between a client and a server.
+            <code>ovn-controller</code> defines DUID Based on
+            Link-layer Address [DUID-LL]
+          </p>
+        </column>
+      </group>
+
+      <group title="IPv6 DHCPv6 options">
+        <p>
+          Below are the supported DHCPv6 options whose values are an IPv6
+          address, e.g. <code>aef0::4</code>.  Some options accept multiple
+          IPv6 addresses enclosed within curly braces, e.g. <code>{aef0::4,
+          aef0::5}</code>. Please refer the RFC 3315 for more details on
+          DHCPv6 options and their codes.
+        </p>
+
+        <column name="options" key="dns_server">
+          <p>
+            The DHCPv6 option code for this option is 23. This option is used
+            to specify the DNS servers.
+          </p>
+        </column>
+      </group>
+
+      <group title="String DHCPv6 options">
+        <p>
+          These options accept string values.
+        </p>
+
+        <column name="options" key="domain_search">
+          <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>
+            Example: <code>"ovn.org"</code>.
+          </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 7f6758a..602d873 100644
--- a/tests/ovn.at
+++ b/tests/ovn.at
@@ -3296,6 +3296,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