sonic处理netlink事件

2023-05-16

sonic处理netlink事件

​ sonic在处理路由,接口up/down,接口地址变化,team等事件上极大的依赖内核。sonic通过监听rtnl事件来响应linux事件。从而感知相关信息变化。

libnl

sonic使用libnl库来操作netlink事件,详细内容可以访问http://www.infradead.org/~tgr...。sonic在libnl库基础上封装了类NetLink进行netlink操作。

NetLink

class NetLink : public Selectable {
public:
    NetLink(int pri = 0);
    ~NetLink() override;

    void registerGroup(int rtnlGroup);//注册想要监听的事件,加入组播组
    void dumpRequest(int rtmGetCommand);

    int getFd() override;//判断句柄是否在select的可用事件中
    void readData() override;//获取socket中的信息,触发回调函数

private:
    static int onNetlinkMsg(struct nl_msg *msg, void *arg);//回调函数

    nl_sock *m_socket;//套接口描述符
};

NetLink::NetLink(int pri) :

NetLink::NetLink(int pri) :
    Selectable(pri), m_socket(NULL)
{
    m_socket = nl_socket_alloc();//申请描述符
    if (!m_socket)
    {
        SWSS_LOG_ERROR("Unable to allocated netlink socket");
        throw system_error(make_error_code(errc::address_not_available),
                           "Unable to allocated netlink socket");
    }

    nl_socket_disable_seq_check(m_socket);//不进行序列号检查
    //注册回调函数,读取信息时,会自动回调该函数onNetlinkMsg
    nl_socket_modify_cb(m_socket, NL_CB_VALID, NL_CB_CUSTOM, onNetlinkMsg, this);
    //连接内核netlink
    int err = nl_connect(m_socket, NETLINK_ROUTE);
    if (err < 0)
    {
        SWSS_LOG_ERROR("Unable to connect netlink socket: %s", nl_geterror(err));
        nl_socket_free(m_socket);
        m_socket = NULL;
        throw system_error(make_error_code(errc::address_not_available),
                           "Unable to connect netlink socket");
    }
    //非阻塞
    nl_socket_set_nonblocking(m_socket);
    /* Set socket buffer size to 256KB */
    nl_socket_set_buffer_size(m_socket, 2097152, 0);
}

 

void NetLink::registerGroup(int rtnlGroup)

void NetLink::registerGroup(int rtnlGroup)
{
    int err = nl_socket_add_membership(m_socket, rtnlGroup);//加入组播组
    if (err < 0)
    {
        SWSS_LOG_ERROR("Unable to register to group %d: %s", rtnlGroup,
                       nl_geterror(err));
        throw system_error(make_error_code(errc::address_not_available),
                           "Unable to register group");
    }
}

int NetLink::getFd()

int NetLink::getFd()//获取套接口句柄
{
    return nl_socket_get_fd(m_socket);
}

void NetLink::readData()

void NetLink::readData()
{
    int err;

    do
    {
        err = nl_recvmsgs_default(m_socket);//读取数据,有libnl触发回调函数,处理业务
    }
    while(err == -NLE_INTR); // Retry if the process was interrupted by a signal

    if (err < 0)
    {
        if (err == -NLE_NOMEM)
            SWSS_LOG_ERROR("netlink reports out of memory on reading a netlink socket. High possiblity of a lost message");
        else if (err == -NLE_AGAIN)
            SWSS_LOG_DEBUG("netlink reports NLE_AGAIN on reading a netlink socket");
        else
            SWSS_LOG_ERROR("netlink reports an error=%d on reading a netlink socket", err);
    }
}

int NetLink::onNetlinkMsg(......)

//回调函数,读取消息时回调该函数,该函数是一个消息分发器
int NetLink::onNetlinkMsg(struct nl_msg *msg, void *arg)
{
    NetDispatcher::getInstance().onNetlinkMessage(msg);
    return NL_OK;
}

void NetLink::dumpRequest(.....)

void NetLink::dumpRequest(int rtmGetCommand)//用于获取信息,实现get命令,查看内核相关信息
{
    int err = nl_rtgen_request(m_socket, rtmGetCommand, AF_UNSPEC, NLM_F_DUMP);
    if (err < 0)
    {
        SWSS_LOG_ERROR("Unable to request dump on group %d: %s", rtmGetCommand,
                       nl_geterror(err));
        throw system_error(make_error_code(errc::address_not_available),
                           "Unable to request dump");
    }
}

消息分发器

