linux下的netlink编程

来源:互联网 发布:淘宝网银支付到账 编辑:程序博客网 时间:2024/05/19 03:28

在 linux学习 2.4 版以后版本的内核中,几乎全部的中断过程与用户态进程的通信都是使用 netlink 套接字实现的,同时还使用 netlink 实现了 ip queue 工具,但 ip queue 的使用有其局限性,不能自由地用于各种中断过程。内核的帮助文档和其他一些 linux学习 相关文章都没有对 netlink 套接字在中断过程和用户空间通信的应用上作详细的说明,使得很多用户对此只有一个模糊的概念。

Unicast Communication between Kernel and Application
在下面的例子中,一个用户空间进程发送一个netlink消息给内核模块,内核模块应答一个消息给发送进程,这里是用户空间的代码:

 

#include <sys/socket.h>
#include 
<linux学习/netlink.h>
#define MAX_PAYLOAD 1024  /* maximum payload size*/
struct sockaddr_nl src_addr, dest_addr;
struct msghdr msg;
struct nlmsghdr *nlh = NULL;
struct iovec iov;
int sock_fd;
void main() {
 sock_fd 
= socket(PF_NETLINK, SOCK_RAW,NETLINK_TEST);
 memset(
&src_addr, 0sizeof(src_addr));
 src__addr.nl_family 
= AF_NETLINK;      
 src_addr.nl_pid 
= getpid();  /* self pid */
 src_addr.nl_groups 
= 0;  /* not in mcast groups */
 bind(sock_fd, (
struct sockaddr*)&src_addr, 
      
sizeof(src_addr));
 memset(
&dest_addr, 0sizeof(dest_addr));
 dest_addr.nl_family 
= AF_NETLINK;
 dest_addr.nl_pid 
= 0;   /* For Linux Kernel */
 dest_addr.nl_groups 
= 0/* unicast */
 nlh
=(struct nlmsghdr *)malloc(
                         NLMSG_SPACE(MAX_PAYLOAD));
 
/* Fill the netlink message header */
 nlh
->nlmsg_len = NLMSG_SPACE(MAX_PAYLOAD);
 nlh
->nlmsg_pid = getpid();  /* self pid */
 nlh
->nlmsg_flags = 0;
 
/* Fill in the netlink message payload */
 strcpy(NLMSG_DATA(nlh), 
"Hello you!");
 iov.iov_base 
= (void *)nlh;
 iov.iov_len 
= nlh->nlmsg_len;
 msg.msg_name 
= (void *)&dest_addr;
 msg.msg_namelen 
= sizeof(dest_addr);
 msg.msg_iov 
= &iov;
 msg.msg_iovlen 
= 1;
 sendmsg(fd, 
&msg, 0);
 
/* Read message from kernel */
 memset(nlh, 
0, NLMSG_SPACE(MAX_PAYLOAD));
 recvmsg(fd, 
&msg, 0);
 printf(
" Received message payload: %s "
        NLMSG_DATA(nlh));
    
 
/* Close Netlink Socket */
 close(sock_fd);
}
    

这里是内核代码:

 

struct sock *nl_sk = NULL;
void nl_data_ready (struct sock *sk, int len)
{
  wake_up_interruptible(sk
->sleep);
}

void netlink_test() {
 
struct sk_buff *skb = NULL;
 
struct nlmsghdr *nlh = NULL;
 
int err;
 u32 pid;     
 nl_sk 
= netlink_kernel_create(NETLINK_TEST, 
                                   nl_data_ready);
 
/* wait for message coming down from user-space */
 skb 
= skb_recv_datagram(nl_sk, 00&err);
 nlh 
= (struct nlmsghdr *)skb->data;
 printk(
"%s: received netlink message payload:%s "
        __FUNCTION__, NLMSG_DATA(nlh));
 pid 
= nlh->nlmsg_pid; /*pid of sending process */
 NETLINK_CB(skb).groups 
= 0/* not in mcast group */
 NETLINK_CB(skb).pid 
= 0;      /* from kernel */
 NETLINK_CB(skb).dst_pid 
= pid;
 NETLINK_CB(skb).dst_groups 
= 0;  /* unicast */
 netlink_unicast(nl_sk, skb, pid, MSG_DONTWAIT);
 sock_release(nl_sk
->socket);
}
 

