Linux内核和应用层程序通信get/setsockopt示例

最近开发一个realtek网卡配置程序,基于realtek提供的一套配置程序代码开发,分析代码的时候发现用户层和驱动之间传递消息是使用的是setsockopt、getsockopt,这个对我来说比较新鲜,以前见到的大都是netlink、proc等等,使用这种方式特别简单,应用层这边只需要创建套接字,而内核层只需要注册一个struct nf_sockopt_ops 结构体就可以了,这种方式以后可能会用到,先记录一下:

getsockopt, setsockopt声明:

#include <sys/types.h>          /* See NOTES */
#include <sys/socket.h>
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);

socketfd : 套接字
level : 类型,可以是SOL_SOCKET,IPPROTO_IP, IPPROTO_IPV6
optname : 操作类型,内核部分和应用部分关于操作类型定义一直,使用的时候不要和系统混用
optval : 选项值
optLen : 选项长度
成功返回0,失败返回-1并置errno

内核需要定义的结构体: 

/* 内核部分定义结构体 */
struct nf_sockopt_ops {
	struct list_head list;

	u_int8_t pf; /* 协议族, AF_INET | AF_INET6 */

	/* Non-inclusive ranges: use 0/0/NULL to never get called. */
	int set_optmin;    /* SET类操作类型最小值 */
	int set_optmax;    /* SET类操作类型最大值 */
	int (*set)(struct sock *sk, int optval, void __user *user, unsigned int len); /* SET回调处理函数 */
	int get_optmin;    /* GET类操作类型最小值 */
	int get_optmax;    /* GET类操作类型最大值 */
	int (*get)(struct sock *sk, int optval, void __user *user, int *len); /* SET回调处理函数 */
	/* Use the module struct to lock set/get code in place */
	struct module *owner;
};

结构体定义好后内核层做好相应的处理函数就可以了,用例:

内核和应用层公共头文件:

sockopt_test.h

#ifndef __SOCKOPT_TEST_H_
#define __SOCKOPT_TEST_H_
 
#define SOCKOPT_BASE                 (10240)
#define SOCKOPT_SET_MIN              ((SOCKOPT_BASE) + 1)
#define SOCKOPT_SET_BUFFER                ((SOCKOPT_BASE) + 1)
#define SOCKOPT_SET_MAX              ((SOCKOPT_BASE) + 2)
 
#define SOCKOPT_GET_MIN              ((SOCKOPT_BASE) + 1)
#define SOCKOPT_GET_BUFFER                ((SOCKOPT_BASE) + 1)
#define SOCKOPT_GET_MAX              ((SOCKOPT_BASE) + 2)
 
#endif

内核实现:sockopt_test.c

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

#define BUFFER_LEN_MAX 1024
static char buffer[BUFFER_LEN_MAX];

/* setsockopt 回调处理函数 */ 
int setsockopt_handler(struct sock *sk, int optval, void __user *user, unsigned int len)
{

        switch(optval)
        {
            case SOCKOPT_SET_BUFFER:
                if ( copy_from_user((void*)&buffer, user, len) != 0 ) 
                {
                        return -EFAULT;
                }

                break;
            default:
                printk(KERN_INFO "invalid setsockopt opt : %d\n", optval);
                return -EFAULT;
        }
 
        printk(KERN_INFO "buffer[]: %s\n", buffer);
        return 0;
}

/* getsockopt 回调处理函数 */  
int getsockopt_handler(struct sock *sk, int optval, void __user *user, int *len)
{
        unsigned int cpy_len;
        
        cpy_len = *len > BUFFER_LEN_MAX ? BUFFER_LEN_MAX : *len;
        switch(optval)
        {
            case SOCKOPT_GET_BUFFER:
                if(copy_to_user(user, (void*)&buffer[0], cpy_len) != 0) 
                {
                        printk(KERN_INFO "getsockopt_handler fail \n");
                        return -EFAULT;
                }

                    break;
            default:
                    printk(KERN_INFO "unrecognized getsockopt optvalue : %d\n", optval);
                    return -EFAULT;
        }
 
        return 0;
}

/* 定义nf_sockopt_ops结构体 */ 
static struct nf_sockopt_ops sockopt_ops_test =
{
        .pf                     = PF_INET,
        .set_optmin             = SOCKOPT_SET_MIN,
        .set_optmax             = SOCKOPT_SET_MAX,
        .set                    = setsockopt_handler,
        .get_optmin             = SOCKOPT_GET_MIN,
        .get_optmax             = SOCKOPT_GET_MAX,
        .get                    = getsockopt_handler,
};
 
static __init int sockopt_test_init(void)
{
        int result;
        /* 注册sockopt */
        result = nf_register_sockopt(&sockopt_ops_test);
        if(result != 0) 
        {
                printk("register sockopt error!\n");
        }
 
        printk("sockopt_test register success !\n");
        return 0;
}
 
static __exit void sockopt_test_exit(void)
{
        /* 注销sockopt */
        nf_unregister_sockopt(&sockopt_ops_test);
        printk("sockopt_test unregister\n");
}
 
module_init(sockopt_test_init);
module_exit(sockopt_test_exit);
 
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Stone");

应用层:

sockopt_user.c

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

#define USAGE "./sockopt get \n"  \
              "./sockopt set buffer \n"

#define BUFFER_LEN_MAX 1024
static char buffer[BUFFER_LEN_MAX];

