- 论坛徽章:
- 0
|
以下讨论基于linux-2.6.23.9
1. netfilter的hook遍历函数nf_iterate(注意对NF_REPEAT的处理)
list_for_each_continue_rcu(*i, head) { //#1
struct nf_hook_ops *elem = (struct nf_hook_ops *)*i;
verdict = elem->hook(hook, skb, indev, outdev, okfn); //#2
if (verdict != NF_ACCEPT) {
if (verdict != NF_REPEAT)
return verdict;
*i = (*i)->prev; //#3
}
}
注意hook的遍历是无锁的rcu
2. netfilter的注销hook的逻辑nf_unregister_hook
mutex_lock(&nf_hook_mutex);
list_del_rcu(®->list);
mutex_unlock(&nf_hook_mutex);
synchronize_net();
3. 其中list_del_rcu的实现为
next->prev = prev;
prev->next = next;
entry->prev = LIST_POISON2; //#4
A. 假设当前有3个hook(这里只关注list_head)
a->next = b; b->next = c; c->next = X;
a->prev = X; b->prev = a; c->prev = b;
(list_head是循环指针,出于简化这里用X表示头尾相连)
B. 某个时刻hook-b被撤销,期望的结果为
a->next = c; b->next = c; c->next = X;
a->prev = X; b->prev = 0; c->prev = a;
(为了对齐,用0表示LIST_POSITION2)
C. 在SMP环境下,遍历和注销在两个CPU上并发进行,
即CPU0在遍历hook(a->b->c,当前正遍历到hook-b)
CPU1注销一个hook(而且是可以返回REPEAT的hook-b)
D. 如果CPU1撤销发生在CPU0的#2和#3之间(*i指向b)
通常情况下后续遍历hook的逻辑没有问题(#1走b->next仍然可以达到c,rcu_list的好处)
但是如果此时hook-b返回REPEAT,
则#3引用*i = b->prev为0,#1再走*i->next是不是就有问题了?
CPU0 CPU1
遍历到b 注销b
*i=b a->next=c
执行b->hook c->prev=a
返回NF_REPEAT b->prev=POSITION2
取(*i)->prev
*i=POSITION2
下一轮遍历
*i=*i->next <<<<<< crash
请各位大侠帮忙分析一下以上的情景分析是否合理(可以返回REPEAT的hook被注销)?
没有继续跟踪最新的内核,不知道是否有人提出过类似的问题以及相应的fix?
PS: 返回NF_REPEAT的hook确实是存在的
最常见的可能就是nf_conntrack的hook(ipv4_conntrack_in)对tcp的处理逻辑(tcp_packet)
参见nf_conntrack_proto_tcp.c的注释
/* Attempt to reopen a closed/aborted connection.
* Delete this connection and look up again. */ |
|