I added them myself, thanks for the reminder.

On Wed, Jul 27, 2016 at 01:12:38AM +0530, Numan Siddique wrote:
> This patch has "Tested-by: Ramu Ramamurthy <ramu.ramamur...@us.ibm.com>"
> and "Acked-by: Ramu Ramamurthy <ramu.ramamur...@us.ibm.com>" and every time
> I forget to add this when I resubmit the patch.
> 
> Ramu - My apologies
> 
> Thanks
> Numan
> 
> 
> On Wed, Jul 27, 2016 at 12:54 AM, Numan Siddique <nusid...@redhat.com>
> wrote:
> 
> > 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. Logical ports refer to this table to configure the DHCPv4
> > 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 <nusid...@redhat.com>
> > Co-authored-by: Ben Pfaff <b...@ovn.org>
> > Signed-off-by: Ben Pfaff <b...@ovn.org>
> > ---
> >  ovn/northd/ovn-northd.8.xml   |  91 +++++++++++++-
> >  ovn/northd/ovn-northd.c       | 256 +++++++++++++++++++++++++++++++++++++-
> >  ovn/ovn-nb.ovsschema          |  20 ++-
> >  ovn/ovn-nb.xml                | 270
> > ++++++++++++++++++++++++++++++++++++++++
> >  ovn/utilities/ovn-nbctl.8.xml |  30 +++++
> >  ovn/utilities/ovn-nbctl.c     | 197 +++++++++++++++++++++++++++++
> >  tests/ovn.at                  | 281
> > ++++++++++++++++++++++++++++++++++++++++++
> >  7 files changed, 1135 insertions(+), 10 deletions(-)
> >
> > diff --git a/ovn/northd/ovn-northd.8.xml b/ovn/northd/ovn-northd.8.xml
> > index ced2839..b95caef 100644
> > --- a/ovn/northd/ovn-northd.8.xml
> > +++ b/ovn/northd/ovn-northd.8.xml
> > @@ -457,7 +457,90 @@ output;
> >        </li>
> >      </ul>
> >
> > -    <h3>Ingress Table 10: Destination Lookup</h3>
> > +    <h3>Ingress Table 10: DHCP option processing</h3>
> > +
> > +    <p>
> > +      This table adds the DHCPv4 options to a DHCPv4 packet from the
> > +      logical ports configured with IPv4 address(es) and DHCPv4 options.
> > +    </p>
> > +
> > +    <ul>
> > +      <li>
> > +        <p>
> > +          A priority-100 logical flow is added for these logical ports
> > +          which matches the IPv4 packet with <code>udp.src</code> = 68 and
> > +          <code>udp.dst</code> = 67 and applies the action
> > +          <code>put_dhcp_opts</code> and advances the packet to the next
> > table.
> > +        </p>
> > +
> > +        <pre>
> > +reg0[3] = put_dhcp_opts(offer_ip = <var>O</var>, <i>options</i>...);
> > +next;
> > +        </pre>
> > +
> > +        <p>
> > +          For DHCPDISCOVER and DHCPREQUEST, this transforms the packet
> > into a
> > +          DHCP reply, adds the DHCP offer IP <var>O</var> and options to
> > the
> > +          packet, and stores 1 into reg0[3].  For other kinds of packets,
> > it
> > +          just stores 0 into reg0[3].  Either way, it continues to the
> > next
> > +          table.
> > +        </p>
> > +
> > +      </li>
> > +
> > +      <li>
> > +        A priority-0 flow that matches all packets to advances to table
> > 11.
> > +      </li>
> > +    </ul>
> > +
> > +    <h3>Ingress Table 11: DHCP responses</h3>
> > +
> > +    <p>
> > +      This table implements DHCP responder for the DHCP replies generated
> > by
> > +      the previous table.
> > +    </p>
> > +
> > +    <ul>
> > +      <li>
> > +        <p>
> > +          A priority 100 logical flow is added for the logical ports
> > configured
> > +          with DHCPv4 options which matches IPv4 packets with
> > <code>udp.src == 68
> > +          &amp;&amp; udp.dst == 67 &amp;&amp; reg0[3] == 1</code> and
> > +          responds back to the <code>inport</code> after applying these
> > +          actions.  If <code>reg0[3]</code> is set to 1, it means that the
> > +          action <code>put_dhcp_opts</code> was successful.
> > +        </p>
> > +
> > +        <pre>
> > +eth.dst = eth.src;
> > +eth.src = <var>E</var>;
> > +ip4.dst = <var>O</var>;
> > +ip4.src = <var>S</var>;
> > +udp.src = 67;
> > +udp.dst = 68;
> > +outport = <var>P</var>;
> > +inport = ""; /* Allow sending out inport. */
> > +output;
> > +        </pre>
> > +
> > +        <p>
> > +          where <var>E</var> is the server MAC address and <var>S</var>
> > is the
> > +          server IPv4 address defined in the DHCPv4 options and
> > <var>O</var> is
> > +          the IPv4 address defined in the logical port's addresses column.
> > +        </p>
> > +
> > +        <p>
> > +          (This terminates ingress packet processing; the packet does not
> > go
> > +           to the next ingress table.)
> > +        </p>
> > +      </li>
> > +
> > +      <li>
> > +        A priority-0 flow that matches all packets to advances to table
> > 12.
> > +      </li>
> > +    </ul>
> > +
> > +    <h3>Ingress Table 12: Destination Lookup</h3>
> >
> >      <p>
> >        This table implements switching behavior.  It contains these logical
> > @@ -531,6 +614,12 @@ output;
> >        there are no rules added for load balancing new connections.
> >      </p>
> >
> > +    <p>
> > +      Also a priority 34000 logical flow is added for each logical port
> > which
> > +      has DHCPv4 options defined to allow the DHCPv4 reply packet from the
> > +      <code>Ingress Table 11: DHCP responses</code>.
> > +    </p>
> > +
> >      <h3>Egress Table 6: Egress Port Security - IP</h3>
> >
> >      <p>
> > diff --git a/ovn/northd/ovn-northd.c b/ovn/northd/ovn-northd.c
> > index 38a3d30..578fbbb 100644
> > --- a/ovn/northd/ovn-northd.c
> > +++ b/ovn/northd/ovn-northd.c
> > @@ -27,6 +27,7 @@
> >  #include "openvswitch/hmap.h"
> >  #include "openvswitch/json.h"
> >  #include "ovn/lib/lex.h"
> > +#include "ovn/lib/ovn-dhcp.h"
> >  #include "ovn/lib/ovn-nb-idl.h"
> >  #include "ovn/lib/ovn-sb-idl.h"
> >  #include "ovn/lib/ovn-util.h"
> > @@ -99,7 +100,9 @@ enum ovn_stage {
> >      PIPELINE_STAGE(SWITCH, IN,  LB,             7, "ls_in_lb")           \
> >      PIPELINE_STAGE(SWITCH, IN,  STATEFUL,       8, "ls_in_stateful")     \
> >      PIPELINE_STAGE(SWITCH, IN,  ARP_ND_RSP,     9, "ls_in_arp_rsp")      \
> > -    PIPELINE_STAGE(SWITCH, IN,  L2_LKUP,       10, "ls_in_l2_lkup")      \
> > +    PIPELINE_STAGE(SWITCH, IN,  DHCP_OPTIONS,   10, "ls_in_dhcp_options")
> > \
> > +    PIPELINE_STAGE(SWITCH, IN,  DHCP_RESPONSE,  11,
> > "ls_in_dhcp_response") \
> > +    PIPELINE_STAGE(SWITCH, IN,  L2_LKUP,        12, "ls_in_l2_lkup")
> > \
> >                                                                        \
> >      /* Logical switch egress stages. */                               \
> >      PIPELINE_STAGE(SWITCH, OUT, PRE_LB,       0, "ls_out_pre_lb")     \
> > @@ -140,6 +143,7 @@ enum ovn_stage {
> >  #define REGBIT_CONNTRACK_DEFRAG "reg0[0]"
> >  #define REGBIT_CONNTRACK_COMMIT "reg0[1]"
> >  #define REGBIT_CONNTRACK_NAT    "reg0[2]"
> > +#define REGBIT_DHCP_OPTS_RESULT "reg0[3]"
> >
> >  /* Returns an "enum ovn_stage" built from the arguments. */
> >  static enum ovn_stage
> > @@ -1371,6 +1375,77 @@ 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->nbsp->dhcpv4_options) {
> > +        /* CMS has disabled native DHCPv4 for this lport. */
> > +        return false;
> > +    }
> > +
> > +    ovs_be32 host_ip, mask;
> > +    char *error = ip_parse_masked(op->nbsp->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 DHCPv4 options.
> > +        *  */
> > +        free(error);
> > +        return false;
> > +    }
> > +
> > +    const char *server_ip = smap_get(
> > +        &op->nbsp->dhcpv4_options->options, "server_id");
> > +    const char *server_mac = smap_get(
> > +        &op->nbsp->dhcpv4_options->options, "server_mac");
> > +    const char *lease_time = smap_get(
> > +        &op->nbsp->dhcpv4_options->options, "lease_time");
> > +    const char *router = smap_get(
> > +            &op->nbsp->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->nbsp->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,
> > +                  REGBIT_DHCP_OPTS_RESULT" = 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++) {
> > @@ -1774,6 +1849,35 @@ build_acls(struct ovn_datapath *od, struct hmap
> > *lflows)
> >              }
> >          }
> >      }
> > +
> > +    /* 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
> > @@ -1955,8 +2059,8 @@ build_lswitch_flows(struct hmap *datapaths, struct
> > hmap *ports,
> >          ovn_lflow_add(lflows, od, S_SWITCH_IN_PORT_SEC_IP, 0, "1",
> > "next;");
> >      }
> >
> > -    /* Ingress table 9: ARP responder, skip requests coming from localnet
> > ports.
> > -     * (priority 100). */
> > +    /* Ingress table 9: ARP/ND responder, skip requests coming from
> > localnet
> > +     * ports. (priority 100). */
> >      HMAP_FOR_EACH (op, key_node, ports) {
> >          if (!op->nbsp) {
> >              continue;
> > @@ -2052,7 +2156,69 @@ build_lswitch_flows(struct hmap *datapaths, struct
> > hmap *ports,
> >          ovn_lflow_add(lflows, od, S_SWITCH_IN_ARP_ND_RSP, 0, "1",
> > "next;");
> >      }
> >
> > -    /* Ingress table 10: Destination lookup, broadcast and multicast
> > handling
> > +    /* Logical switch ingress table 10 and 11: DHCP options and response
> > +         * priority 100 flows. */
> > +    HMAP_FOR_EACH (op, key_node, ports) {
> > +        if (!op->nbsp) {
> > +           continue;
> > +        }
> > +
> > +        if (!lsp_is_enabled(op->nbsp) || !strcmp(op->nbsp->type,
> > "router")) {
> > +            /* Don't add the DHCP flows if the port is not enabled or if
> > the
> > +             * port is a router port. */
> > +            continue;
> > +        }
> > +
> > +        if (!op->nbsp->dhcpv4_options) {
> > +            /* CMS has disabled native DHCPv4 for this lport. */
> > +            continue;
> > +        }
> > +
> > +        for (size_t i = 0; i < op->n_lsp_addrs; i++) {
> > +            for (size_t j = 0; j < op->lsp_addrs[i].n_ipv4_addrs; j++) {
> > +                struct ds options_action = DS_EMPTY_INITIALIZER;
> > +                struct ds response_action = DS_EMPTY_INITIALIZER;
> > +                if (build_dhcpv4_action(
> > +                        op, op->lsp_addrs[i].ipv4_addrs[j].addr,
> > +                        &options_action, &response_action)) {
> > +                    struct ds match = DS_EMPTY_INITIALIZER;
> > +                    ds_put_format(
> > +                        &match, "inport == %s && eth.src == %s && "
> > +                        "ip4.src == 0.0.0.0 && ip4.dst == 255.255.255.255
> > && "
> > +                        "udp.src == 68 && udp.dst == 67", 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, it means the
> > +                     * put_dhcp_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;
> > +                }
> > +            }
> > +        }
> > +    }
> > +
> > +    /* Ingress table 10 and 11: 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 12: Destination lookup, broadcast and multicast
> > handling
> >       * (priority 100). */
> >      HMAP_FOR_EACH (op, key_node, ports) {
> >          if (!op->nbsp) {
> > @@ -2072,7 +2238,7 @@ build_lswitch_flows(struct hmap *datapaths, struct
> > hmap *ports,
> >                        "outport = \""MC_FLOOD"\"; output;");
> >      }
> >
> > -    /* Ingress table 10: Destination lookup, unicast handling (priority
> > 50), */
> > +    /* Ingress table 12: Destination lookup, unicast handling (priority
> > 50), */
> >      HMAP_FOR_EACH (op, key_node, ports) {
> >          if (!op->nbsp) {
> >              continue;
> > @@ -2105,7 +2271,7 @@ build_lswitch_flows(struct hmap *datapaths, struct
> > hmap *ports,
> >          }
> >      }
> >
> > -    /* Ingress table 10: Destination lookup for unknown MACs (priority
> > 0). */
> > +    /* Ingress table 12: Destination lookup for unknown MACs (priority
> > 0). */
> >      HMAP_FOR_EACH (od, key_node, datapaths) {
> >          if (!od->nbs) {
> >              continue;
> > @@ -3092,6 +3258,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 *
> > @@ -3260,6 +3497,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);
> >
> >      ovsdb_idl_add_table(ovnsb_idl_loop.idl, &sbrec_table_address_set);
> >      add_column_noalert(ovnsb_idl_loop.idl, &sbrec_address_set_col_name);
> > @@ -3277,6 +3518,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 460d5bd..3cf07c1 100644
> > --- a/ovn/ovn-nb.ovsschema
> > +++ b/ovn/ovn-nb.ovsschema
> > @@ -1,7 +1,7 @@
> >  {
> >      "name": "OVN_Northbound",
> > -    "version": "5.0.0",
> > -    "cksum": "849073644 7576",
> > +    "version": "5.1.0",
> > +    "cksum": "2201958537 8295",
> >      "tables": {
> >          "Logical_Switch": {
> >              "columns": {
> > @@ -48,6 +48,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": "weak"},
> > +                                 "min": 0,
> > +                                 "max": 1}},
> >                  "external_ids": {
> >                      "type": {"key": "string", "value": "string",
> >                               "min": 0, "max": "unlimited"}}},
> > @@ -149,6 +154,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 e571eeb..86dbfa7 100644
> > --- a/ovn/ovn-nb.xml
> > +++ b/ovn/ovn-nb.xml
> > @@ -509,6 +509,12 @@
> >      </group>
> >
> >      <group title="Common Columns">
> > +      <column name="dhcpv4_options">
> > +        This column defines the DHCPv4 Options to be included by the
> > +        <code>ovn-controller</code> when it replies to the DHCPv4
> > requests.
> > +        Please see the <ref table="DHCP_Options"/> table.
> > +      </column>
> > +
> >        <column name="external_ids">
> >          See <em>External IDs</em> at the beginning of this document.
> >        </column>
> > @@ -926,4 +932,268 @@
> >      </column>
> >    </table>
> >
> > +  <table name="DHCP_Options" title="DHCP options.">
> > +    <p>
> > +      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.
> > +    </p>
> > +
> > +    <column name="cidr">
> > +      <p>
> > +        The DHCPv4 options will be included if the logical port has the
> > IPv4
> > +        address in this <ref column="cidr"/>.
> > +      </p>
> > +    </column>
> > +
> > +    <group title="DHCPv4 options">
> > +      <p>
> > +        CMS should define the set of DHCPv4 options as key/value pairs in
> > the
> > +        <ref column="options"/> column of this table. In order for the
> > +        <code>ovn-controller</code> to include these DHCPv4 options, the
> > +        <ref column="dhcpv4_options"/> of <ref
> > table="Logical_Switch_Port"/>
> > +        should refer to an entry in this table.
> > +      </p>
> > +
> > +      <group title="Supported v4 options">
> > +        <p>
> > +          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 <code>"https://tools.ietf.org/html/rfc2132";</code> for
> > +          more details on the DHCPv4 options and their codes.
> > +        </p>
> > +
> > +        <column name="options" key="netmask">
> > +          <p>
> > +            The DHCPv4 option code for this option is 1.
> > +          </p>
> > +
> > +          <p>
> > +            Example. key="netmask", value="255.255.255.0"
> > +          </p>
> > +        </column>
> > +
> > +        <column name="options" key="router">
> > +          <p>
> > +            The DHCPv4 option code for this option is 3.
> > +          </p>
> > +        </column>
> > +
> > +        <column name="options" key="dns_server">
> > +          <p>
> > +            The DHCPv4 option code for this option is 6.
> > +          </p>
> > +        </column>
> > +
> > +        <column name="options" key="log_server">
> > +          <p>
> > +            The DHCPv4 option code for this option is 7.
> > +          </p>
> > +        </column>
> > +
> > +        <column name="options" key="lpr_server">
> > +          <p>
> > +            The DHCPv4 option code for this option is 9.
> > +          </p>
> > +        </column>
> > +
> > +        <column name="options" key="swap_server">
> > +          <p>
> > +            The DHCPv4 option code for this option is 16.
> > +          </p>
> > +        </column>
> > +
> > +        <column name="options" key="policy_filter">
> > +          <p>
> > +            The DHCPv4 option code for this option is 21.
> > +          </p>
> > +        </column>
> > +
> > +        <column name="options" key="router_solicitation">
> > +          <p>
> > +            The DHCPv4 option code for this option is 32.
> > +          </p>
> > +        </column>
> > +
> > +        <column name="options" key="nis_server">
> > +          <p>
> > +            The DHCPv4 option code for this option is 41.
> > +          </p>
> > +        </column>
> > +
> > +        <column name="options" key="ntp_server">
> > +          <p>
> > +            The DHCPv4 option code for this option is 42.
> > +          </p>
> > +        </column>
> > +
> > +        <column name="options" key="server_id">
> > +          <p>
> > +            The DHCPv4 option code for this option is 54.
> > +          </p>
> > +        </column>
> > +
> > +        <column name="options" key="tftp_server">
> > +          <p>
> > +            The DHCPv4 option code for this option is 66.
> > +          </p>
> > +        </column>
> > +
> > +        <column name="options" key="classless_static_route">
> > +          <p>
> > +            The DHCPv4 option code for this option is 121.
> > +          </p>
> > +
> > +          <p>
> > +             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.
> > +          </p>
> > +
> > +          <p>
> > +            Example.
> > +            key="classless_static_route"
> > +            value="{30.0.0.0/24,10.0.0.10, 0.0.0.0/0,10.0.0.1}"
> > +          </p>
> > +        </column>
> > +
> > +        <column name="options" key="ms_classless_static_route">
> > +          <p>
> > +            The DHCPv4 option code for this option is 249. This option is
> > +            similar to <code>classless_static_route</code> supported by
> > +            Microsoft Windows DHCPv4 clients.
> > +          </p>
> > +        </column>
> > +
> > +        <column name="options" key="server_mac">
> > +          <p>
> > +            <code>eth.src</code> will be set to this value in the DHCPv4
> > +            response packet.
> > +          </p>
> > +        </column>
> > +      </group>
> > +
> > +      <group title="Other supported DHCPv4 options">
> > +        <column name="options" key="ip_forward_enable">
> > +          <p>
> > +            The DHCPv4 option code for this option is 19.
> > +          </p>
> > +
> > +          <p>
> > +            The value of this DHCPv4 option is of type <code>bool</code>.
> > +
> > +            Example. key="ip_forward_enable", value="1"
> > +          </p>
> > +        </column>
> > +
> > +        <column name="options" key="router_discovery">
> > +          <p>
> > +            The DHCPv4 option code for this option is 31.
> > +          </p>
> > +
> > +          <p>
> > +            The value of this DHCPv4 option is of type <code>bool</code>.
> > +          </p>
> > +        </column>
> > +
> > +        <column name="options" key="ethernet_encap">
> > +          <p>
> > +            The DHCPv4 option code for this option is 36.
> > +          </p>
> > +
> > +          <p>
> > +            The value of this DHCPv4 option is of type <code>bool</code>.
> > +          </p>
> > +        </column>
> > +
> > +        <column name="options" key="default_ttl">
> > +          <p>
> > +            The DHCPv4 option code for this option is 23.
> > +          </p>
> > +
> > +          <p>
> > +            The value of this DHCPv4 option is of type <code>uint8</code>.
> > +
> > +            Example. key="default_ttl", value="128".
> > +          </p>
> > +        </column>
> > +
> > +        <column name="options" key="tcp_ttl">
> > +          <p>
> > +            The DHCPv4 option code for this option is 37.
> > +          </p>
> > +
> > +          <p>
> > +            The value of this DHCPv4 option is of type <code>uint8</code>.
> > +          </p>
> > +        </column>
> > +
> > +        <column name="options" key="mtu">
> > +          <p>
> > +            The DHCPv4 option code for this option is 26.
> > +          </p>
> > +
> > +          <p>
> > +            The value of this DHCPv4 option is of type
> > <code>uint16</code>.
> > +          </p>
> > +        </column>
> > +
> > +        <column name="options" key="lease_time">
> > +          <p>
> > +            The DHCPv4 option code for this option is 51.
> > +          </p>
> > +
> > +          <p>
> > +            The value of this DHCPv4 option is of type
> > <code>uint32</code>.
> > +
> > +            Example. key="lease_time", value="42000"
> > +          </p>
> > +        </column>
> > +
> > +        <column name="options" key="T1">
> > +          <p>
> > +            The DHCPv4 option code for this option is 58.
> > +          </p>
> > +
> > +          <p>
> > +            The value of this DHCPv4 option is of type
> > <code>uint32</code>.
> > +
> > +            Example. key="T1", value="30000"
> > +          </p>
> > +        </column>
> > +
> > +        <column name="options" key="T2">
> > +          <p>
> > +            The DHCPv4 option code for this option is 59.
> > +          </p>
> > +
> > +          <p>
> > +            The value of this DHCPv4 option is of type
> > <code>uint32</code>.
> > +
> > +            Example. key="T2", value="40000"
> > +          </p>
> > +        </column>
> > +      </group>
> > +
> > +      <group title="Mandatory DHCPv4 options">
> > +        <p>
> > +          DHCPv4 options <code>"server_id"</code>,
> > <code>"server_mac"</code>,
> > +          <code>"router"</code> and <code>"lease_time"</code> are
> > mandatory
> > +          options which CMS should define for <code>OVN</code> to support
> > +          native DHCPv4.
> > +        </p>
> > +      </group>
> > +    </group>
> > +
> > +    <group title="Common Columns">
> > +      <column name="external_ids">
> > +        See <em>External IDs</em> at the beginning of this document.
> > +      </column>
> > +    </group>
> > +  </table>
> >  </database>
> > diff --git a/ovn/utilities/ovn-nbctl.8.xml b/ovn/utilities/ovn-nbctl.8.xml
> > index 6330aa1..9d58bc5 100644
> > --- a/ovn/utilities/ovn-nbctl.8.xml
> > +++ b/ovn/utilities/ovn-nbctl.8.xml
> > @@ -391,6 +391,36 @@
> >        </dd>
> >      </dl>
> >
> > +    <h1>DHCP Options commands</h1>
> > +
> > +    <dl>
> > +      <dt><code>dhcp-options-create</code> <var>cidr</var>
> > [<var>key=value</var>]</dt>
> > +      <dd>
> > +        Creates a new DHCP Options entry in the <code>DHCP_Options</code>
> > table
> > +        with the specified <code>cidr</code> and optional
> > <code>external-ids</code>.
> > +      </dd>
> > +
> > +      <dt><code>dhcp-options-list</code></dt>
> > +      <dd>
> > +        Lists the DHCP Options entries.
> > +      </dd>
> > +
> > +      <dt><code>dhcp-options-del</code> <var>dhcp-option</var></dt>
> > +      <dd>
> > +        Deletes the DHCP Options entry referred by <var>dhcp-option</var>
> > UUID.
> > +      </dd>
> > +
> > +      <dt><code>dhcp-options-set-options</code> <var>dhcp-option</var>
> > [<var>key=value</var>]...</dt>
> > +      <dd>
> > +        Set the DHCP Options for the <var>dhcp-option</var> UUID.
> > +      </dd>
> > +
> > +      <dt><code>dhcp-options-get-options</code>
> > <var>dhcp-option</var></dt>
> > +      <dd>
> > +        Lists the DHCP Options for the <var>dhcp-option</var> UUID.
> > +      </dd>
> > +    </dl>
> > +
> >      <h1>Database Commands</h1>
> >      <p>These commands query and modify the contents of <code>ovsdb</code>
> > tables.
> >      They are a slight abstraction of the <code>ovsdb</code> interface and
> > diff --git a/ovn/utilities/ovn-nbctl.c b/ovn/utilities/ovn-nbctl.c
> > index d523eec..d34a92c 100644
> > --- a/ovn/utilities/ovn-nbctl.c
> > +++ b/ovn/utilities/ovn-nbctl.c
> > @@ -70,6 +70,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[])
> > @@ -334,6 +336,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\
> > @@ -358,6 +363,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\
> > @@ -1027,6 +1045,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);
> > +            ctl_fatal("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
> > @@ -1283,6 +1340,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);
> > +            ctl_fatal("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)
> > @@ -1927,6 +2104,11 @@ static const struct ctl_table_class tables[] = {
> >       {{&nbrec_table_address_set, &nbrec_address_set_col_name, NULL},
> >        {NULL, NULL, NULL}}},
> >
> > +    {&nbrec_table_dhcp_options,
> > +     {{&nbrec_table_dhcp_options, NULL,
> > +       NULL},
> > +      {NULL, NULL, NULL}}},
> > +
> >      {NULL, {{NULL, NULL, NULL}, {NULL, NULL, NULL}}}
> >  };
> >
> > @@ -2173,6 +2355,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,
> > @@ -2199,6 +2385,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 614a4bb..86efcf5 100644
> > --- a/tests/ovn.at
> > +++ b/tests/ovn.at
> > @@ -3067,6 +3067,287 @@ OVN_CLEANUP([hv1],[hv2])
> >
> >  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 DHCPDISCOVER.
> > +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.
> > +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 DHCPREQUEST.
> > +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.
> > +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.
> > +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.
> > +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])
> > --
> > 2.7.4
> >
> >
> > _______________________________________________
> > dev mailing list
> > dev@openvswitch.org
> > http://openvswitch.org/mailman/listinfo/dev
> >
> _______________________________________________
> dev mailing list
> dev@openvswitch.org
> http://openvswitch.org/mailman/listinfo/dev
_______________________________________________
dev mailing list
dev@openvswitch.org
http://openvswitch.org/mailman/listinfo/dev

Reply via email to