Thanks for the patch, looks good to me. Tested-by: Yifeng Sun <pkusunyif...@gmail.com>
Reviewed-by: Yifeng Sun <pkusunyif...@gmail.com> On Thu, Jan 18, 2018 at 2:45 PM, Ben Pfaff <b...@ovn.org> wrote: > ofproto/trace takes a bunch of options that have weird placement and > syntax. This commit changes the syntax so that the options can be placed > anywhere and consistently use a double-dash option prefix. For > compatibility, the previous syntax is also supported. > > An upcoming commit will add new options and this change allows that > upcoming commit to be less confusing. > > Signed-off-by: Ben Pfaff <b...@ovn.org> > --- > ofproto/ofproto-dpif-trace.c | 194 +++++++++++++++++++++--------- > ------------- > ofproto/ofproto-unixctl.man | 42 +++++----- > tests/ofproto-dpif.at | 2 +- > 3 files changed, 120 insertions(+), 118 deletions(-) > > diff --git a/ofproto/ofproto-dpif-trace.c b/ofproto/ofproto-dpif-trace.c > index 4999d1d6f326..75730155080c 100644 > --- a/ofproto/ofproto-dpif-trace.c > +++ b/ofproto/ofproto-dpif-trace.c > @@ -183,41 +183,11 @@ oftrace_node_print_details(struct ds *output, > } > } > > -static char * OVS_WARN_UNUSED_RESULT > -parse_oftrace_options(int argc, const char *argv[], > - struct ovs_list *next_ct_states) > -{ > - int k; > - struct ds ds = DS_EMPTY_INITIALIZER; > - > - for (k = 0; k < argc; k++) { > - if (!strncmp(argv[k], "--ct-next", 9)) { > - if (k + 1 > argc) { > - return xasprintf("Missing argument for option %s", > argv[k]); > - } > - > - uint32_t ct_state; > - if (!parse_ct_state(argv[++k], 0, &ct_state, &ds)) { > - return ds_steal_cstr(&ds); > - } > - if (!validate_ct_state(ct_state, &ds)) { > - return ds_steal_cstr(&ds); > - } > - oftrace_push_ct_state(next_ct_states, ct_state); > - } else { > - return xasprintf("Invalid option %s", argv[k]); > - } > - } > - > - ds_destroy(&ds); > - return NULL; > -} > - > /* Parses the 'argc' elements of 'argv', ignoring argv[0]. The following > * forms are supported: > * > - * - [dpname] odp_flow [OPTIONS] [-generate | packet] > - * - bridge br_flow [OPTIONS] [-generate | packet] > + * - [options] [dpname] odp_flow [packet] > + * - [options] bridge br_flow [packet] > * > * On success, initializes '*ofprotop' and 'flow' and returns NULL. On > failure > * returns a nonnull malloced error message. */ > @@ -225,67 +195,109 @@ static char * OVS_WARN_UNUSED_RESULT > parse_flow_and_packet(int argc, const char *argv[], > struct ofproto_dpif **ofprotop, struct flow *flow, > struct dp_packet **packetp, > - struct ovs_list *next_ct_states) > + struct ovs_list *next_ct_states, > + bool *consistent) > { > const struct dpif_backer *backer = NULL; > const char *error = NULL; > char *m_err = NULL; > struct simap port_names = SIMAP_INITIALIZER(&port_names); > - struct dp_packet *packet; > + struct dp_packet *packet = NULL; > struct ofpbuf odp_key; > struct ofpbuf odp_mask; > - int first_option; > > ofpbuf_init(&odp_key, 0); > ofpbuf_init(&odp_mask, 0); > > - /* Handle "-generate" or a hex string as the last argument. */ > - if (!strcmp(argv[argc - 1], "-generate")) { > - packet = dp_packet_new(0); > - argc--; > - } else { > - error = eth_from_hex(argv[argc - 1], &packet); > - if (!error) { > - argc--; > - } else if (argc == 4) { > - /* The 3-argument form must end in "-generate' or a hex > string. */ > - goto exit; > - } > - error = NULL; > + const char *args[3]; > + int n_args = 0; > + bool generate_packet = false; > + if (consistent) { > + *consistent = false; > } > + for (int i = 1; i < argc; i++) { > + const char *arg = argv[i]; > + if (!strcmp(arg, "-generate") || !strcmp(arg, "--generate")) { > + generate_packet = true; > + } else if (consistent > + && (!strcmp(arg, "-consistent") || > + !strcmp(arg, "--consistent"))) { > + *consistent = true; > + } else if (!strcmp(arg, "--ct-next")) { > + if (i + 1 >= argc) { > + m_err = xasprintf("Missing argument for option %s", arg); > + goto exit; > + } > > - /* Parse options. */ > - if (argc >= 4) { > - if (!strncmp(argv[2], "--", 2)) { > - first_option = 2; > - } else if (!strncmp(argv[3], "--", 2)) { > - first_option = 3; > - } else { > - error = "Syntax error: invalid option"; > + uint32_t ct_state; > + struct ds ds = DS_EMPTY_INITIALIZER; > + if (!parse_ct_state(argv[++i], 0, &ct_state, &ds) > + || !validate_ct_state(ct_state, &ds)) { > + m_err = ds_steal_cstr(&ds); > + goto exit; > + } > + oftrace_push_ct_state(next_ct_states, ct_state); > + } else if (arg[0] == '-') { > + m_err = xasprintf("%s: unknown option", arg); > + goto exit; > + } else if (n_args >= ARRAY_SIZE(args)) { > + m_err = xstrdup("too many arguments"); > goto exit; > + } else { > + args[n_args++] = arg; > } > + } > > - m_err = parse_oftrace_options(argc - first_option, argv + > first_option, > - next_ct_states); > - if (m_err) { > + /* 'args' must now have one of the following forms: > + * > + * odp_flow > + * dpname odp_flow > + * bridge br_flow > + * odp_flow packet > + * dpname odp_flow packet > + * bridge br_flow packet > + * > + * Parse the packet if it's there. Note that: > + * > + * - If there is one argument, there cannot be a packet. > + * > + * - If there are three arguments, there must be a packet. > + * > + * If there is a packet, we strip it off. > + */ > + if (!generate_packet && n_args > 1) { > + error = eth_from_hex(args[n_args - 1], &packet); > + if (!error) { > + n_args--; > + } else if (n_args > 2) { > + /* The 3-argument form must end in a hex string. */ > goto exit; > } > - argc = first_option; > + error = NULL; > } > > - /* odp_flow can have its in_port specified as a name instead of port > no. > - * We do not yet know whether a given flow is a odp_flow or a br_flow. > - * But, to know whether a flow is odp_flow through > odp_flow_from_string(), > - * we need to create a simap of name to port no. */ > - if (argc == 3) { > + /* We stripped off the packet if there was one, so 'args' now has one > of > + * the following forms: > + * > + * odp_flow > + * dpname odp_flow > + * bridge br_flow > + * > + * Before we parse the flow, try to identify the backer, then use that > + * backer to assemble a collection of port names. The port names are > + * useful so that the user can specify ports by name instead of > number in > + * the flow. */ > + if (n_args == 2) { > + /* args[0] might be dpname. */ > const char *dp_type; > - if (!strncmp(argv[1], "ovs-", 4)) { > - dp_type = argv[1] + 4; > + if (!strncmp(args[0], "ovs-", 4)) { > + dp_type = args[0] + 4; > } else { > - dp_type = argv[1]; > + dp_type = args[0]; > } > backer = shash_find_data(&all_dpif_backers, dp_type); > - } else if (argc == 2) { > + } else if (n_args == 1) { > + /* Pick default backer. */ > struct shash_node *node; > if (shash_count(&all_dpif_backers) == 1) { > node = shash_first(&all_dpif_backers); > @@ -308,7 +320,7 @@ parse_flow_and_packet(int argc, const char *argv[], > * bridge is specified. If function odp_flow_key_from_string() > * returns 0, the flow is a odp_flow. If function > * parse_ofp_exact_flow() returns NULL, the flow is a br_flow. */ > - if (!odp_flow_from_string(argv[argc - 1], &port_names, > + if (!odp_flow_from_string(args[n_args - 1], &port_names, > &odp_key, &odp_mask)) { > if (!backer) { > error = "Cannot find the datapath"; > @@ -349,14 +361,14 @@ parse_flow_and_packet(int argc, const char *argv[], > } else { > char *err; > > - if (argc != 3) { > + if (n_args != 2) { > error = "Must specify bridge name"; > goto exit; > } > > - *ofprotop = ofproto_dpif_lookup_by_name(argv[1]); > + *ofprotop = ofproto_dpif_lookup_by_name(args[0]); > if (!*ofprotop) { > - error = "Unknown bridge name"; > + m_err = xasprintf("%s: unknown bridge", args[0]); > goto exit; > } > > @@ -368,7 +380,7 @@ parse_flow_and_packet(int argc, const char *argv[], > } > err = parse_ofp_exact_flow(flow, NULL, > ofproto_get_tun_tab(&(*ofprotop)->up), > - argv[argc - 1], &map); > + args[n_args - 1], &map); > ofputil_port_map_destroy(&map); > if (err) { > m_err = xasprintf("Bad openflow flow syntax: %s", err); > @@ -377,16 +389,15 @@ parse_flow_and_packet(int argc, const char *argv[], > } > } > > - /* Generate a packet, if requested. */ > - if (packet) { > - if (!dp_packet_size(packet)) { > - flow_compose(packet, flow, 0); > - } else { > - /* Use the metadata from the flow and the packet argument > - * to reconstruct the flow. */ > - pkt_metadata_from_flow(&packet->md, flow); > - flow_extract(packet, flow); > - } > + if (generate_packet) { > + /* Generate a packet, as requested. */ > + packet = dp_packet_new(0); > + flow_compose(packet, flow, 0); > + } else if (packet) { > + /* Use the metadata from the flow and the packet argument to > + * reconstruct the flow. */ > + pkt_metadata_from_flow(&packet->md, flow); > + flow_extract(packet, flow); > } > > exit: > @@ -423,7 +434,7 @@ ofproto_unixctl_trace(struct unixctl_conn *conn, int > argc, const char *argv[], > struct ovs_list next_ct_states = OVS_LIST_INITIALIZER(&next_ct_ > states); > > error = parse_flow_and_packet(argc, argv, &ofproto, &flow, &packet, > - &next_ct_states); > + &next_ct_states, NULL); > if (!error) { > struct ds result; > > @@ -471,19 +482,8 @@ ofproto_unixctl_trace_actions(struct unixctl_conn > *conn, int argc, > goto exit; > } > > - /* OpenFlow 1.1 and later suggest that the switch enforces certain > forms of > - * consistency between the flow and the actions. With -consistent, we > - * enforce consistency even for a flow supported in OpenFlow 1.0. */ > - if (!strcmp(argv[1], "-consistent")) { > - enforce_consistency = true; > - argv++; > - argc--; > - } else { > - enforce_consistency = false; > - } > - > error = parse_flow_and_packet(argc, argv, &ofproto, &match.flow, > &packet, > - &next_ct_states); > + &next_ct_states, &enforce_consistency); > if (error) { > unixctl_command_reply_error(conn, error); > free(error); > diff --git a/ofproto/ofproto-unixctl.man b/ofproto/ofproto-unixctl.man > index ee1f81fceaec..8f1d12c654ab 100644 > --- a/ofproto/ofproto-unixctl.man > +++ b/ofproto/ofproto-unixctl.man > @@ -6,10 +6,10 @@ These commands manage the core OpenFlow switch > implementation (called > Lists the names of the running ofproto instances. These are the names > that may be used on \fBofproto/trace\fR. > . > -.IP "\fBofproto/trace\fR [\fIdpname\fR] \fIodp_flow\fR [\fIOPTIONS\fR] > [\fB\-generate \fR| \fIpacket\fR]" > -.IQ "\fBofproto/trace\fR \fIbridge\fR \fIbr_flow\fR [\fIOPTIONS\fR] > [\fB\-generate \fR| \fIpacket\fR]" > -.IQ "\fBofproto/trace\-packet\-out\fR [\fB\-consistent\fR] > [\fIdpname\fR] \fIodp_flow\fR [\fIOPTIONS\fR] [\fB\-generate \fR| > \fIpacket\fR] \fIactions\fR" > -.IQ "\fBofproto/trace\-packet\-out\fR [\fB\-consistent\fR] \fIbridge\fR > \fIbr_flow\fR [\fIOPTIONS\fR] [\fB\-generate \fR| \fIpacket\fR] > \fIactions\fR" > +.IP "\fBofproto/trace\fR [\fIoptions\fR] [\fIdpname\fR] \fIodp_flow\fR > [\fIpacket\fR] > +.IQ "\fBofproto/trace\fR [\fIoptions\fR] \fIbridge\fR \fIbr_flow\fR > [\fIpacket\fR]] > +.IQ "\fBofproto/trace\-packet\-out\fR [\fIoptions\fR] [\fIdpname\fR] > \fIodp_flow\fR [\fIpacket\fR] \fIactions\fR" > +.IQ "\fBofproto/trace\-packet\-out\fR [\fIoptions\fR \fIbridge\fR > \fIbr_flow\fR [\fIpacket\fR] \fIactions\fR" > Traces the path of an imaginary packet through \fIswitch\fR and > reports the path that it took. The initial treatment of the packet > varies based on the command: > @@ -49,7 +49,21 @@ wildcards.) \fIbridge\fR names of the bridge through > which > . > .IP > .RS > -\fBofproto/trace\fR supports the following options: > +These commands support the following options: > +.IP \fB\-\-generate\fR > +Generate a packet from the flow (see below for more information). > +. > +.IP \fB\-\-consistent\fR > +Accepted by \fBofproto\-trace\-packet\-out\fR only. With this option, > +the command rejects \fIactions\fR that are inconsistent with the > +specified packet. (An example of an inconsistency is attempting to > +strip the VLAN tag from a packet that does not have a VLAN tag.) Open > +vSwitch ignores most forms of inconsistency in OpenFlow 1.0 and > +rejects inconsistencies in later versions of OpenFlow. The option is > +necessary because the command does not ordinarily imply a particular > +OpenFlow version. One exception is that, when \fIactions\fR includes > +an action that only OpenFlow 1.1 and later supports (such as > +\fBpush_vlan\fR), \fB\-\-consistent\fR is automatically enabled. > . > .IP "--ct-next \fIflags\fR" > When the traced flow triggers conntrack actions, \fBofproto/trace\fR > @@ -124,12 +138,12 @@ If you wish to include a packet as part of a trace > operation, there > are two ways to do it: > . > .RS > -.IP \fB\-generate\fR > +.IP \fB\-\-generate\fR > This option, added to one of the ways to specify a flow already > described, causes Open vSwitch to internally generate a packet with > the flow described and then to use that packet. If your goal is to > -execute side effects, then \fB\-generate\fR is the easiest way to do > -it, but \fB\-generate\fR is not a good way to fill in incomplete > +execute side effects, then \fB\-\-generate\fR is the easiest way to do > +it, but \fB\-\-generate\fR is not a good way to fill in incomplete > information, because it generates packets based on only the flow > information, which means that the packets really do not have any more > information than the flow. > @@ -169,18 +183,6 @@ The in_port value is kernel datapath port number for > the first format > and OpenFlow port number for the second format. The numbering of these > two types of port usually differs and there is no relationship. > . > -.IP > -\fBofproto\-trace\-packet\-out\fR accepts an additional > -\fB\-consistent\fR option. With this option specified, the command > -rejects \fIactions\fR that are inconsistent with the specified packet. > -(An example of an inconsistency is attempting to strip the VLAN tag > -from a packet that does not have a VLAN tag.) Open vSwitch ignores > -most forms of inconsistency in OpenFlow 1.0 and rejects > -inconsistencies in later versions of OpenFlow. The option is > -necessary because the command does not ordinarily imply a particular > -OpenFlow version. One exception is that, when \fIactions\fR includes > -an action that only OpenFlow 1.1 and later supports (such as > -\fBpush_vlan\fR), \fB\-consistent\fR is automatically enabled. > . > .IP "Usage examples:" > .RS 4 > diff --git a/tests/ofproto-dpif.at b/tests/ofproto-dpif.at > index a582aaf391b1..cbe0d91352fa 100644 > --- a/tests/ofproto-dpif.at > +++ b/tests/ofproto-dpif.at > @@ -5302,7 +5302,7 @@ m4_foreach( > [AT_CHECK([ovs-appctl ofproto/trace ovs-dummy "$br_flow" option], > [2], [], [stderr]) > AT_CHECK([tail -2 stderr], [0], [dnl > -Unknown bridge name > +ovs-dummy: unknown bridge > ovs-appctl: ovs-vswitchd: server returned an error > ])]) > > -- > 2.10.2 > > _______________________________________________ > dev mailing list > d...@openvswitch.org > https://mail.openvswitch.org/mailman/listinfo/ovs-dev > _______________________________________________ dev mailing list d...@openvswitch.org https://mail.openvswitch.org/mailman/listinfo/ovs-dev