1. 程式人生 > >contiki之uip-ds6-route標頭檔案

contiki之uip-ds6-route標頭檔案

可能有點跳躍吧,但是不管了,能理解就寫出來。這次看的原始碼是關於路由表的一些資料結構還有操作函式,可以預見RPL協議將用到這個路由表。

還是從標頭檔案開始。

詳細的分析可以看這篇uip_ds6_route.h程式碼詳解,這裡主要講主要的幾個資料結構

#define UIP_DS6_NOTIFICATION_DEFRT_ADD 0
#define UIP_DS6_NOTIFICATION_DEFRT_RM  1
#define UIP_DS6_NOTIFICATION_ROUTE_ADD 2
#define UIP_DS6_NOTIFICATION_ROUTE_RM  3

typedef void (* uip_ds6_notification_callback)(int event,
        uip_ipaddr_t *route,
        uip_ipaddr_t *nexthop,
        int num_routes);
struct uip_ds6_notification
{
    struct uip_ds6_notification *next;
    uip_ds6_notification_callback callback;
};
這個是通知回撥函式,上層可以通過註冊回撥函式,然後當底層的路由表發生變化的時候,比如增加或者減少,這個uip_ds6_route模組就會呼叫回撥函式通知上層。

typedef struct rpl_route_entry
{
    uint32_t lifetime;
    struct rpl_dag *dag;
    uint8_t dao_seqno_out;
    uint8_t dao_seqno_in;
    uint8_t state_flags;
#if DS6_ROUTER_SAVE_MEMORY
    uint8_t length;
#endif
} rpl_route_entry_t;
這個是跟RPL有關的資料結構,到時候會是一個路由表項裡的內容,具體啥作用,現在還不知道,估計得等學到RPL才明白。

struct uip_ds6_route_neighbor_routes
{
    LIST_STRUCT(route_list);
};
定義一種包含list指標的結構體,學過list我們知道這個結構體裡只有兩個成員,一個是指標,另外一個是指向這個指標的指標

typedef struct uip_ds6_route
{
    struct uip_ds6_route *next;
    struct uip_ds6_route_neighbor_routes *neighbor_routes;
    uip_ipaddr_t ipaddr;
#ifdef UIP_DS6_ROUTE_STATE_TYPE
    UIP_DS6_ROUTE_STATE_TYPE state;
#endif
    uint8_t length;
} uip_ds6_route_t;
這個就是路由表裡的每一個表項的結構體了,看到next成員應該明白,這後續估計是要搞成連結串列了。UIP_DS6_ROUTE_STATE_TYPE就是上面說到的RPL相關的結構體。另外一個比較糾結的地方就是這個結構體裡還有一個指標,指標型別是上面包含了list指標的結構體。待會我們用圖來說明。

struct uip_ds6_route_neighbor_route
{
    struct uip_ds6_route_neighbor_route *next;
    struct uip_ds6_route *route;
};
又來一個結構體,最無語的是這個結構體型別的名字居然跟剛剛那個包含了list指標的結構名字幾乎一樣,就是最後多了一個s而已,不仔細看的話很容易就搞混了,作者是詞窮了麼。這個結構體也有next,估計又是一個連結串列,還有一個指向路由表項結構體的指標。

連著三個結構體,環環相扣,後一個結構體都包含指向前一個結構體的指標,可是看起來每種結構體都可以做成連結串列。

typedef struct uip_ds6_defrt
{
    struct uip_ds6_defrt *next;
    uip_ipaddr_t ipaddr;
    struct stimer lifetime;
    uint8_t isinfinite;
} uip_ds6_defrt_t;
最後一個結構體了,預設路由結構體。

這樣看還是比較暈乎,不知道這些結構要幹嘛。來張圖就明白了,這裡可能會提前說到原始檔的一些東西。





剛剛講到的那個包含了list指標的結構型別,在原始檔裡會用來建立一個鄰居列表結構體,如上圖所示。會用MEMB來建立一個路由表項結構體型別的記憶體池(也就是陣列),還會用MEMB建立一個uip_ds6_route_neighbor_route結構體型別的記憶體池,這個結構體型別真不知道該怎麼稱呼它好,以下代稱臨時結構體。

這三個結構體是路由表裡最核心最重要的三種結構體了。一個路由表項最基本的內容,按我的理解應該是這樣

目的ip地址   ------>   下一跳ip地址。

可以看到上面定義路由表項結構體裡只有一個ip地址,這個地址是目的地址還是下一跳地址呢,答案是目的地址。那麼,下一跳地址去哪了呢。下一跳地址,就是根據路由表項裡的指向list鄰居列表結構體的指標,去找到某個鄰居列表結構體成員,根據我們之前分析過的鄰居列表結構體原始碼可以知道,這個成員雖然沒有用指標明確地指向某個實體地址,但是確實跟實體地址列表key,索引一一對應,這個對應的實體地址,就是下一跳的實體地址。下一跳的ip地址,是存在另外一個鄰居列表結構體的,不過既然我們已經找到了實體地址,根據索引,當然可以順藤摸瓜地找到IP地址了。需要注意的是,如果有多個目的地址的下一跳都是一樣的話,那麼就有多個路由表項結構體裡的指標是指向同一個鄰居列表結構體的某一個成員。可能正是考慮到這種多個目的地址下一跳是同一個地址的情況,所以路由表項裡才只有目的地址,而下一跳地址單獨地是另外一個結構體。

所有的路由表項結構體都是用list模組提供的函式串成一個連結串列的。

一開始令我很不明白的地方在於,路由表項結構體只是利用了鄰居列表結構體成員跟實體地址之間的這種索引關係,它只要知道這個鄰居列表結構體成員在數組裡的排序即可,根本沒有用到成員內容,難道這個內容只是個擺設?看下去就明白了。


 
 

最奇葩的還是這個我們代稱為臨時結構體,即圖裡最底下這一排結構體。它跟另外兩個結構體的關係是這樣的。每一個這個臨時結構體成員都儲存了指向某一個路由表項結構體的指標。奇特的地方在於,鄰居列表結構體成員裡的list指標,如上圖所示,居然指向了這個臨時結構體,而且還是這個臨時結構體連結串列的頭指標。說不清,還是用圖吧。


上面這個圖只在有多個目的地址的下一跳地址是一樣的時候,如果是一個目的地址對應一個下一跳地址的話,這個臨時結構體是不會串成連結串列的。可以從圖裡看出來,能檢索到下一跳地址的那個鄰居列表結構體成員裡的list指標指向了這個臨時結構體連結串列的連結串列頭,而每個臨時結構體連結串列裡的每一個成員都有一個指向包含了目的地址的路由表項指標。每一個包含了目的地的的路由表項結構體又都有一個指向可以檢索得到下一跳地址的鄰居列表結構體成員指標。這不就間接地形成了一個環路麼。就是說,知道目的地址,我們可以找到對應的下一跳,反過來,知道了下一跳,我就可以知道對應的一個或者多個目的地址。路由表核心的三個結構體就是這樣設計出來,完成了路由表的基本功能。