通过netfilter使用get/setsockopt实现用户态与内核模块之间的通信

一般情况下,get/setsockopt第第三个参数和第二个参数紧密相关,即一般选定了level,optname也基本就选定了范围。但是可以原是套接字可以自定义sockopt,并且netfilter还为此提供了几个接口。使用方法如下:

首先要定义一个struct nf_sockopt_ops类型的结构体,然后通过nf_register_sockopt将其注册,最后通过 nf_sockopt_ops的get和set函数对外提供结构,供用户态使用get/setsockopt来操作。废话不多说,上代码。

内核模块代码hello.c

#include <linux/init.h>

#include <linux/module.h>

#include <linux/kernel.h>

#include <linux/socket.h>

#include <linux/netfilter_ipv4.h>

#include <linux/string.h>

 

#define SOCKET_OPS_BASE128/* base for firewall socket options */

#define SOCKET_OPS_SET_HELLO(SOCKET_OPS_BASE + 0)

#define SOCKET_OPS_SET_WORLD(SOCKET_OPS_BASE + 1)

#define SOCKET_OPS_GET_HELLO(SOCKET_OPS_BASE + 0)

#define SOCKET_OPS_GET_WORLD(SOCKET_OPS_BASE + 1)

// If SOCKET_OPS_BASE+1, ERROR

#define SOCKET_OPS_MAX(SOCKET_OPS_BASE + 2)

 

static inthello= 0;

static charworld[16]= {'\0'};

 

MODULE_LICENSE("GPL");

MODULE_AUTHOR("LGW");

MODULE_DESCRIPTION("LGW Kernel Module");

 

static int set_var(struct sock *sk, int cmd, void __user *user, unsigned int len)

{

int ret = 0;

printk(KERN_INFO "sockopt: set_var\n"); 

 

switch(cmd)

{

case SOCKET_OPS_SET_HELLO:

if (user == NULL)

{

ret = -1;

break;

}

ret = copy_from_user(&hello, user, len);

printk("hello from user is %d\n", hello);

break;

 

case SOCKET_OPS_SET_WORLD:

if (user == NULL)

{

ret = -1;

break;

}

ret = copy_from_user(&world, user, len);

printk("world from user is %s\n", world);

break;

 

default:

printk("undefined sock opt when set\n");

}

 

return ret;

 

}

 

static int get_var(struct sock *sk, int cmd, void __user *user, int *len)

{

int ret = 0;

printk(KERN_INFO "sockopt: get_var\n"); 

 

switch(cmd)

{

case SOCKET_OPS_GET_HELLO:

hello = 100;

ret = copy_to_user(user, &hello, sizeof(hello));

break;

 

case SOCKET_OPS_GET_WORLD:

memcpy(world, "hello,World!", sizeof("hello,world!"));

ret = copy_to_user(user, world, sizeof("hello,World!"));

break;

 

default:

printk("undefined sock opt when get\n");

}

return ret;

}

 

 

 

static struct nf_sockopt_ops my_sockopts = {

.pf= PF_INET,

.set_optmin= SOCKET_OPS_BASE,

.set_optmax= SOCKET_OPS_MAX,

.set= set_var,

.get_optmin= SOCKET_OPS_BASE,

.get_optmax= SOCKET_OPS_MAX,

.get= get_var,

};

 

 

 

static int __init hello_init(void)

{

int ret = 0;

printk("Loading LGW Kernel Module...\n");

ret = nf_register_sockopt(&my_sockopts);

return ret;

}

 

static void __exit hello_exit(void)

{

printk("Good Bye, LGW Kernel Module!\n");

nf_unregister_sockopt(&my_sockopts);

}

 

module_init(hello_init);

module_exit(hello_exit);用户态代码world.c

#include <sys/socket.h>

#include <netinet/in.h>

#include <unistd.h>

#include <string.h>

#include <stdio.h>

#define SOCKET_OPS_BASE128/ base for firewall socket options /

#define SOCKET_OPS_SET_HELLO(SOCKET_OPS_BASE + 0)

#define SOCKET_OPS_SET_WORLD(SOCKET_OPS_BASE + 1)

