Skip to content

Commit

Permalink
ofproto-dpif: APIs and CLI option to add/delete static fdb entry.
Browse files Browse the repository at this point in the history
Currently there is an option to add/flush/show ARP/ND neighbor. This
covers L3 side.  For L2 side, there is only fdb show command.  This
commit gives an option to add/del an fdb entry via ovs-appctl.

CLI command looks like:

To add:
    ovs-appctl fdb/add <bridge> <port> <vlan> <Mac>
    ovs-appctl fdb/add br0 p1 0 50:54:00:00:00:05

To del:
    ovs-appctl fdb/del <bridge> <vlan> <Mac>
    ovs-appctl fdb/del br0 0 50:54:00:00:00:05

Added two new APIs to provide convenient interface to add and delete
static-macs.
bool xlate_add_static_mac_entry(const struct ofproto_dpif *,
                                ofp_port_t in_port,
                                struct eth_addr dl_src, int vlan);
bool xlate_delete_static_mac_entry(const struct ofproto_dpif *,
                                   struct eth_addr dl_src, int vlan);

1. Static entry should not age.  To indicate that entry being
   programmed is a static entry, 'expires' field in 'struct mac_entry'
   will be set to a MAC_ENTRY_AGE_STATIC_ENTRY. A check for this value
   is made while deleting mac entry as part of regular aging process.
2. Another change to the mac-update logic, when a packet with same
   dl_src as that of a static-mac entry arrives on any port, the logic
   will not modify the expires field.
3. While flushing fdb entries, made sure static ones are not evicted.
4. Updated "ovs-appctl fdb/stats-show br0" to display number of static
   entries in switch

Added following tests:
  ofproto-dpif - static-mac add/del/flush
  ofproto-dpif - static-mac mac moves

Reported-at: https://mail.openvswitch.org/pipermail/ovs-discuss/2019-June/048894.html
Reported-at: https://bugzilla.redhat.com/show_bug.cgi?id=1597752
Signed-off-by: Vasu Dasari <[email protected]>
Tested-by: Eelco Chaudron <[email protected]>
Acked-by: Eelco Chaudron <[email protected]>
Signed-off-by: Ilya Maximets <[email protected]>
  • Loading branch information
vasu-dasari authored and igsilya committed Jul 16, 2021
1 parent ae24246 commit ccc24fc
Show file tree
Hide file tree
Showing 8 changed files with 412 additions and 29 deletions.
4 changes: 4 additions & 0 deletions NEWS
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,10 @@ Post-v2.15.0
- OVS now reports the datapath capability 'ct_zero_snat', which reflects
whether the SNAT with all-zero IP address is supported.
See ovs-vswitchd.conf.db(5) for details.
- ovs-appctl:
* Added ability to add and delete static mac entries using:
'ovs-appctl fdb/add <bridge> <port> <vlan> <mac>'
'ovs-appctl fdb/del <bridge> <vlan> <mac>'


v2.15.0 - 15 Feb 2021
Expand Down
151 changes: 134 additions & 17 deletions lib/mac-learning.c
Original file line number Diff line number Diff line change
Expand Up @@ -34,13 +34,24 @@ COVERAGE_DEFINE(mac_learning_learned);
COVERAGE_DEFINE(mac_learning_expired);
COVERAGE_DEFINE(mac_learning_evicted);
COVERAGE_DEFINE(mac_learning_moved);
COVERAGE_DEFINE(mac_learning_static_none_move);

/* Returns the number of seconds since 'e' (within 'ml') was last learned. */
/*
* This function will return age of mac entry in the fdb.
* It will return either one of the following:
* 1. Number of seconds since 'e' (within 'ml') was last learned.
* 2. If the mac entry is a static entry, it returns
* MAC_ENTRY_AGE_STATIC_ENTRY. */
int
mac_entry_age(const struct mac_learning *ml, const struct mac_entry *e)
{
time_t remaining = e->expires - time_now();
return ml->idle_time - remaining;
/* For static fdb entries, expires would be MAC_ENTRY_AGE_STATIC_ENTRY. */
if (MAC_ENTRY_AGE_STATIC_ENTRY == e->expires) {
return MAC_ENTRY_AGE_STATIC_ENTRY;
} else {
time_t remaining = e->expires - time_now();
return ml->idle_time - remaining;
}
}

