diff --git a/NEWS b/NEWS
index eb825ac72..f0ada38b4 100644
--- a/NEWS
+++ b/NEWS
@@ -19,6 +19,7 @@ Post-v2.7.0
* Gratuitous ARP for NAT addresses on a distributed logical router.
* Allow ovn-controller SSL configuration to be obtained from vswitchd
database.
+ * ovn-trace now has basic support for tracing distributed firewalls.
- Add the command 'ovs-appctl stp/show' (see ovs-vswitchd(8)).
- OpenFlow:
* Increased support for OpenFlow 1.6 (draft).
diff --git a/ovn/utilities/ovn-trace.8.xml b/ovn/utilities/ovn-trace.8.xml
index 78914240d..8bb329bfb 100644
--- a/ovn/utilities/ovn-trace.8.xml
+++ b/ovn/utilities/ovn-trace.8.xml
@@ -307,6 +307,64 @@
+
+
--ct=flags
+
+
+ This option sets the ct_state
flags that a
+ ct_next
logical action will report. The flags
+ must be a comma- or space-separated list of the following connection
+ tracking flags:
+
+
+
+ -
+
trk
: Include to indicate connection tracking has taken
+ place. (This bit is set automatically even if not listed in
+ flags.
+
+ new
: Include to indicate a new flow.
+ est
: Include to indicate an established flow.
+ rel
: Include to indicate a related flow.
+ rpl
: Include to indicate a reply flow.
+ inv
: Include to indicate a connection entry in a
+ bad state.
+ dnat
: Include to indicate a packet whose
+ destination IP address has been changed.
+ snat
: Include to indicate a packet whose source IP
+ address has been changed.
+
+
+
+ The ct_next
action is used to implement the OVN
+ distributed firewall. For testing, useful flag combinations include:
+
+
+
+ trk,new
: A packet in a flow in either direction
+ through a firewall that has not yet been committed (with
+ ct_commit
).
+ trk,est
: A packet in an established flow going out
+ through a firewall.
+ trk,rpl
: A packet coming in through a firewall in
+ reply to an established flow.
+ trk,inv
: An invalid packet in either direction.
+
+
+
+ A packet might pass through the connection tracker twice in one trip
+ through OVN: once following egress from a VM as it passes outward
+ through a firewall, and once preceding ingress to a second VM as it
+ passes inward through a firewall. Use multiple --ct
+ options to specify the flags for multiple ct_next
actions.
+
+
+
+ When --ct
is unspecified, or when there are fewer
+ --ct
options than ct_next
actions, the
+ flags default to trk,est
.
+
+
Daemon Options
diff --git a/ovn/utilities/ovn-trace.c b/ovn/utilities/ovn-trace.c
index 66844b11a..e9463f02a 100644
--- a/ovn/utilities/ovn-trace.c
+++ b/ovn/utilities/ovn-trace.c
@@ -69,6 +69,11 @@ static bool minimal;
static const char *ovs;
static struct vconn *vconn;
+/* --ct: Connection tracking state to use for ct_next() actions. */
+static uint32_t *ct_states;
+static size_t n_ct_states;
+static size_t ct_state_idx;
+
OVS_NO_RETURN static void usage(void);
static void parse_options(int argc, char *argv[]);
static char *trace(const char *datapath, const char *flow);
@@ -155,6 +160,42 @@ default_ovs(void)
return xasprintf("unix:%s/br-int.mgmt", ovs_rundir());
}
+static void
+parse_ct_option(const char *state_s_)
+{
+ uint32_t state = CS_TRACKED;
+
+ char *state_s = xstrdup(state_s_);
+ char *save_ptr = NULL;
+ for (char *cs = strtok_r(state_s, ", ", &save_ptr); cs;
+ cs = strtok_r(NULL, ", ", &save_ptr)) {
+ uint32_t bit = ct_state_from_string(cs);
+ if (!bit) {
+ ovs_fatal(0, "%s: unknown connection tracking state flag", cs);
+ }
+ state |= bit;
+ }
+ free(state_s);
+
+ /* Check constraints listed in ovs-fields(7). */
+ if (state & CS_INVALID && state & ~(CS_TRACKED | CS_INVALID)) {
+ VLOG_WARN("%s: invalid connection state: "
+ "when \"inv\" is set, only \"trk\" may also be set",
+ state_s_);
+ }
+ if (state & CS_NEW && state & CS_ESTABLISHED) {
+ VLOG_WARN("%s: invalid connection state: "
+ "\"new\" and \"est\" are mutually exclusive", state_s_);
+ }
+ if (state & CS_NEW && state & CS_REPLY_DIR) {
+ VLOG_WARN("%s: invalid connection state: "
+ "\"new\" and \"rpy\" are mutually exclusive", state_s_);
+ }
+
+ ct_states = xrealloc(ct_states, (n_ct_states + 1) * sizeof *ct_states);
+ ct_states[n_ct_states++] = state;
+}
+
static void
parse_options(int argc, char *argv[])
{
@@ -166,6 +207,7 @@ parse_options(int argc, char *argv[])
OPT_MINIMAL,
OPT_ALL,
OPT_OVS,
+ OPT_CT,
DAEMON_OPTION_ENUMS,
SSL_OPTION_ENUMS,
VLOG_OPTION_ENUMS
@@ -178,6 +220,7 @@ parse_options(int argc, char *argv[])
{"minimal", no_argument, NULL, OPT_MINIMAL},
{"all", no_argument, NULL, OPT_ALL},
{"ovs", optional_argument, NULL, OPT_OVS},
+ {"ct", required_argument, NULL, OPT_CT},
{"help", no_argument, NULL, 'h'},
{"version", no_argument, NULL, 'V'},
DAEMON_LONG_OPTIONS,
@@ -225,6 +268,10 @@ parse_options(int argc, char *argv[])
ovs = optarg ? optarg : default_ovs();
break;
+ case OPT_CT:
+ parse_ct_option(optarg);
+ break;
+
case 'h':
usage();
@@ -1428,6 +1475,40 @@ execute_next(const struct ovnact_next *next,
trace__(dp, uflow, next->ltable, next->pipeline, super);
}
+static void
+execute_ct_next(const struct ovnact_ct_next *ct_next,
+ const struct ovntrace_datapath *dp, struct flow *uflow,
+ enum ovnact_pipeline pipeline, struct ovs_list *super)
+{
+ /* Figure out ct_state. */
+ uint32_t state;
+ const char *comment;
+ if (ct_state_idx < n_ct_states) {
+ state = ct_states[ct_state_idx++];
+ comment = "";
+ } else {
+ state = CS_ESTABLISHED | CS_TRACKED;
+ comment = " /* default (use --ct to customize) */";
+ }
+
+ /* Make a sub-node for attaching the next table. */
+ struct ds s = DS_EMPTY_INITIALIZER;
+ format_flags(&s, ct_state_to_string, state, '|');
+ struct ovntrace_node *node = ovntrace_node_append(
+ super, OVNTRACE_NODE_TRANSFORMATION, "ct_next(ct_state=%s%s)",
+ ds_cstr(&s), comment);
+ ds_destroy(&s);
+
+ /* Trace the actions in the next table. */
+ struct flow ct_flow = *uflow;
+ ct_flow.ct_state = state;
+ trace__(dp, &ct_flow, ct_next->ltable, pipeline, &node->subs);
+
+ /* Upon return, we will trace the actions following the ct action in the
+ * original table. The pipeline forked, so we're using the original
+ * flow, not ct_flow. */
+}
+
static void
trace_actions(const struct ovnact *ovnacts, size_t ovnacts_len,
const struct ovntrace_datapath *dp, struct flow *uflow,
@@ -1484,13 +1565,27 @@ trace_actions(const struct ovnact *ovnacts, size_t ovnacts_len,
break;
case OVNACT_CT_NEXT:
+ execute_ct_next(ovnact_get_CT_NEXT(a), dp, uflow, pipeline, super);
+ break;
+
case OVNACT_CT_COMMIT:
+ /* Nothing to do. */
+ break;
+
case OVNACT_CT_DNAT:
case OVNACT_CT_SNAT:
- case OVNACT_CT_LB:
- case OVNACT_CT_CLEAR:
ovntrace_node_append(super, OVNTRACE_NODE_ERROR,
- "*** ct_* actions not implemented");
+ "*** ct_dnat and ct_snat actions "
+ "not implemented");
+ break;
+
+ case OVNACT_CT_LB:
+ ovntrace_node_append(super, OVNTRACE_NODE_ERROR,
+ "*** ct_lb action not implemented");
+ break;
+
+ case OVNACT_CT_CLEAR:
+ flow_clear_conntrack(uflow);
break;
case OVNACT_CLONE: