iis服务器助手广告广告
返回顶部
首页 > 资讯 > 精选 >如何进行Netlink源码及实例的分析
  • 297
分享到

如何进行Netlink源码及实例的分析

2023-06-15 22:06:19 297人浏览 独家记忆
摘要

本篇文章给大家分享的是有关如何进行Netlink源码及实例的分析,小编觉得挺实用的,因此分享给大家学习,希望大家阅读完这篇文章后可以有所收获,话不多说,跟着小编一起来看看吧。前言这几天在看 ipvs 相关代码的时候又遇到了 netlink

本篇文章给大家分享的是有关如何进行Netlink源码及实例的分析,小编觉得挺实用的,因此分享给大家学习,希望大家阅读完这篇文章后可以有所收获,话不多说,跟着小编一起来看看吧。

前言

这几天在看 ipvs 相关代码的时候又遇到了 netlink 的事情,所以这两天花了点时间重新把 netlink 的事情梳理了一下。

什么是 netlink

linux  内核一直存在的一个严重问题就是内核态和用户态的交互的问题,对于这个问题内核大佬们一直在研究各种方法,想让内核和用户态交互能够安全高效的进行。如系统调用,proc,sysfs等内存文件系统,但是这些方式一般都比较简单,只能在用户空间轮询访问内核的变化,内核的变化无法主动的推送出来。

而 netlink 的出现比较好的解决了这个问题,而且 netlink 还有以下一些优势:

  1. 鸿蒙官方战略合作共建——HarmonyOS技术社区

  2. 可以直接使用 Socket 套接字的 api 进行内核和用户态的通信,开发使用上相对简单了很多。

  3. 利用内核协议栈有了缓冲队列,是一种异步通信机制。

  4. 可以是内核和用户态的双向通信,内核可以主动向用户态进程发送消息。这个是以往通信方式不具备的。

  5. 针对同一个协议类型的所有用户进程,内核可以广播消息给所有的进程,也可以指定进程 pid 进行消息发送。

目前 netlink 的这种机制被广泛使用在各种场景中,在 Linux 内核中使用 netlink 进行应用与内核通信的应用很多; 包括:路由  daemon(NETLINK_ROUTE),用户态 socket  协议(NETLINK_USERSOCK),防火墙(NETLINK_FIREWALL),netfilter  子系统(NETLINK_NETFILTER),内核事件向用户态通知(NETLINK_KOBJECT_UEVENT)等。具体支持的类型可以查看这个文件  include/uapi/linux/netlink.h。

netlink 内核代码走读

netlink 内核相关文件介绍

netlink 的内核代码在内核代码的 net/netlink/ 目录下,我目前看的是 5.7.10 的内核版本,netlink  内核相关的文件不多,还是比较清晰的:

helightxu@  ~/open_code/linux-5.7.10  ls net/netlink config      Makefile     af_netlink.c af_netlink.h diag.c       genetlink.c helightxu@  ~/open_code/linux-5.7.10 
文件描述
af_netlink.c 和 af_netlink.h:是 netlink 的核心文件,这个也是下面详细走读的内容。
diag.c对 netlink sock 进行监控,可以插入到内核或者从内核中卸载
genetlink.c这个可以看作是 netlink 的升级版本,或者说是一种高层封装。

注:

genetlink.c 额外说明:netlink 默认支持了 30 多种的场景,但是对于其它场景并没有具体定义,这个时候这种通用封装就有了很大的好处,可以在不改动内核的前提下进行应用场景扩展,这部分内容可以看这个 wiki:https://wiki.linuxfoundation.org/networking/generic_netlink_howto

还有一个头文件是在 include  目录,如下所示,这个头文件是一些辅助函数、宏定义和相关数据结构,大家学习的同学一定要看这个文件,它里面的注释非常详细。这些注释对理解 netlink  的消息结构非常有用,建议可以详细看看。

helightxu@  ~/open_code/linux-5.7.10  ls include/net/netlink.h

af_netlink.c 代码走读

在 af_netlink.c 这个文件的最下面有一行代码:

core_initcall(netlink_proto_init);

这段代码的意思是什么呢?通过看这个代码最终的实现可以看出,就是告诉编译器把 netlink_proto_init 这个函数放入到最终编译出来二进制文件的  .init 段中,内核在启动的时候会从这个端里面的函数挨个的执行。这里也就是说 netlink  是内核默认就直接支持的,是原生内核的一部分(这里其实想和内核的动态插拔模块区别)。

