(本章基于: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;
}