Linux Kernel 学习笔记17:内核与用户层通信之sockopt

(本章基于:Linux-4.4.0-37)

sockopt是内核与用户层通信方法中非常简单的一种,其本质是通过copy_to_user/copy_from_user在内核与用户层传递数据,因此效率不高,常用于传递控制、状态等信息;


内核层:

数据结构:

linux/netfilter.h

static struct nf_sockopt_ops my_sockopt_ops =
{
        .pf                     = PF_INET,             //协议族
        .set_optmin             = MY_SOCKOPT_SET_MIN,  //最小set命令
        .set_optmax             = MY_SOCKOPT_SET_MAX,  //最大set命令
        .set                    = my_sockopt_set,      //set处理函数
        .get_optmin             = MY_SOCKOPT_GET_MIN,  //最小get命令
        .get_optmax             = MY_SOCKOPT_GET_MAX,  //最大get命令
        .get                    = my_sockopt_get,      //get处理函数
};
在内核中由结构struct nf_sockopt_ops来描述sockopt,其中定义了set/get处理函数对应用户层的setsockopt/getsockopt。最小、最大命令字规定了命令字范围,命令字必须大于等于最小命令字,小于最大命令字。命令字的值自行在内核层与用户层定义,尽量定义较大的命令字,以避免与内核已存在的命令字重复。


操作函数:

int nf_register_sockopt(struct nf_sockopt_ops *reg);
void nf_unregister_sockopt(struct nf_sockopt_ops *reg);

用户层:

用户层使用getsockopt/setsockopt调用底层接口,定义如下:

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

例:

在本例中,内核层定义了一个5个空间的大小的整型数组,通过set/get接口可修改/读取每个空间的数据;


头文件 my_sockopt.h

#ifndef __MY_SOCKOPT_H_
#define __MY_SOCKOPT_H_

#define MY_SOCKOPT_BASE                 (2413)
#define MY_SOCKOPT_SET_MIN              ((MY_SOCKOPT_BASE) + 1)
#define MY_SOCKOPT_SET_1                ((MY_SOCKOPT_BASE) + 1)
#define MY_SOCKOPT_SET_2                ((MY_SOCKOPT_BASE) + 2)
#define MY_SOCKOPT_SET_3                ((MY_SOCKOPT_BASE) + 3)
#define MY_SOCKOPT_SET_4                ((MY_SOCKOPT_BASE) + 4)
#define MY_SOCKOPT_SET_5                ((MY_SOCKOPT_BASE) + 5)
#define MY_SOCKOPT_SET_MAX              ((MY_SOCKOPT_BASE) + 6)

#define MY_SOCKOPT_GET_MIN              ((MY_SOCKOPT_BASE) + 6)
#define MY_SOCKOPT_GET_1                ((MY_SOCKOPT_BASE) + 6)
#define MY_SOCKOPT_GET_2                ((MY_SOCKOPT_BASE) + 7)
#define MY_SOCKOPT_GET_3                ((MY_SOCKOPT_BASE) + 8)
#define MY_SOCKOPT_GET_4                ((MY_SOCKOPT_BASE) + 9)
#define MY_SOCKOPT_GET_5                ((MY_SOCKOPT_BASE) + 10)
#define MY_SOCKOPT_GET_MAX              ((MY_SOCKOPT_BASE) + 11)


#endif

内核层sockopt.c

#include <linux/init.h>
#include <linux/module.h>
#include <linux/netfilter.h>
#include "my_sockopt.h"

static int buf[5];

int my_sockopt_set(struct sock *sk, int optval, void __user *user, unsigned int len)
{
        int value;
        int cmd;

        switch(optval)
        {
        case MY_SOCKOPT_SET_1:
                cmd = 0;
                break;
        case MY_SOCKOPT_SET_2:
                cmd = 1;
                break;
        case MY_SOCKOPT_SET_3:
                cmd = 2;
                break;
        case MY_SOCKOPT_SET_4:
                cmd = 3;
                break;
        case MY_SOCKOPT_SET_5:
                cmd = 4;
                break;
        default:
                printk(KERN_INFO "my sockopt set fault\n");
                return -EFAULT;
        }

        if ( copy_from_user((void*)&value, user, len) != 0 ) {
                return -EFAULT;
        }
        buf[cmd] = value;
        printk(KERN_INFO "my sockopt set%d:%d\n", cmd, value);
        return 0;
}