在 netlink_proto_init 函数中最关键的一行代码就是下面最后一行,把 netlink 的协议族注册到网络协议栈中去。

static const struct net_proto_family netlink_family_ops = {     .family = PF_NETLINK,     .create = netlink_create,     .owner  = THIS_MODULE,   }; ... sock_reGISter(&netlink_family_ops);

PF_NETLINK 是表示 netlink 的协议族,在后面我们在客户端创建 netlink socket  的时候就要用到这个东东。如下面的代码,代码来自我的测试代码  Https://GitHub.com/helight/kernel_modules/tree/master/netlink_test  中的客户端代码,可以看出:PF_NETLINK 表示我们所用的就是 netlink 的协议,SOCK_RAW  表示我们使用的是原始协议包,NETLINK_USER 这个我们自己定义的一个协议字段。netlink 我们前面说了有 30  多种应用场景,这些都已经在内核代码中固定了,所以在客户端使用的时候会指定这个字段来表示和内核中的那个应用场景的函数模块进行交互。

//int socket(int domain, int type, int protocol);     sock_fd = socket(PF_NETLINK, SOCK_RAW, NETLINK_USER);

sock_register 这个函数的作用主要就是注册 PF_NETLINK 这个协议类型到内核中,让内核认识这个协议,在内核网络协议中建立 socket  的时候知道该使用那个协议为它提供操作支持。

注册了之后内核就支持了 netlink 协议了,接下来就是内核中创建监听 socket,用户态创建链接 socket 了。

netlink 用户态和内核交互过程

这里我简单画一个图来表示一下,socket 通信主要有 2 个操作对象:server 端和 client 端。netlink 的操作原理是这样的:

对象所处位置-
server 端内核中-
client 端用户态进程-

netlink 关键数据结构和函数

sockaddr_nl 协议套接字

netlink 的地址表示由 sockaddr_nl 负责

struct sockaddr_nl {     __kernel_sa_family_t    nl_family;         unsigned short          nl_pad;             __u32                   nl_pid;             __u32                   nl_groups;     };

nl_family 制定了协议族,netlink 有自己独立的值:AF_NETLINK,nl_pid 一般取为进程 pid。nl_groups  用以多播,当不需要多播时,该字段为 0。

nlmsghdr 消息体

netlink 消息是作为套接字缓冲区 sk_buff 的数据部分传递的,其消息本身又分为头部和数据。头部为:

struct nlmsghdr {     __u32        nlmsg_len;         __u16        nlmsg_type;         __u16        nlmsg_flags;         __u32        nlmsg_seq;         __u32        nlmsg_pid;     };

nlmsg_len 为消息的长度,包含该头部在内。nlmsg_pid 为发送进程的端口 ID,这个用户可以自定义,一般也是使用进程 pid。

msghdr 用户态系发送消息体

使用 sendmsg 和 recvmsg 函数进行发送和接收消息,使用的消息体是这个样子的。

struct iovec {                         void  *iov_base;                   size_t iov_len;                };  struct msghdr {     void    *    msg_name;         int          msg_namelen;         struct iovec *    msg_iov;         __kernel_size_t   msg_iovlen;         void     *         msg_control;         __kernel_size_t    msg_controllen;         unsigned int      msg_flags; }; 

逻辑结构如下:

如何进行Netlink源码及实例的分析

socket 也是一种特殊的文件,通过 VFS 的接口同样可以对其进行使用管理。socket  本身就需要实现文件系统的相应接口,有自己的操作方法集。

netlink 常用宏

#define NLMSG_ALIGNTO   4U #define NLMSG_ALIGN(len) ( ((len)+NLMSG_ALIGNTO-1) & ~(NLMSG_ALIGNTO-1) )  #define NLMSG_HDRLEN     ((int) NLMSG_ALIGN(sizeof(struct nlmsghdr)))  #define NLMSG_LENGTH(len) ((len) + NLMSG_HDRLEN)  #define NLMSG_SPACE(len) NLMSG_ALIGN(NLMSG_LENGTH(len))  #define NLMSG_DATA(nlh)  ((void*)(((char*)nlh) + NLMSG_LENGTH(0)))  #define NLMSG_NEXT(nlh,len)  ((len) -= NLMSG_ALIGN((nlh)->nlmsg_len), \                  (struct nlmsghdr*)(((char*)(nlh)) + NLMSG_ALIGN((nlh)->nlmsg_len)))  #define NLMSG_OK(nlh,len) ((len) >= (int)sizeof(struct nlmsghdr) && \               (nlh)->nlmsg_len >= sizeof(struct nlmsghdr) && \               (nlh)->nlmsg_len <= (len))  #define NLMSG_PAYLOAD(nlh,len) ((nlh)->nlmsg_len - NLMSG_SPACE((len)))

