sonic 接口管理处理流程
同步 config_db.json 中接口 IP 信息到内核
该部分通过 redis 的键空间机制订阅键 config_db 的键事件,将其同步到内核,本进程存在 swss docker 中。
相关文件:
intfmgrd.cpp
intfmgrd.h
intfmgr.cpp
IntfMgr
class IntfMgr : public Orch
{
public:
IntfMgr(DBConnector *cfgDb, DBConnector *appDb, DBConnector *stateDb, const vector<string> &tableNames);
using Orch::doTask;
private:
ProducerStateTable m_appIntfTableProducer;// 作为 appdb 的生产者
Table m_cfgIntfTable, m_cfgVlanIntfTable;
Table m_statePortTable, m_stateLagTable, m_stateVlanTable;
bool setIntfIp(const string &alias, const string &opCmd, const string &ipPrefixStr);
void doTask(Consumer &consumer);
bool isIntfStateOk(const string &alias);
};
}
实现
// 该进程的功能是根据配置文件中的接口地址信息配置 linux 内核。// 使用如下命令进行配置://ip address add 10.254.229.226/27 dev Ethernet20
// 不支持在加载配置后使用 ip address add 命令修改地址,想要修改数据口地址需要更改 config_db
// 后重新 reload 配置。// 本进程的目标是将配置文件的关于接口 IP 的信息同步到内核。int main(int argc, char **argv)
{Logger::linkToDbNative("intfmgrd");
SWSS_LOG_ENTER();
SWSS_LOG_NOTICE("--- Starting intfmgrd ---");
try
{
// 订阅了 db4 中的三个表格
vector<string> cfg_intf_tables = {
CFG_INTF_TABLE_NAME,
CFG_LAG_INTF_TABLE_NAME,
CFG_VLAN_INTF_TABLE_NAME,
};
// 连接 CONFIG_DB,APPL_DB,STATE_DB,用于读取信息
DBConnector cfgDb(CONFIG_DB, DBConnector::DEFAULT_UNIXSOCKET, 0);
DBConnector appDb(APPL_DB, DBConnector::DEFAULT_UNIXSOCKET, 0);
DBConnector stateDb(STATE_DB, DBConnector::DEFAULT_UNIXSOCKET, 0);
IntfMgr intfmgr(&cfgDb, &appDb, &stateDb, cfg_intf_tables);
// TODO: add tables in stateDB which interface depends on to monitor list
std::vector<Orch *> cfgOrchList = {&intfmgr};
swss::Select s;
for (Orch *o : cfgOrchList)// 运行 epoll,监听数据库信息
{s.addSelectables(o->getSelectables());
}
SWSS_LOG_NOTICE("starting main loop");
while (true)
{
Selectable *sel;
int ret;
// 一秒超时,即使订阅的三个数据库没有事件发生也要周期性的处理事件,类似于实现了一个 1 秒定时器
ret = s.select(&sel, SELECT_TIMEOUT);
if (ret == Select::ERROR)
{SWSS_LOG_NOTICE("Error: %s!", strerror(errno));
continue;
}
// 超时依然需要运行该函数
if (ret == Select::TIMEOUT)
{intfmgr.doTask();
continue;
}
auto *c = (Executor *)sel;
c->execute();// 执行 do-task}
}
catch(const std::exception &e)
{SWSS_LOG_ERROR("Runtime error: %s", e.what());
}
return -1;
}
#define VLAN_PREFIX "Vlan"
#define LAG_PREFIX "PortChannel"
IntfMgr::IntfMgr(DBConnector *cfgDb, DBConnector *appDb, DBConnector *stateDb, const vector<string> &tableNames) :
Orch(cfgDb, tableNames),
m_cfgIntfTable(cfgDb, CFG_INTF_TABLE_NAME),
m_cfgVlanIntfTable(cfgDb, CFG_VLAN_INTF_TABLE_NAME),
m_statePortTable(stateDb, STATE_PORT_TABLE_NAME),// 读取 port 状态信息
m_stateLagTable(stateDb, STATE_LAG_TABLE_NAME),// 读取 lag 状态信息
m_stateVlanTable(stateDb, STATE_VLAN_TABLE_NAME),// 读取 vlan 状态信息
m_appIntfTableProducer(appDb, APP_INTF_TABLE_NAME)// 作为 appdb 的生产者,将配置文件的接口配置信息写入 APP_INTF_TABLE_NAME 表
{
}
// 调用 ip address add 10.254.229.226/27 dev Ethernet20 命令设置接口 IP
bool IntfMgr::setIntfIp(const string &alias, const string &opCmd, const string &ipPrefixStr)
{
stringstream cmd;
string res;
cmd << IP_CMD << "address" << opCmd << "" << ipPrefixStr <<" dev " << alias;;
int ret = swss::exec(cmd.str(), res);
return (ret == 0);
}
// 从 db 6 中获取端口状态信息
bool IntfMgr::isIntfStateOk(const string &alias)
{
vector<FieldValueTuple> temp;
if (!alias.compare(0, strlen(VLAN_PREFIX), VLAN_PREFIX))//vlan 接口
{if (m_stateVlanTable.get(alias, temp))
{SWSS_LOG_DEBUG("Vlan %s is ready", alias.c_str());
return true;
}
}
else if (!alias.compare(0, strlen(LAG_PREFIX), LAG_PREFIX))//lag 接口
{if (m_stateLagTable.get(alias, temp))
{SWSS_LOG_DEBUG("Lag %s is ready", alias.c_str());
return true;
}
}
else if (m_statePortTable.get(alias, temp))//port 接口
{SWSS_LOG_DEBUG("Port %s is ready", alias.c_str());
return true;
}
return false;
}
//"INTERFACE|Ethernet20|10.254.229.226/27"
//"PORTCHANNEL_INTERFACE|PortChannel1|10.8.8.200/24"
// 该程序会处理 INTERFACE,PORTCHANNEL_INTERFACE,VLAN_INTERFACE 三类接口信息
// 配置了 IP,interface 表示一个三层的概念,包括子接口,vlanif,lag-if,phy-if
void IntfMgr::doTask(Consumer &consumer)
{SWSS_LOG_ENTER();
auto it = consumer.m_toSync.begin();
while (it != consumer.m_toSync.end())
{
KeyOpFieldsValuesTuple t = it->second;
string keySeparator = CONFIGDB_KEY_SEPARATOR;
vector<string> keys = tokenize(kfvKey(t), keySeparator[0]);
string alias(keys[0]);//keys[0] 为 INTERFACE
//vlan 接口的 ip 地址到此结束
if (alias.compare(0, strlen(VLAN_PREFIX), VLAN_PREFIX))
{
/* handle IP over vlan Only for now, skip the rest */
it = consumer.m_toSync.erase(it);
continue;
}
size_t pos = kfvKey(t).find(CONFIGDB_KEY_SEPARATOR);
if (pos == string::npos)
{SWSS_LOG_DEBUG("Invalid key %s", kfvKey(t).c_str());
it = consumer.m_toSync.erase(it);
continue;
}
// 提取 key 中的 10.254.229.226/27 部分
IpPrefix ip_prefix(kfvKey(t).substr(pos+1));
SWSS_LOG_DEBUG("intfs doTask: %s", (dumpTuple(consumer, t)).c_str());
string op = kfvOp(t);
if (op == SET_COMMAND)
{
/*
* Don't proceed if port/lag/VLAN is not ready yet.
* The pending task will be checked periodially and retried.
* TODO: Subscribe to stateDB for port/lag/VLAN state and retry
* pending tasks immediately upon state change.
* 由定时器超时 1 秒触发一次,只有没有 up 的端口将会由定时器持续检查。*/
if (!isIntfStateOk(alias))// 判断端口是否已经 ok
{SWSS_LOG_DEBUG("Interface is not ready, skipping %s", kfvKey(t).c_str());
it++;
continue;
}
string opCmd("add");
string ipPrefixStr = ip_prefix.to_string();
// 设置接口 IP 地址
setIntfIp(alias, opCmd, ipPrefixStr);
}
else if (op == DEL_COMMAND)
{string opCmd("del");
string ipPrefixStr = ip_prefix.to_string();
setIntfIp(alias, opCmd, ipPrefixStr);
}
it = consumer.m_toSync.erase(it);
continue;
}
}
响应内核接口 IP 信息变化到 APP_DB
前面分析过 sonic 对 netlink 的相关处理,此处不再赘述。
该部分的主要文件有:
intfsync.cpp
intfsync.h
intfsyncd.cpp
实现 NetMsg
class IntfSync : public NetMsg
{
public:
enum {MAX_ADDR_SIZE = 64};
IntfSync(DBConnector *db);// 连接数据库 APP_DB
virtual void onMsg(int nlmsg_type, struct nl_object *obj);
private:
ProducerStateTable m_intfTable;//app_db 的 INTF_TABLE 表生产者实例
};
}
// 消息处理函数
void IntfSync::onMsg(int nlmsg_type, struct nl_object *obj)
{char addrStr[MAX_ADDR_SIZE + 1] = {0};
struct rtnl_addr *addr = (struct rtnl_addr *)obj;
string key;
string scope = "global";
string family;
// 响应新地址,获取地址,删除地址三个信息
if ((nlmsg_type != RTM_NEWADDR) && (nlmsg_type != RTM_GETADDR) &&
(nlmsg_type != RTM_DELADDR))
return;
/* Don't sync local routes,不同步 local 地址信息 */
if (rtnl_addr_get_scope(addr) != RT_SCOPE_UNIVERSE)
{
scope = "local";
return;
}
if (rtnl_addr_get_family(addr) == AF_INET)
family = IPV4_NAME;
else if (rtnl_addr_get_family(addr) == AF_INET6)
family = IPV6_NAME;
else
// Not supported
return;
// 获取接口名字以及地址,组合成 key
key = LinkCache::getInstance().ifindexToName(rtnl_addr_get_ifindex(addr));
key+= ":";
nl_addr2str(rtnl_addr_get_local(addr), addrStr, MAX_ADDR_SIZE);
key+= addrStr;
if (nlmsg_type == RTM_DELADDR)// 地址删除,删除 key
{m_intfTable.del(key);
return;
}
// 添加 key
std::vector<FieldValueTuple> fvVector;
FieldValueTuple f("family", family);
FieldValueTuple s("scope", scope);
fvVector.push_back(s);
fvVector.push_back(f);
m_intfTable.set(key, fvVector);
}
实现 main
// 进程运行在容器 swss 中
int main(int argc, char **argv)
{swss::Logger::linkToDbNative("intfsyncd");
DBConnector db(APPL_DB, DBConnector::DEFAULT_UNIXSOCKET, 0);// 连接 APPL_DB
IntfSync sync(&db);// 实例化 netmsg
// 订阅消息,加入组播组
NetDispatcher::getInstance().registerMessageHandler(RTM_NEWADDR, &sync);
NetDispatcher::getInstance().registerMessageHandler(RTM_DELADDR, &sync);
while (1)
{
try
{
NetLink netlink;
Select s;
netlink.registerGroup(RTNLGRP_IPV4_IFADDR);
netlink.registerGroup(RTNLGRP_IPV6_IFADDR);
cout << "Listens to interface messages..." << endl;
netlink.dumpRequest(RTM_GETADDR);// 打印所有地址
s.addSelectable(&netlink);// 加入 select 事件
while (true)
{
Selectable *temps;
s.select(&temps);// 监听 select 事件
}
}
catch (const std::exception& e)
{cout << "Exception \"" << e.what() << "\" had been thrown in deamon" << endl;
return 0;
}
}
return 1;
}
写入 app_db 的数据案例:
127.0.0.1:6379> HGETALL "INTF_TABLE:PortChannel1:10.8.8.200/24"
1) "scope"
2) "global"
3) "family"
4) "IPv4"
127.0.0.1:6379>
响应 APP_DB 的 INTF_TABLE 表信息变化到 ASIC_DB
该部分作为 app_db 的一个订阅者,订阅 INTF_TABLE 变化。添加一个 IP 则在 sonic 层添加一个 rif 口,一个物理口只能添加一个 IP。
主要相关文件:
intfsorch.cpp
intfsorch.h
该部分收到 INTF_TABLE 信息变化后会产生四部分信息:
- 添加 rif 接口,一个物理接口一个 IP,则一个 rif,这个是从 linux 内核角度出发的,实际上在使用的时候一个屋里接口会创建多个 rif。
- rif 即为子网,会创建网段路由,添加子网。
- 创建 32 位掩码的本机路由。
- 添加广播地址邻居
该部分会将 linux 内核的 local 路由表同步到 sonic 中。
IntfsOrch
class IntfsOrch : public Orch
{
public:
IntfsOrch(DBConnector *db, string tableName);// 订阅 table
sai_object_id_t getRouterIntfsId(const string&);
std::set<IpPrefix> getRouterIntfsIpAddresses(const string&);
void increaseRouterIntfsRefCount(const string&);
void decreaseRouterIntfsRefCount(const string&);
private:
IntfsTable m_syncdIntfses;
void doTask(Consumer &consumer);
int getRouterIntfsRefCount(const string&);
bool addRouterIntfs(Port &port);
bool removeRouterIntfs(Port &port);
// 添加 rif 网段子网路由
void addSubnetRoute(const Port &port, const IpPrefix &ip_prefix);
void removeSubnetRoute(const Port &port, const IpPrefix &ip_prefix);
// 添加本机路由
void addIp2MeRoute(const IpPrefix &ip_prefix);
void removeIp2MeRoute(const IpPrefix &ip_prefix);
// 添加邻居
void addDirectedBroadcast(const Port &port, const IpAddress &ip_addr);
void removeDirectedBroadcast(const Port &port, const IpAddress &ip_addr);
};
具体实现
const int intfsorch_pri = 35;
// 构造函数,生成一个 orch,订阅表格 APP_INTF_TABLE_NAME
IntfsOrch::IntfsOrch(DBConnector *db, string tableName) :
Orch(db, tableName, intfsorch_pri)
{SWSS_LOG_ENTER();
}
// 根据接口名获取 port 相关信息
sai_object_id_t IntfsOrch::getRouterIntfsId(const string &alias)
{
Port port;
gPortsOrch->getPort(alias, port);
assert(port.m_rif_id);
return port.m_rif_id;
}
void IntfsOrch::increaseRouterIntfsRefCount(const string &alias)
{SWSS_LOG_ENTER();
m_syncdIntfses[alias].ref_count++;
SWSS_LOG_DEBUG("Router interface %s ref count is increased to %d",
alias.c_str(), m_syncdIntfses[alias].ref_count);
}
void IntfsOrch::decreaseRouterIntfsRefCount(const string &alias)
{SWSS_LOG_ENTER();
m_syncdIntfses[alias].ref_count--;
SWSS_LOG_DEBUG("Router interface %s ref count is decreased to %d",
alias.c_str(), m_syncdIntfses[alias].ref_count);
}
void IntfsOrch::doTask(Consumer &consumer)
{SWSS_LOG_ENTER();
if (!gPortsOrch->isInitDone())
{return;}
auto it = consumer.m_toSync.begin();
while (it != consumer.m_toSync.end())
{
KeyOpFieldsValuesTuple t = it->second;
vector<string> keys = tokenize(kfvKey(t), ':');
string alias(keys[0]);
IpPrefix ip_prefix(kfvKey(t).substr(kfvKey(t).find(':')+1));
if (alias == "eth0" || alias == "docker0")
{it = consumer.m_toSync.erase(it);
continue;
}
string op = kfvOp(t);
if (op == SET_COMMAND)
{if (alias == "lo")
{addIp2MeRoute(ip_prefix);
it = consumer.m_toSync.erase(it);
continue;
}
Port port;
if (!gPortsOrch->getPort(alias, port))
{
/* TODO: Resolve the dependency relationship and add ref_count to port */
it++;
continue;
}
// buffer configuration hasn't been applied yet, hold from intf config.
if (!gBufferOrch->isPortReady(alias))
{
it++;
continue;
}
auto it_intfs = m_syncdIntfses.find(alias);
if (it_intfs == m_syncdIntfses.end())
{if (addRouterIntfs(port))
{
IntfsEntry intfs_entry;
intfs_entry.ref_count = 0;
m_syncdIntfses[alias] = intfs_entry;
}
else
{
it++;
continue;
}
}
if (m_syncdIntfses[alias].ip_addresses.count(ip_prefix))
{
/* Duplicate entry */
it = consumer.m_toSync.erase(it);
continue;
}
/* NOTE: Overlap checking is required to handle ifconfig weird behavior.
* When set IP address using ifconfig command it applies it in two stages.
* On stage one it sets IP address with netmask /8. On stage two it
* changes netmask to specified in command. As DB is async event to
* add IP address with original netmask may come before event to
* delete IP with netmask /8. To handle this we in case of overlap
* we should wait until entry with /8 netmask will be removed.
* Time frame between those event is quite small.*/
bool overlaps = false;
for (const auto &prefixIt: m_syncdIntfses[alias].ip_addresses)
{if (prefixIt.isAddressInSubnet(ip_prefix.getIp()) ||
ip_prefix.isAddressInSubnet(prefixIt.getIp()))
{
overlaps = true;
SWSS_LOG_NOTICE("Router interface %s IP %s overlaps with %s.", port.m_alias.c_str(),
prefixIt.to_string().c_str(), ip_prefix.to_string().c_str());
break;
}
}
if (overlaps)
{
/* Overlap of IP address network */
++it;
continue;
}
addSubnetRoute(port, ip_prefix);
addIp2MeRoute(ip_prefix);
if(port.m_type == Port::VLAN && ip_prefix.isV4())
{addDirectedBroadcast(port, ip_prefix.getBroadcastIp());
}
m_syncdIntfses[alias].ip_addresses.insert(ip_prefix);
it = consumer.m_toSync.erase(it);
}
else if (op == DEL_COMMAND)
{if (alias == "lo")
{removeIp2MeRoute(ip_prefix);
it = consumer.m_toSync.erase(it);
continue;
}
Port port;
/* Cannot locate interface */
if (!gPortsOrch->getPort(alias, port))
{it = consumer.m_toSync.erase(it);
continue;
}
if (m_syncdIntfses.find(alias) != m_syncdIntfses.end())
{if (m_syncdIntfses[alias].ip_addresses.count(ip_prefix))
{removeSubnetRoute(port, ip_prefix);
removeIp2MeRoute(ip_prefix);
if(port.m_type == Port::VLAN && ip_prefix.isV4())
{removeDirectedBroadcast(port, ip_prefix.getBroadcastIp());
}
m_syncdIntfses[alias].ip_addresses.erase(ip_prefix);
}
/* Remove router interface that no IP addresses are associated with */
if (m_syncdIntfses[alias].ip_addresses.size() == 0)
{if (removeRouterIntfs(port))
{m_syncdIntfses.erase(alias);
it = consumer.m_toSync.erase(it);
}
else
it++;
}
else
{it = consumer.m_toSync.erase(it);
}
}
else
/* Cannot locate the interface */
it = consumer.m_toSync.erase(it);
}
}
}
// 添加路由接口 rif
bool IntfsOrch::addRouterIntfs(Port &port)
{SWSS_LOG_ENTER();
/* Return true if the router interface exists */
if (port.m_rif_id)
{
SWSS_LOG_WARN("Router interface already exists on %s",
port.m_alias.c_str());
return true;
}
/* Create router interface if the router interface doesn't exist */
sai_attribute_t attr;
vector<sai_attribute_t> attrs;
attr.id = SAI_ROUTER_INTERFACE_ATTR_VIRTUAL_ROUTER_ID;
attr.value.oid = gVirtualRouterId;
attrs.push_back(attr);
attr.id = SAI_ROUTER_INTERFACE_ATTR_SRC_MAC_ADDRESS;
memcpy(attr.value.mac, gMacAddress.getMac(), sizeof(sai_mac_t));
attrs.push_back(attr);
attr.id = SAI_ROUTER_INTERFACE_ATTR_TYPE;
switch(port.m_type)
{
case Port::PHY:
case Port::LAG:
attr.value.s32 = SAI_ROUTER_INTERFACE_TYPE_PORT;
break;
case Port::VLAN:
attr.value.s32 = SAI_ROUTER_INTERFACE_TYPE_VLAN;
break;
default:
SWSS_LOG_ERROR("Unsupported port type: %d", port.m_type);
break;
}
attrs.push_back(attr);
switch(port.m_type)
{
case Port::PHY:
attr.id = SAI_ROUTER_INTERFACE_ATTR_PORT_ID;
attr.value.oid = port.m_port_id;
break;
case Port::LAG:
attr.id = SAI_ROUTER_INTERFACE_ATTR_PORT_ID;
attr.value.oid = port.m_lag_id;
break;
case Port::VLAN:
attr.id = SAI_ROUTER_INTERFACE_ATTR_VLAN_ID;
attr.value.oid = port.m_vlan_info.vlan_oid;
break;
default:
SWSS_LOG_ERROR("Unsupported port type: %d", port.m_type);
break;
}
attrs.push_back(attr);
attr.id = SAI_ROUTER_INTERFACE_ATTR_MTU;
attr.value.u32 = port.m_mtu;
attrs.push_back(attr);
try {gCrmOrch->preCrmResCounter(CrmResourceType::CRM_SUBNET_TABLE);
sai_status_t status = sai_router_intfs_api->create_router_interface(&port.m_rif_id, gSwitchId, (uint32_t)attrs.size(), attrs.data());
if (status != SAI_STATUS_SUCCESS)
{SWSS_LOG_ERROR("Failed to create router interface for port %s, rv:%d", port.m_alias.c_str(), status);
throw runtime_error("Failed to create router interface.");
}
gCrmOrch->incCrmResUsedCounter(CrmResourceType::CRM_SUBNET_TABLE);
} catch (...) {SWSS_LOG_ERROR("Failed to create router interface for port %s", port.m_alias.c_str());
return false;
}
gPortsOrch->setPort(port.m_alias, port);
SWSS_LOG_NOTICE("Create router interface for port %s mtu %u", port.m_alias.c_str(), port.m_mtu);
return true;
}
bool IntfsOrch::removeRouterIntfs(Port &port)
{SWSS_LOG_ENTER();
if (m_syncdIntfses[port.m_alias].ref_count > 0)
{SWSS_LOG_NOTICE("Router interface is still referenced");
return false;
}
try {sai_status_t status = sai_router_intfs_api->remove_router_interface(port.m_rif_id);
if (status != SAI_STATUS_SUCCESS)
{SWSS_LOG_ERROR("Failed to remove router interface for port %s, rv:%d", port.m_alias.c_str(), status);
throw runtime_error("Failed to remove router interface.");
}
gCrmOrch->decCrmResUsedCounter(CrmResourceType::CRM_SUBNET_TABLE);
} catch (...) {SWSS_LOG_ERROR("Failed to remove router interface for port %s", port.m_alias.c_str());
return false;
}
port.m_rif_id = 0;
gPortsOrch->setPort(port.m_alias, port);
SWSS_LOG_NOTICE("Remove router interface for port %s", port.m_alias.c_str());
return true;
}
// 添加子网路由
void IntfsOrch::addSubnetRoute(const Port &port, const IpPrefix &ip_prefix)
{
sai_route_entry_t unicast_route_entry;
unicast_route_entry.switch_id = gSwitchId;
unicast_route_entry.vr_id = gVirtualRouterId;
unicast_route_entry.table_id = SAI_NULL_OBJECT_ID;
copy(unicast_route_entry.destination, ip_prefix);
subnet(unicast_route_entry.destination, unicast_route_entry.destination);
sai_attribute_t attr;
vector<sai_attribute_t> attrs;
attr.id = SAI_ROUTE_ENTRY_ATTR_PACKET_ACTION;
attr.value.s32 = SAI_PACKET_ACTION_FORWARD;
attrs.push_back(attr);
// 指向出接口为本 rif
attr.id = SAI_ROUTE_ENTRY_ATTR_NEXT_HOP_ID;
attr.value.oid = port.m_rif_id;
attrs.push_back(attr);
sai_status_t status = sai_route_api->create_route_entry(&unicast_route_entry, (uint32_t)attrs.size(), attrs.data());
if (status != SAI_STATUS_SUCCESS)
{
SWSS_LOG_ERROR("Failed to create subnet route to %s from %s, rv:%d",
ip_prefix.to_string().c_str(), port.m_alias.c_str(), status);
throw runtime_error("Failed to create subnet route.");
}
SWSS_LOG_NOTICE("Create subnet route to %s from %s",
ip_prefix.to_string().c_str(), port.m_alias.c_str());
increaseRouterIntfsRefCount(port.m_alias);
gCrmOrch->incCrmResUsedCounter(CrmResourceType::CRM_UNDERLAY_LPM_ROUTE);
if (unicast_route_entry.destination.addr_family == SAI_IP_ADDR_FAMILY_IPV4)
{gCrmOrch->incCrmResUsedCounter(CrmResourceType::CRM_IPV4_ROUTE);
}
else
{gCrmOrch->incCrmResUsedCounter(CrmResourceType::CRM_IPV6_ROUTE);
}
}
// 删除子网路由
void IntfsOrch::removeSubnetRoute(const Port &port, const IpPrefix &ip_prefix)
{
sai_route_entry_t unicast_route_entry;
unicast_route_entry.switch_id = gSwitchId;
unicast_route_entry.vr_id = gVirtualRouterId;
unicast_route_entry.table_id = SAI_NULL_OBJECT_ID;
copy(unicast_route_entry.destination, ip_prefix);
subnet(unicast_route_entry.destination, unicast_route_entry.destination);
sai_status_t status = sai_route_api->remove_route_entry(&unicast_route_entry);
if (status != SAI_STATUS_SUCCESS)
{
SWSS_LOG_ERROR("Failed to remove subnet route to %s from %s, rv:%d",
ip_prefix.to_string().c_str(), port.m_alias.c_str(), status);
throw runtime_error("Failed to remove subnet route.");
}
SWSS_LOG_NOTICE("Remove subnet route to %s from %s",
ip_prefix.to_string().c_str(), port.m_alias.c_str());
decreaseRouterIntfsRefCount(port.m_alias);
gCrmOrch->decCrmResUsedCounter(CrmResourceType::CRM_UNDERLAY_LPM_ROUTE);
if (unicast_route_entry.destination.addr_family == SAI_IP_ADDR_FAMILY_IPV4)
{gCrmOrch->decCrmResUsedCounter(CrmResourceType::CRM_IPV4_ROUTE);
}
else
{gCrmOrch->decCrmResUsedCounter(CrmResourceType::CRM_IPV6_ROUTE);
}
}
// 添加上送本机的路由,32 位本机路由
void IntfsOrch::addIp2MeRoute(const IpPrefix &ip_prefix)
{
sai_route_entry_t unicast_route_entry;
unicast_route_entry.switch_id = gSwitchId;
unicast_route_entry.vr_id = gVirtualRouterId;
unicast_route_entry.table_id = SAI_NULL_OBJECT_ID;
copy(unicast_route_entry.destination, ip_prefix.getIp());
sai_attribute_t attr;
vector<sai_attribute_t> attrs;
attr.id = SAI_ROUTE_ENTRY_ATTR_PACKET_ACTION;
attr.value.s32 = SAI_PACKET_ACTION_FORWARD;
attrs.push_back(attr);
Port cpu_port;
gPortsOrch->getCpuPort(cpu_port);
// 指向出接口为 cpu 的物理接口
attr.id = SAI_ROUTE_ENTRY_ATTR_NEXT_HOP_ID;
attr.value.oid = cpu_port.m_port_id;
attrs.push_back(attr);
sai_status_t status = sai_route_api->create_route_entry(&unicast_route_entry, (uint32_t)attrs.size(), attrs.data());
if (status != SAI_STATUS_SUCCESS)
{SWSS_LOG_ERROR("Failed to create IP2me route ip:%s, rv:%d", ip_prefix.getIp().to_string().c_str(), status);
throw runtime_error("Failed to create IP2me route.");
}
SWSS_LOG_NOTICE("Create IP2me route ip:%s", ip_prefix.getIp().to_string().c_str());
gCrmOrch->incCrmResUsedCounter(CrmResourceType::CRM_UNDERLAY_HOST_ROUTE);
if (unicast_route_entry.destination.addr_family == SAI_IP_ADDR_FAMILY_IPV4)
{gCrmOrch->incCrmResUsedCounter(CrmResourceType::CRM_IPV4_ROUTE);
}
else
{gCrmOrch->incCrmResUsedCounter(CrmResourceType::CRM_IPV6_ROUTE);
}
}
// 删除上送本机的路由
void IntfsOrch::removeIp2MeRoute(const IpPrefix &ip_prefix)
{
sai_route_entry_t unicast_route_entry;
unicast_route_entry.switch_id = gSwitchId;
unicast_route_entry.vr_id = gVirtualRouterId;
unicast_route_entry.table_id = SAI_NULL_OBJECT_ID;
copy(unicast_route_entry.destination, ip_prefix.getIp());
sai_status_t status = sai_route_api->remove_route_entry(&unicast_route_entry);
if (status != SAI_STATUS_SUCCESS)
{SWSS_LOG_ERROR("Failed to remove IP2me route ip:%s, rv:%d", ip_prefix.getIp().to_string().c_str(), status);
throw runtime_error("Failed to remove IP2me route.");
}
SWSS_LOG_NOTICE("Remove packet action trap route ip:%s", ip_prefix.getIp().to_string().c_str());
gCrmOrch->decCrmResUsedCounter(CrmResourceType::CRM_UNDERLAY_HOST_ROUTE);
if (unicast_route_entry.destination.addr_family == SAI_IP_ADDR_FAMILY_IPV4)
{gCrmOrch->decCrmResUsedCounter(CrmResourceType::CRM_IPV4_ROUTE);
}
else
{gCrmOrch->decCrmResUsedCounter(CrmResourceType::CRM_IPV6_ROUTE);
}
}
// 设置本 rif 口的邻居为广播
void IntfsOrch::addDirectedBroadcast(const Port &port, const IpAddress &ip_addr)
{
sai_status_t status;
sai_neighbor_entry_t neighbor_entry;
neighbor_entry.rif_id = port.m_rif_id;
neighbor_entry.switch_id = gSwitchId;
copy(neighbor_entry.ip_address, ip_addr);
sai_attribute_t neighbor_attr;
neighbor_attr.id = SAI_NEIGHBOR_ENTRY_ATTR_DST_MAC_ADDRESS;
memcpy(neighbor_attr.value.mac, MacAddress("ff:ff:ff:ff:ff:ff").getMac(), 6);
status = sai_neighbor_api->create_neighbor_entry(&neighbor_entry, 1, &neighbor_attr);
if (status != SAI_STATUS_SUCCESS)
{
SWSS_LOG_ERROR("Failed to create broadcast entry %s rv:%d",
ip_addr.to_string().c_str(), status);
return;
}
SWSS_LOG_NOTICE("Add broadcast route for ip:%s", ip_addr.to_string().c_str());
}
void IntfsOrch::removeDirectedBroadcast(const Port &port, const IpAddress &ip_addr)
{
sai_status_t status;
sai_neighbor_entry_t neighbor_entry;
neighbor_entry.rif_id = port.m_rif_id;
neighbor_entry.switch_id = gSwitchId;
copy(neighbor_entry.ip_address, ip_addr);
status = sai_neighbor_api->remove_neighbor_entry(&neighbor_entry);
if (status != SAI_STATUS_SUCCESS)
{if (status == SAI_STATUS_ITEM_NOT_FOUND)
{SWSS_LOG_ERROR("No broadcast entry found for %s", ip_addr.to_string().c_str());
}
else
{
SWSS_LOG_ERROR("Failed to remove broadcast entry %s rv:%d",
ip_addr.to_string().c_str(), status);
}
return;
}
SWSS_LOG_NOTICE("Remove broadcast route ip:%s", ip_addr.to_string().c_str());
}
std::set<IpPrefix> IntfsOrch::getRouterIntfsIpAddresses(const string& alias) {auto iter = m_syncdIntfses.find(alias);
if (iter == m_syncdIntfses.end())
return set<IpPrefix>();
return iter->second.ip_addresses;
}
该 orch 不以单独进程出现,它是 orchagent 进程的一个子 orch。
下入 asic-db 的数据示例:
127.0.0.1:6379[1]> KEYS *ROUTER*
1) "ASIC_STATE:SAI_OBJECT_TYPE_VIRTUAL_ROUTER:oid:0x3000000000084"
2) "ASIC_STATE:SAI_OBJECT_TYPE_ROUTER_INTERFACE:oid:0x60000000005d3"
127.0.0.1:6379[1]> HGETALL "ASIC_STATE:SAI_OBJECT_TYPE_ROUTER_INTERFACE:oid:0x60000000005d3"
1) "SAI_ROUTER_INTERFACE_ATTR_VIRTUAL_ROUTER_ID"
2) "oid:0x3000000000084"
3) "SAI_ROUTER_INTERFACE_ATTR_SRC_MAC_ADDRESS"
4) "00:90:FB:60:E2:86"
5) "SAI_ROUTER_INTERFACE_ATTR_TYPE"
6) "SAI_ROUTER_INTERFACE_TYPE_PORT"
7) "SAI_ROUTER_INTERFACE_ATTR_PORT_ID"
8) "oid:0x20000000005d2"
9) "SAI_ROUTER_INTERFACE_ATTR_MTU"
10) "9100"
127.0.0.1:6379[1]>
127.0.0.1:6379[1]> KEYS *ROUTE*
1) "ASIC_STATE:SAI_OBJECT_TYPE_VIRTUAL_ROUTER:oid:0x3000000000084"
2) "ASIC_STATE:SAI_OBJECT_TYPE_ROUTE_ENTRY:{\"dest\":\"10.8.8.200/32\",\"switch_id\":\"oid:0x21000000000000\",\"vr\":\"oid:0x3000000000084\"}"
3) "ASIC_STATE:SAI_OBJECT_TYPE_ROUTE_ENTRY:{\"dest\":\"10.1.0.32/32\",\"switch_id\":\"oid:0x21000000000000\",\"vr\":\"oid:0x3000000000084\"}"
4) "ASIC_STATE:SAI_OBJECT_TYPE_ROUTER_INTERFACE:oid:0x60000000005d3"
127.0.0.1:6379[1]>
127.0.0.1:6379[1]> KEYS *NEIG*
1) "ASIC_STATE:SAI_OBJECT_TYPE_NEIGHBOR_ENTRY:{\"ip\":\"10.8.8.87\",\"rif\":\"oid:0x60000000005d3\",\"switch_id\":\"oid:0x21000000000000\"}"
127.0.0.1:6379[1]>
由 syncd 将 ASIC_DB 接口信息同步到硬件
syncd 响应 asic-db 中的信息变化,将配置同步到硬件。