static uint32_t
Expand Down Expand Up @@ -214,6 +225,7 @@ mac_learning_create(unsigned int idle_time)
ovs_refcount_init(&ml->ref_cnt);
ovs_rwlock_init(&ml->rwlock);
mac_learning_clear_statistics(ml);
ml->static_entries = 0;
return ml;
}

Expand Down Expand Up @@ -309,16 +321,19 @@ mac_learning_may_learn(const struct mac_learning *ml,
}

/* Searches 'ml' for and returns a MAC learning entry for 'src_mac' in 'vlan',
* inserting a new entry if necessary. The caller must have already verified,
* by calling mac_learning_may_learn(), that 'src_mac' and 'vlan' are
* learnable.
* inserting a new entry if necessary. If entry being added is a
* 1. cache entry: caller must have already verified, by calling
* mac_learning_may_learn(), that 'src_mac' and 'vlan' are learnable.
* 2. static entry: new mac static fdb entry will be created or if one
* exists already, converts that entry to a static fdb type.
*
* If the returned MAC entry is new (that is, if it has a NULL client-provided
* port, as returned by mac_entry_get_port()), then the caller must initialize
* the new entry's port to a nonnull value with mac_entry_set_port(). */
struct mac_entry *
mac_learning_insert(struct mac_learning *ml,
const struct eth_addr src_mac, uint16_t vlan)
static struct mac_entry *
mac_learning_insert__(struct mac_learning *ml, const struct eth_addr src_mac,
uint16_t vlan, bool is_static)
OVS_REQ_WRLOCK(ml->rwlock)
{
struct mac_entry *e;

Expand All @@ -336,8 +351,11 @@ mac_learning_insert(struct mac_learning *ml,
e->vlan = vlan;
e->grat_arp_lock = TIME_MIN;
e->mlport = NULL;
COVERAGE_INC(mac_learning_learned);
ml->total_learned++;
e->expires = 0;
if (!is_static) {
COVERAGE_INC(mac_learning_learned);
ml->total_learned++;
}
} else {
ovs_list_remove(&e->lru_node);
}
Expand All @@ -348,11 +366,75 @@ mac_learning_insert(struct mac_learning *ml,
ovs_list_remove(&e->port_lru_node);
ovs_list_push_back(&e->mlport->port_lrus, &e->port_lru_node);
}
e->expires = time_now() + ml->idle_time;

/* Update 'expires' for mac entry. */
if (is_static) {
/* Increment static_entries only if entry is a new one or entry is
* converted from cache to static type. */
if (e->expires != MAC_ENTRY_AGE_STATIC_ENTRY) {
ml->static_entries++;
}
e->expires = MAC_ENTRY_AGE_STATIC_ENTRY;
} else {
e->expires = time_now() + ml->idle_time;
}

return e;
}

/* Adds a new dynamic mac entry to fdb. */
struct mac_entry *
mac_learning_insert(struct mac_learning *ml,
const struct eth_addr src_mac, uint16_t vlan)
{
return mac_learning_insert__(ml, src_mac, vlan, false);
}

/* Adds a new static mac entry to fdb.
*
* Returns 'true' if mac entry is inserted, 'false' otherwise. */
bool
mac_learning_add_static_entry(struct mac_learning *ml,
const struct eth_addr src_mac, uint16_t vlan,
void *in_port)
OVS_EXCLUDED(ml->rwlock)
{
struct mac_entry *mac = NULL;
bool inserted = false;

ovs_rwlock_wrlock(&ml->rwlock);
mac = mac_learning_insert__(ml, src_mac, vlan, true);
if (mac) {
mac_entry_set_port(ml, mac, in_port);
inserted = true;
}
ovs_rwlock_unlock(&ml->rwlock);

return inserted;
}

/* Delete a static mac entry from fdb if it exists.
*
* Returns 'true' if mac entry is found, 'false' otherwise. */
bool
mac_learning_del_static_entry(struct mac_learning *ml,
const struct eth_addr dl_src, uint16_t vlan)
{
struct mac_entry *mac = NULL;
bool deleted = false;

ovs_rwlock_wrlock(&ml->rwlock);
mac = mac_learning_lookup(ml, dl_src, vlan);
if (mac && mac_entry_age(ml, mac) == MAC_ENTRY_AGE_STATIC_ENTRY) {
mac_learning_expire(ml, mac);
ml->static_entries--;
deleted = true;
}
ovs_rwlock_unlock(&ml->rwlock);

return deleted;
}

