Netlink使用总结

1 概述
Netlink套接字是用以实现用户进程与内核进程通信的一种特殊的进程间通信(IPC) ,也是网络应用程序与内核通信的最常用的接口。
Netlink套接字可以使用标准的套接字APIs来创建。socket(), bind(), sendmsg(), recvmsg() 和 close()很容易地应用到 netlink socket。

Netlink有如下优点:
 使用Netlink通过自定义一种新的协议并加入协议族即可通过socket API使用Netlink协议完成数据交换,而ioctl和proc文件系统均需要通过程序加入相应的设备或文件。
 Netlink使用socket缓存队列,是一种异步通信机制,而ioctl是同步通信机制,如果传输的数据量较大,会影响系统性能。
 Netlink支持多播,属于一个Netlink组的模块和进程都能获得该多播消息。
 Netlink允许内核发起会话,而ioctl和系统调用只能由用户空间进程发起。

在平台 设备管理模块 接口管理模块中,和内核部分的交互都是采用netlink通信方式。

2 Netlink涉及的结构
2.1 Netlink套接字地址
struct sockaddr_nl {
__kernel_sa_family_t nl_family; /* AF_NETLINK */
unsigned short nl_pad; /* zero */
__u32 nl_pid; /* port ID */
__u32 nl_groups; /* multicast groups mask */
};
nl_pid参数,当该socketaddr_nl结构用来bind一个socketfd时(也就是提供服务时),需要根据nl_pid来做唯一性区分,当在用户态时且每个进程只有一个时,采用getpid()比较好,如果一个进程内有多个netlink socket,那么仍然需要继续加以区分。
当该结构用来sendto时,nl_pid指定服务端的地址即可(也就是服务端的nl_pid),而发送到内核,该位赋值为0.

nl_groups参数,用来指定多播组,32bit,每个bit代表一个多播组。
Bind时,表示这个服务加入多播组。
Sendto时,配合nl_pid发往该组

2.2 Netlink通信消息结构
Netlink通信消息结构分为两部分,头和数据。
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 */
};

nlmsg_len 是头和数据一共的长度。
nlmsg_pid 是消息来源的进程pid
nlmsg_seq 是消息的序列号,如果一个消息是由多个数据报组成,也就是说这个消息有多个首部,当然每个首部后面跟着数据部分。那么除了最后数据报,每个部分的首部都要在 nlmsg_flags设置NLM_F_MULTI,最后数据报的首部nlmsg_type设置为NLMSG_DONE。
nlmsg_type 一般由内核设置:
1) NLMSG_NOOP : message is to be ignored;这个消息类型表示数据内容为空,应用可以忽略该报文
2) NLMSG_ERROR:message signals an error and the payload contains an nlmsgerr structure 这个消息类型表示数据部分是一个错误信息,用struct nlmsgerr结构表示,这里不描述。
3) NLMSG_DONE :上面讲nlmsg_seq时已经说明使用场景。
4) NLMSG_OVERRUN : Data lost。数据丢失
关于nlmsg_type这块,在netlink的其它协议,比如NETLINK_ROUTE都有自己的具体实现,我们定义的协议号是22,针对22这个协议我们没有添加关于这块的流程处理。Uware.ko实际上是拿这个域来区分不同模块的netlink请求。
nlmsg_flags 这位我们也没有用到,置为0.

3 Netlink通信流程
Netlink的通信方式和tcp socket通信模型一样,遵循那种C/S交互:

3.1 内核部分的流程
3.1.1 创建服务
static inline struct sock *
netlink_kernel_create(struct net *net, int unit, struct netlink_kernel_cfg *cfg);

参数:
Struct net * net 网络设备命名空间指针。
int unit netlink的协议类型
struct netlink_kernel_cfg *cfg netlink的配置结构体

关于协议号(unit 参数指定), 系统用到的已经定义在了kernel/include/uapi/linux下,不过全是被其它占用的,我们uware在使用netlink时,自定义了一个协议类型,定义在uware_netlink.h下。这个函数做协议类型检查时有个限制,定义自己的协议类型不要超过这个限制:
if (unit < 0 || unit >= MAX_LINKS) // MAX_LINKS = 32
return NULL;

配置结构体包括一些操作netlink的函数指针、多播组、flags、锁。目前使用只将input域赋值,
void (*input)(struct sk_buff *skb); 当netlink有消息到来时的处理函数。

举例:
我们uware_netlink.c中初始化的该结构
struct netlink_kernel_cfg cfg = {
.input = uware_netlink_rcv,
};

3.1.2 接收数据
当进程有数据发送过来时,内核部分会接收数据,上送的包是struct sk_buff *skb,我们可以通过netlink提供的一系列操作函数来获取消息头以及数据。
消息头 = nlmsg_hdr(skb);
消息数据 = NLMSG_DATA(nlh);

3.1.3 发送数据
发送数据时需要先创建一个sk_buff结构:
skb = nlmsg_new(size, GFP_KERNEL);

对新消息头进行赋值
nlhnew = nlmsg_put(skb, 0, 0, UWARE_NTLK_DEV, sizeof(DEV_NETLINK_UWARE_S), 0);

获取新消息中私有数据指针,并对私有数据进行赋值
if_data = nlmsg_data(nlhnew);

调用netlink_unicast发送数据到指定用户进程:
int netlink_unicast(struct sock *ssk, struct sk_buff *skb, u32 portid, int nonblock)

参数:
struct sock ssk 创建服务时返回的struct sock . 也就是一个socket结构。
struct sk_buff *skb 要发送的数据,这里要封成sk_buff发送。
u32 portid 发送的目标nl_pid, 常常指目标进程的pid.
int nonblack 是否阻塞
3.2 进程部分的流程
指定服务的地址
struct sockaddr_nl st_peer_addr;
st_peer_addr.nl_family = AF_NETLINK;
st_peer_addr.nl_pad = 0; /always set to zero/
st_peer_addr.nl_pid = 0; /kernel’s pid is zero/
st_peer_addr.nl_groups = 0; /multicast groups mask, if unicast set to zero/

    为消息头以及私有数据赋值:

memset(&st_snd_buf, 0, sizeof(st_snd_buf));
st_snd_buf.hdr.nlmsg_len = sizeof(st_snd_buf);
st_snd_buf.hdr.nlmsg_flags = 0;
st_snd_buf.hdr.nlmsg_type = UWARE_NTLK_IF;
st_snd_buf.hdr.nlmsg_pid = getpid(); /设置发送者的PID/

st_snd_buf.cmd_type = UWARE_NTLK_GETIFINFO;
st_snd_buf.value1 = iPortId;

        发送数据:

ret = sendto(socketfd, &st_snd_buf, sizeof(st_snd_buf), 0,
(struct sockaddr*)&st_peer_addr, sizeof(st_peer_addr));

        接收数据:
    ret = recvfrom (socketfd, szRcvBuf, sizeof(szRcvBuf), 0 , NULL, NULL);

3.3 netlink操作接口
3.1中讲了部分操作netlink 结构的接口、协议类型定义分别在:
include/net/netlink.h
include/uapi/linux/netlink.h
include/linux/netlink.h

你可能感兴趣的:(Netlink使用总结)