netlink 内核常用函数

netlink_kernel_create

这个内核函数用于创建内核 socket,以提供和用户态通信

static inline struct sock * netlink_kernel_create(struct net *net, int unit, struct netlink_kernel_cfg *cfg)    struct netlink_kernel_cfg {     unsigned int    groups;     unsigned int    flags;     void        (*input)(struct sk_buff *skb);      struct mutex    *cb_mutex;     void        (*bind)(int group);     bool        (*compare)(struct net *net, struct sock *sk); };

单播函数 netlink_unicast() 和多播函数 netlink_broadcast()

 extern int netlink_unicast(struct sock *ssk, struct sk_buff *skb, __u32 portid, int nonblock);    extern int netlink_broadcast(struct sock *ssk, struct sk_buff *skb, __u32 portid,                  __u32 group, gfp_t allocation); 

测试例子代码

netlink 内核建立 socket 过程

内核的代码非常简单,这里给出了核心代码,就这么多,接收函数中直接打印了接收到的消息。

#include <linux/module.h>#include <linux/kernel.h>#include <linux/init.h>#include <net/sock.h>#include <asm/types.h>#include <linux/netlink.h>#include <linux/skbuff.h> #define NETLINK_XUX           31          static struct sock *xux_sock = NULL;  // 接收消息的回调函数,接收参数是 sk_buff static void recv_netlink(struct sk_buff *skb) {     struct nlmsghdr *nlh;     nlh = nlmsg_hdr(skb); // 取得消息体     printk("receive data from user process: %s", (char *)NLMSG_DATA(nlh)); // 打印接收的数据内容      ... }  int __init init_link(void) {     struct netlink_kernel_cfg cfg = {         .input = recv_netlink,     };     xux_sock = netlink_kernel_create(&init_net, NETLINK_XUX, &cfg); // 创建内核 socket     if (!xux_sock){         printk("cannot initialize netlink socket");         return -1;     }          printk("Init OK!\n");     return 0; }

netlink 用户态建立链接和收发信息

... // 上面的就省了 #define NETLINK_USER 31  //self defined #define MAX_PAYLOAD 1024  struct sockaddr_nl src_addr, dest_addr; struct nlmsghdr *nlh = NULL; struct msghdr msg; struct iovec iov; int sock_fd;  int main(int args, char *argv[]) {     sock_fd = socket(PF_NETLINK, SOCK_RAW, NETLINK_USER); // 建立 socket      if(sock_fd < 0)         return -1;      memset(&src_addr, 0, sizeof(src_addr));     src_addr.nl_family = AF_NETLINK;     src_addr.nl_pid = getpid();       if(bind(sock_fd, (struct sockaddr*)&src_addr, sizeof(src_addr))){ // 和指定协议进行 socket 绑定         perror("bind() error\n");         close(skfd);         return -1;     }      memset(&dest_addr, 0, sizeof(dest_addr));     dest_addr.nl_family = AF_NETLINK;     dest_addr.nl_pid = 0;            dest_addr.nl_groups = 0;          nlh = (struct nlmsghdr *)malloc(NLMSG_SPACE(MAX_PAYLOAD));     memset(nlh, 0, NLMSG_SPACE(MAX_PAYLOAD));     nlh->nlmsg_len = NLMSG_SPACE(MAX_PAYLOAD);     nlh->nlmsg_pid = getpid();  //self pid     nlh->nlmsg_flags = 0;     // 拷贝信息到发送缓冲中     strcpy(NLMSG_DATA(nlh), "Hello this is a msg from userspace");     // 构造发送消息体     iov.iov_base = (void *)nlh;         //iov -> nlh     iov.iov_len = nlh->nlmsg_len;     msg.msg_name = (void *)&dest_addr;     msg.msg_namelen = sizeof(dest_addr);     msg.msg_iov = &iov;  // iov 中存放 netlink 消息头和消息数据     msg.msg_iovlen = 1;      printf("Sending message to kernel\n");      int ret = sendmsg(sock_fd, &msg, 0);  // 发送消息到内核     printf("send ret: %d\n", ret);      printf("Waiting for message from kernel\n");           recvmsg(sock_fd, &msg, 0);     printf("Received message payload: %s\n", NLMSG_DATA(nlh));  // 打印接收到的消息      close(sock_fd);     return 0; }