/* Checks whether a MAC learning update is necessary for MAC learning table
* 'ml' given that a packet matching 'src' was received on 'in_port' in 'vlan',
* and given that the packet was gratuitous ARP if 'is_gratuitous_arp' is
Expand All @@ -372,13 +454,32 @@ is_mac_learning_update_needed(const struct mac_learning *ml,
OVS_REQ_RDLOCK(ml->rwlock)
{
struct mac_entry *mac;
int age;

if (!mac_learning_may_learn(ml, src, vlan)) {
return false;
}

mac = mac_learning_lookup(ml, src, vlan);
if (!mac || mac_entry_age(ml, mac)) {
/* If mac entry is missing it needs to be added to fdb. */
if (!mac) {
return true;
}

age = mac_entry_age(ml, mac);
/* If mac is a static entry, then there is no need to update. */
if (age == MAC_ENTRY_AGE_STATIC_ENTRY) {
/* Coverage counter to increment when a packet with same
* static-mac appears on a different port. */
if (mac_entry_get_port(ml, mac) != in_port) {
COVERAGE_INC(mac_learning_static_none_move);
}
return false;
}

/* If entry is still alive, just update the mac_entry so, that expires
* gets updated. */
if (age > 0) {
return true;
}

Expand Down Expand Up @@ -513,13 +614,29 @@ mac_learning_expire(struct mac_learning *ml, struct mac_entry *e)
free(e);
}

