当前面初始化完成的时候,zebra客户端线程(zebra_apic)静静等待客户端的消息,zebra dplane也急不可待了。

本次我们以BGP 为例,当BGP 根据自己的规则优选路由后,就会发给zebra,在函数bgp_process_main_one里面,把路由发布出去后,就会执行fib update动作,调用bgp_zebra_announce函数发布路由到zebra里面。

他们之间通过struct zapi_route 消息通信

首先填充消息内容,最后调用zclient_route_send 发送到zebra进程处理。


按照前面初始化阶段的分析,我们知道zebra为BGP创建的zebra_apic线程会被唤醒,执行fd可读的事件回调函数zserv_read。

本次我们先借助zserv_read来看下sock读取字节流的报文的处理方法,然后在继续讨论路由的处理。
- 先读取头部

- 解析头部,获取头部后面的数据区长度

- 在根据头部里面的数据区长度,获取数据

获取完所有的消息后,又把消息添加事件到zebra主线程继续处理?为啥这么处理了?zebra_apic线程就只读取下消息,然后所有的消息给主线程处理?减少锁?

我们先看看zserv_process_messages函数的注释,然后继续跟踪消息的处理:


先从client的ibuf里面出队报文,然后缓存起来,然后遍历缓存,每个消息调用zserv_handle_commands来实现函数调转,路由添加的cmd是ZEBRA_ROUTE_ADD,对应执行函数是zread_route_add,此函数继续解析处理路由消息。

- 使用zapi_route_decode解析路由消息的msg,获取前缀、nexthop等各种信息,还原struct zapi_route消息

- 生成一个新的route entry然后填充里面的各种信息,这里我们先认识下zebra里面最重要的路由的节点的数据结构


然后处理nexthop的信息,遍历所有的nexthop生成struct nexthop数据结构,我们在来认识下这个重要的数据结构

生成nexthop信息,并加入route entry的nexthop链表里面,后续用于负载均衡

如果消息里面有mpls label的信息,也需要添加到nexthop里面

Route entry填充好后,继续调用rib_add_multipath处理路由信息添加,让我们继续走下去细看。

rib_add_multipath 又引入了两个新的数据结构struct route_table和struct route_node,我们来看下:


结合前面给出的nexthop,整个路由表项的处理就是由route_table\route_node\route_entry 组织而成的,route_table包含了一个二叉树结构来保存所有的路由前缀和下一跳路由表项,prefix结构保持了路由前缀的长度和值,用来做最长前缀匹配,那,说好的mtire树呢? 好吧,我们不太可能把成千上万的路由表项塞给linux内核,够用就行。
整体的数据结构关系如下图所示:

rib_add_multipath 先查找本次的路由表的route_table,然后在table中根据前缀查找是否有相同的route_node表项,其中srcdest_rnode_get查找如果没有node,则会生成一个新的route_node,然后加入route_table的二叉树中,然后调用rib_addnode,添加route_entry表项。


rib_addnode 直接调用rib_link 继续处理,首先会在route_node的info字段生成一个rib_dest_t的结构体,上面的图也已经画了出来,同时会把route_node里面的route_entry使用链表连接起来,表示同一个前缀的不同路由。
然后会判断是否有重分发的配置,如果bgp的路由重分发到ospf等,本次不分析,如果没有重分发,那么直接调用rib_queue_add入zebrad.mq work queue处理,当work queue调度处理的时候,会调回调函数meta_queue_process继续处理



本文详细解析了BGP如何与Zebra进行路由交互,包括BGP优选路由后向Zebra发布路由的过程,以及Zebra如何接收并处理这些路由信息。深入探讨了ZAPI消息通信、路由消息解析、nexthop处理及路由表更新机制。
2320

被折叠的 条评论
为什么被折叠?



