SONIC VLAN配置流程
sonic vlan配置通过订阅config_db的键空间事件完成vlan配置信息从config_db到内核和硬件。config_db.json格式如下:
"VLAN": {
"Vlan1000": {
"vlanid": "1000"
}
},
"VLAN_MEMBER": {
"Vlan1000|Ethernet16": {
"tagging_mode": "untagged"
}
},
"VLAN_INTERFACE": {
"Vlan1000|192.168.0.1/27": {}
},
在swss容器中存在一个vlanmgrd进行用于订阅config_db的vlan键空间,响应vlan配置,解析后将其同步到内核和app_db。由portsorch订阅app_db vlan事件,将其同步到asic-db,最终将其同步到硬件。VLAN_INTERFACE接口信息由intfmgr和intforch进行处理。
vlanmgr
该进程运行在swss容器中,用于订阅config_db的vlan键空间,响应vlan配置,解析后将其同步到内核和app_db。
该部分涉及文件:
vlanmgrd.cpp
vlanmgr.cpp
vlanmgr.h
class VlanMgr : public Orch
{
public:
VlanMgr(DBConnector *cfgDb, DBConnector *appDb, DBConnector *stateDb, const vector<string> &tableNames);
using Orch::doTask;
private:
ProducerStateTable m_appVlanTableProducer, m_appVlanMemberTableProducer;
Table m_cfgVlanTable, m_cfgVlanMemberTable;
Table m_statePortTable, m_stateLagTable;
Table m_stateVlanTable;
std::set<std::string> m_vlans;
void doTask(Consumer &consumer);//主任务
void doVlanTask(Consumer &consumer);//处理vlan事件
void doVlanMemberTask(Consumer &consumer);//处理vlan成员事件
void processUntaggedVlanMembers(string vlan, const string &members);//处理vlan内untag成员,暂时没用
bool addHostVlan(int vlan_id);//将vlan同步到host,即创建vlan
bool removeHostVlan(int vlan_id);//删除host中的vlan
bool setHostVlanAdminState(int vlan_id, const string &admin_status);//设置hostvlan状态
bool setHostVlanMtu(int vlan_id, uint32_t mtu);//设置host vlan接口的mtu
//添加成员
bool addHostVlanMember(int vlan_id, const string &port_alias, const string& tagging_mode);
bool removeHostVlanMember(int vlan_id, const string &port_alias);//删除host vlan成员
bool isMemberStateOk(const string &alias);//从state db中获取成员端口是否ok
bool isVlanStateOk(const string &alias);//从state db中获取vlan是否ok
bool isVlanMacOk();//判断该vlan的mac地址是否ok
};
实现
int main(int argc, char **argv)
{
Logger::linkToDbNative("vlanmgrd");
SWSS_LOG_ENTER();
SWSS_LOG_NOTICE("--- Starting vlanmgrd ---");
try
{
vector<string> cfg_vlan_tables = {//订阅了两个config db table
CFG_VLAN_TABLE_NAME,
CFG_VLAN_MEMBER_TABLE_NAME,
};
//连接了三个数据库
DBConnector cfgDb(CONFIG_DB, DBConnector::DEFAULT_UNIXSOCKET, 0);//读和订阅config-db数据
DBConnector appDb(APPL_DB, DBConnector::DEFAULT_UNIXSOCKET, 0);//将配置希尔app-db
DBConnector stateDb(STATE_DB, DBConnector::DEFAULT_UNIXSOCKET, 0);//获取vlan端口是否准备好
/*
* swss service starts after interfaces-config.service which will have
* switch_mac set.
* Dynamic switch_mac update is not supported for now.
* 获取全局的mac地址作为vlan-if的mac地址
*/
Table table(&cfgDb, "DEVICE_METADATA");
std::vector<FieldValueTuple> ovalues;
table.get("localhost", ovalues);
auto it = std::find_if( ovalues.begin(), ovalues.end(), [](const FieldValueTuple& t){ return t.first == "mac";} );
if ( it == ovalues.end() ) {
throw runtime_error("couldn't find MAC address of the device from config DB");
}
gMacAddress = MacAddress(it->second);
//构造vlanmgr
VlanMgr vlanmgr(&cfgDb, &appDb, &stateDb, cfg_vlan_tables);
std::vector<Orch *> cfgOrchList = {&vlanmgr};
swss::Select s;
for (Orch *o : cfgOrchList)
{
s.addSelectables(o->getSelectables());
}
SWSS_LOG_NOTICE("starting main loop");
while (true)
{
Selectable *sel;
int ret;
ret = s.select(&sel, SELECT_TIMEOUT);
if (ret == Select::ERROR)
{
SWSS_LOG_NOTICE("Error: %s!", strerror(errno));
continue;
}
if (ret == Select::TIMEOUT)
{
vlanmgr.doTask();
continue;
}
auto *c = (Executor *)sel;
c->execute();
}
}
catch(const std::exception &e)
{
SWSS_LOG_ERROR("Runtime error: %s", e.what());
}
return -1;
}
VlanMgr::doTask(Consumer &consumer)
//vlanmgr主任务,vlan没有sync进程监听内核的netlink事件,内核与硬件的vlan事件都
//由vlanmgr负责,vlanmgr负责在linux内核创建vlan和vlan-member,同时往app-db中写入
//vlan事件。同时vlanif的IP地址在intfmgr中进行添加,并同步到内核
void VlanMgr::doTask(Consumer &consumer)
{
SWSS_LOG_ENTER();
//获取事件表格名
string table_name = consumer.getTableName();
if (table_name == CFG_VLAN_TABLE_NAME)
{
doVlanTask(consumer);//vlan添加创建
}
else if (table_name == CFG_VLAN_MEMBER_TABLE_NAME)
{
doVlanMemberTask(consumer);//vlan成员添加创建
}
else
{
SWSS_LOG_ERROR("Unknown config table %s ", table_name.c_str());
throw runtime_error("VlanMgr doTask failure.");
}
}
VlanMgr::doVlanTask(Consumer &consumer)
//执行vlan事件
void VlanMgr::doVlanTask(Consumer &consumer)
{
if (!isVlanMacOk())//vlanmac是否准备好了,没有的话等待,这里就是个判断,vlanmac在vlanmgr起来时已经获取了
{
SWSS_LOG_DEBUG("VLAN mac not ready, delaying VLAN task");
return;
}
auto it = consumer.m_toSync.begin();
while (it != consumer.m_toSync.end())
{
auto &t = it->second;
string key = kfvKey(t);
/* Ensure the key starts with "Vlan" otherwise ignore */
if (strncmp(key.c_str(), VLAN_PREFIX, 4))
{
SWSS_LOG_ERROR("Invalid key format. No 'Vlan' prefix: %s", key.c_str());
it = consumer.m_toSync.erase(it);
continue;
}
int vlan_id;
vlan_id = stoi(key.substr(4));
string vlan_alias, port_alias;
string op = kfvOp(t);
if (op == SET_COMMAND)
{
string admin_status;
uint32_t mtu = 0;
vector<FieldValueTuple> fvVector;
string members;
/* Add host VLAN when it has not been created. */
//添加vlan
if (m_vlans.find(key) == m_vlans.end())
{
addHostVlan(vlan_id);
}
/* set up host env .... */
//设置host vlan相关信息
for (auto i : kfvFieldsValues(t))
{
/* Set vlan admin status 设置管理状态*/
if (fvField(i) == "admin_status")
{
admin_status = fvValue(i);
setHostVlanAdminState(vlan_id, admin_status);
fvVector.push_back(i);
}
/* Set vlan mtu 设置mtu */
else if (fvField(i) == "mtu")
{
mtu = (uint32_t)stoul(fvValue(i));
/*
* TODO: support host VLAN mtu setting.
* Host VLAN mtu should be set only after member configured
* and VLAN state is not UNKNOWN.
*/
SWSS_LOG_DEBUG("%s mtu %u: Host VLAN mtu setting to be supported.", key.c_str(), mtu);
fvVector.push_back(i);
}
else if (fvField(i) == "members@") {
members = fvValue(i);
}
}
/* fvVector should not be empty */
if (fvVector.empty())
{
FieldValueTuple a("admin_status", "up");
fvVector.push_back(a);
}
//写入app-db
m_appVlanTableProducer.set(key, fvVector);
m_vlans.insert(key);
//写入state-db(6)
fvVector.clear();
FieldValueTuple s("state", "ok");
fvVector.push_back(s);
m_stateVlanTable.set(key, fvVector);
it = consumer.m_toSync.erase(it);
/*
* Members configured together with VLAN in untagged mode.
* This is to be compatible with access VLAN configuration from minigraph.
* untag模式配置vlan时,会跟随着vlan一起下发,所以在这里解决vlan成员添加问题。
* tag vlan则在单独的mem表中添加。
*/
if (!members.empty())
{
processUntaggedVlanMembers(key, members);
}
}
else if (op == DEL_COMMAND)//删除
{
if (m_vlans.find(key) != m_vlans.end())
{
removeHostVlan(vlan_id);//找到删除
m_vlans.erase(key);
m_appVlanTableProducer.del(key);//通知app-db
m_stateVlanTable.del(key);//删除状态
}
else
{
SWSS_LOG_ERROR("%s doesn't exist", key.c_str());
}
SWSS_LOG_DEBUG("%s", (dumpTuple(consumer, t)).c_str());
it = consumer.m_toSync.erase(it);
}
else
{
SWSS_LOG_ERROR("Unknown operation type %s", op.c_str());
SWSS_LOG_DEBUG("%s", (dumpTuple(consumer, t)).c_str());
it = consumer.m_toSync.erase(it);
}
}
}
void VlanMgr::doVlanMemberTask(Consumer &consumer)
//处理vlan-member事件
void VlanMgr::doVlanMemberTask(Consumer &consumer)
{
auto it = consumer.m_toSync.begin();
while (it != consumer.m_toSync.end())
{
auto &t = it->second;
string key = kfvKey(t);
/* Ensure the key starts with "Vlan" otherwise ignore */
if (strncmp(key.c_str(), VLAN_PREFIX, 4))
{
SWSS_LOG_ERROR("Invalid key format. No 'Vlan' prefix: %s", key.c_str());
it = consumer.m_toSync.erase(it);
continue;
}
key = key.substr(4);
size_t found = key.find(CONFIGDB_KEY_SEPARATOR);
int vlan_id;
string vlan_alias, port_alias;
if (found != string::npos)
{
vlan_id = stoi(key.substr(0, found));
port_alias = key.substr(found+1);
}
else
{
SWSS_LOG_ERROR("Invalid key format. No member port is presented: %s",
kfvKey(t).c_str());
it = consumer.m_toSync.erase(it);
continue;
}
//获取vlan名字
vlan_alias = VLAN_PREFIX + to_string(vlan_id);
string op = kfvOp(t);
// TODO: store port/lag/VLAN data in local data structure and perform more validations.
if (op == SET_COMMAND)
{
/* Don't proceed if member port/lag is not ready yet */
if (!isMemberStateOk(port_alias) || !isVlanStateOk(vlan_alias))
{
SWSS_LOG_DEBUG("%s not ready, delaying", kfvKey(t).c_str());
it++;
continue;
}
string tagging_mode = "untagged";
for (auto i : kfvFieldsValues(t))
{
if (fvField(i) == "tagging_mode")
{
tagging_mode = fvValue(i);
}
}
if (tagging_mode != "untagged" &&
tagging_mode != "tagged" &&
tagging_mode != "priority_tagged")
{
SWSS_LOG_ERROR("Wrong tagging_mode '%s' for key: %s", tagging_mode.c_str(), kfvKey(t).c_str());
it = consumer.m_toSync.erase(it);
continue;
}
//添加vlan成员
if (addHostVlanMember(vlan_id, port_alias, tagging_mode))
{
key = VLAN_PREFIX + to_string(vlan_id);
key += DEFAULT_KEY_SEPARATOR;
key += port_alias;//写入app-db数据库
m_appVlanMemberTableProducer.set(key, kfvFieldsValues(t));
}
it = consumer.m_toSync.erase(it);
}
else if (op == DEL_COMMAND)
{
removeHostVlanMember(vlan_id, port_alias);
key = VLAN_PREFIX + to_string(vlan_id);
key += DEFAULT_KEY_SEPARATOR;
key += port_alias;
m_appVlanMemberTableProducer.del(key);
SWSS_LOG_DEBUG("%s", (dumpTuple(consumer, t)).c_str());
it = consumer.m_toSync.erase(it);
}
else
{
SWSS_LOG_ERROR("Unknown operation type %s", op.c_str());
it = consumer.m_toSync.erase(it);
}
}
}
同步app_db数据到asic_db
app_db的vlan事件由portsorch进行同步,该部分涉及文件:
portsorch.cpp
portsorch.h
app_db中数据样例:
127.0.0.1:6379> KEYS *VLAN*
1) "VLAN_TABLE:Vlan1000"
2) "VLAN_MEMBER_TABLE:Vlan1000:Ethernet16"
127.0.0.1:6379> HGETALL "VLAN_MEMBER_TABLE:Vlan1000:Ethernet16"
1) "tagging_mode"
2) "untagged"
127.0.0.1:6379> HGETALL "VLAN_TABLE:Vlan1000"
1) "admin_status"
2) "up"
127.0.0.1:6379>
void PortsOrch::doTask(Consumer &consumer)
void PortsOrch::doTask(Consumer &consumer)
{
SWSS_LOG_ENTER();
string table_name = consumer.getTableName();
if (table_name == APP_PORT_TABLE_NAME)
{
doPortTask(consumer);
}
else
{
/* Wait for all ports to be initialized */
if (!isInitDone())
{
return;
}
if (table_name == APP_VLAN_TABLE_NAME)//处理vlan
{
doVlanTask(consumer);
}
else if (table_name == APP_VLAN_MEMBER_TABLE_NAME)//处理vlan成员
{
doVlanMemberTask(consumer);
}
else if (table_name == APP_LAG_TABLE_NAME)
{
doLagTask(consumer);
}
else if (table_name == APP_LAG_MEMBER_TABLE_NAME)
{
doLagMemberTask(consumer);
}
}
}
void PortsOrch::doVlanTask(Consumer &consumer)
void PortsOrch::doVlanTask(Consumer &consumer)
{
SWSS_LOG_ENTER();
auto it = consumer.m_toSync.begin();
while (it != consumer.m_toSync.end())
{
auto &t = it->second;
string key = kfvKey(t);
/* Ensure the key starts with "Vlan" otherwise ignore */
if (strncmp(key.c_str(), VLAN_PREFIX, 4))
{
SWSS_LOG_ERROR("Invalid key format. No 'Vlan' prefix: %s", key.c_str());
it = consumer.m_toSync.erase(it);
continue;
}
int vlan_id;
vlan_id = stoi(key.substr(4)); // FIXME: might raise exception
string vlan_alias, port_alias;
vlan_alias = VLAN_PREFIX + to_string(vlan_id);
string op = kfvOp(t);
if (op == SET_COMMAND)
{
/*
* 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.
* 创建一个vlan
*/
if (m_portList.find(vlan_alias) == m_portList.end())
{
if (!addVlan(vlan_alias))
{
it++;
continue;
}
}
it = consumer.m_toSync.erase(it);
}
else if (op == DEL_COMMAND)
{
Port vlan;
getPort(vlan_alias, vlan);
if (removeVlan(vlan))
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);
}
}
}
void PortsOrch::doVlanMemberTask(Consumer &consumer)
void PortsOrch::doVlanMemberTask(Consumer &consumer)
{
SWSS_LOG_ENTER();
auto it = consumer.m_toSync.begin();
while (it != consumer.m_toSync.end())
{
auto &t = it->second;
string key = kfvKey(t);
/* Ensure the key starts with "Vlan" otherwise ignore */
if (strncmp(key.c_str(), VLAN_PREFIX, 4))
{
SWSS_LOG_ERROR("Invalid key format. No 'Vlan' prefix: %s", key.c_str());
it = consumer.m_toSync.erase(it);
continue;
}
key = key.substr(4);
size_t found = key.find(':');
int vlan_id;
string vlan_alias, port_alias;
if (found != string::npos)
{
vlan_id = stoi(key.substr(0, found)); // FIXME: might raise exception
port_alias = key.substr(found+1);
}
else
{
SWSS_LOG_ERROR("Invalid key format. No member port is presented: %s",
kfvKey(t).c_str());
it = consumer.m_toSync.erase(it);
continue;
}
//vlan名字
vlan_alias = VLAN_PREFIX + to_string(vlan_id);
string op = kfvOp(t);
assert(m_portList.find(vlan_alias) != m_portList.end());
Port vlan, port;
/* When VLAN member is to be created before VLAN is created */
if (!getPort(vlan_alias, vlan))
{
SWSS_LOG_INFO("Failed to locate VLAN %s", vlan_alias.c_str());
it++;
continue;
}
if (!getPort(port_alias, port))
{
SWSS_LOG_DEBUG("%s is not not yet created, delaying", port_alias.c_str());
it++;
continue;
}
if (op == SET_COMMAND)
{
string tagging_mode = "untagged";
for (auto i : kfvFieldsValues(t))
{
if (fvField(i) == "tagging_mode")
tagging_mode = fvValue(i);
}
if (tagging_mode != "untagged" &&
tagging_mode != "tagged" &&
tagging_mode != "priority_tagged")
{
SWSS_LOG_ERROR("Wrong tagging_mode '%s' for key: %s", tagging_mode.c_str(), kfvKey(t).c_str());
it = consumer.m_toSync.erase(it);
continue;
}
/* Duplicate entry */
if (vlan.m_members.find(port_alias) != vlan.m_members.end())
{
it = consumer.m_toSync.erase(it);
continue;
}
//加入vlan的端口将会创建一个桥接口,桥接口是一个二层口的抽象,屏蔽lag,phy等接口的差异
if (addBridgePort(port) && addVlanMember(vlan, port, tagging_mode))
it = consumer.m_toSync.erase(it);
else
it++;
}
else if (op == DEL_COMMAND)
{
if (vlan.m_members.find(port_alias) != vlan.m_members.end())
{
if (removeVlanMember(vlan, port))
{
if (port.m_vlan_members.empty())
{
removeBridgePort(port);
}
it = consumer.m_toSync.erase(it);
}
else
{
it++;
}
}
else
/* Cannot locate the VLAN */
it = consumer.m_toSync.erase(it);
}
else
{
SWSS_LOG_ERROR("Unknown operation type %s", op.c_str());
it = consumer.m_toSync.erase(it);
}
}
}
配置host vlan步骤
sudo ip link add Bridge type bridge up #创建一个网桥,等价于sudo brctl addbr Bridge
sudo bridge vlan add vid 6 dev Bridge self #给网桥创建一个vlan表项,vid为6
#创建一个vlan虚接口,其附着在Bridge接口上,设置其mac为00:00:00:00:00:01,这是一个vlan-if接口
sudo ip link add link Bridge up name Vlan6 address 00:00:00:00:00:01 type vlan id 6
#将网卡添加到Bridge中,等价于命令sudo brctl addif Bridge Ethernet32
sudo ip link set Ethernet32 master Bridge
#配置网卡Ethernet32以tagged模式加入vlan6
sudo bridge vlan add vid 6 dev Ethernet32 tagged
查看vlan配置
admin@switch:~$ sudo bridge vlan
port vlan ids
docker0 None
Bridge 6
Ethernet32 6
admin@switch:~$
从上面可以看出,有两个设备加入了vlan6:Bridge和Ethernet32