Netlink 介绍(译)
原文地址:http://people.redhat.com/nhorman/papers/netlink.pdf
译文:
1 介绍
在Linux和Unix的众多发行版中的网络配置功能, 都是编程者事后需求的功能, 导致像添加路由、邻居表条目和配置接口等功能有着很多杂乱的方法, 比如raw socket, ioctl调用以及专门的伪网络协议等方法。在Linux 2.4内核中, 开发者努力实现了一种更标准化的配置网络的方法。这种方法被命名为netlink sockets, 它旨在创建一个适合所有网络控制方面的通信框架,虽然建立的netlink子系统不是完善的, 但是这是一种新的网络配置方法, 也是可靠的基础。此文档旨在介绍如何使用netlink socket族和其实现的协议。
本文假设读者有C和socket编程的基础。
2 Netlink 地址族
2.1 socket创建
netlink地址族使用标准的BSD socket API作为用户空间程序和内核交互的使者。创建一个netlink套接字和创建其它套接字是类似的方式。
socket fd=socket(AF_NETLINK, SOCK_RAW, protocol);
地址族参数总是AF_NETLINK, 并且类型值总是SOCK_RAW, 唯一可变的参数 就是协议protocol域, 此域将会继续添加可选项, 增加了它的可配置性, 下表是protocol的可选项(来自linux-2.6.32 kernel)。
#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 /* Firewalling hook */
#define NETLINK_INET_DIAG 4 /* INET 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_L2TP 20 #if defined(CONFIG_RTL_819X)
#define NETLINK_RTK_DEBUG 21
#define NETLINK_RTK_FILTER 22
#define NETLINK_MULTICAST_DELETE 23
#define NETLINK_RTK_FB 24
#define NETLINK_RTK_HW_QOS 25
#endif
2.2 发送和接收数据包
netlink套接字是无连接的, 收发的数据报就表示类似UDP套接字, 发送数据报通过sendto或者sendmsg系统调用, 用recvfrom或者recvmsg接收数据报。 注意, netlink套接字不使用send和recv交互, 这是因为netlink套接字是无连接的。就像UDP套接字, netlink消息是数据报格式, 虽然netlink消息头部有一些机制设计用于编程者增加协议的可靠性, 但仍旧不能保证连接是可靠的。
2.3 netlink套接字地址结构
struct sockaddr_nl 是它的地址结构, 用于netlink套接字的接收和发送, 定义如下:
struct sockaddr_nl
{
sa_family_t nl_family; /* AF_NETLINK */
unsigned short nl_pad; /* zero */
__u32 nl_pid; /* port ID */
__u32 nl_groups; /* multicast groups mask */
};
- nl_family:此域定义了消息的地址族, 应该总是AF_NETLINK
- nl_pad : 总是为0
- nl_pid : 一般设置为本进程的pid或者填0理, 如果是进程接收来自内核netlink消息, 此域应为本进程PID, 如果是向内核发送netlink消息, 此域应置0
- nl_groups :用于指定多播组, 如对接收来自内核的netlink消息来说, 内核可将要发送的消息指定一个多播组, 那么此消息就会发向同一多播组的接收端。而对于进程发送消息来讲, 设置了多播组就只会发送到此多播组的内核接收端。此域是32位, 最多可支持32个多播组。
3 Netlink 消息格式
与每个IP消息头一样, netlink消息也有类似的头部, 然而和其他协议不同的是, 编程者需要为每个数据包构建这个头部(一般的TCP/UDP socket都是直接操作报文的payload部分), 这个头部用来保存每个消息和格式的元数据, 这个头部也是netlink协议的基础。
struct nlmsghdr {
u32 nlmsg_len;
u16 nlmsg_type;
u16 nlmsg_flags;
u32 nlmsg_seq;
u32 nlmsg_pid;
}
- nlmsg_len:每个netlink头后面跟随者0个或者多个字节的辅助数据, 此域记录了消息的整个长度, 包括了头部在内。
- nlmsg_type:此域标识了头部后面数据的格式。此域的取值和2.1中的protocol有关。
- nlmsg_flags:此域标识了消息由谁进行处理和解析, 有如下取值
NLM_F_REQUEST - 这个标志暗示这是一个请求消息, 它应该被设置到大多数应用程序的初始化消息中。
NLM_F_ACK - 这个标志暗示对前一个请求消息的回应, 序列号和pid值能够辨别请求的回应报文。
NLM_F_ECHO - 这个标志表示发出的报文将回响给发送进程一份。
NLM_F_MULTI - 这个标志表示此消息是多个消息的一部分, 可用宏NLMSG_NEXT获得下一个消息。
NLM_F_ROOT - 用于请求多个netlink消息, 有此标志的请求消息表示请求回复整个条目表而不是一条, 回复的报文通常是
NLM_F_MULTI标志的。注意:此标志只适合特定的nlmsg_type才有效。
NLM_F_ATOMIC - 标志任何通过get-->回复报文的过程都是原子的, 防止在中间有资源改变引起歧义。
NLM_F_REPLACE - 替换条目表中的一条, 可用于覆盖条目表。
NLM_F_CREATE - 在条目表中设置一个新的条目(比如添加一条新路由)
NLM_F_APPEND - 在条目表末尾添加一个条目
NLM_F_EXCL - 结合了CREATE和APPEND, 如果要添加的条目已存在将返回错误(推荐使用) nlmsg_seq:seq用来联系请求和回复报文, 顺序标志的作用
- nlmsg_pid:和seq类似
3.2 netlink 实用宏
#define NLMSG_ALIGNTO 4
/* 返回不小于len的以4字节对齐的最近数字 (ex: len=9 --> 返回值为12)*/
#define NLMSG_ALIGN(len) ( ((len)+NLMSG_ALIGNTO-1) & ~(NLMSG_ALIGNTO-1) )
/* 返回netlink消息头的字节数, 且务必是以4字节对齐的 */
#define NLMSG_HDRLEN ((int) NLMSG_ALIGN(sizeof(struct nlmsghdr)))
/* 返回'len + netlink消息头长度' 一般len指定为消息的payload长度, 此宏可用来填充nlmsg_len域 */
#define NLMSG_LENGTH(len) ((len)+NLMSG_ALIGN(NLMSG_HDRLEN))
/* 返回值同 NLMSG_LENGTH 宏一样有效, 主要是确保整个netlink消息长度4字节对齐(nlmsg_len域是否最好用此域来填充) */
#define NLMSG_SPACE(len) NLMSG_ALIGN(NLMSG_LENGTH(len))
/* 返回指向netlink消息payload处的指针(nlh 参数一般是指向struct nlmsghdr结构的指针) */
#define NLMSG_DATA(nlh) ((void*)(((char*)nlh) + NLMSG_LENGTH(0)))
-----------------------------
| struct nlmsghdr | payload |
-----------------------------
|
NLMSG_DATA(nlh)
/* 返回指向下一个netlink消息的指针 */
/* 很多netlink回应由多个netlink消息组成 */
*参数:
nlh --> 指向struct nlmsghdr结构的指针
len --> 一般为recvmsg函数的返回值(整个消息的长度)
#define NLMSG_NEXT(nlh,len) ((len) -= NLMSG_ALIGN((nlh)->nlmsg_len), \
(struct nlmsghdr*)(((char*)(nlh)) + NLMSG_ALIGN((nlh)->nlmsg_len)))
---------------------------------------------------------
| struct nlmsghdr | payload | struct nlmsghdr | payload |
---------------------------------------------------------
| |
nlh |
NLMSG_NEXT(nlh,len) |--------------------------len--------------------------|
*此宏先用len(整个消息的长度)减掉第一个消息的长度, 如果为0, 表示没有下一个消息了, 返回空, 若为非0, 表示后边还有消息, 指针移动指向下一个消息处并返回.
/* 确保nlh指向的消息大小不大于len */
*参数:
nlh --> 指向struct nlmsghdr结构的指针
len --> 一般为recvmsg函数的返回值
#define NLMSG_OK(nlh,len) ((len) >= (int)sizeof(struct nlmsghdr) && \
(nlh)->nlmsg_len >= sizeof(struct nlmsghdr) && \
(nlh)->nlmsg_len <= (len))
3.3 总结
下图是netlink消息的内存分布图:
4 NETLINK_FIREWALL 协议
由3可知, netlink消息头nlmsg_type成员的取值和2.1中的protocol有关, 现在就介绍protocol为NETLINK_FIREWALL时的情况。
这个协议是非常有用的开发协议。首先它是有意被设计来在用户空间来调试iptables模块的架构,这个协议与很多iptables模块相关联。ip-queue模块就是其中一个(详见:http://blog.csdn.net/u010807313/article/details/9236581),在创建此协议的netlink套接字之前都需要先安装相关模块,发往相关模块的报文都会同样发给由NETLINK_FIREWALL协议创建的netlink套接字, 由此实现在用户空间监听流经netfilter的报文的目的。例如:
iptables -I OUTPUT -j QUEUE -p tcp –destination-port
上面一条命令表示在OUTPUT链上, 发往端口7551的TCP报文都交给QUEUE链来处理, 而ip-queue模块正好和NETLINK_FIREWALL协议的套接字关联(内核实现的), 所以套接字同样也会收到这样的报文, 实现了监听的目的, 自行修改iptables命令可达到监听多种类型报文的目的。
4.1 创建和使用
socket fd=socket(AF_NETLINK, SOCK_RAW, NETLINK_FIREWALL);
此协议没有使用多播组, 所以地址结构struct sockaddr_nl中的nl_groups应该总是设置为0,并且此协议的套接字不需要bind函数,因为报文只是在进程和内核中传输,所以从进程发向内核的报文struct sockaddr_nl中的nl_pid应该设置为0。
4.2 消息类型
NETLINK_FIREWALL协议的套接字有三种消息类型(如3中所述, 成员nlmsg_type的取值),每个消息类型都有它各自的数据结构来描述。
- IPQM_MODE
- IPQM_PACKET < 这个是内核向用户空间返回的报文类型 >
- IPQM_VERDICT
4.2.1 IPQM_MODE
此类型是使用NETLINK_FIREWALL协议需要第一个发向内核的包, 内核收到之后才会将匹配的报文从内核发至用户空间的netlink套接字。此报文的数据结构如下, 它是紧随在struct nlmsghdr之后的:
typedef struct ipq mode msg {
unsigned char value;
size t range;
};
value的取值有三种:
- IPQ_COPY_NONE - 不常用, 设置此值发给内核将导致iptables将所有到QUEUE链的报文丢弃。
- IPQ_COPY_META - 表示我希望内核返回报文的元数据(我理解是struct nlmsghdr + struct ipq_packet_msg 两个头部)部分。
- IPQ_COPY_PACKET - 表示希望内核返回报文, 报文长度由range控制, 若range为0表示返回整个报文。如果你需要在用户空间分析流经QUEUE链的报文应该设置此项并将range设置为0。
range:
此域只在value = IPQ_COPY_PACKET时才有效。
也就是说, 用户进程使用IPQM_MODE类型的报文告诉内核, 我需要你返回给我的报文是什么样的(不需要 or 要元数据 or 要range长的报文)
4.2.2 IPQM_PACKET
这个类型的报文是根据4.2.1之后内核根据需求返回的报文。只要之前设置的value不是IPQ_COPY_NONE, socket就会收到此类型的报文, 结构如下:
typedef struct ipq packet msg {
unsigned long packet_id;
unsigned long mark;
long timestamp sec;
long timestamp usec;
unsigned int hook;
char indev name[IFNAMSIZ];
char outdev name[IFNAMSIZ];
unsigned short hw_protocol;
unsigned short hw_type;
unsigned char hw_addrlen;
unsigned char hw_addr[];
size t data len;
unsigned char payload[];
};
- packet_id - 这个是内核产生的独一无二的标识, 在4.2.3中发送IPQM_VERDICT报文需要。
- mark - //
- timestap_sec - 报文抵达时间(秒)
- timestap_usec - 报文抵达时间(微秒)
- hook - 报文被重定向到QUEUE的hook number
- indev_name - //
- outdev_name - //
- hw_protocol - 通常是ETH_P_IP
- hw_type - 通常是ARPHDR_ETHER
- hw_addrlen - 通常为6
- hw_addr - 报文的源MAC地址
- data_len - payload数据长度
- payload - 柔性数组头部, 指向了payload数据的头部
4.2.3 IPQM_VERDICT
此类型的报文是在收到内核的回复报文之后, 用户经过自己的检测, 决定对此报文执行何种操作, IPQM_MODE --> IPQM_PACKET <----> IPQM_VERDICT, 是顺序的过程。也就是说, 只有你向内核发送IPQM_VERDICT说明了报文处理方式之后, 你才能recvmsg下一个到达QUEUE链的报文, 否则recvmsg会一直阻塞。结构如下:
typedef struct ipq verdict msg {
unsigned int value;
unsigned long id;
size t data_len;
unsigned char payload;
};
value 指示了对报文的处理方式:
- NF_DROP - 立即丢弃报文
- NF_ACCEPT - 接收报文(不参与之后的iptables链了)
- NF_STOLEN - //
- NF_QUEUE - 不常使用
- NF_REPEAT - 将报文移入下一个iptables链
id - 指示了要对哪个报文进行处理, 对应4.2.2的packet_id成员, 这个成员唯一关联了一个进入QUEUE的报文
data_len - 指verdict报文的payload数据长度, 因为verdict是用户发向内核的, 此域一般设置为0
payload - //
5 NETLINK_ROUTE 协议
5.1 创建和使用
socket fd=socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE);
NETLINK_ROUTE协议是netlink套接字最大且是最成熟的协议, 它有它自己消息的处理宏(类似3.2节), 这些NETLINK_ROUTE的宏是为了添加它的辅助数据段和定制化特别的消息类型而做的。
每个族都有同样的命名空间和辅助数据结构, 辅助数据结构后又跟着一个或多个消息。
每个族都包含三个方法, NEW, DEL, GET; 这些方法用于创建、删除和接收路由相关条目。
5.2 NETLINK_ROUTE消息宏
NETLINK_ROUTE消息实际有自己的数据结构, 如下所示。
struct rtattr {
unsigned short rta_len;
unsigned short rta_type;
}
下面是NETLINK_ROUTE的消息内存布局:
对于struct rtattr结构, 与netlink消息头struct nlmsghdr结构相似, 有一些宏进行辅助处理, 参考3.2节。
• int RTA OK(struct rtattr *rta, int rtabuflen); - Verify the data
integrity of the data which succedes this rtattr header.
• void * RTA DATA(struct rtattr *rta); - Return a pointer to the
ancilliary data associated with this rtattr header.
• struct rtattr *RTA NEXT(struct rtattr *rta); - Return a pointer
to the next rtattr header in the chain.
• unsigned int RTA PAYLOAD(struct rtattr *rta); - Return the
length of the ancilliary data associated with the passed rtattr header.
• unsigned int RTA LENGTH(unsigned int length); - Return the
aligned length for the passed payload length. This value is assigned to
the rta len field of the rtattr header
• unsigned int RTA SPACE(unsigned int length); - Return the
length of the ancilliary data, when aligned.
5.3 消息类型
在使用NETLINK_ROUTE协议的情况下, netlink控制块struct nlmsghdr中的nlmsg_type域标识了多种消息类型,举例如下:
5.3.1 LINK消息
LINK消息族允许设置和获取关于系统接口的消息nlmsg_type有如下取值:
- RTM_NEWLINK - 创建一个新接口/有一个新接口被创建
- RTM_DELLINK - 删除一个接口
- RTM_GETLINK - 接收一个接口消息
每个消息的辅助数据结构是struct ifinfomsg:
struct ifinfomsg {
unsigned char ifi_family;
unsigned short ifi_type;
int ifi index;
unsigned int ifi_flags;
unsigned int ifi_change;
};
5.3.2 LINK消息struct rtattr结构rta_type取值
5.3.3 LINK消息内存布局
5.4 其它消息类型
比如ADDR消息, ROUTE消息等和LINK消息类似, 不同的是它们有各自的struct ifinfomsg消息和支持不同的rta_type。
Netlink 介绍(译)的更多相关文章
- CGI version1.1-第一章 介绍 (译)
CGI version1.1-第一章 介绍 1.简介 1.1 用途 CGI 是为 HTTP服务器 与 CGI脚本 在 响应客户端请求分配职责, 客户请求由url,方法与关于传输协议的附属信息, CGI ...
- ISO c++ 14 重点介绍[译]
原文链接 http://marknelson.us/2014/09/11/highlights-of-iso-c14/ 下面是对你的日常开发有重大影响的C++14新变动,列出了一些示例代码,并讨论何时 ...
- swift 学习资源 大集合
今天看到一个swift学习网站,其中我们收集了大量的学习资源 Swift 介绍 Swift 介绍 来自 Apple 官方 Swift 简单介绍 (@peng_gong) 一篇不错的中文简单介绍 [译] ...
- 采用OCR识别自动识别财务报表
一. 财务报表有什么作用 财务报表又叫会计报表,包含:资产负债表.损益表.现金流量表三表.财务报表对企业经营状况有重要的参考意义: n 全面系统地揭示企业一定时期的财务状况.经营成果 ...
- OpenTracing:开放式分布式追踪规范
前言 想实现一个简单的追踪系统似乎是容易的,需要必要的调用链id,时间戳等:想实现一款易用不侵入代码的追踪系统就很麻烦了,需要接触CLR和IL相关知识:即使你费劲心力做出了那些,如果性能不够好,也没有 ...
- 移动端车牌识别/车牌OCR识别
周末,小编约了朋友商场shopping. 开车进地下车库时,“滴”的一声,完成车牌录入:开车离开时,扫描二维码,输入车牌,完成停车收费.小编不禁感叹科技改变生活,人工智能给生活带来的便利. 车牌自动识 ...
- [译] AR SDK的种类比你想得要多!这里介绍七个棒棒哒
作者:Eddie Offermann 原文:There are dozens more Augmented Reality SDKs than you think! Here are seven gr ...
- [译]AngularJS 1.3.0 开发者指南(一) -- 介绍
[译]AngularJS 1.3.0 开发者指南(一) -- 介绍 Angular是什么 ? AngularJS是一款针对动态web应用的结构框架. 它可以让像使用模板语言使用HTML, 并且可以扩展 ...
- [转帖]Kubernetes及容器编排的总体介绍【译】
Kubernetes及容器编排的总体介绍[译] 翻译自The New Stack<Kubernetes 生态环境>作者:JANAKIRAM MSV和 KRISHNAN SUBRAMANIA ...
随机推荐
- 网页调起App之应用实践
声明:本文由入驻搜狐公众平台的作者撰写,除搜狐官方账号外,观点仅代表作者本人,不代表搜狐立场.举报 新春佳节即将到来,北京的上地&西二旗.望京&国贸.五道口&中关村地区等程序员 ...
- 第38次Scrum会议(12/4)【欢迎来怼】
一.小组信息 队名:欢迎来怼 小组成员 队长:田继平 成员:李圆圆,葛美义,王伟东,姜珊,邵朔,阚博文 小组照片 二.开会信息 时间:2017/12/4 17:50~18:20,总计30min. 地点 ...
- JAVA开发环境的熟悉
北京电子科技学院(BESTI) 实 验 报 告 课程:Java程序设计 班级:1352 姓名:马悦 学号:20135235 成绩: 指导教师:娄嘉鹏 实验日期:2015.4.13 实验密级: 预习程度 ...
- POJ 2411 Mondriaan's Dream 插头dp
题目链接: http://poj.org/problem?id=2411 Mondriaan's Dream Time Limit: 3000MSMemory Limit: 65536K 问题描述 S ...
- Do~Hamburger~
在上一次的结对编程中,我的结对队友是 方俊杰 ,大家都称他为“JJ师兄”. 我们两个彼此在合作中发现错误并在合作中一起进步. First(汉堡上层面包): JJ他的JAVA功底比我扎实很多,所 ...
- 在数组中找出两数之和为10的所有组合(JAVA)
/*利用冒泡排序实现*/ import java.util.Scanner;public class Paixun { public static void main(String[] args) { ...
- WCF 和 ASP.NET Web API
地址:https://docs.microsoft.com/zh-cn/dotnet/framework/wcf/wcf-and-aspnet-web-api WCF 是 Microsoft 为生成面 ...
- c# Parallel 并行运算 异步处理
var list = new List<string> { "https://www.baidu.com","https://associates.amazo ...
- 小学四则运算练习(JAVA编写)
源码在Github的仓库主页链接地址:https://github.com/rucr9/rucr 看到这个题目,大概很多人会发出“切,这也太简单了吧!有必要小题大做?”的感叹!是的,仅仅作为一道数学运 ...
- 前端切图相关ps技术
标签(空格分隔): 前端切图 复制图层到一个新的ps文件 对于单个图层 1.选中图层 2.CTRL+A全选 3.CTRL+C 4.CTRL+N新建文件,文件大小默认就可以(背景透明也在这个面板设置), ...