From patchwork Mon Jul 18 19:07:46 2016
Content-Type: text/plain; charset="utf-8"
MIME-Version: 1.0
Content-Transfer-Encoding: 7bit
X-Patchwork-Submitter: Numan Siddique
X-Patchwork-Id: 649743
Return-Path:
X-Original-To: incoming@patchwork.ozlabs.org
Delivered-To: patchwork-incoming@bilbo.ozlabs.org
Received: from archives.nicira.com (archives.nicira.com [96.126.127.54])
by ozlabs.org (Postfix) with ESMTP id 3rtXkM1w11z9s9G
for ;
Tue, 19 Jul 2016 05:07:55 +1000 (AEST)
Received: from archives.nicira.com (localhost [127.0.0.1])
by archives.nicira.com (Postfix) with ESMTP id 8050E10A9D;
Mon, 18 Jul 2016 12:07:54 -0700 (PDT)
X-Original-To: dev@openvswitch.org
Delivered-To: dev@openvswitch.org
Received: from mx1e3.cudamail.com (mx1.cudamail.com [69.90.118.67])
by archives.nicira.com (Postfix) with ESMTPS id 7B08510A88
for ; Mon, 18 Jul 2016 12:07:53 -0700 (PDT)
Received: from bar5.cudamail.com (localhost [127.0.0.1])
by mx1e3.cudamail.com (Postfix) with ESMTPS id F37F4420AB2
for ; Mon, 18 Jul 2016 13:07:52 -0600 (MDT)
X-ASG-Debug-ID: 1468868871-09eadd26a975900001-byXFYA
Received: from mx1-pf2.cudamail.com ([192.168.24.2]) by bar5.cudamail.com
with
ESMTP id RP96cEpPKDHnCBDA (version=TLSv1 cipher=DHE-RSA-AES256-SHA
bits=256 verify=NO) for ;
Mon, 18 Jul 2016 13:07:51 -0600 (MDT)
X-Barracuda-Envelope-From: nusiddiq@redhat.com
X-Barracuda-RBL-Trusted-Forwarder: 192.168.24.2
Received: from unknown (HELO mx1.redhat.com) (209.132.183.28)
by mx1-pf2.cudamail.com with ESMTPS (DHE-RSA-AES256-SHA encrypted);
18 Jul 2016 19:07:50 -0000
Received-SPF: pass (mx1-pf2.cudamail.com: SPF record at _spf1.redhat.com
designates 209.132.183.28 as permitted sender)
X-Barracuda-Apparent-Source-IP: 209.132.183.28
X-Barracuda-RBL-IP: 209.132.183.28
Received: from int-mx13.intmail.prod.int.phx2.redhat.com
(int-mx13.intmail.prod.int.phx2.redhat.com [10.5.11.26])
(using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256
bits)) (No client certificate requested)
by mx1.redhat.com (Postfix) with ESMTPS id 8DAFF3B723
for ; Mon, 18 Jul 2016 19:07:49 +0000 (UTC)
Received: from nusiddiq.blr.redhat.com (ovpn-116-90.phx2.redhat.com
[10.3.116.90])
by int-mx13.intmail.prod.int.phx2.redhat.com (8.14.4/8.14.4) with
ESMTP id u6IJ7l1I021566
for ; Mon, 18 Jul 2016 15:07:47 -0400
X-CudaMail-Envelope-Sender: nusiddiq@redhat.com
From: Numan Siddique
X-CudaMail-Whitelist-To: dev@openvswitch.org
X-CudaMail-MID: CM-E2-717054844
X-CudaMail-DTE: 071816
X-CudaMail-Originating-IP: 209.132.183.28
To: ovs dev
X-ASG-Orig-Subj: [##CM-E2-717054844##][PATCH v1 2/2] ovn-northd: Add logical
flows to support DHCPv6
In-Reply-To:
Organization: Red Hat
Message-ID: <1d06bd34-a138-a8ba-510d-704e19eebecc@redhat.com>
Date: Tue, 19 Jul 2016 00:37:46 +0530
User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:45.0) Gecko/20100101
Thunderbird/45.1.1
MIME-Version: 1.0
X-Scanned-By: MIMEDefang 2.68 on 10.5.11.26
X-Greylist: Sender IP whitelisted, not delayed by milter-greylist-4.5.16
(mx1.redhat.com [10.5.110.30]); Mon, 18 Jul 2016 19:07:49 +0000 (UTC)
X-Barracuda-Connect: UNKNOWN[192.168.24.2]
X-Barracuda-Start-Time: 1468868871
X-Barracuda-Encrypted: DHE-RSA-AES256-SHA
X-Barracuda-URL: https://web.cudamail.com:443/cgi-mod/mark.cgi
X-ASG-Whitelist: Header =?UTF-8?B?eFwtY3VkYW1haWxcLXdoaXRlbGlzdFwtdG8=?=
X-Virus-Scanned: by bsmtpd at cudamail.com
X-Barracuda-BRTS-Status: 1
Subject: [ovs-dev] [PATCH v1 2/2] ovn-northd: Add logical flows to support
DHCPv6
X-BeenThere: dev@openvswitch.org
X-Mailman-Version: 2.1.16
Precedence: list
List-Id:
List-Unsubscribe: ,
List-Archive:
List-Post:
List-Help:
List-Subscribe: ,
Errors-To: dev-bounces@openvswitch.org
Sender: "dev"
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
---
lib/packets.c | 19 ++++
lib/packets.h | 2 +
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 ++++++++++++++++++++++++++++++++++++++++++++
7 files changed, 575 insertions(+), 10 deletions(-)
diff --git a/lib/packets.c b/lib/packets.c
index a27264c..ddbef1a 100644
--- a/lib/packets.c
+++ b/lib/packets.c
@@ -693,6 +693,25 @@ struct in6_addr ipv6_addr_bitand(const struct in6_addr *a,
return dst;
}
+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/lib/packets.h b/lib/packets.h
index 077ccfa..2ac831c 100644
--- a/lib/packets.h
+++ b/lib/packets.h
@@ -1027,6 +1027,8 @@ void ipv6_format_masked(const struct in6_addr *addr,
const char * ipv6_string_mapped(char *addr_str, const struct in6_addr *addr);
struct in6_addr ipv6_addr_bitand(const struct in6_addr *src,
const struct in6_addr *mask);
+struct in6_addr ipv6_addr_bitxor(const struct in6_addr *src,
+ const struct in6_addr *mask);
struct in6_addr ipv6_create_mask(int mask);
int ipv6_count_cidr_bits(const struct in6_addr *netmask);
bool ipv6_is_cidr(const struct in6_addr *netmask);
diff --git a/ovn/northd/ovn-northd.8.xml b/ovn/northd/ovn-northd.8.xml
index 447e042..31d8dc0 100644
--- a/ovn/northd/ovn-northd.8.xml
+++ b/ovn/northd/ovn-northd.8.xml
@@ -426,8 +426,9 @@ output;
Ingress Table 10: DHCP option processing
- 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.
@@ -455,6 +456,21 @@ next;
-
+
+ A priority-100 logical flow is added for these logical ports
+ which matches the IPv6 packet with udp.src
= 546 and
+ udp.dst
= 547 and applies the action
+ put_dhcpv6_opts
and advances the packet to the next
+ table.
+
+
+
+reg0[3] = put_dhcpv6_opts(options...);
+next;
+
+
+
+ -
A priority-0 flow that matches all packets to advances to table 11.
@@ -502,6 +518,41 @@ output;
+
+ A priority 100 logical flow is added for the logical ports configured
+ with DHCPv6 options which matches IPv6 packets with udp.src == 546
+ && udp.dst == 547 && reg0[3] == 1
and
+ responds back to the inport
after applying these
+ actions. If reg0[3]
is set to 1, it means that the
+ action put_dhcpv6_opts
was successful.
+
+
+
+eth.dst = eth.src;
+eth.src = E;
+ip6.dst = O;
+ip6.src = S;
+udp.src = 547;
+udp.dst = 546;
+outport = P;
+inport = ""; /* Allow sending out inport. */
+output;
+
+
+
+ where E is the server MAC address and S is the
+ server IPv6 LLA address generated from the SERVER_ID
+ defined in the DHCPv6 options and O is
+ the IPv6 address defined in the logical port's addresses column.
+
+
+
+ (This terminates packet processing; the packet does not go on the
+ next ingress table.)
+
+
+
+
A priority-0 flow that matches all packets to advances to table 12.
@@ -582,7 +633,8 @@ output;
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
Ingress Table 11: DHCP responses
.
diff --git a/ovn/northd/ovn-northd.c b/ovn/northd/ovn-northd.c
index e3276cc..a94b35b 100644
--- a/ovn/northd/ovn-northd.c
+++ b/ovn/northd/ovn-northd.c
@@ -1400,6 +1400,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->nbs->dhcpv6_options) {
+ /* CMS has disabled native DHCPv6 for this lport. */
+ return false;
+ }
+
+ struct in6_addr host_ip, mask;
+
+ char *error = ipv6_parse_masked(op->nbs->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->nbs->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->nbs->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++) {
@@ -1734,6 +1800,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);
+ }
+ }
}
}
}
@@ -2025,8 +2118,9 @@ build_lswitch_flows(struct hmap *datapaths, struct hmap *ports,
continue;
}
- if (!op->nbs->dhcpv4_options) {
- /* CMS has disabled native DHCPv4 for this lport. */
+ if (!op->nbs->dhcpv4_options && !op->nbs->dhcpv6_options) {
+ /* CMS has disabled both native DHCPv4 and DHCPv6 for this lport.
+ */
continue;
}
@@ -2059,6 +2153,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;
+ }
+ }
}
}
@@ -3165,6 +3287,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)
{
@@ -3209,6 +3338,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 *
@@ -3381,7 +3554,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);
@@ -3400,6 +3576,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 table.
+
+ This column defines the DHCPv6 Options to be included by the
+ ovn-controller
when it replies to the DHCPv6 requests.
+ Please see the table.
+
+
See External IDs at the beginning of this document.
@@ -941,11 +947,15 @@
DHCPv4 options to be configured and applied at each compute host
running ovn-controller.
+
+ OVN also implements a native DHCPv6 support which provides stateless
+ replies to DHCPv6 requests.
+
- The DHCPv4 options will be included if the logical port has the IPv4
- address in this .
+ The DHCPv4/DHCPv6 options will be included if the logical port has the
+ IP address in this .
@@ -1190,6 +1200,80 @@
+
+
+ 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 .
+
+
+
+
+ Below are the supported DHCPv6 options. Please refer the RFC 3315
+ which describes the DHCPv6 protocol.
+
+
+
+
+ The DHCPv6 option code for this option is 2. This option is used
+ to carry a DUID which identifies the Server.
+ ovn-controller
defines DUID Based on
+ Link-layer Address [DUID-LL]
+
+
+
+ The value of this DHCPv6 option is of type MAC
.
+
+ Example. key="SERVER_ID", value="F0:00:00:00:00:01".
+
+
+
+
+
+ The DHCPv6 option code for this option is 5. This option is used to
+ specify the offered IPv6 address to the client.
+
+
+
+ The value of this DHCPv6 option is of type IPv6 address
.
+
+ Example. key="SERVER_ID", value="aef0::23:04".
+
+
+
+
+
+ The DHCPv6 option code for this option is 23. This option is used
+ to specify the DNS servers.
+
+
+
+ The value of this DHCPv6 option is of type
+ IPv6 address(es)
.
+
+ Example. key="DNS_RECURSIVE_SERVER",
+ value="{aef0::23:01, aef0::23:02}".
+
+
+
+
+
+ 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.
+
+
+
+ The value of this DHCPv6 option is of type string
.
+
+ Example. key="DOMAIN_SEARCH_LIST", value="ovn.org".
+
+
+
+
+
See External IDs at the beginning of this document.
diff --git a/tests/ovn.at b/tests/ovn.at
index 8b4b06d..a8fc3d7 100644
--- a/tests/ovn.at
+++ b/tests/ovn.at
@@ -3356,6 +3356,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])