2010年3月22日 星期一

Linux Kernel(12)- netfilter


netfilter是一個packet mangling的framework,主要在protocol stack中提供一些hook point(NF_IP_PRE_ROUTING、NF_IP_LOCAL_IN、NF_IP_FORWARD、NF_IP_POST_ROUTING和NF_IP_LOCAL_OUT),讓user可以在這些hook point上註冊並且執行一些hook function,根據hook function傳回來的數值來決定是否要丟棄(NF_DROP)、pass(NF_ACCEPT)、或者queue(NF_QUEUE)等等。



NF_ACCEPT︰ continue traversal as normal.
NF_DROP︰ drop the packet; don't continue traversal.
NF_STOLEN: I've take over the packet; don't continue traversal.
NF_QUEUE︰ queue the packet.
NF_REPEAT︰ call this hook again.

struct nf_hook_ops
{
    struct list_head list;

    /* User fills in from here down. */
    nf_hookfn *hook;
    struct module *owner;
    u_int8_t pf;
    unsigned int hooknum;
    /* Hooks are ordered in ascending priority. */
    int priority;
};
pf是protocol family,目前有NFPROTO_UNSPEC、NFPROTO_IPV4、NFPROTO_ARP、NFPROTO_BRIDGE、NFPROTO_IPV6和NFPROTO_DECNET等等。這些值也等同sock的protocol family。
hooknum則是填入hook point num,netfilter是一個framework,會在很多地方設立hook point,而我們可以使用nf_register_hook()/nf_register_hooks()將我們的hook function掛在這些點上,以IPv4來說(上面的圖示),就提供了五個hook point,包含:
HookCalled
NF_IP_PRE_ROUTINGAfter sanity checks, before routing decisions.
NF_IP_LOCAL_INAfter routing decisions if packet is for this host.
NF_IP_FORWARDIf the packet is destined for another interface.
NF_IP_LOCAL_OUTFor packets coming from local processes on their way out.
NF_IP_POST_ROUTINGJust before outbound packets "hit the wire".

當然有了nf_register_hook()/nf_register_hooks()提供註冊,也會提供nf_unregister_hook()/nf_unregister_hooks()做unregister。
以下就是一個分別在IPv4的五個hook點上,印出saddr和daddr。
#include <linux/init.h>
#include <linux/module.h>
#include <linux/ip.h>
#include <linux/netfilter.h>
#include <linux/netfilter_ipv4.h>

MODULE_LICENSE("GPL");

inline void dumpIpHdr(const char *fn, const struct sk_buff *skb)
{
    const struct iphdr *ip = ip_hdr(skb);

    printk("%s, saddr:%pI4, daddr:%pI4\n", fn, &ip->saddr, &ip->daddr);
}

static unsigned int
prerouting(unsigned int hook, struct sk_buff *skb,
        const struct net_device *in, const struct net_device *out,
        int (*okfn)(struct sk_buff*))
{
    dumpIpHdr(__FUNCTION__, skb);
    return NF_ACCEPT;
}

static unsigned int
localin(unsigned int hook, struct sk_buff *skb,
        const struct net_device *in, const struct net_device *out,
        int (*okfn)(struct sk_buff*))
{
    dumpIpHdr(__FUNCTION__, skb);
    return NF_ACCEPT;
}

static unsigned int
localout(unsigned int hook, struct sk_buff *skb,
        const struct net_device *in, const struct net_device *out,
        int (*okfn)(struct sk_buff*))
{
    dumpIpHdr(__FUNCTION__, skb);
    return NF_ACCEPT;
}

static unsigned int
postrouting(unsigned int hook, struct sk_buff *skb,
        const struct net_device *in, const struct net_device *out,
        int (*okfn)(struct sk_buff*))
{
    dumpIpHdr(__FUNCTION__, skb);
    return NF_ACCEPT;
}