int my_sockopt_get(struct sock *sk, int optval, void __user *user, int *len)
{
        int value;
        printk(KERN_INFO "my sockopt get\n");

        switch(optval)
        {
        case MY_SOCKOPT_GET_1:
                value = buf[0];
                break;
        case MY_SOCKOPT_GET_2:
                value = buf[1];
                break;
        case MY_SOCKOPT_GET_3:
                value = buf[2];
                break;
        case MY_SOCKOPT_GET_4:
                value = buf[3];
                break;
        case MY_SOCKOPT_GET_5:
                value = buf[4];
                break;
        default:
                printk(KERN_INFO "my sockopt get default\n");
                return -EFAULT;
        }

        *len = 4;
        if(copy_to_user(user, (void*)&value, *len) != 0) {
                printk(KERN_INFO "my sockopt get fault\n");
                return -EFAULT;
        }

        return 0;
}

static struct nf_sockopt_ops my_sockopt_ops =
{
        .pf                     = PF_INET,
        .set_optmin             = MY_SOCKOPT_SET_MIN,
        .set_optmax             = MY_SOCKOPT_SET_MAX,
        .set                    = my_sockopt_set,
        .get_optmin             = MY_SOCKOPT_GET_MIN,
        .get_optmax             = MY_SOCKOPT_GET_MAX,
        .get                    = my_sockopt_get,
};

static __init int hello_init(void)
{
        int result;

        memset(buf, 0, sizeof(buf));
        result = nf_register_sockopt(&my_sockopt_ops);
        if(result != 0) {
                printk(KERN_ALERT "register sockopt error!\n");
        }

        printk(KERN_ALERT "hello init success!\n");
        return 0;
}

static __exit void hello_exit(void)
{
        nf_unregister_sockopt(&my_sockopt_ops);
        printk(KERN_WARNING "helloworld exit!\n");
}

module_init(hello_init);
module_exit(hello_exit);

MODULE_LICENSE("GPL");
MODULE_AUTHOR("Stone");

用户层 test.c

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include "my_sockopt.h"

static int
my_sockopt(int direction, int index, int *value)
{
        int fd;
        int size;

        if(!value) {
                return -1;
        }

        fd = socket(PF_INET, SOCK_RAW, IPPROTO_RAW);
        if(!fd) {
                perror("socket");
                return -2;
        }

        if(direction == 0) {
                size = sizeof(int);
                getsockopt(fd, IPPROTO_IP, MY_SOCKOPT_GET_1 + index - 1, value, &size);
        } else if(direction == 1) {
                setsockopt(fd, IPPROTO_IP, MY_SOCKOPT_SET_1 + index - 1, value, sizeof(int));
        } else {
                close(fd);
                return -3;
        }

        close(fd);
        return 0;
}

