2

同步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地址到此结束,其它类型的接口lag,ethernrt都由interface_config.service进行配置

        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中的信息变化,将配置同步到硬件。


ouyangxibao
189 声望161 粉丝

不生产代码,只是代码的搬运工