#define SOCKET_OPS_GET_HELLO(SOCKET_OPS_BASE + 0)

#define SOCKET_OPS_GET_WORLD(SOCKET_OPS_BASE + 1)

static inthello= 0;

static charworld16= {'\0'};

int main()

{

int sockfd;

int len;

int ret;

int hello = 1;

char world16 = "1234567";

sockfd = socket(AF_INET, SOCK_RAW, IPPROTO_RAW);

if(sockfd < 0)

{

printf("socket create error\n");

return -1;

}

setsockopt(sockfd, IPPROTO_IP, SOCKET_OPS_SET_HELLO,

&hello, sizeof(hello));

len = sizeof(hello);

ret = getsockopt(sockfd, IPPROTO_IP, SOCKET_OPS_GET_HELLO,

&hello, &len);

printf("ret is %d\n", ret);

printf("Now hello is: %d\n", hello);

setsockopt(sockfd, IPPROTO_IP, SOCKET_OPS_SET_WORLD,

world, sizeof("1234567"));

len = sizeof(world);

ret = getsockopt(sockfd, IPPROTO_IP, SOCKET_OPS_GET_WORLD,

world, &len);

printf("ret is %d\n", ret);

printf("Now world is: %s, and len is: %d\n", world, len);

return 0;

}

本文在写作过程中参考了:http://hi.baidu.com/mnkee/blog/item/e2094936b78fe1d0a3cc2b4a.html

原创声明,本文系作者授权云+社区发表,未经许可,不得转载。

如有侵权,请联系 yunjia_community@tencent.com 删除。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏技术小站

MySQL之char、varchar和text的设计

最近有表结构设计中出现了varchar(10000)的设计引起了大家的讨论,我们下面就来分析分析。

732
来自专栏杨建荣的学习笔记

通过pl/sql来格式化sql(r4笔记第63天)

在之前的一篇博文中分享了通过java来格式化sql,http://blog.itpub.net/23718752/viewspace-1444910/ 今天突然...

3194
来自专栏Golang语言社区

47. 访问MySql数据库实现增删改查 | 厚土Go学习笔记

作为服务端程序,对数据库的访问是很常见的操作。我们来熟悉一下go语言访问MySql数据库的基本操作(增删改查)。 数据库访问需要用到标准库database/sq...

3748
来自专栏上善若水

004-golang 正则表达式的使用

1938
来自专栏游戏杂谈

Android判断用户的网络类型(2/3/4G、wifi)

很多时候需要先判断当前用户的网络,才会继续之后的一些处理逻辑。但网络类型获取这一块,我用我自己的的手机调试时遇到一些问题,这里记录一下。

682
来自专栏Jerry的SAP技术分享

如何在ABAP里用函数式编程思想打印出非波拉契Fibonacci(数列)

在JavaScript里可以用ES6提供的FunctionGenerator这种黑科技来打印非波拉契数列,具体细节参考我这篇文章。

893
来自专栏杨建荣的学习笔记

通过java来格式化sql语句(r4笔记第61天)

经常在抓取一些sql语句的时候,得到的sql文本有格式的问题,如果尝试得到执行计划,每次都会费一番周折。 比如下面的sql语句,基本包含了常见的格式问题。第3行...

2734
来自专栏c#开发者

LightSwitch 2011 数据字段唯一性验证方案

LightSwitch 2011 数据字段唯一性验证方案 ? 验证单表数据的某个字段不能输入重复值 设置实体字段唯一索引 ? 如果不写代码,那么验证只会在...

3295
来自专栏Golang语言社区

47. 访问MySql数据库实现增删改查 | 厚土Go学习笔记

作为服务端程序,对数据库的访问是很常见的操作。我们来熟悉一下go语言访问MySql数据库的基本操作(增删改查)。 数据库访问需要用到标准库database/sq...

3488
来自专栏MasiMaro 的技术博文

遍历系统中加载的驱动程序以及通过设备对象指针获取设备对象名称

遍历系统中加载的驱动可以在R3层完成,通过几个未导出的函数:ZwOpenDirectoryObject、ZwQueryDirectoryObject,下面是具体...

902

扫码关注云+社区