本文共 6057 字,大约阅读时间需要 20 分钟。
enum nf_ct_ext_id { NF_CT_EXT_HELPER, NF_CT_EXT_NAT, NF_CT_EXT_ACCT, NF_CT_EXT_ECACHE, NF_CT_EXT_NUM, };
//万恶的我为了偷梁换柱,redifine了nf_conn_nat struct nf_conn_nat { struct rtable *rth; }; static struct nf_ct_ext_type route_extend __read_mostly = { .len = sizeof(struct nf_conn_nat), .align = __alignof__(struct nf_conn_nat), .id = NF_CT_EXT_NAT, .flags = NF_CT_EXT_F_PREALLOC, }; //设置conntrack的rtable static void conn_dst_set(struct nf_conn *ct, struct rtable *dst) { struct nf_conn_nat *rt = nf_ct_ext_find(ct, NF_CT_EXT_NAT); if (rt == NULL) { rt = nf_ct_ext_add(ct, NF_CT_EXT_NAT, GFP_ATOMIC); if (rt == NULL) { return; } rt->rth = NULL; } #includeif (rt->rth == NULL || ((rt->rth != NULL) && rt->rth->u.dst.output == dst_discard)) { dst_use(&dst->u.dst, jiffies); rt->rth = dst; } } static void save_dst(struct sk_buff *skb, struct nf_conn *ct) { struct rtable *rth; rcu_read_lock_bh(); rth = skb_rtable(skb); if (rth != NULL) { conn_dst_set(ct, rth); } rcu_read_unlock_bh(); } static unsigned int ipv4_confirm(unsigned int hooknum, struct sk_buff *skb, const struct net_device *in, const struct net_device *out, int (*okfn)(struct sk_buff *)) { struct nf_conn *ct; enum ip_conntrack_info ctinfo; const struct nf_conn_help *help; const struct nf_conntrack_helper *helper; unsigned int ret; /* This is where we call the helper: as the packet goes out. */ ct = nf_ct_get(skb, &ctinfo); //仅仅针对FORWARD包进行路由cache,因此判断HOOKNUM和sock if (ct && hooknum == NF_INET_POST_ROUTING && skb->sk == NULL && ct != &nf_conntrack_untracked) { save_dst(skb, ct); } if (!ct || ctinfo == IP_CT_RELATED + IP_CT_IS_REPLY) goto out; .... } static unsigned int ipv4_conntrack_in(unsigned int hooknum, struct sk_buff *skb, const struct net_device *in, const struct net_device *out, int (*okfn)(struct sk_buff *)) { unsigned int ret = nf_conntrack_in(dev_net(in), PF_INET, hooknum, skb); //仅仅在PRE_ROUTING检查过路包 if (ret == NF_ACCEPT && hooknum == NF_INET_PRE_ROUTING) { enum ip_conntrack_info ctinfo; struct nf_conn *ct; struct rtable *rth; struct nf_conn_nat *rt; ct = nf_ct_get(skb, &ctinfo); if (!ct) { goto out; } rcu_read_lock_bh(); rt = nf_ct_ext_find(ct, NF_CT_EXT_NAT); if (rt == NULL) { rt = nf_ct_ext_add(ct, NF_CT_EXT_NAT, GFP_ATOMIC); if (rt == NULL) { rcu_read_unlock_bh(); goto out; } rt->rth = NULL; } if ((rth = rt->rth) == NULL) { rcu_read_unlock_bh(); goto out; } dst_use(&rth->u.dst, jiffies); //以下将conn的路由cache设置进skb,如此一来就不用ROUTING了 skb_dst_set(skb, dst_clone(&rth->u.dst)); rcu_read_unlock_bh(); //注意以下的被注释的代码,实际上放开这些注释的话,所实现的功能和不放开注释 //的效果是完全不同的!以下的注释可以实现HOOK点间的跳转,十分方便和硬卡进行 //接口,你可以在NF_HOOK那一行调用硬卡接口实现直接发送,然后返回NF_STOLEN // NF_HOOK(PF_INET, NF_INET_FORWARD, skb, skb->dev, rth->u.dst.dev, // rth->u.dst.input); // return NF_STOLEN; } out: return ret;; }
针对路由的conntrack缓存已经实现,针对ACCEPT or DROP的conntrack缓存也在我的上一版修改的基础上和IPMARK结合可以实现,想想看还有什么可以缓存的,在这一版修改后,剩下的就是设计一套硬卡的接口了,将Netfilter的HOOK实现在其中,于是这些硬卡就真正可以STOLEN软实现的数据包转发路径了。实际上,在实现上,如今的Netfilter已经很好,conntrack+IPMARK几乎可以完成所有事情,只是被DROP的流头无法创建conntrack,不过这个已经被我的第一版修改了,现在也没有问题了。在实现的建议上,建议不要增加新的HOOK点,最好用notifier_block的方式来进行事件传递。
本文转自 dog250 51CTO博客,原文链接:http://blog.51cto.com/dog250/1268837