为SandFS的FSHOOK增加netlink配置机制

正如iptables可以通过netlink将规则配置到Netfilter的HOOK点一样,我也希望实现一个fstables通过netlink将规则配置在FSHOOK的特定HOOK点。

于是我实现了一个Demo,但并没有完成,因为后面遇到了问题,我不希望现在就解决问题,而是希望把问题描述一番。

代码在sandfs_with_no_ebpf的devel分支:
https://github.com/marywangran/sandfs_with_no_ebpf/tree/devel

可以看到,增加了一个client目录,里面是一个python程序,它用来模拟fstables的功能:

import os
import socket
import struct

NETLINK_FSHOOK = 31

SANDFS_LOOKUP = 0
SANDFS_OPEN = 1
SANDFS_CLOSE = 2
SANDFS_READ = 3
SANDFS_WRITE = 4

# TODO TODO TODO TODO TODO TODO TODO TODO TODO TODO TODO TODO TODO TODO TODO TODO TODO TODO TODO TODO TODO TODO TODO TODO

sock = socket.socket(socket.AF_NETLINK, socket.SOCK_RAW, NETLINK_FSHOOK)
sock.bind((os.getpid(), RTMGRP_LINK))

name = bytes('test1'.encode('utf-8'))
opt = 'A'
hooknum = SANDFS_READ
uid = 1000
path = bytes('N/A'.encode('utf-8'))
pos = -1
count = 10
buf = bytes('N/A'.encode('utf-8'))

data = struct.pack("@32sBII32sII32s", name, opt, hooknum, uid, path, pos, count, buf);
sock.sendto(data, (0, 0))

非常简单,就是把一个matches结构体通过socket灌进内核,内核通过netlink套接字来接收它:

static void rule_nl_recv_msg(struct sk_buff *skb)
{
	struct rule_match *match;
	struct vfs_rule *rule;
	char *head;
	char name[DESC_MAX] = {0};
	int err = 0;

	head = (char *)skb->data;
	strncpy(name, head, DESC_MAX);
	head += DESC_MAX;
	match = (struct rule_match *)head;
	printk(KERN_INFO "##### opt:%c hook:%x   uid:%x  path:%s  pos:%x  count:%x  buff:%s\n",
			match->option,
			match->hooknum,
			match->uid,
			match->path,
			match->pos,
			match->count,
			match->buff);

	if (match->option == 'A') {
		rule = kzalloc(sizeof(struct vfs_rule), GFP_KERNEL);
		rule->hooknum = match->hooknum;
		strcpy(&rule->name[0], name);
		memcpy(&rule->match, match, sizeof(struct rule_match));
		err = sandfs_register_hook(rule);
	} else if (match->option == 'D') {
		rule = sandfs_unregister_hook(match->hooknum, name);
		if (rule) {
			kfree(rule);
		}
	}
}

逻辑非常简单,即根据配置信息生成一个vfs_rule结构体,插入对应HOOK的链表,即调用 sandfs_register_hook

现在问题来了,vfs_rule的func回调函数如何实现?虽然用户态可以灌下来策略和配置,但没有办法灌下来函数啊!

我试着重构FS_HOOK:

 inline int FS_HOOK(unsigned int hook, struct sandfs_args *args, void *priv)
 {
        int err = FS_ACCEPT;
@@ -54,9 +90,11 @@ inline int FS_HOOK(unsigned int hook, struct sandfs_args *args, void *priv)

        read_lock(&lock);
        list_for_each_entry(rule, &list, list) {
-               if (!rule->func)
-                       continue;
-               err = rule->func(hook, args, priv, &handled);
+               if (!rule->func) {
+                       err = gen_func(rule, args, priv, &handled);
+               } else {
+                       err = rule->func(hook, args, priv, &handled);
+               }
                if (handled == 1) {
                        read_unlock(&lock);
                        return err;

我增加了一个通用的gen_func函数:

static int gen_func(struct vfs_rule *rule, struct sandfs_args *args, void *priv, int *handled)
{
	int err = FS_ACCEPT;
#if 0
	struct rule_match match;
	int num;
	struct cred *cred;
	kuid_t id;
	unsigned int plen;

	match = rule->match;
	num = args->num_args;
	if (num >= 2) {
		cred = (struct cred *)largs->args[SANDFS_IDX_CRED].value;
		id = cred->uid;
		plen = largs->args[SANDFS_IDX_PATH].size;
		path = (char *)largs->args[SANDFS_IDX_PATH].value;
		if (num == 2 && match->uid == id.val && !strncmp(path, match->path, plen)) {
			err = FS_DROP;
			*handled = 1;
		}
	}
	if (num >= 4) {
	}
	if (num >= 5) {
	}
#endif
	return err;
}

但总觉得长得不好看,于是我#if 0掉了它!

嗯,是的,看来必须采用iptables的做法了,事先将各个match分别注册进内核,然后将rule紧密排列。

扫描二维码关注公众号,回复: 8768894 查看本文章

匹配进行的时候,顺序扫描这些排列着的rules,然后根据每一个match的func去进行匹配。就像iptables的xt_ematch_foreach宏那样。

你会发现,紧密排列的这些rules其实就是一张表,所以叫做iptables,ip6tables,arptables…

如果我也这么实现,才配叫做fstables…不过我觉得离目标很近了,不自觉中我的gen_func已经在向ipt_do_table靠拢了。

不过,这种tables有个弊端,那就是tables的内存是静态分配的,新增加一条规则所牵扯的动作如下:

  1. 分配新的连续内存,大小为所有已经rules的大小加上新rule的总大小。
  2. 将已有的rules拷贝进新内存。
  3. 将新rule追加到新内存已有rules之后。
  4. 释放旧内存。

如果规则特别多,tables就会非常大,这是一个非常耗时的操作,于是我在想用一种动态内存的方式搞定match匹配的问题。


浙江温州皮鞋湿,下雨进水不会胖。

发布了1550 篇原创文章 · 获赞 4786 · 访问量 1065万+

猜你喜欢

转载自blog.csdn.net/dog250/article/details/104062659