VSOMEIP代码阅读整理(1) - 网卡状态监听

news/2024/10/3 11:10:16 标签: 服务器, 网络, 运维

一. 概述

在routing进程所使用的配置文件中,存在如下配置项目:
{
    "unicast" : "192.168.56.101",
    ...
    "service-discovery" :
    {
        "enable" : "true",
        "multicast" : "224.244.224.245",
        ...
    }
}
    
其中有 "unicast" : "192.168.56.101"和	"multicast" : "224.244.224.245" 两个通信地址,这两个地址一个是用于vsomeip用于对外通信的单播地址,另一个配置的是service-discovery功能依赖的组播的地址。

作为routingmanager的进程需要监听这个单播地址和组播地址所在的网卡的状态,这部分功能主要在netlink_connector中实现,routing_manager_imp依赖netlink_connector来监听网卡状态,并且在网卡状态ready的情况下才会启动routing。

netlink_connector中使用到了linux平台的netlink协议用于监听内核上网卡相关事件。

二. netlink protocol

netlink是linux平台上第一种IPC机制,主要用于用户态进程与内核进程通信,此外还可以用于用户态进程之间通信(这个使用unix domain socket)也可以做到。

netlink和传统的和内核通信的机制(ioctl,sysfs属性)等不同,netlink是支持全双工的通信的,也就是可以异步通信的,而其他几种传统的内核通信的机制只支持半双工同步通信的方式。在这种情况下,内核甚至支持主动发起通信,而不是由应用发起通信。

此外,netlink支持组播的方式,以组播的方式将消息发给多个进程(根据groupid)。

img

netlink的通信方式使用的是socket API,创建NETLINK socket的时候,需要指定NETLINK socket的协议类型类型()
sock = socket(PF_NETLINK, SOCK_RAW, NETLINK_ROUTE);   // NETLINK_ROUTE是协议类型
目前的linxu系统中支持32中协议类型,个人认为这个协议类型就是事件组(网卡/路由/安全/审计/SCSI设备...等等)。
#define NETLINK_ROUTE        0    /* 用于设置和查询路由表等网络核心模块*/
#define NETLINK_UNUSED        1    /* Unused number                */
#define NETLINK_USERSOCK    2    /* Reserved for user mode socket protocols,保留用于用户态进程间通信     */
#define NETLINK_FIREWALL    3    /* Unused number, formerly ip_queue        */
#define NETLINK_SOCK_DIAG    4    /* socket monitoring                */
#define NETLINK_NFLOG        5    /* netfilter/iptables ULOG */
...
...
NETLINK socket需要做bind操作绑定NETLINK的地址,NETLINK地址结构如下:
struct sockaddr_nl {
    __kernel_sa_family_t    nl_family;    /* 协议族 AF_NETLINK    */
    unsigned short    nl_pad;        /* 固定填写0 zero        */
    __u32        nl_pid;        /* 端口ID,内核填0,应用进程填PID port ID    */
    __u32        nl_groups;    /* 广播组 multicast groups mask */
};
除了nl_pad固定为0以外,其他参数需要填写
struct sockaddr_nl addr;
memset(&addr, 0, sizeof(addr));
addr.nl_family = AF_NETLINK;    // 协议族
// RTMGRP_LINK: 网卡UP/DOWN
// RTMGRP_IPV4_IFADDR: ip地址变化
addr.nl_groups = RTMGRP_LINK | RTMGRP_IPV4_IFADDR;    // 广播组(事件组中的具体事件)
  设置好NETLINK地址后,将其绑定到socket上面
