netlink怎么读_ovs源码阅读--netlink使用
netlink
netlink socket是一种用于用户态进程和内核态进程之间的通信机制。它通过为内核模块提供一组特殊的API,并为用户程序提供了一组标准的socket接口的方式,实现了全双工的通讯连接。
特点:
双向传输,异步通信
用户空间中使用标准socket API
内核空间中使用专门的API
支持多播
可由内核端发起通信
支持32种协议类型
netlink仅支持32种协议类型,这在实际应用中可能并不足够,因此产生了generic netlink(以下简称为genl),
generic netlink支持1023个子协议号,弥补了netlink协议类型较少的缺陷。
通信架构
Netlink子系统:所有genl通信的基础,Netlink子系统中收到的所有Generic类型的netlink数据都被送到genl总线上;从内核发出的数据也经由genl总线送至netlink子系统,再打包送至用户空间
Generic Netlink控制器:作为内核的一部分,负责动态地分配genl通道(即genl family id),并管理genl任务,genl控制器是一个特殊的genl内核用户,它负责监听genl bus上的通信通道
genl通信建立在一系列的通信通道的基础上,每个genl family对应多个通道,这些通道由genl控制器动态分配
相关结构体
genl family
Generic Netlink是基于客户端-服务端模型的通信机制,服务端注册family(family是对genl服务的各项定义的集合),控制器和客户端都通过已注册的信息与服务端通信。
//genl_family主要字段
struct genl_family
{
unsigned intid;//family id
unsigned int hdrsize; //用户自定议头部长度
char name[GENL_NAMSIZ]; //family名,要求不同的family使用不同的名字
unsigned int version;//版本
unsigned int maxattr;//最大attr类型数,使用netlink标准的attr来传输数据
genl_ops *ops;// 操作集合
};
genl_ops
定义了netlink family相关的操作
// genl_ops主要字段
struct genl_ops
{
u8 cmd;//命令名,用于识别genl_ops
unsigned int flags;//设置属性
struct nla_policy *policy; //定义了attr规则,genl在触发事件处理程序之前,会用其进行attr校验
int (*doit)(struct sk_buff *skb, struct genl_info *info);
int (*dumpit)(struct sk_buff *skb, struct netlink_callback *cb);
};
doit:回调函数,在generic netlink收到数据时触发,运行在进程上下文
dumpit:回调函数,当genl_ops的flag标志被添加了NLM_F_DUMP以后,每次收到genl消息即会回触发这个函数
dumpit与doit的区别是:dumpit的第一个参数skb不会携带从客户端发来的数据。相反地,开发者应该在skb中填入需要传给客户端的数据,skb中携带的数据会被自动送到客户端。只要dumpit的返回值大于0,dumpit函数就会再次被调用,并被要求在skb中填入数据。当服务端没有数据要传给客户端时,dumpit要返回0。如果函数中出错,要求返回一个负值。
nal_policy
定义了attr规则
struct nla_policy
{
u16 type;//attr中的数据类型
u16 len;//如果在type字段配置的是字符串有关的值,要把len设置为字符串的最大长度
};
genl_info
内核在接收到用户的genetlink消息后,会对消息解析并封装成genl_info结构
struct genl_info
{
u32 snd_seq; //发送序号
u32 snd_pid; //发送客户端的PID
struct nlmsghdr * nlhdr; //netlink header的指针
struct genlmsghdr * genlhdr; //genl头部的指针(即family头部)
void * userhdr; //用户自定义头部指针
struct nlattr ** attrs; //如果定义了genl_ops->policy,保存被policy过滤以后的结果
};
Generic Netlink服务端(内核)初始化
这里以OVS中packet的处理为例:
1. 定义family
//定义packet family
static struct genl_family dp_packet_genl_family __ro_after_init = {
.hdrsize = sizeof(struct ovs_header),
.name = OVS_PACKET_FAMILY,
.version = OVS_PACKET_VERSION,
.maxattr = OVS_PACKET_ATTR_MAX,
.netnsok = true,
.parallel_ops = true,
.ops = dp_packet_genl_ops, //操作集合
.n_ops = ARRAY_SIZE(dp_packet_genl_ops),
.module = THIS_MODULE,
};
2. 定义operation
// 定义packet family 的操作 --- packet类型的操作只支持OVS_PACKET_CMD_EXECUTE
static struct genl_ops dp_packet_genl_ops[] = {
{ .cmd = OVS_PACKET_CMD_EXECUTE,
.flags = GENL_UNS_ADMIN_PERM,
.policy = packet_policy,
.doit = ovs_packet_cmd_execute //接受数据包时,调用ovs_packet_cmd_execute进行处理
}
};
// 定义packet family 的过滤规则
static const struct nla_policy packet_policy[OVS_PACKET_ATTR_MAX + 1] = {
[OVS_PACKET_ATTR_PACKET] = { .len = ETH_HLEN },
[OVS_PACKET_ATTR_KEY] = { .type = NLA_NESTED },
[OVS_PACKET_ATTR_ACTIONS] = { .type = NLA_NESTED },
[OVS_PACKET_ATTR_PROBE] = { .type = NLA_FLAG },
[OVS_PACKET_ATTR_MRU] = { .type = NLA_U16 },
};
3. 注册family
genl_register_family(&dp_packet_genl_family);
Generic Netlink客户端(用户空间)初始化
struct sockaddr_nl saddr;
int sock;
sock = socket(AF_NETLINK, SOCK_RAW, NETLINK_GENERIC); //创建一个netlink类型的socket
if (sock < 0) {
return -1;
}
memset(&saddr, 0, sizeof(saddr));
saddr.nl_family = AF_NETLINK;
saddr.nl_pid = getpid();//获取family id
if (bind(sock, (struct sockaddr*)&saddr, sizeof(saddr)) < 0) {//绑定
printf("bind fail!\n");
close(*p_sock);
return -1;
}
内核空间接受发送数据
接受数据:内核端一旦收到generic netlink数据,会触发doit函数运行,通过回调函数进行处理
发送数据:将数据打包好之后,可通过单播(genlmsg_unicast)或多播()的形式进行发送
用户空间接受发送数据
接受数据:调用recv函数即可完成从内核来的数据的接收
发送数据:调用sendto来发送数据
netlink收发数据—以ovs中packet为例
参考内容
netlink怎么读_ovs源码阅读--netlink使用相关推荐
- Android源码阅读---init进程
Android源码阅读-init进程 文章目录 Android源码阅读---init进程 1. 编译命令和进程入口 1. init 进程编译命令 2. main函数流程 2. 主函数处理流程 1. 创 ...
- 读Zepto源码之操作DOM
2019独角兽企业重金招聘Python工程师标准>>> 这篇依然是跟 dom 相关的方法,侧重点是操作 dom 的方法. 读Zepto源码系列文章已经放到了github上,欢迎sta ...
- koa源码阅读之koa-compose/application.js
koa源码阅读之koa-compose/application.js koa-Compose 为了理解方便特地把注释也粘进来 //这英语.我也来翻译一波 //大概就是把所有的中间件组合返回一个完整大块 ...
- TiDB 源码阅读系列文章(六)Select 语句概览
在先前的 TiDB 源码阅读系列文章(四) 中,我们介绍了 Insert 语句,想必大家已经了解了 TiDB 是如何写入数据,本篇文章介绍一下 Select 语句是如何执行.相比 Insert,Sel ...
- Spring源码阅读 源码环境搭建(一)
ring 源码阅读的搭建(一) 一 下载spring源码 进入官方网页:https://spring.io/projects/spring-framework 进入相关的github位置,下载zip包 ...
- TiDB 源码阅读系列文章(十九)tikv-client(下)
上篇文章 中,我们介绍了数据读写过程中 tikv-client 需要解决的几个具体问题,本文将继续介绍 tikv-client 里的两个主要的模块--负责处理分布式计算的 copIterator 和执 ...
- 转-OpenJDK源码阅读导航跟编译
OpenJDK源码阅读导航 OpenJDK源码阅读导航 博客分类: Virtual Machine HotSpot VM Java OpenJDK openjdk 这是链接帖.主体内容都在各链接中. ...
- SpringMVC源码阅读系列汇总
1.前言 1.1 导入 SpringMVC是基于Servlet和Spring框架设计的Web框架,做JavaWeb的同学应该都知道 本文基于Spring4.3.7源码分析,(不要被图片欺骗了,手动滑稽 ...
- Java8 LinkedHashMap 源码阅读
如果你对 HashMap 的源码有了解的话,只需要一图就能知道 LinkedHashMap 的原理了,但是具体的实现细节还是需要去读一下源码. 一.LinkedHashMap 简介 1.1 继承结构 ...
最新文章
- unity鼠标控制镜头旋转_unity3D鼠标滚轮来实现镜头拉近拉远及视角旋转
- 前端学习资料及路线名称网站
- 人少钱少需求多的新项目该怎么带?看到这篇我心里有底了!
- FTP的上传下载工具类
- opencv中很有趣的仿射变换(Affine Transformation)
- redis 介绍与安装
- SpringBoot-配置文件创建Bean的过程
- java white case语句_MySQL的CASEWHEN语句使用说明_MySQL
- python从excel读取数据用matplotlib画平面折线图
- 第三阶段应用层——1.8 数码相册—在LCD上显示JPG图片
- 在努力的途中 忤逆满路荆棘
- 有没有一款桌面便签软件,可以手机电脑都能使用的?
- 读英语计算机书籍读后感,英语读后感
- 使用 HTML、CSS 和 JavaScript 的简单模拟时钟
- kodi安卓4.0版及中文插件安装方法
- Asp.Net Core WebApi 身份验证、注册、用户管理
- 网站服务器日常管理,网站管理员的日常生活
- 2014.8.1 饮水思源 兼职信息
- 聊聊API网关的作用
- 谈谈现在低价U盘的质量问题