Merge branch 'master' into docuser

Signed-off-by: Quentin Young <qlyoung@cumulusnetworks.com>
This commit is contained in:
Quentin Young 2018-03-06 10:53:27 -05:00
commit 5da39765e2
No known key found for this signature in database
GPG Key ID: DAF48E0F57E0834F
12 changed files with 487 additions and 126 deletions

View File

@ -106,7 +106,7 @@ static int retain_mode = 0;
/* privileges */ /* privileges */
static zebra_capabilities_t _caps_p[] = { static zebra_capabilities_t _caps_p[] = {
ZCAP_BIND, ZCAP_NET_RAW, ZCAP_NET_ADMIN, ZCAP_BIND, ZCAP_NET_RAW, ZCAP_NET_ADMIN, ZCAP_SYS_ADMIN
}; };
struct zebra_privs_t bgpd_privs = { struct zebra_privs_t bgpd_privs = {

View File

@ -1054,6 +1054,9 @@ void bgp_zebra_announce(struct bgp_node *rn, struct prefix *p,
else else
continue; continue;
api_nh = &api.nexthops[valid_nh_count];
api_nh->vrf_id = bgp->vrf_id;
if (nh_family == AF_INET) { if (nh_family == AF_INET) {
struct in_addr *nexthop; struct in_addr *nexthop;
@ -1078,9 +1081,7 @@ void bgp_zebra_announce(struct bgp_node *rn, struct prefix *p,
nexthop = &mpinfo_cp->attr->nexthop; nexthop = &mpinfo_cp->attr->nexthop;
api_nh = &api.nexthops[valid_nh_count];
api_nh->gate.ipv4 = *nexthop; api_nh->gate.ipv4 = *nexthop;
api_nh->vrf_id = bgp->vrf_id;
/* EVPN type-2 routes are /* EVPN type-2 routes are
programmed as onlink on l3-vni SVI programmed as onlink on l3-vni SVI
*/ */
@ -1135,7 +1136,6 @@ void bgp_zebra_announce(struct bgp_node *rn, struct prefix *p,
if (ifindex == 0) if (ifindex == 0)
continue; continue;
api_nh = &api.nexthops[valid_nh_count];
api_nh->gate.ipv6 = *nexthop; api_nh->gate.ipv6 = *nexthop;
api_nh->ifindex = ifindex; api_nh->ifindex = ifindex;
api_nh->type = NEXTHOP_TYPE_IPV6_IFINDEX; api_nh->type = NEXTHOP_TYPE_IPV6_IFINDEX;

View File

@ -1085,7 +1085,7 @@ int zapi_route_decode(struct stream *s, struct zapi_route *api)
STREAM_GETC(s, api->message); STREAM_GETC(s, api->message);
STREAM_GETC(s, api->safi); STREAM_GETC(s, api->safi);
if (CHECK_FLAG(api->flags, ZEBRA_FLAG_EVPN_ROUTE)) if (CHECK_FLAG(api->flags, ZEBRA_FLAG_EVPN_ROUTE))
stream_get(&(api->rmac), s, sizeof(struct ethaddr)); STREAM_GET(&(api->rmac), s, sizeof(struct ethaddr));
/* Prefix. */ /* Prefix. */
STREAM_GETC(s, api->prefix.family); STREAM_GETC(s, api->prefix.family);

View File

@ -204,26 +204,142 @@ void ospf6_asbr_update_route_ecmp_path(struct ospf6_route *old,
{ {
struct ospf6_route *old_route; struct ospf6_route *old_route;
struct ospf6_path *ecmp_path, *o_path = NULL; struct ospf6_path *ecmp_path, *o_path = NULL;
struct listnode *anode; struct listnode *anode, *anext;
struct listnode *nnode, *rnode, *rnext; struct listnode *nnode, *rnode, *rnext;
struct ospf6_nexthop *nh, *rnh; struct ospf6_nexthop *nh, *rnh;
char buf[PREFIX2STR_BUFFER]; char buf[PREFIX2STR_BUFFER];
bool route_found = false; bool route_found = false;
/* check for old entry match with new route origin,
* delete old entry.
*/
for (old_route = old; old_route; old_route = old_route->next) { for (old_route = old; old_route; old_route = old_route->next) {
if (ospf6_route_is_same(old_route, route) && bool route_updated = false;
(old_route->path.type == route->path.type) &&
(old_route->path.cost == route->path.cost) && if (!ospf6_route_is_same(old_route, route) ||
(old_route->path.u.cost_e2 == route->path.u.cost_e2)) { (old_route->path.type != route->path.type))
continue;
/* Current and New route has same origin,
* delete old entry.
*/
for (ALL_LIST_ELEMENTS(old_route->paths, anode, anext,
o_path)) {
/* Check old route path and route has same
* origin.
*/
if (o_path->area_id != route->path.area_id ||
(memcmp(&(o_path)->origin, &(route)->path.origin,
sizeof(struct ospf6_ls_origin)) != 0))
continue;
/* Cost is not same then delete current path */
if ((o_path->cost == route->path.cost) &&
(o_path->u.cost_e2 == route->path.u.cost_e2))
continue;
if (IS_OSPF6_DEBUG_EXAMIN(AS_EXTERNAL)) { if (IS_OSPF6_DEBUG_EXAMIN(AS_EXTERNAL)) {
prefix2str(&old_route->prefix, buf, prefix2str(&old_route->prefix, buf,
sizeof(buf)); sizeof(buf));
zlog_debug("%s: old route %s path cost %u [%u]", zlog_debug("%s: route %s cost old %u new %u is not same, replace route",
__PRETTY_FUNCTION__, buf,
o_path->cost, route->path.cost);
}
/* Remove selected current rout path's nh from
* effective nh list.
*/
for (ALL_LIST_ELEMENTS_RO(o_path->nh_list, nnode, nh)) {
for (ALL_LIST_ELEMENTS(old_route->nh_list,
rnode, rnext, rnh)) {
if (!ospf6_nexthop_is_same(rnh, nh))
continue;
listnode_delete(old_route->nh_list,
rnh);
ospf6_nexthop_delete(rnh);
route_updated = true;
}
}
listnode_delete(old_route->paths, o_path);
ospf6_path_free(o_path);
/* Current route's path (adv_router info) is similar
* to route being added.
* Replace current route's path with paths list head.
* Update FIB with effective NHs.
*/
if (listcount(old_route->paths)) {
if (old_route->path.origin.id ==
route->path.origin.id &&
old_route->path.origin.adv_router ==
route->path.origin.adv_router) {
struct ospf6_path *h_path;
h_path = (struct ospf6_path *)
listgetdata(listhead(old_route->paths));
old_route->path.origin.type =
h_path->origin.type;
old_route->path.origin.id =
h_path->origin.id;
old_route->path.origin.adv_router =
h_path->origin.adv_router;
}
if (route_updated) {
for (ALL_LIST_ELEMENTS(old_route->paths,
anode, anext, o_path)) {
ospf6_merge_nexthops(
old_route->nh_list,
o_path->nh_list);
}
/* Update RIB/FIB with effective
* nh_list
*/
if (ospf6->route_table->hook_add)
(*ospf6->route_table->hook_add)
(old_route);
break;
}
} else {
if (IS_OSPF6_DEBUG_EXAMIN(AS_EXTERNAL)) {
prefix2str(&old_route->prefix, buf,
sizeof(buf));
zlog_debug("%s: route %s old cost %u new cost %u, delete old entry.",
__PRETTY_FUNCTION__, buf,
old_route->path.cost,
route->path.cost);
}
ospf6_route_remove(old_route,
ospf6->route_table);
break;
}
}
if (route_updated)
break;
}
/* Add new route */
for (old_route = old; old_route; old_route = old_route->next) {
/* Current and New Route prefix or route type
* is not same skip this current node.
*/
if (!ospf6_route_is_same(old_route, route) ||
(old_route->path.type != route->path.type))
continue;
/* Old Route and New Route have Equal Cost, Merge NHs */
if ((old_route->path.cost == route->path.cost) &&
(old_route->path.u.cost_e2 == route->path.u.cost_e2)) {
if (IS_OSPF6_DEBUG_EXAMIN(AS_EXTERNAL)) {
prefix2str(&old_route->prefix, buf,
sizeof(buf));
zlog_debug("%s: old route %s path cost %u e2 %u",
__PRETTY_FUNCTION__, buf, __PRETTY_FUNCTION__, buf,
old_route->path.cost, old_route->path.cost,
ospf6_route_is_same(old_route, old_route->path.u.cost_e2);
route));
} }
route_found = true; route_found = true;
/* check if this path exists already in /* check if this path exists already in
@ -232,9 +348,10 @@ void ospf6_asbr_update_route_ecmp_path(struct ospf6_route *old,
*/ */
for (ALL_LIST_ELEMENTS_RO(old_route->paths, anode, for (ALL_LIST_ELEMENTS_RO(old_route->paths, anode,
o_path)) { o_path)) {
if ((o_path->origin.id == route->path.origin.id) if (o_path->area_id == route->path.area_id &&
&& (o_path->origin.adv_router == (memcmp(&(o_path)->origin,
route->path.origin.adv_router)) &(route)->path.origin,
sizeof(struct ospf6_ls_origin)) == 0))
break; break;
} }
/* If path is not found in old_route paths's list, /* If path is not found in old_route paths's list,
@ -262,12 +379,13 @@ void ospf6_asbr_update_route_ecmp_path(struct ospf6_route *old,
if (IS_OSPF6_DEBUG_EXAMIN(AS_EXTERNAL)) { if (IS_OSPF6_DEBUG_EXAMIN(AS_EXTERNAL)) {
prefix2str(&route->prefix, buf, prefix2str(&route->prefix, buf,
sizeof(buf)); sizeof(buf));
zlog_debug("%s: route %s another path added with nh %u, Paths %u", zlog_debug("%s: route %s another path added with nh %u, effective paths %u nh %u",
__PRETTY_FUNCTION__, buf, __PRETTY_FUNCTION__, buf,
listcount(ecmp_path->nh_list), listcount(ecmp_path->nh_list),
old_route->paths ? old_route->paths ?
listcount(old_route->paths) listcount(old_route->paths)
: 0); : 0,
listcount(old_route->nh_list));
} }
} else { } else {
for (ALL_LIST_ELEMENTS_RO(o_path->nh_list, for (ALL_LIST_ELEMENTS_RO(o_path->nh_list,
@ -313,6 +431,7 @@ void ospf6_asbr_update_route_ecmp_path(struct ospf6_route *old,
* route. * route.
*/ */
ospf6_route_delete(route); ospf6_route_delete(route);
break; break;
} }
} }
@ -426,11 +545,12 @@ void ospf6_asbr_lsa_add(struct ospf6_lsa *lsa)
} }
void ospf6_asbr_lsa_remove(struct ospf6_lsa *lsa) void ospf6_asbr_lsa_remove(struct ospf6_lsa *lsa,
struct ospf6_route *asbr_entry)
{ {
struct ospf6_as_external_lsa *external; struct ospf6_as_external_lsa *external;
struct prefix prefix; struct prefix prefix;
struct ospf6_route *route, *nroute; struct ospf6_route *route, *nroute, *route_to_del;
char buf[PREFIX2STR_BUFFER]; char buf[PREFIX2STR_BUFFER];
external = (struct ospf6_as_external_lsa *)OSPF6_LSA_HEADER_END( external = (struct ospf6_as_external_lsa *)OSPF6_LSA_HEADER_END(
@ -445,6 +565,35 @@ void ospf6_asbr_lsa_remove(struct ospf6_lsa *lsa)
return; return;
} }
route_to_del = ospf6_route_create();
route_to_del->type = OSPF6_DEST_TYPE_NETWORK;
route_to_del->prefix.family = AF_INET6;
route_to_del->prefix.prefixlen = external->prefix.prefix_length;
ospf6_prefix_in6_addr(&route_to_del->prefix.u.prefix6,
&external->prefix);
route_to_del->path.origin.type = lsa->header->type;
route_to_del->path.origin.id = lsa->header->id;
route_to_del->path.origin.adv_router = lsa->header->adv_router;
if (asbr_entry) {
route_to_del->path.area_id = asbr_entry->path.area_id;
if (CHECK_FLAG(external->bits_metric, OSPF6_ASBR_BIT_E)) {
route_to_del->path.type = OSPF6_PATH_TYPE_EXTERNAL2;
route_to_del->path.metric_type = 2;
route_to_del->path.cost = asbr_entry->path.cost;
route_to_del->path.u.cost_e2 =
OSPF6_ASBR_METRIC(external);
} else {
route_to_del->path.type = OSPF6_PATH_TYPE_EXTERNAL1;
route_to_del->path.metric_type = 1;
route_to_del->path.cost =
asbr_entry->path.cost +
OSPF6_ASBR_METRIC(external);
route_to_del->path.u.cost_e2 = 0;
}
}
memset(&prefix, 0, sizeof(struct prefix)); memset(&prefix, 0, sizeof(struct prefix));
prefix.family = AF_INET6; prefix.family = AF_INET6;
prefix.prefixlen = external->prefix.prefix_length; prefix.prefixlen = external->prefix.prefix_length;
@ -459,14 +608,25 @@ void ospf6_asbr_lsa_remove(struct ospf6_lsa *lsa)
return; return;
} }
for (ospf6_route_lock(route); if (IS_OSPF6_DEBUG_EXAMIN(AS_EXTERNAL)) {
route && ospf6_route_is_prefix(&prefix, route); route = nroute) { prefix2str(&prefix, buf, sizeof(buf));
zlog_debug("%s: Current route %s cost %u e2 %u, route to del cost %u e2 %u",
__PRETTY_FUNCTION__, buf, route->path.cost,
route->path.u.cost_e2,
route_to_del->path.cost,
route_to_del->path.u.cost_e2);
}
for (ospf6_route_lock(route); route &&
ospf6_route_is_prefix(&prefix, route); route = nroute) {
nroute = ospf6_route_next(route); nroute = ospf6_route_next(route);
if (route->type != OSPF6_DEST_TYPE_NETWORK) if (route->type != OSPF6_DEST_TYPE_NETWORK)
continue; continue;
/* Route has multiple ECMP paths remove, /* Route has multiple ECMP paths, remove matching
* matching path and update effective route's nh list. * path. Update current route's effective nh list
* after removal of one of the path.
*/ */
if (listcount(route->paths) > 1) { if (listcount(route->paths) > 1) {
struct listnode *anode, *anext; struct listnode *anode, *anext;
@ -481,18 +641,36 @@ void ospf6_asbr_lsa_remove(struct ospf6_lsa *lsa)
*/ */
for (ALL_LIST_ELEMENTS(route->paths, anode, anext, for (ALL_LIST_ELEMENTS(route->paths, anode, anext,
o_path)) { o_path)) {
if (o_path->origin.type != lsa->header->type) if ((o_path->origin.type != lsa->header->type)
|| (o_path->origin.adv_router !=
lsa->header->adv_router) ||
(o_path->origin.id != lsa->header->id))
continue; continue;
if (o_path->origin.id != lsa->header->id)
continue; /* Compare LSA cost with current
if (o_path->origin.adv_router != * route info.
lsa->header->adv_router) */
if (!asbr_entry && (o_path->cost !=
route_to_del->path.cost ||
o_path->u.cost_e2 !=
route_to_del->path.u.cost_e2)) {
if (IS_OSPF6_DEBUG_EXAMIN(
AS_EXTERNAL)) {
prefix2str(&prefix, buf,
sizeof(buf));
zlog_debug(
"%s: route %s to delete is not same, cost %u del cost %u. skip",
__PRETTY_FUNCTION__, buf,
route->path.cost,
route_to_del->path.cost);
}
continue; continue;
}
if (IS_OSPF6_DEBUG_EXAMIN(AS_EXTERNAL)) { if (IS_OSPF6_DEBUG_EXAMIN(AS_EXTERNAL)) {
prefix2str(&prefix, buf, sizeof(buf)); prefix2str(&prefix, buf, sizeof(buf));
zlog_debug( zlog_debug(
"%s: route %s path found with nh %u", "%s: route %s path found with nh %u to remove.",
__PRETTY_FUNCTION__, buf, __PRETTY_FUNCTION__, buf,
listcount(o_path->nh_list)); listcount(o_path->nh_list));
} }
@ -542,13 +720,13 @@ void ospf6_asbr_lsa_remove(struct ospf6_lsa *lsa)
listcount(route->nh_list)); listcount(route->nh_list));
} }
/* Update RIB/FIB w/ effective nh_list */ /* Update RIB/FIB with effective nh_list */
if (ospf6->route_table->hook_add) if (ospf6->route_table->hook_add)
(*ospf6->route_table->hook_add)(route); (*ospf6->route_table->hook_add)(route);
/* route's path is similar to lsa header, /* route's primary path is similar to LSA,
* replace route's path with route's * replace route's primary path with
* paths list head. * route's paths list head.
*/ */
if (route->path.origin.id == lsa->header->id && if (route->path.origin.id == lsa->header->id &&
route->path.origin.adv_router == route->path.origin.adv_router ==
@ -568,12 +746,29 @@ void ospf6_asbr_lsa_remove(struct ospf6_lsa *lsa)
continue; continue;
} else { } else {
if (route->path.origin.type != lsa->header->type) /* Compare LSA origin and cost with current route info.
* if any check fails skip del this route node.
*/
if (asbr_entry && (!ospf6_route_is_same_origin(route,
route_to_del) ||
(route->path.type != route_to_del->path.type) ||
(route->path.cost != route_to_del->path.cost) ||
(route->path.u.cost_e2 !=
route_to_del->path.u.cost_e2))) {
if (IS_OSPF6_DEBUG_EXAMIN(AS_EXTERNAL)) {
prefix2str(&prefix, buf, sizeof(buf));
zlog_debug("%s: route %s to delete is not same, cost %u del cost %u. skip",
__PRETTY_FUNCTION__, buf,
route->path.cost,
route_to_del->path.cost);
}
continue; continue;
if (route->path.origin.id != lsa->header->id) }
continue;
if (route->path.origin.adv_router != if ((route->path.origin.type != lsa->header->type) ||
lsa->header->adv_router) (route->path.origin.adv_router !=
lsa->header->adv_router) ||
(route->path.origin.id != lsa->header->id))
continue; continue;
} }
if (IS_OSPF6_DEBUG_EXAMIN(AS_EXTERNAL)) { if (IS_OSPF6_DEBUG_EXAMIN(AS_EXTERNAL)) {
@ -589,6 +784,8 @@ void ospf6_asbr_lsa_remove(struct ospf6_lsa *lsa)
} }
if (route != NULL) if (route != NULL)
ospf6_route_unlock(route); ospf6_route_unlock(route);
ospf6_route_delete(route_to_del);
} }
void ospf6_asbr_lsentry_add(struct ospf6_route *asbr_entry) void ospf6_asbr_lsentry_add(struct ospf6_route *asbr_entry)
@ -622,7 +819,7 @@ void ospf6_asbr_lsentry_remove(struct ospf6_route *asbr_entry)
type = htons(OSPF6_LSTYPE_AS_EXTERNAL); type = htons(OSPF6_LSTYPE_AS_EXTERNAL);
router = ospf6_linkstate_prefix_adv_router(&asbr_entry->prefix); router = ospf6_linkstate_prefix_adv_router(&asbr_entry->prefix);
for (ALL_LSDB_TYPED_ADVRTR(ospf6->lsdb, type, router, lsa)) for (ALL_LSDB_TYPED_ADVRTR(ospf6->lsdb, type, router, lsa))
ospf6_asbr_lsa_remove(lsa); ospf6_asbr_lsa_remove(lsa, asbr_entry);
} }

View File

@ -71,7 +71,8 @@ struct ospf6_as_external_lsa {
} }
extern void ospf6_asbr_lsa_add(struct ospf6_lsa *lsa); extern void ospf6_asbr_lsa_add(struct ospf6_lsa *lsa);
extern void ospf6_asbr_lsa_remove(struct ospf6_lsa *lsa); extern void ospf6_asbr_lsa_remove(struct ospf6_lsa *lsa,
struct ospf6_route *asbr_entry);
extern void ospf6_asbr_lsentry_add(struct ospf6_route *asbr_entry); extern void ospf6_asbr_lsentry_add(struct ospf6_route *asbr_entry);
extern void ospf6_asbr_lsentry_remove(struct ospf6_route *asbr_entry); extern void ospf6_asbr_lsentry_remove(struct ospf6_route *asbr_entry);

View File

@ -469,6 +469,8 @@ int ospf6_route_cmp(struct ospf6_route *ra, struct ospf6_route *rb)
if (ra->path.type == OSPF6_PATH_TYPE_EXTERNAL2) { if (ra->path.type == OSPF6_PATH_TYPE_EXTERNAL2) {
if (ra->path.u.cost_e2 != rb->path.u.cost_e2) if (ra->path.u.cost_e2 != rb->path.u.cost_e2)
return (ra->path.u.cost_e2 - rb->path.u.cost_e2); return (ra->path.u.cost_e2 - rb->path.u.cost_e2);
else
return (ra->path.cost - rb->path.cost);
} else { } else {
if (ra->path.cost != rb->path.cost) if (ra->path.cost != rb->path.cost)
return (ra->path.cost - rb->path.cost); return (ra->path.cost - rb->path.cost);
@ -627,10 +629,10 @@ struct ospf6_route *ospf6_route_add(struct ospf6_route *route,
if (ospf6_route_is_identical(old, route)) { if (ospf6_route_is_identical(old, route)) {
if (IS_OSPF6_DEBUG_ROUTE(MEMORY)) if (IS_OSPF6_DEBUG_ROUTE(MEMORY))
zlog_debug( zlog_debug(
"%s %p: route add %p: needless update of %p", "%s %p: route add %p: needless update of %p old cost %u",
ospf6_route_table_name(table), ospf6_route_table_name(table),
(void *)table, (void *)route, (void *)table, (void *)route,
(void *)old); (void *)old, old->path.cost);
else if (IS_OSPF6_DEBUG_ROUTE(TABLE)) else if (IS_OSPF6_DEBUG_ROUTE(TABLE))
zlog_debug("%s: route add: needless update", zlog_debug("%s: route add: needless update",
ospf6_route_table_name(table)); ospf6_route_table_name(table));
@ -645,9 +647,10 @@ struct ospf6_route *ospf6_route_add(struct ospf6_route *route,
} }
if (IS_OSPF6_DEBUG_ROUTE(MEMORY)) if (IS_OSPF6_DEBUG_ROUTE(MEMORY))
zlog_debug("%s %p: route add %p: update of %p", zlog_debug("%s %p: route add %p cost %u: update of %p old cost %u",
ospf6_route_table_name(table), (void *)table, ospf6_route_table_name(table), (void *)table,
(void *)route, (void *)old); (void *)route, route->path.cost, (void *)old,
old->path.cost);
else if (IS_OSPF6_DEBUG_ROUTE(TABLE)) else if (IS_OSPF6_DEBUG_ROUTE(TABLE))
zlog_debug("%s: route add: update", zlog_debug("%s: route add: update",
ospf6_route_table_name(table)); ospf6_route_table_name(table));
@ -686,13 +689,14 @@ struct ospf6_route *ospf6_route_add(struct ospf6_route *route,
if (prev || next) { if (prev || next) {
if (IS_OSPF6_DEBUG_ROUTE(MEMORY)) if (IS_OSPF6_DEBUG_ROUTE(MEMORY))
zlog_debug( zlog_debug(
"%s %p: route add %p: another path: prev %p, next %p node refcount %u", "%s %p: route add %p cost %u: another path: prev %p, next %p node ref %u",
ospf6_route_table_name(table), (void *)table, ospf6_route_table_name(table), (void *)table,
(void *)route, (void *)prev, (void *)next, (void *)route, route->path.cost, (void *)prev,
node->lock); (void *)next, node->lock);
else if (IS_OSPF6_DEBUG_ROUTE(TABLE)) else if (IS_OSPF6_DEBUG_ROUTE(TABLE))
zlog_debug("%s: route add: another path found", zlog_debug("%s: route add cost %u: another path found",
ospf6_route_table_name(table)); ospf6_route_table_name(table),
route->path.cost);
if (prev == NULL) if (prev == NULL)
prev = next->prev; prev = next->prev;
@ -814,9 +818,9 @@ void ospf6_route_remove(struct ospf6_route *route,
prefix2str(&route->prefix, buf, sizeof(buf)); prefix2str(&route->prefix, buf, sizeof(buf));
if (IS_OSPF6_DEBUG_ROUTE(MEMORY)) if (IS_OSPF6_DEBUG_ROUTE(MEMORY))
zlog_debug("%s %p: route remove %p: %s refcount %u", zlog_debug("%s %p: route remove %p: %s cost %u refcount %u",
ospf6_route_table_name(table), (void *)table, ospf6_route_table_name(table), (void *)table,
(void *)route, buf, route->lock); (void *)route, buf, route->path.cost, route->lock);
else if (IS_OSPF6_DEBUG_ROUTE(TABLE)) else if (IS_OSPF6_DEBUG_ROUTE(TABLE))
zlog_debug("%s: route remove: %s", zlog_debug("%s: route remove: %s",
ospf6_route_table_name(table), buf); ospf6_route_table_name(table), buf);

View File

@ -72,7 +72,7 @@ static void ospf6_top_lsdb_hook_remove(struct ospf6_lsa *lsa)
{ {
switch (ntohs(lsa->header->type)) { switch (ntohs(lsa->header->type)) {
case OSPF6_LSTYPE_AS_EXTERNAL: case OSPF6_LSTYPE_AS_EXTERNAL:
ospf6_asbr_lsa_remove(lsa); ospf6_asbr_lsa_remove(lsa, NULL);
break; break;
default: default:
@ -96,11 +96,16 @@ static void ospf6_top_route_hook_remove(struct ospf6_route *route)
static void ospf6_top_brouter_hook_add(struct ospf6_route *route) static void ospf6_top_brouter_hook_add(struct ospf6_route *route)
{ {
if (IS_OSPF6_DEBUG_EXAMIN(AS_EXTERNAL)) { if (IS_OSPF6_DEBUG_EXAMIN(AS_EXTERNAL)) {
char buf[PREFIX2STR_BUFFER]; uint32_t brouter_id;
char brouter_name[16];
prefix2str(&route->prefix, buf, sizeof(buf)); brouter_id = ADV_ROUTER_IN_PREFIX(&route->prefix);
zlog_debug("%s: brouter %s add with nh count %u", inet_ntop(AF_INET, &brouter_id, brouter_name,
__PRETTY_FUNCTION__, buf, listcount(route->nh_list)); sizeof(brouter_name));
zlog_debug("%s: brouter %s add with adv router %x nh count %u",
__PRETTY_FUNCTION__, brouter_name,
route->path.origin.adv_router,
listcount(route->nh_list));
} }
ospf6_abr_examin_brouter(ADV_ROUTER_IN_PREFIX(&route->prefix)); ospf6_abr_examin_brouter(ADV_ROUTER_IN_PREFIX(&route->prefix));
ospf6_asbr_lsentry_add(route); ospf6_asbr_lsentry_add(route);
@ -110,11 +115,15 @@ static void ospf6_top_brouter_hook_add(struct ospf6_route *route)
static void ospf6_top_brouter_hook_remove(struct ospf6_route *route) static void ospf6_top_brouter_hook_remove(struct ospf6_route *route)
{ {
if (IS_OSPF6_DEBUG_EXAMIN(AS_EXTERNAL)) { if (IS_OSPF6_DEBUG_EXAMIN(AS_EXTERNAL)) {
char buf[PREFIX2STR_BUFFER]; uint32_t brouter_id;
char brouter_name[16];
prefix2str(&route->prefix, buf, sizeof(buf)); brouter_id = ADV_ROUTER_IN_PREFIX(&route->prefix);
inet_ntop(AF_INET, &brouter_id, brouter_name,
sizeof(brouter_name));
zlog_debug("%s: brouter %s del with nh count %u", zlog_debug("%s: brouter %s del with nh count %u",
__PRETTY_FUNCTION__, buf, listcount(route->nh_list)); __PRETTY_FUNCTION__, brouter_name,
listcount(route->nh_list));
} }
route->flag |= OSPF6_ROUTE_REMOVE; route->flag |= OSPF6_ROUTE_REMOVE;
ospf6_abr_examin_brouter(ADV_ROUTER_IN_PREFIX(&route->prefix)); ospf6_abr_examin_brouter(ADV_ROUTER_IN_PREFIX(&route->prefix));

View File

@ -55,7 +55,7 @@
/* ospfd privileges */ /* ospfd privileges */
zebra_capabilities_t _caps_p[] = { zebra_capabilities_t _caps_p[] = {
ZCAP_NET_RAW, ZCAP_BIND, ZCAP_NET_ADMIN, ZCAP_NET_RAW, ZCAP_BIND, ZCAP_NET_ADMIN, ZCAP_SYS_ADMIN
}; };
struct zebra_privs_t ospfd_privs = { struct zebra_privs_t ospfd_privs = {

View File

@ -47,6 +47,7 @@
#include "thread.h" #include "thread.h"
#include "vty.h" #include "vty.h"
#include "zclient.h" #include "zclient.h"
#include <lib/json.h>
#include "ospfd/ospfd.h" #include "ospfd/ospfd.h"
#include "ospfd/ospf_interface.h" #include "ospfd/ospf_interface.h"
@ -306,7 +307,8 @@ int ospf_sr_init(void)
{ {
int rc = -1; int rc = -1;
zlog_info("SR (%s): Initialize SR Data Base", __func__); if (IS_DEBUG_OSPF_SR)
zlog_info("SR (%s): Initialize SR Data Base", __func__);
memset(&OspfSR, 0, sizeof(struct ospf_sr_db)); memset(&OspfSR, 0, sizeof(struct ospf_sr_db));
OspfSR.enabled = false; OspfSR.enabled = false;
@ -675,6 +677,7 @@ static int ospf_zebra_send_mpls_ftn(int cmd, struct sr_nhlfe nhlfe)
SET_FLAG(api.message, ZAPI_MESSAGE_LABEL); SET_FLAG(api.message, ZAPI_MESSAGE_LABEL);
api_nh->labels[0] = nhlfe.label_out; api_nh->labels[0] = nhlfe.label_out;
api_nh->label_num = 1; api_nh->label_num = 1;
api_nh->vrf_id = VRF_DEFAULT;
api.nexthop_num = 1; api.nexthop_num = 1;
} }
@ -1616,11 +1619,11 @@ static int ospf_sr_update_schedule(struct thread *t)
monotime(&stop_time); monotime(&stop_time);
zlog_info( if (IS_DEBUG_OSPF_SR)
"SR (%s): SPF Processing Time(usecs): %lld\n", zlog_debug("SR (%s): SPF Processing Time(usecs): %lld\n",
__func__, __func__,
(stop_time.tv_sec - start_time.tv_sec) * 1000000LL (stop_time.tv_sec - start_time.tv_sec) * 1000000LL
+ (stop_time.tv_usec - start_time.tv_usec)); + (stop_time.tv_usec - start_time.tv_usec));
OspfSR.update = false; OspfSR.update = false;
return 1; return 1;
@ -2128,91 +2131,197 @@ DEFUN (no_sr_prefix_sid,
static void show_vty_sr_node(struct vty *vty, struct sr_node *srn) static void show_sr_node(struct vty *vty, struct json_object *json,
struct sr_node *srn)
{ {
struct listnode *node; struct listnode *node;
struct sr_link *srl; struct sr_link *srl;
struct sr_prefix *srp; struct sr_prefix *srp;
struct interface *itf; struct interface *itf;
char pref[16]; char pref[19];
char sid[22]; char sid[22];
char label[8]; char label[8];
json_object *json_node = NULL, *json_algo, *json_obj;
json_object *json_prefix = NULL, *json_link = NULL;
/* Sanity Check */ /* Sanity Check */
if (srn == NULL) if (srn == NULL)
return; return;
vty_out(vty, "SR-Node: %s", inet_ntoa(srn->adv_router)); if (json) {
vty_out(vty, "\tSRGB (Size/Label): %u/%u", srn->srgb.range_size, json_node = json_object_new_object();
srn->srgb.lower_bound); json_object_string_add(json_node, "routerID",
vty_out(vty, "\tAlgorithm(s): %s", inet_ntoa(srn->adv_router));
srn->algo[0] == SR_ALGORITHM_SPF ? "SPF" : "S-SPF"); json_object_int_add(json_node, "srgbSize",
for (int i = 1; i < ALGORITHM_COUNT; i++) { srn->srgb.range_size);
if (srn->algo[i] == SR_ALGORITHM_UNSET) json_object_int_add(json_node, "srgbLabel",
continue; srn->srgb.lower_bound);
vty_out(vty, "/%s", json_algo = json_object_new_array();
srn->algo[i] == SR_ALGORITHM_SPF ? "SPF" : "S-SPF"); json_object_object_add(json_node, "algorithms", json_algo);
} for (int i = 0; i < ALGORITHM_COUNT; i++) {
if (srn->msd != 0) if (srn->algo[i] == SR_ALGORITHM_UNSET)
vty_out(vty, "\tMSD: %u", srn->msd); continue;
json_obj = json_object_new_object();
char tmp[2];
vty_out(vty, snprintf(tmp, 2, "%u", i);
"\n\n Prefix or Link Label In Label Out " json_object_string_add(json_obj, tmp,
"Node or Adj. SID Interface Nexthop\n"); srn->algo[i] == SR_ALGORITHM_SPF ?
vty_out(vty, "SPF" : "S-SPF");
"------------------ -------- --------- " json_object_array_add(json_algo, json_obj);
"--------------------- --------- ---------------\n"); }
if (srn->msd != 0)
json_object_int_add(json_node, "nodeMsd", srn->msd);
} else {
vty_out(vty, "SR-Node: %s", inet_ntoa(srn->adv_router));
vty_out(vty, "\tSRGB (Size/Label): %u/%u",
srn->srgb.range_size, srn->srgb.lower_bound);
vty_out(vty, "\tAlgorithm(s): %s",
srn->algo[0] == SR_ALGORITHM_SPF ? "SPF" : "S-SPF");
for (int i = 1; i < ALGORITHM_COUNT; i++) {
if (srn->algo[i] == SR_ALGORITHM_UNSET)
continue;
vty_out(vty, "/%s",
srn->algo[i] == SR_ALGORITHM_SPF ?
"SPF" : "S-SPF");
}
if (srn->msd != 0)
vty_out(vty, "\tMSD: %u", srn->msd);
}
if (!json) {
vty_out(vty,
"\n\n Prefix or Link Label In Label Out "
"Node or Adj. SID Interface Nexthop\n");
vty_out(vty,
"------------------ -------- --------- "
"--------------------- --------- ---------------\n");
}
for (ALL_LIST_ELEMENTS_RO(srn->ext_prefix, node, srp)) { for (ALL_LIST_ELEMENTS_RO(srn->ext_prefix, node, srp)) {
strncpy(pref, inet_ntoa(srp->nhlfe.prefv4.prefix), 16); snprintf(pref, 19, "%s/%u",
inet_ntoa(srp->nhlfe.prefv4.prefix),
srp->nhlfe.prefv4.prefixlen);
snprintf(sid, 22, "SR Pfx (idx %u)", srp->sid); snprintf(sid, 22, "SR Pfx (idx %u)", srp->sid);
if (srp->nhlfe.label_out == MPLS_LABEL_IMPLICIT_NULL) if (srp->nhlfe.label_out == MPLS_LABEL_IMPLICIT_NULL)
sprintf(label, "pop"); sprintf(label, "pop");
else else
sprintf(label, "%u", srp->nhlfe.label_out); sprintf(label, "%u", srp->nhlfe.label_out);
itf = if_lookup_by_index(srp->nhlfe.ifindex, VRF_DEFAULT); itf = if_lookup_by_index(srp->nhlfe.ifindex, VRF_DEFAULT);
vty_out(vty, "%15s/%u %8u %9s %21s %9s %15s\n", pref, if (json) {
srp->nhlfe.prefv4.prefixlen, srp->nhlfe.label_in, label, if (!json_prefix) {
sid, itf ? itf->name : "-", json_prefix = json_object_new_array();
inet_ntoa(srp->nhlfe.nexthop)); json_object_object_add(json_node,
"extendedPrefix", json_prefix);
}
json_obj = json_object_new_object();
json_object_string_add(json_obj, "prefix", pref);
json_object_int_add(json_obj, "sid", srp->sid);
json_object_int_add(json_obj, "inputLabel",
srp->nhlfe.label_in);
json_object_string_add(json_obj, "outputLabel",
label);
json_object_string_add(json_obj, "interface",
itf ? itf->name : "-");
json_object_string_add(json_obj, "nexthop",
inet_ntoa(srp->nhlfe.nexthop));
json_object_array_add(json_prefix, json_obj);
} else {
vty_out(vty, "%18s %8u %9s %21s %9s %15s\n",
pref, srp->nhlfe.label_in, label,
sid, itf ? itf->name : "-",
inet_ntoa(srp->nhlfe.nexthop));
}
} }
for (ALL_LIST_ELEMENTS_RO(srn->ext_link, node, srl)) { for (ALL_LIST_ELEMENTS_RO(srn->ext_link, node, srl)) {
strncpy(pref, inet_ntoa(srl->nhlfe[0].prefv4.prefix), 16); snprintf(pref, 19, "%s/%u",
inet_ntoa(srl->nhlfe[0].prefv4.prefix),
srl->nhlfe[0].prefv4.prefixlen);
snprintf(sid, 22, "SR Adj. (lbl %u)", srl->sid[0]); snprintf(sid, 22, "SR Adj. (lbl %u)", srl->sid[0]);
if (srl->nhlfe[0].label_out == MPLS_LABEL_IMPLICIT_NULL) if (srl->nhlfe[0].label_out == MPLS_LABEL_IMPLICIT_NULL)
sprintf(label, "pop"); sprintf(label, "pop");
else else
sprintf(label, "%u", srl->nhlfe[0].label_out); sprintf(label, "%u", srl->nhlfe[0].label_out);
itf = if_lookup_by_index(srl->nhlfe[0].ifindex, VRF_DEFAULT); itf = if_lookup_by_index(srl->nhlfe[0].ifindex, VRF_DEFAULT);
vty_out(vty, "%15s/%u %8u %9s %21s %9s %15s\n", pref, if (json) {
srl->nhlfe[0].prefv4.prefixlen, srl->nhlfe[0].label_in, if (!json_link) {
label, sid, itf ? itf->name : "-", json_link = json_object_new_array();
inet_ntoa(srl->nhlfe[0].nexthop)); json_object_object_add(json_node,
snprintf(sid, 22, "SR Adj. (lbl %u)", srl->sid[1]); "extendedLink", json_link);
if (srl->nhlfe[1].label_out == MPLS_LABEL_IMPLICIT_NULL) }
sprintf(label, "pop"); /* Primary Link */
else json_obj = json_object_new_object();
sprintf(label, "%u", srl->nhlfe[0].label_out); json_object_string_add(json_obj, "prefix", pref);
vty_out(vty, "%15s/%u %8u %9s %21s %9s %15s\n", pref, json_object_int_add(json_obj, "sid", srl->sid[0]);
srl->nhlfe[1].prefv4.prefixlen, srl->nhlfe[1].label_in, json_object_int_add(json_obj, "inputLabel",
label, sid, itf ? itf->name : "-", srl->nhlfe[0].label_in);
inet_ntoa(srl->nhlfe[1].nexthop)); json_object_string_add(json_obj, "outputLabel",
label);
json_object_string_add(json_obj, "interface",
itf ? itf->name : "-");
json_object_string_add(json_obj, "nexthop",
inet_ntoa(srl->nhlfe[0].nexthop));
json_object_array_add(json_link, json_obj);
/* Backup Link */
json_obj = json_object_new_object();
snprintf(sid, 22, "SR Adj. (lbl %u)", srl->sid[1]);
if (srl->nhlfe[1].label_out == MPLS_LABEL_IMPLICIT_NULL)
sprintf(label, "pop");
else
sprintf(label, "%u", srl->nhlfe[0].label_out);
json_object_string_add(json_obj, "prefix", pref);
json_object_int_add(json_obj, "sid", srl->sid[1]);
json_object_int_add(json_obj, "inputLabel",
srl->nhlfe[1].label_in);
json_object_string_add(json_obj, "outputLabel",
label);
json_object_string_add(json_obj, "interface",
itf ? itf->name : "-");
json_object_string_add(json_obj, "nexthop",
inet_ntoa(srl->nhlfe[1].nexthop));
json_object_array_add(json_link, json_obj);
} else {
vty_out(vty, "%18s %8u %9s %21s %9s %15s\n",
pref, srl->nhlfe[0].label_in,
label, sid, itf ? itf->name : "-",
inet_ntoa(srl->nhlfe[0].nexthop));
snprintf(sid, 22, "SR Adj. (lbl %u)", srl->sid[1]);
if (srl->nhlfe[1].label_out == MPLS_LABEL_IMPLICIT_NULL)
sprintf(label, "pop");
else
sprintf(label, "%u", srl->nhlfe[1].label_out);
vty_out(vty, "%18s %8u %9s %21s %9s %15s\n",
pref, srl->nhlfe[1].label_in,
label, sid, itf ? itf->name : "-",
inet_ntoa(srl->nhlfe[1].nexthop));
}
} }
vty_out(vty, "\n"); if (json)
json_object_array_add(json, json_node);
else
vty_out(vty, "\n");
} }
static void show_srdb_entry(struct hash_backet *backet, void *args) static void show_vty_srdb(struct hash_backet *backet, void *args)
{ {
struct vty *vty = (struct vty *)args; struct vty *vty = (struct vty *)args;
struct sr_node *srn = (struct sr_node *)backet->data; struct sr_node *srn = (struct sr_node *)backet->data;
show_vty_sr_node(vty, srn); show_sr_node(vty, NULL, srn);
}
static void show_json_srdb(struct hash_backet *backet, void *args)
{
struct json_object *json = (struct json_object *)args;
struct sr_node *srn = (struct sr_node *)backet->data;
show_sr_node(NULL, json, srn);
} }
DEFUN (show_ip_opsf_srdb, DEFUN (show_ip_opsf_srdb,
show_ip_ospf_srdb_cmd, show_ip_ospf_srdb_cmd,
"show ip ospf database segment-routing [adv-router A.B.C.D|self-originate]", "show ip ospf database segment-routing [adv-router A.B.C.D|self-originate] [json]",
SHOW_STR SHOW_STR
IP_STR IP_STR
OSPF_STR OSPF_STR
@ -2220,23 +2329,41 @@ DEFUN (show_ip_opsf_srdb,
"Show Segment Routing Data Base\n" "Show Segment Routing Data Base\n"
"Advertising SR node\n" "Advertising SR node\n"
"Advertising SR node ID (as an IP address)\n" "Advertising SR node ID (as an IP address)\n"
"Self-originated SR node\n") "Self-originated SR node\n"
JSON_STR)
{ {
int idx = 0; int idx = 0;
struct in_addr rid; struct in_addr rid;
struct sr_node *srn; struct sr_node *srn;
u_char uj = use_json(argc, argv);
json_object *json = NULL, *json_node_array = NULL;
if (!OspfSR.enabled) { if (!OspfSR.enabled) {
vty_out(vty, "Segment Routing is disabled on this router\n"); vty_out(vty, "Segment Routing is disabled on this router\n");
return CMD_WARNING; return CMD_WARNING;
} }
vty_out(vty, "\n OSPF Segment Routing database for ID %s\n\n", if (uj) {
inet_ntoa(OspfSR.self->adv_router)); json = json_object_new_object();
json_node_array = json_object_new_array();
json_object_string_add(json, "srdbID",
inet_ntoa(OspfSR.self->adv_router));
json_object_object_add(json, "srNodes", json_node_array);
} else {
vty_out(vty,
"\n\t\tOSPF Segment Routing database for ID %s\n\n",
inet_ntoa(OspfSR.self->adv_router));
}
if (argv_find(argv, argc, "self-originate", &idx)) { if (argv_find(argv, argc, "self-originate", &idx)) {
srn = OspfSR.self; srn = OspfSR.self;
show_vty_sr_node(vty, srn); show_sr_node(vty, json_node_array, srn);
if (uj) {
vty_out(vty, "%s\n",
json_object_to_json_string_ext(json,
JSON_C_TO_STRING_PRETTY));
json_object_free(json);
}
return CMD_SUCCESS; return CMD_SUCCESS;
} }
@ -2250,15 +2377,32 @@ DEFUN (show_ip_opsf_srdb,
/* Get the SR Node from the SRDB */ /* Get the SR Node from the SRDB */
srn = (struct sr_node *)hash_lookup(OspfSR.neighbors, srn = (struct sr_node *)hash_lookup(OspfSR.neighbors,
(void *)&rid); (void *)&rid);
show_vty_sr_node(vty, srn); show_sr_node(vty, json_node_array, srn);
if (uj) {
vty_out(vty, "%s\n",
json_object_to_json_string_ext(json,
JSON_C_TO_STRING_PRETTY));
json_object_free(json);
}
return CMD_SUCCESS; return CMD_SUCCESS;
} }
/* No parameters have been provided, Iterate through all the SRDB */ /* No parameters have been provided, Iterate through all the SRDB */
hash_iterate( if (uj) {
OspfSR.neighbors, hash_iterate(
(void (*)(struct hash_backet *, void *))show_srdb_entry, OspfSR.neighbors,
(void *)vty); (void (*)(struct hash_backet *, void *))show_json_srdb,
(void *)json_node_array);
vty_out(vty, "%s\n",
json_object_to_json_string_ext(json,
JSON_C_TO_STRING_PRETTY));
json_object_free(json);
} else {
hash_iterate(
OspfSR.neighbors,
(void (*)(struct hash_backet *, void *))show_vty_srdb,
(void *)vty);
}
return CMD_SUCCESS; return CMD_SUCCESS;
} }

4
tests/.gitignore vendored
View File

@ -24,10 +24,12 @@ __pycache__
/bgpd/test_ecommunity /bgpd/test_ecommunity
/bgpd/test_mp_attr /bgpd/test_mp_attr
/bgpd/test_mpath /bgpd/test_mpath
/bgpd/test_packet
/isisd/test_fuzz_isis_tlv /isisd/test_fuzz_isis_tlv
/isisd/test_fuzz_isis_tlv_tests.h /isisd/test_fuzz_isis_tlv_tests.h
/isisd/test_isis_vertex_queue /isisd/test_isis_vertex_queue
/lib/cli/test_cli /lib/cli/test_cli
/lib/cli/test_cli_clippy.c
/lib/cli/test_commands /lib/cli/test_commands
/lib/cli/test_commands_defun.c /lib/cli/test_commands_defun.c
/lib/test_buffer /lib/test_buffer
@ -38,6 +40,7 @@ __pycache__
/lib/test_memory /lib/test_memory
/lib/test_nexthop_iter /lib/test_nexthop_iter
/lib/test_privs /lib/test_privs
/lib/test_ringbuf
/lib/test_srcdest_table /lib/test_srcdest_table
/lib/test_segv /lib/test_segv
/lib/test_sig /lib/test_sig
@ -48,3 +51,4 @@ __pycache__
/lib/test_ttable /lib/test_ttable
/lib/test_zmq /lib/test_zmq
/ospf6d/test_lsdb /ospf6d/test_lsdb
/ospf6d/test_lsdb_clippy.c

View File

@ -1,4 +1,6 @@
import frrtest import frrtest
class TestRingbuf(frrtest.TestExitNonzero): class TestRingbuf(frrtest.TestMultiOut):
program = './test_ringbuf' program = './test_ringbuf'
TestRingbuf.exit_cleanly()

View File

@ -1166,12 +1166,12 @@ static int zread_route_add(struct zserv *client, u_short length,
switch (api_nh->type) { switch (api_nh->type) {
case NEXTHOP_TYPE_IFINDEX: case NEXTHOP_TYPE_IFINDEX:
nexthop = route_entry_nexthop_ifindex_add( nexthop = route_entry_nexthop_ifindex_add(
re, api_nh->ifindex, re->vrf_id); re, api_nh->ifindex, api_nh->vrf_id);
break; break;
case NEXTHOP_TYPE_IPV4: case NEXTHOP_TYPE_IPV4:
nexthop = route_entry_nexthop_ipv4_add( nexthop = route_entry_nexthop_ipv4_add(
re, &api_nh->gate.ipv4, NULL, re, &api_nh->gate.ipv4, NULL,
re->vrf_id); api_nh->vrf_id);
break; break;
case NEXTHOP_TYPE_IPV4_IFINDEX: { case NEXTHOP_TYPE_IPV4_IFINDEX: {
@ -1188,7 +1188,7 @@ static int zread_route_add(struct zserv *client, u_short length,
nexthop = route_entry_nexthop_ipv4_ifindex_add( nexthop = route_entry_nexthop_ipv4_ifindex_add(
re, &api_nh->gate.ipv4, NULL, ifindex, re, &api_nh->gate.ipv4, NULL, ifindex,
re->vrf_id); api_nh->vrf_id);
/* if this an EVPN route entry, /* if this an EVPN route entry,
program the nh as neigh program the nh as neigh
@ -1211,12 +1211,12 @@ static int zread_route_add(struct zserv *client, u_short length,
} }
case NEXTHOP_TYPE_IPV6: case NEXTHOP_TYPE_IPV6:
nexthop = route_entry_nexthop_ipv6_add( nexthop = route_entry_nexthop_ipv6_add(
re, &api_nh->gate.ipv6, re->vrf_id); re, &api_nh->gate.ipv6, api_nh->vrf_id);
break; break;
case NEXTHOP_TYPE_IPV6_IFINDEX: case NEXTHOP_TYPE_IPV6_IFINDEX:
nexthop = route_entry_nexthop_ipv6_ifindex_add( nexthop = route_entry_nexthop_ipv6_ifindex_add(
re, &api_nh->gate.ipv6, api_nh->ifindex, re, &api_nh->gate.ipv6, api_nh->ifindex,
re->vrf_id); api_nh->vrf_id);
break; break;
case NEXTHOP_TYPE_BLACKHOLE: case NEXTHOP_TYPE_BLACKHOLE:
nexthop = route_entry_nexthop_blackhole_add( nexthop = route_entry_nexthop_blackhole_add(