1. 程式人生 > >ipv6:linux上發生路由查詢的兩個位置

ipv6:linux上發生路由查詢的兩個位置

什麼時候發生路由查詢?

首先認識路由樹:

具體說來:

根據ip地址來區分參考“IPv6地址型別單播/組播/任播地址:

1If the destination address matchesFE80::<EUI64>//資料包是發給本機,執行一般的收發,對於PC機而言就是走這條。通過dst_entryND協議,發給最近的路由主機。

skb->dst->input=ip6_input

 skb->dst->output=ip6_output---

2Else if the destination address’s first 10bits matches FE80::

//資料包需要轉發到預設閘道器(也就是區域網內的路由器)

skb->dst->input=ip6_forward

skb->dst->output=ip6_output

3Else if the destination address’s first 8bits matches FF00:://資料包廣播

skb->dst->input=ip6_mc_input

skb->dst->output=ip6_output

4Else ( no match)

skb->dst->input=ip6_pkt_discard

skb->dst->output=ip6_pkt_discard

ip字首”對應的”路由節點“樹:


其次:關鍵資料結構dst_entry

最終生成的IP資料報的路由稱為目的入口(dst_entry),目的入口反映了相鄰的外部主機在主機內部的一種“映象”

struct dst_entry {

…..

int                        (*input)(structsk_buff *);//決定該路由樹節點是應該轉發還是接收

int                        (*output)(structsk_buff *);//傳送介面

…...

union{

structdst_entry        *next;

structrtable __rcu        *rt_next;

structrt6_info                *rt6_next;

structdn_route __rcu        *dn_next;

};

};

兩種情況會發生路由查詢:

1)接收資料的時候:ip6_route_input()

就是在主機接收到資料包的時候發生路由查詢,為什麼在接收資料的時候呢?因為一個數據包,需要路由判斷其目的地(

如果目的地是本機,則進入接收的流程;

如果不是本機,則通過forward處理,然後進入傳送流程;

如果都不是,則丟棄.

大概路由查詢的原理是:主機需要通過路由表來判斷資料包的目的地ip6_rcv_finish()裡面,會調ip6_route_input(skb),這個函式返回的是路由表中對應的fib6_node,這個節點的input函式就會根據不同的目的地呼叫不同的函式來處理

2)傳送時候發生的路由:ip6_route_output()

路由查詢的呼叫流程和查詢演算法

1)接收資料的時候:ip6_route_input()

ipv6_rcv()---->ip6_rcv_finish()----ip6_route_input{

voidip6_route_input(struct sk_buff *skb)

{

conststruct ipv6hdr *iph = ipv6_hdr(skb);

structnet *net = dev_net(skb->dev);

intflags = RT6_LOOKUP_F_HAS_SADDR;

structflowi6 fl6 = {

.flowi6_iif= skb->dev->ifindex,

.daddr= iph->daddr,

.saddr= iph->saddr,

.flowlabel= ip6_flowinfo(iph),

.flowi6_mark= skb->mark,

.flowi6_proto= iph->nexthdr,

};

skb_dst_set(skb, ip6_route_input_lookup(net, skb->dev,&fl6, flags));

}

static inline voidskb_dst_set(struct sk_buff *skb, struct dst_entry *dst)

{

skb->_skb_refdst = (unsigned long)dst;---執行ip6_route_input_lookup()

}

static structdst_entry *ip6_route_input_lookup(structnet *net,

structnet_device *dev,

structflowi6 *fl6, int flags)

{

if(rt6_need_strict(&fl6->daddr) && dev->type != ARPHRD_PIMREG)

flags|= RT6_LOOKUP_F_IFACE;

returnfib6_rule_lookup(net, fl6,flags, ip6_pol_route_input);

}

最終跑到:ip6_pol_route_input()--->ip6_pol_route()--->fib6_lookup()----->---》fib6_lookup_1()//查詢路由表的Radix樹。

2)傳送時候發生的路由:ip6_route_output()

所有在ip6_xmit之前都會先進行路由查詢:例如:

Tcp_ipv6.c(trunk\kernel-3.10\net\ipv6):                err= ip6_xmit(sk, skb, fl6, np->opt, np->tclass);

Tcp_ipv6.c(trunk\kernel-3.10\net\ipv6):                ip6_xmit(ctl_sk,buff, &fl6, NULL, tclass);

Ipv6.c(trunk\kernel-3.10\net\dccp):                err= ip6_xmit(sk, skb, &fl6, np->opt, np->tclass);

dccp_v6_send_response()---->ip6_dst_lookup_flow()進行路由查詢

Ipv6.c(trunk\kernel-3.10\net\dccp):                ip6_xmit(ctl_sk,skb, &fl6, NULL, 0);

tcp_v6_send_synack()----》inet6_csk_route_req()----ip6_dst_lookup_flow()//進行路由查詢

Ipv6.c(trunk\kernel-3.10\net\sctp):        returnip6_xmit(sk, skb, fl6, np->opt, np->tclass);

Inet6_connection_sock.c(trunk\kernel-3.10\net\ipv6):        res= ip6_xmit(sk, skb, &fl6, np->opt, np->tclass);

例如一般的tcp傳送資料,tcp層到--->網路層:

Inet6_connection_sock.c@inet6_csk_xmit(struct sk_buff *skb, struct flowi*fl_unused){

完成的工作:路由查詢(就是找到一個合適的正確的dst

int inet6_csk_xmit(struct sk_buff *skb, struct flowi*fl_unused)

{

structsock *sk = skb->sk;

structipv6_pinfo *np = inet6_sk(sk);

structflowi6 fl6;

struct dst_entry *dst;

intres;

//開始路由查詢:

dst =inet6_csk_route_socket(sk, &fl6);//查詢路由表,目的是根據路由表資訊,找到該資料包的下一條地址。

...

skb_dst_set_noref(skb, dst);//把查詢到的dst 賦值給,上層下來的skb包中的成員:skb->_skb_refdst

/* Restore final destination back after routing done */

fl6.daddr= np->daddr;

//路由查詢結束;

res= ip6_xmit(sk, skb, &fl6, np->opt,np->tclass);

rcu_read_unlock();

returnres;

}

static structdst_entry *inet6_csk_route_socket(struct sock*sk,

structflowi6 *fl6)

{

structinet_sock *inet = inet_sk(sk);

structipv6_pinfo *np = inet6_sk(sk);

structin6_addr *final_p, final;

structdst_entry *dst;

//更新一條dst 到flowi資訊

memset(fl6,0, sizeof(*fl6));

fl6->flowi6_proto= sk->sk_protocol;

fl6->daddr= np->daddr;

fl6->saddr= np->saddr;

fl6->flowlabel= np->flow_label;

IP6_ECN_flow_xmit(sk,fl6->flowlabel);

fl6->flowi6_oif= sk->sk_bound_dev_if;

fl6->flowi6_mark= sk->sk_mark;

fl6->fl6_sport= inet->inet_sport;

fl6->fl6_dport= inet->inet_dport;

fl6->flowi6_uid= sock_i_uid(sk);

security_sk_classify_flow(sk,flowi6_to_flowi(fl6));

final_p= fl6_update_dst(fl6, np->opt, &final);

//首先查詢路由快取中是否有匹配的dst

dst= __inet6_csk_dst_check(sk, np->dst_cookie);

if(!dst) {

//其次到路由表裡面去查詢 dst

dst= ip6_dst_lookup_flow(sk, fl6, final_p, false);

if(!IS_ERR(dst))

__inet6_csk_dst_store(sk,dst, NULL, NULL);

}

returndst;

}

//到路由表裡面去查詢 dst流程是:

ip6_dst_lookup_flow()-----》ip6_dst_lookup_tail()----ip6_route_output()----》fib6_rule_lookup()----ip6_pol_route_output()

----ip6_pol_route()---》fib6_lookup()---》fib6_lookup_1()//查詢路由表的Radix樹。

3)查詢演算法:

fib6_lookup_1()