netlink编程

转载 2011年01月17日 15:08:00

 

http://blog.csdn.net/max415/archive/2008/02/01/2076814.aspx原文出处

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 
+=   - - 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 ...
        }


        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 ;
}

 

相关文章推荐

netlink 编程介绍(V0.2)

netlink 编程介绍(V0.2) 作者: Hoyt Luo Linux从2.2开始支持PF_NETLINK域的通讯方式,这个方式主要的用途是在Linux的内核空间和用户空间进行通讯。目前在网络上...

Generic netlink编程入门

通过generic netlink可以实现内核和用户空间的通信,genetlink是通过family来管理的(哈希表),ctrl_family是一个特殊的Family, 它是由Generic Netl...

Linux内核工程导论–网络:TCP:netlink与tcp_diag编程

概览http://m.oschina.net/blog/351007有一个示例程序,但是它用的v1的接口。http://kristrev.github.io/2013/07/26/passive-mo...

linux下wifi编程(基于netlink和nl80211.h)

唯一编程方式就是基于netlink的nl80211.h编程。        netlink是一种linux下的用户空间和内核空间通信的方式,传输的都是一个个的帧。用户空间程序通过生成预定义好的结构...

linux下的netlink编程

在 Linux 2.4 版以后版本的内核中,几乎全部的中断过程与用户态进程的通信都是使用 netlink 套接字实现的,同时还使用 netlink 实现了 ip queue 工具,但 ip queue...

netlink编程注意事项

   最近用了一下netlink,比较方便。特将netlink编程中的注意事项列下:    1、选一个没有用到的协议类型 , 注意要避开netlink.h中已经定义好的类型。注意保证内核层和用户层...

netlink socket编程之why & how

http://blog.chinaunix.net/space.php?uid=24835294&do=blog&id=1629943netlink socket编程之why & how 作者: Ke...
  • maijian
  • maijian
  • 2011年07月08日 09:39
  • 424

netlink socket编程实例解析

开发和维护内核是一件很繁杂的工作,因此,只有那些最重要或者与系统性能息息相关的代码才将其安排在内核中。其它程序,比如GUI,管理以及控制部分的代 码,一般都会作为用户态程序。在linux系统中,把系统...

netlink 编程介绍

Linux从2.2开始支持PF_NETLINK 域的通讯方式,这个方式主要的用途是在Linux的内核空间和用户空间进行通讯。目前在网络上面关于netlink编程的中文资料很少,为了促进对 netlin...
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:netlink编程
举报原因:
原因补充:

(最多只允许输入30个字)