Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
112 changes: 112 additions & 0 deletions cfgmgr/vlanmgr.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -413,10 +413,22 @@ void VlanMgr::doVlanTask(Consumer &consumer)
mac = fvValue(i);
setHostVlanMac(vlan_id, mac);
}
else if (fvField(i) == "mac_learning")
{
/* Propagate the per-VLAN MAC-learning setting to APPL_DB for PortsOrch. */
fvVector.push_back(i);
}
else if (fvField(i) == "host_ifname")
{
hostif_name = fvValue(i);
}
/* Propagate per-VLAN BUM flood control to APPL_DB for PortsOrch. */
else if (fvField(i) == "unknown_unicast_flood" ||
fvField(i) == "unknown_multicast_flood" ||
fvField(i) == "broadcast_flood")
{
fvVector.push_back(i);
}
}
/* fvVector should not be empty */
if (fvVector.empty())
Expand Down Expand Up @@ -770,6 +782,102 @@ void VlanMgr::doVlanPacPortTask(Consumer &consumer)
}
}

void VlanMgr::doFdbTask(Consumer &consumer)
{
SWSS_LOG_ENTER();

auto it = consumer.m_toSync.begin();

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

/* CONFIG_DB FDB key format: <VLAN_name>|<MAC_address> */
vector<string> keys = tokenize(kfvKey(t), config_db_key_delimiter, 1);
if (keys.size() != 2 ||
keys[0].compare(0, strlen(VLAN_PREFIX), VLAN_PREFIX) != 0)
{
SWSS_LOG_ERROR("Invalid FDB key %s, expected <Vlan>|<mac>", kfvKey(t).c_str());
it = consumer.m_toSync.erase(it);
continue;
}

/* stoi() and MacAddress() both throw on malformed input; a bad key must
* not crash vlanmgrd, so parse defensively and skip on failure. */
int vlan_id = 0;
MacAddress mac;
try
{
vlan_id = stoi(keys[0].substr(strlen(VLAN_PREFIX)));
mac = MacAddress(keys[1]);
}
catch (const std::exception &e)
{
SWSS_LOG_ERROR("Invalid FDB key %s (%s), skipping", kfvKey(t).c_str(), e.what());
it = consumer.m_toSync.erase(it);
continue;
}

string op = kfvOp(t);

/* APPL_DB FDB_TABLE key: Vlan<id>:<mac> (consumed by FdbOrch) */
string key = VLAN_PREFIX + to_string(vlan_id);
key += DEFAULT_KEY_SEPARATOR;
key += mac.to_string();

if (op == SET_COMMAND)
{
/*
* A static FDB entry may be configured before its port joins the
* VLAN; FdbOrch keeps it as saved FDB until then. Defer only until
* the VLAN itself exists.
*/
if (!m_vlans.count(keys[0]))
{
SWSS_LOG_INFO("Vlan %s not ready, defer static FDB for mac %s",
keys[0].c_str(), keys[1].c_str());
it++;
continue;
}

string port, type = "static";
for (auto i : kfvFieldsValues(t))
{
if (fvField(i) == "port")
{
port = fvValue(i);
}
else if (fvField(i) == "type")
{
type = fvValue(i);
}
}

if (port.empty())
{
SWSS_LOG_ERROR("Static FDB %s has no port, skipping", key.c_str());
it = consumer.m_toSync.erase(it);
continue;
}

vector<FieldValueTuple> fvVector;
fvVector.emplace_back("port", port);
fvVector.emplace_back("type", type);
fvVector.emplace_back("discard", "false");
m_appFdbTableProducer.set(key, fvVector);

SWSS_LOG_NOTICE("Set static FDB %s port %s type %s", key.c_str(), port.c_str(), type.c_str());
}
else if (op == DEL_COMMAND)
{
m_appFdbTableProducer.del(key);
SWSS_LOG_NOTICE("Del static FDB %s", key.c_str());
}

it = consumer.m_toSync.erase(it);
}
}

