@@ -74,6 +74,7 @@ struct ovn_extend_table;
OVNACT(CT_LB_MARK, ovnact_ct_lb) \
OVNACT(SELECT, ovnact_select) \
OVNACT(CT_CLEAR, ovnact_null) \
+ OVNACT(CT_COMMIT_NAT, ovnact_ct_nat) \
OVNACT(CLONE, ovnact_nest) \
OVNACT(ARP, ovnact_nest) \
OVNACT(ICMP4, ovnact_nest) \
@@ -275,6 +276,8 @@ struct ovnact_ct_nat {
uint16_t port_hi;
} port_range;
+ bool commit; /* Explicit commit action. */
+
uint8_t ltable; /* Logical table ID of next table. */
};
@@ -920,6 +920,7 @@ parse_ct_nat(struct action_context *ctx, const char *name,
return;
}
cn->ltable = ctx->pp->cur_ltable + 1;
+ cn->commit = false;
if (lexer_match(ctx->lexer, LEX_T_LPAREN)) {
if (ctx->lexer->token.type != LEX_T_INTEGER
@@ -929,9 +930,11 @@ parse_ct_nat(struct action_context *ctx, const char *name,
return;
}
if (ctx->lexer->token.format == LEX_F_IPV4) {
+ cn->commit = true;
cn->family = AF_INET;
cn->ipv4 = ctx->lexer->token.value.ipv4;
} else if (ctx->lexer->token.format == LEX_F_IPV6) {
+ cn->commit = true;
cn->family = AF_INET6;
cn->ipv6 = ctx->lexer->token.value.ipv6;
}
@@ -1004,6 +1007,24 @@ parse_CT_SNAT_IN_CZONE(struct action_context *ctx)
ovnact_put_CT_SNAT_IN_CZONE(ctx->ovnacts));
}
+static void
+parse_CT_COMMIT_NAT(struct action_context *ctx)
+{
+ add_prerequisite(ctx, "ip");
+
+ if (ctx->pp->cur_ltable >= ctx->pp->n_tables) {
+ lexer_error(ctx->lexer,
+ "\"ct_commit_related\" action not allowed in last table.");
+ return;
+ }
+
+ struct ovnact_ct_nat *cn = ovnact_put_CT_COMMIT_NAT(ctx->ovnacts);
+ cn->commit = true;
+ cn->ltable = ctx->pp->cur_ltable + 1;
+ cn->family = AF_UNSPEC;
+ cn->port_range.exists = false;
+}
+
static void
format_ct_nat(const struct ovnact_ct_nat *cn, const char *name, struct ds *s)
{
@@ -1053,6 +1074,12 @@ format_CT_SNAT_IN_CZONE(const struct ovnact_ct_nat *cn, struct ds *s)
format_ct_nat(cn, "ct_snat_in_czone", s);
}
+static void
+format_CT_COMMIT_NAT(const struct ovnact_ct_nat *cn OVS_UNUSED, struct ds *s)
+{
+ ds_put_cstr(s, "ct_commit_nat;");
+}
+
static void
encode_ct_nat(const struct ovnact_ct_nat *cn,
const struct ovnact_encode_params *ep,
@@ -1104,7 +1131,7 @@ encode_ct_nat(const struct ovnact_ct_nat *cn,
ofpacts->header = ofpbuf_push_uninit(ofpacts, nat_offset);
ct = ofpacts->header;
- if (cn->family == AF_INET || cn->family == AF_INET6) {
+ if (cn->commit) {
ct->flags |= NX_CT_F_COMMIT;
}
ofpact_finish(ofpacts, &ct->ofpact);
@@ -1143,6 +1170,17 @@ encode_CT_SNAT_IN_CZONE(const struct ovnact_ct_nat *cn,
encode_ct_nat(cn, ep, true, ep->common_nat_ct_zone, ofpacts);
}
+static void
+encode_CT_COMMIT_NAT(const struct ovnact_ct_nat *cn,
+ const struct ovnact_encode_params *ep,
+ struct ofpbuf *ofpacts)
+{
+ enum mf_field_id zone = ep->is_switch
+ ? MFF_LOG_CT_ZONE
+ : MFF_LOG_DNAT_ZONE;
+ encode_ct_nat(cn, ep, false, zone, ofpacts);
+}
+
static void
ovnact_ct_nat_free(struct ovnact_ct_nat *ct_nat OVS_UNUSED)
{
@@ -4732,6 +4770,8 @@ parse_action(struct action_context *ctx)
parse_ct_lb_action(ctx, true);
} else if (lexer_match_id(ctx->lexer, "ct_clear")) {
ovnact_put_CT_CLEAR(ctx->ovnacts);
+ } else if (lexer_match_id(ctx->lexer, "ct_commit_nat")) {
+ parse_CT_COMMIT_NAT(ctx);
} else if (lexer_match_id(ctx->lexer, "clone")) {
parse_CLONE(ctx);
} else if (lexer_match_id(ctx->lexer, "arp")) {
@@ -1469,6 +1469,18 @@
Clears connection tracking state.
</dd>
+ <dt><code>ct_commit_nat;</code></dt>
+ <dd>
+ <p>
+ Applies NAT and commits the connection to the CT. Automatically
+ moves on to the next table, as if followed by
+ <code>next</code>.
+ This is very useful for connections that are in related state for
+ already existing connections and allows the NAT to be applied
+ to them as well.
+ </p>
+ </dd>
+
<dt><code>clone { <var>action</var>; </code>...<code> };</code></dt>
<dd>
Makes a copy of the packet being processed and executes each
@@ -1418,6 +1418,11 @@ ct_snat_in_czone(192.168.1.2, 1000-100);
ct_clear;
encodes as ct_clear
+# ct_commit_nat
+ct_commit_nat;
+ encodes as ct(commit,table=19,zone=NXM_NX_REG13[0..15],nat)
+ has prereqs ip
+
# clone
clone { ip4.dst = 255.255.255.255; output; }; next;
encodes as clone(set_field:255.255.255.255->ip_dst,resubmit(,64)),resubmit(,19)
@@ -2438,6 +2438,35 @@ execute_ct_nat(const struct ovnact_ct_nat *ct_nat,
* flow, not ct_flow. */
}
+static void
+execute_ct_commit_nat(const struct ovnact_ct_nat *ct_nat,
+ const struct ovntrace_datapath *dp, struct flow *uflow,
+ enum ovnact_pipeline pipeline, struct ovs_list *super)
+{
+ struct flow ct_flow = *uflow;
+ struct ds s = DS_EMPTY_INITIALIZER;
+
+ ds_put_cstr(&s, "ct_commit_nat /* assuming no"
+ " un-nat entry, so no change */");
+
+ /* ct(nat) implies ct(). */
+ if (!(ct_flow.ct_state & CS_TRACKED)) {
+ ct_flow.ct_state |= next_ct_state(&s);
+ }
+
+ 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
execute_ct_lb(const struct ovnact_ct_lb *ct_lb,
const struct ovntrace_datapath *dp, struct flow *uflow,
@@ -3095,6 +3124,11 @@ trace_actions(const struct ovnact *ovnacts, size_t ovnacts_len,
flow_clear_conntrack(uflow);
break;
+ case OVNACT_CT_COMMIT_NAT:
+ execute_ct_commit_nat(ovnact_get_CT_COMMIT_NAT(a), dp, uflow,
+ pipeline, super);
+ break;
+
case OVNACT_CLONE:
execute_clone(ovnact_get_CLONE(a), dp, uflow, table_id, pipeline,
super);