mirror of
https://github.com/openvswitch/ovs
synced 2025-08-22 01:51:26 +00:00
ofproto-dpif: APIs and CLI option to add/delete static fdb entry.
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 <vdasari@gmail.com> Tested-by: Eelco Chaudron <echaudro@redhat.com> Acked-by: Eelco Chaudron <echaudro@redhat.com> Signed-off-by: Ilya Maximets <i.maximets@ovn.org>
This commit is contained in:
parent
ae2424696c
commit
ccc24fc88d
4
NEWS
4
NEWS
@ -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
|
||||
|
@ -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
|
||||
@ -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;
|
||||
}
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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);
|
||||
}
|
||||
@ -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
|
||||
@ -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;
|
||||
}
|
||||
|
||||
@ -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);
|
||||
}
|
||||
|
@ -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
|
||||
@ -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
|
||||
@ -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;
|
||||
@ -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,
|
||||
|
@ -7993,26 +7993,58 @@ xlate_send_packet(const struct ofport_dpif *ofport, bool oam,
|
||||
ofpacts.data, ofpacts.size, packet);
|
||||
}
|
||||
|
||||
/* 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;
|
||||
|
||||
xbridge = xbridge_lookup(xcfg, ofproto);
|
||||
if (!xbridge) {
|
||||
return 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 xlate_cfg *xcfg = ovsrcu_get(struct xlate_cfg *, &xcfgp);
|
||||
struct xbridge *xbridge;
|
||||
struct xbundle *xbundle;
|
||||
struct xbundle *xbundle = NULL;
|
||||
|
||||
xbridge = xbridge_lookup(xcfg, ofproto);
|
||||
if (!xbridge) {
|
||||
return;
|
||||
}
|
||||
|
||||
xbundle = lookup_input_bundle__(xbridge, in_port, 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
|
||||
|
@ -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 *);
|
||||
|
@ -5874,18 +5874,114 @@ ofproto_unixctl_fdb_show(struct unixctl_conn *conn, int argc OVS_UNUSED,
|
||||
LIST_FOR_EACH (e, lru_node, &ofproto->ml->lrus) {
|
||||
struct ofbundle *bundle = mac_entry_get_port(ofproto->ml, e);
|
||||
char name[OFP_MAX_PORT_NAME_LEN];
|
||||
int age = mac_entry_age(ofproto->ml, e);
|
||||
|
||||
ofputil_port_to_string(ofbundle_get_a_port(bundle)->up.ofp_port,
|
||||
NULL, name, sizeof name);
|
||||
ds_put_format(&ds, "%5s %4d "ETH_ADDR_FMT" %3d\n",
|
||||
name, e->vlan, ETH_ADDR_ARGS(e->mac),
|
||||
mac_entry_age(ofproto->ml, e));
|
||||
NULL, name, sizeof name);
|
||||
ds_put_format(&ds, "%5s %4d "ETH_ADDR_FMT" ",
|
||||
name, e->vlan, ETH_ADDR_ARGS(e->mac));
|
||||
if (MAC_ENTRY_AGE_STATIC_ENTRY == age) {
|
||||
ds_put_format(&ds, "static\n");
|
||||
} else {
|
||||
ds_put_format(&ds, "%3d\n", age);
|
||||
}
|
||||
}
|
||||
ovs_rwlock_unlock(&ofproto->ml->rwlock);
|
||||
unixctl_command_reply(conn, ds_cstr(&ds));
|
||||
ds_destroy(&ds);
|
||||
}
|
||||
|
||||
static void
|
||||
ofproto_unixctl_fdb_add(struct unixctl_conn *conn, int argc OVS_UNUSED,
|
||||
const char *argv[], void *aux OVS_UNUSED)
|
||||
{
|
||||
const struct ofproto_dpif *ofproto;
|
||||
const struct mac_entry *mac_entry;
|
||||
const struct ofbundle *bundle = NULL;
|
||||
struct ds ds = DS_EMPTY_INITIALIZER;
|
||||
struct ofproto_port ofproto_port;
|
||||
ofp_port_t in_port = OFPP_NONE;
|
||||
const char *br_name = argv[1];
|
||||
const char *port_name = argv[2];
|
||||
uint16_t vlan = atoi(argv[3]);
|
||||
struct eth_addr mac;
|
||||
int age;
|
||||
|
||||
ofproto = ofproto_dpif_lookup_by_name(br_name);
|
||||
if (!ofproto) {
|
||||
unixctl_command_reply_error(conn, "no such bridge");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!eth_addr_from_string(argv[4], &mac)) {
|
||||
unixctl_command_reply_error(conn, "bad MAC address");
|
||||
return;
|
||||
}
|
||||
|
||||
if (ofproto_port_query_by_name(&ofproto->up, port_name, &ofproto_port)) {
|
||||
unixctl_command_reply_error(conn,
|
||||
"software error, odp port is present but no ofp port");
|
||||
return;
|
||||
}
|
||||
in_port = ofproto_port.ofp_port;
|
||||
ofproto_port_destroy(&ofproto_port);
|
||||
|
||||
/* Give a bit more information if the entry being added is overriding
|
||||
* an existing entry. */
|
||||
ovs_rwlock_rdlock(&ofproto->ml->rwlock);
|
||||
mac_entry = mac_learning_lookup(ofproto->ml, mac, vlan);
|
||||
if (mac_entry) {
|
||||
bundle = mac_entry_get_port(ofproto->ml, mac_entry);
|
||||
age = mac_entry->expires;
|
||||
}
|
||||
ovs_rwlock_unlock(&ofproto->ml->rwlock);
|
||||
|
||||
if (bundle && (strcmp(bundle->name, port_name) ||
|
||||
age != MAC_ENTRY_AGE_STATIC_ENTRY)) {
|
||||
char old_port_name[OFP_MAX_PORT_NAME_LEN];
|
||||
|
||||
ofputil_port_to_string(ofbundle_get_a_port(bundle)->up.ofp_port,
|
||||
NULL, old_port_name, sizeof old_port_name);
|
||||
ds_put_format(&ds, "Overriding already existing %s entry on %s\n",
|
||||
(age == MAC_ENTRY_AGE_STATIC_ENTRY) ? "static" : "dynamic",
|
||||
old_port_name);
|
||||
}
|
||||
|
||||
if (!xlate_add_static_mac_entry(ofproto, in_port, mac, vlan)) {
|
||||
unixctl_command_reply_error(conn, "could not add static mac entry\n");
|
||||
} else {
|
||||
unixctl_command_reply(conn, ds_cstr(&ds));
|
||||
}
|
||||
|
||||
ds_destroy(&ds);
|
||||
}
|
||||
|
||||
static void
|
||||
ofproto_unixctl_fdb_delete(struct unixctl_conn *conn, int argc OVS_UNUSED,
|
||||
const char *argv[], void *aux OVS_UNUSED)
|
||||
{
|
||||
const struct ofproto_dpif *ofproto;
|
||||
const char *br_name = argv[1];
|
||||
uint16_t vlan = atoi(argv[2]);
|
||||
struct eth_addr mac;
|
||||
|
||||
ofproto = ofproto_dpif_lookup_by_name(br_name);
|
||||
if (!ofproto) {
|
||||
unixctl_command_reply_error(conn, "no such bridge");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!eth_addr_from_string(argv[3], &mac)) {
|
||||
unixctl_command_reply_error(conn, "bad MAC address");
|
||||
return;
|
||||
}
|
||||
if (!xlate_delete_static_mac_entry(ofproto, mac, vlan)) {
|
||||
unixctl_command_reply_error(conn, "could not find static mac entry\n");
|
||||
} else {
|
||||
unixctl_command_reply(conn, NULL);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
ofproto_unixctl_fdb_stats_clear(struct unixctl_conn *conn, int argc,
|
||||
const char *argv[], void *aux OVS_UNUSED)
|
||||
@ -5931,6 +6027,9 @@ ofproto_unixctl_fdb_stats_show(struct unixctl_conn *conn, int argc OVS_UNUSED,
|
||||
ds_put_format(&ds, " Current/maximum MAC entries in the table: %"
|
||||
PRIuSIZE"/%"PRIuSIZE"\n",
|
||||
hmap_count(&ofproto->ml->table), ofproto->ml->max_entries);
|
||||
ds_put_format(&ds,
|
||||
" Current static MAC entries in the table : %"PRIuSIZE"\n",
|
||||
ofproto->ml->static_entries);
|
||||
ds_put_format(&ds,
|
||||
" Total number of learned MAC entries : %"PRIu64"\n",
|
||||
ofproto->ml->total_learned);
|
||||
@ -6437,6 +6536,10 @@ ofproto_unixctl_init(void)
|
||||
}
|
||||
registered = true;
|
||||
|
||||
unixctl_command_register("fdb/add", "bridge port vlan mac", 4, 4,
|
||||
ofproto_unixctl_fdb_add, NULL);
|
||||
unixctl_command_register("fdb/del", "bridge vlan mac", 3, 3,
|
||||
ofproto_unixctl_fdb_delete, NULL);
|
||||
unixctl_command_register("fdb/flush", "[bridge]", 0, 1,
|
||||
ofproto_unixctl_fdb_flush, NULL);
|
||||
unixctl_command_register("fdb/show", "bridge", 1, 1,
|
||||
|
@ -6753,6 +6753,105 @@ PORTNAME
|
||||
portName=p2
|
||||
])])
|
||||
|
||||
AT_SETUP([ofproto-dpif - static-mac add/del/flush])
|
||||
OVS_VSWITCHD_START([set bridge br0 fail-mode=standalone])
|
||||
add_of_ports br0 1 2
|
||||
|
||||
dnl Generate some dynamic fdb entries on some ports.
|
||||
OFPROTO_TRACE([ovs-dummy], [in_port(1),eth(src=50:54:00:00:00:01)], [-generate], [100,2])
|
||||
OFPROTO_TRACE([ovs-dummy], [in_port(2),eth(src=50:54:00:00:00:02)], [-generate], [100,1])
|
||||
|
||||
dnl Add some static mac entries.
|
||||
AT_CHECK([ovs-appctl fdb/add br0 p1 0 50:54:00:00:01:01])
|
||||
AT_CHECK([ovs-appctl fdb/add br0 p2 0 50:54:00:00:02:02])
|
||||
|
||||
dnl Check initial fdb.
|
||||
AT_CHECK_UNQUOTED([ovs-appctl fdb/show br0 | sed 's/ *[[0-9]]\{1,\}$//' | grep -v port | sort], [0], [dnl
|
||||
1 0 50:54:00:00:00:01
|
||||
1 0 50:54:00:00:01:01 static
|
||||
2 0 50:54:00:00:00:02
|
||||
2 0 50:54:00:00:02:02 static
|
||||
])
|
||||
|
||||
dnl Remove static mac entry.
|
||||
AT_CHECK([ovs-appctl fdb/del br0 0 50:54:00:00:01:01])
|
||||
|
||||
dnl Check that entry is removed.
|
||||
AT_CHECK_UNQUOTED([ovs-appctl fdb/show br0 | grep "50:54:00:00:01:01"], [1], [dnl
|
||||
])
|
||||
|
||||
# Add some more cache and static entries, to test out flush operation
|
||||
for i in 0 1 2 3 4 5 6 7 8 9 a b c d e f; do
|
||||
ovs-appctl ofproto/trace ovs-dummy "in_port(1),eth(src=50:54:00:00:0$i:ff)" -generate
|
||||
ovs-appctl fdb/add br0 p2 0 50:54:00:0$i:00:ff
|
||||
done
|
||||
|
||||
for i in 0 1 2 3 4 5 6 7 8 9 a b c d e f; do
|
||||
ovs-appctl fdb/add br0 p2 0 50:54:00:0$i:00:ff
|
||||
done
|
||||
|
||||
dnl Flush mac entries, only dynamic ones should be evicted.
|
||||
AT_CHECK([ovs-appctl fdb/flush br0], [0], [dnl
|
||||
table successfully flushed
|
||||
])
|
||||
|
||||
dnl Count number of static entries remaining.
|
||||
AT_CHECK_UNQUOTED([ovs-appctl fdb/stats-show br0 | grep static], [0], [dnl
|
||||
Current static MAC entries in the table : 17
|
||||
])
|
||||
|
||||
OVS_VSWITCHD_STOP
|
||||
AT_CLEANUP
|
||||
|
||||
AT_SETUP([ofproto-dpif - static-mac mac moves])
|
||||
OVS_VSWITCHD_START([set bridge br0 fail-mode=standalone])
|
||||
add_of_ports br0 1 2
|
||||
|
||||
dnl Generate a dynamic entry.
|
||||
OFPROTO_TRACE([ovs-dummy], [in_port(1),eth(src=50:54:00:00:00:00)], [-generate], [100,2])
|
||||
|
||||
dnl Convert dynamically learnt dl_src to a static-mac.
|
||||
AT_CHECK([ovs-appctl fdb/add br0 p1 0 50:54:00:00:00:00], [0], [dnl
|
||||
Overriding already existing dynamic entry on 1
|
||||
])
|
||||
|
||||
dnl Check fdb.
|
||||
AT_CHECK_UNQUOTED([ovs-appctl fdb/show br0 | sed 's/ *[[0-9]]\{1,\}$//' | grep -v port | sort], [0], [dnl
|
||||
1 0 50:54:00:00:00:00 static
|
||||
])
|
||||
|
||||
dnl Move the static mac to different port.
|
||||
AT_CHECK([ovs-appctl fdb/add br0 p2 0 50:54:00:00:00:00], [0], [dnl
|
||||
Overriding already existing static entry on 1
|
||||
])
|
||||
|
||||
dnl Check fdb.
|
||||
AT_CHECK_UNQUOTED([ovs-appctl fdb/show br0 | sed 's/ *[[0-9]]\{1,\}$//' | grep -v port | sort], [0], [dnl
|
||||
2 0 50:54:00:00:00:00 static
|
||||
])
|
||||
|
||||
dnl static-mac should not be converted to a dynamic one when a packet with same dl_src
|
||||
dnl arrives on any port of the switch.
|
||||
dnl Packet arriving on p1.
|
||||
OFPROTO_TRACE([ovs-dummy], [in_port(1),eth(src=50:54:00:00:00:00)], [-generate], [100,2])
|
||||
AT_CHECK_UNQUOTED([ovs-appctl fdb/show br0 | sed 's/ *[[0-9]]\{1,\}$//' | grep -v port | sort], [0], [dnl
|
||||
2 0 50:54:00:00:00:00 static
|
||||
])
|
||||
|
||||
dnl Packet arriving on p2.
|
||||
OFPROTO_TRACE([ovs-dummy], [in_port(2),eth(src=50:54:00:00:00:00)], [-generate], [100,1])
|
||||
AT_CHECK_UNQUOTED([ovs-appctl fdb/show br0 | sed 's/ *[[0-9]]\{1,\}$//' | grep -v port | sort], [0], [dnl
|
||||
2 0 50:54:00:00:00:00 static
|
||||
])
|
||||
|
||||
dnl Check mac_move coverage counter mac_learning_static_none_move.
|
||||
AT_CHECK([ovs-appctl coverage/read-counter mac_learning_static_none_move], [0], [dnl
|
||||
1
|
||||
])
|
||||
|
||||
OVS_VSWITCHD_STOP
|
||||
AT_CLEANUP
|
||||
|
||||
AT_SETUP([ofproto-dpif - basic truncate action])
|
||||
OVS_VSWITCHD_START
|
||||
add_of_ports br0 1 2 3 4 5
|
||||
|
@ -159,6 +159,12 @@ If \fIbridge\fR is not specified, then displays detailed information about all
|
||||
bridges with RSTP enabled.
|
||||
.SS "BRIDGE COMMANDS"
|
||||
These commands manage bridges.
|
||||
.IP "\fBfdb/add\fR \fIbridge\fR \fIport\fR \fIvlan\fR \fImac\fR"
|
||||
Adds \fImac\fR address to a \fIport\fR and \fIvlan\fR on a \fIbridge\fR. This
|
||||
utility can be used to pre-populate fdb table without relying on dynamic
|
||||
mac learning.
|
||||
.IP "\fBfdb/del\fR \fIbridge\fR \fIvlan\fR \fImac\fR"
|
||||
Deletes \fImac\fR address from a \fIport\fR and \fIvlan\fR on a \fIbridge\fR.
|
||||
.IP "\fBfdb/flush\fR [\fIbridge\fR]"
|
||||
Flushes \fIbridge\fR MAC address learning table, or all learning tables
|
||||
if no \fIbridge\fR is given.
|
||||
|
Loading…
x
Reference in New Issue
Block a user