void VlanMgr::doVlanPacFdbTask(Consumer &consumer)
{
auto it = consumer.m_toSync.begin();
Expand Down Expand Up @@ -988,6 +1096,10 @@ void VlanMgr::doTask(Consumer &consumer)
{
doVlanMemberTask(consumer);
}
else if (table_name == CFG_FDB_TABLE_NAME)
{
doFdbTask(consumer);
}
else if (table_name == STATE_OPER_PORT_TABLE_NAME)
{
doVlanPacPortTask(consumer);
Expand Down
1 change: 1 addition & 0 deletions cfgmgr/vlanmgr.h
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ class VlanMgr : public Orch
void doVlanPacPortTask(Consumer &consumer);
void doVlanPacFdbTask(Consumer &consumer);
void doVlanPacVlanMemberTask(Consumer &consumer);
void doFdbTask(Consumer &consumer);
void addPortToVlan(const std::string& port_alias, const std::string& vlan_alias, const std::string& tagging_mode);
void removePortFromVlan(const std::string& port_alias, const std::string& vlan_alias);
};
Expand Down
1 change: 1 addition & 0 deletions cfgmgr/vlanmgrd.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ int main(int argc, char **argv)
vector<string> cfg_vlan_tables = {
CFG_VLAN_TABLE_NAME,
CFG_VLAN_MEMBER_TABLE_NAME,
CFG_FDB_TABLE_NAME,
};
vector<string> state_vlan_tables = {
STATE_OPER_PORT_TABLE_NAME,
Expand Down
1 change: 1 addition & 0 deletions orchagent/port.h
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,7 @@ struct VlanInfo
sai_vlan_id_t vlan_id = 0;
sai_object_id_t host_intf_id = SAI_NULL_OBJECT_ID;
sai_vlan_flood_control_type_t uuc_flood_type = SAI_VLAN_FLOOD_CONTROL_TYPE_ALL;
sai_vlan_flood_control_type_t umc_flood_type = SAI_VLAN_FLOOD_CONTROL_TYPE_ALL;
sai_vlan_flood_control_type_t bc_flood_type = SAI_VLAN_FLOOD_CONTROL_TYPE_ALL;
sai_object_id_t l2mc_group_id = SAI_NULL_OBJECT_ID;
endpoint_ip_l2mc_group_member_map_t l2mc_members;
Expand Down
134 changes: 131 additions & 3 deletions orchagent/portsorch.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5901,6 +5901,8 @@ void PortsOrch::doVlanTask(Consumer &consumer)
uint32_t mtu = 0;
MacAddress mac;
string hostif_name = "";
string mac_learning = "";
string uuc_flood = "", umc_flood = "", bc_flood = "";
for (auto i : kfvFieldsValues(t))
{
if (fvField(i) == "mtu")
Expand All @@ -5915,12 +5917,28 @@ void PortsOrch::doVlanTask(Consumer &consumer)
{
hostif_name = fvValue(i);
}
if (fvField(i) == "mac_learning")
{
mac_learning = fvValue(i);
}
if (fvField(i) == "unknown_unicast_flood")
{
uuc_flood = fvValue(i);
}
if (fvField(i) == "unknown_multicast_flood")
{
umc_flood = fvValue(i);
}
if (fvField(i) == "broadcast_flood")
{
bc_flood = fvValue(i);
}
}

/*
* Only creation is supported for now.
* We may add support for VLAN mac learning enable/disable,
* VLAN flooding control setting and etc. in the future.
* The VLAN is created on first sight. VLAN attribute updates
* (MTU, MAC, MAC learning, BUM flood control, host interface)
* are applied below on both create and subsequent change.
*/
if (m_portList.find(vlan_alias) == m_portList.end())
{
Expand Down Expand Up @@ -5957,6 +5975,14 @@ void PortsOrch::doVlanTask(Consumer &consumer)
gIntfsOrch->setRouterIntfsMac(vl);
}
}
if (!mac_learning.empty())
{
setVlanMacLearn(vl, mac_learning);
}
if (!uuc_flood.empty() || !umc_flood.empty() || !bc_flood.empty())
{
setVlanFloodControl(vl, uuc_flood, umc_flood, bc_flood);
}
if (!hostif_name.empty())
{
if (!createVlanHostIntf(vl, hostif_name))
Expand Down Expand Up @@ -7562,6 +7588,108 @@ bool PortsOrch::setBridgePortLearnMode(Port &port, sai_bridge_port_fdb_learning_
return true;
}

bool PortsOrch::setVlanMacLearn(Port &vlan, const string &mac_learning)
{
SWSS_LOG_ENTER();

sai_attribute_t attr;
attr.id = SAI_VLAN_ATTR_LEARN_DISABLE;
attr.value.booldata = (mac_learning == "disabled");

sai_status_t status = sai_vlan_api->set_vlan_attribute(vlan.m_vlan_info.vlan_oid, &attr);
if (status != SAI_STATUS_SUCCESS)
{
SWSS_LOG_ERROR("Failed to set MAC learning to %s on VLAN %s, rv:%d",
mac_learning.c_str(), vlan.m_alias.c_str(), status);
task_process_status handle_status = handleSaiSetStatus(SAI_API_VLAN, status);
if (handle_status != task_success)
{
return parseHandleSaiStatusFailure(handle_status);
}
}

SWSS_LOG_NOTICE("Set MAC learning to %s on VLAN %s", mac_learning.c_str(), vlan.m_alias.c_str());
return true;
}

bool PortsOrch::setVlanFloodControl(Port &vlan, const string &uuc_flood,
const string &umc_flood, const string &bc_flood)
{
SWSS_LOG_ENTER();

/*
* Config-driven per-VLAN BUM flood control applies only to a pure-L2 VLAN.
* If a VXLAN/EVPN flood group (COMBINED) already owns the flood type, leave
* it untouched so that feature keeps working.
*/
if (vlan.m_vlan_info.uuc_flood_type == SAI_VLAN_FLOOD_CONTROL_TYPE_COMBINED ||
vlan.m_vlan_info.bc_flood_type == SAI_VLAN_FLOOD_CONTROL_TYPE_COMBINED)
{
SWSS_LOG_WARN("VLAN %s uses a VXLAN/EVPN flood group; skipping per-VLAN BUM flood config",
vlan.m_alias.c_str());
return true;
}

auto applyFlood = [&](const string &value, sai_vlan_attr_t attr_id,
sai_vlan_flood_control_type_t &field) -> bool
{
if (value.empty())
{
return true;
}

sai_vlan_flood_control_type_t flood_type =
(value == "disabled") ? SAI_VLAN_FLOOD_CONTROL_TYPE_NONE
: SAI_VLAN_FLOOD_CONTROL_TYPE_ALL;

sai_attribute_t attr;
attr.id = attr_id;
attr.value.s32 = flood_type;

sai_status_t status = sai_vlan_api->set_vlan_attribute(vlan.m_vlan_info.vlan_oid, &attr);
if (status != SAI_STATUS_SUCCESS)
{
SWSS_LOG_ERROR("Failed to set flood control attr %d on VLAN %s, rv:%d",
attr_id, vlan.m_alias.c_str(), status);
task_process_status handle_status = handleSaiSetStatus(SAI_API_VLAN, status);
if (handle_status != task_success)
{
return parseHandleSaiStatusFailure(handle_status);
}
}

field = flood_type;
return true;
};

/*
* applyFlood() updates the cached flood type in vlan.m_vlan_info only for
* each attribute it programs successfully. Persist the (possibly partial)
* result to m_portList unconditionally so the cache stays consistent with
* the ASIC even if one attribute set fails midway.
*/
bool applied = applyFlood(uuc_flood, SAI_VLAN_ATTR_UNKNOWN_UNICAST_FLOOD_CONTROL_TYPE,
vlan.m_vlan_info.uuc_flood_type) &&
applyFlood(umc_flood, SAI_VLAN_ATTR_UNKNOWN_MULTICAST_FLOOD_CONTROL_TYPE,
vlan.m_vlan_info.umc_flood_type) &&
applyFlood(bc_flood, SAI_VLAN_ATTR_BROADCAST_FLOOD_CONTROL_TYPE,
vlan.m_vlan_info.bc_flood_type);

m_portList[vlan.m_alias] = vlan;

if (!applied)
{
return false;
}

SWSS_LOG_NOTICE("Set per-VLAN BUM flood on %s (uuc=%s umc=%s bc=%s)",
vlan.m_alias.c_str(),
uuc_flood.empty() ? "-" : uuc_flood.c_str(),
umc_flood.empty() ? "-" : umc_flood.c_str(),
bc_flood.empty() ? "-" : bc_flood.c_str());
return true;
}

bool PortsOrch::addVlan(string vlan_alias)
{
SWSS_LOG_ENTER();
Expand Down
2 changes: 2 additions & 0 deletions orchagent/portsorch.h
Original file line number Diff line number Diff line change
Expand Up @@ -460,6 +460,8 @@ class PortsOrch : public Orch, public Subject

bool addVlan(string vlan);
bool removeVlan(Port vlan);
bool setVlanMacLearn(Port &vlan, const string &mac_learning);
bool setVlanFloodControl(Port &vlan, const std::string &uuc_flood, const std::string &umc_flood, const std::string &bc_flood);

bool addLag(string lag, uint32_t spa_id, int32_t switch_id);
bool removeLag(Port lag);
Expand Down
6 changes: 6 additions & 0 deletions tests/dvslib/dvs_vlan.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,12 @@ def create_vlan_with_mac(self, vlanID, mac):
vlan_entry = {"vlanid": vlanID, "mac": mac}
self.config_db.create_entry("VLAN", vlan, vlan_entry)

def set_vlan_property(self, vlanID, property, value):
Comment thread
github-advanced-security[bot] marked this conversation as resolved.
Fixed
vlan = "Vlan{}".format(vlanID)
vlan_entry = self.config_db.get_entry("VLAN", vlan)
vlan_entry[property] = value
self.config_db.update_entry("VLAN", vlan, vlan_entry)

def create_vlan_interface(self, vlanID):
vlan = "Vlan{}".format(vlanID)
vlan_intf_entry = {}
Expand Down
Loading
Loading