Netlink 和 libnl 基础

1 分钟读完

本文介绍了netlink和libnl。

Netlink简介

Netlink is used to transfer information between kernel and user-space processes. It consists of a standard sockets-based interface for user space processes and an internal kernel API for kernel modules.

Netlink是基于socket的用户空间进程和内核态进程通信的方式。 用途有两个,

  • 内核为用户态程序提供了很多接口(如route, firewall, ipsec等),直接在用户空间连接相应的socket,就可以获得广播信息。
  • 自己编写内核模块,需要和用户空间通信,比/proc,设备驱动方式更加灵活。

这里不得不插一句,socket真是个牛逼的工具:

  • 网络间通信(网络上的两个进程)用: Network socket
  • 同一主机内进程间通信(用户态): Unix domain socket
  • 用户态进程和内核态进程通信: Netlink socket

创建一个Netlink socket的方式如下

netlink_socket = socket(AF_NETLINK, socket_type, netlink_family);

实际上domain和socket_type都是固定的,其中socket_type可以是SOCK_DGRAM,但是不予区分。所以创建一个netlink socket的接口基本如下

netlink_socket = socket(AF_NETLINK, SOCK_RAW, netlink_family);

变化的只是netlink_family,socket的第三个参数protocol,为了说明netlink_family的作用,先看看通常的UDP/TCP socket的创建方式

udp_socket = socket(AF_INET, SOCK_DGRAM, 0);
tcp_socket = socket(AF_INET, SOCK_STREAM, 0);

通过here 发现UDP是SOCK_DGRAM类型的默认协议,TCP是SOCK_STREAM类型的默认协议,所以也可以写成

udp_socket = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
tcp_socket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);

那么netlink_family的作用是什么?

netlink_family selects the kernel module or netlink group to communicate with. –netlink manual

The protocol specifies a particular protocol to be used with the socket. Normally only a single protocol exists to support a particular socket type within a given protocol family, in which case protocol can be specified as 0. However, it is possible that many protocols may exist, in which case a particular protocol must be specified in this manner. The protocol number to use is specific to the “communication domain” in which communication is to take place; – socket manual

socket手册对protocol的解释是,如果socket_type不能唯一标识通信协议,就要用protocol来指定明确的通信“协议”(封装标准)。

所以这个protocol就是指定通信标准,常用的网络编程主要是TCP/UDP socket,该参数设置为0,用默认的协议,导致了在netlink socket上的疑虑。 netlink_family 列表见man7.org

实例1

这里有一个Netlink实例

在内核中PF_NETLINK和AF_NETLINK是一个东西,证据在此

  • network socket 用<saddr,sport,daddr,dport>四元组来标识一个通信链接(链路,两个进程)。
  • unix domain socket 用 pid来标识两个进程。 -netlink socket 用pid来标识两个进程 ?
    struct sockaddr_nl {
        sa_family_t     nl_family;  /* AF_NETLINK */
        unsigned short  nl_pad;     /* Zero. */
        pid_t           nl_pid;     /* Port ID. */
        __u32           nl_groups;  /* Multicast groups mask. */
      };

nl_pid is the unicast address of netlink socket. It’s always 0 if the destination is in the kernel. For a user-space process, nl_pid is usually the PID of the process owning the destination socket. However, nl_pid identifies a netlink socket, not a process.

nl_pid在用户空间能标识一个进程,在内核空间通过netlink_kernel_create创建socket并注册消息处理函数(每次收到数据后调用)。
用户态进程发送的数据怎么交付给内核态进程? 应该是nl_family?细节尚未搞懂。

libnl简介

The libnl suite is a collection of libraries providing APIs to netlink protocol based Linux kernel interfaces.

libnl 库封装了netlink socket 底层操作,提供了一系列高级API,简化了netlink编程。 libnl 包含四个主要的lib

  • libnl
  • libnl-genl
  • libnl-route
  • libnl-nf

如下图所示

libnl-framework

从图中可以清楚的看出,原来需要直接操作socket接口的工作,现在可以用libnl来间接完成。

实例2

stackoverflow上同样有一个类似【实例1】的程序,区别在于其用libnl实现,libnl example

参考


  • http://man7.org/linux/man-pages/man7/netlink.7.html
  • http://www.infradead.org/~tgr/libnl/

下一步

根据内核提供的taskstats接口,分析用libnl实现的taskstats监控程序,理解netlink在内核的工作原理。

  • https://github.com/mechanicalpulse/taskstats
  • https://www.kernel.org/doc/Documentation/accounting/taskstats.txt

留下评论