int
main(int argc, char **argv)
{
        int direction;
        int index;
        int value;

        if(argc <= 2) {
                printf("parameter error 1!\n");
                exit(EXIT_FAILURE);
        }

        if(strcmp(argv[1], "get") == 0) {
                direction = 0;
        } else if(strcmp(argv[1], "set") == 0) {
                direction = 1;
        } else {
                printf("parameter error! 2\n");
                exit(EXIT_FAILURE);
        }

        if(direction == 0 && argc != 3) {
                printf("parameter error! 3\n");
                exit(EXIT_FAILURE);
        }
        if(direction == 1 && argc != 4) {
                printf("parameter error! 4\n");
                exit(EXIT_FAILURE);
        }

        index = atoi(argv[2]);
        if(index < 1 || index >5) {
                printf("parameter error! 5\n");
                exit(EXIT_FAILURE);
        }
        if(direction == 1) {
                value = atoi(argv[3]);
        }

        if(my_sockopt(direction, index, &value) == 0) {
                if(direction == 0) {
                        printf("get index%d:%d\n", index, value);
                }
        } else {
                printf("sockopt error!\n");
                exit(EXIT_FAILURE);
        }

        return 0;
}
























  • 4
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论
要安装 Docker,需要在 Linux 内核中开启以下配置: 1. 需要开启 cgroup 控制组支持,包括 cgroup v1 和 cgroup v2,开启方式如下: ``` CONFIG_CGROUPS=y CONFIG_CGROUP_CPUACCT=y CONFIG_CGROUP_DEVICE=y CONFIG_CGROUP_FREEZER=y CONFIG_CGROUP_SCHED=y CONFIG_CGROUP_PERF=y CONFIG_CGROUP_HUGETLB=y CONFIG_CGROUP_NET_CLS=y CONFIG_CGROUP_NET_PRIO=y CONFIG_CGROUP_NET_CLASSID=y CONFIG_CGROUP_BLKIO=y CONFIG_CGROUP_RDMA=y CONFIG_CGROUP_PIDS=y CONFIG_CGROUP_SOCKOPT=y CONFIG_CGROUP_DEBUG=y ``` 2. 需要开启 Namespace 支持,包括 Mount Namespace、UTS Namespace、IPC Namespace、PID Namespace 和 Network Namespace,开启方式如下: ``` CONFIG_NAMESPACES=y CONFIG_UTS_NS=y CONFIG_IPC_NS=y CONFIG_PID_NS=y CONFIG_NET_NS=y ``` 3. 需要开启 OverlayFS 文件系统支持,开启方式如下: ``` CONFIG_OVERLAY_FS=y ``` 4. 需要开启 iptables 软件包过滤支持,开启方式如下: ``` CONFIG_NETFILTER_XT_MATCH_COMMENT=y CONFIG_NETFILTER_XT_MATCH_CONNTRACK=y CONFIG_NETFILTER_XT_MATCH_DCCP=y CONFIG_NETFILTER_XT_MATCH_ESP=y CONFIG_NETFILTER_XT_MATCH_HASHLIMIT=y CONFIG_NETFILTER_XT_MATCH_HELPER=y CONFIG_NETFILTER_XT_MATCH_IPRANGE=y CONFIG_NETFILTER_XT_MATCH_L2TP=y CONFIG_NETFILTER_XT_MATCH_LENGTH=y CONFIG_NETFILTER_XT_MATCH_LIMIT=y CONFIG_NETFILTER_XT_MATCH_MAC=y CONFIG_NETFILTER_XT_MATCH_MARK=y CONFIG_NETFILTER_XT_MATCH_MULTIPORT=y CONFIG_NETFILTER_XT_MATCH_NFACCT=y CONFIG_NETFILTER_XT_MATCH_OSF=y CONFIG_NETFILTER_XT_MATCH_OWNER=y CONFIG_NETFILTER_XT_MATCH_POLICY=y CONFIG_NETFILTER_XT_MATCH_PROTO=y CONFIG_NETFILTER_XT_MATCH_RATEEST=y CONFIG_NETFILTER_XT_MATCH_RECENT=y CONFIG_NETFILTER_XT_MATCH_SCTP=y CONFIG_NETFILTER_XT_MATCH_STATE=y CONFIG_NETFILTER_XT_MATCH_STATISTIC=y CONFIG_NETFILTER_XT_MATCH_STRING=y CONFIG_NETFILTER_XT_MATCH_TCPMSS=y CONFIG_NETFILTER_XT_MATCH_TIME=y CONFIG_NETFILTER_XT_MATCH_U32=y CONFIG_NETFILTER_XT_TARGET_AUDIT=y CONFIG_NETFILTER_XT_TARGET_CHECKSUM=y CONFIG_NETFILTER_XT_TARGET_CLASSIFY=y CONFIG_NETFILTER_XT_TARGET_CONNMARK=y CONFIG_NETFILTER_XT_TARGET_CPU=y CONFIG_NETFILTER_XT_TARGET_DSCP=y CONFIG_NETFILTER_XT_TARGET_HL=y CONFIG_NETFILTER_XT_TARGET_HMARK=y CONFIG_NETFILTER_XT_TARGET_IDLETIMER=y CONFIG_NETFILTER_XT_TARGET_LED=y CONFIG_NETFILTER_XT_TARGET_LOG=y CONFIG_NETFILTER_XT_TARGET_MARK=y CONFIG_NETFILTER_XT_NAT=y CONFIG_NETFILTER_XT_TARGET_NETMAP=y CONFIG_NETFILTER_XT_TARGET_NFLOG=y CONFIG_NETFILTER_XT_TARGET_NFQUEUE=y CONFIG_NETFILTER_XT_TARGET_NOTRACK=y CONFIG_NETFILTER_XT_TARGET_RATEEST=y CONFIG_NETFILTER_XT_TARGET_REDIRECT=y CONFIG_NETFILTER_XT_TARGET_TEE=y CONFIG_NETFILTER_XT_TARGET_TPROXY=y CONFIG_NETFILTER_XT_TARGET_TRACE=y ``` 请注意,这些配置项可能随着不同版本的内核而有所不同。
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

stone8761

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值