- unsigned int
-
nf_conntrack_in(struct net *net, u_int8_t pf, unsigned int hooknum,
-
struct sk_buff *skb)
-
{
-
struct nf_conn *ct, *tmpl = NULL;
-
enum ip_conntrack_info ctinfo;
-
struct nf_conntrack_l3proto *l3proto;
-
struct nf_conntrack_l4proto *l4proto;
-
unsigned int dataoff;
-
u_int8_t protonum;
-
int set_reply = 0;
-
int ret;
/*
这个条件判断不是很明白。为什么可以直接忽略以前的nfct。
看了后面的代码后,skb->nfcth会在后面的代码中重新得到,再配合这里的注释大概明白了用意。
当skb->nfct为有效值时,即意味着该skb已经经过了conn track,再次落到conn track时。
如注释所说,比如发往回环的时候,会有这个情况。
*/
-
if (skb->nfct) {
-
/* Previously seen (loopback or untracked)? Ignore. */
-
tmpl = (struct nf_conn *)skb->nfct;
-
if (!nf_ct_is_template(tmpl)) {
-
NF_CT_STAT_INC_ATOMIC(net, ignore);
-
return NF_ACCEPT;
-
}
-
skb->nfct = NULL;
-
}
-
-
/* rcu_read_lock()ed by nf_hook_slow */
- /*
- 根据proto family,来得到l3的conn track的target。
- 这里对于netfilter来说,它将conn track的具体工作下发到具体的L3层的target处理。
- 这样无论是ipv4还是ipv6,netfilter的处理可以保持一致。甚至其可以支持更多的L3协议。
对于ipv4来说,l3proto就是nf_conntrack_l3proto_ipv4。
- */
-
l3proto = __nf_ct_l3proto_find(pf);
- /*
- 将L3的报文头的偏移即数据包skb传给l3->get_l4proto来得到L4的协议号即L4的起始位置。
- 这时L3的报文头完全由具体的L3层的proto负责解析
- */
-
ret = l3proto->get_l4proto(skb, skb_network_offset(skb),
-
&dataoff, &protonum);
-
if (ret <= 0) {
-
pr_debug("not prepared to track yet or error occured\n");
-
NF_CT_STAT_INC_ATOMIC(net, error);
-
NF_CT_STAT_INC_ATOMIC(net, invalid);
-
ret = -ret;
-
goto out;
-
}
/*
与L3类似,同样是通过L4的协议号得到具体的L4 proto的target。
实现与具体协议的解耦。
如nf_conntrack_l4proto_tcp4, nf_conntrack_l4proto_udp4等等
*/
-
l4proto = __nf_ct_l4proto_find(pf, protonum);
-
-
/* It may be an special packet, error, unclean...
-
* inverse of the return code tells to the netfilter
-
* core what to do with the packet. */
-
if (l4proto->error != NULL) {
- /* 对L4数据包进行检查,由具体的L4协议负责 */
-
ret = l4proto->error(net, tmpl, skb, dataoff, &ctinfo,
-
pf, hooknum);
-
if (ret <= 0) {
-
NF_CT_STAT_INC_ATOMIC(net, error);
-
NF_CT_STAT_INC_ATOMIC(net, invalid);
-
ret = -ret;
-
goto out;
-
}
-
}
/* 根据L3和L4层的信息,得到conn track和其信息。后面会学习resolve_normal_ct */
-
ct = resolve_normal_ct(net, tmpl, skb, dataoff, pf, protonum,
-
l3proto, l4proto, &set_reply, &ctinfo);
-
if (!ct) {
-
/* Not valid part of a connection */
-
NF_CT_STAT_INC_ATOMIC(net, invalid);
-
ret = NF_ACCEPT;
-
goto out;
-
}
-
-
if (IS_ERR(ct)) {
-
/* Too stressed to deal. */
-
NF_CT_STAT_INC_ATOMIC(net, drop);
-
ret = NF_DROP;
-
goto out;
-
}
-
-
NF_CT_ASSERT(skb->nfct);
/*
将数据包传递给具体的L4 target进行特定的操作。
如nf_conntrack_l4proto_udp4,会update连接conn track的age,保证不ageout
对于nf_conntrack_l4proto_tcp4,会有更复杂的操作。
*/
-
ret = l4proto->packet(ct, skb, dataoff, ctinfo, pf, hooknum);
-
if (ret <= 0) {
-
/* Invalid: inverse of the return code tells
-
* the netfilter core what to do */
-
pr_debug("nf_conntrack_in: Can't track with proto module\n");
-
nf_conntrack_put(skb->nfct);
-
skb->nfct = NULL;
-
NF_CT_STAT_INC_ATOMIC(net, invalid);
-
if (ret == -NF_DROP)
-
NF_CT_STAT_INC_ATOMIC(net, drop);
-
ret = -ret;
-
goto out;
-
}
/* 设置conn track的事件,不明白这里为什么叫做cache */
-
if (set_reply && !test_and_set_bit(IPS_SEEN_REPLY_BIT, &ct->status))
-
nf_conntrack_event_cache(IPCT_REPLY, ct);
-
out:
-
if (tmpl)
-
nf_ct_put(tmpl);
-
-
return ret;
-
}