Skip to content

Commit

Permalink
MAC learning: Add new actions - put_fdb, get_fdb and lookup_fdb.
Browse files Browse the repository at this point in the history
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>
  • Loading branch information
numansiddique committed Feb 20, 2021
1 parent 6ec3b12 commit f819ce8
Show file tree
Hide file tree
Showing 9 changed files with 472 additions and 1 deletion.
2 changes: 2 additions & 0 deletions controller/lflow.c
Expand Up @@ -606,6 +606,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);

Expand Down
2 changes: 2 additions & 0 deletions controller/lflow.h
Expand Up @@ -72,6 +72,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
Expand Down
29 changes: 29 additions & 0 deletions include/ovn/actions.h
Expand Up @@ -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 {
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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,
Expand Down
4 changes: 4 additions & 0 deletions include/ovn/logical-fields.h
Expand Up @@ -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 */
Expand Down Expand Up @@ -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
Expand Down
176 changes: 176 additions & 0 deletions lib/actions.c
Expand Up @@ -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)
Expand Down Expand Up @@ -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);
}
Expand Down Expand Up @@ -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");
}
Expand Down
62 changes: 62 additions & 0 deletions ovn-sb.xml
Expand Up @@ -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>
Expand Down

0 comments on commit f819ce8

Please sign in to comment.