文章目录

  • 前言
  • 一、Netlink用户态应用的使用
    • 1.1 Netlink socket
    • 1.2 Netlink宏操作
  • 二、Netlink对应数据结构
    • 2.1 struct sockaddr_nl
    • 2.2 struct nlmsghdr
    • 2.3 struct msghdr
  • 三、用户层实例
  • 参考资料

前言

The Netlink socket family 是一个 Linux 内核接口,用于内核和用户空间进程之间以及不同用户空间进程之间的进程间通信 (IPC),其方式类似于 Unix domain sockets。 与 Unix domain sockets类似,但与 INET sockets不同,Netlink 通信不能跨越主机边界。Unix domain sockets使用文件系统名称空间,Netlink进程通常由进程标识符(pid)寻址。

Netlink 设计用于在内核空间和用户空间进程之间传输各种网络信息。 网络实用程序,例如 iproute2 工具包,使用 Netlink 从用户空间与 Linux 内核进行通信。 Netlink 为用户空间进程提供了一个标准的基于套接字的接口,以及一个供内核模块内部使用的内核端 API。 Netlink 使用 AF_NETLINK socket family。

Netlink 主要是用来进行内核和用户空间进程之间的通信,用户空间进程之间的进程间通信 (IPC)一般不用 Netlink。

Netlink 支持双工通信,是一种异步通信机制,采用数据报信息(SOCK_DGRAM)格式传送数据。在内核与用户态应用之间传递的消息保存在socket缓存队列中,发送消息只是把消息保存在接收者的socket的接收队列,而不需要等待接收者收到消息。

该机制一个非常重要的用户是通用设备模型,使用netlink套接字将各种关于内核内部事务的状态信息传递到用户层,其中包括新设备的注册和移除、硬件层次上发生的特别事件等等。

一、Netlink用户态应用的使用

1.1 Netlink socket

以下命令查看netlink套接字相关信息:

man 7 netlink

Netlink是一个面向数据报的服务。用户态应用使用标准的 socket API 就可以使用 netlink 提供的强大功能,AF_NETLINK 系列提供多个协议子集。 每个接口都连接到不同的内核组件并具有不同的消息传递子集。 该子集由套接字调用中的 the protocol 字段引用:

/* Create a new socket of type TYPE in domain DOMAIN, usingprotocol PROTOCOL.  If PROTOCOL is zero, one is chosen automatically.Returns a file descriptor for the new socket, or -1 for errors.  */
extern int socket (int __domain, int __type, int __protocol) __THROW;int socket(AF_NETLINK, SOCK_DGRAM or SOCK_RAW, protocol)

第一个参数必须是PF_NETLINK或者AF_NETLINK(这两者等价)。

对于第二个参数,由于缺乏标准,SOCK_DGRAM 和 SOCK_RAW 不能保证在给定的 Linux(或其他操作系统)版本中实现。 一些消息来源指出这两个选项都是合理的,Red Hat 推荐使用 SOCK_RAW 作为参数。 但是,iproute2 工具包中可以互换使用两者。

对于第三个参数,netlink提供“协议”来标示通信实体,在创建socket的时候,需要指定netlink的通信协议号。每个协议号代表一种“应用”,上层可以用内核已经定义的协议和内核进行通信,获得内核已经提供的信息。其中protocol的选项可以是:

#define NETLINK_ROUTE        0   /* Routing/device hook              */
#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 */
#define NETLINK_XFRM        6   /* ipsec */
#define NETLINK_SELINUX     7   /* SELinux event notifications */
#define NETLINK_ISCSI       8   /* Open-iSCSI */
#define NETLINK_AUDIT       9   /* auditing */
#define NETLINK_FIB_LOOKUP  10
#define NETLINK_CONNECTOR   11
#define NETLINK_NETFILTER   12  /* netfilter subsystem */
#define NETLINK_IP6_FW      13
#define NETLINK_DNRTMSG     14  /* DECnet routing messages */
#define NETLINK_KOBJECT_UEVENT  15  /* Kernel messages to userspace */
#define NETLINK_GENERIC     16
/* leave room for NETLINK_DM (DM Events) */
#define NETLINK_SCSITRANSPORT   18  /* SCSI Transports */
#define NETLINK_ECRYPTFS    19
#define NETLINK_RDMA        20
#define NETLINK_CRYPTO      21  /* Crypto layer */#define NETLINK_INET_DIAG NETLINK_SOCK_DIAG#define MAX_LINKS 32