class NetDispatcher {
public:
    /**/
    static NetDispatcher& getInstance();//获取消息分发器实例,消息分发器全局一个,静态函数

    /*
     * Register callback class according to message-type.
     *
     * Throw exception if,注册消息处理函数
     */
    void registerMessageHandler(int nlmsg_type, NetMsg *callback);

    /*
     * Called by NetLink or FpmLink classes as indication of new packet arrival
     * 给netlink的回调函数
     */
    void onNetlinkMessage(struct nl_msg *msg);

private:
    NetDispatcher() = default;

    /* nl_msg_parse callback API */
    static void nlCallback(struct nl_object *obj, void *context);

    std::map<int, NetMsg * > m_handlers;//回调函数存储map
};
class NetMsg {
public:
    /* Called by NetDispatcher when netmsg matches filters */
    virtual void onMsg(int nlmsg_type, struct nl_object *obj) = 0;
};

}

NetDispatcher& NetDispatcher::getInstance()

NetDispatcher& NetDispatcher::getInstance()//消息分发器实例获取函数
{
    static NetDispatcher gInstance;//定义了一个静态分发器,全局一个
    return gInstance;
}

void NetDispatcher::registerMessageHandler()

void NetDispatcher::registerMessageHandler(int nlmsg_type, NetMsg *callback)//注册回调函数
{
    if (m_handlers.find(nlmsg_type) != m_handlers.end())
        throw "Trying to registered on already registerd netlink message";

    m_handlers[nlmsg_type] = callback;
}

void NetDispatcher::nlCallback()

void NetDispatcher::nlCallback(struct nl_object *obj, void *context)
{
    NetMsg *callback = (NetMsg *)context;
    callback->onMsg(nl_object_get_msgtype(obj), obj);
}

void NetDispatcher::onNetlinkMessage()

void NetDispatcher::onNetlinkMessage(struct nl_msg *msg)//netlink回调函数的真实实现
{
    struct nlmsghdr *nlmsghdr = nlmsg_hdr(msg);//获取netlink消息头
    auto callback = m_handlers.find(nlmsghdr->nlmsg_type);//获取消息类型对应的NetMsg描述结构

    /* Drop not registered messages */
    if (callback == m_handlers.end())//没有对应的消息处理函数
        return;
    //解析消息,调用NetDispatcher::nlCallback 
    nl_msg_parse(msg, NetDispatcher::nlCallback, (void *)(callback->second));
}

使用实例

我们以接口管理为例进行说明。

实现NetMsg

class IntfSync : public NetMsg
{
public:
    enum { MAX_ADDR_SIZE = 64 };

    IntfSync(DBConnector *db);//连接数据库

    virtual void onMsg(int nlmsg_type, struct nl_object *obj);

private:
    ProducerStateTable m_intfTable;
};

}
//消息处理函数
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

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;
}

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

