Netlink相对于其他的通信机制具有以下优点:
- 使用Netlink通过自定义一种新的协议并加入协议族即可通过socket API使用Netlink协议完成数据交换,而ioctl和proc文件系统均需要通过程序加入相应的设备或文件。
- Netlink使用socket缓存队列,是一种异步通信机制,而ioctl是同步通信机制,如果传输的数据量较大,会影响系统性能。
- Netlink支持多播,属于一个Netlink组的模块和进程都能获得该多播消息。
- Netlink允许内核发起会话,而ioctl和系统调用只能由用户空间进程发起。
内核态代码:
#include <linux/module.h>
#include <linux/crypto.h>
#include <linux/sched.h>
#include <net/netlink.h>
#include <linux/security.h>
#include <net/net_namespace.h>
#include <linux/mutex.h>
#include <linux/kernel.h>
#include <linux/if.h>
#include <linux/timer.h>
#include <linux/netdevice.h>
#include <linux/inetdevice.h>
#include <linux/if_arp.h>
#include <linux/list.h>
#include <linux/string.h>
#include <linux/fs.h>
#include <asm/uaccess.h>
#include "netlink_drv.h"
static struct sock *nlsk;
static int send_id_to_user(struct sock *ntsk, int pid, unsigned char id, int ret)
{
struct sk_buff* pskb;
struct nlmsghdr* nlhnew;
struct nldata_chipid* pstdata;
int err;
pskb = nlmsg_new(sizeof(struct nldata_chipid), GFP_KERNEL);
if (pskb == NULL) {
printk( KERN_WARNING "send_id_to_user \r\n");
return -ENOMEM;
}
nlhnew = nlmsg_put(pskb, 0, 0, NTLK_CHIPID, sizeof(pstdata), 0);
if (nlhnew == NULL) {
printk( KERN_WARNING "send_id_to_user \r\n");
return -1;
}
pstdata = nlmsg_data(nlhnew);
memset(pstdata, 0x0, sizeof(pstdata));
pstdata->chipid = id;
pstdata->ret = ret;
nlmsg_end(pskb, nlhnew);
err = netlink_unicast(ntsk, pskb, pid, MSG_DONTWAIT);
if (err < 0) {
printk( KERN_WARNING "send_id_to_user error:%d \r\n", err);
return err;
}
return 0;
}
static void netlink_msg_rcv(struct sk_buff *skb)
{
struct nlmsghdr *nlh;
int type, i, errnum = 0;
nlh = nlmsg_hdr(skb);
if (nlh->nlmsg_len < NLMSG_HDRLEN || skb->len < nlh->nlmsg_len) {
printk( KERN_WARNING "netlink_msg_rcv error len \r\n");
return;
}
type = nlh->nlmsg_type;
if (type > NTLK_MAX) {
printk( KERN_WARNING "netlink_msg_rcv rcv error type:%d\r\n", type);
return;
}
switch(type) {
/*type自定义,根据用户态传下来的type做相应处理,然后条用api将信息传回用户态 例如传id*/
char id;
send_id_to_user(nlsk, pid, id, ret);
}
if (errnum != 0 ) {
printk( KERN_WARNING "netlink_msg_rcv return :%d \r\n", errnum);
return;
}
return;
}
static int __init netlink_init(void)
{
printk("netlink_kernel init\n");
struct netlink_kernel_cfg cfg = {
.input = netlink_msg_rcv,
};
nlsk = netlink_kernel_create(&init_net, 23, &cfg);
if (!nlsk) {
printk( "netlink_kernel_create return null\r\n");
return -ENOMEM;
}
return 0;
}
static void __exit netlink_exit(void)
{
netlink_kernel_release(nlsk);
}
module_init(netlink_init);
module_exit(netlink_exit);
MODULE_LICENSE("GPL");
用户态代码:
#include <sys/socket.h>
#include <sys/time.h>
#include <linux/netlink.h>
#include <errno.h>
#include <string.h>
#include <stdio.h>
struct id_userdata{
struct nlmsghdr hdr;
unsigned char id;
unsigned int ret;
};
int user_netlink_init(void)
{
int ifd;
int iOpt = 1;
struct sockaddr_nl addr;
ifd = socket(PF_NETLINK, SOCK_DGRAM, 23);
if(ifd < 0) {
printf("%s\n", strerror(errno));
return -1;
}
setsockopt(ifd, SOL_SOCKET, SO_REUSEADDR, &iOpt, sizeof(iOpt));
addr.nl_family = PF_NETLINK;
addr.nl_pad = 0;
addr.nl_pid = getpid();
addr.nl_groups = 0;
if(bind(ifd, (struct sockaddr*)&addr, sizeof(addr)) < 0){
printf("bind < 0\n");
close(ifd);
return -2;
}
//set_nonblocking(ifd);
return ifd;
}
int user_netlink_recv_data(int socketfd, void *data, int send_len, int recv_len)
{
int ret;
int recvcount = 0;
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*/
/*recv message */
ret = sendto(socketfd, data, send_len, 0, (struct sockaddr*)&st_peer_addr, sizeof(st_peer_addr));
if(ret < 0) {
return -1;
}
struct timeval tv_out;
tv_out.tv_sec = 3;
tv_out.tv_usec = 0;
setsockopt(socketfd,SOL_SOCKET,SO_RCVTIMEO,&tv_out, sizeof(tv_out));
ret = recvfrom(socketfd, data, recv_len, 0 , NULL, NULL);
return ret;
}
int id_get(unsigned char* chipid)
{
int ret;
int iskfd = 0;
struct id_userdata stprdata;
iskfd = user_netlink_init();
if (iskfd <= 0) {
return iskfd;
}
memset(&stprdata, 0x00, sizeof(stprdata));
stprdata.hdr.nlmsg_len = sizeof(stprdata); //NLMSG_LENGTH(sizeof(netlink_notify_s))
stprdata.hdr.nlmsg_flags = 0;
stprdata.hdr.nlmsg_type = NTLK_ID; /*自定义的type*/
stprdata.hdr.nlmsg_pid = getpid(); /* set sender PID*/
ret = user_netlink_recv_data(iskfd, &stprdata, sizeof(stprdata), sizeof(stprdata));
if (ret > 0) {
if (stprdata.ret != 0) {
close(iskfd);
return -3;
}
else{
*chipid = stprdata.id;
}
}
else{
close(iskfd);
return -4;
}
close(iskfd);
return 0;
}
注:23为自定义协议类型,netlink.h头文件中定义了MAX_LINKS 32,最大不能超过32内核态与用户态代码协议类型须一致,并且内核态代码必须保证先加载成功,否则自定义的协议类型在用户态创建socket会返回失败;