Chinaunix首页 | 论坛 | 博客
  • 博客访问: 1011349
  • 博文数量: 584
  • 博客积分: 2293
  • 博客等级: 大尉
  • 技术积分: 3045
  • 用 户 组: 普通用户
  • 注册时间: 2006-03-28 11:15
文章分类

全部博文(584)

文章存档

2012年(532)

2011年(47)

2009年(5)

我的朋友

分类:

2012-08-13 13:33:42


Linux内核态与用户态通信的方法有多种比如:ioctl,netlink,proc filesystem等,本文给大家讲了一种比较简单的内核和用户通行的方法,就是利用netfilter提供的注册sockopt的方法。
linux ipvs模块就是用这个方法来通信的。

1. Linux下的sockopt

Linux提供了多种通信方式来实现内核和用户之间的数据通信,基于 socket的sockopt是最常用也比较简单易用的一种方式。它的本质和ioctl()很相似,只是ioctl()需要创建新的设备文件,而 sockopt只需要创建一个socket套接字便可以使用户与内核进行通信。

这里分别从内核和用户两方面来介绍sockopt的使用。

2. 内核中使用sockopt

在内核中,Netfilter提供了struct nf_sockopt_ops来将sockopt的操作定义为一个节点来加入链表中。同时提供了注册/解注册函数来使用sockopt。

数据结构和函数

sockopt操作结构体

struct nf_sockopt_ops

{

struct list_head list;

int pf;

/* Non-inclusive ranges: use 0/0/NULL to never get called. */

int set_optmin;

int set_optmax;

int (*set)(struct sock *sk, int optval, void __user *user, unsigned int len);

int (*compat_set)(struct sock *sk, int optval, void __user *user, unsigned int len);

int get_optmin;

int get_optmax;

int (*get)(struct sock *sk, int optval, void __user *user, int *len);

int (*compat_get)(struct sock *sk, int optval, void __user *user, int *len);

/* Number of users inside set() or get(). */

unsigned int use;

struct task_struct *cleanup_task;

};

注册和解注册函数

int nf_register_sockopt(struct nf_sockopt_ops *reg)

void nf_unregister_sockopt(struct nf_sockopt_ops *reg)

用户数据读写函数

int copy_from_user(void *to, const void __user *from, int n)

int copy_to_user(void __user *to, const void *from, int n)

 

内核模块代码:

sockopt_srv.c

#include
#include
#include
#include
#include
#include
#include

#define SOCKET_OPS_BASE          128
#define SOCKET_OPS_SET       (SOCKET_OPS_BASE)
#define SOCKET_OPS_GET      (SOCKET_OPS_BASE)
#define SOCKET_OPS_MAX       (SOCKET_OPS_BASE + 1)

#define KMSG          "a message from kernel"
#define KMSG_LEN      sizeof("a message from kernel")

MODULE_LICENSE("GPL");

static int recv_msg(struct sock *sk, int cmd, void *user, unsigned int len)
{
int ret = 0;
printk(KERN_INFO "sockopt: recv_msg()\n");
/*
switch(cmd)
{
case IMP1_SET:
{
char umsg[64];
memset(umsg, 0, sizeof(char)*64);
copy_from_user(umsg, user, sizeof(char)*64);
printk("umsg: %s", umsg);
}
break;
}
*/
if (cmd == SOCKET_OPS_SET)
{
char umsg[64];
int len = sizeof(char)*64;
memset(umsg, 0, len);
ret = copy_from_user(umsg, user, len);
printk("recv_msg: umsg = %s. ret = %d\n", umsg, ret);       
}
return 0;
}

static int send_msg(struct sock *sk, int cmd, void *user, int *len)
{
int ret = 0;
printk(KERN_INFO "sockopt: send_msg()\n");
if (cmd == SOCKET_OPS_GET)
{
ret = copy_to_user(user, KMSG, KMSG_LEN);
printk("send_msg: umsg = %s. ret = %d. success\n", KMSG, ret);   
}
return 0;
}

static struct nf_sockopt_ops test_sockops =
{
.pf = PF_INET,
.set_optmin = SOCKET_OPS_SET,
.set_optmax = SOCKET_OPS_MAX,
.set = recv_msg,
.get_optmin = SOCKET_OPS_GET,
.get_optmax = SOCKET_OPS_MAX,
.get = send_msg,
};

static int __init init_sockopt(void)
{
printk(KERN_INFO "sockopt: init_sockopt()\n");
return nf_register_sockopt(&test_sockops);
}

static void __exit fini_sockopt(void)
{
printk(KERN_INFO "sockopt: fini_sockopt()\n");
nf_unregister_sockopt(&test_sockops);
}

module_init(init_sockopt);
module_exit(fini_sockopt);

 

3. 用户使用sockopt

Linux同样提供了一组用户接口来读写sockopt

#include

#include

int getsockopt(int s, int level, int optname, void *optval, socklen_t *optlen);

int setsockopt(int s, int level, int optname, const void *optval, socklen_t optlen);

在用户进程中,我们可以创建一个socket,然后通过socket来调用getsockopt/setsockopt来和内核空间通信。

用户代码

sockopt_clt.c

#include
#include
#include
#include
#include
#include

#define SOCKET_OPS_BASE      128
#define SOCKET_OPS_SET       (SOCKET_OPS_BASE)
#define SOCKET_OPS_GET      (SOCKET_OPS_BASE)
#define SOCKET_OPS_MAX       (SOCKET_OPS_BASE + 1)

#define UMSG      "a message from userspace"
#define UMSG_LEN  sizeof("a message from userspace")

char kmsg[64];

int main()
{
int sockfd;
int len;
int ret;

    sockfd = socket(AF_INET, SOCK_RAW, IPPROTO_RAW);
if(sockfd < 0)
{
printf("can not create a socket\n");
return -1;
}

    /*call function recv_msg()*/
ret = setsockopt(sockfd, IPPROTO_IP, SOCKET_OPS_SET, UMSG, UMSG_LEN);
printf("setsockopt: ret = %d. msg = %s\n", ret, UMSG);
len = sizeof(char)*64;

    /*call function send_msg()*/
ret = getsockopt(sockfd, IPPROTO_IP, SOCKET_OPS_GET, kmsg, &len);
printf("getsockopt: ret = %d. msg = %s\n", ret, kmsg);
if (ret != 0)
{
printf("getsockopt error: errno = %d, errstr = %s\n", errno, strerror(errno));
}

    close(sockfd);
return 0;
}

阅读(499) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~