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指针指向了这个临时结构体链表的链表头,而每个临时结构体链表里的每一个成员都有一个指向包含了目的地址的路由表项指针。每一个包含了目的地的的路由表项结构体又都有一个指向可以检索得到下一跳地址的邻居列表结构体成员指针。这不就间接地形成了一个环路么。就是说,知道目的地址,我们可以找到对应的下一跳,反过来,知道了下一跳,我就可以知道对应的一个或者多个目的地址。路由表核心的三个结构体就是这样设计出来,完成了路由表的基本功能。


猜你喜欢

转载自blog.csdn.net/xbzlxtz/article/details/77749238