ip_route_out route ip_rcv_finis

IP層路由適配(IP route)

  路由表以及規則組成的系統,可以完成路由的管理以及查找的工作,但是爲了使得IP層的路由工作更加的高效,linux的路由體系裏,route.c裏完成大多數IP層與RPDB的適配工作,以及路由緩衝(route cache)的功能。

  調用接口

  IP層的路由接口分爲發送路由接口以及接收路由接口:

  發送路由接口

  IP層在發送數據時如果需要進行路由工作的時候,就會調用ip_route_out函數。這個函數在完成一些鍵值的簡單轉換以後,就會調用ip_route_output_key函數,這個函數首先在緩存裏尋找路由,如果失敗就會調用ip_route_output_slow,ip_route_output_slow裏調用fib_lookup在路由表裏尋找路由,如果命中,首先在緩存裏添加這個路由,然後返回結果。

ip_route_out route.h
ip_route_output_key route.c 1984;
ip_route_output_slow route.c 1690;"

  接收路由接口

  IP層接到一個數據包以後,如果需要進行路由,就調用函數ip_route_input,ip_route_input現在緩存裏尋找,如果失敗則ip_route_inpu調用ip_route_input_slow, ip_route_input_slow裏調用fib_lookup在路由表裏尋找路由,如果命中,首先在緩存裏添加這個路由,然後返回結果。

ip_route_input_slow route.c 1312;" f
ip_route_input route.c 1622;" f

  cache

  路由緩存保存的是最近使用的路由。當IP在路由表進行路由以後,如果命中就會在路由緩存裏增加該路由。同時系統還會定時檢查路由緩存裏的項目是否失效,如果失效則清除。


/*
 * 接收完數據包後的後續處理函數
 */
static int ip_rcv_finish(struct sk_buff *skb)
{
	const struct iphdr *iph = ip_hdr(skb);
	struct rtable *rt;

	 /*
	  * 激活ip_route_input,確定報文的路由,如果ip_route_input無法從FIB中找到路由
	  * 則丟棄數據報<strong>文,ip_route_input將在IP路由中的專題中進行講解</strong>
	  */
	<strong><span style="color:#ff0000;">if (skb_dst(skb) == NULL) {</span>
		int err = ip_route_input(skb, iph->daddr, iph->saddr, iph->tos, skb->dev);
		if (unlikely(err)) {</strong>
			goto drop;
		}
	}

	/*檢查IP報頭裏面是否含有選項,如果含有建立ip_options*/
	if (iph->ihl > 5 && ip_rcv_options(skb))
		goto drop;

	<strong><span style="color:#ff0000;">/*根據dst_entry的結果,使用skb_dst(skb)->input(skb)進行IP的路由選擇
	 *傳遞給本地計算機的單播或多播,進入 ip_local_deliver();
	 *單播轉發的報文進入ip_forward()
	 *多播轉發進入ip_mr_input()
	 */</span></strong>
	return dst_input(skb);
	{
		skb_dst(skb)->input(skb)
	}

drop:
	kfree_skb(skb);
	return NET_RX_DROP;
}



#include <linux/ip.h>
#include <linux/module.h>
#include <linux/skbuff.h>
#include <linux/version.h>
#include <net/netfilter/nf_conntrack.h>
#include <net/dst.h>
#include <net/netfilter/nf_conntrack_acct.h>


MODULE_AUTHOR("xtt");
MODULE_DESCRIPTION("gll");
MODULE_LICENSE("GPL");
MODULE_ALIAS("XTT and GLL");

struct nf_conn_priv {
        struct nf_conn_counter ncc[IP_CT_DIR_MAX];
        struct dst_entry *dst[IP_CT_DIR_MAX];
};

static unsigned int ipv4_conntrack_getdst (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;
        struct nf_conn_counter *acct;
        struct nf_conn_priv *dst_info;
        ct = nf_ct_get(skb, &ctinfo);
        if (!ct || ct == &nf_conntrack_untracked)
                return NF_ACCEPT;
        acct = nf_conn_acct_find(ct);
        if (acct) {
                int dir = CTINFO2DIR(ctinfo);
                dst_info = (struct nf_conn_priv *)acct;
                if (dst_info->dst[dir] == NULL) {
                        dst_hold(skb_dst(skb));
                        dst_info->dst[dir] = skb_dst(skb);
                }
        }
        return NF_ACCEPT;
}

static unsigned int ipv4_conntrack_setdst (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;
        struct nf_conn_counter *acct;
        struct nf_conn_priv *dst_info;
        ct = nf_ct_get(skb, &ctinfo);
        if (!ct || ct == &nf_conntrack_untracked)
                return NF_ACCEPT;
        acct = nf_conn_acct_find(ct);
        if (acct) {
                int dir = CTINFO2DIR(ctinfo);
                dst_info = (struct nf_conn_priv *)acct;
                if (dst_info->dst[dir] != NULL) {
                      // 如果在此設置了skb的dst,那麼在ip_rcv_finish中就不會再去查找路由表了
                        skb_dst_set(skb, dst_info->dst[dir]);

                }
        }
        return NF_ACCEPT;
}
static struct nf_hook_ops ipv4_conn_dst_info[] __read_mostly = {
        {
                .hook          = ipv4_conntrack_getdst,
                .owner          = THIS_MODULE,
                .pf            = NFPROTO_IPV4,
                .hooknum        = NF_INET_POST_ROUTING,
                .priority      = NF_IP_PRI_CONNTRACK + 1,
        },
        {
                .hook          = ipv4_conntrack_getdst,
                .owner          = THIS_MODULE,
                .pf            = NFPROTO_IPV4,
                .hooknum        = NF_INET_LOCAL_IN,
                .priority      = NF_IP_PRI_CONNTRACK + 1,
        },
        {
                .hook          = ipv4_conntrack_setdst,
                .owner          = THIS_MODULE,
                .pf            = NFPROTO_IPV4,
                .hooknum        = NF_INET_PRE_ROUTING,
                .priority      = NF_IP_PRI_CONNTRACK + 1,
        },
};

static int __init test_info_init(void)
{
        int err;
        err = nf_register_hooks(ipv4_conn_dst_info, ARRAY_SIZE(ipv4_conn_dst_info));
        if (err) {
                return err;
        }
        return err;
}

static void __exit test_info_exit(void)
{
        nf_unregister_hooks(ipv4_conn_dst_info, ARRAY_SIZE(ipv4_conn_dst_info));
}

module_init(test_info_init);
module_exit(test_info_exit);


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章