@@ -1,4 +1,4 @@
-/* Copyright (c) 2008, 2009, 2010, 2011, 2013 Nicira, Inc.
+/* Copyright (c) 2008, 2009, 2010, 2011, 2013, 2016 Nicira, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -210,6 +210,26 @@ error:
uuid_zero(uuid);
return false;
}
+
+/* Returns the number of characters at the beginning of 's' that are valid for
+ * a UUID. For example, the "123" at the beginning of "123xyzzy" could begin a
+ * UUID, so uuid_is_partial_string() would return 3; for "xyzzy", this function
+ * would return 0, since "x" can't start a UUID. */
+int
+uuid_is_partial_string(const char *s)
+{
+ static const char tmpl[UUID_LEN] = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx";
+ size_t i;
+ for (i = 0; i < UUID_LEN; i++) {
+ if (tmpl[i] == 'x'
+ ? hexit_value(s[i]) < 0
+ : s[i] != '-') {
+ break;
+ }
+ }
+ return i;
+}
+
static void
sha1_update_int(struct sha1_ctx *sha1_ctx, uintmax_t x)
@@ -1,4 +1,4 @@
-/* Copyright (c) 2008, 2009, 2010 Nicira, Inc.
+/* Copyright (c) 2008, 2009, 2010, 2016 Nicira, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -61,6 +61,7 @@ bool uuid_is_zero(const struct uuid *);
int uuid_compare_3way(const struct uuid *, const struct uuid *);
bool uuid_from_string(struct uuid *, const char *);
bool uuid_from_string_prefix(struct uuid *, const char *);
+int uuid_is_partial_string(const char *);
void uuid_set_bits_v4(struct uuid *);
#endif /* uuid.h */
@@ -266,7 +266,7 @@ consider_logical_flow(const struct lport_index *lports,
}
if (!m->n) {
ofctrl_add_flow(flow_table, ptable, lflow->priority, &m->match,
- &ofpacts);
+ lflow->header_.uuid.parts[0], &ofpacts);
} else {
uint64_t conj_stubs[64 / 8];
struct ofpbuf conj;
@@ -282,7 +282,7 @@ consider_logical_flow(const struct lport_index *lports,
dst->n_clauses = src->n_clauses;
}
ofctrl_add_flow(flow_table, ptable, lflow->priority, &m->match,
- &conj);
+ 0, &conj);
ofpbuf_uninit(&conj);
}
}
@@ -350,7 +350,7 @@ consider_neighbor_flow(const struct lport_index *lports,
uint64_t stub[1024 / 8];
struct ofpbuf ofpacts = OFPBUF_STUB_INITIALIZER(stub);
put_load(mac.ea, sizeof mac.ea, MFF_ETH_DST, 0, 48, &ofpacts);
- ofctrl_add_flow(flow_table, OFTABLE_MAC_BINDING, 100, &match, &ofpacts);
+ ofctrl_add_flow(flow_table, OFTABLE_MAC_BINDING, 100, &match, 0, &ofpacts);
ofpbuf_uninit(&ofpacts);
}
@@ -57,6 +57,7 @@ struct ovn_flow {
/* Data. */
struct ofpact *ofpacts;
size_t ofpacts_len;
+ uint64_t cookie;
};
static uint32_t ovn_flow_hash(const struct ovn_flow *);
@@ -602,7 +603,8 @@ ofctrl_recv(const struct ofp_header *oh, enum ofptype type)
void
ofctrl_add_flow(struct hmap *desired_flows,
uint8_t table_id, uint16_t priority,
- const struct match *match, const struct ofpbuf *actions)
+ const struct match *match, uint64_t cookie,
+ const struct ofpbuf *actions)
{
struct ovn_flow *f = xmalloc(sizeof *f);
f->table_id = table_id;
@@ -611,6 +613,7 @@ ofctrl_add_flow(struct hmap *desired_flows,
f->ofpacts = xmemdup(actions->data, actions->size);
f->ofpacts_len = actions->size;
f->hmap_node.hash = ovn_flow_hash(f);
+ f->cookie = cookie;
if (ovn_flow_lookup(desired_flows, f)) {
static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(5, 5);
@@ -935,6 +938,7 @@ ofctrl_put(struct hmap *flow_table, struct shash *pending_ct_zones,
.table_id = d->table_id,
.ofpacts = d->ofpacts,
.ofpacts_len = d->ofpacts_len,
+ .new_cookie = htonll(d->cookie),
.command = OFPFC_ADD,
};
add_flow_mod(&fm, &msgs);
@@ -46,7 +46,7 @@ void ofctrl_ct_flush_zone(uint16_t zone_id);
/* Flow table interfaces to the rest of ovn-controller. */
void ofctrl_add_flow(struct hmap *desired_flows, uint8_t table_id,
uint16_t priority, const struct match *,
- const struct ofpbuf *ofpacts);
+ uint64_t cookie, const struct ofpbuf *ofpacts);
void ofctrl_flow_table_clear(void);
@@ -219,7 +219,7 @@ put_local_common_flows(uint32_t dp_key, uint32_t port_key,
/* Resubmit to table 34. */
put_resubmit(OFTABLE_CHECK_LOOPBACK, ofpacts_p);
ofctrl_add_flow(flow_table, OFTABLE_LOCAL_OUTPUT, 100,
- &match, ofpacts_p);
+ &match, 0, ofpacts_p);
/* Table 34, Priority 100.
* =======================
@@ -234,7 +234,7 @@ put_local_common_flows(uint32_t dp_key, uint32_t port_key,
match_set_reg(&match, MFF_LOG_INPORT - MFF_REG0, port_key);
match_set_reg(&match, MFF_LOG_OUTPORT - MFF_REG0, port_key);
ofctrl_add_flow(flow_table, OFTABLE_CHECK_LOOPBACK, 100,
- &match, ofpacts_p);
+ &match, 0, ofpacts_p);
/* Table 64, Priority 100.
* =======================
@@ -257,7 +257,8 @@ put_local_common_flows(uint32_t dp_key, uint32_t port_key,
put_load(0, MFF_IN_PORT, 0, 16, ofpacts_p);
put_resubmit(OFTABLE_LOG_TO_PHY, ofpacts_p);
put_stack(MFF_IN_PORT, ofpact_put_STACK_POP(ofpacts_p));
- ofctrl_add_flow(flow_table, OFTABLE_SAVE_INPORT, 100, &match, ofpacts_p);
+ ofctrl_add_flow(flow_table, OFTABLE_SAVE_INPORT, 100,
+ &match, 0, ofpacts_p);
}
static void
@@ -347,7 +348,7 @@ consider_port_binding(enum mf_field_id mff_ovn_geneve,
ofpact_finish_CLONE(ofpacts_p, &clone);
ofctrl_add_flow(flow_table, OFTABLE_LOG_TO_PHY, 100,
- &match, ofpacts_p);
+ &match, 0, ofpacts_p);
return;
}
@@ -471,7 +472,7 @@ consider_port_binding(enum mf_field_id mff_ovn_geneve,
/* Resubmit to first logical ingress pipeline table. */
put_resubmit(OFTABLE_LOG_INGRESS_PIPELINE, ofpacts_p);
ofctrl_add_flow(flow_table, OFTABLE_PHY_TO_LOG,
- tag ? 150 : 100, &match, ofpacts_p);
+ tag ? 150 : 100, &match, 0, ofpacts_p);
if (!tag && (!strcmp(binding->type, "localnet")
|| !strcmp(binding->type, "l2gateway"))) {
@@ -481,7 +482,7 @@ consider_port_binding(enum mf_field_id mff_ovn_geneve,
* action. */
ofpbuf_pull(ofpacts_p, ofpacts_orig_size);
match_set_dl_tci_masked(&match, 0, htons(VLAN_CFI));
- ofctrl_add_flow(flow_table, 0, 100, &match, ofpacts_p);
+ ofctrl_add_flow(flow_table, 0, 100, &match, 0, ofpacts_p);
}
/* Table 65, Priority 100.
@@ -509,7 +510,7 @@ consider_port_binding(enum mf_field_id mff_ovn_geneve,
ofpact_put_STRIP_VLAN(ofpacts_p);
}
ofctrl_add_flow(flow_table, OFTABLE_LOG_TO_PHY, 100,
- &match, ofpacts_p);
+ &match, 0, ofpacts_p);
} else if (!tun) {
/* Remote port connected by localnet port */
/* Table 33, priority 100.
@@ -532,7 +533,7 @@ consider_port_binding(enum mf_field_id mff_ovn_geneve,
/* Resubmit to table 33. */
put_resubmit(OFTABLE_LOCAL_OUTPUT, ofpacts_p);
ofctrl_add_flow(flow_table, OFTABLE_LOCAL_OUTPUT, 100,
- &match, ofpacts_p);
+ &match, 0, ofpacts_p);
} else {
/* Remote port connected by tunnel */
@@ -557,7 +558,7 @@ consider_port_binding(enum mf_field_id mff_ovn_geneve,
/* Resubmit to table 33. */
put_resubmit(OFTABLE_LOCAL_OUTPUT, ofpacts_p);
- ofctrl_add_flow(flow_table, OFTABLE_REMOTE_OUTPUT, 150, &match,
+ ofctrl_add_flow(flow_table, OFTABLE_REMOTE_OUTPUT, 150, &match, 0,
ofpacts_p);
@@ -574,7 +575,7 @@ consider_port_binding(enum mf_field_id mff_ovn_geneve,
/* Output to tunnel. */
ofpact_put_OUTPUT(ofpacts_p)->port = ofport;
ofctrl_add_flow(flow_table, OFTABLE_REMOTE_OUTPUT, 100,
- &match, ofpacts_p);
+ &match, 0, ofpacts_p);
}
}
@@ -662,7 +663,7 @@ consider_mc_group(enum mf_field_id mff_ovn_geneve,
put_load(mc->tunnel_key, MFF_LOG_OUTPORT, 0, 32, ofpacts_p);
ofctrl_add_flow(flow_table, OFTABLE_LOCAL_OUTPUT, 100,
- &match, ofpacts_p);
+ &match, 0, ofpacts_p);
}
/* Table 32, priority 100.
@@ -700,7 +701,7 @@ consider_mc_group(enum mf_field_id mff_ovn_geneve,
put_resubmit(OFTABLE_LOCAL_OUTPUT, remote_ofpacts_p);
}
ofctrl_add_flow(flow_table, OFTABLE_REMOTE_OUTPUT, 100,
- &match, remote_ofpacts_p);
+ &match, 0, remote_ofpacts_p);
}
}
sset_destroy(&remote_chassis);
@@ -900,7 +901,8 @@ physical_run(struct controller_ctx *ctx, enum mf_field_id mff_ovn_geneve,
put_resubmit(OFTABLE_LOCAL_OUTPUT, &ofpacts);
- ofctrl_add_flow(flow_table, OFTABLE_PHY_TO_LOG, 100, &match, &ofpacts);
+ ofctrl_add_flow(flow_table, OFTABLE_PHY_TO_LOG, 100, &match, 0,
+ &ofpacts);
}
/* Add flows for VXLAN encapsulations. Due to the limited amount of
@@ -932,7 +934,7 @@ physical_run(struct controller_ctx *ctx, enum mf_field_id mff_ovn_geneve,
put_load(1, MFF_LOG_FLAGS, MLF_RCV_FROM_VXLAN_BIT, 1, &ofpacts);
put_resubmit(OFTABLE_LOG_INGRESS_PIPELINE, &ofpacts);
- ofctrl_add_flow(flow_table, OFTABLE_PHY_TO_LOG, 100, &match,
+ ofctrl_add_flow(flow_table, OFTABLE_PHY_TO_LOG, 100, &match, 0,
&ofpacts);
}
}
@@ -946,7 +948,7 @@ physical_run(struct controller_ctx *ctx, enum mf_field_id mff_ovn_geneve,
match_init_catchall(&match);
ofpbuf_clear(&ofpacts);
put_resubmit(OFTABLE_LOCAL_OUTPUT, &ofpacts);
- ofctrl_add_flow(flow_table, OFTABLE_REMOTE_OUTPUT, 0, &match, &ofpacts);
+ ofctrl_add_flow(flow_table, OFTABLE_REMOTE_OUTPUT, 0, &match, 0, &ofpacts);
/* Table 34, Priority 0.
* =======================
@@ -960,7 +962,8 @@ physical_run(struct controller_ctx *ctx, enum mf_field_id mff_ovn_geneve,
put_load(0, MFF_REG0 + i, 0, 32, &ofpacts);
}
put_resubmit(OFTABLE_LOG_EGRESS_PIPELINE, &ofpacts);
- ofctrl_add_flow(flow_table, OFTABLE_CHECK_LOOPBACK, 0, &match, &ofpacts);
+ ofctrl_add_flow(flow_table, OFTABLE_CHECK_LOOPBACK, 0, &match, 0,
+ &ofpacts);
/* Table 64, Priority 0.
* =======================
@@ -970,7 +973,7 @@ physical_run(struct controller_ctx *ctx, enum mf_field_id mff_ovn_geneve,
match_init_catchall(&match);
ofpbuf_clear(&ofpacts);
put_resubmit(OFTABLE_LOG_TO_PHY, &ofpacts);
- ofctrl_add_flow(flow_table, OFTABLE_SAVE_INPORT, 0, &match, &ofpacts);
+ ofctrl_add_flow(flow_table, OFTABLE_SAVE_INPORT, 0, &match, 0, &ofpacts);
ofpbuf_uninit(&ofpacts);
@@ -159,9 +159,18 @@ to unbind logical port that is not bound has no effect.
.
.SS "Logical Flow Commands"
.
-.IP "\fBlflow\-list\fR [\fIlogical-datapath\fR]"
+.IP "\fBlflow\-list\fR [\fIlogical-datapath\fR] [\fIlflow\fR...]"
List logical flows. If \fIlogical-datapath\fR is specified, only list
-flows for that logical datapath.
+flows for that logical datapath. The \fIlogical-datapath\fR may be
+given as a UUID or as a datapath name.
+.IP
+If at least one \fIlflow\fR is given, only matching logical flows, if
+any, are listed. Each \fIlflow\fR may be specified as a UUID or the
+first few characters of a UUID, optionally prefixed by \fB0x\fR.
+(Because \fBovn\-controller\fR sets OpenFlow flow cookies to the first
+32 bits of the corresponding logical flow's UUID, this makes it easy
+to look up the logical flow that generated a particular OpenFlow
+flow.)
.
.IP "\fBdump\-flows\fR [\fIlogical-datapath\fR]"
Alias for \fBlflow\-list\fB.
@@ -306,8 +306,8 @@ Port binding commands:\n\
lsp-unbind PORT reset the port binding of logical port PORT\n\
\n\
Logical flow commands:\n\
- lflow-list [DATAPATH] List logical flows for all or a single datapath\n\
- dump-flows [DATAPATH] Alias for lflow-list\n\
+ lflow-list [DATAPATH] [LFLOW...] List logical flows for DATAPATH\n\
+ dump-flows [DATAPATH] [LFLOW...] Alias for lflow-list\n\
\n\
%s\
\n\
@@ -687,53 +687,138 @@ lflow_cmp(const void *lf1_, const void *lf2_)
return 0;
}
+static char *
+parse_partial_uuid(char *s)
+{
+ /* Accept a full or partial UUID. */
+ if (uuid_is_partial_string(s) == strlen(s)) {
+ return s;
+ }
+
+ /* Accept a full or partial UUID prefixed by 0x, since "ovs-ofctl
+ * dump-flows" prints cookies prefixed by 0x. */
+ if (s[0] == '0' && (s[1] == 'x' || s[1] == 'X')
+ && uuid_is_partial_string(s + 2) == strlen(s + 2)) {
+ return s + 2;
+ }
+
+ /* Not a (partial) UUID. */
+ return NULL;
+}
+
+static bool
+is_partial_uuid_match(const struct uuid *uuid, const char *match)
+{
+ char uuid_s[UUID_LEN + 1];
+ snprintf(uuid_s, sizeof uuid_s, UUID_FMT, UUID_ARGS(uuid));
+
+ return !strncmp(uuid_s, match, strlen(match));
+}
+
+static const struct sbrec_datapath_binding *
+lookup_datapath(struct ovsdb_idl *idl, const char *s)
+{
+ const struct sbrec_datapath_binding *datapath;
+
+ struct uuid uuid;
+ if (uuid_from_string(&uuid, s)) {
+ datapath = sbrec_datapath_binding_get_for_uuid(idl, &uuid);
+ if (datapath) {
+ return datapath;
+ }
+ }
+
+ SBREC_DATAPATH_BINDING_FOR_EACH (datapath, idl) {
+ const char *name = smap_get(&datapath->external_ids, "name");
+ if (name && !strcmp(name, s)) {
+ return datapath;
+ }
+ }
+
+ return NULL;
+}
+
static void
cmd_lflow_list(struct ctl_context *ctx)
{
- const char *datapath = ctx->argc == 2 ? ctx->argv[1] : NULL;
- struct uuid datapath_uuid = { .parts = { 0, }};
- const struct sbrec_logical_flow **lflows;
- const struct sbrec_logical_flow *lflow;
- size_t n_flows = 0, n_capacity = 64;
+ const struct sbrec_datapath_binding *datapath = NULL;
+ if (ctx->argc > 1) {
+ datapath = lookup_datapath(ctx->idl, ctx->argv[1]);
+ if (datapath) {
+ ctx->argc--;
+ ctx->argv++;
+ }
+ }
- if (datapath && !uuid_from_string(&datapath_uuid, datapath)) {
- VLOG_ERR("Invalid format of datapath UUID");
- return;
+ for (size_t i = 1; i < ctx->argc; i++) {
+ char *s = parse_partial_uuid(ctx->argv[i]);
+ if (!s) {
+ ctl_fatal("%s is not a UUID or the beginning of a UUID",
+ ctx->argv[i]);
+ }
+ ctx->argv[i] = s;
}
- lflows = xmalloc(sizeof *lflows * n_capacity);
+ const struct sbrec_logical_flow **lflows = NULL;
+ size_t n_flows = 0;
+ size_t n_capacity = 0;
+ const struct sbrec_logical_flow *lflow;
SBREC_LOGICAL_FLOW_FOR_EACH (lflow, ctx->idl) {
+ if (datapath && lflow->logical_datapath != datapath) {
+ continue;
+ }
+
if (n_flows == n_capacity) {
lflows = x2nrealloc(lflows, &n_capacity, sizeof *lflows);
}
lflows[n_flows] = lflow;
n_flows++;
}
-
qsort(lflows, n_flows, sizeof *lflows, lflow_cmp);
- const char *cur_pipeline = "";
- size_t i;
- for (i = 0; i < n_flows; i++) {
+ const struct sbrec_logical_flow *prev = NULL;
+ for (size_t i = 0; i < n_flows; i++) {
lflow = lflows[i];
- if (datapath && !uuid_equals(&datapath_uuid,
- &lflow->logical_datapath->header_.uuid)) {
+
+ /* Figure out whether to print this particular flow. By default, we
+ * print all flows, but if any UUIDs were listed on the command line
+ * then we only print the matching ones. */
+ bool include;
+ if (ctx->argc > 1) {
+ include = false;
+ for (size_t j = 1; j < ctx->argc; j++) {
+ if (is_partial_uuid_match(&lflow->header_.uuid,
+ ctx->argv[j])) {
+ include = true;
+ break;
+ }
+ }
+ } else {
+ include = true;
+ }
+ if (!include) {
continue;
}
- if (strcmp(cur_pipeline, lflow->pipeline)) {
+
+ /* Print a header line for this datapath or pipeline, if we haven't
+ * already done so. */
+ if (!prev
+ || prev->logical_datapath != lflow->logical_datapath
+ || strcmp(prev->pipeline, lflow->pipeline)) {
printf("Datapath: \"%s\" ("UUID_FMT") Pipeline: %s\n",
smap_get_def(&lflow->logical_datapath->external_ids,
"name", ""),
UUID_ARGS(&lflow->logical_datapath->header_.uuid),
lflow->pipeline);
- cur_pipeline = lflow->pipeline;
}
+ /* Print the flow. */
printf(" table=%-2" PRId64 "(%-19s), priority=%-5" PRId64
", match=(%s), action=(%s)\n",
lflow->table_id,
smap_get_def(&lflow->external_ids, "stage-name", ""),
lflow->priority, lflow->match, lflow->actions);
+ prev = lflow;
}
free(lflows);
@@ -1039,9 +1124,11 @@ static const struct ctl_command_syntax sbctl_commands[] = {
"--if-exists", RW},
/* Logical flow commands */
- {"lflow-list", 0, 1, "[DATAPATH]", pre_get_info, cmd_lflow_list, NULL,
+ {"lflow-list", 0, INT_MAX, "[DATAPATH] [LFLOW...]",
+ pre_get_info, cmd_lflow_list, NULL,
"", RO},
- {"dump-flows", 0, 1, "[DATAPATH]", pre_get_info, cmd_lflow_list, NULL,
+ {"dump-flows", 0, INT_MAX, "[DATAPATH] [LFLOW...]",
+ pre_get_info, cmd_lflow_list, NULL,
"", RO}, /* Friendly alias for lflow-list */
/* SSL commands (To Be Added). */
This makes it easy to find the logical flow that generated a particular OpenFlow flow, by running "ovn-sbctl dump-flows <cookie>". Later, this can be refined (and automated for "ofproto/trace"), but this is still a significant advance. Signed-off-by: Ben Pfaff <blp@ovn.org> --- lib/uuid.c | 22 +++++++- lib/uuid.h | 3 +- ovn/controller/lflow.c | 6 +- ovn/controller/ofctrl.c | 6 +- ovn/controller/ofctrl.h | 2 +- ovn/controller/physical.c | 37 +++++++------ ovn/utilities/ovn-sbctl.8.in | 13 ++++- ovn/utilities/ovn-sbctl.c | 129 ++++++++++++++++++++++++++++++++++++------- 8 files changed, 171 insertions(+), 47 deletions(-)