代码路径
git访问方式:https://gitee.com/yuewguo/url_filter.git
svn访问方式:svn://gitee.com/yuewguo/url_filter

一、自定义内核模块
static int UF_init(void) //安装模块入口
{
   int ret = 0;
   /* netfilter-hook */
   ret = UF_NF_hook(); // 注册netfilter处理函数
   if (0 != ret)
   {
       printk(KERN_EMERG"register hook fail!rn");
       return ret;
   }
   /* syscall */
   ret = UF_setsyscall();// 注册中断处理函数,用于接收用户态配置
   UF_rule_init();// 初始化用户配置结构
   printk(KERN_EMERG"install urlfilter succ!rn");
   return 0;
}
static void UF_exit(void) //卸载模块入口
{
   printk(KERN_EMERG"uninstall urlfilter succ!rn");
   UF_NF_unhook();// 卸载注册netfilter处理函数
   UF_resetsyscall();// 还原中断中断处理函数
   UF_rule_exit();// 删除用户配置
   return ;
}
module_init(UF_init);
module_exit(UF_exit);

二、Netfilter处理函数
netfilter处理框架如下图(转自网上):
需要注意的是,netfilter对外提供的hook点仅针对正向收包,应答报文不经过hook的业务点。
如果需要在应答流程中添加hook点需要自行修改netfilter代码。
解读Netfilter示例程序-LMLPHP
struct nf_hook_ops UF_NF_hookfunc[] = {    
   {
       .hook = UF_forward_entry,
       .owner = THIS_MODULE,
       .pf = NFPROTO_IPV4,
       .hooknum = NF_INET_PRE_ROUTING,
       .priority = NF_IP_PRI_FIRST,
   },
   {
       .hook = UF_forward_entry,
       .owner = THIS_MODULE,
       .pf = NFPROTO_IPV4,
       .hooknum = NF_INET_LOCAL_IN,
       .priority = NF_IP_PRI_FIRST,
   },
   {
       .hook = UF_forward_entry,
       .owner = THIS_MODULE,
       .pf = NFPROTO_IPV4,
       .hooknum = NF_INET_FORWARD,
       .priority = NF_IP_PRI_FIRST,
   },
   {
       .hook = UF_forward_entry,
       .owner = THIS_MODULE,
       .pf = NFPROTO_IPV4,
       .hooknum = NF_INET_LOCAL_OUT,
       .priority = NF_IP_PRI_FIRST,
   },
   {
       .hook = UF_forward_entry,
       .owner = THIS_MODULE,
       .pf = NFPROTO_IPV4,
       .hooknum = NF_INET_POST_ROUTING,
       .priority = NF_IP_PRI_FIRST,
   }, 
};
int UF_NF_hook(void) // 接上文
{
   printk(KERN_EMERG"Register Netfilter Hookrn");
   nf_register_hooks(UF_NF_hookfunc, ARRAY_SIZE(UF_NF_hookfunc)); // 注册netfilter处理函数
   return 0;
}

UF_forward_entry
// 这块代码很简单,仅实现了简易的URL过滤功能,有兴趣可以自行补充其他功能。

三、命令行与内核交互
(1)内核部分

UF_setsyscall功能:改写系统223号中断,作为模块用户态和内核态的通信通道。
/* setup __NR_urlfilter   223*/
int UF_setsyscall(void)
{   
    entry = get_sys_call_table();
    if (NULL == entry)
    {
        printk(KERN_EMERG"get_sys_call_table failrn");
        return -1;
    }
    urlfilter_old = (unsigned long *)(entry[__NR_urlfilter]);    
    disable_write_protection();
    entry[__NR_urlfilter] = (unsigned long *)UF_syscall; // 用户态触发223中断,UF_syscall会被执行
    enable_write_protection();    
    return 0;
}

/* UF_syscall */
// 中断函数的入数个数可以自定义,用户态调用时保持一致即可。
// 需要注意的是,内核态和用户态的指针不能直接用,需要用copy_from_user和copy_to_user进行转换。

asmlinkage long UF_syscall(unsigned int optcode,
                           void *input, unsigned int inputlen, 
                           void *output, unsigned int outputlen
) //中断函数的入参
{
    // ...
   return ret;
}
(2)用户态部分
syscall(__NR_urlfilter, opt, input, inputlen, output, outputlen);
// __NR_urlfilter 为中断号
// opt, input, inputlen, output, outputlen 为中断函数的入参,见上文

2020.5.27 合肥

04-24 22:17