+static void
+put_ct_mark(const struct flow *flow, struct flow *base_flow,
+ struct ofpbuf *odp_actions, struct flow_wildcards *wc)
+{
+ struct {
+ uint32_t key;
+ uint32_t mask;
+ } odp_attr;
+
+ odp_attr.key = flow->ct_mark;
+ odp_attr.mask = wc->masks.ct_mark;
+
+ if (odp_attr.mask && odp_attr.key != base_flow->ct_mark) {
+ nl_msg_put_unspec(odp_actions, OVS_CT_ATTR_MARK, &odp_attr,
+ sizeof(odp_attr));
+ }
+}
+
+static void
+put_ct_label(const struct flow *flow, struct flow *base_flow,
+ struct ofpbuf *odp_actions, struct flow_wildcards *wc)
+{
+ if (!ovs_u128_is_zero(&wc->masks.ct_label)
+ && !ovs_u128_equals(&flow->ct_label, &base_flow->ct_label)) {
+ struct {
+ ovs_u128 key;
+ ovs_u128 mask;
+ } *odp_ct_label;
+
+ odp_ct_label = nl_msg_put_unspec_uninit(odp_actions,
+ OVS_CT_ATTR_LABELS,
+ sizeof(*odp_ct_label));
+ odp_ct_label->key = flow->ct_label;
+ odp_ct_label->mask = wc->masks.ct_label;
+ }
+}
+
+static void
+put_ct_helper(struct ofpbuf *odp_actions, struct ofpact_conntrack *ofc)
+{
+ if (ofc->alg) {
+ if (ofc->alg == IPPORT_FTP) {
+ nl_msg_put_string(odp_actions, OVS_CT_ATTR_HELPER, "ftp");
+ } else {
+ VLOG_WARN("Cannot serialize ct_helper %d\n", ofc->alg);
+ }
+ }
+}
+
+static void
+compose_conntrack_action(struct xlate_ctx *ctx, struct ofpact_conntrack *ofc)
+{
+ ovs_u128 old_ct_label = ctx->base_flow.ct_label;
+ uint32_t old_ct_mark = ctx->base_flow.ct_mark;
+ size_t ct_offset;
+ uint16_t zone;
+
+ /* Ensure that any prior actions are applied before composing the new
+ * conntrack action. */
+ xlate_commit_actions(ctx);
+
+ /* Process nested actions first, to populate the key. */
+ do_xlate_actions(ofc->actions, ofpact_ct_get_action_len(ofc), ctx);
+
+ if (ofc->zone_src.field) {
+ zone = mf_get_subfield(&ofc->zone_src, &ctx->xin->flow);
+ } else {
+ zone = ofc->zone_imm;
+ }
+
+ ct_offset = nl_msg_start_nested(ctx->odp_actions, OVS_ACTION_ATTR_CT);
+ if (ofc->flags & NX_CT_F_COMMIT) {
+ nl_msg_put_flag(ctx->odp_actions, OVS_CT_ATTR_COMMIT);
+ }
+ nl_msg_put_u16(ctx->odp_actions, OVS_CT_ATTR_ZONE, zone);
+ put_ct_mark(&ctx->xin->flow, &ctx->base_flow, ctx->odp_actions, ctx->wc);
+ put_ct_label(&ctx->xin->flow, &ctx->base_flow, ctx->odp_actions, ctx->wc);
+ put_ct_helper(ctx->odp_actions, ofc);
+ nl_msg_end_nested(ctx->odp_actions, ct_offset);
+
+ /* Restore the original ct fields in the key. These should only be exposed
+ * after recirculation to another table. */
+ ctx->base_flow.ct_mark = old_ct_mark;
+ ctx->base_flow.ct_label = old_ct_label;
+
+ if (ofc->recirc_table == NX_CT_RECIRC_NONE) {
+ /* If we do not recirculate as part of this action, hide the results of
+ * connection tracking from subsequent recirculations. */
+ ctx->conntracked = false;
+ } else {
+ /* Use ct_* fields from datapath during recirculation upcall. */
+ ctx->conntracked = true;
+ ctx_trigger_recirculation(ctx);
+ compose_recirculate_action__(ctx, ofc->recirc_table);
+ }
+}
+