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使用相关推荐

  1. Android源码阅读---init进程

    Android源码阅读-init进程 文章目录 Android源码阅读---init进程 1. 编译命令和进程入口 1. init 进程编译命令 2. main函数流程 2. 主函数处理流程 1. 创 ...

  2. 读Zepto源码之操作DOM

    2019独角兽企业重金招聘Python工程师标准>>> 这篇依然是跟 dom 相关的方法,侧重点是操作 dom 的方法. 读Zepto源码系列文章已经放到了github上,欢迎sta ...

  3. koa源码阅读之koa-compose/application.js

    koa源码阅读之koa-compose/application.js koa-Compose 为了理解方便特地把注释也粘进来 //这英语.我也来翻译一波 //大概就是把所有的中间件组合返回一个完整大块 ...

  4. TiDB 源码阅读系列文章(六)Select 语句概览

    在先前的 TiDB 源码阅读系列文章(四) 中,我们介绍了 Insert 语句,想必大家已经了解了 TiDB 是如何写入数据,本篇文章介绍一下 Select 语句是如何执行.相比 Insert,Sel ...

  5. Spring源码阅读 源码环境搭建(一)

    ring 源码阅读的搭建(一) 一 下载spring源码 进入官方网页:https://spring.io/projects/spring-framework 进入相关的github位置,下载zip包 ...

  6. TiDB 源码阅读系列文章(十九)tikv-client(下)

    上篇文章 中,我们介绍了数据读写过程中 tikv-client 需要解决的几个具体问题,本文将继续介绍 tikv-client 里的两个主要的模块--负责处理分布式计算的 copIterator 和执 ...

  7. 转-OpenJDK源码阅读导航跟编译

    OpenJDK源码阅读导航 OpenJDK源码阅读导航 博客分类: Virtual Machine HotSpot VM Java OpenJDK openjdk 这是链接帖.主体内容都在各链接中.  ...

  8. SpringMVC源码阅读系列汇总

    1.前言 1.1 导入 SpringMVC是基于Servlet和Spring框架设计的Web框架,做JavaWeb的同学应该都知道 本文基于Spring4.3.7源码分析,(不要被图片欺骗了,手动滑稽 ...

  9. Java8 LinkedHashMap 源码阅读

    如果你对 HashMap 的源码有了解的话,只需要一图就能知道 LinkedHashMap 的原理了,但是具体的实现细节还是需要去读一下源码. 一.LinkedHashMap 简介 1.1 继承结构 ...

最新文章

  1. unity鼠标控制镜头旋转_unity3D鼠标滚轮来实现镜头拉近拉远及视角旋转
  2. 前端学习资料及路线名称网站
  3. 人少钱少需求多的新项目该怎么带?看到这篇我心里有底了!
  4. FTP的上传下载工具类
  5. opencv中很有趣的仿射变换(Affine Transformation)
  6. redis 介绍与安装
  7. SpringBoot-配置文件创建Bean的过程
  8. java white case语句_MySQL的CASEWHEN语句使用说明_MySQL
  9. python从excel读取数据用matplotlib画平面折线图
  10. 第三阶段应用层——1.8 数码相册—在LCD上显示JPG图片
  11. 在努力的途中 忤逆满路荆棘
  12. 有没有一款桌面便签软件,可以手机电脑都能使用的?
  13. 读英语计算机书籍读后感,英语读后感
  14. 使用 HTML、CSS 和 JavaScript 的简单模拟时钟
  15. kodi安卓4.0版及中文插件安装方法
  16. Asp.Net Core WebApi 身份验证、注册、用户管理
  17. 网站服务器日常管理,网站管理员的日常生活
  18. 2014.8.1 饮水思源 兼职信息
  19. 聊聊API网关的作用
  20. 谈谈现在低价U盘的质量问题

热门文章

  1. MS CRM 2011插件调试工具
  2. WPF中的Attached Property
  3. Wcf 双工通信的应用
  4. linux系统基础调优32条技巧
  5. MSDN、RTM、OEM、VOL四大版本之区别
  6. unix executable file
  7. jQuery.validate.js API
  8. IPSec逻辑体系架构
  9. Java:一个分数类的简单设计
  10. 怎么看有没有安装libevent_家里有没有必要安装前置净水器?先听听师傅是怎么说的...