首页 诗词 字典 板报 句子 名言 友答 励志 学校 网站地图
当前位置: 首页 > 教程频道 > 开发语言 > 编程 >

linux上的netlink编程

2013-03-06 
linux下的netlink编程本文来自:http://blog.chinaunix.net/uid-25120309-id-3359137.html在 Linux 2.4 版

linux下的netlink编程
本文来自:http://blog.chinaunix.net/uid-25120309-id-3359137.html
在 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,  0 ,  sizeof (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,  0 ,  sizeof (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,  0 ,  0 ,  & 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,  0 ,  sizeof (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,  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 " );
   /*  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,  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!
以下是一个简单的测试内核事件的应用程序:
#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,  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 ( 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,  0 ,  1 , 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  +=   - 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();   /*  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,  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();   /*  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 ;
}

热点排行