From cfa19b0905752503056e1e8e4246a629da1d8fbd Mon Sep 17 00:00:00 2001
From: Numan Siddique <numans@ovn.org>
Date: Sat, 30 Jan 2021 00:17:17 +0530
Subject: [PATCH 08/16] MAC learning: Add new actions - put_fdb, get_fdb and
lookup_fdb.
This patch adds these OVN actions to learn port-to-mac bindings on
the logical ports whose port security is disabled and are configured
to accept unknown destination packets.
put_fdb(inport, mac) will add an entry in the Southbound 'FDB'
table.
get_fdb(mac) will get the port key on which the mac is learnt.
lookup_fdb(inport, mac) will check if the port-to-mac entry is already
present or not. This is added to limit using the action - put_fdb()
only if required.
An upcoming patch in the series will add the necessary logical flows
which makes use of these actions.
Acked-by: Mark Michelson <mmichels@redhat.com>
Signed-off-by: Numan Siddique <numans@ovn.org>
(cherry picked from upstream commit f819ce8fa2a008d3853f39548f1cef5382ea9cdb)
Change-Id: Iddb4272d93704e5d18a3d87a36cf3b4c9fe52fdb
---
controller/lflow.c | 2 +
controller/lflow.h | 2 +
include/ovn/actions.h | 29 +++++++
include/ovn/logical-fields.h | 4 +
lib/actions.c | 176 +++++++++++++++++++++++++++++++++++++++++++
ovn-sb.xml | 62 +++++++++++++++
tests/ovn.at | 58 ++++++++++++++
tests/test-ovn.c | 2 +
utilities/ovn-trace.c | 138 ++++++++++++++++++++++++++++++++-
9 files changed, 472 insertions(+), 1 deletion(-)
diff --git a/controller/lflow.c b/controller/lflow.c
index 21ff51d..97457a8 100644
--- a/controller/lflow.c
+++ b/controller/lflow.c
@@ -683,6 +683,8 @@ add_matches_to_flow_table(const struct sbrec_logical_flow *lflow,
.lb_hairpin_ptable = OFTABLE_CHK_LB_HAIRPIN,
.lb_hairpin_reply_ptable = OFTABLE_CHK_LB_HAIRPIN_REPLY,
.ct_snat_vip_ptable = OFTABLE_CT_SNAT_FOR_VIP,
+ .fdb_ptable = OFTABLE_GET_FDB,
+ .fdb_lookup_ptable = OFTABLE_LOOKUP_FDB,
};
ovnacts_encode(ovnacts->data, ovnacts->size, &ep, &ofpacts);
diff --git a/controller/lflow.h b/controller/lflow.h
index ba79cc3..d790d51 100644
--- a/controller/lflow.h
+++ b/controller/lflow.h
@@ -71,6 +71,8 @@ struct uuid;
#define OFTABLE_CHK_LB_HAIRPIN 68
#define OFTABLE_CHK_LB_HAIRPIN_REPLY 69
#define OFTABLE_CT_SNAT_FOR_VIP 70
+#define OFTABLE_GET_FDB 71
+#define OFTABLE_LOOKUP_FDB 72
/* The number of tables for the ingress and egress pipelines. */
#define LOG_PIPELINE_LEN 24
diff --git a/include/ovn/actions.h b/include/ovn/actions.h
index a2d28c6..0402131 100644
--- a/include/ovn/actions.h
+++ b/include/ovn/actions.h
@@ -107,6 +107,9 @@ struct ovn_extend_table;
OVNACT(CT_SNAT_TO_VIP, ovnact_null) \
OVNACT(BFD_MSG, ovnact_null) \
OVNACT(SCTP_ABORT, ovnact_nest) \
+ OVNACT(PUT_FDB, ovnact_put_fdb) \
+ OVNACT(GET_FDB, ovnact_get_fdb) \
+ OVNACT(LOOKUP_FDB, ovnact_lookup_fdb) \
/* enum ovnact_type, with a member OVNACT_<ENUM> for each action. */
enum OVS_PACKED_ENUM ovnact_type {
@@ -415,6 +418,28 @@ struct ovnact_fwd_group {
uint8_t ltable; /* Logical table ID of next table. */
};
+/* OVNACT_PUT_FDB. */
+struct ovnact_put_fdb {
+ struct ovnact ovnact;
+ struct expr_field port; /* Logical port name. */
+ struct expr_field mac; /* 48-bit Ethernet address. */
+};
+
+/* OVNACT_GET_FDB. */
+struct ovnact_get_fdb {
+ struct ovnact ovnact;
+ struct expr_field mac; /* 48-bit Ethernet address. */
+ struct expr_field dst; /* 32-bit destination field. */
+};
+
+/* OVNACT_LOOKUP_FDB. */
+struct ovnact_lookup_fdb {
+ struct ovnact ovnact;
+ struct expr_field mac; /* 48-bit Ethernet address. */
+ struct expr_field port; /* Logical port name. */
+ struct expr_field dst; /* 1-bit destination field. */
+};
+
/* 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);
@@ -766,6 +791,10 @@ struct ovnact_encode_params {
* 'chk_lb_hairpin_reply' to resubmit. */
uint8_t ct_snat_vip_ptable; /* OpenFlow table for
* 'ct_snat_to_vip' to resubmit. */
+ uint8_t fdb_ptable; /* OpenFlow table for
+ * 'get_fdb' to resubmit. */
+ uint8_t fdb_lookup_ptable; /* OpenFlow table for
+ * 'lookup_fdb' to resubmit. */
};
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 aee4748..9d8d07f 100644
--- a/include/ovn/logical-fields.h
+++ b/include/ovn/logical-fields.h
@@ -59,6 +59,7 @@ enum mff_log_flags_bits {
MLF_NESTED_CONTAINER_BIT = 5,
MLF_LOOKUP_MAC_BIT = 6,
MLF_LOOKUP_LB_HAIRPIN_BIT = 7,
+ MLF_LOOKUP_FDB_BIT = 8,
};
/* MFF_LOG_FLAGS_REG flag assignments */
@@ -92,6 +93,9 @@ enum mff_log_flags {
MLF_LOOKUP_MAC = (1 << MLF_LOOKUP_MAC_BIT),
MLF_LOOKUP_LB_HAIRPIN = (1 << MLF_LOOKUP_LB_HAIRPIN_BIT),
+
+ /* Indicate that the lookup in the fdb table was successful. */
+ MLF_LOOKUP_FDB = (1 << MLF_LOOKUP_FDB_BIT),
};
/* OVN logical fields
diff --git a/lib/actions.c b/lib/actions.c
index 56b8fab..b3433f4 100644
--- a/lib/actions.c
+++ b/lib/actions.c
@@ -3743,6 +3743,172 @@ encode_CT_SNAT_TO_VIP(const struct ovnact_null *null OVS_UNUSED,
emit_resubmit(ofpacts, ep->ct_snat_vip_ptable);
}
+static void
+format_PUT_FDB(const struct ovnact_put_fdb *put_fdb, struct ds *s)
+{
+ ds_put_cstr(s, "put_fdb(");
+ expr_field_format(&put_fdb->port, s);
+ ds_put_cstr(s, ", ");
+ expr_field_format(&put_fdb->mac, s);
+ ds_put_cstr(s, ");");
+}
+
+static void
+encode_PUT_FDB(const struct ovnact_put_fdb *put_fdb,
+ const struct ovnact_encode_params *ep OVS_UNUSED,
+ struct ofpbuf *ofpacts)
+{
+ const struct arg args[] = {
+ { expr_resolve_field(&put_fdb->port), MFF_LOG_INPORT },
+ { expr_resolve_field(&put_fdb->mac), MFF_ETH_SRC }
+ };
+ encode_setup_args(args, ARRAY_SIZE(args), ofpacts);
+ encode_controller_op(ACTION_OPCODE_PUT_FDB, ofpacts);
+ encode_restore_args(args, ARRAY_SIZE(args), ofpacts);
+}
+
+static void
+parse_put_fdb(struct action_context *ctx, struct ovnact_put_fdb *put_fdb)
+{
+ lexer_force_match(ctx->lexer, LEX_T_LPAREN);
+ action_parse_field(ctx, 0, false, &put_fdb->port);
+ lexer_force_match(ctx->lexer, LEX_T_COMMA);
+ action_parse_field(ctx, 48, false, &put_fdb->mac);
+ lexer_force_match(ctx->lexer, LEX_T_RPAREN);
+}
+
+static void
+ovnact_put_fdb_free(struct ovnact_put_fdb *put_fdb OVS_UNUSED)
+{
+}
+
+static void
+format_GET_FDB(const struct ovnact_get_fdb *get_fdb, struct ds *s)
+{
+ expr_field_format(&get_fdb->dst, s);
+ ds_put_cstr(s, " = get_fdb(");
+ expr_field_format(&get_fdb->mac, s);
+ ds_put_cstr(s, ");");
+}
+
+static void
+encode_GET_FDB(const struct ovnact_get_fdb *get_fdb,
+ const struct ovnact_encode_params *ep,
+ struct ofpbuf *ofpacts)
+{
+ struct mf_subfield dst = expr_resolve_field(&get_fdb->dst);
+ ovs_assert(dst.field);
+
+ const struct arg args[] = {
+ { expr_resolve_field(&get_fdb->mac), MFF_ETH_DST },
+ };
+ encode_setup_args(args, ARRAY_SIZE(args), ofpacts);
+ put_load(0, MFF_LOG_OUTPORT, 0, 32, ofpacts);
+ emit_resubmit(ofpacts, ep->fdb_ptable);
+ encode_restore_args(args, ARRAY_SIZE(args), ofpacts);
+
+ if (dst.field->id != MFF_LOG_OUTPORT) {
+ struct ofpact_reg_move *orm = ofpact_put_REG_MOVE(ofpacts);
+ orm->dst = dst;
+ orm->src.field = mf_from_id(MFF_LOG_OUTPORT);
+ orm->src.ofs = 0;
+ orm->src.n_bits = 32;
+ }
+}
+
+static void
+parse_get_fdb(struct action_context *ctx,
+ struct expr_field *dst,
+ struct ovnact_get_fdb *get_fdb)
+{
+ lexer_get(ctx->lexer); /* Skip get_bfd. */
+ lexer_get(ctx->lexer); /* Skip '('. */
+
+ /* Validate that the destination is a 32-bit, modifiable field if it
+ is not a string field (i.e 'inport' or 'outport'). */
+ if (dst->n_bits) {
+ char *error = expr_type_check(dst, 32, true, ctx->scope);
+ if (error) {
+ lexer_error(ctx->lexer, "%s", error);
+ free(error);
+ return;
+ }
+ }
+ get_fdb->dst = *dst;
+
+ action_parse_field(ctx, 48, false, &get_fdb->mac);
+ lexer_force_match(ctx->lexer, LEX_T_RPAREN);
+}
+
+static void
+ovnact_get_fdb_free(struct ovnact_get_fdb *get_fdb OVS_UNUSED)
+{
+}
+
+static void
+format_LOOKUP_FDB(const struct ovnact_lookup_fdb *lookup_fdb, struct ds *s)
+{
+ expr_field_format(&lookup_fdb->dst, s);
+ ds_put_cstr(s, " = lookup_fdb(");
+ expr_field_format(&lookup_fdb->port, s);
+ ds_put_cstr(s, ", ");
+ expr_field_format(&lookup_fdb->mac, s);
+ ds_put_cstr(s, ");");
+}
+
+static void
+encode_LOOKUP_FDB(const struct ovnact_lookup_fdb *lookup_fdb,
+ const struct ovnact_encode_params *ep,
+ struct ofpbuf *ofpacts)
+{
+ const struct arg args[] = {
+ { expr_resolve_field(&lookup_fdb->port), MFF_LOG_INPORT },
+ { expr_resolve_field(&lookup_fdb->mac), MFF_ETH_SRC },
+ };
+ encode_setup_args(args, ARRAY_SIZE(args), ofpacts);
+
+ struct mf_subfield dst = expr_resolve_field(&lookup_fdb->dst);
+ ovs_assert(dst.field);
+
+ put_load(0, MFF_LOG_FLAGS, MLF_LOOKUP_FDB_BIT, 1, ofpacts);
+ emit_resubmit(ofpacts, ep->fdb_lookup_ptable);
+ encode_restore_args(args, ARRAY_SIZE(args), ofpacts);
+
+ struct ofpact_reg_move *orm = ofpact_put_REG_MOVE(ofpacts);
+ orm->dst = dst;
+ orm->src.field = mf_from_id(MFF_LOG_FLAGS);
+ orm->src.ofs = MLF_LOOKUP_FDB_BIT;
+ orm->src.n_bits = 1;
+}
+
+static void
+parse_lookup_fdb(struct action_context *ctx,
+ struct expr_field *dst,
+ struct ovnact_lookup_fdb *lookup_fdb)
+{
+ lexer_get(ctx->lexer); /* Skip lookup_bfd. */
+ lexer_get(ctx->lexer); /* Skip '('. */
+
+ /* Validate that the destination is a 1-bit, modifiable field. */
+ char *error = expr_type_check(dst, 1, true, ctx->scope);
+ if (error) {
+ lexer_error(ctx->lexer, "%s", error);
+ free(error);
+ return;
+ }
+ lookup_fdb->dst = *dst;
+
+ action_parse_field(ctx, 0, false, &lookup_fdb->port);
+ lexer_force_match(ctx->lexer, LEX_T_COMMA);
+ action_parse_field(ctx, 48, false, &lookup_fdb->mac);
+ lexer_force_match(ctx->lexer, LEX_T_RPAREN);
+}
+
+static void
+ovnact_lookup_fdb_free(struct ovnact_lookup_fdb *get_fdb OVS_UNUSED)
+{
+}
+
/* Parses an assignment or exchange or put_dhcp_opts action. */
static void
parse_set_action(struct action_context *ctx)
@@ -3803,6 +3969,14 @@ parse_set_action(struct action_context *ctx)
&& lexer_lookahead(ctx->lexer) == LEX_T_LPAREN) {
parse_chk_lb_hairpin_reply(
ctx, &lhs, ovnact_put_CHK_LB_HAIRPIN_REPLY(ctx->ovnacts));
+ } else if (!strcmp(ctx->lexer->token.s, "get_fdb")
+ && lexer_lookahead(ctx->lexer) == LEX_T_LPAREN) {
+ parse_get_fdb(
+ ctx, &lhs, ovnact_put_GET_FDB(ctx->ovnacts));
+ } else if (!strcmp(ctx->lexer->token.s, "lookup_fdb")
+ && lexer_lookahead(ctx->lexer) == LEX_T_LPAREN) {
+ parse_lookup_fdb(
+ ctx, &lhs, ovnact_put_LOOKUP_FDB(ctx->ovnacts));
} else {
parse_assignment_action(ctx, false, &lhs);
}
@@ -3895,6 +4069,8 @@ parse_action(struct action_context *ctx)
parse_REJECT(ctx);
} else if (lexer_match_id(ctx->lexer, "ct_snat_to_vip")) {
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 {
lexer_syntax_error(ctx->lexer, "expecting action");
}
diff --git a/ovn-sb.xml b/ovn-sb.xml
index b8912b3..35000ed 100644
--- a/ovn-sb.xml
+++ b/ovn-sb.xml
@@ -1526,6 +1526,68 @@
</p>
</dd>
+ <dt><code><var>P</var> = get_fdb(<var>A</var>);</code></dt>
+
+ <dd>
+ <p>
+ <b>Parameters</b>:48-bit MAC address field <var>A</var>.
+ </p>
+
+ <p>
+ Looks up <var>A</var> in fdb table. If an entry is found, stores
+ the logical port key to the out parameter <code>P</code>.
+ </p>
+
+ <p><b>Example:</b> <code>outport = get_fdb(eth.src);</code></p>
+ </dd>
+
+ <dt>
+ <code>put_fdb(<var>P</var>, <var>A</var>);</code>
+ </dt>
+
+ <dd>
+ <p>
+ <b>Parameters</b>: logical port string field <var>P</var>, 48-bit
+ MAC address field <var>A</var>.
+ </p>
+
+ <p>
+ Adds or updates the entry for Ethernet address <var>A</var> in
+ fdb table, setting its logical port key to <var>P</var>.
+ </p>
+
+ <p><b>Example:</b> <code>put_fdb(inport, arp.spa);</code></p>
+ </dd>
+
+ <dt>
+ <code><var>R</var> = lookup_fdb(<var>P</var>, <var>A</var>);</code>
+ </dt>
+
+ <dd>
+ <p>
+ <b>Parameters</b>: 48-bit MAC address field <var>M</var>,
+ logical port string field <var>P</var>.
+ </p>
+
+ <p>
+ <b>Result</b>: stored to a 1-bit subfield <var>R</var>.
+ </p>
+
+ <p>
+ Looks up <var>A</var> in fdb table. If an entry is found
+ and the the logical port key is <var>P</var>, <code>P</code>,
+ stores <code>1</code> in the 1-bit subfield
+ <var>R</var>, else 0.
+ </p>
+
+ <p>
+ <b>Example:</b>
+ <code>
+ reg0[0] = lookup_fdb(inport, eth.src);
+ </code>
+ </p>
+ </dd>
+
<dt><code>nd_ns { <var>action</var>; </code>...<code> };</code></dt>
<dd>
<p>
diff --git a/tests/ovn.at b/tests/ovn.at
index ead4e83..c13f5d6 100644
--- a/tests/ovn.at
+++ b/tests/ovn.at
@@ -1822,6 +1822,64 @@ ct_snat_to_vip(foo);
handle_bfd_msg();
encodes as controller(userdata=00.00.00.17.00.00.00.00)
+# put_fdb
+put_fdb(inport, arp.sha);
+ encodes as push:NXM_OF_ETH_SRC[],push:NXM_NX_ARP_SHA[],pop:NXM_OF_ETH_SRC[],controller(userdata=00.00.00.19.00.00.00.00),pop:NXM_OF_ETH_SRC[]
+ has prereqs eth.type == 0x806
+
+put_fdb(inport, eth.src);
+ encodes as controller(userdata=00.00.00.19.00.00.00.00)
+
+put_fdb(inport, ip4.src);
+ Cannot use 32-bit field ip4.src[0..31] where 48-bit field is required.
+
+# get_fdb
+outport = get_fdb(eth.dst);
+ encodes as set_field:0->reg15,resubmit(,71)
+
+outport = get_fdb(eth.src);
+ encodes as push:NXM_OF_ETH_DST[],push:NXM_OF_ETH_SRC[],pop:NXM_OF_ETH_DST[],set_field:0->reg15,resubmit(,71),pop:NXM_OF_ETH_DST[]
+
+inport = get_fdb(arp.sha);
+ encodes as push:NXM_OF_ETH_DST[],push:NXM_NX_ARP_SHA[],pop:NXM_OF_ETH_DST[],set_field:0->reg15,resubmit(,71),pop:NXM_OF_ETH_DST[],move:NXM_NX_REG15[]->NXM_NX_REG14[]
+ has prereqs eth.type == 0x806
+
+reg0 = get_fdb(arp.tha);
+ encodes as push:NXM_OF_ETH_DST[],push:NXM_NX_ARP_THA[],pop:NXM_OF_ETH_DST[],set_field:0->reg15,resubmit(,71),pop:NXM_OF_ETH_DST[],move:NXM_NX_REG15[]->NXM_NX_XXREG0[96..127]
+ has prereqs eth.type == 0x806
+
+reg0[1..3] = get_fdb(eth.src);
+ Cannot use 3-bit field reg0[1..3] where 32-bit field is required.
+
+reg15 = get_fdb(eth.dst);
+ Syntax error at `reg15' expecting field name.
+
+outport = get_fdb(ip4.dst);
+ Cannot use 32-bit field ip4.dst[0..31] where 48-bit field is required.
+
+# lookup_fdb
+reg0[0] = lookup_fdb(inport, eth.src);
+ encodes as set_field:0/0x100->reg10,resubmit(,72),move:NXM_NX_REG10[8]->NXM_NX_XXREG0[96]
+
+reg1[4] = lookup_fdb(outport, eth.dst);
+ encodes as push:NXM_NX_REG14[],push:NXM_OF_ETH_SRC[],push:NXM_OF_ETH_DST[],push:NXM_NX_REG15[],pop:NXM_NX_REG14[],pop:NXM_OF_ETH_SRC[],set_field:0/0x100->reg10,resubmit(,72),pop:NXM_OF_ETH_SRC[],pop:NXM_NX_REG14[],move:NXM_NX_REG10[8]->NXM_NX_XXREG0[68]
+
+reg0[0] = lookup_fdb(outport, arp.sha);
+ encodes as push:NXM_NX_REG14[],push:NXM_OF_ETH_SRC[],push:NXM_NX_ARP_SHA[],push:NXM_NX_REG15[],pop:NXM_NX_REG14[],pop:NXM_OF_ETH_SRC[],set_field:0/0x100->reg10,resubmit(,72),pop:NXM_OF_ETH_SRC[],pop:NXM_NX_REG14[],move:NXM_NX_REG10[8]->NXM_NX_XXREG0[96]
+ has prereqs eth.type == 0x806
+
+reg0 = lookup_fdb(outport, arp.sha);
+ Cannot use 32-bit field reg0[0..31] where 1-bit field is required.
+
+outport = lookup_fdb(outport, arp.sha);
+ Cannot use string field outport where numeric field is required.
+
+reg1[1] = lookup_fdb(outport, ip4.src);
+ Cannot use 32-bit field ip4.src[0..31] where 48-bit field is required.
+
+reg1[1] = lookup_fdb(ip4.src, eth.src);
+ Cannot use numeric field ip4.src where string field is required.
+
# Miscellaneous negative tests.
;
Syntax error at `;'.
diff --git a/tests/test-ovn.c b/tests/test-ovn.c
index 49a1947..3fbe90b 100644
--- a/tests/test-ovn.c
+++ b/tests/test-ovn.c
@@ -1346,6 +1346,8 @@ test_parse_actions(struct ovs_cmdl_context *ctx OVS_UNUSED)
.lb_hairpin_ptable = OFTABLE_CHK_LB_HAIRPIN,
.lb_hairpin_reply_ptable = OFTABLE_CHK_LB_HAIRPIN_REPLY,
.ct_snat_vip_ptable = OFTABLE_CT_SNAT_FOR_VIP,
+ .fdb_ptable = OFTABLE_GET_FDB,
+ .fdb_lookup_ptable = OFTABLE_LOOKUP_FDB,
};
struct ofpbuf ofpacts;
ofpbuf_init(&ofpacts, 0);
diff --git a/utilities/ovn-trace.c b/utilities/ovn-trace.c
index 906e507..8376309 100644
--- a/utilities/ovn-trace.c
+++ b/utilities/ovn-trace.c
@@ -405,6 +405,7 @@ struct ovntrace_datapath {
size_t n_flows, allocated_flows;
struct hmap mac_bindings; /* Contains "struct ovntrace_mac_binding"s. */
+ struct hmap fdbs; /* Contains "struct ovntrace_fdb"s. */
bool has_local_l3gateway;
};
@@ -453,12 +454,24 @@ struct ovntrace_mac_binding {
struct eth_addr mac;
};
+struct ovntrace_fdb {
+ struct hmap_node node;
+ uint16_t port_key;
+ struct eth_addr mac;
+};
+
static inline uint32_t
hash_mac_binding(uint16_t port_key, const struct in6_addr *ip)
{
return hash_bytes(ip, sizeof *ip, port_key);
}
+static inline uint32_t
+hash_fdb(const struct eth_addr *mac)
+{
+ return hash_bytes(mac, sizeof *mac, 0);
+}
+
/* Every ovntrace_datapath, by southbound Datapath_Binding record UUID. */
static struct hmap datapaths;
@@ -518,6 +531,18 @@ ovntrace_datapath_find_by_name(const char *name)
return match;
}
+static struct ovntrace_datapath *
+ovntrace_datapath_find_by_key(uint32_t tunnel_key)
+{
+ struct ovntrace_datapath *dp;
+ HMAP_FOR_EACH (dp, sb_uuid_node, &datapaths) {
+ if (dp->tunnel_key == tunnel_key) {
+ return dp;
+ }
+ }
+ return NULL;
+}
+
static const struct ovntrace_port *
ovntrace_port_find_by_key(const struct ovntrace_datapath *dp,
uint16_t tunnel_key)
@@ -598,6 +623,20 @@ ovntrace_mac_binding_find_mac_ip(const struct ovntrace_datapath *dp,
return NULL;
}
+static const struct ovntrace_fdb *
+ovntrace_fdb_find(const struct ovntrace_datapath *dp,
+ const struct eth_addr *mac)
+{
+ const struct ovntrace_fdb *fdb;
+ HMAP_FOR_EACH_WITH_HASH (fdb, node, hash_fdb(mac),
+ &dp->fdbs) {
+ if (eth_addr_equals(fdb->mac, *mac)) {
+ return fdb;
+ }
+ }
+ return NULL;
+}
+
/* If 's' ends with a UUID, returns a copy of it with the UUID truncated to
* just the first 6 characters; otherwise, returns a copy of 's'. */
static char *
@@ -638,7 +677,7 @@ read_datapaths(void)
ovs_list_init(&dp->mcgroups);
hmap_init(&dp->mac_bindings);
-
+ hmap_init(&dp->fdbs);
hmap_insert(&datapaths, &dp->sb_uuid_node, uuid_hash(&dp->sb_uuid));
}
}
@@ -1054,6 +1093,30 @@ read_mac_bindings(void)
}
static void
+read_fdbs(void)
+{
+ const struct sbrec_fdb *fdb;
+ SBREC_FDB_FOR_EACH (fdb, ovnsb_idl) {
+ struct eth_addr mac;
+ if (!eth_addr_from_string(fdb->mac, &mac)) {
+ VLOG_WARN("%s: bad Ethernet address", fdb->mac);
+ continue;
+ }
+
+ struct ovntrace_datapath *dp =
+ ovntrace_datapath_find_by_key(fdb->dp_key);
+ if (!dp) {
+ continue;
+ }
+
+ struct ovntrace_fdb *fdb_t = xmalloc(sizeof *fdb_t);
+ fdb_t->mac = mac;
+ fdb_t->port_key = fdb->port_key;
+ hmap_insert(&dp->fdbs, &fdb_t->node, hash_fdb(&mac));
+ }
+}
+
+static void
read_db(void)
{
read_datapaths();
@@ -1064,6 +1127,7 @@ read_db(void)
read_gen_opts();
read_flows();
read_mac_bindings();
+ read_fdbs();
}
static const struct ovntrace_port *
@@ -2030,6 +2094,66 @@ execute_lookup_mac_bind_ip(const struct ovnact_lookup_mac_bind_ip *bind,
}
static void
+execute_lookup_fdb(const struct ovnact_lookup_fdb *lookup_fdb,
+ const struct ovntrace_datapath *dp,
+ struct flow *uflow,
+ struct ovs_list *super)
+{
+ /* Get logical port number.*/
+ struct mf_subfield port_sf = expr_resolve_field(&lookup_fdb->port);
+ ovs_assert(port_sf.n_bits == 32);
+ uint32_t port_key = mf_get_subfield(&port_sf, uflow);
+
+ /* Get MAC. */
+ struct mf_subfield mac_sf = expr_resolve_field(&lookup_fdb->mac);
+ ovs_assert(mac_sf.n_bits == 48);
+ union mf_subvalue mac_sv;
+ mf_read_subfield(&mac_sf, uflow, &mac_sv);
+
+ const struct ovntrace_fdb *fdb_t
+ = ovntrace_fdb_find(dp, &mac_sv.mac);
+
+ struct mf_subfield dst = expr_resolve_field(&lookup_fdb->dst);
+ uint8_t val = 0;
+
+ if (fdb_t && fdb_t->port_key == port_key) {
+ val = 1;
+ ovntrace_node_append(super, OVNTRACE_NODE_ACTION,
+ "/* MAC lookup for "ETH_ADDR_FMT" found in "
+ "FDB. */", ETH_ADDR_ARGS(uflow->dl_dst));
+ } else {
+ ovntrace_node_append(super, OVNTRACE_NODE_ACTION,
+ "/* lookup mac failed in mac learning table. */");
+ }
+ union mf_subvalue sv = { .u8_val = val };
+ mf_write_subfield_flow(&dst, &sv, uflow);
+}
+
+static void
+execute_get_fdb(const struct ovnact_get_fdb *get_fdb,
+ const struct ovntrace_datapath *dp,
+ struct flow *uflow)
+{
+ /* Get MAC. */
+ struct mf_subfield mac_sf = expr_resolve_field(&get_fdb->mac);
+ ovs_assert(mac_sf.n_bits == 48);
+ union mf_subvalue mac_sv;
+ mf_read_subfield(&mac_sf, uflow, &mac_sv);
+
+ const struct ovntrace_fdb *fdb_t
+ = ovntrace_fdb_find(dp, &mac_sv.mac);
+
+ struct mf_subfield dst = expr_resolve_field(&get_fdb->dst);
+ uint32_t val = 0;
+ if (fdb_t) {
+ val = fdb_t->port_key;
+ }
+
+ union mf_subvalue sv = { .be32_int = htonl(val) };
+ mf_write_subfield_flow(&dst, &sv, uflow);
+}
+
+static void
execute_put_opts(const struct ovnact_put_opts *po,
const char *name, struct flow *uflow,
struct ovs_list *super)
@@ -2638,6 +2762,18 @@ trace_actions(const struct ovnact *ovnacts, size_t ovnacts_len,
break;
case OVNACT_BFD_MSG:
break;
+
+ case OVNACT_PUT_FDB:
+ /* Nothing to do for tracing. */
+ break;
+
+ case OVNACT_GET_FDB:
+ execute_get_fdb(ovnact_get_GET_FDB(a), dp, uflow);
+ break;
+
+ case OVNACT_LOOKUP_FDB:
+ execute_lookup_fdb(ovnact_get_LOOKUP_FDB(a), dp, uflow, super);
+ break;
}
}
ds_destroy(&s);
--
1.8.3.1