[ovs-dev] [PATCH ovn 3/6] MAC learning: Add new actions - put_fdb, get_fdb and lookup_fdb.

numans at ovn.org numans at ovn.org
Fri Feb 5 06:59:50 UTC 2021


From: Numan Siddique <numans at ovn.org>

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.

Signed-off-by: Numan Siddique <numans at ovn.org>
---
 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 946c1e04b7..baf6932d51 100644
--- a/controller/lflow.c
+++ b/controller/lflow.c
@@ -701,6 +701,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 ba79cc374f..d790d518d8 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 a2d28c6a3f..0402131777 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 aee4748562..9d8d07fab2 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 56b8fab629..b3433f49ea 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 b8912b33c8..35000ed681 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 80c9fe138a..8dbd3bf932 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 9bd13a037f..202a96c5dd 100644
--- a/tests/test-ovn.c
+++ b/tests/test-ovn.c
@@ -1348,6 +1348,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 c0a0edc0a3..fead759b49 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));
     }
 }
@@ -1053,6 +1092,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)
 {
@@ -1064,6 +1127,7 @@ read_db(void)
     read_gen_opts();
     read_flows();
     read_mac_bindings();
+    read_fdbs();
 }
 
 static const struct ovntrace_port *
@@ -2029,6 +2093,66 @@ execute_lookup_mac_bind_ip(const struct ovnact_lookup_mac_bind_ip *bind,
     mf_write_subfield_flow(&dst, &sv, uflow);
 }
 
+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,
@@ -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);
-- 
2.29.2



More information about the dev mailing list