netlink(1)_socket及netlink 相关宏

1.基础概念:

内核空间:Linux 系统包含内核空间和用户空间,一般将比较重要的内容放置内核空间运行。

用户空间:把GUI ,管理,控制程序放置用户空间运行。

IPC: 内核空间和用户空间之间需要进行通信,如何通信呢?这就需要IPC. 常用的IPC 有ioctl,系统调用,netlink socket.

2. netlink socket 基本原理:

  • netlink socket 是IPC 中的一种,是一种异步通信机制。发送的消息只暂存在socket 接收缓存中,不需要等待接收者立即处理。
  • 使用netlink socket 实现用户空间和内核空间的通信。用户空间只调用统一的socket API 来使用netlink 通信,内核空间使用统一的内核API 也可使用netlink 通信,从而实现用户空间和内核空间的通信。
  • Netlink协议基于BSD socket和AF_NETLINK地址簇,使用32位的端口号寻址(PID),每个Netlink协议通常与一个或一组内核服务/组件相关联,如常用的NETLINK_ROUTE用于获取和设置路由与链路信息。

3. 用户空间常用socket APIs:

1.socket():

函数作用: 创建一个socket

函数格式:int socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE);

参数介绍

  • 第一个参数表示地址族,AF_NETLINK 表示使用netlink.

  • 第二个参数必须是SOCK_RAW 或SOCK_DGRAM

  • 第三个参数是指netlink 的协议类型。 该协议类型可以是内核空间定义好的,也可以是用户空间定义的。 比如NETLINK_ROUTE 是内核已定义好的,表示路由的协议; NETLINK_GENERIC 是一个通用协议类型,专门为用户使用,用户不必定义添加新的协议类型。(当然用户也可自己添加)。

    • 所有内核定义的协议类型均在 linux/netlink.h (如下所示:)
    #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_RDMA        	20  
    #define NETLINK_CRYPTO			21	/* Crypto layer */
    #define MAX_LINKS 32
    
2. bind(): 函数

函数作用: socket 套接字文件描述符获取成功后,需要对套接字进行地址端口的绑定,之后才能通过该socket 进行数据接收和操作。该函数就是将地址与socketfd绑定。

函数格式: bind(int sockfd, const struct sockaddr *local_addr,socklen_t addr_len);

参数介绍:

  • 第一个参数是前面通过socket() 获取的文件描述符。
  • 第二个参数为要与socket绑定的本地地址,netlink 有专门的结构体地址。具体如下
  • 第三个参数为sockaddr 的长度。

返回值:

  • 返回0 : bind 绑定成功
  • 返回-1: bind 绑定失败
  • errno: 错误值

参考网址:https://blog.csdn.net/xc_tsao/article/details/44132965

相关补充知识

struct sockaddr{
	sa_family_t 	sa_family;   	/* 地址族 AF_XXX */
	char 			sa_data[14];	/* 协议地址 */
};
struct sockaddr_nl{
    sa_family_t nl_family;	/*nelink 地址族,这里通常为AF_NETLINK*/
    unsigned short nl_pad;	/* 通常设置为0 */
    unsigned int nl_pid;	/* 接受消息的进程ID,如果为0 表示接收方为内核或多播组,如果多个进程可以使用pthread_self()<<16|getpid();*/
    unsigned int nl_groups; /* 接收消息的多播组, 是一系列的掩码,一个进程可以存在多个多播组中,消息发送者指定多播组,这样消息将发给多个接收者*/
};
/* 举例 */
int sock= socket(AF_NETLINK,SOCK_RAW,NETLINK_ROUTINE);
struct sockaddr_nl snl;
memset(&snl,0, sizeof(snl));
snl.nl_family = AF_NETLINK;
snl.nl_groups = RTMGRP_LINK | RTM_NEWNEIGH;
if(bind(sock,(struct sockaddr*(&snl)),sizeof(snl))<0)
    printf("socket bind  error\n");
3. sendmsg():

函数作用:

函数格式: ssize_t sendmsg(int sockfd, const struct msghdr *msg, int flags);

参数介绍:

  • sookfd : sock()获取的套接字描述符
  • msghdr*: 信息封装在msghdr 中,具体的数据在iovec 中
  • flags: 一般设置为0

类似函数: ssize_t send(int sockfd, const void *buf, size_t len,int flags)

相关补充知识

1). msghdr 介绍:

/*---------------------iovec-------------------------*/
struct iovec {
    void *iov_base;			/* 基本数据的地址*/
    _kernel_size_t iov_len;	/* 单位是size_t */
    
};
/*----------------msghdr 消息头结构------------------- */
struct msghdr{
    void* 			msg_name;		/* Socket name, 指向sockaddr_nl 结构的指针。在非连接的UDP中,发送者要指定接收方地址,需要该项。在TCP或连接的UDP中(如通过bind 已连接),此值一般设置为NULL*/
    int 			msg_namelen;	/* msg_name 的长度*/
    struct iovec * 	msg_iov;		/* 多io(输入、输出)缓冲区的地址 ,其实是我们要传输的数据块(基址+长度)*/
    _kernel_size_t 	msg_iovlen;		/* 缓冲区的个数 */
    void* 			msg_control;	/* 辅助数据的地址*/
    _kernel_size_t	msg_controllen;	/* 辅助数据的长度*/
    unsigned 		msg_flags;		/* 接受消息的标识*/
}; 

2). nlmsghdr 介绍:

/*-----------nlmsghdr---------------------*/
//struct nlmsghdr 是netlink socket 自己的消息头,被称为netlink的控制块, 应用层向内核发送netlink消息时,必须提供该控制头。
struct nlmsghdr{
    u32 nlmsg_len;		/* msg 的总长度,包括该信息头长度*/
    u16 nlmsg_type;		/* 应用内部定义的消息类型,大部分情况设置为0*/
    u16 nlmsg_flags;	/* 额外的flag,用于设置消息标志*/
    u32 nlmsg_seq;		/* 序列号用于追踪消息*/
    u32 nlmsg_pid;		/* 来源进程ID 用于追踪消息*/
};

  • nlmsg_type: 消息的类型,位于inlucde/linux/rtnetlink.h 中
/*------nlmsg_type 所支持的控制消息------*/
1. NLMSG_NOOP-空消息,什么也不做;
2. NLMSG_ERROR-指明该消息中包含一个错误;
3. NLMSG_DONE-如果内核通过Netlink队列返回了多个消息,那么队列的最后一条消息的类型为NLMSG_DONE,其余所有消息的nlmsg_flags属性都被设置NLM_F_MULTI位有效。
4. NLMSG_OVERRUN-暂时没用到

/*---------nlmsg_type 所用的常用消息------*/
enum {
	RTM_BASE	= 16,
#define RTM_BASE	RTM_BASE

	RTM_NEWLINK	= 16, /*增加特定网络接口的相关信息*/
#define RTM_NEWLINK	RTM_NEWLINK
	RTM_DELLINK,	/*删除特定网络接口的相关信息*/
#define RTM_DELLINK	RTM_DELLINK
	RTM_GETLINK,	/* 获取特定网络接口的相关信息*/
#define RTM_GETLINK	RTM_GETLINK
	RTM_SETLINK,
#define RTM_SETLINK	RTM_SETLINK

	RTM_NEWADDR	= 20,
#define RTM_NEWADDR	RTM_NEWADDR
	RTM_DELADDR,	/*删除一个与网络接口相关的IP地址*/
#define RTM_DELADDR	RTM_DELADDR
	RTM_GETADDR,
#define RTM_GETADDR	RTM_GETADDR

	RTM_NEWROUTE	= 24,
#define RTM_NEWROUTE	RTM_NEWROUTE
	RTM_DELROUTE,
#define RTM_DELROUTE	RTM_DELROUTE
	RTM_GETROUTE,
#define RTM_GETROUTE	RTM_GETROUTE

	RTM_NEWNEIGH	= 28,
#define RTM_NEWNEIGH	RTM_NEWNEIGH
	RTM_DELNEIGH,	/*删除有关邻里表条目,如ARP entry (地址解析协议条目)*/
#define RTM_DELNEIGH	RTM_DELNEIGH
	RTM_GETNEIGH,
#define RTM_GETNEIGH	RTM_GETNEIGH

	RTM_NEWRULE	= 32,
#define RTM_NEWRULE	RTM_NEWRULE
	RTM_DELRULE,
#define RTM_DELRULE	RTM_DELRULE
	RTM_GETRULE,
#define RTM_GETRULE	RTM_GETRULE

	RTM_NEWQDISC	= 36,
#define RTM_NEWQDISC	RTM_NEWQDISC
	RTM_DELQDISC,
#define RTM_DELQDISC	RTM_DELQDISC
	RTM_GETQDISC,
#define RTM_GETQDISC	RTM_GETQDISC

	RTM_NEWTCLASS	= 40,
#define RTM_NEWTCLASS	RTM_NEWTCLASS
	RTM_DELTCLASS,
#define RTM_DELTCLASS	RTM_DELTCLASS
	RTM_GETTCLASS,
#define RTM_GETTCLASS	RTM_GETTCLASS

	RTM_NEWTFILTER	= 44,
#define RTM_NEWTFILTER	RTM_NEWTFILTER
	RTM_DELTFILTER,
#define RTM_DELTFILTER	RTM_DELTFILTER
	RTM_GETTFILTER,
#define RTM_GETTFILTER	RTM_GETTFILTER