sonic处理netlink事件 的相关文章

  • SONIC+P4

    SONIC简介 背景 微软于2015年先后发表了SAI和SONIC SONIC产业日益繁荣 xff0c 已超过六十家 创新 SONiC使用了大量现有的开源技术 xff1a Docker Redis Quagga和LLDPD以及自动化配置工具
  • SONiC中的SAI接口使用方法和原理

    SONiC是一款开源网络操作系统 xff0c SAI Switch Abstraction Interface 接口是用于与硬件交互的接口 SAI接口定义了硬件平台必须实现的功能 xff0c 使得软件能够与硬件无缝衔接 SAI接口使用方法如
  • sonic配置team与实现机制

    sonic实现team代码框架图 xff1a sonic修改lag模式配置步骤 1 修改文件teamd j2 docker exec it teamd bash cd usr share sonic templates vim teamd
  • SONiC中的config文件夹中的__init__.py文件和main.py文件,这两个文件的作用分别是什么...

    在SONiC中 xff0c config文件夹中的 init py文件和main py文件分别有以下作用 xff1a init py文件 xff1a 这个文件是一个Python包的初始化文件 xff0c 它的主要作用是定义这个包的结构和内容
  • 2020年12月30日 当SONiC遇到P4

    当SONiC遇到P4 SONiC P4 是SONiC中运行的P4虚拟交换机 xff0c 当前的SONiC P4是一个docker image xff0c 可以运行在任意的docker环境中 看看SONiC和P4能碰撞出什么火花 SONiC的
  • 2021-02-21 SONiC SAI结构5 VXLAN

    SONiC SAI结构5 VXLAN VXLAN报文处理模型流水线结构 SONiC SAI支持VXLAN协议 xff0c 具备支持VTEP的能力 根据报文处理功能模型的特点 xff0c 不同的功能块可以好像搭积木一样组合在一起形成新的功能
  • 2021-04-26 SONiC: 转发和管理平面接口SAI模型

    2021 04 26 SONiC 转发和管理平面接口SAI模型 SAI模型中转发平面和管理平面接口 转发平面和管理平面之间的接口是控制报文从转发平面传递到控制平面CPU处理的接口 对于各种类型的交换机而言 xff0c 大量不同种类的控制报文
  • 2021-06-25 SONiC 系统BGP配置命令

    2021 06 25 SONiC 系统BGP配置命令 SONiC系统BGP配置 SONiC系统所默认包含的BGP模块在201811版的SONiC之前是开源的Quagga软件 xff0c 之后改成了更流行的FRR FRR中的Show命令是以
  • 2021-08-20 SONiC中的FRR和Zebra

    2021 08 20 SONiC中的FRR和Zebra SONiC中采用FRR和Zebra处理路由协议 以前写过SONiC系统所默认包含的BGP模块在201811版的SONiC之前是开源的Quagga软件 xff0c 之后改成了更流行的FR
  • 2021-08-29 SONiC中基于策略的哈希配置

    SONiC中基于策略的哈希配置 SONiC可以支持对不同类型的报文采取不同的Hash算法 对于多通道 多链路连接的情况 xff0c 如LAG和ECMP的接口上 xff0c 交换机和路由器采用Hash算法对报文中指定的字段进行Hash计算 x
  • 2021-09-19 当SONiC遇到P4之二

    当SONiC遇到P4之二 P4描述SAI 在当SONiC遇到P4中介绍了用P4来实现SAI Model的方式 xff0c 这种方式利用了P4数据平面编程的功能实现了SAI模型 xff0c 将P4和SONiC这两个分别位于网络数据平面和控制平
  • sonic 编译

    本文是转载 xff0c sonic 编译 sonic编译过程讲解 xff0c 点击这里
  • SONiC vs testbed搭建

    准备工作 一台安装Ubuntu18 04的系统 xff0c 内存建议不少于16G 需要支持kvm虚拟化安装ssh server sudo apt update y sudo apt openssh server y 设置sudo免密 sud
  • 编译SONiC交换机镜像(转,参考2)

    sonic buildimage 编译SONiC交换机镜像 描述 以下是关于如何为网络交换机构建 ONIE 兼容网络操作系统 xff08 NOS xff09 安装程序镜像的说明 xff0c 以及如何构建在NOS内运行的Docker镜像 请注
  • sonic开发——修改内核配置

    参考 xff1a https github com Azure sonic linux kernel sonic 中的内核配置修改不需要编译menuconfig xff0c 而是直接修改 patch kconfig exclusions和p
  • 基于netlink的Linux Network Monitor实现

    一 背景 来源于产品开发需求 xff0c 需要在linux系统下实现网络状类型查询及网络类型变更通知 xff0c 比如从Ethernet变为Wifi xff0c 从Wifi变为Ethernet等 二 设计方案 Linux系统提供了Netli
  • Openwrt按键检测分析-窥探Linux内核与用户空间通讯机制netlink使用

    首先看一下Openwrt系统中关于按键功能的使用和修改 以18 06版本为例 按键功能实现在脚本中 比如18 06 package base files files etc rc button reset bin sh lib functi
  • 使用 boost::asio 的 AF_NETLINK (netlink) 套接字

    我正在编写基于的多播客户端 服务器应用程序this and this 效果很好 但是 当计算机中的活动网络接口数量发生变化时 我还需要执行一些操作 例如示例部分中的程序这一页 does 我想我应该使用 boost asio local 中的
  • 内核 qdiscs 模块中的网络数据包缓冲

    我想缓冲源自容器网络接口的输出数据包 这个netlink库文件名为sch plug chttps code woboq org linux linux net sched sch plug c html看起来可以解决问题 但我发现它很难使用
  • 两个linux内核模块之间是否可以通过netlink进行通信?

    众所周知 netlink是用户 内核空间的通信机制 我想从我的内核模块与另一个内核模块进行通信 另一个内核模块已经具有 netlink 接口 是否可以像我们在用户空间中那样从内核模块到 netlink 建立连接 简短回答 不 如果要在两个内

随机推荐