最近开发一个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