netlink 目前感觉还是一个比较好用的内核和用户空间的交互方式,但是也是有他的使用场景,适合用户空间和内核空间主动交互的场景。

但是在单机场景下,大多数的主动权在用户进程,用户进程写数据到内核,用户进程主动读取内核数据。这两种场景覆盖了内核的绝大多数场景。

在内核要主动的场景下,netlink 就比较适合。我能想到的就是内核数据审计,安全触发等,这类场景下内核可以实时的告知用户进程内核发生的情况。

我是在看 ipvs 的代码时候看到了里面有 netlink 的使用,发现早期 iptables 就是使用 netlink 来下发配置指令的,内核中  netfilter 和 iptables 中还有这部分的代码,今天也顺便下载大致走读了一遍,大家可以搜索 NETLINK 这个关键字来看。但是 iptables  后来的代码中没有使用这样的方式,而是采用了一个叫做 iptc 的库,其核心思路还是使用 setsockops 的方式,最终还是  copy_from_user。不过这种方式对于 iptables 这种配置下发的场景来说还是非常实用的。

以上就是如何进行Netlink源码及实例的分析,小编相信有部分知识点可能是我们日常工作会见到或用到的。希望你能通过这篇文章学到更多知识。更多详情敬请关注编程网精选频道。

--结束END--

本文标题: 如何进行Netlink源码及实例的分析

本文链接: https://www.lsjlt.com/news/281742.html(转载时请注明来源链接)

有问题或投稿请发送至: 邮箱/279061341@qq.com    QQ/279061341

本篇文章演示代码以及资料文档资料下载

下载Word文档到电脑,方便收藏和打印~

