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