static unsigned int
fwding(unsigned int hook, struct sk_buff *skb,
        const struct net_device *in, const struct net_device *out,
        int (*okfn)(struct sk_buff*))
{
    dumpIpHdr(__FUNCTION__, skb);
    return NF_ACCEPT;
}

static struct nf_hook_ops brook_ops[] __read_mostly = {
    {
        .hook = prerouting,
        .pf = PF_INET,
        .hooknum = NF_INET_PRE_ROUTING,
        .priority = NF_IP_PRI_RAW,
        .owner = THIS_MODULE,
    }, {
        .hook = localin,
        .pf = PF_INET,
        .hooknum = NF_INET_LOCAL_IN,
        .priority = NF_IP_PRI_RAW,
        .owner = THIS_MODULE,
    }, {
        .hook = fwding,
        .pf = PF_INET,
        .hooknum = NF_INET_FORWARD,
        .priority = NF_IP_PRI_RAW,
        .owner = THIS_MODULE,
    }, {
        .hook = localout,
        .pf = PF_INET,
        .hooknum = NF_INET_LOCAL_OUT,
        .priority = NF_IP_PRI_RAW,
        .owner = THIS_MODULE,
    }, {
        .hook = postrouting,
        .pf = PF_INET,
        .hooknum = NF_INET_POST_ROUTING,
        .priority = NF_IP_PRI_RAW,
        .owner = THIS_MODULE,
    },
};

static int __init init_modules(void)
{
    if (nf_register_hooks(brook_ops, ARRAY_SIZE(brook_ops)) < 0) {
        printk("nf_register_hook failed\n");
    }
    return 0;
}

static void __exit exit_modules(void)
{
    nf_unregister_hooks(brook_ops, ARRAY_SIZE(brook_ops));
}

module_init(init_modules);
module_exit(exit_modules);



以下這張是更為清楚的netfilter packet flow:


2010年2月27日 星期六

JavaScript is not only toy


現在在做Web的時候幾乎脫離不了使用JavaScript,但是超有彈性的JavaScript卻常常帶給人誤解,以至於把它當成一個二三流的programming language,舉幾個簡單的例子:

如果網頁中有多媒體的物件,需要有播放和停止撥放兩個功能,常看到的寫法是:
function MediaPlay() {
    ...
}

function MediaStop() {
    ...
}

其實JavaScript是個OO的language,可以寫得更OO,達到更多的reuse的(這是另一個話題了),我們可以改得更OO一點:
function Media() {
 ...
}

Media.prototype = {
   play: function() {
      ...
   },
   stop: function() {
      ...
   }
};

不同於C++/Java的class-based繼承,JavaScript使用的是prototype-based的繼承,相信大家如果多拜讀一些大師的著作,相信你會發現JavaScript會是一個讓人誤解很深,而且被低估的語言,不過隨著Web應用的多元化,JavaScript會漸漸顯露頭角的。
在學習JavaScript的過程中,還有讓人替他抱屈的地方,就是memory leak的問題了,在寫C大家都知道要free,可是在JavaScript中卻很少人在做歸還memory的事情,望著browser的memory usage逐步上升,然後等他沒有回應,於似乎就是關閉browser,然後怪罪browser或OS不穩,卻很少人去怪罪JavaScript的developer,不過如果是身為一個JavaScript developer,不論是否有人幫你背黑鍋(IE很常背阿,哈哈哈哈),都應該要好好了解JavaScript再來coding,不要低估JavaScript了。


2010年1月31日 星期日

Linux Kernel(11.1)- sysfs and hotplug


Linux提供兩種非同步的hotplug機制通知user-space裝置狀態的改變,一是usermode helper,另一個則是透過netlink。

