分享

使用sockopt实现内核与用户之间通信

 rookie 2011-10-23

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 <linux/module.h>
#include <linux/kernel.h>
#include <linux/types.h>
#include <linux/string.h>
#include <linux/netfilter_ipv4.h>
#include <linux/init.h>
#include <asm/uaccess.h>

#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 <sys/types.h>

#include <sys/socket.h>

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 <unistd.h>
#include <stdio.h>
#include <sys/socket.h>
#include <linux/in.h>
#include <string.h>
#include <errno.h>

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

    本站是提供个人知识管理的网络存储空间,所有内容均由用户发布,不代表本站观点。请注意甄别内容中的联系方式、诱导购买等信息,谨防诈骗。如发现有害或侵权内容,请点击一键举报。
    转藏 分享 献花(0

    0条评论

    发表

    请遵守用户 评论公约

    类似文章 更多