From patchwork Mon Jun 27 12:32:20 2016 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Numan Siddique X-Patchwork-Id: 640984 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 3rdSxp01Gtz9s2G for ; Mon, 27 Jun 2016 22:32:29 +1000 (AEST) Received: from archives.nicira.com (localhost [127.0.0.1]) by archives.nicira.com (Postfix) with ESMTP id DE2E8106D3; Mon, 27 Jun 2016 05:32:28 -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 4F7C9105FF for ; Mon, 27 Jun 2016 05:32:27 -0700 (PDT) Received: from bar5.cudamail.com (localhost [127.0.0.1]) by mx1e3.cudamail.com (Postfix) with ESMTPS id ADD1A420581 for ; Mon, 27 Jun 2016 06:32:26 -0600 (MDT) X-ASG-Debug-ID: 1467030744-09eadd4fa808bd0001-byXFYA Received: from mx3-pf2.cudamail.com ([192.168.14.1]) by bar5.cudamail.com with ESMTP id z97cYBmLuMes1pwe (version=TLSv1 cipher=DHE-RSA-AES256-SHA bits=256 verify=NO) for ; Mon, 27 Jun 2016 06:32:24 -0600 (MDT) X-Barracuda-Envelope-From: nusiddiq@redhat.com X-Barracuda-RBL-Trusted-Forwarder: 192.168.14.1 Received: from unknown (HELO mx1.redhat.com) (209.132.183.28) by mx3-pf2.cudamail.com with ESMTPS (DHE-RSA-AES256-SHA encrypted); 27 Jun 2016 12:32:24 -0000 Received-SPF: pass (mx3-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-mx11.intmail.prod.int.phx2.redhat.com (int-mx11.intmail.prod.int.phx2.redhat.com [10.5.11.24]) (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 1C0D97F0A4 for ; Mon, 27 Jun 2016 12:32:23 +0000 (UTC) Received: from nusiddiq.blr.redhat.com (dhcp-0-8.blr.redhat.com [10.70.1.8]) by int-mx11.intmail.prod.int.phx2.redhat.com (8.14.4/8.14.4) with ESMTP id u5RCWKKs017025 for ; Mon, 27 Jun 2016 08:32:21 -0400 X-CudaMail-Envelope-Sender: nusiddiq@redhat.com From: Numan Siddique X-CudaMail-Whitelist-To: dev@openvswitch.org X-CudaMail-MID: CM-V2-626007489 X-CudaMail-DTE: 062716 X-CudaMail-Originating-IP: 209.132.183.28 To: ovs dev X-ASG-Orig-Subj: [##CM-V2-626007489##][PATCH v2] ovn-northd: Add logical flows to support native DHCPv4 Organization: Red Hat Message-ID: <5596a862-d80a-626f-12b9-2ac21126a6d3@redhat.com> Date: Mon, 27 Jun 2016 18:02:20 +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.24 X-Greylist: Sender IP whitelisted, not delayed by milter-greylist-4.5.16 (mx1.redhat.com [10.5.110.26]); Mon, 27 Jun 2016 12:32:23 +0000 (UTC) X-Barracuda-Connect: UNKNOWN[192.168.14.1] X-Barracuda-Start-Time: 1467030744 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 v2] ovn-northd: Add logical flows to support native DHCPv4 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 a native DHCPv4 support which caters to the common use case of providing an IP address to a booting instance by providing stateless replies to DHCPv4 requests based on statically configured address mappings. To do this it allows a short list of DHCPv4 options to be configured and applied at each compute host running ovn-controller. A new table 'DHCP_Options' is added in OVN NB DB to store the DHCP options. For each logical port configured with DHCPv4 Options following flows are added - A logical flow which copies the DHCPv4 options to the DHCPv4 request packets using the 'put_dhcp_opts' action and advances the packet to the next stage. - A logical flow which implements the DHCP reponder by sending the DHCPv4 reply back to the inport once the 'put_dhcp_opts' action is applied. Signed-off-by: Numan Siddique Co-authored-by: Ben Pfaff Signed-off-by: Ben Pfaff --- ovn/northd/ovn-northd.8.xml | 91 +++++++++++++- ovn/northd/ovn-northd.c | 252 ++++++++++++++++++++++++++++++++++++- ovn/ovn-nb.ovsschema | 20 ++- ovn/ovn-nb.xml | 270 +++++++++++++++++++++++++++++++++++++++ ovn/utilities/ovn-nbctl.8.xml | 30 +++++ ovn/utilities/ovn-nbctl.c | 196 +++++++++++++++++++++++++++++ tests/ovn.at | 285 ++++++++++++++++++++++++++++++++++++++++++ 7 files changed, 1136 insertions(+), 8 deletions(-) diff --git a/ovn/northd/ovn-northd.8.xml b/ovn/northd/ovn-northd.8.xml index 260cc14..5001417 100644 --- a/ovn/northd/ovn-northd.8.xml +++ b/ovn/northd/ovn-northd.8.xml @@ -343,7 +343,90 @@ output; -

Ingress Table 6: Destination Lookup

+

Ingress Table 6: 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. +

+ +
    +
  • +

    + A priority-100 logical flow is added for these logical ports + which matches the IPv4 packet with udp.src = 68 and + udp.dst = 67 and applies the action + put_dhcp_opts and advances the packet to the table 7. +

    + +
    +reg0[0] = put_dhcp_opts(offer_ip = O, options...);
    +next;
    +        
    + +

    + For DHCPDISCOVER and DHCPREQUEST, this transforms the packet into a + DHCP reply, adds the DHCP offer IP O and options to the + packet, and stores 1 into reg0[0]. For other kinds of packets, it + just stores 0 into reg0[0]. Either way, it continues to the next + table. +

    + +
  • + +
  • + A priority-0 flow that matches all packets to advances to table 7. +
  • +
+ +

Ingress Table 7: DHCP responses

+ +

+ This table implements DHCP responder for the DHCP replies generated by + by the previous table. +

+ +
    +
  • +

    + A priority 100 logical flow is added for the logical ports configured + with DHCPv4 options which matches IPv4 packets with udp.src == 68 + && udp.dst == 67 && reg0[0] == 1 and + responds back to the inport after applying these + actions. If reg0[0] is set to 1, it means that the + action put_dhcp_opts was successful. +

    + +
    +eth.dst = eth.src;
    +eth.src = E;
    +ip4.dst = O;
    +ip4.src = S;
    +udp.src = 67;
    +udp.dst = 68;
    +outport = P;
    +inport = ""; /* Allow sending out inport. */
    +output;
    +        
    + +

    + where E is the server MAC address and S is the + server IPv4 address defined in the DHCPv4 options and O is + the IPv4 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 8. +
  • +
+ +

Ingress Table 8: Destination Lookup

This table implements switching behavior. It contains these logical @@ -387,6 +470,12 @@ output; This is similar to ingress table 4 except for to-lport ACLs.

+

+ 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 + Ingress Table 7: DHCP response. +

+

Egress Table 2: Egress Port Security - IP

diff --git a/ovn/northd/ovn-northd.c b/ovn/northd/ovn-northd.c index c2cf15e..c130b54 100644 --- a/ovn/northd/ovn-northd.c +++ b/ovn/northd/ovn-northd.c @@ -26,6 +26,7 @@ #include "hash.h" #include "hmap.h" #include "json.h" +#include "ovn/lib/ovn-dhcp.h" #include "ovn/lib/lex.h" #include "ovn/lib/ovn-nb-idl.h" #include "ovn/lib/ovn-sb-idl.h" @@ -94,7 +95,9 @@ enum ovn_stage { PIPELINE_STAGE(SWITCH, IN, PRE_ACL, 3, "ls_in_pre_acl") \ PIPELINE_STAGE(SWITCH, IN, ACL, 4, "ls_in_acl") \ PIPELINE_STAGE(SWITCH, IN, ARP_RSP, 5, "ls_in_arp_rsp") \ - PIPELINE_STAGE(SWITCH, IN, L2_LKUP, 6, "ls_in_l2_lkup") \ + PIPELINE_STAGE(SWITCH, IN, DHCP_OPTIONS, 6, "ls_in_dhcp_options") \ + PIPELINE_STAGE(SWITCH, IN, DHCP_RESPONSE, 7, "ls_in_dhcp_response") \ + PIPELINE_STAGE(SWITCH, IN, L2_LKUP, 8, "ls_in_l2_lkup") \ \ /* Logical switch egress stages. */ \ PIPELINE_STAGE(SWITCH, OUT, PRE_ACL, 0, "ls_out_pre_acl") \ @@ -1316,6 +1319,76 @@ lsp_is_up(const struct nbrec_logical_switch_port *lsp) } static bool +build_dhcpv4_action(struct ovn_port *op, ovs_be32 offer_ip, + struct ds *options_action, struct ds *response_action) +{ + if (!op->nbs->dhcpv4_options) { + /* CMS has disabled native DHCPv4 for this lport. */ + return false; + } + + ovs_be32 host_ip, mask; + char *error = ip_parse_masked(op->nbs->dhcpv4_options->cidr, &host_ip, + &mask); + if (error || ((offer_ip ^ host_ip) & mask)) { + /* Either + * - cidr defined is invalid or + * - the offer ip of the logical port doesn't belong to the cidr + * defined in the DHCP options. + * */ + free(error); + return false; + } + + const char *server_ip = smap_get( + &op->nbs->dhcpv4_options->options, "server_id"); + const char *server_mac = smap_get( + &op->nbs->dhcpv4_options->options, "server_mac"); + const char *lease_time = smap_get( + &op->nbs->dhcpv4_options->options, "lease_time"); + const char *router = smap_get( + &op->nbs->dhcpv4_options->options, "router"); + + if (!(server_ip && server_mac && lease_time && router)) { + /* "server_id", "server_mac", "lease_time" and "router" should be + * present in the dhcp_options. */ + static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5); + VLOG_WARN_RL(&rl, "Required DHCPv4 options not defined for lport - %s", + op->json_key); + return false; + } + + struct smap dhcpv4_options = SMAP_INITIALIZER(&dhcpv4_options); + smap_clone(&dhcpv4_options, &op->nbs->dhcpv4_options->options); + + /* server_mac is not DHCPv4 option, delete it from the smap. */ + smap_remove(&dhcpv4_options, "server_mac"); + char *netmask = xasprintf(IP_FMT, IP_ARGS(mask)); + smap_add(&dhcpv4_options, "netmask", netmask); + free(netmask); + + ds_put_format(options_action, "reg0[0] = put_dhcp_opts(offerip = " + IP_FMT", ", IP_ARGS(offer_ip)); + struct smap_node *node; + SMAP_FOR_EACH(node, &dhcpv4_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; " + "ip4.dst = "IP_FMT"; ip4.src = %s; udp.src = 67; " + "udp.dst = 68; outport = inport; inport = \"\";" + " /* Allow sending out inport. */ output;", + server_mac, IP_ARGS(offer_ip), server_ip); + + smap_destroy(&dhcpv4_options); + return true; +} + +static bool has_stateful_acl(struct ovn_datapath *od) { for (size_t i = 0; i < od->nbs->n_acls; i++) { @@ -1478,6 +1551,35 @@ build_acls(struct ovn_datapath *od, struct hmap *lflows, struct hmap *ports) acl->match, "drop;"); } } + + /* Add 34000 priority flow to allow DHCP reply from ovn-controller to all + * logical ports of the datapath if the CMS has configured DHCPv4 options*/ + if (od->nbs && od->nbs->n_ports) { + for (size_t i = 0; i < od->nbs->n_ports; i++) { + if (od->nbs->ports[i]->dhcpv4_options) { + const char *server_id = smap_get( + &od->nbs->ports[i]->dhcpv4_options->options, "server_id"); + const char *server_mac = smap_get( + &od->nbs->ports[i]->dhcpv4_options->options, "server_mac"); + const char *lease_time = smap_get( + &od->nbs->ports[i]->dhcpv4_options->options, "lease_time"); + const char *router = smap_get( + &od->nbs->ports[i]->dhcpv4_options->options, "router"); + if (server_id && server_mac && lease_time && router) { + struct ds match = DS_EMPTY_INITIALIZER; + const char *actions = + has_stateful ? "ct_commit; next;" : "next;"; + ds_put_format(&match, "outport == \"%s\" && eth.src == %s " + "&& ip4.src == %s && udp && udp.src == 67 " + "&& udp.dst == 68", od->nbs->ports[i]->name, + server_mac, server_id); + ovn_lflow_add( + lflows, od, S_SWITCH_OUT_ACL, 34000, ds_cstr(&match), + actions); + }; + } + } + } } static void @@ -1635,7 +1737,71 @@ build_lswitch_flows(struct hmap *datapaths, struct hmap *ports, ovn_lflow_add(lflows, od, S_SWITCH_IN_ARP_RSP, 0, "1", "next;"); } - /* Ingress table 6: Destination lookup, broadcast and multicast handling + /* Logical switch ingress table 6 and 7: DHCP options and response + * priority 100 flows. */ + HMAP_FOR_EACH (op, key_node, ports) { + if (!op->nbs) { + continue; + } + + if (!lsp_is_enabled(op->nbs) || !strcmp(op->nbs->type, "router")) { + /* Don't add the DHCP flows if the port is not enabled or if the + * port is a router port. */ + continue; + } + + for (size_t i = 0; i < op->nbs->n_addresses; i++) { + struct lport_addresses laddrs; + if (!extract_lsp_addresses(op->nbs->addresses[i], &laddrs, + false)) { + continue; + } + + for (size_t j = 0; j < laddrs.n_ipv4_addrs; j++) { + struct ds options_action = DS_EMPTY_INITIALIZER; + struct ds response_action = DS_EMPTY_INITIALIZER; + if (build_dhcpv4_action(op, laddrs.ipv4_addrs[j].addr, + &options_action, &response_action)) { + struct ds match = DS_EMPTY_INITIALIZER; + ds_put_format( + &match, "inport == %s && eth.src == "ETH_ADDR_FMT + " && ip4.src == 0.0.0.0 && " + "ip4.dst == 255.255.255.255 && udp.src == 68 && " + "udp.dst == 67", op->json_key, + ETH_ADDR_ARGS(laddrs.ea)); + + ovn_lflow_add(lflows, op->od, S_SWITCH_IN_DHCP_OPTIONS, + 100, ds_cstr(&match), + ds_cstr(&options_action)); + /* If reg0[0] is set, it means the put_dhcp_opts action + * is successful */ + ds_put_cstr(&match, " && reg0[0]"); + 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; + } + } + free(laddrs.ipv4_addrs); + } + } + + /* Ingress table 6 and 7: DHCP options and response, by default goto next. + * (priority 0). */ + + HMAP_FOR_EACH (od, key_node, datapaths) { + if (!od->nbs) { + continue; + } + + ovn_lflow_add(lflows, od, S_SWITCH_IN_DHCP_OPTIONS, 0, "1", "next;"); + ovn_lflow_add(lflows, od, S_SWITCH_IN_DHCP_RESPONSE, 0, "1", "next;"); + } + + /* Ingress table 8: Destination lookup, broadcast and multicast handling * (priority 100). */ HMAP_FOR_EACH (op, key_node, ports) { if (!op->nbs) { @@ -1655,7 +1821,7 @@ build_lswitch_flows(struct hmap *datapaths, struct hmap *ports, "outport = \""MC_FLOOD"\"; output;"); } - /* Ingress table 6: Destination lookup, unicast handling (priority 50), */ + /* Ingress table 8: Destination lookup, unicast handling (priority 50), */ HMAP_FOR_EACH (op, key_node, ports) { if (!op->nbs) { continue; @@ -1692,7 +1858,7 @@ build_lswitch_flows(struct hmap *datapaths, struct hmap *ports, } } - /* Ingress table 6: Destination lookup for unknown MACs (priority 0). */ + /* Ingress table 8: Destination lookup for unknown MACs (priority 0). */ HMAP_FOR_EACH (od, key_node, datapaths) { if (!od->nbs) { continue; @@ -2603,6 +2769,77 @@ ovnsb_db_run(struct northd_context *ctx) } +static struct dhcp_opts_map supported_dhcp_opts[] = { + OFFERIP, + DHCP_OPT_NETMASK, + DHCP_OPT_ROUTER, + DHCP_OPT_DNS_SERVER, + DHCP_OPT_LOG_SERVER, + DHCP_OPT_LPR_SERVER, + DHCP_OPT_SWAP_SERVER, + DHCP_OPT_POLICY_FILTER, + DHCP_OPT_ROUTER_SOLICITATION, + DHCP_OPT_NIS_SERVER, + DHCP_OPT_NTP_SERVER, + DHCP_OPT_SERVER_ID, + DHCP_OPT_TFTP_SERVER, + DHCP_OPT_CLASSLESS_STATIC_ROUTE, + DHCP_OPT_MS_CLASSLESS_STATIC_ROUTE, + DHCP_OPT_IP_FORWARD_ENABLE, + DHCP_OPT_ROUTER_DISCOVERY, + DHCP_OPT_ETHERNET_ENCAP, + DHCP_OPT_DEFAULT_TTL, + DHCP_OPT_TCP_TTL, + DHCP_OPT_MTU, + DHCP_OPT_LEASE_TIME, + DHCP_OPT_T1, + DHCP_OPT_T2 +}; + +static void +check_and_add_supported_dhcp_opts_to_sb_db(struct northd_context *ctx) +{ + static bool nothing_to_add = false; + + if (nothing_to_add) { + return; + } + + struct hmap dhcp_opts_to_add = HMAP_INITIALIZER(&dhcp_opts_to_add); + for (size_t i = 0; (i < sizeof(supported_dhcp_opts) / + sizeof(supported_dhcp_opts[0])); i++) { + hmap_insert(&dhcp_opts_to_add, &supported_dhcp_opts[i].hmap_node, + dhcp_opt_hash(supported_dhcp_opts[i].name)); + } + + const struct sbrec_dhcp_options *opt_row, *opt_row_next; + SBREC_DHCP_OPTIONS_FOR_EACH_SAFE(opt_row, opt_row_next, ctx->ovnsb_idl) { + struct dhcp_opts_map *dhcp_opt = + dhcp_opts_find(&dhcp_opts_to_add, opt_row->name); + if (dhcp_opt) { + hmap_remove(&dhcp_opts_to_add, &dhcp_opt->hmap_node); + } + else { + sbrec_dhcp_options_delete(opt_row); + } + } + + if (!dhcp_opts_to_add.n) { + nothing_to_add = true; + } + + struct dhcp_opts_map *opt; + HMAP_FOR_EACH_POP(opt, hmap_node, &dhcp_opts_to_add) { + struct sbrec_dhcp_options *sbrec_dhcp_option = + sbrec_dhcp_options_insert(ctx->ovnsb_txn); + sbrec_dhcp_options_set_name(sbrec_dhcp_option, opt->name); + sbrec_dhcp_options_set_code(sbrec_dhcp_option, opt->code); + sbrec_dhcp_options_set_type(sbrec_dhcp_option, opt->type); + } + + hmap_destroy(&dhcp_opts_to_add); +} + static char *default_nb_db_; static const char * @@ -2771,6 +3008,10 @@ main(int argc, char *argv[]) add_column_noalert(ovnsb_idl_loop.idl, &sbrec_port_binding_col_options); add_column_noalert(ovnsb_idl_loop.idl, &sbrec_port_binding_col_mac); ovsdb_idl_add_column(ovnsb_idl_loop.idl, &sbrec_port_binding_col_chassis); + ovsdb_idl_add_table(ovnsb_idl_loop.idl, &sbrec_table_dhcp_options); + 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); /* Main loop. */ exiting = false; @@ -2784,6 +3025,9 @@ main(int argc, char *argv[]) ovnnb_db_run(&ctx); ovnsb_db_run(&ctx); + if (ctx.ovnsb_txn) { + check_and_add_supported_dhcp_opts_to_sb_db(&ctx); + } unixctl_server_run(unixctl); unixctl_server_wait(unixctl); diff --git a/ovn/ovn-nb.ovsschema b/ovn/ovn-nb.ovsschema index 58f04b2..9587b94 100644 --- a/ovn/ovn-nb.ovsschema +++ b/ovn/ovn-nb.ovsschema @@ -1,7 +1,7 @@ { "name": "OVN_Northbound", - "version": "3.1.0", - "cksum": "1426508118 6135", + "version": "3.2.0", + "cksum": "27511920 6856", "tables": { "Logical_Switch": { "columns": { @@ -43,6 +43,11 @@ "max": "unlimited"}}, "up": {"type": {"key": "boolean", "min": 0, "max": 1}}, "enabled": {"type": {"key": "boolean", "min": 0, "max": 1}}, + "dhcpv4_options": {"type": {"key": {"type": "uuid", + "refTable": "DHCP_Options", + "refType": "strong"}, + "min": 0, + "max": 1}}, "external_ids": { "type": {"key": "string", "value": "string", "min": 0, "max": "unlimited"}}}, @@ -119,6 +124,15 @@ "snat", "dnat_and_snat" ]]}}}}, - "isRoot": false} + "isRoot": false}, + "DHCP_Options": { + "columns": { + "cidr": {"type": "string"}, + "options": {"type": {"key": "string", "value": "string", + "min": 0, "max": "unlimited"}}, + "external_ids": { + "type": {"key": "string", "value": "string", + "min": 0, "max": "unlimited"}}}, + "isRoot": true} } } diff --git a/ovn/ovn-nb.xml b/ovn/ovn-nb.xml index 6355c44..31fe0f5 100644 --- a/ovn/ovn-nb.xml +++ b/ovn/ovn-nb.xml @@ -478,6 +478,12 @@ + + This column defines the DHCPv4 Options to be included by the + ovn-controller when it replies to the DHCPv4 requests. + Please see the table. + + See External IDs at the beginning of this document. @@ -819,4 +825,268 @@ + +

+ OVN implements a native DHCPv4 support which caters to the common + use case of providing an IPv4 address to a booting instance by + providing stateless replies to DHCPv4 requests based on statically + configured address mappings. To do this it allows a short list of + DHCPv4 options to be configured and applied at each compute host + running ovn-controller. +

+ + +

+ The DHCPv4 options will be included if the logical port has the IPv4 + address in this . +

+
+ + +

+ CMS should define the set of DHCPv4 options as key/value pairs in the + column of this table. In order for the + ovn-controller to include these DHCPv4 options, the + of + should refer to an entry in this table. +

+ + +

+ Below are the supported DHCPv4 options whose values are IPv4 address + or addresses. If the value has more than one IPv4 address, then it + should be enclosed within '{}' braces. Please refer to the + RFC 2132 "https://tools.ietf.org/html/rfc2132" for + more details on the v4 options and their codes. +

+ + +

+ The DHCPv4 option code for this option is 1. +

+ +

+ Example. key="netmask", value="255.255.255.0" +

+
+ + +

+ The DHCPv4 option code for this option is 3. +

+
+ + +

+ The DHCPv4 option code for this option is 6. +

+
+ + +

+ The DHCPv4 option code for this option is 7. +

+
+ + +

+ The DHCPv4 option code for this option is 9. +

+
+ + +

+ The DHCPv4 option code for this option is 16. +

+
+ + +

+ The DHCPv4 option code for this option is 21. +

+
+ + +

+ The DHCPv4 option code for this option is 32. +

+
+ + +

+ The DHCPv4 option code for this option is 41. +

+
+ + +

+ The DHCPv4 option code for this option is 42. +

+
+ + +

+ The DHCPv4 option code for this option is 54. +

+
+ + +

+ The DHCPv4 option code for this option is 66. +

+
+ + +

+ The DHCPv4 option code for this option is 121. +

+ +

+ This option can contain one or more static routes, each of which + consists of a destination descriptor and the IP address of the + router that should be used to reach that destination. Please see + RFC 3442 for more details. +

+ +

+ Example. + key="classless_static_route" + value="{30.0.0.0/24,10.0.0.10, 0.0.0.0/0,10.0.0.1}" +

+
+ + +

+ The DHCPv4 option code for this option is 249. This option is + similar to classless_static_route supported by + Microsoft Windows DHCPv4 clients. +

+
+ + +

+ eth.src will be set to this value in the DHCPv4 + response packet. +

+
+
+ + + +

+ The DHCPv4 option code for this option is 19. +

+ +

+ The value of this DHCPv4 option is of type bool. + + Example. key="ip_forward_enable", value="1" +

+
+ + +

+ The DHCPv4 option code for this option is 31. +

+ +

+ The value of this DHCPv4 option is of type bool. +

+
+ + +

+ The DHCPv4 option code for this option is 36. +

+ +

+ The value of this DHCPv4 option is of type bool. +

+
+ + +

+ The DHCPv4 option code for this option is 23. +

+ +

+ The value of this DHCPv4 option is of type uint8. + + Example. key="default_ttl", value="128". +

+
+ + +

+ The DHCPv4 option code for this option is 37. +

+ +

+ The value of this DHCPv4 option is of type uint8. +

+
+ + +

+ The DHCPv4 option code for this option is 26. +

+ +

+ The value of this DHCPv4 option is of type uint16. +

+
+ + +

+ The DHCPv4 option code for this option is 51. +

+ +

+ The value of this DHCPv4 option is of type uint32. + + Example. key="lease_time", value="42000" +

+
+ + +

+ The DHCPv4 option code for this option is 58. +

+ +

+ The value of this DHCPv4 option is of type uint32. + + Example. key="T1", value="30000" +

+
+ + +

+ The DHCPv4 option code for this option is 59. +

+ +

+ The value of this DHCPv4 option is of type uint32. + + Example. key="T2", value="40000" +

+
+
+ + +

+ DHCPv4 options "server_id", "server_mac", + "router" and "lease_time" are mandatory + options which CMS should define for OVN to support + native DHCPv4. +

+
+
+ + + + See External IDs at the beginning of this document. + + +
diff --git a/ovn/utilities/ovn-nbctl.8.xml b/ovn/utilities/ovn-nbctl.8.xml index c2ca420..cd9840e 100644 --- a/ovn/utilities/ovn-nbctl.8.xml +++ b/ovn/utilities/ovn-nbctl.8.xml @@ -378,6 +378,36 @@ +

DHCP Options commands

+ +
+
dhcp-options-create cidr [key=value]
+
+ Creates a new DHCP Options entry in the DHCP_Options table + with the specified cidr and optional external-ids. +
+ +
dhcp-options-list
+
+ Lists the DHCP Options entries. +
+ +
dhcp-options-del dhcp-option
+
+ Deletes the DHCP Options entry referred by dhcp-option UUID. +
+ +
dhcp-options-set-options dhcp-option [key=value]...
+
+ Set the DHCP Options for the dhcp-option UUID. +
+ +
dhcp-options-get-options dhcp-option
+
+ Lists the DHCP Options for the dhcp-option UUID. +
+
+

Database Commands

These commands query and modify the contents of ovsdb tables. They are a slight abstraction of the ovsdb interface and diff --git a/ovn/utilities/ovn-nbctl.c b/ovn/utilities/ovn-nbctl.c index 345647a..df84c3f 100644 --- a/ovn/utilities/ovn-nbctl.c +++ b/ovn/utilities/ovn-nbctl.c @@ -69,6 +69,8 @@ static void run_prerequisites(struct ctl_command[], size_t n_commands, struct ovsdb_idl *); static bool do_nbctl(const char *args, struct ctl_command *, size_t n, struct ovsdb_idl *); +static const struct nbrec_dhcp_options *dhcp_options_get( + struct ctl_context *ctx, const char *id, bool must_exist); int main(int argc, char *argv[]) @@ -333,6 +335,9 @@ Logical switch port commands:\n\ lsp-set-options PORT KEY=VALUE [KEY=VALUE]...\n\ set options related to the type of PORT\n\ lsp-get-options PORT get the type specific options for PORT\n\ + lsp-set-dhcpv4-options PORT [DHCP_OPTIONS_UUID]\n\ + set dhcpv4 options for PORT\n\ + lsp-get-dhcpv4-options PORT get the dhcpv4 options for PORT\n\ \n\ Logical router commands:\n\ lr-add [ROUTER] create a logical router named ROUTER\n\ @@ -357,6 +362,19 @@ Route commands:\n\ remove routes from ROUTER\n\ lr-route-list ROUTER print routes for ROUTER\n\ \n\ +\n\ +DHCP Options commands:\n\ + dhcp-options-create CIDR [EXTERNAL_IDS]\n\ + create a DHCP options row with CIDR\n\ + dhcp-options-del DHCP_OPTIONS_UUID\n\ + delete DHCP_OPTIONS_UUID\n\ + dhcp-options-list \n\ + lists the DHCP_Options rows\n\ + dhcp-options-set-options DHCP_OPTIONS_UUID KEY=VALUE [KEY=VALUE]...\n\ + set DHCP options to the DHCP_OPTIONS_UUID\n\ + dhcp-options-get-options DHCO_OPTIONS_UUID \n\ + displays the DHCP options of th DHCP_OPTIONS_UUID\n\ +\n\ %s\ \n\ Options:\n\ @@ -1018,6 +1036,45 @@ nbctl_lsp_get_options(struct ctl_context *ctx) } } +static void +nbctl_lsp_set_dhcpv4_options(struct ctl_context *ctx) +{ + const char *id = ctx->argv[1]; + const struct nbrec_logical_switch_port *lsp; + + lsp = lsp_by_name_or_uuid(ctx, id, true); + const struct nbrec_dhcp_options *dhcp_opt = NULL; + if (ctx->argc == 3 ) { + dhcp_opt = dhcp_options_get(ctx, ctx->argv[2], true); + } + + if (dhcp_opt) { + ovs_be32 ip; + unsigned int plen; + char *error = ip_parse_cidr(dhcp_opt->cidr, &ip, &plen); + if (error){ + free(error); + VLOG_WARN("DHCP options cidr '%s' is not IPv4", dhcp_opt->cidr); + return; + } + } + nbrec_logical_switch_port_set_dhcpv4_options(lsp, dhcp_opt); +} + +static void +nbctl_lsp_get_dhcpv4_options(struct ctl_context *ctx) +{ + const char *id = ctx->argv[1]; + const struct nbrec_logical_switch_port *lsp; + + lsp = lsp_by_name_or_uuid(ctx, id, true); + if (lsp->dhcpv4_options) { + ds_put_format(&ctx->output, UUID_FMT " (%s)\n", + UUID_ARGS(&lsp->dhcpv4_options->header_.uuid), + lsp->dhcpv4_options->cidr); + } +} + enum { DIR_FROM_LPORT, DIR_TO_LPORT @@ -1274,6 +1331,126 @@ nbctl_lr_list(struct ctl_context *ctx) free(nodes); } +static const struct nbrec_dhcp_options * +dhcp_options_get(struct ctl_context *ctx, const char *id, bool must_exist) +{ + struct uuid dhcp_opts_uuid; + const struct nbrec_dhcp_options *dhcp_opts = NULL; + if (uuid_from_string(&dhcp_opts_uuid, id)) { + dhcp_opts = nbrec_dhcp_options_get_for_uuid(ctx->idl, &dhcp_opts_uuid); + } + + if (!dhcp_opts && must_exist) { + ctl_fatal("%s: dhcp options UUID not found", id); + } + return dhcp_opts; +} + +static void +nbctl_dhcp_options_create(struct ctl_context *ctx) +{ + /* Validate the cidr */ + ovs_be32 ip; + unsigned int plen; + char *error = ip_parse_cidr(ctx->argv[1], &ip, &plen); + if (error){ + /* check if its IPv6 cidr */ + free(error); + struct in6_addr ipv6; + error = ipv6_parse_cidr(ctx->argv[1], &ipv6, &plen); + if (error) { + free(error); + VLOG_WARN("Invalid cidr format '%s'", ctx->argv[1]); + return; + } + } + + struct nbrec_dhcp_options *dhcp_opts = nbrec_dhcp_options_insert(ctx->txn); + nbrec_dhcp_options_set_cidr(dhcp_opts, ctx->argv[1]); + + struct smap ext_ids = SMAP_INITIALIZER(&ext_ids); + for (size_t i = 2; i < ctx->argc; i++) { + char *key, *value; + value = xstrdup(ctx->argv[i]); + key = strsep(&value, "="); + if (value) { + smap_add(&ext_ids, key, value); + } + free(key); + } + + nbrec_dhcp_options_set_external_ids(dhcp_opts, &ext_ids); + smap_destroy(&ext_ids); +} + +static void +nbctl_dhcp_options_set_options(struct ctl_context *ctx) +{ + const struct nbrec_dhcp_options *dhcp_opts = dhcp_options_get( + ctx, ctx->argv[1], true); + + struct smap dhcp_options = SMAP_INITIALIZER(&dhcp_options); + for (size_t i = 2; i < ctx->argc; i++) { + char *key, *value; + value = xstrdup(ctx->argv[i]); + key = strsep(&value, "="); + if (value) { + smap_add(&dhcp_options, key, value); + } + free(key); + } + + nbrec_dhcp_options_set_options(dhcp_opts, &dhcp_options); + smap_destroy(&dhcp_options); +} + +static void +nbctl_dhcp_options_get_options(struct ctl_context *ctx) +{ + const struct nbrec_dhcp_options *dhcp_opts = dhcp_options_get( + ctx, ctx->argv[1], true); + + struct smap_node *node; + SMAP_FOR_EACH(node, &dhcp_opts->options) { + ds_put_format(&ctx->output, "%s=%s\n", node->key, node->value); + } +} + +static void +nbctl_dhcp_options_del(struct ctl_context *ctx) +{ + bool must_exist = !shash_find(&ctx->options, "--if-exists"); + const char *id = ctx->argv[1]; + const struct nbrec_dhcp_options *dhcp_opts; + + dhcp_opts = dhcp_options_get(ctx, id, must_exist); + if (!dhcp_opts) { + return; + } + + nbrec_dhcp_options_delete(dhcp_opts); +} + +static void +nbctl_dhcp_options_list(struct ctl_context *ctx) +{ + const struct nbrec_dhcp_options *dhcp_opts; + struct smap dhcp_options; + + smap_init(&dhcp_options); + NBREC_DHCP_OPTIONS_FOR_EACH(dhcp_opts, ctx->idl) { + smap_add_format(&dhcp_options, dhcp_opts->cidr, UUID_FMT, + UUID_ARGS(&dhcp_opts->header_.uuid)); + } + const struct smap_node **nodes = smap_sort(&dhcp_options); + for (size_t i = 0; i < smap_count(&dhcp_options); i++) { + const struct smap_node *node = nodes[i]; + ds_put_format(&ctx->output, "%s\n", node->value); + } + smap_destroy(&dhcp_options); + free(nodes); +} + /* The caller must free the returned string. */ static char * normalize_ipv4_prefix(ovs_be32 ipv4, unsigned int plen) @@ -1866,6 +2043,10 @@ static const struct ctl_table_class tables[] = { NULL}, {NULL, NULL, NULL}}}, + {&nbrec_table_dhcp_options, + {{&nbrec_table_dhcp_options, NULL, + NULL}, + {NULL, NULL, NULL}}}, {NULL, {{NULL, NULL, NULL}, {NULL, NULL, NULL}}} }; @@ -2112,6 +2293,10 @@ static const struct ctl_command_syntax nbctl_commands[] = { nbctl_lsp_set_options, NULL, "", RW }, { "lsp-get-options", 1, 1, "PORT", NULL, nbctl_lsp_get_options, NULL, "", RO }, + { "lsp-set-dhcpv4-options", 1, 2, "PORT [DHCP_OPT_UUID]", NULL, + nbctl_lsp_set_dhcpv4_options, NULL, "", RW }, + { "lsp-get-dhcpv4-options", 1, 1, "PORT", NULL, + nbctl_lsp_get_dhcpv4_options, NULL, "", RO }, /* logical router commands. */ { "lr-add", 0, 1, "[ROUTER]", NULL, nbctl_lr_add, NULL, @@ -2137,6 +2322,17 @@ static const struct ctl_command_syntax nbctl_commands[] = { { "lr-route-list", 1, 1, "ROUTER", NULL, nbctl_lr_route_list, NULL, "", RO }, + /* DHCP_Options commands */ + {"dhcp-options-create", 1, INT_MAX, "CIDR [EXTERNAL:IDS]", NULL, + nbctl_dhcp_options_create, NULL, "", RW }, + {"dhcp-options-del", 1, 1, "DHCP_OPT_UUID", NULL, + nbctl_dhcp_options_del, NULL, "", RW}, + {"dhcp-options-list", 0, 0, "", NULL, nbctl_dhcp_options_list, NULL, "", RO}, + {"dhcp-options-set-options", 1, INT_MAX, "DHCP_OPT_UUID KEY=VALUE [KEY=VALUE]...", + NULL, nbctl_dhcp_options_set_options, NULL, "", RW }, + {"dhcp-options-get-options", 1, 1, "DHCP_OPT_UUID", NULL, + nbctl_dhcp_options_get_options, NULL, "", RO }, + {NULL, 0, 0, NULL, NULL, NULL, NULL, "", RO}, }; diff --git a/tests/ovn.at b/tests/ovn.at index 297070c..9ccf5be 100644 --- a/tests/ovn.at +++ b/tests/ovn.at @@ -2844,6 +2844,291 @@ OVS_APP_EXIT_AND_WAIT([ovsdb-server]) AT_CLEANUP +AT_SETUP([ovn -- dhcpv4 : 1 HV, 2 LS, 2 LSPs/LS]) +AT_KEYWORDS([dhcpv4]) +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" + +ovn-nbctl lsp-set-port-security ls1-lp1 "f0:00:00:00:00:01 10.0.0.4" + +ovn-nbctl lsp-add ls1 ls1-lp2 \ +-- lsp-set-addresses ls1-lp2 "f0:00:00:00:00:02 10.0.0.6 20.0.0.4" + +ovn-nbctl lsp-set-port-security ls1-lp2 "f0:00:00:00:00:02 10.0.0.6 20.0.0.4" + +ovn-nbctl ls-add ls2 +ovn-nbctl lsp-add ls2 ls2-lp1 \ +-- lsp-set-addresses ls2-lp1 "f0:00:00:00:00:03 30.0.0.6 40.0.0.4" +ovn-nbctl lsp-set-port-security ls2-lp1 "f0:00:00:00:00:03 30.0.0.6 40.0.0.4" +ovn-nbctl lsp-add ls2 ls2-lp2 \ +-- lsp-set-addresses ls2-lp2 "f0:00:00:00:00:04 30.0.0.7" +ovn-nbctl lsp-set-port-security ls2-lp2 "f0:00:00:00:00:04 30.0.0.7" + +ovn-nbctl -- --id=@d1 create DHCP_Options cidr=10.0.0.0/24 \ +options="\"server_id\"=\"10.0.0.1\" \"server_mac\"=\"ff:10:00:00:00:01\" \ +\"lease_time\"=\"3600\" \"router\"=\"10.0.0.1\"" \ +-- add Logical_Switch_Port ls1-lp1 dhcpv4_options @d1 \ +-- add Logical_Switch_Port ls1-lp2 dhcpv4_options @d1 + +ovn-nbctl -- --id=@d2 create DHCP_Options cidr=30.0.0.0/24 \ +options="\"server_id\"=\"30.0.0.1\" \"server_mac\"=\"ff:10:00:00:00:02\" \ +\"lease_time\"=\"3600\"" -- add Logical_Switch_Port ls2-lp2 dhcpv4_options @d2 + +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 + +as hv1 ovs-vsctl show + +trim_zeros() { + sed 's/\(00\)\{1,\}$//' +} + +# This shell function sends a DHCP request packet +# test_dhcp INPORT SRC_MAC DHCP_TYPE OFFER_IP ... +test_dhcp() { + local inport=$1 src_mac=$2 dhcp_type=$3 offer_ip=$4 + local request=ffffffffffff${src_mac}080045100110000000008011000000000000ffffffff + # udp header and dhcp header + request+=0044004300fc0000 + request+=010106006359aa760000000000000000000000000000000000000000${src_mac} + # client hardware padding + request+=00000000000000000000 + # server hostname + request+=0000000000000000000000000000000000000000000000000000000000000000 + request+=0000000000000000000000000000000000000000000000000000000000000000 + # boot file name + request+=0000000000000000000000000000000000000000000000000000000000000000 + request+=0000000000000000000000000000000000000000000000000000000000000000 + request+=0000000000000000000000000000000000000000000000000000000000000000 + request+=0000000000000000000000000000000000000000000000000000000000000000 + # dhcp magic cookie + request+=63825363 + # dhcp message type + request+=3501${dhcp_type}ff + + if test $offer_ip != 0; then + local srv_mac=$5 srv_ip=$6 expected_dhcp_opts=$7 + # total IP length will be the IP length of the request packet + # (which is 272 in our case) + 8 (padding bytes) + (expected_dhcp_opts / 2) + ip_len=`expr 280 + ${#expected_dhcp_opts} / 2` + udp_len=`expr $ip_len - 20` + printf -v ip_len "%x" $ip_len + printf -v udp_len "%x" $udp_len + # $ip_len var will be in 3 digits i.e 134. So adding a '0' before $ip_len + local reply=${src_mac}${srv_mac}080045100${ip_len}000000008011XXXX${srv_ip}${offer_ip} + # udp header and dhcp header. + # $udp_len var will be in 3 digits. So adding a '0' before $udp_len + reply+=004300440${udp_len}0000020106006359aa760000000000000000 + # your ip address + reply+=${offer_ip} + # next server ip address, relay agent ip address, client mac address + reply+=0000000000000000${src_mac} + # client hardware padding + reply+=00000000000000000000 + # server hostname + reply+=0000000000000000000000000000000000000000000000000000000000000000 + reply+=0000000000000000000000000000000000000000000000000000000000000000 + # boot file name + reply+=0000000000000000000000000000000000000000000000000000000000000000 + reply+=0000000000000000000000000000000000000000000000000000000000000000 + reply+=0000000000000000000000000000000000000000000000000000000000000000 + reply+=0000000000000000000000000000000000000000000000000000000000000000 + # dhcp magic cookie + reply+=63825363 + # dhcp message type + local dhcp_reply_type=02 + if test $dhcp_type = 03; then + dhcp_reply_type=05 + fi + reply+=3501${dhcp_reply_type}${expected_dhcp_opts}00000000ff00000000 + echo $reply >> $inport.expected + else + shift; shift; shift; shift; + 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 +} + +ip_to_hex() { + printf "%02x%02x%02x%02x" "$@" +} + +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 + +#send DHCP DISCOVER +offer_ip=`ip_to_hex 10 0 0 4` +server_ip=`ip_to_hex 10 0 0 1` +expected_dhcp_opts=0104ffffff0003040a00000136040a000001330400000e10 +test_dhcp 1 f00000000001 01 $offer_ip ff1000000001 $server_ip $expected_dhcp_opts + +# NXT_RESUMEs should be 1. +echo "waiting for 1 NXT_RESUMEs..." +OVS_WAIT_UNTIL([test 1 = `cat ofctl_monitor*.log | grep -c NXT_RESUME`]) + +$PYTHON "$top_srcdir/utilities/ovs-pcap.in" hv1/vif1-tx.pcap > 1.packets +cat 1.expected | cut -c -48 > expout +AT_CHECK([cat 1.packets | cut -c -48], [0], [expout]) +# Skipping the IPv4 checksum +cat 1.expected | cut -c 53- > expout +AT_CHECK([cat 1.packets | cut -c 53-], [0], [expout]) + +# ovs-ofctl also resumes the packets and this causes other ports to receive +# the DHCP request packet. So reset the pcap files so that its easier to test. +reset_pcap_file hv1-vif1 hv1/vif1 +reset_pcap_file hv1-vif2 hv1/vif2 +rm -f 1.expected +rm -f 2.expected + +#send DHCP_REQUEST +offer_ip=`ip_to_hex 10 0 0 6` +server_ip=`ip_to_hex 10 0 0 1` +expected_dhcp_opts=0104ffffff0003040a00000136040a000001330400000e10 +test_dhcp 2 f00000000002 03 $offer_ip ff1000000001 $server_ip $expected_dhcp_opts + +# NXT_RESUMEs should be 2. +echo "waiting for 1 NXT_RESUMEs..." +OVS_WAIT_UNTIL([test 2 = `cat ofctl_monitor*.log | grep -c NXT_RESUME`]) + +$PYTHON "$top_srcdir/utilities/ovs-pcap.in" hv1/vif2-tx.pcap > 2.packets +cat 2.expected | cut -c -48 > expout +AT_CHECK([cat 2.packets | cut -c -48], [0], [expout]) +# Skipping the IPv4 checksum +cat 2.expected | cut -c 53- > expout +AT_CHECK([cat 2.packets | cut -c 53-], [0], [expout]) + +reset_pcap_file hv1-vif1 hv1/vif1 +reset_pcap_file hv1-vif2 hv1/vif2 +rm -f 1.expected +rm -f 2.expected + +# Send Invalid DHCPv4 packet on ls1-lp2. It should be received by ovn-controller +# but should be resumed without the reply. +# ls1-lp1 (vif1-tx.pcap) should receive the DHCPv4 request packet twice, +# one from ovn-controller and the other from "ovs-ofctl resume." +offer_ip=0 +test_dhcp 2 f00000000002 08 $offer_ip 1 1 + +# NXT_RESUMEs should be 3. +echo "waiting for 1 NXT_RESUMEs..." +OVS_WAIT_UNTIL([test 3 = `cat ofctl_monitor*.log | grep -c NXT_RESUME`]) + +# vif1-tx.pcap should have received the DHCPv4 (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]) + +reset_pcap_file hv1-vif1 hv1/vif1 +reset_pcap_file hv1-vif2 hv1/vif2 +rm -f 1.expected +rm -f 2.expected + +# Send DHCPv4 packet on ls2-lp1. It doesn't have any DHCPv4 options defined. +# ls2-lp2 (vif4-tx.pcap) should receive the DHCPv4 request packet once. + +test_dhcp 3 f00000000003 01 0 4 + +# Send DHCPv4 packet on ls2-lp2. "router" DHCPv4 option is not defined for +# this lport. +test_dhcp 4 f00000000004 01 0 3 + +# NXT_RESUMEs should be 3. +echo "waiting for 1 NXT_RESUMEs..." +OVS_WAIT_UNTIL([test 3 = `cat ofctl_monitor*.log | grep -c NXT_RESUME`]) + +$PYTHON "$top_srcdir/utilities/ovs-pcap.in" hv1/vif3-tx.pcap | trim_zeros > 3.packets +cat 3.expected > expout +AT_CHECK([cat 3.packets], [0], [expout]) + +$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])