Skip to content

Commit

Permalink
[FDB orch]: Fdb orchagent to support fast-reboot (sonic-net#206)
Browse files Browse the repository at this point in the history
  • Loading branch information
pavel-shirshov authored May 9, 2017
1 parent 132672f commit eed4a2d
Show file tree
Hide file tree
Showing 4 changed files with 282 additions and 9 deletions.
18 changes: 18 additions & 0 deletions doc/swss-schema.md
Original file line number Diff line number Diff line change
Expand Up @@ -169,6 +169,24 @@ and reflects the LAG ports into the redis under: `LAG_TABLE:<team0>:port`
neigh = 12HEXDIG ; mac address of the neighbor
family = "IPv4" / "IPv6" ; address family

---------------------------------------------
###FDB_TABLE

; Stores FDB entries which were inserted into SAI state manually
; Notes:
; - only unicast FDB entries supported
; - only Vlan interfaces are supported
key = FDB_TABLE:"Vlan"vlanid:mac_address ; mac address will be inserted to FDB for the vlan interface
port = ifName ; interface where the entry is bound to
type = "static" / "dynamic" ; type of the entry

Example:
127.0.0.1:6379> hgetall FDB_TABLE:Vlan2:52-54-00-25-06-E9
1) "port"
2) "Ethernet0"
3) "type"
4) "static"

---------------------------------------------
###QUEUE_TABLE

Expand Down
246 changes: 243 additions & 3 deletions orchagent/fdborch.cpp
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
#include <assert.h>
#include <iostream>
#include <vector>

#include "logger.h"
#include "tokenize.h"
#include "fdborch.h"

extern sai_fdb_api_t *sai_fdb_api;
Expand All @@ -19,15 +21,21 @@ void FdbOrch::update(sai_fdb_event_t type, const sai_fdb_entry_t* entry, sai_obj
case SAI_FDB_EVENT_LEARNED:
if (!m_portsOrch->getPort(portOid, update.port))
{
SWSS_LOG_ERROR("Failed to get port for %lu OID\n", portOid);
SWSS_LOG_ERROR("Failed to get port for %lu OID", portOid);
return;
}

update.add = true;

(void)m_entries.insert(update.entry);
SWSS_LOG_DEBUG("FdbOrch notification: mac %s was inserted into vlan %d", update.entry.mac.to_string().c_str(), entry->vlan_id);
break;
case SAI_FDB_EVENT_AGED:
case SAI_FDB_EVENT_FLUSHED:
update.add = false;

(void)m_entries.erase(update.entry);
SWSS_LOG_DEBUG("FdbOrch notification: mac %s was removed from vlan %d", update.entry.mac.to_string().c_str(), entry->vlan_id);
break;
}

Expand All @@ -51,15 +59,247 @@ bool FdbOrch::getPort(const MacAddress& mac, uint16_t vlan, Port& port)
sai_status_t status = sai_fdb_api->get_fdb_entry_attribute(&entry, 1, &attr);
if (status != SAI_STATUS_SUCCESS)
{
SWSS_LOG_INFO("Failed to get port for FDB entry OID\n");
SWSS_LOG_ERROR("Failed to get port for FDB entry OID");
return false;
}

if (!m_portsOrch->getPort(attr.value.oid, port))
{
SWSS_LOG_ERROR("Failed to get port for %lu OID\n", attr.value.oid);
SWSS_LOG_ERROR("Failed to get port for %lu OID", attr.value.oid);
return false;
}

return true;
}

