pimd: Re-deisgn the "clear ip mroute" command.
{
int i;
char *str;
- char *pnt;
int len;
int first;
uint32_t comval;
}
/* Allocate memory. */
- str = pnt = XMALLOC(MTYPE_COMMUNITY_STR, len);
+ str = XCALLOC(MTYPE_COMMUNITY_STR, len);
first = 1;
/* Fill in string. */
if (first)
first = 0;
else
- *pnt++ = ' ';
+ strlcat(str, " ", len);
switch (comval) {
case COMMUNITY_INTERNET:
- strcpy(pnt, "internet");
- pnt += strlen("internet");
+ strlcat(str, "internet", len);
if (make_json) {
json_string =
json_object_new_string("internet");
}
break;
case COMMUNITY_GSHUT:
- strcpy(pnt, "graceful-shutdown");
- pnt += strlen("graceful-shutdown");
+ strlcat(str, "graceful-shutdown", len);
if (make_json) {
json_string = json_object_new_string(
"gracefulShutdown");
}
break;
case COMMUNITY_ACCEPT_OWN:
- strcpy(pnt, "accept-own");
- pnt += strlen("accept-own");
+ strlcat(str, "accept-own", len);
if (make_json) {
json_string = json_object_new_string(
"acceptown");
}
break;
case COMMUNITY_ROUTE_FILTER_TRANSLATED_v4:
- strcpy(pnt, "route-filter-translated-v4");
- pnt += strlen("route-filter-translated-v4");
+ strlcat(str, "route-filter-translated-v4", len);
if (make_json) {
json_string = json_object_new_string(
"routeFilterTranslatedV4");
}
break;
case COMMUNITY_ROUTE_FILTER_v4:
- strcpy(pnt, "route-filter-v4");
- pnt += strlen("route-filter-v4");
+ strlcat(str, "route-filter-v4", len);
if (make_json) {
json_string = json_object_new_string(
"routeFilterV4");
}
break;
case COMMUNITY_ROUTE_FILTER_TRANSLATED_v6:
- strcpy(pnt, "route-filter-translated-v6");
- pnt += strlen("route-filter-translated-v6");
+ strlcat(str, "route-filter-translated-v6", len);
if (make_json) {
json_string = json_object_new_string(
"routeFilterTranslatedV6");
}
break;
case COMMUNITY_ROUTE_FILTER_v6:
- strcpy(pnt, "route-filter-v6");
- pnt += strlen("route-filter-v6");
+ strlcat(str, "route-filter-v6", len);
if (make_json) {
json_string = json_object_new_string(
"routeFilterV6");
}
break;
case COMMUNITY_LLGR_STALE:
- strcpy(pnt, "llgr-stale");
- pnt += strlen("llgr-stale");
+ strlcat(str, "llgr-stale", len);
if (make_json) {
json_string = json_object_new_string(
"llgrStale");
}
break;
case COMMUNITY_NO_LLGR:
- strcpy(pnt, "no-llgr");
- pnt += strlen("no-llgr");
+ strlcat(str, "no-llgr", len);
if (make_json) {
json_string = json_object_new_string(
"noLlgr");
}
break;
case COMMUNITY_ACCEPT_OWN_NEXTHOP:
- strcpy(pnt, "accept-own-nexthop");
- pnt += strlen("accept-own-nexthop");
+ strlcat(str, "accept-own-nexthop", len);
if (make_json) {
json_string = json_object_new_string(
"acceptownnexthop");
}
break;
case COMMUNITY_BLACKHOLE:
- strcpy(pnt, "blackhole");
- pnt += strlen("blackhole");
+ strlcat(str, "blackhole", len);
if (make_json) {
json_string = json_object_new_string(
"blackhole");
}
break;
case COMMUNITY_NO_EXPORT:
- strcpy(pnt, "no-export");
- pnt += strlen("no-export");
+ strlcat(str, "no-export", len);
if (make_json) {
json_string =
json_object_new_string("noExport");
}
break;
case COMMUNITY_NO_ADVERTISE:
- strcpy(pnt, "no-advertise");
- pnt += strlen("no-advertise");
+ strlcat(str, "no-advertise", len);
if (make_json) {
json_string =
json_object_new_string("noAdvertise");
}
break;
case COMMUNITY_LOCAL_AS:
- strcpy(pnt, "local-AS");
- pnt += strlen("local-AS");
+ strlcat(str, "local-AS", len);
if (make_json) {
json_string = json_object_new_string("localAs");
json_object_array_add(json_community_list,
}
break;
case COMMUNITY_NO_PEER:
- strcpy(pnt, "no-peer");
- pnt += strlen("no-peer");
+ strlcat(str, "no-peer", len);
if (make_json) {
json_string = json_object_new_string("noPeer");
json_object_array_add(json_community_list,
default:
as = (comval >> 16) & 0xFFFF;
val = comval & 0xFFFF;
- sprintf(pnt, "%u:%d", as, val);
+ char buf[32];
+ snprintf(buf, sizeof(buf), "%u:%d", as, val);
+ strlcat(str, buf, len);
if (make_json) {
- json_string = json_object_new_string(pnt);
+ json_string = json_object_new_string(buf);
json_object_array_add(json_community_list,
json_string);
}
- pnt += strlen(pnt);
break;
}
}
- *pnt = '\0';
if (make_json) {
json_object_string_add(com->json, "string", str);
if (bgp_debug_zebra(NULL)) {
zlog_debug(
- "installing evpn prefix %s as ip prefix %s in vrf %s",
+ "import evpn prefix %s as ip prefix %s in vrf %s",
prefix2str(evp, buf, sizeof(buf)),
prefix2str(pp, buf1, sizeof(buf)),
vrf_id_to_name(bgp_vrf->vrf_id));
/* apply the route-map */
if (bgp_vrf->adv_cmd_rmap[afi][safi].map) {
- int ret = 0;
+ route_map_result_t ret;
ret = route_map_apply(
bgp_vrf->adv_cmd_rmap[afi][safi]
else if ((peer->status == Established) && (status != Established))
bgp->established_peers--;
- if (BGP_DEBUG(neighbor_events, NEIGHBOR_EVENTS))
- zlog_debug("%s : vrf %u, established_peers %u", __func__,
- bgp->vrf_id, bgp->established_peers);
+ if (bgp_debug_neighbor_events(peer)) {
+ struct vrf *vrf = vrf_lookup_by_id(bgp->vrf_id);
+
+ zlog_debug("%s : vrf %s(%u), Status: %s established_peers %u", __func__,
+ vrf ? vrf->name : "Unknown", bgp->vrf_id,
+ lookup_msg(bgp_status_msg, status, NULL),
+ bgp->established_peers);
+ }
+
/* Set to router ID to the value provided by RIB if there are no peers
* in the established state and peer count did not change
*/
XMALLOC(MTYPE_TMP, bgp_notify.length * 3);
for (i = 0; i < bgp_notify.length; i++)
if (first) {
- sprintf(c, " %02x", data[i]);
- strcat(bgp_notify.data, c);
+ snprintf(c, sizeof(c), " %02x",
+ data[i]);
+ strlcat(bgp_notify.data, c,
+ bgp_notify.length);
} else {
first = 1;
- sprintf(c, "%02x", data[i]);
- strcpy(bgp_notify.data, c);
+ snprintf(c, sizeof(c), "%02x", data[i]);
+ strlcpy(bgp_notify.data, c,
+ bgp_notify.length);
}
}
bgp_notify_print(peer, &bgp_notify, "sending");
XMALLOC(MTYPE_TMP, bgp_notify.length * 3);
for (i = 0; i < bgp_notify.length; i++)
if (first) {
- sprintf(c, " %02x",
+ snprintf(c, sizeof(c), " %02x",
stream_getc(peer->curr));
- strcat(bgp_notify.data, c);
+ strlcat(bgp_notify.data, c,
+ bgp_notify.length);
} else {
first = 1;
- sprintf(c, "%02x",
- stream_getc(peer->curr));
- strcpy(bgp_notify.data, c);
+ snprintf(c, sizeof(c), "%02x",
+ stream_getc(peer->curr));
+ strlcpy(bgp_notify.data, c,
+ bgp_notify.length);
}
bgp_notify.raw_data = (uint8_t *)peer->notify.data;
}
__FUNCTION__, peer->host);
break;
default:
+ /* Suppress uninitialized variable warning */
+ mprc = 0;
+ (void)mprc;
/*
* The message type should have been sanitized before
* we ever got here. Receipt of a message with an
struct bgp *bgp;
struct attr *piattr;
char buf[PREFIX_STRLEN];
- int ret;
+ route_map_result_t ret;
int transparent;
int reflect;
afi_t afi;
/* apply the route-map */
if (bgp->adv_cmd_rmap[afi][safi].map) {
- int ret = 0;
+ route_map_result_t ret;
ret = route_map_apply(
bgp->adv_cmd_rmap[afi][safi].map,
&rn->p, RMAP_BGP, new_select);
- if (ret == RMAP_MATCH)
+ if (ret == RMAP_PERMITMATCH)
bgp_evpn_advertise_type5_route(
bgp, &rn->p, new_select->attr,
afi, safi);
struct bgp_path_info rmap_path;
struct attr attr;
struct attr *attr_new;
- int ret;
+ route_map_result_t ret;
#if ENABLE_BGP_VNC
int vnc_implicit_withdraw = 0;
#endif
if (bgp_static->rmap.name) {
struct attr attr_tmp = attr;
struct bgp_path_info rmap_path;
- int ret;
+ route_map_result_t ret;
rmap_path.peer = bgp->peer_self;
rmap_path.attr = &attr_tmp;
struct attr attr;
struct attr *new_attr;
afi_t afi;
- int ret;
+ route_map_result_t ret;
struct bgp_redist *red;
/* Make default attribute. */
struct route_map *rmap = output_arg;
struct bgp_path_info path;
struct attr dummy_attr;
- int ret;
+ route_map_result_t ret;
bgp_attr_dup(&dummy_attr, pi->attr);
/* Compares the peer specified in the 'match peer' clause with the peer
received in bgp_path_info->peer. If it is the same, or if the peer structure
received is a peer_group containing it, returns RMAP_MATCH. */
-static route_map_result_t route_match_peer(void *rule,
- const struct prefix *prefix,
- route_map_object_t type,
- void *object)
+static enum route_map_match_result_t
+route_match_peer(void *rule, const struct prefix *prefix,
+ route_map_object_t type, void *object)
{
struct bgp_match_peer_compiled *pc;
union sockunion *su;
route_match_peer_free};
#if defined(HAVE_LUA)
-static route_map_result_t route_match_command(void *rule,
- const struct prefix *prefix,
- route_map_object_t type,
- void *object)
+static enum route_map_match_result_t
+route_match_command(void *rule, const struct prefix *prefix,
+ route_map_object_t type, void *object)
{
int status = RMAP_NOMATCH;
u_int32_t locpref = 0;
/* Match function should return 1 if match is success else return
zero. */
-static route_map_result_t route_match_ip_address(void *rule,
- const struct prefix *prefix,
- route_map_object_t type,
- void *object)
+static enum route_map_match_result_t
+route_match_ip_address(void *rule, const struct prefix *prefix,
+ route_map_object_t type, void *object)
{
struct access_list *alist;
/* `match ip next-hop IP_ADDRESS' */
/* Match function return 1 if match is success else return zero. */
-static route_map_result_t route_match_ip_next_hop(void *rule,
- const struct prefix *prefix,
- route_map_object_t type,
- void *object)
+static enum route_map_match_result_t
+route_match_ip_next_hop(void *rule, const struct prefix *prefix,
+ route_map_object_t type, void *object)
{
struct access_list *alist;
struct bgp_path_info *path;
/* `match ip route-source ACCESS-LIST' */
/* Match function return 1 if match is success else return zero. */
-static route_map_result_t route_match_ip_route_source(void *rule,
- const struct prefix *pfx,
- route_map_object_t type,
- void *object)
+static enum route_map_match_result_t
+route_match_ip_route_source(void *rule, const struct prefix *pfx,
+ route_map_object_t type, void *object)
{
struct access_list *alist;
struct bgp_path_info *path;
/* `match ip address prefix-list PREFIX_LIST' */
-static route_map_result_t
+static enum route_map_match_result_t
route_match_ip_address_prefix_list(void *rule, const struct prefix *prefix,
route_map_object_t type, void *object)
{
/* `match ip next-hop prefix-list PREFIX_LIST' */
-static route_map_result_t
+static enum route_map_match_result_t
route_match_ip_next_hop_prefix_list(void *rule, const struct prefix *prefix,
route_map_object_t type, void *object)
{
/* `match ip next-hop type <blackhole>' */
-static route_map_result_t
+static enum route_map_match_result_t
route_match_ip_next_hop_type(void *rule, const struct prefix *prefix,
route_map_object_t type, void *object)
{
/* `match ip route-source prefix-list PREFIX_LIST' */
-static route_map_result_t
+static enum route_map_match_result_t
route_match_ip_route_source_prefix_list(void *rule,
const struct prefix *prefix,
route_map_object_t type, void *object)
/* `match evpn default-route' */
/* Match function should return 1 if match is success else 0 */
-static route_map_result_t route_match_evpn_default_route(void *rule,
- const struct prefix *p,
- route_map_object_t
- type, void *object)
+static enum route_map_match_result_t
+route_match_evpn_default_route(void *rule, const struct prefix *p,
+ route_map_object_t type, void *object)
{
if (type == RMAP_BGP && is_evpn_prefix_default(p))
return RMAP_MATCH;
/* Match function should return 1 if match is success else return
zero. */
-static route_map_result_t route_match_mac_address(void *rule,
- const struct prefix *prefix,
- route_map_object_t type,
- void *object)
+static enum route_map_match_result_t
+route_match_mac_address(void *rule, const struct prefix *prefix,
+ route_map_object_t type, void *object)
{
struct access_list *alist;
struct prefix p;
/* Match function should return 1 if match is success else return
zero. */
-static route_map_result_t route_match_vni(void *rule,
- const struct prefix *prefix,
- route_map_object_t type, void *object)
+static enum route_map_match_result_t
+route_match_vni(void *rule, const struct prefix *prefix,
+ route_map_object_t type, void *object)
{
vni_t vni = 0;
struct bgp_path_info *path = NULL;
/* Match function should return 1 if match is success else return
zero. */
-static route_map_result_t route_match_evpn_route_type(void *rule,
- const struct prefix *pfx,
- route_map_object_t type,
- void *object)
+static enum route_map_match_result_t
+route_match_evpn_route_type(void *rule, const struct prefix *pfx,
+ route_map_object_t type, void *object)
{
uint8_t route_type = 0;
route_match_evpn_route_type_compile, route_match_evpn_route_type_free};
/* Route map commands for VRF route leak with source vrf matching */
-static route_map_result_t
+static enum route_map_match_result_t
route_match_vrl_source_vrf(void *rule, const struct prefix *prefix,
route_map_object_t type, void *object)
{
/* `match local-preference LOCAL-PREF' */
/* Match function return 1 if match is success else return zero. */
-static route_map_result_t route_match_local_pref(void *rule,
- const struct prefix *prefix,
- route_map_object_t type,
- void *object)
+static enum route_map_match_result_t
+route_match_local_pref(void *rule, const struct prefix *prefix,
+ route_map_object_t type, void *object)
{
uint32_t *local_pref;
struct bgp_path_info *path;
/* `match metric METRIC' */
/* Match function return 1 if match is success else return zero. */
-static route_map_result_t route_match_metric(void *rule,
- const struct prefix *prefix,
- route_map_object_t type,
- void *object)
+static enum route_map_match_result_t
+route_match_metric(void *rule, const struct prefix *prefix,
+ route_map_object_t type, void *object)
{
struct rmap_value *rv;
struct bgp_path_info *path;
/* `match as-path ASPATH' */
/* Match function for as-path match. I assume given object is */
-static route_map_result_t route_match_aspath(void *rule,
- const struct prefix *prefix,
- route_map_object_t type,
- void *object)
+static enum route_map_match_result_t
+route_match_aspath(void *rule, const struct prefix *prefix,
+ route_map_object_t type, void *object)
{
struct as_list *as_list;
};
/* Match function for community match. */
-static route_map_result_t route_match_community(void *rule,
- const struct prefix *prefix,
- route_map_object_t type,
- void *object)
+static enum route_map_match_result_t
+route_match_community(void *rule, const struct prefix *prefix,
+ route_map_object_t type, void *object)
{
struct community_list *list;
struct bgp_path_info *path;
route_match_community_free};
/* Match function for lcommunity match. */
-static route_map_result_t route_match_lcommunity(void *rule,
- const struct prefix *prefix,
- route_map_object_t type,
- void *object)
+static enum route_map_match_result_t
+route_match_lcommunity(void *rule, const struct prefix *prefix,
+ route_map_object_t type, void *object)
{
struct community_list *list;
struct bgp_path_info *path;
/* Match function for extcommunity match. */
-static route_map_result_t route_match_ecommunity(void *rule,
- const struct prefix *prefix,
- route_map_object_t type,
- void *object)
+static enum route_map_match_result_t
+route_match_ecommunity(void *rule, const struct prefix *prefix,
+ route_map_object_t type, void *object)
{
struct community_list *list;
struct bgp_path_info *path;
and `address-family vpnv4'. */
/* `match origin' */
-static route_map_result_t route_match_origin(void *rule,
- const struct prefix *prefix,
- route_map_object_t type,
- void *object)
+static enum route_map_match_result_t
+route_match_origin(void *rule, const struct prefix *prefix,
+ route_map_object_t type, void *object)
{
uint8_t *origin;
struct bgp_path_info *path;
/* match probability { */
-static route_map_result_t route_match_probability(void *rule,
- const struct prefix *prefix,
- route_map_object_t type,
- void *object)
+static enum route_map_match_result_t
+route_match_probability(void *rule, const struct prefix *prefix,
+ route_map_object_t type, void *object)
{
long r = random();
/* `match interface IFNAME' */
/* Match function should return 1 if match is success else return
zero. */
-static route_map_result_t route_match_interface(void *rule,
- const struct prefix *prefix,
- route_map_object_t type,
- void *object)
+static enum route_map_match_result_t
+route_match_interface(void *rule, const struct prefix *prefix,
+ route_map_object_t type, void *object)
{
struct interface *ifp;
struct bgp_path_info *path;
/* `set ip next-hop IP_ADDRESS' */
/* Match function return 1 if match is success else return zero. */
-static route_map_result_t route_match_tag(void *rule,
- const struct prefix *prefix,
- route_map_object_t type, void *object)
+static enum route_map_match_result_t
+route_match_tag(void *rule, const struct prefix *prefix,
+ route_map_object_t type, void *object)
{
route_tag_t *tag;
struct bgp_path_info *path;
int unchanged;
};
-static route_map_result_t route_set_ip_nexthop(void *rule,
- const struct prefix *prefix,
- route_map_object_t type,
- void *object)
+static enum route_map_match_result_t
+route_set_ip_nexthop(void *rule, const struct prefix *prefix,
+ route_map_object_t type, void *object)
{
struct rmap_ip_nexthop_set *rins = rule;
struct bgp_path_info *path;
/* `set local-preference LOCAL_PREF' */
/* Set local preference. */
-static route_map_result_t route_set_local_pref(void *rule,
- const struct prefix *prefix,
- route_map_object_t type,
- void *object)
+static enum route_map_match_result_t
+route_set_local_pref(void *rule, const struct prefix *prefix,
+ route_map_object_t type, void *object)
{
struct rmap_value *rv;
struct bgp_path_info *path;
/* `set weight WEIGHT' */
/* Set weight. */
-static route_map_result_t route_set_weight(void *rule,
- const struct prefix *prefix,
- route_map_object_t type,
- void *object)
+static enum route_map_match_result_t
+route_set_weight(void *rule, const struct prefix *prefix,
+ route_map_object_t type, void *object)
{
struct rmap_value *rv;
struct bgp_path_info *path;
/* `set metric METRIC' */
/* Set metric to attribute. */
-static route_map_result_t route_set_metric(void *rule,
- const struct prefix *prefix,
- route_map_object_t type,
- void *object)
+static enum route_map_match_result_t
+route_set_metric(void *rule, const struct prefix *prefix,
+ route_map_object_t type, void *object)
{
struct rmap_value *rv;
struct bgp_path_info *path;
/* `set as-path prepend ASPATH' */
/* For AS path prepend mechanism. */
-static route_map_result_t route_set_aspath_prepend(void *rule,
- const struct prefix *prefix,
- route_map_object_t type,
- void *object)
+static enum route_map_match_result_t
+route_set_aspath_prepend(void *rule, const struct prefix *prefix,
+ route_map_object_t type, void *object)
{
struct aspath *aspath;
struct aspath *new;
* one.
* Make a deep copy of existing AS_PATH, but for the first ASn only.
*/
-static route_map_result_t route_set_aspath_exclude(void *rule,
- const struct prefix *dummy,
- route_map_object_t type,
- void *object)
+static enum route_map_match_result_t
+route_set_aspath_exclude(void *rule, const struct prefix *dummy,
+ route_map_object_t type, void *object)
{
struct aspath *new_path, *exclude_path;
struct bgp_path_info *path;
};
/* For community set mechanism. */
-static route_map_result_t route_set_community(void *rule,
- const struct prefix *prefix,
- route_map_object_t type,
- void *object)
+static enum route_map_match_result_t
+route_set_community(void *rule, const struct prefix *prefix,
+ route_map_object_t type, void *object)
{
struct rmap_com_set *rcs;
struct bgp_path_info *path;
/* For lcommunity set mechanism. */
-static route_map_result_t route_set_lcommunity(void *rule,
- const struct prefix *prefix,
- route_map_object_t type,
- void *object)
+static enum route_map_match_result_t
+route_set_lcommunity(void *rule, const struct prefix *prefix,
+ route_map_object_t type, void *object)
{
struct rmap_lcom_set *rcs;
struct bgp_path_info *path;
/* `set large-comm-list (<1-99>|<100-500>|WORD) delete' */
/* For large community set mechanism. */
-static route_map_result_t route_set_lcommunity_delete(void *rule,
- const struct prefix *pfx,
- route_map_object_t type,
- void *object)
+static enum route_map_match_result_t
+route_set_lcommunity_delete(void *rule, const struct prefix *pfx,
+ route_map_object_t type, void *object)
{
struct community_list *list;
struct lcommunity *merge;
/* `set comm-list (<1-99>|<100-500>|WORD) delete' */
/* For community set mechanism. */
-static route_map_result_t route_set_community_delete(
- void *rule,
- const struct prefix *prefix,
- route_map_object_t type,
- void *object)
+static enum route_map_match_result_t
+route_set_community_delete(void *rule, const struct prefix *prefix,
+ route_map_object_t type, void *object)
{
struct community_list *list;
struct community *merge;
/* `set extcommunity rt COMMUNITY' */
/* For community set mechanism. Used by _rt and _soo. */
-static route_map_result_t route_set_ecommunity(void *rule,
- const struct prefix *prefix,
- route_map_object_t type,
- void *object)
+static enum route_map_match_result_t
+route_set_ecommunity(void *rule, const struct prefix *prefix,
+ route_map_object_t type, void *object)
{
struct ecommunity *ecom;
struct ecommunity *new_ecom;
/* `set origin ORIGIN' */
/* For origin set. */
-static route_map_result_t route_set_origin(void *rule,
- const struct prefix *prefix,
- route_map_object_t type,
- void *object)
+static enum route_map_match_result_t
+route_set_origin(void *rule, const struct prefix *prefix,
+ route_map_object_t type, void *object)
{
uint8_t *origin;
struct bgp_path_info *path;
/* `set atomic-aggregate' */
/* For atomic aggregate set. */
-static route_map_result_t route_set_atomic_aggregate(void *rule,
- const struct prefix *pfx,
- route_map_object_t type,
- void *object)
+static enum route_map_match_result_t
+route_set_atomic_aggregate(void *rule, const struct prefix *pfx,
+ route_map_object_t type, void *object)
{
struct bgp_path_info *path;
struct in_addr address;
};
-static route_map_result_t route_set_aggregator_as(void *rule,
- const struct prefix *prefix,
- route_map_object_t type,
- void *object)
+static enum route_map_match_result_t
+route_set_aggregator_as(void *rule, const struct prefix *prefix,
+ route_map_object_t type, void *object)
{
struct bgp_path_info *path;
struct aggregator *aggregator;
};
/* Set tag to object. object must be pointer to struct bgp_path_info */
-static route_map_result_t route_set_tag(void *rule,
- const struct prefix *prefix,
- route_map_object_t type, void *object)
+static enum route_map_match_result_t
+route_set_tag(void *rule, const struct prefix *prefix,
+ route_map_object_t type, void *object)
{
route_tag_t *tag;
struct bgp_path_info *path;
};
/* Set label-index to object. object must be pointer to struct bgp_path_info */
-static route_map_result_t route_set_label_index(void *rule,
- const struct prefix *prefix,
- route_map_object_t type,
- void *object)
+static enum route_map_match_result_t
+route_set_label_index(void *rule, const struct prefix *prefix,
+ route_map_object_t type, void *object)
{
struct rmap_value *rv;
struct bgp_path_info *path;
/* `match ipv6 address IP_ACCESS_LIST' */
-static route_map_result_t route_match_ipv6_address(void *rule,
- const struct prefix *prefix,
- route_map_object_t type,
- void *object)
+static enum route_map_match_result_t
+route_match_ipv6_address(void *rule, const struct prefix *prefix,
+ route_map_object_t type, void *object)
{
struct access_list *alist;
/* `match ipv6 next-hop IP_ADDRESS' */
-static route_map_result_t route_match_ipv6_next_hop(void *rule,
- const struct prefix *prefix,
- route_map_object_t type,
- void *object)
+static enum route_map_match_result_t
+route_match_ipv6_next_hop(void *rule, const struct prefix *prefix,
+ route_map_object_t type, void *object)
{
struct in6_addr *addr = rule;
struct bgp_path_info *path;
/* `match ipv6 address prefix-list PREFIX_LIST' */
-static route_map_result_t
+static enum route_map_match_result_t
route_match_ipv6_address_prefix_list(void *rule, const struct prefix *prefix,
route_map_object_t type, void *object)
{
/* `match ipv6 next-hop type <TYPE>' */
-static route_map_result_t
+static enum route_map_match_result_t
route_match_ipv6_next_hop_type(void *rule, const struct prefix *prefix,
- route_map_object_t type, void *object)
+ route_map_object_t type, void *object)
{
struct bgp_path_info *path;
struct in6_addr *addr = rule;
/* `set ipv6 nexthop global IP_ADDRESS' */
/* Set nexthop to object. ojbect must be pointer to struct attr. */
-static route_map_result_t route_set_ipv6_nexthop_global(void *rule,
- const struct prefix *p,
- route_map_object_t type,
- void *object)
+static enum route_map_match_result_t
+route_set_ipv6_nexthop_global(void *rule, const struct prefix *p,
+ route_map_object_t type, void *object)
{
struct in6_addr *address;
struct bgp_path_info *path;
route_set_ipv6_nexthop_global_free};
/* Set next-hop preference value. */
-static route_map_result_t
+static enum route_map_match_result_t
route_set_ipv6_nexthop_prefer_global(void *rule, const struct prefix *prefix,
route_map_object_t type, void *object)
{
/* `set ipv6 nexthop local IP_ADDRESS' */
/* Set nexthop to object. ojbect must be pointer to struct attr. */
-static route_map_result_t route_set_ipv6_nexthop_local(void *rule,
- const struct prefix *p,
- route_map_object_t type,
- void *object)
+static enum route_map_match_result_t
+route_set_ipv6_nexthop_local(void *rule, const struct prefix *p,
+ route_map_object_t type, void *object)
{
struct in6_addr *address;
struct bgp_path_info *path;
/* `set ipv6 nexthop peer-address' */
/* Set nexthop to object. ojbect must be pointer to struct attr. */
-static route_map_result_t route_set_ipv6_nexthop_peer(void *rule,
- const struct prefix *pfx,
- route_map_object_t type,
- void *object)
+static enum route_map_match_result_t
+route_set_ipv6_nexthop_peer(void *rule, const struct prefix *pfx,
+ route_map_object_t type, void *object)
{
struct in6_addr peer_address;
struct bgp_path_info *path;
/* `set ipv4 vpn next-hop A.B.C.D' */
-static route_map_result_t route_set_vpnv4_nexthop(void *rule,
- const struct prefix *prefix,
- route_map_object_t type,
- void *object)
+static enum route_map_match_result_t
+route_set_vpnv4_nexthop(void *rule, const struct prefix *prefix,
+ route_map_object_t type, void *object)
{
struct in_addr *address;
struct bgp_path_info *path;
/* `set ipv6 vpn next-hop A.B.C.D' */
-static route_map_result_t route_set_vpnv6_nexthop(void *rule,
- const struct prefix *prefix,
- route_map_object_t type,
- void *object)
+static enum route_map_match_result_t
+route_set_vpnv6_nexthop(void *rule, const struct prefix *prefix,
+ route_map_object_t type, void *object)
{
struct in6_addr *address;
struct bgp_path_info *path;
/* `set originator-id' */
/* For origin set. */
-static route_map_result_t route_set_originator_id(void *rule,
- const struct prefix *prefix,
- route_map_object_t type,
- void *object)
+static enum route_map_match_result_t
+route_set_originator_id(void *rule, const struct prefix *prefix,
+ route_map_object_t type, void *object)
{
struct in_addr *address;
struct bgp_path_info *path;
str = community_str(com, false);
if (additive) {
- argstr = XCALLOC(MTYPE_TMP,
- strlen(str) + strlen(" additive") + 1);
- strcpy(argstr, str);
- strcpy(argstr + strlen(str), " additive");
+ size_t argstr_sz = strlen(str) + strlen(" additive") + 1;
+ argstr = XCALLOC(MTYPE_TMP, argstr_sz);
+ strlcpy(argstr, str, argstr_sz);
+ strlcat(argstr, " additive", argstr_sz);
ret = generic_set_add(vty, VTY_GET_CONTEXT(route_map_index),
"community", argstr);
XFREE(MTYPE_TMP, argstr);
static int is_synchronized(void);
static int is_running(void);
static void route_match_free(void *rule);
-static route_map_result_t route_match(void *rule, const struct prefix *prefix,
- route_map_object_t type, void *object);
+static enum route_map_match_result_t route_match(void *rule,
+ const struct prefix *prefix,
+ route_map_object_t type,
+ void *object);
static void *route_match_compile(const char *arg);
static void revalidate_bgp_node(struct bgp_node *bgp_node, afi_t afi,
safi_t safi);
dest[i] = ntohl(src[i]);
}
-static route_map_result_t route_match(void *rule, const struct prefix *prefix,
- route_map_object_t type, void *object)
+static enum route_map_match_result_t route_match(void *rule,
+ const struct prefix *prefix,
+ route_map_object_t type,
+ void *object)
{
int *rpki_status = rule;
struct bgp_path_info *path;
struct bgp_node *rn;
struct bgp_path_info *ri;
struct peer *peer;
- int ret = RMAP_DENYMATCH;
+ route_map_result_t ret = RMAP_DENYMATCH;
afi_t afi;
safi_t safi;
for (i = 0; i < api.nexthop_num; i++) {
api_nh = &api.nexthops[i];
- if (api_nh->type == NEXTHOP_TYPE_IFINDEX)
+ switch (api_nh->type) {
+ case NEXTHOP_TYPE_IFINDEX:
nh_buf[0] = '\0';
- else {
- if (api_nh->type == NEXTHOP_TYPE_IPV4)
- nh_family = AF_INET;
- else
- nh_family = AF_INET6;
+ break;
+ case NEXTHOP_TYPE_IPV4:
+ case NEXTHOP_TYPE_IPV4_IFINDEX:
+ nh_family = AF_INET;
inet_ntop(nh_family, &api_nh->gate, nh_buf,
sizeof(nh_buf));
+ break;
+ case NEXTHOP_TYPE_IPV6:
+ case NEXTHOP_TYPE_IPV6_IFINDEX:
+ nh_family = AF_INET6;
+ inet_ntop(nh_family, &api_nh->gate, nh_buf,
+ sizeof(nh_buf));
+ break;
+ case NEXTHOP_TYPE_BLACKHOLE:
+ strlcpy(nh_buf, "blackhole", sizeof(nh_buf));
+ break;
+ default:
+ /* Note: add new nexthop case */
+ assert(0);
+ break;
}
label_buf[0] = '\0';
SET_FLAG(api.message, ZAPI_MESSAGE_NEXTHOP);
api_nh = &api.nexthops[0];
+ api.distance = ZEBRA_EBGP_DISTANCE_DEFAULT;
+ SET_FLAG(api.message, ZAPI_MESSAGE_DISTANCE);
+
/* redirect IP */
if (nh->gate.ipv4.s_addr) {
char buff[PREFIX_STRLEN];
timer_service_func = rfapiWithdrawTimerEncap;
break;
default:
+ /* Suppress uninitialized variable warning */
+ rt = NULL;
+ timer_service_func = NULL;
assert(0);
}
--disable-ldpd \
--enable-fpm \
--with-pkg-git-version \
- --with-pkg-extra-version=-MyOwnFRRVersion
+ --with-pkg-extra-version=-MyOwnFRRVersion \
+ SPHINXBUILD=/usr/bin/sphinx-build
make
make check
sudo make install
export TOPOTESTS_CHECK_STDERR=Yes
-(The value doesn't matter at this time. The check is if the env variable exists
-or not) There is no pass/fail on this reporting. The Output will be reported to
-the console::
-
- export TOPOTESTS_CHECK_MEMLEAK="/home/mydir/memleak_"
-
-This will enable the check and output to console and the writing of the
-information to files with the given prefix (followed by testname), ie
-:file:`/home/mydir/memcheck_test_bgp_multiview_topo1.txt` in case of a memory
-leak.
+(The value doesn't matter at this time. The check is whether the env
+variable exists or not.) There is no pass/fail on this reporting; the
+Output will be reported to the console.
Collect Memory Leak Information
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-FRR processes have the capabilities to report remaining memory allocations upon
-exit. To enable the reporting of the memory, define an environment variable
+FRR processes can report unfreed memory allocations upon exit. To
+enable the reporting of memory leaks, define an environment variable
``TOPOTESTS_CHECK_MEMLEAK`` with the file prefix, i.e.::
export TOPOTESTS_CHECK_MEMLEAK="/home/mydir/memleak_"
-This will enable the check and output to console and the writing of the
-information to files with the given prefix (followed by testname), ie
-:file:`/home/mydir/memcheck_test_bgp_multiview_topo1.txt` in case of a memory
-leak.
+This will enable the check and output to console and the writing of
+the information to files with the given prefix (followed by testname),
+ie :file:`/home/mydir/memcheck_test_bgp_multiview_topo1.txt` in case
+of a memory leak.
Running Topotests with AddressSanitizer
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Static Analysis and Sanitizers
------------------------------
-Clang/LLVM comes with a variety of tools that can be used to help find bugs in FRR.
+Clang/LLVM and GCC come with a variety of tools that can be used to help find
+bugs in FRR.
clang-analyze
This is a static analyzer that scans the source code looking for patterns
AddressSanitizer and ThreadSanitizer are available in recent versions of GCC,
but are no longer actively maintained. MemorySanitizer is not available in GCC.
+.. note::
+
+ The different Sanitizers are mostly incompatible with each other. Please
+ refer to GCC/LLVM documentation for details.
+
Additionally, the FRR codebase is regularly scanned with Coverity.
Unfortunately Coverity does not have the ability to handle scanning pull
requests, but after code is merged it will send an email notifying project
members with Coverity access of newly introduced defects.
+Executing non-installed dynamic binaries
+----------------------------------------
+
+Since FRR uses the GNU autotools build system, it inherits its shortcomings.
+To execute a binary directly from the build tree under a wrapper like
+`valgrind`, `gdb` or `strace`, use::
+
+ ./libtool --mode=execute valgrind [--valgrind-opts] zebra/zebra [--zebra-opts]
+
+While replacing valgrind/zebra as needed. The `libtool` script is found in
+the root of the build directory after `./configure` has completed. Its purpose
+is to correctly set up `LD_LIBRARY_PATH` so that libraries from the build tree
+are used. (On some systems, `libtool` is also available from PATH, but this is
+not always the case.)
+
CLI changes
-----------
or IGMP report is received on this interface and the Group is denied by the
prefix-list, PIM will ignore the join or report.
+.. index:: ip igmp last-member-query-count (1-7)
+.. clicmd:: ip igmp last-member-query-count (1-7)
+
+ Set the IGMP last member query count. The default value is 2. 'no' form of
+ this command is used to to configure back to the default value.
+
+.. index:: ip igmp last-member-query-interval (1-255)
+.. clicmd:: ip igmp last-member-query-interval (1-255)
+
+ Set the IGMP last member query interval in deciseconds. The default value is
+ 10 deciseconds. 'no' form of this command is used to to configure back to the
+ default value.
+
.. _pim-multicast-rib-insertion:
PIM Multicast RIB insertion:
Display information about installed into the kernel S,G mroutes and in
addition display data about packet flow for the mroutes.
+.. index:: show ip mroute summary
+.. clicmd:: show ip mroute summary
+
+ Display total number of S,G mroutes and number of S,G mroutes installed
+ into the kernel.
+
.. index:: show ip pim assert
.. clicmd:: show ip pim assert
Create a clause for a route map to match prefixes with the specified RPKI
state.
- **Note** that the matching of invalid prefixes requires that invalid
- prefixes are considered for best path selection, i.e.,
- ``bgp bestpath prefix-validate disallow-invalid`` is not enabled.
-
In the following example, the router prefers valid routes over invalid
prefixes because invalid routes have a lower local preference.
/* `match metric METRIC' */
/* Match function return 1 if match is success else return zero. */
-static route_map_result_t route_match_metric(void *rule, struct prefix *prefix,
- route_map_object_t type,
- void *object)
+static enum route_map_match_result_t
+route_match_metric(void *rule, struct prefix *prefix, route_map_object_t type,
+ void *object)
{
// uint32_t *metric;
// uint32_t check;
/* `match interface IFNAME' */
/* Match function return 1 if match is success else return zero. */
-static route_map_result_t route_match_interface(void *rule,
- struct prefix *prefix,
- route_map_object_t type,
- void *object)
+static enum route_map_match_result_t
+route_match_interface(void *rule, struct prefix *prefix,
+ route_map_object_t type, void *object)
{
// struct rip_info *rinfo;
// struct interface *ifp;
/* `match ip next-hop IP_ACCESS_LIST' */
/* Match function return 1 if match is success else return zero. */
-static route_map_result_t route_match_ip_next_hop(void *rule,
- struct prefix *prefix,
- route_map_object_t type,
- void *object)
+static enum route_map_match_result_t
+route_match_ip_next_hop(void *rule, struct prefix *prefix,
+ route_map_object_t type, void *object)
{
// struct access_list *alist;
// struct rip_info *rinfo;
/* `match ip next-hop prefix-list PREFIX_LIST' */
-static route_map_result_t
+static enum route_map_match_result_t
route_match_ip_next_hop_prefix_list(void *rule, struct prefix *prefix,
route_map_object_t type, void *object)
{
/* Match function should return 1 if match is success else return
zero. */
-static route_map_result_t route_match_ip_address(void *rule,
- struct prefix *prefix,
- route_map_object_t type,
- void *object)
+static enum route_map_match_result_t
+route_match_ip_address(void *rule, struct prefix *prefix,
+ route_map_object_t type, void *object)
{
struct access_list *alist;
/* `match ip address prefix-list PREFIX_LIST' */
-static route_map_result_t
+static enum route_map_match_result_t
route_match_ip_address_prefix_list(void *rule, struct prefix *prefix,
route_map_object_t type, void *object)
{
/* `match tag TAG' */
/* Match function return 1 if match is success else return zero. */
-static route_map_result_t route_match_tag(void *rule, struct prefix *prefix,
- route_map_object_t type, void *object)
+static enum route_map_match_result_t
+route_match_tag(void *rule, struct prefix *prefix, route_map_object_t type,
+ void *object)
{
// unsigned short *tag;
// struct rip_info *rinfo;
"tag", route_match_tag, route_match_tag_compile, route_match_tag_free};
/* Set metric to attribute. */
-static route_map_result_t route_set_metric(void *rule, struct prefix *prefix,
- route_map_object_t type,
- void *object)
+static enum route_map_match_result_t
+route_set_metric(void *rule, struct prefix *prefix,
+ route_map_object_t type, void *object)
{
// if (type == RMAP_RIP)
// {
/* `set ip next-hop IP_ADDRESS' */
/* Set nexthop to object. ojbect must be pointer to struct attr. */
-static route_map_result_t route_set_ip_nexthop(void *rule,
- struct prefix *prefix,
- route_map_object_t type,
- void *object)
+static enum route_map_match_result_t
+route_set_ip_nexthop(void *rule, struct prefix *prefix,
+ route_map_object_t type, void *object)
{
// struct in_addr *address;
// struct rip_info *rinfo;
/* `set tag TAG' */
/* Set tag to object. ojbect must be pointer to struct attr. */
-static route_map_result_t route_set_tag(void *rule, struct prefix *prefix,
- route_map_object_t type, void *object)
+static enum route_map_match_result_t
+route_set_tag(void *rule, struct prefix *prefix,
+ route_map_object_t type, void *object)
{
// unsigned short *tag;
// struct rip_info *rinfo;
ISIS_ROUTE_FLAG_ZEBRA_SYNCED
);
continue;
- } else {
+ } else if (CHECK_FLAG(rinfo->flag,
+ ISIS_ROUTE_FLAG_ACTIVE)) {
/* Clear the ZEBRA_SYNCED flag on the L1
* route when L2 wins, otherwise L1
* won't get reinstalled when it
mrinfo->flag,
ISIS_ROUTE_FLAG_ZEBRA_SYNCED
);
+ } else if (
+ CHECK_FLAG(
+ mrinfo->flag,
+ ISIS_ROUTE_FLAG_ZEBRA_SYNCED)) {
+ continue;
}
}
mrnode->info = rnode->info;
#include "isis_zebra.h"
#include "isis_routemap.h"
-static route_map_result_t route_match_ip_address(void *rule,
- const struct prefix *prefix,
- route_map_object_t type,
- void *object)
+static enum route_map_match_result_t
+route_match_ip_address(void *rule, const struct prefix *prefix,
+ route_map_object_t type, void *object)
{
struct access_list *alist;
/* ------------------------------------------------------------*/
-static route_map_result_t
+static enum route_map_match_result_t
route_match_ip_address_prefix_list(void *rule, const struct prefix *prefix,
route_map_object_t type, void *object)
{
/* ------------------------------------------------------------*/
-static route_map_result_t route_match_ipv6_address(void *rule,
- const struct prefix *prefix,
- route_map_object_t type,
- void *object)
+static enum route_map_match_result_t
+route_match_ipv6_address(void *rule, const struct prefix *prefix,
+ route_map_object_t type, void *object)
{
struct access_list *alist;
/* ------------------------------------------------------------*/
-static route_map_result_t
+static enum route_map_match_result_t
route_match_ipv6_address_prefix_list(void *rule, const struct prefix *prefix,
route_map_object_t type, void *object)
{
/* ------------------------------------------------------------*/
-static route_map_result_t route_set_metric(void *rule,
- const struct prefix *prefix,
- route_map_object_t type,
- void *object)
+static enum route_map_match_result_t
+route_set_metric(void *rule, const struct prefix *prefix,
+ route_map_object_t type, void *object)
{
uint32_t *metric;
struct isis_ext_info *info;
area->area_tag : "null");
if (lspid) {
- struct isis_lsp *lsp = lsp_for_arg(head, lspid);
+ lsp = lsp_for_arg(head, lspid);
if (lsp)
lsp_print_flooding(vty, lsp);
lsp_db_fini(&area->lspdb[1]);
/* invalidate and verify to delete all routes from zebra */
- isis_area_invalidate_routes(area, ISIS_LEVEL1 & ISIS_LEVEL2);
+ isis_area_invalidate_routes(area, area->is_type);
isis_area_verify_routes(area);
spftree_area_del(area);
*/
void print_debug(struct vty *vty, int flags, int onoff)
{
- char onoffs[4];
- if (onoff)
- strcpy(onoffs, "on");
- else
- strcpy(onoffs, "off");
+ const char *onoffs = onoff ? "on" : "off";
if (flags & DEBUG_ADJ_PACKETS)
vty_out(vty,
dirfd = open(".", O_DIRECTORY | O_RDONLY);
/* if dirfd is invalid, directory sync fails, but we're still OK */
- config_file_sav = XMALLOC(
- MTYPE_TMP, strlen(config_file) + strlen(CONF_BACKUP_EXT) + 1);
- strcpy(config_file_sav, config_file);
- strcat(config_file_sav, CONF_BACKUP_EXT);
+ size_t config_file_sav_sz = strlen(config_file) + strlen(CONF_BACKUP_EXT) + 1;
+ config_file_sav = XMALLOC(MTYPE_TMP, config_file_sav_sz);
+ strlcpy(config_file_sav, config_file, config_file_sav_sz);
+ strlcat(config_file_sav, CONF_BACKUP_EXT, config_file_sav_sz);
config_file_tmp = XMALLOC(MTYPE_TMP, strlen(config_file) + 8);
struct cmd_token *word = argv[1];
if (!isalnum((int)word->arg[0])) {
- vty_out(vty, "Please specify string starting with alphabet\n");
+ vty_out(vty,
+ "Please specify string starting with alphabet or number\n");
+ return CMD_WARNING_CONFIG_FAILED;
+ }
+
+ /* With reference to RFC 1123 Section 2.1 */
+ if (strlen(word->arg) > HOSTNAME_LEN) {
+ vty_out(vty, "Hostname length should be less than %d chars\n",
+ HOSTNAME_LEN);
return CMD_WARNING_CONFIG_FAILED;
}
/* Each node's basic commands. */
install_element(VIEW_NODE, &show_version_cmd);
install_element(ENABLE_NODE, &show_startup_config_cmd);
- install_element(ENABLE_NODE, &debug_memstats_cmd);
if (terminal) {
+ install_element(ENABLE_NODE, &debug_memstats_cmd);
+
install_element(VIEW_NODE, &config_list_cmd);
install_element(VIEW_NODE, &config_exit_cmd);
install_element(VIEW_NODE, &config_quit_cmd);
install_element(CONFIG_NODE, &domainname_cmd);
install_element(CONFIG_NODE, &no_domainname_cmd);
install_element(CONFIG_NODE, &frr_version_defaults_cmd);
- install_element(CONFIG_NODE, &debug_memstats_cmd);
if (terminal > 0) {
+ install_element(CONFIG_NODE, &debug_memstats_cmd);
+
install_element(CONFIG_NODE, &password_cmd);
install_element(CONFIG_NODE, &no_password_cmd);
install_element(CONFIG_NODE, &enable_password_cmd);
DECLARE_MTYPE(HOST)
DECLARE_MTYPE(COMPLETION)
+/*
+ * From RFC 1123 (Requirements for Internet Hosts), Section 2.1 on hostnames:
+ * One aspect of host name syntax is hereby changed: the restriction on
+ * the first character is relaxed to allow either a letter or a digit.
+ * Host software MUST support this more liberal syntax.
+ *
+ * Host software MUST handle host names of up to 63 characters and
+ * SHOULD handle host names of up to 255 characters.
+ */
+#define HOSTNAME_LEN 255
+
/* Host configuration variable */
struct host {
/* Host name of this router. */
if (str - sp > 3)
return no_match;
- strncpy(buf, sp, str - sp);
+ memcpy(buf, sp, str - sp);
if (atoi(buf) > 255)
return no_match;
if (str - sp > 3)
return no_match;
- strncpy(buf, sp, str - sp);
+ memcpy(buf, sp, str - sp);
if (atoi(buf) > 255)
return no_match;
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include <zebra.h>
+#include "typesafe.h"
#include "debug.h"
#include "command.h"
-static const struct debug_callbacks *callbacks;
+static struct debug_cb_list_head cb_head;
+
+DECLARE_LIST(debug_cb_list, struct debug_callbacks, item)
/* All code in this section should be reentrant and MT-safe */
DEFUN_NOSH(debug_all, debug_all_cmd, "[no] debug all",
NO_STR DEBUG_STR "Toggle all debugging output\n")
{
+ struct debug_callbacks *cb;
+
bool set = !strmatch(argv[0]->text, "no");
uint32_t mode = DEBUG_NODE2MODE(vty->node);
- if (callbacks->debug_set_all)
- callbacks->debug_set_all(mode, set);
+ frr_each (debug_cb_list, &cb_head, cb)
+ cb->debug_set_all(mode, set);
+
return CMD_SUCCESS;
}
/* ------------------------------------------------------------------------- */
-void debug_init(const struct debug_callbacks *cb)
+void debug_init(struct debug_callbacks *cb)
+{
+ static bool inited = false;
+
+ if (!inited) {
+ inited = true;
+ debug_cb_list_init(&cb_head);
+ }
+
+ debug_cb_list_add_head(&cb_head, cb);
+}
+
+void debug_init_cli(void)
{
- callbacks = cb;
install_element(ENABLE_NODE, &debug_all_cmd);
install_element(CONFIG_NODE, &debug_all_cmd);
}
const char *desc;
};
+PREDECL_LIST(debug_cb_list)
/*
* Callback set for debugging code.
*
* mode set.
*/
struct debug_callbacks {
+ /*
+ * Linked list of Callbacks to call
+ */
+ struct debug_cb_list_item item;
+
/*
* flags
* flags to set on debug flag fields
*
* MT-Safe
*/
-void debug_init(const struct debug_callbacks *cb);
+void debug_init(struct debug_callbacks *cb);
+
+/*
+ * Turn on the cli to turn on/off debugs.
+ * Should only be called by libfrr
+ */
+void debug_init_cli(void);
#ifdef __cplusplus
}
#include "db.h"
#include "northbound_cli.h"
#include "northbound_db.h"
+#include "debug.h"
DEFINE_HOOK(frr_late_init, (struct thread_master * tm), (tm))
DEFINE_KOOH(frr_early_fini, (), ())
#ifdef HAVE_SQLITE3
static char dbfile_default[512];
#endif
-static char vtypath_default[256];
+static char vtypath_default[512];
bool debug_memstats_at_exit = false;
static bool nodetach_term, nodetach_daemon;
{
const struct option *lo;
- strcat(comb_optstr, os->optstr);
- strcat(comb_helpstr, os->helpstr);
+ strlcat(comb_optstr, os->optstr, sizeof(comb_optstr));
+ strlcat(comb_helpstr, os->helpstr, sizeof(comb_helpstr));
for (lo = os->longopts; lo->name; lo++)
memcpy(comb_next_lo++, lo, sizeof(*lo));
}
lib_error_init();
yang_init();
+
+ debug_init_cli();
+
nb_init(master, di->yang_modules, di->n_yang_modules);
if (nb_db_init() != NB_OK)
flog_warn(EC_LIB_NB_DATABASE,
LISTNODE_ATTACH(l, n);
}
-void listnode_delete(struct list *list, void *val)
+void listnode_delete(struct list *list, const void *val)
{
struct listnode *node = listnode_lookup(list, val);
*list = NULL;
}
-struct listnode *listnode_lookup(struct list *list, void *data)
+struct listnode *listnode_lookup(struct list *list, const void *data)
{
struct listnode *node;
* data
* data to insert into list
*/
-extern void listnode_delete(struct list *list, void *data);
+extern void listnode_delete(struct list *list, const void *data);
/*
* Find the listnode corresponding to an element in a list.
* Returns:
* pointer to listnode storing the given data if found, NULL otherwise
*/
-extern struct listnode *listnode_lookup(struct list *list, void *data);
+extern struct listnode *listnode_lookup(struct list *list, const void *data);
/*
* Retrieve the element at the head of a list.
backtrace_symbols_fd(array, size, FD); \
}
#elif defined(HAVE_PRINTSTACK)
+ size = 0;
+
#define DUMP(FD) \
{ \
if (program_counter) \
zlog_debug(
"northbound callback: event [%s] op [%s] xpath [%s] value [%s]",
nb_event_name(event), nb_operation_name(operation), xpath,
- value);
+ value ? value : "(NULL)");
}
/*
/* Initialize the shared candidate configuration. */
vty_shared_candidate_config = nb_config_new(NULL);
- /* Install debug commands */
debug_init(&nb_dbg_cbs);
+
install_node(&nb_debug_node, nb_debug_config_write);
install_element(ENABLE_NODE, &debug_nb_cmd);
install_element(CONFIG_NODE, &debug_nb_cmd);
int save_errno = errno;
if (addr.s_addr == INADDR_ANY)
- strcpy(buf, "*");
+ strlcpy(buf, "*", buf_size);
else {
if (!inet_ntop(AF_INET, &addr, buf, buf_size)) {
if (onfail)
(note, this includes the description for the "NEXT"
and "GOTO" frobs now
- Match | No Match
- |
- permit action | cont
- |
- ------------------+---------------
- |
- deny deny | cont
- |
+ | Match | No Match | No op
+ |-----------|--------------|-------
+ permit | action | cont | cont.
+ | | default:deny | default:permit
+ -------------------+-----------------------
+ | deny | cont | cont.
+ deny | | default:deny | default:permit
+ |-----------|--------------|--------
action)
-Apply Set statements, accept route
We need to make sure our route-map processing matches the above
*/
-static route_map_result_t
+static enum route_map_match_result_t
route_map_apply_match(struct route_map_rule_list *match_list,
const struct prefix *prefix, route_map_object_t type,
void *object)
{
- route_map_result_t ret = RMAP_NOMATCH;
+ enum route_map_match_result_t ret = RMAP_NOMATCH;
struct route_map_rule *match;
route_map_object_t type, void *object)
{
static int recursion = 0;
- int ret = 0;
+ enum route_map_match_result_t match_ret = RMAP_NOMATCH;
+ route_map_result_t ret = 0;
struct route_map_index *index;
struct route_map_rule *set;
for (index = map->head; index; index = index->next) {
/* Apply this index. */
index->applied++;
- ret = route_map_apply_match(&index->match_list, prefix, type,
- object);
+ match_ret = route_map_apply_match(&index->match_list, prefix,
+ type, object);
/* Now we apply the matrix from above */
- if (ret == RMAP_NOMATCH)
+ if (match_ret == RMAP_NOMATCH || match_ret == RMAP_NOOP)
/* 'cont' from matrix - continue to next route-map
* sequence */
continue;
- else if (ret == RMAP_MATCH) {
+ else if (match_ret == RMAP_MATCH) {
if (index->type == RMAP_PERMIT)
/* 'action' */
{
+ /* Match succeeded, rmap is of type permit */
+ ret = RMAP_PERMITMATCH;
+
/* permit+match must execute sets */
for (set = index->set_list.head; set;
set = set->next)
- ret = (*set->cmd->func_apply)(
- set->value, prefix, type,
- object);
+ /*
+ * We dont care abt the return value
+ * for set cmd. Almost always,
+ * RMAP_OKAY is returned. Rarely
+ * do we see RMAP_ERROR
+ */
+ match_ret = (*set->cmd->func_apply)(
+ set->value, prefix, type,
+ object);
/* Call another route-map if available */
if (index->nextrm) {
}
}
}
+
+ if (match_ret == RMAP_NOOP)
+ return RMAP_PERMITMATCH;
+
/* Finally route-map does not match at all. */
return RMAP_DENYMATCH;
}
DECLARE_MTYPE(ROUTE_MAP_RULE)
DECLARE_MTYPE(ROUTE_MAP_COMPILED)
+/*
+ * Route-map match or set result "Eg: match evpn vni xx"
+ * route-map match cmd always returns match/nomatch/noop
+ * match--> found a match
+ * nomatch--> didnt find a match
+ * noop--> invalid
+ * route-map set retuns okay/error
+ * okay --> set was successful
+ * error --> set was not successful
+ */
+enum route_map_match_result_t {
+ /*
+ * route-map match cmd results
+ */
+ RMAP_MATCH,
+ RMAP_NOMATCH,
+ RMAP_NOOP,
+ /*
+ * route-map set cmd results
+ */
+ RMAP_OKAY,
+ RMAP_ERROR
+};
+
/* Route map's type. */
enum route_map_type { RMAP_PERMIT, RMAP_DENY, RMAP_ANY };
typedef enum {
- RMAP_MATCH,
RMAP_DENYMATCH,
- RMAP_NOMATCH,
- RMAP_ERROR,
- RMAP_OKAY
+ RMAP_PERMITMATCH
} route_map_result_t;
typedef enum {
const char *str;
/* Function for value set or match. */
- route_map_result_t (*func_apply)(void *rule,
- const struct prefix *prefix,
- route_map_object_t type,
- void *object);
+ enum route_map_match_result_t (*func_apply)(void *rule,
+ const struct prefix *prefix,
+ route_map_object_t type,
+ void *object);
/* Compile argument and return result as void *. */
void *(*func_compile)(const char *);
return NULL;
default:
+ /* Suppress uninitialized variable warning */
+ node = NULL;
assert(0);
}
static vector Vvty_serv_thread;
/* Current directory. */
-char *vty_cwd = NULL;
+char vty_cwd[MAXPATHLEN];
/* Login password check. */
static int no_password_check = 0;
if (pos == 0)
break;
- strncpy(buf, p, pos);
+ memcpy(buf, p, pos);
buf[pos] = '\0';
vty_out(vty, " %-*s %s\n", cmd_width, cmd, buf);
/* configurable parameters not part of basic init */
vty->v_timeout = vty_timeout_val;
- strcpy(vty->address, buf);
+ strlcpy(vty->address, buf, sizeof(vty->address));
if (no_password_check) {
if (host.advanced)
vty->node = ENABLE_NODE;
*/
vty->node = ENABLE_NODE;
vty->v_timeout = 0;
- strcpy(vty->address, "console");
+ strlcpy(vty->address, "console", sizeof(vty->address));
vty_stdio_resume();
return vty;
int c;
char buffer[512];
- fullpath_sav = malloc(strlen(fullpath) + strlen(CONF_BACKUP_EXT) + 1);
- strcpy(fullpath_sav, fullpath);
- strcat(fullpath_sav, CONF_BACKUP_EXT);
+ size_t fullpath_sav_sz = strlen(fullpath) + strlen(CONF_BACKUP_EXT) + 1;
+ fullpath_sav = malloc(fullpath_sav_sz);
+ strlcpy(fullpath_sav, fullpath, fullpath_sav_sz);
+ strlcat(fullpath_sav, CONF_BACKUP_EXT, fullpath_sav_sz);
sav = open(fullpath_sav, O_RDONLY);
if (sav < 0) {
static void vty_save_cwd(void)
{
- char cwd[MAXPATHLEN];
char *c;
- c = getcwd(cwd, MAXPATHLEN);
+ c = getcwd(vty_cwd, sizeof(vty_cwd));
if (!c) {
/*
SYSCONFDIR, errno);
exit(-1);
}
- if (getcwd(cwd, MAXPATHLEN) == NULL) {
+ if (getcwd(vty_cwd, sizeof(vty_cwd)) == NULL) {
flog_err_sys(EC_LIB_SYSTEM_CALL,
"Failure to getcwd, errno: %d", errno);
exit(-1);
}
}
-
- vty_cwd = XMALLOC(MTYPE_TMP, strlen(cwd) + 1);
- strcpy(vty_cwd, cwd);
}
char *vty_get_cwd(void)
void vty_terminate(void)
{
- XFREE(MTYPE_TMP, vty_cwd);
+ memset(vty_cwd, 0x00, sizeof(vty_cwd));
if (vtyvec && Vvty_serv_thread) {
vty_reset();
struct zclient_options *opt)
{
struct zclient *zclient;
+ size_t stream_size =
+ MAX(ZEBRA_MAX_PACKET_SIZ, sizeof(struct zapi_route));
+
zclient = XCALLOC(MTYPE_ZCLIENT, sizeof(struct zclient));
- zclient->ibuf = stream_new(ZEBRA_MAX_PACKET_SIZ);
- zclient->obuf = stream_new(ZEBRA_MAX_PACKET_SIZ);
+ zclient->ibuf = stream_new(stream_size);
+ zclient->obuf = stream_new(stream_size);
zclient->wb = buffer_new(0);
zclient->master = master;
#include "mlag.h"
/* For input/output buffer to zebra. */
-#define ZEBRA_MAX_PACKET_SIZ 16384
+#define ZEBRA_MAX_PACKET_SIZ 16384U
/* Zebra header size. */
#define ZEBRA_HEADER_SIZE 10
unsigned int nexthop_num,
struct in6_addr *nexthop, route_tag_t tag)
{
- int ret;
+ route_map_result_t ret;
struct ospf6_route troute;
struct ospf6_external_info tinfo;
struct ospf6_route *route, *match;
/* Routemap Functions */
-static route_map_result_t
+static enum route_map_match_result_t
ospf6_routemap_rule_match_address_prefixlist(void *rule,
const struct prefix *prefix,
route_map_object_t type,
/* `match interface IFNAME' */
/* Match function should return 1 if match is success else return
zero. */
-static route_map_result_t
+static enum route_map_match_result_t
ospf6_routemap_rule_match_interface(void *rule, const struct prefix *prefix,
route_map_object_t type, void *object)
{
ospf6_routemap_rule_match_interface_free};
/* Match function for matching route tags */
-static route_map_result_t ospf6_routemap_rule_match_tag(void *rule,
- const struct prefix *p,
- route_map_object_t type,
- void *object)
+static enum route_map_match_result_t
+ospf6_routemap_rule_match_tag(void *rule, const struct prefix *p,
+ route_map_object_t type, void *object)
{
route_tag_t *tag = rule;
struct ospf6_route *route = object;
route_map_rule_tag_free,
};
-static route_map_result_t
+static enum route_map_match_result_t
ospf6_routemap_rule_set_metric_type(void *rule, const struct prefix *prefix,
route_map_object_t type, void *object)
{
ospf6_routemap_rule_set_metric_type_free,
};
-static route_map_result_t
+static enum route_map_match_result_t
ospf6_routemap_rule_set_metric(void *rule, const struct prefix *prefix,
route_map_object_t type, void *object)
{
ospf6_routemap_rule_set_metric_free,
};
-static route_map_result_t
+static enum route_map_match_result_t
ospf6_routemap_rule_set_forwarding(void *rule, const struct prefix *prefix,
route_map_object_t type, void *object)
{
ospf6_routemap_rule_set_forwarding_free,
};
-static route_map_result_t ospf6_routemap_rule_set_tag(void *rule,
- const struct prefix *p,
- route_map_object_t type,
- void *object)
+static enum route_map_match_result_t
+ospf6_routemap_rule_set_tag(void *rule, const struct prefix *p,
+ route_map_object_t type, void *object)
{
route_tag_t *tag = rule;
struct ospf6_route *route = object;
/* `match ip netxthop ' */
/* Match function return 1 if match is success else return zero. */
-static route_map_result_t route_match_ip_nexthop(void *rule,
- const struct prefix *prefix,
- route_map_object_t type,
- void *object)
+static enum route_map_match_result_t
+route_match_ip_nexthop(void *rule, const struct prefix *prefix,
+ route_map_object_t type, void *object)
{
struct access_list *alist;
struct external_info *ei = object;
/* `match ip next-hop prefix-list PREFIX_LIST' */
-static route_map_result_t
+static enum route_map_match_result_t
route_match_ip_next_hop_prefix_list(void *rule, const struct prefix *prefix,
route_map_object_t type, void *object)
{
/* `match ip address IP_ACCESS_LIST' */
/* Match function should return 1 if match is success else return
zero. */
-static route_map_result_t route_match_ip_address(void *rule,
- const struct prefix *prefix,
- route_map_object_t type,
- void *object)
+static enum route_map_match_result_t
+route_match_ip_address(void *rule, const struct prefix *prefix,
+ route_map_object_t type, void *object)
{
struct access_list *alist;
/* struct prefix_ipv4 match; */
route_match_ip_address_free};
/* `match ip address prefix-list PREFIX_LIST' */
-static route_map_result_t
+static enum route_map_match_result_t
route_match_ip_address_prefix_list(void *rule, const struct prefix *prefix,
route_map_object_t type, void *object)
{
/* `match interface IFNAME' */
/* Match function should return 1 if match is success else return
zero. */
-static route_map_result_t route_match_interface(void *rule,
- const struct prefix *prefix,
- route_map_object_t type,
- void *object)
+static enum route_map_match_result_t
+route_match_interface(void *rule, const struct prefix *prefix,
+ route_map_object_t type, void *object)
{
struct interface *ifp;
struct external_info *ei;
route_match_interface_free};
/* Match function return 1 if match is success else return zero. */
-static route_map_result_t route_match_tag(void *rule,
- const struct prefix *prefix,
- route_map_object_t type, void *object)
+static enum route_map_match_result_t
+route_match_tag(void *rule, const struct prefix *prefix,
+ route_map_object_t type, void *object)
{
route_tag_t *tag;
struct external_info *ei;
/* `set metric METRIC' */
/* Set metric to attribute. */
-static route_map_result_t route_set_metric(void *rule,
- const struct prefix *prefix,
- route_map_object_t type,
- void *object)
+static enum route_map_match_result_t
+route_set_metric(void *rule, const struct prefix *prefix,
+ route_map_object_t type, void *object)
{
struct ospf_metric *metric;
struct external_info *ei;
/* `set metric-type TYPE' */
/* Set metric-type to attribute. */
-static route_map_result_t route_set_metric_type(void *rule,
- const struct prefix *prefix,
- route_map_object_t type,
- void *object)
+static enum route_map_match_result_t
+route_set_metric_type(void *rule, const struct prefix *prefix,
+ route_map_object_t type, void *object)
{
uint32_t *metric_type;
struct external_info *ei;
route_set_metric_type_free,
};
-static route_map_result_t route_set_tag(void *rule, const struct prefix *prefix,
- route_map_object_t type, void *object)
+static enum route_map_match_result_t
+route_set_tag(void *rule, const struct prefix *prefix, route_map_object_t type,
+ void *object)
{
route_tag_t *tag;
struct external_info *ei;
struct interface *ifp = NULL;
struct listnode *node;
char *vrf_name = NULL;
- bool all_vrf;
+ bool all_vrf = false;
int inst = 0;
int idx_vrf = 0;
struct ospf *ospf = NULL;
/* apply route-map if needed */
red = ospf_redist_lookup(ospf, type, instance);
if (red && ROUTEMAP_NAME(red)) {
- int ret;
+ route_map_result_t ret;
ret = route_map_apply(ROUTEMAP(red), (struct prefix *)p,
RMAP_OSPF, ei);
new = XCALLOC(MTYPE_PBR_NHG, sizeof(*new));
- strcpy(new->name, pnhgc->name);
+ strlcpy(new->name, pnhgc->name, sizeof(pnhgc->name));
new->table_id = pbr_nht_get_next_tableid(false);
DEBUGD(&pbr_dbg_nht, "%s: NHT: %s assigned Table ID: %u",
long oqpi_msec; /* Other Querier Present Interval */
long qri_msec;
time_t now;
+ int lmqc;
json_object *json = NULL;
json_object *json_row = NULL;
pim_ifp->igmp_query_max_response_time_dsec);
lmqt_msec = PIM_IGMP_LMQT_MSEC(
- pim_ifp->igmp_query_max_response_time_dsec,
- igmp->querier_robustness_variable);
+ pim_ifp->igmp_specific_query_max_response_time_dsec,
+ pim_ifp->igmp_last_member_query_count);
ohpi_msec =
PIM_IGMP_OHPI_DSEC(
pim_ifp->pim_sock_fd);
else
mloop = 0;
+ lmqc = pim_ifp->igmp_last_member_query_count;
if (uj) {
json_row = json_object_new_object();
json_row,
"timerGroupMembershipIntervalMsec",
gmi_msec);
+ json_object_int_add(json_row,
+ "lastMemberQueryCount",
+ lmqc);
json_object_int_add(json_row,
"timerLastMemberQueryMsec",
lmqt_msec);
vty_out(vty,
"Group Membership Interval : %lis\n",
gmi_msec / 1000);
+ vty_out(vty,
+ "Last Member Query Count : %d\n",
+ lmqc);
vty_out(vty,
"Last Member Query Time : %lis\n",
lmqt_msec / 1000);
ifp_in = pim_if_find_by_vif_index(pim, c_oil->oil.mfcc_parent);
if (ifp_in)
- strcpy(in_ifname, ifp_in->name);
+ strlcpy(in_ifname, ifp_in->name, sizeof(in_ifname));
else
- strcpy(in_ifname, "<iif?>");
+ strlcpy(in_ifname, "<iif?>", sizeof(in_ifname));
if (src_or_group) {
if (strcmp(src_or_group, src_str)
now - c_oil->oif_creation[oif_vif_index]);
if (ifp_out)
- strcpy(out_ifname, ifp_out->name);
+ strlcpy(out_ifname, ifp_out->name, sizeof(out_ifname));
else
- strcpy(out_ifname, "<oif?>");
+ strlcpy(out_ifname, "<oif?>", sizeof(out_ifname));
if (uj) {
json_ifp_out = json_object_new_object();
static const char *
pim_upstream_state2brief_str(enum pim_upstream_state join_state,
- char *state_str)
+ char *state_str, size_t state_str_len)
{
switch (join_state) {
case PIM_UPSTREAM_NOTJOINED:
- strcpy(state_str, "NotJ");
+ strlcpy(state_str, "NotJ", state_str_len);
break;
case PIM_UPSTREAM_JOINED:
- strcpy(state_str, "J");
+ strlcpy(state_str, "J", state_str_len);
break;
default:
- strcpy(state_str, "Unk");
+ strlcpy(state_str, "Unk", state_str_len);
}
return state_str;
}
static const char *pim_reg_state2brief_str(enum pim_reg_state reg_state,
- char *state_str)
+ char *state_str, size_t state_str_len)
{
switch (reg_state) {
case PIM_REG_NOINFO:
- strcpy(state_str, "RegNI");
+ strlcpy(state_str, "RegNI", state_str_len);
break;
case PIM_REG_JOIN:
- strcpy(state_str, "RegJ");
+ strlcpy(state_str, "RegJ", state_str_len);
break;
case PIM_REG_JOIN_PENDING:
case PIM_REG_PRUNE:
- strcpy(state_str, "RegP");
+ strlcpy(state_str, "RegP", state_str_len);
break;
default:
- strcpy(state_str, "Unk");
+ strlcpy(state_str, "Unk", state_str_len);
}
return state_str;
}
pim_time_timer_to_hhmmss(msdp_reg_timer, sizeof(msdp_reg_timer),
up->t_msdp_reg_timer);
- pim_upstream_state2brief_str(up->join_state, state_str);
+ pim_upstream_state2brief_str(up->join_state, state_str, sizeof(state_str));
if (up->reg_state != PIM_REG_NOINFO) {
char tmp_str[PIM_REG_STATE_STR_LEN];
sprintf(state_str + strlen(state_str), ",%s",
- pim_reg_state2brief_str(up->reg_state,
- tmp_str));
+ pim_reg_state2brief_str(up->reg_state, tmp_str,
+ sizeof(tmp_str)));
}
if (uj) {
pim_upstream_state2str(up->join_state));
json_object_string_add(
json_row, "regState",
- pim_reg_state2str(up->reg_state, state_str));
+ pim_reg_state2str(up->reg_state, state_str, sizeof(state_str)));
json_object_string_add(json_row, "upTime", uptime);
json_object_string_add(json_row, "joinTimer",
join_timer);
ifp_in = pim_if_find_by_vif_index(pim, c_oil->oil.mfcc_parent);
if (ifp_in)
- strcpy(in_ifname, ifp_in->name);
+ strlcpy(in_ifname, ifp_in->name, sizeof(in_ifname));
else
- strcpy(in_ifname, "<iif?>");
+ strlcpy(in_ifname, "<iif?>", sizeof(in_ifname));
if (uj) {
found_oif = 1;
if (ifp_out)
- strcpy(out_ifname, ifp_out->name);
+ strlcpy(out_ifname, ifp_out->name, sizeof(out_ifname));
else
- strcpy(out_ifname, "<oif?>");
+ strlcpy(out_ifname, "<oif?>", sizeof(out_ifname));
if (uj) {
json_ifp_out = json_object_new_object();
} else {
if (c_oil->oif_flags[oif_vif_index]
& PIM_OIF_FLAG_PROTO_PIM) {
- strcpy(proto, "PIM");
+ strlcpy(proto, "PIM", sizeof(proto));
}
if (c_oil->oif_flags[oif_vif_index]
& PIM_OIF_FLAG_PROTO_IGMP) {
- strcpy(proto, "IGMP");
+ strlcpy(proto, "IGMP", sizeof(proto));
}
if (c_oil->oif_flags[oif_vif_index]
& PIM_OIF_FLAG_PROTO_VXLAN) {
- strcpy(proto, "VxLAN");
+ strlcpy(proto, "VxLAN", sizeof(proto));
}
if (c_oil->oif_flags[oif_vif_index]
& PIM_OIF_FLAG_PROTO_SOURCE) {
- strcpy(proto, "SRC");
+ strlcpy(proto, "SRC", sizeof(proto));
}
if (c_oil->oif_flags[oif_vif_index]
& PIM_OIF_FLAG_PROTO_STAR) {
- strcpy(proto, "STAR");
+ strlcpy(proto, "STAR", sizeof(proto));
}
vty_out(vty,
found_oif = 0;
if (ifp_in)
- strcpy(in_ifname, ifp_in->name);
+ strlcpy(in_ifname, ifp_in->name, sizeof(in_ifname));
else
- strcpy(in_ifname, "<iif?>");
+ strlcpy(in_ifname, "<iif?>", sizeof(in_ifname));
if (uj) {
json_object_string_add(json_source, "iif", in_ifname);
json_oil = NULL;
} else {
- strcpy(proto, "STATIC");
+ strlcpy(proto, "STATIC", sizeof(proto));
}
for (oif_vif_index = 0; oif_vif_index < MAXVIFS;
found_oif = 1;
if (ifp_out)
- strcpy(out_ifname, ifp_out->name);
+ strlcpy(out_ifname, ifp_out->name, sizeof(out_ifname));
else
- strcpy(out_ifname, "<oif?>");
+ strlcpy(out_ifname, "<oif?>", sizeof(out_ifname));
if (uj) {
json_ifp_out = json_object_new_object();
return CMD_SUCCESS;
}
+static void show_mroute_summary(struct pim_instance *pim, struct vty *vty)
+{
+ struct listnode *node;
+ struct channel_oil *c_oil;
+ struct static_route *s_route;
+ uint32_t starg_sw_mroute_cnt = 0;
+ uint32_t sg_sw_mroute_cnt = 0;
+ uint32_t starg_hw_mroute_cnt = 0;
+ uint32_t sg_hw_mroute_cnt = 0;
+
+ vty_out(vty, "Mroute Type Installed/Total\n");
+
+ for (ALL_LIST_ELEMENTS_RO(pim->channel_oil_list, node, c_oil)) {
+ if (!c_oil->installed) {
+ if (c_oil->oil.mfcc_origin.s_addr == INADDR_ANY)
+ starg_sw_mroute_cnt++;
+ else
+ sg_sw_mroute_cnt++;
+ } else {
+ if (c_oil->oil.mfcc_origin.s_addr == INADDR_ANY)
+ starg_hw_mroute_cnt++;
+ else
+ sg_hw_mroute_cnt++;
+ }
+ }
+
+ for (ALL_LIST_ELEMENTS_RO(pim->static_routes, node, s_route)) {
+ if (!s_route->c_oil.installed) {
+ if (s_route->c_oil.oil.mfcc_origin.s_addr == INADDR_ANY)
+ starg_sw_mroute_cnt++;
+ else
+ sg_sw_mroute_cnt++;
+ } else {
+ if (s_route->c_oil.oil.mfcc_origin.s_addr == INADDR_ANY)
+ starg_hw_mroute_cnt++;
+ else
+ sg_hw_mroute_cnt++;
+ }
+ }
+
+ vty_out(vty, "%-20s %d/%d\n", "(*, G)", starg_hw_mroute_cnt,
+ starg_sw_mroute_cnt + starg_hw_mroute_cnt);
+ vty_out(vty, "%-20s %d/%d\n", "(S, G)", sg_hw_mroute_cnt,
+ sg_sw_mroute_cnt + sg_hw_mroute_cnt);
+ vty_out(vty, "------\n");
+ vty_out(vty, "%-20s %d/%d\n", "Total",
+ (starg_hw_mroute_cnt + sg_hw_mroute_cnt),
+ (starg_sw_mroute_cnt +
+ starg_hw_mroute_cnt +
+ sg_sw_mroute_cnt +
+ sg_hw_mroute_cnt));
+}
+
+DEFUN (show_ip_mroute_summary,
+ show_ip_mroute_summary_cmd,
+ "show ip mroute [vrf NAME] summary",
+ SHOW_STR
+ IP_STR
+ MROUTE_STR
+ VRF_CMD_HELP_STR
+ "Summary of all mroutes\n")
+{
+ int idx = 2;
+ struct vrf *vrf = pim_cmd_lookup_vrf(vty, argv, argc, &idx);
+
+ if (!vrf)
+ return CMD_WARNING;
+
+ show_mroute_summary(vrf->info, vty);
+ return CMD_SUCCESS;
+}
+
+DEFUN (show_ip_mroute_summary_vrf_all,
+ show_ip_mroute_summary_vrf_all_cmd,
+ "show ip mroute vrf all summary",
+ SHOW_STR
+ IP_STR
+ MROUTE_STR
+ VRF_CMD_HELP_STR
+ "Summary of all mroutes\n")
+{
+ struct vrf *vrf;
+
+ RB_FOREACH (vrf, vrf_name_head, &vrfs_by_name) {
+ vty_out(vty, "VRF: %s\n", vrf->name);
+ show_mroute_summary(vrf->info, vty);
+ }
+
+ return CMD_SUCCESS;
+}
+
DEFUN (show_ip_rib,
show_ip_rib_cmd,
"show ip rib [vrf NAME] A.B.C.D",
return CMD_SUCCESS;
}
+#define IGMP_LAST_MEMBER_QUERY_COUNT_MIN (1)
+#define IGMP_LAST_MEMBER_QUERY_COUNT_MAX (7)
+
+DEFUN (interface_ip_igmp_last_member_query_count,
+ interface_ip_igmp_last_member_query_count_cmd,
+ "ip igmp last-member-query-count (1-7)",
+ IP_STR
+ IFACE_IGMP_STR
+ IFACE_IGMP_LAST_MEMBER_QUERY_COUNT_STR
+ "Last member query count\n")
+{
+ VTY_DECLVAR_CONTEXT(interface, ifp);
+ struct pim_interface *pim_ifp = ifp->info;
+ int last_member_query_count;
+ int ret;
+
+ if (!pim_ifp) {
+ ret = pim_cmd_igmp_start(vty, ifp);
+ if (ret != CMD_SUCCESS)
+ return ret;
+ pim_ifp = ifp->info;
+ }
+
+ last_member_query_count = atoi(argv[3]->arg);
+
+ pim_ifp->igmp_last_member_query_count = last_member_query_count;
+
+ return CMD_SUCCESS;
+}
+
+DEFUN (interface_no_ip_igmp_last_member_query_count,
+ interface_no_ip_igmp_last_member_query_count_cmd,
+ "no ip igmp last-member-query-count",
+ NO_STR
+ IP_STR
+ IFACE_IGMP_STR
+ IFACE_IGMP_LAST_MEMBER_QUERY_COUNT_STR)
+{
+ VTY_DECLVAR_CONTEXT(interface, ifp);
+ struct pim_interface *pim_ifp = ifp->info;
+
+ if (!pim_ifp)
+ return CMD_SUCCESS;
+
+ pim_ifp->igmp_last_member_query_count =
+ IGMP_DEFAULT_ROBUSTNESS_VARIABLE;
+
+ return CMD_SUCCESS;
+}
+
+#define IGMP_LAST_MEMBER_QUERY_INTERVAL_MIN (1)
+#define IGMP_LAST_MEMBER_QUERY_INTERVAL_MAX (255)
+
+DEFUN (interface_ip_igmp_last_member_query_interval,
+ interface_ip_igmp_last_member_query_interval_cmd,
+ "ip igmp last-member-query-interval (1-255)",
+ IP_STR
+ IFACE_IGMP_STR
+ IFACE_IGMP_LAST_MEMBER_QUERY_INTERVAL_STR
+ "Last member query interval in deciseconds\n")
+{
+ VTY_DECLVAR_CONTEXT(interface, ifp);
+ struct pim_interface *pim_ifp = ifp->info;
+ int last_member_query_interval;
+ int ret;
+
+ if (!pim_ifp) {
+ ret = pim_cmd_igmp_start(vty, ifp);
+ if (ret != CMD_SUCCESS)
+ return ret;
+ pim_ifp = ifp->info;
+ }
+
+ last_member_query_interval = atoi(argv[3]->arg);
+ pim_ifp->igmp_specific_query_max_response_time_dsec
+ = last_member_query_interval;
+
+ return CMD_SUCCESS;
+}
+
+DEFUN (interface_no_ip_igmp_last_member_query_interval,
+ interface_no_ip_igmp_last_member_query_interval_cmd,
+ "no ip igmp last-member-query-interval",
+ NO_STR
+ IP_STR
+ IFACE_IGMP_STR
+ IFACE_IGMP_LAST_MEMBER_QUERY_INTERVAL_STR)
+{
+ VTY_DECLVAR_CONTEXT(interface, ifp);
+ struct pim_interface *pim_ifp = ifp->info;
+
+ if (!pim_ifp)
+ return CMD_SUCCESS;
+
+ pim_ifp->igmp_specific_query_max_response_time_dsec =
+ IGMP_SPECIFIC_QUERY_MAX_RESPONSE_TIME_DSEC;
+
+ return CMD_SUCCESS;
+}
+
DEFUN (interface_ip_pim_drprio,
interface_ip_pim_drprio_cmd,
"ip pim drpriority (1-4294967295)",
pim_time_uptime(timebuf, sizeof(timebuf),
now - mp->uptime);
} else {
- strcpy(timebuf, "-");
+ strlcpy(timebuf, "-", sizeof(timebuf));
}
pim_inet4_dump("<peer?>", mp->peer, peer_str, sizeof(peer_str));
pim_inet4_dump("<local?>", mp->local, local_str,
pim_time_uptime(timebuf, sizeof(timebuf),
now - mp->uptime);
} else {
- strcpy(timebuf, "-");
+ strlcpy(timebuf, "-", sizeof(timebuf));
}
pim_inet4_dump("<local?>", mp->local, local_str,
sizeof(local_str));
if (sa->flags & PIM_MSDP_SAF_PEER) {
pim_inet4_dump("<rp?>", sa->rp, rp_str, sizeof(rp_str));
if (sa->up) {
- strcpy(spt_str, "yes");
+ strlcpy(spt_str, "yes", sizeof(spt_str));
} else {
- strcpy(spt_str, "no");
+ strlcpy(spt_str, "no", sizeof(spt_str));
}
} else {
- strcpy(rp_str, "-");
- strcpy(spt_str, "-");
+ strlcpy(rp_str, "-", sizeof(rp_str));
+ strlcpy(spt_str, "-", sizeof(spt_str));
}
if (sa->flags & PIM_MSDP_SAF_LOCAL) {
- strcpy(local_str, "yes");
+ strlcpy(local_str, "yes", sizeof(local_str));
} else {
- strcpy(local_str, "no");
+ strlcpy(local_str, "no", sizeof(local_str));
}
if (uj) {
json_object_object_get_ex(json, grp_str, &json_group);
pim_inet4_dump("<rp?>", sa->rp, rp_str, sizeof(rp_str));
pim_inet4_dump("<peer?>", sa->peer, peer_str, sizeof(peer_str));
if (sa->up) {
- strcpy(spt_str, "yes");
+ strlcpy(spt_str, "yes", sizeof(spt_str));
} else {
- strcpy(spt_str, "no");
+ strlcpy(spt_str, "no", sizeof(spt_str));
}
} else {
- strcpy(rp_str, "-");
- strcpy(peer_str, "-");
- strcpy(spt_str, "-");
+ strlcpy(rp_str, "-", sizeof(rp_str));
+ strlcpy(peer_str, "-", sizeof(peer_str));
+ strlcpy(spt_str, "-", sizeof(spt_str));
}
if (sa->flags & PIM_MSDP_SAF_LOCAL) {
- strcpy(local_str, "yes");
+ strlcpy(local_str, "yes", sizeof(local_str));
} else {
- strcpy(local_str, "no");
+ strlcpy(local_str, "no", sizeof(local_str));
}
pim_time_timer_to_hhmmss(statetimer, sizeof(statetimer),
sa->sa_state_timer);
&interface_ip_igmp_query_max_response_time_dsec_cmd);
install_element(INTERFACE_NODE,
&interface_no_ip_igmp_query_max_response_time_dsec_cmd);
+ install_element(INTERFACE_NODE,
+ &interface_ip_igmp_last_member_query_count_cmd);
+ install_element(INTERFACE_NODE,
+ &interface_no_ip_igmp_last_member_query_count_cmd);
+ install_element(INTERFACE_NODE,
+ &interface_ip_igmp_last_member_query_interval_cmd);
+ install_element(INTERFACE_NODE,
+ &interface_no_ip_igmp_last_member_query_interval_cmd);
install_element(INTERFACE_NODE, &interface_ip_pim_activeactive_cmd);
install_element(INTERFACE_NODE, &interface_ip_pim_ssm_cmd);
install_element(INTERFACE_NODE, &interface_no_ip_pim_ssm_cmd);
install_element(VIEW_NODE, &show_ip_mroute_vrf_all_cmd);
install_element(VIEW_NODE, &show_ip_mroute_count_cmd);
install_element(VIEW_NODE, &show_ip_mroute_count_vrf_all_cmd);
+ install_element(VIEW_NODE, &show_ip_mroute_summary_cmd);
+ install_element(VIEW_NODE, &show_ip_mroute_summary_vrf_all_cmd);
install_element(VIEW_NODE, &show_ip_rib_cmd);
install_element(VIEW_NODE, &show_ip_ssmpingd_cmd);
install_element(VIEW_NODE, &show_debugging_pim_cmd);
#define IFACE_IGMP_QUERY_INTERVAL_STR "IGMP host query interval\n"
#define IFACE_IGMP_QUERY_MAX_RESPONSE_TIME_STR "IGMP max query response value (seconds)\n"
#define IFACE_IGMP_QUERY_MAX_RESPONSE_TIME_DSEC_STR "IGMP max query response value (deciseconds)\n"
+#define IFACE_IGMP_LAST_MEMBER_QUERY_INTERVAL_STR "IGMP last member query interval\n"
+#define IFACE_IGMP_LAST_MEMBER_QUERY_COUNT_STR "IGMP last member query count\n"
#define DEBUG_IGMP_STR "IGMP protocol activity\n"
#define DEBUG_IGMP_EVENTS_STR "IGMP protocol events\n"
#define DEBUG_IGMP_PACKETS_STR "IGMP protocol packets\n"
IGMP_QUERY_MAX_RESPONSE_TIME_DSEC;
pim_ifp->igmp_specific_query_max_response_time_dsec =
IGMP_SPECIFIC_QUERY_MAX_RESPONSE_TIME_DSEC;
+ pim_ifp->igmp_last_member_query_count =
+ IGMP_DEFAULT_ROBUSTNESS_VARIABLE;
/* BSM config on interface: TRUE by default */
pim_ifp->bsm_enable = true;
int igmp_query_max_response_time_dsec; /* IGMPv3 Max Response Time in
dsecs for general queries */
int igmp_specific_query_max_response_time_dsec; /* IGMPv3 Max Response
- Time in dsecs for
- specific queries */
+ Time in dsecs called
+ as last member query
+ interval, defines the
+ maximum response time
+ advertised in IGMP
+ group-specific
+ queries */
+ int igmp_last_member_query_count; /* IGMP last member query count */
struct list *igmp_socket_list; /* list of struct igmp_sock */
struct list *igmp_join_list; /* list of struct igmp_join */
char query_buf[query_buf_size];
- lmqc = igmp->querier_robustness_variable;
+ lmqc = pim_ifp->igmp_last_member_query_count;
lmqi_msec = 100 * pim_ifp->igmp_specific_query_max_response_time_dsec;
lmqt_msec = lmqc * lmqi_msec;
igmp = group->group_igmp_sock;
pim_ifp = igmp->interface->info;
- lmqc = igmp->querier_robustness_variable;
+ lmqc = pim_ifp->igmp_last_member_query_count;
lmqi_msec = 100 * pim_ifp->igmp_specific_query_max_response_time_dsec;
lmqt_msec = lmqc * lmqi_msec;
*/
static void group_query_send(struct igmp_group *group)
{
+ struct pim_interface *pim_ifp;
+ struct igmp_sock *igmp;
long lmqc; /* Last Member Query Count */
- lmqc = group->group_igmp_sock->querier_robustness_variable;
+ igmp = group->group_igmp_sock;
+ pim_ifp = igmp->interface->info;
+ lmqc = pim_ifp->igmp_last_member_query_count;
/* lower group timer to lmqt */
igmp_group_timer_lower_to_lmqt(group);
igmp = group->group_igmp_sock;
pim_ifp = igmp->interface->info;
- lmqc = igmp->querier_robustness_variable;
+ lmqc = pim_ifp->igmp_last_member_query_count;
lmqi_msec = 100 * pim_ifp->igmp_specific_query_max_response_time_dsec;
lmqt_msec = lmqc * lmqi_msec;
ifname = ifp->name;
lmqi_dsec = pim_ifp->igmp_specific_query_max_response_time_dsec;
- lmqc = igmp->querier_robustness_variable;
+ lmqc = pim_ifp->igmp_last_member_query_count;
lmqt_msec = PIM_IGMP_LMQT_MSEC(
lmqi_dsec, lmqc); /* lmqt_msec = (100 * lmqi_dsec) * lmqc */
ifname = ifp->name;
lmqi_dsec = pim_ifp->igmp_specific_query_max_response_time_dsec;
- lmqc = igmp->querier_robustness_variable;
+ lmqc = pim_ifp->igmp_last_member_query_count;
lmqt_msec = PIM_IGMP_LMQT_MSEC(
lmqi_dsec, lmqc); /* lmqt_msec = (100 * lmqi_dsec) * lmqc */
mp->mesh_group_name = XSTRDUP(MTYPE_PIM_MSDP_MG_NAME, mesh_group_name);
mp->state = PIM_MSDP_INACTIVE;
mp->fd = -1;
- strcpy(mp->last_reset, "-");
+ strlcpy(mp->last_reset, "-", sizeof(mp->last_reset));
/* higher IP address is listener */
if (ntohl(mp->local.s_addr) > ntohl(mp->peer.s_addr)) {
mp->flags |= PIM_MSDP_PEERF_LISTENER;
return "Unknown";
}
-const char *pim_reg_state2str(enum pim_reg_state reg_state, char *state_str)
+const char *pim_reg_state2str(enum pim_reg_state reg_state, char *state_str,
+ size_t state_str_len)
{
switch (reg_state) {
case PIM_REG_NOINFO:
- strcpy(state_str, "RegNoInfo");
+ strlcpy(state_str, "RegNoInfo", state_str_len);
break;
case PIM_REG_JOIN:
- strcpy(state_str, "RegJoined");
+ strlcpy(state_str, "RegJoined", state_str_len);
break;
case PIM_REG_JOIN_PENDING:
- strcpy(state_str, "RegJoinPend");
+ strlcpy(state_str, "RegJoinPend", state_str_len);
break;
case PIM_REG_PRUNE:
- strcpy(state_str, "RegPrune");
+ strlcpy(state_str, "RegPrune", state_str_len);
break;
default:
- strcpy(state_str, "RegUnknown");
+ strlcpy(state_str, "RegUnknown", state_str_len);
}
return state_str;
}
char state_str[PIM_REG_STATE_STR_LEN];
zlog_debug("%s: (S,G)=%s[%s] upstream register stop timer %s",
__PRETTY_FUNCTION__, up->sg_str, pim->vrf->name,
- pim_reg_state2str(up->reg_state, state_str));
+ pim_reg_state2str(up->reg_state, state_str, sizeof(state_str)));
}
switch (up->reg_state) {
const char *pim_upstream_state2str(enum pim_upstream_state join_state);
#define PIM_REG_STATE_STR_LEN 12
-const char *pim_reg_state2str(enum pim_reg_state state, char *state_str);
+const char *pim_reg_state2str(enum pim_reg_state state, char *state_str,
+ size_t state_str_len);
int pim_upstream_inherited_olist_decide(struct pim_instance *pim,
struct pim_upstream *up);
++writes;
}
+ /* IF ip igmp last-member_query-count */
+ if (pim_ifp->igmp_last_member_query_count
+ != IGMP_DEFAULT_ROBUSTNESS_VARIABLE) {
+ vty_out(vty,
+ " ip igmp last-member-query-count %d\n",
+ pim_ifp->igmp_last_member_query_count);
+ ++writes;
+ }
+
+ /* IF ip igmp last-member_query-interval */
+ if (pim_ifp->igmp_specific_query_max_response_time_dsec
+ != IGMP_SPECIFIC_QUERY_MAX_RESPONSE_TIME_DSEC) {
+ vty_out(vty,
+ " ip igmp last-member-query-interval %d\n",
+ pim_ifp->igmp_specific_query_max_response_time_dsec);
+ ++writes;
+ }
+
/* IF ip igmp join */
if (pim_ifp->igmp_join_list) {
struct listnode *node;
/* `match metric METRIC' */
/* Match function return 1 if match is success else return zero. */
-static route_map_result_t route_match_metric(void *rule,
- const struct prefix *prefix,
- route_map_object_t type,
- void *object)
+static enum route_map_match_result_t
+route_match_metric(void *rule, const struct prefix *prefix,
+ route_map_object_t type, void *object)
{
uint32_t *metric;
uint32_t check;
/* `match interface IFNAME' */
/* Match function return 1 if match is success else return zero. */
-static route_map_result_t route_match_interface(void *rule,
- const struct prefix *prefix,
- route_map_object_t type,
- void *object)
+static enum route_map_match_result_t
+route_match_interface(void *rule, const struct prefix *prefix,
+ route_map_object_t type, void *object)
{
struct rip_info *rinfo;
struct interface *ifp;
/* `match ip next-hop IP_ACCESS_LIST' */
/* Match function return 1 if match is success else return zero. */
-static route_map_result_t route_match_ip_next_hop(void *rule,
- const struct prefix *prefix,
- route_map_object_t type,
- void *object)
+static enum route_map_match_result_t
+route_match_ip_next_hop(void *rule, const struct prefix *prefix,
+ route_map_object_t type, void *object)
{
struct access_list *alist;
struct rip_info *rinfo;
/* `match ip next-hop prefix-list PREFIX_LIST' */
-static route_map_result_t
+static enum route_map_match_result_t
route_match_ip_next_hop_prefix_list(void *rule, const struct prefix *prefix,
route_map_object_t type, void *object)
{
/* Match function should return 1 if match is success else return
zero. */
-static route_map_result_t route_match_ip_address(void *rule,
- const struct prefix *prefix,
- route_map_object_t type,
- void *object)
+static enum route_map_match_result_t
+route_match_ip_address(void *rule, const struct prefix *prefix,
+ route_map_object_t type, void *object)
{
struct access_list *alist;
/* `match ip address prefix-list PREFIX_LIST' */
-static route_map_result_t
+static enum route_map_match_result_t
route_match_ip_address_prefix_list(void *rule, const struct prefix *prefix,
route_map_object_t type, void *object)
{
/* `match tag TAG' */
/* Match function return 1 if match is success else return zero. */
-static route_map_result_t route_match_tag(void *rule, const struct prefix *p,
- route_map_object_t type, void *object)
+static enum route_map_match_result_t
+route_match_tag(void *rule, const struct prefix *p, route_map_object_t type,
+ void *object)
{
route_tag_t *tag;
struct rip_info *rinfo;
/* `set metric METRIC' */
/* Set metric to attribute. */
-static route_map_result_t route_set_metric(void *rule,
- const struct prefix *prefix,
- route_map_object_t type,
- void *object)
+static enum route_map_match_result_t
+route_set_metric(void *rule, const struct prefix *prefix,
+ route_map_object_t type, void *object)
{
if (type == RMAP_RIP) {
struct rip_metric_modifier *mod;
/* `set ip next-hop IP_ADDRESS' */
/* Set nexthop to object. ojbect must be pointer to struct attr. */
-static route_map_result_t route_set_ip_nexthop(void *rule,
+static enum route_map_match_result_t route_set_ip_nexthop(void *rule,
const struct prefix *prefix,
route_map_object_t type,
void *object)
/* `set tag TAG' */
/* Set tag to object. ojbect must be pointer to struct attr. */
-static route_map_result_t route_set_tag(void *rule, const struct prefix *prefix,
- route_map_object_t type, void *object)
+static enum route_map_match_result_t
+route_set_tag(void *rule, const struct prefix *prefix, route_map_object_t type,
+ void *object)
{
route_tag_t *tag;
struct rip_info *rinfo;
/* `match metric METRIC' */
/* Match function return 1 if match is success else return zero. */
-static route_map_result_t route_match_metric(void *rule,
- const struct prefix *prefix,
- route_map_object_t type,
- void *object)
+static enum route_map_match_result_t
+route_match_metric(void *rule, const struct prefix *prefix,
+ route_map_object_t type, void *object)
{
uint32_t *metric;
struct ripng_info *rinfo;
/* `match interface IFNAME' */
/* Match function return 1 if match is success else return zero. */
-static route_map_result_t route_match_interface(void *rule,
- const struct prefix *prefix,
- route_map_object_t type,
- void *object)
+static enum route_map_match_result_t
+route_match_interface(void *rule, const struct prefix *prefix,
+ route_map_object_t type, void *object)
{
struct ripng_info *rinfo;
struct interface *ifp;
/* `match tag TAG' */
/* Match function return 1 if match is success else return zero. */
-static route_map_result_t route_match_tag(void *rule,
- const struct prefix *prefix,
- route_map_object_t type, void *object)
+static enum route_map_match_result_t route_match_tag(void *rule,
+ const struct prefix *prefix,
+ route_map_object_t type,
+ void *object)
{
route_tag_t *tag;
struct ripng_info *rinfo;
/* `set metric METRIC' */
/* Set metric to attribute. */
-static route_map_result_t route_set_metric(void *rule,
- const struct prefix *prefix,
- route_map_object_t type,
- void *object)
+static enum route_map_match_result_t
+route_set_metric(void *rule, const struct prefix *prefix,
+ route_map_object_t type, void *object)
{
if (type == RMAP_RIPNG) {
struct rip_metric_modifier *mod;
/* `set ipv6 next-hop local IP_ADDRESS' */
/* Set nexthop to object. ojbect must be pointer to struct attr. */
-static route_map_result_t route_set_ipv6_nexthop_local(void *rule,
- const struct prefix *p,
- route_map_object_t type,
- void *object)
+static enum route_map_match_result_t
+route_set_ipv6_nexthop_local(void *rule, const struct prefix *p,
+ route_map_object_t type, void *object)
{
struct in6_addr *address;
struct ripng_info *rinfo;
/* `set tag TAG' */
/* Set tag to object. ojbect must be pointer to struct attr. */
-static route_map_result_t route_set_tag(void *rule,
- const struct prefix *prefix,
- route_map_object_t type, void *object)
+static enum route_map_match_result_t
+route_set_tag(void *rule, const struct prefix *prefix, route_map_object_t type,
+ void *object)
{
route_tag_t *tag;
struct ripng_info *rinfo;
memset(str, 0, 3);
if (rinfo->suppress)
- strcat(str, "S");
+ strlcat(str, "S", sizeof(str));
switch (rinfo->sub_type) {
case RIPNG_ROUTE_RTE:
- strcat(str, "n");
+ strlcat(str, "n", sizeof(str));
break;
case RIPNG_ROUTE_STATIC:
- strcat(str, "s");
+ strlcat(str, "s", sizeof(str));
break;
case RIPNG_ROUTE_DEFAULT:
- strcat(str, "d");
+ strlcat(str, "d", sizeof(str));
break;
case RIPNG_ROUTE_REDISTRIBUTE:
- strcat(str, "r");
+ strlcat(str, "r", sizeof(str));
break;
case RIPNG_ROUTE_INTERFACE:
- strcat(str, "i");
+ strlcat(str, "i", sizeof(str));
break;
default:
- strcat(str, "?");
+ strlcat(str, "?", sizeof(str));
break;
}
si->tag = tag;
si->vrf_id = svrf->vrf->vrf_id;
si->nh_vrf_id = nh_svrf->vrf->vrf_id;
- strcpy(si->nh_vrfname, nh_svrf->vrf->name);
+ strlcpy(si->nh_vrfname, nh_svrf->vrf->name, sizeof(si->nh_vrfname));
si->table_id = table_id;
si->onlink = onlink;
while (fgets(vty->buf, VTY_BUFSIZ, confp)) {
lineno++;
tried = 0;
- strcpy(vty_buf_copy, vty->buf);
+ strlcpy(vty_buf_copy, vty->buf, VTY_BUFSIZ);
vty_buf_trimmed = trim(vty_buf_copy);
switch (vty->node) {
return CMD_SUCCESS;
}
+DEFUNSH(VTYSH_ALL, vtysh_debug_memstats,
+ vtysh_debug_memstats_cmd, "[no] debug memstats-at-exit",
+ NO_STR
+ "Debug\n"
+ "Print memory statistics at exit\n")
+{
+ return CMD_SUCCESS;
+}
+
DEFUNSH(VTYSH_ALL, no_vtysh_log_timestamp_precision,
no_vtysh_log_timestamp_precision_cmd, "no log timestamp precision",
NO_STR
{
char *integrate_sav = NULL;
- integrate_sav = malloc(strlen(fbackup) + strlen(CONF_BACKUP_EXT) + 1);
- strcpy(integrate_sav, fbackup);
- strcat(integrate_sav, CONF_BACKUP_EXT);
+ size_t integrate_sav_sz = strlen(fbackup) + strlen(CONF_BACKUP_EXT) + 1;
+ integrate_sav = malloc(integrate_sav_sz);
+ strlcpy(integrate_sav, fbackup, integrate_sav_sz);
+ strlcat(integrate_sav, CONF_BACKUP_EXT, integrate_sav_sz);
/* Move current configuration file to backup config file. */
if (unlink(integrate_sav) != 0) {
char *vtysh_prompt(void)
{
- static char buf[100];
+ static char buf[512];
snprintf(buf, sizeof buf, cmd_prompt(vty->node), cmd_hostname_get());
return buf;
install_element(VIEW_NODE, &vtysh_show_debugging_hashtable_cmd);
install_element(ENABLE_NODE, &vtysh_debug_all_cmd);
install_element(CONFIG_NODE, &vtysh_debug_all_cmd);
+ install_element(ENABLE_NODE, &vtysh_debug_memstats_cmd);
+ install_element(CONFIG_NODE, &vtysh_debug_memstats_cmd);
/* misc lib show commands */
install_element(VIEW_NODE, &vtysh_show_memory_cmd);
*/
void vtysh_config_write(void)
{
- char line[81];
+ char line[512];
if (cmd_hostname_get()) {
- sprintf(line, "hostname %s", cmd_hostname_get());
+ snprintf(line, sizeof(line), "hostname %s", cmd_hostname_get());
vtysh_config_parse_line(NULL, line);
}
p = (struct prefix_ipv4 *)dplane_ctx_get_intf_addr(ctx);
memset(&addreq, 0, sizeof(addreq));
- strncpy((char *)&addreq.ifra_name, dplane_ctx_get_ifname(ctx),
+ strlcpy((char *)&addreq.ifra_name, dplane_ctx_get_ifname(ctx),
sizeof(addreq.ifra_name));
memset(&addr, 0, sizeof(struct sockaddr_in));
p = (struct prefix_ipv4 *)dplane_ctx_get_intf_addr(ctx);
memset(&addreq, 0, sizeof(addreq));
- strncpy((char *)&addreq.ifra_name, dplane_ctx_get_ifname(ctx),
+ strlcpy((char *)&addreq.ifra_name, dplane_ctx_get_ifname(ctx),
sizeof(addreq.ifra_name));
memset(&addr, 0, sizeof(struct sockaddr_in));
p = (struct prefix_ipv4 *)dplane_ctx_get_intf_addr(ctx);
- strncpy(ifreq.ifr_name, dplane_ctx_get_ifname(ctx),
+ strlcpy(ifreq.ifr_name, dplane_ctx_get_ifname(ctx),
sizeof(ifreq.ifr_name));
memset(&addr, 0, sizeof(struct sockaddr_in));
size_t rta_getsdlname(caddr_t sap, void *destp, short *destlen)
{
struct sockaddr_dl *sdl = (struct sockaddr_dl *)sap;
- struct sockaddr *sa = (struct sockaddr *)sap;
uint8_t *dest = destp;
size_t tlen, copylen;
copylen = sdl->sdl_nlen;
#ifdef HAVE_STRUCT_SOCKADDR_SA_LEN
+ struct sockaddr *sa = (struct sockaddr *)sap;
+
tlen = (sa->sa_len == 0) ? sizeof(ROUNDUP_TYPE) : ROUNDUP(sa->sa_len);
#else /* !HAVE_STRUCT_SOCKADDR_SA_LEN */
tlen = SAROUNDUP(sap);
int ifm_read(struct if_msghdr *ifm)
{
struct interface *ifp = NULL;
- struct sockaddr_dl *sdl;
+ struct sockaddr_dl *sdl = NULL;
char ifname[IFNAMSIZ];
short ifnlen = 0;
int maskbit;
struct route_entry *newre;
struct route_entry *same;
struct prefix p;
- route_map_result_t ret = RMAP_MATCH;
+ route_map_result_t ret = RMAP_PERMITMATCH;
afi_t afi;
afi = family2afi(rn->p.family);
afi, re->type, re->instance, &rn->p, re->ng.nexthop,
re->vrf_id, re->tag, rmap_name);
- if (ret != RMAP_MATCH) {
+ if (ret != RMAP_PERMITMATCH) {
UNSET_FLAG(re->flags, ZEBRA_FLAG_SELECTED);
zebra_del_import_table_entry(rn, re);
return 0;
/* Link list. */
struct re_list_item next;
- /* Nexthop structure */
+ /* Nexthop structure (from RIB) */
struct nexthop_group ng;
+ /* Nexthop group from FIB (optional) */
+ struct nexthop_group fib_ng;
+
/* Tag */
route_tag_t tag;
DECLARE_HOOK(rib_update, (struct route_node * rn, const char *reason),
(rn, reason))
+/*
+ * Access active nexthop-group, either RIB or FIB version
+ */
+static inline struct nexthop_group *rib_active_nhg(struct route_entry *re)
+{
+ if (re->fib_ng.nexthop)
+ return &(re->fib_ng);
+ else
+ return &(re->ng);
+}
extern void zebra_vty_init(void);
sprintf(temp, "%s(%d) ", ifp ? ifp->name : "Unknown",
oif[count]);
- strcat(oif_list, temp);
+ strlcat(oif_list, temp, sizeof(oif_list));
}
struct zebra_vrf *zvrf = zebra_vrf_lookup_by_id(vrf);
ifp = if_lookup_by_index(iif, vrf);
zebra/zebra_mpls_null.c \
zebra/zebra_mpls_vty.c \
zebra/zebra_mroute.c \
+ zebra/zebra_nhg.c \
zebra/zebra_ns.c \
zebra/zebra_pbr.c \
zebra/zebra_ptm.c \
zebra/zebra_memory.h \
zebra/zebra_mpls.h \
zebra/zebra_mroute.h \
+ zebra/zebra_nhg.h \
zebra/zebra_ns.h \
zebra/zebra_pbr.h \
zebra/zebra_ptm.h \
struct nexthop *nexthop;
int count = 0;
afi_t afi;
+ size_t stream_size =
+ MAX(ZEBRA_MAX_PACKET_SIZ, sizeof(struct zapi_route));
memset(&api, 0, sizeof(api));
api.vrf_id = re->vrf_id;
SET_FLAG(api.message, ZAPI_MESSAGE_MTU);
api.mtu = re->mtu;
- struct stream *s = stream_new(ZEBRA_MAX_PACKET_SIZ);
+ struct stream *s = stream_new(stream_size);
/* Encode route and send. */
if (zapi_route_encode(cmd, s, &api) < 0) {
uint32_t zd_seq;
uint32_t zd_old_seq;
+ /* Some updates may be generated by notifications: allow the
+ * plugin to notice and ignore results from its own notifications.
+ */
+ uint32_t zd_notif_provider;
+
/* TODO -- internal/sub-operation status? */
enum zebra_dplane_result zd_remote_status;
enum zebra_dplane_result zd_kernel_status;
/* Flags */
int dp_flags;
+ int (*dp_start)(struct zebra_dplane_provider *prov);
+
int (*dp_fp)(struct zebra_dplane_provider *prov);
int (*dp_fini)(struct zebra_dplane_provider *prov, bool early_p);
/*
* Allocate a dataplane update context
*/
-static struct zebra_dplane_ctx *dplane_ctx_alloc(void)
+struct zebra_dplane_ctx *dplane_ctx_alloc(void)
{
struct zebra_dplane_ctx *p;
case DPLANE_OP_ROUTE_DELETE:
case DPLANE_OP_SYS_ROUTE_ADD:
case DPLANE_OP_SYS_ROUTE_DELETE:
+ case DPLANE_OP_ROUTE_NOTIFY:
/* Free allocated nexthops */
if ((*pctx)->u.rinfo.zd_ng.nexthop) {
case DPLANE_OP_LSP_INSTALL:
case DPLANE_OP_LSP_UPDATE:
case DPLANE_OP_LSP_DELETE:
+ case DPLANE_OP_LSP_NOTIFY:
{
zebra_nhlfe_t *nhlfe, *next;
return CHECK_FLAG(ctx->zd_flags, DPLANE_CTX_FLAG_NO_KERNEL);
}
+void dplane_ctx_set_op(struct zebra_dplane_ctx *ctx, enum dplane_op_e op)
+{
+ DPLANE_CTX_VALID(ctx);
+ ctx->zd_op = op;
+}
+
enum dplane_op_e dplane_ctx_get_op(const struct zebra_dplane_ctx *ctx)
{
DPLANE_CTX_VALID(ctx);
case DPLANE_OP_ROUTE_DELETE:
ret = "ROUTE_DELETE";
break;
+ case DPLANE_OP_ROUTE_NOTIFY:
+ ret = "ROUTE_NOTIFY";
+ break;
case DPLANE_OP_LSP_INSTALL:
ret = "LSP_INSTALL";
case DPLANE_OP_LSP_DELETE:
ret = "LSP_DELETE";
break;
+ case DPLANE_OP_LSP_NOTIFY:
+ ret = "LSP_NOTIFY";
+ break;
case DPLANE_OP_PW_INSTALL:
ret = "PW_INSTALL";
return ret;
}
+void dplane_ctx_set_dest(struct zebra_dplane_ctx *ctx,
+ const struct prefix *dest)
+{
+ DPLANE_CTX_VALID(ctx);
+
+ prefix_copy(&(ctx->u.rinfo.zd_dest), dest);
+}
+
const struct prefix *dplane_ctx_get_dest(const struct zebra_dplane_ctx *ctx)
{
DPLANE_CTX_VALID(ctx);
return &(ctx->u.rinfo.zd_dest);
}
+void dplane_ctx_set_src(struct zebra_dplane_ctx *ctx, const struct prefix *src)
+{
+ DPLANE_CTX_VALID(ctx);
+
+ if (src)
+ prefix_copy(&(ctx->u.rinfo.zd_src), src);
+ else
+ memset(&(ctx->u.rinfo.zd_src), 0, sizeof(struct prefix));
+}
+
/* Source prefix is a little special - return NULL for "no src prefix" */
const struct prefix *dplane_ctx_get_src(const struct zebra_dplane_ctx *ctx)
{
return ctx->zd_old_seq;
}
+void dplane_ctx_set_vrf(struct zebra_dplane_ctx *ctx, vrf_id_t vrf)
+{
+ DPLANE_CTX_VALID(ctx);
+
+ ctx->zd_vrf_id = vrf;
+}
+
vrf_id_t dplane_ctx_get_vrf(const struct zebra_dplane_ctx *ctx)
{
DPLANE_CTX_VALID(ctx);
return ctx->zd_vrf_id;
}
+bool dplane_ctx_is_from_notif(const struct zebra_dplane_ctx *ctx)
+{
+ DPLANE_CTX_VALID(ctx);
+
+ return (ctx->zd_notif_provider != 0);
+}
+
+uint32_t dplane_ctx_get_notif_provider(const struct zebra_dplane_ctx *ctx)
+{
+ DPLANE_CTX_VALID(ctx);
+
+ return ctx->zd_notif_provider;
+}
+
+void dplane_ctx_set_notif_provider(struct zebra_dplane_ctx *ctx,
+ uint32_t id)
+{
+ DPLANE_CTX_VALID(ctx);
+
+ ctx->zd_notif_provider = id;
+}
+
+void dplane_ctx_set_type(struct zebra_dplane_ctx *ctx, int type)
+{
+ DPLANE_CTX_VALID(ctx);
+
+ ctx->u.rinfo.zd_type = type;
+}
+
int dplane_ctx_get_type(const struct zebra_dplane_ctx *ctx)
{
DPLANE_CTX_VALID(ctx);
return ctx->u.rinfo.zd_old_type;
}
+void dplane_ctx_set_afi(struct zebra_dplane_ctx *ctx, afi_t afi)
+{
+ DPLANE_CTX_VALID(ctx);
+
+ ctx->u.rinfo.zd_afi = afi;
+}
+
afi_t dplane_ctx_get_afi(const struct zebra_dplane_ctx *ctx)
{
DPLANE_CTX_VALID(ctx);
return ctx->u.rinfo.zd_afi;
}
+void dplane_ctx_set_safi(struct zebra_dplane_ctx *ctx, safi_t safi)
+{
+ DPLANE_CTX_VALID(ctx);
+
+ ctx->u.rinfo.zd_safi = safi;
+}
+
safi_t dplane_ctx_get_safi(const struct zebra_dplane_ctx *ctx)
{
DPLANE_CTX_VALID(ctx);
return ctx->u.rinfo.zd_safi;
}
+void dplane_ctx_set_table(struct zebra_dplane_ctx *ctx, uint32_t table)
+{
+ DPLANE_CTX_VALID(ctx);
+
+ ctx->zd_table_id = table;
+}
+
uint32_t dplane_ctx_get_table(const struct zebra_dplane_ctx *ctx)
{
DPLANE_CTX_VALID(ctx);
return ctx->u.rinfo.zd_tag;
}
+void dplane_ctx_set_tag(struct zebra_dplane_ctx *ctx, route_tag_t tag)
+{
+ DPLANE_CTX_VALID(ctx);
+
+ ctx->u.rinfo.zd_tag = tag;
+}
+
route_tag_t dplane_ctx_get_old_tag(const struct zebra_dplane_ctx *ctx)
{
DPLANE_CTX_VALID(ctx);
return ctx->u.rinfo.zd_instance;
}
+void dplane_ctx_set_instance(struct zebra_dplane_ctx *ctx, uint16_t instance)
+{
+ DPLANE_CTX_VALID(ctx);
+
+ ctx->u.rinfo.zd_instance = instance;
+}
+
uint16_t dplane_ctx_get_old_instance(const struct zebra_dplane_ctx *ctx)
{
DPLANE_CTX_VALID(ctx);
return ctx->u.rinfo.zd_distance;
}
+void dplane_ctx_set_distance(struct zebra_dplane_ctx *ctx, uint8_t distance)
+{
+ DPLANE_CTX_VALID(ctx);
+
+ ctx->u.rinfo.zd_distance = distance;
+}
+
uint8_t dplane_ctx_get_old_distance(const struct zebra_dplane_ctx *ctx)
{
DPLANE_CTX_VALID(ctx);
return ctx->u.rinfo.zd_old_distance;
}
+void dplane_ctx_set_nexthops(struct zebra_dplane_ctx *ctx, struct nexthop *nh)
+{
+ DPLANE_CTX_VALID(ctx);
+
+ if (ctx->u.rinfo.zd_ng.nexthop) {
+ nexthops_free(ctx->u.rinfo.zd_ng.nexthop);
+ ctx->u.rinfo.zd_ng.nexthop = NULL;
+ }
+ copy_nexthops(&(ctx->u.rinfo.zd_ng.nexthop), nh, NULL);
+}
+
const struct nexthop_group *dplane_ctx_get_ng(
const struct zebra_dplane_ctx *ctx)
{
return ctx->u.lsp.ile.in_label;
}
+void dplane_ctx_set_in_label(struct zebra_dplane_ctx *ctx, mpls_label_t label)
+{
+ DPLANE_CTX_VALID(ctx);
+
+ ctx->u.lsp.ile.in_label = label;
+}
+
uint8_t dplane_ctx_get_addr_family(const struct zebra_dplane_ctx *ctx)
{
DPLANE_CTX_VALID(ctx);
return ctx->u.lsp.addr_family;
}
+void dplane_ctx_set_addr_family(struct zebra_dplane_ctx *ctx,
+ uint8_t family)
+{
+ DPLANE_CTX_VALID(ctx);
+
+ ctx->u.lsp.addr_family = family;
+}
+
uint32_t dplane_ctx_get_lsp_flags(const struct zebra_dplane_ctx *ctx)
{
DPLANE_CTX_VALID(ctx);
return ctx->u.lsp.flags;
}
+void dplane_ctx_set_lsp_flags(struct zebra_dplane_ctx *ctx,
+ uint32_t flags)
+{
+ DPLANE_CTX_VALID(ctx);
+
+ ctx->u.lsp.flags = flags;
+}
+
const zebra_nhlfe_t *dplane_ctx_get_nhlfe(const struct zebra_dplane_ctx *ctx)
{
DPLANE_CTX_VALID(ctx);
return ctx->u.lsp.nhlfe_list;
}
+zebra_nhlfe_t *dplane_ctx_add_nhlfe(struct zebra_dplane_ctx *ctx,
+ enum lsp_types_t lsp_type,
+ enum nexthop_types_t nh_type,
+ union g_addr *gate,
+ ifindex_t ifindex,
+ mpls_label_t out_label)
+{
+ zebra_nhlfe_t *nhlfe;
+
+ DPLANE_CTX_VALID(ctx);
+
+ nhlfe = zebra_mpls_lsp_add_nhlfe(&(ctx->u.lsp),
+ lsp_type, nh_type, gate,
+ ifindex, out_label);
+
+ return nhlfe;
+}
+
const zebra_nhlfe_t *
dplane_ctx_get_best_nhlfe(const struct zebra_dplane_ctx *ctx)
{
return ctx->u.lsp.best_nhlfe;
}
+const zebra_nhlfe_t *
+dplane_ctx_set_best_nhlfe(struct zebra_dplane_ctx *ctx,
+ zebra_nhlfe_t *nhlfe)
+{
+ DPLANE_CTX_VALID(ctx);
+
+ ctx->u.lsp.best_nhlfe = nhlfe;
+ return ctx->u.lsp.best_nhlfe;
+}
+
uint32_t dplane_ctx_get_lsp_num_ecmp(const struct zebra_dplane_ctx *ctx)
{
DPLANE_CTX_VALID(ctx);
return ret;
}
+/*
+ * Update from an async notification, to bring other fibs up-to-date.
+ */
+enum zebra_dplane_result
+dplane_route_notif_update(struct route_node *rn,
+ struct route_entry *re,
+ enum dplane_op_e op,
+ struct zebra_dplane_ctx *ctx)
+{
+ enum zebra_dplane_result ret = ZEBRA_DPLANE_REQUEST_FAILURE;
+ struct zebra_dplane_ctx *new_ctx = NULL;
+ struct nexthop *nexthop;
+
+ if (rn == NULL || re == NULL)
+ goto done;
+
+ new_ctx = dplane_ctx_alloc();
+ if (new_ctx == NULL)
+ goto done;
+
+ /* Init context with info from zebra data structs */
+ dplane_ctx_route_init(new_ctx, op, rn, re);
+
+ /* For add/update, need to adjust the nexthops so that we match
+ * the notification state, which may not be the route-entry/RIB
+ * state.
+ */
+ if (op == DPLANE_OP_ROUTE_UPDATE ||
+ op == DPLANE_OP_ROUTE_INSTALL) {
+
+ nexthops_free(new_ctx->u.rinfo.zd_ng.nexthop);
+ new_ctx->u.rinfo.zd_ng.nexthop = NULL;
+
+ copy_nexthops(&(new_ctx->u.rinfo.zd_ng.nexthop),
+ (rib_active_nhg(re))->nexthop, NULL);
+
+ for (ALL_NEXTHOPS(new_ctx->u.rinfo.zd_ng, nexthop))
+ UNSET_FLAG(nexthop->flags, NEXTHOP_FLAG_FIB);
+
+ }
+
+ /* Capture info about the source of the notification, in 'ctx' */
+ dplane_ctx_set_notif_provider(new_ctx,
+ dplane_ctx_get_notif_provider(ctx));
+
+ dplane_route_enqueue(new_ctx);
+
+ ret = ZEBRA_DPLANE_REQUEST_QUEUED;
+
+done:
+ return ret;
+}
+
/*
* Enqueue LSP add for the dataplane.
*/
return ret;
}
+/* Update or un-install resulting from an async notification */
+enum zebra_dplane_result
+dplane_lsp_notif_update(zebra_lsp_t *lsp,
+ enum dplane_op_e op,
+ struct zebra_dplane_ctx *notif_ctx)
+{
+ enum zebra_dplane_result result = ZEBRA_DPLANE_REQUEST_FAILURE;
+ int ret = EINVAL;
+ struct zebra_dplane_ctx *ctx = NULL;
+
+ /* Obtain context block */
+ ctx = dplane_ctx_alloc();
+ if (ctx == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ ret = dplane_ctx_lsp_init(ctx, op, lsp);
+ if (ret != AOK)
+ goto done;
+
+ /* Capture info about the source of the notification */
+ dplane_ctx_set_notif_provider(
+ ctx,
+ dplane_ctx_get_notif_provider(notif_ctx));
+
+ ret = dplane_route_enqueue(ctx);
+
+done:
+ /* Update counter */
+ atomic_fetch_add_explicit(&zdplane_info.dg_lsps_in, 1,
+ memory_order_relaxed);
+
+ if (ret == AOK)
+ result = ZEBRA_DPLANE_REQUEST_QUEUED;
+ else {
+ atomic_fetch_add_explicit(&zdplane_info.dg_lsp_errors, 1,
+ memory_order_relaxed);
+ if (ctx)
+ dplane_ctx_free(&ctx);
+ }
+ return result;
+}
+
/*
* Enqueue pseudowire install for the dataplane.
*/
int dplane_provider_register(const char *name,
enum dplane_provider_prio prio,
int flags,
+ int (*start_fp)(struct zebra_dplane_provider *),
int (*fp)(struct zebra_dplane_provider *),
int (*fini_fp)(struct zebra_dplane_provider *,
bool early),
p->dp_priority = prio;
p->dp_fp = fp;
+ p->dp_start = start_fp;
p->dp_fini = fini_fp;
p->dp_data = data;
return AOK;
}
+/*
+ * Enqueue a context directly to zebra main.
+ */
+void dplane_provider_enqueue_to_zebra(struct zebra_dplane_ctx *ctx)
+{
+ struct dplane_ctx_q temp_list;
+
+ /* Zebra's api takes a list, so we need to use a temporary list */
+ TAILQ_INIT(&temp_list);
+
+ TAILQ_INSERT_TAIL(&temp_list, ctx, zd_q_entries);
+ (zdplane_info.dg_results_cb)(&temp_list);
+}
+
/*
* Kernel dataplane provider
*/
res = kernel_dplane_address_update(ctx);
break;
- /* Ignore system 'notifications' - the kernel already knows */
+ /* Ignore 'notifications' - no-op */
case DPLANE_OP_SYS_ROUTE_ADD:
case DPLANE_OP_SYS_ROUTE_DELETE:
+ case DPLANE_OP_ROUTE_NOTIFY:
+ case DPLANE_OP_LSP_NOTIFY:
res = ZEBRA_DPLANE_REQUEST_SUCCESS;
break;
ret = dplane_provider_register("Kernel",
DPLANE_PRIO_KERNEL,
- DPLANE_PROV_FLAGS_DEFAULT,
+ DPLANE_PROV_FLAGS_DEFAULT, NULL,
kernel_dplane_process_func,
NULL,
NULL, NULL);
/* Optional test provider ... */
ret = dplane_provider_register("Test",
DPLANE_PRIO_PRE_KERNEL,
- DPLANE_PROV_FLAGS_DEFAULT,
+ DPLANE_PROV_FLAGS_DEFAULT, NULL,
test_dplane_process_func,
test_dplane_shutdown_func,
NULL /* data */, NULL);
TAILQ_INIT(&error_list);
-
/* Call through to zebra main */
(zdplane_info.dg_results_cb)(&work_list);
*/
void zebra_dplane_start(void)
{
- /* Start dataplane pthread */
-
+ struct zebra_dplane_provider *prov;
struct frr_pthread_attr pattr = {
.start = frr_pthread_attr_default.start,
.stop = frr_pthread_attr_default.stop
};
+ /* Start dataplane pthread */
+
zdplane_info.dg_pthread = frr_pthread_new(&pattr, "Zebra dplane thread",
"Zebra dplane");
thread_add_event(zdplane_info.dg_master, dplane_thread_loop, NULL, 0,
&zdplane_info.dg_t_update);
+ /* Call start callbacks for registered providers */
+
+ DPLANE_LOCK();
+ prov = TAILQ_FIRST(&zdplane_info.dg_providers_q);
+ DPLANE_UNLOCK();
+
+ while (prov) {
+
+ if (prov->dp_start)
+ (prov->dp_start)(prov);
+
+ /* Locate next provider */
+ DPLANE_LOCK();
+ prov = TAILQ_NEXT(prov, dp_prov_link);
+ DPLANE_UNLOCK();
+ }
+
frr_pthread_run(zdplane_info.dg_pthread, NULL);
}
DPLANE_OP_ROUTE_INSTALL,
DPLANE_OP_ROUTE_UPDATE,
DPLANE_OP_ROUTE_DELETE,
+ DPLANE_OP_ROUTE_NOTIFY,
/* LSP update */
DPLANE_OP_LSP_INSTALL,
DPLANE_OP_LSP_UPDATE,
DPLANE_OP_LSP_DELETE,
+ DPLANE_OP_LSP_NOTIFY,
/* Pseudowire update */
DPLANE_OP_PW_INSTALL,
*/
TAILQ_HEAD(dplane_ctx_q, zebra_dplane_ctx);
+/* Allocate a context object */
+struct zebra_dplane_ctx *dplane_ctx_alloc(void);
+
/* Return a dataplane results context block after use; the caller's pointer will
* be cleared.
*/
const char *dplane_res2str(enum zebra_dplane_result res);
enum dplane_op_e dplane_ctx_get_op(const struct zebra_dplane_ctx *ctx);
+void dplane_ctx_set_op(struct zebra_dplane_ctx *ctx, enum dplane_op_e op);
const char *dplane_op2str(enum dplane_op_e op);
const struct prefix *dplane_ctx_get_dest(const struct zebra_dplane_ctx *ctx);
+void dplane_ctx_set_dest(struct zebra_dplane_ctx *ctx,
+ const struct prefix *dest);
/* Retrieve last/current provider id */
uint32_t dplane_ctx_get_provider(const struct zebra_dplane_ctx *ctx);
* to mean "no src prefix"
*/
const struct prefix *dplane_ctx_get_src(const struct zebra_dplane_ctx *ctx);
+void dplane_ctx_set_src(struct zebra_dplane_ctx *ctx, const struct prefix *src);
bool dplane_ctx_is_update(const struct zebra_dplane_ctx *ctx);
uint32_t dplane_ctx_get_seq(const struct zebra_dplane_ctx *ctx);
uint32_t dplane_ctx_get_old_seq(const struct zebra_dplane_ctx *ctx);
+void dplane_ctx_set_vrf(struct zebra_dplane_ctx *ctx, vrf_id_t vrf);
vrf_id_t dplane_ctx_get_vrf(const struct zebra_dplane_ctx *ctx);
+bool dplane_ctx_is_from_notif(const struct zebra_dplane_ctx *ctx);
+void dplane_ctx_set_notif_provider(struct zebra_dplane_ctx *ctx,
+ uint32_t id);
+uint32_t dplane_ctx_get_notif_provider(const struct zebra_dplane_ctx *ctx);
+
/* Accessors for route update information */
+void dplane_ctx_set_type(struct zebra_dplane_ctx *ctx, int type);
int dplane_ctx_get_type(const struct zebra_dplane_ctx *ctx);
int dplane_ctx_get_old_type(const struct zebra_dplane_ctx *ctx);
+void dplane_ctx_set_afi(struct zebra_dplane_ctx *ctx, afi_t afi);
afi_t dplane_ctx_get_afi(const struct zebra_dplane_ctx *ctx);
+void dplane_ctx_set_safi(struct zebra_dplane_ctx *ctx, safi_t safi);
safi_t dplane_ctx_get_safi(const struct zebra_dplane_ctx *ctx);
+void dplane_ctx_set_table(struct zebra_dplane_ctx *ctx, uint32_t table);
uint32_t dplane_ctx_get_table(const struct zebra_dplane_ctx *ctx);
route_tag_t dplane_ctx_get_tag(const struct zebra_dplane_ctx *ctx);
+void dplane_ctx_set_tag(struct zebra_dplane_ctx *ctx, route_tag_t tag);
route_tag_t dplane_ctx_get_old_tag(const struct zebra_dplane_ctx *ctx);
uint16_t dplane_ctx_get_instance(const struct zebra_dplane_ctx *ctx);
+void dplane_ctx_set_instance(struct zebra_dplane_ctx *ctx, uint16_t instance);
uint16_t dplane_ctx_get_old_instance(const struct zebra_dplane_ctx *ctx);
uint32_t dplane_ctx_get_metric(const struct zebra_dplane_ctx *ctx);
uint32_t dplane_ctx_get_old_metric(const struct zebra_dplane_ctx *ctx);
uint32_t dplane_ctx_get_mtu(const struct zebra_dplane_ctx *ctx);
uint32_t dplane_ctx_get_nh_mtu(const struct zebra_dplane_ctx *ctx);
uint8_t dplane_ctx_get_distance(const struct zebra_dplane_ctx *ctx);
+void dplane_ctx_set_distance(struct zebra_dplane_ctx *ctx, uint8_t distance);
uint8_t dplane_ctx_get_old_distance(const struct zebra_dplane_ctx *ctx);
+void dplane_ctx_set_nexthops(struct zebra_dplane_ctx *ctx, struct nexthop *nh);
const struct nexthop_group *dplane_ctx_get_ng(
const struct zebra_dplane_ctx *ctx);
const struct nexthop_group *dplane_ctx_get_old_ng(
/* Accessors for LSP information */
mpls_label_t dplane_ctx_get_in_label(const struct zebra_dplane_ctx *ctx);
+void dplane_ctx_set_in_label(struct zebra_dplane_ctx *ctx,
+ mpls_label_t label);
uint8_t dplane_ctx_get_addr_family(const struct zebra_dplane_ctx *ctx);
+void dplane_ctx_set_addr_family(struct zebra_dplane_ctx *ctx,
+ uint8_t family);
uint32_t dplane_ctx_get_lsp_flags(const struct zebra_dplane_ctx *ctx);
+void dplane_ctx_set_lsp_flags(struct zebra_dplane_ctx *ctx,
+ uint32_t flags);
const zebra_nhlfe_t *dplane_ctx_get_nhlfe(const struct zebra_dplane_ctx *ctx);
+zebra_nhlfe_t *dplane_ctx_add_nhlfe(struct zebra_dplane_ctx *ctx,
+ enum lsp_types_t lsp_type,
+ enum nexthop_types_t nh_type,
+ union g_addr *gate,
+ ifindex_t ifindex,
+ mpls_label_t out_label);
+
const zebra_nhlfe_t *dplane_ctx_get_best_nhlfe(
const struct zebra_dplane_ctx *ctx);
+const zebra_nhlfe_t *dplane_ctx_set_best_nhlfe(struct zebra_dplane_ctx *ctx,
+ zebra_nhlfe_t *nhlfe);
uint32_t dplane_ctx_get_lsp_num_ecmp(const struct zebra_dplane_ctx *ctx);
/* Accessors for pseudowire information */
enum zebra_dplane_result dplane_sys_route_del(struct route_node *rn,
struct route_entry *re);
+/* Update from an async notification, to bring other fibs up-to-date */
+enum zebra_dplane_result dplane_route_notif_update(
+ struct route_node *rn,
+ struct route_entry *re,
+ enum dplane_op_e op,
+ struct zebra_dplane_ctx *ctx);
+
/*
* Enqueue LSP change operations for the dataplane.
*/
enum zebra_dplane_result dplane_lsp_update(zebra_lsp_t *lsp);
enum zebra_dplane_result dplane_lsp_delete(zebra_lsp_t *lsp);
+/* Update or un-install resulting from an async notification */
+enum zebra_dplane_result dplane_lsp_notif_update(zebra_lsp_t *lsp,
+ enum dplane_op_e op,
+ struct zebra_dplane_ctx *ctx);
+
/*
* Enqueue pseudowire operations for the dataplane.
*/
int dplane_show_helper(struct vty *vty, bool detailed);
int dplane_show_provs_helper(struct vty *vty, bool detailed);
-
/*
* Dataplane providers: modules that process or consume dataplane events.
*/
* then checks the provider's outbound queue for completed work.
*/
-/* Providers offer an entry-point for shutdown and cleanup. This is called
+/*
+ * Providers can offer a 'start' callback; if present, the dataplane will
+ * call it when it is starting - when its pthread and event-scheduling
+ * thread_master are available.
+ */
+
+/* Providers can offer an entry-point for shutdown and cleanup. This is called
* with 'early' during shutdown, to indicate that the dataplane subsystem
* is allowing work to move through the providers and finish.
* When called without 'early', the provider should release
int dplane_provider_register(const char *name,
enum dplane_provider_prio prio,
int flags,
+ int (*start_fp)(struct zebra_dplane_provider *),
int (*fp)(struct zebra_dplane_provider *),
int (*fini_fp)(struct zebra_dplane_provider *,
bool early),
int dplane_provider_dequeue_in_list(struct zebra_dplane_provider *prov,
struct dplane_ctx_q *listp);
-/* Enqueue, maintain associated counter and locking */
+/* Enqueue completed work, maintain associated counter and locking */
void dplane_provider_enqueue_out_ctx(struct zebra_dplane_provider *prov,
struct zebra_dplane_ctx *ctx);
+/* Enqueue a context directly to zebra main. */
+void dplane_provider_enqueue_to_zebra(struct zebra_dplane_ctx *ctx);
+
/*
* Initialize the dataplane modules at zebra startup. This is currently called
* by the rib module. Zebra registers a results callback with the dataplane.
case NEXTHOP_TYPE_IPV6_IFINDEX:
inet_ntop(AF_INET6, &snhlfe->gate.ipv6, buf, size);
if (snhlfe->ifindex)
- strcat(buf,
- ifindex2ifname(snhlfe->ifindex, VRF_DEFAULT));
+ strlcat(buf,
+ ifindex2ifname(snhlfe->ifindex, VRF_DEFAULT),
+ size);
break;
default:
break;
dplane_ctx_fini(&ctx);
}
+/*
+ * Process async dplane notifications.
+ */
+void zebra_mpls_process_dplane_notify(struct zebra_dplane_ctx *ctx)
+{
+ struct zebra_vrf *zvrf;
+ zebra_ile_t tmp_ile;
+ struct hash *lsp_table;
+ zebra_lsp_t *lsp;
+ zebra_nhlfe_t *nhlfe;
+ const zebra_nhlfe_t *ctx_nhlfe;
+ struct nexthop *nexthop;
+ const struct nexthop *ctx_nexthop;
+ int start_count = 0, end_count = 0; /* Installed counts */
+ bool changed_p = false;
+ bool is_debug = (IS_ZEBRA_DEBUG_DPLANE | IS_ZEBRA_DEBUG_MPLS);
+
+ if (is_debug)
+ zlog_debug("LSP dplane notif, in-label %u",
+ dplane_ctx_get_in_label(ctx));
+
+ /* Look for zebra LSP object */
+ zvrf = vrf_info_lookup(VRF_DEFAULT);
+ if (zvrf == NULL)
+ goto done;
+
+ lsp_table = zvrf->lsp_table;
+
+ tmp_ile.in_label = dplane_ctx_get_in_label(ctx);
+ lsp = hash_lookup(lsp_table, &tmp_ile);
+ if (lsp == NULL) {
+ if (is_debug)
+ zlog_debug("dplane LSP notif: in-label %u not found",
+ dplane_ctx_get_in_label(ctx));
+ goto done;
+ }
+
+ /*
+ * The dataplane/forwarding plane is notifying zebra about the state
+ * of the nexthops associated with this LSP. First, we take a
+ * pre-scan pass to determine whether the LSP has transitioned
+ * from installed -> uninstalled. In that case, we need to have
+ * the existing state of the LSP objects available before making
+ * any changes.
+ */
+ for (nhlfe = lsp->nhlfe_list; nhlfe; nhlfe = nhlfe->next) {
+ char buf[NEXTHOP_STRLEN];
+
+ nexthop = nhlfe->nexthop;
+ if (!nexthop)
+ continue;
+
+ if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_FIB))
+ start_count++;
+
+ ctx_nexthop = NULL;
+ for (ctx_nhlfe = dplane_ctx_get_nhlfe(ctx);
+ ctx_nhlfe; ctx_nhlfe = ctx_nhlfe->next) {
+
+ ctx_nexthop = ctx_nhlfe->nexthop;
+ if (!ctx_nexthop)
+ continue;
+
+ if ((ctx_nexthop->type == nexthop->type) &&
+ nexthop_same(ctx_nexthop, nexthop)) {
+ /* Matched */
+ break;
+ }
+ }
+
+ if (is_debug)
+ nexthop2str(nexthop, buf, sizeof(buf));
+
+ if (ctx_nhlfe && ctx_nexthop) {
+ if (is_debug) {
+ const char *tstr = "";
+
+ if (!CHECK_FLAG(ctx_nhlfe->flags,
+ NHLFE_FLAG_INSTALLED))
+ tstr = "not ";
+
+ zlog_debug("LSP dplane notif: matched nh %s (%sinstalled)",
+ buf, tstr);
+ }
+
+ /* Test zebra nhlfe install state */
+ if (CHECK_FLAG(ctx_nhlfe->flags,
+ NHLFE_FLAG_INSTALLED)) {
+
+ if (!CHECK_FLAG(nhlfe->flags,
+ NHLFE_FLAG_INSTALLED))
+ changed_p = true;
+
+ /* Update counter */
+ end_count++;
+ } else {
+
+ if (CHECK_FLAG(nhlfe->flags,
+ NHLFE_FLAG_INSTALLED))
+ changed_p = true;
+ }
+
+ } else {
+ /* Not mentioned in lfib set -> uninstalled */
+ if (CHECK_FLAG(nhlfe->flags, NHLFE_FLAG_INSTALLED) ||
+ CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_ACTIVE) ||
+ CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_FIB)) {
+ changed_p = true;
+ }
+
+ if (is_debug)
+ zlog_debug("LSP dplane notif: no match, nh %s",
+ buf);
+ }
+ }
+
+ if (is_debug)
+ zlog_debug("LSP dplane notif: lfib start_count %d, end_count %d%s",
+ start_count, end_count,
+ changed_p ? ", changed" : "");
+
+ /*
+ * Has the LSP become uninstalled?
+ */
+ if (start_count > 0 && end_count == 0) {
+ /* Inform other lfibs */
+ dplane_lsp_notif_update(lsp, DPLANE_OP_LSP_DELETE, ctx);
+ }
+
+ /*
+ * Now we take a second pass and bring the zebra
+ * nexthop state into sync with the forwarding-plane state.
+ */
+ for (nhlfe = lsp->nhlfe_list; nhlfe; nhlfe = nhlfe->next) {
+ char buf[NEXTHOP_STRLEN];
+
+ nexthop = nhlfe->nexthop;
+ if (!nexthop)
+ continue;
+
+ ctx_nexthop = NULL;
+ for (ctx_nhlfe = dplane_ctx_get_nhlfe(ctx);
+ ctx_nhlfe; ctx_nhlfe = ctx_nhlfe->next) {
+
+ ctx_nexthop = ctx_nhlfe->nexthop;
+ if (!ctx_nexthop)
+ continue;
+
+ if ((ctx_nexthop->type == nexthop->type) &&
+ nexthop_same(ctx_nexthop, nexthop)) {
+ /* Matched */
+ break;
+ }
+ }
+
+ if (is_debug)
+ nexthop2str(nexthop, buf, sizeof(buf));
+
+ if (ctx_nhlfe && ctx_nexthop) {
+
+ /* Bring zebra nhlfe install state into sync */
+ if (CHECK_FLAG(ctx_nhlfe->flags,
+ NHLFE_FLAG_INSTALLED)) {
+
+ SET_FLAG(nhlfe->flags, NHLFE_FLAG_INSTALLED);
+
+ } else {
+
+ UNSET_FLAG(nhlfe->flags, NHLFE_FLAG_INSTALLED);
+ }
+
+ if (CHECK_FLAG(ctx_nhlfe->nexthop->flags,
+ NEXTHOP_FLAG_FIB)) {
+ SET_FLAG(nhlfe->nexthop->flags,
+ NEXTHOP_FLAG_ACTIVE);
+ SET_FLAG(nhlfe->nexthop->flags,
+ NEXTHOP_FLAG_FIB);
+ } else {
+ UNSET_FLAG(nhlfe->nexthop->flags,
+ NEXTHOP_FLAG_ACTIVE);
+ UNSET_FLAG(nhlfe->nexthop->flags,
+ NEXTHOP_FLAG_FIB);
+ }
+
+ } else {
+ /* Not mentioned in lfib set -> uninstalled */
+
+ UNSET_FLAG(nhlfe->flags, NHLFE_FLAG_INSTALLED);
+ UNSET_FLAG(nexthop->flags, NEXTHOP_FLAG_FIB);
+ UNSET_FLAG(nexthop->flags, NEXTHOP_FLAG_ACTIVE);
+ }
+ }
+
+ if (end_count > 0) {
+ SET_FLAG(lsp->flags, LSP_FLAG_INSTALLED);
+
+ if (changed_p)
+ dplane_lsp_notif_update(lsp, DPLANE_OP_LSP_UPDATE, ctx);
+
+ } else {
+ UNSET_FLAG(lsp->flags, LSP_FLAG_INSTALLED);
+ clear_nhlfe_installed(lsp);
+ }
+
+done:
+ dplane_ctx_fini(&ctx);
+}
+
/*
* Install dynamic LSP entry.
*/
void zebra_mpls_lsp_dplane_result(struct zebra_dplane_ctx *ctx);
+/* Process async dplane notifications. */
+void zebra_mpls_process_dplane_notify(struct zebra_dplane_ctx *ctx);
+
/*
* Schedule all MPLS label forwarding entries for processing.
* Called upon changes that may affect one or more of them such as
--- /dev/null
+/* Zebra Nexthop Group Code.
+ * Copyright (C) 2019 Cumulus Networks, Inc.
+ * Donald Sharp
+ * Stephen Worley
+ *
+ * This file is part of FRR.
+ *
+ * FRR is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2, or (at your option) any
+ * later version.
+ *
+ * FRR is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with FRR; see the file COPYING. If not, write to the Free
+ * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.
+ */
+#include <zebra.h>
+
+#include "lib/nexthop.h"
+#include "lib/routemap.h"
+
+#include "zebra/connected.h"
+#include "zebra/debug.h"
+#include "zebra/zebra_router.h"
+#include "zebra/zebra_nhg.h"
+#include "zebra/zebra_rnh.h"
+#include "zebra/zebra_routemap.h"
+#include "zebra/rt.h"
+
+static void nexthop_set_resolved(afi_t afi, const struct nexthop *newhop,
+ struct nexthop *nexthop)
+{
+ struct nexthop *resolved_hop;
+
+ resolved_hop = nexthop_new();
+ SET_FLAG(resolved_hop->flags, NEXTHOP_FLAG_ACTIVE);
+
+ resolved_hop->vrf_id = nexthop->vrf_id;
+ switch (newhop->type) {
+ case NEXTHOP_TYPE_IPV4:
+ case NEXTHOP_TYPE_IPV4_IFINDEX:
+ /* If the resolving route specifies a gateway, use it */
+ resolved_hop->type = newhop->type;
+ resolved_hop->gate.ipv4 = newhop->gate.ipv4;
+
+ if (newhop->ifindex) {
+ resolved_hop->type = NEXTHOP_TYPE_IPV4_IFINDEX;
+ resolved_hop->ifindex = newhop->ifindex;
+ }
+ break;
+ case NEXTHOP_TYPE_IPV6:
+ case NEXTHOP_TYPE_IPV6_IFINDEX:
+ resolved_hop->type = newhop->type;
+ resolved_hop->gate.ipv6 = newhop->gate.ipv6;
+
+ if (newhop->ifindex) {
+ resolved_hop->type = NEXTHOP_TYPE_IPV6_IFINDEX;
+ resolved_hop->ifindex = newhop->ifindex;
+ }
+ break;
+ case NEXTHOP_TYPE_IFINDEX:
+ /* If the resolving route is an interface route,
+ * it means the gateway we are looking up is connected
+ * to that interface. (The actual network is _not_ onlink).
+ * Therefore, the resolved route should have the original
+ * gateway as nexthop as it is directly connected.
+ *
+ * On Linux, we have to set the onlink netlink flag because
+ * otherwise, the kernel won't accept the route.
+ */
+ resolved_hop->flags |= NEXTHOP_FLAG_ONLINK;
+ if (afi == AFI_IP) {
+ resolved_hop->type = NEXTHOP_TYPE_IPV4_IFINDEX;
+ resolved_hop->gate.ipv4 = nexthop->gate.ipv4;
+ } else if (afi == AFI_IP6) {
+ resolved_hop->type = NEXTHOP_TYPE_IPV6_IFINDEX;
+ resolved_hop->gate.ipv6 = nexthop->gate.ipv6;
+ }
+ resolved_hop->ifindex = newhop->ifindex;
+ break;
+ case NEXTHOP_TYPE_BLACKHOLE:
+ resolved_hop->type = NEXTHOP_TYPE_BLACKHOLE;
+ resolved_hop->bh_type = nexthop->bh_type;
+ break;
+ }
+
+ if (newhop->flags & NEXTHOP_FLAG_ONLINK)
+ resolved_hop->flags |= NEXTHOP_FLAG_ONLINK;
+
+ /* Copy labels of the resolved route */
+ if (newhop->nh_label)
+ nexthop_add_labels(resolved_hop, newhop->nh_label_type,
+ newhop->nh_label->num_labels,
+ &newhop->nh_label->label[0]);
+
+ resolved_hop->rparent = nexthop;
+ nexthop_add(&nexthop->resolved, resolved_hop);
+}
+
+/*
+ * Given a nexthop we need to properly recursively resolve
+ * the route. As such, do a table lookup to find and match
+ * if at all possible. Set the nexthop->ifindex as appropriate
+ */
+static int nexthop_active(afi_t afi, struct route_entry *re,
+ struct nexthop *nexthop, struct route_node *top)
+{
+ struct prefix p;
+ struct route_table *table;
+ struct route_node *rn;
+ struct route_entry *match = NULL;
+ int resolved;
+ struct nexthop *newhop;
+ struct interface *ifp;
+ rib_dest_t *dest;
+
+ if ((nexthop->type == NEXTHOP_TYPE_IPV4)
+ || nexthop->type == NEXTHOP_TYPE_IPV6)
+ nexthop->ifindex = 0;
+
+ UNSET_FLAG(nexthop->flags, NEXTHOP_FLAG_RECURSIVE);
+ nexthops_free(nexthop->resolved);
+ nexthop->resolved = NULL;
+ re->nexthop_mtu = 0;
+
+ /*
+ * If the kernel has sent us a route, then
+ * by golly gee whiz it's a good route.
+ */
+ if (re->type == ZEBRA_ROUTE_KERNEL || re->type == ZEBRA_ROUTE_SYSTEM)
+ return 1;
+
+ /*
+ * Check to see if we should trust the passed in information
+ * for UNNUMBERED interfaces as that we won't find the GW
+ * address in the routing table.
+ * This check should suffice to handle IPv4 or IPv6 routes
+ * sourced from EVPN routes which are installed with the
+ * next hop as the remote VTEP IP.
+ */
+ if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_ONLINK)) {
+ ifp = if_lookup_by_index(nexthop->ifindex, nexthop->vrf_id);
+ if (!ifp) {
+ if (IS_ZEBRA_DEBUG_RIB_DETAILED)
+ zlog_debug(
+ "\t%s: Onlink and interface: %u[%u] does not exist",
+ __PRETTY_FUNCTION__, nexthop->ifindex,
+ nexthop->vrf_id);
+ return 0;
+ }
+ if (connected_is_unnumbered(ifp)) {
+ if (if_is_operative(ifp))
+ return 1;
+ else {
+ if (IS_ZEBRA_DEBUG_RIB_DETAILED)
+ zlog_debug(
+ "\t%s: Onlink and interface %s is not operative",
+ __PRETTY_FUNCTION__, ifp->name);
+ return 0;
+ }
+ }
+ if (!if_is_operative(ifp)) {
+ if (IS_ZEBRA_DEBUG_RIB_DETAILED)
+ zlog_debug(
+ "\t%s: Interface %s is not unnumbered",
+ __PRETTY_FUNCTION__, ifp->name);
+ return 0;
+ }
+ }
+
+ /* Make lookup prefix. */
+ memset(&p, 0, sizeof(struct prefix));
+ switch (afi) {
+ case AFI_IP:
+ p.family = AF_INET;
+ p.prefixlen = IPV4_MAX_PREFIXLEN;
+ p.u.prefix4 = nexthop->gate.ipv4;
+ break;
+ case AFI_IP6:
+ p.family = AF_INET6;
+ p.prefixlen = IPV6_MAX_PREFIXLEN;
+ p.u.prefix6 = nexthop->gate.ipv6;
+ break;
+ default:
+ assert(afi != AFI_IP && afi != AFI_IP6);
+ break;
+ }
+ /* Lookup table. */
+ table = zebra_vrf_table(afi, SAFI_UNICAST, nexthop->vrf_id);
+ if (!table) {
+ if (IS_ZEBRA_DEBUG_RIB_DETAILED)
+ zlog_debug("\t%s: Table not found",
+ __PRETTY_FUNCTION__);
+ return 0;
+ }
+
+ rn = route_node_match(table, (struct prefix *)&p);
+ while (rn) {
+ route_unlock_node(rn);
+
+ /* Lookup should halt if we've matched against ourselves ('top',
+ * if specified) - i.e., we cannot have a nexthop NH1 is
+ * resolved by a route NH1. The exception is if the route is a
+ * host route.
+ */
+ if (top && rn == top)
+ if (((afi == AFI_IP) && (rn->p.prefixlen != 32))
+ || ((afi == AFI_IP6) && (rn->p.prefixlen != 128))) {
+ if (IS_ZEBRA_DEBUG_RIB_DETAILED)
+ zlog_debug(
+ "\t%s: Matched against ourself and prefix length is not max bit length",
+ __PRETTY_FUNCTION__);
+ return 0;
+ }
+
+ /* Pick up selected route. */
+ /* However, do not resolve over default route unless explicitly
+ * allowed. */
+ if (is_default_prefix(&rn->p)
+ && !rnh_resolve_via_default(p.family)) {
+ if (IS_ZEBRA_DEBUG_RIB_DETAILED)
+ zlog_debug(
+ "\t:%s: Resolved against default route",
+ __PRETTY_FUNCTION__);
+ return 0;
+ }
+
+ dest = rib_dest_from_rnode(rn);
+ if (dest && dest->selected_fib
+ && !CHECK_FLAG(dest->selected_fib->status,
+ ROUTE_ENTRY_REMOVED)
+ && dest->selected_fib->type != ZEBRA_ROUTE_TABLE)
+ match = dest->selected_fib;
+
+ /* If there is no selected route or matched route is EGP, go up
+ tree. */
+ if (!match) {
+ do {
+ rn = rn->parent;
+ } while (rn && rn->info == NULL);
+ if (rn)
+ route_lock_node(rn);
+
+ continue;
+ }
+
+ if (match->type == ZEBRA_ROUTE_CONNECT) {
+ /* Directly point connected route. */
+ newhop = match->ng.nexthop;
+ if (newhop) {
+ if (nexthop->type == NEXTHOP_TYPE_IPV4
+ || nexthop->type == NEXTHOP_TYPE_IPV6)
+ nexthop->ifindex = newhop->ifindex;
+ }
+ return 1;
+ } else if (CHECK_FLAG(re->flags, ZEBRA_FLAG_ALLOW_RECURSION)) {
+ resolved = 0;
+ for (ALL_NEXTHOPS(match->ng, newhop)) {
+ if (!CHECK_FLAG(match->status,
+ ROUTE_ENTRY_INSTALLED))
+ continue;
+ if (CHECK_FLAG(newhop->flags,
+ NEXTHOP_FLAG_RECURSIVE))
+ continue;
+
+ SET_FLAG(nexthop->flags,
+ NEXTHOP_FLAG_RECURSIVE);
+ SET_FLAG(re->status,
+ ROUTE_ENTRY_NEXTHOPS_CHANGED);
+ nexthop_set_resolved(afi, newhop, nexthop);
+ resolved = 1;
+ }
+ if (resolved)
+ re->nexthop_mtu = match->mtu;
+ if (!resolved && IS_ZEBRA_DEBUG_RIB_DETAILED)
+ zlog_debug("\t%s: Recursion failed to find",
+ __PRETTY_FUNCTION__);
+ return resolved;
+ } else if (re->type == ZEBRA_ROUTE_STATIC) {
+ resolved = 0;
+ for (ALL_NEXTHOPS(match->ng, newhop)) {
+ if (!CHECK_FLAG(match->status,
+ ROUTE_ENTRY_INSTALLED))
+ continue;
+ if (CHECK_FLAG(newhop->flags,
+ NEXTHOP_FLAG_RECURSIVE))
+ continue;
+
+ SET_FLAG(nexthop->flags,
+ NEXTHOP_FLAG_RECURSIVE);
+ nexthop_set_resolved(afi, newhop, nexthop);
+ resolved = 1;
+ }
+ if (resolved)
+ re->nexthop_mtu = match->mtu;
+
+ if (!resolved && IS_ZEBRA_DEBUG_RIB_DETAILED)
+ zlog_debug(
+ "\t%s: Static route unable to resolve",
+ __PRETTY_FUNCTION__);
+ return resolved;
+ } else {
+ if (IS_ZEBRA_DEBUG_RIB_DETAILED) {
+ zlog_debug(
+ "\t%s: Route Type %s has not turned on recursion",
+ __PRETTY_FUNCTION__,
+ zebra_route_string(re->type));
+ if (re->type == ZEBRA_ROUTE_BGP
+ && !CHECK_FLAG(re->flags, ZEBRA_FLAG_IBGP))
+ zlog_debug(
+ "\tEBGP: see \"disable-ebgp-connected-route-check\" or \"disable-connected-check\"");
+ }
+ return 0;
+ }
+ }
+ if (IS_ZEBRA_DEBUG_RIB_DETAILED)
+ zlog_debug("\t%s: Nexthop did not lookup in table",
+ __PRETTY_FUNCTION__);
+ return 0;
+}
+
+/* This function verifies reachability of one given nexthop, which can be
+ * numbered or unnumbered, IPv4 or IPv6. The result is unconditionally stored
+ * in nexthop->flags field. The nexthop->ifindex will be updated
+ * appropriately as well. An existing route map can turn
+ * (otherwise active) nexthop into inactive, but not vice versa.
+ *
+ * The return value is the final value of 'ACTIVE' flag.
+ */
+static unsigned nexthop_active_check(struct route_node *rn,
+ struct route_entry *re,
+ struct nexthop *nexthop)
+{
+ struct interface *ifp;
+ route_map_result_t ret = RMAP_PERMITMATCH;
+ int family;
+ char buf[SRCDEST2STR_BUFFER];
+ const struct prefix *p, *src_p;
+ struct zebra_vrf *zvrf;
+
+ srcdest_rnode_prefixes(rn, &p, &src_p);
+
+ if (rn->p.family == AF_INET)
+ family = AFI_IP;
+ else if (rn->p.family == AF_INET6)
+ family = AFI_IP6;
+ else
+ family = 0;
+ switch (nexthop->type) {
+ case NEXTHOP_TYPE_IFINDEX:
+ ifp = if_lookup_by_index(nexthop->ifindex, nexthop->vrf_id);
+ if (ifp && if_is_operative(ifp))
+ SET_FLAG(nexthop->flags, NEXTHOP_FLAG_ACTIVE);
+ else
+ UNSET_FLAG(nexthop->flags, NEXTHOP_FLAG_ACTIVE);
+ break;
+ case NEXTHOP_TYPE_IPV4:
+ case NEXTHOP_TYPE_IPV4_IFINDEX:
+ family = AFI_IP;
+ if (nexthop_active(AFI_IP, re, nexthop, rn))
+ SET_FLAG(nexthop->flags, NEXTHOP_FLAG_ACTIVE);
+ else
+ UNSET_FLAG(nexthop->flags, NEXTHOP_FLAG_ACTIVE);
+ break;
+ case NEXTHOP_TYPE_IPV6:
+ family = AFI_IP6;
+ if (nexthop_active(AFI_IP6, re, nexthop, rn))
+ SET_FLAG(nexthop->flags, NEXTHOP_FLAG_ACTIVE);
+ else
+ UNSET_FLAG(nexthop->flags, NEXTHOP_FLAG_ACTIVE);
+ break;
+ case NEXTHOP_TYPE_IPV6_IFINDEX:
+ /* RFC 5549, v4 prefix with v6 NH */
+ if (rn->p.family != AF_INET)
+ family = AFI_IP6;
+ if (IN6_IS_ADDR_LINKLOCAL(&nexthop->gate.ipv6)) {
+ ifp = if_lookup_by_index(nexthop->ifindex,
+ nexthop->vrf_id);
+ if (ifp && if_is_operative(ifp))
+ SET_FLAG(nexthop->flags, NEXTHOP_FLAG_ACTIVE);
+ else
+ UNSET_FLAG(nexthop->flags, NEXTHOP_FLAG_ACTIVE);
+ } else {
+ if (nexthop_active(AFI_IP6, re, nexthop, rn))
+ SET_FLAG(nexthop->flags, NEXTHOP_FLAG_ACTIVE);
+ else
+ UNSET_FLAG(nexthop->flags, NEXTHOP_FLAG_ACTIVE);
+ }
+ break;
+ case NEXTHOP_TYPE_BLACKHOLE:
+ SET_FLAG(nexthop->flags, NEXTHOP_FLAG_ACTIVE);
+ break;
+ default:
+ break;
+ }
+ if (!CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_ACTIVE)) {
+ if (IS_ZEBRA_DEBUG_RIB_DETAILED)
+ zlog_debug("\t%s: Unable to find a active nexthop",
+ __PRETTY_FUNCTION__);
+ return 0;
+ }
+
+ /* XXX: What exactly do those checks do? Do we support
+ * e.g. IPv4 routes with IPv6 nexthops or vice versa?
+ */
+ if (RIB_SYSTEM_ROUTE(re) || (family == AFI_IP && p->family != AF_INET)
+ || (family == AFI_IP6 && p->family != AF_INET6))
+ return CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_ACTIVE);
+
+ /* The original code didn't determine the family correctly
+ * e.g. for NEXTHOP_TYPE_IFINDEX. Retrieve the correct afi
+ * from the rib_table_info in those cases.
+ * Possibly it may be better to use only the rib_table_info
+ * in every case.
+ */
+ if (!family) {
+ rib_table_info_t *info;
+
+ info = srcdest_rnode_table_info(rn);
+ family = info->afi;
+ }
+
+ memset(&nexthop->rmap_src.ipv6, 0, sizeof(union g_addr));
+
+ zvrf = zebra_vrf_lookup_by_id(nexthop->vrf_id);
+ if (!zvrf) {
+ if (IS_ZEBRA_DEBUG_RIB_DETAILED)
+ zlog_debug("\t%s: zvrf is NULL", __PRETTY_FUNCTION__);
+ return CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_ACTIVE);
+ }
+
+ /* It'll get set if required inside */
+ ret = zebra_route_map_check(family, re->type, re->instance, p, nexthop,
+ zvrf, re->tag);
+ if (ret == RMAP_DENYMATCH) {
+ if (IS_ZEBRA_DEBUG_RIB) {
+ srcdest_rnode2str(rn, buf, sizeof(buf));
+ zlog_debug(
+ "%u:%s: Filtering out with NH out %s due to route map",
+ re->vrf_id, buf,
+ ifindex2ifname(nexthop->ifindex,
+ nexthop->vrf_id));
+ }
+ UNSET_FLAG(nexthop->flags, NEXTHOP_FLAG_ACTIVE);
+ }
+ return CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_ACTIVE);
+}
+
+/*
+ * Iterate over all nexthops of the given RIB entry and refresh their
+ * ACTIVE flag. re->nexthop_active_num is updated accordingly. If any
+ * nexthop is found to toggle the ACTIVE flag, the whole re structure
+ * is flagged with ROUTE_ENTRY_CHANGED.
+ *
+ * Return value is the new number of active nexthops.
+ */
+int nexthop_active_update(struct route_node *rn, struct route_entry *re)
+{
+ struct nexthop *nexthop;
+ union g_addr prev_src;
+ unsigned int prev_active, new_active;
+ ifindex_t prev_index;
+
+ re->nexthop_active_num = 0;
+ UNSET_FLAG(re->status, ROUTE_ENTRY_CHANGED);
+
+ for (nexthop = re->ng.nexthop; nexthop; nexthop = nexthop->next) {
+ /* No protocol daemon provides src and so we're skipping
+ * tracking it */
+ prev_src = nexthop->rmap_src;
+ prev_active = CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_ACTIVE);
+ prev_index = nexthop->ifindex;
+ /*
+ * We need to respect the multipath_num here
+ * as that what we should be able to install from
+ * a multipath perpsective should not be a data plane
+ * decision point.
+ */
+ new_active = nexthop_active_check(rn, re, nexthop);
+ if (new_active
+ && re->nexthop_active_num >= zrouter.multipath_num) {
+ UNSET_FLAG(nexthop->flags, NEXTHOP_FLAG_ACTIVE);
+ new_active = 0;
+ }
+ if (new_active)
+ re->nexthop_active_num++;
+ /* Don't allow src setting on IPv6 addr for now */
+ if (prev_active != new_active || prev_index != nexthop->ifindex
+ || ((nexthop->type >= NEXTHOP_TYPE_IFINDEX
+ && nexthop->type < NEXTHOP_TYPE_IPV6)
+ && prev_src.ipv4.s_addr
+ != nexthop->rmap_src.ipv4.s_addr)
+ || ((nexthop->type >= NEXTHOP_TYPE_IPV6
+ && nexthop->type < NEXTHOP_TYPE_BLACKHOLE)
+ && !(IPV6_ADDR_SAME(&prev_src.ipv6,
+ &nexthop->rmap_src.ipv6)))
+ || CHECK_FLAG(re->status, ROUTE_ENTRY_LABELS_CHANGED)) {
+ SET_FLAG(re->status, ROUTE_ENTRY_CHANGED);
+ SET_FLAG(re->status, ROUTE_ENTRY_NEXTHOPS_CHANGED);
+ }
+ }
+
+ return re->nexthop_active_num;
+}
+
--- /dev/null
+/* Zebra Nexthop Group header.
+ * Copyright (C) 2019 Cumulus Networks, Inc.
+ * Donald Sharp
+ * Stephen Worley
+ *
+ * This file is part of FRR.
+ *
+ * FRR is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2, or (at your option) any
+ * later version.
+ *
+ * FRR is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with FRR; see the file COPYING. If not, write to the Free
+ * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.
+ */
+#ifndef __ZEBRA_NHG_H__
+#define __ZEBRA_NHG_H__
+
+#include "zebra/rib.h"
+
+extern int nexthop_active_update(struct route_node *rn, struct route_entry *re);
+#endif
#include "zebra/zebra_vxlan.h"
#include "zebra/zapi_msg.h"
#include "zebra/zebra_dplane.h"
+#include "zebra/zebra_nhg.h"
/*
* Event, list, and mutex for delivery of dataplane results
srcdest_rnode2str(rn, buf, sizeof(buf));
if (info->safi == SAFI_MULTICAST)
- strcat(buf, " (MRIB)");
+ strlcat(buf, " (MRIB)", sizeof(buf));
} else {
snprintf(buf, sizeof(buf), "{(route_node *) NULL}");
}
return nexthop;
}
-static void nexthop_set_resolved(afi_t afi, const struct nexthop *newhop,
- struct nexthop *nexthop)
-{
- struct nexthop *resolved_hop;
-
- resolved_hop = nexthop_new();
- SET_FLAG(resolved_hop->flags, NEXTHOP_FLAG_ACTIVE);
-
- resolved_hop->vrf_id = nexthop->vrf_id;
- switch (newhop->type) {
- case NEXTHOP_TYPE_IPV4:
- case NEXTHOP_TYPE_IPV4_IFINDEX:
- /* If the resolving route specifies a gateway, use it */
- resolved_hop->type = newhop->type;
- resolved_hop->gate.ipv4 = newhop->gate.ipv4;
-
- if (newhop->ifindex) {
- resolved_hop->type = NEXTHOP_TYPE_IPV4_IFINDEX;
- resolved_hop->ifindex = newhop->ifindex;
- }
- break;
- case NEXTHOP_TYPE_IPV6:
- case NEXTHOP_TYPE_IPV6_IFINDEX:
- resolved_hop->type = newhop->type;
- resolved_hop->gate.ipv6 = newhop->gate.ipv6;
-
- if (newhop->ifindex) {
- resolved_hop->type = NEXTHOP_TYPE_IPV6_IFINDEX;
- resolved_hop->ifindex = newhop->ifindex;
- }
- break;
- case NEXTHOP_TYPE_IFINDEX:
- /* If the resolving route is an interface route,
- * it means the gateway we are looking up is connected
- * to that interface. (The actual network is _not_ onlink).
- * Therefore, the resolved route should have the original
- * gateway as nexthop as it is directly connected.
- *
- * On Linux, we have to set the onlink netlink flag because
- * otherwise, the kernel won't accept the route.
- */
- resolved_hop->flags |= NEXTHOP_FLAG_ONLINK;
- if (afi == AFI_IP) {
- resolved_hop->type = NEXTHOP_TYPE_IPV4_IFINDEX;
- resolved_hop->gate.ipv4 = nexthop->gate.ipv4;
- } else if (afi == AFI_IP6) {
- resolved_hop->type = NEXTHOP_TYPE_IPV6_IFINDEX;
- resolved_hop->gate.ipv6 = nexthop->gate.ipv6;
- }
- resolved_hop->ifindex = newhop->ifindex;
- break;
- case NEXTHOP_TYPE_BLACKHOLE:
- resolved_hop->type = NEXTHOP_TYPE_BLACKHOLE;
- resolved_hop->bh_type = nexthop->bh_type;
- break;
- }
-
- if (newhop->flags & NEXTHOP_FLAG_ONLINK)
- resolved_hop->flags |= NEXTHOP_FLAG_ONLINK;
-
- /* Copy labels of the resolved route */
- if (newhop->nh_label)
- nexthop_add_labels(resolved_hop, newhop->nh_label_type,
- newhop->nh_label->num_labels,
- &newhop->nh_label->label[0]);
-
- resolved_hop->rparent = nexthop;
- nexthop_add(&nexthop->resolved, resolved_hop);
-}
-
-/*
- * Given a nexthop we need to properly recursively resolve
- * the route. As such, do a table lookup to find and match
- * if at all possible. Set the nexthop->ifindex as appropriate
- */
-static int nexthop_active(afi_t afi, struct route_entry *re,
- struct nexthop *nexthop,
- struct route_node *top)
-{
- struct prefix p;
- struct route_table *table;
- struct route_node *rn;
- struct route_entry *match = NULL;
- int resolved;
- struct nexthop *newhop;
- struct interface *ifp;
- rib_dest_t *dest;
-
- if ((nexthop->type == NEXTHOP_TYPE_IPV4)
- || nexthop->type == NEXTHOP_TYPE_IPV6)
- nexthop->ifindex = 0;
-
- UNSET_FLAG(nexthop->flags, NEXTHOP_FLAG_RECURSIVE);
- nexthops_free(nexthop->resolved);
- nexthop->resolved = NULL;
- re->nexthop_mtu = 0;
-
- /*
- * If the kernel has sent us a route, then
- * by golly gee whiz it's a good route.
- */
- if (re->type == ZEBRA_ROUTE_KERNEL ||
- re->type == ZEBRA_ROUTE_SYSTEM)
- return 1;
-
- /*
- * Check to see if we should trust the passed in information
- * for UNNUMBERED interfaces as that we won't find the GW
- * address in the routing table.
- * This check should suffice to handle IPv4 or IPv6 routes
- * sourced from EVPN routes which are installed with the
- * next hop as the remote VTEP IP.
- */
- if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_ONLINK)) {
- ifp = if_lookup_by_index(nexthop->ifindex, nexthop->vrf_id);
- if (!ifp) {
- if (IS_ZEBRA_DEBUG_RIB_DETAILED)
- zlog_debug(
- "\t%s: Onlink and interface: %u[%u] does not exist",
- __PRETTY_FUNCTION__, nexthop->ifindex,
- nexthop->vrf_id);
- return 0;
- }
- if (connected_is_unnumbered(ifp)) {
- if (if_is_operative(ifp))
- return 1;
- else {
- if (IS_ZEBRA_DEBUG_RIB_DETAILED)
- zlog_debug(
- "\t%s: Onlink and interface %s is not operative",
- __PRETTY_FUNCTION__, ifp->name);
- return 0;
- }
- }
- if (!if_is_operative(ifp)) {
- if (IS_ZEBRA_DEBUG_RIB_DETAILED)
- zlog_debug(
- "\t%s: Interface %s is not unnumbered",
- __PRETTY_FUNCTION__, ifp->name);
- return 0;
- }
- }
-
- /* Make lookup prefix. */
- memset(&p, 0, sizeof(struct prefix));
- switch (afi) {
- case AFI_IP:
- p.family = AF_INET;
- p.prefixlen = IPV4_MAX_PREFIXLEN;
- p.u.prefix4 = nexthop->gate.ipv4;
- break;
- case AFI_IP6:
- p.family = AF_INET6;
- p.prefixlen = IPV6_MAX_PREFIXLEN;
- p.u.prefix6 = nexthop->gate.ipv6;
- break;
- default:
- assert(afi != AFI_IP && afi != AFI_IP6);
- break;
- }
- /* Lookup table. */
- table = zebra_vrf_table(afi, SAFI_UNICAST, nexthop->vrf_id);
- if (!table) {
- if (IS_ZEBRA_DEBUG_RIB_DETAILED)
- zlog_debug("\t%s: Table not found",
- __PRETTY_FUNCTION__);
- return 0;
- }
-
- rn = route_node_match(table, (struct prefix *)&p);
- while (rn) {
- route_unlock_node(rn);
-
- /* Lookup should halt if we've matched against ourselves ('top',
- * if specified) - i.e., we cannot have a nexthop NH1 is
- * resolved by a route NH1. The exception is if the route is a
- * host route.
- */
- if (top && rn == top)
- if (((afi == AFI_IP) && (rn->p.prefixlen != 32))
- || ((afi == AFI_IP6) && (rn->p.prefixlen != 128))) {
- if (IS_ZEBRA_DEBUG_RIB_DETAILED)
- zlog_debug(
- "\t%s: Matched against ourself and prefix length is not max bit length",
- __PRETTY_FUNCTION__);
- return 0;
- }
-
- /* Pick up selected route. */
- /* However, do not resolve over default route unless explicitly
- * allowed. */
- if (is_default_prefix(&rn->p)
- && !rnh_resolve_via_default(p.family)) {
- if (IS_ZEBRA_DEBUG_RIB_DETAILED)
- zlog_debug(
- "\t:%s: Resolved against default route",
- __PRETTY_FUNCTION__);
- return 0;
- }
-
- dest = rib_dest_from_rnode(rn);
- if (dest && dest->selected_fib
- && !CHECK_FLAG(dest->selected_fib->status,
- ROUTE_ENTRY_REMOVED)
- && dest->selected_fib->type != ZEBRA_ROUTE_TABLE)
- match = dest->selected_fib;
-
- /* If there is no selected route or matched route is EGP, go up
- tree. */
- if (!match) {
- do {
- rn = rn->parent;
- } while (rn && rn->info == NULL);
- if (rn)
- route_lock_node(rn);
-
- continue;
- }
-
- if (match->type == ZEBRA_ROUTE_CONNECT) {
- /* Directly point connected route. */
- newhop = match->ng.nexthop;
- if (newhop) {
- if (nexthop->type == NEXTHOP_TYPE_IPV4
- || nexthop->type == NEXTHOP_TYPE_IPV6)
- nexthop->ifindex = newhop->ifindex;
- }
- return 1;
- } else if (CHECK_FLAG(re->flags, ZEBRA_FLAG_ALLOW_RECURSION)) {
- resolved = 0;
- for (ALL_NEXTHOPS(match->ng, newhop)) {
- if (!CHECK_FLAG(match->status,
- ROUTE_ENTRY_INSTALLED))
- continue;
- if (CHECK_FLAG(newhop->flags,
- NEXTHOP_FLAG_RECURSIVE))
- continue;
-
- SET_FLAG(nexthop->flags,
- NEXTHOP_FLAG_RECURSIVE);
- SET_FLAG(re->status,
- ROUTE_ENTRY_NEXTHOPS_CHANGED);
- nexthop_set_resolved(afi, newhop, nexthop);
- resolved = 1;
- }
- if (resolved)
- re->nexthop_mtu = match->mtu;
- if (!resolved && IS_ZEBRA_DEBUG_RIB_DETAILED)
- zlog_debug("\t%s: Recursion failed to find",
- __PRETTY_FUNCTION__);
- return resolved;
- } else if (re->type == ZEBRA_ROUTE_STATIC) {
- resolved = 0;
- for (ALL_NEXTHOPS(match->ng, newhop)) {
- if (!CHECK_FLAG(match->status,
- ROUTE_ENTRY_INSTALLED))
- continue;
- if (CHECK_FLAG(newhop->flags,
- NEXTHOP_FLAG_RECURSIVE))
- continue;
-
- SET_FLAG(nexthop->flags,
- NEXTHOP_FLAG_RECURSIVE);
- nexthop_set_resolved(afi, newhop, nexthop);
- resolved = 1;
- }
- if (resolved)
- re->nexthop_mtu = match->mtu;
-
- if (!resolved && IS_ZEBRA_DEBUG_RIB_DETAILED)
- zlog_debug(
- "\t%s: Static route unable to resolve",
- __PRETTY_FUNCTION__);
- return resolved;
- } else {
- if (IS_ZEBRA_DEBUG_RIB_DETAILED) {
- zlog_debug("\t%s: Route Type %s has not turned on recursion",
- __PRETTY_FUNCTION__,
- zebra_route_string(re->type));
- if (re->type == ZEBRA_ROUTE_BGP &&
- !CHECK_FLAG(re->flags, ZEBRA_FLAG_IBGP))
- zlog_debug("\tEBGP: see \"disable-ebgp-connected-route-check\" or \"disable-connected-check\"");
- }
- return 0;
- }
- }
- if (IS_ZEBRA_DEBUG_RIB_DETAILED)
- zlog_debug("\t%s: Nexthop did not lookup in table",
- __PRETTY_FUNCTION__);
- return 0;
-}
-
struct route_entry *rib_match(afi_t afi, safi_t safi, vrf_id_t vrf_id,
union g_addr *addr, struct route_node **rn_out)
{
return NULL;
}
-/* This function verifies reachability of one given nexthop, which can be
- * numbered or unnumbered, IPv4 or IPv6. The result is unconditionally stored
- * in nexthop->flags field. The nexthop->ifindex will be updated
- * appropriately as well. An existing route map can turn
- * (otherwise active) nexthop into inactive, but not vice versa.
- *
- * The return value is the final value of 'ACTIVE' flag.
- */
-static unsigned nexthop_active_check(struct route_node *rn,
- struct route_entry *re,
- struct nexthop *nexthop)
-{
- struct interface *ifp;
- route_map_result_t ret = RMAP_MATCH;
- int family;
- char buf[SRCDEST2STR_BUFFER];
- const struct prefix *p, *src_p;
- struct zebra_vrf *zvrf;
-
- srcdest_rnode_prefixes(rn, &p, &src_p);
-
- if (rn->p.family == AF_INET)
- family = AFI_IP;
- else if (rn->p.family == AF_INET6)
- family = AFI_IP6;
- else
- family = 0;
- switch (nexthop->type) {
- case NEXTHOP_TYPE_IFINDEX:
- ifp = if_lookup_by_index(nexthop->ifindex, nexthop->vrf_id);
- if (ifp && if_is_operative(ifp))
- SET_FLAG(nexthop->flags, NEXTHOP_FLAG_ACTIVE);
- else
- UNSET_FLAG(nexthop->flags, NEXTHOP_FLAG_ACTIVE);
- break;
- case NEXTHOP_TYPE_IPV4:
- case NEXTHOP_TYPE_IPV4_IFINDEX:
- family = AFI_IP;
- if (nexthop_active(AFI_IP, re, nexthop, rn))
- SET_FLAG(nexthop->flags, NEXTHOP_FLAG_ACTIVE);
- else
- UNSET_FLAG(nexthop->flags, NEXTHOP_FLAG_ACTIVE);
- break;
- case NEXTHOP_TYPE_IPV6:
- family = AFI_IP6;
- if (nexthop_active(AFI_IP6, re, nexthop, rn))
- SET_FLAG(nexthop->flags, NEXTHOP_FLAG_ACTIVE);
- else
- UNSET_FLAG(nexthop->flags, NEXTHOP_FLAG_ACTIVE);
- break;
- case NEXTHOP_TYPE_IPV6_IFINDEX:
- /* RFC 5549, v4 prefix with v6 NH */
- if (rn->p.family != AF_INET)
- family = AFI_IP6;
- if (IN6_IS_ADDR_LINKLOCAL(&nexthop->gate.ipv6)) {
- ifp = if_lookup_by_index(nexthop->ifindex,
- nexthop->vrf_id);
- if (ifp && if_is_operative(ifp))
- SET_FLAG(nexthop->flags, NEXTHOP_FLAG_ACTIVE);
- else
- UNSET_FLAG(nexthop->flags, NEXTHOP_FLAG_ACTIVE);
- } else {
- if (nexthop_active(AFI_IP6, re, nexthop, rn))
- SET_FLAG(nexthop->flags, NEXTHOP_FLAG_ACTIVE);
- else
- UNSET_FLAG(nexthop->flags, NEXTHOP_FLAG_ACTIVE);
- }
- break;
- case NEXTHOP_TYPE_BLACKHOLE:
- SET_FLAG(nexthop->flags, NEXTHOP_FLAG_ACTIVE);
- break;
- default:
- break;
- }
- if (!CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_ACTIVE)) {
- if (IS_ZEBRA_DEBUG_RIB_DETAILED)
- zlog_debug("\t%s: Unable to find a active nexthop",
- __PRETTY_FUNCTION__);
- return 0;
- }
-
- /* XXX: What exactly do those checks do? Do we support
- * e.g. IPv4 routes with IPv6 nexthops or vice versa?
- */
- if (RIB_SYSTEM_ROUTE(re) || (family == AFI_IP && p->family != AF_INET)
- || (family == AFI_IP6 && p->family != AF_INET6))
- return CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_ACTIVE);
-
- /* The original code didn't determine the family correctly
- * e.g. for NEXTHOP_TYPE_IFINDEX. Retrieve the correct afi
- * from the rib_table_info in those cases.
- * Possibly it may be better to use only the rib_table_info
- * in every case.
- */
- if (!family) {
- rib_table_info_t *info;
-
- info = srcdest_rnode_table_info(rn);
- family = info->afi;
- }
-
- memset(&nexthop->rmap_src.ipv6, 0, sizeof(union g_addr));
-
- zvrf = zebra_vrf_lookup_by_id(nexthop->vrf_id);
- if (!zvrf) {
- if (IS_ZEBRA_DEBUG_RIB_DETAILED)
- zlog_debug("\t%s: zvrf is NULL", __PRETTY_FUNCTION__);
- return CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_ACTIVE);
- }
-
- /* It'll get set if required inside */
- ret = zebra_route_map_check(family, re->type, re->instance, p,
- nexthop, zvrf, re->tag);
- if (ret == RMAP_DENYMATCH) {
- if (IS_ZEBRA_DEBUG_RIB) {
- srcdest_rnode2str(rn, buf, sizeof(buf));
- zlog_debug(
- "%u:%s: Filtering out with NH out %s due to route map",
- re->vrf_id, buf,
- ifindex2ifname(nexthop->ifindex,
- nexthop->vrf_id));
- }
- UNSET_FLAG(nexthop->flags, NEXTHOP_FLAG_ACTIVE);
- }
- return CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_ACTIVE);
-}
-
-/*
- * Iterate over all nexthops of the given RIB entry and refresh their
- * ACTIVE flag. re->nexthop_active_num is updated accordingly. If any
- * nexthop is found to toggle the ACTIVE flag, the whole re structure
- * is flagged with ROUTE_ENTRY_CHANGED.
- *
- * Return value is the new number of active nexthops.
- */
-static int nexthop_active_update(struct route_node *rn, struct route_entry *re)
-{
- struct nexthop *nexthop;
- union g_addr prev_src;
- unsigned int prev_active, new_active;
- ifindex_t prev_index;
-
- re->nexthop_active_num = 0;
- UNSET_FLAG(re->status, ROUTE_ENTRY_CHANGED);
-
- for (nexthop = re->ng.nexthop; nexthop; nexthop = nexthop->next) {
- /* No protocol daemon provides src and so we're skipping
- * tracking it */
- prev_src = nexthop->rmap_src;
- prev_active = CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_ACTIVE);
- prev_index = nexthop->ifindex;
- /*
- * We need to respect the multipath_num here
- * as that what we should be able to install from
- * a multipath perpsective should not be a data plane
- * decision point.
- */
- new_active = nexthop_active_check(rn, re, nexthop);
- if (new_active
- && re->nexthop_active_num >= zrouter.multipath_num) {
- UNSET_FLAG(nexthop->flags, NEXTHOP_FLAG_ACTIVE);
- new_active = 0;
- }
- if (new_active)
- re->nexthop_active_num++;
- /* Don't allow src setting on IPv6 addr for now */
- if (prev_active != new_active || prev_index != nexthop->ifindex
- || ((nexthop->type >= NEXTHOP_TYPE_IFINDEX
- && nexthop->type < NEXTHOP_TYPE_IPV6)
- && prev_src.ipv4.s_addr
- != nexthop->rmap_src.ipv4.s_addr)
- || ((nexthop->type >= NEXTHOP_TYPE_IPV6
- && nexthop->type < NEXTHOP_TYPE_BLACKHOLE)
- && !(IPV6_ADDR_SAME(&prev_src.ipv6,
- &nexthop->rmap_src.ipv6)))
- || CHECK_FLAG(re->status, ROUTE_ENTRY_LABELS_CHANGED)) {
- SET_FLAG(re->status, ROUTE_ENTRY_CHANGED);
- SET_FLAG(re->status, ROUTE_ENTRY_NEXTHOPS_CHANGED);
- }
- }
-
- return re->nexthop_active_num;
-}
-
/*
* Is this RIB labeled-unicast? It must be of type BGP and all paths
* (nexthops) must have a label.
switch (ret) {
case ZEBRA_DPLANE_REQUEST_QUEUED:
SET_FLAG(re->status, ROUTE_ENTRY_QUEUED);
- if (old)
+
+ if (old) {
SET_FLAG(old->status, ROUTE_ENTRY_QUEUED);
+
+ /* Free old FIB nexthop group */
+ if (old->fib_ng.nexthop) {
+ nexthops_free(old->fib_ng.nexthop);
+ old->fib_ng.nexthop = NULL;
+ }
+
+ if (!RIB_SYSTEM_ROUTE(old)) {
+ /* Clear old route's FIB flags */
+ for (ALL_NEXTHOPS(old->ng, nexthop)) {
+ UNSET_FLAG(nexthop->flags,
+ NEXTHOP_FLAG_FIB);
+ }
+ }
+ }
+
if (zvrf)
zvrf->installs_queued++;
break;
dest->selected_fib = NULL;
+ /* Free FIB nexthop group, if present */
+ if (re->fib_ng.nexthop) {
+ nexthops_free(re->fib_ng.nexthop);
+ re->fib_ng.nexthop = NULL;
+ }
+
for (ALL_NEXTHOPS(re->ng, nexthop))
UNSET_FLAG(nexthop->flags, NEXTHOP_FLAG_FIB);
}
}
/*
- * Route-update results processing after async dataplane update.
+ * Update a route from a dplane context. This consolidates common code
+ * that can be used in processing of results from FIB updates, and in
+ * async notification processing.
+ * The return is 'true' if the installed nexthops changed; 'false' otherwise.
*/
-static void rib_process_result(struct zebra_dplane_ctx *ctx)
+static bool rib_update_re_from_ctx(struct route_entry *re,
+ struct route_node *rn,
+ struct zebra_dplane_ctx *ctx)
+{
+ char dest_str[PREFIX_STRLEN] = "";
+ char nh_str[NEXTHOP_STRLEN];
+ struct nexthop *nexthop, *ctx_nexthop;
+ bool matched;
+ const struct nexthop_group *ctxnhg;
+ bool is_selected = false; /* Is 're' currently the selected re? */
+ bool changed_p = false; /* Change to nexthops? */
+ rib_dest_t *dest;
+
+ /* Note well: only capturing the prefix string if debug is enabled here;
+ * unconditional log messages will have to generate the string.
+ */
+ if (IS_ZEBRA_DEBUG_RIB)
+ prefix2str(&(rn->p), dest_str, sizeof(dest_str));
+
+ dest = rib_dest_from_rnode(rn);
+ if (dest)
+ is_selected = (re == dest->selected_fib);
+
+ if (IS_ZEBRA_DEBUG_RIB_DETAILED)
+ zlog_debug("update_from_ctx: %u:%s: %sSELECTED",
+ re->vrf_id, dest_str, (is_selected ? "" : "NOT "));
+
+ /* Update zebra's nexthop FIB flag for each nexthop that was installed.
+ * If the installed set differs from the set requested by the rib/owner,
+ * we use the fib-specific nexthop-group to record the actual FIB
+ * status.
+ */
+
+ /*
+ * First check the fib nexthop-group, if it's present. The comparison
+ * here is quite strict: we require that the fib sets match exactly.
+ */
+ matched = false;
+ do {
+ if (re->fib_ng.nexthop == NULL)
+ break;
+
+ matched = true;
+
+ /* First check the route's fib nexthops */
+ for (ALL_NEXTHOPS(re->fib_ng, nexthop)) {
+
+ if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_RECURSIVE))
+ continue;
+
+ ctx_nexthop = NULL;
+ for (ALL_NEXTHOPS_PTR(dplane_ctx_get_ng(ctx),
+ ctx_nexthop)) {
+ if (nexthop_same(ctx_nexthop, nexthop))
+ break;
+ }
+
+ if (ctx_nexthop == NULL) {
+ /* Nexthop not in the new installed set */
+ if (IS_ZEBRA_DEBUG_RIB_DETAILED) {
+ nexthop2str(nexthop, nh_str,
+ sizeof(nh_str));
+ zlog_debug("update_from_ctx: no match for fib nh %s",
+ nh_str);
+ }
+
+ matched = false;
+ break;
+ }
+ }
+
+ if (!matched)
+ break;
+
+ /* Check the new installed set */
+ ctx_nexthop = NULL;
+ for (ALL_NEXTHOPS_PTR(dplane_ctx_get_ng(ctx), ctx_nexthop)) {
+
+ if (CHECK_FLAG(ctx_nexthop->flags,
+ NEXTHOP_FLAG_RECURSIVE))
+ continue;
+
+ /* Compare with the current group's nexthops */
+ nexthop = NULL;
+ for (ALL_NEXTHOPS(re->fib_ng, nexthop)) {
+ if (nexthop_same(nexthop, ctx_nexthop))
+ break;
+ }
+
+ if (nexthop == NULL) {
+ /* Nexthop not in the old installed set */
+ if (IS_ZEBRA_DEBUG_RIB_DETAILED) {
+ nexthop2str(ctx_nexthop, nh_str,
+ sizeof(nh_str));
+ zlog_debug("update_from_ctx: no fib match for notif nh %s",
+ nh_str);
+ }
+ matched = false;
+ break;
+ }
+ }
+
+ } while (0);
+
+ /* If the new FIB set matches the existing FIB set, we're done. */
+ if (matched) {
+ if (IS_ZEBRA_DEBUG_RIB)
+ zlog_debug("%u:%s update_from_ctx(): existing fib nhg, no change",
+ re->vrf_id, dest_str);
+ goto done;
+
+ } else if (re->fib_ng.nexthop) {
+ /*
+ * Free stale fib list and move on to check the rib nhg.
+ */
+ if (IS_ZEBRA_DEBUG_RIB)
+ zlog_debug("%u:%s update_from_ctx(): replacing fib nhg",
+ re->vrf_id, dest_str);
+ nexthops_free(re->fib_ng.nexthop);
+ re->fib_ng.nexthop = NULL;
+
+ /* Note that the installed nexthops have changed */
+ changed_p = true;
+ } else {
+ if (IS_ZEBRA_DEBUG_RIB)
+ zlog_debug("%u:%s update_from_ctx(): no fib nhg",
+ re->vrf_id, dest_str);
+ }
+
+ /*
+ * Compare with the rib nexthop group. The comparison here is different:
+ * the RIB group may be a superset of the list installed in the FIB. We
+ * walk the RIB group, looking for the 'installable' candidate
+ * nexthops, and then check those against the set
+ * that is actually installed.
+ */
+ matched = true;
+ for (ALL_NEXTHOPS(re->ng, nexthop)) {
+
+ if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_RECURSIVE))
+ continue;
+
+ if (!CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_ACTIVE))
+ continue;
+
+ /* Check for a FIB nexthop corresponding to the RIB nexthop */
+ ctx_nexthop = NULL;
+ for (ALL_NEXTHOPS_PTR(dplane_ctx_get_ng(ctx), ctx_nexthop)) {
+ if (nexthop_same(ctx_nexthop, nexthop))
+ break;
+ }
+
+ /* If the FIB doesn't know about the nexthop,
+ * it's not installed
+ */
+ if (ctx_nexthop == NULL) {
+ if (IS_ZEBRA_DEBUG_RIB_DETAILED) {
+ nexthop2str(nexthop, nh_str, sizeof(nh_str));
+ zlog_debug("update_from_ctx: no notif match for rib nh %s",
+ nh_str);
+ }
+ matched = false;
+
+ if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_FIB))
+ changed_p = true;
+
+ UNSET_FLAG(nexthop->flags, NEXTHOP_FLAG_FIB);
+ break;
+ }
+
+ if (CHECK_FLAG(ctx_nexthop->flags, NEXTHOP_FLAG_FIB)) {
+ if (!CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_FIB))
+ changed_p = true;
+
+ SET_FLAG(nexthop->flags, NEXTHOP_FLAG_FIB);
+ } else {
+ if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_FIB))
+ changed_p = true;
+
+ UNSET_FLAG(nexthop->flags, NEXTHOP_FLAG_FIB);
+ }
+ }
+
+ /* If all nexthops were processed, we're done */
+ if (matched) {
+ if (IS_ZEBRA_DEBUG_RIB)
+ zlog_debug("%u:%s update_from_ctx(): rib nhg matched, changed '%s'",
+ re->vrf_id, dest_str,
+ (changed_p ? "true" : "false"));
+ goto done;
+ }
+
+ /* FIB nexthop set differs from the RIB set:
+ * create a fib-specific nexthop-group
+ */
+ if (IS_ZEBRA_DEBUG_RIB)
+ zlog_debug("%u:%s update_from_ctx(): changed %s, adding new fib nhg",
+ re->vrf_id, dest_str,
+ (changed_p ? "true" : "false"));
+
+ ctxnhg = dplane_ctx_get_ng(ctx);
+
+ if (ctxnhg->nexthop)
+ copy_nexthops(&(re->fib_ng.nexthop), ctxnhg->nexthop, NULL);
+ else {
+ /* Bit of a special case when the fib has _no_ installed
+ * nexthops.
+ */
+ nexthop = nexthop_new();
+ nexthop->type = NEXTHOP_TYPE_IPV4;
+ nexthop_add(&(re->fib_ng.nexthop), nexthop);
+ }
+
+done:
+ return changed_p;
+}
+
+/*
+ * Helper to locate a zebra route-node from a dplane context. This is used
+ * when processing dplane results, e.g. Note well: the route-node is returned
+ * with a ref held - route_unlock_node() must be called eventually.
+ */
+static struct route_node *
+rib_find_rn_from_ctx(const struct zebra_dplane_ctx *ctx)
{
struct route_table *table = NULL;
- struct zebra_vrf *zvrf = NULL;
struct route_node *rn = NULL;
- struct route_entry *re = NULL, *old_re = NULL, *rib;
- bool is_update = false;
- struct nexthop *nexthop, *ctx_nexthop;
- char dest_str[PREFIX_STRLEN] = "";
- enum dplane_op_e op;
- enum zebra_dplane_result status;
const struct prefix *dest_pfx, *src_pfx;
- uint32_t seq;
/* Locate rn and re(s) from ctx */
dplane_ctx_get_table(ctx));
if (table == NULL) {
if (IS_ZEBRA_DEBUG_DPLANE) {
- zlog_debug("Failed to process dplane results: no table for afi %d, safi %d, vrf %u",
+ zlog_debug("Failed to find route for ctx: no table for afi %d, safi %d, vrf %u",
dplane_ctx_get_afi(ctx),
dplane_ctx_get_safi(ctx),
dplane_ctx_get_vrf(ctx));
goto done;
}
- zvrf = vrf_info_lookup(dplane_ctx_get_vrf(ctx));
+ dest_pfx = dplane_ctx_get_dest(ctx);
+ src_pfx = dplane_ctx_get_src(ctx);
+
+ rn = srcdest_rnode_get(table, dest_pfx,
+ src_pfx ? (struct prefix_ipv6 *)src_pfx : NULL);
+
+done:
+ return rn;
+}
+
+
+
+/*
+ * Route-update results processing after async dataplane update.
+ */
+static void rib_process_result(struct zebra_dplane_ctx *ctx)
+{
+ struct zebra_vrf *zvrf = NULL;
+ struct route_node *rn = NULL;
+ struct route_entry *re = NULL, *old_re = NULL, *rib;
+ bool is_update = false;
+ char dest_str[PREFIX_STRLEN] = "";
+ enum dplane_op_e op;
+ enum zebra_dplane_result status;
+ const struct prefix *dest_pfx, *src_pfx;
+ uint32_t seq;
+ bool fib_changed = false;
+ zvrf = vrf_info_lookup(dplane_ctx_get_vrf(ctx));
dest_pfx = dplane_ctx_get_dest(ctx);
/* Note well: only capturing the prefix string if debug is enabled here;
if (IS_ZEBRA_DEBUG_DPLANE)
prefix2str(dest_pfx, dest_str, sizeof(dest_str));
- src_pfx = dplane_ctx_get_src(ctx);
- rn = srcdest_rnode_get(table, dplane_ctx_get_dest(ctx),
- src_pfx ? (struct prefix_ipv6 *)src_pfx : NULL);
+ /* Locate rn and re(s) from ctx */
+ rn = rib_find_rn_from_ctx(ctx);
if (rn == NULL) {
if (IS_ZEBRA_DEBUG_DPLANE) {
zlog_debug("Failed to process dplane results: no route for %u:%s",
UNSET_FLAG(old_re->status,
ROUTE_ENTRY_INSTALLED);
}
- /* Update zebra nexthop FIB flag for each
- * nexthop that was installed.
- */
- for (ALL_NEXTHOPS_PTR(dplane_ctx_get_ng(ctx),
- ctx_nexthop)) {
-
- if (!re)
- continue;
- for (ALL_NEXTHOPS(re->ng, nexthop)) {
- if (nexthop_same(ctx_nexthop, nexthop))
- break;
+ /* Update zebra route based on the results in
+ * the context struct.
+ */
+ if (re) {
+ fib_changed =
+ rib_update_re_from_ctx(re, rn, ctx);
+
+ if (!fib_changed) {
+ if (IS_ZEBRA_DEBUG_DPLANE_DETAIL)
+ zlog_debug("%u:%s no fib change for re",
+ dplane_ctx_get_vrf(
+ ctx),
+ dest_str);
}
- if (nexthop == NULL)
- continue;
-
- if (CHECK_FLAG(nexthop->flags,
- NEXTHOP_FLAG_RECURSIVE))
- continue;
-
- if (CHECK_FLAG(ctx_nexthop->flags,
- NEXTHOP_FLAG_FIB))
- SET_FLAG(nexthop->flags,
- NEXTHOP_FLAG_FIB);
- else
- UNSET_FLAG(nexthop->flags,
- NEXTHOP_FLAG_FIB);
+ /* Redistribute */
+ redistribute_update(dest_pfx, src_pfx,
+ re, NULL);
}
/*
if (zvrf)
zvrf->installs++;
- /* Redistribute */
- /*
- * TODO -- still calling the redist api using the
- * route_entries, and there's a corner-case here:
- * if there's no client for the 'new' route, a redist
- * deleting the 'old' route will be sent. But if the
- * 'old' context info was stale, 'old_re' will be
- * NULL here and that delete will not be sent.
- */
- if (re)
- redistribute_update(dest_pfx, src_pfx,
- re, old_re);
-
/* Notify route owner */
zsend_route_notify_owner_ctx(ctx, ZAPI_ROUTE_INSTALLED);
dplane_ctx_fini(&ctx);
}
+/*
+ * Handle notification from async dataplane: the dataplane has detected
+ * some change to a route, and notifies zebra so that the control plane
+ * can reflect that change.
+ */
+static void rib_process_dplane_notify(struct zebra_dplane_ctx *ctx)
+{
+ struct route_node *rn = NULL;
+ struct route_entry *re = NULL;
+ struct nexthop *nexthop;
+ char dest_str[PREFIX_STRLEN] = "";
+ const struct prefix *dest_pfx, *src_pfx;
+ rib_dest_t *dest;
+ bool fib_changed = false;
+ bool debug_p = IS_ZEBRA_DEBUG_DPLANE | IS_ZEBRA_DEBUG_RIB;
+ int start_count, end_count;
+ dest_pfx = dplane_ctx_get_dest(ctx);
+
+ /* Note well: only capturing the prefix string if debug is enabled here;
+ * unconditional log messages will have to generate the string.
+ */
+ if (debug_p)
+ prefix2str(dest_pfx, dest_str, sizeof(dest_str));
+
+ /* Locate rn and re(s) from ctx */
+ rn = rib_find_rn_from_ctx(ctx);
+ if (rn == NULL) {
+ if (debug_p) {
+ zlog_debug("Failed to process dplane notification: no routes for %u:%s",
+ dplane_ctx_get_vrf(ctx), dest_str);
+ }
+ goto done;
+ }
+
+ dest = rib_dest_from_rnode(rn);
+ srcdest_rnode_prefixes(rn, &dest_pfx, &src_pfx);
+
+ if (debug_p)
+ zlog_debug("%u:%s Processing dplane notif ctx %p",
+ dplane_ctx_get_vrf(ctx), dest_str, ctx);
+
+ /*
+ * Take a pass through the routes, look for matches with the context
+ * info.
+ */
+ RNODE_FOREACH_RE(rn, re) {
+ if (rib_route_match_ctx(re, ctx, false /*!update*/))
+ break;
+ }
+
+ /* No match? Nothing we can do */
+ if (re == NULL) {
+ if (debug_p)
+ zlog_debug("%u:%s Unable to process dplane notification: no entry for type %s",
+ dplane_ctx_get_vrf(ctx), dest_str,
+ zebra_route_string(
+ dplane_ctx_get_type(ctx)));
+
+ goto done;
+ }
+
+ /* Is this a notification that ... matters? We only really care about
+ * the route that is currently selected for installation.
+ */
+ if (re != dest->selected_fib) {
+ /* TODO -- don't skip processing entirely? We might like to
+ * at least report on the event.
+ */
+ if (debug_p)
+ zlog_debug("%u:%s dplane notif, but type %s not selected_fib",
+ dplane_ctx_get_vrf(ctx), dest_str,
+ zebra_route_string(
+ dplane_ctx_get_type(ctx)));
+ goto done;
+ }
+
+ /* We'll want to determine whether the installation status of the
+ * route has changed: we'll check the status before processing,
+ * and then again if there's been a change.
+ */
+ start_count = 0;
+ for (ALL_NEXTHOPS_PTR(rib_active_nhg(re), nexthop)) {
+ if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_FIB))
+ start_count++;
+ }
+
+ /* Update zebra's nexthop FIB flags based on the context struct's
+ * nexthops.
+ */
+ fib_changed = rib_update_re_from_ctx(re, rn, ctx);
+
+ if (!fib_changed) {
+ if (debug_p)
+ zlog_debug("%u:%s No change from dplane notification",
+ dplane_ctx_get_vrf(ctx), dest_str);
+
+ goto done;
+ }
+
+ /*
+ * Perform follow-up work if the actual status of the prefix
+ * changed.
+ */
+
+ end_count = 0;
+ for (ALL_NEXTHOPS_PTR(rib_active_nhg(re), nexthop)) {
+ if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_FIB))
+ end_count++;
+ }
+
+ /* Various fib transitions: changed nexthops; from installed to
+ * not-installed; or not-installed to installed.
+ */
+ if (start_count > 0 && end_count > 0) {
+
+ /* Changed nexthops - update kernel/others */
+ dplane_route_notif_update(rn, re,
+ DPLANE_OP_ROUTE_UPDATE, ctx);
+
+ } else if (start_count == 0 && end_count > 0) {
+ if (debug_p)
+ zlog_debug("%u:%s installed transition from dplane notification",
+ dplane_ctx_get_vrf(ctx), dest_str);
+
+ /* We expect this to be the selected route, so we want
+ * to tell others about this transistion.
+ */
+ SET_FLAG(re->status, ROUTE_ENTRY_INSTALLED);
+
+ /* Changed nexthops - update kernel/others */
+ dplane_route_notif_update(rn, re, DPLANE_OP_ROUTE_INSTALL, ctx);
+
+ /* Redistribute, lsp, and nht update */
+ redistribute_update(dest_pfx, src_pfx, re, NULL);
+
+ zebra_rib_evaluate_rn_nexthops(
+ rn, zebra_router_get_next_sequence());
+
+ zebra_rib_evaluate_mpls(rn);
+
+ } else if (start_count > 0 && end_count == 0) {
+ if (debug_p)
+ zlog_debug("%u:%s un-installed transition from dplane notification",
+ dplane_ctx_get_vrf(ctx), dest_str);
+
+ /* Transition from _something_ installed to _nothing_
+ * installed.
+ */
+ /* We expect this to be the selected route, so we want
+ * to tell others about this transistion.
+ */
+ UNSET_FLAG(re->status, ROUTE_ENTRY_INSTALLED);
+
+ /* Changed nexthops - update kernel/others */
+ dplane_route_notif_update(rn, re, DPLANE_OP_ROUTE_DELETE, ctx);
+
+ /* Redistribute, lsp, and nht update */
+ redistribute_delete(dest_pfx, src_pfx, re);
+
+ zebra_rib_evaluate_rn_nexthops(
+ rn, zebra_router_get_next_sequence());
+
+ zebra_rib_evaluate_mpls(rn);
+ }
+
+done:
+ if (rn)
+ route_unlock_node(rn);
+
+ /* Return context to dataplane module */
+ dplane_ctx_fini(&ctx);
+}
+
/* Take a list of route_node structs and return 1, if there was a record
* picked from it and processed by rib_process(). Don't process more,
* than one RN record; operate only in the specified sub-queue.
if (IS_ZEBRA_DEBUG_RIB_DETAILED) {
char buf[SRCDEST2STR_BUFFER];
+
srcdest_rnode2str(rnode, buf, sizeof(buf));
zlog_debug("%u:%s: rn %p dequeued from sub-queue %u",
zvrf ? zvrf_id(zvrf) : 0, buf, rnode, qindex);
dest->selected_fib = NULL;
nexthops_free(re->ng.nexthop);
+ nexthops_free(re->fib_ng.nexthop);
+
XFREE(MTYPE_RE, re);
}
apply_mask_ipv6(src_p);
/* Set default distance by route type. */
- if (re->distance == 0) {
+ if (re->distance == 0)
re->distance = route_distance(re->type);
- /* iBGP distance is 200. */
- if (re->type == ZEBRA_ROUTE_BGP
- && CHECK_FLAG(re->flags, ZEBRA_FLAG_IBGP))
- re->distance = 200;
- }
-
/* Lookup route node.*/
rn = srcdest_rnode_get(table, p, src_p);
case DPLANE_OP_ROUTE_INSTALL:
case DPLANE_OP_ROUTE_UPDATE:
case DPLANE_OP_ROUTE_DELETE:
- rib_process_result(ctx);
+ {
+ /* Bit of special case for route updates
+ * that were generated by async notifications:
+ * we don't want to continue processing these
+ * in the rib.
+ */
+ if (dplane_ctx_get_notif_provider(ctx) == 0)
+ rib_process_result(ctx);
+ else
+ dplane_ctx_fini(&ctx);
+ }
+ break;
+
+ case DPLANE_OP_ROUTE_NOTIFY:
+ rib_process_dplane_notify(ctx);
break;
case DPLANE_OP_LSP_INSTALL:
case DPLANE_OP_LSP_UPDATE:
case DPLANE_OP_LSP_DELETE:
- zebra_mpls_lsp_dplane_result(ctx);
+ {
+ /* Bit of special case for LSP updates
+ * that were generated by async notifications:
+ * we don't want to continue processing these.
+ */
+ if (dplane_ctx_get_notif_provider(ctx) == 0)
+ zebra_mpls_lsp_dplane_result(ctx);
+ else
+ dplane_ctx_fini(&ctx);
+ }
+ break;
+
+ case DPLANE_OP_LSP_NOTIFY:
+ zebra_mpls_process_dplane_notify(ctx);
break;
case DPLANE_OP_PW_INSTALL:
{
int at_least_one = 0;
struct nexthop *nexthop;
- int ret;
+ route_map_result_t ret;
if (prn && re) {
for (nexthop = re->ng.nexthop; nexthop;
static void print_nh(struct nexthop *nexthop, struct vty *vty)
{
char buf[BUFSIZ];
- struct zebra_ns *zns = zebra_ns_lookup(NS_DEFAULT);
+ struct zebra_ns *zns = zebra_ns_lookup(nexthop->vrf_id);
switch (nexthop->type) {
case NEXTHOP_TYPE_IPV4:
/* 'match tag TAG'
* Match function return 1 if match is success else return 0
*/
-static route_map_result_t route_match_tag(void *rule,
- const struct prefix *prefix,
- route_map_object_t type, void *object)
+static enum route_map_match_result_t
+route_match_tag(void *rule, const struct prefix *prefix,
+ route_map_object_t type, void *object)
{
route_tag_t *tag;
struct nh_rmap_obj *nh_data;
/* `match interface IFNAME' */
/* Match function return 1 if match is success else return zero. */
-static route_map_result_t route_match_interface(void *rule,
- const struct prefix *prefix,
- route_map_object_t type,
- void *object)
+static enum route_map_match_result_t
+route_match_interface(void *rule, const struct prefix *prefix,
+ route_map_object_t type, void *object)
{
struct nh_rmap_obj *nh_data;
char *ifname = rule;
/* `match ip next-hop IP_ACCESS_LIST' */
/* Match function return 1 if match is success else return zero. */
-static route_map_result_t route_match_ip_next_hop(void *rule,
- const struct prefix *prefix,
- route_map_object_t type,
- void *object)
+static enum route_map_match_result_t
+route_match_ip_next_hop(void *rule, const struct prefix *prefix,
+ route_map_object_t type, void *object)
{
struct access_list *alist;
struct nh_rmap_obj *nh_data;
/* `match ip next-hop prefix-list PREFIX_LIST' */
-static route_map_result_t
+static enum route_map_match_result_t
route_match_ip_next_hop_prefix_list(void *rule, const struct prefix *prefix,
route_map_object_t type, void *object)
{
/* Match function should return 1 if match is success else return
zero. */
-static route_map_result_t route_match_address(afi_t afi, void *rule,
- const struct prefix *prefix,
- route_map_object_t type,
- void *object)
+static enum route_map_match_result_t
+route_match_address(afi_t afi, void *rule, const struct prefix *prefix,
+ route_map_object_t type, void *object)
{
struct access_list *alist;
return RMAP_NOMATCH;
}
-static route_map_result_t route_match_ip_address(void *rule,
- const struct prefix *prefix,
- route_map_object_t type,
- void *object)
+static enum route_map_match_result_t
+route_match_ip_address(void *rule, const struct prefix *prefix,
+ route_map_object_t type, void *object)
{
return route_match_address(AFI_IP, rule, prefix, type, object);
}
-static route_map_result_t route_match_ipv6_address(void *rule,
- const struct prefix *prefix,
- route_map_object_t type,
- void *object)
-
+static enum route_map_match_result_t
+route_match_ipv6_address(void *rule, const struct prefix *prefix,
+ route_map_object_t type, void *object)
{
return route_match_address(AFI_IP6, rule, prefix, type, object);
}
/* `match ip address prefix-list PREFIX_LIST' */
-static route_map_result_t
+static enum route_map_match_result_t
route_match_address_prefix_list(void *rule, const struct prefix *prefix,
route_map_object_t type, void *object, afi_t afi)
{
return RMAP_NOMATCH;
}
-static route_map_result_t
+static enum route_map_match_result_t
route_match_ip_address_prefix_list(void *rule, const struct prefix *prefix,
route_map_object_t type, void *object)
{
route_match_address_prefix_list_compile,
route_match_address_prefix_list_free};
-static route_map_result_t
+static enum route_map_match_result_t
route_match_ipv6_address_prefix_list(void *rule, const struct prefix *prefix,
route_map_object_t type, void *object)
{
/* `match ip address prefix-len PREFIXLEN' */
-static route_map_result_t
+static enum route_map_match_result_t
route_match_address_prefix_len(void *rule, const struct prefix *prefix,
route_map_object_t type, void *object)
{
/* `match ip nexthop prefix-len PREFIXLEN' */
-static route_map_result_t
+static enum route_map_match_result_t
route_match_ip_nexthop_prefix_len(void *rule, const struct prefix *prefix,
route_map_object_t type, void *object)
{
/* `match source-protocol PROTOCOL' */
-static route_map_result_t route_match_source_protocol(void *rule,
- const struct prefix *p,
- route_map_object_t type,
- void *object)
+static enum route_map_match_result_t
+route_match_source_protocol(void *rule, const struct prefix *p,
+ route_map_object_t type, void *object)
{
uint32_t *rib_type = (uint32_t *)rule;
struct nh_rmap_obj *nh_data;
route_match_source_protocol_compile, route_match_source_protocol_free};
/* `source-instance` */
-static route_map_result_t route_match_source_instance(void *rule,
- const struct prefix *p,
- route_map_object_t type,
- void *object)
+static enum route_map_match_result_t
+route_match_source_instance(void *rule, const struct prefix *p,
+ route_map_object_t type, void *object)
{
uint8_t *instance = (uint8_t *)rule;
struct nh_rmap_obj *nh_data;
/* `set src A.B.C.D' */
/* Set src. */
-static route_map_result_t route_set_src(void *rule, const struct prefix *prefix,
- route_map_object_t type, void *object)
+static enum route_map_match_result_t
+route_set_src(void *rule, const struct prefix *prefix, route_map_object_t type,
+ void *object)
{
struct nh_rmap_obj *nh_data;
struct zebra_vrf *zvrf, route_tag_t tag)
{
struct route_map *rmap = NULL;
- route_map_result_t ret = RMAP_MATCH;
+ route_map_result_t ret = RMAP_PERMITMATCH;
struct nh_rmap_obj nh_obj;
nh_obj.nexthop = nexthop;
const char *rmap_name)
{
struct route_map *rmap = NULL;
- route_map_result_t ret = RMAP_DENYMATCH;
+ enum route_map_match_result_t ret = RMAP_DENYMATCH;
struct nh_rmap_obj nh_obj;
nh_obj.nexthop = nexthop;
struct nexthop *nexthop)
{
struct route_map *rmap = NULL;
- route_map_result_t ret = RMAP_MATCH;
+ route_map_result_t ret = RMAP_PERMITMATCH;
struct nh_rmap_obj nh_obj;
nh_obj.nexthop = nexthop;
}
static void vty_show_ip_route(struct vty *vty, struct route_node *rn,
- struct route_entry *re, json_object *json)
+ struct route_entry *re, json_object *json,
+ bool is_fib)
{
struct nexthop *nexthop;
int len = 0;
time_t uptime;
struct tm *tm;
rib_dest_t *dest = rib_dest_from_rnode(rn);
+ struct nexthop_group *nhg;
uptime = monotime(NULL);
uptime -= re->uptime;
tm = gmtime(&uptime);
+ /* If showing fib information, use the fib view of the
+ * nexthops.
+ */
+ if (is_fib)
+ nhg = rib_active_nhg(re);
+ else
+ nhg = &(re->ng);
+
if (json) {
json_route = json_object_new_object();
json_nexthops = json_object_new_array();
json_object_string_add(json_route, "uptime", buf);
- for (ALL_NEXTHOPS(re->ng, nexthop)) {
+ for (ALL_NEXTHOPS_PTR(nhg, nexthop)) {
json_nexthop = json_object_new_object();
json_object_int_add(json_nexthop, "flags",
}
/* Nexthop information. */
- for (ALL_NEXTHOPS(re->ng, nexthop)) {
- if (nexthop == re->ng.nexthop) {
+ for (ALL_NEXTHOPS_PTR(nhg, nexthop)) {
+ if (nexthop == nhg->nexthop) {
/* Prefix information. */
len = vty_out(vty, "%c", zebra_route_char(re->type));
if (re->instance)
*/
if (use_fib && re != dest->selected_fib)
continue;
- vty_show_ip_route(vty, rn, re, json_prefix);
+ vty_show_ip_route(vty, rn, re, json_prefix, use_fib);
}
prefix2str(&rn->p, buf, sizeof(buf));
}
}
- vty_show_ip_route(vty, rn, re, json_prefix);
+ vty_show_ip_route(vty, rn, re, json_prefix, use_fib);
}
if (json_prefix) {
vty_out(vty, SHOW_ROUTE_V6_HEADER);
first = 0;
}
- vty_show_ip_route(vty, rn, re, NULL);
+ vty_show_ip_route(vty, rn, re, NULL, false);
}
return CMD_SUCCESS;
}
vty_out(vty, SHOW_ROUTE_V6_HEADER);
first = 0;
}
- vty_show_ip_route(vty, rn, re, NULL);
+ vty_show_ip_route(vty, rn, re, NULL, false);
}
}
return CMD_SUCCESS;
static struct zserv *zserv_client_create(int sock)
{
struct zserv *client;
+ size_t stream_size =
+ MAX(ZEBRA_MAX_PACKET_SIZ, sizeof(struct zapi_route));
int i;
afi_t afi;
client->sock = sock;
client->ibuf_fifo = stream_fifo_new();
client->obuf_fifo = stream_fifo_new();
- client->ibuf_work = stream_new(ZEBRA_MAX_PACKET_SIZ);
- client->obuf_work = stream_new(ZEBRA_MAX_PACKET_SIZ);
+ client->ibuf_work = stream_new(stream_size);
+ client->obuf_work = stream_new(stream_size);
pthread_mutex_init(&client->ibuf_mtx, NULL);
pthread_mutex_init(&client->obuf_mtx, NULL);
client->wb = buffer_new(0);