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测试程序的编写

有幸看到文章的童鞋,送上一句话。 linux内核的编程遥遥无期,也不知到何时才是头,至少我们知道未来是光明的,而且也为解决的每一个问题感到兴奋无比。 下面是我查找大牛的相关netlink详...
  • dead_angel
  • dead_angel
  • 2013年04月19日 16:19
  • 894

netlink 编程介绍

Linux从2.2开始支持PF_NETLINK域的通讯方式,这个方式主要的用途是在Linux的内核空间和用户空间进行通讯。目前在网络上面关于netlink编程的中文资料很少,为了促进对netlink编...
  • lee244868149
  • lee244868149
  • 2015年04月08日 16:35
  • 819

linux下用户程序同内核通信实例一(netlink)

linux下用户程序同内核通信的方式一般有ioctl, proc文件系统,剩下一个就是Netlink套接字了。 这里先介绍下netlink。 先抄一段前辈对这几种通信方式的比较: 传送门http:...
  • daydring
  • daydring
  • 2014年04月18日 11:50
  • 4896

Netlink 获取网卡接口信息

#include #include #include #include #include #include #include #include ...
  • idwtwt
  • idwtwt
  • 2016年04月04日 19:21
  • 1010

Linux下使用NetLink 监听网络变化

/* * NetMonitor.c * * Created on: 2015年4月25日 * Author: tao */ #include #include #includ...
  • gt945
  • gt945
  • 2015年04月27日 21:18
  • 3398

netlink 判断USB热插拔事件

做嵌入式开发,尤其在网关、路由器或者其他支持USB设备的终端上,为了提高用户体验,我们常常需要支持自动识别并挂载USB设备功能。某些应用程序,在使用USB设备的过程中,也希望能够侦测到USB断开事件,...
  • newnewman80
  • newnewman80
  • 2013年04月07日 11:05
  • 4216

Netlink机制详解

更多文章请多关注个人网站:http://www.readbk.net,谢谢浏览! 机制原理: Netlink 是一种特殊的 socket,它是 Linux 所特有的,由于传送的消息是暂存在so...
  • xinyuan510214
  • xinyuan510214
  • 2016年09月23日 10:48
  • 1568

Netlink使用总结

1 概述 Netlink套接字是用以实现用户进程与内核进程通信的一种特殊的进程间通信(IPC) ,也是网络应用程序与内核通信的最常用的接口。 Netlink套接字可以使用标准的套接字APIs来创建...
  • qq_27204267
  • qq_27204267
  • 2016年04月05日 09:33
  • 1180

第十四章 netlink机制--基于Linux3.10

Netlink基于网络的消息机制,能够让用户和内核空间进行通信,12.3节提到的ifconfig是使用ioctl方法和内核通信的,而ip命令则是使用netlink和内核通信的。该机制初衷是为网络服务的...
  • shichaog
  • shichaog
  • 2015年03月27日 21:16
  • 1503

Netlink 内核实现分析(二):通信

Netlink 是一种用于内核与用户空间通信的IPC(Inter Process Commumicate)机制,本文主要分析内核空间和用户空间使用netlink进行通信的具体流程。...
  • luckyapple1028
  • luckyapple1028
  • 2016年04月03日 16:15
  • 3773
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:netlink编程
举报原因:
原因补充:

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