bind(sock, (struct sockaddr *)&addr, sizeof(addr);
接着,就可以使用该socket和内核进行netlink通信了,通过标准recv接口从内核接收消息
while (running && (len = recv(sock, buffer, 4096, 0)) > 0) {
	nlh = (struct nlmsghdr *)buffer;
	while (NLMSG_OK(nlh, len) && (nlh->nlmsg_type != NLMSG_DONE)) {
		// 解析不同类型的NetLink消息
		...
		// 下一条消息
		nlh = NLMSG_NEXT(nlh, len);
	}
}
close(sock);

三. netlink_connector

​ netlink_connector类依赖了NETLINK通信机制和内核进行通信,用于监控网卡的状态,根据传入的单播地址和组播地址监控。

​ 首先netlink_connector类中也创建了用于NETLINK通信的socket

class netlink_connector : public std::enable_shared_from_this<netlink_connector> {
	...
private:
	...
	boost::asio::basic_raw_socket<nl_protocol> socket_;  // nl_protocol结构体中默认famliy为PF_NETLINK, type为SOCK_RAW
	...
}

void netlink_connector::start() {
	...
	socket_.open(nl_protocol(NETLINK_ROUTE), ec);   // 协议类型为NETLINK_ROUTE,用于设置和查询路由表等网络核心模块
	...
	socket_.bind(nl_endpoint<nl_protocol>(
                RTMGRP_LINK |        // - 当网卡变动时会触发这个多播组
                RTMGRP_IPV4_IFADDR | RTMGRP_IPV6_IFADDR |  // 当ipv4/ipv6地址变动时会触发这个多播组
                RTMGRP_IPV4_ROUTE | RTMGRP_IPV6_ROUTE |    // 当ipv4/ipv6路由变动时会触发这个多播组
                RTMGRP_IPV4_MROUTE | RTMGRP_IPV6_MROUTE), ec);   // 当多播路由发生更新时会触发这个多播组
	

​ 然后,使用创建的socket接收内核的消息并且解析,根据不同的事件回调上层routing_manager_impl

socket_.async_receive(
    boost::asio::buffer(&recv_buffer_[0], recv_buffer_size),
    std::bind(
        &netlink_connector::receive_cbk,
        shared_from_this(),
        std::placeholders::_1,
        std::placeholders::_2
    )
);

void netlink_connector::receive_cbk(boost::system::error_code const &_error,
                 std::size_t _bytes) {
    while ((NLMSG_OK(nlh, len)) && (nlh->nlmsg_type != NLMSG_DONE)) {
            char ifname[IF_NAMESIZE];
            switch (nlh->nlmsg_type) { // 根据多播组内的消息类型分别处理
                case RTM_NEWADDR: {    // IP地址变化
                	// 解析出消息中的IP地址,如果该IP是VSOMEIP配置的单播地址,则往下
                	// 根据IP地址找到网卡,获取其状态(UP/DOWN)
                	// 通知上层handler处理(handler第一个参数标志是单播还是组播地址的网卡)
                }
                break;
                case RTM_NEWLINK: {    // 网卡变化
                	// 获取网卡IP,如果该IP是VSOMEIP配置的单播地址,则往下
                	// 获取网卡状态
                	// 通知上层handler处理
                }
                break;
                case RTM_NEWROUTE: {   // 路由添加
                	check_sd_multicast_route_match(...) {
                		// 读取路由项的目标地址(RTA_DST),判断地址是否为SD的组播地址
                        // 读取路由项的输出网络设备索引,判断设备索引是否为单播地址通信使用的网络设备的索引
                        // 读取路由项的网关地址
                        // 1. 如果单播地址通信使用的网络设备被加到SD的组播中,返回true
                        // 2. 如果单播地址通信使用的网络设备被添加到组播,但是组播地址长度为0,返回true(使用默认路由作为SD的地址)
                        // 3. 不满足上面两种情况,返回false
                	}
                	// check_sd_multicast_route_match返回true,则通知上层组播准备好了
               	}
               	break;
                case RTM_DELROUTE: {   // 路由删除
                	check_sd_multicast_route_match(...) {
                		...
                	}
                	// check_sd_multicast_route_match返回true,则通知上层组播未准备好
                }
                break;
                ...
            }
}

​ 对于netlink_connector,其监听网卡以及组播路由的变化事件。routing_manager_impl则是这些事件的消费者

void routing_manager_impl::start() {
	...
	netlink_connector_->register_net_if_changes_handler(
            std::bind(&routing_manager_impl::on_net_interface_or_route_state_changed,
            this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3));
    ...
}

http://www.niftyadmin.cn/n/5688252.html

相关文章

滚雪球学Oracle[4.3讲]:PL/SQL控制结构与循环的深入解析与优化

全文目录&#xff1a; 前言一、嵌套条件语句的优化1.1 条件语句的基础示例&#xff1a;简单的IF-THEN-ELSE结构 1.2 嵌套条件语句的优化策略 二、PL/SQL中的复杂循环与性能调优2.1 循环结构的基本使用示例&#xff1a;基本的FOR循环 2.2 复杂循环的性能问题复杂循环中的常见问题…

解析TMalign文本文件中的转换矩阵

TM-align 将两个蛋白质结构通过旋转和位移对齐后&#xff1a; TMalign test1.pdb test2.pdb -m mtx.txt 输出转换矩阵&#xff0c;文件内容为&#xff1a; ------ The rotation matrix to rotate Chain_1 to Chain_2 ------ m t[m] u[m][0] u[…

可视化图表与源代码显示配置项及页面的动态调整功能分析

可视化图表与源代码显示配置项及页面的动态调整功能分析 文章目录 可视化图表与源代码显示配置项及页面的动态调整功能分析1.分析图表源代码2.分析源代码显示功能**完整代码参考&#xff1a;** 3.分析源代码显示及动态调整**完整代码参考&#xff1a;** 4.分析代码编辑器及运行…

Cypress自动化测试实战:构建高效的前端测试体系

在快速迭代的软件开发环境中&#xff0c;前端自动化测试是保证代码质量和用户体验的重要手段。Cypress作为一款功能强大的前端自动化测试工具&#xff0c;凭借其丰富的特性、直观的API和高效的测试执行速度&#xff0c;赢得了众多开发者和测试团队的青睐。本文将深入探讨Cypres…

Vue2 + ElementUI + axios + VueRouter入门

之前没有pc端开发基础&#xff0c;工作需要使用若依框架进行了一年的前端开发.最近看到一个视频框架一步步集成&#xff0c;感觉颇受启发&#xff0c;在此记录一下学习心得。视频链接:vue2element ui 快速入门 环境搭建和依赖安装 安装nodejs安装Vue Cli使用vue create proje…

【Kubernetes】常见面试题汇总(五十二)

目录 116. K8S 集群服务暴露失败&#xff1f; 117.外网无法访问 K8S 集群提供的服务&#xff1f; 特别说明&#xff1a; 题目 1-68 属于【Kubernetes】的常规概念题&#xff0c;即 “ 汇总&#xff08;一&#xff09;~&#xff08;二十二&#xff09;” 。 题目 69-…

php函数积累

对称函数 isset 判断数组arr中是否存在键key 返回值true/false isset(name,$arr) unset 删除数组中的键 需存在key不然抛出异常 unset($arr[name]) json_encode 数据转json格式 json_encode($arr) 一般形式 指定字符编码形式 json_decode json格式转原有数据格式 json_d…

IM项目------网关子服务

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 前言所需模块长连接管理模块新增连接管理通过用户ID获取长连接通过长连接获取用户ID和会话ID删除连接管理 服务器搭建业务代码编写 前言 入口网关子服务主要负责三…