下载Word文档
猜你喜欢
  • 如何进行Netlink源码及实例的分析
    本篇文章给大家分享的是有关如何进行Netlink源码及实例的分析,小编觉得挺实用的,因此分享给大家学习,希望大家阅读完这篇文章后可以有所收获,话不多说,跟着小编一起来看看吧。前言这几天在看 ipvs 相关代码的时候又遇到了 netlink ...
    99+
    2023-06-15
  • 如何进行FileZilla的源代码分析
    这篇文章将为大家详细讲解有关如何进行FileZilla的源代码分析,文章内容质量较高,因此小编分享给大家做个参考,希望大家阅读完这篇文章后对相关知识有一定的了解。FileZilla是一种快速、可信赖的FTP客户端以及服务器端开放源代码程式,...
    99+
    2023-06-16
  • 【Mybatis源码解析】mapper实例化及执行流程源码分析
    文章目录 简介 环境搭建 源码解析 附 基础环境:JDK17、SpringBoot3.0、mysql5.7 储备知识:《【Spring6源码・AOP】AOP源码解析》、《JDBC详细...
    99+
    2023-08-20
    mybatis java spring boot
  • 如何进行C#打印设置实现源码的分析
    本篇文章为大家展示了如何进行C#打印设置实现源码的分析,内容简明扼要并且容易理解,绝对能使你眼前一亮,通过这篇文章的详细介绍希望你能有所收获。C#打印设置是如何在实际编程开发中体现的呢?C#打印设置需要注意什么呢?C#打印设置常用属性是如何...
    99+
    2023-06-17
  • 如何进行Redux的源码解析
    如何进行Redux的源码解析,很多新手对此不是很清楚,为了帮助大家解决这个难题,下面小编将为大家详细讲解,有这方面需求的人可以来学习下,希望你能有所收获。预热redux 函数内部包含了大量柯里化函数以及代码...
    99+
    2024-04-02
  • 如何进行jQuery源码的整体框架分析
    这篇文章将为大家详细讲解有关如何进行jQuery源码的整体框架分析,文章内容质量较高,因此小编分享给大家做个参考,希望大家阅读完这篇文章后对相关知识有一定的了解。先附上jQuery的代码结构。JS代码(fu...
    99+
    2024-04-02
  • 如何进行jQuery EasyUI 1.2.6源码合集的分析
    如何进行jQuery EasyUI 1.2.6源码合集的分析,很多新手对此不是很清楚,为了帮助大家解决这个难题,下面小编将为大家详细讲解,有这方面需求的人可以来学习下,希望你能有所收获。相信关注过jQuer...
    99+
    2024-04-02
  • 如何进行HashMap扩容机制源码分析
    这期内容当中小编将会给大家带来有关如何进行HashMap扩容机制源码分析,文章内容丰富且以专业的角度为大家分析和叙述,阅读完这篇文章希望大家可以有所收获。具体看源码之前,我们先简单的说一下HashMap的底层数据结构  1、HashMap底...
    99+
    2023-06-02
  • 如何用源码分析Java HashMap实例
    本篇文章为大家展示了如何用源码分析Java HashMap实例,内容简明扼要并且容易理解,绝对能使你眼前一亮,通过这篇文章的详细介绍希望你能有所收获。引言HashMap在键值对存储中被经常使用,那么它到底是如何实现键值存储的呢?一 Entr...
    99+
    2023-06-17
  • 怎么进行ActionInvoker源码分析
    本篇内容介绍了“怎么进行ActionInvoker源码分析”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成!throw new&nbs...
    99+
    2023-06-17
  • 如何进行Nginx内核优化的源代码分析
    如何进行Nginx内核优化的源代码分析,很多新手对此不是很清楚,为了帮助大家解决这个难题,下面小编将为大家详细讲解,有这方面需求的人可以来学习下,希望你能有所收获。Nginx内核优化在不断的使用中有很多的问...
    99+
    2024-04-02
  • 怎样进行FileZilla的源代码分析
    本篇文章为大家展示了怎样进行FileZilla的源代码分析,内容简明扼要并且容易理解,绝对能使你眼前一亮,通过这篇文章的详细介绍希望你能有所收获。FileZilla是一种快速、可信赖的FTP客户端以及服务器端开放源代码程式,具有多种特色、直...
    99+
    2023-06-16
  • 怎么进行FileZilla源代码分析
    怎么进行FileZilla源代码分析,针对这个问题,这篇文章详细介绍了相对应的分析和解答,希望可以帮助更多想解决这个问题的小伙伴找到更简单易行的方法。FileZilla是一种快速、可信赖的FTP客户端以及服务器端开放源代码程式,具有多种特色...
    99+
    2023-06-16
  • Java从JDK源码角度对Object进行的示例分析
    小编给大家分享一下Java从JDK源码角度对Object进行的示例分析,相信大部分人都还不怎么了解,因此分享这篇文章给大家参考一下,希望大家阅读完这篇文章后大有收获,下面让我们一起去了解一下吧!Object是所有类的父类,也就是说java中...
    99+
    2023-05-30
    java object
  • 如何进行WSRP的示例分析
    今天就跟大家聊聊有关如何进行WSRP的示例分析,可能很多人都不太了解,为了让大家更加了解,小编给大家总结了以下内容,希望大家根据这篇文章可以有所收获。这周除了继续思考数据权限模型的建模和实现以外,主要还研究了一下WSRP(Web Servi...
    99+
    2023-06-03
  • 如何进行Kafka 1.0.0 d代码示例分析
    这篇文章将为大家详细讲解有关如何进行Kafka 1.0.0 d代码示例分析,文章内容质量较高,因此小编分享给大家做个参考,希望大家阅读完这篇文章后对相关知识有一定的了解。package kafka.demo;import ...
    99+
    2023-06-02
  • java LRU算法及Apache LRUMap源码实例分析
    本篇内容主要讲解“java LRU算法及Apache LRUMap源码实例分析”,感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习“java LRU算法及Apache LRUMap源...
    99+
    2023-06-21
  • vue Proxy数据代理进行校验部分源码实例解析
    目录initProxy触发代理数据过滤总结 initProxy 数据拦截的思想除了为构建响应式系统准备,它也可以为数据进行筛选过滤,我们接着往下看初始化的代码,在合并选项后...
    99+
    2024-04-02
  • 如何进行数据库中间件 MyCAT 源码分析
    这篇文章将为大家详细讲解有关如何进行数据库中间件 MyCAT 源码分析,文章内容质量较高,因此小编分享给大家做个参考,希望大家阅读完这篇文章后对相关知识有一定的了解。1. 概述可能你在看到这个标题会小小的吃...
    99+
    2024-04-02
  • Java异步编程中如何进行FutureTask源码分析
    本篇文章给大家分享的是有关Java异步编程中如何进行FutureTask源码分析,小编觉得挺实用的,因此分享给大家学习,希望大家阅读完这篇文章后可以有所收获,话不多说,跟着小编一起来看看吧。Java的异步编程是一项非常常用的多线程技术。但之...
    99+
    2023-06-19
软考高级职称资格查询
编程网,编程工程师的家园,是目前国内优秀的开源技术社区之一,形成了由开源软件库、代码分享、资讯、协作翻译、讨论区和博客等几大频道内容,为IT开发者提供了一个发现、使用、并交流开源技术的平台。
  • 官方手机版

  • 微信公众号

  • 商务合作