NETLINK_ROUTE:提供路由和链接信息, 此信息主要用于用户空间路由守护程序。
NETLINK_FIREWALL:为用户空间应用程序提供接收防火墙数据包的接口。
NETLINK_NFLOG:提供了一个用于Netfilter(内核模块)和iptables(用户空间工具包)之间通信的接口。
NETLINK_ARPD:提供从用户空间管理 ARP 表的接口。
NETLINK_AUDIT:为 Linux 内核版本 2.6.6 及更高版本中的审计子系统提供接口。
NETLINK_IP6_FW:提供一个接口将数据包从 netfilter 传输到用户空间。
NETLINK_XFRM:用于发送和接受有关IPSec的信息。
NETLINK_KOBJECT_UEVENT:提供内核广播 uevent 的接口,通常由 udev 使用,是内核通用模型向用户层发送信息所采用的协议(内核热插拔机制的基础)。
NETLINK_GENERIC:Netlink 协议的缺点之一是协议族的数量被限制为 32 (MAX_LINKS)。这是创建通用 Netlink 族的主要原因之一 – 为增加更多的 families 提供支持。它充当一个Netlink多路复用器,并与单个Netlink families NETLINK_GENERIC一起工作。通用Netlink协议基于Netlink协议并使用它的API

由此可见在Linux内核中,使用nertlink进行应用与内核通信的应用很多。

对于上述协议的用途,比如用来创建一个上层应用,通过使用NETLINK_ROUTE协议通信,可以获得内核的路由信息。当然我们也可以自定义通信协议,但不能使用已有的协议编号,也不能超过MAX_LINKS,因此我们我们自定义netlink通信协议编号可以 22 - 31

1.2 Netlink宏操作

Netlink消息由一个字节流和一个或多个nlmsghdr头以及相关的有效负载组成。只能使用标准的NLMSG_*宏访问字节流。

<linux/netlink.h> 定义了几个标准宏来访问或创建 netlink 数据报。 应该只使用这些宏来访问传入和传出 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)))
NLMSG_ALIGN()Round the length of a netlink message up to align it properly.NLMSG_LENGTH()Given the payload length, len, this macro returns the aligned length to store in the nlmsg_len field of the nlmsghdr.NLMSG_SPACE()Return the number of bytes that a netlink message with payload of len would occupy.NLMSG_DATA()Return a pointer to the payload associated with the passed nlmsghdr.NLMSG_NEXT()Get the next nlmsghdr in a multipart message.  The caller must check if the current nlmsghdr didn't have the NLMSG_DONE set—this function doesn't return NULL on end.  The len  argumentis an lvalue containing the remaining length of the message buffer.  This macro decrements it by the length of the message header.NLMSG_OK()Return true if the netlink message is not truncated and is in a form suitable for parsing.NLMSG_PAYLOAD()Return the length of the payload associated with the nlmsghdr.

二、Netlink对应数据结构

2.1 struct sockaddr_nl

struct sockaddr_nl是netlink套接字通信地址:

struct sockaddr_nl {__kernel_sa_family_t nl_family;  /* AF_NETLINK   */unsigned short            nl_pad;     /* zero     */__u32                 nl_pid;     /* port ID  unicast address */__u32                 nl_groups;  /* multicast groups mask */
};

nl_pid 是 netlink 套接字的单播地址,一个约定的通信端口,用户态使用的时候需要用一个非0的数字,一般来说可以直接采用上层应用的进程ID(不用进程ID号码也没事,只要系统中不冲突的一个数字即可使用)。对于内核的地址,该值必须用0,也就是说,如果上层通过sendto向内核发送netlink消息,peer addr中nl_pid必须填写0。
nl_pid 标识的是 netlink 套接字,而不是进程。本质上,nl_pid就是netlink的通信地址。

通过 protocol 和 nl_pid 组成Netlink的通信方式。

编写netlink通信时,需要源struct sockaddr_nl结构体和目的地 struct sockaddr_nl结构体。

2.2 struct nlmsghdr

Netlink的报文由消息头和消息体构成,struct nlmsghdr即为消息头:

struct nlmsghdr {__u32       nlmsg_len;  /* Length of message including header */__u16       nlmsg_type; /* Message content */__u16      nlmsg_flags;    /* Additional flags */__u32     nlmsg_seq;  /* Sequence number */__u32      nlmsg_pid;  /* Sending process port ID */
};

(1) nlmsg_len:整个消息的长度,按字节计算。包括了Netlink消息头本身。
(2) nlmsg_type:消息的类型,即是数据还是控制消息。
(3) nlmsg_flags:附加在消息上的额外说明信息。


其中的nlmsg_flags:

/* Flags values */#define NLM_F_REQUEST      1   /* It is request message.   */
#define NLM_F_MULTI     2   /* Multipart message, terminated by NLMSG_DONE */
#define NLM_F_ACK       4   /* Reply with ack, with zero or error code */
#define NLM_F_ECHO      8   /* Echo this request        */
#define NLM_F_DUMP_INTR     16  /* Dump was inconsistent due to sequence change *//* Modifiers to GET request */
#define NLM_F_ROOT  0x100   /* specify tree root    */
#define NLM_F_MATCH 0x200   /* return all matching  */
#define NLM_F_ATOMIC    0x400   /* atomic GET       */
#define NLM_F_DUMP  (NLM_F_ROOT|NLM_F_MATCH)/* Modifiers to NEW request */
#define NLM_F_REPLACE   0x100   /* Override existing        */
#define NLM_F_EXCL  0x200   /* Do not touch, if it exists   */
#define NLM_F_CREATE    0x400   /* Create, if it does not exist */
#define NLM_F_APPEND    0x800   /* Add to end of list       */

由于Netlink socketd的type是SOCK_DGRAM ,所以Netlink是一个不可靠的协议。它尽最大努力将消息传递到目的地,但当内存不足的情况或其他错误发生时,可能会丢弃消息。为了实现可靠的传输,发送方可以通过设置NLM_F_ACK标志向接收方请求确认。确认是一个 NLMSG_ERROR 数据包,错误字段设置为 0。应用程序必须为接收到的消息本身生成确认。内核尝试为每个失败的包发送一个NLMSG_ERROR消息。用户进程也应该遵循这个约定。

#define NLMSG_NOOP       0x1 /* Nothing.     */
#define NLMSG_ERROR     0x2 /* Error        */
#define NLMSG_DONE      0x3 /* End of a dump    */
#define NLMSG_OVERRUN       0x4 /* Data lost        */#define NLMSG_MIN_TYPE        0x10    /* < 0x10: reserved control messages */

2.3 struct msghdr

用户层调用sendmsg和recvmsg是所需要的结构体:

/* Send a message described MESSAGE on socket FD.Returns the number of bytes sent, or -1 for errors.This function is a cancellation point and therefore not marked with__THROW.  */
extern ssize_t sendmsg (int __fd, const struct msghdr *__message,int __flags);
/* Receive a message as described by MESSAGE from socket FD.Returns the number of bytes read or -1 for errors.This function is a cancellation point and therefore not marked with__THROW.  */
extern ssize_t recvmsg (int __fd, struct msghdr *__message, int __flags);
/* Structure describing messages sent by`sendmsg' and received by `recvmsg'.  */
struct msghdr{void *msg_name;       /* Address to send to/receive from.  */socklen_t msg_namelen;   /* Length of address data.  */struct iovec *msg_iov;    /* Vector of data to send/receive into.  */size_t msg_iovlen;       /* Number of elements in the vector.  */void *msg_control;      /* Ancillary data (eg BSD filedesc passing). */size_t msg_controllen;   /* Ancillary data buffer length.!! The type should be socklen_t but thedefinition of the kernel is incompatiblewith this.  */int msg_flags;     /* Flags on received message.  */};

其中 struct iovec :

/* Structure for scatter/gather I/O.  */
struct iovec
{void *iov_base;    /* Pointer to data.  */size_t iov_len;  /* Length of data.  */
};

iov_base: iov_base指向数据包缓冲区,即参数buff,iov_len是buff的长度。msghdr中允许一次传递多个buff,以数组的形式组织在 msg_iov中,msg_iovlen就记录数组的长度 (即有多少个buff)。
iov_base指向如下数据结构(包含了消息头 struct nlmsghdr ):

以上三种关系如下图所示:

图片来自于:linux用户空间与内核空间通信——Netlink通信机制

三、用户层实例

NETLINK_ROUTE:接收路由和链路更新,并可用于修改路由表(IPv4和IPv6)、IP地址、链路参数、邻居设置、排队规则、流分类和包分类。

