2010年8月30日 星期一

trace netfilter conntrack nf_conntrack_hash


For some purpose, I need to reduce netfilter connect track time as 1 to expire ASAP.
First all, I need to know how to list /proc/net/ip_conntrack.

在2.6.x早期還是用nf_conntrack_hash為bucket的hash list,所以還看得到如下的global varibles

net/netfilter/nf_conntrack_core.c
struct hlist_head *nf_conntrack_hash __read_mostly;
EXPORT_SYMBOL_GPL(nf_conntrack_hash);
struct nf_conn nf_conntrack_untracked __read_mostly;
EXPORT_SYMBOL_GPL(nf_conntrack_untracked);
新的netfilter會將ipv4和ipv6結合起來, 且因為新的net_namespace架構始得netfilter的connect track有一些變化。
namespace的架構主要是因為linux kernel要support virtual machine,好處是security及virtualize
,但程式碼變得更難理解,(我看不太懂),且上面的這些原本是global varibles也hidden到net_namespace裡了…

anyway,I just want to resolve my problem in project: I need to reset connect expire time in traced sessions.(kernel 2.6.28x)
First, I trace the module: nf_conntrack_ipv4.ko, when module initical, it will create /proc/net/ip_conntrack and /proc/net/nf_conntrack
nf_conntrack_l3proto_ipv4_compat.c
static int __net_init ip_conntrack_net_init(struct net *net)
{
        struct proc_dir_entry *proc, *proc_exp, *proc_stat;

        proc = proc_net_fops_create(net, "ip_conntrack", 0440, &ct_file_ops);
        if (!proc)
                goto err1;

        proc_exp = proc_net_fops_create(net, "ip_conntrack_expect", 0440,
                                        &ip_exp_file_ops);
        if (!proc_exp)
                goto err2;

        proc_stat = proc_create("ip_conntrack", S_IRUGO,
                                net->proc_net_stat, &ct_cpu_seq_fops);


nf_conntrack_l3proto_ipv4_compat.c
static const struct file_operations ct_file_ops = {
        .owner   = THIS_MODULE,
        .open    = ct_open,
        .read    = seq_read,
        .llseek  = seq_lseek,
        .release = seq_release_net,
};


nf_conntrack_l3proto_ipv4_compat.c

static int ct_open(struct inode *inode, struct file *file)
{
        return seq_open_net(inode, file, &ct_seq_ops,
                            sizeof(struct ct_iter_state));//private data stored in seq_xxx
}


nf_conntrack_l3proto_ipv4_compat.c

static const struct seq_operations ct_seq_ops = {
        .start = ct_seq_start,
        .next  = ct_seq_next,
        .stop  = ct_seq_stop,
        .show  = ct_seq_show
};
fs/proc/proc_net.c
int seq_open_net(struct inode *ino, struct file *f,
                 const struct seq_operations *ops, int size)
{
        struct net *net;
        struct seq_net_private *p;

        BUG_ON(size < sizeof(*p));

        net = get_proc_net(ino);
        if (net == NULL)
                return -ENXIO;

        p = __seq_open_private(f, ops, size);
        if (p == NULL) {
                put_net(net);
                return -ENOMEM;
        }
#ifdef CONFIG_NET_NS
        p->net = net;
#endif
        return 0;
}
EXPORT_SYMBOL_GPL(seq_open_net);
Finally, the ct_seq_show dispaly each of session.

static int ct_seq_show(struct seq_file *s, void *v)
{
        const struct nf_conntrack_tuple_hash *hash = v;
        const struct nf_conn *ct = nf_ct_tuplehash_to_ctrack(hash);
        const struct nf_conntrack_l3proto *l3proto;
        const struct nf_conntrack_l4proto *l4proto;

        NF_CT_ASSERT(ct);

        /* we only want to print DIR_ORIGINAL */
        if (NF_CT_DIRECTION(hash))
                return 0;
        if (nf_ct_l3num(ct) != AF_INET)
                return 0;

        l3proto = __nf_ct_l3proto_find(nf_ct_l3num(ct));
        NF_CT_ASSERT(l3proto);
        l4proto = __nf_ct_l4proto_find(nf_ct_l3num(ct), nf_ct_protonum(ct));
        NF_CT_ASSERT(l4proto);
Now!, I try to modify expire time of each session.by mod_timer, eg:
 mod_timer(&ct->timeout, jiffies + HZ/2); /* update expire as 0.5sec. */
after recompiled mdoules, just install it
#insmod nf_conntrack_ipv4.ko
then read again to run mod_timer for each sessions
#cat /proc/net/ip_conntrack
Great!, all sessions  will be deleted after 0.5 sec


--------------------------------------------------------------------------

Extra topic from this case:

net/core/net_namespace.c
int register_pernet_subsys(struct pernet_operations *ops)
{
        int error;
        mutex_lock(&net_mutex);
        error =  register_pernet_operations(first_device, ops);
        mutex_unlock(&net_mutex);
        return error;
}
EXPORT_SYMBOL_GPL(register_pernet_subsys);

include/net/net_namespace.h
struct pernet_operations {
        struct list_head list;
        int (*init)(struct net *net);
        void (*exit)(struct net *net);
};
register_pernet_operations: will call method init from each of net_namespace which regiestered.
But, where or how to get hash bucket by net namesapce....?

-----------------------------------
Used functions in this case
/include/linux/moduleparam.h
#define module_param_call(name, set, get, arg, perm)                          \ __module_param_call(MODULE_PARAM_PREFIX, name, set, get, arg, perm)
rcu_dereference: include/linux/rcupdate.h
#define rcu_dereference(p)     ({ \
                                typeof(p) _________p1 = ACCESS_ONCE(p); \
                                smp_read_barrier_depends(); \
                                (_________p1); \
                                })
http://rd-life.blogspot.com/2009/05/rcu_26.html
http://lxr.linux.no/#linux+v2.6.28/Documentation/RCU/whatisRCU.txt#L122


沒有留言: