netlink sample

在 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 
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, 0,sizeof(src_addr));
 src__addr.nl_family =AF_NETLINK;     
 src_addr.nl_pid =getpid(); 
 src_addr.nl_groups = 0; 
 bind(sock_fd, (structsockaddr*)&src_addr,
     sizeof(src_addr));
 memset(&dest_addr, 0,sizeof(dest_addr));
 dest_addr.nl_family = AF_NETLINK;
 dest_addr.nl_pid =0;  
 dest_addr.nl_groups = 0;
 nlh=(struct nlmsghdr *)malloc(
                        NLMSG_SPACE(MAX_PAYLOAD));
 
 nlh->nlmsg_len =NLMSG_SPACE(MAX_PAYLOAD);
 nlh->nlmsg_pid =getpid(); 
 nlh->nlmsg_flags = 0;
 
 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);
 
 memset(nlh, 0, NLMSG_SPACE(MAX_PAYLOAD));
 recvmsg(fd, &msg, 0);
 printf(" Received message payload: %s ",
       NLMSG_DATA(nlh));
   
 
 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;
 u32pid;    
 nl_sk = netlink_kernel_create(NETLINK_TEST,
                                  nl_data_ready);
 
 skb = skb_recv_datagram(nl_sk, 0, 0,&err);
 nlh = (struct nlmsghdr*)skb->data;
 printk("%s: received netlink message payload:%s",
       __FUNCTION__, NLMSG_DATA(nlh));
 pid = nlh->nlmsg_pid;
 NETLINK_CB(skb).groups = 0;
 NETLINK_CB(skb).pid =0;     
 NETLINK_CB(skb).dst_pid = pid;
 NETLINK_CB(skb).dst_groups =0; 
 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 
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, 0,sizeof(local_addr));
 src_addr.nl_family =AF_NETLINK;      
 src_addr.nl_pid =getpid(); 
  
 src_addr.nl_groups = 1;
 bind(sock_fd, (structsockaddr*)&src_addr,
     sizeof(src_addr));
 memset(&dest_addr, 0,sizeof(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 ");
 
 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; 
 nlh->nlmsg_flags = 0;
 strcpy(NLMSG_DATA(nlh), "Greeting fromkernel!");
 
 NETLINK_CB(skb).groups = 1;
 NETLINK_CB(skb).pid = 0; 
 NETLINK_CB(skb).dst_pid =0; 
 
 NETLINK_CB(skb).dst_groups = 1;
 
 netlink_broadcast(nl_sk, skb, 0, 1,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!

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

 

#defineMAX_PAYLOAD       1024
structsockaddr_nl   src_addr, dest_addr;
char *KernelMsg = NULL;
struct iovec iov;
int sock_fd;
struct msghdr msg;
int msglen;

#defineDEVICE_ADD           "add"
#defineDEVICE_REMOVE       "remove"
#defineDEVICE_NAME           "event0"
#defineDEVICE_NAMELEN   6
void * DeviceManagement(void *arg)
...{
    sock_fd =socket(PF_NETLINK, SOCK_RAW, NETLINK_KOBJECT_UEVENT);
    intmsgle;
   
   memset(&src_addr, 0, sizeof(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, 0, sizeof(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(structnlmsghdr)) ...{
                               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, 0, 1, GFP_KERNEL);
               }
       }
       complete(&exit_completion);
       return 0;
}

static int netlink_exam_readproc(char *page, char **start, off_toff,
                         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 += -w -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, 0, sizeof(saddr));
       memset(&daddr, 0, sizeof(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(); 
               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, 0, sizeof(saddr));
       memset(&daddr, 0, sizeof(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(); 
               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;
}

文章出处:http://www.diybl.com/course/6_system/linux/Linuxjs/20071226/94120.html

你可能感兴趣的:(netlink sample)