	RTM_NEWACTION	= 48,
#define RTM_NEWACTION   RTM_NEWACTION
	RTM_DELACTION,
#define RTM_DELACTION   RTM_DELACTION
	RTM_GETACTION,
#define RTM_GETACTION   RTM_GETACTION

	RTM_NEWPREFIX	= 52,
#define RTM_NEWPREFIX	RTM_NEWPREFIX

	RTM_GETMULTICAST = 58,
#define RTM_GETMULTICAST RTM_GETMULTICAST

	RTM_GETANYCAST	= 62,
#define RTM_GETANYCAST	RTM_GETANYCAST

	RTM_NEWNEIGHTBL	= 64,
#define RTM_NEWNEIGHTBL	RTM_NEWNEIGHTBL
	RTM_GETNEIGHTBL	= 66,
#define RTM_GETNEIGHTBL	RTM_GETNEIGHTBL
	RTM_SETNEIGHTBL,
#define RTM_SETNEIGHTBL	RTM_SETNEIGHTBL

	RTM_NEWNDUSEROPT = 68,
#define RTM_NEWNDUSEROPT RTM_NEWNDUSEROPT

	RTM_NEWADDRLABEL = 72,
#define RTM_NEWADDRLABEL RTM_NEWADDRLABEL
	RTM_DELADDRLABEL,
#define RTM_DELADDRLABEL RTM_DELADDRLABEL
	RTM_GETADDRLABEL,
#define RTM_GETADDRLABEL RTM_GETADDRLABEL

	RTM_GETDCB = 78,
#define RTM_GETDCB RTM_GETDCB
	RTM_SETDCB,
#define RTM_SETDCB RTM_SETDCB

	RTM_NEWNETCONF = 80,
#define RTM_NEWNETCONF RTM_NEWNETCONF
	RTM_DELNETCONF,
#define RTM_DELNETCONF RTM_DELNETCONF
	RTM_GETNETCONF = 82,
#define RTM_GETNETCONF RTM_GETNETCONF

	RTM_NEWMDB = 84,
#define RTM_NEWMDB RTM_NEWMDB
	RTM_DELMDB = 85,
#define RTM_DELMDB RTM_DELMDB
	RTM_GETMDB = 86,
#define RTM_GETMDB RTM_GETMDB

	RTM_NEWNSID = 88,
#define RTM_NEWNSID RTM_NEWNSID
	RTM_DELNSID = 89,
#define RTM_DELNSID RTM_DELNSID
	RTM_GETNSID = 90,
#define RTM_GETNSID RTM_GETNSID

	RTM_NEWSTATS = 92,
#define RTM_NEWSTATS RTM_NEWSTATS
	RTM_GETSTATS = 94,
#define RTM_GETSTATS RTM_GETSTATS

	RTM_NEWCACHEREPORT = 96,
#define RTM_NEWCACHEREPORT RTM_NEWCACHEREPORT

	RTM_NEWCHAIN = 100,
#define RTM_NEWCHAIN RTM_NEWCHAIN
	RTM_DELCHAIN,
#define RTM_DELCHAIN RTM_DELCHAIN
	RTM_GETCHAIN,
#define RTM_GETCHAIN RTM_GETCHAIN

	RTM_NEWNEXTHOP = 104,
#define RTM_NEWNEXTHOP	RTM_NEWNEXTHOP
	RTM_DELNEXTHOP,
#define RTM_DELNEXTHOP	RTM_DELNEXTHOP
	RTM_GETNEXTHOP,
#define RTM_GETNEXTHOP	RTM_GETNEXTHOP

	RTM_NEWLINKPROP = 108,
#define RTM_NEWLINKPROP	RTM_NEWLINKPROP
	RTM_DELLINKPROP,
#define RTM_DELLINKPROP	RTM_DELLINKPROP
	RTM_GETLINKPROP,
#define RTM_GETLINKPROP	RTM_GETLINKPROP

	RTM_NEWVLAN = 112,
#define RTM_NEWNVLAN	RTM_NEWVLAN
	RTM_DELVLAN,
#define RTM_DELVLAN	RTM_DELVLAN
	RTM_GETVLAN,
#define RTM_GETVLAN	RTM_GETVLAN

	RTM_NEWNEXTHOPBUCKET = 116,
#define RTM_NEWNEXTHOPBUCKET	RTM_NEWNEXTHOPBUCKET
	RTM_DELNEXTHOPBUCKET,
#define RTM_DELNEXTHOPBUCKET	RTM_DELNEXTHOPBUCKET
	RTM_GETNEXTHOPBUCKET,
#define RTM_GETNEXTHOPBUCKET	RTM_GETNEXTHOPBUCKET