在内核模块被加载到内核,当我们运行用户程序,我们将看到下面的信息:

Received message payload: Hello you!
 
 

然后用dmesg我们可以看到内核输出:

netlink_test: received netlink message payload:
Hello you!

Multicast Communication between Kernel and Applications
这个例子中,两个应用程序在监听同一个netlink广播组.内核模块发送一个netlink消息给这个广播组,所用的应用程序都收到它,如下是用户程序代码:

#include <sys/socket.h>
#include 
<linux学习/netlink.h>
#define MAX_PAYLOAD 1024  /* maximum payload size*/
struct sockaddr_nl src_addr, dest_addr;
struct nlmsghdr *nlh = NULL;
struct iovec iov;
int sock_fd;
void main() {
 sock_fd
=socket(PF_NETLINK, SOCK_RAW, NETLINK_TEST);
 memset(
&src_addr, 0sizeof(local_addr));
 src_addr.nl_family 
= AF_NETLINK;       
 src_addr.nl_pid 
= getpid();  /* self pid */
 
/* interested in group 1<<0 */  
 src_addr.nl_groups 
= 1;
 bind(sock_fd, (
struct sockaddr*)&src_addr, 
      
sizeof(src_addr));
 memset(
&dest_addr, 0sizeof(dest_addr)); 
 nlh 
= (struct nlmsghdr *)malloc(
                          NLMSG_SPACE(MAX_PAYLOAD));
 memset(nlh, 
0, NLMSG_SPACE(MAX_PAYLOAD));      
    
 iov.iov_base 
= (void *)nlh;
 iov.iov_len 
= NLMSG_SPACE(MAX_PAYLOAD);
 msg.msg_name 
= (void *)&dest_addr;
 msg.msg_namelen 
= sizeof(dest_addr);
 msg.msg_iov 
= &iov;
 msg.msg_iovlen 
= 1;
 printf(
"Waiting for message from kernel ");
 
/* Read message from kernel */
 recvmsg(fd, 
&msg, 0);
 printf(
" Received message payload: %s "
        NLMSG_DATA(nlh));
 close(sock_fd);
}
  

 内核代码:

 

#define MAX_PAYLOAD 1024 
struct sock *nl_sk = NULL;
void netlink_test() {
 sturct sk_buff 
*skb = NULL;
 
struct nlmsghdr *nlh;
 
int err;
 nl_sk 
= netlink_kernel_create(NETLINK_TEST, 
                               nl_data_ready);
 skb
=alloc_skb(NLMSG_SPACE(MAX_PAYLOAD),GFP_KERNEL);
 nlh 
= (struct nlmsghdr *)skb->data;
 nlh
->nlmsg_len = NLMSG_SPACE(MAX_PAYLOAD);
 nlh
->nlmsg_pid = 0;  /* from kernel */
 nlh
->nlmsg_flags = 0;
 strcpy(NLMSG_DATA(nlh), 
"Greeting from kernel!");
 
/* sender is in group 1<<0 */
 NETLINK_CB(skb).groups 
= 1;
 NETLINK_CB(skb).pid 
= 0;  /* from kernel */
 NETLINK_CB(skb).dst_pid 
= 0;  /* multicast */
 
/* to mcast group 1<<0 */
 NETLINK_CB(skb).dst_groups 
= 1;
 
/*multicast the message to all listening processes*/
 netlink_broadcast(nl_sk, skb, 
01, GFP_KERNEL);
 sock_release(nl_sk
->socket);
}
  

我们运行用户程序:

 
./nl_recv &
Waiting for message from kernel
./nl_recv &
Waiting for message from kernel
 
然后我们加载内核模块到内核空间,会看到如下信息::

Received message payload: Greeting from kernel!
Received message payload: Greeting from kernel!

以下是一个简单的测试内核事件的应用程序:

#define MAX_PAYLOAD        1024
struct sockaddr_nl    src_addr, dest_addr;
char *KernelMsg = NULL;
struct iovec iov;
int sock_fd;
struct msghdr msg;
int msglen;

#define DEVICE_ADD            "add"
#define DEVICE_REMOVE        "remove"
#define DEVICE_NAME            "event0"
#define DEVICE_NAMELEN    6
void * DeviceManagement(void *arg)
{
    sock_fd 
= socket(PF_NETLINK, SOCK_RAW, NETLINK_KOBJECT_UEVENT);
    
int msgle;
    
    memset(
&src_addr, 0sizeof(src_addr));
    src_addr.nl_family 
= AF_NETLINK;
    src_addr.nl_pid 
= pthread_self() << 16 | getpid();
    src_addr.nl_groups 
= 1;
    bind(sock_fd, (
struct sockaddr *)&src_addr, sizeof(src_addr));
    
    memset(
&dest_addr, 0sizeof(dest_addr));
    KernelMsg 
= (struct nlmsghdr *)malloc(MAX_PAYLOAD);
    memset(KernelMsg, 
0,     MAX_PAYLOAD);
    
    iov.iov_base 
= (void *)KernelMsg;
    iov.iov_len 
= MAX_PAYLOAD;
    msg.msg_name 
= (void *)&dest_addr;
    msg.msg_namelen 
= sizeof(dest_addr);
    msg.msg_iov 
= &iov;
    msg.msg_iovlen 
= 1;
    
    
while(1{
        
//printf("Waiting for message from kernel ");
        recvmsg(sock_fd, &msg, 0);
        
//printf("Receved message payload: %s ", KernelMsg);
        msglen = strlen(KernelMsg);
        
//printf("Device: %s ", KernelMsg+msglen-DEVICE_NAMELEN);
        if(!strncmp(DEVICE_NAME, KernelMsg+msglen-DEVICE_NAMELEN, DEVICE_NAMELEN)) {
            
if(!strncmp(DEVICE_ADD, KernelMsg, strlen(DEVICE_ADD))) 
            
{
                printf(
"Add event0 device ");
                USBKeyboardReady 
= 1;
            }

            
else if(!strncmp(DEVICE_REMOVE, KernelMsg, strlen(DEVICE_REMOVE))) {
                printf(
"Remove event0 device ");
                USBKeyboardReady 
= 0;
            }

        }

    }

    close(sock_fd);
    
}
 

示例:一个用户进程发送数据到内核,然后通过内核发送给另一个用户进程。

内核进程:netlink-exam-kern.c

//kernel module: netlink-exam-kern.c
#ifndef __KERNEL__
#define __KERNEL__
#endif

#ifndef MODULE
#define MODULE
#endif

#include 
<linux学习/config.h>
#include 
<linux学习/module.h>
#include 
<linux学习/netlink.h>
#include 
<linux学习/sched.h>
#include 
<net/sock.h>
#include 
<linux学习/proc_fs.h>

#define BUF_SIZE 16384
#define NL 30

static struct sock *netlink_exam_sock;
static unsigned char buffer[BUF_SIZE];
static unsigned int buffer_tail = 0;
static int exit_flag = 0;

static DECLARE_COMPLETION(exit_completion);

static void recv_handler(struct sock * sk, int length)
{
        wake_up(sk
->sk_sleep);
}


static int process_message_thread(void * data)
{
        
struct sk_buff * skb = NULL;
        
struct nlmsghdr * nlhdr = NULL;
        
int len;
        DEFINE_WAIT(wait);

        daemonize(
"mynetlink");

        
while (exit_flag == 0{
                prepare_to_wait(netlink_exam_sock
->sk_sleep, &wait, TASK_INTERRUPTIBLE);
                schedule();
                finish_wait(netlink_exam_sock
->sk_sleep, &wait);

                
while ((skb = skb_dequeue(&netlink_exam_sock->sk_receive_queue))
                         
!= NULL) {
                        nlhdr 
= (struct nlmsghdr *)skb->data;
                        
if (nlhdr->nlmsg_len < sizeof(struct nlmsghdr)) {
                                printk(
"Corrupt netlink message. ");
                                
continue;
                        }

                        len 
= nlhdr->nlmsg_len - NLMSG_LENGTH(0);
                        
if (len + buffer_tail > BUF_SIZE) {
                                printk(
"netlink buffer is full. ");
                        }

                        
else {
                                memcpy(buffer 
+ buffer_tail, NLMSG_DATA(nlhdr), len);
                                buffer_tail 
+= len;
                        }

                        nlhdr
->nlmsg_pid = 0;
                        nlhdr
->nlmsg_flags = 0;
                        NETLINK_CB(skb).pid 
= 0;
                        NETLINK_CB(skb).dst_pid 
= 0;
                        NETLINK_CB(skb).dst_group 
= 1;
                        netlink_broadcast(netlink_exam_sock, skb, 
01, GFP_KERNEL);
                }

        }

        complete(
&exit_completion);
        
return 0;
}


static int netlink_exam_readproc(char *page, char **start, off_t off,
                          
int count, int *eof, void *data)
{
        
int len;

        
if (off >= buffer_tail) {
                
* eof = 1;
                
return 0;
        }

        
else {
                len 
= count;
                
if (count > PAGE_SIZE) {
                        len 
= PAGE_SIZE;
                }

                
if (len > buffer_tail - off) {
                        len 
= buffer_tail - off;
                }

                memcpy(page, buffer 
+ off, len);
                
*start = page;
                
return len;
        }


}


static int __init netlink_exam_init(void)
{
        netlink_exam_sock 
= netlink_kernel_create(NL, 0, recv_handler, THIS_MODULE);
        
if (!netlink_exam_sock) {
                printk(
"Fail to create netlink socket. ");
                
return 1;
        }

        kernel_thread(process_message_thread, NULL, CLONE_KERNEL);
        create_proc_read_entry(
"netlink_exam_buffer"0444, NULL, netlink_exam_readproc, 0);
        
return 0;
}


static void __exit netlink_exam_exit(void)
{
        exit_flag 
= 1;
        wake_up(netlink_exam_sock
->sk_sleep);
        wait_for_completion(
&exit_completion);
        sock_release(netlink_exam_sock
->sk_socket);
}


module_init(netlink_exam_init);
module_exit(netlink_exam_exit);
MODULE_LICENSE(
"GPL");

编译成模块:

ifneq ($(KERNELRELEASE),)
debug
-objs := netlink-exam-kern.o
obj
-m := netlink-exam-kern1.o
CFLAGS 
+= --Wimplicit-function-declaration
else
PWD :
= $(shell pwd)
KVER 
?= $(shell uname -r)
KDIR :
= /lib/modules/$(KVER)/build
all:
        $(MAKE) 
-C $(KDIR) M=$(PWD)
clean:
        rm 
-rf .*.cmd *.o *.mod.c *.ko .tmp_versions
endif

用户发送进程:netlink-exam-user-send.c

 

//application sender: netlink-exam-user-send.c
#include <stdio.h>
#include 
<stdlib.h>
#include 
<unistd.h>
#include 
<string.h>
#include 
<sys/types.h>
#include 
<sys/socket.h>
#include 
<linux学习/netlink.h>

#define MAX_MSGSIZE 1024


int main(int argc, char * argv[])
{
        FILE 
* fp;
        
struct sockaddr_nl saddr, daddr;
        
struct nlmsghdr *nlhdr = NULL;
        
struct msghdr msg;
        
struct iovec iov;
        
int sd;
        
char text_line[MAX_MSGSIZE];
        
int ret = -1;

        
if (argc < 2{
                printf(
"Usage: %s atextfilename ", argv[0]);
                exit(
1);
        }


        
if ((fp = fopen(argv[1], "r")) == NULL) {
                printf(
"File %s dosen't exist. ");
                exit(
1);
        }


        sd 
= socket(AF_NETLINK, SOCK_RAW, 30);
        memset(
&saddr, 0sizeof(saddr));
        memset(
&daddr, 0sizeof(daddr));

        saddr.nl_family 
= AF_NETLINK;
        saddr.nl_pid 
= getpid();
        saddr.nl_groups 
= 0;
        bind(sd, (
struct sockaddr*)&saddr, sizeof(saddr));

        daddr.nl_family 
= AF_NETLINK;
        daddr.nl_pid 
= 0;
        daddr.nl_groups 
= 0;

        nlhdr 
= (struct nlmsghdr *)malloc(NLMSG_SPACE(MAX_MSGSIZE));

        
while (fgets(text_line, MAX_MSGSIZE, fp)) {
                memcpy(NLMSG_DATA(nlhdr), text_line, strlen(text_line));
                memset(
&msg, 0 ,sizeof(struct msghdr));

                nlhdr
->nlmsg_len = NLMSG_LENGTH(strlen(text_line));
                nlhdr
->nlmsg_pid = getpid();  /* self pid */
                nlhdr
->nlmsg_flags = 0;

                iov.iov_base 
= (void *)nlhdr;
                iov.iov_len 
= nlhdr->nlmsg_len;
                msg.msg_name 
= (void *)&daddr;
                msg.msg_namelen 
= sizeof(daddr);
                msg.msg_iov 
= &iov;
                msg.msg_iovlen 
= 1;
                ret 
= sendmsg(sd, &msg, 0);
                
if (ret == -1{
                        perror(
"sendmsg error:");
                }

        }


        close(sd);
        
return 0;
}

用户接收进程:netlink-exam-user-recv.c

 

//application sender: netlink-exam-user-send.c
#include <stdio.h>
#include 
<stdlib.h>
#include 
<unistd.h>
#include 
<string.h>
#include 
<sys/types.h>
#include 
<sys/socket.h>
#include 
<linux学习/netlink.h>

#define MAX_MSGSIZE 1024


int main(int argc, char * argv[])
{
        FILE 
* fp;
        
struct sockaddr_nl saddr, daddr;
        
struct nlmsghdr *nlhdr = NULL;
        
struct msghdr msg;
        
struct iovec iov;
        
int sd;
        
char text_line[MAX_MSGSIZE];
        
int ret = -1;

        
if (argc < 2{
                printf(
"Usage: %s atextfilename ", argv[0]);
                exit(
1);
        }


        
if ((fp = fopen(argv[1], "r")) == NULL) {
                printf(
"File %s dosen't exist. ");
                exit(
1);
        }


        sd 
= socket(AF_NETLINK, SOCK_RAW, 30);
        memset(
&saddr, 0sizeof(saddr));
        memset(
&daddr, 0sizeof(daddr));

        saddr.nl_family 
= AF_NETLINK;
        saddr.nl_pid 
= getpid();
        saddr.nl_groups 
= 0;
        bind(sd, (
struct sockaddr*)&saddr, sizeof(saddr));

        daddr.nl_family 
= AF_NETLINK;
        daddr.nl_pid 
= 0;
        daddr.nl_groups 
= 0;

        nlhdr 
= (struct nlmsghdr *)malloc(NLMSG_SPACE(MAX_MSGSIZE));

        
while (fgets(text_line, MAX_MSGSIZE, fp)) {
                memcpy(NLMSG_DATA(nlhdr), text_line, strlen(text_line));
                memset(
&msg, 0 ,sizeof(struct msghdr));

                nlhdr
->nlmsg_len = NLMSG_LENGTH(strlen(text_line));
                nlhdr
->nlmsg_pid = getpid();  /* self pid */
                nlhdr
->nlmsg_flags = 0;

                iov.iov_base 
= (void *)nlhdr;
                iov.iov_len 
= nlhdr->nlmsg_len;
                msg.msg_name 
= (void *)&daddr;
                msg.msg_namelen 
= sizeof(daddr);
                msg.msg_iov 
= &iov;
                msg.msg_iovlen 
= 1;
                ret 
= sendmsg(sd, &msg, 0);
                
if (ret == -1{
                        perror(
"sendmsg error:");
                }

        }


        close(sd);
        
return 0;
}

 

 

 
原创粉丝点击