/*****       General form of address family dependent message.****/struct rtgenmsg {unsigned char        rtgen_family;
};/******************************************************************       Link layer specific messages.****//* struct ifinfomsg* passes link level specific information, not dependent* on network protocol.*/struct ifinfomsg {unsigned char ifi_family;unsigned char    __ifi_pad;unsigned short    ifi_type;       /* ARPHRD_* */int       ifi_index;      /* Link index   */unsigned  ifi_flags;      /* IFF_* flags  */unsigned  ifi_change;     /* IFF_* change mask */
};/********************************************************************
/* Generic structure for encapsulation of optional route information.It is reminiscent of sockaddr, but with sa_family replacedwith attribute type.*/struct rtattr {unsigned short   rta_len;unsigned short  rta_type;
};/* Macros to handle rtattributes */#define RTA_ALIGNTO    4
#define RTA_ALIGN(len) ( ((len)+RTA_ALIGNTO-1) & ~(RTA_ALIGNTO-1) )
#define RTA_OK(rta,len) ((len) >= (int)sizeof(struct rtattr) && \(rta)->rta_len >= sizeof(struct rtattr) && \(rta)->rta_len <= (len))
#define RTA_NEXT(rta,attrlen)   ((attrlen) -= RTA_ALIGN((rta)->rta_len), \(struct rtattr*)(((char*)(rta)) + RTA_ALIGN((rta)->rta_len)))
#define RTA_LENGTH(len) (RTA_ALIGN(sizeof(struct rtattr)) + (len))
#define RTA_SPACE(len)  RTA_ALIGN(RTA_LENGTH(len))
#define RTA_DATA(rta)   ((void*)(((char*)(rta)) + RTA_LENGTH(0)))
#define RTA_PAYLOAD(rta) ((int)((rta)->rta_len) - RTA_LENGTH(0))
enum {IFLA_UNSPEC,IFLA_ADDRESS,IFLA_BROADCAST,IFLA_IFNAME,IFLA_MTU,IFLA_LINK,IFLA_QDISC,IFLA_STATS,......
}

获取网络接口的名字:

/**  Display all network interface names*/
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <linux/netlink.h>
#include <linux/rtnetlink.h> #define BUFSIZE 8192struct nl_req_s {struct nlmsghdr hdr;struct rtgenmsg gen;
};void rtnl_print_link(struct nlmsghdr * h)
{struct ifinfomsg * iface;struct rtattr * attr;int len;iface = NLMSG_DATA(h);len = RTM_PAYLOAD(h);/* loop over all attributes for the NEWLINK message */for (attr = IFLA_RTA(iface); RTA_OK(attr, len); attr = RTA_NEXT(attr, len)){switch (attr->rta_type){case IFLA_IFNAME:printf("Interface %d : %s\n", iface->ifi_index, (char *)RTA_DATA(attr));break;default:break;}}
}int main(void)
{struct sockaddr_nl src_addr;int s, end=0, len;struct msghdr msg;struct nl_req_s req;struct iovec io;char buf[BUFSIZE];//build src_addr netlink addressmemset(&src_addr, 0, sizeof(src_addr));src_addr.nl_family = AF_NETLINK;src_addr.nl_groups = 0;//create a Netlink socketif ((s=socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE)) < 0){perror("socket");return -1;}//build netlink messagememset(&req, 0, sizeof(req));req.hdr.nlmsg_len = NLMSG_LENGTH(sizeof(struct rtgenmsg));req.hdr.nlmsg_type = RTM_GETLINK;req.hdr.nlmsg_flags = NLM_F_REQUEST | NLM_F_DUMP;req.hdr.nlmsg_seq = 1;req.hdr.nlmsg_pid = getpid();req.gen.rtgen_family = AF_INET;memset(&io, 0, sizeof(io));io.iov_base = &req;io.iov_len = req.hdr.nlmsg_len;memset(&msg, 0, sizeof(msg));msg.msg_iov = &io;msg.msg_iovlen = 1;msg.msg_name = &src_addr;msg.msg_namelen = sizeof(src_addr);//send the messageif (sendmsg(s, &msg, 0) < 0){perror("sendmsg");}//parse replywhile (!end){memset(buf, 0, BUFSIZE);msg.msg_iov->iov_base = buf;msg.msg_iov->iov_len = BUFSIZE;if ((len=recvmsg(s, &msg, 0)) < 0){perror("recvmsg");}for (struct nlmsghdr * msg_ptr = (struct nlmsghdr *)buf;NLMSG_OK(msg_ptr, len); msg_ptr = NLMSG_NEXT(msg_ptr, len)){switch (msg_ptr->nlmsg_type){case NLMSG_DONE:end++;break;case RTM_NEWLINK:rtnl_print_link(msg_ptr);break;default:printf("Ignored msg: type=%d, len=%d\n", msg_ptr->nlmsg_type, msg_ptr->nlmsg_len);break;}}}close(s);return 0;
}
[root@localhost netlink]# ./a.out
Interface 1 : lo
Interface 2 : enp1s0
Interface 3 : virbr0
Interface 4 : virbr0-nic

参考资料

https://zhuanlan.zhihu.com/p/269141945
https://www.jianshu.com/p/073bcd9c3b08
https://gist.github.com/cl4u2/5204374
https://en.wikipedia.org/wiki/Netlink

