处理NLRI
获取NLRI的报文长度,填入nlris[NLRI_UPDATE],到现在为止nlris里面的4种类型(如果有的话),已经全部填写到nlris数组结构体里面
然后我们遍历这个数组,处理里面所有的NLRI的类型,本次先分析NLRI_UPDATE,MP后面再分析。
根据前面解析出来的SAFI选取不同的处理函数,afi/safi定义如下:
AFI_IP 的组合定义如下:
IP + UNICAST | 普通的IP 单播路由处理 |
IP + MULTICAST | 组播路由?? |
IP + MPLS_VPN | MPLS VPN 路由的处理 |
IP + ENCAP | ??目前不太清楚 |
IP + EVPN | ?? 还是L2VPN + EVPN ?? |
IP + LABELED_UNICAST | BGP的标签分发的路由处理 |
IP + FLOWSPEC | BGP FLOWSPEC的处理 |
|
|
可以从下面的函数查看组合:
IP + UNICAST/ MULTICAST走的是下面的函数,NLRI里面存放的是前缀,为了方便,把报文也放到下面:
这里又引出BGP的一个数据结构struct prefix
在每次的for循环里面,需要填充好prefix前缀里面的长度、family、前缀值,然后以便后续bgp_update函数继续处理。
bgp_update 引申出struct bgp_node的数据结构,是BGP 存放路由的关键数据结构,我们先认识下,使用关键字prefix组织成radix树
struct bgp_path_info 存放在bgp_node的void *info里面,这个数据结构的意义是什么??
bgp_afi_node_get在bgp对应的afi/safi table里面使用prefix前缀,获取struct bgp_node节点,并加入afi/safi table的二叉树里面。
如果需要记录adj_in,那么把当前的信息存入bgp_adj_in结构体,然后link到bgp_node的链表上面。
然后会进行AS_PATH的防环检查,
attr_new = bgp_attr_intern(&new_attr); 会根据前面解析的attr(局部变量传下来的),申请一个attr_new,以便后续保存。
info_make 会创建一个新的struct bgp_path_info数据结构,attr_new 便保存在里面的,后续会在给bgp_path_info赋值。
函数bgp_path_info_add 会把新的bgp_path_info,连接到bgp_node里面。
bgp_maximum_prefix_overflow 会检查BGP Max Prefix条目限制
bgp_process 会把rn入队work_queue,后续work_queue注册的回调函数bgp_process_wq,会出队继续处理rn,而work_queue是在初始化的时候,就已经初始化好了
这里的work_queue和linux内核的work queue是异曲同工之妙。
WORK_QUEUE 后续处理
work queue的回调函数bgp_process_wq出队queue继续处理,bgp_process_main_one 处理具体的queue node,即bgp route_node的信息,这里提个问题,单线程处理的bgpd,为何这里需要在异步下work_queue继续处理??
bgp_process_main_one 会根据BGP的选路原则选择一个最优的路由,选择原则如下:
然后group_announce_route发布路由,会遍历bgp->update_groups的HASH表,执行回调函数update_group_walkcb,最后会调到函数group_announce_route_walkcb
而这里涉及到的结构体struct update_group/ struct update_subgroup是在bgp_establish的时候调用update_group_adjust_peer_afs创建的
/* assign update-group/subgroup */
update_group_adjust_peer_afs(peer);
回到group_announce_route_walkcb函数,遍历group下的subgroup,然后得到struct bgp_path_info和bgp_node,调用函数subgroup_process_announce_selected,最后调用bgp_adj_out_set_subgroup 加入sync->update表,等待定时器更新发送,但此时传入定时器的时间为0,所以应该是一个实时的时间,此时相当于又异步了一次。
事件执行会调用bgp_generate_updgrp_packets,然后调用subgroup_update_packet,里面封装发送的UPDATE报文,bgp_packet_attribute封装属性,报文封装好后,调用bpacket_reformat_for_peer修正下报文的nexthop,然后调用bgp_packet_add把报文加入到peer的obuf里面,然后调用bgp_writes_on(peer),等fd可写的时候,IO线程会把UPDATE报文发送出去。
到此我们分析了接受到UPDATE消息,然后在BGP 路由表里面添加了路由,并且发送出去的全部过程,涉及的路径很长,细节很大,只有后面再慢慢体会。
而这个BGP 路由下发到ZEBRA和linux内核的过程同样很长,后面再继续分析。