usermode helper
每當kernel收到hotplug event便會執行"CONFIG_UEVENT_HELPER_PATH"(預設值是/sbin/hotplug,可以透過修改/proc/sys/kernel/hotplug修改預設值),在embedded system中,常用的是busybox,所以,通常會把UEVENT HELPER換成/sbin/mdev(找時間來寫寫busybox),執行UEVENT HELPER會攜帶一些環境變數,如ACTION/DEVPATH/SUBSYSTEM/HOME/PATH等等,透過這些環境變數,可以取得Device Name,MAJOR/MINOR number等等,我們先來看部份的程式碼,往後有機會在作深度研究。
int kobject_uevent_env(struct kobject *kobj, enum kobject_action action,
         char *envp_ext[])
{
    ... 略...
    /* call uevent_helper, usually only enabled during early boot */
    if (uevent_helper[0]) {
        char *argv [3];

        argv [0] = uevent_helper;
        argv [1] = (char *)subsystem;
        argv [2] = NULL;
        retval = add_uevent_var(env, "HOME=/");
        if (retval)
            goto exit;
        retval = add_uevent_var(env,
                        "PATH=/sbin:/bin:/usr/sbin:/usr/bin");
        if (retval)
            goto exit;

        retval = call_usermodehelper(argv[0], argv,
                        env->envp, UMH_WAIT_EXEC);
    }
    ... 略...
}

我利用一個shell script來取代UEVENT HELPER,主要目的是要印出有哪些環境變數,和傳遞哪些參數給UEVENT HELPER。
#!/bin/sh
env >> /helper.log
echo "CMD:" $@ >> /helper.log
echo "----------     end     ----------" >> /helper.log



netlink
另外一條路徑就是netlink了,基本上也是傳遞相同的資訊。
int kobject_uevent_env(struct kobject *kobj, enum kobject_action action,
         char *envp_ext[])
{
    ... 略...
#if defined(CONFIG_NET)
    /* send netlink message */
    if (uevent_sock) {
        struct sk_buff *skb;
        size_t len;

        /* allocate message with the maximum possible size */
        len = strlen(action_string) + strlen(devpath) + 2;
        skb = alloc_skb(len + env->buflen, GFP_KERNEL);
        if (skb) {
            char *scratch;

            /* add header */
            scratch = skb_put(skb, len);
            sprintf(scratch, "%s@%s", action_string, devpath);

            /* copy keys to our continuous event payload buffer */
            for (i = 0; i < env->envp_idx; i++) {
                len = strlen(env->envp[i]) + 1;
                scratch = skb_put(skb, len);
                strcpy(scratch, env->envp[i]);
            }

            NETLINK_CB(skb).dst_group = 1;
            retval = netlink_broadcast(uevent_sock, skb, 0, 1,
                            GFP_KERNEL);
            /* ENOBUFS should be handled in userspace */
            if (retval == -ENOBUFS) {
                retval = 0;
            }
        } else {
            retval = -ENOMEM;
        }
    }
#endif
    ... 略...
}

user-space的code如下:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include <sys/poll.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <unistd.h>

#include <linux/types.h>
#include <linux/netlink.h>

int main(int argc, char *argv[])
{
    struct sockaddr_nl nls;
    struct pollfd pfd;
    char buf[512];

    memset(&nls, 0, sizeof(nls));
    nls.nl_family = AF_NETLINK;
    nls.nl_pid = getpid();
    nls.nl_groups = -1;

    pfd.events = POLLIN;
    pfd.fd = socket(PF_NETLINK, SOCK_DGRAM, NETLINK_KOBJECT_UEVENT);
    if (pfd.fd == -1) {
        printf("Not root\n");
        exit(1);
    }

    if (bind(pfd.fd, (void*)&nls, sizeof(nls))) {
        printf("bind failed\n");
        exit(1);
    }
    while (-1 != poll(&pfd, 1, -1)) {
        int i, len = recv(pfd.fd, buf, sizeof(buf), MSG_DONTWAIT);
        if (len == -1) {
            printf("recv\n");
            exit(1);
        }
        i = 0;
        while (i < len) {
            printf("%s\n", buf + i);
            i += strlen(buf+i) + 1;
        }
    }
    printf("\n");
    return 0;
}




熱門文章