diff --git a/bgpd/bgp_vty.c b/bgpd/bgp_vty.c index 833de64c94..6209c1be20 100644 --- a/bgpd/bgp_vty.c +++ b/bgpd/bgp_vty.c @@ -4075,6 +4075,7 @@ DEFUN (neighbor_send_community, "Send Community attribute to this neighbor\n") { int idx_peer = 1; + return peer_af_flag_set_vty(vty, argv[idx_peer]->arg, bgp_node_afi(vty), bgp_node_safi(vty), PEER_FLAG_SEND_COMMUNITY); @@ -4094,6 +4095,7 @@ DEFUN (no_neighbor_send_community, "Send Community attribute to this neighbor\n") { int idx_peer = 2; + return peer_af_flag_unset_vty(vty, argv[idx_peer]->arg, bgp_node_afi(vty), bgp_node_safi(vty), PEER_FLAG_SEND_COMMUNITY); @@ -4117,27 +4119,26 @@ DEFUN (neighbor_send_community_type, "Send Standard Community attributes\n" "Send Large Community attributes\n") { - int idx = 0; + int idx_peer = 1; uint32_t flag = 0; + const char *type = argv[argc - 1]->text; - char *peer = argv[1]->arg; - - if (argv_find(argv, argc, "standard", &idx)) + if (strmatch(type, "standard")) { SET_FLAG(flag, PEER_FLAG_SEND_COMMUNITY); - else if (argv_find(argv, argc, "extended", &idx)) + } else if (strmatch(type, "extended")) { SET_FLAG(flag, PEER_FLAG_SEND_EXT_COMMUNITY); - else if (argv_find(argv, argc, "large", &idx)) + } else if (strmatch(type, "large")) { SET_FLAG(flag, PEER_FLAG_SEND_LARGE_COMMUNITY); - else if (argv_find(argv, argc, "both", &idx)) { + } else if (strmatch(type, "both")) { SET_FLAG(flag, PEER_FLAG_SEND_COMMUNITY); SET_FLAG(flag, PEER_FLAG_SEND_EXT_COMMUNITY); - } else { + } else { /* if (strmatch(type, "all")) */ SET_FLAG(flag, PEER_FLAG_SEND_COMMUNITY); SET_FLAG(flag, PEER_FLAG_SEND_EXT_COMMUNITY); SET_FLAG(flag, PEER_FLAG_SEND_LARGE_COMMUNITY); } - return peer_af_flag_set_vty(vty, peer, bgp_node_afi(vty), + return peer_af_flag_set_vty(vty, argv[idx_peer]->arg, bgp_node_afi(vty), bgp_node_safi(vty), flag); } @@ -4166,33 +4167,27 @@ DEFUN (no_neighbor_send_community_type, "Send Large Community attributes\n") { int idx_peer = 2; - + uint32_t flag = 0; const char *type = argv[argc - 1]->text; - if (strmatch(type, "standard")) - return peer_af_flag_unset_vty( - vty, argv[idx_peer]->arg, bgp_node_afi(vty), - bgp_node_safi(vty), PEER_FLAG_SEND_COMMUNITY); - if (strmatch(type, "extended")) - return peer_af_flag_unset_vty( - vty, argv[idx_peer]->arg, bgp_node_afi(vty), - bgp_node_safi(vty), PEER_FLAG_SEND_EXT_COMMUNITY); - if (strmatch(type, "large")) - return peer_af_flag_unset_vty( - vty, argv[idx_peer]->arg, bgp_node_afi(vty), - bgp_node_safi(vty), PEER_FLAG_SEND_LARGE_COMMUNITY); - if (strmatch(type, "both")) - return peer_af_flag_unset_vty( - vty, argv[idx_peer]->arg, bgp_node_afi(vty), - bgp_node_safi(vty), - PEER_FLAG_SEND_COMMUNITY - | PEER_FLAG_SEND_EXT_COMMUNITY); + if (strmatch(type, "standard")) { + SET_FLAG(flag, PEER_FLAG_SEND_COMMUNITY); + } else if (strmatch(type, "extended")) { + SET_FLAG(flag, PEER_FLAG_SEND_EXT_COMMUNITY); + } else if (strmatch(type, "large")) { + SET_FLAG(flag, PEER_FLAG_SEND_LARGE_COMMUNITY); + } else if (strmatch(type, "both")) { + SET_FLAG(flag, PEER_FLAG_SEND_COMMUNITY); + SET_FLAG(flag, PEER_FLAG_SEND_EXT_COMMUNITY); + } else { /* if (strmatch(type, "all")) */ + SET_FLAG(flag, PEER_FLAG_SEND_COMMUNITY); + SET_FLAG(flag, PEER_FLAG_SEND_EXT_COMMUNITY); + SET_FLAG(flag, PEER_FLAG_SEND_LARGE_COMMUNITY); + } - /* if (strmatch (type, "all")) */ - return peer_af_flag_unset_vty( - vty, argv[idx_peer]->arg, bgp_node_afi(vty), bgp_node_safi(vty), - (PEER_FLAG_SEND_COMMUNITY | PEER_FLAG_SEND_EXT_COMMUNITY - | PEER_FLAG_SEND_LARGE_COMMUNITY)); + return peer_af_flag_unset_vty(vty, argv[idx_peer]->arg, + bgp_node_afi(vty), bgp_node_safi(vty), + flag); } ALIAS_HIDDEN( @@ -5362,8 +5357,8 @@ static int peer_prefix_list_set_vty(struct vty *vty, const char *ip_str, const char *direct_str) { int ret; - struct peer *peer; int direct = FILTER_IN; + struct peer *peer; peer = peer_and_group_lookup_vty(vty, ip_str); if (!peer) diff --git a/bgpd/bgpd.c b/bgpd/bgpd.c index cc09d4991d..0c7b642438 100644 --- a/bgpd/bgpd.c +++ b/bgpd/bgpd.c @@ -787,31 +787,60 @@ int peer_af_flag_check(struct peer *peer, afi_t afi, safi_t safi, uint32_t flag) return CHECK_FLAG(peer->af_flags[afi][safi], flag); } -/* Return true if flag is set for the peer but not the peer-group */ -static int peergroup_af_flag_check(struct peer *peer, afi_t afi, safi_t safi, - uint32_t flag) +void peer_af_flag_inherit(struct peer *peer, afi_t afi, safi_t safi, + uint32_t flag) { - struct peer *g_peer = NULL; + /* Skip if peer is not a peer-group member. */ + if (!peer_group_active(peer)) + return; - if (peer_af_flag_check(peer, afi, safi, flag)) { - if (peer_group_active(peer)) { - g_peer = peer->group->conf; + /* Unset override flag to signal inheritance from peer-group. */ + UNSET_FLAG(peer->af_flags_override[afi][safi], flag); - /* If this flag is not set for the peer's peer-group - * then return true */ - if (!peer_af_flag_check(g_peer, afi, safi, flag)) { - return 1; - } - } + /* Inherit flag state from peer-group. */ + if (CHECK_FLAG(peer->group->conf->af_flags[afi][safi], flag)) + SET_FLAG(peer->af_flags[afi][safi], flag); + else + UNSET_FLAG(peer->af_flags[afi][safi], flag); +} - /* peer is not in a peer-group but the flag is set to return - true */ - else { - return 1; - } +static bool peergroup_af_flag_check(struct peer *peer, afi_t afi, safi_t safi, + uint32_t flag) +{ + if (!peer_group_active(peer)) { + if (CHECK_FLAG(peer->af_flags_invert[afi][safi], flag)) + return !peer_af_flag_check(peer, afi, safi, flag); + else + return !!peer_af_flag_check(peer, afi, safi, flag); } - return 0; + return !!CHECK_FLAG(peer->af_flags_override[afi][safi], flag); +} + +static bool peergroup_filter_check(struct peer *peer, afi_t afi, safi_t safi, + uint8_t type, int direct) +{ + struct bgp_filter *filter; + + if (peer_group_active(peer)) + return !!CHECK_FLAG(peer->filter_override[afi][safi][direct], + type); + + filter = &peer->filter[afi][safi]; + switch (type) { + case PEER_FT_DISTRIBUTE_LIST: + return !!(filter->dlist[direct].name); + case PEER_FT_FILTER_LIST: + return !!(filter->aslist[direct].name); + case PEER_FT_PREFIX_LIST: + return !!(filter->plist[direct].name); + case PEER_FT_ROUTE_MAP: + return !!(filter->map[direct].name); + case PEER_FT_UNSUPPRESS_MAP: + return !!(filter->usmap.name); + default: + return false; + } } /* Reset all address family specific configuration. */ @@ -872,6 +901,13 @@ static void peer_af_flag_reset(struct peer *peer, afi_t afi, safi_t safi) PEER_FLAG_SEND_EXT_COMMUNITY); SET_FLAG(peer->af_flags[afi][safi], PEER_FLAG_SEND_LARGE_COMMUNITY); + + SET_FLAG(peer->af_flags_invert[afi][safi], + PEER_FLAG_SEND_COMMUNITY); + SET_FLAG(peer->af_flags_invert[afi][safi], + PEER_FLAG_SEND_EXT_COMMUNITY); + SET_FLAG(peer->af_flags_invert[afi][safi], + PEER_FLAG_SEND_LARGE_COMMUNITY); } /* Clear neighbor default_originate_rmap */ @@ -1145,7 +1181,7 @@ struct peer *peer_new(struct bgp *bgp) peer = peer_lock(peer); /* initial reference */ peer->password = NULL; - /* Set default flags. */ + /* Set default flags. */ FOREACH_AFI_SAFI (afi, safi) { if (!bgp_option_check(BGP_OPT_CONFIG_CISCO)) { SET_FLAG(peer->af_flags[afi][safi], @@ -1154,8 +1190,14 @@ struct peer *peer_new(struct bgp *bgp) PEER_FLAG_SEND_EXT_COMMUNITY); SET_FLAG(peer->af_flags[afi][safi], PEER_FLAG_SEND_LARGE_COMMUNITY); + + SET_FLAG(peer->af_flags_invert[afi][safi], + PEER_FLAG_SEND_COMMUNITY); + SET_FLAG(peer->af_flags_invert[afi][safi], + PEER_FLAG_SEND_EXT_COMMUNITY); + SET_FLAG(peer->af_flags_invert[afi][safi], + PEER_FLAG_SEND_LARGE_COMMUNITY); } - peer->orf_plist[afi][safi] = NULL; } /* set nexthop-unchanged for l2vpn evpn by default */ @@ -1246,6 +1288,8 @@ void peer_xfer_config(struct peer *peer_dst, struct peer *peer_src) FOREACH_AFI_SAFI (afi, safi) { peer_dst->afc[afi][safi] = peer_src->afc[afi][safi]; peer_dst->af_flags[afi][safi] = peer_src->af_flags[afi][safi]; + peer_dst->af_flags_invert[afi][safi] = + peer_src->af_flags_invert[afi][safi]; peer_dst->allowas_in[afi][safi] = peer_src->allowas_in[afi][safi]; peer_dst->weight[afi][safi] = peer_src->weight[afi][safi]; @@ -1741,6 +1785,7 @@ static void peer_group2peer_config_copy_af(struct peer_group *group, /* peer af_flags apply */ peer->af_flags[afi][safi] = conf->af_flags[afi][safi]; + peer->af_flags_invert[afi][safi] = conf->af_flags_invert[afi][safi]; /* maximum-prefix */ peer->pmax[afi][safi] = conf->pmax[afi][safi]; @@ -3790,14 +3835,14 @@ static const struct peer_flag_action peer_af_flag_action_list[] = { {PEER_FLAG_AS_PATH_UNCHANGED, 1, peer_change_reset_out}, {PEER_FLAG_NEXTHOP_UNCHANGED, 1, peer_change_reset_out}, {PEER_FLAG_MED_UNCHANGED, 1, peer_change_reset_out}, - // PEER_FLAG_DEFAULT_ORIGINATE + {PEER_FLAG_DEFAULT_ORIGINATE, 0, peer_change_none}, {PEER_FLAG_REMOVE_PRIVATE_AS, 1, peer_change_reset_out}, {PEER_FLAG_ALLOWAS_IN, 0, peer_change_reset_in}, {PEER_FLAG_ALLOWAS_IN_ORIGIN, 0, peer_change_reset_in}, {PEER_FLAG_ORF_PREFIX_SM, 1, peer_change_reset}, {PEER_FLAG_ORF_PREFIX_RM, 1, peer_change_reset}, - // PEER_FLAG_MAX_PREFIX - // PEER_FLAG_MAX_PREFIX_WARNING + {PEER_FLAG_MAX_PREFIX, 0, peer_change_none}, + {PEER_FLAG_MAX_PREFIX_WARNING, 0, peer_change_none}, {PEER_FLAG_NEXTHOP_LOCAL_UNCHANGED, 0, peer_change_reset_out}, {PEER_FLAG_FORCE_NEXTHOP_SELF, 1, peer_change_reset_out}, {PEER_FLAG_REMOVE_PRIVATE_AS_ALL, 1, peer_change_reset_out}, @@ -4005,21 +4050,23 @@ int peer_flag_unset(struct peer *peer, uint32_t flag) } static int peer_af_flag_modify(struct peer *peer, afi_t afi, safi_t safi, - uint32_t flag, int set) + uint32_t flag, bool set) { int found; int size; + int addpath_tx_used; + bool invert; struct listnode *node, *nnode; struct peer_group *group; struct peer_flag_action action; struct peer *tmp_peer; struct bgp *bgp; - int addpath_tx_used; memset(&action, 0, sizeof(struct peer_flag_action)); size = sizeof peer_af_flag_action_list / sizeof(struct peer_flag_action); + invert = CHECK_FLAG(peer->af_flags_invert[afi][safi], flag); found = peer_flag_action_set(peer_af_flag_action_list, size, &action, flag); @@ -4043,10 +4090,25 @@ static int peer_af_flag_modify(struct peer *peer, afi_t afi, safi_t safi, /* When current flag configuration is same as requested one. */ if (!CHECK_FLAG(peer->sflags, PEER_STATUS_GROUP)) { - if (set && CHECK_FLAG(peer->af_flags[afi][safi], flag) == flag) + if (set && CHECK_FLAG(peer->af_flags[afi][safi], flag)) { + if (invert) + UNSET_FLAG(peer->af_flags_override[afi][safi], + flag); + else + SET_FLAG(peer->af_flags_override[afi][safi], + flag); return 0; - if (!set && !CHECK_FLAG(peer->af_flags[afi][safi], flag)) + } + + if (!set && !CHECK_FLAG(peer->af_flags[afi][safi], flag)) { + if (invert) + SET_FLAG(peer->af_flags_override[afi][safi], + flag); + else + UNSET_FLAG(peer->af_flags_override[afi][safi], + flag); return 0; + } } /* @@ -4078,10 +4140,22 @@ static int peer_af_flag_modify(struct peer *peer, afi_t afi, safi_t safi, } } - if (set) - SET_FLAG(peer->af_flags[afi][safi], flag); - else - UNSET_FLAG(peer->af_flags[afi][safi], flag); + /* Set/unset flag or inherit from peer-group if appropriate. */ + if (invert) { + if (!set) + UNSET_FLAG(peer->af_flags[afi][safi], flag); + else if (peer_group_active(peer)) + peer_af_flag_inherit(peer, afi, safi, flag); + else + SET_FLAG(peer->af_flags[afi][safi], flag); + } else { + if (set) + SET_FLAG(peer->af_flags[afi][safi], flag); + else if (peer_group_active(peer)) + peer_af_flag_inherit(peer, afi, safi, flag); + else + UNSET_FLAG(peer->af_flags[afi][safi], flag); + } /* Execute action when peer is established. */ if (!CHECK_FLAG(peer->sflags, PEER_STATUS_GROUP) @@ -4105,11 +4179,13 @@ static int peer_af_flag_modify(struct peer *peer, afi_t afi, safi_t safi, /* Peer group member updates. */ if (CHECK_FLAG(peer->sflags, PEER_STATUS_GROUP)) { group = peer->group; - for (ALL_LIST_ELEMENTS(group->peer, node, nnode, tmp_peer)) { + if (CHECK_FLAG(tmp_peer->af_flags_override[afi][safi], + flag)) + continue; + if (set - && CHECK_FLAG(tmp_peer->af_flags[afi][safi], flag) - == flag) + && CHECK_FLAG(tmp_peer->af_flags[afi][safi], flag)) continue; if (!set @@ -4146,6 +4222,11 @@ static int peer_af_flag_modify(struct peer *peer, afi_t afi, safi_t safi, } } } + } else { + if (set != invert) + SET_FLAG(peer->af_flags_override[afi][safi], flag); + else + UNSET_FLAG(peer->af_flags_override[afi][safi], flag); } /* Track if addpath TX is in use */ @@ -4525,71 +4606,98 @@ int peer_update_source_unset(struct peer *peer) int peer_default_originate_set(struct peer *peer, afi_t afi, safi_t safi, const char *rmap) { - struct peer_group *group; + struct peer *member; struct listnode *node, *nnode; - if (!CHECK_FLAG(peer->af_flags[afi][safi], PEER_FLAG_DEFAULT_ORIGINATE) - || (rmap && !peer->default_rmap[afi][safi].name) - || (rmap - && strcmp(rmap, peer->default_rmap[afi][safi].name) != 0)) { - SET_FLAG(peer->af_flags[afi][safi], - PEER_FLAG_DEFAULT_ORIGINATE); - - if (rmap) { + /* Set flag and configuration on peer. */ + peer_af_flag_set(peer, afi, safi, PEER_FLAG_DEFAULT_ORIGINATE); + if (rmap) { + if (!peer->default_rmap[afi][safi].name + || strcmp(rmap, peer->default_rmap[afi][safi].name) != 0) { if (peer->default_rmap[afi][safi].name) XFREE(MTYPE_ROUTE_MAP_NAME, peer->default_rmap[afi][safi].name); + peer->default_rmap[afi][safi].name = XSTRDUP(MTYPE_ROUTE_MAP_NAME, rmap); peer->default_rmap[afi][safi].map = route_map_lookup_by_name(rmap); } + } else if (!rmap) { + if (peer->default_rmap[afi][safi].name) + XFREE(MTYPE_ROUTE_MAP_NAME, + peer->default_rmap[afi][safi].name); + + peer->default_rmap[afi][safi].name = NULL; + peer->default_rmap[afi][safi].map = NULL; } + /* Check if handling a regular peer. */ if (!CHECK_FLAG(peer->sflags, PEER_STATUS_GROUP)) { + /* Update peer route announcements. */ if (peer->status == Established && peer->afc_nego[afi][safi]) { update_group_adjust_peer(peer_af_find(peer, afi, safi)); bgp_default_originate(peer, afi, safi, 0); bgp_announce_route(peer, afi, safi); } + + /* Skip peer-group mechanics for regular peers. */ return 0; } - /* peer-group member updates. */ - group = peer->group; - for (ALL_LIST_ELEMENTS(group->peer, node, nnode, peer)) { - SET_FLAG(peer->af_flags[afi][safi], - PEER_FLAG_DEFAULT_ORIGINATE); + /* + * Set flag and configuration on all peer-group members, unless they are + * explicitely overriding peer-group configuration. + */ + for (ALL_LIST_ELEMENTS(peer->group->peer, node, nnode, member)) { + /* Skip peers with overridden configuration. */ + if (CHECK_FLAG(member->af_flags_override[afi][safi], + PEER_FLAG_DEFAULT_ORIGINATE)) + continue; + /* Set flag and configuration on peer-group member. */ + SET_FLAG(member->af_flags[afi][safi], + PEER_FLAG_DEFAULT_ORIGINATE); if (rmap) { - if (peer->default_rmap[afi][safi].name) + if (member->default_rmap[afi][safi].name) XFREE(MTYPE_ROUTE_MAP_NAME, - peer->default_rmap[afi][safi].name); - peer->default_rmap[afi][safi].name = + member->default_rmap[afi][safi].name); + + member->default_rmap[afi][safi].name = XSTRDUP(MTYPE_ROUTE_MAP_NAME, rmap); - peer->default_rmap[afi][safi].map = + member->default_rmap[afi][safi].map = route_map_lookup_by_name(rmap); } - if (peer->status == Established && peer->afc_nego[afi][safi]) { - update_group_adjust_peer(peer_af_find(peer, afi, safi)); - bgp_default_originate(peer, afi, safi, 0); - bgp_announce_route(peer, afi, safi); + /* Update peer route announcements. */ + if (member->status == Established + && member->afc_nego[afi][safi]) { + update_group_adjust_peer( + peer_af_find(member, afi, safi)); + bgp_default_originate(member, afi, safi, 0); + bgp_announce_route(member, afi, safi); } } + return 0; } int peer_default_originate_unset(struct peer *peer, afi_t afi, safi_t safi) { - struct peer_group *group; + struct peer *member; struct listnode *node, *nnode; - if (CHECK_FLAG(peer->af_flags[afi][safi], - PEER_FLAG_DEFAULT_ORIGINATE)) { - UNSET_FLAG(peer->af_flags[afi][safi], - PEER_FLAG_DEFAULT_ORIGINATE); - + /* Inherit configuration from peer-group if peer is member. */ + if (peer_group_active(peer)) { + peer_af_flag_inherit(peer, afi, safi, + PEER_FLAG_DEFAULT_ORIGINATE); + PEER_STR_ATTR_INHERIT(MTYPE_ROUTE_MAP_NAME, peer, + default_rmap[afi][safi].name); + PEER_ATTR_INHERIT(peer, default_rmap[afi][safi].map); + } else { + /* Otherwise remove flag and configuration from peer. */ + peer_af_flag_unset(peer, afi, safi, + PEER_FLAG_DEFAULT_ORIGINATE); if (peer->default_rmap[afi][safi].name) XFREE(MTYPE_ROUTE_MAP_NAME, peer->default_rmap[afi][safi].name); @@ -4597,33 +4705,46 @@ int peer_default_originate_unset(struct peer *peer, afi_t afi, safi_t safi) peer->default_rmap[afi][safi].map = NULL; } + /* Check if handling a regular peer. */ if (!CHECK_FLAG(peer->sflags, PEER_STATUS_GROUP)) { + /* Update peer route announcements. */ if (peer->status == Established && peer->afc_nego[afi][safi]) { update_group_adjust_peer(peer_af_find(peer, afi, safi)); bgp_default_originate(peer, afi, safi, 1); bgp_announce_route(peer, afi, safi); } + + /* Skip peer-group mechanics for regular peers. */ return 0; } - /* peer-group member updates. */ - group = peer->group; - for (ALL_LIST_ELEMENTS(group->peer, node, nnode, peer)) { + /* + * Remove flag and configuration from all peer-group members, unless + * they are explicitely overriding peer-group configuration. + */ + for (ALL_LIST_ELEMENTS(peer->group->peer, node, nnode, member)) { + /* Skip peers with overridden configuration. */ + if (CHECK_FLAG(member->af_flags_override[afi][safi], + PEER_FLAG_DEFAULT_ORIGINATE)) + continue; + + /* Remove flag and configuration on peer-group member. */ UNSET_FLAG(peer->af_flags[afi][safi], PEER_FLAG_DEFAULT_ORIGINATE); - if (peer->default_rmap[afi][safi].name) XFREE(MTYPE_ROUTE_MAP_NAME, peer->default_rmap[afi][safi].name); peer->default_rmap[afi][safi].name = NULL; peer->default_rmap[afi][safi].map = NULL; + /* Update peer route announcements. */ if (peer->status == Established && peer->afc_nego[afi][safi]) { update_group_adjust_peer(peer_af_find(peer, afi, safi)); bgp_default_originate(peer, afi, safi, 1); bgp_announce_route(peer, afi, safi); } } + return 0; } @@ -4668,81 +4789,87 @@ static void peer_on_policy_change(struct peer *peer, afi_t afi, safi_t safi, /* neighbor weight. */ int peer_weight_set(struct peer *peer, afi_t afi, safi_t safi, uint16_t weight) { - struct peer_group *group; + struct peer *member; struct listnode *node, *nnode; + /* Set flag and configuration on peer. */ + peer_af_flag_set(peer, afi, safi, PEER_FLAG_WEIGHT); if (peer->weight[afi][safi] != weight) { peer->weight[afi][safi] = weight; - SET_FLAG(peer->af_flags[afi][safi], PEER_FLAG_WEIGHT); peer_on_policy_change(peer, afi, safi, 0); } + /* Skip peer-group mechanics for regular peers. */ if (!CHECK_FLAG(peer->sflags, PEER_STATUS_GROUP)) return 0; - /* peer-group member updates. */ - group = peer->group; - for (ALL_LIST_ELEMENTS(group->peer, node, nnode, peer)) { - if (peer->weight[afi][safi] != weight) { - peer->weight[afi][safi] = weight; - SET_FLAG(peer->af_flags[afi][safi], PEER_FLAG_WEIGHT); - peer_on_policy_change(peer, afi, safi, 0); + /* + * Set flag and configuration on all peer-group members, unless they are + * explicitely overriding peer-group configuration. + */ + for (ALL_LIST_ELEMENTS(peer->group->peer, node, nnode, member)) { + /* Skip peers with overridden configuration. */ + if (CHECK_FLAG(member->af_flags_override[afi][safi], + PEER_FLAG_WEIGHT)) + continue; + + /* Set flag and configuration on peer-group member. */ + SET_FLAG(member->af_flags[afi][safi], PEER_FLAG_WEIGHT); + if (member->weight[afi][safi] != weight) { + member->weight[afi][safi] = weight; + peer_on_policy_change(member, afi, safi, 0); } } + return 0; } int peer_weight_unset(struct peer *peer, afi_t afi, safi_t safi) { - struct peer_group *group; + struct peer *member; struct listnode *node, *nnode; - /* not the peer-group itself but a peer in a peer-group */ + if (!CHECK_FLAG(peer->af_flags[afi][safi], PEER_FLAG_WEIGHT)) + return 0; + + /* Inherit configuration from peer-group if peer is member. */ if (peer_group_active(peer)) { - group = peer->group; + peer_af_flag_inherit(peer, afi, safi, PEER_FLAG_WEIGHT); + PEER_ATTR_INHERIT(peer, weight[afi][safi]); - /* inherit weight from the peer-group */ - if (CHECK_FLAG(group->conf->af_flags[afi][safi], - PEER_FLAG_WEIGHT)) { - peer->weight[afi][safi] = - group->conf->weight[afi][safi]; - peer_af_flag_set(peer, afi, safi, PEER_FLAG_WEIGHT); - peer_on_policy_change(peer, afi, safi, 0); - } else { - if (CHECK_FLAG(peer->af_flags[afi][safi], - PEER_FLAG_WEIGHT)) { - peer->weight[afi][safi] = 0; - peer_af_flag_unset(peer, afi, safi, - PEER_FLAG_WEIGHT); - peer_on_policy_change(peer, afi, safi, 0); - } - } + peer_on_policy_change(peer, afi, safi, 0); + return 0; } - else { - if (CHECK_FLAG(peer->af_flags[afi][safi], PEER_FLAG_WEIGHT)) { - peer->weight[afi][safi] = 0; - peer_af_flag_unset(peer, afi, safi, PEER_FLAG_WEIGHT); - peer_on_policy_change(peer, afi, safi, 0); - } + /* Remove flag and configuration from peer. */ + peer_af_flag_unset(peer, afi, safi, PEER_FLAG_WEIGHT); + peer->weight[afi][safi] = 0; + peer_on_policy_change(peer, afi, safi, 0); - /* peer-group member updates. */ - group = peer->group; + /* Skip peer-group mechanics for regular peers. */ + if (!CHECK_FLAG(peer->sflags, PEER_STATUS_GROUP)) + return 0; - if (group) { - for (ALL_LIST_ELEMENTS(group->peer, node, nnode, - peer)) { - if (CHECK_FLAG(peer->af_flags[afi][safi], - PEER_FLAG_WEIGHT)) { - peer->weight[afi][safi] = 0; - peer_af_flag_unset(peer, afi, safi, - PEER_FLAG_WEIGHT); - peer_on_policy_change(peer, afi, safi, - 0); - } - } - } + /* + * Remove flag and configuration from all peer-group members, unless + * they are explicitely overriding peer-group configuration. + */ + for (ALL_LIST_ELEMENTS(peer->group->peer, node, nnode, member)) { + /* Skip peers with overridden configuration. */ + if (CHECK_FLAG(member->af_flags_override[afi][safi], + PEER_FLAG_WEIGHT)) + continue; + + /* Skip peers where flag is already disabled. */ + if (!CHECK_FLAG(member->af_flags[afi][safi], PEER_FLAG_WEIGHT)) + continue; + + /* Remove flag and configuration on peer-group member. */ + UNSET_FLAG(member->af_flags[afi][safi], PEER_FLAG_WEIGHT); + member->weight[afi][safi] = 0; + peer_on_policy_change(member, afi, safi, 0); } + return 0; } @@ -4971,68 +5098,67 @@ void peer_interface_unset(struct peer *peer) int peer_allowas_in_set(struct peer *peer, afi_t afi, safi_t safi, int allow_num, int origin) { - struct peer_group *group; + struct peer *member; struct listnode *node, *nnode; + if (!origin && (allow_num < 1 || allow_num > 10)) + return BGP_ERR_INVALID_VALUE; + + /* Set flag and configuration on peer. */ + peer_af_flag_set(peer, afi, safi, PEER_FLAG_ALLOWAS_IN); if (origin) { - if (peer->allowas_in[afi][safi] - || CHECK_FLAG(peer->af_flags[afi][safi], - PEER_FLAG_ALLOWAS_IN) + if (peer->allowas_in[afi][safi] != 0 || !CHECK_FLAG(peer->af_flags[afi][safi], PEER_FLAG_ALLOWAS_IN_ORIGIN)) { - peer->allowas_in[afi][safi] = 0; - peer_af_flag_unset(peer, afi, safi, - PEER_FLAG_ALLOWAS_IN); peer_af_flag_set(peer, afi, safi, PEER_FLAG_ALLOWAS_IN_ORIGIN); + peer->allowas_in[afi][safi] = 0; peer_on_policy_change(peer, afi, safi, 0); } - - if (!CHECK_FLAG(peer->sflags, PEER_STATUS_GROUP)) - return 0; - - group = peer->group; - for (ALL_LIST_ELEMENTS(group->peer, node, nnode, peer)) { - if (peer->allowas_in[afi][safi] - || CHECK_FLAG(peer->af_flags[afi][safi], - PEER_FLAG_ALLOWAS_IN) - || !CHECK_FLAG(peer->af_flags[afi][safi], - PEER_FLAG_ALLOWAS_IN_ORIGIN)) { - peer->allowas_in[afi][safi] = 0; - peer_af_flag_unset(peer, afi, safi, - PEER_FLAG_ALLOWAS_IN); - peer_af_flag_set(peer, afi, safi, - PEER_FLAG_ALLOWAS_IN_ORIGIN); - peer_on_policy_change(peer, afi, safi, 0); - } - } } else { - if (allow_num < 1 || allow_num > 10) - return BGP_ERR_INVALID_VALUE; - if (peer->allowas_in[afi][safi] != allow_num || CHECK_FLAG(peer->af_flags[afi][safi], PEER_FLAG_ALLOWAS_IN_ORIGIN)) { - peer->allowas_in[afi][safi] = allow_num; - peer_af_flag_set(peer, afi, safi, PEER_FLAG_ALLOWAS_IN); + peer_af_flag_unset(peer, afi, safi, PEER_FLAG_ALLOWAS_IN_ORIGIN); + peer->allowas_in[afi][safi] = allow_num; peer_on_policy_change(peer, afi, safi, 0); } + } - if (!CHECK_FLAG(peer->sflags, PEER_STATUS_GROUP)) - return 0; + /* Skip peer-group mechanics for regular peers. */ + if (!CHECK_FLAG(peer->sflags, PEER_STATUS_GROUP)) + return 0; - group = peer->group; - for (ALL_LIST_ELEMENTS(group->peer, node, nnode, peer)) { - if (peer->allowas_in[afi][safi] != allow_num - || CHECK_FLAG(peer->af_flags[afi][safi], + /* + * Set flag and configuration on all peer-group members, unless + * they are explicitely overriding peer-group configuration. + */ + for (ALL_LIST_ELEMENTS(peer->group->peer, node, nnode, member)) { + /* Skip peers with overridden configuration. */ + if (CHECK_FLAG(member->af_flags_override[afi][safi], + PEER_FLAG_ALLOWAS_IN)) + continue; + + /* Set flag and configuration on peer-group member. */ + SET_FLAG(member->af_flags[afi][safi], PEER_FLAG_ALLOWAS_IN); + if (origin) { + if (member->allowas_in[afi][safi] != 0 + || !CHECK_FLAG(member->af_flags[afi][safi], + PEER_FLAG_ALLOWAS_IN_ORIGIN)) { + SET_FLAG(member->af_flags[afi][safi], + PEER_FLAG_ALLOWAS_IN_ORIGIN); + member->allowas_in[afi][safi] = 0; + peer_on_policy_change(peer, afi, safi, 0); + } + } else { + if (member->allowas_in[afi][safi] != allow_num + || CHECK_FLAG(member->af_flags[afi][safi], PEER_FLAG_ALLOWAS_IN_ORIGIN)) { - peer->allowas_in[afi][safi] = allow_num; - peer_af_flag_set(peer, afi, safi, - PEER_FLAG_ALLOWAS_IN); - peer_af_flag_unset(peer, afi, safi, - PEER_FLAG_ALLOWAS_IN_ORIGIN); + UNSET_FLAG(member->af_flags[afi][safi], + PEER_FLAG_ALLOWAS_IN_ORIGIN); + member->allowas_in[afi][safi] = allow_num; peer_on_policy_change(peer, afi, safi, 0); } } @@ -5043,38 +5169,55 @@ int peer_allowas_in_set(struct peer *peer, afi_t afi, safi_t safi, int peer_allowas_in_unset(struct peer *peer, afi_t afi, safi_t safi) { - struct peer_group *group; - struct peer *tmp_peer; + struct peer *member; struct listnode *node, *nnode; - /* If this is a peer-group we must first clear the flags for all of the - * peer-group members - */ - if (CHECK_FLAG(peer->sflags, PEER_STATUS_GROUP)) { - group = peer->group; - for (ALL_LIST_ELEMENTS(group->peer, node, nnode, tmp_peer)) { - if (CHECK_FLAG(tmp_peer->af_flags[afi][safi], - PEER_FLAG_ALLOWAS_IN) - || CHECK_FLAG(tmp_peer->af_flags[afi][safi], - PEER_FLAG_ALLOWAS_IN_ORIGIN)) { - tmp_peer->allowas_in[afi][safi] = 0; - peer_af_flag_unset(tmp_peer, afi, safi, - PEER_FLAG_ALLOWAS_IN); - peer_af_flag_unset(tmp_peer, afi, safi, - PEER_FLAG_ALLOWAS_IN_ORIGIN); - peer_on_policy_change(tmp_peer, afi, safi, 0); - } - } + /* Skip peer if flag is already disabled. */ + if (!CHECK_FLAG(peer->af_flags[afi][safi], PEER_FLAG_ALLOWAS_IN)) + return 0; + + /* Inherit configuration from peer-group if peer is member. */ + if (peer_group_active(peer)) { + peer_af_flag_inherit(peer, afi, safi, PEER_FLAG_ALLOWAS_IN); + peer_af_flag_inherit(peer, afi, safi, + PEER_FLAG_ALLOWAS_IN_ORIGIN); + PEER_ATTR_INHERIT(peer, allowas_in[afi][safi]); + peer_on_policy_change(peer, afi, safi, 0); + + return 0; } - if (CHECK_FLAG(peer->af_flags[afi][safi], PEER_FLAG_ALLOWAS_IN) - || CHECK_FLAG(peer->af_flags[afi][safi], - PEER_FLAG_ALLOWAS_IN_ORIGIN)) { - peer->allowas_in[afi][safi] = 0; - peer_af_flag_unset(peer, afi, safi, PEER_FLAG_ALLOWAS_IN); - peer_af_flag_unset(peer, afi, safi, - PEER_FLAG_ALLOWAS_IN_ORIGIN); - peer_on_policy_change(peer, afi, safi, 0); + /* Remove flag and configuration from peer. */ + peer_af_flag_unset(peer, afi, safi, PEER_FLAG_ALLOWAS_IN); + peer_af_flag_unset(peer, afi, safi, PEER_FLAG_ALLOWAS_IN_ORIGIN); + peer->allowas_in[afi][safi] = 0; + peer_on_policy_change(peer, afi, safi, 0); + + /* Skip peer-group mechanics if handling a regular peer. */ + if (!CHECK_FLAG(peer->sflags, PEER_STATUS_GROUP)) + return 0; + + /* + * Remove flags and configuration from all peer-group members, unless + * they are explicitely overriding peer-group configuration. + */ + for (ALL_LIST_ELEMENTS(peer->group->peer, node, nnode, member)) { + /* Skip peers with overridden configuration. */ + if (CHECK_FLAG(member->af_flags_override[afi][safi], + PEER_FLAG_ALLOWAS_IN)) + continue; + + /* Skip peers where flag is already disabled. */ + if (!CHECK_FLAG(member->af_flags[afi][safi], + PEER_FLAG_ALLOWAS_IN)) + continue; + + /* Remove flags and configuration on peer-group member. */ + UNSET_FLAG(member->af_flags[afi][safi], PEER_FLAG_ALLOWAS_IN); + UNSET_FLAG(member->af_flags[afi][safi], + PEER_FLAG_ALLOWAS_IN_ORIGIN); + member->allowas_in[afi][safi] = 0; + peer_on_policy_change(member, afi, safi, 0); } return 0; @@ -5302,40 +5445,55 @@ int peer_password_unset(struct peer *peer) int peer_distribute_set(struct peer *peer, afi_t afi, safi_t safi, int direct, const char *name) { + struct peer *member; struct bgp_filter *filter; - struct peer_group *group; struct listnode *node, *nnode; if (direct != FILTER_IN && direct != FILTER_OUT) return BGP_ERR_INVALID_VALUE; + /* Set configuration on peer. */ filter = &peer->filter[afi][safi]; - if (filter->plist[direct].name) return BGP_ERR_PEER_FILTER_CONFLICT; - if (filter->dlist[direct].name) XFREE(MTYPE_BGP_FILTER_NAME, filter->dlist[direct].name); filter->dlist[direct].name = XSTRDUP(MTYPE_BGP_FILTER_NAME, name); filter->dlist[direct].alist = access_list_lookup(afi, name); + /* Check if handling a regular peer. */ if (!CHECK_FLAG(peer->sflags, PEER_STATUS_GROUP)) { + /* Set override-flag and process peer route updates. */ + SET_FLAG(peer->filter_override[afi][safi][direct], + PEER_FT_DISTRIBUTE_LIST); peer_on_policy_change(peer, afi, safi, (direct == FILTER_OUT) ? 1 : 0); + + /* Skip peer-group mechanics for regular peers. */ return 0; } - group = peer->group; - for (ALL_LIST_ELEMENTS(group->peer, node, nnode, peer)) { - filter = &peer->filter[afi][safi]; + /* + * Set configuration on all peer-group members, un less they are + * explicitely overriding peer-group configuration. + */ + for (ALL_LIST_ELEMENTS(peer->group->peer, node, nnode, member)) { + /* Skip peers with overridden configuration. */ + if (CHECK_FLAG(member->filter_override[afi][safi][direct], + PEER_FT_DISTRIBUTE_LIST)) + continue; + /* Set configuration on peer-group member. */ + filter = &member->filter[afi][safi]; if (filter->dlist[direct].name) XFREE(MTYPE_BGP_FILTER_NAME, filter->dlist[direct].name); filter->dlist[direct].name = XSTRDUP(MTYPE_BGP_FILTER_NAME, name); filter->dlist[direct].alist = access_list_lookup(afi, name); - peer_on_policy_change(peer, afi, safi, + + /* Process peer route updates. */ + peer_on_policy_change(member, afi, safi, (direct == FILTER_OUT) ? 1 : 0); } @@ -5344,57 +5502,63 @@ int peer_distribute_set(struct peer *peer, afi_t afi, safi_t safi, int direct, int peer_distribute_unset(struct peer *peer, afi_t afi, safi_t safi, int direct) { + struct peer *member; struct bgp_filter *filter; - struct bgp_filter *gfilter; - struct peer_group *group; struct listnode *node, *nnode; if (direct != FILTER_IN && direct != FILTER_OUT) return BGP_ERR_INVALID_VALUE; - filter = &peer->filter[afi][safi]; + /* Unset override-flag unconditionally. */ + UNSET_FLAG(peer->filter_override[afi][safi][direct], + PEER_FT_DISTRIBUTE_LIST); - /* apply peer-group filter */ + /* Inherit configuration from peer-group if peer is member. */ if (peer_group_active(peer)) { - gfilter = &peer->group->conf->filter[afi][safi]; - - if (gfilter->dlist[direct].name) { - if (filter->dlist[direct].name) - XFREE(MTYPE_BGP_FILTER_NAME, - filter->dlist[direct].name); - filter->dlist[direct].name = - XSTRDUP(MTYPE_BGP_FILTER_NAME, - gfilter->dlist[direct].name); - filter->dlist[direct].alist = - gfilter->dlist[direct].alist; - peer_on_policy_change(peer, afi, safi, - (direct == FILTER_OUT) ? 1 : 0); - return 0; - } - } - - if (filter->dlist[direct].name) - XFREE(MTYPE_BGP_FILTER_NAME, filter->dlist[direct].name); - filter->dlist[direct].name = NULL; - filter->dlist[direct].alist = NULL; - - if (!CHECK_FLAG(peer->sflags, PEER_STATUS_GROUP)) { - peer_on_policy_change(peer, afi, safi, - (direct == FILTER_OUT) ? 1 : 0); - return 0; - } - - group = peer->group; - for (ALL_LIST_ELEMENTS(group->peer, node, nnode, peer)) { + PEER_STR_ATTR_INHERIT(MTYPE_BGP_FILTER_NAME, peer, + filter[afi][safi].dlist[direct].name); + PEER_ATTR_INHERIT(peer, filter[afi][safi].dlist[direct].alist); + } else { + /* Otherwise remove configuration from peer. */ filter = &peer->filter[afi][safi]; - if (filter->dlist[direct].name) XFREE(MTYPE_BGP_FILTER_NAME, filter->dlist[direct].name); filter->dlist[direct].name = NULL; filter->dlist[direct].alist = NULL; + } + + /* Check if handling a regular peer. */ + if (!CHECK_FLAG(peer->sflags, PEER_STATUS_GROUP)) { + /* Process peer route updates. */ peer_on_policy_change(peer, afi, safi, (direct == FILTER_OUT) ? 1 : 0); + + /* Skip peer-group mechanics for regular peers. */ + return 0; + } + + /* + * Remove configuration on all peer-group members, unless they are + * explicitely overriding peer-group configuration. + */ + for (ALL_LIST_ELEMENTS(peer->group->peer, node, nnode, member)) { + /* Skip peers with overridden configuration. */ + if (CHECK_FLAG(member->filter_override[afi][safi][direct], + PEER_FT_DISTRIBUTE_LIST)) + continue; + + /* Remove configuration on peer-group member. */ + filter = &member->filter[afi][safi]; + if (filter->dlist[direct].name) + XFREE(MTYPE_BGP_FILTER_NAME, + filter->dlist[direct].name); + filter->dlist[direct].name = NULL; + filter->dlist[direct].alist = NULL; + + /* Process peer route updates. */ + peer_on_policy_change(member, afi, safi, + (direct == FILTER_OUT) ? 1 : 0); } return 0; @@ -5463,99 +5627,121 @@ static void peer_distribute_update(struct access_list *access) int peer_prefix_list_set(struct peer *peer, afi_t afi, safi_t safi, int direct, const char *name) { + struct peer *member; struct bgp_filter *filter; - struct peer_group *group; struct listnode *node, *nnode; if (direct != FILTER_IN && direct != FILTER_OUT) return BGP_ERR_INVALID_VALUE; + /* Set configuration on peer. */ filter = &peer->filter[afi][safi]; - if (filter->dlist[direct].name) return BGP_ERR_PEER_FILTER_CONFLICT; - if (filter->plist[direct].name) XFREE(MTYPE_BGP_FILTER_NAME, filter->plist[direct].name); filter->plist[direct].name = XSTRDUP(MTYPE_BGP_FILTER_NAME, name); filter->plist[direct].plist = prefix_list_lookup(afi, name); + /* Check if handling a regular peer. */ if (!CHECK_FLAG(peer->sflags, PEER_STATUS_GROUP)) { + /* Set override-flag and process peer route updates. */ + SET_FLAG(peer->filter_override[afi][safi][direct], + PEER_FT_PREFIX_LIST); peer_on_policy_change(peer, afi, safi, (direct == FILTER_OUT) ? 1 : 0); + + /* Skip peer-group mechanics for regular peers. */ return 0; } - group = peer->group; - for (ALL_LIST_ELEMENTS(group->peer, node, nnode, peer)) { - filter = &peer->filter[afi][safi]; + /* + * Set configuration on all peer-group members, unless they are + * explicitely overriding peer-group configuration. + */ + for (ALL_LIST_ELEMENTS(peer->group->peer, node, nnode, member)) { + /* Skip peers with overridden configuration. */ + if (CHECK_FLAG(member->filter_override[afi][safi][direct], + PEER_FT_PREFIX_LIST)) + continue; + /* Set configuration on peer-group member. */ + filter = &member->filter[afi][safi]; if (filter->plist[direct].name) XFREE(MTYPE_BGP_FILTER_NAME, filter->plist[direct].name); filter->plist[direct].name = XSTRDUP(MTYPE_BGP_FILTER_NAME, name); filter->plist[direct].plist = prefix_list_lookup(afi, name); - peer_on_policy_change(peer, afi, safi, + + /* Process peer route updates. */ + peer_on_policy_change(member, afi, safi, (direct == FILTER_OUT) ? 1 : 0); } + return 0; } int peer_prefix_list_unset(struct peer *peer, afi_t afi, safi_t safi, int direct) { + struct peer *member; struct bgp_filter *filter; - struct bgp_filter *gfilter; - struct peer_group *group; struct listnode *node, *nnode; if (direct != FILTER_IN && direct != FILTER_OUT) return BGP_ERR_INVALID_VALUE; - filter = &peer->filter[afi][safi]; + /* Unset override-flag unconditionally. */ + UNSET_FLAG(peer->filter_override[afi][safi][direct], + PEER_FT_PREFIX_LIST); - /* apply peer-group filter */ + /* Inherit configuration from peer-group if peer is member. */ if (peer_group_active(peer)) { - gfilter = &peer->group->conf->filter[afi][safi]; - - if (gfilter->plist[direct].name) { - if (filter->plist[direct].name) - XFREE(MTYPE_BGP_FILTER_NAME, - filter->plist[direct].name); - filter->plist[direct].name = - XSTRDUP(MTYPE_BGP_FILTER_NAME, - gfilter->plist[direct].name); - filter->plist[direct].plist = - gfilter->plist[direct].plist; - peer_on_policy_change(peer, afi, safi, - (direct == FILTER_OUT) ? 1 : 0); - return 0; - } - } - - if (filter->plist[direct].name) - XFREE(MTYPE_BGP_FILTER_NAME, filter->plist[direct].name); - filter->plist[direct].name = NULL; - filter->plist[direct].plist = NULL; - - if (!CHECK_FLAG(peer->sflags, PEER_STATUS_GROUP)) { - peer_on_policy_change(peer, afi, safi, - (direct == FILTER_OUT) ? 1 : 0); - return 0; - } - - group = peer->group; - for (ALL_LIST_ELEMENTS(group->peer, node, nnode, peer)) { + PEER_STR_ATTR_INHERIT(MTYPE_BGP_FILTER_NAME, peer, + filter[afi][safi].plist[direct].name); + PEER_ATTR_INHERIT(peer, filter[afi][safi].plist[direct].plist); + } else { + /* Otherwise remove configuration from peer. */ filter = &peer->filter[afi][safi]; - if (filter->plist[direct].name) XFREE(MTYPE_BGP_FILTER_NAME, filter->plist[direct].name); filter->plist[direct].name = NULL; filter->plist[direct].plist = NULL; + } + + /* Check if handling a regular peer. */ + if (!CHECK_FLAG(peer->sflags, PEER_STATUS_GROUP)) { + /* Process peer route updates. */ peer_on_policy_change(peer, afi, safi, (direct == FILTER_OUT) ? 1 : 0); + + /* Skip peer-group mechanics for regular peers. */ + return 0; + } + + /* + * Remove configuration on all peer-group members, unless they are + * explicitely overriding peer-group configuration. + */ + for (ALL_LIST_ELEMENTS(peer->group->peer, node, nnode, member)) { + /* Skip peers with overridden configuration. */ + if (CHECK_FLAG(member->filter_override[afi][safi][direct], + PEER_FT_PREFIX_LIST)) + continue; + + /* Remove configuration on peer-group member. */ + filter = &member->filter[afi][safi]; + if (filter->plist[direct].name) + XFREE(MTYPE_BGP_FILTER_NAME, + filter->plist[direct].name); + filter->plist[direct].name = NULL; + filter->plist[direct].plist = NULL; + + /* Process peer route updates. */ + peer_on_policy_change(member, afi, safi, + (direct == FILTER_OUT) ? 1 : 0); } return 0; @@ -5625,95 +5811,119 @@ static void peer_prefix_list_update(struct prefix_list *plist) int peer_aslist_set(struct peer *peer, afi_t afi, safi_t safi, int direct, const char *name) { + struct peer *member; struct bgp_filter *filter; - struct peer_group *group; struct listnode *node, *nnode; if (direct != FILTER_IN && direct != FILTER_OUT) return BGP_ERR_INVALID_VALUE; + /* Set configuration on peer. */ filter = &peer->filter[afi][safi]; - if (filter->aslist[direct].name) XFREE(MTYPE_BGP_FILTER_NAME, filter->aslist[direct].name); filter->aslist[direct].name = XSTRDUP(MTYPE_BGP_FILTER_NAME, name); filter->aslist[direct].aslist = as_list_lookup(name); + /* Check if handling a regular peer. */ if (!CHECK_FLAG(peer->sflags, PEER_STATUS_GROUP)) { + /* Set override-flag and process peer route updates. */ + SET_FLAG(peer->filter_override[afi][safi][direct], + PEER_FT_FILTER_LIST); peer_on_policy_change(peer, afi, safi, (direct == FILTER_OUT) ? 1 : 0); + + /* Skip peer-group mechanics for regular peers. */ return 0; } - group = peer->group; - for (ALL_LIST_ELEMENTS(group->peer, node, nnode, peer)) { - filter = &peer->filter[afi][safi]; + /* + * Set configuration on all peer-group members, unless they are + * explicitely overriding peer-group configuration. + */ + for (ALL_LIST_ELEMENTS(peer->group->peer, node, nnode, member)) { + /* Skip peers with overridden configuration. */ + if (CHECK_FLAG(member->filter_override[afi][safi][direct], + PEER_FT_FILTER_LIST)) + continue; + /* Set configuration on peer-group member. */ + filter = &member->filter[afi][safi]; if (filter->aslist[direct].name) XFREE(MTYPE_BGP_FILTER_NAME, filter->aslist[direct].name); filter->aslist[direct].name = XSTRDUP(MTYPE_BGP_FILTER_NAME, name); filter->aslist[direct].aslist = as_list_lookup(name); - peer_on_policy_change(peer, afi, safi, + + /* Process peer route updates. */ + peer_on_policy_change(member, afi, safi, (direct == FILTER_OUT) ? 1 : 0); } + return 0; } int peer_aslist_unset(struct peer *peer, afi_t afi, safi_t safi, int direct) { + struct peer *member; struct bgp_filter *filter; - struct bgp_filter *gfilter; - struct peer_group *group; struct listnode *node, *nnode; if (direct != FILTER_IN && direct != FILTER_OUT) return BGP_ERR_INVALID_VALUE; - filter = &peer->filter[afi][safi]; + /* Unset override-flag unconditionally. */ + UNSET_FLAG(peer->filter_override[afi][safi][direct], + PEER_FT_FILTER_LIST); - /* apply peer-group filter */ + /* Inherit configuration from peer-group if peer is member. */ if (peer_group_active(peer)) { - gfilter = &peer->group->conf->filter[afi][safi]; - - if (gfilter->aslist[direct].name) { - if (filter->aslist[direct].name) - XFREE(MTYPE_BGP_FILTER_NAME, - filter->aslist[direct].name); - filter->aslist[direct].name = - XSTRDUP(MTYPE_BGP_FILTER_NAME, - gfilter->aslist[direct].name); - filter->aslist[direct].aslist = - gfilter->aslist[direct].aslist; - peer_on_policy_change(peer, afi, safi, - (direct == FILTER_OUT) ? 1 : 0); - return 0; - } - } - - if (filter->aslist[direct].name) - XFREE(MTYPE_BGP_FILTER_NAME, filter->aslist[direct].name); - filter->aslist[direct].name = NULL; - filter->aslist[direct].aslist = NULL; - - if (!CHECK_FLAG(peer->sflags, PEER_STATUS_GROUP)) { - peer_on_policy_change(peer, afi, safi, - (direct == FILTER_OUT) ? 1 : 0); - return 0; - } - - group = peer->group; - for (ALL_LIST_ELEMENTS(group->peer, node, nnode, peer)) { + PEER_STR_ATTR_INHERIT(MTYPE_BGP_FILTER_NAME, peer, + filter[afi][safi].aslist[direct].name); + PEER_ATTR_INHERIT(peer, + filter[afi][safi].aslist[direct].aslist); + } else { + /* Otherwise remove configuration from peer. */ filter = &peer->filter[afi][safi]; - if (filter->aslist[direct].name) XFREE(MTYPE_BGP_FILTER_NAME, filter->aslist[direct].name); filter->aslist[direct].name = NULL; filter->aslist[direct].aslist = NULL; + } + + /* Check if handling a regular peer. */ + if (!CHECK_FLAG(peer->sflags, PEER_STATUS_GROUP)) { + /* Process peer route updates. */ peer_on_policy_change(peer, afi, safi, (direct == FILTER_OUT) ? 1 : 0); + + /* Skip peer-group mechanics for regular peers. */ + return 0; + } + + /* + * Remove configuration on all peer-group members, unless they are + * explicitely overriding peer-group configuration. + */ + for (ALL_LIST_ELEMENTS(peer->group->peer, node, nnode, member)) { + /* Skip peers with overridden configuration. */ + if (CHECK_FLAG(member->filter_override[afi][safi][direct], + PEER_FT_FILTER_LIST)) + continue; + + /* Remove configuration on peer-group member. */ + filter = &member->filter[afi][safi]; + if (filter->aslist[direct].name) + XFREE(MTYPE_BGP_FILTER_NAME, + filter->aslist[direct].name); + filter->aslist[direct].name = NULL; + filter->aslist[direct].aslist = NULL; + + /* Process peer route updates. */ + peer_on_policy_change(member, afi, safi, + (direct == FILTER_OUT) ? 1 : 0); } return 0; @@ -5789,36 +5999,51 @@ static void peer_aslist_del(const char *aslist_name) int peer_route_map_set(struct peer *peer, afi_t afi, safi_t safi, int direct, const char *name) { + struct peer *member; struct bgp_filter *filter; - struct peer_group *group; struct listnode *node, *nnode; if (direct != RMAP_IN && direct != RMAP_OUT) return BGP_ERR_INVALID_VALUE; + /* Set configuration on peer. */ filter = &peer->filter[afi][safi]; - if (filter->map[direct].name) XFREE(MTYPE_BGP_FILTER_NAME, filter->map[direct].name); - filter->map[direct].name = XSTRDUP(MTYPE_BGP_FILTER_NAME, name); filter->map[direct].map = route_map_lookup_by_name(name); + /* Check if handling a regular peer. */ if (!CHECK_FLAG(peer->sflags, PEER_STATUS_GROUP)) { + /* Set override-flag and process peer route updates. */ + SET_FLAG(peer->filter_override[afi][safi][direct], + PEER_FT_ROUTE_MAP); peer_on_policy_change(peer, afi, safi, (direct == RMAP_OUT) ? 1 : 0); + + /* Skip peer-group mechanics for regular peers. */ return 0; } - group = peer->group; - for (ALL_LIST_ELEMENTS(group->peer, node, nnode, peer)) { - filter = &peer->filter[afi][safi]; + /* + * Set configuration on all peer-group members, unless they are + * explicitely overriding peer-group configuration. + */ + for (ALL_LIST_ELEMENTS(peer->group->peer, node, nnode, member)) { + /* Skip peers with overridden configuration. */ + if (CHECK_FLAG(member->filter_override[afi][safi][direct], + PEER_FT_ROUTE_MAP)) + continue; + /* Set configuration on peer-group member. */ + filter = &member->filter[afi][safi]; if (filter->map[direct].name) XFREE(MTYPE_BGP_FILTER_NAME, filter->map[direct].name); filter->map[direct].name = XSTRDUP(MTYPE_BGP_FILTER_NAME, name); filter->map[direct].map = route_map_lookup_by_name(name); - peer_on_policy_change(peer, afi, safi, + + /* Process peer route updates. */ + peer_on_policy_change(member, afi, safi, (direct == RMAP_OUT) ? 1 : 0); } return 0; @@ -5827,56 +6052,62 @@ int peer_route_map_set(struct peer *peer, afi_t afi, safi_t safi, int direct, /* Unset route-map from the peer. */ int peer_route_map_unset(struct peer *peer, afi_t afi, safi_t safi, int direct) { + struct peer *member; struct bgp_filter *filter; - struct bgp_filter *gfilter; - struct peer_group *group; struct listnode *node, *nnode; if (direct != RMAP_IN && direct != RMAP_OUT) return BGP_ERR_INVALID_VALUE; - filter = &peer->filter[afi][safi]; + /* Unset override-flag unconditionally. */ + UNSET_FLAG(peer->filter_override[afi][safi][direct], PEER_FT_ROUTE_MAP); - /* apply peer-group filter */ + /* Inherit configuration from peer-group if peer is member. */ if (peer_group_active(peer)) { - gfilter = &peer->group->conf->filter[afi][safi]; - - if (gfilter->map[direct].name) { - if (filter->map[direct].name) - XFREE(MTYPE_BGP_FILTER_NAME, - filter->map[direct].name); - filter->map[direct].name = - XSTRDUP(MTYPE_BGP_FILTER_NAME, - gfilter->map[direct].name); - filter->map[direct].map = gfilter->map[direct].map; - peer_on_policy_change(peer, afi, safi, - (direct == RMAP_OUT) ? 1 : 0); - return 0; - } - } - - if (filter->map[direct].name) - XFREE(MTYPE_BGP_FILTER_NAME, filter->map[direct].name); - filter->map[direct].name = NULL; - filter->map[direct].map = NULL; - - if (!CHECK_FLAG(peer->sflags, PEER_STATUS_GROUP)) { - peer_on_policy_change(peer, afi, safi, - (direct == RMAP_OUT) ? 1 : 0); - return 0; - } - - group = peer->group; - for (ALL_LIST_ELEMENTS(group->peer, node, nnode, peer)) { + PEER_STR_ATTR_INHERIT(MTYPE_BGP_FILTER_NAME, peer, + filter[afi][safi].map[direct].name); + PEER_ATTR_INHERIT(peer, filter[afi][safi].map[direct].map); + } else { + /* Otherwise remove configuration from peer. */ filter = &peer->filter[afi][safi]; - if (filter->map[direct].name) XFREE(MTYPE_BGP_FILTER_NAME, filter->map[direct].name); filter->map[direct].name = NULL; filter->map[direct].map = NULL; + } + + /* Check if handling a regular peer. */ + if (!CHECK_FLAG(peer->sflags, PEER_STATUS_GROUP)) { + /* Process peer route updates. */ peer_on_policy_change(peer, afi, safi, (direct == RMAP_OUT) ? 1 : 0); + + /* Skip peer-group mechanics for regular peers. */ + return 0; } + + /* + * Remove configuration on all peer-group members, unless they are + * explicitely overriding peer-group configuration. + */ + for (ALL_LIST_ELEMENTS(peer->group->peer, node, nnode, member)) { + /* Skip peers with overridden configuration. */ + if (CHECK_FLAG(member->filter_override[afi][safi][direct], + PEER_FT_ROUTE_MAP)) + continue; + + /* Remove configuration on peer-group member. */ + filter = &member->filter[afi][safi]; + if (filter->map[direct].name) + XFREE(MTYPE_BGP_FILTER_NAME, filter->map[direct].name); + filter->map[direct].name = NULL; + filter->map[direct].map = NULL; + + /* Process peer route updates. */ + peer_on_policy_change(member, afi, safi, + (direct == RMAP_OUT) ? 1 : 0); + } + return 0; } @@ -5884,65 +6115,106 @@ int peer_route_map_unset(struct peer *peer, afi_t afi, safi_t safi, int direct) int peer_unsuppress_map_set(struct peer *peer, afi_t afi, safi_t safi, const char *name) { + struct peer *member; struct bgp_filter *filter; - struct peer_group *group; struct listnode *node, *nnode; + /* Set configuration on peer. */ filter = &peer->filter[afi][safi]; - if (filter->usmap.name) XFREE(MTYPE_BGP_FILTER_NAME, filter->usmap.name); - filter->usmap.name = XSTRDUP(MTYPE_BGP_FILTER_NAME, name); filter->usmap.map = route_map_lookup_by_name(name); + /* Check if handling a regular peer. */ if (!CHECK_FLAG(peer->sflags, PEER_STATUS_GROUP)) { + /* Set override-flag and process peer route updates. */ + SET_FLAG(peer->filter_override[afi][safi][0], + PEER_FT_UNSUPPRESS_MAP); peer_on_policy_change(peer, afi, safi, 1); + + /* Skip peer-group mechanics for regular peers. */ return 0; } - group = peer->group; - for (ALL_LIST_ELEMENTS(group->peer, node, nnode, peer)) { - filter = &peer->filter[afi][safi]; + /* + * Set configuration on all peer-group members, unless they are + * explicitely overriding peer-group configuration. + */ + for (ALL_LIST_ELEMENTS(peer->group->peer, node, nnode, member)) { + /* Skip peers with overridden configuration. */ + if (CHECK_FLAG(member->filter_override[afi][safi][0], + PEER_FT_UNSUPPRESS_MAP)) + continue; + /* Set configuration on peer-group member. */ + filter = &member->filter[afi][safi]; if (filter->usmap.name) XFREE(MTYPE_BGP_FILTER_NAME, filter->usmap.name); filter->usmap.name = XSTRDUP(MTYPE_BGP_FILTER_NAME, name); filter->usmap.map = route_map_lookup_by_name(name); - peer_on_policy_change(peer, afi, safi, 1); + + /* Process peer route updates. */ + peer_on_policy_change(member, afi, safi, 1); } + return 0; } /* Unset route-map from the peer. */ int peer_unsuppress_map_unset(struct peer *peer, afi_t afi, safi_t safi) { + struct peer *member; struct bgp_filter *filter; - struct peer_group *group; struct listnode *node, *nnode; - filter = &peer->filter[afi][safi]; + /* Unset override-flag unconditionally. */ + UNSET_FLAG(peer->filter_override[afi][safi][0], PEER_FT_UNSUPPRESS_MAP); - if (filter->usmap.name) - XFREE(MTYPE_BGP_FILTER_NAME, filter->usmap.name); - filter->usmap.name = NULL; - filter->usmap.map = NULL; - - if (!CHECK_FLAG(peer->sflags, PEER_STATUS_GROUP)) { - peer_on_policy_change(peer, afi, safi, 1); - return 0; - } - - group = peer->group; - for (ALL_LIST_ELEMENTS(group->peer, node, nnode, peer)) { + /* Inherit configuration from peer-group if peer is member. */ + if (peer_group_active(peer)) { + PEER_STR_ATTR_INHERIT(MTYPE_BGP_FILTER_NAME, peer, + filter[afi][safi].usmap.name); + PEER_ATTR_INHERIT(peer, filter[afi][safi].usmap.map); + } else { + /* Otherwise remove configuration from peer. */ filter = &peer->filter[afi][safi]; - if (filter->usmap.name) XFREE(MTYPE_BGP_FILTER_NAME, filter->usmap.name); filter->usmap.name = NULL; filter->usmap.map = NULL; - peer_on_policy_change(peer, afi, safi, 1); } + + /* Check if handling a regular peer. */ + if (!CHECK_FLAG(peer->sflags, PEER_STATUS_GROUP)) { + /* Process peer route updates. */ + peer_on_policy_change(peer, afi, safi, 1); + + /* Skip peer-group mechanics for regular peers. */ + return 0; + } + + /* + * Remove configuration on all peer-group members, unless they are + * explicitely overriding peer-group configuration. + */ + for (ALL_LIST_ELEMENTS(peer->group->peer, node, nnode, member)) { + /* Skip peers with overridden configuration. */ + if (CHECK_FLAG(member->filter_override[afi][safi][0], + PEER_FT_UNSUPPRESS_MAP)) + continue; + + /* Remove configuration on peer-group member. */ + filter = &member->filter[afi][safi]; + if (filter->usmap.name) + XFREE(MTYPE_BGP_FILTER_NAME, filter->usmap.name); + filter->usmap.name = NULL; + filter->usmap.map = NULL; + + /* Process peer route updates. */ + peer_on_policy_change(member, afi, safi, 1); + } + return 0; } @@ -5950,66 +6222,55 @@ int peer_maximum_prefix_set(struct peer *peer, afi_t afi, safi_t safi, uint32_t max, uint8_t threshold, int warning, uint16_t restart) { - struct peer_group *group; + struct peer *member; struct listnode *node, *nnode; - /* apply configuration and set flags */ - SET_FLAG(peer->af_flags[afi][safi], PEER_FLAG_MAX_PREFIX); + /* Set flags and configuration on peer. */ + peer_af_flag_set(peer, afi, safi, PEER_FLAG_MAX_PREFIX); if (warning) - SET_FLAG(peer->af_flags[afi][safi], - PEER_FLAG_MAX_PREFIX_WARNING); + peer_af_flag_set(peer, afi, safi, PEER_FLAG_MAX_PREFIX_WARNING); else - UNSET_FLAG(peer->af_flags[afi][safi], - PEER_FLAG_MAX_PREFIX_WARNING); + peer_af_flag_unset(peer, afi, safi, + PEER_FLAG_MAX_PREFIX_WARNING); + peer->pmax[afi][safi] = max; peer->pmax_threshold[afi][safi] = threshold; peer->pmax_restart[afi][safi] = restart; - /* if handling a peer-group, apply to all children */ - if (CHECK_FLAG(peer->sflags, PEER_STATUS_GROUP)) { - group = peer->group; - for (ALL_LIST_ELEMENTS(group->peer, node, nnode, peer)) { - /* - * If peer configuration is user-set, it overrides - * peer-group config. - */ - if (!CHECK_FLAG(peer->af_flags_override[afi][safi], - PEER_FLAG_MAX_PREFIX)) { - SET_FLAG(peer->af_flags[afi][safi], - PEER_FLAG_MAX_PREFIX); - peer->pmax[afi][safi] = max; - peer->pmax_threshold[afi][safi] = threshold; - peer->pmax_restart[afi][safi] = restart; - } - if (!CHECK_FLAG(peer->af_flags_override[afi][safi], - PEER_FLAG_MAX_PREFIX_WARNING)) { - if (warning) - SET_FLAG(peer->af_flags[afi][safi], - PEER_FLAG_MAX_PREFIX_WARNING); - else - UNSET_FLAG( - peer->af_flags[afi][safi], - PEER_FLAG_MAX_PREFIX_WARNING); - } - - if ((peer->status == Established) - && (peer->afc[afi][safi])) - bgp_maximum_prefix_overflow(peer, afi, safi, 1); - } - } else { - /* if not handling a peer-group, set the override flags */ + /* Check if handling a regular peer. */ + if (!CHECK_FLAG(peer->sflags, PEER_STATUS_GROUP)) { + /* Re-check if peer violates maximum-prefix. */ if ((peer->status == Established) && (peer->afc[afi][safi])) bgp_maximum_prefix_overflow(peer, afi, safi, 1); - SET_FLAG(peer->af_flags_override[afi][safi], - PEER_FLAG_MAX_PREFIX); + /* Skip peer-group mechanics for regular peers. */ + return 0; + } + /* + * Set flags and configuration on all peer-group members, unless they + * are explicitely overriding peer-group configuration. + */ + for (ALL_LIST_ELEMENTS(peer->group->peer, node, nnode, member)) { + /* Skip peers with overridden configuration. */ + if (CHECK_FLAG(member->af_flags_override[afi][safi], + PEER_FLAG_MAX_PREFIX)) + continue; + + /* Set flag and configuration on peer-group member. */ + member->pmax[afi][safi] = max; + member->pmax_threshold[afi][safi] = threshold; + member->pmax_restart[afi][safi] = restart; if (warning) - SET_FLAG(peer->af_flags_override[afi][safi], + SET_FLAG(member->af_flags[afi][safi], PEER_FLAG_MAX_PREFIX_WARNING); else - UNSET_FLAG(peer->af_flags_override[afi][safi], + UNSET_FLAG(member->af_flags[afi][safi], PEER_FLAG_MAX_PREFIX_WARNING); + + /* Re-check if peer violates maximum-prefix. */ + if ((member->status == Established) && (member->afc[afi][safi])) + bgp_maximum_prefix_overflow(member, afi, safi, 1); } return 0; @@ -6017,53 +6278,47 @@ int peer_maximum_prefix_set(struct peer *peer, afi_t afi, safi_t safi, int peer_maximum_prefix_unset(struct peer *peer, afi_t afi, safi_t safi) { - struct peer_group *group; + struct peer *member; struct listnode *node, *nnode; - UNSET_FLAG(peer->af_flags[afi][safi], PEER_FLAG_MAX_PREFIX); - UNSET_FLAG(peer->af_flags[afi][safi], PEER_FLAG_MAX_PREFIX_WARNING); - peer->pmax[afi][safi] = 0; - peer->pmax_threshold[afi][safi] = 0; - peer->pmax_restart[afi][safi] = 0; - - /* if not handling a peer-group, unset override flags */ - if (!CHECK_FLAG(peer->sflags, PEER_STATUS_GROUP)) { - UNSET_FLAG(peer->af_flags_override[afi][safi], - PEER_FLAG_MAX_PREFIX); - UNSET_FLAG(peer->af_flags_override[afi][safi], - PEER_FLAG_MAX_PREFIX_WARNING); - /* if peer is part of a peer-group, apply peer-group config */ - if (peer_group_active(peer)) { - peer->pmax[afi][safi] = - peer->group->conf->pmax[afi][safi]; - peer->pmax_threshold[afi][safi] = - peer->group->conf->pmax_threshold[afi][safi]; - peer->pmax_restart[afi][safi] = - peer->group->conf->pmax_restart[afi][safi]; - } + /* Inherit configuration from peer-group if peer is member. */ + if (peer_group_active(peer)) { + peer_af_flag_inherit(peer, afi, safi, PEER_FLAG_MAX_PREFIX); + peer_af_flag_inherit(peer, afi, safi, + PEER_FLAG_MAX_PREFIX_WARNING); + PEER_ATTR_INHERIT(peer, pmax[afi][safi]); + PEER_ATTR_INHERIT(peer, pmax_threshold[afi][safi]); + PEER_ATTR_INHERIT(peer, pmax_restart[afi][safi]); return 0; } + /* Remove flags and configuration from peer. */ + peer_af_flag_unset(peer, afi, safi, PEER_FLAG_MAX_PREFIX); + peer_af_flag_unset(peer, afi, safi, PEER_FLAG_MAX_PREFIX_WARNING); + peer->pmax[afi][safi] = 0; + peer->pmax_threshold[afi][safi] = 0; + peer->pmax_restart[afi][safi] = 0; + /* - * If this peer is a peer-group, set all peers in the group unless they - * have overrides for our config. + * Remove flags and configuration from all peer-group members, unless + * they are explicitely overriding peer-group configuration. */ - group = peer->group; - for (ALL_LIST_ELEMENTS(group->peer, node, nnode, peer)) { - if (!CHECK_FLAG(peer->af_flags_override[afi][safi], - PEER_FLAG_MAX_PREFIX_WARNING)) - UNSET_FLAG(peer->af_flags[afi][safi], - PEER_FLAG_MAX_PREFIX_WARNING); - if (!CHECK_FLAG(peer->af_flags_override[afi][safi], - PEER_FLAG_MAX_PREFIX)) { - UNSET_FLAG(peer->af_flags[afi][safi], - PEER_FLAG_MAX_PREFIX); - peer->pmax[afi][safi] = 0; - peer->pmax_threshold[afi][safi] = 0; - peer->pmax_restart[afi][safi] = 0; - } + for (ALL_LIST_ELEMENTS(peer->group->peer, node, nnode, member)) { + /* Skip peers with overridden configuration. */ + if (CHECK_FLAG(member->af_flags_override[afi][safi], + PEER_FLAG_MAX_PREFIX)) + continue; + + /* Remove flag and configuration on peer-group member. */ + UNSET_FLAG(member->af_flags[afi][safi], PEER_FLAG_MAX_PREFIX); + UNSET_FLAG(member->af_flags[afi][safi], + PEER_FLAG_MAX_PREFIX_WARNING); + member->pmax[afi][safi] = 0; + member->pmax_threshold[afi][safi] = 0; + member->pmax_restart[afi][safi] = 0; } + return 0; } @@ -6431,86 +6686,58 @@ static void bgp_config_write_filter(struct vty *vty, struct peer *peer, afi_t afi, safi_t safi) { struct bgp_filter *filter; - struct bgp_filter *gfilter = NULL; char *addr; - int in = FILTER_IN; - int out = FILTER_OUT; addr = peer->host; filter = &peer->filter[afi][safi]; - if (peer_group_active(peer)) - gfilter = &peer->group->conf->filter[afi][safi]; - /* distribute-list. */ - if (filter->dlist[in].name) - if (!gfilter || !gfilter->dlist[in].name - || strcmp(filter->dlist[in].name, gfilter->dlist[in].name) - != 0) { - vty_out(vty, " neighbor %s distribute-list %s in\n", - addr, filter->dlist[in].name); - } + if (peergroup_filter_check(peer, afi, safi, PEER_FT_DISTRIBUTE_LIST, + FILTER_IN)) + vty_out(vty, " neighbor %s distribute-list %s in\n", addr, + filter->dlist[FILTER_IN].name); - if (filter->dlist[out].name && !gfilter) { + if (peergroup_filter_check(peer, afi, safi, PEER_FT_DISTRIBUTE_LIST, + FILTER_OUT)) vty_out(vty, " neighbor %s distribute-list %s out\n", addr, - filter->dlist[out].name); - } + filter->dlist[FILTER_OUT].name); /* prefix-list. */ - if (filter->plist[in].name) - if (!gfilter || !gfilter->plist[in].name - || strcmp(filter->plist[in].name, gfilter->plist[in].name) - != 0) { - vty_out(vty, " neighbor %s prefix-list %s in\n", addr, - filter->plist[in].name); - } + if (peergroup_filter_check(peer, afi, safi, PEER_FT_PREFIX_LIST, + FILTER_IN)) + vty_out(vty, " neighbor %s prefix-list %s in\n", addr, + filter->plist[FILTER_IN].name); - if (filter->plist[out].name) - if (!gfilter || !gfilter->plist[out].name - || strcmp(filter->plist[out].name, gfilter->plist[out].name) - != 0) { - vty_out(vty, " neighbor %s prefix-list %s out\n", addr, - filter->plist[out].name); - } + if (peergroup_filter_check(peer, afi, safi, PEER_FT_PREFIX_LIST, + FILTER_OUT)) + vty_out(vty, " neighbor %s prefix-list %s out\n", addr, + filter->plist[FILTER_OUT].name); /* route-map. */ - if (filter->map[RMAP_IN].name) - if (!gfilter || !gfilter->map[RMAP_IN].name - || strcmp(filter->map[RMAP_IN].name, - gfilter->map[RMAP_IN].name) - != 0) { - vty_out(vty, " neighbor %s route-map %s in\n", addr, - filter->map[RMAP_IN].name); - } + if (peergroup_filter_check(peer, afi, safi, PEER_FT_ROUTE_MAP, RMAP_IN)) + vty_out(vty, " neighbor %s route-map %s in\n", addr, + filter->map[RMAP_IN].name); - if (filter->map[RMAP_OUT].name) - if (!gfilter || !gfilter->map[RMAP_OUT].name - || strcmp(filter->map[RMAP_OUT].name, - gfilter->map[RMAP_OUT].name) - != 0) { - vty_out(vty, " neighbor %s route-map %s out\n", addr, - filter->map[RMAP_OUT].name); - } + if (peergroup_filter_check(peer, afi, safi, PEER_FT_ROUTE_MAP, + RMAP_OUT)) + vty_out(vty, " neighbor %s route-map %s out\n", addr, + filter->map[RMAP_OUT].name); /* unsuppress-map */ - if (filter->usmap.name && !gfilter) { + if (peergroup_filter_check(peer, afi, safi, PEER_FT_UNSUPPRESS_MAP, 0)) vty_out(vty, " neighbor %s unsuppress-map %s\n", addr, filter->usmap.name); - } /* filter-list. */ - if (filter->aslist[in].name) - if (!gfilter || !gfilter->aslist[in].name - || strcmp(filter->aslist[in].name, gfilter->aslist[in].name) - != 0) { - vty_out(vty, " neighbor %s filter-list %s in\n", addr, - filter->aslist[in].name); - } + if (peergroup_filter_check(peer, afi, safi, PEER_FT_FILTER_LIST, + FILTER_IN)) + vty_out(vty, " neighbor %s filter-list %s in\n", addr, + filter->aslist[FILTER_IN].name); - if (filter->aslist[out].name && !gfilter) { + if (peergroup_filter_check(peer, afi, safi, PEER_FT_FILTER_LIST, + FILTER_OUT)) vty_out(vty, " neighbor %s filter-list %s out\n", addr, - filter->aslist[out].name); - } + filter->aslist[FILTER_OUT].name); } /* BGP peer configuration display function. */ @@ -6841,6 +7068,7 @@ static void bgp_config_write_peer_af(struct vty *vty, struct bgp *bgp, { struct peer *g_peer = NULL; char *addr; + bool flag_scomm, flag_secomm, flag_slcomm; /* Skip dynamic neighbors. */ if (peer_dynamic_neighbor(peer)) @@ -6967,97 +7195,63 @@ static void bgp_config_write_peer_af(struct vty *vty, struct bgp *bgp, } /* send-community print. */ - if (bgp_option_check(BGP_OPT_CONFIG_CISCO)) { - if (peergroup_af_flag_check(peer, afi, safi, - PEER_FLAG_SEND_COMMUNITY) - && peergroup_af_flag_check(peer, afi, safi, - PEER_FLAG_SEND_EXT_COMMUNITY) - && peergroup_af_flag_check( - peer, afi, safi, - PEER_FLAG_SEND_LARGE_COMMUNITY)) { - vty_out(vty, " neighbor %s send-community all\n", - addr); - } else if (peergroup_af_flag_check( - peer, afi, safi, - PEER_FLAG_SEND_LARGE_COMMUNITY)) { - vty_out(vty, " neighbor %s send-community large\n", - addr); - } else if (peergroup_af_flag_check( - peer, afi, safi, - PEER_FLAG_SEND_EXT_COMMUNITY)) { - vty_out(vty, " neighbor %s send-community extended\n", - addr); - } else if (peergroup_af_flag_check(peer, afi, safi, - PEER_FLAG_SEND_COMMUNITY)) { - vty_out(vty, " neighbor %s send-community\n", addr); - } - } else { - if (!peer_af_flag_check(peer, afi, safi, - PEER_FLAG_SEND_COMMUNITY) - && (!g_peer || peer_af_flag_check(g_peer, afi, safi, - PEER_FLAG_SEND_COMMUNITY)) - && !peer_af_flag_check(peer, afi, safi, - PEER_FLAG_SEND_EXT_COMMUNITY) - && (!g_peer - || peer_af_flag_check(g_peer, afi, safi, - PEER_FLAG_SEND_EXT_COMMUNITY)) - && !peer_af_flag_check(peer, afi, safi, - PEER_FLAG_SEND_LARGE_COMMUNITY) - && (!g_peer || peer_af_flag_check( - g_peer, afi, safi, - PEER_FLAG_SEND_LARGE_COMMUNITY))) { + flag_scomm = peergroup_af_flag_check(peer, afi, safi, + PEER_FLAG_SEND_COMMUNITY); + flag_secomm = peergroup_af_flag_check(peer, afi, safi, + PEER_FLAG_SEND_EXT_COMMUNITY); + flag_slcomm = peergroup_af_flag_check(peer, afi, safi, + PEER_FLAG_SEND_LARGE_COMMUNITY); + + if (!bgp_option_check(BGP_OPT_CONFIG_CISCO)) { + if (flag_scomm && flag_secomm && flag_slcomm) { vty_out(vty, " no neighbor %s send-community all\n", addr); } else { - if (!peer_af_flag_check(peer, afi, safi, - PEER_FLAG_SEND_LARGE_COMMUNITY) - && (!g_peer - || peer_af_flag_check( - g_peer, afi, safi, - PEER_FLAG_SEND_LARGE_COMMUNITY))) { - vty_out(vty, - " no neighbor %s send-community large\n", - addr); - } - - if (!peer_af_flag_check(peer, afi, safi, - PEER_FLAG_SEND_EXT_COMMUNITY) - && (!g_peer - || peer_af_flag_check( - g_peer, afi, safi, - PEER_FLAG_SEND_EXT_COMMUNITY))) { - vty_out(vty, - " no neighbor %s send-community extended\n", - addr); - } - - if (!peer_af_flag_check(peer, afi, safi, - PEER_FLAG_SEND_COMMUNITY) - && (!g_peer || peer_af_flag_check( - g_peer, afi, safi, - PEER_FLAG_SEND_COMMUNITY))) { + if (flag_scomm) vty_out(vty, " no neighbor %s send-community\n", addr); - } + if (flag_secomm) + vty_out(vty, + " no neighbor %s send-community extended\n", + addr); + + if (flag_slcomm) + vty_out(vty, + " no neighbor %s send-community large\n", + addr); + } + } else { + if (flag_scomm && flag_secomm && flag_slcomm) { + vty_out(vty, " neighbor %s send-community all\n", + addr); + } else if (flag_scomm && flag_secomm) { + vty_out(vty, " neighbor %s send-community both\n", + addr); + } else { + if (flag_scomm) + vty_out(vty, " neighbor %s send-community\n", + addr); + if (flag_secomm) + vty_out(vty, + " neighbor %s send-community extended\n", + addr); + if (flag_slcomm) + vty_out(vty, + " neighbor %s send-community large\n", + addr); } } /* Default information */ if (peergroup_af_flag_check(peer, afi, safi, - PEER_FLAG_DEFAULT_ORIGINATE) - || (g_peer - && ((peer->default_rmap[afi][safi].name - && !g_peer->default_rmap[afi][safi].name) - || (!peer->default_rmap[afi][safi].name - && g_peer->default_rmap[afi][safi].name) - || (peer->default_rmap[afi][safi].name - && strcmp(peer->default_rmap[afi][safi].name, - g_peer->default_rmap[afi][safi].name))))) { + PEER_FLAG_DEFAULT_ORIGINATE)) { vty_out(vty, " neighbor %s default-originate", addr); + if (peer->default_rmap[afi][safi].name) vty_out(vty, " route-map %s", peer->default_rmap[afi][safi].name); + vty_out(vty, "\n"); } @@ -7068,29 +7262,22 @@ static void bgp_config_write_peer_af(struct vty *vty, struct bgp *bgp, } /* maximum-prefix. */ - if (CHECK_FLAG(peer->af_flags[afi][safi], PEER_FLAG_MAX_PREFIX)) - if (!peer_group_active(peer) - || g_peer->pmax[afi][safi] != peer->pmax[afi][safi] - || g_peer->pmax_threshold[afi][safi] - != peer->pmax_threshold[afi][safi] - || CHECK_FLAG(g_peer->af_flags[afi][safi], - PEER_FLAG_MAX_PREFIX_WARNING) - != CHECK_FLAG(peer->af_flags[afi][safi], - PEER_FLAG_MAX_PREFIX_WARNING)) { - vty_out(vty, " neighbor %s maximum-prefix %lu", addr, - peer->pmax[afi][safi]); - if (peer->pmax_threshold[afi][safi] - != MAXIMUM_PREFIX_THRESHOLD_DEFAULT) - vty_out(vty, " %u", - peer->pmax_threshold[afi][safi]); - if (CHECK_FLAG(peer->af_flags[afi][safi], + if (peergroup_af_flag_check(peer, afi, safi, PEER_FLAG_MAX_PREFIX)) { + vty_out(vty, " neighbor %s maximum-prefix %lu", addr, + peer->pmax[afi][safi]); + + if (peer->pmax_threshold[afi][safi] + != MAXIMUM_PREFIX_THRESHOLD_DEFAULT) + vty_out(vty, " %u", peer->pmax_threshold[afi][safi]); + if (peer_af_flag_check(peer, afi, safi, PEER_FLAG_MAX_PREFIX_WARNING)) - vty_out(vty, " warning-only"); - if (peer->pmax_restart[afi][safi]) - vty_out(vty, " restart %u", - peer->pmax_restart[afi][safi]); - vty_out(vty, "\n"); - } + vty_out(vty, " warning-only"); + if (peer->pmax_restart[afi][safi]) + vty_out(vty, " restart %u", + peer->pmax_restart[afi][safi]); + + vty_out(vty, "\n"); + } /* Route server client. */ if (peergroup_af_flag_check(peer, afi, safi, @@ -7105,42 +7292,22 @@ static void bgp_config_write_peer_af(struct vty *vty, struct bgp *bgp, } /* allowas-in <1-10> */ - if (peer_af_flag_check(peer, afi, safi, PEER_FLAG_ALLOWAS_IN)) { - if (!peer_group_active(peer) - || !peer_af_flag_check(g_peer, afi, safi, - PEER_FLAG_ALLOWAS_IN) - || peer->allowas_in[afi][safi] - != g_peer->allowas_in[afi][safi]) { - if (peer->allowas_in[afi][safi] == 3) { - vty_out(vty, " neighbor %s allowas-in\n", - addr); - } else { - vty_out(vty, " neighbor %s allowas-in %d\n", - addr, peer->allowas_in[afi][safi]); - } - } - } - - /* allowas-in origin */ - else if (peer_af_flag_check(peer, afi, safi, - PEER_FLAG_ALLOWAS_IN_ORIGIN)) { - if (!peer_group_active(peer) - || !peer_af_flag_check(g_peer, afi, safi, - PEER_FLAG_ALLOWAS_IN_ORIGIN)) { + if (peergroup_af_flag_check(peer, afi, safi, PEER_FLAG_ALLOWAS_IN)) { + if (peer_af_flag_check(peer, afi, safi, + PEER_FLAG_ALLOWAS_IN_ORIGIN)) { vty_out(vty, " neighbor %s allowas-in origin\n", addr); + } else if (peer->allowas_in[afi][safi] == 3) { + vty_out(vty, " neighbor %s allowas-in\n", addr); + } else { + vty_out(vty, " neighbor %s allowas-in %d\n", addr, + peer->allowas_in[afi][safi]); } } /* weight */ - if (peer_af_flag_check(peer, afi, safi, PEER_FLAG_WEIGHT)) - if (!peer_group_active(peer) - || !peer_af_flag_check(g_peer, afi, safi, PEER_FLAG_WEIGHT) - || peer->weight[afi][safi] != g_peer->weight[afi][safi]) { - if (peer->weight[afi][safi]) { - vty_out(vty, " neighbor %s weight %lu\n", addr, - peer->weight[afi][safi]); - } - } + if (peergroup_af_flag_check(peer, afi, safi, PEER_FLAG_WEIGHT)) + vty_out(vty, " neighbor %s weight %lu\n", addr, + peer->weight[afi][safi]); /* Filter. */ bgp_config_write_filter(vty, peer, afi, safi); diff --git a/bgpd/bgpd.h b/bgpd/bgpd.h index a685411f6e..24d05c2e80 100644 --- a/bgpd/bgpd.h +++ b/bgpd/bgpd.h @@ -865,6 +865,17 @@ struct peer { * *peer-specific*. */ uint32_t af_flags_override[AFI_MAX][SAFI_MAX]; + /* + * Parallel array to af_flags that indicates whether each flag should + * be treated as regular (defaults to 0) or inverted (defaults to 1). + * If a flag is set to 1 by default, the same bit should be set here. + * + * Notes: + * - This does *not* contain the flag values, rather it contains + * whether the flag at the same position in af_flags is *regular* or + * *inverted*. + */ + uint32_t af_flags_invert[AFI_MAX][SAFI_MAX]; /* * Effective flags, computed by applying peer-group flags and then * overriding with individual flags @@ -1038,6 +1049,32 @@ struct peer { /* Filter structure. */ struct bgp_filter filter[AFI_MAX][SAFI_MAX]; + /* + * Parallel array to filter that indicates whether each filter + * originates from a peer-group or if it is config that is specific to + * this individual peer. If a filter is set independent of the + * peer-group the appropriate bit should be set here. If this peer is a + * peer-group, this memory region should be all zeros. The assumption + * is that the default state for all flags is unset. Due to filters + * having a direction (e.g. in/out/...), this array has a third + * dimension for storing the overrides independently per direction. + * + * Notes: + * - if a filter for an individual peer is unset, the corresponding + * override flag is unset and the peer is considered to be back in + * sync with the peer-group. + * - This does *not* contain the filter values, rather it contains + * whether the filter in filter (struct bgp_filter) is peer-specific. + */ + uint8_t filter_override[AFI_MAX][SAFI_MAX][(FILTER_MAX > RMAP_MAX) + ? FILTER_MAX + : RMAP_MAX]; +#define PEER_FT_DISTRIBUTE_LIST (1 << 0) /* distribute-list */ +#define PEER_FT_FILTER_LIST (1 << 1) /* filter-list */ +#define PEER_FT_PREFIX_LIST (1 << 2) /* prefix-list */ +#define PEER_FT_ROUTE_MAP (1 << 3) /* route-map */ +#define PEER_FT_UNSUPPRESS_MAP (1 << 4) /* unsuppress-map */ + /* ORF Prefix-list */ struct prefix_list *orf_plist[AFI_MAX][SAFI_MAX]; @@ -1115,6 +1152,18 @@ struct peer { }; DECLARE_QOBJ_TYPE(peer) +/* Inherit peer attribute from peer-group. */ +#define PEER_ATTR_INHERIT(peer, attr) ((peer)->attr = (peer)->group->conf->attr) +#define PEER_STR_ATTR_INHERIT(mt, peer, attr) \ + do { \ + if ((peer)->attr) \ + XFREE(mt, (peer)->attr); \ + if ((peer)->group->conf->attr) \ + (peer)->attr = XSTRDUP(mt, (peer)->group->conf->attr); \ + else \ + (peer)->attr = NULL; \ + } while (0) + /* Check if suppress start/restart of sessions to peer. */ #define BGP_PEER_START_SUPPRESSED(P) \ (CHECK_FLAG((P)->flags, PEER_FLAG_SHUTDOWN) \ @@ -1513,6 +1562,8 @@ extern int peer_flag_unset(struct peer *, uint32_t); extern int peer_af_flag_set(struct peer *, afi_t, safi_t, uint32_t); extern int peer_af_flag_unset(struct peer *, afi_t, safi_t, uint32_t); extern int peer_af_flag_check(struct peer *, afi_t, safi_t, uint32_t); +extern void peer_af_flag_inherit(struct peer *peer, afi_t afi, safi_t safi, + uint32_t flag); extern int peer_ebgp_multihop_set(struct peer *, int); extern int peer_ebgp_multihop_unset(struct peer *); diff --git a/tests/.gitignore b/tests/.gitignore index 1ff038573f..5b90b7046c 100644 --- a/tests/.gitignore +++ b/tests/.gitignore @@ -26,6 +26,7 @@ __pycache__ /bgpd/test_mp_attr /bgpd/test_mpath /bgpd/test_packet +/bgpd/test_peer_attr /isisd/test_fuzz_isis_tlv /isisd/test_fuzz_isis_tlv_tests.h /isisd/test_isis_vertex_queue diff --git a/tests/Makefile.am b/tests/Makefile.am index 6a19325927..aefe0d06ac 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -18,6 +18,7 @@ TESTS_BGPD = \ bgpd/test_aspath \ bgpd/test_capability \ bgpd/test_packet \ + bgpd/test_peer_attr \ bgpd/test_ecommunity \ bgpd/test_mp_attr \ bgpd/test_mpath @@ -140,6 +141,7 @@ lib_cli_test_commands_SOURCES = lib/cli/test_commands_defun.c \ bgpd_test_aspath_SOURCES = bgpd/test_aspath.c bgpd_test_capability_SOURCES = bgpd/test_capability.c bgpd_test_packet_SOURCES = bgpd/test_packet.c +bgpd_test_peer_attr_SOURCES = bgpd/test_peer_attr.c bgpd_test_ecommunity_SOURCES = bgpd/test_ecommunity.c bgpd_test_mp_attr_SOURCES = bgpd/test_mp_attr.c bgpd_test_mpath_SOURCES = bgpd/test_mpath.c @@ -179,6 +181,7 @@ lib_cli_test_commands_LDADD = $(ALL_TESTS_LDADD) bgpd_test_aspath_LDADD = $(BGP_TEST_LDADD) bgpd_test_capability_LDADD = $(BGP_TEST_LDADD) bgpd_test_packet_LDADD = $(BGP_TEST_LDADD) +bgpd_test_peer_attr_LDADD = $(BGP_TEST_LDADD) bgpd_test_ecommunity_LDADD = $(BGP_TEST_LDADD) bgpd_test_mp_attr_LDADD = $(BGP_TEST_LDADD) bgpd_test_mpath_LDADD = $(BGP_TEST_LDADD) @@ -193,6 +196,7 @@ EXTRA_DIST = \ bgpd/test_ecommunity.py \ bgpd/test_mp_attr.py \ bgpd/test_mpath.py \ + bgpd/test_peer_attr.py \ helpers/python/frrsix.py \ helpers/python/frrtest.py \ isisd/test_fuzz_isis_tlv.py \ diff --git a/tests/bgpd/test_peer_attr.c b/tests/bgpd/test_peer_attr.c new file mode 100644 index 0000000000..67a6db849f --- /dev/null +++ b/tests/bgpd/test_peer_attr.c @@ -0,0 +1,1001 @@ +/* + * BGP Peer Attribute Unit Tests + * Copyright (C) 2018 Pascal Mathis + * + * This program 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 of the License, or (at your option) + * any later version. + * + * This program 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 this program; see the file COPYING; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ +#include + +#include "memory.h" +#include "plist.h" +#include "bgpd/bgpd.h" +#include "bgpd/bgp_attr.h" +#include "bgpd/bgp_regex.h" +#include "bgpd/bgp_clist.h" +#include "bgpd/bgp_dump.h" +#include "bgpd/bgp_filter.h" +#include "bgpd/bgp_route.h" +#include "bgpd/bgp_vty.h" +#include "bgpd/bgp_zebra.h" + +#ifdef ENABLE_BGP_VNC +#include "bgpd/rfapi/rfapi_backend.h" +#endif + +/* Required variables to link in libbgp */ +struct zebra_privs_t bgpd_privs = {0}; +struct thread_master *master; + +enum test_state { + TEST_SUCCESS, + TEST_COMMAND_ERROR, + TEST_CONFIG_ERROR, + TEST_ASSERT_ERROR, + TEST_INTERNAL_ERROR, +}; + +struct test { + enum test_state state; + char *desc; + char *error; + struct list *log; + + struct vty *vty; + struct bgp *bgp; + struct peer *peer; + struct peer_group *group; +}; + +struct test_config { + int local_asn; + int peer_asn; + const char *peer_address; + const char *peer_group; +}; + +struct test_peer_family { + afi_t afi; + safi_t safi; +}; + +struct test_peer_attr { + const char *cmd; + const char *peer_cmd; + const char *group_cmd; + + enum { PEER_AT_AF_FLAG = 0, + PEER_AT_AF_FILTER = 1, + PEER_AT_GLOBAL_FLAG = 2 } type; + union { + uint32_t flag; + struct { + uint32_t flag; + size_t direct; + } filter; + } u; + struct { + bool invert; + bool use_ibgp; + } o; + + afi_t afi; + safi_t safi; + struct test_peer_family families[AFI_MAX * SAFI_MAX]; +}; + +#define OUT_SYMBOL_INFO "\u25ba" +#define OUT_SYMBOL_OK "\u2714" +#define OUT_SYMBOL_NOK "\u2716" + +#define TEST_ASSERT_EQ(T, A, B) \ + do { \ + if ((T)->state != TEST_SUCCESS || ((A) == (B))) \ + break; \ + (T)->state = TEST_ASSERT_ERROR; \ + (T)->error = str_printf( \ + "assertion failed: %s[%d] == [%d]%s (%s:%d)", (#A), \ + (A), (B), (#B), __FILE__, __LINE__); \ + } while (0) + +static struct test_config cfg = { + .local_asn = 100, + .peer_asn = 200, + .peer_address = "1.1.1.1", + .peer_group = "PG-TEST", +}; + +static struct test_peer_family test_default_families[] = { + {.afi = AFI_IP, .safi = SAFI_UNICAST}, + {.afi = AFI_IP, .safi = SAFI_MULTICAST}, + {.afi = AFI_IP6, .safi = SAFI_UNICAST}, + {.afi = AFI_IP6, .safi = SAFI_MULTICAST}, +}; + +/* clang-format off */ +static struct test_peer_attr test_peer_attrs[] = { + { + .cmd = "addpath-tx-all-paths", + .u.flag = PEER_FLAG_ADDPATH_TX_ALL_PATHS, + }, + { + .cmd = "addpath-tx-bestpath-per-AS", + .u.flag = PEER_FLAG_ADDPATH_TX_BESTPATH_PER_AS, + }, + { + .cmd = "allowas-in", + .peer_cmd = "allowas-in 1", + .group_cmd = "allowas-in 2", + .u.flag = PEER_FLAG_ALLOWAS_IN, + }, + { + .cmd = "allowas-in origin", + .u.flag = PEER_FLAG_ALLOWAS_IN_ORIGIN, + }, + { + .cmd = "as-override", + .u.flag = PEER_FLAG_AS_OVERRIDE, + }, + { + .cmd = "attribute-unchanged as-path", + .u.flag = PEER_FLAG_AS_PATH_UNCHANGED, + }, + { + .cmd = "attribute-unchanged next-hop", + .u.flag = PEER_FLAG_NEXTHOP_UNCHANGED, + }, + { + .cmd = "attribute-unchanged med", + .u.flag = PEER_FLAG_MED_UNCHANGED, + }, + { + .cmd = "attribute-unchanged as-path next-hop", + .u.flag = PEER_FLAG_AS_PATH_UNCHANGED + | PEER_FLAG_NEXTHOP_UNCHANGED, + }, + { + .cmd = "attribute-unchanged as-path med", + .u.flag = PEER_FLAG_AS_PATH_UNCHANGED + | PEER_FLAG_MED_UNCHANGED, + }, + { + .cmd = "attribute-unchanged as-path next-hop med", + .u.flag = PEER_FLAG_AS_PATH_UNCHANGED + | PEER_FLAG_NEXTHOP_UNCHANGED + | PEER_FLAG_MED_UNCHANGED, + }, + { + .cmd = "capability orf prefix-list send", + .u.flag = PEER_FLAG_ORF_PREFIX_SM, + }, + { + .cmd = "capability orf prefix-list receive", + .u.flag = PEER_FLAG_ORF_PREFIX_RM, + }, + { + .cmd = "capability orf prefix-list both", + .u.flag = PEER_FLAG_ORF_PREFIX_SM | PEER_FLAG_ORF_PREFIX_RM, + }, + { + .cmd = "default-originate", + .u.flag = PEER_FLAG_DEFAULT_ORIGINATE, + }, + { + .cmd = "default-originate route-map", + .peer_cmd = "default-originate route-map RM-PEER", + .group_cmd = "default-originate route-map RM-GROUP", + .u.flag = PEER_FLAG_DEFAULT_ORIGINATE, + }, + { + .cmd = "filter-list", + .peer_cmd = "filter-list FL-PEER in", + .group_cmd = "filter-list FL-GROUP in", + .type = PEER_AT_AF_FILTER, + .u.filter.flag = PEER_FT_FILTER_LIST, + .u.filter.direct = FILTER_IN, + }, + { + .cmd = "filter-list", + .peer_cmd = "filter-list FL-PEER out", + .group_cmd = "filter-list FL-GROUP out", + .type = PEER_AT_AF_FILTER, + .u.filter.flag = PEER_FT_FILTER_LIST, + .u.filter.direct = FILTER_OUT, + }, + { + .cmd = "maximum-prefix", + .peer_cmd = "maximum-prefix 10", + .group_cmd = "maximum-prefix 20", + .u.flag = PEER_FLAG_MAX_PREFIX, + }, + { + .cmd = "maximum-prefix", + .peer_cmd = "maximum-prefix 10 restart 100", + .group_cmd = "maximum-prefix 20 restart 200", + .u.flag = PEER_FLAG_MAX_PREFIX, + }, + { + .cmd = "maximum-prefix", + .peer_cmd = "maximum-prefix 10 1 restart 100", + .group_cmd = "maximum-prefix 20 2 restart 200", + .u.flag = PEER_FLAG_MAX_PREFIX, + }, + { + .cmd = "maximum-prefix", + .peer_cmd = "maximum-prefix 10 warning-only", + .group_cmd = "maximum-prefix 20 warning-only", + .u.flag = PEER_FLAG_MAX_PREFIX | PEER_FLAG_MAX_PREFIX_WARNING, + }, + { + .cmd = "maximum-prefix", + .peer_cmd = "maximum-prefix 10 1 warning-only", + .group_cmd = "maximum-prefix 20 2 warning-only", + .u.flag = PEER_FLAG_MAX_PREFIX | PEER_FLAG_MAX_PREFIX_WARNING, + }, + { + .cmd = "next-hop-self", + .u.flag = PEER_FLAG_NEXTHOP_SELF, + }, + { + .cmd = "next-hop-self force", + .u.flag = PEER_FLAG_FORCE_NEXTHOP_SELF, + }, + { + .cmd = "prefix-list", + .peer_cmd = "prefix-list PL-PEER in", + .group_cmd = "prefix-list PL-GROUP in", + .type = PEER_AT_AF_FILTER, + .u.filter.flag = PEER_FT_PREFIX_LIST, + .u.filter.direct = FILTER_IN, + }, + { + .cmd = "prefix-list", + .peer_cmd = "prefix-list PL-PEER out", + .group_cmd = "prefix-list PL-GROUP out", + .type = PEER_AT_AF_FILTER, + .u.filter.flag = PEER_FT_PREFIX_LIST, + .u.filter.direct = FILTER_OUT, + }, + { + .cmd = "remove-private-AS", + .u.flag = PEER_FLAG_REMOVE_PRIVATE_AS, + }, + { + .cmd = "remove-private-AS all", + .u.flag = PEER_FLAG_REMOVE_PRIVATE_AS + | PEER_FLAG_REMOVE_PRIVATE_AS_ALL, + }, + { + .cmd = "remove-private-AS replace-AS", + .u.flag = PEER_FLAG_REMOVE_PRIVATE_AS + | PEER_FLAG_REMOVE_PRIVATE_AS_REPLACE, + }, + { + .cmd = "remove-private-AS all replace-AS", + .u.flag = PEER_FLAG_REMOVE_PRIVATE_AS_ALL_REPLACE, + }, + { + .cmd = "route-map", + .peer_cmd = "route-map RM-PEER in", + .group_cmd = "route-map RM-GROUP in", + .type = PEER_AT_AF_FILTER, + .u.filter.flag = PEER_FT_ROUTE_MAP, + .u.filter.direct = FILTER_IN, + }, + { + .cmd = "route-map", + .peer_cmd = "route-map RM-PEER out", + .group_cmd = "route-map RM-GROUP out", + .type = PEER_AT_AF_FILTER, + .u.filter.flag = PEER_FT_ROUTE_MAP, + .u.filter.direct = FILTER_OUT, + }, + { + .cmd = "route-reflector-client", + .u.flag = PEER_FLAG_REFLECTOR_CLIENT, + .o.use_ibgp = true, + }, + { + .cmd = "route-server-client", + .u.flag = PEER_FLAG_RSERVER_CLIENT, + }, + { + .cmd = "send-community", + .u.flag = PEER_FLAG_SEND_COMMUNITY, + .o.invert = true, + }, + { + .cmd = "send-community extended", + .u.flag = PEER_FLAG_SEND_EXT_COMMUNITY, + .o.invert = true, + }, + { + .cmd = "send-community large", + .u.flag = PEER_FLAG_SEND_LARGE_COMMUNITY, + .o.invert = true, + }, + { + .cmd = "soft-reconfiguration inbound", + .u.flag = PEER_FLAG_SOFT_RECONFIG, + }, + { + .cmd = "unsuppress-map", + .peer_cmd = "unsuppress-map UM-PEER", + .group_cmd = "unsuppress-map UM-GROUP", + .type = PEER_AT_AF_FILTER, + .u.filter.flag = PEER_FT_UNSUPPRESS_MAP, + .u.filter.direct = 0, + }, + { + .cmd = "weight", + .peer_cmd = "weight 100", + .group_cmd = "weight 200", + .u.flag = PEER_FLAG_WEIGHT, + }, + {NULL} +}; +/* clang-format on */ + +static char *str_vprintf(const char *fmt, va_list ap) +{ + int ret; + int buf_size = 0; + char *buf = NULL; + va_list apc; + + while (1) { + va_copy(apc, ap); + ret = vsnprintf(buf, buf_size, fmt, apc); + va_end(apc); + + if (ret >= 0 && ret < buf_size) + break; + + if (ret >= 0) + buf_size = ret + 1; + else + buf_size *= 2; + + buf = XREALLOC(MTYPE_TMP, buf, buf_size); + } + + return buf; +} + +static char *str_printf(const char *fmt, ...) +{ + char *buf; + va_list ap; + + va_start(ap, fmt); + buf = str_vprintf(fmt, ap); + va_end(ap); + + return buf; +} + +static const char *str_from_afi(afi_t afi) +{ + switch (afi) { + case AFI_IP: + return "ipv4"; + case AFI_IP6: + return "ipv6"; + default: + return ""; + } +} + +static const char *str_from_safi(safi_t safi) +{ + switch (safi) { + case SAFI_UNICAST: + return "unicast"; + case SAFI_MULTICAST: + return "multicast"; + default: + return ""; + } +} + +static void test_execute(struct test *test, const char *fmt, ...) +{ + int ret; + char *cmd; + va_list ap; + vector vline; + + /* Skip execution if test instance has previously failed. */ + if (test->state != TEST_SUCCESS) + return; + + /* Format command string with variadic arguments. */ + va_start(ap, fmt); + cmd = str_vprintf(fmt, ap); + va_end(ap); + if (!cmd) { + test->state = TEST_INTERNAL_ERROR; + test->error = + str_printf("could not format command string [%s]", fmt); + return; + } + + /* Tokenize formatted command string. */ + vline = cmd_make_strvec(cmd); + if (vline == NULL) { + test->state = TEST_INTERNAL_ERROR; + test->error = str_printf( + "tokenizing command string [%s] returned empty result", + cmd); + XFREE(MTYPE_TMP, cmd); + + return; + } + + /* Execute command (non-strict). */ + ret = cmd_execute_command(vline, test->vty, NULL, 0); + if (ret != CMD_SUCCESS) { + test->state = TEST_COMMAND_ERROR; + test->error = str_printf( + "execution of command [%s] has failed with code [%d]", + cmd, ret); + } + + /* Free memory. */ + cmd_free_strvec(vline); + XFREE(MTYPE_TMP, cmd); +} + +static void test_config(struct test *test, const char *fmt, bool invert, + va_list ap) +{ + char *matcher; + char *config; + bool matched; + va_list apc; + + /* Skip execution if test instance has previously failed. */ + if (test->state != TEST_SUCCESS) + return; + + /* Format matcher string with variadic arguments. */ + va_copy(apc, ap); + matcher = str_vprintf(fmt, apc); + va_end(apc); + if (!matcher) { + test->state = TEST_INTERNAL_ERROR; + test->error = + str_printf("could not format matcher string [%s]", fmt); + return; + } + + /* Fetch BGP configuration into buffer. */ + bgp_config_write(test->vty); + config = buffer_getstr(test->vty->obuf); + buffer_reset(test->vty->obuf); + + /* Match config against matcher. */ + matched = !!strstr(config, matcher); + if (!matched && !invert) { + test->state = TEST_CONFIG_ERROR; + test->error = str_printf("expected config [%s] to be present", + matcher); + } else if (matched && invert) { + test->state = TEST_CONFIG_ERROR; + test->error = str_printf("expected config [%s] to be absent", + matcher); + } + + /* Free memory and return. */ + XFREE(MTYPE_TMP, matcher); + XFREE(MTYPE_TMP, config); +} + +static void test_config_present(struct test *test, const char *fmt, ...) +{ + va_list ap; + + va_start(ap, fmt); + test_config(test, fmt, false, ap); + va_end(ap); +} + +static void test_config_absent(struct test *test, const char *fmt, ...) +{ + va_list ap; + + va_start(ap, fmt); + test_config(test, fmt, true, ap); + va_end(ap); +} + +static struct test *test_new(const char *desc, bool use_ibgp) +{ + struct test *test; + union sockunion su; + + test = XCALLOC(MTYPE_TMP, sizeof(struct test)); + test->state = TEST_SUCCESS; + test->desc = XSTRDUP(MTYPE_TMP, desc); + test->log = list_new(); + + test->vty = vty_new(); + test->vty->type = VTY_TERM; + test->vty->node = CONFIG_NODE; + + /* Attempt gracefully to purge previous BGP configuration. */ + test_execute(test, "no router bgp"); + test->state = TEST_SUCCESS; + + /* Initialize BGP test environment. */ + test_execute(test, "router bgp %d", cfg.local_asn); + test_execute(test, "no bgp default ipv4-unicast"); + test_execute(test, "neighbor %s peer-group", cfg.peer_group); + test_execute(test, "neighbor %s remote-as %d", cfg.peer_address, + use_ibgp ? cfg.local_asn : cfg.peer_asn); + if (test->state != TEST_SUCCESS) + return test; + + /* Fetch default BGP instance. */ + test->bgp = bgp_get_default(); + if (!test->bgp) { + test->state = TEST_INTERNAL_ERROR; + test->error = + str_printf("could not retrieve default bgp instance"); + return test; + } + + /* Fetch peer instance. */ + str2sockunion(cfg.peer_address, &su); + test->peer = peer_lookup(test->bgp, &su); + if (!test->peer) { + test->state = TEST_INTERNAL_ERROR; + test->error = str_printf( + "could not retrieve instance of bgp peer [%s]", + cfg.peer_address); + return test; + } + + /* Fetch peer-group instance. */ + test->group = peer_group_lookup(test->bgp, cfg.peer_group); + if (!test->group) { + test->state = TEST_INTERNAL_ERROR; + test->error = str_printf( + "could not retrieve instance of bgp peer-group [%s]", + cfg.peer_group); + return test; + } + + return test; +}; + +static void test_log(struct test *test, const char *fmt, ...) +{ + va_list ap; + + /* Skip logging if test instance has previously failed. */ + if (test->state != TEST_SUCCESS) + return; + + /* Store formatted log message. */ + va_start(ap, fmt); + listnode_add(test->log, str_vprintf(fmt, ap)); + va_end(ap); +} + +static void test_finish(struct test *test) +{ + char *msg; + struct listnode *node, *nnode; + + /* Print test output header. */ + printf("%s [test] %s\n", + (test->state == TEST_SUCCESS) ? OUT_SYMBOL_OK : OUT_SYMBOL_NOK, + test->desc); + + /* Print test log messages. */ + for (ALL_LIST_ELEMENTS(test->log, node, nnode, msg)) { + printf("%s %s\n", OUT_SYMBOL_INFO, msg); + XFREE(MTYPE_TMP, msg); + } + + /* Print test error message if available. */ + if (test->state != TEST_SUCCESS && test->error) + printf("%s error: %s\n", OUT_SYMBOL_INFO, test->error); + + /* Print machine-readable result of test. */ + printf("%s\n", test->state == TEST_SUCCESS ? "OK" : "failed"); + + /* Cleanup allocated memory. */ + if (test->vty) { + vty_close(test->vty); + test->vty = NULL; + } + if (test->log) + list_delete_and_null(&test->log); + if (test->desc) + XFREE(MTYPE_TMP, test->desc); + if (test->error) + XFREE(MTYPE_TMP, test->error); + XFREE(MTYPE_TMP, test); +} + +static void test_af_flags(struct test *test, struct peer *peer, + struct test_peer_attr *attr, bool exp_val, + bool exp_ovrd) +{ + bool exp_inv, cur_val, cur_ovrd, cur_inv; + + /* Flip expected values for inverted flags. */ + exp_inv = attr->o.invert; + exp_val ^= exp_inv; + + /* Fetch current state of value, override and invert flags. */ + cur_val = !!CHECK_FLAG(peer->af_flags[attr->afi][attr->safi], + attr->u.flag); + cur_ovrd = !!CHECK_FLAG(peer->af_flags_override[attr->afi][attr->safi], + attr->u.flag); + cur_inv = !!CHECK_FLAG(peer->af_flags_invert[attr->afi][attr->safi], + attr->u.flag); + + /* Assert expected flag states. */ + TEST_ASSERT_EQ(test, cur_val, exp_val); + TEST_ASSERT_EQ(test, cur_ovrd, exp_ovrd); + TEST_ASSERT_EQ(test, cur_inv, exp_inv); +} + +static void test_af_filter(struct test *test, struct peer *peer, + struct test_peer_attr *attr, bool exp_state, + bool exp_ovrd) +{ + bool cur_ovrd; + struct bgp_filter *filter; + + /* Fetch and assert current state of override flag. */ + cur_ovrd = !!CHECK_FLAG(peer->filter_override[attr->afi][attr->safi] + [attr->u.filter.direct], + attr->u.filter.flag); + + TEST_ASSERT_EQ(test, cur_ovrd, exp_ovrd); + + /* Assert that map/list matches expected state (set/unset). */ + filter = &peer->filter[attr->afi][attr->safi]; + + switch (attr->u.filter.flag) { + case PEER_FT_DISTRIBUTE_LIST: + TEST_ASSERT_EQ(test, + !!(filter->dlist[attr->u.filter.direct].name), + exp_state); + break; + case PEER_FT_FILTER_LIST: + TEST_ASSERT_EQ(test, + !!(filter->aslist[attr->u.filter.direct].name), + exp_state); + break; + case PEER_FT_PREFIX_LIST: + TEST_ASSERT_EQ(test, + !!(filter->plist[attr->u.filter.direct].name), + exp_state); + break; + case PEER_FT_ROUTE_MAP: + TEST_ASSERT_EQ(test, + !!(filter->map[attr->u.filter.direct].name), + exp_state); + break; + case PEER_FT_UNSUPPRESS_MAP: + TEST_ASSERT_EQ(test, !!(filter->usmap.name), exp_state); + break; + } +} + +static void test_peer_attr(struct test *test, struct test_peer_attr *pa) +{ + int tc = 1; + const char *type; + const char *ec = pa->o.invert ? "no " : ""; + const char *dc = pa->o.invert ? "" : "no "; + const char *peer_cmd = pa->peer_cmd ?: pa->cmd; + const char *group_cmd = pa->group_cmd ?: pa->cmd; + struct peer *p = test->peer; + struct peer_group *g = test->group; + + if (pa->type == PEER_AT_AF_FLAG) + type = "af-flag"; + else /* if (pa->type == PEER_AT_AF_FILTER) */ + type = "af-filter"; + + /* Test Case: Switch active address-family. */ + if (pa->type == PEER_AT_AF_FLAG || pa->type == PEER_AT_AF_FILTER) { + test_log(test, "prepare: switch address-family to [%s]", + afi_safi_print(pa->afi, pa->safi)); + test_execute(test, "address-family %s %s", + str_from_afi(pa->afi), str_from_safi(pa->safi)); + } + + /* Test Case: Set flag on BGP peer. */ + test_log(test, "case %02d: set %s [%s] on [%s]", tc++, type, peer_cmd, + p->host); + test_execute(test, "%sneighbor %s %s", ec, p->host, peer_cmd); + test_config_present(test, "%sneighbor %s %s", ec, p->host, peer_cmd); + test_config_absent(test, "neighbor %s %s", g->name, pa->cmd); + if (pa->type == PEER_AT_AF_FLAG) { + test_af_flags(test, p, pa, true, true); + test_af_flags(test, g->conf, pa, false, false); + } else if (pa->type == PEER_AT_AF_FILTER) { + test_af_filter(test, p, pa, true, true); + test_af_filter(test, g->conf, pa, false, false); + } + + /* Test Case: Add BGP peer to peer-group. */ + test_log(test, "case %02d: add peer [%s] to group [%s]", tc++, p->host, + g->name); + test_execute(test, "neighbor %s peer-group %s", p->host, g->name); + test_config_present(test, "neighbor %s peer-group %s", p->host, + g->name); + test_config_present(test, "%sneighbor %s %s", ec, p->host, peer_cmd); + test_config_absent(test, "neighbor %s %s", g->name, pa->cmd); + if (pa->type == PEER_AT_AF_FLAG) { + test_af_flags(test, p, pa, true, true); + test_af_flags(test, g->conf, pa, false, false); + } else if (pa->type == PEER_AT_AF_FILTER) { + test_af_filter(test, p, pa, true, true); + test_af_filter(test, g->conf, pa, false, false); + } + + /* Test Case: Re-add BGP peer to peer-group. */ + test_log(test, "case %02d: re-add peer [%s] to group [%s]", tc++, + p->host, g->name); + test_execute(test, "neighbor %s peer-group %s", p->host, g->name); + test_config_present(test, "neighbor %s peer-group %s", p->host, + g->name); + test_config_present(test, "%sneighbor %s %s", ec, p->host, peer_cmd); + test_config_absent(test, "neighbor %s %s", g->name, pa->cmd); + if (pa->type == PEER_AT_AF_FLAG) { + test_af_flags(test, p, pa, true, true); + test_af_flags(test, g->conf, pa, false, false); + } else if (pa->type == PEER_AT_AF_FILTER) { + test_af_filter(test, p, pa, true, true); + test_af_filter(test, g->conf, pa, false, false); + } + + /* Test Case: Set flag on BGP peer-group. */ + test_log(test, "case %02d: set %s [%s] on [%s]", tc++, type, group_cmd, + g->name); + test_execute(test, "%sneighbor %s %s", ec, g->name, group_cmd); + test_config_present(test, "%sneighbor %s %s", ec, p->host, peer_cmd); + test_config_present(test, "%sneighbor %s %s", ec, g->name, group_cmd); + if (pa->type == PEER_AT_AF_FLAG) { + test_af_flags(test, p, pa, true, true); + test_af_flags(test, g->conf, pa, true, false); + } else if (pa->type == PEER_AT_AF_FILTER) { + test_af_filter(test, p, pa, true, true); + test_af_filter(test, g->conf, pa, true, false); + } + + /* Test Case: Unset flag on BGP peer-group. */ + test_log(test, "case %02d: unset %s [%s] on [%s]", tc++, type, + group_cmd, g->name); + test_execute(test, "%sneighbor %s %s", dc, g->name, group_cmd); + test_config_present(test, "%sneighbor %s %s", ec, p->host, peer_cmd); + test_config_absent(test, "neighbor %s %s", g->name, pa->cmd); + if (pa->type == PEER_AT_AF_FLAG) { + test_af_flags(test, p, pa, true, true); + test_af_flags(test, g->conf, pa, false, false); + } else if (pa->type == PEER_AT_AF_FILTER) { + test_af_filter(test, p, pa, true, true); + test_af_filter(test, g->conf, pa, false, false); + } + + /* Test Case: Set flag on BGP peer-group. */ + test_log(test, "case %02d: set %s [%s] on [%s]", tc++, type, group_cmd, + g->name); + test_execute(test, "%sneighbor %s %s", ec, g->name, group_cmd); + test_config_present(test, "%sneighbor %s %s", ec, p->host, peer_cmd); + test_config_present(test, "%sneighbor %s %s", ec, g->name, group_cmd); + if (pa->type == PEER_AT_AF_FLAG) { + test_af_flags(test, p, pa, true, true); + test_af_flags(test, g->conf, pa, true, false); + } else if (pa->type == PEER_AT_AF_FILTER) { + test_af_filter(test, p, pa, true, true); + test_af_filter(test, g->conf, pa, true, false); + } + + /* Test Case: Re-set flag on BGP peer. */ + test_log(test, "case %02d: re-set %s [%s] on [%s]", tc++, type, + peer_cmd, p->host); + test_execute(test, "%sneighbor %s %s", ec, p->host, peer_cmd); + test_config_present(test, "%sneighbor %s %s", ec, p->host, peer_cmd); + test_config_present(test, "%sneighbor %s %s", ec, g->name, group_cmd); + if (pa->type == PEER_AT_AF_FLAG) { + test_af_flags(test, p, pa, true, true); + test_af_flags(test, g->conf, pa, true, false); + } else if (pa->type == PEER_AT_AF_FILTER) { + test_af_filter(test, p, pa, true, true); + test_af_filter(test, g->conf, pa, true, false); + } + + /* Test Case: Unset flag on BGP peer. */ + test_log(test, "case %02d: unset %s [%s] on [%s]", tc++, type, peer_cmd, + p->host); + test_execute(test, "%sneighbor %s %s", dc, p->host, peer_cmd); + test_config_absent(test, "neighbor %s %s", p->host, pa->cmd); + test_config_present(test, "%sneighbor %s %s", ec, g->name, group_cmd); + if (pa->type == PEER_AT_AF_FLAG) { + test_af_flags(test, p, pa, true, false); + test_af_flags(test, g->conf, pa, true, false); + } else if (pa->type == PEER_AT_AF_FILTER) { + test_af_filter(test, p, pa, true, false); + test_af_filter(test, g->conf, pa, true, false); + } + + /* Test Case: Unset flag on BGP peer-group. */ + test_log(test, "case %02d: unset %s [%s] on [%s]", tc++, type, + group_cmd, g->name); + test_execute(test, "%sneighbor %s %s", dc, g->name, group_cmd); + test_config_absent(test, "neighbor %s %s", p->host, pa->cmd); + test_config_absent(test, "neighbor %s %s", g->name, pa->cmd); + if (pa->type == PEER_AT_AF_FLAG) { + test_af_flags(test, p, pa, false, false); + test_af_flags(test, g->conf, pa, false, false); + } else if (pa->type == PEER_AT_AF_FILTER) { + test_af_filter(test, p, pa, false, false); + test_af_filter(test, g->conf, pa, false, false); + } + + /* Test Case: Set flag on BGP peer. */ + test_log(test, "case %02d: set %s [%s] on [%s]", tc++, type, peer_cmd, + p->host); + test_execute(test, "%sneighbor %s %s", ec, p->host, peer_cmd); + test_config_present(test, "%sneighbor %s %s", ec, p->host, peer_cmd); + test_config_absent(test, "neighbor %s %s", g->name, pa->cmd); + if (pa->type == PEER_AT_AF_FLAG) { + test_af_flags(test, p, pa, true, true); + test_af_flags(test, g->conf, pa, false, false); + } else if (pa->type == PEER_AT_AF_FILTER) { + test_af_filter(test, p, pa, true, true); + test_af_filter(test, g->conf, pa, false, false); + } +} + +static void bgp_startup(void) +{ + cmd_init(1); + openzlog("testbgpd", "NONE", 0, LOG_CONS | LOG_NDELAY | LOG_PID, + LOG_DAEMON); + zprivs_preinit(&bgpd_privs); + zprivs_init(&bgpd_privs); + + master = thread_master_create(NULL); + bgp_master_init(master); + bgp_option_set(BGP_OPT_NO_LISTEN); + vrf_init(NULL, NULL, NULL, NULL); + bgp_init(); + bgp_pthreads_run(); +} + +static void bgp_shutdown(void) +{ + struct bgp *bgp; + struct listnode *node, *nnode; + + bgp_terminate(); + bgp_close(); + for (ALL_LIST_ELEMENTS(bm->bgp, node, nnode, bgp)) + bgp_delete(bgp); + bgp_dump_finish(); + bgp_route_finish(); + bgp_route_map_terminate(); + bgp_attr_finish(); + bgp_pthreads_finish(); + access_list_add_hook(NULL); + access_list_delete_hook(NULL); + access_list_reset(); + as_list_add_hook(NULL); + as_list_delete_hook(NULL); + bgp_filter_reset(); + prefix_list_add_hook(NULL); + prefix_list_delete_hook(NULL); + prefix_list_reset(); + community_list_terminate(bgp_clist); + vrf_terminate(); +#ifdef ENABLE_BGP_VNC + vnc_zebra_destroy(); +#endif + bgp_zebra_destroy(); + + bf_free(bm->rd_idspace); + list_delete_and_null(&bm->bgp); + memset(bm, 0, sizeof(*bm)); + + vty_terminate(); + cmd_terminate(); + zprivs_terminate(&bgpd_privs); + thread_master_free(master); + master = NULL; + closezlog(); +} + +int main(void) +{ + int i, ii; + struct list *pa_list; + struct test_peer_attr *pa, *pac; + struct listnode *node, *nnode; + + bgp_startup(); + + pa_list = list_new(); + i = 0; + while (test_peer_attrs[i].cmd) { + pa = &test_peer_attrs[i++]; + + /* Just copy the peer attribute structure for global flags. */ + if (pa->type == PEER_AT_GLOBAL_FLAG) { + pac = XMALLOC(MTYPE_TMP, sizeof(struct test_peer_attr)); + memcpy(pac, pa, sizeof(struct test_peer_attr)); + listnode_add(pa_list, pac); + continue; + } + + /* Fallback to default families if not specified. */ + if (!pa->families[0].afi && !pa->families[0].safi) + memcpy(&pa->families, test_default_families, + sizeof(test_default_families)); + + /* Add peer attribute definition for each address family. */ + ii = 0; + while (pa->families[ii].afi && pa->families[ii].safi) { + pac = XMALLOC(MTYPE_TMP, sizeof(struct test_peer_attr)); + memcpy(pac, pa, sizeof(struct test_peer_attr)); + + pac->afi = pa->families[ii].afi; + pac->safi = pa->families[ii].safi; + listnode_add(pa_list, pac); + + ii++; + } + } + + for (ALL_LIST_ELEMENTS(pa_list, node, nnode, pa)) { + char *desc; + struct test *test; + + /* Build test description string. */ + if (pa->afi && pa->safi) + desc = str_printf("peer\\%s-%s\\%s", + str_from_afi(pa->afi), + str_from_safi(pa->safi), pa->cmd); + else + desc = str_printf("peer\\%s", pa->cmd); + + /* Initialize new test instance. */ + test = test_new(desc, pa->o.use_ibgp); + XFREE(MTYPE_TMP, desc); + + /* Execute tests and finish test instance. */ + test_peer_attr(test, pa); + test_finish(test); + + /* Print empty line as spacer. */ + printf("\n"); + + /* Free memory used for peer-attr declaration. */ + XFREE(MTYPE_TMP, pa); + } + + list_delete_and_null(&pa_list); + bgp_shutdown(); + + return 0; +} diff --git a/tests/bgpd/test_peer_attr.py b/tests/bgpd/test_peer_attr.py new file mode 100644 index 0000000000..d93dfc0050 --- /dev/null +++ b/tests/bgpd/test_peer_attr.py @@ -0,0 +1,172 @@ +import frrtest + +class TestFlag(frrtest.TestMultiOut): + program = './test_peer_attr' + +# List of tests can be generated by executing: +# $> ./test_peer_attr 2>&1 | sed -n 's/\\/\\\\/g; s/\S\+ \[test\] \(.\+\)/TestFlag.okfail(\x27\1\x27)/pg' +# +TestFlag.okfail('peer\\ipv4-unicast\\addpath-tx-all-paths') +TestFlag.okfail('peer\\ipv4-multicast\\addpath-tx-all-paths') +TestFlag.okfail('peer\\ipv6-unicast\\addpath-tx-all-paths') +TestFlag.okfail('peer\\ipv6-multicast\\addpath-tx-all-paths') +TestFlag.okfail('peer\\ipv4-unicast\\addpath-tx-bestpath-per-AS') +TestFlag.okfail('peer\\ipv4-multicast\\addpath-tx-bestpath-per-AS') +TestFlag.okfail('peer\\ipv6-unicast\\addpath-tx-bestpath-per-AS') +TestFlag.okfail('peer\\ipv6-multicast\\addpath-tx-bestpath-per-AS') +TestFlag.okfail('peer\\ipv4-unicast\\allowas-in') +TestFlag.okfail('peer\\ipv4-multicast\\allowas-in') +TestFlag.okfail('peer\\ipv6-unicast\\allowas-in') +TestFlag.okfail('peer\\ipv6-multicast\\allowas-in') +TestFlag.okfail('peer\\ipv4-unicast\\allowas-in origin') +TestFlag.okfail('peer\\ipv4-multicast\\allowas-in origin') +TestFlag.okfail('peer\\ipv6-unicast\\allowas-in origin') +TestFlag.okfail('peer\\ipv6-multicast\\allowas-in origin') +TestFlag.okfail('peer\\ipv4-unicast\\as-override') +TestFlag.okfail('peer\\ipv4-multicast\\as-override') +TestFlag.okfail('peer\\ipv6-unicast\\as-override') +TestFlag.okfail('peer\\ipv6-multicast\\as-override') +TestFlag.okfail('peer\\ipv4-unicast\\attribute-unchanged as-path') +TestFlag.okfail('peer\\ipv4-multicast\\attribute-unchanged as-path') +TestFlag.okfail('peer\\ipv6-unicast\\attribute-unchanged as-path') +TestFlag.okfail('peer\\ipv6-multicast\\attribute-unchanged as-path') +TestFlag.okfail('peer\\ipv4-unicast\\attribute-unchanged next-hop') +TestFlag.okfail('peer\\ipv4-multicast\\attribute-unchanged next-hop') +TestFlag.okfail('peer\\ipv6-unicast\\attribute-unchanged next-hop') +TestFlag.okfail('peer\\ipv6-multicast\\attribute-unchanged next-hop') +TestFlag.okfail('peer\\ipv4-unicast\\attribute-unchanged med') +TestFlag.okfail('peer\\ipv4-multicast\\attribute-unchanged med') +TestFlag.okfail('peer\\ipv6-unicast\\attribute-unchanged med') +TestFlag.okfail('peer\\ipv6-multicast\\attribute-unchanged med') +TestFlag.okfail('peer\\ipv4-unicast\\attribute-unchanged as-path next-hop') +TestFlag.okfail('peer\\ipv4-multicast\\attribute-unchanged as-path next-hop') +TestFlag.okfail('peer\\ipv6-unicast\\attribute-unchanged as-path next-hop') +TestFlag.okfail('peer\\ipv6-multicast\\attribute-unchanged as-path next-hop') +TestFlag.okfail('peer\\ipv4-unicast\\attribute-unchanged as-path med') +TestFlag.okfail('peer\\ipv4-multicast\\attribute-unchanged as-path med') +TestFlag.okfail('peer\\ipv6-unicast\\attribute-unchanged as-path med') +TestFlag.okfail('peer\\ipv6-multicast\\attribute-unchanged as-path med') +TestFlag.okfail('peer\\ipv4-unicast\\attribute-unchanged as-path next-hop med') +TestFlag.okfail('peer\\ipv4-multicast\\attribute-unchanged as-path next-hop med') +TestFlag.okfail('peer\\ipv6-unicast\\attribute-unchanged as-path next-hop med') +TestFlag.okfail('peer\\ipv6-multicast\\attribute-unchanged as-path next-hop med') +TestFlag.okfail('peer\\ipv4-unicast\\capability orf prefix-list send') +TestFlag.okfail('peer\\ipv4-multicast\\capability orf prefix-list send') +TestFlag.okfail('peer\\ipv6-unicast\\capability orf prefix-list send') +TestFlag.okfail('peer\\ipv6-multicast\\capability orf prefix-list send') +TestFlag.okfail('peer\\ipv4-unicast\\capability orf prefix-list receive') +TestFlag.okfail('peer\\ipv4-multicast\\capability orf prefix-list receive') +TestFlag.okfail('peer\\ipv6-unicast\\capability orf prefix-list receive') +TestFlag.okfail('peer\\ipv6-multicast\\capability orf prefix-list receive') +TestFlag.okfail('peer\\ipv4-unicast\\capability orf prefix-list both') +TestFlag.okfail('peer\\ipv4-multicast\\capability orf prefix-list both') +TestFlag.okfail('peer\\ipv6-unicast\\capability orf prefix-list both') +TestFlag.okfail('peer\\ipv6-multicast\\capability orf prefix-list both') +TestFlag.okfail('peer\\ipv4-unicast\\default-originate') +TestFlag.okfail('peer\\ipv4-multicast\\default-originate') +TestFlag.okfail('peer\\ipv6-unicast\\default-originate') +TestFlag.okfail('peer\\ipv6-multicast\\default-originate') +TestFlag.okfail('peer\\ipv4-unicast\\default-originate route-map') +TestFlag.okfail('peer\\ipv4-multicast\\default-originate route-map') +TestFlag.okfail('peer\\ipv6-unicast\\default-originate route-map') +TestFlag.okfail('peer\\ipv6-multicast\\default-originate route-map') +TestFlag.okfail('peer\\ipv4-unicast\\filter-list') +TestFlag.okfail('peer\\ipv4-multicast\\filter-list') +TestFlag.okfail('peer\\ipv6-unicast\\filter-list') +TestFlag.okfail('peer\\ipv6-multicast\\filter-list') +TestFlag.okfail('peer\\ipv4-unicast\\filter-list') +TestFlag.okfail('peer\\ipv4-multicast\\filter-list') +TestFlag.okfail('peer\\ipv6-unicast\\filter-list') +TestFlag.okfail('peer\\ipv6-multicast\\filter-list') +TestFlag.okfail('peer\\ipv4-unicast\\maximum-prefix') +TestFlag.okfail('peer\\ipv4-multicast\\maximum-prefix') +TestFlag.okfail('peer\\ipv6-unicast\\maximum-prefix') +TestFlag.okfail('peer\\ipv6-multicast\\maximum-prefix') +TestFlag.okfail('peer\\ipv4-unicast\\maximum-prefix') +TestFlag.okfail('peer\\ipv4-multicast\\maximum-prefix') +TestFlag.okfail('peer\\ipv6-unicast\\maximum-prefix') +TestFlag.okfail('peer\\ipv6-multicast\\maximum-prefix') +TestFlag.okfail('peer\\ipv4-unicast\\maximum-prefix') +TestFlag.okfail('peer\\ipv4-multicast\\maximum-prefix') +TestFlag.okfail('peer\\ipv6-unicast\\maximum-prefix') +TestFlag.okfail('peer\\ipv6-multicast\\maximum-prefix') +TestFlag.okfail('peer\\ipv4-unicast\\maximum-prefix') +TestFlag.okfail('peer\\ipv4-multicast\\maximum-prefix') +TestFlag.okfail('peer\\ipv6-unicast\\maximum-prefix') +TestFlag.okfail('peer\\ipv6-multicast\\maximum-prefix') +TestFlag.okfail('peer\\ipv4-unicast\\maximum-prefix') +TestFlag.okfail('peer\\ipv4-multicast\\maximum-prefix') +TestFlag.okfail('peer\\ipv6-unicast\\maximum-prefix') +TestFlag.okfail('peer\\ipv6-multicast\\maximum-prefix') +TestFlag.okfail('peer\\ipv4-unicast\\next-hop-self') +TestFlag.okfail('peer\\ipv4-multicast\\next-hop-self') +TestFlag.okfail('peer\\ipv6-unicast\\next-hop-self') +TestFlag.okfail('peer\\ipv6-multicast\\next-hop-self') +TestFlag.okfail('peer\\ipv4-unicast\\next-hop-self force') +TestFlag.okfail('peer\\ipv4-multicast\\next-hop-self force') +TestFlag.okfail('peer\\ipv6-unicast\\next-hop-self force') +TestFlag.okfail('peer\\ipv6-multicast\\next-hop-self force') +TestFlag.okfail('peer\\ipv4-unicast\\prefix-list') +TestFlag.okfail('peer\\ipv4-multicast\\prefix-list') +TestFlag.okfail('peer\\ipv6-unicast\\prefix-list') +TestFlag.okfail('peer\\ipv6-multicast\\prefix-list') +TestFlag.okfail('peer\\ipv4-unicast\\prefix-list') +TestFlag.okfail('peer\\ipv4-multicast\\prefix-list') +TestFlag.okfail('peer\\ipv6-unicast\\prefix-list') +TestFlag.okfail('peer\\ipv6-multicast\\prefix-list') +TestFlag.okfail('peer\\ipv4-unicast\\remove-private-AS') +TestFlag.okfail('peer\\ipv4-multicast\\remove-private-AS') +TestFlag.okfail('peer\\ipv6-unicast\\remove-private-AS') +TestFlag.okfail('peer\\ipv6-multicast\\remove-private-AS') +TestFlag.okfail('peer\\ipv4-unicast\\remove-private-AS all') +TestFlag.okfail('peer\\ipv4-multicast\\remove-private-AS all') +TestFlag.okfail('peer\\ipv6-unicast\\remove-private-AS all') +TestFlag.okfail('peer\\ipv6-multicast\\remove-private-AS all') +TestFlag.okfail('peer\\ipv4-unicast\\remove-private-AS replace-AS') +TestFlag.okfail('peer\\ipv4-multicast\\remove-private-AS replace-AS') +TestFlag.okfail('peer\\ipv6-unicast\\remove-private-AS replace-AS') +TestFlag.okfail('peer\\ipv6-multicast\\remove-private-AS replace-AS') +TestFlag.okfail('peer\\ipv4-unicast\\remove-private-AS all replace-AS') +TestFlag.okfail('peer\\ipv4-multicast\\remove-private-AS all replace-AS') +TestFlag.okfail('peer\\ipv6-unicast\\remove-private-AS all replace-AS') +TestFlag.okfail('peer\\ipv6-multicast\\remove-private-AS all replace-AS') +TestFlag.okfail('peer\\ipv4-unicast\\route-map') +TestFlag.okfail('peer\\ipv4-multicast\\route-map') +TestFlag.okfail('peer\\ipv6-unicast\\route-map') +TestFlag.okfail('peer\\ipv6-multicast\\route-map') +TestFlag.okfail('peer\\ipv4-unicast\\route-map') +TestFlag.okfail('peer\\ipv4-multicast\\route-map') +TestFlag.okfail('peer\\ipv6-unicast\\route-map') +TestFlag.okfail('peer\\ipv6-multicast\\route-map') +TestFlag.okfail('peer\\ipv4-unicast\\route-reflector-client') +TestFlag.okfail('peer\\ipv4-multicast\\route-reflector-client') +TestFlag.okfail('peer\\ipv6-unicast\\route-reflector-client') +TestFlag.okfail('peer\\ipv6-multicast\\route-reflector-client') +TestFlag.okfail('peer\\ipv4-unicast\\route-server-client') +TestFlag.okfail('peer\\ipv4-multicast\\route-server-client') +TestFlag.okfail('peer\\ipv6-unicast\\route-server-client') +TestFlag.okfail('peer\\ipv6-multicast\\route-server-client') +TestFlag.okfail('peer\\ipv4-unicast\\send-community') +TestFlag.okfail('peer\\ipv4-multicast\\send-community') +TestFlag.okfail('peer\\ipv6-unicast\\send-community') +TestFlag.okfail('peer\\ipv6-multicast\\send-community') +TestFlag.okfail('peer\\ipv4-unicast\\send-community extended') +TestFlag.okfail('peer\\ipv4-multicast\\send-community extended') +TestFlag.okfail('peer\\ipv6-unicast\\send-community extended') +TestFlag.okfail('peer\\ipv6-multicast\\send-community extended') +TestFlag.okfail('peer\\ipv4-unicast\\send-community large') +TestFlag.okfail('peer\\ipv4-multicast\\send-community large') +TestFlag.okfail('peer\\ipv6-unicast\\send-community large') +TestFlag.okfail('peer\\ipv6-multicast\\send-community large') +TestFlag.okfail('peer\\ipv4-unicast\\soft-reconfiguration inbound') +TestFlag.okfail('peer\\ipv4-multicast\\soft-reconfiguration inbound') +TestFlag.okfail('peer\\ipv6-unicast\\soft-reconfiguration inbound') +TestFlag.okfail('peer\\ipv6-multicast\\soft-reconfiguration inbound') +TestFlag.okfail('peer\\ipv4-unicast\\unsuppress-map') +TestFlag.okfail('peer\\ipv4-multicast\\unsuppress-map') +TestFlag.okfail('peer\\ipv6-unicast\\unsuppress-map') +TestFlag.okfail('peer\\ipv6-multicast\\unsuppress-map') +TestFlag.okfail('peer\\ipv4-unicast\\weight') +TestFlag.okfail('peer\\ipv4-multicast\\weight') +TestFlag.okfail('peer\\ipv6-unicast\\weight') +TestFlag.okfail('peer\\ipv6-multicast\\weight')