On Mon, Apr 14, 2025 at 10:52 AM Dumitru Ceara <dce...@redhat.com> wrote:
> The new action stores the 8 relevant bits of the OVS ct_state field into > a register. This is implemented through an additional OpenFlow table, > OFTABLE_CT_STATE_SAVE, that contains two (static) rules: > - high-priority, set the target register to 0 if ct_state -trk is set > - low-priority, copy the 8 bits ct_state to the target register > otherwise > > From a logical flow perspective, the action is supposed to be used in > the following way: > R = ct_state_save(); > > Signed-off-by: Dumitru Ceara <dce...@redhat.com> > --- > V2: > - Addressed Ales' comment: > - properly set the saved ct state register if ct_state includes +trk > - Fixed up output of ovn-trace for ct_state_save() to include the actual > value that gets copied to the destination register. > --- > controller/lflow.c | 1 + > controller/lflow.h | 1 + > controller/physical.c | 23 +++++++++++++++++++++++ > include/ovn/actions.h | 3 +++ > include/ovn/logical-fields.h | 2 ++ > lib/actions.c | 27 +++++++++++++++++++++++++++ > lib/ovn-util.c | 2 +- > ovn-sb.xml | 8 ++++++++ > tests/ovn-macros.at | 1 + > tests/ovn.at | 28 ++++++++++++++++++++++++++-- > tests/test-ovn.c | 1 + > utilities/ovn-trace.c | 21 +++++++++++++++++++++ > 12 files changed, 115 insertions(+), 3 deletions(-) > > diff --git a/controller/lflow.c b/controller/lflow.c > index 196f6a21ac..c24b2e2127 100644 > --- a/controller/lflow.c > +++ b/controller/lflow.c > @@ -893,6 +893,7 @@ add_matches_to_flow_table(const struct > sbrec_logical_flow *lflow, > .ct_ip6_dst_load_table = OFTABLE_CT_ORIG_IP6_DST_LOAD, > .ct_tp_dst_load_table = OFTABLE_CT_ORIG_TP_DST_LOAD, > .flood_remote_table = OFTABLE_FLOOD_REMOTE_CHASSIS, > + .ct_state_save_table = OFTABLE_CT_STATE_SAVE, > .ctrl_meter_id = ctrl_meter_id, > .common_nat_ct_zone = get_common_nat_zone(ldp), > }; > diff --git a/controller/lflow.h b/controller/lflow.h > index fa99173e67..3fd7ca99f5 100644 > --- a/controller/lflow.h > +++ b/controller/lflow.h > @@ -99,6 +99,7 @@ struct uuid; > #define OFTABLE_CT_ORIG_IP6_DST_LOAD 82 > #define OFTABLE_CT_ORIG_TP_DST_LOAD 83 > #define OFTABLE_FLOOD_REMOTE_CHASSIS 84 > +#define OFTABLE_CT_STATE_SAVE 85 > > /* Common defines shared between some controller components. */ > #define CHASSIS_FLOOD_INDEX_START 0x8000 > diff --git a/controller/physical.c b/controller/physical.c > index 9e797b3b3c..b4f91b134d 100644 > --- a/controller/physical.c > +++ b/controller/physical.c > @@ -3085,6 +3085,29 @@ physical_run(struct physical_ctx *p_ctx, > ofctrl_add_flow(flow_table, OFTABLE_CT_ORIG_IP6_DST_LOAD, 100, 0, > &match, > &ofpacts, hc_uuid); > > + /* Implement the ct_state_save() logical action. */ > + > + /* Add the flow: > + * match = (1), action = (reg4[0..7] = ct_state[0..7]) > + * table = 85, priority = UINT16_MAX - 1 > + */ > + match_init_catchall(&match); > + ofpbuf_clear(&ofpacts); > + put_move(MFF_CT_STATE, 0, MFF_LOG_CT_SAVED_STATE, 0, 8, &ofpacts); > + ofctrl_add_flow(flow_table, OFTABLE_CT_STATE_SAVE, UINT16_MAX - 1, 0, > + &match, &ofpacts, hc_uuid); > + > + /* Add the flow: > + * match = (!ct.trk), action = (reg4[0..7] = 0) > + * table = 85, priority = UINT16_MAX > + */ > + match_init_catchall(&match); > + ofpbuf_clear(&ofpacts); > + match_set_ct_state_masked(&match, 0, OVS_CS_F_TRACKED); > + put_load(0, MFF_LOG_CT_SAVED_STATE, 0, 8, &ofpacts); > + ofctrl_add_flow(flow_table, OFTABLE_CT_STATE_SAVE, UINT16_MAX, 0, > + &match, &ofpacts, hc_uuid); > + > physical_eval_remote_chassis_flows(p_ctx, &ofpacts, flow_table); > > ofpbuf_uninit(&ofpacts); > diff --git a/include/ovn/actions.h b/include/ovn/actions.h > index 0993ad0e5d..7fd7b26bfe 100644 > --- a/include/ovn/actions.h > +++ b/include/ovn/actions.h > @@ -135,6 +135,7 @@ struct collector_set_ids; > OVNACT(CT_ORIG_IP6_DST, ovnact_result) \ > OVNACT(CT_ORIG_TP_DST, ovnact_result) \ > OVNACT(FLOOD_REMOTE, ovnact_null) \ > + OVNACT(CT_STATE_SAVE, ovnact_result) > > /* enum ovnact_type, with a member OVNACT_<ENUM> for each action. */ > enum OVS_PACKED_ENUM ovnact_type { > @@ -964,6 +965,8 @@ struct ovnact_encode_params { > * to resubmit. */ > uint32_t flood_remote_table; /* OpenFlow table for 'chassis_flood' > * to resubmit. */ > + uint32_t ct_state_save_table; /* OpenFlow table for saving ct_state to > + * register. */ > }; > > void ovnacts_encode(const struct ovnact[], size_t ovnacts_len, > diff --git a/include/ovn/logical-fields.h b/include/ovn/logical-fields.h > index 7c6be6ea42..17154d38bd 100644 > --- a/include/ovn/logical-fields.h > +++ b/include/ovn/logical-fields.h > @@ -63,6 +63,8 @@ enum ovn_controller_event { > #define MFF_LOG_CT_ORIG_IP6_DST_ADDR MFF_XXREG1 /* > REG_ORIG_DIP_IPV6 */ > #define MFF_LOG_CT_ORIG_TP_DST_PORT MFF_REG2 /* > REG_ORIG_TP_DPORT > * (bits 0..15). */ > +#define MFF_LOG_CT_SAVED_STATE MFF_REG4 /* REG_CT_STATE > + * (bits 0..8). */ > > /* Logical registers that are needed for backwards > * compatibility with older northd versions. > diff --git a/lib/actions.c b/lib/actions.c > index b81061f5c1..f6f413be27 100644 > --- a/lib/actions.c > +++ b/lib/actions.c > @@ -5552,6 +5552,29 @@ encode_FLOOD_REMOTE(const struct ovnact_null *null > OVS_UNUSED, > emit_resubmit(ofpacts, ep->flood_remote_table); > } > > +static void > +encode_CT_STATE_SAVE(const struct ovnact_result *res, > + const struct ovnact_encode_params *ep OVS_UNUSED, > + struct ofpbuf *ofpacts) > +{ > + encode_result_action___(res, ep->ct_state_save_table, > + MFF_LOG_CT_SAVED_STATE, 0, 8, ofpacts); > +} > + > +static void > +parse_CT_STATE_SAVE(struct action_context *ctx, const struct expr_field > *dst, > + struct ovnact_result *res) > +{ > + parse_ovnact_result__(ctx, "ct_state_save", NULL, dst, res, 8); > +} > + > +static void > +format_CT_STATE_SAVE(const struct ovnact_result *res, struct ds *s) > +{ > + expr_field_format(&res->dst, s); > + ds_put_cstr(s, " = ct_state_save();"); > +} > + > /* Parses an assignment or exchange or put_dhcp_opts action. */ > static void > parse_set_action(struct action_context *ctx) > @@ -5658,6 +5681,10 @@ parse_set_action(struct action_context *ctx) > lexer_lookahead(ctx->lexer) == LEX_T_LPAREN) { > parse_CT_ORIG_TP_DST(ctx, &lhs, > ovnact_put_CT_ORIG_TP_DST(ctx->ovnacts)); > + } else if (!strcmp(ctx->lexer->token.s, "ct_state_save") && > + lexer_lookahead(ctx->lexer) == LEX_T_LPAREN) { > + parse_CT_STATE_SAVE(ctx, &lhs, > + ovnact_put_CT_STATE_SAVE(ctx->ovnacts)); > } else { > parse_assignment_action(ctx, false, &lhs); > } > diff --git a/lib/ovn-util.c b/lib/ovn-util.c > index f406114db2..c825e0936f 100644 > --- a/lib/ovn-util.c > +++ b/lib/ovn-util.c > @@ -898,7 +898,7 @@ ip_address_and_port_from_lb_key(const char *key, char > **ip_address, > * > * This value is also used to handle some backward compatibility during > * upgrading. It should never decrease or rewind. */ > -#define OVN_INTERNAL_MINOR_VER 7 > +#define OVN_INTERNAL_MINOR_VER 8 > > /* Returns the OVN version. The caller must free the returned value. */ > char * > diff --git a/ovn-sb.xml b/ovn-sb.xml > index 4bcf44d041..9777cc64ad 100644 > --- a/ovn-sb.xml > +++ b/ovn-sb.xml > @@ -2792,6 +2792,14 @@ tcp.flags = RST; > </p> > </dd> > > + <dt><code><var>R</var> = ct_state_save();</code></dt> > + <dd> > + <p> > + This action checks if the packet is tracked and stores the > + conntrack original state in the 8-bit register <var>R</var>. > + </p> > + </dd> > + > <dt><code>sample(probability=<var>packets</var>, ...)</code></dt> > <dd> > <p> > diff --git a/tests/ovn-macros.at b/tests/ovn-macros.at > index fd1a6b197a..0c1c1cd992 100644 > --- a/tests/ovn-macros.at > +++ b/tests/ovn-macros.at > @@ -1494,5 +1494,6 @@ m4_define([OFTABLE_CT_ORIG_NW_DST_LOAD], [81]) > m4_define([OFTABLE_CT_ORIG_IP6_DST_LOAD], [82]) > m4_define([OFTABLE_CT_ORIG_TP_DST_LOAD], [83]) > m4_define([OFTABLE_FLOOD_REMOTE_CHASSIS], [84]) > +m4_define([OFTABLE_CT_STATE_SAVE], [85]) > > m4_define([OFTABLE_SAVE_INPORT_HEX], [m4_eval(OFTABLE_SAVE_INPORT, 16)]) > diff --git a/tests/ovn.at b/tests/ovn.at > index bbaa653a82..938a0d0f0d 100644 > --- a/tests/ovn.at > +++ b/tests/ovn.at > @@ -2287,6 +2287,28 @@ flood_remote; > flood_remote(); > Syntax error at `(' expecting `;'. > > +# ct_state_save() > +reg1[[0..7]] = ct_state_save(); > + encodes as > set_field:0/0xff->reg4,resubmit(,OFTABLE_CT_STATE_SAVE),move:NXM_NX_REG4[[0..7]]->NXM_NX_XXREG0[[64..71]] > + > +reg1[[2..9]] = ct_state_save(); > + encodes as > set_field:0/0xff->reg4,resubmit(,OFTABLE_CT_STATE_SAVE),move:NXM_NX_REG4[[0..7]]->NXM_NX_XXREG0[[66..73]] > + > +reg1[[24..31]] = ct_state_save(); > + encodes as > set_field:0/0xff->reg4,resubmit(,OFTABLE_CT_STATE_SAVE),move:NXM_NX_REG4[[0..7]]->NXM_NX_XXREG0[[88..95]] > + > +xreg1 = ct_state_save(); > + Cannot use 64-bit field xreg1[[0..63]] where 8-bit field is required. > + > +reg1[[0..30]] = ct_state_save(); > + Cannot use 31-bit field reg1[[0..30]] where 8-bit field is required. > + > +ct_state_save; > + Syntax error at `ct_state_save' expecting action. > + > +ct_state_save(); > + Syntax error at `ct_state_save' expecting action. > + > # Miscellaneous negative tests. > ; > Syntax error at `;'. > @@ -37003,8 +37025,10 @@ check_default_flows() { > # Tables 81, 82 and 83 are part of ct_nw_dst(), ct_ip6_dst() and > ct_tp_dst() > # actions respectively and its OK for them to not have default > flows. > # Table 84 is part of flood_remote; action and its OK for > - # it to not have default flows. > - if test ${table} -eq 68 -o ${table} -eq 70 -o ${table} -eq 81 -o > ${table} -eq 82 -o ${table} -eq 83 -o ${table} -eq 84; then > + # it to not have default flows. > + # Table 85 is the implementation of the ct_state_save action and > + # doesn't require a default flow. > + if test ${table} -eq 68 -o ${table} -eq 70 -o ${table} -eq 81 -o > ${table} -eq 82 -o ${table} -eq 83 -o ${table} -eq 84 -o ${table} -eq 85; > then > continue; > fi > AT_CHECK([grep -qe "table=$table.* > priority=0\(,metadata=0x\w*\)\? actions" oflows], [0], [ignore], [ignore], > [echo "Table $table does not contain a default action"]) > diff --git a/tests/test-ovn.c b/tests/test-ovn.c > index de0cb0b444..c4eb2a7dfb 100644 > --- a/tests/test-ovn.c > +++ b/tests/test-ovn.c > @@ -1388,6 +1388,7 @@ test_parse_actions(struct ovs_cmdl_context *ctx > OVS_UNUSED) > .ct_ip6_dst_load_table = OFTABLE_CT_ORIG_IP6_DST_LOAD, > .ct_tp_dst_load_table = OFTABLE_CT_ORIG_TP_DST_LOAD, > .flood_remote_table = OFTABLE_FLOOD_REMOTE_CHASSIS, > + .ct_state_save_table = OFTABLE_CT_STATE_SAVE, > .lflow_uuid.parts = > { 0xaaaaaaaa, 0xbbbbbbbb, 0xcccccccc, 0xdddddddd}, > .dp_key = 0xabcdef, > diff --git a/utilities/ovn-trace.c b/utilities/ovn-trace.c > index b8a9e0f0a6..ce55008a60 100644 > --- a/utilities/ovn-trace.c > +++ b/utilities/ovn-trace.c > @@ -3121,6 +3121,23 @@ execute_check_out_port_sec(const struct > ovnact_result *dl, > mf_write_subfield_flow(&sf, &sv, uflow); > } > > +static void > +execute_ct_save_state(const struct ovnact_result *dl, struct flow *uflow, > + struct ovs_list *super) > +{ > + struct mf_subfield sf = expr_resolve_field(&dl->dst); > + union mf_subvalue sv = { > + .u8_val = uflow->ct_state & CS_TRACKED ? uflow->ct_state : 0, > + }; > + mf_write_subfield_flow(&sf, &sv, uflow); > + > + struct ds s = DS_EMPTY_INITIALIZER; > + expr_field_format(&dl->dst, &s); > + ovntrace_node_append(super, OVNTRACE_NODE_ACTION, "/* %s = %"PRIu8"; > */", > + ds_cstr(&s), sv.u8_val); > + ds_destroy(&s); > +} > + > static void > trace_actions(const struct ovnact *ovnacts, size_t ovnacts_len, > const struct ovntrace_datapath *dp, struct flow *uflow, > @@ -3464,6 +3481,10 @@ trace_actions(const struct ovnact *ovnacts, size_t > ovnacts_len, > case OVNACT_FLOOD_REMOTE: > ovntrace_node_append(super, OVNTRACE_NODE_OUTPUT, > "/* Flood to all remote chassis */"); > + break; > + case OVNACT_CT_STATE_SAVE: > + execute_ct_save_state(ovnact_get_CT_STATE_SAVE(a), uflow, > super); > + break; > } > } > ofpbuf_uninit(&stack); > -- > 2.49.0 > > Looks good to me, thanks. Acked-by: Ales Musil <amu...@redhat.com> _______________________________________________ dev mailing list d...@openvswitch.org https://mail.openvswitch.org/mailman/listinfo/ovs-dev