解讀Netfilter示例程序

代碼路徑
git訪問方式:https://gitee.com/yuewguo/url_filter.git
svn訪問方式:svn://gitee.com/yuewguo/url_filtergit

1、自定義內核模塊
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);框架

2、Netfilter處理函數
netfilter處理框架以下圖(轉自網上):
須要注意的是,netfilter對外提供的hook點僅針對正向收包,應答報文不通過hook的業務點。
若是須要在應答流程中添加hook點須要自行修改netfilter代碼。

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過濾功能,有興趣能夠自行補充其餘功能。
svn

3、命令行與內核交互
(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 合肥
url