/* Expires all the mac-learning entries in 'ml'. */
/* Expires all the dynamic mac-learning entries in 'ml'. */
void
mac_learning_flush(struct mac_learning *ml)
{
struct mac_entry *e;
while (get_lru(ml, &e)){
mac_learning_expire(ml, e);
struct mac_entry *e, *first_static_mac = NULL;

while (get_lru(ml, &e) && (e != first_static_mac)) {

/* Static mac should not be evicted. */
if (MAC_ENTRY_AGE_STATIC_ENTRY == e->expires) {

/* Make note of first static-mac encountered, so that this while
* loop will break on visting this mac again via get_lru(). */
if (!first_static_mac) {
first_static_mac = e;
}

/* Remove from lru head and append it to tail. */
ovs_list_remove(&e->lru_node);
ovs_list_push_back(&ml->lrus, &e->lru_node);
} else {
mac_learning_expire(ml, e);
}
}
hmap_shrink(&ml->table);
}
Expand Down
17 changes: 17 additions & 0 deletions lib/mac-learning.h
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,11 @@
* list starting from the LRU end, deleting each entry that has been idle too
* long.
*
* Fourth, a mac entry can be configured statically via API or appctl commands.
* Static entries are programmed to have an age of MAC_ENTRY_AGE_STATIC_ENTRY.
* Age of static entries will not be updated by a receiving packet as part of
* regular packet processing.
*
* Finally, the number of MAC learning table entries has a configurable maximum
* size to prevent memory exhaustion. When a new entry must be inserted but
* the table is already full, the implementation uses an eviction strategy
Expand Down Expand Up @@ -94,6 +99,9 @@ struct mac_learning;
/* Time, in seconds, before expiring a mac_entry due to inactivity. */
#define MAC_ENTRY_DEFAULT_IDLE_TIME 300

/* Age value to represent a static entry. */
#define MAC_ENTRY_AGE_STATIC_ENTRY INT_MAX

/* Time, in seconds, to lock an entry updated by a gratuitous ARP to avoid
* relearning based on a reflection from a bond member. */
#define MAC_GRAT_ARP_LOCK_TIME 5
Expand Down Expand Up @@ -156,6 +164,7 @@ struct mac_learning {
unsigned long *flood_vlans; /* Bitmap of learning disabled VLANs. */
unsigned int idle_time; /* Max age before deleting an entry. */
size_t max_entries; /* Max number of learned MACs. */
size_t static_entries; /* Current number of static MAC entries. */
struct ovs_refcount ref_cnt;
struct ovs_rwlock rwlock;
bool need_revalidate;
Expand Down Expand Up @@ -218,6 +227,14 @@ bool mac_learning_update(struct mac_learning *ml, struct eth_addr src,
int vlan, bool is_gratuitous_arp, bool is_bond,
void *in_port)
OVS_EXCLUDED(ml->rwlock);
bool mac_learning_add_static_entry(struct mac_learning *ml,
const struct eth_addr src,
uint16_t vlan, void *in_port)
OVS_EXCLUDED(ml->rwlock);
bool mac_learning_del_static_entry(struct mac_learning *ml,
const struct eth_addr src,
uint16_t vlan)
OVS_EXCLUDED(ml->rwlock);

/* Lookup. */
struct mac_entry *mac_learning_lookup(const struct mac_learning *ml,
Expand Down
48 changes: 40 additions & 8 deletions ofproto/ofproto-dpif-xlate.c
Original file line number Diff line number Diff line change
Expand Up @@ -7993,26 +7993,58 @@ xlate_send_packet(const struct ofport_dpif *ofport, bool oam,
ofpacts.data, ofpacts.size, packet);
}

void
xlate_mac_learning_update(const struct ofproto_dpif *ofproto,
ofp_port_t in_port, struct eth_addr dl_src,
int vlan, bool is_grat_arp)
/* Get xbundle for a ofp_port in a ofproto datapath. */
static struct xbundle*
ofp_port_to_xbundle(const struct ofproto_dpif *ofproto, ofp_port_t ofp_port)
{
struct xlate_cfg *xcfg = ovsrcu_get(struct xlate_cfg *, &xcfgp);
struct xbridge *xbridge;
struct xbundle *xbundle;

xbridge = xbridge_lookup(xcfg, ofproto);
if (!xbridge) {
return;
return NULL;
}

xbundle = lookup_input_bundle__(xbridge, in_port, NULL);
return lookup_input_bundle__(xbridge, ofp_port, NULL);
}

void
xlate_mac_learning_update(const struct ofproto_dpif *ofproto,
ofp_port_t in_port, struct eth_addr dl_src,
int vlan, bool is_grat_arp)
{
struct xbundle *xbundle = NULL;

xbundle = ofp_port_to_xbundle(ofproto, in_port);
if (!xbundle) {
return;
}

update_learning_table__(xbridge, xbundle, dl_src, vlan, is_grat_arp);
update_learning_table__(xbundle->xbridge,
xbundle, dl_src, vlan, is_grat_arp);
}

bool
xlate_add_static_mac_entry(const struct ofproto_dpif *ofproto,
ofp_port_t in_port,
struct eth_addr dl_src, int vlan)
{
struct xbundle *xbundle = ofp_port_to_xbundle(ofproto, in_port);

/* Return here if xbundle is NULL. */
if (!xbundle || (xbundle == &ofpp_none_bundle)) {
return false;
}

return mac_learning_add_static_entry(ofproto->ml, dl_src, vlan,
xbundle->ofbundle);
}

bool
xlate_delete_static_mac_entry(const struct ofproto_dpif *ofproto,
struct eth_addr dl_src, int vlan)
{
return mac_learning_del_static_entry(ofproto->ml, dl_src, vlan);
}

void
Expand Down
5 changes: 5 additions & 0 deletions ofproto/ofproto-dpif-xlate.h
Original file line number Diff line number Diff line change
Expand Up @@ -225,6 +225,11 @@ int xlate_send_packet(const struct ofport_dpif *, bool oam, struct dp_packet *);
void xlate_mac_learning_update(const struct ofproto_dpif *ofproto,
ofp_port_t in_port, struct eth_addr dl_src,
int vlan, bool is_grat_arp);
bool xlate_add_static_mac_entry(const struct ofproto_dpif *,
ofp_port_t in_port,
struct eth_addr dl_src, int vlan);
bool xlate_delete_static_mac_entry(const struct ofproto_dpif *,
struct eth_addr dl_src, int vlan);

void xlate_set_support(const struct ofproto_dpif *,
const struct dpif_backer_support *);
Expand Down
Loading

0 comments on commit ccc24fc

Please sign in to comment.