linux下的netlink编程

在 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),)debugobjs:=netlinkexamkern.oobjm:=netlinkexamkern1.oCFLAGS+=wWimplicitfunctiondeclarationelsePWD:= (shell pwd)
KVER ?= (shellunamer)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 ;

}

from:http://blog.csdn.net/dipperkun/archive/2007/12/19/1953526.aspx

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值