当FRR学到一个本地VNI被配置,如果用户没有显示的为这个VNI配置RD和RT的时候,FRR会自动为该VNI派生RD和RTS(包括导入和导出)。派生规则如下:
RD格式为 “Type:Routerid:VNI-Index”
RT格式为 “AS:VNI”
RD和RT在进行EVPN路由交换的时候会被使用。RD用于区分不同的VNI的EVPN路由(因为在不同的VLAN中可以存在相同的mac地址,在不同的VRF中可以存在相同的IP地址,这里的RD是与VNI一一对应的,与我之前以为的一个L3 VNI一个RD是不同的)。RT用来描述这个路由的所属关系,即谁发布了该路由,用于路由过滤。通过结合导入导出RT来对路由的导入导出进行过滤。(通俗的将,RT就好像一个密码,路由器发送路由的时候携带对应的导出RT,接收方收到该路由后,检查该RT是否和本地该VNI对应的导入RT是否一致,一致则接受,否则拒绝)。
VNI-index是路由器本地维护的一个ID分配器生成的,本地配置一个VNI后,将会为该VNI分配一个索引。所以RD的作用仅仅是隔开不同VNI之间的路由,没有其它的含义,是一个本地的ID。邻居路由器接收到路由后不会解析RD的具体含义,只用作路由区分符号。该值的范围为0到65535。
RD由8个字节组成:
TYPE:RD类型,两个字节,现在有三个值:RD_TYPE_AS(两字节AS类型,Routerid字段填写的是AS号,这个是否该字段占用两个字节,剩下两个字节会有其它字段补齐,类型值为0),RD_TYPE_IP(IP类型,RouterId字段填写的是RouterId,该字段四个字节),RD_TYPE_AS4(四字节AS类型,Routerid字段填写的是AS号,这个是否该字段占用四个字节,类型值为2)。该字段在配置过程中不可见,由系统填充。
RouterId:路由器ID,四个字节,不同的类型填充的内容不同。
VNI-Index:两个字节。
RT值由两部分组成,6个字节
其中AS部分只占两个字节,即使使用的是AS4,该字段也是两个字节,取AS4的低两字节,VNI字段占用四个字节。由VNI字段来保证不同VNI全局唯一。RT用在EVPN的扩展团体中,在发布路由时使用。RT字段确保不同VNI有不同的RT,同时确保同一个自治区中的相同VNI有相同的RT。
对于eBGP EVPN对等体来说,它们的AS是不同的。如果使用派生的RT来进行路由导入,将会导致路由导入不成功。为了解决ebgp对等体导入同一个VNI的路由问题。FRR在实现的时候做了特殊处理:当收到一个邻居的EVPN路由时,首先检测路由中的VNI对应的RT值是否是本路由器中的对应的VNI的RT值,如果是则允许导入,如果不是则使用“*:VNI”格式进行匹配(即通配AS部分)。
这种方法只会在RT是派生的情况下生效,当RT是用户手动配置的,将会采用严格匹配方式。
用户手动配置RD和RT
FRR支持手动配置RD和RT。
address-family l2vpn evpn
advertise-all-vni
vni 10200
rd 172.16.100.1:20
route-target import 65100:20
上面的配置中,缩进表示所属关系。从配置可知,RD和RT需要在对应的VNI下进行配置,而且必须在evpn地址族下配置。当用户删除RD和RT后,系统将会再次使用派生的RD和RT,就好像用户没有配置过一样。
RD值与VNI值是一一对应的,而RT值,一个VNI可以配置多个。
address-family l2vpn evpn
vni 10400
route-target import 100:400
route-target import 100:500
vni 10500
route-target import 65000:500
route-target export 65000:500
EVPN RD
/*
* Derive RD automatically for VNI using passed information - it
* is of the form RouterId:unique-id-for-vni.
* 自动为VNI派生一个RD,RD格式为 路由ID:VNI-INDEX。
*/
void bgp_evpn_derive_auto_rd(struct bgp *bgp, struct bgpevpn *vpn)
{
char buf[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);
}
int str2prefix_rd(const char *str, struct prefix_rd *prd)
{
int ret; /* ret of called functions */
int lret; /* local ret, of this func */
char *p;
char *p2;
struct stream *s = NULL;
char *half = NULL;
struct in_addr addr;
s = stream_new(8);
prd->family = AF_UNSPEC;
prd->prefixlen = 64;
lret = 0;
//先查找冒号,冒号前面是路由id
p = strchr(str, ':');
if (!p)
goto out;
//冒号后面是vni-index,必须全是数字。
if (!all_digit(p + 1))
goto out;
half = XMALLOC(MTYPE_TMP, (p - str) + 1);
memcpy(half, str, (p - str));
half[p - str] = '\0';
//点分十进制,找到第一个点号。
p2 = strchr(str, '.');
//非IP地址,可能是as。evpn使用的IP地址。
if (!p2) {
unsigned long as_val;
if (!all_digit(half))
goto out;
as_val = atol(half);
if (as_val > 0xffff) {
stream_putw(s, RD_TYPE_AS4);
stream_putl(s, as_val);
stream_putw(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);
if (!ret)
goto out;
//设置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, half);
return lret;
}
EVPN RT
派生RT
/*
* Create RT extended community automatically from passed information:
* of the form AS:VNI.
* NOTE: We use only the lower 16 bits of the AS. This is sufficient as
* the need is to get a RT value that will be unique across different
* VNIs but the same across routers (in the same AS) for a particular
* VNI.
*/
static void form_auto_rt(struct bgp *bgp, vni_t vni, struct list *rtl)
{
struct ecommunity_val eval;
struct ecommunity *ecomadd;
if (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);
}
/*
* Encode BGP Route Target AS:nn.
*/
static inline void encode_route_target_as(as_t as, uint32_t val,
struct ecommunity_val *eval)
{
eval->val[0] = ECOMMUNITY_ENCODE_AS;//扩展团体类型
eval->val[1] = ECOMMUNITY_ROUTE_TARGET;//扩展团体子类型
eval->val[2] = (as >> 8) & 0xff;//as
eval->val[3] = as & 0xff;
eval->val[4] = (val >> 24) & 0xff;//vni
eval->val[5] = (val >> 16) & 0xff;
eval->val[6] = (val >> 8) & 0xff;
eval->val[7] = val & 0xff;
}
/*
* Map the RTs (configured or automatically derived) of a VNI to the VNI.
* The mapping will be used during route processing.
* 映射RTS到对应的VNI。主要是import rt。这些rt在收到update消息时进行路由过滤。
*/
void bgp_evpn_map_vni_to_its_rts(struct bgp *bgp, struct bgpevpn *vpn)
{
int i;
struct ecommunity_val *eval;
struct listnode *node, *nnode;
struct ecommunity *ecom;
for (ALL_LIST_ELEMENTS(vpn->import_rtl, node, nnode, ecom)) {
for (i = 0; i < ecom->size; i++) {
eval = (struct ecommunity_val *)(ecom->val
+ (i
* ECOMMUNITY_SIZE));
map_vni_to_rt(bgp, vpn, eval);
}
}
}
/*
* Map one RT to specified VNI.
* 映射一个RT到一个指定的VNI。
*/
static void map_vni_to_rt(struct bgp *bgp, struct bgpevpn *vpn,
struct ecommunity_val *eval)
{
struct irt_node *irt;
struct ecommunity_val eval_tmp;
/* If using "automatic" RT, we only care about the local-admin
* sub-field.
* This is to facilitate using VNI as the RT for EBGP peering too.
*/
memcpy(&eval_tmp, eval, ECOMMUNITY_SIZE);
if (!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))
/* Already mapped. */
return;
if (!irt) {
irt = import_rt_new(bgp, &eval_tmp);
assert(irt);
}
/* Add VNI to the hash list for this RT. */
listnode_add(irt->vnis, vpn);
}
派生导入RT
/*
* Derive Import RT automatically for VNI and map VNI to RT.
* The mapping will be used during route processing.
* 为vni派生import rt
*/
void bgp_evpn_derive_auto_rt_import(struct bgp *bgp, struct bgpevpn *vpn)
{
form_auto_rt(bgp, vpn->vni, vpn->import_rtl);
UNSET_FLAG(vpn->flags, VNI_FLAG_IMPRT_CFGD);
/* Map RT to VNI 将RT添加到对应的vni描述控制块中 */
bgp_evpn_map_vni_to_its_rts(bgp, vpn);
}
派生导出RT
/*
* Derive Export RT automatically for VNI.
*/
void bgp_evpn_derive_auto_rt_export(struct bgp *bgp, struct bgpevpn *vpn)
{
form_auto_rt(bgp, vpn->vni, vpn->export_rtl);
UNSET_FLAG(vpn->flags, VNI_FLAG_EXPRT_CFGD);
}
RT在路由导入时的作用
/*
* Given a route entry and a VNI, see if this route entry should be
* imported into the VNI i.e., RTs match.
* 给定一个路由表项和一个VNI。查看路由表项是否应该导入该VNI表项,主要是
* 匹配RTs。
*/
static int is_route_matching_for_vni(struct bgp *bgp, struct bgpevpn *vpn,
struct bgp_path_info *pi)
{
struct attr *attr = pi->attr;
struct ecommunity *ecom;
int i;
assert(attr);
/* Route should have valid RT to be even considered. */
/* RT放在扩展团体中,所以必须携带扩展团体属性 */
if (!(attr->flag & ATTR_FLAG_BIT(BGP_ATTR_EXT_COMMUNITIES)))
return 0;
ecom = attr->ecommunity;
if (!ecom || !ecom->size)
return 0;
/* For each extended community RT, see if it matches this VNI. If any RT
* matches, we're done.
*/
for (i = 0; i < ecom->size; i++) {
uint8_t *pnt;
uint8_t type, sub_type;
struct ecommunity_val *eval;
struct ecommunity_val eval_tmp;
struct irt_node *irt;
/* Only deal with RTs */
pnt = (ecom->val + (i * ECOMMUNITY_SIZE));
eval = (struct ecommunity_val *)(ecom->val
+ (i * ECOMMUNITY_SIZE));
type = *pnt++;
sub_type = *pnt++;
// 我们只关心RT扩展团体。
if (sub_type != ECOMMUNITY_ROUTE_TARGET)
continue;
/* See if this RT matches specified VNIs import RTs */
/* 查找本地是否配置了该RT */
irt = lookup_import_rt(bgp, eval);
if (irt)//配置了则需要进一步校验,vpn对应的vni是否使用了该rt
if (is_vni_present_in_irt_vnis(irt->vnis, vpn))
return 1;
/* Also check for non-exact match. In this, we mask out the AS
* and
* only check on the local-admin sub-field. This is to
* facilitate using
* VNI as the RT for EBGP peering too.
* 没有匹配的话,进行通配处理,这种情况用于解决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是否使用了该rt
if (irt)
if (is_vni_present_in_irt_vnis(irt->vnis, vpn))
return 1;
}
return 0;
}
/*
* Mask off global-admin field of specified extended community (RT),
* just retain the local-admin field.
* 只匹配RT扩展团体的本地管理域。即只匹配VNI域,将as域置为0。
*/
static inline void mask_ecom_global_admin(struct ecommunity_val *dst,
struct ecommunity_val *src)
{
uint8_t type;
//第一个字节是类型
type = src->val[0];
dst->val[0] = 0;
if (type == ECOMMUNITY_ENCODE_AS) {
dst->val[2] = dst->val[3] = 0;//将as赋值为0
} else if (type == ECOMMUNITY_ENCODE_AS4
|| type == ECOMMUNITY_ENCODE_IP) {
dst->val[2] = dst->val[3] = 0;
dst->val[4] = dst->val[5] = 0;
}
}
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。