Tested to work end-to-end with openstack.
On Sat, Jul 23, 2016 at 5:49 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(-) > > > v6 -> v7 > * Rebased and resolved the merge conflicts. > > v5 -> v6 > * Rebased and fixed the compilation errors because of renaming of struct > ovn_port's nbs > to nbsp > > 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 > + && udp.dst == 67 && 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 a3d1672..fc2148d 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; > @@ -3111,6 +3277,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 * > @@ -3279,6 +3516,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); > @@ -3296,6 +3537,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