当前位置: 首页 > Linux

BGPEVPNRD和RTS

时间:2023-04-06 22:10:07 Linux

当FRR得知配置了一个本地VNI时,如果用户没有明确为这个VNI配置RD和RT,FRR会自动为这个VNI导出RD和RTS(包括导入和导出)。推导规则如下:RD格式为“Type:Routerid:VNI-Index”RT格式为“AS:VNI”RD和RT在交换EVPN路由时会用到。RD用于区分不同VNI的EVPN路由(因为同一个mac地址可以存在于不同的VLAN中,同一个IP地址可以存在于不同的VRF中,所以这里的RD与VNI是一一对应的,即和我之前的OneL3VNIoneRD想的不一样)。RT用于描述路由的归属,即谁发布了这条路由,用于路由过滤。通过结合导入和导出RT过滤路由的导入和导出。(说白了,RT就像密码一样,路由器在发送路由时,携带了对应的导出RT,接收方收到路由后,检查RT是否与本地VNI对应的导入RT一致,如果是一致,接受它,否则拒绝)。VNI索引由路由器本地维护的ID分配器生成。在本地配置一个VNI后,会为该VNI分配一个索引。所以RD的作用只是分隔不同VNI之间的路由,没有其他意义,是一个本地ID。邻居路由器收到路由后不会分析RD的具体含义,而只是将其作为路由区分符号。取值范围0到65535。RD由8个字节组成:TYPE:RD类型,两个字节,现在有三个值:RD_TYPE_AS(两个字节的AS类型,Routerid字段填写AS号,该字段是否占用两个bytes,剩下的两个字节会填充其他字段,type值为0),RD_TYPE_IP(IP类型,RouterId字段填充RouterId,该字段为四个字节),RD_TYPE_AS4(四个字节AS类型,Routerid字段填写的是AS号,该字段是否占用4个字节,type值为2)。该字段在配置期间不可见,由系统填充。RouterId:路由器ID,四个字节,不同类型填写的内容不同。VNI-索引:两个字节。RT值由两部分组成,共6个字节,其中AS部分只占两个字节。即使使用AS4,这个字段也是两个字节,取AS4的低两个字节,VNI字段占四个字节。.不同VNI的全局唯一性由VNI字段保证。RT在EVPN扩展社区中发布路由时使用。RT字段保证不同的VNI有不同的RT,同时保证同一个自治区的同一个VNI有相同的RT。对于eBGPEVPN对等体,它们的AS是不同的。如果派生的RT用于路由导入,路由导入会失败。为了解决ebgp对等体导入相同VNI的路由问题。在实现FRR时,做了特殊处理:当收到邻居发来的EVPN路由时,首先检查路由中VNI对应的RT值是否为路由器中对应VNI的RT值,如果是,允许进口;如果不是,则使用“*:VNI”格式进行匹配(即通配符AS部分)。该方法只有在导出RT时才会生效。当用户手动配置RT时,将采用严格的匹配方式。RD和RTFRR的用户手动配置支持RD和RT的手动配置。address-familyl2vpnevpnadvertise-all-vnivni10200rd172.16.100.1:20route-targetimport65100:20上述配置中,缩进表示归属关系。从配置可以看出,RD和RT需要配置在对应的VNI下,而且必须配置在evpn地址族下。用户删除RD和RT后,系统会重新使用派生的RD和RT,就好像用户没有配置过一样。RD值和VNI值是一一对应的,一个VNI可以配置多个RT值。地址系列l2vpnevpnvni10400路由目标导入100:400路由目标导入100:500vni10500路由目标导入65000:500路由目标导出65000:500EVPNRD/**派生assRDed自动格式化*形式为RouterId:unique-id-for-vni。*自动为VNI派生一个RD,RD格式为RouterID:VNI-INDEX。*/voidbgp_evpn_derive_auto_rd(structbgp*bgp,structbgpevpn*vpn){charbuf[100];vpn->prd.family=AF_UNSPEC;vpn->prd.prefixlen=64;sprintf(buf,"%s:%hu",inet_ntoa(bgp->router_id),vpn->rd_id);(void)str2prefix_rd(buf,&vpn->prd);UNSET_FLAG(vpn->flags,VNI_FLAG_RD_CFGD);}intstr2prefix_rd(constchar*str,structprefix_rd*prd){intret;/*被调用函数的ret*/intlret;/*这个函数的本地ret*/char*p;字符*p2;结构流*s=NULL;字符*一半=NULL;in_addr地址;s=stream_new(8);prd->family=AF_UNSPEC;prd->prefixlen=64;lret=0;//先找冒号,冒号前是路由idp=strchr(str,':');如果(!p)跳出;//冒号后面是vni-index,必须全是数字。如果(!all_digit(p+1))跳出;一半=XMALLOC(MTYPE_TMP,(p-str)+1);memcpy(half,str,(p-str));half[p-str]='\0';//点分小数,找到第一个点。p2=strchr(str,'.');//非IP地址,可作为。evpn使用的IP地址。如果(!p2){无符号长as_val;如果(!all_digit(half))跳出;as_val=atol(一半);如果(as_val>0xffff){stream_putw(s,RD_TYPE_AS4);stream_putl(s,as_val);(s,atol(p+1));}else{stream_putw(s,RD_TYPE_AS);stream_putw(s,as_val);stream_putl(s,atol(p+1));}}else{//点分十进制转换为整数。ret=inet_aton(half,&addr);如果(!ret)退出;//设置RD类型为IP。哦,后面是IP地址,然后是两字节的vni-index。//RD一共有8个字节。stream_putw(s,RD_TYPE_IP);stream_put_in_addr(s,&addr);stream_putw(s,atol(p+1));}memcpy(prd->val,s->data,8);lret=1;out:if(s)stream_free(s);XFREE(MTYPE_TMP,一半);returnlret;}EVPNRT派生RT/**根据传递的信息自动创建RT扩展社区:*形式为AS:VNI。*注意:我们只使用AS的低16位。这已经足够*需要获得一个RT值,该值在不同*VNI之间是唯一的,但对于特定*VNI在路由器(在同一AS中)之间是相同的。*/staticvoidform_auto_rt(structbgp*bgp,vni_tvni,structlist*rtl){structecommunity_valeval;结构ecommunity*ecomadd;如果(bgp->advertise_autort_rfc8365)vni|=EVPN_AUTORT_VXLAN;encode_route_target_as((bgp->as&0xFFFF),vni,&eval);//创建一个新的扩展体ecomadd=ecommunity_new();//设置扩展团的值ecommunity_add_val(ecomadd,&eval);//将rt扩展社区添加到rt链表listnode_add_sort(rtl,ecomadd);}/**EncodeBGPRouteTargetAS:nn.*/staticinlinevoidencode_route_target_as(as_tas,uint32_tval,structeconomy_val*eval){eval->val[0]=ECOMMUNITY_ENCODE_AS;//扩展社区类型eval->val[1]=ECOMMUNITY_ROUTE_TARGET;//扩展社区子类型eval->val[2]=(as>>8)&0xff;//作为eval->val[3]=as&0xff;eval->val[4]=(val>>24)&0xff;//vnieval->val[5]=(val>>16)&0xff;eval->val[6]=(val>>8)&0xff;eval->val[7]=val&0xff;}/**将VNI的RT(配置的或自动派生的)映射到VNI。*映射将在路由处理期间使用。*将RTS映射到对应的VNI主要是importrt。这些rts在收到更新消息时进行路由过滤。*/voidbgp_evpn_map_vni_to_its_rts(structbgp*bgp,structbgpevpn*vpn){inti;结构ecommunity_val*eval;结构列表节点*node,*nnode;结构ecommunity*ecom;对于(ALL_LIST_ELEMENTS(vpn->import_rtl,node,nnode,ecom)){for(i=0;isize;i++){eval=(structecommunity_val*)(ecom->val+(i*ECOMMUNITY_SIZE));map_vni_to_rt(bgp,vpn,eval);}}}/**将一个RT映射到指定的VNI。*将一个RT映射到一个指定的VNI。*/staticvoidmap_vni_to_rt(structbgp*bgp,structbgpevpn*vpn,structecommunity_val*eval){structirt_node*irt;结构ecommunity_valeval_tmp;/*如果使用“自动”RT,我们只关心local-admin*子字段。*这也是为了便于将VNI用作EBGP对等互连的RT。*/memcpy(&eval_tmp,eval,ECOMMUNITY_SIZE);如果(!is_import_rt_configured(vpn))mask_ecom_global_admin(&eval_tmp,eval);irt=lookup_import_rt(bgp,&eval_tmp);if(irt)if(is_vni_present_in_irt_vnis(irt->vnis,vpn))/*已经映射。*/返回;如果(!irt){irt=import_rt_new(bgp,&eval_tmp);断言(IRT);}/*将VNI添加到此RT的哈希列表中。*/listnode_add(irt->vnis,vpn);}派生导入RT/**为VNI自动导出ImportRT,将VNI映射到RT。*映射将在路由处理期间使用。*为vni派生importrt*/voidbgp_evpn_derive_auto_rt_import(structbgp*bgp,structbgpevpn*vpn){form_auto_rt(bgp,vpn->vni,vpn->import_rtl);UNSET_FLAG(vpn->flags,VNI_FLAG_IMPRT_CFGD);/*MapRTtoVNI将RT添加到对应的vni描述控制块中*/bgp_evpn_map_vni_to_its_rts(bgp,vpn);}deriveexportRT/**DeriveExportRTautomaticallyforVNI.*/voidbgp_evpn_derive_auto_rt_export(structbgp*bgp,structbgpevpn*vpn){form_auto_rt(bgp,vpn-vpn)>vni,vpn->export_rtl);UNSET_FLAG(vpn->flags,VNI_FLAG_EXPRT_CFGD);}RT在路由导入中的作用/**给定一个路由条目和一个VNI,看看这个路由条目是否应该*导入到VNI中,即RT匹配。*给定一个路由条目和一个VNI,看路由条目是否应该导入VNI条目,主要*匹配RT。*/staticintis_route_matching_for_vni(structbgp*bgp,structbgpevpn*vpn,structbgp_path_info*pi){structattr*attr=pi->attr;结构ecommunity*ecom;诠释我;断言(属性);/*路由应该有有效的RT才能被考虑。*//*RT放于扩展团体中,所以必须携带扩展团体属性*/if(!(attr->flag&ATTR_FLAG_BIT(BGP_ATTR_EXT_COMMUNITIES)))return0;ecom=attr->ecommunity;如果(!ecom||!ecom->size)返回0;/*对于每一个扩展的社区RT,看它是否匹配这个VNI。如果任何RT*匹配,我们就完成了。*/for(i=0;isize;i++){uint8_t*pnt;uint8_t类型,子类型;结构ecommunity_val*eval;结构ecommunity_valeval_tmp;结构irt_node*irt;/*只处理RTs*/pnt=(ecom->val+(i*ECOMMUNITY_SIZE));eval=(structecommunity_val*)(ecom->val+(i*ECOMMUNITY_SIZE));类型=*pnt++;sub_type=*pnt++;//我们只关心RT扩展社区if(sub_type!=ECOMMUNITY_ROUTE_TARGET)continue;/*看这个RT是否匹配指定的VNIsimportRTs*//*看看本地是否配置了这个RT*/irt=lookup_import_rt(bgp,eval);if(irt)//已配置,需要进一步验证,vpn对应的vni是否使用这个rtif(is_vni_present_in_irt_vnis(irt->vnis,vpn))return1;/*同时检查非精确匹配。在此,我们屏蔽了AS*和*仅检查本地管理员子字段。这是为了*也便于使用*VNI作为EBGP对等的RT。*如果没有匹配,则进行通配符处理。这种情况是用来解析ebgp邻居的,因为ebgp和ebgp有不同的as。所以用*:xxx来匹配。*/irt=NULL;if(type==ECOMMUNITY_ENCODE_AS||type==ECOMMUNITY_ENCODE_AS4||type==ECOMMUNITY_ENCODE_IP){memcpy(&eval_tmp,eval,ECOMMUNITY_SIZE);//掩码匹配rt属性。用于解决ebgp邻居之间的路由导入,通配符as。mask_ecom_global_admin(&eval_tmp,eval);//继续搜索。irt=lookup_import_rt(bgp,&eval_tmp);}//如果找到了,看看对应的vni是否使用这个rtif(irt)if(is_vni_present_in_irt_vnis(irt->vnis,vpn))return1;}return0;}/**屏蔽指定扩展社区(RT)的全局管理字段,*只保留本地管理字段。*只匹配RT扩展社区的本地管理字段。即只匹配VNI域,as域设置为0。//第一个字节是类型type=src->val[0];dst->val[0]=0;if(type==ECOMMUNITY_ENCODE_AS){dst->val[2]=dst->val[3]=0;//赋值为0}elseif(type==ECOMMUNITY_ENCODE_AS4||type==ECOMMUNITY_ENCODE_IP){dst->val[2]=dst->val[3]=0;dst->val[4]=dst->val[5]=0;}}