commit_ecmp_nh action translates to an openflow "learn" action that inserts two new flows in the OFTABLE_ECMP_NH_MAC and OFTABLE_ECMP_NH tables. These new flows are used to match on the the 5-tuple and the expected next-hop mac address and set REGBIT_KNOWN_ECMP_NH. commit_ecmp_nh action will be used to improve ECMP symmetric routing reliability.
Signed-off-by: Lorenzo Bianconi <[email protected]> --- controller/lflow.h | 2 + include/ovn/actions.h | 7 ++ include/ovn/logical-fields.h | 3 + lib/actions.c | 176 +++++++++++++++++++++++++++++++++++ ovn-sb.xml | 34 +++++++ utilities/ovn-trace.c | 2 + 6 files changed, 224 insertions(+) diff --git a/controller/lflow.h b/controller/lflow.h index 342967917..543d3cd96 100644 --- a/controller/lflow.h +++ b/controller/lflow.h @@ -79,6 +79,8 @@ struct uuid; #define OFTABLE_CHK_IN_PORT_SEC 73 #define OFTABLE_CHK_IN_PORT_SEC_ND 74 #define OFTABLE_CHK_OUT_PORT_SEC 75 +#define OFTABLE_ECMP_NH_MAC 76 +#define OFTABLE_ECMP_NH 77 enum ref_type { REF_TYPE_ADDRSET, diff --git a/include/ovn/actions.h b/include/ovn/actions.h index 33c319f1c..cef930e1a 100644 --- a/include/ovn/actions.h +++ b/include/ovn/actions.h @@ -118,6 +118,7 @@ struct ovn_extend_table; OVNACT(LOOKUP_FDB, ovnact_lookup_fdb) \ OVNACT(CHECK_IN_PORT_SEC, ovnact_result) \ OVNACT(CHECK_OUT_PORT_SEC, ovnact_result) \ + OVNACT(COMMIT_ECMP_NH, ovnact_commit_ecmp_nh) \ /* enum ovnact_type, with a member OVNACT_<ENUM> for each action. */ enum OVS_PACKED_ENUM ovnact_type { @@ -453,6 +454,12 @@ struct ovnact_lookup_fdb { struct expr_field dst; /* 1-bit destination field. */ }; +/* OVNACT_COMMIT_ECMP_NH. */ +struct ovnact_commit_ecmp_nh { + struct ovnact ovnact; + bool ipv6; +}; + /* Internal use by the helpers below. */ void ovnact_init(struct ovnact *, enum ovnact_type, size_t len); void *ovnact_put(struct ofpbuf *, enum ovnact_type, size_t len); diff --git a/include/ovn/logical-fields.h b/include/ovn/logical-fields.h index bfb07ebef..3db7265e4 100644 --- a/include/ovn/logical-fields.h +++ b/include/ovn/logical-fields.h @@ -70,6 +70,7 @@ enum mff_log_flags_bits { MLF_LOCALPORT_BIT = 10, MLF_USE_SNAT_ZONE = 11, MLF_CHECK_PORT_SEC_BIT = 12, + MLF_LOOKUP_COMMIT_ECMP_NH_BIT = 13, }; /* MFF_LOG_FLAGS_REG flag assignments */ @@ -113,6 +114,8 @@ enum mff_log_flags { /* Indicate the packet has been received from a localport */ MLF_LOCALPORT = (1 << MLF_LOCALPORT_BIT), + + MLF_LOOKUP_COMMIT_ECMP_NH = (1 << MLF_LOOKUP_COMMIT_ECMP_NH_BIT), }; /* OVN logical fields diff --git a/lib/actions.c b/lib/actions.c index aab044306..132c63228 100644 --- a/lib/actions.c +++ b/lib/actions.c @@ -41,6 +41,7 @@ #include "uuid.h" #include "socket-util.h" #include "lib/ovn-util.h" +#include "controller/lflow.h" VLOG_DEFINE_THIS_MODULE(actions); @@ -4278,6 +4279,179 @@ encode_CHECK_OUT_PORT_SEC(const struct ovnact_result *dl, MLF_CHECK_PORT_SEC_BIT, ofpacts); } +static void +parse_commit_ecmp_nh(struct action_context *ctx, + struct ovnact_commit_ecmp_nh *ecmp_nh) +{ + bool ipv6; + + lexer_force_match(ctx->lexer, LEX_T_LPAREN); /* Skip '('. */ + if (!lexer_match_id(ctx->lexer, "ipv6")) { + lexer_syntax_error(ctx->lexer, "invalid parameter"); + return; + } + if (!lexer_force_match(ctx->lexer, LEX_T_EQUALS)) { + lexer_syntax_error(ctx->lexer, "invalid parameter"); + return; + } + if (lexer_match_string(ctx->lexer, "true") || + lexer_match_id(ctx->lexer, "true")) { + ipv6 = true; + } else if (lexer_match_string(ctx->lexer, "false") || + lexer_match_id(ctx->lexer, "false")) { + ipv6 = false; + } else { + lexer_syntax_error(ctx->lexer, + "expecting true or false"); + return; + } + lexer_force_match(ctx->lexer, LEX_T_SEMICOLON); + + lexer_force_match(ctx->lexer, LEX_T_RPAREN); /* Skip ')'. */ + + ecmp_nh->ipv6 = ipv6; +} + +static void +format_COMMIT_ECMP_NH(const struct ovnact_commit_ecmp_nh *ecmp_nh, + struct ds *s) +{ + ds_put_format(s, "commit_ecmp_nh(ipv6=%s;);", + ecmp_nh->ipv6 ? "true" : "false"); +} + +static void +ovnact_commit_ecmp_nh_free(struct ovnact_commit_ecmp_nh *ecmp_nh OVS_UNUSED) +{ +} + +static void +commit_ecmp_learn_action(struct ofpbuf *ofpacts, bool nw_conn, bool ipv6) +{ + struct ofpact_learn *ol = ofpact_put_LEARN(ofpacts); + struct match match = MATCH_CATCHALL_INITIALIZER; + struct ofpact_learn_spec *ol_spec; + unsigned int imm_bytes; + uint8_t *src_imm; + + ol->flags = NX_LEARN_F_DELETE_LEARNED; + ol->idle_timeout = 20; /* seconds. */ + ol->hard_timeout = 30; /* seconds. */ + ol->priority = OFP_DEFAULT_PRIORITY; + ol->table_id = nw_conn ? OFTABLE_ECMP_NH_MAC : OFTABLE_ECMP_NH; + + /* Match on metadata of the packet that created the new table. */ + ol_spec = ofpbuf_put_zeros(ofpacts, sizeof *ol_spec); + ol_spec->dst.field = mf_from_id(MFF_METADATA); + ol_spec->dst.ofs = 0; + ol_spec->dst.n_bits = ol_spec->dst.field->n_bits; + ol_spec->n_bits = ol_spec->dst.n_bits; + ol_spec->dst_type = NX_LEARN_DST_MATCH; + ol_spec->src_type = NX_LEARN_SRC_FIELD; + ol_spec->src.field = mf_from_id(MFF_METADATA); + + if (nw_conn) { + ol_spec = ofpbuf_put_zeros(ofpacts, sizeof *ol_spec); + ol_spec->dst.field = mf_from_id(MFF_ETH_SRC); + ol_spec->src.field = mf_from_id(MFF_ETH_SRC); + ol_spec->dst.ofs = 0; + ol_spec->dst.n_bits = ol_spec->dst.field->n_bits; + ol_spec->n_bits = ol_spec->dst.n_bits; + ol_spec->dst_type = NX_LEARN_DST_MATCH; + ol_spec->src_type = NX_LEARN_SRC_FIELD; + } + + /* Match on the same ETH type as the packet that created the new table. */ + ol_spec = ofpbuf_put_zeros(ofpacts, sizeof *ol_spec); + ol_spec->dst.field = mf_from_id(MFF_ETH_TYPE); + ol_spec->dst.ofs = 0; + ol_spec->dst.n_bits = ol_spec->dst.field->n_bits; + ol_spec->n_bits = ol_spec->dst.n_bits; + ol_spec->dst_type = NX_LEARN_DST_MATCH; + ol_spec->src_type = NX_LEARN_SRC_IMMEDIATE; + union mf_value imm_eth_type = { + .be16 = !ipv6 ? htons(ETH_TYPE_IP) : htons(ETH_TYPE_IPV6) + }; + mf_write_subfield_value(&ol_spec->dst, &imm_eth_type, &match); + /* Push value last, as this may reallocate 'ol_spec'. */ + imm_bytes = DIV_ROUND_UP(ol_spec->dst.n_bits, 8); + src_imm = ofpbuf_put_zeros(ofpacts, OFPACT_ALIGN(imm_bytes)); + memcpy(src_imm, &imm_eth_type, imm_bytes); + + /* IP src. */ + ol_spec = ofpbuf_put_zeros(ofpacts, sizeof *ol_spec); + ol_spec->dst.field = + !ipv6 ? mf_from_id(MFF_IPV4_SRC) : mf_from_id(MFF_IPV6_SRC); + if (nw_conn) { + ol_spec->src.field = + !ipv6 ? mf_from_id(MFF_IPV4_SRC) : mf_from_id(MFF_IPV6_SRC); + } else { + ol_spec->src.field = + !ipv6 ? mf_from_id(MFF_IPV4_DST) : mf_from_id(MFF_IPV6_DST); + } + ol_spec->dst.ofs = 0; + ol_spec->dst.n_bits = ol_spec->dst.field->n_bits; + ol_spec->n_bits = ol_spec->dst.n_bits; + ol_spec->dst_type = NX_LEARN_DST_MATCH; + ol_spec->src_type = NX_LEARN_SRC_FIELD; + + /* IP dst. */ + ol_spec = ofpbuf_put_zeros(ofpacts, sizeof *ol_spec); + ol_spec->dst.field = + !ipv6 ? mf_from_id(MFF_IPV4_DST) : mf_from_id(MFF_IPV6_DST); + if (nw_conn) { + ol_spec->src.field = + !ipv6 ? mf_from_id(MFF_IPV4_DST) : mf_from_id(MFF_IPV6_DST); + } else { + ol_spec->src.field = + !ipv6 ? mf_from_id(MFF_IPV4_SRC) : mf_from_id(MFF_IPV6_SRC); + } + ol_spec->dst.ofs = 0; + ol_spec->dst.n_bits = ol_spec->dst.field->n_bits; + ol_spec->n_bits = ol_spec->dst.n_bits; + ol_spec->dst_type = NX_LEARN_DST_MATCH; + ol_spec->src_type = NX_LEARN_SRC_FIELD; + + /* IP proto. */ + ol_spec = ofpbuf_put_zeros(ofpacts, sizeof *ol_spec); + ol_spec->dst.field = mf_from_id(MFF_IP_PROTO); + ol_spec->src.field = mf_from_id(MFF_IP_PROTO); + ol_spec->dst.ofs = 0; + ol_spec->dst.n_bits = ol_spec->dst.field->n_bits; + ol_spec->n_bits = ol_spec->dst.n_bits; + ol_spec->dst_type = NX_LEARN_DST_MATCH; + ol_spec->src_type = NX_LEARN_SRC_FIELD; + + /* Set MLF_LOOKUP_COMMIT_ECMP_NH_BIT for ecmp replies. */ + ol_spec = ofpbuf_put_zeros(ofpacts, sizeof *ol_spec); + ol_spec->dst.field = mf_from_id(MFF_LOG_FLAGS); + ol_spec->dst.ofs = MLF_LOOKUP_COMMIT_ECMP_NH_BIT; + ol_spec->dst.n_bits = 1; + ol_spec->n_bits = ol_spec->dst.n_bits; + ol_spec->dst_type = NX_LEARN_DST_LOAD; + ol_spec->src_type = NX_LEARN_SRC_IMMEDIATE; + union mf_value imm_reg_value = { + .u8 = 1 + }; + mf_write_subfield_value(&ol_spec->dst, &imm_reg_value, &match); + + /* Push value last, as this may reallocate 'ol_spec' */ + imm_bytes = DIV_ROUND_UP(ol_spec->dst.n_bits, 8); + src_imm = ofpbuf_put_zeros(ofpacts, OFPACT_ALIGN(imm_bytes)); + memcpy(src_imm, &imm_reg_value, imm_bytes); + + ofpact_finish_LEARN(ofpacts, &ol); +} + +static void +encode_COMMIT_ECMP_NH(const struct ovnact_commit_ecmp_nh *ecmp_nh, + const struct ovnact_encode_params *ep OVS_UNUSED, + struct ofpbuf *ofpacts) +{ + commit_ecmp_learn_action(ofpacts, true, ecmp_nh->ipv6); + commit_ecmp_learn_action(ofpacts, false, ecmp_nh->ipv6); +} + /* Parses an assignment or exchange or put_dhcp_opts action. */ static void parse_set_action(struct action_context *ctx) @@ -4458,6 +4632,8 @@ parse_action(struct action_context *ctx) ovnact_put_CT_SNAT_TO_VIP(ctx->ovnacts); } else if (lexer_match_id(ctx->lexer, "put_fdb")) { parse_put_fdb(ctx, ovnact_put_PUT_FDB(ctx->ovnacts)); + } else if (lexer_match_id(ctx->lexer, "commit_ecmp_nh")) { + parse_commit_ecmp_nh(ctx, ovnact_put_COMMIT_ECMP_NH(ctx->ovnacts)); } else { lexer_syntax_error(ctx->lexer, "expecting action"); } diff --git a/ovn-sb.xml b/ovn-sb.xml index 59ad3aa2d..ef5586020 100644 --- a/ovn-sb.xml +++ b/ovn-sb.xml @@ -2573,6 +2573,40 @@ tcp.flags = RST; <b>Example:</b> <code>reg8[0..7] = check_out_port_sec();</code> </p> </dd> + + <dt><code>commit_ecmp_nh(<var>ipv6</var>);</code></dt> + <dd> + <p> + <b>Parameters</b>: IPv4/IPv6 traffic. + </p> + + <p> + This action translates to an openflow "learn" action that inserts + two new flows in the <code>OFTABLE_ECMP_NH_MAC</code> and in the + <code>OFTABLE_ECMP_NH</code> tables. + </p> + + <ul> + <li> + Match on the the 5-tuple and the expected next-hop mac address + in <code>OFTABLE_ECMP_NH_MAC</code> table: + <code>nw_src=ip0</code>, <code>nw_dst=ip1</code>, + <code>ip_proto</code>,<code>dl_src=ethaddr</code> and + set <code>REGBIT_KNOWN_ECMP_NH</code>. + </li> + <li> + Match on the 5-tuple in <code>OFTABLE_ECMP_NH</code> table: + <code>nw_src=ip1</code>, <code>nw_dst=ip0</code>, + <code>ip_proto</code> and set <code>REGBIT_KNOWN_ECMP_NH</code> + to 1 + </li> + </ul> + + <p> + This action is applied if the packet arrives via ECMP route or + if it is routed via an ECMP route + </p> + </dd> </dl> </column> diff --git a/utilities/ovn-trace.c b/utilities/ovn-trace.c index c4110de0a..fd84a1a5e 100644 --- a/utilities/ovn-trace.c +++ b/utilities/ovn-trace.c @@ -3224,6 +3224,8 @@ trace_actions(const struct ovnact *ovnacts, size_t ovnacts_len, execute_check_out_port_sec(ovnact_get_CHECK_OUT_PORT_SEC(a), dp, uflow); break; + case OVNACT_COMMIT_ECMP_NH: + break; } } ofpbuf_uninit(&stack); -- 2.36.1 _______________________________________________ dev mailing list [email protected] https://mail.openvswitch.org/mailman/listinfo/ovs-dev