void FdbOrch::doTask(Consumer& consumer)
{
SWSS_LOG_ENTER();

auto it = consumer.m_toSync.begin();
while (it != consumer.m_toSync.end())
{
KeyOpFieldsValuesTuple t = it->second;

string key = kfvKey(t);
string op = kfvOp(t);

FdbEntry entry;
if (!splitKey(key, entry))
{
it = consumer.m_toSync.erase(it);
continue;
}

if (op == SET_COMMAND)
{
string port;
string type;
bool port_defined = false;
bool type_defined = false;

for (auto i : kfvFieldsValues(t))
{
if (fvField(i) == "port")
{
port = fvValue(i);
port_defined = true;
}

if (fvField(i) == "type")
{
type = fvValue(i);
type_defined = true;
}
}

if (!port_defined)
{
SWSS_LOG_ERROR("FDB entry with key:'%s' must have 'port' attribute", key.c_str());
it = consumer.m_toSync.erase(it);
continue;
}

if (!type_defined)
{
SWSS_LOG_ERROR("FDB entry with key:'%s' must have 'type' attribute", key.c_str());
it = consumer.m_toSync.erase(it);
continue;
}

// check that type either static or dynamic
if (type != "static" && type != "dynamic")
{
SWSS_LOG_ERROR("FDB entry with key: '%s' has type '%s'. But allowed only types: 'static' or 'dynamic'",
key.c_str(), type.c_str());
it = consumer.m_toSync.erase(it);
continue;
}

if (addFdbEntry(entry, port, type))
it = consumer.m_toSync.erase(it);
else
it++;

// Remove AppDb entry if FdbEntry type == 'dynamic'
if (type == "dynamic")
m_table.del(key);
}
else if (op == DEL_COMMAND)
{
if (removeFdbEntry(entry))
it = consumer.m_toSync.erase(it);
else
it++;

}
else
{
SWSS_LOG_ERROR("Unknown operation type %s", op.c_str());
it = consumer.m_toSync.erase(it);
}
}
}

bool FdbOrch::addFdbEntry(const FdbEntry& entry, const string& port_name, const string& type)
{
SWSS_LOG_ENTER();

SWSS_LOG_DEBUG("mac=%s, vlan=%d. port_name %s. type %s",
entry.mac.to_string().c_str(), entry.vlan, port_name.c_str(), type.c_str());

if (m_entries.count(entry) != 0) // we already have such entries
{
// FIXME: should we check that the entry are moving to another port?
// FIXME: should we check that the entry are changing its type?
SWSS_LOG_ERROR("FDB entry already exists. mac=%s vlan=%d", entry.mac.to_string().c_str(), entry.vlan);
return true;
}

sai_status_t status;

sai_fdb_entry_t fdb_entry;
memcpy(fdb_entry.mac_address, entry.mac.getMac(), sizeof(sai_mac_t));
fdb_entry.vlan_id = entry.vlan;

Port port;
if (!m_portsOrch->getPort(port_name, port))
{
SWSS_LOG_ERROR("Failed to get port id for %s", port_name.c_str());
return true;
}

sai_attribute_t attr;
vector<sai_attribute_t> attrs;

attr.id = SAI_FDB_ENTRY_ATTR_TYPE;
attr.value.s32 = (type == "dynamic") ? SAI_FDB_ENTRY_DYNAMIC : SAI_FDB_ENTRY_STATIC;
attrs.push_back(attr);

attr.id = SAI_FDB_ENTRY_ATTR_PORT_ID;
attr.value.oid = port.m_port_id;
attrs.push_back(attr);

attr.id = SAI_FDB_ENTRY_ATTR_PACKET_ACTION;
attr.value.s32 = SAI_PACKET_ACTION_FORWARD;
attrs.push_back(attr);

status = sai_fdb_api->create_fdb_entry(&fdb_entry, attrs.size(), attrs.data());
if (status != SAI_STATUS_SUCCESS)
{
SWSS_LOG_ERROR("Failed to add FDB entry. mac=%s, vlan=%d. port_name %s. type %s",
entry.mac.to_string().c_str(), entry.vlan, port_name.c_str(), type.c_str());
return true;
}

(void)m_entries.insert(entry);

return true;
}

bool FdbOrch::removeFdbEntry(const FdbEntry& entry)
{
SWSS_LOG_ENTER();

SWSS_LOG_DEBUG("mac=%s, vlan=%d", entry.mac.to_string().c_str(), entry.vlan);

if (m_entries.count(entry) == 0)
{
SWSS_LOG_ERROR("FDB entry isn't found. mac=%s vlan=%d", entry.mac.to_string().c_str(), entry.vlan);
return true;
}

sai_status_t status;
sai_fdb_entry_t fdb_entry;
memcpy(fdb_entry.mac_address, entry.mac.getMac(), sizeof(sai_mac_t));
fdb_entry.vlan_id = entry.vlan;

status = sai_fdb_api->remove_fdb_entry(&fdb_entry);
if (status != SAI_STATUS_SUCCESS)
{
SWSS_LOG_ERROR("Failed to remove FDB entry. mac=%s, vlan=%d",
entry.mac.to_string().c_str(), entry.vlan);
return true;
}

(void)m_entries.erase(entry);

return true;
}