Linux 网络之netlink 简介相关推荐

  1. java IO初识与Linux网络I/O模型简介

    Java的 I/O发展简史 从 JDK1.0到 JDK1.3, Java的 I/O类库都非常原始,很多 UNIX网络编程中的概念或接口在l/O类库中都没有体现,例如 Pipe. Channel. Bu ...

  2. Linux网络服务-Web Service之【HTTP协议简介】(一)

    一.什么是HTTP? 超文本传输协议(英文:HyperText Transfer Protocol,缩写:HTTP)是互联网上应用最为广泛的一种网络协议.设计HTTP最初的目的是为了提供一种发布和接收 ...

  3. Linux网络编程--进程间通信(一)

    进程间通信简介(摘自<Linux网络编程>p85) AT&T 在 UNIX System V 中引入了几种新的进程通讯方式,即消息队列( MessageQueues),信号量( s ...

  4. Java I/O演进与Linux网络I/O模型

    参考文章: 简书-浅谈Linux五种IO:http://www.jianshu.com/p/486b0965c296 一.linux基础概念 1.1 内存空间 linux系统中的使用的是虚拟存储器,即 ...

  5. Linux网络相关、firewalld、netfilter及其5表5链、iptables语法

    2019独角兽企业重金招聘Python工程师标准>>> 1.Linux 网络相关 ifconfig 查看网卡IP,见下图, net-tools 包之前安装过了,这边还可以执行这个命令 ...

  6. Linux网络常用工具分类介绍

    Linux网络命令较多,单纯的介绍网络命令的用法也没什么意思.本文将常见的网络命令进行分类,并做出思维导图,对每个分类的命令选择性的介绍其作用.常见选项和用法举例.BTW,不建议记住所有命令,了解一下 ...

  7. 鸟哥的Linux私房菜(服务器)- 第六章、 Linux 网络侦错

    第六章. Linux 网络侦错 最近更新日期:2011/07/19 虽然我们在第四章谈完了连上 Internet 的方法,也大略介绍了五个主要的网络检查步骤.不过,网络是很复杂的东西, 鸟哥也是接触了 ...

  8. 【Linux】一步一步学Linux网络编程教程汇总(更新中......)

    00. 目录 文章目录 00. 目录 01. 基础理论知识 02. 初级编程 03. 高级编程 04. LibEvent库 05. 06. 07. 01. 基础理论知识 [Linux网络编程]网络协议 ...

  9. Linux Kernel TCP/IP Stack|Linux网络硬核系列

    大家好,我是Alex,今天给大家介绍Linux网络技术中最核心的部分--TCP/IP协议栈 . 我们先看一下抽象的网络协议栈模型 TCP/IP四层(参考)模型 再按分层思想看Linux内核协议栈实现框 ...

最新文章

  1. Save could not be completed. Eclipse国际化的问题解决
  2. 两平面平行方向向量关系_立体几何平行证明的四大必杀绝技------赞!很赞!!非常赞!!!...
  3. C语言scanf()函数格式化输入和printf()格式化输出。
  4. 垃圾收集中的代际差异
  5. 惯用过程模型_惯用的Ruby:编写漂亮的代码
  6. golang基本语法——变量使用详解
  7. 国内几大主流论坛(bbs)系统
  8. 使用 MQL5 绘制阻力和支撑级别
  9. DNW的详细配置及使用过程
  10. Spring Boot Actuator与Spring Boot Admin详解
  11. NTKO OFFICE文档控件使用
  12. (最简单)红米手机5A的USB调试模式在哪里开启的方法
  13. android计算手机的分辨率/像素/密度/屏幕尺寸/DPI值的方法
  14. 【NOIP2017提高组正式赛】列队
  15. 方便实用的--股票涨跌停价计算器
  16. 数据库中@代表什么意思
  17. 5118股市基金行业词库数据【高频词+疑问词+行业根词】
  18. linux 配置阿里云ddns 定时任务定时更新
  19. sqoop数据迁移工具
  20. host 计算机英语作文,英语作文_英文天天写:Host_沪江英语

热门文章

  1. log4cplus总结
  2. 从零手写pm-cli脚手架,统一阿里拍卖源码架构
  3. 程序员年薪20万、30万、40万都是如何生活的?
  4. OLTP系统与DSS系统对比
  5. 1. 机器人动力学—动力学的数学基础
  6. 读书笔记:《量化投资实务》
  7. JMS之——JMS简介
  8. Netapp常用命令
  9. awk——awk基础介绍
  10. day9-为什么会有GIL锁