static int
getsockopt_handler(void)
{
    int fd, ret, size;
    
    size = sizeof(buffer);
    memset(buffer, '\0', size);
    fd = socket(PF_INET, SOCK_DGRAM, 0);
    if(fd == -1) 
    {
            printf("socket error, errno : %d\n", errno);
            return -1;
    }
    
    ret = getsockopt(fd, IPPROTO_IP, SOCKOPT_GET_BUFFER, buffer, &size);
    if (ret == -1)
    {
        printf("getsockopt fail, errno : %d\n", errno);
    }
    else
    {
        printf("getsockopt return buffer : %s\n", buffer);
    }
    close(fd);
    return 0;
}

static int
setsockopt_handler(char *arg)
{
    int fd, ret, cpy_len;
    unsigned int size;
    
    size = sizeof(buffer);
    cpy_len = size > strlen(arg) ? strlen(arg) : size;        
    memset(buffer, '\0', size);
    memcpy(buffer, arg, cpy_len);
    fd = socket(PF_INET, SOCK_DGRAM, 0);
    if(fd == -1) 
    {
            printf("socket error, errno : %d\n", errno);
            return -1;
    }
    
    ret = setsockopt(fd, IPPROTO_IP, SOCKOPT_SET_BUFFER, buffer, size);
    if (ret == -1)
    {
        printf("setsockopt fail, errno : %d\n", errno);
    }
    
    close(fd);
    return 0;
}

 
int
main(int argc, char **argv)
{
        int direction;
        int index;
        int value = 9;
 
        if(argc < 2) 
        {                
            goto FAIL;
        }
 
        if(strcmp(argv[1], "get") == 0) 
        {
            return getsockopt_handler();
        } 
        else if(strcmp(argv[1], "set") == 0) 
        {
            if (argc != 3)
            {
                goto FAIL;
            }
            
            return setsockopt_handler(argv[2]);
        } 
 
 FAIL:
    printf(USAGE);
    exit(EXIT_FAILURE);
}

 运行示例:

参考资料:

1.man getsockopt,  https://linux.die.net/man/2/getsockopt

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

https://blog.csdn.net/stone8761/article/details/76049865#commentBox

  • 1
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: getsockopt/setsockopt函数是用于设置和获取套接字选项的函数。其中,getsockopt用于获取指定套接字选项的值,而setsockopt用于设置指定套接字选项的值。这两个函数在网络编程中经常被用到。 ### 回答2: getsockoptsetsockopt函数是用来获取和设置套接字选项的API函数,在网络编程中十分常见。套接字选项是一个可选的参数,它允许用户设置套接字的某些属性,例如超时时间、是否启用广播、缓冲区大小等等。 getsockopt函数用于获取套接字选项的当前值。它需要传入以下参数: 1. sockfd: 套接字描述符; 2. level: 选项定义的协议层; 3. optname: 需要获取的选项名称; 4. optval: 指向一个缓冲区,用于存储获取到的选项值; 5. optlen: 缓冲区的大小。 setsockopt函数用于设置套接字选项的值。它需要传入以下参数: 1. sockfd: 套接字描述符; 2. level: 选项定义的协议层; 3. optname: 需要设置的选项名称; 4. optval: 指向一个缓冲区,用于存储需要设置的选项值; 5. optlen: 缓冲区的大小。 需要注意的是,不同的协议层和选项名称对应的值有所不同。可以通过man手册或者网络查找相关资料来获取更详细的信息。 这些函数在网络编程中经常被使用,例如可以通过setsockopt函数设置套接字的超时时间,当网络I/O操作超过指定时间时,会自动返回一个超时错误;也可以通过getsockopt函数来获取当前是否启用了广播选项等等。 总之,getsockoptsetsockopt函数可以让我们更方便地控制和监控套接字的行为,提高网络通信的效率和可靠性。 ### 回答3: getsockoptsetsockopt函数都是与网络编程相关的系统调用函数,它们用于控制和获取套接字选项。在进行网络编程时,我们需要使用这两个函数来调整套接字的设置,以便实现更好的网络通信效果。 具体地说,setsockopt函数可以设置某个套接字的选项值,例如设置套接字的发送和接收缓冲区大小、设置是否对数据包进行分片等等。而getsockopt函数则可以获取某个套接字的选项值,以便进行校验或者输出日志等功能。 关于参数方面,getsockoptsetsockopt函数都需要传入当前操作的套接字句柄,以及要设置或者获取的选项值。setsockopt函数还需要传入对应选项值的指针和大小;而getsockopt函数需要传入一个指向缓冲区的指针,用来接收获取的选项值和大小。 需要注意的是,每个选项都有对应的协议层级,而不同的选项可能只在特定协议的套接字上有效。因此,在使用这两个函数时,需要注意选项的协议层级,以及操作的套接字所使用的协议类型,避免出现无法设置或获取选项值的情况。 同时,getsockoptsetsockopt函数也有一定的返回值,setsockopt函数返回是否设置成功,而getsockopt函数返回获取的选项值的长度。在使用时,我们需要对返回值进行判断,以便及时发现并解决问题。 总之,getsockoptsetsockopt函数是网络编程中必不可少的函数。它们用于设置和获取套接字的选项值,以便我们能够更加精细地控制网络通信的行为,从而提高网络通信效率和质量。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值