bool FdbOrch::splitKey(const string& key, FdbEntry& entry)
{
SWSS_LOG_ENTER();

string mac_address_str;
string vlan_str;

auto fields = tokenize(key, ':');

if (fields.size() < 2 || fields.size() > 2)
{
SWSS_LOG_ERROR("Failed to parse key: %s", key.c_str());
return false;
}

vlan_str = fields[0];
mac_address_str = fields[1];

if (vlan_str.length() <= 4) // "Vlan"
{
SWSS_LOG_ERROR("Failed to extract vlan interface name from the key: %s", key.c_str());
return false;
}

uint8_t mac_array[6];
if (!MacAddress::parseMacString(mac_address_str, mac_array))
{
SWSS_LOG_ERROR("Failed to parse mac address: %s in key: %s", mac_address_str.c_str(), key.c_str());
return false;
}

if (mac_array[0] & 0x01)
{
SWSS_LOG_ERROR("Mac address %s in key %s should be unicast", mac_address_str.c_str(), key.c_str());
return false;
}

entry.mac = MacAddress(mac_array);

Port port;
if (!m_portsOrch->getPort(vlan_str, port))
{
SWSS_LOG_ERROR("Failed to get port for %s", vlan_str.c_str());
return false;
}

if (port.m_type != Port::VLAN)
{
SWSS_LOG_ERROR("Port %s from key %s must be a vlan port", vlan_str.c_str(), key.c_str());
return false;
}

entry.vlan = port.m_vlan_id;

return true;
}
23 changes: 19 additions & 4 deletions orchagent/fdborch.h
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,12 @@
struct FdbEntry
{
MacAddress mac;
uint16_t vlan;
sai_vlan_id_t vlan;

bool operator<(const FdbEntry& other) const
{
return tie(mac, vlan) < tie(other.mac, other.vlan);
}
};

struct FdbUpdate
Expand All @@ -18,11 +23,13 @@ struct FdbUpdate
bool add;
};

class FdbOrch: public Subject
class FdbOrch: public Orch, public Subject
{
public:
FdbOrch(PortsOrch *port) :
m_portsOrch(port)
FdbOrch(DBConnector *db, string tableName, PortsOrch *port) :
Orch(db, tableName),
m_portsOrch(port),
m_table(Table(m_db, tableName))
{
}

Expand All @@ -31,6 +38,14 @@ class FdbOrch: public Subject

private:
PortsOrch *m_portsOrch;
set<FdbEntry> m_entries;
Table m_table;

void doTask(Consumer& consumer);

bool addFdbEntry(const FdbEntry&, const string&, const string&);
bool removeFdbEntry(const FdbEntry&);
bool splitKey(const string&, FdbEntry&);
};

#endif /* SWSS_FDBORCH_H */
4 changes: 2 additions & 2 deletions orchagent/orchdaemon.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ bool OrchDaemon::init()
};

gPortsOrch = new PortsOrch(m_applDb, ports_tables);
gFdbOrch = new FdbOrch(gPortsOrch);
gFdbOrch = new FdbOrch(m_applDb, APP_FDB_TABLE_NAME, gPortsOrch);
IntfsOrch *intfs_orch = new IntfsOrch(m_applDb, APP_INTF_TABLE_NAME);
NeighOrch *neigh_orch = new NeighOrch(m_applDb, APP_NEIGH_TABLE_NAME, intfs_orch);
RouteOrch *route_orch = new RouteOrch(m_applDb, APP_ROUTE_TABLE_NAME, neigh_orch);
Expand Down Expand Up @@ -79,7 +79,7 @@ bool OrchDaemon::init()
};
AclOrch *acl_orch = new AclOrch(m_applDb, acl_tables, gPortsOrch, mirror_orch);

m_orchList = { gPortsOrch, intfs_orch, neigh_orch, route_orch, copp_orch, tunnel_decap_orch, qos_orch, buffer_orch, mirror_orch, acl_orch};
m_orchList = { gPortsOrch, intfs_orch, neigh_orch, route_orch, copp_orch, tunnel_decap_orch, qos_orch, buffer_orch, mirror_orch, acl_orch, gFdbOrch};
m_select = new Select();

return true;
Expand Down

0 comments on commit eed4a2d

Please sign in to comment.