nf_register_sockop
Posted Li-Yongjun
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了nf_register_sockop相关的知识,希望对你有一定的参考价值。
简介
netfilter 提供了 nf_register_sockopt() 和 nf_unregister_sockopt() 来动态登记或取消 sockopt 命令字。
打开一个网络 socket 后可以使用 set/getsockopt(2) 实现用户空间与内核的通信,本质和 ioctl 差不多,区别在于set/getsockopt 不用新建设备,直接利用系统已有的 socket 类型就可以进行,可用setsockopt() 函数向内核写数据,用 getsockopt() 从内核读数据。
示例环境
系统:Ubuntu 20.04
kernel:5.15.0-52-generic
源码
sockopt.c
#include "sockopt.h"
#include <linux/init.h>
#include <linux/module.h>
#include <linux/netfilter.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.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
Makefile
obj-m:=sockopt.o
CURRENT_PATH:=$(shell pwd)
VERSION_NUM:=$(shell uname -r)
LINUX_PATH:=/usr/src/linux-headers-$(VERSION_NUM)
all :
make -C $(LINUX_PATH) M=$(CURRENT_PATH) modules
clean:
make -C $(LINUX_PATH) M=$(CURRENT_PATH) clean
测试代码
test.c
#include <stdio.h>
#include <errno.h>
#include <netinet/in.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/socket.h>
#include <sys/types.h>
#include "sockopt.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);
编译
报错
$ make
make -C /usr/src/linux-headers-5.15.0-52-generic M=/home/liyongjun/project/c/C_study/kernel/netfilter/2 modules
make[1]: 进入目录“/usr/src/linux-headers-5.15.0-52-generic”
CC [M] /home/liyongjun/project/c/C_study/kernel/netfilter/2/sockopt.o
/home/liyongjun/project/c/C_study/kernel/netfilter/2/sockopt.c:61:35: error: initialization of ‘int (*)(struct sock *, int, sockptr_t, unsigned int)’ aka ‘int (*)(struct sock *, int, struct <anonymous>, unsigned int)’ from incompatible pointer type ‘int (*)(struct sock *, int, void *, unsigned int)’ [-Werror=incompatible-pointer-types]
61 | .set = setsockopt_handler,
| ^~~~~~~~~~~~~~~~~~
/home/liyongjun/project/c/C_study/kernel/netfilter/2/sockopt.c:61:35: note: (near initialization for ‘sockopt_ops_test.set’)
cc1: some warnings being treated as errors
make[2]: *** [scripts/Makefile.build:297:/home/liyongjun/project/c/C_study/kernel/netfilter/2/sockopt.o] 错误 1
make[1]: *** [Makefile:1900:/home/liyongjun/project/c/C_study/kernel/netfilter/2] 错误 2
make[1]: 离开目录“/usr/src/linux-headers-5.15.0-52-generic”
make: *** [Makefile:9:all] 错误 2
出错原因是,从内核 5.9 版本开始,struct nf_sockopt_ops 中 set 函数指针的定义由
int (*set)(struct sock *sk, int optval, void __user *user, unsigned int len);
改为了
int (*set)(struct sock *sk, int optval, sockptr_t arg, unsigned int len);
所以,我们也要将 setsockopt_handler 实现改为如下,① 第三个参数改为 sockptr_t 类型,② copy_from_user() 改为 copy_from_sockptr()。
int setsockopt_handler(struct sock *sk, int optval, sockptr_t user, unsigned int len)
switch (optval)
case SOCKOPT_SET_BUFFER:
if (copy_from_sockptr((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;
测试
$ sudo insmod sockopt.ko
$ ./a.out set hello
$ ./a.out get
getsockopt return buffer : hello
以上是关于nf_register_sockop的主要内容,如果未能解决你的问题,请参考以下文章