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])