	__RTM_MAX,
#define RTM_MAX		(((__RTM_MAX + 3) & ~3) - 1)
};
  • nlmsg_flags :msg的标志定义在include/linux/netlink.h
/* Flags values */

#define NLM_F_REQUEST		0x01	/* It is request message. 	消息是一个请求,所有应用首先发起的消息都应该设置该标志*/
#define NLM_F_MULTI		0x02		/* Multipart message, terminated by NLMSG_DONE 表示该消息是一个多部分消息的一部分,后续的消息通过NLMSG_NEXT 获取*/
#define NLM_F_ACK		0x04		/* Reply with ack, with zero or error code 表示该消息是一个请求消息的响应,顺序号和进程ID可以把请求和响应关联起来 */
#define NLM_F_ECHO		0x08		/* Echo this request 表示该消息是一个相关的包的回传	*/
#define NLM_F_DUMP_INTR		0x10	/* Dump was inconsistent due to sequence change */
#define NLM_F_DUMP_FILTERED	0x20	/* Dump was filtered as requested */

/* Modifiers to GET request */
#define NLM_F_ROOT	0x100			/* specify tree	root	请求的数据表应当整体返回用户应用,而不是一个条目一个条目的返回,被许多netlink协议的各种数据获取操作使用*/
#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		*/

/* Modifiers to DELETE request */
#define NLM_F_NONREC	0x100		/* Do not delete recursively	*/

/* Flags for ACK message */
#define NLM_F_CAPPED	0x100		/* request was capped */
#define NLM_F_ACK_TLVS	0x200		/* extended ACK TVLs were included */

3). 举例说明:

#include <sys/types.h>
#include <sys/socket.h>

//sendmsg test
int main()
{
    struct nlmsghdr *n   
  	struct iovec iov = { (void*) n, n->nlmsg_len };
    struct sockaddr_nl snl;
  	struct msghdr msg = {(void*) &snl, sizeof snl, &iov, 1, NULL, 0, 0};
  
  	status = sendmsg(sock, &msg, 0);    
    return 0;
}

4. send()

函数作用:类似sendmsg(), 发送消息到sock 对列

函数格式:size_t send(int sockfd, const void * buf, size_t len,int flag);

5. recvmsg():

函数作用: 接收来自套接字的信息message。

函数格式: recvmsg(int sockfd, struct msghdr * msg, int flags);

参数介绍:

  • sockfd: 套接字文件描述符
  • msghdr: 减少函数参数的数量而设计的结构
  • flag: 设置为0

举例:

	int res;  
	char buf[1024];  
    struct iovec iov = {buf, sizeof(buf)};  
    struct sockaddr_nl snl;  
    struct msghdr msg = { (void*)&snl, sizeof(snl), &iov, 1, NULL, 0, 0};  
    struct nlmsghdr *h;  
    res = recvmsg (sock, &msg, 0);  
6. close()

函数作用: 关闭套接字

函数格式: close(int sockfd)

参数介绍:

  • sockfd: 套接字文件描述符

4. netlink 中的相关宏介绍:

netlink 中定义的相关宏主要是为了计算方便,方便得到数据的位置,方便得到下一个消息。

#define NLMSG_ALIGNTO 4

#define NLMSG_ALLIGN(len) (((len)+NLMSG_ALIGNTO-1)& ~(NLMSG_ALIGNTO-1))
/*上述NLMSG_ALLIGN(len)得到不小于len且字节对齐的最小数值*/

#define NLMSG_HDRLEN         ((int) NLMSG_ALIGN(sizeof(struct nlmsghdr)))
/*得到不小于nlmsghd且字节对齐的值 */

#define NLMSG_LENGTH(len) ((len)+NLMSG_ALIGN(NLMSG_HDRLEN))
/*NLMSG_LENGTH(len)得到数据部分长度为len时实际的消息长度,它一般用于分配消息缓存*/

#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)))
/*得到下一个消息的首地址,并且len的长度为剩余消息的长度。该宏一般在一个消息被分成几个部分发送或接收时使用*/

#define NLMSG_OK(nlh,len) ((len) >= (int)sizeof(struct nlmsghdr) && \

                               (nlh)->nlmsg_len >= sizeof(struct nlmsghdr) && \

                               (nlh)->nlmsg_len <= (len))

/*宏NLMSG_OK(nlh,len)用于判断消息是否有len这么长*/


#define NLMSG_PAYLOAD(nlh,len) ((nlh)->nlmsg_len - NLMSG_SPACE((len)))
/*宏NLMSG_PAYLOAD(nlh,len)用于返回payload的长度*/

温馨提示: 主要用于学习和笔记记载,如发现错误欢迎提出。

  • 5
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值