diff --git a/ovn/utilities/ovn-trace.8.xml b/ovn/utilities/ovn-trace.8.xml
index 8bb329bfbd71..b2d46ac3d50b 100644
--- a/ovn/utilities/ovn-trace.8.xml
+++ b/ovn/utilities/ovn-trace.8.xml
@@ -166,6 +166,56 @@
     output;
   </pre>
 
+  <h2>Stateful Actions</h2>
+
+  <p>
+    Some OVN logical actions use or update state that is not available in the
+    southbound database.  <code>ovn-trace</code> handles these actions as
+    described below:
+  </p>
+
+  <dl>
+    <dt>ct_next</dt>
+    <dd>
+      By default <code>ovn-trace</code> treats flows as ``tracked'' and
+      ``established.''  The <code>--ct</code> option overrides this behavior;
+      refer to its description for more information.
+    </dd>
+
+    <dt>ct_commit</dt>
+    <dd>
+      This action is treated as a no-op.
+    </dd>
+
+    <dt>ct_dnat</dt>
+    <dt>ct_snat</dt>
+    <dd>
+      <p>
+        When one of these action is used without arguments, to ``un-NAT'' a
+        packet, <code>ovn-trace</code> assumes that no NAT state was available
+        and treats it as a no-op.
+      </p>
+
+      <p>
+        With an argument, <code>ovn-trace</code> sets the IP destination or
+        source, as appropriate, to the given address. It also sets
+        <code>ct.dnat</code> or <code>ct.snat</code> to 1 to indicate that NAT
+        has taken place.
+      </p>
+    </dd>
+
+    <dt>ct_lb</dt>
+    <dd>
+      Not yet implemented; currently implemented as a no-op.
+    </dd>
+
+    <dt>put_arp</dt>
+    <dt>put_nd</dt>
+    <dd>
+      This action is treated as a no-op.
+    </dd>
+  </dl>
+
   <h2>Summary Output</h2>
 
   <p>
diff --git a/ovn/utilities/ovn-trace.c b/ovn/utilities/ovn-trace.c
index 647c8a51cd0d..4346fb39268a 100644
--- a/ovn/utilities/ovn-trace.c
+++ b/ovn/utilities/ovn-trace.c
@@ -1510,6 +1510,42 @@ execute_ct_next(const struct ovnact_ct_next *ct_next,
 }
 
 static void
+execute_ct_nat(const struct ovnact_ct_nat *ct_nat,
+               const struct ovntrace_datapath *dp, struct flow *uflow,
+               enum ovnact_pipeline pipeline, struct ovs_list *super)
+{
+    bool is_dst = ct_nat->ovnact.type == OVNACT_CT_DNAT;
+    const char *direction = is_dst ? "dst" : "src";
+
+    /* Make a sub-node for attaching the next table,
+     * and figure out the changes if any. */
+    struct flow ct_flow = *uflow;
+    struct ds s = DS_EMPTY_INITIALIZER;
+    ds_put_format(&s, "ct_%cnat", direction[0]);
+    if (ct_nat->ip) {
+        ds_put_format(&s, "(ip4.%s="IP_FMT")", direction, IP_ARGS(ct_nat->ip));
+        ovs_be32 *ip = is_dst ? &ct_flow.nw_dst : &ct_flow.nw_src;
+        *ip = ct_nat->ip;
+
+        uint8_t state = is_dst ? CS_DST_NAT : CS_SRC_NAT;
+        ct_flow.ct_state |= state;
+    } else {
+        ds_put_format(&s, " /* assuming no un-%cnat entry, so no change */",
+                      direction[0]);
+    }
+    struct ovntrace_node *node = ovntrace_node_append(
+        super, OVNTRACE_NODE_TRANSFORMATION, "%s", ds_cstr(&s));
+    ds_destroy(&s);
+
+    /* Trace the actions in the next table. */
+    trace__(dp, &ct_flow, ct_nat->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,
               uint8_t table_id, enum ovnact_pipeline pipeline,
@@ -1573,10 +1609,11 @@ trace_actions(const struct ovnact *ovnacts, size_t ovnacts_len,
             break;
 
         case OVNACT_CT_DNAT:
+            execute_ct_nat(ovnact_get_CT_DNAT(a), dp, uflow, pipeline, super);
+            break;
+
         case OVNACT_CT_SNAT:
-            ovntrace_node_append(super, OVNTRACE_NODE_ERROR,
-                                 "*** ct_dnat and ct_snat actions "
-                                 "not implemented");
+            execute_ct_nat(ovnact_get_CT_SNAT(a), dp, uflow, pipeline, super);
             break;
 
         case OVNACT_CT_LB:
