顯示具有 Netfilter 標籤的文章。 顯示所有文章
顯示具有 Netfilter 標籤的文章。 顯示所有文章

2011年1月27日 星期四

nf_sockopt: A Mechanism for IPC between The Kernel And User Space

今天trace ebtables如何把rule下到kernel的過程中, 學到了一種kernel/user space溝通的方式:nf_sockopt。

nf_sockopt的使用相當簡單:

1. 定義get/set options
#ifndef __MY_SOCKOPT_H__
#define __MY_SOCKOPT_H__

/* {g,s}etsockopt numbers */
#define MY_BASE_CTL            999

enum
{
    MY_SO_SET_CMD1 = MY_BASE_CTL,
    MY_SO_SET_CMD2,
    MY_SO_SET_MAX,
}MY_SO_SET;

enum
{
    MY_SO_GET_CMD1 = MY_BASE_CTL,
    MY_SO_GET_CMD2,
    MY_SO_GET_CMD3,
    MY_SO_GET_MAX
}MY_SO_GET;

#endif
2. 在kernel space註冊要handle的options
#include <linux/module.h>
#include <net/sock.h>
#include <linux/netfilter.h>
#include "my_sockopt.h"

static int do_my_set_ctl(struct sock *sk, int cmd, void __user *user, unsigned int len)
{
    printk("Get from user space [%s]\n", (char*)user);
   
    return 0;
}

static int do_my_get_ctl(struct sock *sk, int cmd, void __user *user, int *len)
{
    int ret;
    printk("Send to user space [%d]\n", cmd);
    ret = cmd;
    //copy_to_user(void __user *to, const void *from, unsigned long n)
    ret = copy_to_user(user, &ret, sizeof(ret));
   
    return ret;
}

static struct nf_sockopt_ops my_sockopts =
{
    .pf         = PF_INET,
    .set_optmin = MY_BASE_CTL,
    .set_optmax = MY_SO_SET_MAX + 1,
    .set        = do_my_set_ctl,
    .get_optmin = MY_BASE_CTL,
    .get_optmax = MY_SO_GET_MAX + 1,
    .get        = do_my_get_ctl,
};

static int __init mysockopt_init(void)
{
    int ret;
   
    if ((ret = nf_register_sockopt(&my_sockopts)) < 0)
    {
        printk("nf_register_sockopt failed [%d]\n", ret);
        return ret;
    }
    return 0;
}

static void __exit mysockopt_fini(void)
{
    nf_unregister_sockopt(&my_sockopts);
}

module_init(mysockopt_init);
module_exit(mysockopt_fini);
MODULE_LICENSE("GPL");
3. 在user space開一個RAW socket, 再利用g/setsockopt透過設定socket的options, 便可以取得/設定kernel space的資訊
#include <getopt.h>
#include <string.h>
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <unistd.h>
#include <netinet/in.h>
#include <sys/types.h>
#include <sys/socket.h>

#include "my_sockopt.h"

int sockfd = -1;

static int get_sockfd()
{
    int ret = 0;
    if (sockfd == -1) {
        sockfd = socket(AF_INET, SOCK_RAW, PF_INET);
        if (sockfd < 0)
        {
            perror("get_sockfd");
            ret = -1;
        }
    }
    return ret;
}

int set_to_kernel()
{
    char buf[64] = "Hello, sockopt!!!";

    if (get_sockfd())
        return -1;
       
    if (!setsockopt(sockfd, IPPROTO_IP, MY_SO_SET_CMD1, buf, sizeof(buf)))
        return -1;

    return 0;
}

int get_from_kernel()
{
    int ret = -1;
    int len = 0;

    if (get_sockfd())
        return -1;
       
    if (getsockopt(sockfd, IPPROTO_IP, MY_SO_GET_CMD3, &ret, &len))
        return -1;
   
    printf("Get from kernel [%d]\n", ret);

    return 0;
}

int main()
{
    set_to_kernel();
    get_from_kernel();
    return 0;
}
相較於netlink, nf_sockopt容易許多。兩者間的差異, 比較能夠直接感受到的, 便是netlink可以雙向initiate session; 而nf_sockopt則是由user space去主控, kernel space只是被動地聽命行事。


參考資料:
[1] ebtables.c
[2] communication.c
[3] 使用sockopt与內核交換數据

2011年1月6日 星期四

Using iptables to Implement URL Filter

URL filter是router很常見的功能; 在PC上, 或許可以透過squid來達成, 若是在flash很小的embedded system上, 還不知道有什麼軟體可以實現該功能(squid-lite?!). 因此, 只好先透過iptables來做URL filter

有幾個match可能可以拿來實現URL filter:  destination, string, webstr, weburl

destination
iptables -D FORWARD -d www.google.com.tw -j DROP
在某個版本以後的iptables會幫忙做DNS反查,若該Domain Name對應到多個IP時, iptables會幫你加上所有的rule
$iptables -L FORWARD -v -n
Chain FORWARD (policy ACCEPT 0 packets, 0 bytes)
 pkts bytes target     prot opt in     out     source          destination        
    0     0 DROP       all  --  *      *       0.0.0.0/0       72.14.203.106      
    0     0 DROP       all  --  *      *       0.0.0.0/0       72.14.203.147      
    0     0 DROP       all  --  *      *       0.0.0.0/0       72.14.203.99       
    0     0 DROP       all  --  *      *       0.0.0.0/0       72.14.203.103      
    0     0 DROP       all  --  *      *       0.0.0.0/0       72.14.203.104      
    0     0 DROP       all  --  *      *       0.0.0.0/0       72.14.203.105
但是如果你再試一次的話
$iptables -F FORWARD
$iptables -A FORWARD -d www.google.com.tw -j DROP
$iptables -L FORWARD -v -n
Chain FORWARD (policy ACCEPT 0 packets, 0 bytes)
 pkts bytes target     prot opt in     out     source          destination        
    0     0 DROP       all  --  *      *       0.0.0.0/0       64.233.183.104     
    0     0 DROP       all  --  *      *       0.0.0.0/0       64.233.183.105     
    0     0 DROP       all  --  *      *       0.0.0.0/0       64.233.183.106     
    0     0 DROP       all  --  *      *       0.0.0.0/0       64.233.183.147     
    0     0 DROP       all  --  *      *       0.0.0.0/0       64.233.183.99      
    0     0 DROP       all  --  *      *       0.0.0.0/0       64.233.183.103
IP變了...囧...但用來鎖一般的網站應該還OK...

string
iptables -A FORWARD -m string --string "www.google.com" –algo bm -j DROP
這樣做應該可以成功阻擋連到google的封包, 但是你也會發現, 若是有些網頁的內容含有字串"www.google.com", 那麼那個網頁應該也不能開了, 一個可能的解法, 就是利用string match裡面的--from/--to去限制字串比對的範圍

webstr
iptables -A FORWARD -m webstr --url "www.google.com" -j DROP
xt_webstr.c, 可以大概知道, 他是去抓HTTP Get/Post/Head封包裡帶的URL來作比對
webstr解決string match比對範圍的問題, 多了些HTTP相關條件, 減少誤判的可能性

weburl
URL Filter有時候可能會用regex或某個關鍵字而非完整的URL; 如輸入"yahoo", 是要擋"www.yahoo.com"與"tw.yahoo.com", 這時候就可利用weburl
iptables -A FORWARD -m weburl --contains "yahoo" -j DROP
原始碼可參考Gargoyle


參考資料:
[1] OpenWrt
[2] iptables webstr not blocking https

2010年12月24日 星期五

L7-filter

第一次碰到要去filter一些application的時候, 都慢慢的去找每個application用到哪些port, 再用iptables把那些port擋掉. 但像ftp, telnet之類的, 雖然有預設的port, 但user卻能自己改成自己喜歡的, 這個時候L7 filter就派上用場了.

L7 filter的安裝可參考官網, 步驟為:
1. Patch kernel, 並打開kernel選項
2. 把l7 match加到iptables的extensions裡
3. 下載protocol pattern files

使用的指令:
iptables [-t table -A chain] -m layer7 --l7proto [protocol name] -j [action]
舉例來說, 若要擋yahoo即時通:
iptables -t mangle -A PREROUTING -m layer7 --l7proto yahoo -j DROP
net/netfilter/xt_layer7.c中可看出 (雖然不是很懂), 基本上他的作法就是抓TCP/UDP header後的payload跟protocol pattern file (*.pat) 內的regular expression利用regexec做比對.
在"L7-filter Pattern Writing HOWTO"中, 介紹如何透過regular expression去增加自己的filter. (題外話, 文中也講到regex API在kernel spaceuser space是有點差異的)

拿HTTP的一個封包來看


可以用下列的regex來辨識
http
http/(0\.9|1\.0|1\.1) [1-5][0-9][0-9]
但真的要自己寫出個堪用的pattern還是照L7-filter Pattern Writing HOWTO上的準則去做比較可行.


參考資料:
[1] Application Layer Packet Classifier for Linux
[2] 在 C 程式中,使用 Regex (Regular Expression) library